index.vue 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  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="
  43. margin-bottom: 6px;
  44. cursor: pointer;
  45. border: 1px solid #ccc;
  46. border-radius: 6px;
  47. padding: 3px;
  48. background-color: transparent;
  49. box-shadow: 0 1px 2px rgba(0,0,0,0.05);
  50. "
  51. @click.stop="openDialogEdit(course)"
  52. >
  53. {{ course.classdetail }} 老师:({{ course.teacher_name }}) 教室:{{ course.classroom }}
  54. <el-link
  55. type="danger"
  56. style="font-size: 12px; top: 0; right: 0"
  57. @click.stop="deleteCourse(course.id)"
  58. >
  59. 删除
  60. </el-link>
  61. </div>
  62. </div>
  63. </template>
  64. </el-table-column>
  65. </el-table>
  66. </div>
  67. <!-- <fs-crud ref="crudRef" v-bind="crudBinding"> </fs-crud> -->
  68. <AdditionDialog ref="dialogRef" @saved="loadData" />
  69. <!-- </fs-page> -->
  70. </div>
  71. </template>
  72. <script lang="ts" setup name="timetablemanage">
  73. import { useFs } from '@fast-crud/fast-crud';
  74. import dayjs from 'dayjs';
  75. import isLeapYear from 'dayjs/plugin/isLeapYear';
  76. import isoWeek from 'dayjs/plugin/isoWeek';
  77. import isoWeeksInYear from 'dayjs/plugin/isoWeeksInYear';
  78. import weekOfYear from 'dayjs/plugin/weekOfYear';
  79. import { computed, onMounted, ref } from 'vue';
  80. import { GetPermission,getScheduleData,DelObj } from './api';
  81. import { createCrudOptions } from './crud';
  82. import { handleColumnPermission } from '/@/utils/columnPermission';
  83. import AdditionDialog from './AdditionDialog/index.vue';
  84. import { inject } from 'vue';
  85. import { ElMessage } from 'element-plus';
  86. const isEmbedded = inject('isEmbedded', false);
  87. const onCourseSelected = inject('onCourseSelected', () => {});
  88. const { crudBinding, crudRef, crudExpose, crudOptions, resetCrudOptions } = useFs({ createCrudOptions });
  89. dayjs.extend(weekOfYear)
  90. dayjs.extend(isoWeek)
  91. dayjs.extend(isLeapYear)
  92. dayjs.extend(isoWeeksInYear)
  93. const currentWeek = ref(dayjs().isoWeek())
  94. const totalWeeks = ref(dayjs().isoWeeksInYear())
  95. const weekOffset = ref(0)
  96. const columnColorMap: string[] = [
  97. '#e3f2fd',
  98. '#e8f5e9',
  99. '#fff3e0',
  100. '#fce4ec',
  101. '#ede7f6',
  102. '#f3e5f5',
  103. '#fbe9e7'
  104. ]
  105. const timeLabels = ref<string[]>([]); // 从接口获取
  106. function getTimeSlot(index: number): string {
  107. return timeLabels.value[index] || '—';
  108. }
  109. function getPeriodLabel(index: number): string {
  110. return `时间段`;
  111. }
  112. const displayWeek = computed(() => currentWeek.value + weekOffset.value)
  113. const dialogRef = ref();
  114. function openDialog(data?: any) {
  115. dialogRef.value.open(data);
  116. }
  117. const selectedCourses = ref<any[]>([]);
  118. const courseSelectVisible = ref(false);
  119. function editCourse(course: any) {
  120. courseSelectVisible.value = false;
  121. openDialog(course);
  122. }
  123. async function deleteCourse(courseId: number) {
  124. try {
  125. await DelObj(courseId);
  126. ElMessage.success('删除成功');
  127. await loadData(); // 重新加载数据以刷新视图
  128. } catch (error) {
  129. ElMessage.error('删除失败,请稍后重试');
  130. // console.error('删除课程失败:', error);
  131. }
  132. }
  133. function getStartTime(time: number): string {
  134. const timeMap = {
  135. 1: '09:00:00',
  136. 2: '14:00:00',
  137. 3: '19:00:00'
  138. };
  139. return timeMap[time] || '09:00';
  140. }
  141. function openDialogEdit(course?: any) {
  142. const courseId = course?.id || '';
  143. const courseClassdetail = course?.classdetail || '';
  144. const courseteacher_name = course?.teacher_name || '';
  145. const courseclassroom = course?.classroom || '';
  146. const coursetime_label = course?.time_label || '';
  147. const courseclassdate=course?.class_date||'';
  148. const timeStr = getStartTime(course?.time);
  149. const expectedStartTime = `${courseclassdate} ${timeStr}`;
  150. const courseName = `${courseClassdetail} - ${courseteacher_name} - ${courseclassroom} - ${coursetime_label}`;
  151. // eslint-disable-next-line no-console
  152. // console.log("expectedStartTime:::",expectedStartTime)
  153. // eslint-disable-next-line no-console
  154. // console.log("courseName:::",courseName)
  155. if (isEmbedded && typeof onCourseSelected === 'function') {
  156. try {
  157. onCourseSelected({
  158. course_id: courseId,
  159. course_Name:courseName,
  160. expected_start_time: expectedStartTime
  161. });
  162. // dialogRef.value.open(course);
  163. } catch (e) {
  164. // eslint-disable-next-line no-console
  165. console.warn('onCourseSelected failed:', e);
  166. }
  167. } else {
  168. dialogRef.value.open(course);
  169. }
  170. }
  171. function nextWeek() {
  172. if (currentWeek.value + weekOffset.value < totalWeeks.value){
  173. weekOffset.value++;
  174. loadData() ;
  175. }
  176. }
  177. function prevWeek() {
  178. weekOffset.value--;
  179. loadData() ;
  180. }
  181. const headerStyle = {
  182. backgroundColor: '#f5f5f5',
  183. fontWeight: 'bold',
  184. color: '#333',
  185. textAlign: 'center'
  186. }
  187. const scheduleData = ref<any[]>([])
  188. const weekDates = ref<string[]>([]);
  189. async function loadData() {
  190. const result = await GetPermission(weekOffset.value);
  191. timeLabels.value = result.data.time_labels;
  192. weekDates.value = result.data.week_labels_with_date;
  193. // scheduleData.value = await getScheduleData(weekOffset.value);
  194. scheduleData.value = await getScheduleData(result);
  195. }
  196. function getSchedule(row: any, index: number) {
  197. return row[`day${index + 1}`] || ''
  198. }
  199. // 页面打开后获取列表数据
  200. onMounted(async () => {
  201. // 设置列权限
  202. const newOptions = await handleColumnPermission(GetPermission, crudOptions);
  203. //重置crudBinding
  204. resetCrudOptions(newOptions);
  205. // 刷新
  206. crudExpose.doRefresh();
  207. await loadData();
  208. });
  209. </script>
  210. <style scoped>
  211. .header-bar {
  212. display: flex;
  213. align-items: center;
  214. justify-content: center;
  215. gap: 20px;
  216. margin: 16px auto;
  217. width: fit-content;
  218. }
  219. .week-display {
  220. font-size: 16px;
  221. font-weight: bold;
  222. padding: 0 12px;
  223. min-width: 80px;
  224. text-align: center;
  225. }
  226. </style>