Selaa lähdekoodia

feat(rating): 为企业评级页面添加二维码下载功能

- 新增 VITE_APP_SITE_URL 环境变量配置
- 实现单个企业二维码预览和下载功能
- 添加批量下载选中企业二维码功能
- 集成 qr-code-styling 库生成自定义二维码
- 优化企业评级列表页面布局和交互体验
- 添加表格多选和批量操作支持
- 升级项目依赖包版本
nahida 4 kuukautta sitten
vanhempi
commit
7e59385293
7 muutettua tiedostoa jossa 535 lisäystä ja 140 poistoa
  1. 2 0
      .env.development
  2. 2 0
      .env.production
  3. 1 0
      package.json
  4. 208 96
      pnpm-lock.yaml
  5. 1 1
      src/App.vue
  6. 312 39
      src/views/CEEnterprise/CERating/index.vue
  7. 9 4
      src/views/visualization/index.vue

+ 2 - 0
.env.development

@@ -9,4 +9,6 @@ VITE_APP_BASE_API = '/dev-api'
 
 VITE_APP_BASE_URL = 'https://localhost:20002'
 
+VITE_APP_SITE_URL = 'http://192.168.110.93:9999/qyxyfjfl/#'
+
 VITE_AUTH_KEY = '性与暴力'

+ 2 - 0
.env.production

@@ -8,6 +8,8 @@ VITE_APP_ENV = 'production'
 VITE_APP_BASE_API = 'https://gxq.huaihua.gov.cn/qyxyfjflserver'
 VITE_APP_BASE_URL = 'https://gxq.huaihua.gov.cn/qyxyfjflserver'
 
+VITE_APP_SITE_URL = 'https://gxq.huaihua.gov.cn/qyxyfjfl/#'
+
 # 是否在打包时开启压缩,支持 gzip 和 brotli
 VITE_BUILD_COMPRESS = gzip
 VITE_AUTH_KEY = '爱与和平'

+ 1 - 0
package.json

@@ -31,6 +31,7 @@
     "jsencrypt": "3.2.1",
     "mapv-three": "^1.0.18",
     "nprogress": "0.2.0",
+    "qr-code-styling": "^1.9.2",
     "vue": "3.2.26",
     "vue-cropper": "1.0.2",
     "vue-router": "4.0.12",

+ 208 - 96
pnpm-lock.yaml

@@ -56,6 +56,9 @@ importers:
       nprogress:
         specifier: 0.2.0
         version: 0.2.0
+      qr-code-styling:
+        specifier: ^1.9.2
+        version: 1.9.2
       vue:
         specifier: 3.2.26
         version: 3.2.26
@@ -80,7 +83,7 @@ importers:
     devDependencies:
       '@vitejs/plugin-vue':
         specifier: 1.9.4
-        version: 1.9.4(vite@2.6.14(sass@1.45.0))
+        version: 1.9.4(vite@2.9.18(sass@1.45.0))
       '@vue/compiler-sfc':
         specifier: 3.2.22
         version: 3.2.22
@@ -89,19 +92,19 @@ importers:
         version: 1.45.0
       unplugin-auto-import:
         specifier: 0.5.3
-        version: 0.5.3(@vueuse/core@10.9.0(vue@3.2.26))(rollup@2.79.1)(vite@2.6.14(sass@1.45.0))(webpack@1.15.0)
+        version: 0.5.3(@vueuse/core@10.9.0(vue@3.2.26))(rollup@2.79.1)(vite@2.9.18(sass@1.45.0))(webpack@1.15.0)
       vite:
         specifier: ^2.6.14
-        version: 2.6.14(sass@1.45.0)
+        version: 2.9.18(sass@1.45.0)
       vite-plugin-compression:
         specifier: 0.3.6
-        version: 0.3.6(vite@2.6.14(sass@1.45.0))
+        version: 0.3.6(vite@2.9.18(sass@1.45.0))
       vite-plugin-svg-icons:
         specifier: 1.0.5
-        version: 1.0.5(vite@2.6.14(sass@1.45.0))
+        version: 1.0.5(vite@2.9.18(sass@1.45.0))
       vite-plugin-vue-setup-extend:
         specifier: 0.1.0
-        version: 0.1.0(vite@2.6.14(sass@1.45.0))
+        version: 0.1.0(vite@2.9.18(sass@1.45.0))
 
 packages:
 
@@ -138,6 +141,12 @@ packages:
     peerDependencies:
       vue: ^3.2.0
 
+  '@esbuild/linux-loong64@0.14.54':
+    resolution: {integrity: sha512-bZBrLAIX1kpWelV0XemxBZllyRmM6vgFQQG2GdNb+r3Fkp0FOh1NJSvekXDs7jq70k4euu1cryLMfU+mTXlEpw==}
+    engines: {node: '>=12'}
+    cpu: [loong64]
+    os: [linux]
+
   '@popperjs/core@2.11.8':
     resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==}
 
@@ -1008,93 +1017,129 @@ packages:
     resolution: {integrity: sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==}
     engines: {node: '>=0.12'}
 
-  esbuild-android-arm64@0.13.15:
-    resolution: {integrity: sha512-m602nft/XXeO8YQPUDVoHfjyRVPdPgjyyXOxZ44MK/agewFFkPa8tUo6lAzSWh5Ui5PB4KR9UIFTSBKh/RrCmg==}
+  esbuild-android-64@0.14.54:
+    resolution: {integrity: sha512-Tz2++Aqqz0rJ7kYBfz+iqyE3QMycD4vk7LBRyWaAVFgFtQ/O8EJOnVmTOiDWYZ/uYzB4kvP+bqejYdVKzE5lAQ==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [android]
+
+  esbuild-android-arm64@0.14.54:
+    resolution: {integrity: sha512-F9E+/QDi9sSkLaClO8SOV6etqPd+5DgJje1F9lOWoNncDdOBL2YF59IhsWATSt0TLZbYCf3pNlTHvVV5VfHdvg==}
+    engines: {node: '>=12'}
     cpu: [arm64]
     os: [android]
 
-  esbuild-darwin-64@0.13.15:
-    resolution: {integrity: sha512-ihOQRGs2yyp7t5bArCwnvn2Atr6X4axqPpEdCFPVp7iUj4cVSdisgvEKdNR7yH3JDjW6aQDw40iQFoTqejqxvQ==}
+  esbuild-darwin-64@0.14.54:
+    resolution: {integrity: sha512-jtdKWV3nBviOd5v4hOpkVmpxsBy90CGzebpbO9beiqUYVMBtSc0AL9zGftFuBon7PNDcdvNCEuQqw2x0wP9yug==}
+    engines: {node: '>=12'}
     cpu: [x64]
     os: [darwin]
 
-  esbuild-darwin-arm64@0.13.15:
-    resolution: {integrity: sha512-i1FZssTVxUqNlJ6cBTj5YQj4imWy3m49RZRnHhLpefFIh0To05ow9DTrXROTE1urGTQCloFUXTX8QfGJy1P8dQ==}
+  esbuild-darwin-arm64@0.14.54:
+    resolution: {integrity: sha512-OPafJHD2oUPyvJMrsCvDGkRrVCar5aVyHfWGQzY1dWnzErjrDuSETxwA2HSsyg2jORLY8yBfzc1MIpUkXlctmw==}
+    engines: {node: '>=12'}
     cpu: [arm64]
     os: [darwin]
 
-  esbuild-freebsd-64@0.13.15:
-    resolution: {integrity: sha512-G3dLBXUI6lC6Z09/x+WtXBXbOYQZ0E8TDBqvn7aMaOCzryJs8LyVXKY4CPnHFXZAbSwkCbqiPuSQ1+HhrNk7EA==}
+  esbuild-freebsd-64@0.14.54:
+    resolution: {integrity: sha512-OKwd4gmwHqOTp4mOGZKe/XUlbDJ4Q9TjX0hMPIDBUWWu/kwhBAudJdBoxnjNf9ocIB6GN6CPowYpR/hRCbSYAg==}
+    engines: {node: '>=12'}
     cpu: [x64]
     os: [freebsd]
 
-  esbuild-freebsd-arm64@0.13.15:
-    resolution: {integrity: sha512-KJx0fzEDf1uhNOZQStV4ujg30WlnwqUASaGSFPhznLM/bbheu9HhqZ6mJJZM32lkyfGJikw0jg7v3S0oAvtvQQ==}
+  esbuild-freebsd-arm64@0.14.54:
+    resolution: {integrity: sha512-sFwueGr7OvIFiQT6WeG0jRLjkjdqWWSrfbVwZp8iMP+8UHEHRBvlaxL6IuKNDwAozNUmbb8nIMXa7oAOARGs1Q==}
+    engines: {node: '>=12'}
     cpu: [arm64]
     os: [freebsd]
 
-  esbuild-linux-32@0.13.15:
-    resolution: {integrity: sha512-ZvTBPk0YWCLMCXiFmD5EUtB30zIPvC5Itxz0mdTu/xZBbbHJftQgLWY49wEPSn2T/TxahYCRDWun5smRa0Tu+g==}
+  esbuild-linux-32@0.14.54:
+    resolution: {integrity: sha512-1ZuY+JDI//WmklKlBgJnglpUL1owm2OX+8E1syCD6UAxcMM/XoWd76OHSjl/0MR0LisSAXDqgjT3uJqT67O3qw==}
+    engines: {node: '>=12'}
     cpu: [ia32]
     os: [linux]
 
-  esbuild-linux-64@0.13.15:
-    resolution: {integrity: sha512-eCKzkNSLywNeQTRBxJRQ0jxRCl2YWdMB3+PkWFo2BBQYC5mISLIVIjThNtn6HUNqua1pnvgP5xX0nHbZbPj5oA==}
+  esbuild-linux-64@0.14.54:
+    resolution: {integrity: sha512-EgjAgH5HwTbtNsTqQOXWApBaPVdDn7XcK+/PtJwZLT1UmpLoznPd8c5CxqsH2dQK3j05YsB3L17T8vE7cp4cCg==}
+    engines: {node: '>=12'}
     cpu: [x64]
     os: [linux]
 
-  esbuild-linux-arm64@0.13.15:
-    resolution: {integrity: sha512-bYpuUlN6qYU9slzr/ltyLTR9YTBS7qUDymO8SV7kjeNext61OdmqFAzuVZom+OLW1HPHseBfJ/JfdSlx8oTUoA==}
+  esbuild-linux-arm64@0.14.54:
+    resolution: {integrity: sha512-WL71L+0Rwv+Gv/HTmxTEmpv0UgmxYa5ftZILVi2QmZBgX3q7+tDeOQNqGtdXSdsL8TQi1vIaVFHUPDe0O0kdig==}
+    engines: {node: '>=12'}
     cpu: [arm64]
     os: [linux]
 
-  esbuild-linux-arm@0.13.15:
-    resolution: {integrity: sha512-wUHttDi/ol0tD8ZgUMDH8Ef7IbDX+/UsWJOXaAyTdkT7Yy9ZBqPg8bgB/Dn3CZ9SBpNieozrPRHm0BGww7W/jA==}
+  esbuild-linux-arm@0.14.54:
+    resolution: {integrity: sha512-qqz/SjemQhVMTnvcLGoLOdFpCYbz4v4fUo+TfsWG+1aOu70/80RV6bgNpR2JCrppV2moUQkww+6bWxXRL9YMGw==}
+    engines: {node: '>=12'}
     cpu: [arm]
     os: [linux]
 
-  esbuild-linux-mips64le@0.13.15:
-    resolution: {integrity: sha512-KlVjIG828uFPyJkO/8gKwy9RbXhCEUeFsCGOJBepUlpa7G8/SeZgncUEz/tOOUJTcWMTmFMtdd3GElGyAtbSWg==}
+  esbuild-linux-mips64le@0.14.54:
+    resolution: {integrity: sha512-qTHGQB8D1etd0u1+sB6p0ikLKRVuCWhYQhAHRPkO+OF3I/iSlTKNNS0Lh2Oc0g0UFGguaFZZiPJdJey3AGpAlw==}
+    engines: {node: '>=12'}
     cpu: [mips64el]
     os: [linux]
 
-  esbuild-linux-ppc64le@0.13.15:
-    resolution: {integrity: sha512-h6gYF+OsaqEuBjeesTBtUPw0bmiDu7eAeuc2OEH9S6mV9/jPhPdhOWzdeshb0BskRZxPhxPOjqZ+/OqLcxQwEQ==}
+  esbuild-linux-ppc64le@0.14.54:
+    resolution: {integrity: sha512-j3OMlzHiqwZBDPRCDFKcx595XVfOfOnv68Ax3U4UKZ3MTYQB5Yz3X1mn5GnodEVYzhtZgxEBidLWeIs8FDSfrQ==}
+    engines: {node: '>=12'}
     cpu: [ppc64]
     os: [linux]
 
-  esbuild-netbsd-64@0.13.15:
-    resolution: {integrity: sha512-3+yE9emwoevLMyvu+iR3rsa+Xwhie7ZEHMGDQ6dkqP/ndFzRHkobHUKTe+NCApSqG5ce2z4rFu+NX/UHnxlh3w==}
+  esbuild-linux-riscv64@0.14.54:
+    resolution: {integrity: sha512-y7Vt7Wl9dkOGZjxQZnDAqqn+XOqFD7IMWiewY5SPlNlzMX39ocPQlOaoxvT4FllA5viyV26/QzHtvTjVNOxHZg==}
+    engines: {node: '>=12'}
+    cpu: [riscv64]
+    os: [linux]
+
+  esbuild-linux-s390x@0.14.54:
+    resolution: {integrity: sha512-zaHpW9dziAsi7lRcyV4r8dhfG1qBidQWUXweUjnw+lliChJqQr+6XD71K41oEIC3Mx1KStovEmlzm+MkGZHnHA==}
+    engines: {node: '>=12'}
+    cpu: [s390x]
+    os: [linux]
+
+  esbuild-netbsd-64@0.14.54:
+    resolution: {integrity: sha512-PR01lmIMnfJTgeU9VJTDY9ZerDWVFIUzAtJuDHwwceppW7cQWjBBqP48NdeRtoP04/AtO9a7w3viI+PIDr6d+w==}
+    engines: {node: '>=12'}
     cpu: [x64]
     os: [netbsd]
 
-  esbuild-openbsd-64@0.13.15:
-    resolution: {integrity: sha512-wTfvtwYJYAFL1fSs8yHIdf5GEE4NkbtbXtjLWjM3Cw8mmQKqsg8kTiqJ9NJQe5NX/5Qlo7Xd9r1yKMMkHllp5g==}
+  esbuild-openbsd-64@0.14.54:
+    resolution: {integrity: sha512-Qyk7ikT2o7Wu76UsvvDS5q0amJvmRzDyVlL0qf5VLsLchjCa1+IAvd8kTBgUxD7VBUUVgItLkk609ZHUc1oCaw==}
+    engines: {node: '>=12'}
     cpu: [x64]
     os: [openbsd]
 
-  esbuild-sunos-64@0.13.15:
-    resolution: {integrity: sha512-lbivT9Bx3t1iWWrSnGyBP9ODriEvWDRiweAs69vI+miJoeKwHWOComSRukttbuzjZ8r1q0mQJ8Z7yUsDJ3hKdw==}
+  esbuild-sunos-64@0.14.54:
+    resolution: {integrity: sha512-28GZ24KmMSeKi5ueWzMcco6EBHStL3B6ubM7M51RmPwXQGLe0teBGJocmWhgwccA1GeFXqxzILIxXpHbl9Q/Kw==}
+    engines: {node: '>=12'}
     cpu: [x64]
     os: [sunos]
 
-  esbuild-windows-32@0.13.15:
-    resolution: {integrity: sha512-fDMEf2g3SsJ599MBr50cY5ve5lP1wyVwTe6aLJsM01KtxyKkB4UT+fc5MXQFn3RLrAIAZOG+tHC+yXObpSn7Nw==}
+  esbuild-windows-32@0.14.54:
+    resolution: {integrity: sha512-T+rdZW19ql9MjS7pixmZYVObd9G7kcaZo+sETqNH4RCkuuYSuv9AGHUVnPoP9hhuE1WM1ZimHz1CIBHBboLU7w==}
+    engines: {node: '>=12'}
     cpu: [ia32]
     os: [win32]
 
-  esbuild-windows-64@0.13.15:
-    resolution: {integrity: sha512-9aMsPRGDWCd3bGjUIKG/ZOJPKsiztlxl/Q3C1XDswO6eNX/Jtwu4M+jb6YDH9hRSUflQWX0XKAfWzgy5Wk54JQ==}
+  esbuild-windows-64@0.14.54:
+    resolution: {integrity: sha512-AoHTRBUuYwXtZhjXZbA1pGfTo8cJo3vZIcWGLiUcTNgHpJJMC1rVA44ZereBHMJtotyN71S8Qw0npiCIkW96cQ==}
+    engines: {node: '>=12'}
     cpu: [x64]
     os: [win32]
 
-  esbuild-windows-arm64@0.13.15:
-    resolution: {integrity: sha512-zzvyCVVpbwQQATaf3IG8mu1IwGEiDxKkYUdA4FpoCHi1KtPa13jeScYDjlW0Qh+ebWzpKfR2ZwvqAQkSWNcKjA==}
+  esbuild-windows-arm64@0.14.54:
+    resolution: {integrity: sha512-M0kuUvXhot1zOISQGXwWn6YtS+Y/1RT9WrVIOywZnJHo3jCDyewAc79aKNQWFCQm+xNHVTq9h8dZKvygoXQQRg==}
+    engines: {node: '>=12'}
     cpu: [arm64]
     os: [win32]
 
-  esbuild@0.13.15:
-    resolution: {integrity: sha512-raCxt02HBKv8RJxE8vkTSCXGIyKHdEdGfUmiYb8wnabnaEmHzyW7DCHb5tEN0xU8ryqg5xw54mcwnYkC4x3AIw==}
+  esbuild@0.14.54:
+    resolution: {integrity: sha512-Cy9llcy8DvET5uznocPyqL3BFRrFXSVqbgpMJ9Wz8oVjZlh/zUSNbPRbov0VX7VxN2JH1Oa0uNxZ7eLRb62pJA==}
+    engines: {node: '>=12'}
     hasBin: true
 
   escape-string-regexp@1.0.5:
@@ -1964,6 +2009,11 @@ packages:
   nan@2.22.0:
     resolution: {integrity: sha512-nbajikzWTMwsW+eSsNm3QwlOs7het9gGJU5dDZzRTQGk03vyBOauxgI4VakDzE0PtsGTmXPsXTbbjVhRwR5mpw==}
 
+  nanoid@3.3.11:
+    resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
+    engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+    hasBin: true
+
   nanoid@3.3.7:
     resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==}
     engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
@@ -2103,6 +2153,9 @@ packages:
   picocolors@1.0.0:
     resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
 
+  picocolors@1.1.1:
+    resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
+
   picomatch@2.3.1:
     resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
     engines: {node: '>=8.6'}
@@ -2128,6 +2181,10 @@ packages:
     resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==}
     engines: {node: ^10 || ^12 || >=14}
 
+  postcss@8.5.6:
+    resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==}
+    engines: {node: ^10 || ^12 || >=14}
+
   posthtml-parser@0.2.1:
     resolution: {integrity: sha512-nPC53YMqJnc/+1x4fRYFfm81KV2V+G9NZY+hTohpYg64Ay7NemWWcV4UWuy/SgMupqQ3kJ88M/iRfZmSnxT+pw==}
 
@@ -2185,6 +2242,13 @@ packages:
 
       (For a CapTP with native promises, see @endo/eventual-send and @endo/captp)
 
+  qr-code-styling@1.9.2:
+    resolution: {integrity: sha512-RgJaZJ1/RrXJ6N0j7a+pdw3zMBmzZU4VN2dtAZf8ZggCfRB5stEQ3IoDNGaNhYY3nnZKYlYSLl5YkfWN5dPutg==}
+    engines: {node: '>=18.18.0'}
+
+  qrcode-generator@1.5.2:
+    resolution: {integrity: sha512-pItrW0Z9HnDBnFmgiNrY1uxRdri32Uh9EjNYLPVC2zZ3ZRIIEqBoDgm4DkvDwNNDHTK7FNkmr8zAa77BYc9xNw==}
+
   qs@6.13.0:
     resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==}
     engines: {node: '>=0.6'}
@@ -2310,6 +2374,11 @@ packages:
   ripemd160@0.2.0:
     resolution: {integrity: sha512-JJsJ74Mw4sUDDisXGDnNNyN9xWmt5HcH6Kwvb/0m/IvTKjnLAtZfzeoLdpxk44AxQZki54oCCd+Kt0nPQ2AF2g==}
 
+  rollup@2.77.3:
+    resolution: {integrity: sha512-/qxNTG7FbmefJWoeeYJFbHehJ2HNWnjkAFRKzWN/45eNBBF/r8lo992CwcJXEzyVxs5FmfId+vTSTQDb+bxA+g==}
+    engines: {node: '>=10.0.0'}
+    hasBin: true
+
   rollup@2.79.1:
     resolution: {integrity: sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==}
     engines: {node: '>=10.0.0'}
@@ -2422,6 +2491,10 @@ packages:
     resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==}
     engines: {node: '>=0.10.0'}
 
+  source-map-js@1.2.1:
+    resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
+    engines: {node: '>=0.10.0'}
+
   source-map-resolve@0.5.3:
     resolution: {integrity: sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==}
     deprecated: See https://github.com/lydell/source-map-resolve#deprecated
@@ -2767,8 +2840,8 @@ packages:
     peerDependencies:
       vite: '>=2.0.0'
 
-  vite@2.6.14:
-    resolution: {integrity: sha512-2HA9xGyi+EhY2MXo0+A2dRsqsAG3eFNEVIo12olkWhOmc8LfiM+eMdrXf+Ruje9gdXgvSqjLI9freec1RUM5EA==}
+  vite@2.9.18:
+    resolution: {integrity: sha512-sAOqI5wNM9QvSEE70W3UGMdT8cyEn0+PmJMTFvTB8wB0YbYUWw3gUbY62AOyrXosGieF2htmeLATvNxpv/zNyQ==}
     engines: {node: '>=12.2.0'}
     hasBin: true
     peerDependencies:
@@ -2937,6 +3010,9 @@ snapshots:
     dependencies:
       vue: 3.2.26
 
+  '@esbuild/linux-loong64@0.14.54':
+    optional: true
+
   '@popperjs/core@2.11.8': {}
 
   '@rollup/pluginutils@4.2.1':
@@ -2991,9 +3067,9 @@ snapshots:
       '@uppy/utils': 4.1.3
       nanoid: 3.3.7
 
-  '@vitejs/plugin-vue@1.9.4(vite@2.6.14(sass@1.45.0))':
+  '@vitejs/plugin-vue@1.9.4(vite@2.9.18(sass@1.45.0))':
     dependencies:
-      vite: 2.6.14(sass@1.45.0)
+      vite: 2.9.18(sass@1.45.0)
 
   '@vue/compiler-core@3.2.22':
     dependencies:
@@ -4015,76 +4091,89 @@ snapshots:
       d: 1.0.2
       ext: 1.7.0
 
-  esbuild-android-arm64@0.13.15:
+  esbuild-android-64@0.14.54:
+    optional: true
+
+  esbuild-android-arm64@0.14.54:
+    optional: true
+
+  esbuild-darwin-64@0.14.54:
     optional: true
 
-  esbuild-darwin-64@0.13.15:
+  esbuild-darwin-arm64@0.14.54:
     optional: true
 
-  esbuild-darwin-arm64@0.13.15:
+  esbuild-freebsd-64@0.14.54:
     optional: true
 
-  esbuild-freebsd-64@0.13.15:
+  esbuild-freebsd-arm64@0.14.54:
     optional: true
 
-  esbuild-freebsd-arm64@0.13.15:
+  esbuild-linux-32@0.14.54:
     optional: true
 
-  esbuild-linux-32@0.13.15:
+  esbuild-linux-64@0.14.54:
     optional: true
 
-  esbuild-linux-64@0.13.15:
+  esbuild-linux-arm64@0.14.54:
     optional: true
 
-  esbuild-linux-arm64@0.13.15:
+  esbuild-linux-arm@0.14.54:
     optional: true
 
-  esbuild-linux-arm@0.13.15:
+  esbuild-linux-mips64le@0.14.54:
     optional: true
 
-  esbuild-linux-mips64le@0.13.15:
+  esbuild-linux-ppc64le@0.14.54:
     optional: true
 
-  esbuild-linux-ppc64le@0.13.15:
+  esbuild-linux-riscv64@0.14.54:
     optional: true
 
-  esbuild-netbsd-64@0.13.15:
+  esbuild-linux-s390x@0.14.54:
     optional: true
 
-  esbuild-openbsd-64@0.13.15:
+  esbuild-netbsd-64@0.14.54:
     optional: true
 
-  esbuild-sunos-64@0.13.15:
+  esbuild-openbsd-64@0.14.54:
     optional: true
 
-  esbuild-windows-32@0.13.15:
+  esbuild-sunos-64@0.14.54:
     optional: true
 
-  esbuild-windows-64@0.13.15:
+  esbuild-windows-32@0.14.54:
     optional: true
 
-  esbuild-windows-arm64@0.13.15:
+  esbuild-windows-64@0.14.54:
     optional: true
 
-  esbuild@0.13.15:
+  esbuild-windows-arm64@0.14.54:
+    optional: true
+
+  esbuild@0.14.54:
     optionalDependencies:
-      esbuild-android-arm64: 0.13.15
-      esbuild-darwin-64: 0.13.15
-      esbuild-darwin-arm64: 0.13.15
-      esbuild-freebsd-64: 0.13.15
-      esbuild-freebsd-arm64: 0.13.15
-      esbuild-linux-32: 0.13.15
-      esbuild-linux-64: 0.13.15
-      esbuild-linux-arm: 0.13.15
-      esbuild-linux-arm64: 0.13.15
-      esbuild-linux-mips64le: 0.13.15
-      esbuild-linux-ppc64le: 0.13.15
-      esbuild-netbsd-64: 0.13.15
-      esbuild-openbsd-64: 0.13.15
-      esbuild-sunos-64: 0.13.15
-      esbuild-windows-32: 0.13.15
-      esbuild-windows-64: 0.13.15
-      esbuild-windows-arm64: 0.13.15
+      '@esbuild/linux-loong64': 0.14.54
+      esbuild-android-64: 0.14.54
+      esbuild-android-arm64: 0.14.54
+      esbuild-darwin-64: 0.14.54
+      esbuild-darwin-arm64: 0.14.54
+      esbuild-freebsd-64: 0.14.54
+      esbuild-freebsd-arm64: 0.14.54
+      esbuild-linux-32: 0.14.54
+      esbuild-linux-64: 0.14.54
+      esbuild-linux-arm: 0.14.54
+      esbuild-linux-arm64: 0.14.54
+      esbuild-linux-mips64le: 0.14.54
+      esbuild-linux-ppc64le: 0.14.54
+      esbuild-linux-riscv64: 0.14.54
+      esbuild-linux-s390x: 0.14.54
+      esbuild-netbsd-64: 0.14.54
+      esbuild-openbsd-64: 0.14.54
+      esbuild-sunos-64: 0.14.54
+      esbuild-windows-32: 0.14.54
+      esbuild-windows-64: 0.14.54
+      esbuild-windows-arm64: 0.14.54
 
   escape-string-regexp@1.0.5: {}
 
@@ -4969,6 +5058,8 @@ snapshots:
   nan@2.22.0:
     optional: true
 
+  nanoid@3.3.11: {}
+
   nanoid@3.3.7: {}
 
   nanomatch@1.2.13(supports-color@3.2.3):
@@ -5127,6 +5218,8 @@ snapshots:
 
   picocolors@1.0.0: {}
 
+  picocolors@1.1.1: {}
+
   picomatch@2.3.1: {}
 
   posix-character-classes@0.1.1: {}
@@ -5150,6 +5243,12 @@ snapshots:
       picocolors: 1.0.0
       source-map-js: 1.2.0
 
+  postcss@8.5.6:
+    dependencies:
+      nanoid: 3.3.11
+      picocolors: 1.1.1
+      source-map-js: 1.2.1
+
   posthtml-parser@0.2.1:
     dependencies:
       htmlparser2: 3.10.1
@@ -5193,6 +5292,12 @@ snapshots:
 
   q@1.5.1: {}
 
+  qr-code-styling@1.9.2:
+    dependencies:
+      qrcode-generator: 1.5.2
+
+  qrcode-generator@1.5.2: {}
+
   qs@6.13.0:
     dependencies:
       side-channel: 1.0.6
@@ -5346,9 +5451,14 @@ snapshots:
 
   ripemd160@0.2.0: {}
 
+  rollup@2.77.3:
+    optionalDependencies:
+      fsevents: 2.3.3
+
   rollup@2.79.1:
     optionalDependencies:
       fsevents: 2.3.3
+    optional: true
 
   safe-array-concat@1.1.2:
     dependencies:
@@ -5471,6 +5581,8 @@ snapshots:
 
   source-map-js@1.2.0: {}
 
+  source-map-js@1.2.1: {}
+
   source-map-resolve@0.5.3:
     dependencies:
       atob: 2.1.2
@@ -5620,7 +5732,7 @@ snapshots:
       css-select: 4.3.0
       css-tree: 1.1.3
       csso: 4.2.0
-      picocolors: 1.0.0
+      picocolors: 1.1.1
       stable: 0.1.8
 
   tapable@0.1.10: {}
@@ -5763,14 +5875,14 @@ snapshots:
 
   universalify@2.0.1: {}
 
-  unplugin-auto-import@0.5.3(@vueuse/core@10.9.0(vue@3.2.26))(rollup@2.79.1)(vite@2.6.14(sass@1.45.0))(webpack@1.15.0):
+  unplugin-auto-import@0.5.3(@vueuse/core@10.9.0(vue@3.2.26))(rollup@2.79.1)(vite@2.9.18(sass@1.45.0))(webpack@1.15.0):
     dependencies:
       '@antfu/utils': 0.3.0
       '@rollup/pluginutils': 4.2.1
       local-pkg: 0.4.3
       magic-string: 0.25.9
       resolve: 1.22.8
-      unplugin: 0.2.21(rollup@2.79.1)(vite@2.6.14(sass@1.45.0))(webpack@1.15.0)
+      unplugin: 0.2.21(rollup@2.79.1)(vite@2.9.18(sass@1.45.0))(webpack@1.15.0)
     optionalDependencies:
       '@vueuse/core': 10.9.0(vue@3.2.26)
     transitivePeerDependencies:
@@ -5778,12 +5890,12 @@ snapshots:
       - vite
       - webpack
 
-  unplugin@0.2.21(rollup@2.79.1)(vite@2.6.14(sass@1.45.0))(webpack@1.15.0):
+  unplugin@0.2.21(rollup@2.79.1)(vite@2.9.18(sass@1.45.0))(webpack@1.15.0):
     dependencies:
       webpack-virtual-modules: 0.4.6
     optionalDependencies:
       rollup: 2.79.1
-      vite: 2.6.14(sass@1.45.0)
+      vite: 2.9.18(sass@1.45.0)
       webpack: 1.15.0
 
   unset-value@1.0.0:
@@ -5834,16 +5946,16 @@ snapshots:
       clone-stats: 0.0.1
       replace-ext: 0.0.1
 
-  vite-plugin-compression@0.3.6(vite@2.6.14(sass@1.45.0)):
+  vite-plugin-compression@0.3.6(vite@2.9.18(sass@1.45.0)):
     dependencies:
       chalk: 4.1.2
       debug: 4.3.4
       fs-extra: 10.1.0
-      vite: 2.6.14(sass@1.45.0)
+      vite: 2.9.18(sass@1.45.0)
     transitivePeerDependencies:
       - supports-color
 
-  vite-plugin-svg-icons@1.0.5(vite@2.6.14(sass@1.45.0)):
+  vite-plugin-svg-icons@1.0.5(vite@2.9.18(sass@1.45.0)):
     dependencies:
       '@types/svgo': 2.6.4
       cors: 2.8.5
@@ -5852,22 +5964,22 @@ snapshots:
       fs-extra: 10.1.0
       svg-baker: 1.7.0
       svgo: 2.8.0
-      vite: 2.6.14(sass@1.45.0)
+      vite: 2.9.18(sass@1.45.0)
     transitivePeerDependencies:
       - supports-color
 
-  vite-plugin-vue-setup-extend@0.1.0(vite@2.6.14(sass@1.45.0)):
+  vite-plugin-vue-setup-extend@0.1.0(vite@2.9.18(sass@1.45.0)):
     dependencies:
       '@vue/compiler-sfc': 3.2.22
       magic-string: 0.25.9
-      vite: 2.6.14(sass@1.45.0)
+      vite: 2.9.18(sass@1.45.0)
 
-  vite@2.6.14(sass@1.45.0):
+  vite@2.9.18(sass@1.45.0):
     dependencies:
-      esbuild: 0.13.15
-      postcss: 8.4.38
+      esbuild: 0.14.54
+      postcss: 8.5.6
       resolve: 1.22.8
-      rollup: 2.79.1
+      rollup: 2.77.3
     optionalDependencies:
       fsevents: 2.3.3
       sass: 1.45.0

+ 1 - 1
src/App.vue

@@ -1,3 +1,3 @@
 <template>
   <router-view></router-view>
-</template>
+</template>

+ 312 - 39
src/views/CEEnterprise/CERating/index.vue

@@ -6,7 +6,7 @@
         <el-form :model="queryParams" ref="queryRef" :inline="true">
           <el-form-item label="企业信用代码" prop="unifiedSocialCreditCode">
             <el-input v-model="queryParams.unifiedSocialCreditCode" placeholder="请输入企业信用代码" clearable
-              style="width: 240px" />
+                      style="width: 240px" />
           </el-form-item>
           <el-form-item label="企业名称" prop="enterpriseName">
             <el-input v-model="queryParams.enterpriseName" placeholder="请输入企业名称" clearable
@@ -15,56 +15,80 @@
           <el-form-item>
             <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
             <el-button icon="Refresh" @click="resetQuery">重置</el-button>
+            <el-button type="success" icon="Download" @click="handleBatchDownload" :disabled="selectedRows.length === 0">批量下载</el-button>
           </el-form-item>
         </el-form>
       </el-col>
     </el-row>
-    <el-table stripe :data="tableData" style="width: 100%" height="710">
+
+    <el-table stripe :data="tableData" style="width: 100%" height="710" @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" />
       <el-table-column prop="enterpriseName" label="企业名称">
       </el-table-column>
       <el-table-column label="企业信用代码">
         <template #default="scope">
           <el-link v-if="store.getters.permissions.includes('*:*:*') || store.getters.permissions.includes('CEEnterprise:companydetail')" type="primary" @click="handleClick(scope.row)">{{ scope.row.unifiedSocialCreditCode }}</el-link>
-          <div v-else type="primary">{{ scope.row.unifiedSocialCreditCode }}</div>
+          <div v-else>{{ scope.row.unifiedSocialCreditCode }}</div>
         </template>
       </el-table-column>
-<!--      <el-table-column prop="total" label="总数">-->
-<!--      </el-table-column>-->
-      <el-table-column label="总分" align="center" prop="total">
+      <el-table-column prop="total" label="总分" align="center" width="100">
         <template #default="scope">
           {{ scope.row.total.toFixed(2) }}
         </template>
       </el-table-column>
-      <el-table-column prop="grade" label="等级">
+      <el-table-column prop="grade" label="等级" width="100">
+      </el-table-column>
+      <el-table-column label="操作">
+        <template #default="scope">
+          <el-button @click="handlePreview(scope.row)" type="warning">预览企业二维码</el-button>
+          <el-button @click="handleDownload(scope.row)" type="primary" :loading="downloadingRows.has(scope.row.unifiedSocialCreditCode)">
+            下载企业二维码
+          </el-button>
+        </template>
       </el-table-column>
     </el-table>
+
     <div style="position: fixed;bottom: 20px;right: 10px;">
       <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum"
-        v-model:limit="queryParams.pageSize" @pagination="getList" />
+                  v-model:limit="queryParams.pageSize" @pagination="getList" />
     </div>
+
+    <el-dialog v-model="dialogQrcodeVisible" title="二维码预览" width="340px">
+      <template #default>
+        <div ref="qrcodeExport">
+          <el-image style="width: 300px; height: 300px; margin: 0 auto;" :src="previewQrcodeSrc" />
+          <div style="text-align: center;font-size: 14px;">{{currentPreviewInfo.unifiedSocialCreditCode}}</div>
+          <div style="text-align: center;font-size: 16px;">{{currentPreviewInfo.enterpriseName}}</div>
+        </div>
+      </template>
+    </el-dialog>
+
+    <!-- 隐藏的canvas用于生成二维码 -->
+    <canvas ref="hiddenCanvas" style="display: none;"></canvas>
   </div>
 </template>
 
 <script setup name="getCerating">
-// import { getToken } from '@/utils/auth'
-import { getCreditScoreList } from '@/api/ceenterprise/cerating'
-import {useRouter} from "vue-router";
+import { ref, reactive, toRefs, onMounted } from 'vue';
+import { ElMessage, ElLoading } from 'element-plus';
+import { getCreditScoreList } from '@/api/ceenterprise/cerating';
+import { useRouter } from "vue-router";
 import store from "@/store";
-const router = useRouter();
+import QRCodeStyling from "qr-code-styling";
+import logo from '/public/favicon.ico';
 
-const tableData = ref([])
-const handleClick = (row)=>{
-  const {unifiedSocialCreditCode, enterpriseName} = row
-  router.push({
-    path: '/CEEnterprise/companydetail',
-    query: {
-      unifiedSocialCreditCode,
-      enterpriseName
-    }
-  })
-}
+const router = useRouter();
+const site_url = import.meta.env.VITE_APP_SITE_URL;
 
-const total = ref(0)
+const currentPreviewInfo = ref({});
+const dialogQrcodeVisible = ref(false);
+const previewQrcodeSrc = ref('');
+const tableData = ref([]);
+const total = ref(0);
+const selectedRows = ref([]);
+const qrcodeExport = ref(null);
+const hiddenCanvas = ref(null);
+const downloadingRows = ref(new Set()); // 跟踪正在下载的行
 
 const data = reactive({
   form: {},
@@ -72,36 +96,285 @@ const data = reactive({
     pageNum: 1,
     pageSize: 20,
     unifiedSocialCreditCode: '',
-    grade:null,
-    total:null,
-    enterpriseName:'',
-    // userName: undefined,
-    // phonenumber: undefined,
-    // status: undefined,
-    // deptId: store.state.user.userInfo.deptId
+    grade: null,
+    total: null,
+    enterpriseName: '',
   },
-})
-const { queryParams } = toRefs(data)
+});
 
+const {queryParams} = toRefs(data);
+
+// Initial data fetch on component mount
+onMounted(() => {
+  getList();
+});
 
 /** 搜索按钮操作 */
 function handleQuery() {
   queryParams.value.pageNum = 1;
-  getList()
+  getList();
 }
 
 /** 重置按钮操作 */
 function resetQuery() {
   queryParams.value.unifiedSocialCreditCode = "";
-  handleQuery()
+  queryParams.value.enterpriseName = "";
+  handleQuery();
 }
 
 /** 查询用户列表 */
 function getList() {
   getCreditScoreList(queryParams.value).then((res) => {
-    tableData.value = res.data.result
-    total.value = res.data.totalSize
-  })
+    tableData.value = res.data.result;
+    total.value = res.data.totalSize;
+  }).catch(error => {
+    console.error("获取列表失败:", error);
+    ElMessage.error("获取列表失败,请稍后再试。");
+  });
 }
-getList()
+
+/** 处理行点击,跳转到详情页 */
+const handleClick = (row) => {
+  const {unifiedSocialCreditCode, enterpriseName} = row;
+  router.push({
+    path: '/CEEnterprise/companydetail',
+    query: {
+      unifiedSocialCreditCode,
+      enterpriseName
+    }
+  });
+};
+
+/** 生成二维码并返回canvas */
+const generateQRCode = async (row) => {
+  return new Promise((resolve, reject) => {
+    try {
+      const qrCode = new QRCodeStyling({
+        width: 600,
+        height: 600,
+        type: "canvas",
+        data: site_url + '/mobile/companyPreview?uniCode=' + row.unifiedSocialCreditCode,
+        image: logo,
+        dotsOptions: {
+          color: "#000000",
+          type: "rounded"
+        },
+        backgroundOptions: {
+          color: "#e9ebee",
+        },
+        imageOptions: {
+          crossOrigin: "anonymous",
+          margin: 20
+        }
+      });
+
+      // 创建一个临时的canvas容器
+      const tempContainer = document.createElement('div');
+      tempContainer.style.position = 'absolute';
+      tempContainer.style.left = '-9999px';
+      tempContainer.style.top = '-9999px';
+      document.body.appendChild(tempContainer);
+
+      qrCode.append(tempContainer);
+
+      // 等待二维码生成完成
+      setTimeout(() => {
+        const canvas = tempContainer.querySelector('canvas');
+        if (canvas) {
+          // 创建最终的canvas,包含二维码和文字
+          const finalCanvas = document.createElement('canvas');
+          const ctx = finalCanvas.getContext('2d');
+
+          // 设置最终canvas的尺寸(二维码 + 文字区域)
+          finalCanvas.width = 600;
+          finalCanvas.height = 700; // 增加高度以容纳文字
+
+          // 设置白色背景
+          ctx.fillStyle = '#ffffff';
+          ctx.fillRect(0, 0, finalCanvas.width, finalCanvas.height);
+
+          // 绘制二维码
+          ctx.drawImage(canvas, 0, 0);
+
+          // 绘制文字
+          ctx.fillStyle = '#000000';
+          ctx.textAlign = 'center';
+
+          // 绘制企业信用代码
+          ctx.font = '24px Arial';
+          ctx.fillText(row.unifiedSocialCreditCode, 300, 640);
+
+          // 绘制企业名称
+          ctx.font = 'bold 28px Arial';
+          ctx.fillText(row.enterpriseName, 300, 680);
+
+          // 清理临时元素
+          document.body.removeChild(tempContainer);
+
+          resolve(finalCanvas);
+        } else {
+          document.body.removeChild(tempContainer);
+          reject(new Error('无法生企业二维码'));
+        }
+      }, 1000); // 给足够时间让二维码生成
+    } catch (error) {
+      reject(error);
+    }
+  });
+};
+
+/** 下载canvas为图片 */
+const downloadCanvas = (canvas, filename) => {
+  try {
+    const dataURL = canvas.toDataURL('image/png', 1.0);
+    const link = document.createElement('a');
+    link.href = dataURL;
+    link.download = filename;
+    document.body.appendChild(link);
+    link.click();
+    document.body.removeChild(link);
+  } catch (error) {
+    console.error('下载失败:', error);
+    throw error;
+  }
+};
+
+/** 处理下载单个二维码 */
+const handleDownload = async (row) => {
+  const creditCode = row.unifiedSocialCreditCode;
+
+  if (downloadingRows.value.has(creditCode)) {
+    return; // 防止重复下载
+  }
+
+  downloadingRows.value.add(creditCode);
+
+  try {
+    const canvas = await generateQRCode(row);
+    const filename = `${row.enterpriseName}_${creditCode}_二维码.png`;
+    downloadCanvas(canvas, filename);
+    ElMessage.success('二维码下载成功!');
+  } catch (error) {
+    console.error('下载二维码失败:', error);
+    ElMessage.error('下载二维码失败,请稍后再试。');
+  } finally {
+    downloadingRows.value.delete(creditCode);
+  }
+};
+
+/** 处理预览单个二维码 */
+const handlePreview = (row) => {
+  currentPreviewInfo.value = row;
+  const w = new QRCodeStyling({
+    width: 600,
+    height: 600,
+    type: "svg",
+    data: site_url + '/mobile/companyPreview?uniCode=' + row.unifiedSocialCreditCode,
+    image: logo,
+    dotsOptions: {
+      color: "#000000",
+      type: "rounded"
+    },
+    backgroundOptions: {
+      color: "#e9ebee",
+    },
+    imageOptions: {
+      crossOrigin: "anonymous",
+      margin: 20
+    }
+  });
+
+  w.getRawData().then(blob => {
+    previewQrcodeSrc.value = URL.createObjectURL(blob);
+  });
+
+  dialogQrcodeVisible.value = true;
+};
+
+/** 处理表格行选择变化 */
+const handleSelectionChange = (selection) => {
+  selectedRows.value = selection;
+};
+
+/** 批量下载二维码 */
+const handleBatchDownload = async () => {
+  if (selectedRows.value.length === 0) {
+    ElMessage.warning('请至少选择一个企业进行下载!');
+    return;
+  }
+
+  const loadingInstance = ElLoading.service({
+    fullscreen: true,
+    text: `正在下载 ${selectedRows.value.length} 个二维码...`,
+    background: 'rgba(0, 0, 0, 0.3)'
+  });
+
+  let successCount = 0;
+  let failCount = 0;
+
+  try {
+    // 使用 Promise.allSettled 来处理并发下载,但限制并发数量
+    const batchSize = 3; // 每批处理3个,避免浏览器卡顿
+
+    for (let i = 0; i < selectedRows.value.length; i += batchSize) {
+      const batch = selectedRows.value.slice(i, i + batchSize);
+
+      const promises = batch.map(async (row) => {
+        try {
+          const canvas = await generateQRCode(row);
+          const filename = `${row.enterpriseName}_${row.unifiedSocialCreditCode}_二维码.png`;
+          downloadCanvas(canvas, filename);
+          return {success: true, row};
+        } catch (error) {
+          console.error(`下载 ${row.enterpriseName} 的二维码失败:`, error);
+          return {success: false, row, error};
+        }
+      });
+
+      const results = await Promise.allSettled(promises);
+
+      results.forEach((result) => {
+        if (result.status === 'fulfilled' && result.value.success) {
+          successCount++;
+        } else {
+          failCount++;
+        }
+      });
+
+      // 更新加载文本
+      loadingInstance.setText(`正在下载二维码... (${successCount + failCount}/${selectedRows.value.length})`);
+
+      // 批次间稍作延迟,避免浏览器卡顿
+      if (i + batchSize < selectedRows.value.length) {
+        await new Promise(resolve => setTimeout(resolve, 500));
+      }
+    }
+
+    // 显示结果消息
+    if (failCount === 0) {
+      ElMessage.success(`成功下载 ${successCount} 个企业二维码!`);
+    } else if (successCount > 0) {
+      ElMessage.warning(`下载完成:成功 ${successCount} 个,失败 ${failCount} 个`);
+    } else {
+      ElMessage.error('批量下载失败,请稍后再试。');
+    }
+
+  } catch (error) {
+    console.error("批量下载过程中发生错误:", error);
+    ElMessage.error("批量下载过程中发生错误。");
+  } finally {
+    loadingInstance.close();
+  }
+};
 </script>
+
+<style scoped>
+.app-container {
+  padding: 20px;
+}
+
+/* 添加一些加载状态的样式 */
+.el-button.is-loading {
+  pointer-events: none;
+}
+</style>

+ 9 - 4
src/views/visualization/index.vue

@@ -256,10 +256,13 @@ const getTaxTop10 = async () => {
     if (res.data[0].lastQuarterCurrentTotal == 0 || res.data[0].lastQuarterCurrentTotal == null) {
       res = await getLastQuarterYoY2(specialQuarter2.value.quarter,false);
     }
-    res.data = res.data.slice(0, 10);
-    if(!specialQuarter.value.isPassYear && !specialQuarter2.value.isPassYear){
+    // console.log(!specialQuarter.value.isPassYear);
+    // console.log(!specialQuarter2.value.isPassYear);
+    if(!specialQuarter.value.isPassYear){
       res.data.forEach(w => w.lastQuarterCurrentTotal = (w.lastQuarterCurrentTotal / 1).toFixed(2));
-      res.data.sort((a, b) => b.lastQuarterCurrentTotal - a.lastQuarterCurrentTotal)
+      res.data.sort((a, b) => b.lastQuarterCurrentTotal - a.lastQuarterCurrentTotal);
+      res.data = res.data.slice(0, 10);
+      // console.log(res.data);
       RanKingData.value = res.data.map((q, index) => {
         // console.log(q);
         return ({
@@ -270,7 +273,9 @@ const getTaxTop10 = async () => {
       })
     }else {
       res.data.forEach(w => w.lastQuarterLastTotal = (w.lastQuarterLastTotal / 1).toFixed(2));
-      res.data.sort((a, b) => b.lastQuarterLastTotal - a.lastQuarterLastTotal)
+      res.data.sort((a, b) => b.lastQuarterLastTotal - a.lastQuarterLastTotal);
+      res.data = res.data.slice(0, 10);
+      // console.log(res.data);
       RanKingData.value = res.data.map((q, index) => {
         return ({
           name: q.enterpriseName,