前端代码

This commit is contained in:
liumengyu
2026-08-21 18:42:51 +08:00
parent 054947482f
commit 04e06a9c9a
762 changed files with 62992 additions and 0 deletions
@@ -0,0 +1,53 @@
<template>
<div class="teach-calendar app-container">
<school-calendar-editor
v-if="nd"
:nd="nd"
:semester-name="semesterName"
@close="handleClose"
/>
<div v-else class="calendar-empty">未指定学期,请从学期管理页面进入。</div>
</div>
</template>
<script>
import SchoolCalendarEditor from '@/views/teachBusiness/calendar/components/SchoolCalendarEditor'
export default {
name: 'SemesterCalendarEdit',
components: { SchoolCalendarEditor },
data() {
return {
nd: '',
semesterName: ''
}
},
created() {
this.nd = this.$route.query.nd || ''
this.semesterName = this.$route.query.name || ''
},
methods: {
handleClose() {
this.$router.replace({ path: '/teachBusiness/semester' })
}
}
}
</script>
<style scoped>
.teach-calendar {
height: 100%;
display: flex;
flex-direction: column;
padding: 12px;
box-sizing: border-box;
}
.calendar-empty {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
color: #909399;
font-size: 14px;
}
</style>
@@ -0,0 +1,596 @@
<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="success" plain icon="el-icon-edit" size="mini" :disabled="single" @click="handleUpdate">编辑</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-col :span="1.5">
<el-button type="warning" plain icon="el-icon-date" size="mini" :disabled="single" @click="handleEditCalendar">编辑校历</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>
<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() {
if (this.ids.length !== 1) return
this.reset()
this.isAdd = false
getSemester(this.ids[0]).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() {
if (this.ids.length !== 1) return
const nd = this.ids[0]
this.$router.push({
path: '/teachBusiness/semester/semesterCalendar',
query: { nd: nd, name: this.getSemesterName(nd) }
})
},
/** 删除按钮 */
handleDelete() {
if (!this.ids.length) return
const names = this.ids.map(nd => this.getSemesterName(nd)).join('、')
this.$modal.confirm('确认删除学期【' + names + '】吗?').then(() => {
// 批量删除:逐个调用
const delList = this.ids.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>