前端文件

This commit is contained in:
2026-09-10 17:42:46 +08:00
parent 255a0e56a5
commit 345f425a1d
361 changed files with 73359 additions and 0 deletions
@@ -0,0 +1,495 @@
<template>
<div class="app-container venue-page">
<!-- 查询条件 -->
<el-card shadow="never" class="search-card">
<el-form :model="searchForm" label-width="100px" class="search-form" @submit.native.prevent>
<el-row :gutter="24">
<el-col :xs="24" :md="8">
<el-form-item label="教室编号">
<el-input v-model="searchForm.jsbh" placeholder="请输入教室编号" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :md="8">
<el-form-item label="教室名称">
<el-input v-model="searchForm.jsmc" placeholder="请输入教室名称" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :md="8">
<el-form-item label="教学楼代号">
<el-input v-model="searchForm.jxldh" placeholder="请输入教学楼代号" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :md="8">
<el-form-item label="教学场地类型">
<el-select v-model="searchForm.jxcdlx" placeholder="请选择场地类型" clearable filterable allow-create
default-first-option class="w-full">
<el-option v-for="item in jxcdlxOptions" :key="item" :label="item" :value="item" />
</el-select>
</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="false">否</el-radio>
<el-radio :label="true">是</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 icon="el-icon-download" @click="handleDownloadTemplate">【教学场地管理模板】下载</el-button>
</div>
</div>
<el-table v-loading="loading" :data="tableData" border stripe highlight-current-row>
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="jsbh" label="教室编号" width="130" align="center" show-overflow-tooltip />
<el-table-column prop="jsmc" label="教室名称" width="140" align="center" show-overflow-tooltip />
<el-table-column prop="jxldh" label="教学楼代号" width="150" align="center" show-overflow-tooltip />
<el-table-column prop="jxcdlx" label="教学场地类型" width="150" align="center" show-overflow-tooltip />
<el-table-column prop="jc" label="简称" width="100" align="center" show-overflow-tooltip />
<el-table-column prop="rnrs" label="容纳人数" width="90" align="center" />
<el-table-column prop="mj" label="面积" width="90" align="center" />
<el-table-column prop="ksrnrs" label="考试容纳人数" width="110" align="center" />
<el-table-column prop="ewyxkbs" label="额外可开班数" width="100" align="center" />
<el-table-column label="虚实类型" width="90" align="center">
<template slot-scope="scope">{{ fmtXslx(scope.row.xslx) }}</template>
</el-table-column>
<el-table-column label="停用" width="80" align="center">
<template slot-scope="scope">
<el-tag :type="isTrue(scope.row.ty) ? 'danger' : 'success'" size="mini">
{{ isTrue(scope.row.ty) ? '停用' : '正常' }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="sssb" label="设施设备" width="140" show-overflow-tooltip header-align="center" />
<el-table-column label="操作" align="center" fixed="right">
<template slot-scope="scope">
<el-button type="text" size="mini" icon="el-icon-edit" @click="handleEdit(scope.row)">编辑</el-button>
<el-button type="text" size="mini" icon="el-icon-delete" 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="780px" append-to-body
:close-on-click-modal="false">
<el-form ref="classroomForm" :model="dialog.form" :rules="rules" label-width="110px">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="教室名称" prop="jsmc">
<el-input v-model="dialog.form.jsmc" placeholder="请输入教室名称" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="教学楼代号">
<el-input v-model="dialog.form.jxldh" placeholder="请输入教学楼代号" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="教学场地类型">
<el-select v-model="dialog.form.jxcdlx" placeholder="请选择场地类型" clearable filterable allow-create
default-first-option class="w-full">
<el-option v-for="item in jxcdlxOptions" :key="item" :label="item" :value="item" />
</el-select>
</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.rnrs" :min="0" controls-position="right" class="w-full" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="面积">
<div class="number-unit-field">
<el-input-number
v-model="dialog.form.mj"
:min="0"
:precision="1"
controls-position="right"
/>
<span class="number-unit">㎡</span>
</div>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="考试容纳人数">
<el-input-number v-model="dialog.form.ksrnrs" :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.ewyxkbs" :min="0" 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.py" placeholder="请输入拼音" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="虚实类型">
<el-radio-group v-model="dialog.form.xslx">
<el-radio :label="true">实教</el-radio>
<el-radio :label="false">非实教</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.ty">
<el-radio :label="false">正常</el-radio>
<el-radio :label="true">停用</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="设施设备">
<el-input v-model="dialog.form.sssb" placeholder="请输入设施设备" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="备注">
<el-input v-model="dialog.form.bz" type="textarea" :rows="2" placeholder="请输入备注" />
</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>
</div>
</template>
<script>
import { saveAs } from 'file-saver'
import {
addClassroom,
deleteClassroom,
updateClassroom,
listClassroom,
listAllClassroom,
downloadClassroomTemplate
} from '@/api/teachBusiness/classroom'
export default {
name: 'Venue',
data() {
return {
loading: false,
searchForm: {
jsbh: '',
jsmc: '',
jxldh: '',
jxcdlx: '',
ty: false
},
tableData: [],
jxcdlxOptions: [],
total: 0,
pageNum: 1,
pageSize: 20,
dialog: {
visible: false,
title: '',
submitting: false,
form: this.createEmptyForm()
},
rules: {
jsmc: [{ required: true, message: '请输入教室名称', trigger: 'blur' }]
}
}
},
created() {
this.loadVenueTypeOptions()
this.fetchList()
},
methods: {
/**
* 独立加载全部场地类型,避免查询结果收窄后下拉候选项同步丢失。
*/
loadVenueTypeOptions() {
return listAllClassroom().then(response => {
this.jxcdlxOptions = this.collectVenueTypes(response.data || [])
}).catch(() => {
// 全量接口异常时保留已收集选项,避免影响列表的基本查询功能。
this.mergeVenueTypeOptions(this.tableData)
})
},
collectVenueTypes(classrooms) {
const venueTypes = new Set()
classrooms.forEach(classroom => {
const venueType = classroom.jxcdlx && String(classroom.jxcdlx).trim()
if (venueType) {
venueTypes.add(venueType)
}
})
return Array.from(venueTypes).sort((first, second) => first.localeCompare(second, 'zh-CN'))
},
mergeVenueTypeOptions(classrooms) {
const venueTypes = new Set(this.jxcdlxOptions)
this.collectVenueTypes(classrooms).forEach(venueType => venueTypes.add(venueType))
this.jxcdlxOptions = Array.from(venueTypes)
.sort((first, second) => first.localeCompare(second, 'zh-CN'))
},
/* ---------- 列表加载 ---------- */
fetchList() {
this.loading = true
const params = {
pageNum: this.pageNum,
pageSize: this.pageSize
}
Object.keys(this.searchForm).forEach(key => {
const value = this.searchForm[key]
if (value !== '' && value !== null && value !== undefined) {
params[key] = value
}
})
listClassroom(params).then(response => {
const data = response.data || {}
this.tableData = data.records || []
this.mergeVenueTypeOptions(this.tableData)
this.total = data.total || 0
}).catch(() => {
this.tableData = []
this.total = 0
}).finally(() => {
this.loading = false
})
},
/* ---------- 查询 / 重置 ---------- */
handleQuery() {
this.pageNum = 1
this.fetchList()
},
handleReset() {
this.searchForm = {
jsbh: '',
jsmc: '',
jxldh: '',
jxcdlx: '',
ty: false
}
this.pageNum = 1
this.fetchList()
},
/* ---------- 分页 ---------- */
handleSizeChange(size) {
this.pageSize = size
this.pageNum = 1
this.fetchList()
},
handlePageChange(page) {
this.pageNum = page
this.fetchList()
},
/* ---------- 新增 / 编辑 ---------- */
handleAdd() {
this.dialog.title = '新增教学场地'
this.dialog.form = this.createEmptyForm()
this.dialog.visible = true
this.$nextTick(() => {
if (this.$refs.classroomForm) this.$refs.classroomForm.clearValidate()
})
},
handleEdit(row) {
this.dialog.title = '编辑教学场地'
this.dialog.form = Object.assign({}, this.createEmptyForm(), row)
this.dialog.visible = true
this.$nextTick(() => {
if (this.$refs.classroomForm) this.$refs.classroomForm.clearValidate()
})
},
submitDialog() {
this.$refs.classroomForm.validate(valid => {
if (!valid) return
this.dialog.submitting = true
const payload = this.cleanPayload(this.dialog.form)
const isEdit = !!payload.id
const req = isEdit ? updateClassroom(payload) : addClassroom(payload)
req.then(() => {
this.$message.success(isEdit ? '修改成功' : '新增成功')
this.dialog.visible = false
this.loadVenueTypeOptions()
this.fetchList()
}).catch(() => {}).finally(() => {
this.dialog.submitting = false
})
})
},
/* ---------- 删除(后端仅提供单条删除,参数为 id) ---------- */
handleDelete(row) {
this.$confirm('确定删除教室「' + (row.jsmc || row.jsbh) + '」吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
return deleteClassroom(row.id)
}).then(() => {
this.$message.success('删除成功')
this.loadVenueTypeOptions()
this.fetchList()
}).catch(() => {})
},
/* ---------- 模板下载 ---------- */
handleDownloadTemplate() {
downloadClassroomTemplate().then(blob => {
saveAs(blob, '教学场地管理模板.xls')
this.$message.success('模板下载成功')
}).catch(() => {})
},
/* ---------- 工具 ---------- */
createEmptyForm() {
return {
id: '',
jsbh: '',
jsmc: '',
jxldh: '',
jxcdlx: '',
jc: '',
rnrs: null,
mj: null,
ksrnrs: null,
ewyxkbs: null,
xh: '',
py: '',
xslx: false,
ty: false,
sssb: '',
bz: ''
}
},
/** 移除空值(''/null/undefined),保留 0/false 等有效值 */
cleanPayload(obj) {
const payload = {}
Object.keys(obj).forEach(key => {
const value = obj[key]
if (value !== '' && value !== null && value !== undefined) {
payload[key] = value
}
})
return payload
},
isTrue(val) {
return val === true || val === 1 || val === '1' || val === 'true'
},
fmtXslx(val) {
return this.isTrue(val) ? '实教' : '非实教'
}
}
}
</script>
<style scoped lang="scss">
.app-container {
padding: 20px;
}
.venue-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;
}
}
}
.number-unit-field {
display: flex;
align-items: center;
gap: 12px;
width: 100%;
.el-input-number {
flex: 1;
width: auto;
min-width: 0;
}
.number-unit {
display: inline-flex;
flex: none;
align-items: center;
justify-content: center;
height: 36px;
color: #606266;
}
}
.danger-text-btn {
color: #f56c6c;
&:hover {
color: #f78989;
}
}
</style>