This commit is contained in:
2026-01-15 01:15:07 +08:00
parent 8c1f4ec05e
commit 90b646297d
45 changed files with 62864 additions and 5820 deletions

File diff suppressed because one or more lines are too long

View File

@@ -23,7 +23,9 @@ export function getDictsByTypes(types: string[]) {
return request({
url: '/admin/dict/item/typeList',
method: 'post',
data: types,
data: {
typeList: types,
},
});
}
@@ -165,6 +167,8 @@ export function queryDictByTypeList(types: string[]) {
return request({
url: '/admin/dict/queryDictByTypeList',
method: 'post',
data: types,
data: {
typeList: types,
},
});
}

View File

@@ -1,12 +1,12 @@
import request from '/@/utils/request';
/**
* 获取学生证件照列表(分页)
* 获取学生证件照列表
* @param query
*/
export const fetchList = (query?: any) => {
return request({
url: '/basic/basicstudent/avatar/page',
url: '/basic/basicstudent/avatar/list',
method: 'get',
params: query,
});

View File

@@ -5,7 +5,7 @@ import request from '/@/utils/request';
*/
export const fetchList = (query: any) => {
return request({
url: '/stuwork/classhygienedaily/page',
url: '/stuwork/classhygienedailyanalysis/queryClassHygieneDailyAnalysis',
method: 'get',
params: query
});

View File

@@ -1,5 +1,16 @@
import request from '/@/utils/request';
/**
* 分页查询宿舍楼列表
*/
export const fetchList = (query?: any) => {
return request({
url: '/stuwork/dormbuilding/newPage',
method: 'get',
params: query
});
};
/**
* 获取楼号列表
*/
@@ -10,3 +21,47 @@ export const getBuildingList = () => {
});
};
/**
* 新增宿舍楼
*/
export const addObj = (data: any) => {
return request({
url: '/stuwork/dormbuilding',
method: 'post',
data
});
};
/**
* 编辑宿舍楼
*/
export const putObj = (data: any) => {
return request({
url: '/stuwork/dormbuilding/edit',
method: 'post',
data
});
};
/**
* 删除宿舍楼
*/
export const delObjs = (ids: string[]) => {
return request({
url: '/stuwork/dormbuilding/delete',
method: 'post',
data: ids
});
};
/**
* 获取宿舍楼详情
*/
export const getObj = (id: string) => {
return request({
url: `/stuwork/dormbuilding/detail`,
method: 'get',
params: { id }
});
};

View File

@@ -0,0 +1,24 @@
import request from '/@/utils/request';
/**
* 分页查询宿舍管理员列表
*/
export const fetchList = (query?: any) => {
return request({
url: '/stuwork/dormbuildingmanger/page',
method: 'get',
params: query
});
};
/**
* 删除宿舍管理员
*/
export const delObjs = (ids: string[]) => {
return request({
url: '/stuwork/dormbuildingmanger/delete',
method: 'post',
data: ids
});
};

View File

@@ -0,0 +1,57 @@
import request from '/@/utils/request';
/**
* 分页查询日卫生检查列表
*/
export const fetchList = (query?: any) => {
return request({
url: '/stuwork/dormhygienedaily/page',
method: 'get',
params: query
});
};
/**
* 新增日卫生检查
*/
export const addObj = (data: any) => {
return request({
url: '/stuwork/dormhygienedaily',
method: 'post',
data
});
};
/**
* 编辑日卫生检查
*/
export const putObj = (data: any) => {
return request({
url: '/stuwork/dormhygienedaily/edit',
method: 'post',
data
});
};
/**
* 删除日卫生检查
*/
export const delObjs = (ids: string[]) => {
return request({
url: '/stuwork/dormhygienedaily/delete',
method: 'post',
data: ids
});
};
/**
* 获取日卫生检查详情
*/
export const getObj = (id: string) => {
return request({
url: `/stuwork/dormhygienedaily/detail`,
method: 'get',
params: { id }
});
};

View File

@@ -0,0 +1,57 @@
import request from '/@/utils/request';
/**
* 分页查询月卫生检查整改列表
*/
export const fetchList = (query?: any) => {
return request({
url: '/stuwork/dormreform/page',
method: 'get',
params: query
});
};
/**
* 新增月卫生检查整改
*/
export const addObj = (data: any) => {
return request({
url: '/stuwork/dormreform',
method: 'post',
data
});
};
/**
* 编辑月卫生检查整改
*/
export const putObj = (data: any) => {
return request({
url: '/stuwork/dormreform/edit',
method: 'post',
data
});
};
/**
* 删除月卫生检查整改
*/
export const delObjs = (ids: string[]) => {
return request({
url: '/stuwork/dormreform/delete',
method: 'post',
data: ids
});
};
/**
* 获取月卫生检查整改详情
*/
export const getObj = (id: string) => {
return request({
url: `/stuwork/dormreform/detail`,
method: 'get',
params: { id }
});
};

View File

@@ -0,0 +1,89 @@
import request from '/@/utils/request';
/**
* 分页查询宿舍房间列表
*/
export const fetchList = (query?: any) => {
return request({
url: '/stuwork/dormroom/page',
method: 'get',
params: query
});
};
/**
* 新增宿舍房间
*/
export const addObj = (data: any) => {
return request({
url: '/stuwork/dormroom',
method: 'post',
data
});
};
/**
* 编辑宿舍房间
*/
export const putObj = (data: any) => {
return request({
url: '/stuwork/dormroom/edit',
method: 'post',
data
});
};
/**
* 删除宿舍房间
*/
export const delObj = (id: string) => {
return request({
url: `/stuwork/dormroom/delete`,
method: 'post',
data: [id]
});
};
/**
* 学院安排
*/
export const editDept = (data: { deptCode: string; ids: string[] }) => {
return request({
url: '/stuwork/dormroom/editDept',
method: 'post',
data
});
};
/**
* 获取宿舍房间详情
*/
export const getObj = (id: string) => {
return request({
url: `/stuwork/dormroom/${id}`,
method: 'get'
});
};
/**
* 获取宿舍号选择列表
*/
export const getRoomList = () => {
return request({
url: '/stuwork/dormroom/list',
method: 'get'
});
};
/**
* 获取宿舍树状列表
* @param dormdataType 宿舍空几人类型
*/
export const fetchDormRoomTreeList = (dormdataType?: string) => {
return request({
url: '/stuwork/dormroom/fetchDormRoomTreeList',
method: 'get',
params: { dormdataType }
});
};

View File

@@ -11,3 +11,94 @@ export const fearchStuNumByClassCode = (classCode: string | number) => {
});
};
/**
* 查询空宿舍列表
* @param buildingNo 楼号
*/
export const queryEmptyRoomWithBuildingNo = (buildingNo: string) => {
return request({
url: '/stuwork/dormroomstudent/queryEmptyRoomWithBuildingNo',
method: 'get',
params: { buildingNo }
});
};
/**
* 查询空几人宿舍列表
* @param buildingNo 楼号
* @param roomType 空几人1-5
*/
export const queryEmtryRoomDetail = (buildingNo: string, roomType: string) => {
return request({
url: '/stuwork/dormroomstudent/queryEmtryRoomDetail',
method: 'get',
params: { buildingNo, roomType }
});
};
/**
* 查询异常住宿学生列表
*/
export const queryStudentAbnormal = () => {
return request({
url: '/stuwork/dormroomstudent/queryStudentAbnormal',
method: 'get'
});
};
/**
* 分页查询住宿学生列表
*/
export const fetchList = (query?: any) => {
return request({
url: '/stuwork/dormroomstudent/page',
method: 'get',
params: query
});
};
/**
* 新增住宿学生
*/
export const addObj = (data: any) => {
return request({
url: '/stuwork/dormroomstudent',
method: 'post',
data
});
};
/**
* 转宿
*/
export const editObj = (data: any) => {
return request({
url: '/stuwork/dormroomstudent/edit',
method: 'post',
data
});
};
/**
* 退宿
*/
export const delObjs = (ids: string[]) => {
return request({
url: '/stuwork/dormroomstudent/delete',
method: 'post',
data: ids
});
};
/**
* 获取床位号列表
* @param roomNo 宿舍号
*/
export const fearchRoomStuNum = (roomNo: string) => {
return request({
url: '/stuwork/dormroomstudent/fearchRoomStuNum',
method: 'get',
params: { roomNo }
});
};

View File

@@ -0,0 +1,14 @@
import request from '/@/utils/request';
/**
* 获取待办工作列表
* @param query 查询参数
*/
export const fetchList = (query?: any) => {
return request({
url: '/stuwork/pendingwork/getPendingWork',
method: 'get',
params: query
});
};

View File

@@ -0,0 +1,46 @@
import request from '/@/utils/request';
/**
* 分页查询工学交替列表
*/
export const fetchList = (query?: any) => {
return request({
url: '/stuwork/stuworkstudyalternate/page',
method: 'get',
params: query
});
};
/**
* 新增工学交替
*/
export const addObj = (data: any) => {
return request({
url: '/stuwork/stuworkstudyalternate',
method: 'post',
data
});
};
/**
* 指定带班教师
*/
export const chooseTeacherAttendance = (data: any) => {
return request({
url: '/stuwork/stuworkstudyalternate/chooseTeacherAttendance',
method: 'post',
data
});
};
/**
* 删除工学交替
*/
export const delObj = (ids: string[]) => {
return request({
url: '/stuwork/stuworkstudyalternate/delete',
method: 'post',
data: ids
});
};

View File

@@ -0,0 +1,57 @@
import request from '/@/utils/request';
/**
* 分页查询宿舍水电明细列表
*/
export const fetchList = (query?: any) => {
return request({
url: '/stuwork/waterdetail/page',
method: 'get',
params: query
});
};
/**
* 新增宿舍水电明细
*/
export const addObj = (data: any) => {
return request({
url: '/stuwork/waterdetail',
method: 'post',
data
});
};
/**
* 编辑宿舍水电明细
*/
export const putObj = (data: any) => {
return request({
url: '/stuwork/waterdetail/edit',
method: 'post',
data
});
};
/**
* 删除宿舍水电明细
*/
export const delObjs = (ids: string[]) => {
return request({
url: '/stuwork/waterdetail/delete',
method: 'post',
data: ids
});
};
/**
* 初始化本期水电补贴
*/
export const initWaterOrder = (data: { costMoney: number }) => {
return request({
url: '/stuwork/waterdetail/initWaterOrder',
method: 'post',
data
});
};

View File

@@ -0,0 +1,14 @@
import request from '/@/utils/request';
/**
* 查看水电明细
* @param roomNo 宿舍号
*/
export const lookDetails = (roomNo: string) => {
return request({
url: '/stuwork/watermonthreport/lookDetails',
method: 'get',
params: { roomNo }
});
};

View File

@@ -0,0 +1,67 @@
import request from '/@/utils/request';
/**
* 分页查询水电充值订单列表
*/
export const fetchList = (query?: any) => {
return request({
url: '/stuwork/waterorder/page',
method: 'get',
params: query
});
};
/**
* 新增水电充值订单
*/
export const addObj = (data: any) => {
return request({
url: '/stuwork/waterorder',
method: 'post',
data
});
};
/**
* 编辑水电充值订单
*/
export const putObj = (data: any) => {
return request({
url: '/stuwork/waterorder/edit',
method: 'post',
data
});
};
/**
* 删除水电充值订单
*/
export const delObjs = (ids: string[]) => {
return request({
url: '/stuwork/waterorder/delete',
method: 'post',
data: ids
});
};
/**
* 获取水电充值订单详情
*/
export const getObj = (id: string) => {
return request({
url: '/stuwork/waterorder/detail',
method: 'get',
params: { id }
});
};
/**
* 获取宿舍号列表
*/
export const getRoomList = () => {
return request({
url: '/stuwork/dormroom/list',
method: 'get'
});
};

View File

@@ -0,0 +1,35 @@
import request from '/@/utils/request';
/**
* 分页查询工学交替考勤列表
*/
export const fetchList = (query?: any) => {
return request({
url: '/stuwork/stuworkstudyalternate/page',
method: 'get',
params: query
});
};
/**
* 查询考勤记录
*/
export const queryHistoryList = (data: any) => {
return request({
url: '/stuwork/workstudyattendance/queryHistoryList',
method: 'post',
data
});
};
/**
* 提交考勤
*/
export const submitAttendance = (data: any[]) => {
return request({
url: '/stuwork/workstudyattendance',
method: 'post',
data
});
};

View File

@@ -99,15 +99,20 @@ function initHandle() {
/**
* 点击某一节点时触发的事件,更新当前的选中值和展开状态。
* @param {Object} data - 被点击的节点数据
* @param {Object} node - 被点击的节点对象
*/
function handleNodeClick(node) {
valueTitle.value = node[props.objMap.label];
valueId.value = node[props.objMap.value];
function handleNodeClick(data, node) {
// el-tree 的 node-click 事件传递 (data, node) 两个参数
const nodeData = data || (node && node.data) || node;
if (nodeData) {
valueTitle.value = nodeData[props.objMap.label] || nodeData[props.objMap.value] || '';
valueId.value = nodeData[props.objMap.value] || '';
defaultExpandedKey.value = [];
proxy.$refs.treeSelect.blur();
selectFilterData('');
}
}
/**
* 搜索过滤函数,根据输入的值来过滤显示的节点。

View File

@@ -361,8 +361,8 @@
<script setup lang="ts" name="BasicStudent">
import { ref, reactive, defineAsyncComponent, computed, onMounted } from 'vue'
import { BasicTableProps, useTable } from "/@/hooks/table";
import { fetchList } from "/@/api/basic/basicstudentinfo";
import {
fetchList,
delObj,
exportStudentData,
applyInternship,
@@ -477,63 +477,27 @@ const handleSelectionChange = (selection: any[]) => {
// 学生信息导出
const handleExportStudent = async () => {
try {
await exportStudentData(searchForm)
useMessage().success('导出成功')
} catch (err: any) {
useMessage().error(err.msg || '导出失败')
}
useMessage().warning('功能开发中')
}
// 申请顶岗
const handleApplyInternship = async () => {
if (selectedRows.value.length === 0) {
useMessage().warning('请先选择学生')
return
}
try {
const stuNos = selectedRows.value.map((row: any) => row.stuNo)
await applyInternship({ stuNos })
useMessage().success('申请成功')
getDataList()
} catch (err: any) {
useMessage().error(err.msg || '申请失败')
}
useMessage().warning('功能开发中')
}
// 导出头像
const handleExportAvatar = async () => {
try {
const params: any = { ...searchForm }
if (selectedRows.value.length > 0) {
params.stuNos = selectedRows.value.map((row: any) => row.stuNo)
}
await getDownPic(params)
useMessage().success('导出成功')
} catch (err: any) {
useMessage().error(err.msg || '导出失败')
}
useMessage().warning('功能开发中')
}
// 批量打印
const handleBatchPrint = async () => {
if (selectedRows.value.length === 0) {
useMessage().warning('请先选择学生')
return
}
try {
const stuNos = selectedRows.value.map((row: any) => row.stuNo)
await preBatchPrint({ stuNos })
useMessage().success('打印准备成功')
} catch (err: any) {
useMessage().error(err.msg || '打印准备失败')
}
useMessage().warning('功能开发中')
}
// 段段清证书导入
const handleImportCertificate = () => {
importCertificateDialogVisible.value = true
fileList.value = []
useMessage().warning('功能开发中')
}
// 上传相关
@@ -579,111 +543,47 @@ const handleUploadError = (err: any) => {
// 学籍卡导出
const handleExportStudentCard = async () => {
try {
const params: any = { ...searchForm }
if (selectedRows.value.length > 0) {
params.stuNos = selectedRows.value.map((row: any) => row.stuNo)
}
await exportStuInfoCard(params)
useMessage().success('导出成功')
} catch (err: any) {
useMessage().error(err.msg || '导出失败')
}
useMessage().warning('功能开发中')
}
// 证书导出
const handleExportCertificate = async () => {
try {
const params: any = { ...searchForm }
if (selectedRows.value.length > 0) {
params.stuNos = selectedRows.value.map((row: any) => row.stuNo)
}
await exportCertificate(params)
useMessage().success('导出成功')
} catch (err: any) {
useMessage().error(err.msg || '导出失败')
}
useMessage().warning('功能开发中')
}
// 简单信息维护
const handleSimpleEdit = (row: any) => {
if (simpleEditDialogRef.value) {
simpleEditDialogRef.value.openDialog(row)
}
useMessage().warning('功能开发中')
}
// 查看详情
const handleViewDetail = (row: any) => {
if (detailDialogRef.value) {
detailDialogRef.value.openDialog(row.id)
}
useMessage().warning('功能开发中')
}
// 打印证件照
const handlePrintPhoto = async (row: any) => {
try {
await prePrint(row.stuNo)
useMessage().success('打印准备成功')
} catch (err: any) {
useMessage().error(err.msg || '打印准备失败')
}
useMessage().warning('功能开发中')
}
// 重置密码
const handleResetPassword = async (row: any) => {
try {
await useMessageBox().confirm('确定要重置该学生的密码吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
await resetPassWord({ stuNo: row.stuNo })
useMessage().success('重置成功')
} catch (err: any) {
if (err !== 'cancel') {
useMessage().error(err.msg || '重置失败')
}
}
useMessage().warning('功能开发中')
}
// 设为班干部
const handleSetLeader = async (row: any) => {
try {
await editIsleader({ id: row.id, isClassLeader: 1 })
useMessage().success('设置成功')
getDataList()
} catch (err: any) {
useMessage().error(err.msg || '设置失败')
}
useMessage().warning('功能开发中')
}
// 取消班干部
const handleCancelLeader = async (row: any) => {
try {
await editIsleader({ id: row.id, isClassLeader: 0 })
useMessage().success('取消成功')
getDataList()
} catch (err: any) {
useMessage().error(err.msg || '取消失败')
}
useMessage().warning('功能开发中')
}
// 禁止进出
const handleForbidInout = async (row: any) => {
try {
await useMessageBox().confirm('确定要禁止该学生进出吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
await updateInout({ id: row.id, isAllowInout: 0 })
useMessage().success('操作成功')
getDataList()
} catch (err: any) {
if (err !== 'cancel') {
useMessage().error(err.msg || '操作失败')
}
}
useMessage().warning('功能开发中')
}
// 获取学院列表

View File

@@ -53,9 +53,9 @@
<el-table-column prop="headImg" label="头像" width="120" align="center">
<template #default="scope">
<el-image
v-if="scope.row.headImg"
:src="scope.row.headImg"
:preview-src-list="[scope.row.headImg]"
v-if="scope.row.headImg || scope.row.imageUrl || scope.row.qrCode"
:src="scope.row.headImg || scope.row.imageUrl || scope.row.qrCode"
:preview-src-list="[scope.row.headImg || scope.row.imageUrl || scope.row.qrCode]"
fit="cover"
style="width: 80px; height: 100px; cursor: pointer;"
lazy>
@@ -96,10 +96,44 @@ const searchForm = reactive({
classCode: ''
})
// 配置 useTable
// 配置 useTable - 接口返回的数据结构是 { classes: [], students: [] }
const state: BasicTableProps = reactive<BasicTableProps>({
queryForm: searchForm,
pageList: fetchList,
pageList: async (queryParams: any) => {
const res = await fetchList(queryParams)
// 接口返回的数据结构是 { classes: [], students: [] }
// 需要将 students 数组转换为表格数据,并关联班级信息
if (res.data && res.data.students) {
const students = res.data.students || []
const classes = res.data.classes || []
const classMap = new Map()
classes.forEach((cls: any) => {
classMap.set(cls.classCode, cls)
})
// 将学生数据与班级信息合并
const dataList = students.map((stu: any) => {
const classInfo = classMap.get(stu.classCode)
return {
...stu,
className: classInfo ? classInfo.classNo : stu.className || '',
classNo: classInfo ? classInfo.classNo : ''
}
})
return {
...res,
data: {
records: dataList,
total: dataList.length,
current: 1,
size: dataList.length,
pages: 1
}
}
}
return res
},
props: {
item: 'records',
totalCount: 'total'

View File

@@ -29,21 +29,6 @@
clearable
style="width: 200px" />
</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>
<el-button type="primary" plain icon="Search" @click="handleSearch">查询</el-button>
<el-button icon="Refresh" @click="handleReset">重置</el-button>
@@ -166,7 +151,6 @@ import { ref, reactive, defineAsyncComponent, onMounted, computed } from 'vue'
import { BasicTableProps, useTable } from "/@/hooks/table";
import { fetchList } from "/@/api/stuwork/classhygienedailyanalysis";
import { useMessage } from "/@/hooks/message";
import { getClassListByRole } from '/@/api/basic/basicclass'
import { getBuildingList } from '/@/api/stuwork/dormbuilding'
// 定义变量内容
@@ -174,7 +158,6 @@ const searchFormRef = ref()
const scoreFormRef = ref()
// 搜索变量
const showSearch = ref(true)
const classList = ref<any[]>([])
const buildingList = ref<any[]>([])
const scoreDialogVisible = ref(false)
const scoreDialogTitle = ref('加分')
@@ -192,8 +175,7 @@ const scoreForm = reactive({
// 搜索表单
const searchForm = reactive({
buildingNo: '',
month: '',
classCode: ''
month: ''
})
// 计算月份的天数列
@@ -206,31 +188,77 @@ const dayColumns = computed(() => {
return Array.from({ length: daysInMonth }, (_, i) => i + 1)
})
// 配置 useTable - 需要将月份转换为开始和结束时间,且不传分页参数
// 配置 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]
}
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()
params.startTime = `${year}-${String(month).padStart(2, '0')}-01`
params.endTime = `${year}-${String(month).padStart(2, '0')}-${String(daysInMonth).padStart(2, '0')}`
for (let i = 1; i <= daysInMonth; i++) {
if (!processed.hasOwnProperty(`day${i}`)) {
processed[`day${i}`] = 0
}
}
}
// 如果选择了班号转换为数组格式接口要求classCode是数组
if (searchForm.classCode) {
params.classCode = Array.isArray(searchForm.classCode) ? searchForm.classCode : [searchForm.classCode]
return processed
})
return {
...res,
data: processedData
}
// 楼号参数 - 接口文档中没有buildingNo参数可能需要通过其他方式处理
// 暂时不传递buildingNo如果需要可以后续添加
// 不传递分页参数current、size
return await fetchList(params)
},
props: {
item: 'records',
@@ -262,7 +290,6 @@ const handleReset = () => {
searchFormRef.value?.resetFields()
searchForm.buildingNo = ''
searchForm.month = ''
searchForm.classCode = ''
getDataList()
}
@@ -317,19 +344,6 @@ const submitScore = async () => {
}
}
// 获取班号列表
const getClassListData = async () => {
try {
const res = await getClassListByRole()
if (res.data) {
classList.value = Array.isArray(res.data) ? res.data : []
}
} catch (err) {
console.error('获取班号列表失败', err)
classList.value = []
}
}
// 获取楼号列表
const getBuildingListData = async () => {
try {
@@ -352,7 +366,6 @@ const getBuildingListData = async () => {
// 初始化
onMounted(() => {
getClassListData()
getBuildingListData()
})
</script>

View File

@@ -0,0 +1,182 @@
<template>
<el-dialog
:title="dialogTitle"
v-model="visible"
:width="1000"
:close-on-click-modal="false"
draggable>
<el-table
:data="tableData"
v-loading="loading"
border
style="width: 100%">
<!-- 空宿舍列表序号宿舍几人间是否有空调 -->
<template v-if="roomType === 'all'">
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="roomNo" label="宿舍" show-overflow-tooltip align="center" />
<el-table-column prop="bedNum" label="几人间" show-overflow-tooltip align="center" />
<el-table-column prop="isHaveAir" label="是否有空调" show-overflow-tooltip align="center">
<template #default="scope">
<span>{{ scope.row.isHaveAir === '1' || scope.row.isHaveAir === 1 ? '已安装' : '未安装' }}</span>
</template>
</el-table-column>
</template>
<!-- 空几人宿舍列表宿舍床位1-6是否有空调 -->
<template v-else>
<el-table-column prop="roomNo" label="宿舍" show-overflow-tooltip align="center" />
<el-table-column prop="bedNo1" label="床位1" show-overflow-tooltip align="center">
<template #default="scope">
<span>{{ scope.row.bedNo1 || '-' }}</span>
</template>
</el-table-column>
<el-table-column prop="bedNo2" label="床位2" show-overflow-tooltip align="center">
<template #default="scope">
<span>{{ scope.row.bedNo2 || '-' }}</span>
</template>
</el-table-column>
<el-table-column prop="bedNo3" label="床位3" show-overflow-tooltip align="center">
<template #default="scope">
<span>{{ scope.row.bedNo3 || '-' }}</span>
</template>
</el-table-column>
<el-table-column prop="bedNo4" label="床位4" show-overflow-tooltip align="center">
<template #default="scope">
<span>{{ scope.row.bedNo4 || '-' }}</span>
</template>
</el-table-column>
<el-table-column prop="bedNo5" label="床位5" show-overflow-tooltip align="center">
<template #default="scope">
<span>{{ scope.row.bedNo5 || '-' }}</span>
</template>
</el-table-column>
<el-table-column prop="bedNo6" label="床位6" show-overflow-tooltip align="center">
<template #default="scope">
<span>{{ scope.row.bedNo6 || '-' }}</span>
</template>
</el-table-column>
<el-table-column prop="isHaveAir" label="是否有空调" show-overflow-tooltip align="center">
<template #default="scope">
<span>{{ scope.row.isHaveAir === '1' || scope.row.isHaveAir === 1 ? '已安装' : '未安装' }}</span>
</template>
</el-table-column>
</template>
</el-table>
<!-- 分页 -->
<pagination
v-if="isPage"
@size-change="sizeChangeHandle"
@current-change="currentChangeHandle"
v-bind="pagination" />
<template #footer>
<span class="dialog-footer">
<el-button @click="visible = false"> </el-button>
</span>
</template>
</el-dialog>
</template>
<script setup lang="ts" name="EmptyRoomDialog">
import { ref, reactive, computed } from 'vue'
import { queryEmptyRoomWithBuildingNo, queryEmtryRoomDetail } from '/@/api/stuwork/dormroomstudent'
import { useMessage } from '/@/hooks/message'
// 定义变量内容
const visible = ref(false)
const loading = ref(false)
const buildingNo = ref('')
const roomType = ref('')
const tableData = ref<any[]>([])
const isPage = ref(false)
const pagination = reactive({
current: 1,
size: 10,
total: 0,
pageSizes: [10, 20, 50, 100],
layout: 'total, sizes, prev, pager, next, jumper'
})
// 对话框标题
const dialogTitle = computed(() => {
if (roomType.value === 'all') {
return `空宿舍列表 - ${buildingNo.value}号楼`
} else {
return `${roomType.value}人宿舍列表 - ${buildingNo.value}号楼`
}
})
// 打开对话框
const openDialog = async (building: string, type: string) => {
visible.value = true
buildingNo.value = building
roomType.value = type
tableData.value = []
pagination.current = 1
pagination.total = 0
await loadData()
}
// 加载数据
const loadData = async () => {
loading.value = true
try {
let res: any
if (roomType.value === 'all') {
// 查询所有空宿舍
res = await queryEmptyRoomWithBuildingNo(buildingNo.value)
} else {
// 查询空几人宿舍
res = await queryEmtryRoomDetail(buildingNo.value, roomType.value)
}
// 处理返回数据
if (res.data) {
if (Array.isArray(res.data)) {
tableData.value = res.data
isPage.value = false
} else if (res.data.records && Array.isArray(res.data.records)) {
tableData.value = res.data.records
pagination.total = res.data.total || 0
isPage.value = true
} else if (res.data.list && Array.isArray(res.data.list)) {
tableData.value = res.data.list
isPage.value = false
} else {
tableData.value = []
isPage.value = false
}
} else {
tableData.value = []
isPage.value = false
}
} catch (err: any) {
console.error('加载空宿舍列表失败', err)
useMessage().error(err.msg || '加载失败')
tableData.value = []
} finally {
loading.value = false
}
}
// 分页大小改变
const sizeChangeHandle = (val: number) => {
pagination.size = val
pagination.current = 1
loadData()
}
// 当前页改变
const currentChangeHandle = (val: number) => {
pagination.current = val
loadData()
}
// 暴露方法给父组件
defineExpose({
openDialog
})
</script>

View File

@@ -0,0 +1,126 @@
<template>
<el-dialog :title="form.id ? '编辑' : '新增'" v-model="visible" :width="600" :close-on-click-modal="false" draggable>
<el-form ref="dataFormRef" :model="form" :rules="dataRules" label-width="100px" v-loading="loading">
<el-form-item label="楼号" prop="buildingNo">
<el-input v-model="form.buildingNo" placeholder="请输入楼号" />
</el-form-item>
<el-form-item label="层数" prop="layers">
<el-input-number
v-model="form.layers"
:min="1"
:max="20"
placeholder="请输入层数"
style="width: 100%" />
</el-form-item>
<el-form-item label="电话" prop="phone">
<el-input v-model="form.phone" placeholder="请输入电话" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="visible = false"> </el-button>
<el-button type="primary" @click="onSubmit" :disabled="loading"> </el-button>
</span>
</template>
</el-dialog>
</template>
<script setup lang="ts" name="DormBuildingFormDialog">
import { ref, reactive, nextTick } from 'vue'
import { useMessage } from '/@/hooks/message'
import { addObj, putObj, getObj } from '/@/api/stuwork/dormbuilding'
const emit = defineEmits(['refresh'])
// 定义变量内容
const dataFormRef = ref()
const visible = ref(false)
const loading = ref(false)
const operType = ref('add') // add 或 edit
// 提交表单数据
const form = reactive({
id: '',
buildingNo: '',
layers: undefined as number | undefined,
phone: ''
})
// 定义校验规则
const dataRules = {
buildingNo: [
{ required: true, message: '请输入楼号', trigger: 'blur' }
],
layers: [
{ required: true, message: '请输入层数', trigger: 'blur' },
{ type: 'number', min: 1, max: 20, message: '层数必须在1-20之间', trigger: 'blur' }
],
phone: [
{ required: true, message: '请输入电话', trigger: 'blur' }
]
}
// 打开弹窗
const openDialog = (type: string = 'add', row?: any) => {
visible.value = true
operType.value = type
// 重置表单数据
nextTick(() => {
dataFormRef.value?.resetFields()
form.id = ''
form.buildingNo = ''
form.layers = undefined
form.phone = ''
// 编辑时填充数据
if (type === 'edit' && row) {
form.id = row.id
form.buildingNo = row.buildingNo || ''
form.layers = row.layers
form.phone = row.phone || ''
}
})
}
// 提交表单
const onSubmit = async () => {
if (!dataFormRef.value) return
await dataFormRef.value.validate(async (valid: boolean) => {
if (!valid) return
loading.value = true
try {
if (operType.value === 'add') {
await addObj({
buildingNo: form.buildingNo,
layers: form.layers,
phone: form.phone
})
useMessage().success('新增成功')
} else {
await putObj({
id: form.id,
buildingNo: form.buildingNo,
layers: form.layers,
phone: form.phone
})
useMessage().success('编辑成功')
}
visible.value = false
emit('refresh')
} catch (err: any) {
useMessage().error(err.msg || (operType.value === 'add' ? '新增失败' : '编辑失败'))
} finally {
loading.value = false
}
})
}
// 暴露方法给父组件
defineExpose({
openDialog
})
</script>

View File

@@ -0,0 +1,251 @@
<template>
<div class="layout-padding">
<div class="layout-padding-auto layout-padding-view">
<!-- 操作按钮 -->
<el-row>
<div class="mb8" style="width: 100%">
<el-button
icon="FolderAdd"
type="primary"
class="ml10"
@click="formDialogRef.openDialog()">
</el-button>
<right-toolbar
class="ml10 mr20"
style="float: right;"
@queryTable="getDataList">
</right-toolbar>
</div>
</el-row>
<!-- 表格 -->
<el-table
:data="state.dataList"
v-loading="state.loading"
border
:cell-style="tableStyle.cellStyle"
:header-cell-style="tableStyle.headerCellStyle"
@sort-change="sortChangeHandle"
show-summary
:summary-method="getSummaries">
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="buildingNo" label="楼号" show-overflow-tooltip align="center" />
<el-table-column prop="layers" label="层数" show-overflow-tooltip align="center" />
<el-table-column prop="allAlreadyNum" label="已住人数" show-overflow-tooltip align="center" />
<el-table-column prop="nowNum" label="现住人数" show-overflow-tooltip align="center" />
<el-table-column prop="surplusNum" label="剩余可住人数" show-overflow-tooltip align="center" />
<el-table-column prop="roomEmptyNum" label="空宿舍数" show-overflow-tooltip align="center">
<template #default="scope">
<el-link
v-if="scope.row.roomEmptyNum > 0"
type="primary"
:underline="false"
@click="handleViewEmptyRoom(scope.row, 'all')">
{{ scope.row.roomEmptyNum }}
</el-link>
<span v-else>{{ scope.row.roomEmptyNum || 0 }}</span>
</template>
</el-table-column>
<el-table-column prop="roomEmptyNum5" label="空5人宿舍数" show-overflow-tooltip align="center">
<template #default="scope">
<el-link
v-if="scope.row.roomEmptyNum5 > 0"
type="primary"
:underline="false"
@click="handleViewEmptyRoom(scope.row, '5')">
{{ scope.row.roomEmptyNum5 }}
</el-link>
<span v-else>{{ scope.row.roomEmptyNum5 || 0 }}</span>
</template>
</el-table-column>
<el-table-column prop="roomEmptyNum4" label="空4人宿舍数" show-overflow-tooltip align="center">
<template #default="scope">
<el-link
v-if="scope.row.roomEmptyNum4 > 0"
type="primary"
:underline="false"
@click="handleViewEmptyRoom(scope.row, '4')">
{{ scope.row.roomEmptyNum4 }}
</el-link>
<span v-else>{{ scope.row.roomEmptyNum4 || 0 }}</span>
</template>
</el-table-column>
<el-table-column prop="roomEmptyNum3" label="空3人宿舍数" show-overflow-tooltip align="center">
<template #default="scope">
<el-link
v-if="scope.row.roomEmptyNum3 > 0"
type="primary"
:underline="false"
@click="handleViewEmptyRoom(scope.row, '3')">
{{ scope.row.roomEmptyNum3 }}
</el-link>
<span v-else>{{ scope.row.roomEmptyNum3 || 0 }}</span>
</template>
</el-table-column>
<el-table-column prop="roomEmptyNum2" label="空2人宿舍数" show-overflow-tooltip align="center">
<template #default="scope">
<el-link
v-if="scope.row.roomEmptyNum2 > 0"
type="primary"
:underline="false"
@click="handleViewEmptyRoom(scope.row, '2')">
{{ scope.row.roomEmptyNum2 }}
</el-link>
<span v-else>{{ scope.row.roomEmptyNum2 || 0 }}</span>
</template>
</el-table-column>
<el-table-column prop="roomEmptyNum1" label="空1人宿舍数" show-overflow-tooltip align="center">
<template #default="scope">
<el-link
v-if="scope.row.roomEmptyNum1 > 0"
type="primary"
:underline="false"
@click="handleViewEmptyRoom(scope.row, '1')">
{{ scope.row.roomEmptyNum1 }}
</el-link>
<span v-else>{{ scope.row.roomEmptyNum1 || 0 }}</span>
</template>
</el-table-column>
<el-table-column prop="phone" label="电话" show-overflow-tooltip align="center" />
<el-table-column label="操作" width="150" align="center" fixed="right">
<template #default="scope">
<el-button
icon="Edit"
text
type="primary"
@click="handleEdit(scope.row)">
编辑
</el-button>
<el-button
icon="Delete"
text
type="danger"
@click="handleDelete(scope.row)">
删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<pagination
@size-change="sizeChangeHandle"
@current-change="currentChangeHandle"
v-bind="state.pagination" />
</div>
<!-- 编辑新增 -->
<FormDialog ref="formDialogRef" @refresh="getDataList(false)" />
<!-- 空宿舍详情对话框 -->
<EmptyRoomDialog ref="emptyRoomDialogRef" />
</div>
</template>
<script setup lang="ts" name="DormBuilding">
import { ref, reactive, defineAsyncComponent } from 'vue'
import { BasicTableProps, useTable } from "/@/hooks/table";
import { fetchList, delObjs } from "/@/api/stuwork/dormbuilding";
import { useMessage, useMessageBox } from "/@/hooks/message";
// 引入组件
const FormDialog = defineAsyncComponent(() => import('./form.vue'));
const EmptyRoomDialog = defineAsyncComponent(() => import('./emptyRoomDialog.vue'));
// 定义变量内容
const formDialogRef = ref()
const emptyRoomDialogRef = ref()
// 搜索表单
const searchForm = reactive({
})
// 配置 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 handleEdit = (row: any) => {
if (formDialogRef.value) {
formDialogRef.value.openDialog('edit', row)
}
}
// 删除
const handleDelete = async (row: any) => {
try {
await useMessageBox().confirm('确定要删除该宿舍楼吗?')
await delObjs([row.id])
useMessage().success('删除成功')
getDataList()
} catch (err: any) {
if (err !== 'cancel') {
useMessage().error(err.msg || '删除失败')
}
}
}
// 合计行统计
const getSummaries = (param: any) => {
const { columns, data } = param
const sums: string[] = []
columns.forEach((column: any, index: number) => {
if (index === 0) {
// 序号列显示"合计"
sums[index] = '合计'
return
}
// 跳过序号、楼号、层数、电话、操作列
const prop = column.property
if (!prop || prop === 'buildingNo' || prop === 'layers' || prop === 'phone') {
sums[index] = ''
return
}
// 统计数值字段
const values = data.map((item: any) => Number(item[prop]))
if (!values.every((value: any) => isNaN(value))) {
const sum = values.reduce((prev: number, curr: number) => {
const value = Number(curr)
if (!isNaN(value)) {
return prev + curr
} else {
return prev
}
}, 0)
sums[index] = sum.toString()
} else {
sums[index] = ''
}
})
return sums
}
// 查看空宿舍详情
const handleViewEmptyRoom = (row: any, roomType: string) => {
if (emptyRoomDialogRef.value) {
emptyRoomDialogRef.value.openDialog(row.buildingNo, roomType)
}
}
</script>

View File

@@ -0,0 +1,77 @@
<template>
<div class="layout-padding">
<div class="layout-padding-auto layout-padding-view">
<!-- 表格 -->
<el-table
:data="state.dataList"
v-loading="state.loading"
border
:cell-style="tableStyle.cellStyle"
:header-cell-style="tableStyle.headerCellStyle"
@sort-change="sortChangeHandle">
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="buildingNo" label="楼号" show-overflow-tooltip align="center" />
<el-table-column prop="username" label="管理员工号" show-overflow-tooltip align="center" />
<el-table-column label="操作" width="100" align="center" fixed="right">
<template #default="scope">
<el-button
icon="Delete"
text
type="danger"
@click="handleDelete(scope.row)">
删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<pagination
@size-change="sizeChangeHandle"
@current-change="currentChangeHandle"
v-bind="state.pagination" />
</div>
</div>
</template>
<script setup lang="ts" name="DormBuildingManger">
import { reactive } from 'vue'
import { BasicTableProps, useTable } from "/@/hooks/table";
import { fetchList, delObjs } from "/@/api/stuwork/dormbuildingmanger";
import { useMessage, useMessageBox } from "/@/hooks/message";
// 配置 useTable
const state: BasicTableProps = reactive<BasicTableProps>({
queryForm: {},
pageList: fetchList,
props: {
item: 'records',
totalCount: 'total'
},
createdIsNeed: true
})
// table hook
const {
getDataList,
currentChangeHandle,
sizeChangeHandle,
sortChangeHandle,
tableStyle
} = useTable(state)
// 删除
const handleDelete = async (row: any) => {
try {
await useMessageBox().confirm('确定要删除该宿舍管理员吗?')
await delObjs([row.id])
useMessage().success('删除成功')
getDataList()
} catch (err: any) {
if (err !== 'cancel') {
useMessage().error(err.msg || '删除失败')
}
}
}
</script>

View File

@@ -0,0 +1,173 @@
<template>
<el-dialog :title="form.id ? '编辑' : '新增'" v-model="visible" :width="600" :close-on-click-modal="false" draggable>
<el-form ref="dataFormRef" :model="form" :rules="dataRules" label-width="100px" v-loading="loading">
<el-form-item label="楼号" prop="buildingNo">
<el-select
v-model="form.buildingNo"
placeholder="请选择楼号"
clearable
filterable
style="width: 100%">
<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="roomNo">
<el-input v-model="form.roomNo" placeholder="请输入宿舍号" />
</el-form-item>
<el-form-item label="记录日期" prop="recordDate">
<el-date-picker
v-model="form.recordDate"
type="date"
placeholder="选择日期"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
style="width: 100%" />
</el-form-item>
<el-form-item label="情况记录" prop="note">
<el-input
v-model="form.note"
type="textarea"
:rows="4"
placeholder="请输入情况记录" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="visible = false"> </el-button>
<el-button type="primary" @click="onSubmit" :disabled="loading"> </el-button>
</span>
</template>
</el-dialog>
</template>
<script setup lang="ts" name="DormHygieneDailyFormDialog">
import { ref, reactive, nextTick, onMounted } from 'vue'
import { useMessage } from '/@/hooks/message'
import { addObj, putObj, getObj } from '/@/api/stuwork/dormhygienedaily'
import { getBuildingList } from '/@/api/stuwork/dormbuilding'
const emit = defineEmits(['refresh'])
// 定义变量内容
const dataFormRef = ref()
const visible = ref(false)
const loading = ref(false)
const operType = ref('add') // add 或 edit
const buildingList = ref<any[]>([])
// 提交表单数据
const form = reactive({
id: '',
buildingNo: '',
roomNo: '',
recordDate: '',
note: ''
})
// 定义校验规则
const dataRules = {
buildingNo: [
{ required: true, message: '请选择楼号', trigger: 'change' }
],
roomNo: [
{ required: true, message: '请输入宿舍号', trigger: 'blur' }
],
recordDate: [
{ required: true, message: '请选择记录日期', trigger: 'change' }
],
note: [
{ required: true, message: '请输入情况记录', trigger: 'blur' }
]
}
// 打开弹窗
const openDialog = (type: string = 'add', row?: any) => {
visible.value = true
operType.value = type
// 重置表单数据
nextTick(() => {
dataFormRef.value?.resetFields()
form.id = ''
form.buildingNo = ''
form.roomNo = ''
form.recordDate = ''
form.note = ''
// 编辑时填充数据
if (type === 'edit' && row) {
form.id = row.id
form.buildingNo = row.buildingNo || ''
form.roomNo = row.roomNo || ''
// 处理日期格式
if (row.recordDate) {
form.recordDate = row.recordDate.split(' ')[0] || row.recordDate
}
form.note = row.note || ''
}
})
}
// 提交表单
const onSubmit = async () => {
if (!dataFormRef.value) return
await dataFormRef.value.validate(async (valid: boolean) => {
if (!valid) return
loading.value = true
try {
const submitData: any = {
buildingNo: form.buildingNo,
roomNo: form.roomNo,
recordDate: form.recordDate + ' 00:00:00',
note: form.note
}
if (operType.value === 'add') {
await addObj(submitData)
useMessage().success('新增成功')
} else {
submitData.id = form.id
await putObj(submitData)
useMessage().success('编辑成功')
}
visible.value = false
emit('refresh')
} catch (err: any) {
useMessage().error(err.msg || (operType.value === 'add' ? '新增失败' : '编辑失败'))
} finally {
loading.value = false
}
})
}
// 获取楼号列表
const getBuildingListData = async () => {
try {
const res = await getBuildingList()
if (res.data) {
buildingList.value = Array.isArray(res.data) ? res.data : []
}
} catch (err) {
console.error('获取楼号列表失败', err)
buildingList.value = []
}
}
// 初始化
onMounted(() => {
getBuildingListData()
})
// 暴露方法给父组件
defineExpose({
openDialog
})
</script>

View File

@@ -0,0 +1,261 @@
<template>
<div class="layout-padding">
<div class="layout-padding-auto layout-padding-view">
<!-- 搜索表单 -->
<el-row v-show="showSearch">
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch">
<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-select
v-model="searchForm.schoolTerm"
placeholder="请选择学期"
clearable
style="width: 200px">
<el-option
v-for="item in schoolTermList"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
<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>
<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-row>
<!-- 操作按钮 -->
<el-row>
<div class="mb8" style="width: 100%">
<el-button
icon="FolderAdd"
type="primary"
class="ml10"
@click="formDialogRef.openDialog()">
</el-button>
<right-toolbar
v-model:showSearch="showSearch"
class="ml10 mr20"
style="float: right;"
@queryTable="getDataList">
</right-toolbar>
</div>
</el-row>
<!-- 表格 -->
<el-table
:data="state.dataList"
v-loading="state.loading"
border
:cell-style="tableStyle.cellStyle"
:header-cell-style="tableStyle.headerCellStyle"
@sort-change="sortChangeHandle">
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="schoolYear" label="学年" show-overflow-tooltip align="center" />
<el-table-column prop="schoolTerm" label="学期" show-overflow-tooltip align="center">
<template #default="scope">
<span>{{ scope.row.schoolTerm === '1' ? '第一学期' : scope.row.schoolTerm === '2' ? '第二学期' : scope.row.schoolTerm }}</span>
</template>
</el-table-column>
<el-table-column prop="buildingNo" label="楼号" show-overflow-tooltip align="center" />
<el-table-column prop="roomNo" label="宿舍号" show-overflow-tooltip align="center" />
<el-table-column prop="recordDate" label="记录日期" show-overflow-tooltip align="center">
<template #default="scope">
<span>{{ scope.row.recordDate ? scope.row.recordDate.split(' ')[0] : '-' }}</span>
</template>
</el-table-column>
<el-table-column prop="note" label="情况记录" show-overflow-tooltip align="center" />
<el-table-column label="操作" width="150" align="center" fixed="right">
<template #default="scope">
<el-button
icon="Edit"
text
type="primary"
@click="handleEdit(scope.row)">
编辑
</el-button>
<el-button
icon="Delete"
text
type="danger"
@click="handleDelete(scope.row)">
删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<pagination
@size-change="sizeChangeHandle"
@current-change="currentChangeHandle"
v-bind="state.pagination" />
</div>
<!-- 编辑新增 -->
<FormDialog ref="formDialogRef" @refresh="getDataList(false)" />
</div>
</template>
<script setup lang="ts" name="DormHygieneDaily">
import { ref, reactive, defineAsyncComponent, onMounted } from 'vue'
import { BasicTableProps, useTable } from "/@/hooks/table";
import { fetchList, delObjs } from "/@/api/stuwork/dormhygienedaily";
import { getBuildingList } from "/@/api/stuwork/dormbuilding";
import { queryAllSchoolYear } from "/@/api/basic/basicyear";
import { useMessage, useMessageBox } from "/@/hooks/message";
import { getDicts } from "/@/api/admin/dict";
// 引入组件
const FormDialog = defineAsyncComponent(() => import('./form.vue'));
// 定义变量内容
const formDialogRef = ref()
const searchFormRef = ref()
const showSearch = ref(true)
const schoolYearList = ref<any[]>([])
const schoolTermList = ref<any[]>([])
const buildingList = ref<any[]>([])
// 搜索表单
const searchForm = reactive({
schoolYear: '',
schoolTerm: '',
buildingNo: ''
})
// 配置 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?.formRef?.resetFields()
searchForm.schoolYear = ''
searchForm.schoolTerm = ''
searchForm.buildingNo = ''
getDataList()
}
// 编辑
const handleEdit = (row: any) => {
if (formDialogRef.value) {
formDialogRef.value.openDialog('edit', row)
}
}
// 删除
const handleDelete = async (row: any) => {
try {
await useMessageBox().confirm('确定要删除该记录吗?')
await delObjs([row.id])
useMessage().success('删除成功')
getDataList()
} catch (err: any) {
if (err !== 'cancel') {
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) {
console.error('获取学年列表失败', err)
schoolYearList.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) {
console.error('获取学期字典失败', err)
schoolTermList.value = []
}
}
// 获取楼号列表
const getBuildingListData = async () => {
try {
const res = await getBuildingList()
if (res.data) {
buildingList.value = Array.isArray(res.data) ? res.data : []
}
} catch (err) {
console.error('获取楼号列表失败', err)
buildingList.value = []
}
}
// 初始化
onMounted(() => {
getSchoolYearList()
getSchoolTermDict()
getBuildingListData()
})
</script>

View File

@@ -0,0 +1,131 @@
<template>
<el-dialog :title="form.id ? '编辑' : '新增'" v-model="visible" :width="600" :close-on-click-modal="false" draggable>
<el-form ref="dataFormRef" :model="form" :rules="dataRules" label-width="100px" v-loading="loading">
<el-form-item label="房间号" prop="roomNo">
<el-input v-model="form.roomNo" placeholder="请输入房间号" />
</el-form-item>
<el-form-item label="整改时间" prop="reformDate">
<el-date-picker
v-model="form.reformDate"
type="date"
placeholder="选择日期"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
style="width: 100%" />
</el-form-item>
<el-form-item label="整改内容" prop="reformContent">
<el-input
v-model="form.reformContent"
type="textarea"
:rows="4"
placeholder="请输入整改内容" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="visible = false"> </el-button>
<el-button type="primary" @click="onSubmit" :disabled="loading"> </el-button>
</span>
</template>
</el-dialog>
</template>
<script setup lang="ts" name="DormReformFormDialog">
import { ref, reactive, nextTick } from 'vue'
import { useMessage } from '/@/hooks/message'
import { addObj, putObj } from '/@/api/stuwork/dormreform'
const emit = defineEmits(['refresh'])
// 定义变量内容
const dataFormRef = ref()
const visible = ref(false)
const loading = ref(false)
const operType = ref('add') // add 或 edit
// 提交表单数据
const form = reactive({
id: '',
roomNo: '',
reformDate: '',
reformContent: ''
})
// 定义校验规则
const dataRules = {
roomNo: [
{ required: true, message: '请输入房间号', trigger: 'blur' }
],
reformDate: [
{ required: true, message: '请选择整改时间', trigger: 'change' }
],
reformContent: [
{ required: true, message: '请输入整改内容', trigger: 'blur' }
]
}
// 打开弹窗
const openDialog = (type: string = 'add', row?: any) => {
visible.value = true
operType.value = type
// 重置表单数据
nextTick(() => {
dataFormRef.value?.resetFields()
form.id = ''
form.roomNo = ''
form.reformDate = ''
form.reformContent = ''
// 编辑时填充数据
if (type === 'edit' && row) {
form.id = row.id
form.roomNo = row.roomNo || ''
// 处理日期格式
if (row.reformDate) {
form.reformDate = row.reformDate.split(' ')[0] || row.reformDate
}
form.reformContent = row.reformContent || ''
}
})
}
// 提交表单
const onSubmit = async () => {
if (!dataFormRef.value) return
await dataFormRef.value.validate(async (valid: boolean) => {
if (!valid) return
loading.value = true
try {
const submitData: any = {
roomNo: form.roomNo,
reformDate: form.reformDate,
reformContent: form.reformContent
}
if (operType.value === 'add') {
await addObj(submitData)
useMessage().success('新增成功')
} else {
submitData.id = form.id
await putObj(submitData)
useMessage().success('编辑成功')
}
visible.value = false
emit('refresh')
} catch (err: any) {
useMessage().error(err.msg || (operType.value === 'add' ? '新增失败' : '编辑失败'))
} finally {
loading.value = false
}
})
}
// 暴露方法给父组件
defineExpose({
openDialog
})
</script>

View File

@@ -0,0 +1,309 @@
<template>
<div class="layout-padding">
<div class="layout-padding-auto layout-padding-view">
<!-- 搜索表单 -->
<el-row v-show="showSearch">
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch">
<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="roomNo">
<el-input
v-model="searchForm.roomNo"
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"
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-row>
<!-- 操作按钮 -->
<el-row>
<div class="mb8" style="width: 100%">
<el-button
icon="FolderAdd"
type="primary"
class="ml10"
@click="formDialogRef.openDialog()">
</el-button>
<el-button
icon="Download"
type="warning"
class="ml10"
@click="handleExport">
</el-button>
<right-toolbar
v-model:showSearch="showSearch"
class="ml10 mr20"
style="float: right;"
@queryTable="getDataList">
</right-toolbar>
</div>
</el-row>
<!-- 表格 -->
<el-table
:data="state.dataList"
v-loading="state.loading"
border
:cell-style="tableStyle.cellStyle"
:header-cell-style="tableStyle.headerCellStyle"
@sort-change="sortChangeHandle">
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="deptName" label="学院" show-overflow-tooltip align="center" />
<el-table-column prop="classNos" label="班级" show-overflow-tooltip align="center" />
<el-table-column prop="roomNo" label="房间号" show-overflow-tooltip align="center" />
<el-table-column prop="reformDate" label="整改时间" show-overflow-tooltip align="center">
<template #default="scope">
<span>{{ scope.row.reformDate ? scope.row.reformDate.split(' ')[0] : '-' }}</span>
</template>
</el-table-column>
<el-table-column prop="reformContent" label="整改内容" show-overflow-tooltip align="center" />
<el-table-column prop="reformStatus" label="整改结果" show-overflow-tooltip align="center">
<template #default="scope">
<span>{{ formatReformStatus(scope.row.reformStatus) }}</span>
</template>
</el-table-column>
<el-table-column prop="remarks" label="关联扣分明细" show-overflow-tooltip align="center" />
<el-table-column label="操作" width="350" align="center" fixed="right">
<template #default="scope">
<el-button
text
type="success"
@click="handleSetStatus(scope.row, '合格')"
:disabled="isStatusDisabled(scope.row.reformStatus, '合格')">
合格
</el-button>
<el-button
text
type="danger"
@click="handleSetStatus(scope.row, '不合格')"
:disabled="isStatusDisabled(scope.row.reformStatus, '不合格')">
不合格
</el-button>
<el-button
text
type="warning"
@click="handleSetStatus(scope.row, '未整改')"
:disabled="isStatusDisabled(scope.row.reformStatus, '未整改')">
未整改
</el-button>
<el-button
icon="Edit"
text
type="primary"
@click="handleEdit(scope.row)">
编辑
</el-button>
<el-button
icon="Delete"
text
type="danger"
@click="handleDelete(scope.row)">
删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<pagination
@size-change="sizeChangeHandle"
@current-change="currentChangeHandle"
v-bind="state.pagination" />
</div>
<!-- 编辑新增 -->
<FormDialog ref="formDialogRef" @refresh="getDataList(false)" />
</div>
</template>
<script setup lang="ts" name="DormHygieneMonthly">
import { ref, reactive, defineAsyncComponent, onMounted } from 'vue'
import { BasicTableProps, useTable } from "/@/hooks/table";
import { fetchList, putObj, delObjs } from "/@/api/stuwork/dormreform";
import { getBuildingList } from "/@/api/stuwork/dormbuilding";
import { useMessage, useMessageBox } from "/@/hooks/message";
import { getDicts } from "/@/api/admin/dict";
import { downBlobFile, adaptationUrl } from "/@/utils/other";
// 引入组件
const FormDialog = defineAsyncComponent(() => import('./form.vue'));
// 定义变量内容
const formDialogRef = ref()
const searchFormRef = ref()
const showSearch = ref(true)
const buildingList = ref<any[]>([])
const reformStatusDict = ref<any[]>([])
// 搜索表单
const searchForm = reactive({
buildingNo: '',
roomNo: '',
month: ''
})
// 配置 useTable
const state: BasicTableProps = reactive<BasicTableProps>({
queryForm: searchForm,
pageList: fetchList,
props: {
item: 'records',
totalCount: 'total'
},
createdIsNeed: true
})
// table hook
const {
getDataList,
currentChangeHandle,
sizeChangeHandle,
sortChangeHandle,
tableStyle
} = useTable(state)
// 查询
const handleSearch = () => {
getDataList()
}
// 重置
const handleReset = () => {
searchFormRef.value?.formRef?.resetFields()
searchForm.buildingNo = ''
searchForm.roomNo = ''
searchForm.month = ''
getDataList()
}
// 判断状态按钮是否禁用
const isStatusDisabled = (currentStatus: string | number, targetStatus: string) => {
if (!currentStatus) return false
const currentDictItem = reformStatusDict.value.find(item => item.value == currentStatus)
const currentLabel = currentDictItem ? currentDictItem.label : currentStatus
return currentLabel === targetStatus
}
// 设置整改状态
const handleSetStatus = async (row: any, status: string) => {
try {
// 根据字典值设置整改状态
const statusValue = reformStatusDict.value.find(item => item.label === status)?.value
if (!statusValue) {
useMessage().error('未找到对应的整改状态')
return
}
await putObj({
id: row.id,
roomNo: row.roomNo,
reformDate: row.reformDate ? row.reformDate.split(' ')[0] : row.reformDate,
reformContent: row.reformContent,
reformStatus: statusValue
})
useMessage().success('设置成功')
getDataList()
} catch (err: any) {
useMessage().error(err.msg || '设置失败')
}
}
// 编辑
const handleEdit = (row: any) => {
if (formDialogRef.value) {
formDialogRef.value.openDialog('edit', row)
}
}
// 删除
const handleDelete = async (row: any) => {
try {
await useMessageBox().confirm('确定要删除该记录吗?')
await delObjs([row.id])
useMessage().success('删除成功')
getDataList()
} catch (err: any) {
if (err !== 'cancel') {
useMessage().error(err.msg || '删除失败')
}
}
}
// 导出
const handleExport = () => {
downBlobFile(adaptationUrl('/stuwork/dormreform/export'), searchForm, '月卫生检查整改.xlsx')
}
// 格式化整改结果
const formatReformStatus = (value: string | number) => {
if (value === null || value === undefined || value === '') {
return '-'
}
const dictItem = reformStatusDict.value.find(item => item.value == value)
return dictItem ? dictItem.label : value
}
// 获取楼号列表
const getBuildingListData = async () => {
try {
const res = await getBuildingList()
if (res.data) {
buildingList.value = Array.isArray(res.data) ? res.data : []
}
} catch (err) {
console.error('获取楼号列表失败', err)
buildingList.value = []
}
}
// 获取整改结果字典
const getReformStatusDict = async () => {
try {
const res = await getDicts('reform_status')
if (res.data) {
reformStatusDict.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) {
console.error('获取整改结果字典失败', err)
reformStatusDict.value = []
}
}
// 初始化
onMounted(() => {
getBuildingListData()
getReformStatusDict()
})
</script>

View File

@@ -0,0 +1,237 @@
<template>
<el-dialog :title="form.id ? '编辑' : '新增'" v-model="visible" :width="600" :close-on-click-modal="false" draggable>
<el-form ref="dataFormRef" :model="form" :rules="dataRules" label-width="100px" v-loading="loading">
<el-form-item label="学院" prop="deptCode">
<el-select
v-model="form.deptCode"
placeholder="请选择学院"
clearable
filterable
style="width: 100%">
<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="buildingNo">
<el-select
v-model="form.buildingNo"
placeholder="请选择楼号"
clearable
filterable
style="width: 100%">
<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="roomNo">
<el-input v-model="form.roomNo" placeholder="请输入房间号" />
</el-form-item>
<el-form-item label="几人间" prop="bedNum">
<el-select
v-model="form.bedNum"
placeholder="请选择几人间"
clearable
style="width: 100%">
<el-option
v-for="item in bedNumList"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="已住人数" prop="livedNum">
<el-input-number
v-model="form.livedNum"
:min="0"
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>
<span class="dialog-footer">
<el-button @click="visible = false"> </el-button>
<el-button type="primary" @click="onSubmit" :disabled="loading"> </el-button>
</span>
</template>
</el-dialog>
</template>
<script setup lang="ts" name="DormRoomFormDialog">
import { ref, reactive, nextTick, onMounted } from 'vue'
import { useMessage } from '/@/hooks/message'
import { addObj, putObj, getObj } from '/@/api/stuwork/dormroom'
import { getDeptList } from '/@/api/basic/basicclass'
import { getBuildingList } from '/@/api/stuwork/dormbuilding'
import { getDicts } from '/@/api/admin/dict'
const emit = defineEmits(['refresh'])
// 定义变量内容
const dataFormRef = ref()
const visible = ref(false)
const loading = ref(false)
const operType = ref('add') // add 或 edit
const deptList = ref<any[]>([])
const buildingList = ref<any[]>([])
const bedNumList = ref<any[]>([])
// 提交表单数据
const form = reactive({
id: '',
deptCode: '',
buildingNo: '',
roomNo: '',
bedNum: '',
livedNum: 0,
remarks: ''
})
// 定义校验规则
const dataRules = {
buildingNo: [
{ required: true, message: '请选择楼号', trigger: 'change' }
],
roomNo: [
{ required: true, message: '请输入房间号', trigger: 'blur' }
],
bedNum: [
{ required: true, message: '请选择几人间', trigger: 'change' }
]
}
// 打开弹窗
const openDialog = (type: string = 'add', row?: any) => {
visible.value = true
operType.value = type
// 重置表单数据
nextTick(() => {
dataFormRef.value?.resetFields()
form.id = ''
form.deptCode = ''
form.buildingNo = ''
form.roomNo = ''
form.bedNum = ''
form.livedNum = 0
form.remarks = ''
// 编辑时填充数据
if (type === 'edit' && row) {
form.id = row.id
form.deptCode = row.deptCode || ''
form.buildingNo = row.buildingNo || ''
form.roomNo = row.roomNo || ''
form.bedNum = row.bedNum || ''
form.livedNum = row.livedNum || 0
form.remarks = row.remarks || ''
}
})
}
// 提交表单
const onSubmit = async () => {
if (!dataFormRef.value) return
await dataFormRef.value.validate(async (valid: boolean) => {
if (!valid) return
loading.value = true
try {
const submitData: any = {
deptCode: form.deptCode || '',
buildingNo: form.buildingNo,
roomNo: form.roomNo,
bedNum: form.bedNum,
livedNum: form.livedNum || '',
remarks: form.remarks || ''
}
if (operType.value === 'add') {
await addObj(submitData)
useMessage().success('新增成功')
} else {
submitData.id = form.id
await putObj(submitData)
useMessage().success('编辑成功')
}
visible.value = false
emit('refresh')
} catch (err: any) {
useMessage().error(err.msg || (operType.value === 'add' ? '新增失败' : '编辑失败'))
} finally {
loading.value = false
}
})
}
// 获取学院列表
const getDeptListData = async () => {
try {
const res = await getDeptList()
if (res.data) {
deptList.value = Array.isArray(res.data) ? res.data : []
}
} catch (err) {
console.error('获取学院列表失败', err)
deptList.value = []
}
}
// 获取楼号列表
const getBuildingListData = async () => {
try {
const res = await getBuildingList()
if (res.data) {
buildingList.value = Array.isArray(res.data) ? res.data : []
}
} catch (err) {
console.error('获取楼号列表失败', err)
buildingList.value = []
}
}
// 获取几人间字典
const getBedNumDict = async () => {
try {
const res = await getDicts('room_stu_num')
if (res.data) {
bedNumList.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) {
console.error('获取几人间字典失败', err)
bedNumList.value = []
}
}
// 初始化
onMounted(() => {
getDeptListData()
getBuildingListData()
getBedNumDict()
})
// 暴露方法给父组件
defineExpose({
openDialog
})
</script>

View File

@@ -0,0 +1,379 @@
<template>
<div class="layout-padding">
<div class="layout-padding-auto layout-padding-view">
<!-- 搜索表单 -->
<el-row v-show="showSearch">
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch">
<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="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="roomNo">
<el-input
v-model="searchForm.roomNo"
placeholder="请输入房间号"
clearable
style="width: 200px" />
</el-form-item>
<el-form-item label="几人间" prop="bedNum">
<el-select
v-model="searchForm.bedNum"
placeholder="请选择几人间"
clearable
style="width: 200px">
<el-option
v-for="item in bedNumList"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="已住人数" prop="livedNum">
<el-input
v-model="searchForm.livedNum"
placeholder="请输入已住人数"
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-row>
<!-- 操作按钮 -->
<el-row>
<div class="mb8" style="width: 100%">
<el-button
icon="FolderAdd"
type="primary"
class="ml10"
@click="formDialogRef.openDialog()">
</el-button>
<el-button
icon="OfficeBuilding"
type="success"
class="ml10"
@click="handleDeptAssign">
学院安排
</el-button>
<el-button
icon="Download"
type="warning"
class="ml10"
@click="handleExport">
</el-button>
<right-toolbar
v-model:showSearch="showSearch"
class="ml10 mr20"
style="float: right;"
@queryTable="getDataList">
</right-toolbar>
</div>
</el-row>
<!-- 表格 -->
<el-table
:data="state.dataList"
v-loading="state.loading"
border
:cell-style="tableStyle.cellStyle"
:header-cell-style="tableStyle.headerCellStyle"
@sort-change="sortChangeHandle"
@selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="deptName" label="学院" show-overflow-tooltip align="center" />
<el-table-column prop="buildingNo" label="楼号" show-overflow-tooltip align="center" />
<el-table-column prop="roomNo" label="房间号" show-overflow-tooltip align="center" />
<el-table-column prop="bedNum" label="几人间" show-overflow-tooltip align="center" />
<el-table-column prop="livedNum" label="已住人数" show-overflow-tooltip align="center" />
<el-table-column prop="remarks" label="备注" show-overflow-tooltip align="center" />
<el-table-column label="操作" width="150" align="center" fixed="right">
<template #default="scope">
<el-button
icon="Edit"
text
type="primary"
@click="handleEdit(scope.row)">
编辑
</el-button>
<el-button
icon="Delete"
text
type="danger"
@click="handleDelete(scope.row)">
删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<pagination
@size-change="sizeChangeHandle"
@current-change="currentChangeHandle"
v-bind="state.pagination" />
</div>
<!-- 编辑新增 -->
<FormDialog ref="formDialogRef" @refresh="getDataList(false)" />
<!-- 学院安排对话框 -->
<el-dialog
v-model="deptAssignDialogVisible"
title="学院安排"
:width="500"
:close-on-click-modal="false">
<el-form :model="deptAssignForm" label-width="100px">
<el-form-item label="学院" required>
<el-select
v-model="deptAssignForm.deptCode"
placeholder="请选择学院"
filterable
style="width: 100%">
<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>
<template #footer>
<span class="dialog-footer">
<el-button @click="deptAssignDialogVisible = false"> </el-button>
<el-button type="primary" @click="confirmDeptAssign" :loading="deptAssignLoading"> </el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts" name="DormRoom">
import { ref, reactive, defineAsyncComponent, onMounted } from 'vue'
import { BasicTableProps, useTable } from "/@/hooks/table";
import { fetchList, delObj, editDept } from "/@/api/stuwork/dormroom";
import { getDeptList } from "/@/api/basic/basicclass";
import { getBuildingList } from "/@/api/stuwork/dormbuilding";
import { useMessage, useMessageBox } from "/@/hooks/message";
import { downBlobFile, adaptationUrl } from "/@/utils/other";
import { getDicts } from "/@/api/admin/dict";
// 引入组件
const FormDialog = defineAsyncComponent(() => import('./form.vue'));
// 定义变量内容
const formDialogRef = ref()
const searchFormRef = ref()
const showSearch = ref(true)
const deptList = ref<any[]>([])
const buildingList = ref<any[]>([])
const bedNumList = ref<any[]>([])
const selectedRows = ref<any[]>([])
const deptAssignDialogVisible = ref(false)
const deptAssignLoading = ref(false)
// 搜索表单
const searchForm = reactive({
deptCode: '',
buildingNo: '',
roomNo: '',
bedNum: '',
livedNum: ''
})
// 学院安排表单
const deptAssignForm = reactive({
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 handleSelectionChange = (selection: any[]) => {
selectedRows.value = selection
}
// 查询
const handleSearch = () => {
getDataList()
}
// 重置
const handleReset = () => {
searchFormRef.value?.formRef?.resetFields()
searchForm.deptCode = ''
searchForm.buildingNo = ''
searchForm.roomNo = ''
searchForm.bedNum = ''
searchForm.livedNum = ''
getDataList()
}
// 编辑
const handleEdit = (row: any) => {
if (formDialogRef.value) {
formDialogRef.value.openDialog('edit', row)
}
}
// 删除
const handleDelete = async (row: any) => {
try {
await useMessageBox().confirm('确定要删除该宿舍房间吗?')
await delObj(row.id)
useMessage().success('删除成功')
getDataList()
} catch (err: any) {
if (err !== 'cancel') {
useMessage().error(err.msg || '删除失败')
}
}
}
// 学院安排
const handleDeptAssign = () => {
if (selectedRows.value.length === 0) {
useMessage().warning('请先选择要安排的宿舍房间')
return
}
deptAssignDialogVisible.value = true
deptAssignForm.deptCode = ''
}
// 确认学院安排
const confirmDeptAssign = async () => {
if (!deptAssignForm.deptCode) {
useMessage().warning('请选择学院')
return
}
try {
deptAssignLoading.value = true
const ids = selectedRows.value.map((row: any) => row.id)
await editDept({
deptCode: deptAssignForm.deptCode,
ids
})
useMessage().success('安排成功')
deptAssignDialogVisible.value = false
getDataList()
} catch (err: any) {
useMessage().error(err.msg || '安排失败')
} finally {
deptAssignLoading.value = false
}
}
// 导出
const handleExport = async () => {
try {
await downBlobFile(
adaptationUrl('/stuwork/dormroom/export'),
searchForm,
'宿舍房间.xlsx'
)
useMessage().success('导出成功')
} catch (err: any) {
useMessage().error(err.msg || '导出失败')
}
}
// 获取学院列表
const getDeptListData = async () => {
try {
const res = await getDeptList()
if (res.data) {
deptList.value = Array.isArray(res.data) ? res.data : []
}
} catch (err) {
console.error('获取学院列表失败', err)
deptList.value = []
}
}
// 获取楼号列表
const getBuildingListData = async () => {
try {
const res = await getBuildingList()
if (res.data) {
buildingList.value = Array.isArray(res.data) ? res.data : []
}
} catch (err) {
console.error('获取楼号列表失败', err)
buildingList.value = []
}
}
// 获取几人间字典
const getBedNumDict = async () => {
try {
const res = await getDicts('room_stu_num')
if (res.data) {
bedNumList.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) {
console.error('获取几人间字典失败', err)
bedNumList.value = []
}
}
// 初始化
onMounted(() => {
getDeptListData()
getBuildingListData()
getBedNumDict()
})
</script>

View File

@@ -0,0 +1,92 @@
<template>
<div class="layout-padding">
<div class="layout-padding-auto layout-padding-view">
<!-- 表格 -->
<el-table
:data="state.dataList"
v-loading="state.loading"
border
:cell-style="tableStyle.cellStyle"
:header-cell-style="tableStyle.headerCellStyle">
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="deptName" label="学院" show-overflow-tooltip align="center" />
<el-table-column prop="classNo" label="班号" show-overflow-tooltip align="center" />
<el-table-column prop="stuNo" label="学号" show-overflow-tooltip align="center" />
<el-table-column prop="realName" label="姓名" show-overflow-tooltip align="center" />
<el-table-column prop="roomNo" label="房间号" show-overflow-tooltip align="center" />
<el-table-column prop="bedNo" label="床位号" show-overflow-tooltip align="center" />
<el-table-column prop="teacherRealName" label="班主任" show-overflow-tooltip align="center" />
<el-table-column prop="teacherPhone" label="班主任电话" show-overflow-tooltip align="center" />
<el-table-column label="操作" width="150" align="center" fixed="right">
<template #default="scope">
<el-button
icon="View"
text
type="primary"
@click="handleView(scope.row)">
查看
</el-button>
</template>
</el-table-column>
</el-table>
</div>
</div>
</template>
<script setup lang="ts" name="DormAbnormal">
import { reactive, onMounted } from 'vue'
import { BasicTableProps, useTable } from "/@/hooks/table";
import { queryStudentAbnormal } from "/@/api/stuwork/dormroomstudent";
import { useMessage } from "/@/hooks/message";
// 配置 useTable
const state: BasicTableProps = reactive<BasicTableProps>({
queryForm: {},
isPage: false, // 接口不支持分页
pageList: async () => {
try {
const res = await queryStudentAbnormal()
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
}
return {
...res,
data: dataList
}
} catch (err: any) {
useMessage().error(err.msg || '获取数据失败')
return {
data: []
}
}
},
props: {
item: 'records',
totalCount: 'total'
},
createdIsNeed: true
})
// table hook
const {
getDataList,
tableStyle
} = useTable(state)
// 查看详情
const handleView = (row: any) => {
// TODO: 实现查看详情功能
console.log('查看详情', row)
}
// 初始化
onMounted(() => {
getDataList()
})
</script>

View File

@@ -0,0 +1,327 @@
<template>
<el-dialog
title="新增住宿生"
v-model="visible"
:close-on-click-modal="false"
draggable
width="800px">
<el-form
ref="dataFormRef"
:model="form"
:rules="dataRules"
label-width="100px"
:validate-on-rule-change="false"
v-loading="loading">
<el-row :gutter="24">
<el-col :span="12" class="mb20">
<el-form-item label="班级" prop="classCode">
<el-select
v-model="form.classCode"
placeholder="请选择班级"
clearable
filterable
style="width: 100%"
@change="handleClassChange">
<el-option
v-for="item in classList"
:key="item.classCode"
:label="item.classNo"
:value="item.classCode">
</el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item label="姓名" prop="realName">
<el-select
v-model="form.stuNo"
placeholder="请选择姓名"
clearable
filterable
style="width: 100%"
:disabled="!form.classCode"
@change="handleStudentChange">
<el-option
v-for="item in studentList"
:key="item.stuNo"
:label="item.realName"
:value="item.stuNo">
</el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item label="学号" prop="stuNo">
<el-input
v-model="form.stuNo"
placeholder="学号"
disabled />
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item label="房间号" prop="roomNo">
<el-select
v-model="form.roomNo"
placeholder="请选择房间号"
clearable
filterable
style="width: 100%"
@change="handleRoomChange">
<el-option
v-for="item in roomList"
:key="item.roomNo"
:label="item.roomNo"
:value="item.roomNo">
</el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item label="床位号" prop="bedNo">
<el-select
v-model="form.bedNo"
placeholder="请选择床位号"
clearable
style="width: 100%">
<el-option
v-for="item in bedNoList"
:key="item"
:label="item"
:value="item">
</el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item label="是否为宿舍长" prop="isLeader">
<el-select
v-model="form.isLeader"
placeholder="请选择"
clearable
style="width: 100%">
<el-option label="是" value="1" />
<el-option label="否" value="0" />
</el-select>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="visible = false"> </el-button>
<el-button type="primary" @click="onSubmit" :disabled="loading"> </el-button>
</span>
</template>
</el-dialog>
</template>
<script setup lang="ts" name="DormRoomStudentFormDialog">
import { ref, reactive, nextTick, onMounted } from 'vue'
import { useMessage } from '/@/hooks/message'
import { addObj } from '/@/api/stuwork/dormroomstudent'
import { getRoomList } from '/@/api/stuwork/dormroom'
import { fearchRoomStuNum } from '/@/api/stuwork/dormroomstudent'
import { getClassListByRole } from '/@/api/basic/basicclass'
import { fetchList as getStudentList } from '/@/api/basic/basicstudentinfo'
const emit = defineEmits(['refresh'])
// 定义变量内容
const dataFormRef = ref()
const visible = ref(false)
const loading = ref(false)
const classList = ref<any[]>([])
const studentList = ref<any[]>([])
const roomList = ref<any[]>([])
const bedNoList = ref<string[]>([])
// 提交表单数据
const form = reactive({
classCode: '',
realName: '',
stuNo: '',
roomNo: '',
bedNo: '',
isLeader: '0'
})
// 定义校验规则
const dataRules = {
classCode: [
{ required: true, message: '请选择班级', trigger: 'change' }
],
stuNo: [
{ required: true, message: '请选择姓名', trigger: 'change' }
],
roomNo: [
{ required: true, message: '请选择房间号', trigger: 'change' }
],
bedNo: [
{ required: true, message: '请选择床位号', trigger: 'change' }
],
isLeader: [
{ required: true, message: '请选择是否为宿舍长', trigger: 'change' }
]
}
// 班级变化时获取学生列表
const handleClassChange = async (classCode: string) => {
if (!classCode) {
studentList.value = []
form.stuNo = ''
form.realName = ''
return
}
try {
const res = await getStudentList({
classCode: classCode,
current: 1,
size: 1000
})
if (res.data && res.data.records) {
studentList.value = res.data.records
} else {
studentList.value = []
}
form.stuNo = ''
form.realName = ''
} catch (err) {
console.error('获取学生列表失败', err)
studentList.value = []
form.stuNo = ''
form.realName = ''
}
}
// 学生选择变化
const handleStudentChange = (stuNo: string) => {
const student = studentList.value.find(item => item.stuNo === stuNo)
if (student) {
form.realName = student.realName || ''
}
}
// 房间号变化时获取床位号列表
const handleRoomChange = async (roomNo: string) => {
if (!roomNo) {
bedNoList.value = []
form.bedNo = ''
return
}
try {
const res = await fearchRoomStuNum(roomNo)
if (res.data) {
// 根据返回的数据结构处理床位号列表
// 假设返回的是数字数组或对象数组
if (Array.isArray(res.data)) {
bedNoList.value = res.data.map((item: any) => {
if (typeof item === 'number' || typeof item === 'string') {
return String(item)
}
return String(item.bedNo || item.value || item)
})
} else if (res.data.bedNos && Array.isArray(res.data.bedNos)) {
bedNoList.value = res.data.bedNos.map((item: any) => String(item))
} else {
bedNoList.value = []
}
} else {
bedNoList.value = []
}
form.bedNo = ''
} catch (err) {
console.error('获取床位号列表失败', err)
bedNoList.value = []
form.bedNo = ''
}
}
// 打开弹窗
const openDialog = async () => {
visible.value = true
// 重置表单数据
nextTick(() => {
dataFormRef.value?.resetFields()
form.classCode = ''
form.realName = ''
form.stuNo = ''
form.roomNo = ''
form.bedNo = ''
form.isLeader = '0'
studentList.value = []
bedNoList.value = []
})
}
// 提交表单
const onSubmit = async () => {
if (!dataFormRef.value) return
await dataFormRef.value.validate(async (valid: boolean) => {
if (!valid) return
loading.value = true
try {
await addObj({
roomNo: form.roomNo,
bedNo: form.bedNo,
stuNo: form.stuNo,
isLeader: form.isLeader
})
useMessage().success('新增成功')
visible.value = false
emit('refresh')
} catch (err: any) {
useMessage().error(err.msg || '新增失败')
} finally {
loading.value = false
}
})
}
// 获取班级列表
const getClassListData = async () => {
try {
const res = await getClassListByRole()
if (res.data) {
classList.value = Array.isArray(res.data) ? res.data : []
}
} catch (err) {
console.error('获取班级列表失败', err)
classList.value = []
}
}
// 获取房间号列表
const getRoomListData = async () => {
try {
const res = await getRoomList()
if (res.data) {
roomList.value = Array.isArray(res.data) ? res.data : []
}
} catch (err) {
console.error('获取房间号列表失败', err)
roomList.value = []
}
}
// 初始化
onMounted(() => {
getClassListData()
getRoomListData()
})
// 暴露方法给父组件
defineExpose({
openDialog
})
</script>

View File

@@ -0,0 +1,410 @@
<template>
<div class="layout-padding">
<div class="layout-padding-auto layout-padding-view">
<!-- 搜索表单 -->
<el-row v-show="showSearch">
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch">
<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="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="gender">
<el-select
v-model="searchForm.gender"
placeholder="请选择性别"
clearable
style="width: 200px">
<el-option label="男" value="1" />
<el-option label="女" value="2" />
</el-select>
</el-form-item>
<el-form-item label="筛选类型" prop="dormdataType">
<el-select
v-model="searchForm.dormdataType"
placeholder="请选择筛选类型"
clearable
style="width: 200px"
@change="handleDormDataTypeChange">
<el-option label="所有" value="" />
<el-option label="空宿舍" value="0" />
<el-option label="空1人宿舍" value="1" />
<el-option label="空2人宿舍" value="2" />
<el-option label="空3人宿舍" value="3" />
<el-option label="空4人宿舍" value="4" />
<el-option label="空5人宿舍" value="5" />
</el-select>
</el-form-item>
<el-form-item label="宿舍号" prop="roomNo">
<TreeSelect
v-model="searchForm.roomNo"
:options="dormRoomTreeList"
:objMap="treeProps"
placeholder="请选择宿舍号"
style="width: 200px" />
</el-form-item>
<el-form-item label="房间号" prop="roomNoInput">
<el-input
v-model="searchForm.roomNoInput"
placeholder="请输入房间号"
clearable
style="width: 200px" />
</el-form-item>
<el-form-item label="班号" prop="classNo">
<el-input
v-model="searchForm.classNo"
placeholder="请输入班号"
clearable
style="width: 200px" />
</el-form-item>
<el-form-item label="学号" prop="stuNo">
<el-input
v-model="searchForm.stuNo"
placeholder="请输入学号"
clearable
style="width: 200px" />
</el-form-item>
<el-form-item label="姓名" prop="realName">
<el-input
v-model="searchForm.realName"
placeholder="请输入姓名"
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-row>
<!-- 操作按钮 -->
<el-row>
<div class="mb8" style="width: 100%">
<el-button
icon="FolderAdd"
type="primary"
class="ml10"
@click="formDialogRef.openDialog()">
新增住宿生
</el-button>
<el-button
icon="Printer"
type="success"
class="ml10"
@click="handlePrintCard">
打印宿舍卡
</el-button>
<el-button
icon="Switch"
type="warning"
class="ml10"
@click="handleRoomSwap">
宿舍互换
</el-button>
<el-button
icon="Download"
type="info"
class="ml10"
@click="handleExport">
</el-button>
<el-button
icon="Document"
type="primary"
plain
class="ml10"
@click="handleExportList">
名单导出
</el-button>
<right-toolbar
v-model:showSearch="showSearch"
class="ml10 mr20"
style="float: right;"
@queryTable="getDataList">
</right-toolbar>
</div>
</el-row>
<!-- 表格 -->
<el-table
:data="state.dataList"
v-loading="state.loading"
border
:cell-style="tableStyle.cellStyle"
:header-cell-style="tableStyle.headerCellStyle"
@sort-change="sortChangeHandle">
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="deptName" label="学院" show-overflow-tooltip align="center" />
<el-table-column prop="roomNo" label="房间号" show-overflow-tooltip align="center" />
<el-table-column prop="classNo" label="班号" show-overflow-tooltip align="center" />
<el-table-column prop="teacherRealName" label="班主任" show-overflow-tooltip align="center" />
<el-table-column prop="teacherPhone" label="班主任电话" show-overflow-tooltip align="center" />
<el-table-column prop="stuNo" label="学号" show-overflow-tooltip align="center" />
<el-table-column prop="realName" label="姓名" show-overflow-tooltip align="center" />
<el-table-column prop="bedNo" label="床位号" show-overflow-tooltip align="center" />
<el-table-column prop="isLeader" label="是否舍长" show-overflow-tooltip align="center">
<template #default="scope">
<span>{{ scope.row.isLeader === '1' || scope.row.isLeader === 1 ? '是' : '否' }}</span>
</template>
</el-table-column>
<el-table-column prop="phone" label="学生电话" show-overflow-tooltip align="center" />
<el-table-column prop="tel" label="家长电话" show-overflow-tooltip align="center" />
<el-table-column label="操作" width="150" align="center" fixed="right">
<template #default="scope">
<el-button
icon="Switch"
text
type="primary"
@click="handleTransfer(scope.row)">
转宿
</el-button>
<el-button
icon="Delete"
text
type="danger"
@click="handleCheckout(scope.row)">
退宿
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<pagination
@size-change="sizeChangeHandle"
@current-change="currentChangeHandle"
v-bind="state.pagination" />
</div>
<!-- 新增/转宿表单弹窗 -->
<FormDialog ref="formDialogRef" @refresh="getDataList" />
<!-- 转宿弹窗 -->
<TransferDialog ref="transferDialogRef" @refresh="getDataList" />
</div>
</template>
<script setup lang="ts" name="DormRoomStudent">
import { reactive, ref, onMounted } from 'vue'
import { BasicTableProps, useTable } from "/@/hooks/table";
import { fetchList, delObjs } from "/@/api/stuwork/dormroomstudent";
import { getDeptList } from "/@/api/basic/basicclass";
import { getBuildingList } from "/@/api/stuwork/dormbuilding";
import { fetchDormRoomTreeList } from "/@/api/stuwork/dormroom";
import { useMessage, useMessageBox } from "/@/hooks/message";
import FormDialog from './form.vue';
import TransferDialog from './transfer.vue';
import TreeSelect from '/@/components/TreeSelect/index.vue';
// 定义变量内容
const formDialogRef = ref()
const transferDialogRef = ref()
const searchFormRef = ref()
const showSearch = ref(true)
const deptList = ref<any[]>([])
const buildingList = ref<any[]>([])
const dormRoomTreeList = ref<any[]>([])
// 树形选择器配置
const treeProps = {
value: 'id', // 值字段
label: 'id', // 显示字段id 和 name 一样,所以直接用 id
children: 'children' // 子节点字段
}
// 搜索表单
const searchForm = reactive({
deptCode: '',
buildingNo: '',
gender: '',
dormdataType: '', // 筛选类型:空宿舍、空几人宿舍等
roomNo: '', // 树形选择的宿舍号
roomNoInput: '', // 手动输入的房间号
classNo: '',
stuNo: '',
realName: ''
})
// 配置 useTable
const state: BasicTableProps = reactive<BasicTableProps>({
queryForm: searchForm,
pageList: async (queryParams: any) => {
// 处理查询参数:如果树形选择有值,优先使用树形选择的值;否则使用手动输入的值
const params = {
...queryParams,
roomNo: queryParams.roomNo || queryParams.roomNoInput || ''
}
// 移除 roomNoInput只保留 roomNo
delete params.roomNoInput
return await fetchList(params)
},
props: {
item: 'records',
totalCount: 'total'
},
createdIsNeed: true
})
// table hook
const {
getDataList,
currentChangeHandle,
sizeChangeHandle,
sortChangeHandle,
tableStyle
} = useTable(state)
// 查询
const handleSearch = () => {
getDataList()
}
// 重置
const handleReset = () => {
searchFormRef.value?.resetFields()
searchForm.deptCode = ''
searchForm.buildingNo = ''
searchForm.gender = ''
searchForm.dormdataType = ''
searchForm.roomNo = ''
searchForm.roomNoInput = ''
searchForm.classNo = ''
searchForm.stuNo = ''
searchForm.realName = ''
// 重置时重新获取所有宿舍树形列表
getDormRoomTreeListData()
getDataList()
}
// 筛选类型变化时重新获取树形列表
const handleDormDataTypeChange = (dormdataType: string) => {
// 清空已选择的宿舍号
searchForm.roomNo = ''
// 重新获取树形列表
getDormRoomTreeListData(dormdataType)
}
// 打印宿舍卡
const handlePrintCard = () => {
useMessage().warning('功能开发中')
}
// 宿舍互换
const handleRoomSwap = () => {
useMessage().warning('功能开发中')
}
// 导出
const handleExport = () => {
useMessage().warning('功能开发中')
}
// 名单导出
const handleExportList = () => {
useMessage().warning('功能开发中')
}
// 转宿
const handleTransfer = (row: any) => {
transferDialogRef.value.openDialog(row)
}
// 退宿
const handleCheckout = async (row: any) => {
try {
await useMessageBox().confirm('确定要退宿该学生吗?')
await delObjs([row.id])
useMessage().success('退宿成功')
getDataList()
} catch (err: any) {
if (err !== 'cancel') {
useMessage().error(err.msg || '退宿失败')
}
}
}
// 获取学院列表
const getDeptListData = async () => {
try {
const res = await getDeptList()
if (res.data) {
deptList.value = Array.isArray(res.data) ? res.data : []
}
} catch (err) {
console.error('获取学院列表失败', err)
deptList.value = []
}
}
// 获取楼号列表
const getBuildingListData = async () => {
try {
const res = await getBuildingList()
if (res.data) {
buildingList.value = Array.isArray(res.data) ? res.data : []
}
} catch (err) {
console.error('获取楼号列表失败', err)
buildingList.value = []
}
}
// 获取宿舍树状列表
const getDormRoomTreeListData = async (dormdataType?: string) => {
try {
const res = await fetchDormRoomTreeList(dormdataType || searchForm.dormdataType)
if (res.data) {
// 处理树状数据,确保数据结构符合 TreeSelect 组件要求
dormRoomTreeList.value = Array.isArray(res.data) ? res.data : []
// 如果返回的数据结构不同,可能需要转换
// 例如:如果返回的是 { buildingNo: '1', rooms: [...] } 的结构
// 需要转换为树形结构
if (dormRoomTreeList.value.length === 0 && res.data) {
// 尝试处理不同的数据结构
if (typeof res.data === 'object' && !Array.isArray(res.data)) {
// 如果是对象,可能需要转换为数组
dormRoomTreeList.value = [res.data]
}
}
}
} catch (err) {
console.error('获取宿舍树状列表失败', err)
dormRoomTreeList.value = []
}
}
// 初始化
onMounted(() => {
getDeptListData()
getBuildingListData()
getDormRoomTreeListData()
})
</script>

View File

@@ -0,0 +1,227 @@
<template>
<el-dialog
title="转宿"
v-model="visible"
:close-on-click-modal="false"
draggable
width="800px">
<el-form
ref="dataFormRef"
:model="form"
:rules="dataRules"
label-width="100px"
:validate-on-rule-change="false"
v-loading="loading">
<el-row :gutter="24">
<el-col :span="12" class="mb20">
<el-form-item label="房间号" prop="roomNo">
<el-select
v-model="form.roomNo"
placeholder="请选择房间号"
clearable
filterable
style="width: 100%"
@change="handleRoomChange">
<el-option
v-for="item in roomList"
:key="item.roomNo"
:label="item.roomNo"
:value="item.roomNo">
</el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item label="床位号" prop="bedNo">
<el-select
v-model="form.bedNo"
placeholder="请选择床位号"
clearable
style="width: 100%">
<el-option
v-for="item in bedNoList"
:key="item"
:label="item"
:value="item">
</el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item label="学号" prop="stuNo">
<el-input
v-model="form.stuNo"
placeholder="请输入学号"
disabled />
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item label="是否舍长" prop="isLeader">
<el-select
v-model="form.isLeader"
placeholder="请选择"
clearable
style="width: 100%">
<el-option label="是" value="1" />
<el-option label="否" value="0" />
</el-select>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="visible = false"> </el-button>
<el-button type="primary" @click="onSubmit" :disabled="loading"> </el-button>
</span>
</template>
</el-dialog>
</template>
<script setup lang="ts" name="DormRoomStudentTransferDialog">
import { ref, reactive, nextTick, onMounted } from 'vue'
import { useMessage } from '/@/hooks/message'
import { editObj } from '/@/api/stuwork/dormroomstudent'
import { getRoomList } from '/@/api/stuwork/dormroom'
import { fearchRoomStuNum } from '/@/api/stuwork/dormroomstudent'
const emit = defineEmits(['refresh'])
// 定义变量内容
const dataFormRef = ref()
const visible = ref(false)
const loading = ref(false)
const roomList = ref<any[]>([])
const bedNoList = ref<string[]>([])
// 提交表单数据
const form = reactive({
id: '',
roomNo: '',
bedNo: '',
stuNo: '',
isLeader: '0'
})
// 定义校验规则
const dataRules = {
roomNo: [
{ required: true, message: '请选择房间号', trigger: 'change' }
],
bedNo: [
{ required: true, message: '请选择床位号', trigger: 'change' }
],
isLeader: [
{ required: true, message: '请选择是否舍长', trigger: 'change' }
]
}
// 房间号变化时获取床位号列表
const handleRoomChange = async (roomNo: string) => {
if (!roomNo) {
bedNoList.value = []
form.bedNo = ''
return
}
try {
const res = await fearchRoomStuNum(roomNo)
if (res.data) {
if (Array.isArray(res.data)) {
bedNoList.value = res.data.map((item: any) => {
if (typeof item === 'number' || typeof item === 'string') {
return String(item)
}
return String(item.bedNo || item.value || item)
})
} else if (res.data.bedNos && Array.isArray(res.data.bedNos)) {
bedNoList.value = res.data.bedNos.map((item: any) => String(item))
} else {
bedNoList.value = []
}
} else {
bedNoList.value = []
}
form.bedNo = ''
} catch (err) {
console.error('获取床位号列表失败', err)
bedNoList.value = []
form.bedNo = ''
}
}
// 打开弹窗
const openDialog = async (row: any) => {
visible.value = true
// 重置表单数据
nextTick(() => {
dataFormRef.value?.resetFields()
form.id = row.id || ''
form.roomNo = row.roomNo || ''
form.bedNo = row.bedNo || ''
form.stuNo = row.stuNo || ''
form.isLeader = row.isLeader || '0'
bedNoList.value = []
// 如果有房间号,获取床位号列表
if (form.roomNo) {
handleRoomChange(form.roomNo)
}
})
}
// 提交表单
const onSubmit = async () => {
if (!dataFormRef.value) return
await dataFormRef.value.validate(async (valid: boolean) => {
if (!valid) return
loading.value = true
try {
await editObj({
id: form.id,
roomNo: form.roomNo,
bedNo: form.bedNo,
stuNo: form.stuNo,
isLeader: form.isLeader
})
useMessage().success('转宿成功')
visible.value = false
emit('refresh')
} catch (err: any) {
useMessage().error(err.msg || '转宿失败')
} finally {
loading.value = false
}
})
}
// 获取房间号列表
const getRoomListData = async () => {
try {
const res = await getRoomList()
if (res.data) {
roomList.value = Array.isArray(res.data) ? res.data : []
}
} catch (err) {
console.error('获取房间号列表失败', err)
roomList.value = []
}
}
// 初始化
onMounted(() => {
getRoomListData()
})
// 暴露方法给父组件
defineExpose({
openDialog
})
</script>

View File

@@ -0,0 +1,245 @@
<template>
<div class="layout-padding">
<div class="layout-padding-auto layout-padding-view">
<!-- 搜索表单 -->
<el-row v-show="showSearch">
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch">
<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="classNo">
<el-input
v-model="searchForm.classNo"
placeholder="请输入班号"
clearable
style="width: 200px" />
</el-form-item>
<el-form-item label="异动类型" prop="changeType">
<el-select
v-model="searchForm.changeType"
placeholder="请选择异动类型"
clearable
style="width: 200px">
<el-option
v-for="item in changeTypeList"
:key="item.value"
:label="item.label"
:value="item.value">
</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-row>
<!-- 操作按钮 -->
<el-row>
<div class="mb8" style="width: 100%">
<right-toolbar
v-model:showSearch="showSearch"
class="ml10 mr20"
style="float: right;"
@queryTable="getDataList">
</right-toolbar>
</div>
</el-row>
<!-- 表格 -->
<el-table
:data="state.dataList"
v-loading="state.loading"
border
:cell-style="tableStyle.cellStyle"
:header-cell-style="tableStyle.headerCellStyle">
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="createTime" label="变动时间" show-overflow-tooltip align="center">
<template #default="scope">
<span>{{ scope.row.createTime ? scope.row.createTime.split(' ')[0] + ' ' + scope.row.createTime.split(' ')[1] : '-' }}</span>
</template>
</el-table-column>
<el-table-column prop="deptName" label="学院" show-overflow-tooltip align="center" />
<el-table-column prop="classNo" label="班号" show-overflow-tooltip align="center" />
<el-table-column prop="stuNo" label="学号" show-overflow-tooltip align="center" />
<el-table-column prop="realName" label="姓名" show-overflow-tooltip align="center" />
<el-table-column prop="changeType" label="异动类型" show-overflow-tooltip align="center">
<template #default="scope">
<span>{{ formatChangeType(scope.row.changeType) }}</span>
</template>
</el-table-column>
<el-table-column prop="roomNo" label="原房间号" show-overflow-tooltip align="center" />
<el-table-column prop="bedNo" label="原床位号" show-overflow-tooltip align="center" />
<el-table-column prop="newRoomNo" label="新房间号" show-overflow-tooltip align="center" />
<el-table-column prop="newBedNo" label="新床位号" show-overflow-tooltip align="center" />
<el-table-column label="操作" width="100" align="center" fixed="right">
<template #default="scope">
<el-button
icon="View"
text
type="primary"
@click="handleView(scope.row)">
查看
</el-button>
</template>
</el-table-column>
</el-table>
</div>
</div>
</template>
<script setup lang="ts" name="DormRoomStudentChange">
import { reactive, ref, onMounted, computed } from 'vue'
import { BasicTableProps, useTable } from "/@/hooks/table";
import { queryStudentAbnormal } from "/@/api/stuwork/dormroomstudent";
import { getDeptList } from "/@/api/basic/basicclass";
import { useMessage } from "/@/hooks/message";
import { getDicts } from "/@/api/admin/dict";
// 定义变量内容
const searchFormRef = ref()
const showSearch = ref(true)
const deptList = ref<any[]>([])
const changeTypeList = ref<any[]>([])
const allDataList = ref<any[]>([])
// 搜索表单
const searchForm = reactive({
deptCode: '',
classNo: '',
changeType: ''
})
// 配置 useTable
const state: BasicTableProps = reactive<BasicTableProps>({
queryForm: searchForm,
isPage: false, // 接口不支持分页
pageList: async () => {
try {
const res = await queryStudentAbnormal()
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
}
allDataList.value = dataList
return {
...res,
data: filterData(dataList)
}
} catch (err: any) {
useMessage().error(err.msg || '获取数据失败')
return {
data: []
}
}
},
props: {
item: 'records',
totalCount: 'total'
},
createdIsNeed: true
})
// 过滤数据
const filterData = (data: any[]) => {
let filtered = data
if (searchForm.deptCode) {
filtered = filtered.filter(item => item.deptCode === searchForm.deptCode)
}
if (searchForm.classNo) {
filtered = filtered.filter(item => item.classNo && item.classNo.includes(searchForm.classNo))
}
if (searchForm.changeType) {
filtered = filtered.filter(item => item.changeType === searchForm.changeType)
}
return filtered
}
// table hook
const {
getDataList,
tableStyle
} = useTable(state)
// 查询
const handleSearch = () => {
getDataList()
}
// 重置
const handleReset = () => {
searchFormRef.value?.formRef?.resetFields()
searchForm.deptCode = ''
searchForm.classNo = ''
searchForm.changeType = ''
getDataList()
}
// 查看详情
const handleView = (row: any) => {
// TODO: 实现查看详情功能
console.log('查看详情', row)
}
// 格式化异动类型
const formatChangeType = (value: string | number) => {
if (value === null || value === undefined || value === '') {
return '-'
}
const dictItem = changeTypeList.value.find(item => item.value == value)
return dictItem ? dictItem.label : value
}
// 获取学院列表
const getDeptListData = async () => {
try {
const res = await getDeptList()
if (res.data) {
deptList.value = Array.isArray(res.data) ? res.data : []
}
} catch (err) {
console.error('获取学院列表失败', err)
deptList.value = []
}
}
// 获取异动类型字典
const getChangeTypeDict = async () => {
try {
const res = await getDicts('change_type')
if (res.data) {
changeTypeList.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) {
console.error('获取异动类型字典失败', err)
changeTypeList.value = []
}
}
// 初始化
onMounted(() => {
getDeptListData()
getChangeTypeDict()
})
</script>

View File

@@ -0,0 +1,195 @@
<template>
<div class="layout-padding">
<div class="layout-padding-auto layout-padding-view">
<!-- 搜索表单 -->
<el-row v-show="showSearch">
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch">
<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 classList"
: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-row>
<!-- 操作按钮 -->
<el-row>
<div class="mb8" style="width: 100%">
<right-toolbar
v-model:showSearch="showSearch"
class="ml10 mr20"
style="float: right;"
@queryTable="getDataList">
</right-toolbar>
</div>
</el-row>
<!-- 表格 -->
<el-table
:data="state.dataList"
v-loading="state.loading"
border
:cell-style="tableStyle.cellStyle"
:header-cell-style="tableStyle.headerCellStyle">
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="deptName" label="学院" show-overflow-tooltip align="center" />
<el-table-column prop="classNo" label="班级" show-overflow-tooltip align="center" />
<el-table-column prop="pendingWork" label="待办事项" show-overflow-tooltip align="center" />
<el-table-column prop="nums" label="数量" show-overflow-tooltip align="center">
<template #default="scope">
<el-tag type="warning" v-if="scope.row.nums > 0">{{ scope.row.nums }}</el-tag>
<span v-else>{{ scope.row.nums || 0 }}</span>
</template>
</el-table-column>
<el-table-column prop="createTime" label="创建时间" show-overflow-tooltip align="center" width="180">
<template #default="scope">
<span>{{ scope.row.createTime ? scope.row.createTime.split(' ')[0] + ' ' + scope.row.createTime.split(' ')[1] : '-' }}</span>
</template>
</el-table-column>
<el-table-column prop="updateTime" label="更新时间" show-overflow-tooltip align="center" width="180">
<template #default="scope">
<span>{{ scope.row.updateTime ? scope.row.updateTime.split(' ')[0] + ' ' + scope.row.updateTime.split(' ')[1] : '-' }}</span>
</template>
</el-table-column>
<el-table-column label="操作" width="150" align="center" fixed="right">
<template #default="scope">
<el-button
icon="View"
text
type="primary"
@click="handleView(scope.row)">
查看详情
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<pagination
@size-change="sizeChangeHandle"
@current-change="currentChangeHandle"
v-bind="state.pagination" />
</div>
</div>
</template>
<script setup lang="ts" name="PendingWork">
import { reactive, ref, onMounted } from 'vue'
import { BasicTableProps, useTable } from "/@/hooks/table";
import { fetchList } from "/@/api/stuwork/pendingwork";
import { getDeptList } from "/@/api/basic/basicclass";
import { getClassListByRole } from "/@/api/basic/basicclass";
import { useMessage } from "/@/hooks/message";
// 定义变量内容
const searchFormRef = ref()
const showSearch = ref(true)
const deptList = ref<any[]>([])
const classList = ref<any[]>([])
// 搜索表单
const searchForm = reactive({
deptCode: '',
classCode: ''
})
// 配置 useTable
const state: BasicTableProps = reactive<BasicTableProps>({
queryForm: searchForm,
pageList: fetchList,
props: {
item: 'records',
totalCount: 'total'
},
createdIsNeed: true
})
// table hook
const {
getDataList,
currentChangeHandle,
sizeChangeHandle,
tableStyle
} = useTable(state)
// 查询
const handleSearch = () => {
getDataList()
}
// 重置
const handleReset = () => {
searchFormRef.value?.resetFields()
searchForm.deptCode = ''
searchForm.classCode = ''
getDataList()
}
// 查看详情
const handleView = (row: any) => {
useMessage().warning('查看详情功能待实现')
console.log('查看详情', row)
}
// 获取学院列表
const getDeptListData = async () => {
try {
const res = await getDeptList()
if (res.data) {
deptList.value = Array.isArray(res.data) ? res.data : []
}
} catch (err) {
console.error('获取学院列表失败', err)
deptList.value = []
}
}
// 获取班级列表
const getClassListData = async () => {
try {
const res = await getClassListByRole()
if (res.data) {
classList.value = Array.isArray(res.data) ? res.data : []
}
} catch (err) {
console.error('获取班级列表失败', err)
classList.value = []
}
}
// 初始化
onMounted(() => {
getDeptListData()
getClassListData()
})
</script>

View File

@@ -0,0 +1,149 @@
<template>
<el-dialog
title="新增工学交替"
v-model="visible"
:close-on-click-modal="false"
draggable
width="800px">
<el-form
ref="dataFormRef"
:model="form"
:rules="dataRules"
label-width="120px"
v-loading="loading">
<el-row :gutter="24">
<el-col :span="12" class="mb20">
<el-form-item label="班级" prop="classCode">
<el-select
v-model="form.classCode"
placeholder="请选择班级"
clearable
filterable
style="width: 100%">
<el-option
v-for="item in classList"
:key="item.classCode"
:label="item.classNo"
:value="item.classCode">
</el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item label="工学交替日期范围" prop="dateRange">
<el-date-picker
v-model="form.dateRange"
type="daterange"
range-separator=""
start-placeholder="开始日期"
end-placeholder="结束日期"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
style="width: 100%" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="visible = false"> </el-button>
<el-button type="primary" @click="onSubmit" :disabled="loading"> </el-button>
</span>
</template>
</el-dialog>
</template>
<script setup lang="ts" name="StuWorkStudyAlternateFormDialog">
import { ref, reactive, nextTick, onMounted } from 'vue'
import { useMessage } from '/@/hooks/message'
import { addObj } from '/@/api/stuwork/stuworkstudyalternate'
import { getClassListByRole } from '/@/api/basic/basicclass'
const emit = defineEmits(['refresh'])
// 定义变量内容
const dataFormRef = ref()
const visible = ref(false)
const loading = ref(false)
const classList = ref<any[]>([])
// 提交表单数据
const form = reactive({
classCode: '',
dateRange: null as [string, string] | null
})
// 定义校验规则
const dataRules = {
classCode: [
{ required: true, message: '请选择班级', trigger: 'change' }
],
dateRange: [
{ required: true, message: '请选择工学交替日期范围', trigger: 'change' }
]
}
// 打开弹窗
const openDialog = async () => {
visible.value = true
nextTick(() => {
dataFormRef.value?.resetFields()
form.classCode = ''
form.dateRange = null
})
}
// 提交表单
const onSubmit = async () => {
if (!dataFormRef.value) return
await dataFormRef.value.validate(async (valid: boolean) => {
if (!valid) return
if (!form.dateRange || form.dateRange.length !== 2) {
useMessage().error('请选择日期范围')
return
}
loading.value = true
try {
await addObj({
classCode: form.classCode,
dateRange: form.dateRange
})
useMessage().success('新增成功')
visible.value = false
emit('refresh')
} catch (err: any) {
useMessage().error(err.msg || '新增失败')
} finally {
loading.value = false
}
})
}
// 获取班级列表
const getClassListData = async () => {
try {
const res = await getClassListByRole()
if (res.data) {
classList.value = Array.isArray(res.data) ? res.data : []
}
} catch (err) {
console.error('获取班级列表失败', err)
classList.value = []
}
}
// 初始化
onMounted(() => {
getClassListData()
})
// 暴露方法给父组件
defineExpose({
openDialog
})
</script>

View File

@@ -0,0 +1,317 @@
<template>
<div class="layout-padding">
<div class="layout-padding-auto layout-padding-view">
<!-- 搜索表单 -->
<el-row v-show="showSearch">
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch">
<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-select
v-model="searchForm.schoolTerm"
placeholder="请选择学期"
clearable
style="width: 200px">
<el-option
v-for="item in schoolTermList"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</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="classNo">
<el-input
v-model="searchForm.classNo"
placeholder="请输入班号"
clearable
style="width: 200px" />
</el-form-item>
<el-form-item label="学号" prop="stuNo">
<el-input
v-model="searchForm.stuNo"
placeholder="请输入学号"
clearable
style="width: 200px" />
</el-form-item>
<el-form-item label="姓名" prop="realName">
<el-input
v-model="searchForm.realName"
placeholder="请输入姓名"
clearable
style="width: 200px" />
</el-form-item>
<el-form-item label="日期范围" prop="dateRange">
<el-date-picker
v-model="searchForm.dateRange"
type="daterange"
range-separator=""
start-placeholder="开始日期"
end-placeholder="结束日期"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
clearable
style="width: 240px" />
</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-row>
<!-- 操作按钮 -->
<el-row>
<div class="mb8" style="width: 100%">
<el-button
icon="FolderAdd"
type="primary"
class="ml10"
@click="formDialogRef.openDialog()">
</el-button>
<el-button
icon="User"
type="success"
class="ml10"
@click="teacherDialogRef.openDialog()">
指定带班教师
</el-button>
<right-toolbar
v-model:showSearch="showSearch"
class="ml10 mr20"
style="float: right;"
@queryTable="getDataList">
</right-toolbar>
</div>
</el-row>
<!-- 表格 -->
<el-table
:data="state.dataList"
v-loading="state.loading"
border
:cell-style="tableStyle.cellStyle"
:header-cell-style="tableStyle.headerCellStyle">
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="schoolYear" label="学年" show-overflow-tooltip align="center" />
<el-table-column prop="schoolTerm" label="学期" show-overflow-tooltip align="center" />
<el-table-column prop="deptName" label="系部名称" show-overflow-tooltip align="center" />
<el-table-column prop="stuNo" label="学号" show-overflow-tooltip align="center" />
<el-table-column prop="realName" label="姓名" show-overflow-tooltip align="center" />
<el-table-column prop="classNo" label="班号" show-overflow-tooltip align="center" />
<el-table-column prop="className" label="班级简称" show-overflow-tooltip align="center" />
<el-table-column prop="startTime" label="工学交替开始时间" show-overflow-tooltip align="center" width="180">
<template #default="scope">
<span>{{ scope.row.startTime ? scope.row.startTime.split(' ')[0] : '-' }}</span>
</template>
</el-table-column>
<el-table-column prop="endTime" label="工学交替结束时间" show-overflow-tooltip align="center" width="180">
<template #default="scope">
<span>{{ scope.row.endTime ? scope.row.endTime.split(' ')[0] : '-' }}</span>
</template>
</el-table-column>
<el-table-column prop="attendanceTeacherName" label="带班教师" show-overflow-tooltip align="center">
<template #default="scope">
<span>{{ scope.row.attendanceTeacherName || '-' }}</span>
</template>
</el-table-column>
<el-table-column label="操作" width="100" align="center" fixed="right">
<template #default="scope">
<el-button
icon="Delete"
text
type="danger"
@click="handleDelete(scope.row)">
删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<pagination
@size-change="sizeChangeHandle"
@current-change="currentChangeHandle"
v-bind="state.pagination" />
</div>
<!-- 新增表单弹窗 -->
<form-dialog ref="formDialogRef" @refresh="getDataList" />
<!-- 指定带班教师弹窗 -->
<teacher-dialog ref="teacherDialogRef" @refresh="getDataList" />
</div>
</template>
<script setup lang="ts" name="StuWorkStudyAlternate">
import { reactive, ref, onMounted } from 'vue'
import { BasicTableProps, useTable } from "/@/hooks/table";
import { fetchList, delObj } from "/@/api/stuwork/stuworkstudyalternate";
import { getDeptList } from "/@/api/basic/basicclass";
import { queryAllSchoolYear } from "/@/api/basic/basicyear";
import { getDicts } from "/@/api/admin/dict";
import { useMessage, useMessageBox } from "/@/hooks/message";
import FormDialog from './form.vue'
import TeacherDialog from './teacher.vue'
// 定义变量内容
const searchFormRef = ref()
const showSearch = ref(true)
const deptList = ref<any[]>([])
const schoolYearList = ref<any[]>([])
const schoolTermList = ref<any[]>([])
const formDialogRef = ref()
const teacherDialogRef = ref()
// 搜索表单
const searchForm = reactive({
schoolYear: '',
schoolTerm: '',
deptCode: '',
classNo: '',
stuNo: '',
realName: '',
dateRange: null as [string, string] | null
})
// 配置 useTable
const state: BasicTableProps = reactive<BasicTableProps>({
queryForm: searchForm,
pageList: fetchList,
props: {
item: 'tableData.records',
totalCount: 'tableData.total'
},
createdIsNeed: true
})
// table hook
const {
getDataList,
currentChangeHandle,
sizeChangeHandle,
tableStyle
} = useTable(state)
// 查询
const handleSearch = () => {
// 处理日期范围
const params: any = { ...searchForm }
if (searchForm.dateRange && searchForm.dateRange.length === 2) {
params.dateRangeStr = `${searchForm.dateRange[0]},${searchForm.dateRange[1]}`
}
delete params.dateRange
// 处理学年数组
if (params.schoolYear) {
params.schoolYear = [params.schoolYear]
}
// 更新查询参数
Object.assign(searchForm, params)
getDataList()
}
// 重置
const handleReset = () => {
searchFormRef.value?.resetFields()
searchForm.schoolYear = ''
searchForm.schoolTerm = ''
searchForm.deptCode = ''
searchForm.classNo = ''
searchForm.stuNo = ''
searchForm.realName = ''
searchForm.dateRange = null
getDataList()
}
// 删除
const handleDelete = async (row: any) => {
try {
await useMessageBox().confirm('确定要删除这条记录吗?')
await delObj([row.id])
useMessage().success('删除成功')
getDataList()
} catch (err: any) {
if (err !== 'cancel') {
useMessage().error(err.msg || '删除失败')
}
}
}
// 获取学院列表
const getDeptListData = async () => {
try {
const res = await getDeptList()
if (res.data) {
deptList.value = Array.isArray(res.data) ? res.data : []
}
} catch (err) {
console.error('获取学院列表失败', err)
deptList.value = []
}
}
// 获取学年列表
const getSchoolYearList = async () => {
try {
const res = await queryAllSchoolYear()
if (res.data && Array.isArray(res.data)) {
schoolYearList.value = res.data
}
} catch (err) {
console.error('获取学年列表失败', err)
schoolYearList.value = []
}
}
// 获取学期列表
const getSchoolTermList = async () => {
try {
const res = await getDicts('school_term')
if (res.data && Array.isArray(res.data)) {
schoolTermList.value = res.data
}
} catch (err) {
console.error('获取学期列表失败', err)
schoolTermList.value = []
}
}
// 初始化
onMounted(() => {
getDeptListData()
getSchoolYearList()
getSchoolTermList()
})
</script>

View File

@@ -0,0 +1,211 @@
<template>
<el-dialog
title="指定带班教师"
v-model="visible"
:close-on-click-modal="false"
draggable
width="600px">
<el-form
ref="dataFormRef"
:model="form"
:rules="dataRules"
label-width="120px"
v-loading="loading">
<el-form-item label="学年" prop="schoolYear">
<el-select
v-model="form.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-select
v-model="form.schoolTerm"
placeholder="请选择学期"
clearable
style="width: 100%">
<el-option
v-for="item in schoolTermList"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="班号" prop="classNo">
<el-input
v-model="form.classNo"
placeholder="请输入班号"
clearable
style="width: 100%" />
</el-form-item>
<el-form-item label="教师" prop="teacherNo">
<el-select
v-model="form.teacherNo"
placeholder="请选择教师"
clearable
filterable
style="width: 100%"
@search="handleTeacherSearch">
<el-option
v-for="item in teacherList"
:key="item.teacherNo"
:label="`${item.realName}(${item.teacherNo})`"
:value="item.teacherNo">
</el-option>
</el-select>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="visible = false"> </el-button>
<el-button type="primary" @click="onSubmit" :disabled="loading"> </el-button>
</span>
</template>
</el-dialog>
</template>
<script setup lang="ts" name="StuWorkStudyAlternateTeacherDialog">
import { ref, reactive, nextTick, onMounted } from 'vue'
import { useMessage } from '/@/hooks/message'
import { chooseTeacherAttendance } from '/@/api/stuwork/stuworkstudyalternate'
import { queryAllSchoolYear } from '/@/api/basic/basicyear'
import { getDicts } from '/@/api/admin/dict'
import { getTeacherInfoCommon } from '/@/api/professional/professionaluser/teacherbase'
const emit = defineEmits(['refresh'])
// 定义变量内容
const dataFormRef = ref()
const visible = ref(false)
const loading = ref(false)
const schoolYearList = ref<any[]>([])
const schoolTermList = ref<any[]>([])
const teacherList = ref<any[]>([])
// 提交表单数据
const form = reactive({
schoolYear: '',
schoolTerm: '',
classNo: '',
teacherNo: ''
})
// 定义校验规则
const dataRules = {
schoolYear: [
{ required: true, message: '请选择学年', trigger: 'change' }
],
schoolTerm: [
{ required: true, message: '请选择学期', trigger: 'change' }
],
classNo: [
{ required: true, message: '请输入班号', trigger: 'blur' }
],
teacherNo: [
{ required: true, message: '请选择教师', trigger: 'change' }
]
}
// 教师搜索
const handleTeacherSearch = async (keyword: string) => {
if (!keyword) {
teacherList.value = []
return
}
try {
const res = await getTeacherInfoCommon({ searchKeywords: keyword })
if (res.data && Array.isArray(res.data)) {
teacherList.value = res.data
}
} catch (err) {
console.error('搜索教师失败', err)
teacherList.value = []
}
}
// 打开弹窗
const openDialog = async () => {
visible.value = true
nextTick(() => {
dataFormRef.value?.resetFields()
form.schoolYear = ''
form.schoolTerm = ''
form.classNo = ''
form.teacherNo = ''
teacherList.value = []
})
}
// 提交表单
const onSubmit = async () => {
if (!dataFormRef.value) return
await dataFormRef.value.validate(async (valid: boolean) => {
if (!valid) return
loading.value = true
try {
await chooseTeacherAttendance({
schoolYear: form.schoolYear,
schoolTerm: form.schoolTerm,
classNo: form.classNo,
teacherNo: form.teacherNo
})
useMessage().success('指定成功')
visible.value = false
emit('refresh')
} catch (err: any) {
useMessage().error(err.msg || '指定失败')
} finally {
loading.value = false
}
})
}
// 获取学年列表
const getSchoolYearList = async () => {
try {
const res = await queryAllSchoolYear()
if (res.data && Array.isArray(res.data)) {
schoolYearList.value = res.data
}
} catch (err) {
console.error('获取学年列表失败', err)
schoolYearList.value = []
}
}
// 获取学期列表
const getSchoolTermList = async () => {
try {
const res = await getDicts('school_term')
if (res.data && Array.isArray(res.data)) {
schoolTermList.value = res.data
}
} catch (err) {
console.error('获取学期列表失败', err)
schoolTermList.value = []
}
}
// 初始化
onMounted(() => {
getSchoolYearList()
getSchoolTermList()
})
// 暴露方法给父组件
defineExpose({
openDialog
})
</script>

View File

@@ -0,0 +1,114 @@
<template>
<el-dialog
title="水电明细"
v-model="visible"
:close-on-click-modal="false"
draggable
width="1200px">
<el-table
:data="detailList"
v-loading="loading"
border
:cell-style="{ textAlign: 'center' }"
:header-cell-style="{ textAlign: 'center', background: 'var(--el-table-row-hover-bg-color)' }">
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="year" label="年份" show-overflow-tooltip align="center" />
<el-table-column prop="month" label="月份" show-overflow-tooltip align="center" />
<el-table-column prop="subiMonthSum" label="用量" show-overflow-tooltip align="center" />
<el-table-column prop="subWatFlagSum" label="费用" show-overflow-tooltip align="center">
<template #default="scope">
<span>{{ scope.row.subWatFlagSum ? Number(scope.row.subWatFlagSum).toFixed(2) : '0.00' }}</span>
</template>
</el-table-column>
<el-table-column prop="flag" label="用电用水" show-overflow-tooltip align="center">
<template #default="scope">
<span>{{ formatFlag(scope.row.flag) }}</span>
</template>
</el-table-column>
<el-table-column prop="meterNum" label="冷水热水" show-overflow-tooltip align="center">
<template #default="scope">
<span>{{ formatMeterNum(scope.row.meterNum) }}</span>
</template>
</el-table-column>
</el-table>
<template #footer>
<span class="dialog-footer">
<el-button @click="visible = false"> </el-button>
</span>
</template>
</el-dialog>
</template>
<script setup lang="ts" name="WaterDetailDialog">
import { ref, onMounted } from 'vue'
import { useMessage } from '/@/hooks/message'
import { lookDetails } from '/@/api/stuwork/watermonthreport'
// 定义变量内容
const visible = ref(false)
const loading = ref(false)
const detailList = ref<any[]>([])
const currentRoomNo = ref('')
// 格式化用电用水
const formatFlag = (value: string | number) => {
if (value === '1' || value === 1) {
return '用电'
} else if (value === '2' || value === 2) {
return '用水'
}
return value || '-'
}
// 格式化冷水热水
const formatMeterNum = (value: string | number) => {
if (value === '11' || value === 11) {
return '冷水'
} else if (value === '12' || value === 12) {
return '热水'
}
return value || '-'
}
// 打开弹窗
const openDialog = async (roomNo: string) => {
visible.value = true
currentRoomNo.value = roomNo
detailList.value = []
// 获取明细数据
await getDetailData(roomNo)
}
// 获取明细数据
const getDetailData = async (roomNo: string) => {
loading.value = true
try {
const res = await lookDetails(roomNo)
if (res.data) {
if (Array.isArray(res.data)) {
detailList.value = res.data
} else if (res.data.records && Array.isArray(res.data.records)) {
detailList.value = res.data.records
} else if (res.data.list && Array.isArray(res.data.list)) {
detailList.value = res.data.list
} else {
detailList.value = []
}
} else {
detailList.value = []
}
} catch (err: any) {
useMessage().error(err.msg || '获取明细数据失败')
detailList.value = []
} finally {
loading.value = false
}
}
// 暴露方法给父组件
defineExpose({
openDialog
})
</script>

View File

@@ -0,0 +1,279 @@
<template>
<el-dialog
:title="form.id ? '编辑' : '新增'"
v-model="visible"
:close-on-click-modal="false"
draggable
width="800px">
<el-form
ref="dataFormRef"
:model="form"
:rules="dataRules"
label-width="100px"
:validate-on-rule-change="false"
v-loading="loading">
<el-row :gutter="24">
<el-col :span="12" class="mb20">
<el-form-item label="楼号" prop="buildNo">
<el-select
v-model="form.buildNo"
placeholder="请选择楼号"
clearable
filterable
style="width: 100%">
<el-option
v-for="item in buildingList"
:key="item.buildingNo"
:label="item.buildingNo"
:value="item.buildingNo">
</el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item label="宿舍号" prop="roomNo">
<el-input v-model="form.roomNo" placeholder="请输入宿舍号" />
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item label="几人间" prop="bedNum">
<el-select
v-model="form.bedNum"
placeholder="请选择几人间"
clearable
style="width: 100%">
<el-option
v-for="item in bedNumList"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item label="已住人数" prop="liveNum">
<el-input-number
v-model="form.liveNum"
:min="0"
placeholder="请输入已住人数"
style="width: 100%" />
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item label="班号" prop="classNo">
<el-input v-model="form.classNo" placeholder="请输入班号" />
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item label="班主任工号" prop="teacherNo">
<el-input v-model="form.teacherNo" placeholder="请输入班主任工号" />
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item label="班主任姓名" prop="teacherRealName">
<el-input v-model="form.teacherRealName" placeholder="请输入班主任姓名" />
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item label="校园补贴" prop="oddbMoney">
<el-input-number
v-model="form.oddbMoney"
:precision="2"
:min="0"
placeholder="请输入校园补贴"
style="width: 100%" />
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item label="消费金额" prop="costMoney">
<el-input-number
v-model="form.costMoney"
:precision="2"
:min="0"
placeholder="请输入消费金额"
style="width: 100%" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="visible = false"> </el-button>
<el-button type="primary" @click="onSubmit" :disabled="loading"> </el-button>
</span>
</template>
</el-dialog>
</template>
<script setup lang="ts" name="WaterDetailFormDialog">
import { ref, reactive, nextTick, onMounted } from 'vue'
import { useMessage } from '/@/hooks/message'
import { addObj, putObj } from '/@/api/stuwork/waterdetail'
import { getBuildingList } from '/@/api/stuwork/dormbuilding'
import { getDicts } from '/@/api/admin/dict'
const emit = defineEmits(['refresh'])
// 定义变量内容
const dataFormRef = ref()
const visible = ref(false)
const loading = ref(false)
const operType = ref('add') // add 或 edit
const buildingList = ref<any[]>([])
const bedNumList = ref<any[]>([])
// 提交表单数据
const form = reactive({
id: '',
buildNo: '',
roomNo: '',
bedNum: '',
liveNum: 0,
classNo: '',
teacherNo: '',
teacherRealName: '',
oddbMoney: 0,
costMoney: 0
})
// 定义校验规则
const dataRules = {
buildNo: [
{ required: true, message: '请选择楼号', trigger: 'change' }
],
roomNo: [
{ required: true, message: '请输入宿舍号', trigger: 'blur' }
],
bedNum: [
{ required: true, message: '请选择床位数', trigger: 'change' }
],
liveNum: [
{ required: true, message: '请输入已住人数', trigger: 'blur' },
{ type: 'number', min: 0, message: '已住人数必须大于等于0', trigger: 'blur' }
]
}
// 打开弹窗
const openDialog = async (type: string = 'add', row?: any) => {
visible.value = true
operType.value = type
// 重置表单数据
nextTick(() => {
dataFormRef.value?.resetFields()
form.id = ''
form.buildNo = ''
form.roomNo = ''
form.bedNum = ''
form.liveNum = 0
form.classNo = ''
form.teacherNo = ''
form.teacherRealName = ''
form.oddbMoney = 0
form.costMoney = 0
// 编辑时填充数据
if (type === 'edit' && row) {
form.id = row.id
form.buildNo = row.buildNo || ''
form.roomNo = row.roomNo || ''
form.bedNum = row.bedNum || ''
form.liveNum = row.liveNum || 0
form.classNo = row.classNo || ''
form.teacherNo = row.teacherNo || ''
form.teacherRealName = row.teacherRealName || ''
form.oddbMoney = row.oddbMoney || 0
form.costMoney = row.costMoney || 0
}
})
}
// 提交表单
const onSubmit = async () => {
if (!dataFormRef.value) return
await dataFormRef.value.validate(async (valid: boolean) => {
if (!valid) return
loading.value = true
try {
const submitData: any = {
buildNo: form.buildNo,
roomNo: form.roomNo,
bedNum: form.bedNum,
liveNum: form.liveNum,
classNo: form.classNo,
teacherNo: form.teacherNo,
teacherRealName: form.teacherRealName,
oddbMoney: form.oddbMoney,
costMoney: form.costMoney
}
if (operType.value === 'add') {
await addObj(submitData)
useMessage().success('新增成功')
} else {
submitData.id = form.id
await putObj(submitData)
useMessage().success('编辑成功')
}
visible.value = false
emit('refresh')
} catch (err: any) {
useMessage().error(err.msg || (operType.value === 'add' ? '新增失败' : '编辑失败'))
} finally {
loading.value = false
}
})
}
// 获取楼号列表
const getBuildingListData = async () => {
try {
const res = await getBuildingList()
if (res.data) {
buildingList.value = Array.isArray(res.data) ? res.data : []
}
} catch (err) {
console.error('获取楼号列表失败', err)
buildingList.value = []
}
}
// 获取几人间字典
const getBedNumDict = async () => {
try {
const res = await getDicts('room_stu_num')
if (res.data) {
bedNumList.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) {
console.error('获取几人间字典失败', err)
bedNumList.value = []
}
}
// 初始化
onMounted(() => {
getBuildingListData()
getBedNumDict()
})
// 暴露方法给父组件
defineExpose({
openDialog
})
</script>

View File

@@ -0,0 +1,335 @@
<template>
<div class="layout-padding">
<div class="layout-padding-auto layout-padding-view">
<!-- 搜索表单 -->
<el-row v-show="showSearch">
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch">
<el-form-item label="可用余额" prop="effectiveMoney">
<el-input-number
v-model="searchForm.effectiveMoney"
placeholder="可用余额大于"
:precision="2"
:min="0"
clearable
style="width: 200px" />
</el-form-item>
<el-form-item label="已住人数" prop="isLiveNum">
<el-select
v-model="searchForm.isLiveNum"
placeholder="请选择"
clearable
style="width: 200px">
<el-option label="大于0" :value="true" />
<el-option label="等于0" :value="false" />
</el-select>
</el-form-item>
<el-form-item label="楼号" prop="buildNo">
<el-select
v-model="searchForm.buildNo"
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="roomNo">
<el-input
v-model="searchForm.roomNo"
placeholder="请输入宿舍号"
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-row>
<!-- 操作按钮 -->
<el-row>
<div class="mb8" style="width: 100%">
<el-button
icon="FolderAdd"
type="primary"
class="ml10"
@click="formDialogRef.openDialog()">
</el-button>
<el-button
icon="Download"
type="success"
class="ml10"
@click="handleExport">
</el-button>
<el-button
icon="Setting"
type="warning"
class="ml10"
@click="handleInitWaterOrder">
初始化本期水电补贴
</el-button>
<right-toolbar
v-model:showSearch="showSearch"
class="ml10 mr20"
style="float: right;"
@queryTable="getDataList">
</right-toolbar>
</div>
</el-row>
<!-- 表格 -->
<el-table
:data="state.dataList"
v-loading="state.loading"
border
:cell-style="tableStyle.cellStyle"
:header-cell-style="tableStyle.headerCellStyle"
@sort-change="sortChangeHandle">
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="buildNo" label="楼号" show-overflow-tooltip align="center" />
<el-table-column prop="roomNo" label="宿舍号" show-overflow-tooltip align="center" />
<el-table-column prop="bedNum" label="几人间" show-overflow-tooltip align="center" />
<el-table-column prop="liveNum" label="已住人数" show-overflow-tooltip align="center" />
<el-table-column prop="oddbMoney" label="校园补贴" show-overflow-tooltip align="center">
<template #default="scope">
<span>{{ scope.row.oddbMoney ? Number(scope.row.oddbMoney).toFixed(2) : '0.00' }}</span>
</template>
</el-table-column>
<el-table-column prop="rechargeMoney" label="充值金额" show-overflow-tooltip align="center">
<template #default="scope">
<span>{{ scope.row.rechargeMoney ? Number(scope.row.rechargeMoney).toFixed(2) : '0.00' }}</span>
</template>
</el-table-column>
<el-table-column prop="costMoney" label="消费金额" show-overflow-tooltip align="center">
<template #default="scope">
<span>{{ scope.row.costMoney ? Number(scope.row.costMoney).toFixed(2) : '0.00' }}</span>
</template>
</el-table-column>
<el-table-column prop="effectiveMoney" label="可用余额" show-overflow-tooltip align="center">
<template #default="scope">
<span :style="{ color: Number(scope.row.effectiveMoney) < 0 ? 'red' : '' }">
{{ scope.row.effectiveMoney ? Number(scope.row.effectiveMoney).toFixed(2) : '0.00' }}
</span>
</template>
</el-table-column>
<el-table-column label="操作" width="200" align="center" fixed="right">
<template #default="scope">
<el-button
icon="Edit"
text
type="primary"
@click="handleEdit(scope.row)">
编辑
</el-button>
<el-button
icon="View"
text
type="info"
@click="handleViewDetail(scope.row)">
明细
</el-button>
<el-button
icon="Delete"
text
type="danger"
@click="handleDelete(scope.row)">
删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<pagination
@size-change="sizeChangeHandle"
@current-change="currentChangeHandle"
v-bind="state.pagination" />
</div>
<!-- 表单弹窗 -->
<FormDialog ref="formDialogRef" @refresh="getDataList()" />
<!-- 明细弹窗 -->
<DetailDialog ref="detailDialogRef" />
<!-- 初始化水电补贴弹窗 -->
<el-dialog v-model="initDialogVisible" title="初始化本期水电补贴" :width="500" :close-on-click-modal="false" draggable>
<el-form ref="initFormRef" :model="initForm" :rules="initRules" label-width="120px">
<el-form-item label="补贴金额" prop="costMoney">
<el-input-number
v-model="initForm.costMoney"
:precision="2"
:min="0"
placeholder="请输入补贴金额"
style="width: 100%" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="initDialogVisible = false"> </el-button>
<el-button type="primary" @click="confirmInit" :loading="initLoading"> </el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts" name="WaterDetail">
import { reactive, ref, onMounted } from 'vue'
import { BasicTableProps, useTable } from "/@/hooks/table";
import { fetchList, delObjs, initWaterOrder } from "/@/api/stuwork/waterdetail";
import { getBuildingList } from "/@/api/stuwork/dormbuilding";
import { useMessage, useMessageBox } from "/@/hooks/message";
import FormDialog from './form.vue';
import DetailDialog from './detail.vue';
// 定义变量内容
const searchFormRef = ref()
const formDialogRef = ref()
const detailDialogRef = ref()
const initFormRef = ref()
const showSearch = ref(true)
const buildingList = ref<any[]>([])
const initDialogVisible = ref(false)
const initLoading = ref(false)
// 搜索表单
const searchForm = reactive({
effectiveMoney: undefined as number | undefined,
isLiveNum: undefined as boolean | undefined,
buildNo: '',
roomNo: ''
})
// 初始化表单
const initForm = reactive({
costMoney: 0
})
// 初始化表单验证规则
const initRules = {
costMoney: [
{ required: true, message: '请输入补贴金额', trigger: 'blur' },
{ type: 'number', min: 0, message: '补贴金额必须大于等于0', trigger: 'blur' }
]
}
// 配置 useTable
const state: BasicTableProps = reactive<BasicTableProps>({
queryForm: searchForm,
pageList: fetchList,
props: {
item: 'records',
totalCount: 'total'
},
createdIsNeed: true
})
// table hook
const {
getDataList,
currentChangeHandle,
sizeChangeHandle,
sortChangeHandle,
tableStyle
} = useTable(state)
// 查询
const handleSearch = () => {
getDataList()
}
// 重置
const handleReset = () => {
searchFormRef.value?.formRef?.resetFields()
searchForm.effectiveMoney = undefined
searchForm.isLiveNum = undefined
searchForm.buildNo = ''
searchForm.roomNo = ''
getDataList()
}
// 编辑
const handleEdit = (row: any) => {
formDialogRef.value.openDialog('edit', row)
}
// 查看明细
const handleViewDetail = (row: any) => {
detailDialogRef.value.openDialog(row.roomNo)
}
// 删除
const handleDelete = async (row: any) => {
try {
await useMessageBox().confirm('确定要删除该记录吗?')
await delObjs([row.id])
useMessage().success('删除成功')
getDataList()
} catch (err: any) {
if (err !== 'cancel') {
useMessage().error(err.msg || '删除失败')
}
}
}
// 导出
const handleExport = () => {
// TODO: 实现导出功能
useMessage().warning('导出功能开发中')
}
// 初始化本期水电补贴
const handleInitWaterOrder = () => {
initDialogVisible.value = true
initForm.costMoney = 0
}
// 确认初始化
const confirmInit = async () => {
if (!initFormRef.value) return
await initFormRef.value.validate(async (valid: boolean) => {
if (!valid) return
initLoading.value = true
try {
await initWaterOrder({ costMoney: initForm.costMoney })
useMessage().success('初始化成功')
initDialogVisible.value = false
getDataList()
} catch (err: any) {
useMessage().error(err.msg || '初始化失败')
} finally {
initLoading.value = false
}
})
}
// 获取楼号列表
const getBuildingListData = async () => {
try {
const res = await getBuildingList()
if (res.data) {
buildingList.value = Array.isArray(res.data) ? res.data : []
}
} catch (err) {
console.error('获取楼号列表失败', err)
buildingList.value = []
}
}
// 初始化
onMounted(() => {
getBuildingListData()
})
</script>

View File

@@ -0,0 +1,262 @@
<template>
<el-dialog :title="form.id ? '编辑' : '新增'" v-model="visible" :width="600" :close-on-click-modal="false" draggable>
<el-form ref="dataFormRef" :model="form" :rules="dataRules" label-width="120px" v-loading="loading">
<el-form-item label="宿舍号" prop="roomNo">
<el-select
v-model="form.roomNo"
placeholder="请选择宿舍号"
clearable
filterable
style="width: 100%">
<el-option
v-for="item in roomList"
:key="item.roomNo"
:label="item.roomNo"
:value="item.roomNo">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="学年" prop="year">
<el-select
v-model="form.year"
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="period">
<el-select
v-model="form.period"
placeholder="请选择学期"
clearable
style="width: 100%">
<el-option
v-for="item in schoolTermList"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="类型" prop="type">
<el-select
v-model="form.type"
placeholder="请选择类型"
clearable
style="width: 100%">
<el-option
v-for="item in typeList"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="金额" prop="money">
<el-input-number
v-model="form.money"
:precision="2"
:min="0"
placeholder="请输入金额"
style="width: 100%" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="visible = false"> </el-button>
<el-button type="primary" @click="onSubmit" :disabled="loading"> </el-button>
</span>
</template>
</el-dialog>
</template>
<script setup lang="ts" name="WaterOrderFormDialog">
import { ref, reactive, nextTick, onMounted } from 'vue'
import { useMessage } from '/@/hooks/message'
import { addObj, putObj, getObj, getRoomList } from '/@/api/stuwork/waterorder'
import { queryAllSchoolYear } from '/@/api/basic/basicyear'
import { getDicts } from '/@/api/admin/dict'
const emit = defineEmits(['refresh'])
// 定义变量内容
const dataFormRef = ref()
const visible = ref(false)
const loading = ref(false)
const operType = ref('add') // add 或 edit
const schoolYearList = ref<any[]>([])
const schoolTermList = ref<any[]>([])
const typeList = ref<any[]>([])
const roomList = ref<any[]>([])
// 提交表单数据
const form = reactive({
id: '',
roomNo: '',
year: '',
period: '',
type: '',
money: 0
})
// 定义校验规则
const dataRules = {
roomNo: [
{ required: true, message: '请选择宿舍号', trigger: 'change' }
],
year: [
{ required: true, message: '请选择学年', trigger: 'change' }
],
period: [
{ required: true, message: '请选择学期', trigger: 'change' }
],
type: [
{ required: true, message: '请选择类型', trigger: 'change' }
],
money: [
{ required: true, message: '请输入金额', trigger: 'blur' },
{ type: 'number', min: 0, message: '金额必须大于等于0', trigger: 'blur' }
]
}
// 打开弹窗
const openDialog = async (type: string = 'add', row?: any) => {
visible.value = true
operType.value = type
// 重置表单数据
nextTick(() => {
dataFormRef.value?.resetFields()
form.id = ''
form.roomNo = ''
form.year = ''
form.period = ''
form.type = ''
form.money = 0
// 编辑时填充数据
if (type === 'edit' && row) {
form.id = row.id
form.roomNo = row.roomNo || ''
form.year = row.year || ''
form.period = row.period || ''
form.type = row.type || ''
form.money = row.money || 0
}
})
}
// 提交表单
const onSubmit = async () => {
if (!dataFormRef.value) return
await dataFormRef.value.validate(async (valid: boolean) => {
if (!valid) return
loading.value = true
try {
const submitData: any = {
roomNo: form.roomNo,
year: form.year,
period: form.period,
type: form.type,
money: form.money
}
if (operType.value === 'add') {
await addObj(submitData)
useMessage().success('新增成功')
} else {
submitData.id = form.id
await putObj(submitData)
useMessage().success('编辑成功')
}
visible.value = false
emit('refresh')
} catch (err: any) {
useMessage().error(err.msg || (operType.value === 'add' ? '新增失败' : '编辑失败'))
} finally {
loading.value = false
}
})
}
// 获取学年列表
const getSchoolYearList = async () => {
try {
const res = await queryAllSchoolYear()
if (res.data) {
schoolYearList.value = Array.isArray(res.data) ? res.data : []
}
} catch (err) {
console.error('获取学年列表失败', err)
schoolYearList.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) {
console.error('获取学期字典失败', err)
schoolTermList.value = []
}
}
// 获取类型字典
const getTypeDict = async () => {
try {
const res = await getDicts('dorm_water_source_type')
if (res.data) {
typeList.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) {
console.error('获取类型字典失败', err)
typeList.value = []
}
}
// 获取宿舍号列表
const getRoomListData = async () => {
try {
const res = await getRoomList()
if (res.data) {
roomList.value = Array.isArray(res.data) ? res.data : []
}
} catch (err) {
console.error('获取宿舍号列表失败', err)
roomList.value = []
}
}
// 初始化
onMounted(() => {
getSchoolYearList()
getSchoolTermDict()
getTypeDict()
getRoomListData()
})
// 暴露方法给父组件
defineExpose({
openDialog
})
</script>

View File

@@ -0,0 +1,370 @@
<template>
<div class="layout-padding">
<div class="layout-padding-auto layout-padding-view">
<!-- 搜索表单 -->
<el-row v-show="showSearch">
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch">
<el-form-item label="宿舍号" prop="roomNo">
<el-input
v-model="searchForm.roomNo"
placeholder="请输入宿舍号"
clearable
style="width: 200px" />
</el-form-item>
<el-form-item label="学年" prop="year">
<el-select
v-model="searchForm.year"
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="period">
<el-select
v-model="searchForm.period"
placeholder="请选择学期"
clearable
style="width: 200px">
<el-option
v-for="item in schoolTermList"
:key="item.value"
:label="item.label"
:value="item.value">
</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-row>
<!-- 操作按钮 -->
<el-row>
<div class="mb8" style="width: 100%">
<el-button
icon="FolderAdd"
type="primary"
class="ml10"
@click="formDialogRef.openDialog()">
</el-button>
<right-toolbar
v-model:showSearch="showSearch"
class="ml10 mr20"
style="float: right;"
@queryTable="getDataList">
</right-toolbar>
</div>
</el-row>
<!-- 表格 -->
<el-table
:data="state.dataList"
v-loading="state.loading"
border
:cell-style="tableStyle.cellStyle"
:header-cell-style="tableStyle.headerCellStyle"
@sort-change="sortChangeHandle">
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="roomNo" label="宿舍号" show-overflow-tooltip align="center" />
<el-table-column prop="year" label="学年" show-overflow-tooltip align="center" />
<el-table-column prop="period" label="学期" show-overflow-tooltip align="center">
<template #default="scope">
<span>{{ formatSchoolTerm(scope.row.period) }}</span>
</template>
</el-table-column>
<el-table-column prop="orderNum" label="订单号" show-overflow-tooltip align="center" />
<el-table-column prop="type" label="类型" show-overflow-tooltip align="center">
<template #default="scope">
<span>{{ formatType(scope.row.type) }}</span>
</template>
</el-table-column>
<el-table-column prop="paymentCode" label="充值类型" show-overflow-tooltip align="center">
<template #default="scope">
<span>{{ formatPaymentType(scope.row.paymentCode) }}</span>
</template>
</el-table-column>
<el-table-column prop="money" label="金额" show-overflow-tooltip align="center" />
<el-table-column prop="chargeAccount" label="充值人的账户" show-overflow-tooltip align="center" />
<el-table-column prop="chargeRealname" label="充值人的真实姓名" show-overflow-tooltip align="center" />
<el-table-column prop="chargeState" label="充值结果" show-overflow-tooltip align="center">
<template #default="scope">
<span>{{ formatChargeState(scope.row.chargeState) }}</span>
</template>
</el-table-column>
<el-table-column prop="state" label="状态" show-overflow-tooltip align="center">
<template #default="scope">
<span>{{ formatState(scope.row.state) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" width="150" align="center" fixed="right">
<template #default="scope">
<el-button
icon="Edit"
text
type="primary"
@click="handleEdit(scope.row)">
编辑
</el-button>
<el-button
icon="Delete"
text
type="danger"
@click="handleDelete(scope.row)">
删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<pagination
@size-change="sizeChangeHandle"
@current-change="currentChangeHandle"
v-bind="state.pagination" />
</div>
<!-- 表单弹窗 -->
<FormDialog ref="formDialogRef" @refresh="getDataList()" />
</div>
</template>
<script setup lang="ts" name="WaterOrder">
import { reactive, ref, onMounted } from 'vue'
import { BasicTableProps, useTable } from "/@/hooks/table";
import { fetchList } from "/@/api/stuwork/waterorder";
import { queryAllSchoolYear } from "/@/api/basic/basicyear";
import { getDicts } from "/@/api/admin/dict";
import { useMessage, useMessageBox } from "/@/hooks/message";
import FormDialog from './form.vue';
// 定义变量内容
const searchFormRef = ref()
const formDialogRef = ref()
const showSearch = ref(true)
const schoolYearList = ref<any[]>([])
const schoolTermList = ref<any[]>([])
const typeList = ref<any[]>([])
const paymentTypeList = ref<any[]>([])
const chargeStateList = ref<any[]>([])
const stateList = ref<any[]>([])
// 搜索表单
const searchForm = reactive({
roomNo: '',
year: '',
period: ''
})
// 配置 useTable
const state: BasicTableProps = reactive<BasicTableProps>({
queryForm: searchForm,
pageList: fetchList,
props: {
item: 'records',
totalCount: 'total'
},
createdIsNeed: true
})
// table hook
const {
getDataList,
currentChangeHandle,
sizeChangeHandle,
sortChangeHandle,
tableStyle
} = useTable(state)
// 查询
const handleSearch = () => {
getDataList()
}
// 重置
const handleReset = () => {
searchFormRef.value?.formRef?.resetFields()
searchForm.roomNo = ''
searchForm.year = ''
searchForm.period = ''
getDataList()
}
// 编辑
const handleEdit = (row: any) => {
formDialogRef.value.openDialog('edit', row)
}
// 删除
const handleDelete = async (row: any) => {
try {
await useMessageBox().confirm('确定要删除该记录吗?')
const { delObjs } = await import("/@/api/stuwork/waterorder")
await delObjs([row.id])
useMessage().success('删除成功')
getDataList()
} catch (err: any) {
if (err !== 'cancel') {
useMessage().error(err.msg || '删除失败')
}
}
}
// 格式化学期
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
}
// 格式化类型
const formatType = (value: string | number) => {
if (value === null || value === undefined || value === '') {
return '-'
}
const dictItem = typeList.value.find(item => item.value == value)
return dictItem ? dictItem.label : value
}
// 格式化充值类型
const formatPaymentType = (value: string | number) => {
if (value === null || value === undefined || value === '') {
return '-'
}
const dictItem = paymentTypeList.value.find(item => item.value == value)
return dictItem ? dictItem.label : value
}
// 格式化充值结果
const formatChargeState = (value: string | number) => {
if (value === null || value === undefined || value === '') {
return '-'
}
const dictItem = chargeStateList.value.find(item => item.value == value)
return dictItem ? dictItem.label : value
}
// 格式化状态
const formatState = (value: string | number) => {
if (value === null || value === undefined || value === '') {
return '-'
}
const dictItem = stateList.value.find(item => item.value == value)
return dictItem ? dictItem.label : value
}
// 获取学年列表
const getSchoolYearList = async () => {
try {
const res = await queryAllSchoolYear()
if (res.data) {
schoolYearList.value = Array.isArray(res.data) ? res.data : []
}
} catch (err) {
console.error('获取学年列表失败', err)
schoolYearList.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) {
console.error('获取学期字典失败', err)
schoolTermList.value = []
}
}
// 获取类型字典
const getTypeDict = async () => {
try {
const res = await getDicts('dorm_water_source_type')
if (res.data) {
typeList.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) {
console.error('获取类型字典失败', err)
typeList.value = []
}
}
// 获取充值类型字典
const getPaymentTypeDict = async () => {
try {
const res = await getDicts('dorm_water_payment_type')
if (res.data) {
paymentTypeList.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) {
console.error('获取充值类型字典失败', err)
paymentTypeList.value = []
}
}
// 获取充值结果字典
const getChargeStateDict = async () => {
try {
const res = await getDicts('dorm_water_charge_state')
if (res.data) {
chargeStateList.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) {
console.error('获取充值结果字典失败', err)
chargeStateList.value = []
}
}
// 获取状态字典
const getStateDict = async () => {
try {
const res = await getDicts('dorm_water_state')
if (res.data) {
stateList.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) {
console.error('获取状态字典失败', err)
stateList.value = []
}
}
// 初始化
onMounted(() => {
getSchoolYearList()
getSchoolTermDict()
getTypeDict()
getPaymentTypeDict()
getChargeStateDict()
getStateDict()
})
</script>

View File

@@ -0,0 +1,287 @@
<template>
<div class="layout-padding">
<div class="layout-padding-auto layout-padding-view">
<!-- 搜索表单 -->
<el-row v-show="showSearch">
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch">
<el-form-item label="学院" prop="deptCode">
<el-select
v-model="searchForm.deptCode"
placeholder="请选择学院"
clearable
filterable
style="width: 200px"
@change="handleDeptChange">
<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 classList"
:key="item.classCode"
:label="item.classNo"
:value="item.classCode">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="来源" prop="source">
<el-select
v-model="searchForm.source"
placeholder="请选择来源"
clearable
style="width: 200px"
@change="handleSourceChange">
<el-option label="考勤记录" value="history" />
<el-option label="考勤" value="attendance" />
</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-row>
<!-- 操作按钮 -->
<el-row>
<div class="mb8" style="width: 100%">
<right-toolbar
v-model:showSearch="showSearch"
class="ml10 mr20"
style="float: right;"
@queryTable="getDataList">
</right-toolbar>
</div>
</el-row>
<!-- 表格 -->
<el-table
:data="state.dataList"
v-loading="state.loading"
border
:cell-style="tableStyle.cellStyle"
:header-cell-style="tableStyle.headerCellStyle">
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="schoolYear" label="学年" show-overflow-tooltip align="center" />
<el-table-column prop="schoolTerm" label="学期" show-overflow-tooltip align="center" />
<el-table-column prop="deptName" label="系部名称" show-overflow-tooltip align="center" />
<el-table-column prop="classCode" label="班级代码" show-overflow-tooltip align="center" />
<el-table-column prop="classNo" label="班级简称" show-overflow-tooltip align="center" />
<el-table-column prop="classProName" label="班级规范名称" show-overflow-tooltip align="center" min-width="200" />
<el-table-column prop="stuNo" label="学号" show-overflow-tooltip align="center" />
<el-table-column prop="realName" label="姓名" show-overflow-tooltip align="center" />
<el-table-column prop="attendanceType" label="考勤类型" show-overflow-tooltip align="center">
<template #default="scope">
<span>{{ formatAttendanceType(scope.row.attendanceType) }}</span>
</template>
</el-table-column>
<el-table-column
v-if="showRemarks"
prop="remarks"
label="落实情况"
show-overflow-tooltip
align="center"
min-width="150">
<template #default="scope">
<span>{{ scope.row.remarks || '-' }}</span>
</template>
</el-table-column>
<el-table-column label="操作" width="150" align="center" fixed="right">
<template #default="scope">
<el-button
icon="View"
text
type="primary"
@click="handleView(scope.row)">
查看详情
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<pagination
@size-change="sizeChangeHandle"
@current-change="currentChangeHandle"
v-bind="state.pagination" />
</div>
</div>
</template>
<script setup lang="ts" name="WorkStudyAttendance">
import { reactive, ref, onMounted } from 'vue'
import { BasicTableProps, useTable } from "/@/hooks/table";
import { fetchList, queryHistoryList } from "/@/api/stuwork/workstudyattendance";
import { getDeptList } from "/@/api/basic/basicclass";
import { getClassListByRole } from "/@/api/basic/basicclass";
import { useMessage } from "/@/hooks/message";
// 定义变量内容
const searchFormRef = ref()
const showSearch = ref(true)
const deptList = ref<any[]>([])
const classList = ref<any[]>([])
const showRemarks = ref(false) // 控制落实情况列的显示
// 搜索表单
const searchForm = reactive({
deptCode: '',
classCode: '',
source: '' // 来源选择:考勤记录、考勤
})
// 配置 useTable
const state: BasicTableProps = reactive<BasicTableProps>({
queryForm: searchForm,
pageList: fetchList,
props: {
item: 'tableData.records',
totalCount: 'tableData.total'
},
createdIsNeed: true
})
// table hook
const {
getDataList,
currentChangeHandle,
sizeChangeHandle,
tableStyle
} = useTable(state)
// 格式化考勤类型
const formatAttendanceType = (value: string | number) => {
if (value === '1' || value === 1) return '到岗'
if (value === '2' || value === 2) return '未到岗'
if (value === '3' || value === 3) return '请假'
return '-'
}
// 学院变化时更新班级列表
const handleDeptChange = () => {
searchForm.classCode = ''
getClassListData()
}
// 来源选择变化
const handleSourceChange = async (value: string) => {
if (value === 'history') {
// 选择考勤记录时,显示落实情况列
showRemarks.value = true
// 查询考勤记录(不强制要求学院和班级)
try {
state.loading = true
const params: any = {}
if (searchForm.deptCode) {
params.deptCode = searchForm.deptCode
}
if (searchForm.classCode) {
params.classCode = searchForm.classCode
}
const res = await queryHistoryList(params)
if (res.data && Array.isArray(res.data)) {
state.dataList = res.data
if (state.pagination) {
state.pagination.total = res.data.length
}
} else {
state.dataList = []
if (state.pagination) {
state.pagination.total = 0
}
}
} catch (err: any) {
useMessage().error(err.msg || '查询考勤记录失败')
state.dataList = []
if (state.pagination) {
state.pagination.total = 0
}
} finally {
state.loading = false
}
} else if (value === 'attendance') {
// 选择考勤时,不显示落实情况列,使用普通列表接口
showRemarks.value = false
getDataList()
} else {
// 清空选择时,不显示落实情况列
showRemarks.value = false
getDataList()
}
}
// 查询
const handleSearch = () => {
if (searchForm.source === 'history') {
handleSourceChange('history')
} else {
showRemarks.value = false
getDataList()
}
}
// 重置
const handleReset = () => {
searchFormRef.value?.resetFields()
searchForm.deptCode = ''
searchForm.classCode = ''
searchForm.source = ''
showRemarks.value = false
getDataList()
}
// 查看详情
const handleView = (row: any) => {
useMessage().warning('查看详情功能待实现')
console.log('查看详情', row)
}
// 获取学院列表
const getDeptListData = async () => {
try {
const res = await getDeptList()
if (res.data) {
deptList.value = Array.isArray(res.data) ? res.data : []
}
} catch (err) {
console.error('获取学院列表失败', err)
deptList.value = []
}
}
// 获取班级列表
const getClassListData = async () => {
try {
const res = await getClassListByRole()
if (res.data) {
let list = Array.isArray(res.data) ? res.data : []
// 如果选择了学院,过滤班级列表
if (searchForm.deptCode) {
list = list.filter((item: any) => item.deptCode === searchForm.deptCode)
}
classList.value = list
}
} catch (err) {
console.error('获取班级列表失败', err)
classList.value = []
}
}
// 初始化
onMounted(() => {
getDeptListData()
getClassListData()
})
</script>