fix(产品需求、项目需求、工作报告、我的绩效、加班申请): 1、修复搜索区域的下拉框,无法搜索的问题。2、修复工作报告内容溢出的问题。3、修复我的待办 - 待审批,里面的二级tabs没有加权限的问题。4、优化周报里工作日志展示时的分隔符。5、优化项目需求池、我的绩效请求重复发送、影响效率的问题。

feat(我的绩效): 1、增加我的绩效在工作台可以直接处理的功能。2、增加我的绩效全部导出时,合并多个excel的sheet为一个excel的功能。
This commit is contained in:
dk
2026-06-24 18:02:36 +08:00
parent b26a9c8a39
commit 3ffdad142d
16 changed files with 569 additions and 81 deletions

View File

@@ -18,6 +18,7 @@ import {
fetchGetProjectReportApprovalPage,
fetchGetProjectTask,
fetchGetWeeklyReportApprovalPage,
fetchPerformanceSheetPage,
fetchRejectOvertimeApplication
} from '@/service/api';
import { useAuthStore } from '@/store/modules/auth';
@@ -29,6 +30,9 @@ import TaskWorklogDialog from '@/views/project/project/execution/modules/task-wo
import PersonalItemDetailDialog from '@/views/personal-center/my-item/modules/personal-item-detail-dialog.vue';
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 PerformanceActionDialog from '@/views/personal-center/my-performance/modules/performance-action-dialog.vue';
import PerformanceExcelEditorDrawer from '@/views/personal-center/my-performance/modules/performance-excel-editor-drawer.vue';
import { PerformancePermission } from '@/views/personal-center/my-performance/modules/performance-shared';
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';
@@ -61,7 +65,8 @@ import IconMdiPlayCircleOutline from '~icons/mdi/play-circle-outline';
type SortKey = 'created' | 'priority' | 'deadline';
type OvertimeApprovalActionType = 'approve' | 'reject';
type ApprovalBizType = 'overtime_application' | WorkReportType;
type PerformanceApprovalActionType = 'confirm' | 'reject';
type ApprovalBizType = 'overtime_application' | 'performance' | WorkReportType;
defineOptions({ name: 'WorkbenchTodoPanel' });
@@ -80,6 +85,11 @@ const { routerPushByKey } = useRouterPush();
const authStore = useAuthStore();
const currentUserId = computed(() => authStore.userInfo.userId || '');
const buttonPermissions = computed(() => new Set(authStore.userInfo.buttons || []));
function hasButtonPermission(permission: string) {
return buttonPermissions.value.has(permission);
}
// 工时填报在工作台内弹层完成不切路由需广播给「我的工时」widget 重拉
const { notify: notifyWorklogChanged } = useWorkbenchWorklogSignal();
@@ -89,6 +99,7 @@ const { loading, refresh } = useWorkbenchRefresh(async () => {
loadMyTaskItems(),
loadPersonalTodoItems(),
loadOvertimeApprovalItems(),
loadPerformanceApprovalItems(),
loadWorkReportApprovalItems()
]);
});
@@ -130,9 +141,30 @@ const approvalBizTabs: Array<{ key: ApprovalBizType; label: string }> = [
{ key: 'weekly', label: '周报' },
{ key: 'monthly', label: '月报' },
{ key: 'project', label: '项目半月报' },
{ key: 'performance', label: '我的绩效' },
{ key: 'overtime_application', label: '加班申请' }
];
const hasWorkReportApprovePermission = computed(() => hasButtonPermission('project:work-report:approve'));
const hasOvertimeApprovePermission = computed(() => hasButtonPermission('project:overtime-application:approve'));
const hasPerformanceApprovePermission = computed(
() =>
hasButtonPermission(PerformancePermission.SheetConfirm) && hasButtonPermission(PerformancePermission.SheetReject)
);
const visibleApprovalBizTabs = computed(() =>
approvalBizTabs.filter(tab => {
if (tab.key === 'performance') {
return hasPerformanceApprovePermission.value;
}
if (tab.key === 'overtime_application') {
return hasOvertimeApprovePermission.value;
}
return hasWorkReportApprovePermission.value;
})
);
const myTaskItems = ref<WorkbenchTodoItem[]>([]);
// 保留任务原始行,供操作图标按 availableActions 渲染并取 projectId / executionId 调状态变更接口
const myTaskRows = ref<Api.Project.MyTaskItem[]>([]);
@@ -141,6 +173,8 @@ const personalTodoItems = ref<WorkbenchTodoItem[]>([]);
const personalItemRows = ref<Api.PersonalItem.PersonalItem[]>([]);
const overtimeApprovalItems = ref<WorkbenchTodoItem[]>([]);
const overtimeApprovalRows = ref<Api.OvertimeApplication.OvertimeApplication[]>([]);
const performanceApprovalItems = ref<WorkbenchTodoItem[]>([]);
const performanceApprovalRows = ref<Api.Performance.Sheet.Sheet[]>([]);
const workReportApprovalItems = ref<WorkbenchTodoItem[]>([]);
const weeklyApprovalRows = ref<Api.WorkReport.Weekly.WeeklyReport[]>([]);
const monthlyApprovalRows = ref<Api.WorkReport.Monthly.MonthlyReport[]>([]);
@@ -150,6 +184,7 @@ const mergedItems = computed(() => [
...myTaskItems.value,
...personalTodoItems.value,
...overtimeApprovalItems.value,
...performanceApprovalItems.value,
...workReportApprovalItems.value
]);
@@ -179,6 +214,10 @@ const batchSubmitting = ref(false);
const workReportDetailVisible = ref(false);
const currentWorkReport = ref<WorkReportRow | null>(null);
const currentWorkReportType = ref<WorkReportType>('weekly');
const performanceDetailVisible = ref(false);
const performanceActionVisible = ref(false);
const currentPerformanceSheet = ref<Api.Performance.Sheet.Sheet | null>(null);
const currentPerformanceActionType = ref<PerformanceApprovalActionType>('confirm');
// 批量审批选中状态(存原始加班申请 id避免映射转换
const selectedOvertimeIds = ref<Set<string>>(new Set());
@@ -191,6 +230,7 @@ function getApprovalCategoryLabel(bizType: ApprovalBizType) {
if (bizType === 'weekly') return '周报';
if (bizType === 'monthly') return '月报';
if (bizType === 'project') return '项目半月报';
if (bizType === 'performance') return '我的绩效';
if (bizType === 'overtime_application') return '加班申请';
return '待审批';
}
@@ -464,6 +504,7 @@ const filteredItems = computed(() => {
const approvalBizTabCounts = computed(() => {
const counts: Record<ApprovalBizType, number> = {
overtime_application: 0,
performance: 0,
weekly: 0,
monthly: 0,
project: 0
@@ -472,6 +513,12 @@ const approvalBizTabCounts = computed(() => {
itemsInTab.value.forEach(item => {
if (item.approvalBizType === 'overtime_application') {
counts.overtime_application += 1;
return;
}
if (item.approvalBizType === 'performance') {
counts.performance += 1;
return;
}
if (isWorkReportApprovalBizType(item.approvalBizType)) {
@@ -510,6 +557,17 @@ watch([activeTab, activeDeadlineFilter, activeSort], () => {
currentPage.value = 1;
});
watch(
visibleApprovalBizTabs,
tabs => {
if (!tabs.length) return;
if (!tabs.some(tab => tab.key === activeApprovalBizType.value)) {
activeApprovalBizType.value = tabs[0].key;
}
},
{ immediate: true }
);
function handleSelectTab(key: WorkbenchTodoMainTab) {
if (activeTab.value === key) return;
activeTab.value = key;
@@ -538,6 +596,11 @@ function handleClickItem(item: WorkbenchTodoItem) {
return;
}
if (item.approvalBizType === 'performance') {
openPerformanceDetail(item);
return;
}
if (isWorkReportApprovalBizType(item.approvalBizType)) {
openWorkReportDetail(item);
return;
@@ -568,6 +631,14 @@ function findOvertimeApprovalRow(item: WorkbenchTodoItem) {
return overtimeApprovalRows.value.find(row => row.id === item.approvalBizId) || null;
}
function findPerformanceApprovalRow(item: WorkbenchTodoItem) {
if (!item.approvalBizId) {
return null;
}
return performanceApprovalRows.value.find(row => row.id === item.approvalBizId) || null;
}
function openOvertimeDetail(item: WorkbenchTodoItem) {
const row = findOvertimeApprovalRow(item);
if (!row) return;
@@ -576,6 +647,21 @@ function openOvertimeDetail(item: WorkbenchTodoItem) {
overtimeDetailVisible.value = true;
}
function openPerformanceDetail(item: WorkbenchTodoItem) {
const row = findPerformanceApprovalRow(item);
if (!row) return;
currentPerformanceSheet.value = row;
performanceDetailVisible.value = true;
}
function openCurrentPerformanceAction(actionType: PerformanceApprovalActionType) {
if (!currentPerformanceSheet.value) return;
currentPerformanceActionType.value = actionType;
performanceActionVisible.value = true;
}
function findWorkReportApprovalRow(item: WorkbenchTodoItem) {
if (!item.approvalBizId || !isWorkReportApprovalBizType(item.approvalBizType)) {
return null;
@@ -627,6 +713,12 @@ async function handleWorkReportSubmitted() {
await loadWorkReportApprovalItems();
}
async function handlePerformanceActionSubmitted() {
performanceActionVisible.value = false;
performanceDetailVisible.value = false;
await loadPerformanceApprovalItems();
}
// 优先级角标用字典 label 原样回显rdms_req_priorityP0~P3不翻译成高/中/低
const { getLabel: getPriorityLabel } = useDict(RDMS_REQ_PRIORITY_DICT_CODE);
@@ -774,6 +866,12 @@ async function handleBatchActionSubmit(payload: { actionType: OvertimeApprovalAc
}
async function loadOvertimeApprovalItems() {
if (!hasOvertimeApprovePermission.value) {
overtimeApprovalRows.value = [];
overtimeApprovalItems.value = [];
return;
}
const { error, data } = await fetchGetOvertimeApplicationApprovalPage({
pageNo: 1,
pageSize: 20,
@@ -809,6 +907,42 @@ async function loadOvertimeApprovalItems() {
);
}
async function loadPerformanceApprovalItems() {
if (!hasPerformanceApprovePermission.value) {
performanceApprovalRows.value = [];
performanceApprovalItems.value = [];
return;
}
const { error, data } = await fetchPerformanceSheetPage({
pageNo: 1,
pageSize: 20,
statusCode: 'sent'
});
if (error || !data) {
performanceApprovalRows.value = [];
performanceApprovalItems.value = [];
return;
}
performanceApprovalRows.value = data.list;
performanceApprovalItems.value = buildWorkbenchTodoItems(
data.list.map(item => ({
id: `performance-${item.id}`,
category: 'approval',
categoryLabel: '我的绩效',
title: `${item.periodMonth} 绩效待确认`,
createdTime: item.sentTime || item.createTime || item.updateTime || '',
deadline: item.sentTime || item.createTime || item.updateTime || null,
source: `我的绩效 · ${item.managerName || '--'}`,
priority: 'mid',
approvalBizType: 'performance',
approvalBizId: item.id
}))
);
}
function buildWorkReportApprovalItems<T extends WorkReportRow>(
bizType: WorkReportType,
rows: T[]
@@ -832,6 +966,14 @@ function buildWorkReportApprovalItems<T extends WorkReportRow>(
}
async function loadWorkReportApprovalItems() {
if (!hasWorkReportApprovePermission.value) {
weeklyApprovalRows.value = [];
monthlyApprovalRows.value = [];
projectApprovalRows.value = [];
workReportApprovalItems.value = [];
return;
}
const [weeklyResult, monthlyResult, projectResult] = await Promise.all([
fetchGetWeeklyReportApprovalPage({
pageNo: 1,
@@ -938,7 +1080,7 @@ onActivated(refresh);
</div>
<div v-else class="workbench-todo__filters-left">
<button
v-for="tab in approvalBizTabs"
v-for="tab in visibleApprovalBizTabs"
:key="tab.key"
type="button"
class="workbench-todo__filter"
@@ -1076,6 +1218,13 @@ onActivated(refresh);
</ElButton>
</ElTooltip>
</div>
<div v-else-if="item.approvalBizType === 'performance'" class="workbench-todo__actions" @click.stop>
<ElTooltip content="查看绩效">
<ElButton link type="primary" class="workbench-todo__action-btn" @click="openPerformanceDetail(item)">
<IconMdiEyeOutline class="text-15px" />
</ElButton>
</ElTooltip>
</div>
<div
v-else-if="isWorkReportApprovalBizType(item.approvalBizType)"
class="workbench-todo__actions"
@@ -1212,6 +1361,22 @@ onActivated(refresh);
:row-data="currentWorkReport"
@submitted="handleWorkReportSubmitted"
/>
<PerformanceExcelEditorDrawer
v-model:visible="performanceDetailVisible"
:row-data="currentPerformanceSheet"
mode="view"
show-approval-footer
@start-approval="openCurrentPerformanceAction('confirm')"
/>
<PerformanceActionDialog
v-model:visible="performanceActionVisible"
:row-data="currentPerformanceSheet"
:action-type="currentPerformanceActionType"
selectable-action-type
@submitted="handlePerformanceActionSubmitted"
/>
</WorkbenchModuleCard>
</template>