index.vue 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. <template>
  2. <!-- <fs-page> -->
  3. <div>
  4. <div class="schedule-wrapper">
  5. <div class="header">
  6. <div class="header-bar">
  7. <el-button type="primary" icon="el-icon-arrow-left" @click="prevWeek" circle ><</el-button>
  8. <span class="week-display">{{ displayWeek }} 周</span>
  9. <el-button type="primary" icon="el-icon-arrow-right" @click="nextWeek" circle
  10. :disabled="currentWeek + weekOffset >= totalWeeks" >></el-button>
  11. </div>
  12. <div>
  13. <el-button type="success" icon="el-icon-plus" @click="openDialog()">添加课程</el-button>
  14. </div>
  15. </div>
  16. <el-table :data="scheduleData" style="width: 100%; background-color: white" :header-cell-style="headerStyle">
  17. <el-table-column label="时间段" width="100">
  18. <template #default="scope">
  19. {{ scope.row.timeLabel }}
  20. </template>
  21. </el-table-column>
  22. <!-- 星期一至星期日列 -->
  23. <el-table-column
  24. v-for="(day, index) in weekDates"
  25. :key="index"
  26. :label="day"
  27. min-width="160"
  28. >
  29. <template #default="scope">
  30. <div
  31. :style="{
  32. backgroundColor: columnColorMap[index],
  33. padding: '6px',
  34. borderRadius: '4px',
  35. whiteSpace: 'pre-line',
  36. minHeight: '40px'
  37. }"
  38. >
  39. <div
  40. v-for="course in scope.row[`day${index + 1}`]"
  41. :key="course.id"
  42. style="margin-bottom: 4px; cursor: pointer"
  43. @click.stop="openDialogEdit(course, scope.row.timeLabel, index)"
  44. >
  45. {{ course.classdetail }} 老师:({{ course.teacher_name }}) 班级:{{ course.classroom }}
  46. </div>
  47. </div>
  48. </template>
  49. </el-table-column>
  50. </el-table>
  51. </div>
  52. <fs-crud ref="crudRef" v-bind="crudBinding"> </fs-crud>
  53. <AdditionDialog ref="dialogRef" @saved="loadData" />
  54. <!-- </fs-page> -->
  55. </div>
  56. </template>
  57. <script lang="ts" setup name="timetablemanage">
  58. import { useFs } from '@fast-crud/fast-crud';
  59. import dayjs from 'dayjs';
  60. import isLeapYear from 'dayjs/plugin/isLeapYear';
  61. import isoWeek from 'dayjs/plugin/isoWeek';
  62. import isoWeeksInYear from 'dayjs/plugin/isoWeeksInYear';
  63. import weekOfYear from 'dayjs/plugin/weekOfYear';
  64. import { computed, onMounted, ref } from 'vue';
  65. import { GetPermission,getScheduleData } from './api';
  66. import { createCrudOptions } from './crud';
  67. import { handleColumnPermission } from '/@/utils/columnPermission';
  68. import AdditionDialog from './AdditionDialog/index.vue';
  69. import { inject } from 'vue';
  70. const isEmbedded = inject('isEmbedded', false);
  71. const onCourseSelected = inject('onCourseSelected', () => {});
  72. const { crudBinding, crudRef, crudExpose, crudOptions, resetCrudOptions } = useFs({ createCrudOptions });
  73. dayjs.extend(weekOfYear)
  74. dayjs.extend(isoWeek)
  75. dayjs.extend(isLeapYear)
  76. dayjs.extend(isoWeeksInYear)
  77. const currentWeek = ref(dayjs().isoWeek())
  78. const totalWeeks = ref(dayjs().isoWeeksInYear())
  79. const weekOffset = ref(0)
  80. const columnColorMap: string[] = [
  81. '#e3f2fd',
  82. '#e8f5e9',
  83. '#fff3e0',
  84. '#fce4ec',
  85. '#ede7f6',
  86. '#f3e5f5',
  87. '#fbe9e7'
  88. ]
  89. const timeLabels = ref<string[]>([]); // 从接口获取
  90. function getTimeSlot(index: number): string {
  91. return timeLabels.value[index] || '—';
  92. }
  93. function getPeriodLabel(index: number): string {
  94. return `时间段`;
  95. }
  96. const displayWeek = computed(() => currentWeek.value + weekOffset.value)
  97. const dialogRef = ref();
  98. function openDialog(data?: any) {
  99. dialogRef.value.open(data);
  100. }
  101. const selectedCourses = ref<any[]>([]);
  102. const courseSelectVisible = ref(false);
  103. function editCourse(course: any) {
  104. courseSelectVisible.value = false;
  105. openDialog(course);
  106. }
  107. function openDialogEdit(course?: any, timeLabel?: string, dayIndex?: number) {
  108. const courseName = course?.classdetail || '';
  109. const dateLabel = weekDates.value[dayIndex];
  110. const dateMatch = dateLabel.match(/\d{4}-\d{2}-\d{2}/);
  111. const dateStr = dateMatch ? dateMatch[0] : dayjs().format('YYYY-MM-DD');
  112. const timeStr = timeLabel?.split('-')[0] || '09:00';
  113. const expectedStartTime = `${dateStr} ${timeStr}:00`;
  114. if (isEmbedded && typeof onCourseSelected === 'function') {
  115. try {
  116. onCourseSelected({
  117. course_name: courseName,
  118. expected_start_time: expectedStartTime
  119. });
  120. dialogRef.value.open(course);
  121. } catch (e) {
  122. console.warn('onCourseSelected failed:', e);
  123. }
  124. } else {
  125. dialogRef.value.open(course);
  126. }
  127. }
  128. function nextWeek() {
  129. if (currentWeek.value + weekOffset.value < totalWeeks.value){
  130. weekOffset.value++;
  131. loadData() ;
  132. }
  133. }
  134. function prevWeek() {
  135. weekOffset.value--;
  136. loadData() ;
  137. }
  138. const headerStyle = {
  139. backgroundColor: '#f5f5f5',
  140. fontWeight: 'bold',
  141. color: '#333',
  142. textAlign: 'center'
  143. }
  144. const scheduleData = ref<any[]>([])
  145. const weekDates = ref<string[]>([]);
  146. async function loadData() {
  147. const result = await GetPermission(weekOffset.value);
  148. timeLabels.value = result.data.time_labels;
  149. weekDates.value = result.data.week_labels_with_date;
  150. // scheduleData.value = await getScheduleData(weekOffset.value);
  151. scheduleData.value = await getScheduleData(result);
  152. }
  153. function getSchedule(row: any, index: number) {
  154. return row[`day${index + 1}`] || ''
  155. }
  156. // 页面打开后获取列表数据
  157. onMounted(async () => {
  158. // 设置列权限
  159. const newOptions = await handleColumnPermission(GetPermission, crudOptions);
  160. //重置crudBinding
  161. resetCrudOptions(newOptions);
  162. // 刷新
  163. crudExpose.doRefresh();
  164. await loadData();
  165. });
  166. </script>
  167. <style scoped>
  168. .header-bar {
  169. display: flex;
  170. align-items: center;
  171. justify-content: center;
  172. gap: 20px;
  173. margin: 16px auto;
  174. width: fit-content;
  175. }
  176. .week-display {
  177. font-size: 16px;
  178. font-weight: bold;
  179. padding: 0 12px;
  180. min-width: 80px;
  181. text-align: center;
  182. }
  183. </style>