feat(xg): 请假审批修复、点名今日汇总与寝室扣分条例脚本
同步请假/销假审批上下文与状态、宿舍点名今日汇总与假期留宿标识,并补充寝室检查扣分条例生产 SQL 与执行说明。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
3e6349112c
commit
7cea0566ac
48 files changed
+1966
-116
No files matched your search
@@ -38,6 +38,14 @@ export const flowHisTaskList = (businessId: string | number) => {
|
||||
});
|
||||
};
|
||||
|
||||
/** 办理页审批上下文:是否可审批、taskId、建议业务状态 */
|
||||
export const approvalContext = (businessId: string | number) => {
|
||||
return request({
|
||||
url: `/workflow/instance/approvalContext/${businessId}`,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 分页查询当前登录人单据
|
||||
* @param query
|
||||
|
||||
@@ -62,6 +62,8 @@ export interface DormRollCallVO {
|
||||
presentCount: number;
|
||||
/** 请假人数 */
|
||||
leaveCount: number;
|
||||
/** 假期留宿人数 */
|
||||
holidayStayCount: number;
|
||||
/** 不在人数 */
|
||||
absentCount: number;
|
||||
/** 空床位 */
|
||||
@@ -106,7 +108,7 @@ export interface DormRollCallDetailVO {
|
||||
collegeId?: string | number;
|
||||
/** 学院名称 */
|
||||
collegeName?: string;
|
||||
/** 点名状态(0在寝/1请假/2不在/3空床位) */
|
||||
/** 点名状态(0在寝/1请假/2不在/3空床位/4假期留宿) */
|
||||
rollCallStatus: string;
|
||||
/** 请假ID */
|
||||
leaveId?: string | number;
|
||||
@@ -116,6 +118,14 @@ export interface DormRollCallDetailVO {
|
||||
leaveStartTime?: string;
|
||||
/** 请假结束时间 */
|
||||
leaveEndTime?: string;
|
||||
/** 假期留宿ID */
|
||||
holidayStayId?: string | number;
|
||||
/** 假期类型 */
|
||||
holidayType?: string;
|
||||
/** 留宿开始日期 */
|
||||
holidayStartDate?: string;
|
||||
/** 留宿结束日期 */
|
||||
holidayEndDate?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -134,6 +144,8 @@ export interface RoomRollCallStatusVO {
|
||||
presentCount: number;
|
||||
/** 请假人数 */
|
||||
leaveCount: number;
|
||||
/** 假期留宿人数 */
|
||||
holidayStayCount: number;
|
||||
/** 不在人数 */
|
||||
absentCount: number;
|
||||
/** 空床位 */
|
||||
@@ -182,6 +194,8 @@ export interface DormRollCallForm extends BaseEntity {
|
||||
presentCount?: number;
|
||||
/** 请假人数 */
|
||||
leaveCount?: number;
|
||||
/** 假期留宿人数 */
|
||||
holidayStayCount?: number;
|
||||
/** 不在人数 */
|
||||
absentCount?: number;
|
||||
/** 空床位 */
|
||||
@@ -222,7 +236,7 @@ export interface DormRollCallDetailForm {
|
||||
collegeId?: string | number;
|
||||
/** 学院名称 */
|
||||
collegeName?: string;
|
||||
/** 点名状态(0在寝/1请假/2不在/3空床位) */
|
||||
/** 点名状态(0在寝/1请假/2不在/3空床位/4假期留宿) */
|
||||
rollCallStatus?: string;
|
||||
/** 请假ID */
|
||||
leaveId?: string | number;
|
||||
@@ -232,4 +246,12 @@ export interface DormRollCallDetailForm {
|
||||
leaveStartTime?: string;
|
||||
/** 请假结束时间 */
|
||||
leaveEndTime?: string;
|
||||
/** 假期留宿ID */
|
||||
holidayStayId?: string | number;
|
||||
/** 假期类型 */
|
||||
holidayType?: string;
|
||||
/** 留宿开始日期 */
|
||||
holidayStartDate?: string;
|
||||
/** 留宿结束日期 */
|
||||
holidayEndDate?: string;
|
||||
}
|
||||
@@ -19,7 +19,9 @@ const props = defineProps({
|
||||
status: propTypes.string.def(''),
|
||||
pageType: propTypes.string.def(''),
|
||||
buttonLoading: propTypes.bool.def(false),
|
||||
id: propTypes.string.def('') || propTypes.number.def()
|
||||
id: propTypes.string.def('') || propTypes.number.def(),
|
||||
/** 当前用户是否有可办审批待办(优先于 status===waiting) */
|
||||
canApprove: propTypes.bool.def(false)
|
||||
});
|
||||
const emits = defineEmits(['submitForm', 'approvalVerifyOpen', 'handleApprovalRecord']);
|
||||
//暂存,提交
|
||||
@@ -43,8 +45,11 @@ const submitButtonShow = computed(() => {
|
||||
);
|
||||
});
|
||||
|
||||
//校验审批按钮是否显示
|
||||
// 审批按钮:有待办可审,或传统 pageType=approval + waiting
|
||||
const approvalButtonShow = computed(() => {
|
||||
if (props.canApprove) {
|
||||
return true;
|
||||
}
|
||||
return props.pageType === 'approval' && props.status && props.status === 'waiting';
|
||||
});
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
<div class="summary-bar">
|
||||
<span>在寝: <b style="color: #67c23a">{{ rollCallData.presentCount }}</b></span>
|
||||
<span>请假: <b style="color: #e6a23c">{{ rollCallData.leaveCount }}</b></span>
|
||||
<span>假期留宿: <b style="color: #409eff">{{ rollCallData.holidayStayCount || 0 }}</b></span>
|
||||
<span>不在: <b style="color: #f56c6c">{{ rollCallData.absentCount }}</b></span>
|
||||
<span>空床位: <b style="color: #909399">{{ rollCallData.emptyCount }}</b></span>
|
||||
</div>
|
||||
@@ -21,17 +22,19 @@
|
||||
<el-table-column label="床位" prop="bedName" width="70" />
|
||||
<el-table-column label="姓名" prop="studentName" width="80" />
|
||||
<el-table-column label="学号" prop="studentNo" width="120" />
|
||||
<el-table-column label="状态" prop="rollCallStatus" width="80">
|
||||
<el-table-column label="状态" prop="rollCallStatus" width="90">
|
||||
<template #default="scope">
|
||||
<el-tag v-if="scope.row.rollCallStatus === '0'" type="success" size="small">在寝</el-tag>
|
||||
<el-tag v-else-if="scope.row.rollCallStatus === '1'" type="warning" size="small">请假</el-tag>
|
||||
<el-tag v-else-if="scope.row.rollCallStatus === '4'" type="primary" size="small">假期留宿</el-tag>
|
||||
<el-tag v-else-if="scope.row.rollCallStatus === '2'" type="danger" size="small">不在</el-tag>
|
||||
<el-tag v-else type="info" size="small">空床位</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="请假信息" min-width="120">
|
||||
<el-table-column label="备注信息" min-width="120">
|
||||
<template #default="scope">
|
||||
<span v-if="scope.row.leaveTypeName">{{ scope.row.leaveTypeName }}</span>
|
||||
<span v-else-if="scope.row.holidayType || scope.row.holidayStayId">假期留宿{{ scope.row.holidayType ? `(${scope.row.holidayType})` : '' }}</span>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
@@ -18,6 +18,11 @@
|
||||
<span style="color: #e6a23c">{{ scope.row.leaveCount }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="留宿" prop="holidayStayCount" width="60" align="center">
|
||||
<template #default="scope">
|
||||
<span style="color: #409eff">{{ scope.row.holidayStayCount || 0 }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="不在" prop="absentCount" width="60" align="center">
|
||||
<template #default="scope">
|
||||
<span style="color: #f56c6c">{{ scope.row.absentCount }}</span>
|
||||
|
||||
@@ -18,6 +18,9 @@
|
||||
<div v-if="bed.leaveId" class="leave-info">
|
||||
<el-tag type="warning" size="small">{{ bed.leaveTypeName || '请假' }}</el-tag>
|
||||
</div>
|
||||
<div v-else-if="bed.holidayStayId" class="leave-info">
|
||||
<el-tag type="primary" size="small">假期留宿</el-tag>
|
||||
</div>
|
||||
<div class="status-label">
|
||||
{{ getStatusLabel(bed.rollCallStatus) }}
|
||||
</div>
|
||||
@@ -28,6 +31,7 @@
|
||||
<div v-if="bedDetails.length > 0" class="summary-bar">
|
||||
<span>在寝: <b style="color: #67c23a">{{ summary.present }}</b></span>
|
||||
<span>请假: <b style="color: #e6a23c">{{ summary.leave }}</b></span>
|
||||
<span>假期留宿: <b style="color: #409eff">{{ summary.holidayStay }}</b></span>
|
||||
<span>不在: <b style="color: #f56c6c">{{ summary.absent }}</b></span>
|
||||
<span>空床位: <b style="color: #909399">{{ summary.empty }}</b></span>
|
||||
</div>
|
||||
@@ -73,6 +77,7 @@ interface BedItem extends DormRollCallDetailForm {
|
||||
bedId: string | number;
|
||||
originalStatus?: string;
|
||||
isLeaveAuto?: boolean;
|
||||
isHolidayStayAuto?: boolean;
|
||||
}
|
||||
|
||||
const loading = ref(false);
|
||||
@@ -80,21 +85,23 @@ const submitting = ref(false);
|
||||
const bedDetails = ref<BedItem[]>([]);
|
||||
|
||||
const summary = computed(() => {
|
||||
let present = 0, leave = 0, absent = 0, empty = 0;
|
||||
let present = 0, leave = 0, holidayStay = 0, absent = 0, empty = 0;
|
||||
bedDetails.value.forEach(bed => {
|
||||
const status = bed.rollCallStatus;
|
||||
if (status === '0') present++;
|
||||
else if (status === '1') leave++;
|
||||
else if (status === '4') holidayStay++;
|
||||
else if (status === '2') absent++;
|
||||
else if (status === '3') empty++;
|
||||
});
|
||||
return { present, leave, absent, empty };
|
||||
return { present, leave, holidayStay, absent, empty };
|
||||
});
|
||||
|
||||
const getBedCardClass = (bed: BedItem) => {
|
||||
const status = bed.rollCallStatus;
|
||||
if (status === '0') return 'bed-present';
|
||||
if (status === '1') return 'bed-leave';
|
||||
if (status === '4') return 'bed-holiday-stay';
|
||||
if (status === '2') return 'bed-absent';
|
||||
if (status === '3') return 'bed-empty';
|
||||
return '';
|
||||
@@ -103,6 +110,7 @@ const getBedCardClass = (bed: BedItem) => {
|
||||
const getStatusLabel = (status?: string) => {
|
||||
if (status === '0') return '在寝';
|
||||
if (status === '1') return '请假';
|
||||
if (status === '4') return '假期留宿';
|
||||
if (status === '2') return '不在';
|
||||
if (status === '3') return '空床位';
|
||||
return '';
|
||||
@@ -113,6 +121,10 @@ const handleBedClick = (bed: BedItem) => {
|
||||
ElMessage.info('该学生处于请假状态,不可修改');
|
||||
return;
|
||||
}
|
||||
if (bed.isHolidayStayAuto) {
|
||||
ElMessage.info('该学生处于假期留宿状态,不可修改');
|
||||
return;
|
||||
}
|
||||
if (!bed.studentId) {
|
||||
return;
|
||||
}
|
||||
@@ -201,6 +213,13 @@ const loadBedDetails = async () => {
|
||||
bed.leaveTypeName = item.leaveInfo.leaveTypeName;
|
||||
bed.leaveStartTime = item.leaveInfo.leaveStartTime;
|
||||
bed.leaveEndTime = item.leaveInfo.leaveEndTime;
|
||||
} else if (item.holidayStayInfo) {
|
||||
bed.rollCallStatus = '4';
|
||||
bed.isHolidayStayAuto = true;
|
||||
bed.holidayStayId = item.holidayStayInfo.holidayStayId;
|
||||
bed.holidayType = item.holidayStayInfo.holidayType;
|
||||
bed.holidayStartDate = item.holidayStayInfo.applyStartDate;
|
||||
bed.holidayEndDate = item.holidayStayInfo.applyEndDate;
|
||||
}
|
||||
|
||||
bed.originalStatus = bed.rollCallStatus;
|
||||
@@ -247,7 +266,11 @@ const handleSubmit = async () => {
|
||||
leaveId: bed.leaveId,
|
||||
leaveTypeName: bed.leaveTypeName,
|
||||
leaveStartTime: bed.leaveStartTime,
|
||||
leaveEndTime: bed.leaveEndTime
|
||||
leaveEndTime: bed.leaveEndTime,
|
||||
holidayStayId: bed.holidayStayId,
|
||||
holidayType: bed.holidayType,
|
||||
holidayStartDate: bed.holidayStartDate,
|
||||
holidayEndDate: bed.holidayEndDate
|
||||
}));
|
||||
|
||||
const submitData: DormRollCallForm = {
|
||||
@@ -259,6 +282,7 @@ const handleSubmit = async () => {
|
||||
totalBeds: bedDetails.value.length,
|
||||
presentCount: summary.value.present,
|
||||
leaveCount: summary.value.leave,
|
||||
holidayStayCount: summary.value.holidayStay,
|
||||
absentCount: summary.value.absent,
|
||||
emptyCount: summary.value.empty,
|
||||
details
|
||||
@@ -325,6 +349,12 @@ watch(() => props.visible, (val) => {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.bed-holiday-stay {
|
||||
background: #ecf5ff;
|
||||
border-color: #409eff;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.bed-absent {
|
||||
background: #ffebee;
|
||||
border-color: #f56c6c;
|
||||
|
||||
@@ -76,6 +76,11 @@
|
||||
<span style="color: #e6a23c">{{ scope.row.leaveCount }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="假期留宿" align="center" prop="holidayStayCount" width="90">
|
||||
<template #default="scope">
|
||||
<span style="color: #409eff">{{ scope.row.holidayStayCount || 0 }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="不在" align="center" prop="absentCount" width="70">
|
||||
<template #default="scope">
|
||||
<span style="color: #f56c6c">{{ scope.row.absentCount }}</span>
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
<div class="room-stats">
|
||||
<span class="stat-item stat-present" title="在寝">{{ room.presentCount }}</span>
|
||||
<span class="stat-item stat-leave" title="请假">{{ room.leaveCount }}</span>
|
||||
<span class="stat-item stat-holiday-stay" title="假期留宿">{{ room.holidayStayCount || 0 }}</span>
|
||||
<span class="stat-item stat-absent" title="不在">{{ room.absentCount }}</span>
|
||||
<span class="stat-item stat-empty" title="空床位">{{ room.emptyCount }}</span>
|
||||
</div>
|
||||
@@ -248,6 +249,11 @@ onMounted(() => {
|
||||
color: #e6a23c;
|
||||
}
|
||||
|
||||
.stat-holiday-stay {
|
||||
background: #ecf5ff;
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
.stat-absent {
|
||||
background: #ffebee;
|
||||
color: #f56c6c;
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
:id="form.id"
|
||||
:status="form.status"
|
||||
:pageType="routeParams.type"
|
||||
:canApprove="canApprove"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -309,6 +310,7 @@ import { StartProcessBo } from '@/api/workflow/workflowCommon/types';
|
||||
import { addStudentLeave, getStudentLeave, updateStudentLeave } from '@/api/workflow/studentLeave/studentLeave';
|
||||
import { getStudentMsg, getClassTeacherId, getCollegeLeaveApprovers, getCollegeLeaders } from '@/api/xg/xsjcxx/studentMsg/studentMsg';
|
||||
import { startWorkFlow } from '@/api/workflow/task';
|
||||
import { approvalContext } from '@/api/workflow/instance';
|
||||
import { StudentLeaveForm, StudentLeaveVO } from '@/api/workflow/studentLeave/studentLeave/types';
|
||||
import { StudentMsgVO } from '@/api/xg/xsjcxx/studentMsg/studentMsg/types';
|
||||
|
||||
@@ -474,6 +476,8 @@ const submitVerifyRef = ref<InstanceType<typeof SubmitVerify>>();
|
||||
const approvalRecordRef = ref<InstanceType<typeof ApprovalRecord>>();
|
||||
//按钮组件
|
||||
const approvalButtonRef = ref<InstanceType<typeof ApprovalButton>>();
|
||||
/** 当前用户是否可审批(以流程待办为准) */
|
||||
const canApprove = ref(false);
|
||||
|
||||
const leaveFormRef = ref<ElFormInstance>();
|
||||
|
||||
@@ -1177,6 +1181,31 @@ const openExternalMap = () => {
|
||||
// }
|
||||
// );
|
||||
|
||||
/**
|
||||
* 以流程引擎待办为准刷新审批能力(覆盖 draft/back 残留导致无按钮)
|
||||
*/
|
||||
const ensureApprovalCapability = async (leaveId: string | number) => {
|
||||
try {
|
||||
canApprove.value = false;
|
||||
const resp = await approvalContext(leaveId);
|
||||
const ctx = resp?.data || {};
|
||||
if (ctx.suggestedBusinessStatus) {
|
||||
form.approvalStatus = ctx.suggestedBusinessStatus;
|
||||
form.status = ctx.suggestedBusinessStatus;
|
||||
}
|
||||
if (ctx.canApprove) {
|
||||
canApprove.value = true;
|
||||
routeParams.value = {
|
||||
...routeParams.value,
|
||||
type: 'approval',
|
||||
taskId: ctx.taskId != null ? String(ctx.taskId) : routeParams.value.taskId
|
||||
};
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('请假审批上下文检查失败(不影响查看):', e);
|
||||
}
|
||||
};
|
||||
|
||||
// 根据id获取学生请假信息
|
||||
const getStudentLeaveInfo = async (id: string | number) => {
|
||||
try {
|
||||
@@ -1193,6 +1222,8 @@ const getStudentLeaveInfo = async (id: string | number) => {
|
||||
// todo
|
||||
form.status = leaveInfo.approvalStatus;
|
||||
|
||||
await ensureApprovalCapability(id);
|
||||
|
||||
// 如果有学生ID,则获取学生信息
|
||||
if (leaveInfo.studentId) {
|
||||
await getStudentInfo(leaveInfo.studentId);
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
:id="cancelForm.id"
|
||||
:status="leaveInfo.status"
|
||||
:pageType="routeParams.type"
|
||||
:canApprove="canApprove"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -237,6 +238,7 @@ import ApprovalRecord from '@/components/Process/approvalRecord.vue';
|
||||
import ApprovalButton from '@/components/Process/approvalButton.vue';
|
||||
import { AxiosResponse } from 'axios';
|
||||
import { StartProcessBo } from '@/api/workflow/workflowCommon/types';
|
||||
import { approvalContext } from '@/api/workflow/instance';
|
||||
import { getStudentLeave, updateStudentLeave } from '@/api/workflow/studentLeave/studentLeave';
|
||||
import { getStudentMsg } from '@/api/xg/xsjcxx/studentMsg/studentMsg';
|
||||
import { startWorkFlow } from '@/api/workflow/task';
|
||||
@@ -369,6 +371,7 @@ const submitVerifyRef = ref<InstanceType<typeof SubmitVerify>>();
|
||||
const approvalRecordRef = ref<InstanceType<typeof ApprovalRecord>>();
|
||||
//按钮组件
|
||||
const approvalButtonRef = ref<InstanceType<typeof ApprovalButton>>();
|
||||
const canApprove = ref(false);
|
||||
|
||||
const submitFormData = ref<StartProcessBo>({
|
||||
businessId: '',
|
||||
@@ -786,6 +789,10 @@ const getStudentLeaveInfo = async (id: string | number) => {
|
||||
|
||||
console.log('销假表单初始化完成:', cancelForm);
|
||||
|
||||
if (cancelForm.id) {
|
||||
await ensureApprovalCapability(cancelForm.id);
|
||||
}
|
||||
|
||||
// 如果有学生ID,则获取学生信息
|
||||
if (leaveData.studentId) {
|
||||
await getStudentInfo(leaveData.studentId);
|
||||
@@ -955,6 +962,29 @@ const handleCancelAttachmentData = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
/** 以流程引擎待办为准刷新销假审批能力 */
|
||||
const ensureApprovalCapability = async (cancelLeaveId: string | number) => {
|
||||
try {
|
||||
canApprove.value = false;
|
||||
const resp = await approvalContext(cancelLeaveId);
|
||||
const ctx = resp?.data || {};
|
||||
if (ctx.suggestedBusinessStatus) {
|
||||
leaveInfo.status = ctx.suggestedBusinessStatus;
|
||||
cancelForm.cancelStatus = ctx.suggestedBusinessStatus;
|
||||
}
|
||||
if (ctx.canApprove) {
|
||||
canApprove.value = true;
|
||||
routeParams.value = {
|
||||
...routeParams.value,
|
||||
type: 'approval',
|
||||
taskId: ctx.taskId != null ? String(ctx.taskId) : routeParams.value.taskId
|
||||
};
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('销假审批上下文检查失败(不影响查看):', e);
|
||||
}
|
||||
};
|
||||
|
||||
// 初始化页面数据
|
||||
const initPageData = () => {
|
||||
// 重置销假表单数据
|
||||
|
||||
Reference in new issue
Block a user