From 4e8766d35771bc71c858583b768600a2169a46c0 Mon Sep 17 00:00:00 2001
From: zhizhijie <zhizhijie@users.noreply.gitee.com>
Date: 星期二, 22 九月 2026 10:49:11 +0800
Subject: [PATCH] feat(数据查看): 隐藏报表期/所属地区+冻结收窄为市州与企业名称、网约车按市州竖排、市州月报显示项目名称、列名补单位、数值千分位与同比按百分比显示

---
 traffic-audit-web/src/views/DataImport.vue |  138 ++++++++++++++++++++++++++++++++++++++-------
 1 files changed, 116 insertions(+), 22 deletions(-)

diff --git a/traffic-audit-web/src/views/DataImport.vue b/traffic-audit-web/src/views/DataImport.vue
index d9a6e8c..deb64c6 100644
--- a/traffic-audit-web/src/views/DataImport.vue
+++ b/traffic-audit-web/src/views/DataImport.vue
@@ -37,7 +37,7 @@
                                 <div class="module-sel-row">
                   <el-select v-model="moduleSel[g.key]" placeholder="閫夋嫨鏁版嵁绫诲瀷" style="width:260px"
                              @change="onModuleChange(g.key)" clearable>
-                    <el-option v-for="t in g.types" :key="t.value" :label="t.label" :value="t.value"></el-option>
+                    <el-option v-for="t in typeList(g)" :key="t.value" :label="t.label" :value="t.value"></el-option>
                   </el-select>
                   <div v-if="importType" class="tpl-box">
                     <span class="tpl-name">妯℃澘锛歿{ templateName }}</span>
@@ -114,7 +114,7 @@
                 <div v-else class="overview-grid">
                   <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-item" v-for="t in typeList(g)" :key="t.value" :class="overviewState(t.value).cls">
                       <div class="ov-name">{{ t.label }}</div>
                       <div class="ov-status">{{ overviewState(t.value).text }}</div>
                     </div>
@@ -134,7 +134,7 @@
             <div v-if="clearScope === 'types'" class="clear-types">
               <el-select v-model="clearTypes" multiple filterable collapse-tags placeholder="閫夋嫨瑕佹竻绌虹殑鏁版嵁绫诲瀷" style="width:100%">
                 <el-option-group v-for="g in clearableGroups()" :key="g.key" :label="g.name">
-                  <el-option v-for="t in g.types" :key="t.value" :value="t.value"
+                  <el-option v-for="t in typeList(g)" :key="t.value" :value="t.value"
                              :label="t.label + '锛�' + overviewState(t.value).text + '锛�'"></el-option>
                 </el-option-group>
               </el-select>
@@ -148,6 +148,19 @@
             <el-button @click="clearDialog = false">鍙栨秷</el-button>
             <el-button type="danger" :loading="clearing"
                        :disabled="clearScope === 'types' && !clearTypes.length" @click="confirmClear">纭娓呯┖</el-button>
+          </span>
+        </el-dialog>
+        <!-- 瀵煎叆缁撴灉寮圭獥锛氬彲鎷栧姩銆佸唴瀹瑰尯婊氬姩銆佸簳閮ㄦ寜閽浐瀹氬彲瑙侊紙鍘� $alert 鍐呭闀挎椂搴曢儴鎸夐挳浼氳鎸ゅ嚭灞忓箷锛� -->
+        <el-dialog :title="resultDialog.title" :visible.sync="resultDialog.visible" width="860px" top="5vh"
+                   append-to-body :close-on-click-modal="false" custom-class="import-result-dialog" v-dialog-drag>
+          <div v-if="resultDialog.level" class="ird-level" :class="'ird-' + resultDialog.level">
+            {{ resultDialog.level === 'error' ? '瀵煎叆澶辫触' : (resultDialog.level === 'warning' ? '瀛樺湪闇�瑕佺‘璁ょ殑鍐呭' : '瀵煎叆鎴愬姛') }}
+          </div>
+          <div class="ird-body" v-html="resultDialog.html"></div>
+          <span slot="footer">
+            <el-button v-if="resultDialog.rows.length" size="small" icon="el-icon-download"
+                       @click="exportResultDialog">瀵煎嚭鏄庣粏</el-button>
+            <el-button type="primary" @click="resultDialog.visible = false">纭</el-button>
           </span>
         </el-dialog>
       </el-main>
@@ -193,6 +206,7 @@
       clearDialog: false,
       clearScope: 'all',
       clearTypes: [],
+      resultDialog: { visible: false, title: '', html: '', rows: [], columns: [], fileName: '', level: '' },
       sideOpen: false,
       result: '',
       error: '',
@@ -226,7 +240,7 @@
             { value: 'cityTaxi', label: '宸℃父鍑虹姹借溅杩愯惀鏈嶅姟鎯呭喌鏈堟姤' },
             { value: 'cityTaxiAuth', label: '鍑虹杞﹁繍鏀胯溅杈�' },
             { value: 'wycTotal', label: '缃戠害杞︽�绘暟' },
-            { value: 'wycOrder', label: '缃戠害杞﹁鍗曞強鍏ㄧ渷鎬婚噺' },
+            { value: 'wycOrder', label: '缃戠害杞﹁鍗曞強鍏ㄧ渷鎬婚噺', hidden: true },
             { value: 'wycOrderReport', label: '缃戠害杞﹁鍗曟姤鍛�' }
           ]
         },
@@ -334,7 +348,20 @@
     typeLabel(t) { return TYPE_LABELS[t] || t },
     downloadTemplate() {
       if (!this.templateUrl) return
-      window.open('/api/templates/' + this.templateUrl)
+      const name = this.templateUrl.split('/').pop()
+      api.get('/templates/' + this.templateUrl, { responseType: 'blob' })
+        .then(res => {
+          const blob = res instanceof Blob ? res : new Blob([res])
+          const url = window.URL.createObjectURL(blob)
+          const a = document.createElement('a')
+          a.href = url
+          a.download = name
+          document.body.appendChild(a)
+          a.click()
+          window.URL.revokeObjectURL(url)
+          document.body.removeChild(a)
+        })
+        .catch(() => this.$message.error('妯℃澘涓嬭浇澶辫触锛岃绋嶅悗閲嶈瘯'))
     },
     onModuleChange(key) {
       this.importType = this.moduleSel[key] || ''
@@ -450,11 +477,15 @@
             (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: '鐭ラ亾浜�'
+          this.showResultDialog({
+            title: (label || type) + ' 鎵归噺瀵煎叆瀹屾垚锛氬叡 ' + d.total + ' 涓枃浠�' +
+              (d.fail > 0 ? '锛屽け璐� ' + d.fail + ' 鏉�' : '') + '锛堟垚鍔� ' + d.success + ' 鏉★級',
+            html: html,
+            level: errCount > 0 ? 'warning' : 'success',
+            columns: [{ key: 'fileName', label: '鏂囦欢鍚�' }, { key: 'city', label: '甯傚窞' },
+                      { key: 'success', label: '瑙f瀽鏉℃暟' }, { key: 'error', label: '閿欒' }],
+            rows: files.map(f => ({ fileName: f.fileName || '', city: f.city || '', success: f.success || 0, error: f.error || '' })),
+            fileName: (label || '鎵归噺瀵煎叆') + '_' + this.period + '_瀵煎叆缁撴灉'
           })
           if (type === 'investment' || type === 'investmentLogistics') {
             this.checkInvest(this.period)
@@ -492,12 +523,16 @@
             (f.city ? '锛�' + f.city + '锛�' : '') + '锛氳В鏋� ' + (f.success || 0) + ' 涓」鐩�' +
             (f.error ? ' <span style="color:#F56C6C">' + f.error + '</span>' : '') + '</div>'
           ).join('')
-          this.$alert(html, '甯傚窞鍗曡〃鎵归噺瀵煎叆瀹屾垚锛氬叡 ' + d.total + ' 鏉★紝鎴愬姛 ' + d.success + ' 鏉�' +
-            (d.fail > 0 ? '锛屽け璐� ' + d.fail + ' 鏉�' : '') +
-            '锛堝崄浜斾簲椤圭洰鍖归厤 ' + d.fiveYearMatched + '/' + d.fiveYearTemplateProjects + '锛�', {
-            dangerouslyUseHTMLString: true,
-            type: errCount > 0 ? 'warning' : 'success',
-            confirmButtonText: '鐭ラ亾浜�'
+          this.showResultDialog({
+            title: '甯傚窞鍗曡〃鎵归噺瀵煎叆瀹屾垚锛氬叡 ' + d.total + ' 鏉★紝鎴愬姛 ' + d.success + ' 鏉�' +
+              (d.fail > 0 ? '锛屽け璐� ' + d.fail + ' 鏉�' : '') +
+              '锛堝崄浜斾簲椤圭洰鍖归厤 ' + d.fiveYearMatched + '/' + d.fiveYearTemplateProjects + '锛�',
+            html: html,
+            level: errCount > 0 ? 'warning' : 'success',
+            columns: [{ key: 'fileName', label: '鏂囦欢鍚�' }, { key: 'city', label: '甯傚窞' },
+                      { key: 'success', label: '瑙f瀽椤圭洰鏁�' }, { key: 'error', label: '閿欒' }],
+            rows: files.map(f => ({ fileName: f.fileName || '', city: f.city || '', success: f.success || 0, error: f.error || '' })),
+            fileName: '甯傚窞鍗曡〃瀵煎叆缁撴灉_' + this.period
           })
           this.afterImport()
         })
@@ -514,10 +549,10 @@
         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: '鐭ラ亾浜�'
+        this.showResultDialog({
+          title: '鏁版嵁瀵煎叆澶辫触',
+          html: (msg || '鏁版嵁瀵煎叆澶辫触锛岃瑙佷笅鏂瑰け璐ヨ褰�').replace(/\n/g, '<br>'),
+          level: 'error'
         })
       }
     },
@@ -570,6 +605,10 @@
     },
     clearableGroups() {
       return this.groups.filter(g => !g.hidden)
+    },
+    // 鍗曚釜鏁版嵁绫诲瀷涔熷彲闅愯棌锛坔idden: true锛夛細鍏ュ彛銆佹瑙堛�佹竻绌轰笅鎷夌粺涓�杩囨护
+    typeList(g) {
+      return (g && g.types ? g.types : []).filter(t => !t.hidden)
     },
     confirmClear() {
       if (!this.period) { this.$message.warning('璇峰厛閫夋嫨鎶ヨ〃鏈�'); return }
@@ -629,14 +668,55 @@
                 '<br/>瀹為檯锛�' + (x.actualValue || '') + '銆�鍩哄噯锛�' + (x.thresholdValue || '') +
                 '<br/>宸紓锛�' + (x.deviation || '') + '</div>'
             }).join('')
-            this.$alert(html, '鎶曡祫鏁版嵁涓庢姇璧勭郴缁熶笉涓�鑷达紙' + pending.length + ' 鏉★級', {
-              dangerouslyUseHTMLString: true, type: 'warning', confirmButtonText: '鐭ラ亾浜�'
+            this.showResultDialog({
+              title: '鎶曡祫鏁版嵁涓庢姇璧勭郴缁熶笉涓�鑷达紙' + pending.length + ' 鏉★級',
+              html: html,
+              level: 'warning',
+              columns: [{ key: 'rule', label: '瑙勫垯' }, { key: 'name', label: '椤圭洰/浼佷笟' },
+                        { key: 'actual', label: '瀹為檯鍊�' }, { key: 'base', label: '鍩哄噯鍊�' }, { key: 'dev', label: '宸紓' }],
+              rows: pending.map(x => ({
+                rule: x.ruleName || '',
+                name: x.enterpriseName || x.actualValue || '',
+                actual: x.actualValue || '',
+                base: x.thresholdValue || '',
+                dev: x.deviation || ''
+              })),
+              fileName: '鎶曡祫鏁版嵁涓庢姇璧勭郴缁熶笉涓�鑷確' + this.period
             })
           } else {
             this.$message.success('宸蹭笌鎶曡祫绯荤粺鏍稿锛屾湰鏈堟暟鎹竴鑷�')
           }
         })
         .catch(() => {})
+    },
+  showResultDialog(opts) {
+      const o = opts || {}
+      this.resultDialog = {
+        visible: true,
+        title: o.title || '瀵煎叆缁撴灉',
+        html: o.html || '',
+        level: o.level || '',
+        rows: o.rows || [],
+        columns: o.columns || [],
+        fileName: o.fileName || ('瀵煎叆缁撴灉_' + (this.period || ''))
+      }
+    },
+    exportResultDialog() {
+      const cols = this.resultDialog.columns || []
+      const rows = this.resultDialog.rows || []
+      if (!cols.length || !rows.length) { this.$message.warning('娌℃湁鍙鍑虹殑鏄庣粏'); return }
+      const esc = v => '"' + String(v === null || v === undefined ? '' : v).replace(/"/g, '""') + '"'
+      const lines = [cols.map(c => esc(c.label)).join(',')]
+      rows.forEach(r => lines.push(cols.map(c => esc(r[c.key])).join(',')))
+      const blob = new Blob(['\ufeff' + lines.join('\r\n')], { type: 'text/csv;charset=utf-8' })
+      const url = URL.createObjectURL(blob)
+      const a = document.createElement('a')
+      a.href = url
+      a.download = this.resultDialog.fileName + '.csv'
+      document.body.appendChild(a)
+      a.click()
+      document.body.removeChild(a)
+      URL.revokeObjectURL(url)
     },
   parseFailDetails(row) {
     if (!row || !row.errorDetail) return []
@@ -706,3 +786,17 @@
 .module-tabs-head { margin-bottom: 10px; }
 .module-tab-pane { min-height: 120px; }
 </style>
+
+<style>
+/* 瀵煎叆缁撴灉寮圭獥锛歟l-dialog 榛樿 append-to-body锛孌OM 鎸傚湪 body 涓婏紝scoped 閫夋嫨鍣ㄥ尮閰嶄笉鍒帮紝蹇呴』鍐欏叏灞� */
+.import-result-dialog { border-radius: 10px; overflow: hidden; }
+.import-result-dialog .el-dialog__header { cursor: move; user-select: none; -webkit-user-select: none; }
+.import-result-dialog .el-dialog__headerbtn { cursor: pointer; }
+.import-result-dialog .el-dialog__body { max-height: calc(100vh - 210px); overflow-y: auto; overscroll-behavior: contain; padding: 12px 20px; }
+.import-result-dialog .el-dialog__footer { border-top: 1px solid #EBEEF5; padding: 10px 20px; }
+.import-result-dialog .ird-body { line-height: 1.7; font-size: 13px; color: #303133; }
+.import-result-dialog .ird-level { margin-bottom: 8px; font-weight: 600; }
+.import-result-dialog .ird-success { color: #67C23A; }
+.import-result-dialog .ird-warning { color: #E6A23C; }
+.import-result-dialog .ird-error { color: #F56C6C; }
+</style>

--
Gitblit v1.9.1