From e2094831e4282a7ac687f4907b9ff247ca97c889 Mon Sep 17 00:00:00 2001 From: dk <1260500659@qq.com> Date: Wed, 8 Jul 2026 20:08:04 +0800 Subject: [PATCH] =?UTF-8?q?feat(performance=E3=80=81workReport=E3=80=81wor?= =?UTF-8?q?kbench):=20=E6=B7=BB=E5=8A=A0=E7=BB=A9=E6=95=88=E8=A1=A8?= =?UTF-8?q?=E9=A2=84=E5=A1=AB=E5=8A=9F=E8=83=BD=E5=B9=B6=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E5=B7=A5=E4=BD=9C=E5=8F=B0=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/service/api/performance.ts | 38 +++++ src/typings/api/performance.d.ts | 10 ++ .../performance-excel-editor-drawer.vue | 147 ++++++++++++++++-- .../monthly/modules/approval-page.vue | 4 +- .../work-report/project/modules/fill-page.vue | 4 +- .../work-report/weekly/modules/fill-page.vue | 4 +- .../modules/workbench-todo-panel.vue | 6 +- 7 files changed, 187 insertions(+), 26 deletions(-) diff --git a/src/service/api/performance.ts b/src/service/api/performance.ts index a666f66..a643d2a 100644 --- a/src/service/api/performance.ts +++ b/src/service/api/performance.ts @@ -65,6 +65,11 @@ type MonthlyResultResponse = Omit & { + sourceSheetId?: StringIdResponse | null; + cellValues?: Record | null; +}; + type TeamSummaryResponse = Omit< Api.Performance.Team.Summary, | 'expectedSheetCount' @@ -185,6 +190,28 @@ function normalizeMonthlyResult(response: MonthlyResultResponse): Api.Performanc }; } +function normalizePrefillData(response: PrefillResponse): Api.Performance.Sheet.PrefillData { + const cellValues = Object.fromEntries( + Object.entries(response.cellValues || {}).flatMap(([address, value]) => { + const normalizedAddress = String(address || '') + .trim() + .toUpperCase(); + + if (!normalizedAddress || value === null || value === undefined) { + return []; + } + + return [[normalizedAddress, String(value)]]; + }) + ); + + return { + sourceSheetId: normalizeNullableStringId(response.sourceSheetId), + sourcePeriodMonth: response.sourcePeriodMonth ?? null, + cellValues + }; +} + function normalizeTeamSummary(response: TeamSummaryResponse): Api.Performance.Team.Summary { return { ...response, @@ -464,6 +491,17 @@ export async function fetchPerformanceMonthlyResult(employeeId: string, periodMo ); } +export async function fetchPerformanceSheetPrefill(employeeId: string, periodMonth: string) { + const result = await request({ + ...safeJsonRequestConfig, + url: `${SHEET_PREFIX}/prefill`, + method: 'get', + params: { employeeId, periodMonth } + }); + + return mapServiceResult(result as ServiceRequestResult, normalizePrefillData); +} + export function fetchPerformanceSheetStatusDict() { return request({ ...safeJsonRequestConfig, diff --git a/src/typings/api/performance.d.ts b/src/typings/api/performance.d.ts index f8b476f..2d12ddb 100644 --- a/src/typings/api/performance.d.ts +++ b/src/typings/api/performance.d.ts @@ -21,6 +21,10 @@ declare namespace Api { actualScoreTotalCell?: string | null; baseScoreTotalCell?: string | null; extraScoreTotalCell?: string | null; + resultDescriptionCells?: string[] | null; + actualScoreCells?: string[] | null; + baseScoreCells?: string[] | null; + extraScoreCells?: string[] | null; } interface Template { @@ -172,6 +176,12 @@ declare namespace Api { extraScoreTotal?: string | number | null; statusCode?: Common.SheetStatusCode | null; } + + interface PrefillData { + sourceSheetId?: string | null; + sourcePeriodMonth?: string | null; + cellValues: Record; + } } namespace Team { diff --git a/src/views/personal-center/my-performance/modules/performance-excel-editor-drawer.vue b/src/views/personal-center/my-performance/modules/performance-excel-editor-drawer.vue index eb00f4b..b92bc0e 100644 --- a/src/views/personal-center/my-performance/modules/performance-excel-editor-drawer.vue +++ b/src/views/personal-center/my-performance/modules/performance-excel-editor-drawer.vue @@ -7,6 +7,7 @@ import { createPerformanceSheet, downloadFile, fetchPerformanceSheet, + fetchPerformanceSheetPrefill, fetchPerformanceTemplateCurrent, sendPerformanceSheet, updatePerformanceSheetExcel, @@ -54,12 +55,16 @@ const { createRequiredRule } = useFormRules(); const containerRef = ref(); const loading = ref(false); +const prefillLoading = ref(false); const saving = ref(false); const sending = ref(false); const currentSheet = ref(null); const currentTemplate = ref(null); const errorMessage = ref(''); +const prefillSourcePeriodMonth = ref(''); const viewportWidth = ref(typeof window === 'undefined' ? 1920 : window.innerWidth); +const loadedCreateContextKey = ref(''); +const createContextInitializing = ref(false); const createForm = reactive({ periodMonth: createDefaultPeriodMonth(), @@ -380,6 +385,10 @@ function getActiveWorkbook() { return univerAPI?.getActiveWorkbook?.(); } +function getCreateContextKey() { + return `${createForm.periodMonth || ''}::${createForm.employeeId || ''}`; +} + function parseCellAddress(address?: string | null) { const text = String(address || '') .trim() @@ -415,6 +424,29 @@ function readCell(address?: string | null) { } } +function writeCell(address: string, value: string) { + const position = parseCellAddress(address); + const workbook = getActiveWorkbook(); + const sheet = workbook?.getActiveSheet?.(); + if (!position || !sheet) return false; + + try { + const range = sheet.getRange(position.row, position.column); + range?.setValue?.(value); + return true; + } catch { + return false; + } +} + +function applyPrefillValues(cellValues: Record) { + Object.entries(cellValues).forEach(([address, value]) => { + if (!address) return; + + writeCell(address, value); + }); +} + function readScores() { const mapping = currentTemplate.value?.scoreCellMapping; @@ -451,9 +483,36 @@ function createInitialFileName() { return '绩效表.xlsx'; } +async function applyCreatePrefill() { + prefillSourcePeriodMonth.value = ''; + + if (!isCreateMode.value || !createForm.employeeId || !createForm.periodMonth) { + return; + } + + prefillLoading.value = true; + + try { + const { error, data } = await fetchPerformanceSheetPrefill(createForm.employeeId, createForm.periodMonth); + if (error || !data) { + return; + } + + prefillSourcePeriodMonth.value = data.sourcePeriodMonth ?? ''; + + if (Object.keys(data.cellValues).length) { + applyPrefillValues(data.cellValues); + } + } finally { + prefillLoading.value = false; + } +} + async function loadWorkbook() { loading.value = true; + prefillLoading.value = false; errorMessage.value = ''; + prefillSourcePeriodMonth.value = ''; currentSheet.value = null; currentTemplate.value = null; @@ -505,6 +564,14 @@ async function loadWorkbook() { const snapshot = await transformExcelToUniver(file); await nextTick(); createWorkbook(applyCreateEmployeeName(snapshot)); + + if (isCreateMode.value) { + await nextTick(); + await applyCreatePrefill(); + loadedCreateContextKey.value = getCreateContextKey(); + } else { + loadedCreateContextKey.value = ''; + } } catch (error) { errorMessage.value = error instanceof Error ? error.message : 'Excel 解析失败'; } finally { @@ -546,6 +613,7 @@ function createInitialPeriodMonth() { function initializeCreateForm() { createForm.periodMonth = createInitialPeriodMonth(); createForm.employeeId = props.initialEmployeeId || ''; + prefillSourcePeriodMonth.value = ''; } async function executeSave(): Promise { @@ -643,31 +711,48 @@ watch(visible, async isVisible => { disposeUniver(); currentSheet.value = null; currentTemplate.value = null; + loadedCreateContextKey.value = ''; + createContextInitializing.value = false; + prefillLoading.value = false; + prefillSourcePeriodMonth.value = ''; initializeCreateForm(); return; } - if (isCreateMode.value) { - initializeCreateForm(); - } + createContextInitializing.value = isCreateMode.value; - await nextTick(); - await loadWorkbook(); + try { + if (isCreateMode.value) { + initializeCreateForm(); + } + + await nextTick(); + await loadWorkbook(); + } finally { + createContextInitializing.value = false; + } }); watch( - () => createForm.employeeId, - async (employeeId, previousEmployeeId) => { - if (!visible.value || !isCreateMode.value || !employeeId || employeeId === previousEmployeeId) { + () => [createForm.employeeId, createForm.periodMonth] as const, + async ([employeeId, periodMonth], [previousEmployeeId, previousPeriodMonth]) => { + if (!visible.value || !isCreateMode.value || createContextInitializing.value) { return; } - const workbook = getActiveWorkbook(); - if (!workbook) return; + const currentKey = `${periodMonth || ''}::${employeeId || ''}`; + const previousKey = `${previousPeriodMonth || ''}::${previousEmployeeId || ''}`; + if ( + currentKey === previousKey || + currentKey === loadedCreateContextKey.value || + loading.value || + prefillLoading.value + ) { + return; + } - const snapshot = workbook.save(); await nextTick(); - createWorkbook(applyCreateEmployeeName(snapshot)); + await loadWorkbook(); } ); @@ -701,14 +786,24 @@ onMounted(() => { type="month" value-format="YYYY-MM" placeholder="选择绩效月份" + :disabled="loading || prefillLoading || saving || sending" /> - + +
+ {{ prefillLoading ? '正在自动带出上一份绩效数据...' : `已自动带出 ${prefillSourcePeriodMonth} 的绩效数据` }} +
@@ -727,14 +822,26 @@ onMounted(() => { @@ -763,6 +870,12 @@ onMounted(() => { margin-right: 24px; } +.performance-excel-editor__prefill-tip { + margin-top: 12px; + color: var(--el-text-color-secondary); + font-size: 12px; +} + .performance-excel-editor__footer { display: flex; justify-content: flex-end; diff --git a/src/views/personal-center/work-report/monthly/modules/approval-page.vue b/src/views/personal-center/work-report/monthly/modules/approval-page.vue index 3411525..96c7a29 100644 --- a/src/views/personal-center/work-report/monthly/modules/approval-page.vue +++ b/src/views/personal-center/work-report/monthly/modules/approval-page.vue @@ -864,8 +864,8 @@ watch(
- 退出审批 - 开始审批 + 退出 + 审批
提交审批
- 退出审批 - 开始审批 + 退出 + 审批
diff --git a/src/views/personal-center/work-report/weekly/modules/fill-page.vue b/src/views/personal-center/work-report/weekly/modules/fill-page.vue index 7a4da53..3b90f3d 100644 --- a/src/views/personal-center/work-report/weekly/modules/fill-page.vue +++ b/src/views/personal-center/work-report/weekly/modules/fill-page.vue @@ -1500,8 +1500,8 @@ function syncRichSupport(item: PlanItem, event: Event) { 提交审批
- 退出审批 - 开始审批 + 退出 + 审批