forked from liweijie/education
校历编辑
This commit is contained in:
@@ -18,6 +18,15 @@ export function getXqxlb(bh) {
|
||||
})
|
||||
}
|
||||
|
||||
// 预生成学期整张校历占位记录(首次进入新学期时调用,接口自带去重)
|
||||
export function addXqxlb(startDate, endDate) {
|
||||
return request({
|
||||
url: '/xqxlb/add',
|
||||
method: 'post',
|
||||
params: { startDate: startDate, endDate: endDate }
|
||||
})
|
||||
}
|
||||
|
||||
// 新增 / 修改校历假期(无 bh 时为新增,带 bh 时为修改)
|
||||
export function updateXqxlb(data) {
|
||||
return request({
|
||||
|
||||
@@ -91,7 +91,7 @@
|
||||
|
||||
<script>
|
||||
import { getSemester } from '@/api/teachBusiness/semester'
|
||||
import { listXqxlb, updateXqxlb } from '@/api/teachBusiness/xqxlb'
|
||||
import { listXqxlb, addXqxlb, updateXqxlb } from '@/api/teachBusiness/xqxlb'
|
||||
|
||||
// 节次定义:12/34/56 常显,78/晚上/夜间由开关控制可见
|
||||
const SLOT_DEFS = [
|
||||
@@ -182,8 +182,11 @@ export default {
|
||||
})
|
||||
return cols
|
||||
},
|
||||
// xqxlb 的年度(nd)为年份(如 2026),由 6 位学期代号(如 202601)截取前 4 位得出
|
||||
// xqxlb 的 nd 存的是「年份」,必须与某条记录的 jqsj 实际日期同一年,
|
||||
// 按「学期代号前4位」取会与格子真实年月脱节(如 2027 春学期首周落在 2026 年)。
|
||||
// 这里统一以「日历起点 calendarStart 所在年」作为年度口径。
|
||||
xqxlbNd() {
|
||||
if (this.calendarStart) return this.calendarStart.getFullYear()
|
||||
return Number(String(this.nd).slice(0, 4))
|
||||
}
|
||||
},
|
||||
@@ -260,16 +263,39 @@ export default {
|
||||
})
|
||||
},
|
||||
// 从后端拉取本学期校历事件,渲染到对应时间格(年度由 6 位学期代号截取前 4 位)
|
||||
// XQXLB 的 nd 存的是「年份」(如 2026),春/夏/秋三学期共用同一批年度记录;
|
||||
// 后端 updateById 只能改已存在的 bh,所以每个格子必须有预生成的记录才能存。
|
||||
// 若本学期日期区间 [kxrq, jsrq] 内还没有任何占位记录(首次进入该学期),
|
||||
// 调用 /xqxlb/add 只按本学期 kxrq/jsrq 预生成这一张网格,再重新拉取,
|
||||
// 这样后续 /xqxlb/update 的 updateById 才能命中每一格。(区间有覆盖则跳过,幂等)
|
||||
loadXqxlbEvents() {
|
||||
listXqxlb({ nd: this.xqxlbNd }).then(res => {
|
||||
const fetchList = () => {
|
||||
console.warn('[校历编辑] 查询nd=', this.xqxlbNd, '学期nd=', this.nd)
|
||||
return listXqxlb({ nd: this.xqxlbNd }).then(res => {
|
||||
const data = res.data
|
||||
const list = Array.isArray(data) ? data : (data && data.records) || []
|
||||
return list
|
||||
}).then(list => {
|
||||
const loaded = {}
|
||||
const arr = Array.isArray(data) ? data : (data && data.records) || []
|
||||
console.warn('[校历编辑] 返回条数=', arr.length, '含事件名=', arr.filter(it => it.jqmc).map(it => ({ nd: it.nd, jqsj: it.jqsj, jqmc: it.jqmc, courseClass: it.courseClass })).slice(0, 10))
|
||||
return arr
|
||||
})
|
||||
}
|
||||
const render = (list) => {
|
||||
// 后端 add() 的按「月日」去重对已存的全量日期不生效,同一年度多次打开/刷新会
|
||||
// 积累同一格的重复占位记录(各带不同 bh)。updateById 只改其中一行,其余空记录
|
||||
// 会在重新拉取时把刚保存的事件名覆盖掉。故这里按「周-星期-节次」先去重:
|
||||
// 同一格保留有事件名(jqmc非空)的一条;都没有则保留最后一条兜底(保证有 bh)。
|
||||
const unique = {}
|
||||
list.forEach(item => {
|
||||
const pos = this.locateXqxlb(item)
|
||||
if (!pos) return
|
||||
const stable = this.cellStableKey(pos.wIdx, pos.dayIndex, pos.slotKey)
|
||||
const prev = unique[stable]
|
||||
if (!prev || (item.jqmc && !prev.item.jqmc)) {
|
||||
unique[stable] = { item: item, pos: pos }
|
||||
}
|
||||
})
|
||||
const loaded = {}
|
||||
Object.keys(unique).forEach(stable => {
|
||||
const { item, pos } = unique[stable]
|
||||
const ev = {
|
||||
bh: item.bh,
|
||||
name: item.jqmc || '',
|
||||
@@ -289,10 +315,27 @@ export default {
|
||||
})
|
||||
// 合并到现有格子(后端数据覆盖已有状态)
|
||||
this.events = Object.assign({}, this.events, loaded)
|
||||
}).catch(() => {
|
||||
}
|
||||
fetchList().then(list => {
|
||||
const hasCoverage = list.some(item => this.isInRange(item.jqsj))
|
||||
if (!hasCoverage && this.startDate && this.endDate) {
|
||||
// 本学期区间尚无占位记录:仅用本期 kxrq/jsrq 预生成整张网格
|
||||
const startStr = fmtDate(this.startDate)
|
||||
const endStr = fmtDate(this.endDate)
|
||||
return addXqxlb(startStr, endStr).then(() => fetchList())
|
||||
}
|
||||
return list
|
||||
}).then(render).catch(() => {
|
||||
this.$message.warning('校历事件加载失败')
|
||||
})
|
||||
},
|
||||
// 判断某条记录(jqsj 日期)是否落在本学期 [startDate, endDate] 区间内
|
||||
isInRange(jqsj) {
|
||||
if (!this.startDate || !this.endDate || !jqsj) return false
|
||||
const d = new Date(String(jqsj).slice(0, 10).replace(/-/g, '/'))
|
||||
if (isNaN(d.getTime())) return false
|
||||
return d >= this.startDate && d <= this.endDate
|
||||
},
|
||||
// 根据假期记录反推时间格位置(jqsj 日期 + courseClass 节次),与列是否可见无关
|
||||
locateXqxlb(item) {
|
||||
if (!this.calendarStart || !item.jqsj) return null
|
||||
@@ -485,7 +528,9 @@ export default {
|
||||
keys.forEach(key => {
|
||||
const prev = this.events[key]
|
||||
this.$set(this.events, key, {
|
||||
bh: prev ? prev.bh : undefined,
|
||||
// 编辑已有事件:沿用后端返回的 bh;新增事件:bh 由 loadXqxlbEvents
|
||||
// 通过 /xqxlb/add 预生成的占位行提供(首次进入学期时已落库)
|
||||
bh: prev && prev.bh ? prev.bh : undefined,
|
||||
name: name,
|
||||
bold: this.toolbar.bold,
|
||||
schedulable: this.toolbar.schedulable,
|
||||
@@ -615,7 +660,9 @@ export default {
|
||||
return {
|
||||
delFlag: 0,
|
||||
bh: ev.bh || undefined,
|
||||
nd: this.xqxlbNd,
|
||||
// nd 必须与 jqsj 同一年:取本条记录真实日期的年份,
|
||||
// 不能沿用学派代号前4位(如 2027 春学期首周可能落在 2026 年,会存错年度、查不到)。
|
||||
nd: d.getFullYear(),
|
||||
jqsj: fmtDate(d),
|
||||
jqmc: ev.name || '',
|
||||
jc: !!ev.bold,
|
||||
|
||||
@@ -192,16 +192,46 @@ export default {
|
||||
this.$message.warning('学期详情获取失败,请确认学期数据')
|
||||
})
|
||||
},
|
||||
// 从后端拉取本学期校历事件,渲染到对应时间格(年度为 6 位学期代号截取前 4 位)
|
||||
// 从后端拉取校历事件,渲染到对应时间格
|
||||
// xqxlb.nd 存的是记录真实日期的年份,不是学期代号前4位(避免跨年度学期查询出错)
|
||||
loadXqxlbEvents() {
|
||||
listXqxlb({ nd: String(this.nd).slice(0, 4) }).then(res => {
|
||||
if (!this.calendarStart) return
|
||||
// 学期校历从 calendarStart 到 endDate,查询覆盖整个跨度的全部年份
|
||||
const startYear = this.calendarStart.getFullYear()
|
||||
const endYear = (this.endDate || this.calendarStart).getFullYear()
|
||||
// 一次性拉取覆盖起止年份的所有记录,由前端过滤
|
||||
const requests = []
|
||||
const range = []
|
||||
for (let y = startYear; y <= endYear; y++) {
|
||||
range.push(y)
|
||||
requests.push(listXqxlb({ nd: y }))
|
||||
}
|
||||
console.warn('[校历只读] 查询nd范围=', range, 'calendarStart=', fmtDate(this.calendarStart), 'endDate=', this.endDate ? fmtDate(this.endDate) : null)
|
||||
Promise.all(requests).then(responses => {
|
||||
const list = []
|
||||
responses.forEach(res => {
|
||||
const data = res.data
|
||||
const list = Array.isArray(data) ? data : (data && data.records) || []
|
||||
const part = Array.isArray(data) ? data : (data && data.records) || []
|
||||
list.push(...part)
|
||||
})
|
||||
console.warn('[校历只读] listXqxlb 返回记录条数=', list.length, 'nd字段分布=', list.reduce((m, it) => { const k = String(it.nd); m[k] = (m[k] || 0) + 1; return m }, {}), '样例=', list.slice(0, 3).map(it => ({ nd: it.nd, jqsj: it.jqsj, jqmc: it.jqmc, courseClass: it.courseClass })))
|
||||
const loaded = {}
|
||||
let matched = 0
|
||||
// 23694 条里同一格可能有多条(add() 去重失效累积的重复占位)。
|
||||
// 按格子去重:同一格优先保留「有事件名(jqmc非空)」的那条,避免空占位把事件盖成空白。
|
||||
const unique = {}
|
||||
list.forEach(item => {
|
||||
const pos = this.locateXqxlb(item)
|
||||
if (pos) {
|
||||
loaded[pos.key] = {
|
||||
if (!pos) return
|
||||
const prev = unique[pos.key]
|
||||
const has = !!item.jqmc
|
||||
if (!prev || (has && !prev.has)) {
|
||||
unique[pos.key] = { has: has, item: item }
|
||||
}
|
||||
})
|
||||
Object.keys(unique).forEach(key => {
|
||||
const { item } = unique[key]
|
||||
loaded[key] = {
|
||||
bh: item.bh,
|
||||
name: item.jqmc || '',
|
||||
schedulable: !!item.kpk,
|
||||
@@ -209,12 +239,11 @@ export default {
|
||||
remarkShow: !!item.bzxs,
|
||||
remark: item.bz || ''
|
||||
}
|
||||
}
|
||||
if (item.jqmc) matched++
|
||||
})
|
||||
console.warn('[校历只读] 定位到的事件条数=', matched, 'events=', Object.keys(loaded).length, loaded)
|
||||
this.events = loaded
|
||||
}).catch(() => {
|
||||
this.$message.warning('校历事件加载失败')
|
||||
})
|
||||
}).catch(e => { console.error('[校历只读] 拉取失败', e) })
|
||||
},
|
||||
// 根据假期记录反推时间格 key(jqsj 日期 + courseClass 节次)
|
||||
locateXqxlb(item) {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
<div class="teach-calendar app-container">
|
||||
<school-calendar-editor
|
||||
v-if="nd"
|
||||
:key="nd"
|
||||
:nd="nd"
|
||||
:semester-name="semesterName"
|
||||
@close="handleClose"
|
||||
@@ -26,6 +27,16 @@ export default {
|
||||
this.nd = this.$route.query.nd || ''
|
||||
this.semesterName = this.$route.query.name || ''
|
||||
},
|
||||
watch: {
|
||||
// 编辑校历页被 keep-alive 缓存复用:同路径下 query.nd 变化时不会重新走 created(),
|
||||
// 需在此同步到 nd,并通过 :key="nd" 让编辑器随学期切换全新挂载,读取正确学期数据。
|
||||
'$route.query.nd'(val) {
|
||||
if (val && String(val) !== String(this.nd)) {
|
||||
this.nd = val
|
||||
this.semesterName = this.$route.query.name || ''
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleClose() {
|
||||
this.$router.replace({ path: '/teachBusiness/semester' })
|
||||
|
||||
@@ -43,18 +43,18 @@
|
||||
<span>{{ formatDate(scope.row.jsrq) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="周数" align="center" prop="sdzs" width="100" />
|
||||
<el-table-column label="调课不审批" align="center" width="120">
|
||||
<el-table-column label="周数" align="center" prop="sdzs" width="55" />
|
||||
<el-table-column label="调课不审批" align="center" width="100">
|
||||
<template slot-scope="scope">
|
||||
<el-tag :type="scope.row.tkbsp ? 'primary' : 'info'" size="mini">{{ scope.row.tkbsp ? '是' : '否' }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="禁止调课" align="center" width="120">
|
||||
<el-table-column label="禁止调课" align="center" width="80">
|
||||
<template slot-scope="scope">
|
||||
<el-tag :type="scope.row.jztk ? 'danger' : 'info'" size="mini">{{ scope.row.jztk ? '是' : '否' }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="终结成绩定及格" align="center" width="160">
|
||||
<el-table-column label="终结成绩定及格" align="center" width="130">
|
||||
<template slot-scope="scope">
|
||||
<el-tag :type="scope.row.zjcjdjg ? 'primary' : 'info'" size="mini">{{ scope.row.zjcjdjg ? '是' : '否'
|
||||
}}</el-tag>
|
||||
|
||||
Reference in New Issue
Block a user