Ver Fonte

监控导出

Zimo há 11 horas atrás
pai
commit
a08bae3033

+ 1 - 0
package.json

@@ -43,6 +43,7 @@
     "@vueuse/core": "^10.9.0",
     "@wangeditor/editor": "^5.1.23",
     "@wangeditor/editor-for-vue": "^5.1.10",
+    "@zumer/snapdom": "^2.0.2",
     "@zxcvbn-ts/core": "^3.0.4",
     "animate.css": "^4.1.1",
     "axios": "^1.6.8",

+ 27 - 0
pnpm-lock.yaml

@@ -62,6 +62,9 @@ importers:
       '@wangeditor/editor-for-vue':
         specifier: ^5.1.10
         version: 5.1.12(@wangeditor/editor@5.1.23)(vue@3.5.12(typescript@5.3.3))
+      '@zumer/snapdom':
+        specifier: ^2.0.2
+        version: 2.0.2
       '@zxcvbn-ts/core':
         specifier: ^3.0.4
         version: 3.0.4
@@ -1533,36 +1536,42 @@ packages:
     engines: {node: '>= 10.0.0'}
     cpu: [arm]
     os: [linux]
+    libc: [glibc]
 
   '@parcel/watcher-linux-arm-musl@2.5.0':
     resolution: {integrity: sha512-6uHywSIzz8+vi2lAzFeltnYbdHsDm3iIB57d4g5oaB9vKwjb6N6dRIgZMujw4nm5r6v9/BQH0noq6DzHrqr2pA==}
     engines: {node: '>= 10.0.0'}
     cpu: [arm]
     os: [linux]
+    libc: [musl]
 
   '@parcel/watcher-linux-arm64-glibc@2.5.0':
     resolution: {integrity: sha512-BfNjXwZKxBy4WibDb/LDCriWSKLz+jJRL3cM/DllnHH5QUyoiUNEp3GmL80ZqxeumoADfCCP19+qiYiC8gUBjA==}
     engines: {node: '>= 10.0.0'}
     cpu: [arm64]
     os: [linux]
+    libc: [glibc]
 
   '@parcel/watcher-linux-arm64-musl@2.5.0':
     resolution: {integrity: sha512-S1qARKOphxfiBEkwLUbHjCY9BWPdWnW9j7f7Hb2jPplu8UZ3nes7zpPOW9bkLbHRvWM0WDTsjdOTUgW0xLBN1Q==}
     engines: {node: '>= 10.0.0'}
     cpu: [arm64]
     os: [linux]
+    libc: [musl]
 
   '@parcel/watcher-linux-x64-glibc@2.5.0':
     resolution: {integrity: sha512-d9AOkusyXARkFD66S6zlGXyzx5RvY+chTP9Jp0ypSTC9d4lzyRs9ovGf/80VCxjKddcUvnsGwCHWuF2EoPgWjw==}
     engines: {node: '>= 10.0.0'}
     cpu: [x64]
     os: [linux]
+    libc: [glibc]
 
   '@parcel/watcher-linux-x64-musl@2.5.0':
     resolution: {integrity: sha512-iqOC+GoTDoFyk/VYSFHwjHhYrk8bljW6zOhPuhi5t9ulqiYq1togGJB5e3PwYVFFfeVgc6pbz3JdQyDoBszVaA==}
     engines: {node: '>= 10.0.0'}
     cpu: [x64]
     os: [linux]
+    libc: [musl]
 
   '@parcel/watcher-win32-arm64@2.5.0':
     resolution: {integrity: sha512-twtft1d+JRNkM5YbmexfcH/N4znDtjgysFaV9zvZmmJezQsKpkfLYJ+JFV3uygugK6AtIM2oADPkB2AdhBrNig==}
@@ -1671,46 +1680,55 @@ packages:
     resolution: {integrity: sha512-9OwUnK/xKw6DyRlgx8UizeqRFOfi9mf5TYCw1uolDaJSbUmBxP85DE6T4ouCMoN6pXw8ZoTeZCSEfSaYo+/s1w==}
     cpu: [arm]
     os: [linux]
+    libc: [glibc]
 
   '@rollup/rollup-linux-arm-musleabihf@4.27.4':
     resolution: {integrity: sha512-Vgdo4fpuphS9V24WOV+KwkCVJ72u7idTgQaBoLRD0UxBAWTF9GWurJO9YD9yh00BzbkhpeXtm6na+MvJU7Z73A==}
     cpu: [arm]
     os: [linux]
+    libc: [musl]
 
   '@rollup/rollup-linux-arm64-gnu@4.27.4':
     resolution: {integrity: sha512-pleyNgyd1kkBkw2kOqlBx+0atfIIkkExOTiifoODo6qKDSpnc6WzUY5RhHdmTdIJXBdSnh6JknnYTtmQyobrVg==}
     cpu: [arm64]
     os: [linux]
+    libc: [glibc]
 
   '@rollup/rollup-linux-arm64-musl@4.27.4':
     resolution: {integrity: sha512-caluiUXvUuVyCHr5DxL8ohaaFFzPGmgmMvwmqAITMpV/Q+tPoaHZ/PWa3t8B2WyoRcIIuu1hkaW5KkeTDNSnMA==}
     cpu: [arm64]
     os: [linux]
+    libc: [musl]
 
   '@rollup/rollup-linux-powerpc64le-gnu@4.27.4':
     resolution: {integrity: sha512-FScrpHrO60hARyHh7s1zHE97u0KlT/RECzCKAdmI+LEoC1eDh/RDji9JgFqyO+wPDb86Oa/sXkily1+oi4FzJQ==}
     cpu: [ppc64]
     os: [linux]
+    libc: [glibc]
 
   '@rollup/rollup-linux-riscv64-gnu@4.27.4':
     resolution: {integrity: sha512-qyyprhyGb7+RBfMPeww9FlHwKkCXdKHeGgSqmIXw9VSUtvyFZ6WZRtnxgbuz76FK7LyoN8t/eINRbPUcvXB5fw==}
     cpu: [riscv64]
     os: [linux]
+    libc: [glibc]
 
   '@rollup/rollup-linux-s390x-gnu@4.27.4':
     resolution: {integrity: sha512-PFz+y2kb6tbh7m3A7nA9++eInGcDVZUACulf/KzDtovvdTizHpZaJty7Gp0lFwSQcrnebHOqxF1MaKZd7psVRg==}
     cpu: [s390x]
     os: [linux]
+    libc: [glibc]
 
   '@rollup/rollup-linux-x64-gnu@4.27.4':
     resolution: {integrity: sha512-Ni8mMtfo+o/G7DVtweXXV/Ol2TFf63KYjTtoZ5f078AUgJTmaIJnj4JFU7TK/9SVWTaSJGxPi5zMDgK4w+Ez7Q==}
     cpu: [x64]
     os: [linux]
+    libc: [glibc]
 
   '@rollup/rollup-linux-x64-musl@4.27.4':
     resolution: {integrity: sha512-5AeeAF1PB9TUzD+3cROzFTnAJAcVUGLuR8ng0E0WXGkYhp6RD6L+6szYVX+64Rs0r72019KHZS1ka1q+zU/wUw==}
     cpu: [x64]
     os: [linux]
+    libc: [musl]
 
   '@rollup/rollup-win32-arm64-msvc@4.27.4':
     resolution: {integrity: sha512-yOpVsA4K5qVwu2CaS3hHxluWIK5HQTjNV4tWjQXluMiiiu4pJj4BN98CvxohNCpcjMeTXk/ZMJBRbgRg8HBB6A==}
@@ -1756,24 +1774,28 @@ packages:
     engines: {node: '>=10'}
     cpu: [arm64]
     os: [linux]
+    libc: [glibc]
 
   '@swc/core-linux-arm64-musl@1.9.3':
     resolution: {integrity: sha512-tzVH480RY6RbMl/QRgh5HK3zn1ZTFsThuxDGo6Iuk1MdwIbdFYUY034heWUTI4u3Db97ArKh0hNL0xhO3+PZdg==}
     engines: {node: '>=10'}
     cpu: [arm64]
     os: [linux]
+    libc: [musl]
 
   '@swc/core-linux-x64-gnu@1.9.3':
     resolution: {integrity: sha512-ivXXBRDXDc9k4cdv10R21ccBmGebVOwKXT/UdH1PhxUn9m/h8erAWjz5pcELwjiMf27WokqPgaWVfaclDbgE+w==}
     engines: {node: '>=10'}
     cpu: [x64]
     os: [linux]
+    libc: [glibc]
 
   '@swc/core-linux-x64-musl@1.9.3':
     resolution: {integrity: sha512-ILsGMgfnOz1HwdDz+ZgEuomIwkP1PHT6maigZxaCIuC6OPEhKE8uYna22uU63XvYcLQvZYDzpR3ms47WQPuNEg==}
     engines: {node: '>=10'}
     cpu: [x64]
     os: [linux]
+    libc: [musl]
 
   '@swc/core-win32-arm64-msvc@1.9.3':
     resolution: {integrity: sha512-e+XmltDVIHieUnNJHtspn6B+PCcFOMYXNJB1GqoCcyinkEIQNwC8KtWgMqUucUbEWJkPc35NHy9k8aCXRmw9Kg==}
@@ -2456,6 +2478,9 @@ packages:
     resolution: {integrity: sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==}
     engines: {node: '>=10.0.0'}
 
+  '@zumer/snapdom@2.0.2':
+    resolution: {integrity: sha512-W6quT4lMcPu8Q9O/Q6witSfc6/+xuY8C8yDoHug/+o7zYKCNE/e0I3//XsWDkyq9C0mDE0TAWF/8bwCR7x3gHQ==}
+
   '@zxcvbn-ts/core@3.0.4':
     resolution: {integrity: sha512-aQeiT0F09FuJaAqNrxynlAwZ2mW/1MdXakKWNmGM1Qp/VaY6CnB/GfnMS2T8gB2231Esp1/maCWd8vTG4OuShw==}
 
@@ -8088,6 +8113,8 @@ snapshots:
 
   '@xmldom/xmldom@0.8.10': {}
 
+  '@zumer/snapdom@2.0.2': {}
+
   '@zxcvbn-ts/core@3.0.4':
     dependencies:
       fastest-levenshtein: 1.0.16

+ 17 - 22
src/views/oli-connection/monitoring/detail.vue

@@ -18,6 +18,7 @@ import { cancelAllRequests, IotStatApi } from '@/api/pms/stat'
 import { Dimensions, formatIotValue, HeaderItem, useSocketBus } from '@/utils/useSocketBus'
 import { rangeShortcuts } from '@/utils/formatTime'
 import { useFullscreen } from '@vueuse/core'
+import { snapdom } from '@zumer/snapdom'
 
 const { query } = useRoute()
 
@@ -663,30 +664,24 @@ function handleClickSpec(modelName: string) {
   })
 }
 
-const exportChart = () => {
-  if (!chart) return
-  let img = new Image()
-  img.src = chart.getDataURL({
-    type: 'png',
-    pixelRatio: 1,
-    backgroundColor: '#fff'
-  })
-
-  img.onload = function () {
-    let canvas = document.createElement('canvas')
-    canvas.width = img.width
-    canvas.height = img.height
-    let ctx = canvas.getContext('2d')
-    ctx?.drawImage(img, 0, 0)
-    let dataURL = canvas.toDataURL('image/png')
+const downloadRef = ref(null)
 
-    let a = document.createElement('a')
+const exportChart = async () => {
+  try {
+    if (!downloadRef.value) return
 
-    let event = new MouseEvent('click')
+    const result = await snapdom(downloadRef.value, {
+      scale: 2,
+      backgroundColor: '#fff'
+    })
 
-    a.href = dataURL
-    a.download = `${data.value.deviceName}-设备监控-${dayjs().format('YYYY-MM-DD HH:mm:ss')}.png`
-    a.dispatchEvent(event)
+    await result.download({
+      filename: `${data.value.deviceName}-设备监控`,
+      type: 'png'
+    })
+  } catch (error) {
+    console.error(error)
+    ElMessage.error('导出图表失败')
   }
 }
 
@@ -878,7 +873,7 @@ const { toggle, isFullscreen } = useFullscreen(targetArea)
             </div>
           </header>
 
-          <div class="flex flex-1">
+          <div ref="downloadRef" class="flex flex-1">
             <div class="flex gap-1 select-none">
               <div
                 v-for="item of maxmin"

+ 17 - 21
src/views/pms/device/monitor/TdDeviceInfo.vue

@@ -19,6 +19,7 @@ import { cancelAllRequests, IotStatApi } from '@/api/pms/stat'
 import { rangeShortcuts } from '@/utils/formatTime'
 import { Dimensions, formatIotValue, HeaderItem } from '@/utils/useSocketBus'
 import { useFullscreen } from '@vueuse/core'
+import { snapdom } from '@zumer/snapdom'
 
 const { query } = useRoute()
 
@@ -566,30 +567,25 @@ function handleClickSpec(modelName: string) {
     chart?.resize()
   })
 }
-const exportChart = () => {
-  if (!chart) return
-  let img = new Image()
-  img.src = chart.getDataURL({
-    type: 'png',
-    pixelRatio: 1,
-    backgroundColor: '#fff'
-  })
 
-  img.onload = function () {
-    let canvas = document.createElement('canvas')
-    canvas.width = img.width
-    canvas.height = img.height
-    let ctx = canvas.getContext('2d')
-    ctx?.drawImage(img, 0, 0)
-    let dataURL = canvas.toDataURL('image/png')
+const downloadRef = ref(null)
 
-    let a = document.createElement('a')
+const exportChart = async () => {
+  try {
+    if (!downloadRef.value) return
 
-    let event = new MouseEvent('click')
+    const result = await snapdom(downloadRef.value, {
+      scale: 2,
+      backgroundColor: '#fff'
+    })
 
-    a.href = dataURL
-    a.download = `${data.value.deviceName}-设备监控-${dayjs().format('YYYY-MM-DD HH:mm:ss')}.png`
-    a.dispatchEvent(event)
+    await result.download({
+      filename: `${data.value.deviceName}-设备监控`,
+      type: 'png'
+    })
+  } catch (error) {
+    console.error(error)
+    ElMessage.error('导出图表失败')
   }
 }
 
@@ -780,7 +776,7 @@ const { toggle, isFullscreen } = useFullscreen(targetArea)
             </div>
           </header>
 
-          <div class="flex flex-1">
+          <div ref="downloadRef" class="flex flex-1">
             <div class="flex gap-1 select-none">
               <div
                 v-for="item of maxmin"