Files
education/frontend/src/views/subjectMajor/discipline/index.vue
T

621 lines
22 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 discipline-page">
<!-- ==================== 1. 查询条件 ==================== -->
<el-card shadow="never" class="search-card">
<el-form ref="searchFormRef" :model="searchForm" label-width="90px" class="search-form">
<el-row :gutter="24">
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
<el-form-item label="专业名称">
<el-input v-model="searchForm.zymc" placeholder="请输入专业名称" clearable @keyup.enter.native="handleSearch" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
<el-form-item label="专业代码">
<el-input v-model="searchForm.zydm" placeholder="请输入专业代码" clearable @keyup.enter.native="handleSearch" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
<el-form-item label="专业方向">
<el-input v-model="searchForm.zyfx" placeholder="请输入专业方向" clearable @keyup.enter.native="handleSearch" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
<el-form-item label="学年制">
<el-input v-model="searchForm.xnz" placeholder="请输入学年制" clearable @keyup.enter.native="handleSearch" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
<el-form-item label="停用" class="checkbox-form-item">
<el-radio-group v-model="searchForm.ty">
<el-radio :label="false">否</el-radio>
<el-radio :label="true">是</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
<el-form-item label="培训类型2">
<el-select v-model="searchForm.pxlx2" placeholder="请选择" clearable class="w-full">
<el-option label="类型1" value="1" />
<el-option label="类型2" value="2" />
<el-option label="其他" value="3" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24" class="search-actions">
<div class="search-buttons">
<el-button type="primary" @click="handleSearch">查询</el-button>
<el-button @click="handleReset">重置</el-button>
</div>
</el-col>
</el-row>
</el-form>
</el-card>
<!-- ==================== 2. 列表 ==================== -->
<el-card shadow="never" class="table-card">
<div class="table-header">
<span class="table-title">学科专业列表:</span>
<el-button type="primary" icon="el-icon-plus" @click="handleAdd">新建学科专业</el-button>
</div>
<el-table v-loading="loading" :data="tableData" stripe border highlight-current-row>
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="序号" width="60" align="center">
<template slot-scope="scope">{{ fmtXh(scope.$index) }}</template>
</el-table-column>
<el-table-column prop="jxgljgbh" label="教学管理机构" width="200" show-overflow-tooltip />
<el-table-column prop="zydm" label="专业代码" width="200" />
<el-table-column prop="zymc" label="专业名称" width="200" show-overflow-tooltip />
<el-table-column prop="zdyfl" label="分类" width="150" align="center" />
<el-table-column prop="xylb" label="学员类别" width="150" align="center" />
<el-table-column prop="xnz" label="学年制" width="120" align="center" />
<el-table-column prop="xqs" label="学期数" width="120" align="center" />
<el-table-column prop="zgzy" label="主干专业" align="center" />
<el-table-column label="操作" width="230" align="center" fixed="right">
<template slot-scope="{ row }">
<el-button type="text" size="small" icon="el-icon-view" @click="handleDetail(row)">详情</el-button>
<el-button type="text" size="small" icon="el-icon-edit" @click="handleEdit(row)">编辑</el-button>
<el-button v-if="row.ty === 1" type="text" size="small" class="text-success" @click="handleEnable(row)">
启用
</el-button>
<el-button v-else type="text" size="small" class="text-danger" @click="handleDisable(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>
<!-- ==================== 3. 新增/编辑学科专业对话框 ==================== -->
<el-dialog
:visible="formDialogVisible"
:title="dialogTitle"
width="750px"
:close-on-click-modal="false"
@update:visible="val => formDialogVisible = val"
>
<el-form ref="formRef" :model="form" :rules="formRules" label-width="130px" class="add-form">
<el-row :gutter="16">
<el-col :span="12">
<el-form-item label="规范名称" prop="gfmc">
<el-input v-model="form.gfmc" placeholder="请输入规范名称" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16">
<el-col :span="12">
<el-form-item label="专业代码" prop="zydm">
<el-input v-model="form.zydm" placeholder="请输入专业代码" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="专业名称" prop="zymc">
<el-input v-model="form.zymc" placeholder="请输入专业名称" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16">
<el-col :span="12">
<el-form-item label="专业方向" prop="zyfx">
<el-input v-model="form.zyfx" placeholder="请输入专业方向" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="学年制" prop="xnz">
<el-input v-model="form.xnz" placeholder="请输入学年制" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16">
<el-col :span="12">
<el-form-item label="学期数" prop="xqs">
<el-input-number v-model="form.xqs" :min="0" controls-position="right" style="width: 100%" placeholder="请输入学期数" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="培训层次" prop="pxcc">
<el-select v-model="form.pxcc" placeholder="请选择培训层次" filterable class="w-full">
<el-option
v-for="item in trainingLevelOptions"
:key="item.dictCode || item.dictValue"
:label="item.dictLabel"
:value="item.dictLabel"
/>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16">
<el-col :span="12">
<el-form-item label="培训类型" prop="pxlx">
<el-select v-model="form.pxlx" placeholder="请选择培训类型" filterable class="w-full">
<el-option
v-for="item in trainingTypeOptions"
:key="item.dictCode || item.dictValue"
:label="item.dictLabel"
:value="item.dictLabel"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="培训类型2" prop="pxlx2">
<el-input v-model="form.pxlx2" placeholder="请输入培训类型2" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16">
<el-col :span="12">
<el-form-item label="教学管理机构编号" prop="jxgljgbh">
<el-input v-model="form.jxgljgbh" placeholder="请输入教学管理机构编号" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="学员类别" prop="xylb">
<el-select v-model="form.xylb" placeholder="请选择学员类别" filterable class="w-full">
<el-option
v-for="item in studentCategoryOptions"
:key="item.dictCode || item.dictValue"
:label="item.dictLabel"
:value="item.dictLabel"
/>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16">
<el-col :span="12">
<el-form-item label="自定义分类" prop="zdyfl">
<el-input v-model="form.zdyfl" placeholder="请输入自定义分类" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="主干专业" prop="zgzy">
<el-input v-model="form.zgzy" placeholder="请输入主干专业" />
</el-form-item>
</el-col>
</el-row>
<el-form-item label="备注">
<el-input v-model="form.bz" type="textarea" :rows="3" placeholder="请输入备注" />
</el-form-item>
</el-form>
<div slot="footer">
<el-button @click="formDialogVisible = false">取消</el-button>
<el-button type="primary" :loading="formSaving" @click="handleFormSubmit">确定</el-button>
</div>
</el-dialog>
<!-- ==================== 4. 详情对话框 ==================== -->
<el-dialog title="学科专业详情" :visible="detailDialogVisible" width="700px" :close-on-click-modal="false"
@update:visible="val => detailDialogVisible = val">
<div v-loading="detailLoading" class="detail-body">
<el-descriptions :column="2" border>
<el-descriptions-item label="规范名称">{{ detailForm.gfmc || '-' }}</el-descriptions-item>
<el-descriptions-item label="专业代码">{{ detailForm.zydm || '-' }}</el-descriptions-item>
<el-descriptions-item label="专业名称">{{ detailForm.zymc || '-' }}</el-descriptions-item>
<el-descriptions-item label="专业方向">{{ detailForm.zyfx || '-' }}</el-descriptions-item>
<el-descriptions-item label="学年制">{{ detailForm.xnz || '-' }}</el-descriptions-item>
<el-descriptions-item label="学期数">{{ fmtValue(detailForm.xqs) }}</el-descriptions-item>
<el-descriptions-item label="培训层次">{{ detailForm.pxcc || '-' }}</el-descriptions-item>
<el-descriptions-item label="培训类型">{{ detailForm.pxlx || '-' }}</el-descriptions-item>
<el-descriptions-item label="培训类型2">{{ detailForm.pxlx2 || '-' }}</el-descriptions-item>
<el-descriptions-item label="教学管理机构编号">{{ detailForm.jxgljgbh || '-' }}</el-descriptions-item>
<el-descriptions-item label="学员类别">{{ detailForm.xylb || '-' }}</el-descriptions-item>
<el-descriptions-item label="自定义分类">{{ detailForm.zdyfl || '-' }}</el-descriptions-item>
<el-descriptions-item label="主干专业">{{ detailForm.zgzy || '-' }}</el-descriptions-item>
<el-descriptions-item label="停用">
<el-tag :type="detailForm.ty === 1 ? 'danger' : 'success'" size="mini">
{{ detailForm.ty === 1 ? '是' : '否' }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="停用时间">{{ detailForm.tysj || '-' }}</el-descriptions-item>
<el-descriptions-item label="备注" :span="2">{{ detailForm.bz || '-' }}</el-descriptions-item>
</el-descriptions>
</div>
<div slot="footer">
<el-button @click="detailDialogVisible = false">关闭</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import {
listDiscipline,
getDiscipline,
addDiscipline,
updateDiscipline,
disableDiscipline
} from '@/api/subjectMajor/discipline'
import { getDicts } from '@/api/system/dict/data'
import { optionselect } from '@/api/system/dict/type'
const TRAINING_TYPE_DICT_CODE = 'train_type'
const TRAINING_LEVEL_DICT_CODE = 'train_level'
export default {
name: 'DisciplineIndex',
data() {
return {
// ==================== 查询条件 ====================
searchForm: {
zymc: '',
zydm: '',
zyfx: '',
xnz: '',
ty: false,
pxlx2: ''
},
// ==================== 列表数据 ====================
loading: false,
tableData: [],
total: 0,
pageNum: 1,
pageSize: 20,
// ==================== 新增/编辑 ====================
formDialogVisible: false,
dialogTitle: '新增学科专业',
isAdd: true,
formSaving: false,
form: this.createEmptyForm(),
formRules: {
gfmc: [{ required: true, message: '请输入规范名称', trigger: 'blur' }],
zydm: [{ required: true, message: '请输入专业代码', trigger: 'blur' }],
zymc: [{ required: true, message: '请输入专业名称', trigger: 'blur' }],
zyfx: [{ required: true, message: '请输入专业方向', trigger: 'blur' }],
xnz: [{ required: true, message: '请输入学年制', trigger: 'blur' }],
xqs: [{ required: true, message: '请输入学期数', trigger: 'change' }],
pxcc: [{ required: true, message: '请选择培训层次', trigger: 'change' }],
pxlx: [{ required: true, message: '请选择培训类型', trigger: 'change' }],
pxlx2: [{ required: true, message: '请输入培训类型2', trigger: 'blur' }],
jxgljgbh: [{ required: true, message: '请输入教学管理机构编号', trigger: 'blur' }],
xylb: [{ required: true, message: '请选择学员类别', trigger: 'change' }],
zdyfl: [{ required: true, message: '请输入自定义分类', trigger: 'blur' }],
zgzy: [{ required: true, message: '请输入主干专业', trigger: 'blur' }]
},
trainingTypeOptions: [],
trainingLevelOptions: [],
studentCategoryOptions: [],
// ==================== 详情 ====================
detailDialogVisible: false,
detailLoading: false,
detailForm: {}
}
},
mounted() {
this.loadDisciplineDictionaries()
this.fetchList()
},
methods: {
/** 加载学科专业表单字典,业务字段统一提交字典标签。 */
async loadDisciplineDictionaries() {
const emptyResponse = { data: [] }
const [typeResponse, levelResponse, dictTypeResponse] = await Promise.all([
getDicts(TRAINING_TYPE_DICT_CODE).catch(() => emptyResponse),
getDicts(TRAINING_LEVEL_DICT_CODE).catch(() => emptyResponse),
optionselect().catch(() => emptyResponse)
])
this.trainingTypeOptions = typeResponse.data || []
this.trainingLevelOptions = levelResponse.data || []
this.studentCategoryOptions = await this.loadNamedDictionary(dictTypeResponse.data || [], '学员类别')
},
async loadNamedDictionary(dictTypes, dictName) {
const dictType = dictTypes.find(item => item.dictName === dictName) ||
dictTypes.find(item => String(item.dictName || '').includes(dictName))
if (!dictType || !dictType.dictType) {
return []
}
try {
const response = await getDicts(dictType.dictType)
return response.data || []
} catch (error) {
return []
}
},
/** 序号:按当前页数据索引生成跨页连续的序号 */
fmtXh(index) {
return (this.pageNum - 1) * this.pageSize + index + 1
},
createEmptyForm() {
return {
bsh: '',
gfmc: '',
zydm: '',
zymc: '',
zyfx: '',
xnz: '',
xqs: undefined,
pxcc: '',
pxlx: '',
pxlx2: '',
jxgljgbh: '',
xylb: '',
zdyfl: '',
jsonzd: '',
zgzy: '',
bz: ''
}
},
// ==================== 查询列表 ====================
fetchList() {
this.loading = true
const params = {
pageNum: this.pageNum,
pageSize: this.pageSize
}
params.ty = this.searchForm.ty ? 1 : 0
// 其余文本条件非空时才传
;['zymc', 'zydm', 'zyfx', 'xnz', 'pxlx2'].forEach(key => {
const value = this.searchForm[key]
if (value !== '' && value !== null && value !== undefined) {
params[key] = String(value).trim()
}
})
listDiscipline(params).then(response => {
const data = response.data || {}
this.tableData = data.records || []
this.total = data.total || 0
this.loading = false
}).catch(() => {
this.tableData = []
this.total = 0
this.loading = false
})
},
handleSearch() {
this.pageNum = 1
this.fetchList()
},
handleReset() {
this.$refs.searchFormRef.resetFields()
this.pageNum = 1
this.fetchList()
},
handlePageChange(current) {
this.pageNum = current
this.fetchList()
},
handleSizeChange(size) {
this.pageSize = size
this.pageNum = 1
this.fetchList()
},
// ==================== 新增/编辑 ====================
handleAdd() {
this.isAdd = true
this.dialogTitle = '新增学科专业'
this.form = this.createEmptyForm()
this.formDialogVisible = true
this.$nextTick(() => {
this.$refs.formRef && this.$refs.formRef.clearValidate()
})
},
handleEdit(row) {
this.isAdd = false
this.dialogTitle = '编辑学科专业'
this.form = this.createEmptyForm()
this.formDialogVisible = true
this.$nextTick(() => {
this.$refs.formRef && this.$refs.formRef.clearValidate()
})
getDiscipline(row.bsh).then(response => {
const data = response.data || {}
this.form = {
bsh: data.bsh || '',
gfmc: data.gfmc || '',
zydm: data.zydm || '',
zymc: data.zymc || '',
zyfx: data.zyfx || '',
xnz: data.xnz || '',
xqs: data.xqs !== null && data.xqs !== undefined ? data.xqs : undefined,
pxcc: data.pxcc || '',
pxlx: data.pxlx || '',
pxlx2: data.pxlx2 || '',
jxgljgbh: data.jxgljgbh || '',
xylb: data.xylb || '',
zdyfl: data.zdyfl || '',
jsonzd: data.jsonzd || '',
zgzy: data.zgzy || '',
bz: data.bz || ''
}
}).catch(() => {})
},
buildPayload() {
const f = this.form
const payload = {
bsh: f.bsh || undefined,
gfmc: f.gfmc,
zydm: f.zydm,
zymc: f.zymc,
zyfx: f.zyfx,
xnz: f.xnz,
pxcc: f.pxcc,
pxlx: f.pxlx,
pxlx2: f.pxlx2,
jxgljgbh: f.jxgljgbh,
xylb: f.xylb,
zdyfl: f.zdyfl,
zgzy: f.zgzy,
jsonzd: f.jsonzd,
bz: f.bz
}
// 学期数为数值字段,转为数字后再提交。
if (f.xqs !== '' && f.xqs !== null && f.xqs !== undefined) {
payload.xqs = Number(f.xqs)
}
return payload
},
handleFormSubmit() {
this.$refs.formRef.validate(valid => {
if (!valid) {
return
}
this.formSaving = true
const payload = this.buildPayload()
const request = this.isAdd ? addDiscipline(payload) : updateDiscipline(payload)
request.then(() => {
this.$message.success(this.isAdd ? '新增成功' : '修改成功')
this.formDialogVisible = false
this.fetchList()
}).catch(() => {
}).finally(() => {
this.formSaving = false
})
})
},
// ==================== 停用/启用 ====================
handleDisable(row) {
this.$confirm(`确定要停用「${row.zymc || row.zydm || '该专业'}」吗?`, '系统提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
return disableDiscipline(row.bsh)
}).then(() => {
this.$message.success('停用成功')
this.fetchList()
}).catch(() => {})
},
// 后端暂未提供启用接口,仅作提示
handleEnable(row) {
this.$message.info('后端暂未提供该接口')
},
// ==================== 详情 ====================
// 数值字段空值显示为 '-'(保留 0)
fmtValue(val) {
return val === null || val === undefined || val === '' ? '-' : val
},
handleDetail(row) {
this.detailDialogVisible = true
this.detailLoading = true
this.detailForm = {}
getDiscipline(row.bsh).then(response => {
this.detailForm = response.data || {}
this.detailLoading = false
}).catch(() => {
this.detailLoading = false
})
}
}
}
</script>
<style scoped lang="scss">
.discipline-page {
.search-form {
.w-full {
width: 100%;
}
.checkbox-form-item {
::v-deep(.el-form-item__content) {
display: flex;
align-items: center;
gap: 16px;
}
}
.search-actions {
display: flex;
align-items: center;
justify-content: space-between;
padding-top: 8px;
.search-tip {
font-size: 12px;
color: #f56c6c;
}
.search-buttons {
display: flex;
gap: 12px;
}
}
}
.table-card {
.table-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 16px;
.table-title {
font-size: 16px;
font-weight: 600;
color: #303133;
}
}
}
.add-form {
max-height: 60vh;
overflow-y: auto;
padding-right: 4px;
}
.text-success {
color: #67c23a;
padding: 0;
}
.text-danger {
color: #f56c6c;
padding: 0;
}
}
</style>