forked from liweijie/education
前端代码
This commit is contained in:
@@ -0,0 +1,415 @@
|
||||
<template>
|
||||
<div class="page-container">
|
||||
<!-- 近期申请统计卡片 -->
|
||||
<div class="section-card">
|
||||
<div class="section-title">近期课表调整申请</div>
|
||||
<div class="stat-grid">
|
||||
<div
|
||||
v-for="card in statCards"
|
||||
:key="card.title"
|
||||
class="stat-card"
|
||||
:style="{ background: card.gradient }"
|
||||
tabindex="0"
|
||||
role="button"
|
||||
@click="handleStatClick(card)"
|
||||
@keydown.enter="handleStatClick(card)"
|
||||
>
|
||||
<span class="stat-text">{{ card.title }}</span>
|
||||
<span class="stat-value">{{ card.value }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 查询条件 -->
|
||||
<div class="section-card">
|
||||
<el-form :model="queryParams" :inline="true" size="small" class="search-form">
|
||||
<el-form-item label="课程名称">
|
||||
<el-input v-model="queryParams.kcmc" placeholder="请输入课程名称" clearable style="width: 180px" @keyup.enter.native="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="队别(班次)">
|
||||
<el-input v-model="queryParams.dbbc" placeholder="请输入队别班次" clearable style="width: 160px" @keyup.enter.native="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="申请教员">
|
||||
<el-input v-model="queryParams.sqjy" placeholder="请输入申请教员" clearable style="width: 160px" @keyup.enter.native="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态">
|
||||
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable style="width: 140px">
|
||||
<el-option v-for="item in statusOptions" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" @click="handleQuery">查询</el-button>
|
||||
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<!-- 调课调整申请列表 -->
|
||||
<div class="section-card">
|
||||
<div class="section-header">
|
||||
<div class="section-title">调停补课申请列表</div>
|
||||
<div class="header-actions">
|
||||
<el-button type="primary" plain icon="el-icon-plus" size="small" @click="openAdd">新增申请</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-table :data="filteredData" v-loading="loading" size="small" border stripe class="compact-table" max-height="520">
|
||||
<el-table-column type="index" label="序号" width="55" align="center" />
|
||||
<el-table-column prop="kcmc" label="课程名称" min-width="140" show-overflow-tooltip />
|
||||
<el-table-column prop="jxsj" label="教学时间" width="110" align="center" />
|
||||
<el-table-column prop="sksj" label="上课时间" width="180" />
|
||||
<el-table-column prop="jy" label="教员" width="90" align="center" />
|
||||
<el-table-column prop="jxcd" label="教学场地" width="120" show-overflow-tooltip />
|
||||
<el-table-column prop="dbbc" label="队别(班次)" min-width="140" show-overflow-tooltip />
|
||||
<el-table-column prop="sqjy" label="申请教员" width="90" align="center" />
|
||||
<el-table-column prop="zxkb" label="拟调整时间" width="180" />
|
||||
<el-table-column label="申请类型" width="90" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-tag :type="typeTag(scope.row.lx)" size="mini">{{ scope.row.lx }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" width="90" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-tag :type="statusTag(scope.row.status).type" size="mini">{{ statusTag(scope.row.status).label }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="150" align="center" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-button type="text" size="mini" icon="el-icon-view" @click="openDetail(scope.row)">查看</el-button>
|
||||
<template v-if="scope.row.status === '0'">
|
||||
<el-button type="text" size="mini" icon="el-icon-check" @click="approve(scope.row)">批准</el-button>
|
||||
<el-button type="text" size="mini" icon="el-icon-close" class="danger-btn" @click="reject(scope.row)">驳回</el-button>
|
||||
</template>
|
||||
<el-button v-else type="text" size="mini" class="danger-btn" @click="removeRow(scope.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-empty v-show="!loading && filteredData.length === 0" description="暂无调整申请" />
|
||||
</div>
|
||||
|
||||
<!-- 新增申请弹窗 -->
|
||||
<el-dialog title="新增课表调整申请" :visible.sync="dialogVisible" width="640px" append-to-body>
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="100px">
|
||||
<el-form-item label="课程名称" prop="kcmc">
|
||||
<el-input v-model="form.kcmc" placeholder="请输入课程名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="申请类型" prop="lx">
|
||||
<el-radio-group v-model="form.lx">
|
||||
<el-radio label="调课" />
|
||||
<el-radio label="停课" />
|
||||
<el-radio label="补课" />
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="队别(班次)" prop="dbbc">
|
||||
<el-input v-model="form.dbbc" placeholder="请输入队别班次" />
|
||||
</el-form-item>
|
||||
<el-form-item label="原上课时间" prop="sksj">
|
||||
<el-input v-model="form.sksj" placeholder="如:周一 第3-4节" />
|
||||
</el-form-item>
|
||||
<el-form-item label="拟调整时间" prop="zxkb">
|
||||
<el-input v-model="form.zxkb" placeholder="请输入拟调整后的上课时间" />
|
||||
</el-form-item>
|
||||
<el-form-item label="申请理由" prop="bz">
|
||||
<el-input v-model="form.bz" type="textarea" :rows="3" placeholder="请输入申请理由" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" :loading="submitLoading" @click="submitForm">提交申请</el-button>
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 查看详情弹窗 -->
|
||||
<el-dialog title="申请详情" :visible.sync="detailVisible" width="580px" append-to-body>
|
||||
<el-descriptions v-if="detailTarget" :column="2" border size="small">
|
||||
<el-descriptions-item label="课程名称">{{ detailTarget.kcmc }}</el-descriptions-item>
|
||||
<el-descriptions-item label="申请类型">
|
||||
<el-tag :type="typeTag(detailTarget.lx)" size="mini">{{ detailTarget.lx }}</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="队别(班次)">{{ detailTarget.dbbc }}</el-descriptions-item>
|
||||
<el-descriptions-item label="教员">{{ detailTarget.jy }} / {{ detailTarget.sqjy }}</el-descriptions-item>
|
||||
<el-descriptions-item label="教学场地">{{ detailTarget.jxcd }}</el-descriptions-item>
|
||||
<el-descriptions-item label="执行时间">{{ detailTarget.zxsj }}</el-descriptions-item>
|
||||
<el-descriptions-item label="原上课时间" :span="2">{{ detailTarget.sksj }}</el-descriptions-item>
|
||||
<el-descriptions-item label="拟调整时间" :span="2">{{ detailTarget.zxkb }}</el-descriptions-item>
|
||||
<el-descriptions-item label="申请理由" :span="2">{{ detailTarget.bz }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="detailVisible = false">关闭</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// 课表调整申请静态示例数据(待对接 getTimetableAdjustList 接口)
|
||||
const MOCK_LIST = [
|
||||
{ id: 1, kcmc: '高等数学', lx: '调课', jxsj: '2026-2027-1', sksj: '周一 第1-2节', zxkb: '周二 第3-4节', jy: '张教员', sqjy: '张教员', jxcd: '1号教学楼-101', dbbc: '一班', zxsj: '2026-09-01', bz: '因学院集中活动调整上课时间', status: '0' },
|
||||
{ id: 2, kcmc: '大学英语', lx: '停课', jxsj: '2026-2027-1', sksj: '周三 第5-6节', zxkb: '—', jy: '李教员', sqjy: '王教员', jxcd: '1号教学楼-205', dbbc: '二班', zxsj: '2026-09-03', bz: '教员参加教研会议,申请停课顺延', status: '1' },
|
||||
{ id: 3, kcmc: '计算机基础', lx: '补课', jxsj: '2026-2027-1', sksj: '周五 第7-8节', zxkb: '周六 第1-2节', jy: '王教员', sqjy: '王教员', jxcd: '实验楼-303', dbbc: '三班', zxsj: '2026-09-04', bz: '补上周五因停课落下的课程', status: '1' },
|
||||
{ id: 4, kcmc: '军事理论', lx: '调课', jxsj: '2026-2027-1', sksj: '周四 第3-4节', zxkb: '周五 第3-4节', jy: '赵教员', sqjy: '赵教员', jxcd: '3号教学楼-110', dbbc: '一班', zxsj: '2026-09-05', bz: '场地临时被占用,调换上课时间', status: '2' },
|
||||
{ id: 5, kcmc: '高等数学', lx: '补课', jxsj: '2026-2027-1', sksj: '周二 第1-2节', zxkb: '周四 第7-8节', jy: '张教员', sqjy: '张教员', jxcd: '1号教学楼-101', dbbc: '二班', zxsj: '2026-09-06', bz: '补国庆假期调休停上的课程', status: '0' }
|
||||
]
|
||||
|
||||
// 状态描述
|
||||
const STATUS_MAP = {
|
||||
'0': { label: '待审批', type: 'warning' },
|
||||
'1': { label: '已批准', type: 'success' },
|
||||
'2': { label: '已驳回', type: 'danger' }
|
||||
}
|
||||
|
||||
export default {
|
||||
name: 'TimetableAdjust',
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
submitLoading: false,
|
||||
allData: [],
|
||||
queryParams: {
|
||||
kcmc: undefined,
|
||||
dbbc: undefined,
|
||||
sqjy: undefined,
|
||||
status: undefined
|
||||
},
|
||||
statusOptions: [
|
||||
{ label: '待审批', value: '0' },
|
||||
{ label: '已批准', value: '1' },
|
||||
{ label: '已驳回', value: '2' }
|
||||
],
|
||||
statCards: [],
|
||||
dialogVisible: false,
|
||||
detailVisible: false,
|
||||
detailTarget: null,
|
||||
form: {},
|
||||
rules: {
|
||||
kcmc: [{ required: true, message: '请输入课程名称', trigger: 'blur' }],
|
||||
lx: [{ required: true, message: '请选择申请类型', trigger: 'change' }],
|
||||
dbbc: [{ required: true, message: '请输入队别班次', trigger: 'blur' }],
|
||||
bz: [{ required: true, message: '请输入申请理由', trigger: 'blur' }]
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
filteredData() {
|
||||
const q = this.queryParams
|
||||
return this.allData.filter(item => {
|
||||
if (q.kcmc && !(item.kcmc || '').includes(q.kcmc)) return false
|
||||
if (q.dbbc && !(item.dbbc || '').includes(q.dbbc)) return false
|
||||
if (q.sqjy && !(item.sqjy || '').includes(q.sqjy)) return false
|
||||
if (q.status !== undefined && q.status !== '' && item.status !== q.status) return false
|
||||
return true
|
||||
})
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.loadData()
|
||||
},
|
||||
methods: {
|
||||
typeTag(lx) {
|
||||
const map = { '调课': 'warning', '停课': 'danger', '补课': 'success' }
|
||||
return map[lx] || 'info'
|
||||
},
|
||||
statusTag(status) {
|
||||
return STATUS_MAP[status] || { label: status, type: 'info' }
|
||||
},
|
||||
loadData() {
|
||||
this.loading = true
|
||||
// 模拟接口延迟,后续替换为真实接口
|
||||
setTimeout(() => {
|
||||
this.allData = MOCK_LIST.map((item, index) => ({ ...item, xh: index + 1 }))
|
||||
this.refreshStatCards()
|
||||
this.loading = false
|
||||
}, 200)
|
||||
},
|
||||
refreshStatCards() {
|
||||
const total = this.allData.length
|
||||
const pending = this.allData.filter(i => i.status === '0').length
|
||||
const approved = this.allData.filter(i => i.status === '1').length
|
||||
const rejected = this.allData.filter(i => i.status === '2').length
|
||||
this.statCards = [
|
||||
{ title: '申请总数', value: total, gradient: 'linear-gradient(135deg, #00875a 0%, #00b877 100%)' },
|
||||
{ title: '待审批', value: pending, gradient: 'linear-gradient(135deg, #e6a23c 0%, #f8b04c 100%)' },
|
||||
{ title: '已批准', value: approved, gradient: 'linear-gradient(135deg, #409eff 0%, #66b1ff 100%)' },
|
||||
{ title: '已驳回', value: rejected, gradient: 'linear-gradient(135deg, #f56c6c 0%, #f78989 100%)' }
|
||||
]
|
||||
},
|
||||
handleStatClick(card) {
|
||||
const statusMap = { '待审批': '0', '已批准': '1', '已驳回': '2' }
|
||||
if (statusMap[card.title]) {
|
||||
this.queryParams.status = this.queryParams.status === statusMap[card.title] ? undefined : statusMap[card.title]
|
||||
this.$message.info(`已按「${card.title}」筛选`)
|
||||
}
|
||||
},
|
||||
handleQuery() {
|
||||
this.$message.success('查询完成')
|
||||
},
|
||||
resetQuery() {
|
||||
this.queryParams = {
|
||||
kcmc: undefined,
|
||||
dbbc: undefined,
|
||||
sqjy: undefined,
|
||||
status: undefined
|
||||
}
|
||||
},
|
||||
openAdd() {
|
||||
this.form = { kcmc: '', lx: '调课', dbbc: '', sksj: '', zxkb: '', bz: '' }
|
||||
this.$nextTick(() => this.$refs.form && this.$refs.form.clearValidate())
|
||||
this.dialogVisible = true
|
||||
},
|
||||
submitForm() {
|
||||
this.$refs.form.validate(valid => {
|
||||
if (!valid) return
|
||||
this.submitLoading = true
|
||||
setTimeout(() => {
|
||||
const nextId = this.allData.length ? Math.max(...this.allData.map(i => i.id)) + 1 : 1
|
||||
this.allData.unshift({
|
||||
id: nextId,
|
||||
kcmc: this.form.kcmc,
|
||||
lx: this.form.lx,
|
||||
jxsj: '2026-2027-1',
|
||||
sksj: this.form.sksj || '待定',
|
||||
zxkb: this.form.lx === '停课' ? '—' : (this.form.zxkb || '待定'),
|
||||
jy: '-',
|
||||
sqjy: this.form.sqjy || '-',
|
||||
jxcd: '-',
|
||||
dbbc: this.form.dbbc,
|
||||
zxsj: this.getNow(),
|
||||
bz: this.form.bz,
|
||||
status: '0'
|
||||
})
|
||||
this.refreshStatCards()
|
||||
this.submitLoading = false
|
||||
this.dialogVisible = false
|
||||
this.$message.success('申请已提交')
|
||||
}, 300)
|
||||
})
|
||||
},
|
||||
getNow() {
|
||||
const d = new Date()
|
||||
const p = n => (n < 10 ? '0' + n : '' + n)
|
||||
return d.getFullYear() + '-' + p(d.getMonth() + 1) + '-' + p(d.getDate())
|
||||
},
|
||||
approve(row) {
|
||||
row.status = '1'
|
||||
this.refreshStatCards()
|
||||
this.$message.success('已批准该申请')
|
||||
},
|
||||
reject(row) {
|
||||
row.status = '2'
|
||||
this.refreshStatCards()
|
||||
this.$message.warning('已驳回该申请')
|
||||
},
|
||||
removeRow(row) {
|
||||
this.$confirm('确认删除该申请?', '提示', { type: 'warning' })
|
||||
.then(() => {
|
||||
this.allData = this.allData.filter(i => i.id !== row.id)
|
||||
this.refreshStatCards()
|
||||
this.$message.success('删除成功')
|
||||
})
|
||||
.catch(() => {})
|
||||
},
|
||||
openDetail(row) {
|
||||
this.detailTarget = row
|
||||
this.detailVisible = true
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.page-container {
|
||||
padding: 16px;
|
||||
|
||||
.section-card {
|
||||
background: #fff;
|
||||
border: 1px solid #ebeef5;
|
||||
border-radius: 6px;
|
||||
padding: 16px 20px;
|
||||
margin-bottom: 16px;
|
||||
|
||||
.section-title {
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.section-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
margin-bottom: 16px;
|
||||
|
||||
.header-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 统计卡片 */
|
||||
.stat-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 16px;
|
||||
margin-top: 14px;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
padding: 16px 20px;
|
||||
border-radius: 6px;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
.stat-text {
|
||||
font-size: 13px;
|
||||
opacity: 0.92;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
margin-top: 8px;
|
||||
font-size: 26px;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* 查询区域 */
|
||||
.search-form {
|
||||
margin-bottom: -16px;
|
||||
|
||||
::v-deep .el-form-item {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.compact-table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.danger-btn {
|
||||
color: #f56c6c;
|
||||
|
||||
&:hover {
|
||||
color: #f78989;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 992px) {
|
||||
.page-container .stat-grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user