This commit is contained in:
吴红兵
2026-03-07 12:35:45 +08:00
parent 271710e870
commit b997b3ba48
423 changed files with 79612 additions and 91574 deletions

View File

@@ -1,91 +1,82 @@
<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>
<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";
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
})
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 { getDataList, tableStyle } = useTable(state);
// 查看详情
const handleView = (row: any) => {
// TODO: 实现查看详情功能
}
// TODO: 实现查看详情功能
};
// 初始化
onMounted(() => {
getDataList()
})
getDataList();
});
</script>

View File

@@ -1,336 +1,273 @@
<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-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="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="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="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.bedNo"
:value="item.bedNo">
<span :class="{ 'bed-option-occupied': item.haveStudent }">
{{ item.bedNo }}{{ item.haveStudent ? ' (有人)' : '' }}
</span>
</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.bedNo" :value="item.bedNo">
<span :class="{ 'bed-option-occupied': item.haveStudent }"> {{ item.bedNo }}{{ item.haveStudent ? ' (有人)' : '' }} </span>
</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>
<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'
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 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 dataFormRef = ref();
const visible = ref(false);
const loading = ref(false);
const classList = ref<any[]>([]);
const studentList = ref<any[]>([]);
const roomList = ref<any[]>([]);
// 床位列表:支持 haveStudent 标记true=有人false=无人)
const bedNoList = ref<Array<{ bedNo: string; haveStudent: boolean }>>([])
const bedNoList = ref<Array<{ bedNo: string; haveStudent: boolean }>>([]);
// 提交表单数据
const form = reactive({
classCode: '',
realName: '',
stuNo: '',
roomNo: '',
bedNo: '',
isLeader: '0'
})
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' }
]
}
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) {
studentList.value = []
form.stuNo = ''
form.realName = ''
}
}
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) {
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 student = studentList.value.find((item) => item.stuNo === stuNo);
if (student) {
form.realName = student.realName || '';
}
};
// 房间号变化时获取床位号列表(支持 haveStudenttrue=有人false=无人)
const handleRoomChange = async (roomNo: string) => {
if (!roomNo) {
bedNoList.value = []
form.bedNo = ''
return
}
if (!roomNo) {
bedNoList.value = [];
form.bedNo = '';
return;
}
const toBedItem = (item: any): { bedNo: string; haveStudent: boolean } => {
if (typeof item === 'number' || typeof item === 'string') {
return { bedNo: String(item), haveStudent: false }
}
return {
bedNo: String(item?.bedNo ?? item?.value ?? item ?? ''),
haveStudent: !!(item && item.haveStudent === true)
}
}
const toBedItem = (item: any): { bedNo: string; haveStudent: boolean } => {
if (typeof item === 'number' || typeof item === 'string') {
return { bedNo: String(item), haveStudent: false };
}
return {
bedNo: String(item?.bedNo ?? item?.value ?? item ?? ''),
haveStudent: !!(item && item.haveStudent === true),
};
};
try {
const res = await fearchRoomStuNum(roomNo)
if (res.data) {
if (Array.isArray(res.data)) {
bedNoList.value = res.data.map(toBedItem).filter((b) => b.bedNo)
} else if (res.data.bedNos && Array.isArray(res.data.bedNos)) {
bedNoList.value = res.data.bedNos.map((item: any) =>
toBedItem(typeof item === 'object' ? item : { bedNo: String(item), haveStudent: false })
)
} else {
bedNoList.value = []
}
} else {
bedNoList.value = []
}
form.bedNo = ''
} catch (err) {
bedNoList.value = []
form.bedNo = ''
}
}
try {
const res = await fearchRoomStuNum(roomNo);
if (res.data) {
if (Array.isArray(res.data)) {
bedNoList.value = res.data.map(toBedItem).filter((b) => b.bedNo);
} else if (res.data.bedNos && Array.isArray(res.data.bedNos)) {
bedNoList.value = res.data.bedNos.map((item: any) =>
toBedItem(typeof item === 'object' ? item : { bedNo: String(item), haveStudent: false })
);
} else {
bedNoList.value = [];
}
} else {
bedNoList.value = [];
}
form.bedNo = '';
} catch (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 = []
})
}
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
}
})
}
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) {
classList.value = []
}
}
try {
const res = await getClassListByRole();
if (res.data) {
classList.value = Array.isArray(res.data) ? res.data : [];
}
} catch (err) {
classList.value = [];
}
};
// 获取房间号列表
const getRoomListData = async () => {
try {
const res = await getRoomList()
if (res.data) {
roomList.value = Array.isArray(res.data) ? res.data : []
}
} catch (err) {
roomList.value = []
}
}
try {
const res = await getRoomList();
if (res.data) {
roomList.value = Array.isArray(res.data) ? res.data : [];
}
} catch (err) {
roomList.value = [];
}
};
// 初始化
onMounted(() => {
getClassListData()
getRoomListData()
})
getClassListData();
getRoomListData();
});
// 暴露方法给父组件
defineExpose({
openDialog
})
openDialog,
});
</script>
<style scoped lang="scss">
.bed-option-occupied {
color: var(--el-color-warning);
color: var(--el-color-warning);
}
</style>

File diff suppressed because it is too large Load Diff

View File

@@ -1,108 +1,102 @@
<template>
<el-dialog
title="打印宿舍卡"
v-model="visible"
:close-on-click-modal="false"
width="640px"
destroy-on-close
@closed="onClosed">
<div v-loading="loading">
<el-form :inline="true" class="mb16">
<el-form-item label="房间号">
<el-input v-model="roomNo" placeholder="请输入房间号" clearable style="width: 180px" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleFetch">查询并预览</el-button>
</el-form-item>
</el-form>
<div v-if="printData.length" ref="printAreaRef" class="print-area">
<div v-for="(room, idx) in printData" :key="idx" class="room-card mb16">
<div class="room-title">房间号{{ room.roomNo }}</div>
<table class="print-table">
<thead>
<tr>
<th>姓名</th>
<th>学号</th>
<th>床位</th>
<th>是否舍长</th>
<th>班级</th>
</tr>
</thead>
<tbody>
<tr v-for="(s, i) in (room.dormRoomStudentVOList || [])" :key="i">
<td>{{ s.realName }}</td>
<td>{{ s.stuNo }}</td>
<td>{{ s.bedNo }}</td>
<td>{{ s.isLeader === '1' ? '是' : '否' }}</td>
<td>{{ s.className || s.classNo }}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="visible = false"> </el-button>
<el-button type="primary" @click="handlePrint" :disabled="!printData.length"> </el-button>
</span>
</template>
</el-dialog>
<el-dialog title="打印宿舍卡" v-model="visible" :close-on-click-modal="false" width="640px" destroy-on-close @closed="onClosed">
<div v-loading="loading">
<el-form :inline="true" class="mb16">
<el-form-item label="房间号">
<el-input v-model="roomNo" placeholder="请输入房间号" clearable style="width: 180px" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleFetch">查询并预览</el-button>
</el-form-item>
</el-form>
<div v-if="printData.length" ref="printAreaRef" class="print-area">
<div v-for="(room, idx) in printData" :key="idx" class="room-card mb16">
<div class="room-title">房间号{{ room.roomNo }}</div>
<table class="print-table">
<thead>
<tr>
<th>姓名</th>
<th>学号</th>
<th>床位</th>
<th>是否舍长</th>
<th>班级</th>
</tr>
</thead>
<tbody>
<tr v-for="(s, i) in room.dormRoomStudentVOList || []" :key="i">
<td>{{ s.realName }}</td>
<td>{{ s.stuNo }}</td>
<td>{{ s.bedNo }}</td>
<td>{{ s.isLeader === '1' ? '是' : '否' }}</td>
<td>{{ s.className || s.classNo }}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="visible = false"> </el-button>
<el-button type="primary" @click="handlePrint" :disabled="!printData.length"> </el-button>
</span>
</template>
</el-dialog>
</template>
<script setup lang="ts" name="DormRoomStudentPrintCard">
import { ref } from 'vue'
import { useMessage } from '/@/hooks/message'
import { printDormRoomData } from '/@/api/stuwork/dormroomstudent'
import { ref } from 'vue';
import { useMessage } from '/@/hooks/message';
import { printDormRoomData } from '/@/api/stuwork/dormroomstudent';
const visible = ref(false)
const loading = ref(false)
const roomNo = ref('')
const printData = ref<any[]>([])
const printAreaRef = ref<HTMLElement>()
const visible = ref(false);
const loading = ref(false);
const roomNo = ref('');
const printData = ref<any[]>([]);
const printAreaRef = ref<HTMLElement>();
const openDialog = (initialRoomNo?: string) => {
visible.value = true
roomNo.value = initialRoomNo || ''
printData.value = []
}
visible.value = true;
roomNo.value = initialRoomNo || '';
printData.value = [];
};
const onClosed = () => {
printData.value = []
}
printData.value = [];
};
const handleFetch = async () => {
const no = (roomNo.value || '').trim()
if (!no) {
useMessage().warning('请输入房间号')
return
}
loading.value = true
try {
const res = await printDormRoomData(no)
const data = res?.data ?? res
printData.value = Array.isArray(data) ? data : (data ? [data] : [])
if (!printData.value.length) {
useMessage().info('该房间暂无数据')
}
} catch (err: any) {
useMessage().error(err?.msg || '查询失败')
printData.value = []
} finally {
loading.value = false
}
}
const no = (roomNo.value || '').trim();
if (!no) {
useMessage().warning('请输入房间号');
return;
}
loading.value = true;
try {
const res = await printDormRoomData(no);
const data = res?.data ?? res;
printData.value = Array.isArray(data) ? data : data ? [data] : [];
if (!printData.value.length) {
useMessage().info('该房间暂无数据');
}
} catch (err: any) {
useMessage().error(err?.msg || '查询失败');
printData.value = [];
} finally {
loading.value = false;
}
};
const handlePrint = () => {
if (!printData.value.length) return
const win = window.open('', '_blank')
if (!win) {
useMessage().error('无法打开打印窗口')
return
}
const el = printAreaRef.value
if (el) {
win.document.write(`
if (!printData.value.length) return;
const win = window.open('', '_blank');
if (!win) {
useMessage().error('无法打开打印窗口');
return;
}
const el = printAreaRef.value;
if (el) {
win.document.write(`
<!DOCTYPE html><html><head><meta charset="utf-8"><title>宿舍卡</title>
<style>
table { border-collapse: collapse; width: 100%; }
@@ -110,43 +104,43 @@ const handlePrint = () => {
.room-title { font-weight: bold; margin-bottom: 8px; }
</style>
</head><body>${el.innerHTML}</body></html>
`)
win.document.close()
win.focus()
setTimeout(() => {
win.print()
win.close()
}, 300)
}
}
`);
win.document.close();
win.focus();
setTimeout(() => {
win.print();
win.close();
}, 300);
}
};
defineExpose({ openDialog })
defineExpose({ openDialog });
</script>
<style scoped lang="scss">
.print-area {
max-height: 60vh;
overflow: auto;
max-height: 60vh;
overflow: auto;
}
.room-card {
padding: 12px;
border: 1px solid var(--el-border-color);
border-radius: 4px;
padding: 12px;
border: 1px solid var(--el-border-color);
border-radius: 4px;
}
.room-title {
margin-bottom: 8px;
margin-bottom: 8px;
}
.print-table {
width: 100%;
border-collapse: collapse;
th,
td {
border: 1px solid var(--el-border-color);
padding: 6px 8px;
text-align: center;
}
width: 100%;
border-collapse: collapse;
th,
td {
border: 1px solid var(--el-border-color);
padding: 6px 8px;
text-align: center;
}
}
.mb16 {
margin-bottom: 16px;
margin-bottom: 16px;
}
</style>

View File

@@ -1,136 +1,115 @@
<template>
<el-dialog
title="宿舍互换"
v-model="visible"
:close-on-click-modal="false"
draggable
width="520px">
<el-form
ref="formRef"
:model="form"
:rules="rules"
label-width="100px"
:validate-on-rule-change="false"
v-loading="loading">
<el-form-item label="学生一" prop="sourceSutNo">
<el-select
v-model="form.sourceSutNo"
placeholder="请选择学生(学号)"
clearable
filterable
style="width: 100%"
@change="onSourceChange">
<el-option
v-for="item in studentOptions"
:key="item.stuNo"
:label="`${item.realName}${item.stuNo}${item.roomNo || ''}`"
:value="item.stuNo" />
</el-select>
</el-form-item>
<el-form-item label="学生二" prop="targetStuNO">
<el-select
v-model="form.targetStuNO"
placeholder="请选择学生(学号)"
clearable
filterable
style="width: 100%"
@change="onTargetChange">
<el-option
v-for="item in studentOptions"
:key="item.stuNo"
:label="`${item.realName}${item.stuNo}${item.roomNo || ''}`"
:value="item.stuNo" />
</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" :loading="submitting"> </el-button>
</span>
</template>
</el-dialog>
<el-dialog title="宿舍互换" v-model="visible" :close-on-click-modal="false" draggable width="520px">
<el-form ref="formRef" :model="form" :rules="rules" label-width="100px" :validate-on-rule-change="false" v-loading="loading">
<el-form-item label="学生一" prop="sourceSutNo">
<el-select v-model="form.sourceSutNo" placeholder="请选择学生(学号)" clearable filterable style="width: 100%" @change="onSourceChange">
<el-option
v-for="item in studentOptions"
:key="item.stuNo"
:label="`${item.realName}${item.stuNo}${item.roomNo || ''}`"
:value="item.stuNo"
/>
</el-select>
</el-form-item>
<el-form-item label="学生二" prop="targetStuNO">
<el-select v-model="form.targetStuNO" placeholder="请选择学生(学号)" clearable filterable style="width: 100%" @change="onTargetChange">
<el-option
v-for="item in studentOptions"
:key="item.stuNo"
:label="`${item.realName}${item.stuNo}${item.roomNo || ''}`"
:value="item.stuNo"
/>
</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" :loading="submitting"> </el-button>
</span>
</template>
</el-dialog>
</template>
<script setup lang="ts" name="DormRoomStudentSwapDialog">
import { ref, reactive } from 'vue'
import { useMessage } from '/@/hooks/message'
import { exchangeRoom } from '/@/api/stuwork/dormroomstudent'
import { fetchList } from '/@/api/stuwork/dormroomstudent'
import { ref, reactive } from 'vue';
import { useMessage } from '/@/hooks/message';
import { exchangeRoom } from '/@/api/stuwork/dormroomstudent';
import { fetchList } from '/@/api/stuwork/dormroomstudent';
const emit = defineEmits(['refresh'])
const emit = defineEmits(['refresh']);
const formRef = ref()
const visible = ref(false)
const loading = ref(false)
const submitting = ref(false)
const studentOptions = ref<any[]>([])
const formRef = ref();
const visible = ref(false);
const loading = ref(false);
const submitting = ref(false);
const studentOptions = ref<any[]>([]);
const form = reactive({
sourceSutNo: '',
targetStuNO: ''
})
sourceSutNo: '',
targetStuNO: '',
});
const rules = {
sourceSutNo: [{ required: true, message: '请选择学生一', trigger: 'change' }],
targetStuNO: [{ required: true, message: '请选择学生二', trigger: 'change' }]
}
sourceSutNo: [{ required: true, message: '请选择学生一', trigger: 'change' }],
targetStuNO: [{ required: true, message: '请选择学生二', trigger: 'change' }],
};
const onSourceChange = () => {
if (form.sourceSutNo && form.targetStuNO && form.sourceSutNo === form.targetStuNO) {
form.targetStuNO = ''
}
}
if (form.sourceSutNo && form.targetStuNO && form.sourceSutNo === form.targetStuNO) {
form.targetStuNO = '';
}
};
const onTargetChange = () => {
if (form.sourceSutNo && form.targetStuNO && form.sourceSutNo === form.targetStuNO) {
form.sourceSutNo = ''
}
}
if (form.sourceSutNo && form.targetStuNO && form.sourceSutNo === form.targetStuNO) {
form.sourceSutNo = '';
}
};
const openDialog = async (queryParams?: any) => {
visible.value = true
form.sourceSutNo = ''
form.targetStuNO = ''
loading.value = true
try {
const res = await fetchList({
current: 1,
size: 500,
...queryParams
})
const list = res?.data?.records ?? res?.records ?? []
studentOptions.value = Array.isArray(list) ? list : []
} catch (err) {
studentOptions.value = []
} finally {
loading.value = false
}
}
visible.value = true;
form.sourceSutNo = '';
form.targetStuNO = '';
loading.value = true;
try {
const res = await fetchList({
current: 1,
size: 500,
...queryParams,
});
const list = res?.data?.records ?? res?.records ?? [];
studentOptions.value = Array.isArray(list) ? list : [];
} catch (err) {
studentOptions.value = [];
} finally {
loading.value = false;
}
};
const onSubmit = async () => {
if (!formRef.value) return
await formRef.value.validate(async (valid: boolean) => {
if (!valid) return
if (form.sourceSutNo === form.targetStuNO) {
useMessage().warning('请选择两名不同学生')
return
}
submitting.value = true
try {
await exchangeRoom({
sourceSutNo: form.sourceSutNo,
targetStuNO: form.targetStuNO
})
useMessage().success('互换成功')
visible.value = false
emit('refresh')
} catch (err: any) {
console.error(err)
} finally {
submitting.value = false
}
})
}
if (!formRef.value) return;
await formRef.value.validate(async (valid: boolean) => {
if (!valid) return;
if (form.sourceSutNo === form.targetStuNO) {
useMessage().warning('请选择两名不同学生');
return;
}
submitting.value = true;
try {
await exchangeRoom({
sourceSutNo: form.sourceSutNo,
targetStuNO: form.targetStuNO,
});
useMessage().success('互换成功');
visible.value = false;
emit('refresh');
} catch (err: any) {
console.error(err);
} finally {
submitting.value = false;
}
});
};
defineExpose({ openDialog })
defineExpose({ openDialog });
</script>

View File

@@ -1,241 +1,197 @@
<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-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.bedNo"
:value="item.bedNo">
<span :class="{ 'bed-option-occupied': item.haveStudent }">
{{ item.bedNo }}{{ item.haveStudent ? ' (有人)' : '' }}
</span>
</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.bedNo" :value="item.bedNo">
<span :class="{ 'bed-option-occupied': item.haveStudent }"> {{ item.bedNo }}{{ item.haveStudent ? ' (有人)' : '' }} </span>
</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="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>
<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'
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 emit = defineEmits(['refresh']);
// 定义变量内容
const dataFormRef = ref()
const visible = ref(false)
const loading = ref(false)
const roomList = ref<any[]>([])
const dataFormRef = ref();
const visible = ref(false);
const loading = ref(false);
const roomList = ref<any[]>([]);
// 床位列表:支持 haveStudent 标记true=有人false=无人)
const bedNoList = ref<Array<{ bedNo: string; haveStudent: boolean }>>([])
const bedNoList = ref<Array<{ bedNo: string; haveStudent: boolean }>>([]);
// 提交表单数据
const form = reactive({
id: '',
roomNo: '',
bedNo: '',
stuNo: '',
isLeader: '0'
})
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' }
]
}
roomNo: [{ required: true, message: '请选择房间号', trigger: 'change' }],
bedNo: [{ required: true, message: '请选择床位号', trigger: 'change' }],
isLeader: [{ required: true, message: '请选择是否舍长', trigger: 'change' }],
};
// 房间号变化时获取床位号列表(支持 haveStudenttrue=有人false=无人)
const handleRoomChange = async (roomNo: string) => {
if (!roomNo) {
bedNoList.value = []
form.bedNo = ''
return
}
if (!roomNo) {
bedNoList.value = [];
form.bedNo = '';
return;
}
const toBedItem = (item: any): { bedNo: string; haveStudent: boolean } => {
if (typeof item === 'number' || typeof item === 'string') {
return { bedNo: String(item), haveStudent: false }
}
return {
bedNo: String(item?.bedNo ?? item?.value ?? item ?? ''),
haveStudent: !!(item && item.haveStudent === true)
}
}
const toBedItem = (item: any): { bedNo: string; haveStudent: boolean } => {
if (typeof item === 'number' || typeof item === 'string') {
return { bedNo: String(item), haveStudent: false };
}
return {
bedNo: String(item?.bedNo ?? item?.value ?? item ?? ''),
haveStudent: !!(item && item.haveStudent === true),
};
};
try {
const res = await fearchRoomStuNum(roomNo)
if (res.data) {
if (Array.isArray(res.data)) {
bedNoList.value = res.data.map(toBedItem).filter((b) => b.bedNo)
} else if (res.data.bedNos && Array.isArray(res.data.bedNos)) {
bedNoList.value = res.data.bedNos.map((item: any) =>
toBedItem(typeof item === 'object' ? item : { bedNo: String(item), haveStudent: false })
)
} else {
bedNoList.value = []
}
} else {
bedNoList.value = []
}
form.bedNo = ''
} catch (err) {
bedNoList.value = []
form.bedNo = ''
}
}
try {
const res = await fearchRoomStuNum(roomNo);
if (res.data) {
if (Array.isArray(res.data)) {
bedNoList.value = res.data.map(toBedItem).filter((b) => b.bedNo);
} else if (res.data.bedNos && Array.isArray(res.data.bedNos)) {
bedNoList.value = res.data.bedNos.map((item: any) =>
toBedItem(typeof item === 'object' ? item : { bedNo: String(item), haveStudent: false })
);
} else {
bedNoList.value = [];
}
} else {
bedNoList.value = [];
}
form.bedNo = '';
} catch (err) {
bedNoList.value = [];
form.bedNo = '';
}
};
// 打开弹窗
const openDialog = async (row: any) => {
visible.value = true
// 重置表单数据
await 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 = []
visible.value = true;
// 如果有房间号,先拉取床位列表再回填床位号(避免 handleRoomChange 清空 bedNo
if (form.roomNo) {
await handleRoomChange(form.roomNo)
form.bedNo = row.bedNo || ''
}
}
// 重置表单数据
await 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 = [];
// 如果有房间号,先拉取床位列表再回填床位号(避免 handleRoomChange 清空 bedNo
if (form.roomNo) {
await handleRoomChange(form.roomNo);
form.bedNo = row.bedNo || '';
}
};
// 提交表单
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) {
// 统一交给全局拦截器处理错误提示,避免在这里重复弹出一次
console.error('转宿失败', err)
} finally {
loading.value = false
}
})
}
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) {
// 统一交给全局拦截器处理错误提示,避免在这里重复弹出一次
console.error('转宿失败', err);
} 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) {
roomList.value = []
}
}
try {
const res = await getRoomList();
if (res.data) {
roomList.value = Array.isArray(res.data) ? res.data : [];
}
} catch (err) {
roomList.value = [];
}
};
// 初始化
onMounted(() => {
getRoomListData()
})
getRoomListData();
});
// 暴露方法给父组件
defineExpose({
openDialog
})
openDialog,
});
</script>
<style scoped lang="scss">
.bed-option-occupied {
color: var(--el-color-warning);
color: var(--el-color-warning);
}
</style>