wuzhen 更新假期住宿申请
This commit is contained in:
1 parent
1d67506451
commit
3518ea697e
35 files changed
+2173
-98
No files matched your search
@@ -84,6 +84,16 @@ export interface DormHygieneCheckVO {
|
||||
*/
|
||||
campusName: string;
|
||||
|
||||
/**
|
||||
* 归属班级ID(宿舍扣分归属)
|
||||
*/
|
||||
classId?: string | number;
|
||||
|
||||
/**
|
||||
* 归属班级名称
|
||||
*/
|
||||
className?: string;
|
||||
|
||||
/**
|
||||
* 宿舍总分(0-100分)
|
||||
*/
|
||||
@@ -227,6 +237,16 @@ export interface DormHygieneCheckForm extends BaseEntity {
|
||||
*/
|
||||
campusName?: string;
|
||||
|
||||
/**
|
||||
* 归属班级ID(宿舍扣分归属)
|
||||
*/
|
||||
classId?: string | number;
|
||||
|
||||
/**
|
||||
* 归属班级名称
|
||||
*/
|
||||
className?: string;
|
||||
|
||||
/**
|
||||
* 宿舍总分(0-100分)
|
||||
*/
|
||||
|
||||
@@ -142,3 +142,57 @@ export const getBedOptions = (roomId?: string | number): AxiosPromise<any[]> =>
|
||||
params: roomId ? { roomId } : undefined
|
||||
});
|
||||
};
|
||||
|
||||
/** 学生提交留宿申请 */
|
||||
export const applyHolidayStay = (data: HolidayStayForm) => {
|
||||
return request({
|
||||
url: '/holidayStay/holidayStay/apply',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
};
|
||||
|
||||
/** 当前学生申请列表 */
|
||||
export const listMyHolidayStay = (query?: HolidayStayQuery): AxiosPromise<HolidayStayVO[]> => {
|
||||
return request({
|
||||
url: '/holidayStay/holidayStay/myList',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
};
|
||||
|
||||
/** 班主任本班申请列表 */
|
||||
export const listTeacherHolidayStay = (query?: HolidayStayQuery): AxiosPromise<HolidayStayVO[]> => {
|
||||
return request({
|
||||
url: '/holidayStay/holidayStay/teacherList',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
};
|
||||
|
||||
/** 班主任审批通过 */
|
||||
export const approveHolidayStay = (data: { id: string | number; auditOpinion?: string }) => {
|
||||
return request({
|
||||
url: '/holidayStay/holidayStay/approve',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
};
|
||||
|
||||
/** 班主任驳回 */
|
||||
export const rejectHolidayStay = (data: { id: string | number; auditOpinion?: string }) => {
|
||||
return request({
|
||||
url: '/holidayStay/holidayStay/reject',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
};
|
||||
|
||||
/** 班主任撤销已通过申请 */
|
||||
export const revokeHolidayStay = (data: { id: string | number; revokeReason: string }) => {
|
||||
return request({
|
||||
url: '/holidayStay/holidayStay/revoke',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
};
|
||||
@@ -173,6 +173,56 @@ export interface HolidayStayVO {
|
||||
* 备注信息
|
||||
*/
|
||||
remark: string;
|
||||
|
||||
/**
|
||||
* 申请状态(0草稿 1待审 2已通过 3已驳回 4已撤销)
|
||||
*/
|
||||
applyStatus?: string;
|
||||
|
||||
/**
|
||||
* 提交申请时间
|
||||
*/
|
||||
applyTime?: string;
|
||||
|
||||
/**
|
||||
* 审批人用户ID
|
||||
*/
|
||||
auditorId?: string | number;
|
||||
|
||||
/**
|
||||
* 审批人姓名
|
||||
*/
|
||||
auditorName?: string;
|
||||
|
||||
/**
|
||||
* 审批时间
|
||||
*/
|
||||
auditTime?: string;
|
||||
|
||||
/**
|
||||
* 审批意见
|
||||
*/
|
||||
auditOpinion?: string;
|
||||
|
||||
/**
|
||||
* 撤销人用户ID
|
||||
*/
|
||||
revokeBy?: string | number;
|
||||
|
||||
/**
|
||||
* 撤销人姓名
|
||||
*/
|
||||
revokeByName?: string;
|
||||
|
||||
/**
|
||||
* 撤销时间
|
||||
*/
|
||||
revokeTime?: string;
|
||||
|
||||
/**
|
||||
* 撤销原因
|
||||
*/
|
||||
revokeReason?: string;
|
||||
}
|
||||
|
||||
export interface HolidayStayForm extends BaseEntity {
|
||||
@@ -350,6 +400,11 @@ export interface HolidayStayForm extends BaseEntity {
|
||||
* 备注信息
|
||||
*/
|
||||
remark?: string;
|
||||
|
||||
/**
|
||||
* 申请状态(0草稿 1待审 2已通过 3已驳回 4已撤销)
|
||||
*/
|
||||
applyStatus?: string;
|
||||
}
|
||||
|
||||
export interface HolidayStayQuery extends PageQuery {
|
||||
@@ -523,6 +578,11 @@ export interface HolidayStayQuery extends PageQuery {
|
||||
*/
|
||||
status?: string;
|
||||
|
||||
/**
|
||||
* 申请状态(0草稿 1待审 2已通过 3已驳回 4已撤销)
|
||||
*/
|
||||
applyStatus?: string;
|
||||
|
||||
/**
|
||||
* 日期范围参数
|
||||
*/
|
||||
|
||||
+92
@@ -57,6 +57,25 @@
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="6">
|
||||
<el-form-item label="归属班级" prop="classId">
|
||||
<el-select
|
||||
v-model="hygieneForm.classId"
|
||||
:placeholder="classOptions.length ? '请选择归属班级' : '当前寝室无在住学生班级'"
|
||||
style="width: 100%"
|
||||
filterable
|
||||
:disabled="!classOptions.length"
|
||||
@change="handleClassChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in classOptions"
|
||||
:key="String(item.classId)"
|
||||
:label="item.className"
|
||||
:value="item.classId"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="宿舍扣分" prop="totalScore">
|
||||
<el-input-number
|
||||
@@ -407,6 +426,8 @@ const hygieneForm = reactive({
|
||||
campusName: '',
|
||||
disciplineTypeId: null as number | null,
|
||||
disciplineTypeName: '',
|
||||
classId: undefined as string | number | undefined,
|
||||
className: '',
|
||||
totalScore: 0,
|
||||
hygieneGrade: 'A',
|
||||
checkRemarks: '',
|
||||
@@ -418,6 +439,7 @@ const hygieneRules = reactive({
|
||||
checkTitle: [{ required: true, message: '检查标题不能为空', trigger: 'blur' }],
|
||||
checkDate: [{ required: true, message: '检查日期不能为空', trigger: 'change' }],
|
||||
checkerName: [{ required: true, message: '检查员不能为空', trigger: 'blur' }],
|
||||
classId: [{ required: true, message: '请选择归属班级', trigger: 'change' }],
|
||||
totalScore: [
|
||||
{
|
||||
validator: (rule: any, value: any, callback: (error?: string | Error) => void) => {
|
||||
@@ -440,6 +462,8 @@ const hygieneRules = reactive({
|
||||
|
||||
// 学生评分数据
|
||||
const studentScores = ref<any[]>([]);
|
||||
/** 本寝室在住学生班级(去重) */
|
||||
const classOptions = ref<Array<{ classId: string | number; className: string }>>([]);
|
||||
|
||||
// 违纪记录数据(使用普通对象,避免 Map 变更无法触发 Vue 响应式更新)
|
||||
const violationRecords = ref<Record<string, any[]>>({});
|
||||
@@ -488,6 +512,41 @@ const handleDeductionTotalChange = (total: number) => {
|
||||
hygieneForm.totalScore = total;
|
||||
};
|
||||
|
||||
/** 从本房学生提取班级选项,并默认选中第一项(已选且仍有效则保留) */
|
||||
const syncClassOptionsFromStudents = (students: any[], preferClassId?: string | number | null) => {
|
||||
const map = new Map<string, { classId: string | number; className: string }>();
|
||||
students.forEach((student) => {
|
||||
if (student.classId == null || student.classId === '' || student.classId === 0 || student.classId === '0') {
|
||||
return;
|
||||
}
|
||||
const key = String(student.classId);
|
||||
if (!map.has(key)) {
|
||||
map.set(key, {
|
||||
classId: student.classId,
|
||||
className: student.className || key
|
||||
});
|
||||
}
|
||||
});
|
||||
classOptions.value = Array.from(map.values());
|
||||
|
||||
if (!classOptions.value.length) {
|
||||
hygieneForm.classId = undefined;
|
||||
hygieneForm.className = '';
|
||||
return;
|
||||
}
|
||||
|
||||
const preferred = preferClassId ?? hygieneForm.classId;
|
||||
const matched = preferred != null ? classOptions.value.find((item) => String(item.classId) === String(preferred)) : undefined;
|
||||
const selected = matched || classOptions.value[0];
|
||||
hygieneForm.classId = selected.classId;
|
||||
hygieneForm.className = selected.className;
|
||||
};
|
||||
|
||||
const handleClassChange = (classId: string | number) => {
|
||||
const matched = classOptions.value.find((item) => String(item.classId) === String(classId));
|
||||
hygieneForm.className = matched?.className || '';
|
||||
};
|
||||
|
||||
// 上传配置
|
||||
const uploadAction = computed(() => {
|
||||
return (import.meta as any).env.VITE_APP_BASE_API + '/resource/oss/upload';
|
||||
@@ -623,14 +682,38 @@ const loadStudentScores = async () => {
|
||||
|
||||
studentScores.value = scores;
|
||||
console.log('学生评分数据加载成功:', scores.length, '条');
|
||||
|
||||
// 班级选项优先用评分中的班级;若缺失则从当前寝室在住学生补全
|
||||
let classSource = scores;
|
||||
const hasClass = scores.some(
|
||||
(s: any) => s.classId != null && s.classId !== '' && s.classId !== 0 && s.classId !== '0'
|
||||
);
|
||||
if (!hasClass && hygieneForm.roomId) {
|
||||
try {
|
||||
const roomRes = await request({
|
||||
url: '/dormBedStudent/dormBedStudent/getRoomStudents',
|
||||
method: 'get',
|
||||
params: { roomId: hygieneForm.roomId }
|
||||
});
|
||||
if (roomRes.code === 200 && roomRes.data) {
|
||||
classSource = roomRes.data;
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('加载寝室班级选项失败', error);
|
||||
}
|
||||
}
|
||||
syncClassOptionsFromStudents(classSource, hygieneForm.classId);
|
||||
|
||||
await loadStudentLeaves();
|
||||
} else {
|
||||
studentScores.value = [];
|
||||
syncClassOptionsFromStudents([], hygieneForm.classId);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载学生评分数据失败:', error);
|
||||
ElMessage.error('加载学生评分数据失败');
|
||||
studentScores.value = [];
|
||||
syncClassOptionsFromStudents([], hygieneForm.classId);
|
||||
} finally {
|
||||
studentsLoading.value = false;
|
||||
}
|
||||
@@ -1179,6 +1262,12 @@ const submitForm = async () => {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!hygieneForm.classId) {
|
||||
ElMessage.error('请选择归属班级(取自当前寝室在住学生)');
|
||||
return;
|
||||
}
|
||||
handleClassChange(hygieneForm.classId);
|
||||
|
||||
buttonLoading.value = true;
|
||||
|
||||
// 提交前同步个人扣分为违纪合计
|
||||
@@ -1194,8 +1283,11 @@ const submitForm = async () => {
|
||||
studentId: student.studentId,
|
||||
studentName: student.studentName,
|
||||
studentNo: student.studentNo,
|
||||
classId: student.classId,
|
||||
className: student.className,
|
||||
majorId: student.majorId,
|
||||
majorName: student.majorName,
|
||||
collegeId: student.collegeId,
|
||||
collegeName: student.collegeName,
|
||||
bedId: student.bedId,
|
||||
bedCode: student.bedCode,
|
||||
|
||||
+77
@@ -22,6 +22,25 @@
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="归属班级" prop="classId">
|
||||
<el-select
|
||||
v-model="hygieneForm.classId"
|
||||
:placeholder="classOptions.length ? '请选择归属班级' : '当前寝室无在住学生班级'"
|
||||
style="width: 100%"
|
||||
filterable
|
||||
:disabled="!classOptions.length"
|
||||
@change="handleClassChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in classOptions"
|
||||
:key="String(item.classId)"
|
||||
:label="item.className"
|
||||
:value="item.classId"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="宿舍扣分" prop="totalScore">
|
||||
<el-input-number
|
||||
@@ -336,6 +355,8 @@ const deductionItems = ref<HygieneDeductionItem[]>([]);
|
||||
const hygieneForm = reactive({
|
||||
disciplineTypeId: null as number | null,
|
||||
disciplineTypeName: '',
|
||||
classId: undefined as string | number | undefined,
|
||||
className: '',
|
||||
totalScore: 0,
|
||||
hygieneGrade: 'A',
|
||||
checkRemarks: '',
|
||||
@@ -344,6 +365,7 @@ const hygieneForm = reactive({
|
||||
|
||||
// 表单验证规则
|
||||
const hygieneRules = reactive({
|
||||
classId: [{ required: true, message: '请选择归属班级', trigger: 'change' }],
|
||||
totalScore: [
|
||||
{
|
||||
validator: (rule: any, value: any, callback: (error?: string | Error) => void) => {
|
||||
@@ -366,6 +388,8 @@ const hygieneRules = reactive({
|
||||
|
||||
// 学生相关数据
|
||||
const roomStudents = ref<any[]>([]);
|
||||
/** 本寝室在住学生班级(去重) */
|
||||
const classOptions = ref<Array<{ classId: string | number; className: string }>>([]);
|
||||
const violationRecords = ref<Map<string | number, any[]>>(new Map());
|
||||
const studentLeaves = ref<Map<string | number, any>>(new Map());
|
||||
|
||||
@@ -444,10 +468,48 @@ const handleDeductionTotalChange = (total: number) => {
|
||||
hygieneForm.totalScore = total;
|
||||
};
|
||||
|
||||
/** 从本房学生提取班级选项,并默认选中第一项(已选且仍有效则保留) */
|
||||
const syncClassOptionsFromStudents = (students: any[], preferClassId?: string | number | null) => {
|
||||
const map = new Map<string, { classId: string | number; className: string }>();
|
||||
students.forEach((student) => {
|
||||
const classId = student.classId;
|
||||
const hasClassId = classId != null && classId !== '' && classId !== 0 && classId !== '0';
|
||||
if (!hasClassId) {
|
||||
return;
|
||||
}
|
||||
const key = String(classId);
|
||||
if (!map.has(key)) {
|
||||
map.set(key, {
|
||||
classId,
|
||||
className: (student.className || '').toString().trim() || key
|
||||
});
|
||||
}
|
||||
});
|
||||
classOptions.value = Array.from(map.values());
|
||||
|
||||
if (!classOptions.value.length) {
|
||||
hygieneForm.classId = undefined;
|
||||
hygieneForm.className = '';
|
||||
return;
|
||||
}
|
||||
|
||||
const preferred = preferClassId ?? hygieneForm.classId;
|
||||
const matched = preferred != null ? classOptions.value.find((item) => String(item.classId) === String(preferred)) : undefined;
|
||||
const selected = matched || classOptions.value[0];
|
||||
hygieneForm.classId = selected.classId;
|
||||
hygieneForm.className = selected.className;
|
||||
};
|
||||
|
||||
const handleClassChange = (classId: string | number) => {
|
||||
const matched = classOptions.value.find((item) => String(item.classId) === String(classId));
|
||||
hygieneForm.className = matched?.className || '';
|
||||
};
|
||||
|
||||
// 加载房间学生信息
|
||||
const loadRoomStudents = async () => {
|
||||
if (!props.roomInfo?.id) {
|
||||
roomStudents.value = [];
|
||||
syncClassOptionsFromStudents([]);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -467,16 +529,19 @@ const loadRoomStudents = async () => {
|
||||
attendance_status: '1',
|
||||
student_grade: 'A'
|
||||
}));
|
||||
syncClassOptionsFromStudents(roomStudents.value);
|
||||
|
||||
// 加载学生请假信息
|
||||
await loadStudentLeaves();
|
||||
} else {
|
||||
roomStudents.value = [];
|
||||
syncClassOptionsFromStudents([]);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载房间学生信息失败:', error);
|
||||
ElMessage.error('加载学生信息失败');
|
||||
roomStudents.value = [];
|
||||
syncClassOptionsFromStudents([]);
|
||||
} finally {
|
||||
studentsLoading.value = false;
|
||||
}
|
||||
@@ -878,6 +943,12 @@ const submitForm = async () => {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!hygieneForm.classId) {
|
||||
ElMessage.error('请选择归属班级(取自当前寝室在住学生)');
|
||||
return;
|
||||
}
|
||||
handleClassChange(hygieneForm.classId);
|
||||
|
||||
buttonLoading.value = true;
|
||||
|
||||
syncStudentScoreFromViolations();
|
||||
@@ -891,8 +962,11 @@ const submitForm = async () => {
|
||||
studentId: student.studentId,
|
||||
studentName: student.studentName,
|
||||
studentNo: student.studentNo,
|
||||
classId: student.classId,
|
||||
className: student.className,
|
||||
majorId: student.majorId,
|
||||
majorName: student.majorName,
|
||||
collegeId: student.collegeId,
|
||||
collegeName: student.collegeName,
|
||||
bedId: student.bedId,
|
||||
bedCode: student.bedCode,
|
||||
@@ -999,12 +1073,15 @@ const resetForm = () => {
|
||||
Object.assign(hygieneForm, {
|
||||
disciplineTypeId: null,
|
||||
disciplineTypeName: '',
|
||||
classId: undefined,
|
||||
className: '',
|
||||
totalScore: 0,
|
||||
hygieneGrade: 'A',
|
||||
checkRemarks: '',
|
||||
attachmentIds: ''
|
||||
});
|
||||
deductionItems.value = [];
|
||||
classOptions.value = [];
|
||||
|
||||
roomStudents.value = [];
|
||||
violationRecords.value.clear();
|
||||
|
||||
@@ -105,6 +105,7 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="房间名称" align="center" prop="roomName" />
|
||||
<el-table-column label="归属班级" align="center" prop="className" min-width="140" show-overflow-tooltip />
|
||||
<el-table-column label="宿舍楼名称" align="center" prop="buildingName" />
|
||||
<el-table-column label="校区名称" align="center" prop="campusName" />
|
||||
<el-table-column label="扣分项" align="center" prop="disciplineTypeName" min-width="160" show-overflow-tooltip />
|
||||
|
||||
@@ -16,6 +16,11 @@
|
||||
<el-form-item label="学生姓名" prop="studentName">
|
||||
<el-input v-model="queryParams.studentName" placeholder="请输入学生姓名" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="申请状态" prop="applyStatus">
|
||||
<el-select v-model="queryParams.applyStatus" placeholder="请选择申请状态" clearable>
|
||||
<el-option v-for="dict in xg_holiday_stay_apply_status" :key="dict.value" :label="dict.label" :value="dict.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable>
|
||||
<el-option v-for="dict in xg_holiday_stay_status" :key="dict.value" :label="dict.label" :value="dict.value" />
|
||||
@@ -223,6 +228,11 @@
|
||||
<el-table-column label="扩展字段2" align="center" prop="extField2" v-if="false" />
|
||||
<el-table-column label="扩展字段3" align="center" prop="extField3" v-if="false" />
|
||||
<el-table-column label="备注信息" align="center" prop="remark" />
|
||||
<el-table-column label="申请状态" align="center" prop="applyStatus" width="100">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="xg_holiday_stay_apply_status" :value="scope.row.applyStatus" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" align="center" prop="status" width="90">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getStatusType(scope.row)" size="small">
|
||||
@@ -801,8 +811,8 @@ import {
|
||||
} from '@/api/xg/xgss/holidayStay/holidayStay/types-sg';
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const { xg_holiday_type, xg_stay_reason_type, sys_yesno, xg_holiday_stay_status } = toRefs<any>(
|
||||
proxy?.useDict('xg_holiday_type', 'xg_stay_reason_type', 'sys_yesno', 'xg_holiday_stay_status')
|
||||
const { xg_holiday_type, xg_stay_reason_type, sys_yesno, xg_holiday_stay_status, xg_holiday_stay_apply_status } = toRefs<any>(
|
||||
proxy?.useDict('xg_holiday_type', 'xg_stay_reason_type', 'sys_yesno', 'xg_holiday_stay_status', 'xg_holiday_stay_apply_status')
|
||||
);
|
||||
|
||||
// 用户store - 用于获取当前登录用户信息,实现权限控制
|
||||
@@ -920,6 +930,7 @@ const data = reactive<PageData<HolidayStayForm, HolidayStayQuery>>({
|
||||
extField1: undefined,
|
||||
extField2: undefined,
|
||||
extField3: undefined,
|
||||
applyStatus: undefined,
|
||||
status: 'valid', // 默认展示有效状态的数据
|
||||
params: {}
|
||||
},
|
||||
@@ -1210,15 +1221,22 @@ const handleDelete = async (row?: HolidayStayVO) => {
|
||||
await getList();
|
||||
};
|
||||
|
||||
/** 导出按钮操作 */
|
||||
/** 导出按钮操作:未选申请状态时默认导出已通过名单 */
|
||||
const handleExport = () => {
|
||||
proxy?.download(
|
||||
'holidayStay/holidayStay/export',
|
||||
{
|
||||
...queryParams.value
|
||||
},
|
||||
`holidayStay_${new Date().getTime()}.xlsx`
|
||||
);
|
||||
const exportParams: any = { ...queryParams.value };
|
||||
if (!exportParams.applyStatus) {
|
||||
exportParams.applyStatus = '2';
|
||||
}
|
||||
if (exportParams.status === 'valid' || exportParams.status === 'invalid') {
|
||||
const currentTime = new Date().toISOString().slice(0, 19).replace('T', ' ');
|
||||
exportParams.params = {
|
||||
...exportParams.params,
|
||||
statusType: exportParams.status,
|
||||
currentTime
|
||||
};
|
||||
delete exportParams.status;
|
||||
}
|
||||
proxy?.download('holidayStay/holidayStay/export', exportParams, `holidayStay_${new Date().getTime()}.xlsx`);
|
||||
};
|
||||
|
||||
/** 远程搜索学生 */
|
||||
|
||||
@@ -13,8 +13,13 @@
|
||||
<el-form-item label="学生姓名" prop="studentName">
|
||||
<el-input v-model="queryParams.studentName" placeholder="请输入学生姓名" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable>
|
||||
<el-form-item label="申请状态" prop="applyStatus">
|
||||
<el-select v-model="queryParams.applyStatus" placeholder="请选择申请状态" clearable>
|
||||
<el-option v-for="dict in xg_holiday_stay_apply_status" :key="dict.value" :label="dict.label" :value="dict.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="有效期" prop="status">
|
||||
<el-select v-model="queryParams.status" placeholder="请选择有效期" clearable>
|
||||
<el-option label="有效" value="valid" />
|
||||
<el-option label="失效" value="invalid" />
|
||||
</el-select>
|
||||
@@ -157,9 +162,11 @@
|
||||
>删除</el-button
|
||||
>
|
||||
</el-col> -->
|
||||
<!-- <el-col :span="1.5">
|
||||
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['holidayStay:holidayStay:export']">导出</el-button>
|
||||
</el-col> -->
|
||||
<el-col :span="1.5">
|
||||
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['holidayStay:holidayStay:export']"
|
||||
>导出留宿名单</el-button
|
||||
>
|
||||
</el-col>
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
</template>
|
||||
@@ -221,15 +228,47 @@
|
||||
<el-table-column label="扩展字段2" align="center" prop="extField2" v-if="false" />
|
||||
<el-table-column label="扩展字段3" align="center" prop="extField3" v-if="false" />
|
||||
<el-table-column label="备注信息" align="center" prop="remark" />
|
||||
<el-table-column label="状态" align="center" prop="status" width="90">
|
||||
<el-table-column label="申请状态" align="center" prop="applyStatus" width="100">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="xg_holiday_stay_apply_status" :value="scope.row.applyStatus" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="有效期" align="center" prop="status" width="90">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getStatusType(scope.row)" size="small">
|
||||
{{ getStatusText(scope.row) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="100">
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="220">
|
||||
<template #default="scope">
|
||||
<el-tooltip content="通过" placement="top" v-if="scope.row.applyStatus === '1'">
|
||||
<el-button
|
||||
link
|
||||
type="success"
|
||||
@click="openAuditDialog(scope.row, 'approve')"
|
||||
v-hasPermi="['holidayStay:holidayStay:audit']"
|
||||
>通过</el-button
|
||||
>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="驳回" placement="top" v-if="scope.row.applyStatus === '1'">
|
||||
<el-button
|
||||
link
|
||||
type="warning"
|
||||
@click="openAuditDialog(scope.row, 'reject')"
|
||||
v-hasPermi="['holidayStay:holidayStay:audit']"
|
||||
>驳回</el-button
|
||||
>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="撤销" placement="top" v-if="scope.row.applyStatus === '2'">
|
||||
<el-button
|
||||
link
|
||||
type="danger"
|
||||
@click="openAuditDialog(scope.row, 'revoke')"
|
||||
v-hasPermi="['holidayStay:holidayStay:revoke']"
|
||||
>撤销</el-button
|
||||
>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="修改" placement="top">
|
||||
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['holidayStay:holidayStay:edit']"></el-button>
|
||||
</el-tooltip>
|
||||
@@ -553,6 +592,26 @@
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 审批 / 撤销 -->
|
||||
<el-dialog :title="auditDialog.title" v-model="auditDialog.visible" width="480px" append-to-body>
|
||||
<el-form label-width="100px">
|
||||
<el-form-item :label="auditDialog.mode === 'revoke' ? '撤销原因' : '审批意见'" :required="auditDialog.mode === 'revoke'">
|
||||
<el-input
|
||||
v-model="auditDialog.opinion"
|
||||
type="textarea"
|
||||
:rows="4"
|
||||
:placeholder="auditDialog.mode === 'revoke' ? '请输入撤销原因(必填)' : '请输入审批意见(选填)'"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button :loading="auditDialog.loading" type="primary" @click="submitAudit">确 定</el-button>
|
||||
<el-button @click="auditDialog.visible = false">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -565,9 +624,8 @@ import { UploadFilled, Document, Loading, Download, Delete } from '@element-plus
|
||||
import { getToken } from '@/utils/auth';
|
||||
import ImagePreview from '@/components/ImagePreview/index.vue';
|
||||
import request from '@/utils/request';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
import {
|
||||
listHolidayStay,
|
||||
listTeacherHolidayStay,
|
||||
getHolidayStay,
|
||||
delHolidayStay,
|
||||
addHolidayStay,
|
||||
@@ -577,7 +635,10 @@ import {
|
||||
getAreaOptions,
|
||||
getBuildingOptions,
|
||||
getRoomOptions,
|
||||
getBedOptions
|
||||
getBedOptions,
|
||||
approveHolidayStay,
|
||||
rejectHolidayStay,
|
||||
revokeHolidayStay
|
||||
} from '@/api/xg/xgss/holidayStay/holidayStay';
|
||||
import {
|
||||
HolidayStayVO,
|
||||
@@ -593,10 +654,9 @@ import {
|
||||
} from '@/api/xg/xgss/holidayStay/holidayStay/types';
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const { xg_holiday_type, xg_stay_reason_type, sys_yesno } = toRefs<any>(proxy?.useDict('xg_holiday_type', 'xg_stay_reason_type', 'sys_yesno'));
|
||||
|
||||
// 用户store - 用于获取当前登录用户信息,实现权限控制
|
||||
const userStore = useUserStore();
|
||||
const { xg_holiday_type, xg_stay_reason_type, sys_yesno, xg_holiday_stay_apply_status } = toRefs<any>(
|
||||
proxy?.useDict('xg_holiday_type', 'xg_stay_reason_type', 'sys_yesno', 'xg_holiday_stay_apply_status')
|
||||
);
|
||||
|
||||
const holidayStayList = ref<HolidayStayVO[]>([]);
|
||||
const buttonLoading = ref(false);
|
||||
@@ -701,6 +761,7 @@ const data = reactive<PageData<HolidayStayForm, HolidayStayQuery>>({
|
||||
extField1: undefined,
|
||||
extField2: undefined,
|
||||
extField3: undefined,
|
||||
applyStatus: '1', // 班主任默认看待审
|
||||
status: 'valid', // 默认显示有效状态的记录
|
||||
params: {}
|
||||
},
|
||||
@@ -734,34 +795,23 @@ const uploadData = computed(() => {
|
||||
};
|
||||
});
|
||||
|
||||
/** 查询假期留宿管理列表 */
|
||||
/** 查询假期留宿管理列表(班主任本班) */
|
||||
const getList = async () => {
|
||||
loading.value = true;
|
||||
|
||||
// 确保用户信息已加载
|
||||
if (!userStore.userId) {
|
||||
await userStore.getInfo();
|
||||
}
|
||||
|
||||
// 处理状态过滤逻辑
|
||||
const requestParams = { ...queryParams.value };
|
||||
|
||||
// 添加创建人过滤条件 - 班主任只能看到自己创建的数据
|
||||
(requestParams as any).createBy = userStore.userId;
|
||||
|
||||
// 根据状态设置时间过滤条件
|
||||
// 根据有效期设置时间过滤条件
|
||||
if (requestParams.status) {
|
||||
const currentTime = new Date().toISOString().slice(0, 19).replace('T', ' ');
|
||||
|
||||
if (requestParams.status === 'valid') {
|
||||
// 查询有效状态:结束时间大于等于当前时间
|
||||
requestParams.params = {
|
||||
...requestParams.params,
|
||||
statusType: 'valid',
|
||||
currentTime: currentTime
|
||||
};
|
||||
} else if (requestParams.status === 'invalid') {
|
||||
// 查询失效状态:结束时间小于当前时间
|
||||
requestParams.params = {
|
||||
...requestParams.params,
|
||||
statusType: 'invalid',
|
||||
@@ -769,11 +819,10 @@ const getList = async () => {
|
||||
};
|
||||
}
|
||||
|
||||
// 移除status参数,因为后端不需要这个字段
|
||||
delete requestParams.status;
|
||||
}
|
||||
|
||||
const res = await listHolidayStay(requestParams);
|
||||
const res = await listTeacherHolidayStay(requestParams);
|
||||
holidayStayList.value = res.rows;
|
||||
total.value = res.total;
|
||||
loading.value = false;
|
||||
@@ -911,15 +960,64 @@ const handleDelete = async (row?: HolidayStayVO) => {
|
||||
await getList();
|
||||
};
|
||||
|
||||
/** 导出按钮操作 */
|
||||
/** 导出按钮操作:默认导出已通过名单;若已选申请状态则按筛选导出 */
|
||||
const handleExport = () => {
|
||||
proxy?.download(
|
||||
'holidayStay/holidayStay/export',
|
||||
{
|
||||
...queryParams.value
|
||||
},
|
||||
`holidayStay_${new Date().getTime()}.xlsx`
|
||||
);
|
||||
const exportParams: any = { ...queryParams.value };
|
||||
if (!exportParams.applyStatus) {
|
||||
exportParams.applyStatus = '2';
|
||||
}
|
||||
if (exportParams.status) {
|
||||
const currentTime = new Date().toISOString().slice(0, 19).replace('T', ' ');
|
||||
exportParams.params = {
|
||||
...exportParams.params,
|
||||
statusType: exportParams.status,
|
||||
currentTime
|
||||
};
|
||||
delete exportParams.status;
|
||||
}
|
||||
proxy?.download('holidayStay/holidayStay/export', exportParams, `holidayStay_${new Date().getTime()}.xlsx`);
|
||||
};
|
||||
|
||||
const auditDialog = reactive({
|
||||
visible: false,
|
||||
title: '',
|
||||
mode: 'approve' as 'approve' | 'reject' | 'revoke',
|
||||
rowId: undefined as string | number | undefined,
|
||||
opinion: '',
|
||||
loading: false
|
||||
});
|
||||
|
||||
const openAuditDialog = (row: HolidayStayVO, mode: 'approve' | 'reject' | 'revoke') => {
|
||||
auditDialog.mode = mode;
|
||||
auditDialog.rowId = row.id;
|
||||
auditDialog.opinion = '';
|
||||
auditDialog.title = mode === 'approve' ? '审批通过' : mode === 'reject' ? '审批驳回' : '撤销留宿';
|
||||
auditDialog.visible = true;
|
||||
};
|
||||
|
||||
const submitAudit = async () => {
|
||||
if (!auditDialog.rowId) return;
|
||||
if (auditDialog.mode === 'revoke' && !auditDialog.opinion?.trim()) {
|
||||
proxy?.$modal.msgWarning('请填写撤销原因');
|
||||
return;
|
||||
}
|
||||
auditDialog.loading = true;
|
||||
try {
|
||||
if (auditDialog.mode === 'approve') {
|
||||
await approveHolidayStay({ id: auditDialog.rowId, auditOpinion: auditDialog.opinion });
|
||||
proxy?.$modal.msgSuccess('已通过');
|
||||
} else if (auditDialog.mode === 'reject') {
|
||||
await rejectHolidayStay({ id: auditDialog.rowId, auditOpinion: auditDialog.opinion });
|
||||
proxy?.$modal.msgSuccess('已驳回');
|
||||
} else {
|
||||
await revokeHolidayStay({ id: auditDialog.rowId, revokeReason: auditDialog.opinion });
|
||||
proxy?.$modal.msgSuccess('已撤销');
|
||||
}
|
||||
auditDialog.visible = false;
|
||||
await getList();
|
||||
} finally {
|
||||
auditDialog.loading = false;
|
||||
}
|
||||
};
|
||||
|
||||
/** 远程搜索学生 */
|
||||
|
||||
Reference in new issue
Block a user