排课功能优化
This commit is contained in:
@@ -58,3 +58,47 @@ export function listSchedulingLogs(sskcbh, czlx) {
|
||||
params: { sskcbh, czlx }
|
||||
})
|
||||
}
|
||||
|
||||
// 编辑课程教学任务信息(学时/周课时/课程简称/责任教员/默认场地)
|
||||
export function updateSchedulingCourse(data) {
|
||||
return request({
|
||||
url: '/schedulingWindow/course/update',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 教学班次(合班)当前列表 + 候选班次
|
||||
export function getCourseTeams(sskcbh) {
|
||||
return request({
|
||||
url: '/schedulingWindow/course/teams',
|
||||
method: 'get',
|
||||
params: { sskcbh }
|
||||
})
|
||||
}
|
||||
|
||||
// 教学保障选项(保障类别 + 保障资源,含总量与单位)
|
||||
export function getSupportOptions() {
|
||||
return request({
|
||||
url: '/schedulingWindow/support/options',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 批量发布【运行课表】(手册 12.1)
|
||||
export function publishRunningCourses(xydxqbhs) {
|
||||
return request({
|
||||
url: '/schedulingWindow/publish',
|
||||
method: 'post',
|
||||
data: { xydxqbhs }
|
||||
})
|
||||
}
|
||||
|
||||
// 批量删除【运行课表】(手册 12.2)
|
||||
export function withdrawRunningCourses(xydxqbhs) {
|
||||
return request({
|
||||
url: '/schedulingWindow/withdraw',
|
||||
method: 'post',
|
||||
data: { xydxqbhs }
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 教员历(后端 /teacherCalendar/jyl)。
|
||||
// 教员历 = 教员自己的工作时间表:标出的时段「不可排课」。
|
||||
// 排课窗口的硬冲突「教员历不可用」与批量冲突检查的教员时间冲突都读这张表。
|
||||
|
||||
// 按教员编号查询该教员已登记的教员历(返回全部,前端按周过滤)
|
||||
export function listTeacherCalendarByJybh(jybh) {
|
||||
return request({
|
||||
url: '/teacherCalendar/jyl/listByJybh',
|
||||
method: 'get',
|
||||
params: { jybh }
|
||||
})
|
||||
}
|
||||
|
||||
// 批量保存(按 教员编号+日期+节次 幂等覆盖)
|
||||
export function batchSaveTeacherCalendar(list) {
|
||||
return request({
|
||||
url: '/teacherCalendar/jyl/batchSave',
|
||||
method: 'post',
|
||||
data: list
|
||||
})
|
||||
}
|
||||
|
||||
// 批量删除(教员历表无 del_flag 审计列,为物理删除)
|
||||
export function batchDeleteTeacherCalendar(bhs) {
|
||||
return request({
|
||||
url: '/teacherCalendar/jyl/batchDelete',
|
||||
method: 'post',
|
||||
data: bhs
|
||||
})
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -99,11 +99,12 @@ import { listAllSemester } from '@/api/teachBusiness/semester'
|
||||
|
||||
// 四类检查卡片定义(标题/描述与后端 TimetableConflictDimension 枚举一致,作为页面布局常量;
|
||||
// 检查状态与冲突数完全来自后端接口)
|
||||
// 注意:教员卡片含「教员历不可排」、教室卡片含「场地历不可用」,与排课窗口硬冲突口径一致
|
||||
const CARD_DEFS = [
|
||||
{ key: 'TEAM_CONFLICT', label: '教学班时间冲突检查', desc: '同一教学班次在同一时间段被安排多门课程' },
|
||||
{ key: 'ELECTIVE_REQUIRED_CONFLICT', label: '教学班必修与选修时间冲突检查', desc: '教学班次必修课与选修课时间重叠' },
|
||||
{ key: 'TEACHER_CONFLICT', label: '教员时间冲突检查', desc: '同一教员在同一时间段被安排多门课程' },
|
||||
{ key: 'CLASSROOM_CONFLICT', label: '教室时间冲突检查', desc: '同一教室在同一时间段被多门课程占用' }
|
||||
{ key: 'TEACHER_CONFLICT', label: '教员时间冲突检查', desc: '同一教员在同一时间段被安排多门课程,或教员历不可排课时段已有课程' },
|
||||
{ key: 'CLASSROOM_CONFLICT', label: '教室时间冲突检查', desc: '同一教室在同一时间段被多门课程占用,或场地历不可用时段已有课程' }
|
||||
]
|
||||
|
||||
export default {
|
||||
|
||||
@@ -64,6 +64,8 @@
|
||||
<el-button type="success" plain icon="el-icon-date" :disabled="!currentRow" @click="handleEditEventCalendar">编辑班历</el-button>
|
||||
<el-button type="success" plain icon="el-icon-calendar" :disabled="!currentRow" @click="handleEditCalendar">编辑班次教学任务</el-button>
|
||||
<el-button type="success" plain icon="el-icon-data-line" :disabled="!currentRow" @click="handleAllocation">教学配当</el-button>
|
||||
<el-button type="primary" plain icon="el-icon-upload2" :disabled="!selection.length" @click="handlePublishRunning">发布运行课表</el-button>
|
||||
<el-button type="danger" plain icon="el-icon-delete-solid" :disabled="!selection.length" @click="handleWithdrawRunning">删除运行课表</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -333,6 +335,7 @@ import { batchAutoGenerateTasks } from '@/api/studentRecords/classCalendar'
|
||||
import { listSemester, allSemester, addSemester, updateSemester, delSemester, batchDeleteSemester, batchUpdateDateRange, batchUpdateJxrwbh, batchCreateSemester, batchUpdateXqdc, batchUpdateKfjypk, batchWaveMerge, batchWaveSplit, listCreatableClasses, presetSemesterDates } from '@/api/studentRecords/semester'
|
||||
import { listAllSemester } from '@/api/teachBusiness/semester'
|
||||
import { listTeachingTask } from '@/api/teachBusiness/teachingTask'
|
||||
import { publishRunningCourses, withdrawRunningCourses } from '@/api/teachBusiness/schedulingWindow'
|
||||
|
||||
export default {
|
||||
name: 'ShiftSemester',
|
||||
@@ -346,6 +349,8 @@ export default {
|
||||
return {
|
||||
loading: false,
|
||||
saving: false,
|
||||
/** 运行课表批量发布/删除进行中 */
|
||||
runningPublishing: false,
|
||||
|
||||
// ==================== 查询条件 ====================
|
||||
searchForm: { xydbh: '', xqdc: undefined },
|
||||
@@ -663,6 +668,70 @@ export default {
|
||||
this.allocationVisible = true
|
||||
},
|
||||
|
||||
// 批量发布【运行课表】(手册 12.1):把课程任务发布为运行课程,之后才能在排课窗口排课
|
||||
handlePublishRunning() {
|
||||
const rows = this.selection.slice()
|
||||
if (!rows.length) {
|
||||
this.$message.warning('请先勾选班次学期')
|
||||
return
|
||||
}
|
||||
const ids = rows.map(item => item.bh || item.xydxqbh).filter(Boolean)
|
||||
this.$confirm(
|
||||
`将发布所选 ${ids.length} 个班次学期的课程到运行课表。<br/>发布前要求:教学任务已发布未结束、每门课程已指定责任教员与默认场地。`,
|
||||
'批量发布【运行课表】',
|
||||
{ type: 'warning', dangerouslyUseHTMLString: true }
|
||||
).then(() => {
|
||||
this.batchRunningPublish(publishRunningCourses, ids, '发布')
|
||||
}).catch(() => {})
|
||||
},
|
||||
|
||||
// 批量删除【运行课表】(手册 12.2):撤回运行课程,已排课次一并删除
|
||||
handleWithdrawRunning() {
|
||||
const rows = this.selection.slice()
|
||||
if (!rows.length) {
|
||||
this.$message.warning('请先勾选班次学期')
|
||||
return
|
||||
}
|
||||
const ids = rows.map(item => item.bh || item.xydxqbh).filter(Boolean)
|
||||
this.$confirm(
|
||||
`将从运行课表删除所选 ${ids.length} 个班次学期的全部课程。<br/><b>这些班次的排课信息(已排课次)将一并删除</b>,编制侧任务保留。`,
|
||||
'批量删除【运行课表】',
|
||||
{ type: 'warning', dangerouslyUseHTMLString: true }
|
||||
).then(() => {
|
||||
this.batchRunningPublish(withdrawRunningCourses, ids, '删除')
|
||||
}).catch(() => {})
|
||||
},
|
||||
|
||||
// 批量发布/删除公共处理:逐条展示成功与失败原因
|
||||
batchRunningPublish(api, ids, action) {
|
||||
this.runningPublishing = true
|
||||
api(ids).then(res => {
|
||||
const list = (res && res.data) || []
|
||||
const ok = list.filter(i => i.success)
|
||||
const fail = list.filter(i => !i.success)
|
||||
const okText = ok.length
|
||||
? `成功 ${ok.length} 个:` + ok.map(i => `${this.semesterNameOf(i.xydxqbh)}(${i.message})`).join(';')
|
||||
: ''
|
||||
const failText = fail.length
|
||||
? `失败 ${fail.length} 个:` + fail.map(i => `${this.semesterNameOf(i.xydxqbh)}(${i.message})`).join(';')
|
||||
: ''
|
||||
const html = [okText, failText].filter(Boolean).join('<br/>')
|
||||
if (fail.length) {
|
||||
this.$alert(html, `运行课表${action}结果`, { dangerouslyUseHTMLString: true })
|
||||
} else {
|
||||
this.$message.success(`运行课表${action}完成:成功 ${ok.length} 个`)
|
||||
}
|
||||
this.loadList()
|
||||
}).finally(() => {
|
||||
this.runningPublishing = false
|
||||
})
|
||||
},
|
||||
|
||||
semesterNameOf(bh) {
|
||||
const row = this.selection.find(i => (i.bh || i.xydxqbh) === bh)
|
||||
return row ? (row.xydmc || row.bh) : bh
|
||||
},
|
||||
|
||||
handleBatchGenerateTasks() {
|
||||
if (!this.selection.length) {
|
||||
this.$message.warning('请先勾选班次学期')
|
||||
|
||||
@@ -0,0 +1,461 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
:visible="dialogVisible"
|
||||
:title="`教员历——${teacherTitle}`"
|
||||
width="92%"
|
||||
top="4vh"
|
||||
:close-on-click-modal="false"
|
||||
class="teacher-calendar-dialog"
|
||||
@update:visible="val => dialogVisible = val"
|
||||
>
|
||||
<div class="dialog-toolbar">
|
||||
<span class="toolbar-label">学期</span>
|
||||
<el-select
|
||||
v-model="nd"
|
||||
size="small"
|
||||
filterable
|
||||
:loading="semesterLoading"
|
||||
placeholder="请选择学期"
|
||||
class="semester-select"
|
||||
@change="onSemesterChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in semesterOptions"
|
||||
:key="item.nd"
|
||||
:label="getSemesterName(item.nd)"
|
||||
:value="item.nd"
|
||||
/>
|
||||
</el-select>
|
||||
<span class="toolbar-label">周次</span>
|
||||
<el-select v-model="weekIndex" size="small" class="week-select" :disabled="!weeks.length">
|
||||
<el-option
|
||||
v-for="(w, i) in weeks"
|
||||
:key="'w-' + i"
|
||||
:label="`第 ${i + 1} 周(${w.rangeText})`"
|
||||
:value="i"
|
||||
/>
|
||||
</el-select>
|
||||
<el-button size="small" :disabled="weekIndex <= 0" @click="handlePrevWeek">上一周</el-button>
|
||||
<el-button size="small" :disabled="weekIndex >= weeks.length - 1" @click="handleNextWeek">下一周</el-button>
|
||||
<el-button size="small" type="warning" plain @click="handleClearWeek">清除本周标记</el-button>
|
||||
<span class="hint">点击格子标记「不可排课」。标记后,排课窗口会判为硬冲突「教员历不可用」,批量冲突检查的「教员时间冲突」也会报出。</span>
|
||||
</div>
|
||||
<div v-loading="loading" class="editor-wrap">
|
||||
<table v-if="dayColumns.length" class="tcal-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="jc-col">节次</th>
|
||||
<th
|
||||
v-for="d in dayColumns"
|
||||
:key="d.dateStr"
|
||||
:class="{ weekend: d.weekend }"
|
||||
>
|
||||
<span class="day-label">{{ d.weekLabel }}</span>
|
||||
<span class="day-date">{{ d.dateStr.slice(5) }}</span>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="jc in JC_LIST" :key="'jc-' + jc">
|
||||
<td class="jc-col">
|
||||
第{{ jc }}节<span class="slot-hint">{{ slotGroup(jc) }}</span>
|
||||
</td>
|
||||
<td
|
||||
v-for="d in dayColumns"
|
||||
:key="d.dateStr + '#' + jc"
|
||||
class="tcal-cell"
|
||||
:class="{ marked: isMarked(d.dateStr, jc), weekend: d.weekend }"
|
||||
@click="toggleCell(d.dateStr, jc)"
|
||||
>
|
||||
<span v-if="isMarked(d.dateStr, jc)">不可排课</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<el-empty v-else description="请选择学期(学期需先维护开学/结束日期)" :image-size="60" />
|
||||
</div>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<span class="pending-tip">
|
||||
本次改动:新增/更新 {{ addCount }} 格,取消 {{ removeCount }} 格
|
||||
</span>
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="saving" @click="handleSave">保存</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listAllSemester, getSemester } from '@/api/teachBusiness/semester'
|
||||
import {
|
||||
listTeacherCalendarByJybh,
|
||||
batchSaveTeacherCalendar,
|
||||
batchDeleteTeacherCalendar
|
||||
} from '@/api/teachOffice/teacherCalendar'
|
||||
|
||||
// 节次 1-12:与 实施_课程表.节次 / 教学场地历.节次 同一套编号,
|
||||
// 冲突检查按 (教员 + 日期 + 节次) 精确比对,所以这里按单节次存储,不做 1-2 节合并。
|
||||
const JC_LIST = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
|
||||
const SLOT_GROUP = {
|
||||
1: '上午', 2: '上午', 3: '上午', 4: '上午',
|
||||
5: '下午', 6: '下午', 7: '下午', 8: '下午',
|
||||
9: '晚上', 10: '晚上', 11: '夜间', 12: '夜间'
|
||||
}
|
||||
const WEEK_LABELS = ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
|
||||
|
||||
function pad2(n) {
|
||||
return n < 10 ? '0' + n : '' + n
|
||||
}
|
||||
|
||||
function fmtDate(d) {
|
||||
return d.getFullYear() + '-' + pad2(d.getMonth() + 1) + '-' + pad2(d.getDate())
|
||||
}
|
||||
|
||||
export default {
|
||||
name: 'TeacherCalendarDialog',
|
||||
props: {
|
||||
visible: { type: Boolean, default: false },
|
||||
// 教员行(教员表):需要 jybh / jyxm
|
||||
teacher: { type: Object, default: null }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
JC_LIST,
|
||||
nd: '',
|
||||
semesterOptions: [],
|
||||
semesterLoading: false,
|
||||
startDate: null,
|
||||
endDate: null,
|
||||
weeks: [],
|
||||
weekIndex: 0,
|
||||
// 'yyyy-MM-dd#jc' -> 记录编号(新增未保存的格值为 null)
|
||||
marked: {},
|
||||
// 载入时的原始快照:'yyyy-MM-dd#jc' -> 记录编号
|
||||
original: {},
|
||||
loading: false,
|
||||
saving: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
dialogVisible: {
|
||||
get() { return this.visible },
|
||||
set(val) { this.$emit('update:visible', val) }
|
||||
},
|
||||
teacherTitle() {
|
||||
const t = this.teacher || {}
|
||||
return t.jyxm || t.jybh || '教员历'
|
||||
},
|
||||
jybh() {
|
||||
const t = this.teacher || {}
|
||||
return t.jybh || ''
|
||||
},
|
||||
dayColumns() {
|
||||
const w = this.weeks[this.weekIndex]
|
||||
if (!w || !w.days) return []
|
||||
return w.days.map((d, i) => ({
|
||||
dateStr: d,
|
||||
weekLabel: WEEK_LABELS[i],
|
||||
weekend: i >= 5
|
||||
}))
|
||||
},
|
||||
addCount() {
|
||||
return Object.keys(this.marked).filter(k => !this.original[k]).length
|
||||
},
|
||||
removeCount() {
|
||||
return Object.keys(this.original).filter(k => !(k in this.marked)).length
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
visible(val) {
|
||||
if (val) {
|
||||
this.loadSemesters()
|
||||
}
|
||||
},
|
||||
jybh(val) {
|
||||
if (val && this.visible) {
|
||||
this.loadMarks()
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
slotGroup(jc) {
|
||||
return SLOT_GROUP[jc] || ''
|
||||
},
|
||||
isMarked(dateStr, jc) {
|
||||
return Object.prototype.hasOwnProperty.call(this.marked, dateStr + '#' + jc)
|
||||
},
|
||||
toggleCell(dateStr, jc) {
|
||||
const key = dateStr + '#' + jc
|
||||
if (Object.prototype.hasOwnProperty.call(this.marked, key)) {
|
||||
this.$delete(this.marked, key)
|
||||
} else {
|
||||
this.$set(this.marked, key, null)
|
||||
}
|
||||
},
|
||||
handlePrevWeek() {
|
||||
if (this.weekIndex > 0) this.weekIndex -= 1
|
||||
},
|
||||
handleNextWeek() {
|
||||
if (this.weekIndex < this.weeks.length - 1) this.weekIndex += 1
|
||||
},
|
||||
handleClearWeek() {
|
||||
const cols = this.dayColumns
|
||||
if (!cols.length) return
|
||||
cols.forEach(d => {
|
||||
this.JC_LIST.forEach(jc => {
|
||||
const key = d.dateStr + '#' + jc
|
||||
if (Object.prototype.hasOwnProperty.call(this.marked, key)) {
|
||||
this.$delete(this.marked, key)
|
||||
}
|
||||
})
|
||||
})
|
||||
this.$message.info('已清除本周标记,记得点「保存」')
|
||||
},
|
||||
|
||||
/* ---------- 学期与周次 ---------- */
|
||||
loadSemesters() {
|
||||
if (this.semesterOptions.length && this.nd) {
|
||||
this.loadMarks()
|
||||
return
|
||||
}
|
||||
this.semesterLoading = true
|
||||
listAllSemester().then(response => {
|
||||
const list = Array.isArray(response.data) ? response.data : (response.data && response.data.records) || []
|
||||
this.semesterOptions = list
|
||||
if (!this.nd) {
|
||||
const current = list.find(i => i.dqxq) || list[0]
|
||||
if (current) this.nd = current.nd
|
||||
}
|
||||
return this.loadSemesterRange()
|
||||
}).catch(() => {
|
||||
this.semesterOptions = []
|
||||
}).finally(() => {
|
||||
this.semesterLoading = false
|
||||
})
|
||||
},
|
||||
onSemesterChange() {
|
||||
this.weekIndex = 0
|
||||
return this.loadSemesterRange()
|
||||
},
|
||||
loadSemesterRange() {
|
||||
if (!this.nd) return Promise.resolve()
|
||||
return getSemester(this.nd).then(res => {
|
||||
const data = res.data || res || {}
|
||||
const kx = (data.kxrq || '').toString().slice(0, 10)
|
||||
const jx = (data.jsrq || '').toString().slice(0, 10)
|
||||
if (kx && jx) {
|
||||
this.startDate = new Date(kx.replace(/-/g, '/'))
|
||||
this.endDate = new Date(jx.replace(/-/g, '/'))
|
||||
this.buildWeeks()
|
||||
} else {
|
||||
this.weeks = []
|
||||
}
|
||||
return this.loadMarks()
|
||||
}).catch(() => {
|
||||
this.weeks = []
|
||||
this.$message.warning('学期详情获取失败,请确认该学期已维护开学/结束日期')
|
||||
return this.loadMarks()
|
||||
})
|
||||
},
|
||||
getMonday(date) {
|
||||
const day = date.getDay()
|
||||
const diff = date.getDate() - day + (day === 0 ? -6 : 1)
|
||||
const d = new Date(date)
|
||||
d.setDate(diff)
|
||||
return d
|
||||
},
|
||||
buildWeeks() {
|
||||
if (!this.startDate || !this.endDate) return
|
||||
const start = this.getMonday(new Date(this.startDate))
|
||||
const end = new Date(this.endDate)
|
||||
const days = Math.round((end - start) / (24 * 3600 * 1000)) + 1
|
||||
const totalWeeks = Math.max(1, Math.ceil(days / 7))
|
||||
const weeks = []
|
||||
for (let w = 0; w < totalWeeks; w++) {
|
||||
const wkStart = new Date(start)
|
||||
wkStart.setDate(start.getDate() + w * 7)
|
||||
const wkEnd = new Date(wkStart)
|
||||
wkEnd.setDate(wkStart.getDate() + 6)
|
||||
const dayList = []
|
||||
for (let i = 0; i < 7; i++) {
|
||||
const d = new Date(wkStart)
|
||||
d.setDate(wkStart.getDate() + i)
|
||||
dayList.push(fmtDate(d))
|
||||
}
|
||||
weeks.push({
|
||||
rangeText: pad2(wkStart.getMonth() + 1) + '-' + pad2(wkStart.getDate()) +
|
||||
'至' + pad2(wkEnd.getMonth() + 1) + '-' + pad2(wkEnd.getDate()),
|
||||
days: dayList
|
||||
})
|
||||
}
|
||||
this.weeks = weeks
|
||||
if (this.weekIndex > weeks.length - 1) this.weekIndex = 0
|
||||
},
|
||||
/** 学期代号 -> 学期名称(如 202601 -> 2026年春季学期) */
|
||||
getSemesterName(nd) {
|
||||
if (!nd) return ''
|
||||
const s = String(nd)
|
||||
const xn = s.slice(0, 4)
|
||||
const xq = s.slice(4)
|
||||
const map = { '01': '春季学期', '02': '夏季学期', '03': '秋季学期' }
|
||||
return xn + '年' + (map[xq] || '第' + xq + '学期')
|
||||
},
|
||||
|
||||
/* ---------- 教员历读取与保存 ---------- */
|
||||
loadMarks() {
|
||||
if (!this.jybh) {
|
||||
this.marked = {}
|
||||
this.original = {}
|
||||
return Promise.resolve()
|
||||
}
|
||||
this.loading = true
|
||||
return listTeacherCalendarByJybh(this.jybh).then(res => {
|
||||
const list = Array.isArray(res.data) ? res.data : (res.data && res.data.records) || []
|
||||
const marked = {}
|
||||
const original = {}
|
||||
list.forEach(row => {
|
||||
if (!row || !row.rq || row.jc === null || row.jc === undefined) return
|
||||
const key = String(row.rq).slice(0, 10) + '#' + row.jc
|
||||
marked[key] = row.bh
|
||||
original[key] = row.bh
|
||||
})
|
||||
this.marked = marked
|
||||
this.original = original
|
||||
}).catch(() => {
|
||||
this.marked = {}
|
||||
this.original = {}
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
handleSave() {
|
||||
if (!this.jybh) {
|
||||
this.$message.warning('缺少教员编号,无法保存')
|
||||
return
|
||||
}
|
||||
const rows = []
|
||||
const del = []
|
||||
Object.keys(this.marked).forEach(k => {
|
||||
if (this.original[k]) return // 原有记录且未改动
|
||||
const parts = k.split('#')
|
||||
rows.push({
|
||||
jybh: this.jybh,
|
||||
// JYL.rq 是 LocalDateTime,Jackson 只认 ISO-8601,必须带 'T';
|
||||
// 用 'yyyy-MM-dd HH:mm:ss'(场地历 JXCDL 是 java.util.Date 才吃这种)会 400。
|
||||
rq: parts[0] + 'T00:00:00',
|
||||
jc: Number(parts[1]),
|
||||
kpk: 0, // 教员历的语义:标出的时段不可排课
|
||||
mc: '不可排课',
|
||||
bz: null
|
||||
})
|
||||
})
|
||||
Object.keys(this.original).forEach(k => {
|
||||
if (!(k in this.marked)) del.push(this.original[k])
|
||||
})
|
||||
if (!rows.length && !del.length) {
|
||||
this.$message.info('没有改动需要保存')
|
||||
return
|
||||
}
|
||||
this.saving = true
|
||||
const tasks = []
|
||||
if (rows.length) tasks.push(batchSaveTeacherCalendar(rows))
|
||||
if (del.length) tasks.push(batchDeleteTeacherCalendar(del))
|
||||
Promise.all(tasks).then(() => {
|
||||
this.$message.success('已保存:新增/更新 ' + rows.length + ' 格,取消 ' + del.length + ' 格')
|
||||
return this.loadMarks()
|
||||
}).catch(() => {
|
||||
this.$message.error('教员历保存失败')
|
||||
}).finally(() => {
|
||||
this.saving = false
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.dialog-toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.toolbar-label {
|
||||
font-size: 13px;
|
||||
color: #606266;
|
||||
}
|
||||
.semester-select {
|
||||
width: 180px;
|
||||
}
|
||||
.week-select {
|
||||
width: 200px;
|
||||
}
|
||||
.hint {
|
||||
color: #909399;
|
||||
font-size: 12px;
|
||||
}
|
||||
.editor-wrap {
|
||||
height: 68vh;
|
||||
overflow: auto;
|
||||
}
|
||||
.tcal-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
table-layout: fixed;
|
||||
}
|
||||
.tcal-table th,
|
||||
.tcal-table td {
|
||||
border: 1px solid #ebeef5;
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
color: #303133;
|
||||
height: 34px;
|
||||
}
|
||||
.tcal-table thead th {
|
||||
background: #f5f7fa;
|
||||
color: #606266;
|
||||
font-weight: 500;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
.jc-col {
|
||||
width: 96px;
|
||||
background: #fafafa;
|
||||
color: #606266;
|
||||
}
|
||||
.slot-hint {
|
||||
display: block;
|
||||
font-size: 11px;
|
||||
color: #909399;
|
||||
}
|
||||
.day-date {
|
||||
font-size: 11px;
|
||||
color: #909399;
|
||||
}
|
||||
.weekend {
|
||||
background: #fdf6ec;
|
||||
}
|
||||
.tcal-cell {
|
||||
cursor: pointer;
|
||||
}
|
||||
.tcal-cell:hover {
|
||||
background: #ecf5ff;
|
||||
}
|
||||
.tcal-cell.marked {
|
||||
background: #fef0f0;
|
||||
color: #f56c6c;
|
||||
font-weight: 500;
|
||||
}
|
||||
.dialog-footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
gap: 8px;
|
||||
}
|
||||
.pending-tip {
|
||||
margin-right: auto;
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
}
|
||||
</style>
|
||||
@@ -78,10 +78,11 @@
|
||||
@click="handleCreateAccount(row)">创建账号</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" fixed="right" width="200">
|
||||
<el-table-column label="操作" align="center" fixed="right" width="260">
|
||||
<template slot-scope="{ row }">
|
||||
<el-button type="text" size="small" icon="el-icon-view" @click="handleViewAttribute(row)">查看属性</el-button>
|
||||
<el-button type="text" size="small" icon="el-icon-edit" @click="handleEdit(row)">编辑</el-button>
|
||||
<el-button type="text" size="small" icon="el-icon-date" @click="handleTeacherCalendar(row)">教员历</el-button>
|
||||
<el-button type="text" size="small" class="danger-text" @click="handleDisable(row)">离职</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -700,6 +701,12 @@
|
||||
|
||||
<!-- 对齐结果清单弹窗 -->
|
||||
<org-sync-result-dialog ref="alignResult" title="对齐结果" />
|
||||
|
||||
<!-- 教员历编辑弹窗(标记不可排课时段,供排课窗口与冲突检查使用) -->
|
||||
<teacher-calendar-dialog
|
||||
:visible.sync="teacherCalendarVisible"
|
||||
:teacher="teacherCalendarRow"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -723,10 +730,11 @@ import { getDicts } from '@/api/system/dict/data'
|
||||
import { listRole } from '@/api/system/role'
|
||||
import { saveAs } from 'file-saver'
|
||||
import OrgSyncResultDialog from '@/components/OrgSyncResultDialog'
|
||||
import TeacherCalendarDialog from './components/TeacherCalendarDialog.vue'
|
||||
|
||||
export default {
|
||||
name: 'Teacher',
|
||||
components: { OrgSyncResultDialog },
|
||||
components: { OrgSyncResultDialog, TeacherCalendarDialog },
|
||||
data() {
|
||||
return {
|
||||
// 查询条件(仅后端 JYBMapper.xml 支持的字段:jyxm 模糊 / jysdh 等值 / zc 模糊 / jylb 等值)
|
||||
@@ -790,6 +798,10 @@ export default {
|
||||
attributeData: {},
|
||||
attributeForm: {},
|
||||
attributeLoading: false,
|
||||
|
||||
// 教员历(标记不可排课时段)
|
||||
teacherCalendarVisible: false,
|
||||
teacherCalendarRow: null,
|
||||
attributeSaving: false,
|
||||
attributeJybh: '',
|
||||
// 字典选项
|
||||
@@ -1086,6 +1098,17 @@ export default {
|
||||
return payload
|
||||
},
|
||||
|
||||
// ==================== 教员历 ====================
|
||||
/** 打开该教员的教员历编辑弹窗(标记不可排课时段) */
|
||||
handleTeacherCalendar(row) {
|
||||
if (!row || !row.jybh) {
|
||||
this.$message.warning('该教员缺少教员工号,无法维护教员历')
|
||||
return
|
||||
}
|
||||
this.teacherCalendarRow = row
|
||||
this.teacherCalendarVisible = true
|
||||
},
|
||||
|
||||
// ==================== 编辑 ====================
|
||||
handleEdit(row) {
|
||||
this.editDialogVisible = true
|
||||
|
||||
Reference in New Issue
Block a user