Files
school-developer/src/views/stuwork/classroomhygienemonthly/index.vue
2026-01-29 16:38:09 +08:00

591 lines
17 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="modern-page-container">
<div class="page-wrapper">
<!-- 搜索表单卡片 -->
<el-card v-show="showSearch" class="search-card" shadow="never">
<template #header>
<div class="card-header">
<span class="card-title">
<el-icon class="title-icon"><Search /></el-icon>
筛选条件 </span>
</div>
</template>
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch" class="search-form">
<el-form-item label="学年" prop="schoolYear">
<el-select
v-model="searchForm.schoolYear"
placeholder="请选择学年"
clearable
filterable
style="width: 200px">
<el-option
v-for="item in schoolYearList"
:key="item.year"
:label="item.year"
:value="item.year">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="学期" prop="schoolTerm">
<el-input
v-model="searchForm.schoolTerm"
placeholder="请输入学期"
clearable
style="width: 200px" />
</el-form-item>
<el-form-item label="月份" prop="month">
<el-date-picker
v-model="searchForm.month"
type="month"
placeholder="请选择月份"
format="YYYY-MM"
value-format="YYYY-MM"
clearable
style="width: 200px" />
</el-form-item>
<el-form-item label="学院" prop="deptCode">
<el-select
v-model="searchForm.deptCode"
placeholder="请选择学院"
clearable
filterable
style="width: 200px">
<el-option
v-for="item in deptList"
:key="item.deptCode"
:label="item.deptName"
:value="item.deptCode">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="班号" prop="classCode">
<el-select
v-model="searchForm.classCode"
placeholder="请选择班号"
clearable
filterable
style="width: 200px">
<el-option
v-for="item in filteredClassList"
:key="item.classCode"
:label="item.classNo"
:value="item.classCode">
</el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" plain icon="Search" @click="handleSearch">查询</el-button>
<el-button icon="Refresh" @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
</el-card>
<!-- 内容卡片 -->
<el-card class="content-card" shadow="never">
<template #header>
<div class="card-header">
<span class="card-title">
<el-icon class="title-icon"><Document /></el-icon>
教室卫生月度分析列表
</span>
<div class="header-actions">
<el-button
icon="Upload"
type="primary"
@click="handleImport">
导入
</el-button>
<el-button
icon="DocumentChecked"
type="success"
class="ml10"
@click="handleCheck">
考核
</el-button>
<right-toolbar
v-model:showSearch="showSearch"
class="ml10"
@queryTable="getDataList">
<TableColumnControl
ref="columnControlRef"
:columns="tableColumns"
v-model="visibleColumns"
trigger-type="default"
trigger-circle
@change="handleColumnChange"
@order-change="handleColumnOrderChange"
>
<template #trigger>
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
<el-button circle style="margin-left: 0;">
<el-icon><Menu /></el-icon>
</el-button>
</el-tooltip>
</template>
</TableColumnControl>
</right-toolbar>
</div>
</div>
</template>
<!-- 表格 -->
<el-table
ref="tableRef"
:data="state.dataList"
v-loading="state.loading"
stripe
:cell-style="tableStyle.cellStyle"
:header-cell-style="tableStyle.headerCellStyle"
class="modern-table"
@sort-change="sortChangeHandle">
<el-table-column type="index" label="序号" width="70" align="center">
<template #header>
<el-icon><List /></el-icon>
</template>
<template #default="{ $index }">
{{ $index + 1 + ((state.pagination?.current || 1) - 1) * (state.pagination?.size || 10) }}
</template>
</el-table-column>
<template v-for="col in visibleColumnsSorted" :key="col.prop">
<el-table-column
v-if="checkColumnVisible(col.prop || '')"
:prop="col.prop"
:label="col.label"
show-overflow-tooltip>
<template #header>
<el-icon><component :is="columnConfigMap[col.prop || '']?.icon || Calendar" /></el-icon>
<span style="margin-left: 4px">{{ col.label }}</span>
</template>
<!-- 学期列特殊模板-->
<template v-if="col.prop === 'schoolTerm'" #default="scope">
<el-tag size="small" type="primary" effect="plain">
{{ formatSchoolTerm(scope.row.schoolTerm) }}
</el-tag>
</template>
<!-- 评分列特殊模板-->
<template v-else-if="col.prop === 'score'" #default="scope">
<el-tag v-if="scope.row.score !== undefined && scope.row.score !== null" size="small" :type="scope.row.score >= 80 ? 'success' : scope.row.score >= 60 ? 'warning' : 'danger'" effect="plain">
{{ scope.row.score }}
</el-tag>
<span v-else>-</span>
</template>
<!-- 月份列特殊模板-->
<template v-else-if="col.prop === 'month'" #default="scope">
<el-tag v-if="scope.row.month" size="small" type="info" effect="plain">
{{ scope.row.month }}
</el-tag>
<span v-else>-</span>
</template>
</el-table-column>
</template>
<el-table-column label="操作" width="150" align="center" fixed="right">
<template #header>
<el-icon><Setting /></el-icon>
<span style="margin-left: 4px">操作</span>
</template>
<template #default="scope">
<el-button
icon="Delete"
link
type="danger"
@click="handleDelete([scope.row.id])">
删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<div class="pagination-wrapper">
<pagination
@size-change="sizeChangeHandle"
@current-change="currentChangeHandle"
v-bind="state.pagination" />
</div>
</el-card>
</div>
<!-- 导入对话框-->
<upload-excel
ref="uploadExcelRef"
:title="'导入教室月卫生'"
:url="'/stuwork/classroomhygienemonthly/importData'"
:temp-url="templateUrl"
@refreshDataList="getDataList" />
<!-- 考核对话框-->
<el-dialog
v-model="checkDialogVisible"
title="教室卫生考核"
:close-on-click-modal="false"
draggable
width="500px">
<el-form
ref="checkFormRef"
:model="checkForm"
label-width="100px">
<el-form-item label="学年" prop="schoolYear">
<el-select
v-model="checkForm.schoolYear"
placeholder="请选择学年"
clearable
filterable
style="width: 100%">
<el-option
v-for="item in schoolYearList"
:key="item.year"
:label="item.year"
:value="item.year">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="学期" prop="schoolTerm">
<el-input
v-model="checkForm.schoolTerm"
placeholder="请输入学期"
clearable />
</el-form-item>
<el-form-item label="月份" prop="month">
<el-date-picker
v-model="checkForm.month"
type="month"
placeholder="请选择月份"
format="YYYY-MM"
value-format="YYYY-MM"
clearable
style="width: 100%" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="checkDialogVisible = false">取消</el-button>
<el-button type="primary" @click="confirmCheck">确定</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts" name="ClassRoomHygieneMonthly">
import { ref, reactive, defineAsyncComponent, computed, onMounted } from 'vue'
import { useRoute } from 'vue-router'
import { BasicTableProps, useTable } from "/@/hooks/table";
import { fetchList, delObjs, checkClassRoomHygieneMonthly } from "/@/api/stuwork/classroomhygienemonthly";
import { useMessage, useMessageBox } from "/@/hooks/message";
import { queryAllSchoolYear } from '/@/api/basic/basicyear'
import { getDeptList } from '/@/api/basic/basicclass'
import { getClassListByRole } from '/@/api/basic/basicclass'
import { getDicts } from '/@/api/admin/dict'
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
import type { TableInstance } from 'element-plus'
// 引入组件
const UploadExcel = defineAsyncComponent(() => import('/@/components/Upload/Excel.vue'));
import { List, Calendar, Clock, OfficeBuilding, Grid, UserFilled, Location, DataAnalysis, Document, Setting, Menu, Search } from '@element-plus/icons-vue'
import { useTableColumnControl } from '/@/hooks/tableColumn'
// 定义变量内容
const route = useRoute()
const searchFormRef = ref()
const uploadExcelRef = ref()
const checkFormRef = ref()
const tableRef = ref<TableInstance>()
const columnControlRef = ref<any>()
// 搜索变量
const showSearch = ref(true)
const schoolYearList = ref<any[]>([])
const schoolTermList = ref<any[]>([])
const deptList = ref<any[]>([])
const classList = ref<any[]>([])
const checkDialogVisible = ref(false)
// 模板文件URL
const templateUrl = ref('assets/file/教室月卫生导入模板.xlsx')
// 表格列配置
const tableColumns = [
{ prop: 'schoolYear', label: '学年' },
{ prop: 'schoolTerm', label: '学期' },
{ prop: 'deptName', label: '学院' },
{ prop: 'classNo', label: '班号' },
{ prop: 'classMasterName', label: '班主任' },
{ prop: 'buildingNo', label: '教学楼号' },
{ prop: 'position', label: '教室位置' },
{ prop: 'score', label: '评分' },
{ prop: 'note', label: '检查记录' },
{ prop: 'month', label: '月份' },
{ prop: '操作', label: '操作', alwaysShow: true, fixed: 'right' as const }
]
// 使用表格列控制hook
const {
visibleColumns,
visibleColumnsSorted,
checkColumnVisible,
handleColumnChange,
handleColumnOrderChange
} = useTableColumnControl(tableColumns.filter(col => !col.alwaysShow && !col.fixed))
// 列配置映射,包含每个列的渲染信息
const columnConfigMap: Record<string, any> = {
schoolYear: {
prop: 'schoolYear',
label: '学年',
icon: Calendar,
template: null
},
schoolTerm: {
prop: 'schoolTerm',
label: '学期',
icon: Clock,
template: 'schoolTerm'
},
deptName: {
prop: 'deptName',
label: '学院',
icon: OfficeBuilding,
template: null
},
classNo: {
prop: 'classNo',
label: '班号',
icon: Grid,
template: null
},
classMasterName: {
prop: 'classMasterName',
label: '班主任',
icon: UserFilled,
template: null
},
buildingNo: {
prop: 'buildingNo',
label: '教学楼号',
icon: OfficeBuilding,
template: null
},
position: {
prop: 'position',
label: '教室位置',
icon: Location,
template: null
},
score: {
prop: 'score',
label: '评分',
icon: DataAnalysis,
template: 'score'
},
note: {
prop: 'note',
label: '检查记录',
icon: Document,
template: null
},
month: {
prop: 'month',
label: '月份',
icon: Calendar,
template: 'month'
}
}
// 初始化函数会在onMounted中调用确保 DOM 已准备好
// 注意visibleColumns和columnOrder需要在组件挂载前初始化以便传递给 TableColumnControl
// 考核表单
const checkForm = reactive({
schoolYear: '',
schoolTerm: '',
month: ''
})
// 搜索表单
const searchForm = reactive({
schoolYear: '',
schoolTerm: '',
month: '',
deptCode: '',
classCode: ''
})
// 根据学院筛选班级列表
const filteredClassList = computed(() => {
if (!searchForm.deptCode) {
return classList.value
}
return classList.value.filter((item: any) => item.deptCode === searchForm.deptCode)
})
// 配置 useTable
const state: BasicTableProps = reactive<BasicTableProps>({
queryForm: searchForm,
pageList: fetchList,
props: {
item: 'records',
totalCount: 'total'
}
})
// table hook
const {
getDataList,
currentChangeHandle,
sizeChangeHandle,
sortChangeHandle,
tableStyle
} = useTable(state)
// 查询
const handleSearch = () => {
getDataList()
}
// 重置
const handleReset = () => {
searchFormRef.value?.resetFields()
searchForm.schoolYear = ''
searchForm.schoolTerm = ''
searchForm.month = ''
searchForm.deptCode = ''
searchForm.classCode = ''
getDataList()
}
// 导入
const handleImport = () => {
(uploadExcelRef.value as any)?.show()
}
// 考核
const handleCheck = () => {
checkDialogVisible.value = true
Object.assign(checkForm, {
schoolYear: '',
schoolTerm: '',
month: ''
})
}
// 确认考核
const confirmCheck = async () => {
if (!checkForm.schoolYear || !checkForm.schoolTerm || !checkForm.month) {
useMessage().warning('请填写完整的考核信息')
return
}
// 格式化月份显示2026-01
const monthDisplay = checkForm.month
try {
await useMessageBox().confirm(
`是否确认对${monthDisplay}月进行考核?\n1如果当前指定年月份已经有考核数据则会做覆盖处理\n2考核对象为所有有评分的班级`
)
} catch {
return
}
try {
await checkClassRoomHygieneMonthly({
schoolYear: checkForm.schoolYear,
schoolTerm: checkForm.schoolTerm,
month: checkForm.month
})
useMessage().success('考核成功')
checkDialogVisible.value = false
getDataList()
} catch (err: any) {
useMessage().error(err.msg || '考核失败')
}
}
// 删除操作
const handleDelete = async (ids: string[]) => {
try {
await useMessageBox().confirm('此操作将永久删除')
} catch {
return
}
try {
await delObjs(ids)
getDataList()
useMessage().success('删除成功')
} catch (err: any) {
useMessage().error(err.msg || '删除失败')
}
}
// 获取学年列表
const getSchoolYearList = async () => {
try {
const res = await queryAllSchoolYear()
if (res.data) {
schoolYearList.value = Array.isArray(res.data) ? res.data : []
}
} catch (err) {
schoolYearList.value = []
}
}
// 获取学院列表
const getDeptListData = async () => {
try {
const res = await getDeptList()
if (res.data) {
deptList.value = Array.isArray(res.data) ? res.data : []
}
} catch (err) {
deptList.value = []
}
}
// 获取班号列表
const getClassListData = async () => {
try {
const res = await getClassListByRole()
if (res.data) {
classList.value = Array.isArray(res.data) ? res.data : []
}
} catch (err) {
classList.value = []
}
}
// 获取学期字典
const getSchoolTermDict = async () => {
try {
const res = await getDicts('school_term')
if (res.data) {
schoolTermList.value = Array.isArray(res.data) ? res.data.map((item: any) => ({
label: item.label || item.dictLabel || item.name,
value: item.value || item.dictValue || item.code
})) : []
}
} catch (err) {
schoolTermList.value = []
}
}
// 格式化学期
const formatSchoolTerm = (value: string | number) => {
if (value === null || value === undefined || value === '') {
return '-'
}
const dictItem = schoolTermList.value.find(item => item.value == value)
return dictItem ? dictItem.label : value
}
// 初始化
onMounted(() => {
getSchoolYearList()
getDeptListData()
getClassListData()
getSchoolTermDict()
})
</script>
<style scoped lang="scss">
@import '/@/assets/styles/modern-page.scss';
</style>