Files
education/frontend/src/views/teachOffice/subject/index.vue
T

745 lines
26 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="app-container management-page subject-page">
<!-- ==================== 2. 查询条件区域 ==================== -->
<el-card shadow="never" class="search-card">
<div class="section-heading">
<div>
<h2 class="section-title">筛选条件</h2>
<p class="section-description">按课程名称代码和培训属性筛选课程科目</p>
</div>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">查询</el-button>
</div>
<el-form :model="searchForm">
<el-row :gutter="32">
<!-- 左栏 -->
<el-col :xs="24" :md="12">
<div class="query-field">
<span class="q-label no-check">课名称</span>
<el-input v-model="searchForm.kmc" placeholder="请输入课名称" clearable class="q-control" />
</div>
<div class="query-field">
<span class="q-label no-check">科目代码</span>
<el-input v-model="searchForm.kmdm" placeholder="请输入科目代码" clearable class="q-control" />
</div>
<div class="query-field">
<span class="q-label no-check">培训层次</span>
<el-input v-model="searchForm.pxcc" placeholder="请输入培训层次" clearable class="q-control" />
</div>
<div class="query-field">
<span class="q-label no-check">课程类型</span>
<el-input v-model="searchForm.kclx" placeholder="请输入课程类型" clearable class="q-control" />
</div>
</el-col>
<!-- 右栏 -->
<el-col :xs="24" :md="12">
<div class="query-field">
<span class="q-label no-check">简称</span>
<el-input v-model="searchForm.jc" placeholder="请输入简称" clearable class="q-control" />
</div>
<div class="query-field">
<span class="q-label no-check">教研室代号</span>
<el-input v-model="searchForm.jysdh" placeholder="请输入教研室代号" clearable class="q-control" />
</div>
<div class="query-field">
<span class="q-label no-check">培训类型</span>
<el-input v-model="searchForm.pxlx" placeholder="请输入培训类型" clearable class="q-control" />
</div>
</el-col>
</el-row>
</el-form>
</el-card>
<!-- ==================== 3. 操作按钮区域 ==================== -->
<el-card shadow="never" class="action-card">
<div class="section-heading section-heading--compact">
<div>
<h2 class="section-title">课程科目操作</h2>
<p class="section-description">维护课程科目或下载业务数据与导入模板</p>
</div>
</div>
<div class="action-row">
<el-button type="primary" icon="el-icon-plus" @click="handleOpenDialog">新建课程科目</el-button>
<el-button type="danger" plain icon="el-icon-delete" @click="handleBatchDelete">删除所选</el-button>
<el-button icon="el-icon-download" @click="handleDownloadCourse">下载课程科目基本信息</el-button>
<el-button icon="el-icon-download" @click="handleDownloadTextbook">下载课程教材基本信息</el-button>
</div>
<div class="action-row">
<el-button icon="el-icon-download" @click="handleDownloadTemplate">课程科目数据文件模板下载</el-button>
</div>
</el-card>
<!-- ==================== 4. 文件上传区域 ==================== -->
<el-card shadow="never" class="upload-card">
<div class="section-heading section-heading--compact">
<div>
<h2 class="section-title">数据导入</h2>
<p class="section-description">选择填写完成的课程科目模板并上传</p>
</div>
</div>
<div class="upload-row">
<div class="upload-left">
<el-button icon="el-icon-folder-opened" @click="handleSelectFile">选择文件</el-button>
<span class="file-name" :class="{ 'has-file': selectedFile }">{{ fileName }}</span>
<input ref="fileInputRef" type="file" style="display: none" @change="handleFileChange" />
</div>
<div class="upload-right">
<el-checkbox v-model="coverExist">覆盖已存在课程科目</el-checkbox>
<el-button type="primary" icon="el-icon-upload2" @click="handleUpload">上传数据</el-button>
</div>
</div>
</el-card>
<!-- ==================== 6. 数据表格区域 ==================== -->
<el-card shadow="never" class="table-card">
<div class="section-heading section-heading--compact table-heading">
<div>
<h2 class="section-title">课程科目列表</h2>
<p class="section-description"> {{ total }} 条数据</p>
</div>
</div>
<el-table v-loading="loading" :data="tableData" stripe border highlight-current-row
@selection-change="handleSelectionChange">
<el-table-column type="selection" width="40" align="center" />
<el-table-column prop="kbh" label="课编号" width="130" show-overflow-tooltip />
<el-table-column prop="kmc" label="课名称" width="200" show-overflow-tooltip />
<el-table-column prop="jc" label="简称" width="130" show-overflow-tooltip />
<el-table-column prop="jysdh" label="教研室代号" width="150" show-overflow-tooltip />
<el-table-column prop="kclx" label="课程类型" width="150" align="center" />
<el-table-column prop="pxlx" label="培训类型" width="150" align="center" />
<el-table-column prop="pxcc" label="培训层次" width="200" align="center" />
<el-table-column prop="xf" label="学分" align="center" />
<el-table-column prop="xs" label="学时" width="160" align="center" />
<el-table-column label="操作" width="150" align="center" fixed="right">
<template slot-scope="{ row }">
<el-button type="text" size="small" @click="handleEdit(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-wrapper"
@current-change="handlePageChange"
@size-change="handleSizeChange"
/>
</el-card>
<!-- 新增/编辑课程科目弹窗 -->
<el-dialog
:visible="dialogVisible"
:title="dialogTitle"
width="1000px"
:close-on-click-modal="false"
class="add-dialog"
@update:visible="val => dialogVisible = val"
>
<el-form ref="addFormRef" :model="addForm" :rules="rules" label-width="120px" class="add-form" :status-icon="false">
<el-row :gutter="24">
<el-col :xs="24" :sm="12" :md="8">
<el-form-item label="课编号" prop="kbh">
<el-input v-model="addForm.kbh" :disabled="isEdit" placeholder="新增时留空自动生成" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8">
<el-form-item label="课名称" prop="kmc">
<el-input v-model="addForm.kmc" placeholder="请输入课名称" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8">
<el-form-item label="简称" prop="jc">
<el-input v-model="addForm.jc" placeholder="请输入简称" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8">
<el-form-item label="教研室代号" prop="jysdh">
<el-input v-model="addForm.jysdh" placeholder="请输入教研室代号" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8">
<el-form-item label="学分" prop="xf">
<el-input-number v-model="addForm.xf" :min="0" :precision="1" controls-position="right" class="w-full" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8">
<el-form-item label="学时" prop="xs">
<el-input-number v-model="addForm.xs" :min="0" controls-position="right" class="w-full" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8">
<el-form-item label="培训层次" prop="pxcc">
<el-input v-model="addForm.pxcc" placeholder="请输入培训层次" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8">
<el-form-item label="培训类型" prop="pxlx">
<el-input v-model="addForm.pxlx" placeholder="请输入培训类型" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8">
<el-form-item label="课程类型" prop="kclx">
<el-input v-model="addForm.kclx" placeholder="请输入课程类型" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8">
<el-form-item label="考试课时" prop="ksks">
<el-input-number v-model="addForm.ksks" :min="0" controls-position="right" class="w-full" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8">
<el-form-item label="成绩分制" prop="cjfz">
<el-input v-model="addForm.cjfz" placeholder="请输入成绩分制" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8">
<el-form-item label="不计入学员平均分" prop="bjrxypjf">
<el-select v-model="addForm.bjrxypjf" class="w-full">
<el-option label="否" :value="0" />
<el-option label="是" :value="1" />
</el-select>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8">
<el-form-item label="理论学时" prop="llxs">
<el-input-number v-model="addForm.llxs" :min="0" controls-position="right" class="w-full" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8">
<el-form-item label="实践学时" prop="sjxs">
<el-input-number v-model="addForm.sjxs" :min="0" controls-position="right" class="w-full" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8">
<el-form-item label="周课时" prop="zks">
<el-input-number v-model="addForm.zks" :min="0" controls-position="right" class="w-full" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8">
<el-form-item label="课类型" prop="klx">
<el-input v-model="addForm.klx" placeholder="请输入课类型" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8">
<el-form-item label="考试课时不显示" prop="ksksbxs">
<el-select v-model="addForm.ksksbxs" class="w-full">
<el-option label="否" :value="0" />
<el-option label="是" :value="1" />
</el-select>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8">
<el-form-item label="课程统一编号" prop="kctybh">
<el-input v-model="addForm.kctybh" placeholder="请输入课程统一编号" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8">
<el-form-item label="辅导答疑课时" prop="fddyks">
<el-input-number v-model="addForm.fddyks" :min="0" controls-position="right" class="w-full" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8">
<el-form-item label="自修课时" prop="zxks">
<el-input-number v-model="addForm.zxks" :min="0" controls-position="right" class="w-full" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8">
<el-form-item label="修订日期" prop="xdrq">
<el-input v-model="addForm.xdrq" placeholder="如 2026-08-24" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8">
<el-form-item label="节次优选级" prop="jcyxj">
<el-input v-model="addForm.jcyxj" placeholder="请输入节次优选级" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8">
<el-form-item label="课程支持方式" prop="kczcfs">
<el-input v-model="addForm.kczcfs" placeholder="请输入课程支持方式" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8">
<el-form-item label="课程建设方式" prop="kcjsfs">
<el-input v-model="addForm.kcjsfs" placeholder="请输入课程建设方式" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8">
<el-form-item label="政治类课程" prop="zzlkc">
<el-select v-model="addForm.zzlkc" class="w-full">
<el-option label="否" :value="0" />
<el-option label="是" :value="1" />
</el-select>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8">
<el-form-item label="考核方式" prop="khfs">
<el-input v-model="addForm.khfs" placeholder="请输入考核方式" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8">
<el-form-item label="形成性成绩分值" prop="xcxcjfz">
<el-input-number v-model="addForm.xcxcjfz" :min="0" controls-position="right" class="w-full" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8">
<el-form-item label="终结性成绩分值" prop="zjxcjfz">
<el-input-number v-model="addForm.zjxcjfz" :min="0" controls-position="right" class="w-full" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8">
<el-form-item label="密级" prop="mj">
<el-input v-model="addForm.mj" placeholder="请输入密级" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8">
<el-form-item label="评选情况" prop="pxqk">
<el-input v-model="addForm.pxqk" placeholder="请输入评选情况" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24">
<el-form-item label="备注">
<el-input v-model="addForm.bz" type="textarea" :rows="2" placeholder="请输入备注" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<div slot="footer">
<div class="dialog-footer">
<el-button @click="handleCancel">取消</el-button>
<el-button type="primary" :loading="addLoading" @click="handleSubmit">保存</el-button>
</div>
</div>
</el-dialog>
</div>
</template>
<script>
import { listKb as listSubject, addKb as addSubject, updateKb as updateSubject, deleteKb as deleteSubject, getKb } from '@/api/teachOffice/kb'
export default {
name: 'SubjectIndex',
data() {
return {
// ==================== 1. 顶部状态提示 ====================
statusTip: '请输入查询条件',
// ==================== 2. 查询条件 ====================
searchForm: {
kmc: '',
jc: '',
kmdm: '',
jysdh: '',
kclx: '',
pxlx: '',
pxcc: ''
},
// ==================== 列表数据 ====================
loading: false,
tableData: [],
total: 0,
pageNum: 1,
pageSize: 20,
selectedRows: [],
// ==================== 文件上传 ====================
selectedFile: null,
coverExist: false,
// ==================== 新增/编辑弹窗 ====================
dialogVisible: false,
addLoading: false,
isEdit: false,
addForm: this.createEmptyForm(),
rules: {
kmc: [{ required: true, message: '请输入课名称', trigger: 'blur' }],
jysdh: [{ required: true, message: '请输入教研室代号', trigger: 'blur' }],
xf: [{ required: true, message: '请输入学分', trigger: 'blur' }],
pxcc: [{ required: true, message: '请输入培训层次', trigger: 'blur' }],
pxlx: [{ required: true, message: '请输入培训类型', trigger: 'blur' }],
kclx: [{ required: true, message: '请输入课程类型', trigger: 'blur' }],
ksks: [{ required: true, message: '请输入考试课时', trigger: 'blur' }],
cjfz: [{ required: true, message: '请输入成绩分制', trigger: 'blur' }],
llxs: [{ required: true, message: '请输入理论学时', trigger: 'blur' }],
sjxs: [{ required: true, message: '请输入实践学时', trigger: 'blur' }],
zks: [{ required: true, message: '请输入周课时', trigger: 'blur' }],
klx: [{ required: true, message: '请输入课类型', trigger: 'blur' }],
kctybh: [{ required: true, message: '请输入课程统一编号', trigger: 'blur' }],
fddyks: [{ required: true, message: '请输入辅导答疑课时', trigger: 'blur' }],
zxks: [{ required: true, message: '请输入自修课时', trigger: 'blur' }],
xdrq: [{ required: true, message: '请输入修订日期', trigger: 'blur' }],
jcyxj: [{ required: true, message: '请输入节次优选级', trigger: 'blur' }],
kczcfs: [{ required: true, message: '请输入课程支持方式', trigger: 'blur' }],
kcjsfs: [{ required: true, message: '请输入课程建设方式', trigger: 'blur' }],
khfs: [{ required: true, message: '请输入考核方式', trigger: 'blur' }],
xcxcjfz: [{ required: true, message: '请输入形成性成绩分值', trigger: 'blur' }],
zjxcjfz: [{ required: true, message: '请输入终结性成绩分值', trigger: 'blur' }],
mj: [{ required: true, message: '请输入密级', trigger: 'blur' }],
pxqk: [{ required: true, message: '请输入评选情况', trigger: 'blur' }]
}
}
},
computed: {
dialogTitle() {
return this.isEdit ? '编辑课程科目' : '新增课程科目'
},
fileName() {
return (this.selectedFile && this.selectedFile.name) || '未选择任何文件'
}
},
mounted() {
this.fetchList()
},
methods: {
/** 移除空值 */
cleanPayload(obj) {
const payload = {}
Object.keys(obj).forEach(key => {
const value = obj[key]
if (value !== '' && value !== null && value !== undefined) {
payload[key] = value
}
})
return payload
},
/** 创建空白课程科目表单 */
createEmptyForm() {
return {
kbh: '', kmc: '', jysdh: '', bz: '', xh: '', xf: 0, jc: '', xs: 0,
pxcc: '', pxlx: '', kclx: '', ksks: 0, py: '', gfmc: '', cjfz: '',
bjrxypjf: 0, jxglbmbh: '', llxs: 0, sjxs: 0, kmdm: '', zks: 0, klx: '',
ksksbxs: 0, kctybh: '', sydx: '', fddyks: 0, zxks: 0, bb: '', xdrq: '',
jcyxj: '', kczcfs: '', kcjsfs: '', jsonzd: '', zzlkc: 0, khfs: '',
xcxcjfz: 0, zjxcjfz: 0, mj: '', pxqk: ''
}
},
resetForm() {
this.addForm = this.createEmptyForm()
},
// ==================== 列表加载 ====================
buildQuery() {
return this.cleanPayload({
pageNum: this.pageNum,
pageSize: this.pageSize,
kmc: this.searchForm.kmc,
jc: this.searchForm.jc,
kmdm: this.searchForm.kmdm,
jysdh: this.searchForm.jysdh,
kclx: this.searchForm.kclx,
pxlx: this.searchForm.pxlx,
pxcc: this.searchForm.pxcc
})
},
fetchList() {
this.loading = true
listSubject(this.buildQuery())
.then(res => {
const data = (res && res.data) || {}
this.tableData = data.records || []
this.total = data.total || 0
this.statusTip = `共检索到 ${this.total} 条记录`
})
.finally(() => {
this.loading = false
})
},
// ==================== 查询 ====================
handleQuery() {
this.pageNum = 1
this.fetchList()
},
handlePageChange(current) {
this.pageNum = current
this.fetchList()
},
handleSizeChange(size) {
this.pageSize = size
this.pageNum = 1
this.fetchList()
},
handleSelectionChange(rows) {
this.selectedRows = rows
},
// ==================== 删除所选 ====================
handleBatchDelete() {
if (this.selectedRows.length === 0) {
this.$message.warning('请先选择要删除的记录')
return
}
const kbhList = this.selectedRows.map(r => r.kbh)
this.$confirm(`确定删除所选 ${kbhList.length} 条课程科目吗?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
const tasks = kbhList.map(kbh => deleteSubject(kbh))
Promise.all(tasks)
.then(() => {
this.$message.success(`已删除 ${kbhList.length} 条记录`)
this.fetchList()
})
.catch(() => {
this.$message.error('删除失败')
})
}).catch(() => {})
},
/** 单条删除 */
handleDelete(row) {
this.$confirm(`确定删除课程科目「${row.kmc || row.kbh}」吗?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
return deleteSubject(row.kbh)
}).then(() => {
this.$message.success('删除成功')
this.fetchList()
}).catch((err) => {
if (err && err !== 'cancel') {
this.$message.error('删除失败')
}
})
},
// ==================== 下载(后端暂未提供) ====================
handleDownloadCourse() {
this.$message.warning('后端暂未提供该接口')
},
handleDownloadTextbook() {
this.$message.warning('后端暂未提供该接口')
},
handleDownloadTemplate() {
this.$message.warning('后端暂未提供该接口')
},
// ==================== 文件上传(后端暂未提供) ====================
handleSelectFile() {
this.$refs.fileInputRef && this.$refs.fileInputRef.click()
},
handleFileChange(e) {
const input = e.target
this.selectedFile = (input.files && input.files[0]) || null
},
handleUpload() {
this.$message.warning('后端暂未提供该接口')
},
// ==================== 新增/编辑弹窗 ====================
handleOpenDialog() {
this.isEdit = false
this.resetForm()
this.dialogVisible = true
},
handleEdit(row) {
this.addLoading = true
getKb(row.kbh)
.then(res => {
const data = (res && res.data) || row
this.isEdit = true
this.addForm = Object.assign(this.createEmptyForm(), data)
this.dialogVisible = true
})
.finally(() => {
this.addLoading = false
})
},
handleSubmit() {
this.$refs.addFormRef.validate((valid) => {
if (!valid) return
this.addLoading = true
const payload = this.cleanPayload({ ...this.addForm })
const requestFn = this.isEdit ? updateSubject : addSubject
requestFn(payload)
.then(() => {
this.$message.success(this.isEdit ? '修改成功' : '保存成功')
this.dialogVisible = false
this.fetchList()
})
.finally(() => {
this.addLoading = false
})
})
},
handleCancel() {
this.dialogVisible = false
}
}
}
</script>
<style scoped lang="scss">
.app-container {
padding: 20px;
}
// ==================== 1. 顶部状态提示 ====================
.status-tip {
display: inline-block;
background: #dc3545;
color: #fff;
font-size: 12px;
padding: 3px 10px;
border-radius: 4px;
margin-bottom: 12px;
}
// ==================== 2. 查询条件区域 ====================
.search-card {
margin-bottom: 16px;
.query-field {
display: flex;
align-items: center;
margin-bottom: 14px;
min-height: 32px;
::v-deep(.el-checkbox) {
margin-right: 6px;
flex-shrink: 0;
}
.q-label {
width: 120px;
flex-shrink: 0;
font-size: 13px;
color: #303133;
text-align: right;
margin-right: 10px;
white-space: nowrap;
&.no-check {
margin-left: 24px;
}
}
.q-control {
flex: 1;
min-width: 0;
}
}
.notice {
font-size: 12px;
color: #f56c6c;
margin-top: 8px;
text-align: left;
}
}
// ==================== 3. 操作按钮区域 ====================
.action-card {
margin-bottom: 16px;
.action-row {
display: flex;
flex-wrap: wrap;
gap: 12px;
margin-bottom: 12px;
&:last-child {
margin-bottom: 0;
}
}
}
// ==================== 4. 文件上传区域 ====================
.upload-card {
margin-bottom: 16px;
.upload-row {
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
gap: 12px;
.upload-left {
display: flex;
align-items: center;
gap: 12px;
.file-name {
font-size: 12px;
color: #999;
&.has-file {
color: #303133;
}
}
}
.upload-right {
display: flex;
align-items: center;
gap: 12px;
}
}
}
// ==================== 5. 红色提示文字 ====================
.red-tip {
font-size: 14px;
color: #f56c6c;
margin-bottom: 16px;
line-height: 1.6;
}
// ==================== 6. 数据表格区域 ====================
.table-card {
.pagination-wrapper {
display: flex;
justify-content: flex-end;
margin-top: 16px;
}
::v-deep(.el-table .cell) {
font-size: 12px;
}
.text-danger {
color: #f56c6c;
}
}
// ==================== 新增/编辑弹窗 ====================
.add-dialog {
::v-deep(.el-dialog__body) {
max-height: 70vh;
overflow: auto;
}
}
.add-form {
.w-full {
width: 100%;
}
}
.dialog-footer {
display: flex;
justify-content: flex-end;
gap: 12px;
}
</style>