Files
school-developer/src/views/stuwork/classroomhygienedailyanalysis/index.vue
吴红兵 b997b3ba48 fix
2026-03-07 12:35:45 +08:00

450 lines
14 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="buildingNo">
<el-select v-model="searchForm.buildingNo" placeholder="请选择楼号" clearable filterable style="width: 200px">
<el-option v-for="item in buildingList" :key="item.buildingNo" :label="item.buildingNo" :value="item.buildingNo"> </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 classList" :key="item.classCode" :label="item.classNo" :value="item.classCode"> </el-option>
</el-select>
</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>
<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">
<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
: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 }}
</template>
</el-table-column>
<template v-for="col in visibleColumnsSorted" :key="col.prop || col.label">
<el-table-column
v-if="checkColumnVisible(col.prop || '') && col.prop !== '操作' && !col.prop?.startsWith('day')"
:prop="col.prop"
:label="col.label"
:width="col.width"
:min-width="col.minWidth"
:show-overflow-tooltip="col.showOverflowTooltip !== false"
:align="col.align || 'center'"
>
<template #header>
<el-icon v-if="col.icon"><component :is="col.icon" /></el-icon>
<span :style="{ marginLeft: col.icon ? '4px' : '0' }">{{ col.label }}</span>
</template>
<!-- 总分列特殊模板 -->
<template v-if="col.prop === 'totalScore'" #default="scope">
<el-tag v-if="scope.row.totalScore !== undefined && scope.row.totalScore !== null" size="small" type="success" effect="plain">
{{ scope.row.totalScore }}
</el-tag>
<span v-else>-</span>
</template>
<!-- 排名列特殊模板 -->
<template v-else-if="col.prop === 'rank'" #default="scope">
<el-tag v-if="scope.row.rank" size="small" :type="scope.row.rank <= 3 ? 'warning' : 'info'" effect="plain">
{{ scope.row.rank }}
</el-tag>
<span v-else>-</span>
</template>
</el-table-column>
</template>
<!-- 动态日期列不受列控制影响始终显示 -->
<el-table-column
v-for="day in dayColumns"
:key="day"
:prop="`day${day}`"
:label="`${day}日`"
show-overflow-tooltip
align="center"
width="80"
>
<template #header>
<el-icon><Calendar /></el-icon>
<span style="margin-left: 4px">{{ day }}</span>
</template>
</el-table-column>
<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="Plus" link type="success" @click="handleAddScore(scope.row)"> 加分 </el-button>
<el-button icon="Minus" link type="danger" @click="handleSubtractScore(scope.row)"> 减分 </el-button>
</template>
</el-table-column>
<template #empty>
<el-empty description="暂无数据" :image-size="120" />
</template>
</el-table>
</el-card>
</div>
<!-- 加分/减分对话框 -->
<el-dialog v-model="scoreDialogVisible" :title="scoreDialogTitle" :close-on-click-modal="false" draggable width="500px">
<el-form ref="scoreFormRef" :model="scoreForm" label-width="100px">
<el-form-item label="班级" prop="classNo">
<el-input v-model="scoreForm.classNo" disabled />
</el-form-item>
<el-form-item label="日期" prop="date">
<el-date-picker
v-model="scoreForm.date"
type="date"
placeholder="请选择日期"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
style="width: 100%"
/>
</el-form-item>
<el-form-item label="分数" prop="score">
<el-input-number v-model="scoreForm.score" :precision="0" :step="1" :min="0" placeholder="请输入分数" style="width: 100%" />
</el-form-item>
<el-form-item label="说明" prop="note">
<el-input v-model="scoreForm.note" type="textarea" :rows="3" placeholder="请输入说明" maxlength="500" show-word-limit />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="scoreDialogVisible = false"> </el-button>
<el-button type="primary" @click="submitScore"> </el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts" name="ClassRoomHygieneDailyAnalysis">
import { ref, reactive, onMounted } from 'vue';
import { BasicTableProps, useTable } from '/@/hooks/table';
import { fetchList } from '/@/api/stuwork/classroomhygienedailyanalysis';
import { useMessage } from '/@/hooks/message';
import { getBuildingList } from '/@/api/stuwork/teachbuilding';
import { getClassListByRole } from '/@/api/basic/basicclass';
import TableColumnControl from '/@/components/TableColumnControl/index.vue';
import { List, DataAnalysis, Trophy, Grid, Calendar, Setting, Menu, Search, Document } from '@element-plus/icons-vue';
import { useTableColumnControl } from '/@/hooks/tableColumn';
// 定义变量内容
const searchFormRef = ref();
const columnControlRef = ref<any>();
const scoreFormRef = ref();
// 搜索变量
const showSearch = ref(true);
const buildingList = ref<any[]>([]);
const classList = ref<any[]>([]);
const scoreDialogVisible = ref(false);
const scoreDialogTitle = ref('加分');
const isAddScore = ref(true); // true: 加分, false: 减分
// 表格列配置(只包含固定列,动态日期列不包含在内)
const tableColumns = [
{ prop: 'totalScore', label: '总分', icon: DataAnalysis },
{ prop: 'rank', label: '排名', icon: Trophy },
{ prop: 'classNo', label: '班级', icon: Grid },
];
// 使用表格列控制 Hook
const { visibleColumns, visibleColumnsSorted, checkColumnVisible, handleColumnChange, handleColumnOrderChange } = useTableColumnControl(tableColumns);
// 加分/减分表单
const scoreForm = reactive({
classCode: '',
classNo: '',
date: '',
score: 0,
note: '',
});
// 搜索表单
const searchForm = reactive({
buildingNo: '',
classCode: '',
month: '',
});
// 计算月份的天数列
const dayColumns = computed(() => {
if (!searchForm.month) {
return [];
}
const [year, month] = searchForm.month.split('-').map(Number);
const daysInMonth = new Date(year, month, 0).getDate();
return Array.from({ length: daysInMonth }, (_, i) => i + 1);
});
// 配置 useTable - 接口参数为 buildingNo 和 month数组格式
const state: BasicTableProps = reactive<BasicTableProps>({
queryForm: searchForm,
isPage: false, // 接口不支持分页
pageList: async (queryParams: any) => {
const params: any = {};
// 接口文档要求 buildingNo 和 month 都是数组格式
if (searchForm.buildingNo) {
params.buildingNo = Array.isArray(searchForm.buildingNo) ? searchForm.buildingNo : [searchForm.buildingNo];
}
if (searchForm.month) {
params.month = Array.isArray(searchForm.month) ? searchForm.month : [searchForm.month];
}
if (searchForm.classCode) {
params.classCode = searchForm.classCode;
}
const res = await fetchList(params);
// 接口返回的数据结构:数组,每个元素包含日期字段(如 2025-12-01和班级信息
// 需要确保返回数组格式给 useTable
let dataList = [];
if (Array.isArray(res.data)) {
dataList = res.data;
} else if (res.data && Array.isArray(res.data.records)) {
dataList = res.data.records;
} else if (res.data && Array.isArray(res.data.list)) {
dataList = res.data.list;
} else if (res.data && typeof res.data === 'object') {
// 如果 res.data 是单个对象,转换为数组
dataList = [res.data];
}
// 处理数据:将日期字段转换为 day1, day2 等格式,方便表格显示
const processedData = dataList.map((item: any) => {
const processed: any = {
classCode: item.classCode || '',
classNo: item.classNo || '',
totalScore: item.scoreTotal || 0,
rank: item.order || 0,
isAddScore: item.isAddScore || 0,
};
// 提取所有日期字段(格式为 YYYY-MM-DD
const datePattern = /^\d{4}-\d{2}-\d{2}$/;
Object.keys(item).forEach((key) => {
if (datePattern.test(key)) {
// 将日期转换为 day1, day2 等格式(根据日期中的天数)
const date = new Date(key);
const day = date.getDate();
processed[`day${day}`] = item[key];
}
});
// 确保所有日期字段都存在即使值为0或null避免表格列无法显示
if (searchForm.month) {
const [year, month] = searchForm.month.split('-').map(Number);
const daysInMonth = new Date(year, month, 0).getDate();
for (let i = 1; i <= daysInMonth; i++) {
if (!processed.hasOwnProperty(`day${i}`)) {
processed[`day${i}`] = 0;
}
}
}
return processed;
});
return {
...res,
data: processedData,
};
},
props: {
item: 'records',
totalCount: 'total',
},
createdIsNeed: false, // 不自动加载,需要手动调用
});
// table hook
const { getDataList, currentChangeHandle, sizeChangeHandle, sortChangeHandle, tableStyle } = useTable(state);
// 查询
const handleSearch = () => {
if (!searchForm.month) {
useMessage().warning('请选择月份');
return;
}
getDataList();
};
// 重置
const handleReset = () => {
searchFormRef.value?.resetFields();
searchForm.buildingNo = '';
searchForm.classCode = '';
searchForm.month = '';
getDataList();
};
// 加分
const handleAddScore = (row: any) => {
isAddScore.value = true;
scoreDialogTitle.value = '加分';
Object.assign(scoreForm, {
classCode: row.classCode || '',
classNo: row.classNo || '',
date: '',
score: 0,
note: '',
});
scoreDialogVisible.value = true;
};
// 减分
const handleSubtractScore = (row: any) => {
isAddScore.value = false;
scoreDialogTitle.value = '减分';
Object.assign(scoreForm, {
classCode: row.classCode || '',
classNo: row.classNo || '',
date: '',
score: 0,
note: '',
});
scoreDialogVisible.value = true;
};
// 提交加分/减分
const submitScore = async () => {
if (!scoreForm.date) {
useMessage().warning('请选择日期');
return;
}
if (!scoreForm.score || scoreForm.score <= 0) {
useMessage().warning('请输入有效的分数');
return;
}
try {
// TODO: 调用加分/减分接口
// const api = isAddScore.value ? addScore : subtractScore
// await api(scoreForm)
useMessage().success(isAddScore.value ? '加分成功' : '减分成功');
scoreDialogVisible.value = false;
getDataList();
} catch (err: any) {
useMessage().error(err.msg || (isAddScore.value ? '加分失败' : '减分失败'));
}
};
// 获取楼号列表(教学楼)
const getBuildingListData = async () => {
try {
const res = await getBuildingList();
if (res.data) {
// 处理返回的数据,可能是分页数据
if (res.data.records) {
buildingList.value = Array.isArray(res.data.records) ? res.data.records : [];
} else if (Array.isArray(res.data)) {
buildingList.value = res.data;
} else {
buildingList.value = [];
}
}
} catch (err) {
buildingList.value = [];
}
};
// 获取班级列表
const getClassListData = async () => {
try {
const res = await getClassListByRole({});
if (res.data) {
if (Array.isArray(res.data.records)) {
classList.value = res.data.records;
} else if (Array.isArray(res.data)) {
classList.value = res.data;
} else {
classList.value = [];
}
} else {
classList.value = [];
}
} catch (err) {
classList.value = [];
}
};
// 初始化
onMounted(() => {
getBuildingListData();
getClassListData();
});
</script>
<style scoped lang="scss">
@import '/@/assets/styles/modern-page.scss';
</style>