From 244d3d199047018fe30a7c287afec6d047ed5f73 Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期三, 14 十月 2026 15:51:55 +0800
Subject: [PATCH] fix(hrm): 薪资核算页去掉「执行核算」按钮,强制走「预览数据 → 保存核算」流程
---
src/views/mes/qc/components/import-form-modal.vue | 204 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 204 insertions(+), 0 deletions(-)
diff --git a/src/views/mes/qc/components/import-form-modal.vue b/src/views/mes/qc/components/import-form-modal.vue
new file mode 100644
index 0000000..93a97ca
--- /dev/null
+++ b/src/views/mes/qc/components/import-form-modal.vue
@@ -0,0 +1,204 @@
+<script lang="ts" setup>
+import type { FileType } from 'ant-design-vue/es/upload/interface';
+
+import { computed, ref } from 'vue';
+
+import { useVbenModal } from '@vben/common-ui';
+import { downloadFileFromBlobPart } from '@vben/utils';
+
+import { Button, message, Progress, Upload } from 'ant-design-vue';
+
+/** 瀵煎叆缁撴灉锛圛QC/IPQC/OQC 涓変釜瀵煎叆鎺ュ彛杩斿洖缁撴瀯涓�鑷达級 */
+interface ImportResult {
+ createList?: string[];
+ failureList?: Record<string, string>;
+}
+
+interface ImportModalData {
+ /** 寮圭獥鏍囬 */
+ title?: string;
+ /** 妯℃澘涓嬭浇鍑芥暟锛歳equestClient.download(...) 杩斿洖 Blob */
+ templateApi: () => Promise<Blob>;
+ /** 瀵煎叆鍑芥暟锛坢ultipart 涓婁紶 Excel锛� */
+ importApi: (file: File) => Promise<ImportResult>;
+ /** 涓嬭浇鐨勬ā鏉挎枃浠跺悕 */
+ fileName: string;
+ /** 鐏拌壊鎻愮ず鍖虹殑璇存槑娈佃惤 */
+ hints?: string[];
+}
+
+defineOptions({ name: 'QcImportFormModal' });
+
+const emit = defineEmits(['success']);
+
+const modalData = ref<ImportModalData>();
+const title = ref('鎵归噺瀵煎叆');
+const hints = ref<string[]>([]);
+const fileList = ref<FileType[]>([]);
+const importResult = ref<ImportResult>();
+const importing = ref(false);
+const progressPercent = ref(0);
+let progressTimer: ReturnType<typeof setInterval> | undefined;
+
+const createCount = computed(() => importResult.value?.createList?.length ?? 0);
+const failureCount = computed(
+ () => Object.keys(importResult.value?.failureList ?? {}).length,
+);
+
+/** 妯℃嫙杩涘害鏉★細璇锋眰鏈熼棿浠� 0 璧板埌 90% */
+function startMockProgress() {
+ progressPercent.value = 0;
+ progressTimer = setInterval(() => {
+ if (progressPercent.value < 90) {
+ progressPercent.value = Math.min(
+ 90,
+ progressPercent.value + Math.random() * 12,
+ );
+ }
+ }, 300);
+}
+
+function stopMockProgress() {
+ if (progressTimer) {
+ clearInterval(progressTimer);
+ progressTimer = undefined;
+ }
+}
+
+function delay(ms: number) {
+ return new Promise((resolve) => setTimeout(resolve, ms));
+}
+
+const [Modal, modalApi] = useVbenModal({
+ async onConfirm() {
+ const data = modalData.value;
+ if (!data) {
+ return;
+ }
+ if (fileList.value.length === 0) {
+ message.warning('璇烽�夋嫨瑕佸鍏ョ殑鏂囦欢');
+ return;
+ }
+ if (importing.value) {
+ return;
+ }
+ modalApi.lock();
+ importing.value = true;
+ startMockProgress();
+ let shouldClose = false;
+ try {
+ importResult.value = await data.importApi(fileList.value[0] as File);
+ stopMockProgress();
+ progressPercent.value = 100;
+ await delay(300);
+ emit('success');
+ if (failureCount.value > 0) {
+ message.warning(
+ `瀵煎叆瀹屾垚锛氭柊澧� ${createCount.value} 鏉★紝澶辫触 ${failureCount.value} 鏉★紝璇锋煡鐪嬩笅鏂瑰け璐ユ槑缁哷,
+ );
+ } else {
+ message.success('瀵煎叆瀹屾垚');
+ shouldClose = true;
+ }
+ } finally {
+ stopMockProgress();
+ importing.value = false;
+ modalApi.unlock();
+ if (shouldClose) {
+ await modalApi.close();
+ }
+ }
+ },
+ onOpenChange(isOpen: boolean) {
+ if (!isOpen) {
+ modalData.value = undefined;
+ fileList.value = [];
+ importResult.value = undefined;
+ importing.value = false;
+ progressPercent.value = 0;
+ return;
+ }
+ const data = modalApi.getData<ImportModalData>();
+ modalData.value = data;
+ title.value = data?.title ?? '鎵归噺瀵煎叆';
+ hints.value = data?.hints ?? [];
+ modalApi.setState({ confirmText: '瀵煎叆' });
+ },
+});
+
+/** 涓婁紶鍓嶏細鍙繚鐣欎竴涓枃浠讹紝浜ょ敱 onConfirm 缁熶竴鎻愪氦 */
+function beforeUpload(file: FileType) {
+ fileList.value = [file];
+ importResult.value = undefined;
+ return false;
+}
+
+function handleRemove() {
+ fileList.value = [];
+ importResult.value = undefined;
+}
+
+async function handleDownloadTemplate() {
+ const data = modalData.value;
+ if (!data) {
+ return;
+ }
+ const blob = await data.templateApi();
+ downloadFileFromBlobPart({ fileName: data.fileName, source: blob });
+}
+</script>
+
+<template>
+ <Modal :title="title" class="w-[720px]">
+ <div class="mx-4">
+ <Upload
+ :max-count="1"
+ accept=".xls,.xlsx"
+ :file-list="fileList"
+ :before-upload="beforeUpload"
+ @remove="handleRemove"
+ >
+ <Button type="primary">閫夋嫨 Excel 鏂囦欢</Button>
+ </Upload>
+ <div
+ v-if="hints.length > 0"
+ class="mt-2 rounded-md bg-gray-50 p-2 text-xs leading-5 text-gray-500"
+ >
+ <p v-for="(hint, index) in hints" :key="index">{{ hint }}</p>
+ </div>
+ <!-- 瀵煎叆杩涘害鏉� -->
+ <div v-if="importing" class="mt-4">
+ <div class="mb-2 text-sm text-gray-600">
+ 姝e湪瀵煎叆妫�楠屽崟鏁版嵁锛岃绋嶅��...
+ </div>
+ <Progress :percent="progressPercent" status="active" />
+ </div>
+ <!-- 瀵煎叆缁撴灉 -->
+ <div
+ v-if="importResult"
+ class="mt-4 rounded-md border border-gray-200 bg-gray-50 p-3"
+ >
+ <div class="mb-2 font-medium">瀵煎叆缁撴灉</div>
+ <div class="mb-2 flex gap-4">
+ <span class="text-green-600">鏂板锛歿{ createCount }} 鏉�</span>
+ <span class="text-red-600">澶辫触锛歿{ failureCount }} 鏉�</span>
+ </div>
+ <div v-if="createCount > 0" class="text-sm text-gray-600">
+ 鏂板妫�楠屽崟涓恒�屽緟妫�楠屻�嶇姸鎬侊紝璇峰湪鍒楄〃涓紪杈戝苟褰曞叆妫�娴嬬粨鏋�
+ </div>
+ <div v-if="failureCount > 0" class="mt-2 max-h-40 overflow-y-auto">
+ <div
+ v-for="(reason, name) in importResult.failureList"
+ :key="name"
+ class="text-sm leading-6 text-red-500"
+ >
+ {{ name }}锛歿{ reason }}
+ </div>
+ </div>
+ </div>
+ </div>
+ <template #prepend-footer>
+ <Button @click="handleDownloadTemplate">涓嬭浇瀵煎叆妯℃澘</Button>
+ </template>
+ </Modal>
+</template>
--
Gitblit v1.9.3