index.vue 6.5 KB

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