Files
school-developer/src/views/stuwork/dormhygienemonthly/index.vue
2026-03-09 12:03:19 +08:00

499 lines
15 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="month">
<el-date-picker
v-model="searchForm.month"
type="month"
placeholder="选择月份"
format="YYYY-MM"
value-format="YYYY-MM"
style="width: 180px"
clearable
/>
</el-form-item>
<el-form-item label="楼号" prop="buildingNo">
<el-select v-model="searchForm.buildingNo" placeholder="请选择楼号" clearable filterable style="width: 150px">
<el-option v-for="item in buildingList" :key="item.buildingNo" :label="item.buildingNo" :value="item.buildingNo" />
</el-select>
</el-form-item>
<el-form-item label="宿舍号" prop="roomNo">
<el-input v-model="searchForm.roomNo" placeholder="请输入宿舍号" clearable style="width: 150px" />
</el-form-item>
<el-form-item>
<el-button type="primary" 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>
<right-toolbar v-model:showSearch="showSearch" class="ml10" @queryTable="getDataList" />
</div>
</div>
</template>
<!-- 表格 -->
<el-table
:data="dataList"
v-loading="loading"
stripe
:cell-style="tableStyle.cellStyle"
:header-cell-style="tableStyle.headerCellStyle"
class="modern-table"
>
<el-table-column type="index" label="序号" width="70" align="center">
<template #default="{ $index }">
{{ (page.currentPage - 1) * page.pageSize + $index + 1 }}
</template>
</el-table-column>
<el-table-column prop="month" label="月份" width="100" align="center">
<template #default="{ row }">
<el-tag type="primary" size="small">{{ row.month }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="buildingNo" label="楼号" width="100" align="center" />
<el-table-column prop="roomNo" label="宿舍号" width="100" align="center" />
<el-table-column prop="score" label="评分" width="100" align="center">
<template #default="{ row }">
<el-tag :type="getScoreTagType(row.score)" size="small" effect="plain">
{{ row.score }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="remarks" label="备注" min-width="200" show-overflow-tooltip />
<el-table-column prop="createTime" label="创建时间" width="160" align="center">
<template #default="{ row }">
{{ formatDateTime(row.createTime) }}
</template>
</el-table-column>
<el-table-column label="操作" width="150" align="center" fixed="right">
<template #default="{ row }">
<el-button icon="Edit" link type="primary" @click="handleEdit(row)">编辑</el-button>
<el-button icon="Delete" link type="danger" @click="handleDelete(row)">删除</el-button>
</template>
</el-table-column>
<template #empty>
<el-empty description="暂无数据" :image-size="120" />
</template>
</el-table>
<!-- 分页 -->
<el-pagination
v-model:current-page="page.currentPage"
v-model:page-size="page.pageSize"
:page-sizes="[10, 20, 50, 100]"
:total="page.total"
layout="total, sizes, prev, pager, next, jumper"
class="pagination"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</el-card>
</div>
<!-- 新增/编辑弹窗 -->
<el-dialog v-model="dialogVisible" :title="dialogTitle" width="500px" :close-on-click-modal="false">
<el-form :model="form" ref="formRef" :rules="rules" label-width="80px">
<el-form-item label="月份" prop="month">
<el-date-picker v-model="form.month" type="month" placeholder="选择月份" format="YYYY-MM" value-format="YYYY-MM" style="width: 100%" />
</el-form-item>
<el-form-item label="楼号" prop="buildingNo">
<el-select v-model="form.buildingNo" placeholder="请选择楼号" filterable style="width: 100%">
<el-option v-for="item in buildingList" :key="item.buildingNo" :label="item.buildingNo" :value="item.buildingNo" />
</el-select>
</el-form-item>
<el-form-item label="宿舍号" prop="roomNo">
<el-input v-model="form.roomNo" placeholder="请输入宿舍号" />
</el-form-item>
<el-form-item label="评分" prop="score">
<el-input-number v-model="form.score" :min="0" :max="100" :precision="2" placeholder="请输入评分" style="width: 100%" />
</el-form-item>
<el-form-item label="备注" prop="remarks">
<el-input v-model="form.remarks" type="textarea" :rows="3" placeholder="请输入备注" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitForm" :loading="submitLoading">确定</el-button>
</template>
</el-dialog>
<!-- 评比弹窗 -->
<el-dialog v-model="evalDialogVisible" title="触发宿舍月卫生评比" width="400px" :close-on-click-modal="false">
<el-form :model="evalForm" ref="evalFormRef" label-width="80px">
<el-form-item label="评比月份" prop="month">
<el-date-picker
v-model="evalForm.month"
type="month"
placeholder="选择评比月份"
format="YYYY-MM"
value-format="YYYY-MM"
style="width: 100%"
/>
<div class="form-tip">为空则默认评比上个月</div>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="evalDialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitEvaluation" :loading="evalLoading">确定评比</el-button>
</template>
</el-dialog>
<!-- 导入对话框 -->
<el-dialog v-model="importDialogVisible" title="导入宿舍月卫生" width="500px" :close-on-click-modal="false">
<el-form :model="importForm" ref="importFormRef" label-width="80px" style="margin-bottom: 15px">
<el-form-item label="楼号" prop="buildingNo">
<el-select v-model="importForm.buildingNo" placeholder="请选择楼号" filterable style="width: 100%">
<el-option v-for="item in buildingList" :key="item.buildingNo" :label="item.buildingNo" :value="item.buildingNo" />
</el-select>
</el-form-item>
<el-form-item label="月份" prop="month">
<el-date-picker v-model="importForm.month" type="month" placeholder="选择月份" format="YYYY-MM" value-format="YYYY-MM" style="width: 100%" />
</el-form-item>
<el-form-item>
<el-button icon="Download" type="success" @click="handleDownloadTemplate"> 下载模板 </el-button>
</el-form-item>
</el-form>
<el-upload ref="importUploadRef" :auto-upload="false" :on-change="handleImportFileChange" :limit="1" accept=".xlsx,.xls" drag>
<i class="el-icon-upload"></i>
<div class="el-upload__text">将文件拖到此处<em>点击上传</em></div>
<template #tip>
<div class="el-upload__tip text-center">只能上传 xlsx/xls 文件请先选择楼号和月份后下载模板</div>
</template>
</el-upload>
<template #footer>
<el-button @click="importDialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleImportSubmit" :disabled="!importFile || importLoading">确认导入</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts" name="DormHygieneMonthly">
import { reactive, ref, onMounted } from 'vue';
import { fetchList, addObj, editObj, delObj, triggerEvaluation } from '/@/api/stuwork/dormhygienemonthly';
import { exportDormHygieneMonthlyTemplate, importDormHygieneMonthly, downloadBlobFile } from '/@/api/stuwork/file';
import { getBuildingList } from '/@/api/stuwork/dormbuilding';
import { useMessage } from '/@/hooks/message';
import { Search, Document, Plus, Edit, Delete, Promotion } from '@element-plus/icons-vue';
// 定义变量内容
const searchFormRef = ref();
const formRef = ref();
const evalFormRef = ref();
const importFormRef = ref();
const importUploadRef = ref();
const showSearch = ref(true);
const loading = ref(false);
const submitLoading = ref(false);
const evalLoading = ref(false);
const importLoading = ref(false);
const dataList = ref<any[]>([]);
const buildingList = ref<any[]>([]);
const dialogVisible = ref(false);
const dialogTitle = ref('新增宿舍月卫生');
const evalDialogVisible = ref(false);
const importDialogVisible = ref(false);
const importFile = ref<File | null>(null);
// 分页
const page = reactive({
currentPage: 1,
pageSize: 20,
total: 0,
});
// 搜索表单
const searchForm = reactive({
month: '',
buildingNo: '',
roomNo: '',
});
// 表单
const form = reactive({
id: '',
month: '',
buildingNo: '',
roomNo: '',
score: 100,
remarks: '',
});
// 评比表单
const evalForm = reactive({
month: '',
});
// 导入表单
const importForm = reactive({
buildingNo: '',
month: '',
});
// 表单校验规则
const rules = {
month: [{ required: true, message: '请选择月份', trigger: 'change' }],
buildingNo: [{ required: true, message: '请选择楼号', trigger: 'change' }],
roomNo: [{ required: true, message: '请输入宿舍号', trigger: 'blur' }],
score: [{ required: true, message: '请输入评分', trigger: 'blur' }],
};
// 表格样式
const tableStyle = {
cellStyle: { textAlign: 'center' },
headerCellStyle: {
textAlign: 'center',
background: 'var(--el-table-row-hover-bg-color)',
color: 'var(--el-text-color-primary)',
},
};
// 格式化日期时间
const formatDateTime = (dateTime: string) => {
if (!dateTime) return '-';
return dateTime.replace('T', ' ').substring(0, 19);
};
// 根据分数获取标签类型
const getScoreTagType = (score: number) => {
if (score >= 90) return 'success';
if (score >= 80) return 'primary';
if (score >= 60) return 'warning';
return 'danger';
};
// 查询
const handleSearch = () => {
page.currentPage = 1;
getDataList();
};
// 重置
const handleReset = () => {
searchFormRef.value?.resetFields();
getDataList();
};
// 分页
const handleSizeChange = (val: number) => {
page.pageSize = val;
getDataList();
};
const handleCurrentChange = (val: number) => {
page.currentPage = val;
getDataList();
};
// 获取数据列表
const getDataList = async () => {
loading.value = true;
try {
const res = await fetchList({
current: page.currentPage,
size: page.pageSize,
...searchForm,
});
if (res.data && res.data.records) {
dataList.value = res.data.records;
page.total = res.data.total;
} else {
dataList.value = [];
page.total = 0;
}
} catch (err: any) {
useMessage().error(err.msg || '获取数据失败');
} finally {
loading.value = false;
}
};
// 新增
const handleAdd = () => {
resetForm();
dialogTitle.value = '新增宿舍月卫生';
dialogVisible.value = true;
};
// 编辑
const handleEdit = (row: any) => {
resetForm();
dialogTitle.value = '编辑宿舍月卫生';
Object.assign(form, row);
dialogVisible.value = true;
};
// 删除
const handleDelete = async (row: any) => {
try {
await useMessage().confirm('确定要删除该记录吗?');
await delObj([row.id]);
useMessage().success('删除成功');
getDataList();
} catch (err: any) {
if (err !== 'cancel') {
useMessage().error(err.msg || '删除失败');
}
}
};
// 触发评比
const handleEvaluation = () => {
evalForm.month = '';
evalDialogVisible.value = true;
};
// 导入
const handleImport = () => {
importForm.buildingNo = '';
importForm.month = '';
importFile.value = null;
importDialogVisible.value = true;
};
// 下载模板
const handleDownloadTemplate = async () => {
if (!importForm.buildingNo) {
useMessage().warning('请先选择楼号');
return;
}
if (!importForm.month) {
useMessage().warning('请先选择月份');
return;
}
try {
await downloadBlobFile(exportDormHygieneMonthlyTemplate(importForm), `宿舍月卫生导入模板_${importForm.month}.xlsx`);
} catch (err: any) {
useMessage().error(err?.msg || '下载模板失败');
}
};
// 导入文件变化
const handleImportFileChange = (file: any) => {
importFile.value = file.raw;
};
// 提交导入
const handleImportSubmit = async () => {
if (!importFile.value) {
useMessage().warning('请先选择要上传的文件');
return;
}
importLoading.value = true;
try {
const formData = new FormData();
formData.append('file', importFile.value);
await importDormHygieneMonthly(formData);
useMessage().success('导入成功');
importDialogVisible.value = false;
importFile.value = null;
getDataList();
} catch (err: any) {
useMessage().error(err.msg || '导入失败');
} finally {
importLoading.value = false;
}
};
// 提交评比
const submitEvaluation = async () => {
evalLoading.value = true;
try {
await triggerEvaluation({ month: evalForm.month });
useMessage().success('评比任务已触发');
evalDialogVisible.value = false;
} catch (err: any) {
useMessage().error(err.msg || '评比触发失败');
} finally {
evalLoading.value = false;
}
};
// 重置表单
const resetForm = () => {
Object.assign(form, {
id: '',
month: '',
buildingNo: '',
roomNo: '',
score: 100,
remarks: '',
});
};
// 提交表单
const submitForm = async () => {
try {
await formRef.value?.validate();
submitLoading.value = true;
if (form.id) {
await editObj(form);
useMessage().success('修改成功');
} else {
await addObj(form);
useMessage().success('新增成功');
}
dialogVisible.value = false;
getDataList();
} catch (err: any) {
if (err !== false) {
useMessage().error(err.msg || '操作失败');
}
} finally {
submitLoading.value = false;
}
};
// 获取楼号列表
const getBuildingListData = async () => {
try {
const res = await getBuildingList();
if (res.data) {
buildingList.value = Array.isArray(res.data) ? res.data : [];
}
} catch (err) {
buildingList.value = [];
}
};
// 初始化
onMounted(() => {
getBuildingListData();
getDataList();
});
</script>
<style scoped lang="scss">
@import '/@/assets/styles/modern-page.scss';
.form-tip {
font-size: 12px;
color: #999;
margin-top: 4px;
}
</style>