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/crm/invoice/modules/import-form.vue |  157 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 157 insertions(+), 0 deletions(-)

diff --git a/src/views/crm/invoice/modules/import-form.vue b/src/views/crm/invoice/modules/import-form.vue
new file mode 100644
index 0000000..8529726
--- /dev/null
+++ b/src/views/crm/invoice/modules/import-form.vue
@@ -0,0 +1,157 @@
+<script lang="ts" setup>
+import type { FileType } from 'ant-design-vue/es/upload/interface';
+import type { InvoiceImportResult } from '#/api/crm/invoice';
+
+import { computed, ref } from 'vue';
+
+import { useVbenModal } from '#/packages/effects/common-ui/src';
+import { downloadFileFromBlobPart } from '#/packages/utils/src';
+
+import { Button, message, Progress, Upload } from 'ant-design-vue';
+
+import { importInvoice, importInvoiceTemplate } from '#/api/crm/invoice';
+
+const emit = defineEmits(['success']);
+
+const fileList = ref<FileType[]>([]);
+const importResult = ref<InvoiceImportResult>();
+const importing = ref(false);
+const progressPercent = ref(0);
+let progressTimer: ReturnType<typeof setInterval> | undefined;
+
+const [Modal, modalApi] = useVbenModal({
+  async onConfirm() {
+    let shouldClose = false;
+    if (fileList.value.length === 0) {
+      message.warning('璇烽�夋嫨瑕佸鍏ョ殑鏂囦欢');
+      return;
+    }
+    if (importing.value) return;
+    modalApi.lock();
+    importing.value = true;
+    startMockProgress();
+    try {
+      importResult.value = await importInvoice(fileList.value[0] as File);
+      // 瀹屾垚锛氳繘搴︽潯璧版弧
+      stopMockProgress();
+      progressPercent.value = 100;
+      await delay(300);
+      emit('success');
+      const failedCount = Object.keys(
+        importResult.value?.failureList ?? {},
+      ).length;
+      if (failedCount > 0) {
+        message.warning(
+          `瀵煎叆瀹屾垚锛氭柊澧� ${createCount.value} 鏉★紝澶辫触 ${failedCount} 鏉★紝璇锋煡鐪嬩笅鏂瑰け璐ユ槑缁哷,
+        );
+      } else {
+        message.success('瀵煎叆瀹屾垚');
+        shouldClose = true;
+      }
+    } finally {
+      stopMockProgress();
+      importing.value = false;
+      modalApi.unlock();
+      if (shouldClose) {
+        await modalApi.close();
+      }
+    }
+  },
+});
+
+/** 妯℃嫙杩涘害鏉★細璇锋眰鏈熼棿浠� 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));
+}
+
+function beforeUpload(file: FileType) {
+  fileList.value = [file];
+  importResult.value = undefined;
+  return false;
+}
+
+function handleRemove() {
+  fileList.value = [];
+  importResult.value = undefined;
+}
+
+async function handleDownloadTemplate() {
+  const data = await importInvoiceTemplate();
+  downloadFileFromBlobPart({ fileName: '寮�绁ㄥ鍏ユā鏉�.xls', source: data });
+}
+
+const createCount = computed(() => importResult.value?.createList?.length ?? 0);
+const failureCount = computed(
+  () => Object.keys(importResult.value?.failureList ?? {}).length,
+);
+</script>
+
+<template>
+  <Modal 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 class="mt-2 rounded-md bg-gray-50 p-2 text-xs leading-5 text-gray-500">
+        <p>浠呭厑璁稿鍏� xls銆亁lsx 鏍煎紡鏂囦欢锛涘繀濉垪锛氬悎鍚岀紪鍙枫�佸彂绁ㄧ被鍨嬨�佸紑绁ㄩ噾棰�(鍏�)銆傗�滃悎鍚岀紪鍙封�濋渶涓庣郴缁熶腑宸插鏍搁�氳繃鐨勫悎鍚岀紪鍙蜂竴鑷达紙缂栧彿闇�瀹屽叏涓�鑷达級锛屽鎴风敱鍚堝悓鑷姩甯﹀叆锛屾棤闇�濉啓锛涒�滃彂绁ㄧ被鍨嬧�濇寜瀛楀吀鍚嶇О濉啓锛堝鍊肩◣涓撶敤鍙戠エ / 澧炲�肩◣鏅�氬彂绁� / 鐢靛瓙鍙戠エ锛夛紱鈥滃紑绁ㄩ噾棰�(鍏�)鈥濋渶澶т簬 0 涓斾笉瓒呰繃璇ュ悎鍚屽墿浣欏彲寮�绁ㄩ噾棰濄��</p>
+        <p>鈥滃紑绁ㄧ紪鍙封�濈暀绌哄垯鐢辩郴缁熻嚜鍔ㄧ敓鎴愶紝濉啓鏃朵笉鍙笌绯荤粺涓凡鏈夊紑绁ㄧ紪鍙烽噸澶嶏紱鈥滃紑绁ㄦ棩鏈熲�濇牸寮忎负 2026-01-01 10:00:00 鎴� 2026-01-01锛岀暀绌哄彇褰撳墠鏃堕棿銆�</p>
+        <p>閫愯瀵煎叆锛屽け璐ヨ涓嶅奖鍝嶆垚鍔熻锛涘鍏ョ殑寮�绁ㄨ礋璐d汉 = 褰撳墠鐧诲綍鐢ㄦ埛锛岀姸鎬佷负鈥滄湭鎻愪氦鈥濓紝鍙湪鍒楄〃涓户缁紪杈戙�佷笂浼犲彂绁ㄩ檮浠跺悗鎻愪氦瀹℃壒銆�</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="failureCount > 0" class="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