fix(工作报告): 修复工作报告存在的若干问题。

feat(加班申请): 支持批量审批。
This commit is contained in:
dk
2026-06-13 13:06:39 +08:00
parent 5061eced32
commit 80f028bcb9
19 changed files with 1845 additions and 790 deletions

View File

@@ -6,6 +6,8 @@ import { OBJECT_CONTEXT_QUERY_KEY } from '@/constants/object-context';
import { RDMS_REQ_PRIORITY_DICT_CODE } from '@/constants/dict';
import {
fetchApproveOvertimeApplication,
fetchBatchApproveOvertimeApplication,
fetchBatchRejectOvertimeApplication,
fetchChangePersonalItemStatus,
fetchChangeProjectTaskStatus,
fetchGetMonthlyReportApprovalPage,
@@ -28,13 +30,15 @@ import PersonalItemDetailDialog from '@/views/personal-center/my-item/modules/pe
import PersonalItemOperateDialog from '@/views/personal-center/my-item/modules/personal-item-operate-dialog.vue';
import PersonalItemStatusActionDialog from '@/views/personal-center/my-item/modules/personal-item-status-action-dialog.vue';
import OvertimeApplicationActionDialog from '@/views/personal-center/overtime-application/modules/overtime-application-action-dialog.vue';
import OvertimeApplicationBatchDetailDialog from '@/views/personal-center/overtime-application/modules/overtime-application-batch-detail-dialog.vue';
import OvertimeApplicationDetailDialog from '@/views/personal-center/overtime-application/modules/overtime-application-detail-dialog.vue';
import WorkReportPrototypePageDialog from '@/views/personal-center/work-report/shared/components/prototype-page-dialog.vue';
import {
WORK_REPORT_TYPE_LABEL,
type WorkReportRow,
type WorkReportType,
formatPeriod
formatPeriod,
formatWeeklyPeriodLabel
} from '@/views/personal-center/work-report/shared/types';
import {
type WorkbenchTodoDeadlineFilter,
@@ -169,10 +173,19 @@ const overtimeActionVisible = ref(false);
const overtimeActionSubmitting = ref(false);
const currentOvertimeApplication = ref<Api.OvertimeApplication.OvertimeApplication | null>(null);
const currentOvertimeActionType = ref<OvertimeApprovalActionType>('approve');
const batchDetailVisible = ref(false);
const batchActionVisible = ref(false);
const batchSubmitting = ref(false);
const workReportDetailVisible = ref(false);
const currentWorkReport = ref<WorkReportRow | null>(null);
const currentWorkReportType = ref<WorkReportType>('weekly');
// 批量审批选中状态(存原始加班申请 id避免映射转换
const selectedOvertimeIds = ref<Set<string>>(new Set());
// 批量审批是否为当前操作来源(区分单条审批和批量审批的 submit 回调)
const isBatchActionMode = ref(false);
const OVERTIME_APPROVAL_ACTION_ICONS = {
detail: markRaw(IconMdiEyeOutline)
};
@@ -680,6 +693,97 @@ async function loadPersonalTodoItems() {
);
}
// 批量审批选中状态管理
function isOvertimeItemSelected(item: WorkbenchTodoItem) {
return item.approvalBizId ? selectedOvertimeIds.value.has(item.approvalBizId) : false;
}
function toggleOvertimeItem(item: WorkbenchTodoItem, checked: boolean) {
if (!item.approvalBizId) return;
if (checked) {
selectedOvertimeIds.value.add(item.approvalBizId);
} else {
selectedOvertimeIds.value.delete(item.approvalBizId);
}
}
function toggleSelectAllOvertimeItems(checked: boolean) {
if (checked) {
overtimeApprovalItems.value.forEach(item => {
if (item.approvalBizId) selectedOvertimeIds.value.add(item.approvalBizId);
});
} else {
selectedOvertimeIds.value.clear();
}
}
function clearOvertimeSelection() {
selectedOvertimeIds.value.clear();
}
// 切换审批子页签时清空选中
watch(activeApprovalBizType, () => {
clearOvertimeSelection();
});
// 当前页加班申请是否全部选中(用于全选复选框状态)
const allOvertimeItemsSelected = computed(() => {
const items = overtimeApprovalItems.value;
if (items.length === 0) return false;
return items.every(item => item.approvalBizId && selectedOvertimeIds.value.has(item.approvalBizId));
});
// 当前页是否有部分选中
const someOvertimeItemsSelected = computed(() => {
return overtimeApprovalItems.value.some(
item => item.approvalBizId && selectedOvertimeIds.value.has(item.approvalBizId)
);
});
// 批量审批选中的 id 数组(用于传给批量详情弹窗)
const batchSelectedIds = computed(() => Array.from(selectedOvertimeIds.value));
// 打开批量审批详情弹窗
function handleBatchReview() {
batchDetailVisible.value = true;
}
// 批量详情弹窗中点击"通过"或"退回"
function openBatchActionDialog(actionType: OvertimeApprovalActionType) {
currentOvertimeActionType.value = actionType;
isBatchActionMode.value = true;
batchActionVisible.value = true;
}
// 批量审批提交(对所有选中项执行批量 API
async function handleBatchActionSubmit(reason: string | null) {
const ids = Array.from(selectedOvertimeIds.value);
if (ids.length === 0) return;
const fn =
currentOvertimeActionType.value === 'approve'
? fetchBatchApproveOvertimeApplication
: fetchBatchRejectOvertimeApplication;
batchSubmitting.value = true;
const { error, data } = await fn({ ids, reason });
batchSubmitting.value = false;
if (error || !data) return;
batchActionVisible.value = false;
batchDetailVisible.value = false;
clearOvertimeSelection();
if (data.failCount > 0) {
window.$message?.warning(`成功 ${data.successCount} 条,失败 ${data.failCount}`);
} else {
window.$message?.success(`已批量处理 ${data.successCount}`);
}
await loadOvertimeApprovalItems();
}
async function loadOvertimeApprovalItems() {
const { error, data } = await fetchGetOvertimeApplicationApprovalPage({
pageNo: 1,
@@ -725,7 +829,7 @@ function buildWorkReportApprovalItems<T extends WorkReportRow>(
rows.map(item => ({
id: `${bizType}-${item.id}`,
category: 'approval',
title: `${reportTypeLabel} · ${formatPeriod(item)} 待审批`,
title: `${reportTypeLabel} · ${bizType === 'weekly' ? formatWeeklyPeriodLabel(item) : formatPeriod(item)} 待审批`,
createdTime: item.submitTime || item.createTime || '',
deadline: item.submitTime || item.createTime || null,
source: `${reportTypeLabel} · ${'projectName' in item ? item.projectName : item.reporterName}`,
@@ -882,9 +986,60 @@ onMounted(async () => {
</div>
<div class="workbench-todo__content">
<!-- 批量操作栏仅加班申请待审批时显示 -->
<div
v-if="
activeTab === 'approval' &&
activeApprovalBizType === 'overtime_application' &&
overtimeApprovalItems.length > 0
"
class="workbench-todo__batch-bar"
:class="{ 'workbench-todo__batch-bar--active': selectedOvertimeIds.size > 0 }"
>
<div class="workbench-todo__batch-bar-left">
<ElCheckbox
:model-value="allOvertimeItemsSelected"
:indeterminate="someOvertimeItemsSelected && !allOvertimeItemsSelected"
@change="val => toggleSelectAllOvertimeItems(Boolean(val))"
@click.stop
>
全选
</ElCheckbox>
<span v-if="selectedOvertimeIds.size > 0" class="workbench-todo__batch-bar-count">
已选择 {{ selectedOvertimeIds.size }} 项
</span>
</div>
<div v-if="selectedOvertimeIds.size > 0" class="workbench-todo__batch-bar-right">
<ElButton size="small" type="primary" :loading="batchSubmitting" @click.stop="handleBatchReview">
批量审批
</ElButton>
<ElButton size="small" link @click.stop="clearOvertimeSelection">取消选择</ElButton>
</div>
</div>
<div v-if="pagedItems.length" class="workbench-todo__list">
<article v-for="item in pagedItems" :key="item.id" class="workbench-todo__item">
<article
v-for="item in pagedItems"
:key="item.id"
class="workbench-todo__item"
:class="{
'workbench-todo__item--clickable': Boolean(item.routeKey || item.approvalBizType),
'workbench-todo__item--selected': isOvertimeItemSelected(item)
}"
@click="handleClickItem(item)"
>
<div class="workbench-todo__leading">
<!-- 加班申请待审批时显示复选框 -->
<ElCheckbox
v-if="
activeTab === 'approval' &&
activeApprovalBizType === 'overtime_application' &&
item.approvalBizType === 'overtime_application'
"
:model-value="isOvertimeItemSelected(item)"
@change="val => toggleOvertimeItem(item, Boolean(val))"
@click.stop
/>
<span class="workbench-todo__category" :class="`workbench-todo__category--${item.categoryTone}`">
{{ item.categoryLabel }}
</span>
@@ -1052,6 +1207,7 @@ onMounted(async () => {
:row-data="currentOvertimeApplication"
show-approval-actions
:action-loading="overtimeActionSubmitting"
append-to-body
@approve="openCurrentOvertimeAction('approve')"
@reject="openCurrentOvertimeAction('reject')"
/>
@@ -1059,9 +1215,30 @@ onMounted(async () => {
v-model:visible="overtimeActionVisible"
:action-type="currentOvertimeActionType"
:loading="overtimeActionSubmitting"
append-to-body
@submit="handleOvertimeActionSubmit"
/>
<!-- 批量审批详情弹窗(左右箭头切换 + 通过/退回按钮) -->
<OvertimeApplicationBatchDetailDialog
v-model:visible="batchDetailVisible"
:selected-ids="batchSelectedIds"
:rows="overtimeApprovalRows"
:action-loading="batchSubmitting"
append-to-body
@approve="openBatchActionDialog('approve')"
@reject="openBatchActionDialog('reject')"
/>
<!-- 批量审批意见/退回原因对话框 -->
<OvertimeApplicationActionDialog
v-model:visible="batchActionVisible"
:action-type="currentOvertimeActionType"
:loading="batchSubmitting"
append-to-body
@submit="handleBatchActionSubmit"
/>
<WorkReportPrototypePageDialog
v-model:visible="workReportDetailVisible"
mode="detail"
@@ -1261,6 +1438,44 @@ onMounted(async () => {
margin: auto;
}
/* 批量操作栏 */
.workbench-todo__batch-bar {
display: flex;
justify-content: space-between;
align-items: center;
gap: 12px;
padding: 8px 14px;
margin-bottom: 10px;
border: 1px solid rgb(226 232 240 / 90%);
border-radius: 12px;
background-color: rgb(248 250 252 / 96%);
font-size: 13px;
color: rgb(71 85 105 / 94%);
transition: all 160ms ease;
}
.workbench-todo__batch-bar--active {
border-color: rgb(14 116 144 / 40%);
background-color: rgb(240 253 250 / 80%);
color: rgb(14 116 144 / 96%);
}
.workbench-todo__batch-bar-left {
display: flex;
align-items: center;
gap: 10px;
}
.workbench-todo__batch-bar-right {
display: flex;
align-items: center;
gap: 6px;
}
.workbench-todo__batch-bar-count {
font-weight: 600;
}
.workbench-todo__list {
display: flex;
flex-direction: column;
@@ -1281,6 +1496,20 @@ onMounted(async () => {
background-color 160ms ease;
}
.workbench-todo__item--clickable {
cursor: pointer;
}
.workbench-todo__item--clickable:hover {
border-color: rgb(14 116 144 / 60%);
background-color: rgb(240 253 250 / 84%);
}
.workbench-todo__item--selected {
border-color: rgb(14 116 144 / 60%);
background-color: rgb(240 253 250 / 90%);
}
.workbench-todo__leading {
display: flex;
align-items: center;