forked from liweijie/education
808 lines
27 KiB
Vue
808 lines
27 KiB
Vue
<template>
|
||
<div class="app-container syllabus-page">
|
||
<!-- 查询条件 -->
|
||
<el-card shadow="never" class="search-card">
|
||
<el-form :model="searchForm" label-width="100px" class="search-form" @submit.native.prevent>
|
||
<el-row :gutter="32">
|
||
<el-col :xs="24" :md="8">
|
||
<el-form-item label="专业代号">
|
||
<el-input v-model="searchForm.zydh" placeholder="请输入专业代号" clearable />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :xs="24" :md="8">
|
||
<el-form-item label="停用">
|
||
<el-radio-group v-model="searchForm.ty">
|
||
<el-radio :label="0">否</el-radio>
|
||
<el-radio :label="1">是</el-radio>
|
||
</el-radio-group>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :xs="24" :md="8">
|
||
<div class="search-actions">
|
||
<el-button type="primary" icon="el-icon-search" @click="handleQuery">查询</el-button>
|
||
<el-button icon="el-icon-refresh" @click="handleReset">重置</el-button>
|
||
</div>
|
||
</el-col>
|
||
</el-row>
|
||
</el-form>
|
||
</el-card>
|
||
|
||
<!-- 数据列表 -->
|
||
<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="handleAdd">新增</el-button>
|
||
<el-button
|
||
v-if="Number(searchForm.ty) !== 1"
|
||
type="danger"
|
||
plain
|
||
icon="el-icon-circle-close"
|
||
:disabled="!activeSelection.length"
|
||
@click="handleBatchDelete"
|
||
>停用所选</el-button>
|
||
<el-button icon="el-icon-download" @click="handleDownload">下载</el-button>
|
||
<el-button icon="el-icon-download" @click="handleDownloadTemplate">下载模板</el-button>
|
||
<el-button icon="el-icon-upload2" @click="openImportDialog">导入</el-button>
|
||
</div>
|
||
</div>
|
||
<el-table v-loading="loading" :data="pagedData" border stripe highlight-current-row
|
||
@selection-change="handleSelectionChange">
|
||
<el-table-column type="selection" width="50" align="center" />
|
||
<el-table-column type="index" label="序号" width="50" align="center" />
|
||
<el-table-column prop="zydh" label="专业代号" width="150" align="center" />
|
||
<el-table-column prop="kbh" label="课编号" width="110" show-overflow-tooltip align="center" />
|
||
<el-table-column prop="jc" label="简称" width="100" show-overflow-tooltip align="center" />
|
||
<el-table-column prop="kcdw" label="课程定位" width="120" show-overflow-tooltip align="center" />
|
||
<el-table-column prop="mk" label="模块" width="100" show-overflow-tooltip align="center" />
|
||
<el-table-column prop="jysdh" label="教研室" width="100" show-overflow-tooltip align="center" />
|
||
<el-table-column prop="klx" label="课类型" width="80" align="center" />
|
||
<el-table-column prop="xqdc" label="学期第次" width="80" align="center" />
|
||
<el-table-column prop="xs" label="学时" width="70" align="center" />
|
||
<el-table-column prop="xf" label="学分" width="70" align="center" />
|
||
<el-table-column prop="llxs" label="理论学时" width="80" align="center" />
|
||
<el-table-column prop="sjxs" label="实践学时" width="80" align="center" />
|
||
<el-table-column prop="zks" label="周课时" width="70" align="center" />
|
||
<el-table-column prop="ksks" label="考试课时" width="80" align="center" />
|
||
<el-table-column prop="cjfz" label="成绩分制" width="90" align="center" />
|
||
<el-table-column label="不计入平均分" width="110" align="center">
|
||
<template slot-scope="scope">{{ fmtYesNo(scope.row.bjrxypjf) }}</template>
|
||
</el-table-column>
|
||
<el-table-column label="考试课时不显示" width="130" align="center">
|
||
<template slot-scope="scope">{{ fmtYesNo(scope.row.ksksbxs) }}</template>
|
||
</el-table-column>
|
||
<el-table-column label="大纲课程" width="90" align="center">
|
||
<template slot-scope="scope">{{ fmtYesNo(scope.row.dgkc) }}</template>
|
||
</el-table-column>
|
||
<el-table-column label="停用" width="80" align="center">
|
||
<template slot-scope="scope">
|
||
<el-tag :type="Number(scope.row.ty) === 1 ? 'danger' : 'success'" size="mini">
|
||
{{ Number(scope.row.ty) === 1 ? '停用' : '正常' }}
|
||
</el-tag>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="启用时间" width="150" align="center">
|
||
<template slot-scope="scope">{{ fmtDateTime(scope.row.qysj) }}</template>
|
||
</el-table-column>
|
||
<el-table-column label="停用时间" width="150" align="center">
|
||
<template slot-scope="scope">{{ fmtDateTime(scope.row.tysj) }}</template>
|
||
</el-table-column>
|
||
<el-table-column label="操作" width="160" align="center" fixed="right" class-name="table-action-column">
|
||
<template slot-scope="scope">
|
||
<el-button type="text" size="mini" icon="el-icon-edit" @click="handleEdit(scope.row)">编辑</el-button>
|
||
<el-button
|
||
v-if="!isDisabled(scope.row)"
|
||
type="text"
|
||
size="mini"
|
||
icon="el-icon-circle-close"
|
||
class="danger-text-btn"
|
||
@click="handleDelete(scope.row)"
|
||
>停用</el-button>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
<el-pagination
|
||
class="pagination"
|
||
background
|
||
layout="total, sizes, prev, pager, next"
|
||
:current-page="pageNum"
|
||
:page-size="pageSize"
|
||
:total="total"
|
||
:page-sizes="[10, 20, 50, 100]"
|
||
@size-change="handleSizeChange"
|
||
@current-change="handlePageChange"
|
||
/>
|
||
</el-card>
|
||
|
||
<!-- 新增/编辑弹窗 -->
|
||
<el-dialog :title="dialog.title" :visible.sync="dialog.visible" width="720px" append-to-body
|
||
:close-on-click-modal="false">
|
||
<el-form ref="syllabusForm" :model="dialog.form" :rules="rules" label-width="130px">
|
||
<el-row :gutter="20">
|
||
<el-col :span="12">
|
||
<el-form-item label="专业" prop="zydh">
|
||
<el-select
|
||
v-model="dialog.form.zydh"
|
||
filterable
|
||
clearable
|
||
placeholder="请选择专业"
|
||
class="w-full"
|
||
:loading="referenceDataLoading"
|
||
>
|
||
<el-option
|
||
v-for="item in majorOptions"
|
||
:key="item.zydh"
|
||
:label="item.zymc"
|
||
:value="item.zydh"
|
||
/>
|
||
</el-select>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="课程" prop="kbh">
|
||
<el-select
|
||
v-model="dialog.form.kbh"
|
||
filterable
|
||
clearable
|
||
placeholder="请选择课程"
|
||
class="w-full"
|
||
:loading="referenceDataLoading"
|
||
>
|
||
<el-option
|
||
v-for="item in subjectOptions"
|
||
:key="item.kbh"
|
||
:label="item.kmc"
|
||
:value="item.kbh"
|
||
/>
|
||
</el-select>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="学期第次" prop="xqdc">
|
||
<el-input-number v-model="dialog.form.xqdc" :min="1" :max="20" controls-position="right" class="w-full" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="课类型" prop="klx">
|
||
<el-select
|
||
v-model="dialog.form.klx"
|
||
filterable
|
||
clearable
|
||
placeholder="请选择课类型"
|
||
class="w-full"
|
||
:loading="referenceDataLoading"
|
||
>
|
||
<el-option
|
||
v-for="item in courseTypeOptions"
|
||
:key="item.dictValue"
|
||
:label="item.dictLabel"
|
||
:value="item.dictValue"
|
||
/>
|
||
</el-select>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="学时" prop="xs">
|
||
<el-input-number v-model="dialog.form.xs" :min="0" controls-position="right" class="w-full" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="停用" prop="ty">
|
||
<el-radio-group v-model="dialog.form.ty">
|
||
<el-radio :label="0">否</el-radio>
|
||
<el-radio :label="1">是</el-radio>
|
||
</el-radio-group>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="简称">
|
||
<el-input v-model="dialog.form.jc" placeholder="请输入课程简称" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="学分">
|
||
<el-input-number v-model="dialog.form.xf" :min="0" :step="0.5" :precision="1" controls-position="right" class="w-full" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="课程定位">
|
||
<el-input v-model="dialog.form.kcdw" placeholder="请输入课程定位" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="模块">
|
||
<el-input v-model="dialog.form.mk" placeholder="请输入模块" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="成绩分制">
|
||
<el-input v-model="dialog.form.cjfz" placeholder="请输入成绩分制" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="教研室">
|
||
<el-select
|
||
v-model="dialog.form.jysdh"
|
||
filterable
|
||
clearable
|
||
placeholder="请选择教研室"
|
||
class="w-full"
|
||
:loading="referenceDataLoading"
|
||
>
|
||
<el-option
|
||
v-for="item in officeOptions"
|
||
:key="item.jysdh"
|
||
:label="item.jysmc"
|
||
:value="item.jysdh"
|
||
/>
|
||
</el-select>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="考试课时">
|
||
<el-input-number v-model="dialog.form.ksks" :min="0" controls-position="right" class="w-full" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="理论学时">
|
||
<el-input-number v-model="dialog.form.llxs" :min="0" controls-position="right" class="w-full" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="实践学时">
|
||
<el-input-number v-model="dialog.form.sjxs" :min="0" controls-position="right" class="w-full" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="周课时">
|
||
<el-input-number v-model="dialog.form.zks" :min="0" controls-position="right" class="w-full" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="不计入平均分">
|
||
<el-radio-group v-model="dialog.form.bjrxypjf">
|
||
<el-radio :label="0">否</el-radio>
|
||
<el-radio :label="1">是</el-radio>
|
||
</el-radio-group>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="考试课时不显示">
|
||
<el-radio-group v-model="dialog.form.ksksbxs">
|
||
<el-radio :label="0">否</el-radio>
|
||
<el-radio :label="1">是</el-radio>
|
||
</el-radio-group>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="大纲课程">
|
||
<el-radio-group v-model="dialog.form.dgkc">
|
||
<el-radio :label="0">否</el-radio>
|
||
<el-radio :label="1">是</el-radio>
|
||
</el-radio-group>
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
</el-form>
|
||
<div slot="footer" class="dialog-footer">
|
||
<el-button @click="dialog.visible = false">取 消</el-button>
|
||
<el-button type="primary" :loading="dialog.submitting" @click="submitDialog">确 定</el-button>
|
||
</div>
|
||
</el-dialog>
|
||
|
||
<!-- 模板导入弹窗 -->
|
||
<el-dialog title="教学大纲导入" :visible.sync="importDialog.visible" width="660px" append-to-body
|
||
:close-on-click-modal="false">
|
||
<el-alert type="info" :closable="false" show-icon class="import-tip"
|
||
title="请先下载导入模板,按「专业教学计划」sheet 列填写后再上传;「专业代号+课编号+学期第次」已存在的行将跳过。" />
|
||
<el-form label-width="110px" class="import-form">
|
||
<el-form-item label="导入模板">
|
||
<el-button icon="el-icon-download" :loading="importDialog.downloading" @click="handleDownloadTemplate">
|
||
下载导入模板
|
||
</el-button>
|
||
<span class="tip-text">红色表头列为必填,详见模板「填写说明」sheet</span>
|
||
</el-form-item>
|
||
<el-form-item label="数据文件">
|
||
<div class="file-input">
|
||
<input ref="importFileInput" type="file" accept=".xls,.xlsx" style="display: none"
|
||
@change="handleImportFileChange" />
|
||
<el-button icon="el-icon-folder-opened" @click="handleChooseFile">选择文件</el-button>
|
||
<span class="file-name">{{ importDialog.fileName || '未选择任何文件' }}</span>
|
||
<el-button v-if="importDialog.file" type="text" class="danger-text-btn" @click="clearImportFile">
|
||
清除
|
||
</el-button>
|
||
</div>
|
||
</el-form-item>
|
||
<el-form-item label="导入结果" v-if="importDialog.result">
|
||
<div class="import-result">
|
||
共导入 <span class="num">{{ importDialog.result.insertCount }}</span> 条<template
|
||
v-if="importDialog.result.skipCount > 0">,跳过 <span
|
||
class="num warn">{{ importDialog.result.skipCount }}</span> 条(数据已存在)</template><template
|
||
v-if="importDialog.result.failCount > 0">,失败 <span
|
||
class="num warn">{{ importDialog.result.failCount }}</span> 条</template>。
|
||
<div v-if="importDialog.result.failList.length" class="skip-detail">
|
||
<div v-for="(item, idx) in importDialog.result.failList" :key="'f' + idx">{{ item }}</div>
|
||
</div>
|
||
<div v-if="importDialog.result.skipList.length" class="skip-detail">
|
||
<div v-for="(item, idx) in importDialog.result.skipList" :key="'s' + idx">{{ item }}</div>
|
||
</div>
|
||
</div>
|
||
</el-form-item>
|
||
</el-form>
|
||
<div slot="footer" class="dialog-footer">
|
||
<el-button @click="importDialog.visible = false">关 闭</el-button>
|
||
<el-button type="primary" :loading="importDialog.importing" :disabled="!importDialog.file"
|
||
@click="handleImport">开始导入</el-button>
|
||
</div>
|
||
</el-dialog>
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
import {
|
||
addSyllabus,
|
||
deleteSyllabus,
|
||
batchDeleteSyllabus,
|
||
updateSyllabus,
|
||
listByZydhAndTy,
|
||
exportSyllabus,
|
||
downloadSyllabusTemplate,
|
||
importSyllabus
|
||
} from '@/api/teachBusiness/syllabus'
|
||
import { listMajor } from '@/api/subjectMajor/major'
|
||
import { listKb } from '@/api/teachOffice/kb'
|
||
import { listOffice } from '@/api/teachOffice/office'
|
||
import { getDicts } from '@/api/system/dict/data'
|
||
import { saveAs } from 'file-saver'
|
||
|
||
const REFERENCE_DATA_PAGE_SIZE = 10000
|
||
const COURSE_TYPE_DICT_CODE = 'course_type'
|
||
|
||
export default {
|
||
name: 'SyllabusIndex',
|
||
data() {
|
||
return {
|
||
loading: false,
|
||
searchForm: {
|
||
zydh: '',
|
||
ty: 0
|
||
},
|
||
tableData: [],
|
||
total: 0,
|
||
pageNum: 1,
|
||
pageSize: 20,
|
||
localPaging: false,
|
||
selection: [],
|
||
referenceDataLoading: false,
|
||
majorOptions: [],
|
||
subjectOptions: [],
|
||
courseTypeOptions: [],
|
||
officeOptions: [],
|
||
dialog: {
|
||
visible: false,
|
||
title: '',
|
||
submitting: false,
|
||
form: this.createEmptyForm()
|
||
},
|
||
importDialog: {
|
||
visible: false,
|
||
downloading: false,
|
||
importing: false,
|
||
file: null,
|
||
fileName: '',
|
||
result: null
|
||
},
|
||
rules: {
|
||
zydh: [{ required: true, message: '请选择专业', trigger: 'change' }],
|
||
kbh: [{ required: true, message: '请选择课程', trigger: 'change' }],
|
||
xqdc: [{ required: true, message: '请输入学期第次', trigger: 'blur' }],
|
||
klx: [{ required: true, message: '请选择课类型', trigger: 'change' }],
|
||
xs: [{ required: true, message: '请输入学时', trigger: 'blur' }],
|
||
ty: [{ required: true, message: '请选择停用标识', trigger: 'change' }]
|
||
}
|
||
}
|
||
},
|
||
computed: {
|
||
pagedData() {
|
||
if (!this.localPaging) {
|
||
return this.tableData
|
||
}
|
||
const start = (this.pageNum - 1) * this.pageSize
|
||
return this.tableData.slice(start, start + this.pageSize)
|
||
},
|
||
activeSelection() {
|
||
return this.selection.filter(row => !this.isDisabled(row))
|
||
}
|
||
},
|
||
created() {
|
||
this.fetchList()
|
||
this.loadReferenceData()
|
||
},
|
||
methods: {
|
||
loadReferenceData() {
|
||
this.referenceDataLoading = true
|
||
const query = { pageNum: 1, pageSize: REFERENCE_DATA_PAGE_SIZE }
|
||
const requests = [
|
||
listMajor(query),
|
||
listKb(query),
|
||
listOffice(query),
|
||
getDicts(COURSE_TYPE_DICT_CODE)
|
||
].map(request => {
|
||
return request
|
||
.then(response => this.getResponseRecords(response))
|
||
.catch(() => [])
|
||
})
|
||
|
||
Promise.all(requests).then(([majors, subjects, offices, courseTypes]) => {
|
||
this.majorOptions = this.getUniqueOptions(majors, 'zydh', 'zymc')
|
||
this.subjectOptions = this.getUniqueOptions(subjects, 'kbh', 'kmc')
|
||
this.officeOptions = this.getUniqueOptions(offices, 'jysdh', 'jysmc')
|
||
this.courseTypeOptions = this.getUniqueOptions(courseTypes, 'dictValue', 'dictLabel')
|
||
}).finally(() => {
|
||
this.referenceDataLoading = false
|
||
})
|
||
},
|
||
getResponseRecords(response) {
|
||
const data = response.data || {}
|
||
|
||
return Array.isArray(data) ? data : data.records || []
|
||
},
|
||
getUniqueOptions(items, valueKey, labelKey) {
|
||
const values = new Set()
|
||
|
||
return items.filter(item => {
|
||
const value = item[valueKey]
|
||
const label = item[labelKey]
|
||
if (!value || !label || values.has(value)) {
|
||
return false
|
||
}
|
||
values.add(value)
|
||
|
||
return true
|
||
})
|
||
},
|
||
/* ---------- 列表加载 ---------- */
|
||
fetchList() {
|
||
this.loading = true
|
||
this.localPaging = false
|
||
const query = {
|
||
pageNum: this.pageNum,
|
||
pageSize: this.pageSize,
|
||
ty: this.searchForm.ty
|
||
}
|
||
if (this.searchForm.zydh) {
|
||
query.zydh = this.searchForm.zydh
|
||
}
|
||
listByZydhAndTy(query).then(response => {
|
||
const data = response.data || {}
|
||
this.tableData = Array.isArray(data) ? data : (data.records || [])
|
||
this.total = Array.isArray(data) ? data.length : Number(data.total || 0)
|
||
}).catch(() => {
|
||
this.tableData = []
|
||
this.total = 0
|
||
}).finally(() => {
|
||
this.loading = false
|
||
})
|
||
},
|
||
handleQuery() {
|
||
this.pageNum = 1
|
||
this.fetchList()
|
||
},
|
||
handleReset() {
|
||
this.searchForm = { zydh: '', ty: 0 }
|
||
this.pageNum = 1
|
||
this.fetchList()
|
||
},
|
||
handleSelectionChange(val) {
|
||
this.selection = val
|
||
},
|
||
|
||
/* ---------- 新增 / 编辑 ---------- */
|
||
handleAdd() {
|
||
this.dialog.title = '新增教学大纲'
|
||
this.dialog.form = this.createEmptyForm()
|
||
this.dialog.visible = true
|
||
this.$nextTick(() => {
|
||
if (this.$refs.syllabusForm) this.$refs.syllabusForm.clearValidate()
|
||
})
|
||
},
|
||
handleEdit(row) {
|
||
this.dialog.title = '编辑教学大纲'
|
||
this.dialog.form = Object.assign({}, this.createEmptyForm(), row)
|
||
this.dialog.visible = true
|
||
this.$nextTick(() => {
|
||
if (this.$refs.syllabusForm) this.$refs.syllabusForm.clearValidate()
|
||
})
|
||
},
|
||
submitDialog() {
|
||
this.$refs.syllabusForm.validate(valid => {
|
||
if (!valid) return
|
||
this.dialog.submitting = true
|
||
const payload = this.cleanPayload(this.dialog.form)
|
||
const isEdit = !!payload.bh
|
||
const req = isEdit ? updateSyllabus(payload) : addSyllabus(payload)
|
||
req.then(() => {
|
||
this.$message.success(isEdit ? '修改成功' : '新增成功')
|
||
this.dialog.visible = false
|
||
if (!isEdit) {
|
||
this.pageNum = 1
|
||
}
|
||
this.fetchList()
|
||
}).finally(() => {
|
||
this.dialog.submitting = false
|
||
})
|
||
})
|
||
},
|
||
|
||
/* ---------- 停用 / 批量停用 ---------- */
|
||
isDisabled(row) {
|
||
return Number(row && row.ty) === 1
|
||
},
|
||
handleDelete(row) {
|
||
if (this.isDisabled(row)) {
|
||
return
|
||
}
|
||
this.$confirm('确认停用该条教学大纲吗?', '提示', {
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: 'warning'
|
||
}).then(() => {
|
||
return deleteSyllabus(row.bh)
|
||
}).then(() => {
|
||
this.$message.success('停用成功')
|
||
this.fetchList()
|
||
}).catch(() => {})
|
||
},
|
||
handleBatchDelete() {
|
||
const bhList = this.activeSelection.map(row => row.bh)
|
||
if (!bhList.length) {
|
||
this.$message.warning('请先勾选要停用的记录')
|
||
return
|
||
}
|
||
this.$confirm('确定停用所选 ' + bhList.length + ' 条记录吗?', '提示', {
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: 'warning'
|
||
}).then(() => {
|
||
return batchDeleteSyllabus(bhList)
|
||
}).then(() => {
|
||
this.$message.success('批量停用成功')
|
||
this.fetchList()
|
||
}).catch(() => {})
|
||
},
|
||
|
||
/* ---------- 下载 ---------- */
|
||
handleDownload() {
|
||
const query = {}
|
||
if (this.searchForm.zydh) query.zydh = this.searchForm.zydh
|
||
if (this.searchForm.ty === 0 || this.searchForm.ty === 1) query.ty = this.searchForm.ty
|
||
exportSyllabus(query).then(blob => {
|
||
saveAs(blob, '教学大纲.xlsx')
|
||
this.$message.success('导出成功')
|
||
}).catch(() => {})
|
||
},
|
||
|
||
/* ---------- 模板导入 ---------- */
|
||
openImportDialog() {
|
||
this.importDialog.file = null
|
||
this.importDialog.fileName = ''
|
||
this.importDialog.result = null
|
||
this.importDialog.visible = true
|
||
if (this.$refs.importFileInput) this.$refs.importFileInput.value = ''
|
||
},
|
||
handleDownloadTemplate() {
|
||
this.importDialog.downloading = true
|
||
downloadSyllabusTemplate().then(blob => {
|
||
saveAs(blob, '教学大纲导入模板.xlsx')
|
||
this.$message.success('模板下载成功')
|
||
}).catch(() => {}).finally(() => {
|
||
this.importDialog.downloading = false
|
||
})
|
||
},
|
||
handleChooseFile() {
|
||
if (this.$refs.importFileInput) this.$refs.importFileInput.click()
|
||
},
|
||
handleImportFileChange(e) {
|
||
const input = e.target
|
||
this.importDialog.result = null
|
||
if (!input.files || input.files.length === 0) {
|
||
this.importDialog.file = null
|
||
this.importDialog.fileName = ''
|
||
return
|
||
}
|
||
const file = input.files[0]
|
||
const name = (file.name || '').toLowerCase()
|
||
if (!name.endsWith('.xls') && !name.endsWith('.xlsx')) {
|
||
this.$message.warning('仅支持 .xls / .xlsx 格式的 Excel 文件')
|
||
this.clearImportFile()
|
||
input.value = ''
|
||
return
|
||
}
|
||
this.importDialog.file = file
|
||
this.importDialog.fileName = file.name
|
||
},
|
||
clearImportFile() {
|
||
this.importDialog.file = null
|
||
this.importDialog.fileName = ''
|
||
this.importDialog.result = null
|
||
if (this.$refs.importFileInput) this.$refs.importFileInput.value = ''
|
||
},
|
||
handleImport() {
|
||
if (!this.importDialog.file) {
|
||
this.$message.warning('请先选择文件')
|
||
return
|
||
}
|
||
this.importDialog.importing = true
|
||
importSyllabus(this.importDialog.file).then(response => {
|
||
const data = (response && response.data) || {}
|
||
this.importDialog.file = null
|
||
this.importDialog.fileName = ''
|
||
if (this.$refs.importFileInput) this.$refs.importFileInput.value = ''
|
||
this.importDialog.result = {
|
||
insertCount: data.insertCount || 0,
|
||
skipCount: data.skipCount || 0,
|
||
failCount: data.failCount || 0,
|
||
skipList: data.skipList || [],
|
||
failList: data.failList || []
|
||
}
|
||
this.$message.success(data.message || '导入完成')
|
||
this.fetchList()
|
||
}).catch(() => {}).finally(() => {
|
||
this.importDialog.importing = false
|
||
})
|
||
},
|
||
|
||
/* ---------- 分页 ---------- */
|
||
handleSizeChange(size) {
|
||
this.pageSize = size
|
||
this.pageNum = 1
|
||
this.fetchList()
|
||
},
|
||
handlePageChange(page) {
|
||
this.pageNum = page
|
||
this.fetchList()
|
||
},
|
||
|
||
/* ---------- 工具 ---------- */
|
||
createEmptyForm() {
|
||
return {
|
||
bh: '',
|
||
zydh: '',
|
||
kbh: '',
|
||
xqdc: 1,
|
||
klx: '',
|
||
xs: 0,
|
||
ty: 0,
|
||
jc: '',
|
||
xf: null,
|
||
kcdw: '',
|
||
ksks: 0,
|
||
mk: '',
|
||
cjfz: '',
|
||
bjrxypjf: 0,
|
||
llxs: 0,
|
||
sjxs: 0,
|
||
zks: 0,
|
||
ksksbxs: 0,
|
||
dgkc: 0,
|
||
jysdh: ''
|
||
}
|
||
},
|
||
/** 移除空值(''/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
|
||
},
|
||
fmtYesNo(val) {
|
||
return Number(val) === 1 ? '是' : '否'
|
||
},
|
||
fmtDateTime(val) {
|
||
return (val || '').substring(0, 16) || '-'
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style scoped lang="scss">
|
||
.app-container {
|
||
padding: 20px;
|
||
}
|
||
|
||
.syllabus-page {
|
||
.search-card {
|
||
margin-bottom: 16px;
|
||
|
||
.search-form {
|
||
.w-full {
|
||
width: 100%;
|
||
}
|
||
|
||
.search-actions {
|
||
padding-top: 4px;
|
||
}
|
||
}
|
||
}
|
||
|
||
.table-card {
|
||
.list-header {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin-bottom: 16px;
|
||
|
||
.list-title {
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
color: #303133;
|
||
}
|
||
}
|
||
|
||
.pagination {
|
||
margin-top: 16px;
|
||
text-align: right;
|
||
}
|
||
}
|
||
}
|
||
|
||
.danger-text-btn {
|
||
color: #f56c6c;
|
||
|
||
&:hover {
|
||
color: #f78989;
|
||
}
|
||
}
|
||
|
||
.import-tip {
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
.tip-text {
|
||
margin-left: 10px;
|
||
font-size: 12px;
|
||
color: #909399;
|
||
}
|
||
|
||
.file-input {
|
||
display: flex;
|
||
align-items: center;
|
||
|
||
.file-name {
|
||
margin-left: 10px;
|
||
font-size: 13px;
|
||
color: #606266;
|
||
}
|
||
}
|
||
|
||
.import-result {
|
||
font-size: 13px;
|
||
color: #303133;
|
||
|
||
.num {
|
||
font-weight: 600;
|
||
color: #409eff;
|
||
}
|
||
|
||
.num.warn {
|
||
color: #e6a23c;
|
||
}
|
||
|
||
.skip-detail {
|
||
margin-top: 8px;
|
||
max-height: 180px;
|
||
overflow-y: auto;
|
||
padding: 8px;
|
||
background: #f5f7fa;
|
||
border-radius: 4px;
|
||
font-size: 12px;
|
||
color: #909399;
|
||
line-height: 1.8;
|
||
}
|
||
}
|
||
</style>
|