fb3d20def6
2、学员信息管理,操作列改为两行显示
1071 lines
40 KiB
Vue
1071 lines
40 KiB
Vue
<template>
|
||
<div class="app-container shift-semester-page">
|
||
<!-- ==================== 页面标题 ==================== -->
|
||
<div class="page-title">班次学期信息管理</div>
|
||
|
||
<!-- ==================== 1. 查询条件区域 ==================== -->
|
||
<el-card shadow="never" class="search-card">
|
||
<el-form :inline="true" :model="searchForm" class="search-form">
|
||
<el-form-item label="班次编号">
|
||
<el-input
|
||
v-model="searchForm.xydbh"
|
||
placeholder="请输入班次编号(模糊)"
|
||
clearable
|
||
style="width: 220px"
|
||
@keyup.enter.native="handleQuery"
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item label="年度">
|
||
<el-select
|
||
v-model="filterYear"
|
||
placeholder="全部年度"
|
||
clearable
|
||
filterable
|
||
style="width: 140px"
|
||
@change="handleQuery"
|
||
>
|
||
<el-option v-for="y in yearOptions" :key="y" :label="`${y}年`" :value="y" />
|
||
</el-select>
|
||
</el-form-item>
|
||
<el-form-item label="学期第次">
|
||
<el-select v-model="searchForm.xqdc" placeholder="全部" clearable style="width: 120px" @change="handleQuery">
|
||
<el-option label="第1学期" :value="1" />
|
||
<el-option label="第2学期" :value="2" />
|
||
<el-option label="第3学期" :value="3" />
|
||
</el-select>
|
||
</el-form-item>
|
||
<el-form-item>
|
||
<el-checkbox v-model="sortWave" @change="handleQuery">按波次排序</el-checkbox>
|
||
</el-form-item>
|
||
<el-form-item>
|
||
<el-button type="primary" icon="el-icon-search" @click="handleQuery">查询</el-button>
|
||
<el-button icon="el-icon-refresh" @click="handleReset">重置</el-button>
|
||
</el-form-item>
|
||
</el-form>
|
||
</el-card>
|
||
|
||
<!-- ==================== 2. 班次学期信息列表 ==================== -->
|
||
<el-card shadow="never" class="table-card">
|
||
<div class="list-header">
|
||
<div class="list-title">班次学期列表</div>
|
||
<div class="list-actions">
|
||
<el-button type="primary" icon="el-icon-plus" @click="handleBatchAdd">批量创建班次学期</el-button>
|
||
<el-button type="primary" plain icon="el-icon-document-add" @click="handleAdd">新增</el-button>
|
||
<el-button type="primary" plain icon="el-icon-edit" :disabled="selection.length !== 1" @click="handleEdit()">编辑</el-button>
|
||
<el-button type="danger" plain icon="el-icon-delete" :disabled="!selection.length" @click="handleDelete">删除所选</el-button>
|
||
<el-button type="primary" plain icon="el-icon-date" :disabled="!selection.length" @click="handleQuickSetDates">快速设定学期日期</el-button>
|
||
<el-button type="primary" plain icon="el-icon-download" :disabled="selection.length !== 1" @click="handleAbsorbDates">吸取预设日期</el-button>
|
||
<el-button type="primary" plain icon="el-icon-notebook-2" :disabled="!selection.length" @click="handleSetJxrw">批量设置教学任务</el-button>
|
||
<el-button type="primary" plain :disabled="!selection.length" @click="handleSetXqdc">批量学期第次</el-button>
|
||
<el-button type="primary" plain :disabled="!selection.length" @click="handleSetKfjypk">开放教员排课</el-button>
|
||
<el-button type="warning" plain :disabled="selection.length < 2" @click="handleWaveMerge">预设合班</el-button>
|
||
<el-button type="warning" plain :disabled="!selection.length" @click="handleWaveSplit">预设拆班</el-button>
|
||
<el-button type="primary" plain :disabled="!selection.length" @click="handleBatchGenerateTasks">批量生成必修课程</el-button>
|
||
<el-button type="success" plain icon="el-icon-date" :disabled="selection.length !== 1" @click="handleEditEventCalendar()">编辑班历</el-button>
|
||
<el-button type="success" plain icon="el-icon-calendar" :disabled="selection.length !== 1" @click="handleEditCalendar()">编辑班次课程</el-button>
|
||
<el-button type="success" plain icon="el-icon-data-line" :disabled="selection.length !== 1" @click="handleAllocation()">教学配当</el-button>
|
||
<el-button type="success" plain icon="el-icon-download" :disabled="!selection.length" @click="handleExportAllocation">导出配当</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>
|
||
<el-table
|
||
ref="tableRef"
|
||
v-loading="loading"
|
||
:data="displayData"
|
||
border
|
||
stripe
|
||
highlight-current-row
|
||
size="small"
|
||
max-height="480"
|
||
class="class-table"
|
||
:row-class-name="waveRowClass"
|
||
@selection-change="handleSelectionChange"
|
||
>
|
||
<template slot="empty">
|
||
<span>无数据!</span>
|
||
</template>
|
||
<el-table-column type="selection" width="44" align="center" />
|
||
<el-table-column type="index" label="序号" width="55" align="center" />
|
||
<el-table-column prop="xydmc" label="班次名称" min-width="140" align="center" show-overflow-tooltip>
|
||
<template slot-scope="{ row }">{{ row.xydmc || '-' }}</template>
|
||
</el-table-column>
|
||
<el-table-column prop="xydbh" label="班次编号" min-width="130" align="center" show-overflow-tooltip />
|
||
<el-table-column prop="ysbbh" label="预设编班号" width="100" align="center">
|
||
<template slot-scope="{ row }">{{ waveLabel(row.ysbbh) }}</template>
|
||
</el-table-column>
|
||
<el-table-column prop="nd" label="年度" width="80" align="center" show-overflow-tooltip />
|
||
<el-table-column label="学期第次" width="90" align="center">
|
||
<template slot-scope="{ row }">{{ xqdcLabel(row.xqdc) }}</template>
|
||
</el-table-column>
|
||
<el-table-column label="开学日期" width="110" align="center">
|
||
<template slot-scope="{ row }">{{ formatDate(row.kxrq) }}</template>
|
||
</el-table-column>
|
||
<el-table-column label="结束日期" width="110" align="center">
|
||
<template slot-scope="{ row }">{{ formatDate(row.jsrq) }}</template>
|
||
</el-table-column>
|
||
<el-table-column label="专用教室" min-width="140" align="center" show-overflow-tooltip>
|
||
<template slot-scope="{ row }">{{ roomLabel(row) }}</template>
|
||
</el-table-column>
|
||
<el-table-column label="教学任务" min-width="160" align="center" show-overflow-tooltip>
|
||
<template slot-scope="{ row }">{{ taskName(row.jxrwbh) }}</template>
|
||
</el-table-column>
|
||
<el-table-column prop="zkcs" label="总课程数" width="90" align="center">
|
||
<template slot-scope="{ row }">{{ row.zkcs == null ? 0 : row.zkcs }}</template>
|
||
</el-table-column>
|
||
<el-table-column label="开放教员排课" width="110" align="center">
|
||
<template slot-scope="{ row }">
|
||
<el-tag v-if="Number(row.kfjypk) === 1" type="success" size="small">开放</el-tag>
|
||
<el-tag v-else type="info" size="small">关闭</el-tag>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="bz" label="备注" min-width="120" align="left" header-align="center" show-overflow-tooltip />
|
||
<el-table-column label="操作" width="210" align="center" fixed="right" class-name="table-action-column">
|
||
<template slot-scope="{ row }">
|
||
<div class="op-btn-grid">
|
||
<el-button type="text" size="small" @click="handleEdit(row)">编辑</el-button>
|
||
<el-button type="text" size="small" @click="handleEditEventCalendar(row)">班历</el-button>
|
||
<el-button type="text" size="small" @click="handleEditCalendar(row)">班次课程</el-button>
|
||
<el-button type="text" size="small" @click="handleSchedule(row)">排课</el-button>
|
||
<el-button type="text" size="small" class="text-danger" @click="handleDeleteRow(row)">删除</el-button>
|
||
</div>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
|
||
<el-pagination
|
||
class="list-pagination"
|
||
:current-page="pageNum"
|
||
:page-size="pageSize"
|
||
:page-sizes="[10, 20, 50, 100]"
|
||
:total="total"
|
||
layout="total, sizes, prev, pager, next, jumper"
|
||
@size-change="handleSizeChange"
|
||
@current-change="handlePageChange"
|
||
/>
|
||
</el-card>
|
||
|
||
<!-- ==================== 班次学期新增/编辑弹窗 ==================== -->
|
||
<ClassSemesterDetailDialog
|
||
:visible="detailVisible"
|
||
:mode="detailMode"
|
||
:semester="detailSemester"
|
||
:team-info="detailTeamInfo"
|
||
:year-options="yearOptions"
|
||
:task-options="taskOptions"
|
||
:preset-dates="presetDates"
|
||
@update:visible="val => detailVisible = val"
|
||
@submit="handleDetailSubmit"
|
||
/>
|
||
|
||
<!-- ==================== 班次课程弹窗 ==================== -->
|
||
<ClassCalendarDialog
|
||
:visible="calendarVisible"
|
||
:semester="calendarSemester"
|
||
:semester-options="semesterOptions"
|
||
@update:visible="val => calendarVisible = val"
|
||
/>
|
||
|
||
<ClassEventCalendarDialog
|
||
:visible="eventCalendarVisible"
|
||
:semester="eventCalendarSemester"
|
||
:semester-options="semesterOptions"
|
||
@update:visible="val => eventCalendarVisible = val"
|
||
/>
|
||
|
||
<TeachingAllocationDialog
|
||
:visible="allocationVisible"
|
||
:semester="allocationSemester"
|
||
@update:visible="val => allocationVisible = val"
|
||
/>
|
||
|
||
<!-- ==================== 批量创建班次学期弹窗 ==================== -->
|
||
<el-dialog
|
||
:visible="batchAddVisible"
|
||
title="批量创建班次学期"
|
||
width="520px"
|
||
:close-on-click-modal="false"
|
||
@update:visible="val => batchAddVisible = val"
|
||
>
|
||
<el-form ref="batchAddFormRef" :model="batchAddForm" :rules="batchAddRules" label-width="100px">
|
||
<el-form-item label="年度" prop="nd">
|
||
<el-select v-model="batchAddForm.nd" placeholder="请选择年度" filterable style="width: 100%" @change="loadBatchPreset">
|
||
<el-option v-for="y in yearOptions" :key="y" :label="`${y}年`" :value="y" />
|
||
</el-select>
|
||
</el-form-item>
|
||
<el-form-item label="学期" prop="xqdc">
|
||
<el-select v-model="batchAddForm.xqdc" placeholder="请选择学期" style="width: 100%" @change="loadBatchPreset">
|
||
<el-option label="第1学期" :value="1" />
|
||
<el-option label="第2学期" :value="2" />
|
||
<el-option label="第3学期" :value="3" />
|
||
</el-select>
|
||
</el-form-item>
|
||
<el-form-item label="开始日期" prop="startTime">
|
||
<el-date-picker v-model="batchAddForm.startTime" type="date" value-format="yyyy-MM-dd" placeholder="请选择开始日期" style="width: 100%" />
|
||
</el-form-item>
|
||
<el-form-item label="结束日期" prop="endTime">
|
||
<el-date-picker v-model="batchAddForm.endTime" type="date" value-format="yyyy-MM-dd" placeholder="请选择结束日期" style="width: 100%" />
|
||
</el-form-item>
|
||
<el-form-item>
|
||
<el-button size="small" @click="loadBatchPreset">按学校学期带出日期</el-button>
|
||
<el-button size="small" :disabled="selection.length !== 1" @click="absorbIntoBatch">吸取选中班次日期</el-button>
|
||
</el-form-item>
|
||
<el-alert
|
||
type="info"
|
||
:closable="false"
|
||
show-icon
|
||
:title="`将为未毕业且尚未建立的班次创建学期信息,当前可新建 ${batchEligibleCount} 个班次`"
|
||
/>
|
||
</el-form>
|
||
<div slot="footer">
|
||
<el-button @click="batchAddVisible = false">取消</el-button>
|
||
<el-button type="primary" :loading="saving" @click="handleBatchAddSubmit">确定</el-button>
|
||
</div>
|
||
</el-dialog>
|
||
|
||
<!-- ==================== 快速设定学期日期弹窗 ==================== -->
|
||
<el-dialog
|
||
:visible="dateRangeVisible"
|
||
title="快速设定学期日期"
|
||
width="480px"
|
||
:close-on-click-modal="false"
|
||
@update:visible="val => dateRangeVisible = val"
|
||
>
|
||
<el-alert
|
||
type="info"
|
||
:closable="false"
|
||
show-icon
|
||
:title="`将对选中的 ${selection.length} 条班次学期统一设定开学/结束日期`"
|
||
style="margin-bottom: 16px"
|
||
/>
|
||
<el-form ref="dateRangeFormRef" :model="dateRangeForm" :rules="dateRangeRules" label-width="100px">
|
||
<el-form-item label="开学日期" prop="kxrq">
|
||
<el-date-picker v-model="dateRangeForm.kxrq" type="date" value-format="yyyy-MM-dd" placeholder="请选择开学日期" style="width: 100%" />
|
||
</el-form-item>
|
||
<el-form-item label="结束日期" prop="jsrq">
|
||
<el-date-picker v-model="dateRangeForm.jsrq" type="date" value-format="yyyy-MM-dd" placeholder="请选择结束日期" style="width: 100%" />
|
||
</el-form-item>
|
||
</el-form>
|
||
<div slot="footer">
|
||
<el-button @click="dateRangeVisible = false">取消</el-button>
|
||
<el-button type="primary" :loading="saving" @click="handleDateRangeSubmit">确定</el-button>
|
||
</div>
|
||
</el-dialog>
|
||
|
||
<!-- ==================== 批量设置教学任务弹窗 ==================== -->
|
||
<el-dialog
|
||
:visible="jxrwVisible"
|
||
title="批量设置教学任务"
|
||
width="480px"
|
||
:close-on-click-modal="false"
|
||
@update:visible="val => jxrwVisible = val"
|
||
>
|
||
<el-alert
|
||
type="info"
|
||
:closable="false"
|
||
show-icon
|
||
:title="`将对选中的 ${selection.length} 条班次学期统一设置教学任务`"
|
||
style="margin-bottom: 16px"
|
||
/>
|
||
<el-form ref="jxrwFormRef" :model="jxrwForm" :rules="jxrwRules" label-width="100px">
|
||
<el-form-item label="教学任务" prop="jxrwbh">
|
||
<el-select v-model="jxrwForm.jxrwbh" placeholder="请选择教学任务" filterable style="width: 100%">
|
||
<el-option v-for="opt in taskOptions" :key="opt.bh" :label="opt.rwmc" :value="opt.bh" />
|
||
</el-select>
|
||
</el-form-item>
|
||
</el-form>
|
||
<div slot="footer">
|
||
<el-button @click="jxrwVisible = false">取消</el-button>
|
||
<el-button type="primary" :loading="saving" @click="handleJxrwSubmit">确定</el-button>
|
||
</div>
|
||
</el-dialog>
|
||
|
||
<el-dialog
|
||
:visible="xqdcVisible"
|
||
title="批量设定学期第次"
|
||
width="420px"
|
||
:close-on-click-modal="false"
|
||
@update:visible="val => xqdcVisible = val"
|
||
>
|
||
<el-form label-width="100px">
|
||
<el-form-item label="学期第次">
|
||
<el-select v-model="xqdcForm.xqdc" placeholder="请选择学期第次" style="width: 100%">
|
||
<el-option label="第1学期" :value="1" />
|
||
<el-option label="第2学期" :value="2" />
|
||
<el-option label="第3学期" :value="3" />
|
||
</el-select>
|
||
</el-form-item>
|
||
</el-form>
|
||
<div slot="footer">
|
||
<el-button @click="xqdcVisible = false">取消</el-button>
|
||
<el-button type="primary" :loading="saving" @click="handleXqdcSubmit">确定</el-button>
|
||
</div>
|
||
</el-dialog>
|
||
|
||
<el-dialog
|
||
:visible="kfjypkVisible"
|
||
title="批量设定开放教员排课"
|
||
width="420px"
|
||
:close-on-click-modal="false"
|
||
@update:visible="val => kfjypkVisible = val"
|
||
>
|
||
<el-form label-width="120px">
|
||
<el-form-item label="开放教员排课">
|
||
<el-radio-group v-model="kfjypkForm.kfjypk">
|
||
<el-radio :label="1">开放</el-radio>
|
||
<el-radio :label="0">关闭</el-radio>
|
||
</el-radio-group>
|
||
</el-form-item>
|
||
</el-form>
|
||
<div slot="footer">
|
||
<el-button @click="kfjypkVisible = false">取消</el-button>
|
||
<el-button type="primary" :loading="saving" @click="handleKfjypkSubmit">确定</el-button>
|
||
</div>
|
||
</el-dialog>
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
import ClassSemesterDetailDialog from './ClassSemesterDetailDialog.vue'
|
||
import ClassCalendarDialog from './ClassCalendarDialog.vue'
|
||
import ClassEventCalendarDialog from './ClassEventCalendarDialog.vue'
|
||
import TeachingAllocationDialog from './TeachingAllocationDialog.vue'
|
||
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 { allocationExport } from '@/api/teachBusiness/allocation'
|
||
import { publishRunningCourses, withdrawRunningCourses } from '@/api/teachBusiness/schedulingWindow'
|
||
|
||
export default {
|
||
name: 'ShiftSemester',
|
||
components: {
|
||
ClassSemesterDetailDialog,
|
||
ClassCalendarDialog,
|
||
ClassEventCalendarDialog,
|
||
TeachingAllocationDialog
|
||
},
|
||
data() {
|
||
return {
|
||
loading: false,
|
||
saving: false,
|
||
/** 运行课表批量发布/删除进行中 */
|
||
runningPublishing: false,
|
||
|
||
// ==================== 查询条件 ====================
|
||
searchForm: { xydbh: '', xqdc: undefined },
|
||
filterYear: undefined,
|
||
sortWave: true,
|
||
presetDates: { kxrq: '', jsrq: '' },
|
||
|
||
// ==================== 列表与分页 ====================
|
||
tableData: [],
|
||
selection: [],
|
||
pageNum: 1,
|
||
pageSize: 10,
|
||
total: 0,
|
||
|
||
// ==================== 下拉数据 ====================
|
||
/** 年度下拉(来自 /semester/all) */
|
||
yearOptions: [],
|
||
/** 教学任务下拉({ bh, rwmc }) */
|
||
taskOptions: [],
|
||
/** 全部班次学期(用于班历复制目标选择) */
|
||
semesterOptions: [],
|
||
|
||
// ==================== 班次学期新增/编辑 ====================
|
||
detailVisible: false,
|
||
detailMode: 'add',
|
||
detailSemester: null,
|
||
detailTeamInfo: null,
|
||
|
||
// ==================== 班历管理 ====================
|
||
calendarVisible: false,
|
||
calendarSemester: null,
|
||
eventCalendarVisible: false,
|
||
eventCalendarSemester: null,
|
||
|
||
// ==================== 教学配当 ====================
|
||
allocationVisible: false,
|
||
allocationSemester: null,
|
||
|
||
// ==================== 批量创建班次学期 ====================
|
||
batchAddVisible: false,
|
||
batchAddForm: { nd: undefined, xqdc: undefined, startTime: '', endTime: '' },
|
||
batchEligibleCount: 0,
|
||
batchAddRules: {
|
||
nd: [{ required: true, message: '请选择年度', trigger: 'change' }],
|
||
xqdc: [{ required: true, message: '请选择学期', trigger: 'change' }],
|
||
startTime: [{ required: true, message: '请选择开始日期', trigger: 'change' }],
|
||
endTime: [{ required: true, message: '请选择结束日期', trigger: 'change' }]
|
||
},
|
||
|
||
// ==================== 快速设定学期日期 ====================
|
||
dateRangeVisible: false,
|
||
dateRangeForm: { kxrq: '', jsrq: '' },
|
||
dateRangeRules: {
|
||
kxrq: [{ required: true, message: '请选择开学日期', trigger: 'change' }],
|
||
jsrq: [{ required: true, message: '请选择结束日期', trigger: 'change' }]
|
||
},
|
||
|
||
// ==================== 批量设置教学任务 ====================
|
||
jxrwVisible: false,
|
||
jxrwForm: { jxrwbh: undefined },
|
||
jxrwRules: {
|
||
jxrwbh: [{ required: true, message: '请选择教学任务', trigger: 'change' }]
|
||
},
|
||
xqdcVisible: false,
|
||
xqdcForm: { xqdc: 1 },
|
||
kfjypkVisible: false,
|
||
kfjypkForm: { kfjypk: 1 }
|
||
}
|
||
},
|
||
computed: {
|
||
displayData() {
|
||
return this.tableData
|
||
}
|
||
},
|
||
mounted() {
|
||
this.loadYearOptions()
|
||
this.loadTaskOptions()
|
||
this.loadSemesterOptions()
|
||
this.loadList()
|
||
},
|
||
methods: {
|
||
/* ==================== 格式化 ==================== */
|
||
formatDate(val) {
|
||
if (val == null || val === '') return '-'
|
||
if (Array.isArray(val) && val.length >= 3) {
|
||
return `${val[0]}-${String(val[1]).padStart(2, '0')}-${String(val[2]).padStart(2, '0')}`
|
||
}
|
||
if (typeof val === 'object') {
|
||
const year = val.year
|
||
const month = val.monthValue || val.month
|
||
const day = val.dayOfMonth || val.day
|
||
if (year && month && day) {
|
||
return `${year}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')}`
|
||
}
|
||
return '-'
|
||
}
|
||
const text = String(val)
|
||
return text.length >= 10 ? text.slice(0, 10) : text
|
||
},
|
||
yearFromNd(nd) {
|
||
const text = String(nd || '')
|
||
if (text.length >= 6) return Number(text.slice(0, 4))
|
||
const n = Number(text)
|
||
return Number.isFinite(n) && n > 0 ? n : null
|
||
},
|
||
roomLabel(row) {
|
||
if (!row) return '-'
|
||
if (row.zyjsmc && row.zyjsbh) return `${row.zyjsmc}(${row.zyjsbh})`
|
||
return row.zyjsmc || row.zyjsbh || '-'
|
||
},
|
||
waveLabel(val) {
|
||
const n = Number(val)
|
||
return n > 0 ? n : '未编波'
|
||
},
|
||
waveRowClass({ row }) {
|
||
const n = Number(row.ysbbh)
|
||
if (!n) return ''
|
||
return 'wave-row wave-row-' + (n % 8)
|
||
},
|
||
xqdcLabel(val) {
|
||
if (val === 1 || val === '1') return '第1学期'
|
||
if (val === 2 || val === '2') return '第2学期'
|
||
if (val === 3 || val === '3') return '第3学期'
|
||
return val !== undefined && val !== null && val !== '' ? `第${val}学期` : '-'
|
||
},
|
||
/** 教学任务编号 → 名称 */
|
||
taskName(bh) {
|
||
if (!bh) return '-'
|
||
const opt = this.taskOptions.find(o => o.bh === bh)
|
||
return opt ? opt.rwmc : bh
|
||
},
|
||
cleanPayload(obj) {
|
||
const payload = {}
|
||
Object.keys(obj).forEach(key => {
|
||
const value = obj[key]
|
||
if (value !== '' && value !== null && value !== undefined) {
|
||
payload[key] = value
|
||
}
|
||
})
|
||
return payload
|
||
},
|
||
|
||
/* ==================== 下拉数据加载 ==================== */
|
||
loadYearOptions() {
|
||
listAllSemester().then(res => {
|
||
const list = (res && res.data) || []
|
||
const years = list.map(x => this.yearFromNd(x.nd)).filter(v => v)
|
||
this.yearOptions = [...new Set(years)].sort((a, b) => Number(a) - Number(b))
|
||
}).catch(() => {
|
||
this.yearOptions = []
|
||
})
|
||
},
|
||
loadTaskOptions() {
|
||
listTeachingTask({ pageNum: 1, pageSize: 1000 }).then(res => {
|
||
const list = (res && res.data && res.data.records) || []
|
||
this.taskOptions = list.filter(o => o.bh)
|
||
}).catch(() => {
|
||
this.taskOptions = []
|
||
})
|
||
},
|
||
loadSemesterOptions() {
|
||
allSemester().then(res => {
|
||
this.semesterOptions = (res && res.data) || []
|
||
}).catch(() => {
|
||
this.semesterOptions = []
|
||
})
|
||
},
|
||
|
||
/* ==================== 列表加载 ==================== */
|
||
loadList() {
|
||
this.loading = true
|
||
const query = this.cleanPayload({
|
||
pageNum: this.pageNum,
|
||
pageSize: this.pageSize,
|
||
xydbh: this.searchForm.xydbh,
|
||
nd: this.filterYear,
|
||
xqdc: this.searchForm.xqdc,
|
||
sortWave: this.sortWave ? 1 : 0
|
||
})
|
||
listSemester(query).then(res => {
|
||
const data = (res && res.data) || {}
|
||
this.tableData = data.records || []
|
||
this.total = data.total || 0
|
||
this.loading = false
|
||
// fixed 克隆层在异步数据回填后需要重排,否则行位置与主表错位会透出下层文字
|
||
this.$nextTick(() => {
|
||
this.$refs.tableRef && this.$refs.tableRef.doLayout()
|
||
})
|
||
}).catch(() => {
|
||
this.tableData = []
|
||
this.total = 0
|
||
this.loading = false
|
||
})
|
||
},
|
||
handleSizeChange(size) {
|
||
this.pageSize = size
|
||
this.loadList()
|
||
},
|
||
handlePageChange(page) {
|
||
this.pageNum = page
|
||
this.loadList()
|
||
},
|
||
handleSelectionChange(rows) {
|
||
this.selection = rows
|
||
},
|
||
|
||
/* ==================== 查询 / 重置 ==================== */
|
||
handleQuery() {
|
||
this.pageNum = 1
|
||
this.loadList()
|
||
},
|
||
handleReset() {
|
||
this.searchForm.xydbh = ''
|
||
this.searchForm.xqdc = undefined
|
||
this.filterYear = undefined
|
||
this.pageNum = 1
|
||
this.loadList()
|
||
},
|
||
|
||
/* ==================== 新增 / 编辑 ==================== */
|
||
handleAdd() {
|
||
this.detailMode = 'add'
|
||
this.detailSemester = null
|
||
this.detailTeamInfo = null
|
||
this.detailVisible = true
|
||
},
|
||
handleEdit(row) {
|
||
const target = row || this.selection[0]
|
||
if (!target) {
|
||
this.$message.warning('请先选择一条班次学期信息')
|
||
return
|
||
}
|
||
this.detailMode = 'edit'
|
||
this.detailSemester = target
|
||
this.detailTeamInfo = null
|
||
this.detailVisible = true
|
||
},
|
||
handleDetailSubmit(payload) {
|
||
this.saving = true
|
||
const req = this.detailMode === 'edit'
|
||
? updateSemester(payload)
|
||
: addSemester(payload)
|
||
req.then(() => {
|
||
this.saving = false
|
||
this.detailVisible = false
|
||
this.$message.success(this.detailMode === 'edit' ? '班次学期已更新' : '班次学期已新增')
|
||
this.loadList()
|
||
this.loadSemesterOptions()
|
||
}).catch(() => {
|
||
this.saving = false
|
||
})
|
||
},
|
||
|
||
/* ==================== 删除 ==================== */
|
||
handleDelete() {
|
||
if (!this.selection.length) {
|
||
this.$message.warning('请先勾选要删除的班次学期')
|
||
return
|
||
}
|
||
this.$confirm(`确定删除选中的 ${this.selection.length} 条班次学期吗?若已有课程任务将禁止删除。`, '提示', {
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: 'warning'
|
||
}).then(() => {
|
||
const bhList = this.selection.map(r => r.bh)
|
||
batchDeleteSemester(bhList).then(() => {
|
||
this.$message.success('已删除')
|
||
this.loadList()
|
||
this.loadSemesterOptions()
|
||
})
|
||
}).catch(() => {})
|
||
},
|
||
handleDeleteRow(row) {
|
||
this.$confirm(`确定删除班次学期「${row.xydbh || row.bh}」吗?若已有课程任务将禁止删除。`, '提示', {
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: 'warning'
|
||
}).then(() => {
|
||
delSemester(row.bh).then(() => {
|
||
this.$message.success('已删除')
|
||
this.loadList()
|
||
this.loadSemesterOptions()
|
||
})
|
||
}).catch(() => {})
|
||
},
|
||
|
||
handleEditEventCalendar(row) {
|
||
const target = row || this.selection[0]
|
||
if (!target) {
|
||
this.$message.warning('请先在列表中选择一条班次学期信息')
|
||
return
|
||
}
|
||
this.eventCalendarSemester = target
|
||
this.eventCalendarVisible = true
|
||
},
|
||
|
||
// 跳转到排课窗口,并带上当前班次学期编号
|
||
handleSchedule(row) {
|
||
const target = row || this.selection[0]
|
||
if (!target) {
|
||
this.$message.warning('请先在列表中选择一条班次学期信息')
|
||
return
|
||
}
|
||
const bh = target.bh || target.xydxqbh
|
||
if (!bh) {
|
||
this.$message.warning('该班次学期缺少编号,无法进入排课窗口')
|
||
return
|
||
}
|
||
this.$router.push({ path: '/schedule/schedulingWindow', query: { xydxqbh: bh } })
|
||
},
|
||
|
||
handleAllocation(row) {
|
||
const target = row || this.selection[0]
|
||
if (!target) {
|
||
this.$message.warning('请先在列表中选择一条班次学期信息')
|
||
return
|
||
}
|
||
this.allocationSemester = target
|
||
this.allocationVisible = true
|
||
},
|
||
|
||
// 导出配当(多学期批量):勾选几个班次学期就导出几个
|
||
handleExportAllocation() {
|
||
const ids = this.selection.map(item => item.bh || item.xydxqbh).filter(Boolean)
|
||
if (!ids.length) {
|
||
this.$message.warning('请先勾选要导出的班次学期')
|
||
return
|
||
}
|
||
allocationExport(ids).then(blob => {
|
||
const url = window.URL.createObjectURL(new Blob([blob]))
|
||
const link = document.createElement('a')
|
||
link.href = url
|
||
link.download = `教学配当_${ids.length}个班次学期.xlsx`
|
||
link.click()
|
||
window.URL.revokeObjectURL(url)
|
||
})
|
||
},
|
||
|
||
// 批量发布【运行课表】(手册 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('请先勾选班次学期')
|
||
return
|
||
}
|
||
const ids = this.selection.map(item => item.bh).filter(Boolean)
|
||
this.$confirm(`将为所选 ${ids.length} 个班次学期按人培补齐必修课程,已有课程不会重复生成。`, '批量生成必修课程', {
|
||
type: 'warning'
|
||
}).then(() => {
|
||
batchAutoGenerateTasks(ids).then(res => {
|
||
this.$message.success(`已生成 ${res.data || 0} 条课程任务`)
|
||
this.loadList()
|
||
})
|
||
}).catch(() => {})
|
||
},
|
||
|
||
/* ==================== 编辑班次课程 ==================== */
|
||
handleEditCalendar(row) {
|
||
const target = row || this.selection[0]
|
||
if (!target) {
|
||
this.$message.warning('请先在列表中选择一条班次学期信息')
|
||
return
|
||
}
|
||
this.calendarSemester = target
|
||
this.calendarVisible = true
|
||
},
|
||
|
||
/* ==================== 批量创建班次学期 ==================== */
|
||
handleBatchAdd() {
|
||
this.batchAddForm = {
|
||
nd: this.filterYear || this.yearOptions[this.yearOptions.length - 1] || undefined,
|
||
xqdc: this.searchForm.xqdc || 1,
|
||
startTime: this.presetDates.kxrq || '',
|
||
endTime: this.presetDates.jsrq || ''
|
||
}
|
||
this.batchEligibleCount = 0
|
||
this.batchAddVisible = true
|
||
this.loadBatchPreset()
|
||
this.$nextTick(() => {
|
||
if (this.$refs.batchAddFormRef) this.$refs.batchAddFormRef.clearValidate()
|
||
})
|
||
},
|
||
loadBatchPreset() {
|
||
const f = this.batchAddForm
|
||
if (!f.nd || !f.xqdc) return
|
||
presetSemesterDates({ nd: f.nd, xqdc: f.xqdc }).then(res => {
|
||
const data = (res && res.data) || {}
|
||
if (data.kxrq) this.batchAddForm.startTime = data.kxrq
|
||
if (data.jsrq) this.batchAddForm.endTime = data.jsrq
|
||
}).catch(() => {})
|
||
listCreatableClasses({ nd: f.nd, xqdc: f.xqdc }).then(res => {
|
||
this.batchEligibleCount = ((res && res.data) || []).length
|
||
}).catch(() => {
|
||
this.batchEligibleCount = 0
|
||
})
|
||
},
|
||
absorbIntoBatch() {
|
||
if (this.selection.length !== 1) return
|
||
this.absorbDatesFrom(this.selection[0])
|
||
this.batchAddForm.startTime = this.presetDates.kxrq
|
||
this.batchAddForm.endTime = this.presetDates.jsrq
|
||
},
|
||
handleBatchAddSubmit() {
|
||
this.$refs.batchAddFormRef.validate(valid => {
|
||
if (!valid) return
|
||
const f = this.batchAddForm
|
||
this.saving = true
|
||
batchCreateSemester({
|
||
nd: f.nd,
|
||
xqdc: f.xqdc,
|
||
kxrq: f.startTime,
|
||
jsrq: f.endTime
|
||
}).then(res => {
|
||
this.saving = false
|
||
this.batchAddVisible = false
|
||
const count = Number((res && res.data) || 0)
|
||
this.$message.success(`已创建 ${count} 个班次学期`)
|
||
this.loadList()
|
||
this.loadSemesterOptions()
|
||
}).catch(() => {
|
||
this.saving = false
|
||
})
|
||
})
|
||
},
|
||
handleAbsorbDates() {
|
||
if (this.selection.length !== 1) {
|
||
this.$message.warning('请勾选一条班次学期')
|
||
return
|
||
}
|
||
this.absorbDatesFrom(this.selection[0])
|
||
this.$message.success(`已吸取开学日期 ${this.presetDates.kxrq}、结束日期 ${this.presetDates.jsrq}`)
|
||
},
|
||
absorbDatesFrom(row) {
|
||
this.presetDates = {
|
||
kxrq: this.formatDate(row.kxrq) === '-' ? '' : this.formatDate(row.kxrq),
|
||
jsrq: this.formatDate(row.jsrq) === '-' ? '' : this.formatDate(row.jsrq)
|
||
}
|
||
},
|
||
handleSetXqdc() {
|
||
if (!this.selection.length) return
|
||
this.xqdcForm = { xqdc: this.selection[0].xqdc || 1 }
|
||
this.xqdcVisible = true
|
||
},
|
||
handleXqdcSubmit() {
|
||
this.saving = true
|
||
batchUpdateXqdc({
|
||
bhList: this.selection.map(r => r.bh),
|
||
xqdc: this.xqdcForm.xqdc
|
||
}).then(res => {
|
||
this.saving = false
|
||
this.xqdcVisible = false
|
||
this.$message.success(`已更新 ${Number((res && res.data) || 0)} 条学期第次`)
|
||
this.loadList()
|
||
}).catch(() => {
|
||
this.saving = false
|
||
})
|
||
},
|
||
handleSetKfjypk() {
|
||
if (!this.selection.length) return
|
||
this.kfjypkForm = { kfjypk: 1 }
|
||
this.kfjypkVisible = true
|
||
},
|
||
handleKfjypkSubmit() {
|
||
this.saving = true
|
||
batchUpdateKfjypk({
|
||
bhList: this.selection.map(r => r.bh),
|
||
kfjypk: this.kfjypkForm.kfjypk
|
||
}).then(res => {
|
||
this.saving = false
|
||
this.kfjypkVisible = false
|
||
this.$message.success(`已更新 ${Number((res && res.data) || 0)} 条开放教员排课`)
|
||
this.loadList()
|
||
}).catch(() => {
|
||
this.saving = false
|
||
})
|
||
},
|
||
handleWaveMerge() {
|
||
if (this.selection.length < 2) {
|
||
this.$message.warning('请至少勾选两个班次')
|
||
return
|
||
}
|
||
this.$confirm(`将选中的 ${this.selection.length} 个班次设为同一预设编班号。只改编波,不改课次序号。`, '预设合班', {
|
||
type: 'warning'
|
||
}).then(() => batchWaveMerge({ bhList: this.selection.map(r => r.bh) })).then(res => {
|
||
this.$message.success(`已合班 ${Number((res && res.data) || 0)} 个班次`)
|
||
this.loadList()
|
||
}).catch(() => {})
|
||
},
|
||
handleWaveSplit() {
|
||
if (!this.selection.length) return
|
||
this.$confirm(`将选中的 ${this.selection.length} 个班次各自独立成波。只改编波,不改课次序号。`, '预设拆班', {
|
||
type: 'warning'
|
||
}).then(() => batchWaveSplit({ bhList: this.selection.map(r => r.bh) })).then(res => {
|
||
this.$message.success(`已拆班 ${Number((res && res.data) || 0)} 个班次`)
|
||
this.loadList()
|
||
}).catch(() => {})
|
||
},
|
||
|
||
/* ==================== 快速设定学期日期 ==================== */
|
||
handleQuickSetDates() {
|
||
if (!this.selection.length) {
|
||
this.$message.warning('请先勾选要设定日期的班次学期')
|
||
return
|
||
}
|
||
this.dateRangeForm = {
|
||
kxrq: this.presetDates.kxrq || '',
|
||
jsrq: this.presetDates.jsrq || ''
|
||
}
|
||
this.dateRangeVisible = true
|
||
this.$nextTick(() => {
|
||
if (this.$refs.dateRangeFormRef) this.$refs.dateRangeFormRef.clearValidate()
|
||
})
|
||
},
|
||
handleDateRangeSubmit() {
|
||
this.$refs.dateRangeFormRef.validate(valid => {
|
||
if (!valid) return
|
||
const f = this.dateRangeForm
|
||
this.saving = true
|
||
batchUpdateDateRange({
|
||
bhList: this.selection.map(r => r.bh),
|
||
xqkssj: f.kxrq,
|
||
xqjssj: f.jsrq
|
||
}).then(res => {
|
||
this.saving = false
|
||
this.dateRangeVisible = false
|
||
const count = (res && res.data) || 0
|
||
this.$message.success(`已更新 ${count} 条班次学期的日期`)
|
||
this.loadList()
|
||
}).catch(() => {
|
||
this.saving = false
|
||
})
|
||
})
|
||
},
|
||
|
||
/* ==================== 批量设置教学任务 ==================== */
|
||
handleSetJxrw() {
|
||
if (!this.selection.length) {
|
||
this.$message.warning('请先勾选要设置教学任务的班次学期')
|
||
return
|
||
}
|
||
this.jxrwForm = { jxrwbh: undefined }
|
||
this.jxrwVisible = true
|
||
this.$nextTick(() => {
|
||
if (this.$refs.jxrwFormRef) this.$refs.jxrwFormRef.clearValidate()
|
||
})
|
||
},
|
||
handleJxrwSubmit() {
|
||
this.$refs.jxrwFormRef.validate(valid => {
|
||
if (!valid) return
|
||
this.saving = true
|
||
batchUpdateJxrwbh({
|
||
bhList: this.selection.map(r => r.bh),
|
||
jxrwbh: this.jxrwForm.jxrwbh
|
||
}).then(res => {
|
||
this.saving = false
|
||
this.jxrwVisible = false
|
||
const count = (res && res.data) || 0
|
||
this.$message.success(`已更新 ${count} 条班次学期的教学任务`)
|
||
this.loadList()
|
||
}).catch(() => {
|
||
this.saving = false
|
||
})
|
||
})
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style scoped lang="scss">
|
||
.app-container {
|
||
padding: 20px;
|
||
}
|
||
|
||
.shift-semester-page {
|
||
// ========== 页面标题 ==========
|
||
.page-title {
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
color: #303133;
|
||
margin-bottom: 12px;
|
||
padding-left: 10px;
|
||
border-left: 4px solid var(--edu-green-primary);
|
||
}
|
||
|
||
// ========== 1. 查询条件区域 ==========
|
||
.search-card {
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
// ========== 2. 数据表格区 ==========
|
||
.table-card {
|
||
.list-header {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
flex-wrap: wrap;
|
||
gap: 12px;
|
||
margin-bottom: 16px;
|
||
|
||
.list-title {
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
color: #303133;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.list-actions {
|
||
display: flex;
|
||
align-items: center;
|
||
flex-wrap: wrap;
|
||
justify-content: flex-end;
|
||
gap: 8px;
|
||
|
||
.el-button + .el-button {
|
||
margin-left: 0;
|
||
}
|
||
}
|
||
}
|
||
|
||
.class-table {
|
||
width: 100%;
|
||
}
|
||
|
||
// ========== 操作列:按钮分两行(三列网格)显示 ==========
|
||
.op-btn-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(3, 1fr);
|
||
justify-items: center;
|
||
row-gap: 2px;
|
||
|
||
.el-button {
|
||
margin-left: 0;
|
||
}
|
||
}
|
||
|
||
.text-danger {
|
||
color: #f56c6c;
|
||
}
|
||
|
||
.list-pagination {
|
||
margin-top: 12px;
|
||
text-align: right;
|
||
}
|
||
|
||
::v-deep .wave-row-1 td { background: #f0f9eb !important; }
|
||
::v-deep .wave-row-2 td { background: #ecf5ff !important; }
|
||
::v-deep .wave-row-3 td { background: #fdf6ec !important; }
|
||
::v-deep .wave-row-4 td { background: #fef0f0 !important; }
|
||
::v-deep .wave-row-5 td { background: #f4f4f5 !important; }
|
||
::v-deep .wave-row-6 td { background: #f0f9ff !important; }
|
||
::v-deep .wave-row-7 td { background: #f9f0ff !important; }
|
||
::v-deep .wave-row-0 td { background: #fff7e6 !important; }
|
||
}
|
||
}
|
||
</style>
|