forked from liweijie/education
514 lines
17 KiB
Vue
514 lines
17 KiB
Vue
<template>
|
||
<div class="app-container task-plan-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.rwmc"
|
||
placeholder="请输入任务名称"
|
||
clearable
|
||
style="width: 180px"
|
||
@keyup.enter.native="handleQuery"
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item label="年度">
|
||
<el-select v-model="searchForm.nd" placeholder="请选择年度" clearable style="width: 140px">
|
||
<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.zt" placeholder="请选择状态" clearable style="width: 140px">
|
||
<el-option
|
||
v-for="opt in statusOptions"
|
||
:key="opt.value"
|
||
:label="opt.label"
|
||
:value="opt.value"
|
||
/>
|
||
</el-select>
|
||
</el-form-item>
|
||
<el-form-item>
|
||
<el-button type="primary" icon="el-icon-search" @click="handleQuery">查询</el-button>
|
||
</el-form-item>
|
||
</el-form>
|
||
</el-card>
|
||
|
||
<!-- ==================== 2. 操作区域 ==================== -->
|
||
<div class="list-toolbar">
|
||
<div class="left-group">
|
||
<el-button type="primary" icon="el-icon-plus" @click="handleAdd">新建教学任务</el-button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ==================== 3. 数据表格 ==================== -->
|
||
<el-card shadow="never" class="table-card">
|
||
<el-table v-loading="loading" :data="tableData" border stripe class="task-table">
|
||
<template slot="empty">
|
||
<span>无数据!</span>
|
||
</template>
|
||
<el-table-column type="index" label="序号" width="70" align="center" />
|
||
<el-table-column prop="rwmc" label="任务名称" width="250" align="left" header-align="center" show-overflow-tooltip />
|
||
<el-table-column prop="nd" label="年度" width="150" align="center" />
|
||
<el-table-column label="状态" width="100" align="center">
|
||
<template slot-scope="{ row }">
|
||
<el-tag :type="row.zt === '发布' ? 'success' : 'info'" size="small">
|
||
{{ row.zt || '-' }}
|
||
</el-tag>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="fbsj" label="发布时间" width="180" align="center" :formatter="fmtDateTime" />
|
||
<el-table-column prop="jssj" label="结束时间" width="180" align="center" :formatter="fmtDateTime" />
|
||
<el-table-column prop="cjsj" label="创建时间" width="180" align="center" :formatter="fmtDateTime" />
|
||
<el-table-column prop="jcxqscsj" label="教材需求生成时间" align="center" :formatter="fmtDateTime" />
|
||
<el-table-column label="操作" width="300" align="center" fixed="right">
|
||
<template slot-scope="{ row }">
|
||
<el-button type="text" size="small" @click="handleDetail(row)">详情</el-button>
|
||
<el-button type="text" size="small" @click="handleEdit(row)">编辑</el-button>
|
||
<el-button
|
||
type="text"
|
||
size="small"
|
||
:disabled="row.zt === '发布'"
|
||
@click="handlePublish(row)"
|
||
>发布</el-button>
|
||
<el-button type="text" size="small" class="text-danger" @click="handleDelete(row)">删除</el-button>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
|
||
<el-pagination
|
||
:current-page="pageNum"
|
||
:page-size="pageSize"
|
||
:total="total"
|
||
:page-sizes="[10, 20, 50, 100]"
|
||
layout="total, sizes, prev, pager, next, jumper"
|
||
class="pagination"
|
||
@current-change="handlePageChange"
|
||
@size-change="handleSizeChange"
|
||
/>
|
||
</el-card>
|
||
|
||
<!-- ==================== 4. 新增/修改对话框 ==================== -->
|
||
<el-dialog
|
||
:visible="dialogVisible"
|
||
:title="dialogTitle"
|
||
width="560px"
|
||
:close-on-click-modal="false"
|
||
@update:visible="val => dialogVisible = val"
|
||
>
|
||
<el-form ref="formRef" :model="form" :rules="rules" label-width="140px" class="add-form">
|
||
<el-form-item v-if="isEdit" label="编号" prop="bh">
|
||
<el-input v-model="form.bh" disabled placeholder="编号不可修改" />
|
||
</el-form-item>
|
||
<el-form-item v-else label="编号">
|
||
<el-input disabled placeholder="新增后由系统自动生成" />
|
||
</el-form-item>
|
||
<el-form-item label="任务名称" prop="rwmc">
|
||
<el-input v-model="form.rwmc" placeholder="请输入任务名称" clearable />
|
||
</el-form-item>
|
||
<el-form-item label="年度" prop="nd">
|
||
<el-select v-model="form.nd" placeholder="请选择年度" style="width: 100%">
|
||
<el-option v-for="y in yearOptions" :key="y" :label="y" :value="y" />
|
||
</el-select>
|
||
</el-form-item>
|
||
<el-form-item label="状态" prop="zt">
|
||
<el-select v-model="form.zt" placeholder="请选择状态" style="width: 100%">
|
||
<el-option
|
||
v-for="opt in statusOptions"
|
||
:key="opt.value"
|
||
:label="opt.label"
|
||
:value="opt.value"
|
||
/>
|
||
</el-select>
|
||
</el-form-item>
|
||
<el-form-item label="发布时间">
|
||
<el-date-picker
|
||
v-model="form.fbsj"
|
||
type="datetime"
|
||
value-format="yyyy-MM-ddTHH:mm:ss"
|
||
placeholder="请选择发布时间"
|
||
style="width: 100%"
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item label="结束时间">
|
||
<el-date-picker
|
||
v-model="form.jssj"
|
||
type="datetime"
|
||
value-format="yyyy-MM-ddTHH:mm:ss"
|
||
placeholder="请选择结束时间"
|
||
style="width: 100%"
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item label="教材需求生成时间">
|
||
<el-date-picker
|
||
v-model="form.jcxqscsj"
|
||
type="datetime"
|
||
value-format="yyyy-MM-ddTHH:mm:ss"
|
||
placeholder="请选择教材需求生成时间"
|
||
style="width: 100%"
|
||
/>
|
||
</el-form-item>
|
||
</el-form>
|
||
<div slot="footer">
|
||
<el-button @click="dialogVisible = false">取消</el-button>
|
||
<el-button type="primary" :loading="saving" @click="handleSubmit">确定</el-button>
|
||
</div>
|
||
</el-dialog>
|
||
|
||
<!-- ==================== 5. 详情对话框 ==================== -->
|
||
<el-dialog
|
||
:visible="detailVisible"
|
||
title="教学任务详情"
|
||
width="620px"
|
||
:close-on-click-modal="false"
|
||
@update:visible="val => detailVisible = val"
|
||
>
|
||
<el-descriptions v-if="detailData.bh" :column="2" border>
|
||
<el-descriptions-item label="编号">{{ detailData.bh || '-' }}</el-descriptions-item>
|
||
<el-descriptions-item label="任务名称">{{ detailData.rwmc || '-' }}</el-descriptions-item>
|
||
<el-descriptions-item label="年度">{{ fmtVal(detailData.nd) }}</el-descriptions-item>
|
||
<el-descriptions-item label="状态">{{ detailData.zt || '-' }}</el-descriptions-item>
|
||
<el-descriptions-item label="发布时间">{{ fmtVal(detailData.fbsj) }}</el-descriptions-item>
|
||
<el-descriptions-item label="结束时间">{{ fmtVal(detailData.jssj) }}</el-descriptions-item>
|
||
<el-descriptions-item label="创建时间">{{ fmtVal(detailData.cjsj) }}</el-descriptions-item>
|
||
<el-descriptions-item label="教材需求生成时间">{{ fmtVal(detailData.jcxqscsj) }}</el-descriptions-item>
|
||
</el-descriptions>
|
||
<div v-else v-loading="detailLoading" class="detail-empty">加载中...</div>
|
||
<div slot="footer">
|
||
<el-button @click="detailVisible = false">关闭</el-button>
|
||
</div>
|
||
</el-dialog>
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
/**
|
||
* 教学任务计划管理(完整管理页)
|
||
* 对应菜单:teachOffice/taskPlan
|
||
* 提供 teachingTask 全套真实接口操作:分页查询 list、详情 get?bh=、新增 add、编辑 update、
|
||
* 发布 batchPublish?bh=(需先填写教研室任务书)、删除 delete?bh=(级联删除教研室任务书)
|
||
* 只读查询视图在 src/views/teachBusiness/teachingTask/index.vue(教学任务列表)提供,避免重复操作同一实体
|
||
*/
|
||
import {
|
||
listTeachingTask,
|
||
getTeachingTask,
|
||
addTeachingTask,
|
||
updateTeachingTask,
|
||
deleteTeachingTask,
|
||
publishTeachingTask
|
||
} from '@/api/teachBusiness/teachingTask'
|
||
import { listAllSemester } from '@/api/teachBusiness/semester'
|
||
|
||
export default {
|
||
name: 'TaskPlan',
|
||
data() {
|
||
return {
|
||
// ==================== 1. 查询条件 ====================
|
||
searchForm: {
|
||
rwmc: '',
|
||
nd: undefined,
|
||
zt: ''
|
||
},
|
||
yearOptions: [],
|
||
statusOptions: [
|
||
{ label: '未发布', value: '未发布' },
|
||
{ label: '发布', value: '发布' }
|
||
],
|
||
|
||
// ==================== 2. 表格数据 ====================
|
||
loading: false,
|
||
tableData: [],
|
||
pageNum: 1,
|
||
pageSize: 10,
|
||
total: 0,
|
||
|
||
// ==================== 3. 新增/修改表单 ====================
|
||
dialogVisible: false,
|
||
dialogTitle: '新建教学任务',
|
||
isEdit: false,
|
||
saving: false,
|
||
form: this.createEmptyForm(),
|
||
rules: {
|
||
rwmc: [{ required: true, message: '请输入任务名称', trigger: 'blur' }],
|
||
nd: [{ required: true, message: '请选择年度', trigger: 'change' }],
|
||
zt: [{ required: true, message: '请选择状态', trigger: 'change' }]
|
||
},
|
||
|
||
// ==================== 4. 详情 ====================
|
||
detailVisible: false,
|
||
detailLoading: false,
|
||
detailData: {}
|
||
}
|
||
},
|
||
created() {
|
||
this.loadYearOptions().then(() => this.fetchList())
|
||
},
|
||
methods: {
|
||
/* ---------- 年度下拉(数据来自 /semester/all,禁止硬编码) ---------- */
|
||
loadYearOptions() {
|
||
return listAllSemester().then(response => {
|
||
const list = response.data || []
|
||
const map = {}
|
||
list.forEach(item => {
|
||
if (item.nd !== null && item.nd !== undefined && item.nd !== '') map[item.nd] = item
|
||
})
|
||
const arr = Object.keys(map).sort((a, b) => String(b).localeCompare(String(a)))
|
||
this.yearOptions = arr
|
||
// 默认年度:优先当前学期(dqxq=true),否则取最新年度
|
||
const current = list.find(item => item.dqxq === true)
|
||
const defaultNd = (current && current.nd) || arr[0]
|
||
if (!this.searchForm.nd) this.searchForm.nd = defaultNd
|
||
return arr
|
||
}).catch(() => {
|
||
this.yearOptions = []
|
||
return []
|
||
})
|
||
},
|
||
|
||
/* ---------- 通用格式化 ---------- */
|
||
fmtDateTime(row, column, cellValue) {
|
||
if (cellValue === null || cellValue === undefined || cellValue === '') return '-'
|
||
return String(cellValue).replace('T', ' ').slice(0, 16)
|
||
},
|
||
fmtVal(val) {
|
||
if (val === null || val === undefined || val === '') return '-'
|
||
return String(val).replace('T', ' ').slice(0, 16)
|
||
},
|
||
/** 移除空值(''/null/undefined),保留 0 等有效值 */
|
||
cleanPayload(obj) {
|
||
const payload = {}
|
||
Object.keys(obj).forEach(key => {
|
||
const value = obj[key]
|
||
if (value !== '' && value !== null && value !== undefined) {
|
||
payload[key] = value
|
||
}
|
||
})
|
||
return payload
|
||
},
|
||
|
||
/* ---------- 列表加载 ---------- */
|
||
fetchList() {
|
||
this.loading = true
|
||
const params = { pageNum: this.pageNum, pageSize: this.pageSize }
|
||
if (this.searchForm.rwmc && this.searchForm.rwmc.trim()) params.rwmc = this.searchForm.rwmc.trim()
|
||
if (this.searchForm.nd !== undefined && this.searchForm.nd !== null && this.searchForm.nd !== '') {
|
||
params.nd = this.searchForm.nd
|
||
}
|
||
if (this.searchForm.zt && this.searchForm.zt.trim()) params.zt = this.searchForm.zt.trim()
|
||
listTeachingTask(params).then(res => {
|
||
const data = (res && res.data) || {}
|
||
this.tableData = data.records || []
|
||
this.total = data.total || 0
|
||
this.loading = false
|
||
}).catch(() => {
|
||
this.tableData = []
|
||
this.total = 0
|
||
this.loading = false
|
||
})
|
||
},
|
||
|
||
handleQuery() {
|
||
this.pageNum = 1
|
||
this.fetchList()
|
||
},
|
||
|
||
handlePageChange(page) {
|
||
this.pageNum = page
|
||
this.fetchList()
|
||
},
|
||
|
||
handleSizeChange(size) {
|
||
this.pageSize = size
|
||
this.pageNum = 1
|
||
this.fetchList()
|
||
},
|
||
|
||
/* ---------- 新增 / 修改 ---------- */
|
||
createEmptyForm() {
|
||
return {
|
||
bh: '',
|
||
rwmc: '',
|
||
nd: undefined,
|
||
zt: '未发布',
|
||
fbsj: '',
|
||
jssj: '',
|
||
jcxqscsj: ''
|
||
}
|
||
},
|
||
|
||
handleAdd() {
|
||
this.form = this.createEmptyForm()
|
||
if (!this.form.nd && this.searchForm.nd) this.form.nd = this.searchForm.nd
|
||
this.isEdit = false
|
||
this.dialogTitle = '新建教学任务'
|
||
this.dialogVisible = true
|
||
this.$nextTick(() => {
|
||
this.$refs.formRef && this.$refs.formRef.clearValidate()
|
||
})
|
||
},
|
||
|
||
handleEdit(row) {
|
||
this.form = {
|
||
bh: row.bh || '',
|
||
rwmc: row.rwmc || '',
|
||
nd: row.nd,
|
||
zt: row.zt || '未发布',
|
||
fbsj: row.fbsj || '',
|
||
jssj: row.jssj || '',
|
||
jcxqscsj: row.jcxqscsj || ''
|
||
}
|
||
this.isEdit = true
|
||
this.dialogTitle = '修改教学任务'
|
||
this.dialogVisible = true
|
||
this.$nextTick(() => {
|
||
this.$refs.formRef && this.$refs.formRef.clearValidate()
|
||
})
|
||
},
|
||
|
||
handleSubmit() {
|
||
this.$refs.formRef.validate(valid => {
|
||
if (!valid) {
|
||
this.$message.warning('请完善必填项后再提交')
|
||
return
|
||
}
|
||
const payload = this.cleanPayload(this.buildPayload())
|
||
this.saving = true
|
||
const req = this.isEdit ? updateTeachingTask(payload) : addTeachingTask(payload)
|
||
req.then(() => {
|
||
this.$message.success(this.isEdit ? '修改成功' : '新建成功')
|
||
this.dialogVisible = false
|
||
this.fetchList()
|
||
}).catch(() => {}).finally(() => {
|
||
this.saving = false
|
||
})
|
||
})
|
||
},
|
||
|
||
/** 构建请求体:年度转数字以匹配后端 Integer,时间字段有值才传 */
|
||
buildPayload() {
|
||
const f = this.form
|
||
const payload = {}
|
||
if (this.isEdit && f.bh && String(f.bh).trim()) payload.bh = String(f.bh).trim()
|
||
if (f.rwmc && String(f.rwmc).trim()) payload.rwmc = String(f.rwmc).trim()
|
||
if (f.nd !== undefined && f.nd !== null && f.nd !== '') payload.nd = Number(f.nd)
|
||
if (f.zt && String(f.zt).trim()) payload.zt = String(f.zt).trim()
|
||
if (f.fbsj) payload.fbsj = f.fbsj
|
||
if (f.jssj) payload.jssj = f.jssj
|
||
if (f.jcxqscsj) payload.jcxqscsj = f.jcxqscsj
|
||
return payload
|
||
},
|
||
|
||
/* ---------- 详情 ---------- */
|
||
handleDetail(row) {
|
||
this.detailVisible = true
|
||
this.detailLoading = true
|
||
this.detailData = {}
|
||
getTeachingTask(row.bh).then(res => {
|
||
this.detailData = (res && res.data) || {}
|
||
this.detailLoading = false
|
||
}).catch(() => {
|
||
this.detailLoading = false
|
||
})
|
||
},
|
||
|
||
/* ---------- 删除 ---------- */
|
||
handleDelete(row) {
|
||
this.$confirm(`确定要删除「${row.rwmc || row.bh || '该记录'}」吗?删除将级联删除关联的教研室任务书。`, '系统提示', {
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: 'warning'
|
||
}).then(() => deleteTeachingTask(row.bh)).then(() => {
|
||
this.$message.success('删除成功')
|
||
this.fetchList()
|
||
}).catch(() => {})
|
||
},
|
||
|
||
/* ---------- 发布 ---------- */
|
||
handlePublish(row) {
|
||
this.$confirm(`确定要发布教学任务「${row.rwmc || row.bh || '该记录'}」吗?发布前请先填写关联的教研室任务书。`, '系统提示', {
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: 'warning'
|
||
}).then(() => publishTeachingTask(row.bh)).then(() => {
|
||
this.$message.success('发布成功')
|
||
this.fetchList()
|
||
}).catch(() => {})
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style scoped lang="scss">
|
||
.app-container {
|
||
padding: 20px;
|
||
}
|
||
|
||
.task-plan-page {
|
||
.page-title {
|
||
text-align: center;
|
||
font-size: 20px;
|
||
font-weight: 600;
|
||
color: #303133;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
// ========== 1. 查询条件区域 ==========
|
||
.search-card {
|
||
margin-bottom: 16px;
|
||
|
||
.search-form {
|
||
.el-form-item {
|
||
margin-bottom: 0;
|
||
margin-right: 24px;
|
||
}
|
||
}
|
||
}
|
||
|
||
// ========== 2. 工具栏 ==========
|
||
.list-toolbar {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
flex-wrap: wrap;
|
||
gap: 12px;
|
||
margin-bottom: 16px;
|
||
|
||
.left-group,
|
||
.right-group {
|
||
display: flex;
|
||
align-items: center;
|
||
flex-wrap: wrap;
|
||
gap: 10px;
|
||
}
|
||
}
|
||
|
||
// ========== 3. 数据表格区域 ==========
|
||
.table-card {
|
||
.task-table {
|
||
width: 100%;
|
||
}
|
||
|
||
.pagination {
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
margin-top: 12px;
|
||
}
|
||
|
||
.text-danger {
|
||
color: #f56c6c;
|
||
}
|
||
}
|
||
|
||
// ========== 4. 详情 ==========
|
||
.detail-empty {
|
||
min-height: 120px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
color: #909399;
|
||
}
|
||
}
|
||
</style>
|