forked from liweijie/education
453 lines
17 KiB
Vue
453 lines
17 KiB
Vue
<template>
|
||
<div class="app-container timetable-page">
|
||
<el-tabs v-model="activeTab" class="timetable-tabs">
|
||
<!-- ==================== 今日教学实施计划 ==================== -->
|
||
<el-tab-pane label="今日教学实施计划" name="today">
|
||
<div class="tab-body">
|
||
<div class="date-header">
|
||
<el-link type="primary" :underline="false" class="nav-link" @click="handlePrevDay">前一天</el-link>
|
||
<div class="date-title">{{ dateTitle }}</div>
|
||
<el-link type="primary" :underline="false" class="nav-link" @click="handleNextDay">后一天</el-link>
|
||
</div>
|
||
|
||
<div class="table-card">
|
||
<el-table :data="todayData" v-loading="loading" stripe border>
|
||
<el-table-column type="index" label="序号" width="55" align="center" />
|
||
<el-table-column label="上课时间" width="130" align="center">
|
||
<template #default="{ row }">{{ formatJc(row.jc) }}</template>
|
||
</el-table-column>
|
||
<el-table-column prop="kcmc" label="课程" min-width="140" show-overflow-tooltip header-align="center" />
|
||
<el-table-column prop="zrdw" label="责任单位" width="110" align="center" />
|
||
<el-table-column prop="bc" label="班次" width="120" show-overflow-tooltip header-align="center" />
|
||
<el-table-column prop="skjy" label="教员" width="90" align="center" />
|
||
<el-table-column label="场地" width="140" show-overflow-tooltip header-align="center">
|
||
<template #default="{ row }">{{ row.jxcd || '-' }}</template>
|
||
</el-table-column>
|
||
<el-table-column label="教学内容方法" min-width="160" show-overflow-tooltip header-align="center">
|
||
<template #default="{ row }">{{ row.jxnrff || '-' }}</template>
|
||
</el-table-column>
|
||
<el-table-column prop="jybz" label="备注" width="100" show-overflow-tooltip header-align="center">
|
||
<template #default="{ row }">{{ row.jybz || '-' }}</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
<el-empty v-show="!loading && !todayData.length" description="暂无教学计划数据" />
|
||
</div>
|
||
</div>
|
||
</el-tab-pane>
|
||
|
||
<!-- ==================== 本周教学实施计划 ==================== -->
|
||
<el-tab-pane label="本周教学实施计划" name="week">
|
||
<div class="tab-body">
|
||
<div class="week-header">
|
||
<el-link type="primary" :underline="false" class="nav-link" @click="handlePrevWeek">上一周</el-link>
|
||
<div class="week-title">{{ weekRangeText }}</div>
|
||
<el-link type="primary" :underline="false" class="nav-link" @click="handleNextWeek">下一周</el-link>
|
||
</div>
|
||
|
||
<div class="table-card">
|
||
<el-table :data="weekData" v-loading="loading" stripe border>
|
||
<el-table-column type="index" label="序号" width="55" align="center" />
|
||
<el-table-column label="上课时间" width="170" align="center" show-overflow-tooltip>
|
||
<template #default="{ row }">{{ row.sjsj || '-' }}</template>
|
||
</el-table-column>
|
||
<el-table-column prop="kcmc" label="课程" min-width="130" show-overflow-tooltip header-align="center" />
|
||
<el-table-column prop="zrdw" label="责任单位" width="100" align="center" />
|
||
<el-table-column prop="bc" label="班次" width="130" show-overflow-tooltip header-align="center" />
|
||
<el-table-column prop="skjy" label="教员" width="90" align="center" />
|
||
<el-table-column label="场地" width="140" show-overflow-tooltip header-align="center">
|
||
<template #default="{ row }">{{ row.jxcd || '-' }}</template>
|
||
</el-table-column>
|
||
<el-table-column label="教学内容方法" min-width="150" show-overflow-tooltip header-align="center">
|
||
<template #default="{ row }">{{ row.jxnrff || '-' }}</template>
|
||
</el-table-column>
|
||
<el-table-column prop="jybz" label="备注" width="100" show-overflow-tooltip header-align="center">
|
||
<template #default="{ row }">{{ row.jybz || '-' }}</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
<el-empty v-show="!loading && !weekData.length" description="本周暂无教学计划数据" />
|
||
</div>
|
||
</div>
|
||
</el-tab-pane>
|
||
|
||
<!-- ==================== 教学班次教学实施计划 ==================== -->
|
||
<el-tab-pane label="教学班次教学实施计划" name="shift">
|
||
<div class="tab-body">
|
||
<!-- 选择条件 -->
|
||
<div class="search-card">
|
||
<el-form :model="searchForm" inline class="search-form">
|
||
<el-form-item label="队别班次">
|
||
<el-select v-model="searchForm.teamClass" placeholder="请选择队别班次" clearable filterable class="shift-select">
|
||
<el-option v-for="item in teamClassOptions" :key="item" :label="item" :value="item" />
|
||
</el-select>
|
||
</el-form-item>
|
||
<el-form-item>
|
||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">查询</el-button>
|
||
</el-form-item>
|
||
</el-form>
|
||
</div>
|
||
|
||
<!-- 标题 -->
|
||
<div class="page-title">{{ pageTitle }}</div>
|
||
|
||
<!-- 下载操作按钮 -->
|
||
<el-card shadow="never" class="action-card">
|
||
<div class="button-row">
|
||
<el-button type="primary" plain size="mini" @click="handleExportPlan">学期教学实施计划</el-button>
|
||
<el-button type="primary" plain size="mini" @click="handleExportGrades">学期成绩表</el-button>
|
||
<el-button type="primary" plain size="mini" @click="handleExportCourses">课程列表另存Excel</el-button>
|
||
</div>
|
||
</el-card>
|
||
|
||
<!-- 数据表格 -->
|
||
<div class="table-card">
|
||
<el-table :data="shiftData" v-loading="loading" border stripe style="width: 100%">
|
||
<el-table-column type="index" label="序号" width="60" align="center" />
|
||
<el-table-column label="课程名称/课时系数" min-width="220" header-align="center">
|
||
<template #default="{ row }">
|
||
<div>{{ row.kcmc || '-' }}</div>
|
||
<div class="sub-text">{{ row.kssk || '-' }}</div>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="zrjs" label="责任教员/课次" min-width="150" show-overflow-tooltip header-align="center" />
|
||
<el-table-column prop="bc" label="班次" min-width="120" show-overflow-tooltip header-align="center" />
|
||
<el-table-column prop="nj" label="年级" width="80" align="center" />
|
||
<el-table-column prop="zy" label="专业" min-width="120" show-overflow-tooltip header-align="center" />
|
||
<el-table-column prop="jhxs" label="计划学时" width="90" align="center" />
|
||
<el-table-column prop="yxxs" label="运行学时" width="90" align="center" />
|
||
<el-table-column prop="khlxfs" label="考核类型方式" min-width="140" show-overflow-tooltip header-align="center" />
|
||
<el-table-column prop="ssjs" label="实施教员" min-width="120" show-overflow-tooltip header-align="center" />
|
||
<el-table-column prop="rscd" label="人数/场地" min-width="140" show-overflow-tooltip header-align="center" />
|
||
<el-table-column prop="kcjd" label="课程进度" width="120" align="center" />
|
||
<el-table-column prop="ssjhbg" label="实施计划变更" min-width="120" align="center" show-overflow-tooltip />
|
||
</el-table>
|
||
<el-empty v-show="!loading && !shiftData.length" description="请选择队别班次后查询" />
|
||
</div>
|
||
</div>
|
||
</el-tab-pane>
|
||
</el-tabs>
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
import { listToday, listWeek, listClass, exportClassPlan, exportClassGrades, exportClassCourses } from '@/api/teachBusiness/timetable'
|
||
import { listTeam } from '@/api/studentRecords/team'
|
||
|
||
const JC_SECTION = {
|
||
1: '1-2节', 2: '3-4节', 3: '5-6节', 4: '7-8节', 5: '9-10节', 6: '11-12节'
|
||
}
|
||
|
||
const WEEK_DAY_NAMES = ['日', '一', '二', '三', '四', '五', '六']
|
||
|
||
function pad2(n) {
|
||
return n < 10 ? '0' + n : '' + n
|
||
}
|
||
|
||
function formatDate(d) {
|
||
return d.getFullYear() + '-' + pad2(d.getMonth() + 1) + '-' + pad2(d.getDate())
|
||
}
|
||
|
||
function getMonday(d) {
|
||
const day = d.getDay() || 7
|
||
const monday = new Date(d)
|
||
monday.setDate(d.getDate() - day + 1)
|
||
return monday
|
||
}
|
||
|
||
export default {
|
||
name: 'Timetable',
|
||
data() {
|
||
return {
|
||
activeTab: 'today',
|
||
loading: false,
|
||
// 今日
|
||
todayDate: new Date(),
|
||
todayData: [],
|
||
// 本周
|
||
weekStart: getMonday(new Date()),
|
||
weekData: [],
|
||
// 班次
|
||
searchForm: { teamClass: '' },
|
||
teamClassOptions: [],
|
||
teamMap: {},
|
||
shiftData: []
|
||
}
|
||
},
|
||
computed: {
|
||
dateTitle() {
|
||
return formatDate(this.todayDate) + ' 今日教学实施计划'
|
||
},
|
||
weekRangeText() {
|
||
const end = new Date(this.weekStart)
|
||
end.setDate(end.getDate() + 6)
|
||
return formatDate(this.weekStart) + '(' + this.weekDayName(this.weekStart) + ') 至 ' +
|
||
formatDate(end) + '(' + this.weekDayName(end) + ') 本周教学实施计划'
|
||
},
|
||
pageTitle() {
|
||
const name = this.searchForm.teamClass || ''
|
||
return (name ? name + ' ' : '') + '教学班次教学实施计划'
|
||
}
|
||
},
|
||
created() {
|
||
this.loadTeamOptions()
|
||
this.getTodayList()
|
||
this.getWeekList()
|
||
},
|
||
methods: {
|
||
weekDayName(d) {
|
||
return WEEK_DAY_NAMES[d.getDay()]
|
||
},
|
||
formatJc(jc) {
|
||
if (jc === null || jc === undefined || jc === '') return '第?节'
|
||
return JC_SECTION[jc] || ('第' + jc + '节')
|
||
},
|
||
/* ---------- 今日:日期导航 ---------- */
|
||
handlePrevDay() {
|
||
const d = new Date(this.todayDate)
|
||
d.setDate(d.getDate() - 1)
|
||
this.todayDate = d
|
||
this.getTodayList()
|
||
},
|
||
handleNextDay() {
|
||
const d = new Date(this.todayDate)
|
||
d.setDate(d.getDate() + 1)
|
||
this.todayDate = d
|
||
this.getTodayList()
|
||
},
|
||
/* ---------- 本周:周次导航 ---------- */
|
||
handlePrevWeek() {
|
||
const d = new Date(this.weekStart)
|
||
d.setDate(d.getDate() - 7)
|
||
this.weekStart = d
|
||
this.getWeekList()
|
||
},
|
||
handleNextWeek() {
|
||
const d = new Date(this.weekStart)
|
||
d.setDate(d.getDate() + 7)
|
||
this.weekStart = d
|
||
this.getWeekList()
|
||
},
|
||
/* ---------- 队别班次下拉(真实接口) ---------- */
|
||
loadTeamOptions() {
|
||
listTeam({ pageNum: 1, pageSize: 1000 }).then(response => {
|
||
const data = response.data || {}
|
||
const list = data.records || []
|
||
const map = {}
|
||
;(list || []).forEach(item => {
|
||
if (item.xydmc) map[item.xydmc] = item.xydbh
|
||
})
|
||
this.teamMap = map
|
||
this.teamClassOptions = list.map(item => item.xydmc).filter(Boolean)
|
||
}).catch(() => {
|
||
this.teamMap = {}
|
||
this.teamClassOptions = []
|
||
})
|
||
},
|
||
/* ---------- 数据字段映射(接口字段 -> 页面字段) ---------- */
|
||
parseSection(sksj) {
|
||
const m = String(sksj || '').match(/第(\d+)节/)
|
||
return m ? Number(m[1]) : undefined
|
||
},
|
||
mapLessonRow(apiRow) {
|
||
// 接口今日/本周实体:bh sksj kc zrdw bc jy cd jxnrxff bz
|
||
const row = Object.assign({}, apiRow)
|
||
row.kcmc = apiRow.kc
|
||
row.skjy = apiRow.jy
|
||
row.jxcd = apiRow.cd
|
||
row.jxnrff = apiRow.jxnrxff
|
||
row.jybz = apiRow.bz
|
||
row.sjsj = apiRow.sksj
|
||
row.jc = this.parseSection(apiRow.sksj)
|
||
return row
|
||
},
|
||
mapClassRow(apiRow) {
|
||
// 接口班次实体:sskcbh kcmcksxs zrjykc jhxs yxxs khlxfs ssjy rscd ssjhbg
|
||
const row = Object.assign({}, apiRow)
|
||
const parts = String(apiRow.kcmcksxs || '').split(' / ')
|
||
row.kcmc = parts[0] || '-'
|
||
row.kssk = parts[1] || '-'
|
||
row.zrjs = apiRow.zrjykc
|
||
row.ssjs = apiRow.ssjy
|
||
return row
|
||
},
|
||
/* ---------- 今日:拉取数据 ---------- */
|
||
getTodayList() {
|
||
this.loading = true
|
||
listToday({}).then(response => {
|
||
const data = response.data
|
||
this.todayData = Array.isArray(data)
|
||
? (data || []).map(item => this.mapLessonRow(item))
|
||
: ((data && data.records) || []).map(item => this.mapLessonRow(item))
|
||
this.loading = false
|
||
}).catch(() => {
|
||
this.todayData = []
|
||
this.loading = false
|
||
})
|
||
},
|
||
/* ---------- 本周:拉取数据 ---------- */
|
||
getWeekList() {
|
||
this.loading = true
|
||
listWeek({}).then(response => {
|
||
const data = response.data
|
||
this.weekData = Array.isArray(data)
|
||
? (data || []).map(item => this.mapLessonRow(item))
|
||
: ((data && data.records) || []).map(item => this.mapLessonRow(item))
|
||
this.loading = false
|
||
}).catch(() => {
|
||
this.weekData = []
|
||
this.loading = false
|
||
})
|
||
},
|
||
/* ---------- 班次:拉取数据 ---------- */
|
||
getShiftList() {
|
||
if (!this.searchForm.teamClass) return
|
||
const xydbh = this.teamMap[this.searchForm.teamClass] || this.searchForm.teamClass
|
||
const params = { xydbh: xydbh, pageNum: 1, pageSize: 1000 }
|
||
this.loading = true
|
||
listClass(params).then(response => {
|
||
const data = response.data || {}
|
||
this.shiftData = (data.records || []).map(item => this.mapClassRow(item))
|
||
this.loading = false
|
||
}).catch(() => {
|
||
this.shiftData = []
|
||
this.loading = false
|
||
})
|
||
},
|
||
/* ---------- 班次:查询 ---------- */
|
||
handleQuery() {
|
||
if (!this.searchForm.teamClass) {
|
||
this.$message.warning('请先选择队别班次')
|
||
return
|
||
}
|
||
this.getShiftList()
|
||
},
|
||
/* ---------- 下载 ---------- */
|
||
downloadBlob(blob, filename) {
|
||
const link = document.createElement('a')
|
||
link.href = window.URL.createObjectURL(blob)
|
||
link.download = filename
|
||
document.body.appendChild(link)
|
||
link.click()
|
||
document.body.removeChild(link)
|
||
window.URL.revokeObjectURL(link.href)
|
||
},
|
||
shiftQueryParams() {
|
||
if (!this.searchForm.teamClass) return null
|
||
const xydbh = this.teamMap[this.searchForm.teamClass] || this.searchForm.teamClass
|
||
return { xydbh: xydbh }
|
||
},
|
||
/** 下载学期教学实施计划 Excel(GET /kcb/class/export-plan) */
|
||
handleExportPlan() {
|
||
const params = this.shiftQueryParams()
|
||
if (!params) {
|
||
this.$message.warning('请先选择队别班次')
|
||
return
|
||
}
|
||
exportClassPlan(params).then(blob => {
|
||
this.downloadBlob(blob, '学期教学实施计划.xlsx')
|
||
this.$message.success('学期教学实施计划导出成功')
|
||
}).catch(() => {})
|
||
},
|
||
/** 下载学期成绩表 Excel(GET /kcb/class/export-grades) */
|
||
handleExportGrades() {
|
||
const params = this.shiftQueryParams()
|
||
if (!params) {
|
||
this.$message.warning('请先选择队别班次')
|
||
return
|
||
}
|
||
exportClassGrades(params).then(blob => {
|
||
this.downloadBlob(blob, '学期成绩表.xlsx')
|
||
this.$message.success('学期成绩表导出成功')
|
||
}).catch(() => {})
|
||
},
|
||
/** 班次课程列表另存 Excel(GET /kcb/class/export-courses) */
|
||
handleExportCourses() {
|
||
const params = this.shiftQueryParams()
|
||
if (!params) {
|
||
this.$message.warning('请先选择队别班次')
|
||
return
|
||
}
|
||
exportClassCourses(params).then(blob => {
|
||
this.downloadBlob(blob, '班次课程列表.xlsx')
|
||
this.$message.success('班次课程列表导出成功')
|
||
}).catch(() => {})
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style scoped lang="scss">
|
||
.timetable-page {
|
||
.timetable-tabs {
|
||
background: #fff;
|
||
padding: 0 16px;
|
||
border: 1px solid #ebeef5;
|
||
border-radius: 6px;
|
||
}
|
||
|
||
.tab-body {
|
||
padding-bottom: 16px;
|
||
}
|
||
|
||
.sub-text {
|
||
font-size: 12px;
|
||
color: #909399;
|
||
margin-top: 2px;
|
||
}
|
||
|
||
/* 日期/周次导航 */
|
||
.date-header,
|
||
.week-header {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 24px;
|
||
margin-bottom: 12px;
|
||
padding: 14px 16px;
|
||
background: #fafafa;
|
||
border: 1px solid #ebeef5;
|
||
border-radius: 4px;
|
||
|
||
.nav-link {
|
||
font-size: 14px;
|
||
}
|
||
|
||
.date-title,
|
||
.week-title {
|
||
font-size: 16px;
|
||
font-weight: 700;
|
||
color: #303133;
|
||
}
|
||
}
|
||
|
||
/* 班次 */
|
||
.search-card {
|
||
margin-bottom: 12px;
|
||
|
||
.search-form {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.shift-select {
|
||
width: 240px;
|
||
}
|
||
}
|
||
|
||
.page-title {
|
||
font-size: 17px;
|
||
font-weight: 700;
|
||
color: #303133;
|
||
text-align: center;
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
.action-card {
|
||
margin-bottom: 12px;
|
||
|
||
.button-row {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 8px;
|
||
}
|
||
}
|
||
}
|
||
</style> |