616 lines
21 KiB
Vue
616 lines
21 KiB
Vue
<template>
|
||
<div class="app-container teach-semester">
|
||
<!-- 工具栏 -->
|
||
<el-row :gutter="10" class="mb8">
|
||
<el-col :span="1.5">
|
||
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd">新添</el-button>
|
||
</el-col>
|
||
<el-col :span="1.5">
|
||
<el-button type="danger" plain icon="el-icon-delete" size="mini" :disabled="multiple" @click="handleDelete">
|
||
批量删除
|
||
</el-button>
|
||
</el-col>
|
||
</el-row>
|
||
|
||
<!-- 数据表格 -->
|
||
<el-table
|
||
v-loading="loading"
|
||
:data="semesterList"
|
||
border
|
||
:height="tableHeight"
|
||
@selection-change="handleSelectionChange"
|
||
>
|
||
<el-table-column type="selection" align="center" width="50" />
|
||
<el-table-column label="学期名称" align="center" min-width="190">
|
||
<template slot-scope="scope">
|
||
<span>{{ getSemesterName(scope.row.nd) }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="当前" align="center" width="70">
|
||
<template slot-scope="scope">
|
||
<el-tag v-if="scope.row.dqxq" type="success" size="mini">当前</el-tag>
|
||
<span v-else>-</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="开学日期" align="center" prop="kxrq" min-width="130">
|
||
<template slot-scope="scope">
|
||
<span>{{ formatDate(scope.row.kxrq) }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="结束日期" align="center" prop="jsrq" min-width="130">
|
||
<template slot-scope="scope">
|
||
<span>{{ formatDate(scope.row.jsrq) }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="周数" align="center" prop="sdzs" width="70" />
|
||
<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="90">
|
||
<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="120">
|
||
<template slot-scope="scope">
|
||
<el-tag :type="scope.row.zjcjdjg ? 'primary' : 'info'" size="mini">{{ scope.row.zjcjdjg ? '是' : '否' }}</el-tag>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="课时系数方案" align="center" min-width="120">
|
||
<template slot-scope="scope">
|
||
<span>{{ getCoefficientLabel(scope.row.ksxsfabh) }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="锁定时长" align="center" min-width="100">
|
||
<template slot-scope="scope">
|
||
<span>{{ scope.row.sdjldw || '-' }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column
|
||
label="操作"
|
||
align="center"
|
||
width="220"
|
||
fixed="right"
|
||
class-name="table-action-column"
|
||
>
|
||
<template slot-scope="scope">
|
||
<el-button type="text" size="mini" icon="el-icon-edit" @click="handleUpdate(scope.row)">编辑</el-button>
|
||
<el-button type="text" size="mini" icon="el-icon-date" @click="handleEditCalendar(scope.row)">
|
||
编辑校历
|
||
</el-button>
|
||
<el-button
|
||
type="text"
|
||
size="mini"
|
||
icon="el-icon-delete"
|
||
class="text-danger"
|
||
@click="handleDelete(scope.row)"
|
||
>删除</el-button>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
|
||
<pagination
|
||
v-show="total > 0"
|
||
:total="total"
|
||
:page.sync="queryParams.pageNum"
|
||
:limit.sync="queryParams.pageSize"
|
||
@pagination="getList"
|
||
/>
|
||
|
||
<!-- 学期信息弹窗 -->
|
||
<el-dialog :title="title" :visible.sync="open" width="900px" append-to-body class="semester-dialog">
|
||
<el-form ref="form" class="semester-dialog-form" :model="form" :rules="rules" label-width="96px" label-position="right">
|
||
<el-row :gutter="20" class="semester-dialog-body">
|
||
<!-- 左侧表单区 -->
|
||
<el-col :span="15" class="semester-form-col">
|
||
<el-form-item label="学期名称">
|
||
<el-input
|
||
:value="getSemesterName(buildNd(form.xn, form.xq))"
|
||
readonly
|
||
placeholder="由年份+学期类型自动拼接"
|
||
prefix-icon="el-icon-office-building"
|
||
/>
|
||
</el-form-item>
|
||
<el-row :gutter="14">
|
||
<el-col :span="12">
|
||
<el-form-item label="年份" prop="xn">
|
||
<el-select v-model="form.xn" placeholder="请选择年份" :disabled="!isAdd" style="width: 100%">
|
||
<el-option v-for="item in yearOptions" :key="item" :label="item + '年'" :value="item" />
|
||
</el-select>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="学期类型" prop="xq">
|
||
<el-select v-model="form.xq" placeholder="请选择学期类型" :disabled="!isAdd" style="width: 100%">
|
||
<el-option v-for="item in semesterOptions" :key="item.value" :label="item.label" :value="item.value" />
|
||
</el-select>
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
<el-row :gutter="14">
|
||
<el-col :span="12">
|
||
<el-form-item label="开学日期" prop="kxrq">
|
||
<el-date-picker
|
||
v-model="form.kxrq"
|
||
type="date"
|
||
placeholder="开学日期"
|
||
value-format="yyyy-MM-dd"
|
||
style="width: 100%"
|
||
/>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="结束日期" prop="jsrq">
|
||
<el-date-picker
|
||
v-model="form.jsrq"
|
||
type="date"
|
||
placeholder="结束日期"
|
||
value-format="yyyy-MM-dd"
|
||
style="width: 100%"
|
||
/>
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
<el-row :gutter="14">
|
||
<el-col :span="12">
|
||
<el-form-item label="学期周数" prop="sdzs">
|
||
<el-input-number v-model="form.sdzs" :min="1" :max="60" controls-position="right" style="width: 100%" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="课时系数方案" prop="ksxsfabh">
|
||
<el-select v-model="form.ksxsfabh" placeholder="请选择" clearable style="width: 100%">
|
||
<el-option v-for="item in coefficientOptions" :key="item.value" :label="item.label" :value="item.value" />
|
||
</el-select>
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
<el-form-item label="锁定时长">
|
||
<div class="lock-group">
|
||
<el-input-number v-model="form.lockValue" :min="0" :max="99" controls-position="right" style="width: 120px" />
|
||
<el-radio-group v-model="form.lockUnit" class="lock-unit" style="margin-left: 28px">
|
||
<el-radio label="周">周</el-radio>
|
||
<el-radio label="月">月</el-radio>
|
||
</el-radio-group>
|
||
</div>
|
||
</el-form-item>
|
||
<div class="form-tip">以当前周星期四中午12点为界限,锁定【锁定周数】周的教学实施计划,被锁定的教学实施计划不允许教员自行调整。</div>
|
||
</el-col>
|
||
|
||
<!-- 右侧业务开关区 -->
|
||
<el-col :span="9" class="semester-switch-col">
|
||
<div class="switch-panel-title">业务开关</div>
|
||
<div class="switch-panel">
|
||
<div class="switch-item">
|
||
<el-checkbox v-model="form.dqxq">当前学期</el-checkbox>
|
||
<div class="switch-tip">设定当前学期后,系统启动时会自动将该学期设置为默认学期。</div>
|
||
</div>
|
||
|
||
<div class="switch-item">
|
||
<el-checkbox v-model="form.jztk">禁止调课</el-checkbox>
|
||
<div class="switch-tip">设定【禁止调课】后,教学信息系统中就无法对课程信息进行调整。</div>
|
||
</div>
|
||
|
||
<div class="switch-item">
|
||
<el-checkbox v-model="form.tkbsp">调课不审批</el-checkbox>
|
||
<div class="switch-tip">设定【调课不审批】后,在教学信息系统中调课时,如果只是更改教员,或更改授课地点,调课申请会立即生效。而对于改变授课地点,调课申请会立即生效。而对于改变授课时间、增加或减少授课教员的调课申请依然不受该设置影响,依然需要逐级审批后才能生效。</div>
|
||
</div>
|
||
|
||
<div class="switch-item">
|
||
<el-checkbox v-model="form.zjcjdjg">终结成绩定及格</el-checkbox>
|
||
<div class="switch-tip">设定【终结成绩定及格】后,学员终结性成绩不及格的,最终成绩即为不及格。</div>
|
||
</div>
|
||
|
||
<div class="switch-item">
|
||
<el-checkbox v-model="form.jwldshtk">教务机关审核教学计划调整</el-checkbox>
|
||
<div class="switch-tip">勾选:教务机关需要从行政入口对每个教学计划调整申请进行审批,同时再由管理员身份教务参谋进行最后的把关;不勾选:仅由管理员身份教务参谋进行最后的把关。</div>
|
||
</div>
|
||
</div>
|
||
</el-col>
|
||
</el-row>
|
||
</el-form>
|
||
<div slot="footer" class="dialog-footer">
|
||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||
<el-button @click="cancel">取 消</el-button>
|
||
</div>
|
||
</el-dialog>
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
import { listSemester, getSemester, addSemester, updateSemester, delSemester } from "@/api/teachBusiness/semester"
|
||
|
||
export default {
|
||
name: "Semester",
|
||
data() {
|
||
return {
|
||
// 遮罩层
|
||
loading: true,
|
||
// 表格高度
|
||
tableHeight: window.innerHeight - 280,
|
||
// 总条数
|
||
total: 0,
|
||
// 学期列表数据
|
||
semesterList: [],
|
||
// 选中的学期数组
|
||
ids: [],
|
||
// 非单个禁用
|
||
single: true,
|
||
// 非多个禁用
|
||
multiple: true,
|
||
// 弹出层标题
|
||
title: "",
|
||
// 是否显示弹出层
|
||
open: false,
|
||
// 是否新增
|
||
isAdd: true,
|
||
// 学年选项(当前年份 ± 10 年)
|
||
yearOptions: [],
|
||
// 学期选项:01 春季、02 夏季、03 秋季
|
||
semesterOptions: [
|
||
{ value: '01', label: '春季学期' },
|
||
{ value: '02', label: '夏季学期' },
|
||
{ value: '03', label: '秋季学期' }
|
||
],
|
||
// 课时系数方案选项(引用基础数据表,暂以静态占位)
|
||
coefficientOptions: [
|
||
{ value: '1', label: '标准系数方案' },
|
||
{ value: '2', label: '综合系数方案' },
|
||
{ value: '3', label: '实训系数方案' }
|
||
],
|
||
// 查询参数
|
||
queryParams: {
|
||
pageNum: 1,
|
||
pageSize: 10
|
||
},
|
||
// 表单参数
|
||
form: {},
|
||
// 表单校验
|
||
rules: {
|
||
xn: [
|
||
{ required: true, message: "请选择年份", trigger: "change" }
|
||
],
|
||
xq: [
|
||
{ required: true, message: "请选择学期类型", trigger: "change" }
|
||
],
|
||
kxrq: [
|
||
{ required: true, message: "请选择开学日期", trigger: "change" }
|
||
],
|
||
jsrq: [
|
||
{ required: true, message: "请选择结束日期", trigger: "change" }
|
||
]
|
||
}
|
||
}
|
||
},
|
||
created() {
|
||
this.initYearOptions()
|
||
this.getList()
|
||
// 顶栏切换当前学期后,自动刷新本页数据
|
||
this.$root.$on('semester-current-changed', this.handleCurrentChanged)
|
||
},
|
||
beforeDestroy() {
|
||
this.$root.$off('semester-current-changed', this.handleCurrentChanged)
|
||
},
|
||
methods: {
|
||
/** 顶栏切换当前学期后自动刷新 */
|
||
handleCurrentChanged() {
|
||
this.getList()
|
||
},
|
||
/** 初始化学年选项 */
|
||
initYearOptions() {
|
||
const current = new Date().getFullYear()
|
||
const list = []
|
||
for (let i = current - 10; i <= current + 10; i++) {
|
||
list.push(i)
|
||
}
|
||
this.yearOptions = list
|
||
},
|
||
/** 学年+学期 -> 学期代号(如 2026 + 01 = 202601) */
|
||
buildNd(xn, xq) {
|
||
if (xn === undefined || xn === null || xn === '' || !xq) return ''
|
||
return String(xn) + String(xq)
|
||
},
|
||
/** 学期代号 -> 学期名称(如 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 + '学期')
|
||
},
|
||
/** 学期代号 -> {xn, xq} */
|
||
parseNd(nd) {
|
||
if (!nd) return { xn: undefined, xq: undefined }
|
||
const s = String(nd)
|
||
return { xn: s.slice(0, 4), xq: s.slice(4) }
|
||
},
|
||
/** 课时系数方案编号 -> 名称 */
|
||
getCoefficientLabel(value) {
|
||
if (!value && value !== 0) return '-'
|
||
const found = this.coefficientOptions.find(o => String(o.value) === String(value))
|
||
return found ? found.label : value
|
||
},
|
||
/** 格式化后端 LocalDateTime(2026-02-23T00:00:00 -> 2026-02-23) */
|
||
formatDate(value) {
|
||
if (!value) return ''
|
||
return String(value).slice(0, 10)
|
||
},
|
||
/** 查询学期列表 */
|
||
getList() {
|
||
this.loading = true
|
||
listSemester(this.queryParams).then(response => {
|
||
const data = response.data || {}
|
||
this.semesterList = data.records || []
|
||
this.total = data.total || 0
|
||
this.loading = false
|
||
}).catch(() => {
|
||
this.semesterList = []
|
||
this.total = 0
|
||
this.loading = false
|
||
})
|
||
},
|
||
/** 多选变化 */
|
||
handleSelectionChange(selection) {
|
||
this.ids = selection.map(item => item.nd)
|
||
this.single = selection.length !== 1
|
||
this.multiple = !selection.length
|
||
},
|
||
/** 组装提交数据 */
|
||
buildPayload(form) {
|
||
const lockValue = form.lockValue !== undefined && form.lockValue !== null ? form.lockValue : ''
|
||
return {
|
||
delFlag: 0,
|
||
nd: this.buildNd(form.xn, form.xq),
|
||
dqxq: !!form.dqxq,
|
||
kxrq: form.kxrq ? form.kxrq + 'T00:00:00' : null,
|
||
jsrq: form.jsrq ? form.jsrq + 'T00:00:00' : null,
|
||
tkbsp: !!form.tkbsp,
|
||
jztk: !!form.jztk,
|
||
zjcjdjg: !!form.zjcjdjg,
|
||
jwldshtk: !!form.jwldshtk,
|
||
ksxsfabh: form.ksxsfabh || null,
|
||
sdzs: form.sdzs,
|
||
sdjldw: lockValue === '' ? '' : lockValue + form.lockUnit,
|
||
xh: form.xh,
|
||
jsonzd: "{'1':1}",
|
||
kstjsj: null
|
||
}
|
||
},
|
||
/** 新增按钮 */
|
||
handleAdd() {
|
||
this.reset()
|
||
this.isAdd = true
|
||
this.open = true
|
||
this.title = "学期信息 - 新增"
|
||
},
|
||
/** 编辑按钮 */
|
||
handleUpdate(row) {
|
||
const semesterId = row && row.nd ? row.nd : this.ids[0]
|
||
if (!semesterId) return
|
||
this.reset()
|
||
this.isAdd = false
|
||
getSemester(semesterId).then(response => {
|
||
const data = response.data || {}
|
||
const { xn, xq } = this.parseNd(data.nd)
|
||
// 解析锁定时长(如 "2周" / "1月" / "1")
|
||
let lockValue = data.sdjldw
|
||
let lockUnit = '周'
|
||
if (lockValue) {
|
||
const s = String(lockValue)
|
||
if (s.endsWith('周')) { lockValue = s.slice(0, -1); lockUnit = '周' }
|
||
else if (s.endsWith('月')) { lockValue = s.slice(0, -1); lockUnit = '月' }
|
||
}
|
||
this.form = {
|
||
xn: xn,
|
||
xq: xq,
|
||
dqxq: !!data.dqxq,
|
||
jztk: !!data.jztk,
|
||
tkbsp: !!data.tkbsp,
|
||
zjcjdjg: !!data.zjcjdjg,
|
||
jwldshtk: !!data.jwldshtk,
|
||
ksxsfabh: data.ksxsfabh,
|
||
kxrq: this.formatDate(data.kxrq),
|
||
jsrq: this.formatDate(data.jsrq),
|
||
sdzs: data.sdzs,
|
||
lockValue: lockValue,
|
||
lockUnit: lockUnit,
|
||
xh: data.xh
|
||
}
|
||
this.open = true
|
||
this.title = "学期信息 - 编辑"
|
||
}).catch(() => {})
|
||
},
|
||
/** 编辑校历按钮 */
|
||
handleEditCalendar(row) {
|
||
const nd = row && row.nd ? row.nd : this.ids[0]
|
||
if (!nd) return
|
||
this.$router.push({
|
||
path: '/teachBusiness/semester/semesterCalendar',
|
||
query: { nd: nd, name: this.getSemesterName(nd) }
|
||
})
|
||
},
|
||
/** 删除按钮 */
|
||
handleDelete(row) {
|
||
const semesterIds = row && row.nd ? [row.nd] : this.ids
|
||
if (!semesterIds.length) return
|
||
const names = semesterIds.map(nd => this.getSemesterName(nd)).join('、')
|
||
this.$modal.confirm('确认删除学期【' + names + '】吗?').then(() => {
|
||
// 批量删除:逐个调用
|
||
const delList = semesterIds.map(nd => delSemester(nd))
|
||
return Promise.all(delList)
|
||
}).then(() => {
|
||
this.getList()
|
||
this.$modal.msgSuccess("删除成功")
|
||
}).catch(() => {})
|
||
},
|
||
/** 提交表单 */
|
||
submitForm() {
|
||
this.$refs["form"].validate(valid => {
|
||
if (valid) {
|
||
const payload = this.buildPayload(this.form)
|
||
const selfNd = payload.nd
|
||
// 当前学期全局只能有一个:若本次设为当前,先把其他 dqxq=true 的取消
|
||
const cancelOthers = payload.dqxq
|
||
? this.cancelOtherCurrent(selfNd)
|
||
: Promise.resolve()
|
||
cancelOthers.then(() => {
|
||
if (this.isAdd) {
|
||
return addSemester(payload)
|
||
}
|
||
return updateSemester(payload)
|
||
}).then(() => {
|
||
this.$modal.msgSuccess(this.isAdd ? "新增成功" : "修改成功")
|
||
this.open = false
|
||
this.getList()
|
||
}).catch(() => {})
|
||
}
|
||
})
|
||
},
|
||
/** 取消其它“当前学期”,保证全局唯一 */
|
||
cancelOtherCurrent(selfNd) {
|
||
return listSemester({ pageNum: 1, pageSize: 100 }).then(response => {
|
||
const data = response.data || {}
|
||
const list = data.records || []
|
||
const others = list.filter(i => i.dqxq && String(i.nd) !== String(selfNd))
|
||
if (!others.length) return Promise.resolve()
|
||
return Promise.all(others.map(o => updateSemester({ ...o, dqxq: false })))
|
||
})
|
||
},
|
||
/** 重置表单 */
|
||
reset() {
|
||
this.form = {
|
||
xn: undefined,
|
||
xq: undefined,
|
||
dqxq: false,
|
||
jztk: false,
|
||
tkbsp: false,
|
||
zjcjdjg: false,
|
||
jwldshtk: false,
|
||
ksxsfabh: undefined,
|
||
kxrq: undefined,
|
||
jsrq: undefined,
|
||
sdzs: 20,
|
||
lockValue: 1,
|
||
lockUnit: '周',
|
||
xh: 1
|
||
}
|
||
this.resetForm("form")
|
||
},
|
||
/** 取消按钮 */
|
||
cancel() {
|
||
this.open = false
|
||
this.reset()
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style scoped lang="scss">
|
||
.teach-semester {
|
||
.mb8 {
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
/* 弹窗内表单:防止 label 文字换行、字段拥挤换行 */
|
||
::v-deep .el-dialog__body {
|
||
padding-top: 16px;
|
||
padding-bottom: 16px;
|
||
}
|
||
|
||
/* 对话框整体两栏布局 */
|
||
.semester-dialog-body {
|
||
display: block;
|
||
}
|
||
|
||
.semester-form-col {
|
||
padding-right: 4px;
|
||
}
|
||
|
||
.semester-switch-col {
|
||
padding-left: 24px;
|
||
border-left: 1px solid #ebeef5;
|
||
}
|
||
|
||
.semester-dialog-form {
|
||
::v-deep .el-form-item {
|
||
margin-bottom: 18px;
|
||
}
|
||
|
||
::v-deep .el-form-item__label {
|
||
white-space: nowrap;
|
||
line-height: 32px;
|
||
padding-right: 10px;
|
||
color: #606266;
|
||
}
|
||
|
||
::v-deep .el-form-item__content {
|
||
line-height: 32px;
|
||
}
|
||
|
||
.el-input,
|
||
.el-select,
|
||
.el-date-editor {
|
||
width: 100%;
|
||
}
|
||
}
|
||
|
||
.form-tip {
|
||
font-size: 12px;
|
||
color: #909399;
|
||
line-height: 1.5;
|
||
margin-top: 4px;
|
||
white-space: normal;
|
||
padding-left: 2px;
|
||
}
|
||
|
||
.lock-group {
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
.switch-panel-title {
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
color: #303133;
|
||
margin-bottom: 14px;
|
||
padding-left: 2px;
|
||
}
|
||
|
||
.switch-panel {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 12px;
|
||
}
|
||
|
||
.switch-item {
|
||
padding: 11px 13px;
|
||
background: #fafafa;
|
||
border: 1px solid #ebeef5;
|
||
border-radius: 6px;
|
||
transition: border-color 0.2s, background 0.2s;
|
||
|
||
&:hover {
|
||
border-color: var(--edu-green-primary);
|
||
background: #fff;
|
||
}
|
||
|
||
.el-checkbox {
|
||
height: 26px;
|
||
font-weight: 600;
|
||
color: #303133;
|
||
}
|
||
|
||
.switch-tip {
|
||
font-size: 12px;
|
||
line-height: 1.6;
|
||
color: #909399;
|
||
text-align: justify;
|
||
margin: 2px 0 0 2px;
|
||
}
|
||
}
|
||
}
|
||
</style>
|