Files
education/frontend/src/views/teachOffice/subject/index.vue
T
2026-08-21 18:42:51 +08:00

876 lines
33 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 subject-page">
<!-- ==================== 1. 顶部状态提示 ==================== -->
<div class="status-tip">{{ statusTip }}</div>
<!-- ==================== 2. 查询条件区域 ==================== -->
<el-card shadow="never" class="search-card">
<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.kcbh" placeholder="请输入课程别名" clearable class="q-control" />
</div>
<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>
<div class="q-control range-control">
<el-input v-model="searchForm.xfMin" placeholder="0" />
<span class="range-sep">至</span>
<el-input v-model="searchForm.xfMax" placeholder="0" />
</div>
</div>
<div class="query-field">
<span class="q-label no-check">学时</span>
<div class="q-control range-control">
<el-input v-model="searchForm.xsMin" placeholder="0" />
<span class="range-sep">至</span>
<el-input v-model="searchForm.xsMax" placeholder="0" />
</div>
</div>
<div class="query-field">
<el-checkbox v-model="searchForm.zcfsEnabled" />
<span class="q-label">支持方式</span>
<el-select v-model="searchForm.zcfs" class="q-control" :disabled="!searchForm.zcfsEnabled">
<el-option v-for="item in zcfsOptions" :key="item" :label="item" :value="item" />
</el-select>
</div>
<div class="query-field">
<el-checkbox v-model="searchForm.khlxEnabled" />
<span class="q-label">考核类型</span>
<el-select v-model="searchForm.khlx" class="q-control" :disabled="!searchForm.khlxEnabled">
<el-option v-for="item in khlxOptions" :key="item" :label="item" :value="item" />
</el-select>
</div>
<div class="query-field">
<span class="q-label no-check">秘密等级</span>
<el-select v-model="searchForm.mmdj" placeholder="请选择" class="q-control">
<el-option v-for="item in mmdjOptions" :key="item" :label="item" :value="item" />
</el-select>
</div>
<div class="query-field">
<el-checkbox v-model="searchForm.dgxzEnabled" />
<span class="q-label">大纲性质</span>
<el-radio-group v-model="searchForm.dgxz" class="q-control" :disabled="!searchForm.dgxzEnabled">
<el-radio :label="'大纲课程'">大纲课程</el-radio>
<el-radio :label="'非大纲课程'">非大纲课程</el-radio>
</el-radio-group>
</div>
<div class="query-field">
<el-checkbox v-model="searchForm.zzglEnabled" />
<span class="q-label">政治类课程</span>
<el-radio-group v-model="searchForm.zzgl" class="q-control" :disabled="!searchForm.zzglEnabled">
<el-radio :label="'政治类'">政治类</el-radio>
<el-radio :label="'非政治类'">非政治类</el-radio>
</el-radio-group>
</div>
<div class="query-field">
<span class="q-label no-check">备注</span>
<el-input v-model="searchForm.bz" placeholder="请输入备注" clearable class="q-control" />
</div>
</el-col>
<!-- 右栏 -->
<el-col :xs="24" :md="12">
<div class="query-field">
<el-checkbox v-model="searchForm.jysEnabled" />
<span class="q-label">教研室</span>
<el-select v-model="searchForm.jys" class="q-control" :disabled="!searchForm.jysEnabled">
<el-option v-for="item in jysOptions" :key="item" :label="item" :value="item" />
</el-select>
</div>
<div class="query-field">
<el-checkbox v-model="searchForm.pxccEnabled" />
<span class="q-label">培训层次</span>
<el-select v-model="searchForm.pxcc" class="q-control" :disabled="!searchForm.pxccEnabled">
<el-option v-for="item in pxccOptions" :key="item" :label="item" :value="item" />
</el-select>
</div>
<div class="query-field">
<el-checkbox v-model="searchForm.pxlxEnabled" />
<span class="q-label">培训类型</span>
<el-select v-model="searchForm.pxlx" class="q-control" :disabled="!searchForm.pxlxEnabled">
<el-option v-for="item in pxlxOptions" :key="item" :label="item" :value="item" />
</el-select>
</div>
<div class="query-field">
<el-checkbox v-model="searchForm.kclxEnabled" />
<span class="q-label">课程类型</span>
<el-select v-model="searchForm.kclx" class="q-control" :disabled="!searchForm.kclxEnabled">
<el-option v-for="item in kclxOptions" :key="item" :label="item" :value="item" />
</el-select>
</div>
<div class="query-field">
<el-checkbox v-model="searchForm.jsfsEnabled" />
<span class="q-label">建设方式</span>
<el-select v-model="searchForm.jsfs" class="q-control" :disabled="!searchForm.jsfsEnabled">
<el-option v-for="item in jsfsOptions" :key="item" :label="item" :value="item" />
</el-select>
</div>
<div class="query-field">
<el-checkbox v-model="searchForm.pjqkEnabled" />
<span class="q-label">评选情况</span>
<el-select v-model="searchForm.pjqk" class="q-control" :disabled="!searchForm.pjqkEnabled">
<el-option v-for="item in pjqkOptions" :key="item" :label="item" :value="item" />
</el-select>
</div>
<div class="query-field">
<el-checkbox v-model="searchForm.gljgEnabled" />
<span class="q-label">管理机构</span>
<el-select v-model="searchForm.gljg" class="q-control" :disabled="!searchForm.gljgEnabled">
<el-option v-for="item in gljgOptions" :key="item" :label="item" :value="item" />
</el-select>
</div>
<div class="query-field">
<el-checkbox v-model="searchForm.zyxzEnabled" />
<span class="q-label">在用选项</span>
<el-radio-group v-model="searchForm.zyxz" class="q-control" :disabled="!searchForm.zyxzEnabled">
<el-radio :label="'在用'">在用</el-radio>
<el-radio :label="'其他'">其他</el-radio>
</el-radio-group>
</div>
<div class="query-field">
<el-checkbox v-model="searchForm.bbxxEnabled" />
<span class="q-label">版本选项</span>
<el-radio-group v-model="searchForm.bbxx" class="q-control" :disabled="!searchForm.bbxxEnabled">
<el-radio :label="'最新版本'">最新版本</el-radio>
<el-radio :label="'存在多版本'">存在多版本</el-radio>
<el-radio :label="'全部版本'">全部版本</el-radio>
</el-radio-group>
</div>
<div class="query-field">
<el-checkbox v-model="searchForm.bmbhEnabled" />
<span class="q-label">别名包含规范名称</span>
<el-radio-group v-model="searchForm.bmbh" class="q-control" :disabled="!searchForm.bmbhEnabled">
<el-radio :label="'完全包含'">完全包含</el-radio>
<el-radio :label="'未完全包含'">未完全包含</el-radio>
</el-radio-group>
</div>
</el-col>
</el-row>
</el-form>
<div class="notice">注意:勾选"选择框"表示启用该项对应的查询条件,输入框非空表示启用查询条件。</div>
<div class="query-footer">
<el-button class="gray-btn" @click="handleQuery">查询</el-button>
</div>
</el-card>
<!-- ==================== 3. 操作按钮区域 ==================== -->
<el-card shadow="never" class="action-card">
<div class="action-row">
<el-button type="primary" @click="handleOpenDialog">新建课程科目</el-button>
<el-button type="primary" @click="handleBatchDelete">删除所选</el-button>
<el-button type="primary" @click="handleDownloadCourse">下载课程科目基本信息</el-button>
<el-button type="primary" @click="handleDownloadTextbook">下载课程教材基本信息</el-button>
</div>
<div class="action-row">
<el-button type="primary" @click="handleDownloadTemplate">【课程科目数据文件模板】下载</el-button>
</div>
</el-card>
<!-- ==================== 4. 文件上传区域 ==================== -->
<el-card shadow="never" class="upload-card">
<div class="upload-row">
<div class="upload-left">
<el-button @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" @click="handleUpload">上传数据</el-button>
</div>
</div>
</el-card>
<!-- ==================== 5. 红色提示文字 ==================== -->
<div class="red-tip">
提示:课程体系按"课程-专题-课题"构建的,课程名称的命名格式为"课程名称\专题名称\课题名称"或"课程名称\课题名称"。
</div>
<!-- ==================== 6. 数据表格区域 ==================== -->
<el-card shadow="never" class="table-card">
<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="manage" label="管理" width="70" align="center" />
<el-table-column prop="code" label="编码" width="100" show-overflow-tooltip />
<el-table-column prop="name" label="课程名称" min-width="130" show-overflow-tooltip />
<el-table-column prop="cat" label="分类" width="100" align="center" />
<el-table-column prop="exam" label="考核类型方式" width="110" align="center" />
<el-table-column prop="inClass" label="课内学时" width="90" align="center" />
<el-table-column prop="outClass" label="课外学时" width="90" align="center" />
<el-table-column prop="other" label="其他" width="80" align="center" />
<el-table-column prop="textbook" label="教材" min-width="120" show-overflow-tooltip />
<el-table-column prop="majors" label="开设课程专业名称" min-width="150" show-overflow-tooltip />
<el-table-column prop="teacher" label="在职主讲教员" min-width="110" show-overflow-tooltip />
</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="1100px"
: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" :lg="8" :xl="8">
<el-form-item label="编号" prop="bh">
<el-input v-model="addForm.bh" placeholder="请输入编号" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8" :lg="8" :xl="8">
<el-form-item label="课标号" prop="kbh">
<el-input v-model="addForm.kbh" placeholder="请输入课标号" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8" :lg="8" :xl="8">
<el-form-item label="课程名称" prop="mc">
<el-input v-model="addForm.mc" placeholder="请输入课程名称" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8" :lg="8" :xl="8">
<el-form-item label="适用专业" prop="syzy">
<el-input v-model="addForm.syzy" placeholder="请输入适用专业" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8" :lg="8" :xl="8">
<el-form-item label="课时数" prop="kcs">
<el-input-number v-model="addForm.kcs" :min="0" controls-position="right" class="w-full" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8" :lg="8" :xl="8">
<el-form-item label="录入人编号" prop="zxrbh">
<el-input v-model="addForm.zxrbh" placeholder="请输入录入人编号" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8" :lg="8" :xl="8">
<el-form-item label="录入时间">
<el-input v-model="addForm.zxsj" placeholder="请输入录入时间" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="正文">
<el-input v-model="addForm.zw" type="textarea" :rows="3" placeholder="请输入正文" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="备注">
<el-input v-model="addForm.bz" type="textarea" :rows="2" placeholder="请输入备注" />
</el-form-item>
</el-col>
</el-row>
<div class="classroom-section">
<div class="classroom-header">
<span class="classroom-title">课堂列表</span>
<el-button type="primary" size="small" @click="handleAddClassroom">添加课堂</el-button>
</div>
<el-table :data="addForm.ktList" border size="small">
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column label="课堂编号" width="140">
<template slot-scope="{ row }">
<el-input v-model="row.bh" placeholder="课堂编号" />
</template>
</el-table-column>
<el-table-column label="课堂次序" width="90">
<template slot-scope="{ row }">
<el-input-number v-model="row.ktjc" :min="1" controls-position="right" class="w-full" />
</template>
</el-table-column>
<el-table-column label="教学内容" min-width="150">
<template slot-scope="{ row }">
<el-input v-model="row.jxnr" placeholder="教学内容" />
</template>
</el-table-column>
<el-table-column label="教学要点" min-width="150">
<template slot-scope="{ row }">
<el-input v-model="row.jxyd" placeholder="教学要点" />
</template>
</el-table-column>
<el-table-column label="教学方法" min-width="150">
<template slot-scope="{ row }">
<el-input v-model="row.jxff" placeholder="教学方法" />
</template>
</el-table-column>
<el-table-column label="主讲教员数" width="100">
<template slot-scope="{ row }">
<el-input-number v-model="row.zjjys" :min="0" controls-position="right" class="w-full" />
</template>
</el-table-column>
<el-table-column label="辅讲教员数" width="100">
<template slot-scope="{ row }">
<el-input-number v-model="row.fjjys" :min="0" controls-position="right" class="w-full" />
</template>
</el-table-column>
<el-table-column label="教学目的" min-width="150">
<template slot-scope="{ row }">
<el-input v-model="row.jxmd" placeholder="教学目的" />
</template>
</el-table-column>
<el-table-column label="教学要求" min-width="150">
<template slot-scope="{ row }">
<el-input v-model="row.jxyq" placeholder="教学要求" />
</template>
</el-table-column>
<el-table-column label="操作" width="80" align="center" fixed="right">
<template slot-scope="{ $index }">
<el-button type="text" class="text-danger" @click="handleDeleteClassroom($index)">删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
</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>
<!-- 撤回弹窗 -->
<el-dialog
:visible="withdrawDialogVisible"
title="撤回课程科目"
width="500px"
:close-on-click-modal="false"
@update:visible="val => withdrawDialogVisible = val"
>
<el-form :model="withdrawForm" label-width="100px">
<el-form-item label="课程编号">
<el-input v-model="withdrawForm.bh" disabled />
</el-form-item>
<el-form-item label="撤回原因">
<el-input v-model="withdrawForm.chyy" type="textarea" :rows="4" placeholder="请输入撤回原因" />
</el-form-item>
</el-form>
<div slot="footer">
<div class="dialog-footer">
<el-button @click="handleCancelWithdraw">取消</el-button>
<el-button type="primary" :loading="withdrawLoading" @click="handleWithdraw">确定撤回</el-button>
</div>
</div>
</el-dialog>
</div>
</template>
<script>
export default {
name: 'SubjectIndex',
data() {
return {
// ==================== 1. 顶部状态提示 ====================
statusTip: '请输入查询条件',
// ==================== 下拉选项 ====================
zcfsOptions: ['其他', '讲授', '研讨', '实操'],
khlxOptions: ['讲座', '考试', '考查', '实操'],
mmdjOptions: ['公开', '内部', '秘密', '机密'],
jysOptions: ['未确定教研室', '军事基础教研室', '政治工作教研室', '装备保障教研室'],
pxccOptions: ['无', '本科', '硕士', '博士'],
pxlxOptions: ['军官基础教育', '士兵职业教育', '研究生教育', '其他'],
kclxOptions: ['其他类', '公共基础', '专业基础', '专业核心'],
jsfsOptions: ['其他', '自建', '共建', '引进'],
pjqkOptions: ['无', '国家级', '军队级', '院校级'],
gljgOptions: ['教务处', '教研室1', '教研室2'],
// ==================== 2. 查询条件 ====================
searchForm: {
kcbh: '',
jc: '',
xfMin: '',
xfMax: '',
xsMin: '',
xsMax: '',
zcfsEnabled: false,
zcfs: '其他',
khlxEnabled: false,
khlx: '讲座',
mmdj: '',
dgxzEnabled: false,
dgxz: '大纲课程',
zzglEnabled: false,
zzgl: '政治类',
bz: '',
jysEnabled: false,
jys: '未确定教研室',
pxccEnabled: false,
pxcc: '无',
pxlxEnabled: false,
pxlx: '军官基础教育',
kclxEnabled: false,
kclx: '其他类',
jsfsEnabled: false,
jsfs: '其他',
pjqkEnabled: false,
pjqk: '无',
gljgEnabled: false,
gljg: '教务处',
zyxzEnabled: true,
zyxz: '在用',
bbxxEnabled: true,
bbxx: '最新版本',
bmbhEnabled: false,
bmbh: '完全包含'
},
// ==================== 列表数据 ====================
/** 模拟列表数据(后端接口未提供) */
mockTableData: [
{ id: 1, manage: '编辑', code: 'KCB001', name: '高等数学', cat: '公共基础', exam: '考试', inClass: 48, outClass: 16, other: '—', textbook: '高等数学(上册)', majors: '炮兵装备运用/通信工程', teacher: '张三' },
{ id: 2, manage: '编辑', code: 'KCB002', name: '导弹发射原理', cat: '专业核心', exam: '考试', inClass: 40, outClass: 16, other: '—', textbook: '导弹发射原理', majors: '炮兵装备运用', teacher: '李四' },
{ id: 3, manage: '编辑', code: 'KCB003', name: '通信原理', cat: '专业核心', exam: '考试', inClass: 48, outClass: 16, other: '—', textbook: '通信原理', majors: '通信工程', teacher: '王五' },
{ id: 4, manage: '编辑', code: 'KCB004', name: '军事英语', cat: '公共基础', exam: '考查', inClass: 30, outClass: 8, other: '—', textbook: '军事英语教程', majors: '全军各专业', teacher: '赵六' },
{ id: 5, manage: '编辑', code: 'KCB005', name: '联合作战指挥', cat: '专业核心', exam: '综合考核', inClass: 40, outClass: 20, other: '—', textbook: '联合作战指挥', majors: '军事指挥', teacher: '孙七' },
{ id: 6, manage: '编辑', code: 'KCB006', name: '装备维修训练', cat: '实践环节', exam: '实操', inClass: 0, outClass: 48, other: '—', textbook: '装备维修手册', majors: '装备保障', teacher: '周八' }
],
loading: false,
tableData: [],
total: 0,
pageNum: 1,
pageSize: 20,
selectedRows: [],
// ==================== 文件上传 ====================
selectedFile: null,
coverExist: false,
// ==================== 新增/编辑弹窗 ====================
dialogVisible: false,
addLoading: false,
editLoading: false,
isEdit: false,
addForm: this.createEmptyForm(),
rules: {
bh: [{ required: true, message: '请输入编号', trigger: 'blur' }],
kbh: [{ required: true, message: '请输入课标号', trigger: 'blur' }],
mc: [{ required: true, message: '请输入课程名称', trigger: 'blur' }],
syzy: [{ required: true, message: '请输入适用专业', trigger: 'blur' }],
kcs: [{ required: true, message: '请输入课时数', trigger: 'blur' }],
zxrbh: [{ required: true, message: '请输入录入人编号', trigger: 'blur' }]
},
// ==================== 撤回弹窗 ====================
withdrawDialogVisible: false,
withdrawLoading: false,
withdrawForm: { bh: '', chyy: '' }
}
},
computed: {
dialogTitle() {
return this.isEdit ? '编辑课程科目' : '新增课程科目'
},
fileName() {
return (this.selectedFile && this.selectedFile.name) || '未选择任何文件'
}
},
created() {
// 初始化表单(保证 getCurrentTime 可用)
this.addForm = this.createEmptyForm()
},
mounted() {
this.fetchList()
},
methods: {
// ==================== 表单辅助 ====================
getCurrentTime() {
return new Date().toLocaleString('zh-CN', { hour12: false }).replace(/\//g, '-')
},
/** 创建空白课程表单(含一条空课堂记录) */
createEmptyForm() {
return {
bh: '', kbh: '', mc: '', syzy: '', kcs: 0, bz: '', zxrbh: '', zxsj: this.getCurrentTime(),
zxzt: '0', sbzt: '0', sbsj: '', sbrbh: '', chyy: '', zw: '', jsonzd: '', ktList: [this.defaultClassroom()]
}
},
/** 默认课堂项 */
defaultClassroom() {
return { bh: '', ktjc: 1, jxnr: '', jxyd: '', jxff: '', zjjys: 0, fjjys: 0, bz: '', jxmd: '', jxyq: '' }
},
resetForm() {
this.addForm = this.createEmptyForm()
},
// ==================== 列表加载 ====================
// TODO: 后端接口未提供,暂用模拟数据;接口就绪后替换为 getCourseTeachingList
fetchList() {
this.loading = true
setTimeout(() => {
this.tableData = [...this.mockTableData]
this.total = this.mockTableData.length
this.loading = false
}, 200)
},
// ==================== 查询 ====================
handleQuery() {
const now = new Date()
const hh = String(now.getHours()).padStart(2, '0')
const mm = String(now.getMinutes()).padStart(2, '0')
const ss = String(now.getSeconds()).padStart(2, '0')
this.statusTip = `[${hh}:${mm}:${ss}]查询成功!共检索到${this.total || 982}条记录。`
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 ids = this.selectedRows.map((r) => r.id)
this.tableData = this.tableData.filter((r) => ids.indexOf(r.id) === -1)
this.total = this.tableData.length
this.$message.success(`已删除所选 ${ids.length} 条记录(前端模拟)`)
},
// ==================== 下载 ====================
downloadBlob(content, filename) {
const blob = new Blob(['\ufeff' + content], { type: 'text/plain;charset=utf-8;' })
const link = document.createElement('a')
link.href = URL.createObjectURL(blob)
link.download = filename
link.click()
URL.revokeObjectURL(link.href)
},
handleDownloadCourse() {
this.downloadBlob('编码,课程名称,分类,考核类型方式,课内学时,课外学时\n', '课程科目基本信息.txt')
this.$message.success('课程科目基本信息已下载(前端模拟)')
},
handleDownloadTextbook() {
this.downloadBlob('课程名称,教材,在职主讲教员\n', '课程教材基本信息.txt')
this.$message.success('课程教材基本信息已下载(前端模拟)')
},
handleDownloadTemplate() {
this.downloadBlob('编码,课程名称,分类,考核类型方式,课内学时,课外学时,教材,开设课程专业名称,在职主讲教员\n', '课程科目数据文件模板.txt')
this.$message.success('【课程科目数据文件模板】已下载(前端模拟)')
},
// ==================== 文件上传 ====================
handleSelectFile() {
this.$refs.fileInputRef && this.$refs.fileInputRef.click()
},
handleFileChange(e) {
const input = e.target
this.selectedFile = (input.files && input.files[0]) || null
},
handleUpload() {
if (!this.selectedFile) {
this.$message.warning('请先选择文件')
return
}
this.$message.success(`文件「${this.selectedFile.name}」上传成功(前端模拟)`)
},
// ==================== 新增/编辑弹窗 ====================
handleOpenDialog() {
this.isEdit = false
this.resetForm()
this.dialogVisible = true
},
handleAddClassroom() {
this.addForm.ktList.push(this.defaultClassroom())
},
handleDeleteClassroom(index) {
this.addForm.ktList.splice(index, 1)
},
// TODO: 后端接口未提供,保存成功后刷新列表为前端模拟;接口就绪后替换为 addCourseTeaching/updateCourseTeaching
handleSubmit() {
this.$refs.addFormRef.validate((valid) => {
if (!valid) return
this.addLoading = true
setTimeout(() => {
this.$message.success(this.isEdit ? '修改成功' : '保存成功')
this.dialogVisible = false
this.fetchList()
this.addLoading = false
}, 300)
})
},
handleCancel() {
this.dialogVisible = false
},
// ==================== 撤回弹窗 ====================
// TODO: 后端接口未提供,撤回为前端模拟;接口就绪后替换为 deleteCourseTeaching
handleWithdraw() {
if (!this.withdrawForm.chyy.trim()) {
this.$message.warning('请输入撤回原因')
return
}
this.withdrawLoading = true
setTimeout(() => {
this.$message.success('撤回成功')
this.withdrawDialogVisible = false
this.fetchList()
this.withdrawLoading = false
}, 300)
},
handleCancelWithdraw() {
this.withdrawDialogVisible = 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;
}
.range-control {
display: flex;
align-items: center;
gap: 6px;
.el-input {
flex: 1;
}
.range-sep {
flex-shrink: 0;
font-size: 12px;
color: #606266;
}
}
}
.notice {
font-size: 12px;
color: #f56c6c;
margin-top: 8px;
text-align: left;
}
.query-footer {
display: flex;
justify-content: flex-end;
margin-top: 12px;
padding-top: 12px;
border-top: 1px solid #ebeef5;
}
}
// ==================== 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;
}
}
// ==================== 新增/编辑弹窗 ====================
.add-dialog {
::v-deep(.el-dialog__body) {
max-height: 70vh;
overflow: auto;
}
}
.add-form {
.w-full {
width: 100%;
}
.classroom-section {
margin-top: 16px;
.classroom-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 12px;
.classroom-title {
font-size: 15px;
font-weight: 600;
color: #303133;
}
}
}
// 课堂列表删除按钮红色文字
.text-danger {
color: #f56c6c;
padding: 0;
}
}
.dialog-footer {
display: flex;
justify-content: flex-end;
gap: 12px;
}
.gray-btn {
height: 28px;
padding: 2px 16px;
font-size: 12px;
background: #f5f5f5;
border: 1px solid #c0c4cc;
color: #333;
border-radius: 2px;
&:hover,
&:focus {
background: #ebebeb;
border-color: #c0c4cc;
color: #333;
}
}
</style>