From 93ccd040546983c92ecdcd906df81eec67a10ad6 Mon Sep 17 00:00:00 2001
From: zhizhijie <zhizhijie@users.noreply.gitee.com>
Date: 星期四, 10 九月 2026 08:44:19 +0800
Subject: [PATCH] feat: 数据导入页新增清空按钮——按报表期清空数据库业务数据/审核结果/已审核标记/导入批次,支持整月重导(含功能测试报告与工作日结)

---
 traffic-audit-web/src/views/DataImport.vue |  170 +++++++++++++++++++++++++++++++++++++++++++++++---------
 1 files changed, 143 insertions(+), 27 deletions(-)

diff --git a/traffic-audit-web/src/views/DataImport.vue b/traffic-audit-web/src/views/DataImport.vue
index e010f1c..e496ed4 100644
--- a/traffic-audit-web/src/views/DataImport.vue
+++ b/traffic-audit-web/src/views/DataImport.vue
@@ -1,4 +1,4 @@
-<template>
+SKIP/CHECK (0): TYPE_LABELS upper <template>
   <el-container direction="vertical">
     <app-header title="浜ら�氱粺璁℃姤琛ㄥ鏍哥郴缁� - 鏁版嵁瀵煎叆"></app-header>
     <el-container>
@@ -29,10 +29,10 @@
             <div class="card-head">閫夋嫨鏁版嵁绫诲瀷</div>
             <div class="module-tabs-head">
               <el-radio-group v-model="activeModule" size="small" @change="onModuleSwitch">
-                <el-radio-button v-for="g in groups" :key="g.key" :label="g.key">{{ g.name }}</el-radio-button>
+                <el-radio-button v-for="g in visibleGroups" :key="g.key" :label="g.key">{{ g.name }}</el-radio-button>
               </el-radio-group>
             </div>
-            <div v-for="g in groups" :key="g.key" v-show="activeModule === g.key" class="module-tab-pane">
+            <div v-for="g in visibleGroups" :key="g.key" v-show="activeModule === g.key" class="module-tab-pane">
               <div class="module-sel">
                                 <div class="module-sel-row">
                   <el-select v-model="moduleSel[g.key]" placeholder="閫夋嫨鏁版嵁绫诲瀷" style="width:260px"
@@ -51,15 +51,15 @@
                   <el-button size="mini" type="primary" icon="el-icon-upload2" :loading="cityDirLoading"
                              @click="importCityDir(moduleSel[g.key])">浠庣洰褰曟壒閲忓鍏�</el-button>
                 </div>
-                <div v-else-if="moduleSel[g.key]" class="module-file">
-                  <div class="file-name" :title="fileNames[moduleSel[g.key]]">{{ fileNames[moduleSel[g.key]] || '鏈�夋嫨鏂囦欢' }}</div>
+                <div v-else-if="moduleSel[g.key] && !isDirOnlyType(moduleSel[g.key])" class="module-file">
+                  <div class="file-name" :title="fileNamesText(moduleSel[g.key])">{{ fileNamesText(moduleSel[g.key]) || '鏈�夋嫨鏂囦欢' }}</div>
                   <div class="file-btns">
-                    <el-upload action="#" :auto-upload="false" :show-file-list="false" accept=".xlsx,.xls"
+                    <el-upload action="#" :auto-upload="false" :show-file-list="false" multiple accept=".xlsx,.xls,.et"
                                :ref="'up' + g.key" :on-change="f => onFilePicked(g.key, f)" style="display:inline-block">
-                      <el-button size="mini" type="primary" plain icon="el-icon-folder-opened">閫夋嫨鏂囦欢</el-button>
+                      <el-button size="mini" type="primary" plain icon="el-icon-folder-opened" :disabled="importing">閫夋嫨鏂囦欢</el-button>
                     </el-upload>
-                    <el-button size="mini" type="primary" icon="el-icon-upload2" :disabled="!files[moduleSel[g.key]]"
-                               @click="importNow(g.key)">绔嬪嵆瀵煎叆</el-button>
+                    <el-button size="mini" type="primary" icon="el-icon-upload2" :loading="importing" :disabled="importing || !fileCount(moduleSel[g.key])"
+                               @click="importNow(g.key)">{{ fileCount(moduleSel[g.key]) > 1 ? '鎵归噺瀵煎叆 ' + fileCount(moduleSel[g.key]) + ' 涓枃浠�' : '绔嬪嵆瀵煎叆' }}</el-button>
                   </div>
                 </div>
               </div>
@@ -103,11 +103,15 @@
               <div v-show="sideOpen" class="side-content">
                 <div class="side-head">
                   <span>鏈湀瀵煎叆杩涘害锛坽{ period || '鏈�夋嫨鎶ヨ〃鏈�' }}锛�</span>
-                  <el-button type="text" icon="el-icon-refresh" @click="loadData">鍒锋柊</el-button>
+                  <span class="side-head-btns">
+                    <el-button type="text" icon="el-icon-refresh" @click="loadData">鍒锋柊</el-button>
+                    <el-button type="text" icon="el-icon-delete" style="color:#F56C6C" :loading="clearing"
+                               :disabled="!period || clearing || importing" @click="clearPeriodData">娓呯┖</el-button>
+                  </span>
                 </div>
                 <div v-if="!period" class="tip">璇峰厛鍦ㄤ笂鏂归�夋嫨鎶ヨ〃鏈�</div>
                 <div v-else class="overview-grid">
-                  <div class="ov-row" v-for="g in groups" :key="g.key">
+                  <div class="ov-row" v-for="g in visibleGroups" :key="g.key">
                     <div class="ov-group">{{ g.name }}</div>
                     <div class="ov-item" v-for="t in g.types" :key="t.value" :class="overviewState(t.value).cls">
                       <div class="ov-name">{{ t.label }}</div>
@@ -134,10 +138,11 @@
   investCityPassenger: '甯傚窞鍗曡〃锛堝杩愮珯鍦猴級', investCityLogistics: '甯傚窞鍗曡〃锛堢墿娴佸洯鍖猴級',
   'INVEST_瀹㈣繍绔欏満_CITY': '甯傚窞鍗曡〃锛堝杩愮珯鍦猴級', 'INVEST_鐗╂祦鍥尯_CITY': '甯傚窞鍗曡〃锛堢墿娴佸洯鍖猴級',
   cityBus: '鍩庡競鍏氦鏈堟姤', cityTaxi: '宸℃父鍑虹鏈堟姤', cityTaxiAuth: '鍑虹杞﹁繍鏀胯溅杈�',
+  wycTotal: '缃戠害杞︽�绘暟', wycOrder: '缃戠害杞﹁鍗曞強鍏ㄧ渷鎬婚噺',
   H2032: 'H203-2鏈堟姤', H2031: 'H203-1鏃呭鏈堟姤', H204: 'H204鑳借�楀鎶�', PASSENGER_AUTH: '鏃呭杩愭斂杞﹁締鏁�',
   PASSENGER_INDIVIDUAL: '涓綋瀹㈣繍閲�/鍛ㄨ浆閲�', ENERGY_AUTH: '鑳借�楄溅杈嗚繍鏀夸俊鎭�', TRANSPORT_AUTH: '杩愭斂杞﹁締',
   TRACK_MILEAGE: '杞ㄨ抗閲岀▼', SCALE_SPLIT: '瑙勪笂瑙勪笅鎷嗗垎', FREIGHT_TURNOVER: '璐ц繍閲忓懆杞噺',
-  INVEST_SYSTEM: '鎶曡祫绯荤粺瀵煎嚭', CITY_BUS: '鍩庡競鍏氦鏈堟姤', CITY_TAXI: '宸℃父鍑虹鏈堟姤', CITY_TAXI_AUTH: '鍑虹杞﹁繍鏀胯溅杈�'
+  INVEST_SYSTEM: '鎶曡祫绯荤粺瀵煎嚭', CITY_BUS: '鍩庡競鍏氦鏈堟姤', CITY_TAXI: '宸℃父鍑虹鏈堟姤', CITY_TAXI_AUTH: '鍑虹杞﹁繍鏀胯溅杈�', WYC_TOTAL: '缃戠害杞︽�绘暟', WYC_ORDER: '缃戠害杞﹁鍗曞強鍏ㄧ渷鎬婚噺'
 }
 const BATCH_TYPE_MAP = {
   H2032: 'h2032', TRANSPORT_AUTH: 'transportAuth', TRACK_MILEAGE: 'trackMileage',
@@ -145,6 +150,7 @@
   H2031: 'passengerMonthly', PASSENGER_AUTH: 'passengerAuth', PASSENGER_INDIVIDUAL: 'passengerIndividual',
   H204: 'energyMonthly', ENERGY_AUTH: 'energyAuth',
   CITY_BUS: 'cityBus', CITY_TAXI: 'cityTaxi', CITY_TAXI_AUTH: 'cityTaxiAuth',
+  WYC_TOTAL: 'wycTotal', WYC_ORDER: 'wycOrder',
   INVEST_SYSTEM: 'investmentSystem'
 }
 export default {
@@ -155,6 +161,8 @@
       importType: 'h2032',
       period: (() => { const d = new Date(); return d.getFullYear() + '-' + String(d.getMonth() + 1).padStart(2, '0') })(),
       cityDirLoading: false,
+      importing: false,
+      clearing: false,
       sideOpen: false,
       result: '',
       error: '',
@@ -186,7 +194,9 @@
           key: 'city', name: '鍩庡競瀹㈣繍', types: [
             { value: 'cityBus', label: '鍩庡競鍏叡浜ら�氭湀搴﹁繍钀ユ儏鍐�' },
             { value: 'cityTaxi', label: '宸℃父鍑虹姹借溅杩愯惀鏈嶅姟鎯呭喌鏈堟姤' },
-            { value: 'cityTaxiAuth', label: '鍑虹杞﹁繍鏀胯溅杈�' }
+            { value: 'cityTaxiAuth', label: '鍑虹杞﹁繍鏀胯溅杈�' },
+            { value: 'wycTotal', label: '缃戠害杞︽�绘暟' },
+            { value: 'wycOrder', label: '缃戠害杞﹁鍗曞強鍏ㄧ渷鎬婚噺' }
           ]
         },
         {
@@ -205,7 +215,7 @@
           ]
         },
         {
-          key: 'annual', name: '骞存姤', types: [
+          key: 'annual', hidden: true, name: '骞存姤', types: [
             { value: 'annualCompanyBasic', label: '鍗曚綅鍩烘湰鎯呭喌' },
             { value: 'annualEnterpriseFinance', label: '娉曚汉浼佷笟璐㈠姟鐘跺喌' },
             { value: 'annualPassengerEnergy', label: '鍏矾鏃呭杩愯緭鑳芥簮娑堣�楁儏鍐�' },
@@ -223,6 +233,8 @@
     }
   },
   computed: {
+    // 骞存姤鏆備笉寮�鏀撅細闅愯棌鍏ュ彛锛堟暟鎹繚鐣欙紝鍚庣瀹炵幇鍚庡彲绉婚櫎 hidden锛�
+    visibleGroups() { return this.groups.filter(g => !g.hidden) },
     failBatches() { return this.batches.filter(b => b.failRows > 0) },
     failRowsFlat() {
       const out = []
@@ -244,7 +256,7 @@
         scaleSplit: '妯℃澘_瑙勪笂瑙勪笅鎷嗗垎杩愯緭閲�.xlsx',
         freightTurnover: '妯℃澘_璐ц繍閲忓懆杞噺.xlsx',
         passengerMonthly: '鍏矾鏃呭杩愯緭鏈堝害鐢熶骇鎯呭喌.xlsx锛堝惈杩愭斂杞﹁締鏁皊heet锛�',
-        passengerAuth: '妯℃澘_鍏矾鏃呭杩愭斂杞﹁締鏁�.xlsx锛堝垪锛氫紒涓氫唬鐮�/浼佷笟鍚嶇О/杞﹁締鏁�/杞藉浣嶆暟锛�',
+        passengerAuth: '妯℃澘_鍏矾鏃呭杩愭斂杞﹁締鏁�.xls锛堟敮鎸� 浼佷笟浠g爜/浼佷笟鍚嶇О/杞﹁締鏁�/杞藉浣嶆暟 涓� 杩愭斂搴撳鍑� OWNERNAME/鎬昏溅杈嗘暟/瀹綅鏁帮級',
         passengerIndividual: '涓綋瀹㈣繍閲忔眹鎬�.xlsx锛堝垪锛氭墍灞炲湴鍖�/浼佷笟浠g爜/浼佷笟鍚嶇О/涓綋瀹㈣繍閲�/涓綋鏃呭鍛ㄨ浆閲忥級',
         energyMonthly: '閬撹矾璐ц繍杞﹁締鑳芥簮娑堣�楁儏鍐碉紙2026骞达級.xlsx锛堣兘鑰楁槑缁�+杩愭斂杞﹁締淇℃伅涓heet锛�',
         energyAuth: '妯℃澘_鑳借�楄溅杈嗚繍鏀夸俊鎭�.xlsx锛堝垪锛氳溅鐗屽彿/杞﹁締绫诲瀷/鐕冩枡绫诲瀷/鏍囪鍚ㄤ綅/鍑嗙壍寮曡川閲忥級',
@@ -255,7 +267,9 @@
         investCityLogistics: '鈥旓紙鎵归噺鎵弿鐩綍锛屾棤闇�妯℃澘锛�',
         cityBus: '瀵煎叆_鍩庡競鍏叡浜ら�氭湀搴﹁繍钀ユ儏鍐碉紙YYYY骞碝鏈堬級.xls/.xlsx锛堜紒涓氱骇鏄庣粏锛屽彇鍏叡姹界數杞﹀瓧娈碉級',
         cityTaxi: '瀵煎叆_宸℃父鍑虹姹借溅杩愯惀鏈嶅姟鎯呭喌鏈堟姤锛圷YYY骞碝鏈堬級.xls锛堝競宸炵骇17琛岋級',
-        cityTaxiAuth: '瀵煎叆妯℃澘_鍑虹杞︿俊鎭�(杩愭斂杞﹁締淇℃伅).xlsx锛堣溅杈嗙骇鏄庣粏锛岀敤浜庤繍钀ヨ溅鏁板姣旓級'
+        cityTaxiAuth: '瀵煎叆妯℃澘_鍑虹杞︿俊鎭�(杩愭斂杞﹁締淇℃伅).xlsx锛堣溅杈嗙骇鏄庣粏锛岀敤浜庤繍钀ヨ溅鏁板姣旓級',
+        wycTotal: '妯℃澘_缃戠害杞︽�绘暟.xlsx锛圓鍒�=鏈堜唤锛孊-E=鍏ㄧ渷瀹㈣繍閲�/鍩庡競鍐�/鍛ㄨ浆閲�/鍩庡競鍐呭懆杞噺锛�',
+        wycOrder: '妯℃澘_缃戠害杞﹁鍗曞強鍏ㄧ渷鎬婚噺.xlsx锛圓鍒�=鏈堜唤锛孊-E=鍏ㄧ渷pin锛孎=璁㈠崟鍚堣锛孏-W=17甯傚窞璁㈠崟锛�'
       }
       return map[this.importType] || ''
     },
@@ -267,13 +281,15 @@
         scaleSplit: '璐ц繍/妯℃澘_瑙勪笂瑙勪笅鎷嗗垎杩愯緭閲�.xlsx',
         freightTurnover: '璐ц繍/妯℃澘_璐ц繍閲忓懆杞噺.xlsx',
         passengerMonthly: '鍏矾鏃呭/妯℃澘_浜や紒缁烪203-1琛╛鍏矾鏃呭杩愯緭鏈堝害鐢熶骇鎯呭喌.xlsx',
-        passengerAuth: '鍏矾鏃呭/妯℃澘_鍏矾鏃呭杩愭斂杞﹁締鏁�.xlsx',
+        passengerAuth: '鍏矾鏃呭/妯℃澘_鍏矾鏃呭杩愭斂杞﹁締鏁�.xls',
         passengerIndividual: '鍏矾鏃呭/涓綋瀹㈣繍閲忔眹鎬�.xlsx',
         energyMonthly: '鑳借��/妯℃澘_閬撹矾璐ц繍杞﹁締鑳芥簮娑堣�楁儏鍐碉紙2026锛�.xlsx',
         energyAuth: '鑳借��/妯℃澘_鑳借�楄溅杈嗚繍鏀夸俊鎭�.xlsx',
-        cityBus: '鍩庡競瀹㈣繍/瀵煎叆妯℃澘_鍩庡競鍏氦.xlsx',
-        cityTaxi: '鍩庡競瀹㈣繍/瀵煎叆妯℃澘_宸℃父鍑虹.xlsx',
+        cityBus: '鍩庡競瀹㈣繍/鐢熸垚_鍩庡競鍏氦瀹㈣繍閲忓垎甯傚窞鏄庣粏.xlsx',
+        cityTaxi: '鍩庡競瀹㈣繍/鐢熸垚_宸℃父鍑虹瀹㈣繍閲忓垎甯傚窞鏄庣粏.xlsx',
         cityTaxiAuth: '鍩庡競瀹㈣繍/瀵煎叆妯℃澘_鍑虹杞︿俊鎭�(杩愭斂杞﹁締淇℃伅).xlsx',
+        wycTotal: '鍩庡競瀹㈣繍/妯℃澘_缃戠害杞︽�绘暟.xlsx',
+        wycOrder: '鍩庡競瀹㈣繍/妯℃澘_缃戠害杞﹁鍗曞強鍏ㄧ渷鎬婚噺.xlsx',
         investment: '鎶曡祫/杈撳叆/瀹㈣繍锛堝杩愮珯锛�/鏇存柊_7鏈堝杩愮珯鎶曡祫鏄庣粏琛紙甯傚窞姹囨�诲ぇ琛紝缁欓瀵硷級.xlsx',
         investmentLogistics: '鎶曡祫/杈撳叆/鐗╂祦锛堢墿娴佸洯鍖猴級/鏇存柊_7鏈堢墿娴佺珯鍦烘姇璧勬槑缁�.xls',
         investmentSystem: '鎶曡祫/妯℃澘_鏌ヨ缁撴灉(鎶曡祫绯荤粺-瀹㈣繍+鐗╂祦 2026.7).xls'
@@ -311,28 +327,42 @@
         investmentSystem: '/import/investmentSystem',
         cityBus: '/import/cityBus',
         cityTaxi: '/import/cityTaxi',
-        cityTaxiAuth: '/import/cityTaxiAuth'
+        cityTaxiAuth: '/import/cityTaxiAuth',
+        wycTotal: '/import/wycTotal',
+        wycOrder: '/import/wycOrder'
       }
       return map[t] || ''
     },
     onFilePicked(key, file) {
-      this.$set(this.files, this.moduleSel[key], file.raw)
-      this.$set(this.fileNames, this.moduleSel[key], file.name)
+      const type = this.moduleSel[key]
+      if (!type) return
+      if (this.importing) { this.$message.warning('姝e湪瀵煎叆涓紝璇风◢鍊欏啀閫夋嫨鏂囦欢'); return }
+      const list = (this.files[type] || []).filter(f => f.name !== file.name)
+      list.push(file.raw)
+      this.$set(this.files, type, list)
+      this.$set(this.fileNames, type, list.map(f => f.name))
       const up = this.$refs['up' + key]
       if (up && up.clearFiles) up.clearFiles()
     },
     importNow(key) {
       const type = this.moduleSel[key]
       if (!type) return
-      if (this.isCityDirType(type)) {
+      if (this.isDirOnlyType(type)) {
         this.importCityDir(type)
         this.moduleSel[key] = ''
         this.importType = ''
         return
       }
-      const file = this.files[type]
-      if (!file) { this.$message.warning('璇峰厛涓鸿绫诲瀷閫夋嫨鏂囦欢'); return }
+      const fileList = this.files[type] || []
+      if (!fileList.length) { this.$message.warning('璇峰厛涓鸿绫诲瀷閫夋嫨鏂囦欢'); return }
       if (!this.period) { this.$message.warning('璇峰厛閫夋嫨鎶ヨ〃鏈�'); return }
+      if (this.importing) { this.$message.warning('姝e湪瀵煎叆涓紝璇风◢鍊欌��'); return }
+      this.importing = true
+      if (fileList.length > 1 || type === 'investment' || type === 'investmentLogistics') {
+        this.batchImport(type, key, fileList).finally(() => { this.importing = false })
+        return
+      }
+      const file = fileList[0]
       const g = this.groups.find(x => x.key === key)
       const t = g.types.find(x => x.value === type)
       const row = {
@@ -347,6 +377,7 @@
         message: ''
       }
       this.importOne(row).then(() => {
+        this.showImportResult(row)
         if ((row.type === 'investment' || row.type === 'investmentLogistics') && row.status === 'success') {
           this.checkInvest(this.period)
         }
@@ -355,12 +386,59 @@
         this.importType = ''
         this.files[type] = null
         this.$delete(this.fileNames, type)
-      })
+      }).finally(() => { this.importing = false })
+    },
+    fileCount(type) { return (this.files[type] || []).length },
+    fileNamesText(type) {
+      const list = this.fileNames[type] || []
+      return list.length ? '宸查�� ' + list.length + ' 涓枃浠讹細' + list.join('銆�') : ''
+    },
+    batchImport(type, key, fileList) {
+      if (!this.period) { this.$message.warning('璇峰厛閫夋嫨鎶ヨ〃鏈�'); return }
+      const fd = new FormData()
+      fd.append('type', type)
+      fd.append('period', this.period)
+      fileList.forEach(f => fd.append('files', f))
+      const g = this.groups.find(x => x.key === key)
+      const label = g ? g.name : type
+      this.$message({ message: '姝e湪鎵归噺瀵煎叆 ' + fileList.length + ' 涓枃浠垛��', duration: 3000 })
+      return api.post('/import/batch', fd, { timeout: 600000 })
+        .then(res => {
+          const d = (res && res.data) || {}
+          if (!d.files) {
+            this.$message.error((res && res.message) || '鎵归噺瀵煎叆澶辫触')
+            return
+          }
+          const files = d.files || []
+          const errCount = files.filter(f => f.error || f.success === 0).length
+          const html = files.map(f =>
+            '<div style="margin:6px 0;line-height:1.6"><b>' + (f.fileName || '') + '</b>' +
+            (f.city ? '锛�' + f.city + '锛�' : '') + '锛氳В鏋� ' + (f.success || 0) + ' 鏉�' +
+            (f.error ? ' <span style="color:#F56C6C">' + f.error + '</span>' : '') + '</div>'
+          ).join('')
+          this.$alert(html, (label || type) + ' 鎵归噺瀵煎叆瀹屾垚锛氬叡 ' + d.total + ' 涓枃浠�' +
+            (d.fail > 0 ? '锛屽け璐� ' + d.fail + ' 鏉�' : '') + '锛堟垚鍔� ' + d.success + ' 鏉★級', {
+            dangerouslyUseHTMLString: true,
+            type: errCount > 0 ? 'warning' : 'success',
+            confirmButtonText: '鐭ラ亾浜�'
+          })
+          if (type === 'investment' || type === 'investmentLogistics') {
+            this.checkInvest(this.period)
+          }
+          this.afterImport()
+          this.files[type] = null
+          this.$delete(this.fileNames, type)
+        })
+        .catch(e => {
+          if (e && e._toast) return
+          this.$message.error((e.response && e.response.data && e.response.data.message) || '鎵归噺瀵煎叆澶辫触')
+        })
     },
     isCityDirType(t) { return t === 'investCityPassenger' || t === 'investCityLogistics' },
+    isDirOnlyType(t) { return t === 'investCityPassenger' || t === 'investCityLogistics' },
     importCityDir(type) {
       if (!this.period) { this.$message.warning('璇峰厛閫夋嫨鎶ヨ〃鏈�'); return }
-      const category = type === 'investCityPassenger' ? '瀹㈣繍绔欏満' : '鐗╂祦鍥尯'
+      const category = (type === 'investCityPassenger' || type === 'investment') ? '瀹㈣繍绔欏満' : '鐗╂祦鍥尯'
       const fd = new FormData()
       fd.append('period', this.period)
       fd.append('category', category)
@@ -393,6 +471,20 @@
           this.$message.error((e.response && e.response.data && e.response.data.message) || '鎵归噺瀵煎叆澶辫触')
         })
         .finally(() => { this.cityDirLoading = false })
+    },
+    showImportResult(row) {
+      if (!row || !row.status) return
+      const msg = (row.message || '').toString()
+      if (row.status === 'success') {
+        const text = (msg && msg.indexOf('瀵煎叆') !== -1) ? msg : '鏁版嵁瀵煎叆鎴愬姛'
+        this.$message({ message: text, type: 'success', duration: 5000 })
+      } else {
+        this.$alert((msg || '鏁版嵁瀵煎叆澶辫触锛岃瑙佷笅鏂瑰け璐ヨ褰�').replace(/\n/g, '<br>'), '鏁版嵁瀵煎叆澶辫触', {
+          dangerouslyUseHTMLString: true,
+          type: 'error',
+          confirmButtonText: '鐭ラ亾浜�'
+        })
+      }
     },
     importOne(row) {
       if (!this.period) { this.$message.warning('璇峰厛閫夋嫨鎶ヨ〃鏈�'); return Promise.resolve() }
@@ -432,6 +524,28 @@
         })
         .catch(e => { if (!e || !e._toast) this.$message.error('瀵煎叆璁板綍鍔犺浇澶辫触锛岃鍒锋柊閲嶈瘯') })
         .finally(() => { this.batchLoading = false })
+    },
+    clearPeriodData() {
+      if (!this.period) { this.$message.warning('璇峰厛閫夋嫨鎶ヨ〃鏈�'); return }
+      if (this.importing) { this.$message.warning('姝e湪瀵煎叆鏁版嵁锛岃绛夊緟瀵煎叆瀹屾垚鍚庡啀娓呯┖'); return }
+      if (this.clearing) return
+      this.$confirm(
+        '灏嗘竻绌� ' + this.period + ' 鎶ヨ〃鏈熷湪鏁版嵁搴撲腑鐨勫叏閮ㄥ凡瀵煎叆鏁版嵁锛屽悓鏃跺垹闄よ鎶ヨ〃鏈熺殑瀹℃牳缁撴灉涓庡鍏ユ壒娆¤褰曪紙渚夸簬鏁存湀閲嶆柊瀵煎叆锛夈�俓n姝ゆ搷浣滀笉鍙挙閿�锛屾槸鍚︾户缁紵',
+        '娓呯┖ ' + this.period + ' 鏁版嵁',
+        { type: 'warning', confirmButtonText: '纭娓呯┖', cancelButtonText: '鍙栨秷' }
+      ).then(() => {
+        this.clearing = true
+        api.post('/import/clear', null, { params: { period: this.period }, timeout: 600000 })
+          .then(() => {
+            this.$message.success('宸叉竻绌� ' + this.period + ' 鎶ヨ〃鏈熺殑鏁版嵁锛屽彲浠ラ噸鏂板鍏�')
+            this.loadData()
+          })
+          .catch(e => {
+            if (e && e._toast) return
+            this.$message.error((e.response && e.response.data && e.response.data.message) || '娓呯┖澶辫触锛岃绋嶅悗閲嶈瘯')
+          })
+          .finally(() => { this.clearing = false })
+      }).catch(() => {})
     },
     batchKeyToType(k) {
       if (BATCH_TYPE_MAP[k]) return BATCH_TYPE_MAP[k]
@@ -504,6 +618,8 @@
 .import-side.side-open .side-toggle-text { writing-mode: horizontal-tb; letter-spacing: normal; margin-top: 0; }
 .side-content { padding: 8px 12px 12px; max-height: calc(100vh - 220px); overflow-y: auto; overscroll-behavior: contain; }
 .side-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; font-weight: 600; margin-bottom: 6px; }
+.side-head-btns { display: flex; align-items: center; gap: 2px; flex-shrink: 0; }
+.side-head-btns .el-button + .el-button { margin-left: 0; }
 .import-side .overview-grid { display: block; }
 .import-side .ov-row { display: block; margin-bottom: 6px; }
 .import-side .ov-group { width: auto; }

--
Gitblit v1.9.1