diff --git a/frontend/src/api/teachBusiness/timetable.js b/frontend/src/api/teachBusiness/timetable.js new file mode 100644 index 000000000..4dca5ab93 --- /dev/null +++ b/frontend/src/api/teachBusiness/timetable.js @@ -0,0 +1,89 @@ +import request from '@/utils/request' + +// 课表查询管理(KCBController /kcb,前端 baseURL 为 /api,此处不重复 /api 前缀) +// 接口依据:课表页面接口规格 +// 查询实体 TimetableQuery:nd 年度(可选,不传自动取当前学期)、xydbh 学员队编号/班次(可选,按班次筛选) + +/** + * 今日课表 + * GET /kcb/today/list + * @param params TimetableQuery:nd、xydbh(均可选) + * 返回:今日课表记录(实体字段 bh/sksj/kc/zrdw/bc/jy/cd/jxnrxff/bz) + */ +export function listToday(data) { + return request({ + url: '/kcb/today/list', + method: 'get', + params: data + }) +} + +/** + * 本周课表 + * GET /kcb/week/list + * @param params TimetableQuery:nd、xydbh(均可选) + * 返回:本周课表记录 + */ +export function listWeek(data) { + return request({ + url: '/kcb/week/list', + method: 'get', + params: data + }) +} + +/** + * 班次课表(分页) + * GET /kcb/class/list + * @param params pageNum、pageSize,cond=TimetableQuery:nd、xydbh + * 返回:PageResult<班次课程记录>(records/total) + */ +export function listClass(params) { + return request({ + url: '/kcb/class/list', + method: 'get', + params + }) +} + +/** + * 下载学期教学实施计划 Excel + * GET /kcb/class/export-plan + * @param data TimetableQuery:nd、xydbh + */ +export function exportClassPlan(data) { + return request({ + url: '/kcb/class/export-plan', + method: 'get', + params: data, + responseType: 'blob' + }) +} + +/** + * 下载学期成绩表 Excel + * GET /kcb/class/export-grades + * @param data TimetableQuery:nd、xydbh + */ +export function exportClassGrades(data) { + return request({ + url: '/kcb/class/export-grades', + method: 'get', + params: data, + responseType: 'blob' + }) +} + +/** + * 班次课程列表另存 Excel + * GET /kcb/class/export-courses + * @param data TimetableQuery:nd、xydbh + */ +export function exportClassCourses(data) { + return request({ + url: '/kcb/class/export-courses', + method: 'get', + params: data, + responseType: 'blob' + }) +} \ No newline at end of file diff --git a/frontend/src/views/teachBusiness/calendar/components/SchoolCalendarEditor.vue b/frontend/src/views/teachBusiness/calendar/components/SchoolCalendarEditor.vue index 5b1f45f3b..e7a47594f 100644 --- a/frontend/src/views/teachBusiness/calendar/components/SchoolCalendarEditor.vue +++ b/frontend/src/views/teachBusiness/calendar/components/SchoolCalendarEditor.vue @@ -18,7 +18,7 @@
事件名称: - 加粗显示 + 加粗显示 可排课 正课 备注显示 @@ -74,18 +74,8 @@ :data-key="cellKey(wIdx, col.colIndex)" @dblclick="onCellDblClick(wIdx, col)" > - {{ getEvent(wIdx, col.colIndex).name }} + {{ getEvent(wIdx, col.colIndex).name }} {{ dateNumberOf(wIdx, col.dayIndex) }} - - @@ -147,8 +137,6 @@ export default { totalWeeks: 0, dateRangeText: '', weeks: [], - // 加粗显示:全局开关,勾选后所有有事件的格子事件名加粗(纯前端效果,不持久化) - boldDisplay: false, // 显示开关(默认不勾选,每天仅显示 1-2/3-4/5-6 三个节次) show78: false, showNight: false, @@ -156,6 +144,7 @@ export default { // 工具栏表单 toolbar: { eventName: '', + bold: false, schedulable: false, mainCourse: false, remarkShow: false, @@ -163,6 +152,8 @@ export default { }, // 事件存储:key = cellKey -> event events: {}, + // 暂存被隐藏节次列上的事件:key = 周-星期-节次 -> event,列再次显示时恢复 + hiddenEvents: {}, // 选择集(用数组以保证 Vue2 响应式) selectedKeys: [], // 拖拽状态 @@ -188,6 +179,10 @@ export default { }) }) return cols + }, + // xqxlb 的年度(nd)为年份(如 2026),由 6 位学期代号(如 202601)截取前 4 位得出 + xqxlbNd() { + return Number(String(this.nd).slice(0, 4)) } }, watch: { @@ -197,7 +192,43 @@ export default { if (val) this.loadSemester() } }, - visibleColumns() { + visibleColumns(newCols, oldCols) { + // 节次列显隐会改变列索引,按「周期-星期-节次」重定位事件,避免事件随时间格列偏移 + if (oldCols && oldCols.length) { + const oldPosOf = index => (oldCols[index] || null) + const newIndex = (dayIndex, slotKey) => { + const c = newCols.find(x => x.dayIndex === dayIndex && x.slotKey === slotKey) + return c ? c.colIndex : -1 + } + const next = {} + Object.keys(this.events).forEach(key => { + const m = key.match(/^w(\d+)-c(\d+)$/) + if (!m) return + const wIdx = +m[1] + const old = oldPosOf(+m[2]) + if (!old) return + const newC = newIndex(old.dayIndex, old.slotKey) + if (newC < 0) { + // 该节次列被隐藏:事件暂存,待列再次显示时恢复 + this.hiddenEvents[this.cellStableKey(wIdx, old.dayIndex, old.slotKey)] = this.events[key] + return + } + next['w' + wIdx + '-c' + newC] = this.events[key] + }) + // 恢复重新显示的隐藏节次事件 + Object.keys(this.hiddenEvents).forEach(stable => { + const p = stable.split('-') + const wIdx = +p[0] + const dayIndex = +p[1] + const slotKey = p[2] + const newC = newIndex(dayIndex, slotKey) + if (newC >= 0) { + next['w' + wIdx + '-c' + newC] = this.hiddenEvents[stable] + delete this.hiddenEvents[stable] + } + }) + this.events = next + } // 列变化后清空无效选择(列索引含义会变) this.selectedKeys = [] } @@ -225,9 +256,9 @@ export default { this.$message.warning('学期详情获取失败,请确认学期数据') }) }, - // 从后端拉取本学期校历假期,渲染到对应时间格(nd 为 6 位学期代号,如 202601) + // 从后端拉取本学期校历事件,渲染到对应时间格(年度由 6 位学期代号截取前 4 位) loadXqxlbEvents() { - listXqxlb({ nd: this.nd }).then(res => { + listXqxlb({ nd: this.xqxlbNd }).then(res => { const data = res.data const list = Array.isArray(data) ? data : (data && data.records) || [] return list @@ -235,15 +266,22 @@ export default { const loaded = {} list.forEach(item => { const pos = this.locateXqxlb(item) - if (pos) { - loaded[pos.key] = { - bh: item.bh, - name: item.jqmc || '', - schedulable: !!item.kpk, - mainCourse: !!item.zdpk, - remarkShow: !!item.bzxs, - remark: item.bz || '' - } + if (!pos) return + const ev = { + bh: item.bh, + name: item.jqmc || '', + bold: !!item.jc, + schedulable: !!item.kpk, + mainCourse: !!item.zdpk, + remarkShow: !!item.bzxs, + remark: item.bz || '' + } + const colIndex = this.colIndexOf(pos.dayIndex, pos.slotKey) + if (colIndex >= 0) { + loaded[this.cellKey(pos.wIdx, colIndex)] = ev + } else { + // 该节次列当前隐藏:暂存(含 bh),待列显示时由 visibleColumns 监听恢复 + this.hiddenEvents[this.cellStableKey(pos.wIdx, pos.dayIndex, pos.slotKey)] = ev } }) // 合并到现有格子(后端数据覆盖已有状态) @@ -252,7 +290,7 @@ export default { this.$message.warning('校历事件加载失败') }) }, - // 根据假期记录反推时间格 key(jqsj 日期 + courseClass 节次) + // 根据假期记录反推时间格位置(jqsj 日期 + courseClass 节次),与列是否可见无关 locateXqxlb(item) { if (!this.startDate || !item.jqsj) return null const d = new Date(String(item.jqsj).slice(0, 10).replace(/-/g, '/')) @@ -263,9 +301,7 @@ export default { const dayIndex = offset % 7 const slotKey = this.courseClassToSlotKey(item.courseClass) if (slotKey === null) return null - const colIndex = this.colIndexOf(dayIndex, slotKey) - if (colIndex < 0) return null - return { key: this.cellKey(wIdx, colIndex) } + return { wIdx, dayIndex, slotKey } }, // 后端 courseClass 节次范围 -> 前端节次 key courseClassToSlotKey(courseClass) { @@ -307,6 +343,10 @@ export default { cellKey(wIdx, colIndex) { return 'w' + wIdx + '-c' + colIndex }, + // 稳定的单元格身份:不随列显隐变化的周-星期-节次 + cellStableKey(wIdx, dayIndex, slotKey) { + return wIdx + '-' + dayIndex + '-' + slotKey + }, getEvent(wIdx, colIndex) { return this.events[this.cellKey(wIdx, colIndex)] || null }, @@ -437,6 +477,7 @@ export default { this.$set(this.events, key, { bh: prev ? prev.bh : undefined, name: name, + bold: this.toolbar.bold, schedulable: this.toolbar.schedulable, mainCourse: this.toolbar.mainCourse, remarkShow: this.toolbar.remarkShow, @@ -467,6 +508,7 @@ export default { return } this.toolbar.eventName = target.name + this.toolbar.bold = !!target.bold this.toolbar.schedulable = !!target.schedulable this.toolbar.mainCourse = !!target.mainCourse this.toolbar.remarkShow = !!target.remarkShow @@ -477,6 +519,7 @@ export default { const ev = this.getEvent(wIdx, col.colIndex) if (ev) { this.toolbar.eventName = ev.name + this.toolbar.bold = !!ev.bold this.toolbar.schedulable = !!ev.schedulable this.toolbar.mainCourse = !!ev.mainCourse this.toolbar.remarkShow = !!ev.remarkShow @@ -489,6 +532,7 @@ export default { this.selectedKeys = [] this.absorbPreview = '' this.events = {} + this.hiddenEvents = {} this.loadXqxlbEvents() this.$message.info('已刷新') }, @@ -526,10 +570,10 @@ export default { return { delFlag: 0, bh: ev.bh || undefined, - nd: this.nd, + nd: this.xqxlbNd, jqsj: fmtDate(d), jqmc: ev.name || '', - jc: null, + jc: !!ev.bold, bz: ev.remark || null, kpk: !!ev.schedulable, bzxs: !!ev.remarkShow, @@ -694,16 +738,6 @@ export default { background: #d4ecdf !important; } } - - /* 状态标记小方块 */ - .sce-flag { - position: absolute; - width: 7px; - height: 7px; - border-radius: 1px; - } - .sce-flag-noschedule { left: 2px; top: 2px; background: #909399; } - .sce-flag-maincourse { right: 2px; top: 2px; background: #409eff; } } .sce-status { diff --git a/frontend/src/views/teachBusiness/calendar/index.vue b/frontend/src/views/teachBusiness/calendar/index.vue index b9885121f..d5bcb56fd 100644 --- a/frontend/src/views/teachBusiness/calendar/index.vue +++ b/frontend/src/views/teachBusiness/calendar/index.vue @@ -42,16 +42,6 @@ > {{ dateNumberOf(wIdx, col.dayIndex) }} @@ -199,9 +189,9 @@ export default { this.$message.warning('学期详情获取失败,请确认学期数据') }) }, - // 从后端拉取本学期校历假期,渲染到对应时间格(nd 为 6 位学期代号,如 202601) + // 从后端拉取本学期校历事件,渲染到对应时间格(年度为 6 位学期代号截取前 4 位) loadXqxlbEvents() { - listXqxlb({ nd: this.nd }).then(res => { + listXqxlb({ nd: String(this.nd).slice(0, 4) }).then(res => { const data = res.data const list = Array.isArray(data) ? data : (data && data.records) || [] const loaded = {} @@ -485,16 +475,6 @@ export default { &.has-event .cal-event-name { color: #00663e; font-weight: 500; } &.no-schedule { background: #fde2e2; } } - - /* 状态标记小方块 */ - .cal-flag { - position: absolute; - width: 7px; - height: 7px; - border-radius: 1px; - } - .cal-flag-noschedule { left: 2px; top: 2px; background: #909399; } - .cal-flag-maincourse { right: 2px; top: 2px; background: #409eff; } } /* 底部状态栏 */ diff --git a/frontend/src/views/teachBusiness/syllabus/index.vue b/frontend/src/views/teachBusiness/syllabus/index.vue index 72dd933b6..b2cf102df 100644 --- a/frontend/src/views/teachBusiness/syllabus/index.vue +++ b/frontend/src/views/teachBusiness/syllabus/index.vue @@ -74,7 +74,7 @@ - +