feat: 导出先更新一次

This commit is contained in:
2025-12-11 18:18:38 +08:00
parent 10747e9158
commit 1ac4807851
4 changed files with 338 additions and 13 deletions

153
package-lock.json generated
View File

@ -1745,6 +1745,12 @@
"integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==",
"dev": true "dev": true
}, },
"@types/raf": {
"version": "3.4.3",
"resolved": "https://registry.npmmirror.com/@types/raf/-/raf-3.4.3.tgz",
"integrity": "sha512-c4YAvMedbPZ5tEyxzQdMoOhhJ4RD3rngZIdwC2/qDN3d7JpEhB6fiBRKVY1lg5B7Wk+uPBjn5f39j1/2MY1oOw==",
"optional": true
},
"@types/range-parser": { "@types/range-parser": {
"version": "1.2.4", "version": "1.2.4",
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz",
@ -3206,8 +3212,7 @@
"atob": { "atob": {
"version": "2.1.2", "version": "2.1.2",
"resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
"integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg=="
"dev": true
}, },
"autoprefixer": { "autoprefixer": {
"version": "9.8.8", "version": "9.8.8",
@ -3394,6 +3399,11 @@
"integrity": "sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==", "integrity": "sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==",
"dev": true "dev": true
}, },
"base64-arraybuffer": {
"version": "1.0.2",
"resolved": "https://registry.npmmirror.com/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
"integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ=="
},
"base64-js": { "base64-js": {
"version": "1.5.1", "version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
@ -3707,6 +3717,11 @@
"update-browserslist-db": "^1.0.5" "update-browserslist-db": "^1.0.5"
} }
}, },
"btoa": {
"version": "1.2.1",
"resolved": "https://registry.npmmirror.com/btoa/-/btoa-1.2.1.tgz",
"integrity": "sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g=="
},
"buffer": { "buffer": {
"version": "4.9.2", "version": "4.9.2",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz",
@ -3921,6 +3936,22 @@
"integrity": "sha512-pJYArGHrPp3TUqQzFYRmP/lwJlj8RCbVe3Gd3eJQkAV8SAC6b19XS9BjMvRdvaS8RMkaTN8ZhoHP6S1y8zzwEQ==", "integrity": "sha512-pJYArGHrPp3TUqQzFYRmP/lwJlj8RCbVe3Gd3eJQkAV8SAC6b19XS9BjMvRdvaS8RMkaTN8ZhoHP6S1y8zzwEQ==",
"dev": true "dev": true
}, },
"canvg": {
"version": "3.0.11",
"resolved": "https://registry.npmmirror.com/canvg/-/canvg-3.0.11.tgz",
"integrity": "sha512-5ON+q7jCTgMp9cjpu4Jo6XbvfYwSB2Ow3kzHKfIyJfaCAOHLbdKPQqGKgfED/R5B+3TFFfe8pegYA+b423SRyA==",
"optional": true,
"requires": {
"@babel/runtime": "^7.12.5",
"@types/raf": "^3.4.0",
"core-js": "^3.8.3",
"raf": "^3.4.1",
"regenerator-runtime": "^0.13.7",
"rgbcolor": "^1.0.1",
"stackblur-canvas": "^2.0.0",
"svg-pathdata": "^6.0.3"
}
},
"case-sensitive-paths-webpack-plugin": { "case-sensitive-paths-webpack-plugin": {
"version": "2.4.0", "version": "2.4.0",
"resolved": "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz", "resolved": "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz",
@ -4980,6 +5011,14 @@
} }
} }
}, },
"css-line-break": {
"version": "2.1.0",
"resolved": "https://registry.npmmirror.com/css-line-break/-/css-line-break-2.1.0.tgz",
"integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==",
"requires": {
"utrie": "^1.0.2"
}
},
"css-loader": { "css-loader": {
"version": "3.6.0", "version": "3.6.0",
"resolved": "https://registry.npmjs.org/css-loader/-/css-loader-3.6.0.tgz", "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-3.6.0.tgz",
@ -5763,6 +5802,11 @@
"integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==", "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==",
"dev": true "dev": true
}, },
"draggable-resizable-vue3": {
"version": "1.0.94-beta",
"resolved": "https://registry.npmmirror.com/draggable-resizable-vue3/-/draggable-resizable-vue3-1.0.94-beta.tgz",
"integrity": "sha512-5faM6lkLIrh9lgiviOeA+kOYatU6MjGHiA93P4B/eFTGYu9EkLDniN0QudhxTm60aLAFLBO3QlZOskeYKTH9dg=="
},
"duplexer": { "duplexer": {
"version": "0.1.2", "version": "0.1.2",
"resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz",
@ -6058,6 +6102,11 @@
"es6-symbol": "^3.1.1" "es6-symbol": "^3.1.1"
} }
}, },
"es6-promise": {
"version": "4.2.8",
"resolved": "https://registry.npmmirror.com/es6-promise/-/es6-promise-4.2.8.tgz",
"integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w=="
},
"es6-symbol": { "es6-symbol": {
"version": "3.1.4", "version": "3.1.4",
"resolved": "https://registry.npmmirror.com/es6-symbol/-/es6-symbol-3.1.4.tgz", "resolved": "https://registry.npmmirror.com/es6-symbol/-/es6-symbol-3.1.4.tgz",
@ -7005,6 +7054,11 @@
"websocket-driver": ">=0.5.1" "websocket-driver": ">=0.5.1"
} }
}, },
"fflate": {
"version": "0.8.2",
"resolved": "https://registry.npmmirror.com/fflate/-/fflate-0.8.2.tgz",
"integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A=="
},
"figgy-pudding": { "figgy-pudding": {
"version": "3.5.2", "version": "3.5.2",
"resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz", "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz",
@ -7816,6 +7870,25 @@
} }
} }
}, },
"html2canvas": {
"version": "1.4.1",
"resolved": "https://registry.npmmirror.com/html2canvas/-/html2canvas-1.4.1.tgz",
"integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==",
"requires": {
"css-line-break": "^2.1.0",
"text-segmentation": "^1.0.3"
}
},
"html2pdf.js": {
"version": "0.10.1",
"resolved": "https://registry.npmmirror.com/html2pdf.js/-/html2pdf.js-0.10.1.tgz",
"integrity": "sha512-3onwwhOWsZfNjIZwV6YIJ6FVhXk+X9YxHSqzeS6hup+1dGi2DHI+zZYUJ+iFnvtaYcjlhyrILL1fvRCUOa8Fcg==",
"requires": {
"es6-promise": "^4.2.5",
"html2canvas": "^1.0.0",
"jspdf": "^2.3.1"
}
},
"htmlparser2": { "htmlparser2": {
"version": "6.1.0", "version": "6.1.0",
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz",
@ -8877,6 +8950,34 @@
"graceful-fs": "^4.1.6" "graceful-fs": "^4.1.6"
} }
}, },
"jspdf": {
"version": "2.5.2",
"resolved": "https://registry.npmmirror.com/jspdf/-/jspdf-2.5.2.tgz",
"integrity": "sha512-myeX9c+p7znDWPk0eTrujCzNjT+CXdXyk7YmJq5nD5V7uLLKmSXnlQ/Jn/kuo3X09Op70Apm0rQSnFWyGK8uEQ==",
"requires": {
"@babel/runtime": "^7.23.2",
"atob": "^2.1.2",
"btoa": "^1.2.1",
"canvg": "^3.0.6",
"core-js": "^3.6.0",
"dompurify": "^2.5.4",
"fflate": "^0.8.1",
"html2canvas": "^1.0.0-rc.5"
},
"dependencies": {
"@babel/runtime": {
"version": "7.28.4",
"resolved": "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.28.4.tgz",
"integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ=="
},
"dompurify": {
"version": "2.5.8",
"resolved": "https://registry.npmmirror.com/dompurify/-/dompurify-2.5.8.tgz",
"integrity": "sha512-o1vSNgrmYMQObbSSvF/1brBYEQPHhV1+gsmrusO7/GXtp1T9rCS8cXFqVxK/9crT1jA6Ccv+5MTSjBNqr7Sovw==",
"optional": true
}
}
},
"jsprim": { "jsprim": {
"version": "1.4.2", "version": "1.4.2",
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz",
@ -10743,8 +10844,7 @@
"performance-now": { "performance-now": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
"integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow=="
"dev": true
}, },
"picocolors": { "picocolors": {
"version": "1.0.0", "version": "1.0.0",
@ -12364,6 +12464,15 @@
"resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz", "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz",
"integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==" "integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw=="
}, },
"raf": {
"version": "3.4.1",
"resolved": "https://registry.npmmirror.com/raf/-/raf-3.4.1.tgz",
"integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==",
"optional": true,
"requires": {
"performance-now": "^2.1.0"
}
},
"randombytes": { "randombytes": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
@ -12791,6 +12900,12 @@
"integrity": "sha512-zgn5OjNQXLUTdq8m17KdaicF6w89TZs8ZU8y0AYENIU6wG8GG6LLm0yLSiPY8DmaYmHdgRW8rnApjoT0fQRfMg==", "integrity": "sha512-zgn5OjNQXLUTdq8m17KdaicF6w89TZs8ZU8y0AYENIU6wG8GG6LLm0yLSiPY8DmaYmHdgRW8rnApjoT0fQRfMg==",
"dev": true "dev": true
}, },
"rgbcolor": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/rgbcolor/-/rgbcolor-1.0.1.tgz",
"integrity": "sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==",
"optional": true
},
"rimraf": { "rimraf": {
"version": "2.7.1", "version": "2.7.1",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
@ -13693,6 +13808,12 @@
"integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==",
"dev": true "dev": true
}, },
"stackblur-canvas": {
"version": "2.7.0",
"resolved": "https://registry.npmmirror.com/stackblur-canvas/-/stackblur-canvas-2.7.0.tgz",
"integrity": "sha512-yf7OENo23AGJhBriGx0QivY5JP6Y1HbrrDI6WLt6C5auYZXlQrheoY8hD4ibekFKz1HOfE48Ww8kMWMnJD/zcQ==",
"optional": true
},
"stackframe": { "stackframe": {
"version": "1.3.4", "version": "1.3.4",
"resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz",
@ -14195,6 +14316,12 @@
} }
} }
}, },
"svg-pathdata": {
"version": "6.0.3",
"resolved": "https://registry.npmmirror.com/svg-pathdata/-/svg-pathdata-6.0.3.tgz",
"integrity": "sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==",
"optional": true
},
"svg-sprite-loader": { "svg-sprite-loader": {
"version": "6.0.11", "version": "6.0.11",
"resolved": "https://registry.npmjs.org/svg-sprite-loader/-/svg-sprite-loader-6.0.11.tgz", "resolved": "https://registry.npmjs.org/svg-sprite-loader/-/svg-sprite-loader-6.0.11.tgz",
@ -14456,6 +14583,14 @@
"resolved": "https://registry.npmjs.org/tesseract.js-core/-/tesseract.js-core-6.0.0.tgz", "resolved": "https://registry.npmjs.org/tesseract.js-core/-/tesseract.js-core-6.0.0.tgz",
"integrity": "sha512-1Qncm/9oKM7xgrQXZXNB+NRh19qiXGhxlrR8EwFbK5SaUbPZnS5OMtP/ghtqfd23hsr1ZvZbZjeuAGcMxd/ooA==" "integrity": "sha512-1Qncm/9oKM7xgrQXZXNB+NRh19qiXGhxlrR8EwFbK5SaUbPZnS5OMtP/ghtqfd23hsr1ZvZbZjeuAGcMxd/ooA=="
}, },
"text-segmentation": {
"version": "1.0.3",
"resolved": "https://registry.npmmirror.com/text-segmentation/-/text-segmentation-1.0.3.tgz",
"integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==",
"requires": {
"utrie": "^1.0.2"
}
},
"text-table": { "text-table": {
"version": "0.2.0", "version": "0.2.0",
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
@ -15085,6 +15220,14 @@
"integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
"dev": true "dev": true
}, },
"utrie": {
"version": "1.0.2",
"resolved": "https://registry.npmmirror.com/utrie/-/utrie-1.0.2.tgz",
"integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==",
"requires": {
"base64-arraybuffer": "^1.0.2"
}
},
"uuid": { "uuid": {
"version": "3.4.0", "version": "3.4.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
@ -15323,7 +15466,7 @@
"integrity": "sha512-OzIuTaIfC8QXEPmJvB4Y4kw34rSXdCJzxcD1kFStBvr8bK6X1zQAYDo0CNMjojnfTqRQCJ0I7prlErcoRiET2A==" "integrity": "sha512-OzIuTaIfC8QXEPmJvB4Y4kw34rSXdCJzxcD1kFStBvr8bK6X1zQAYDo0CNMjojnfTqRQCJ0I7prlErcoRiET2A=="
}, },
"@popperjs/core": { "@popperjs/core": {
"version": "npm:@sxzz/popperjs-es@2.11.7", "version": "npm:@popperjs/core@2.11.7",
"resolved": "https://registry.npmmirror.com/@sxzz/popperjs-es/-/popperjs-es-2.11.7.tgz", "resolved": "https://registry.npmmirror.com/@sxzz/popperjs-es/-/popperjs-es-2.11.7.tgz",
"integrity": "sha512-Ccy0NlLkzr0Ex2FKvh2X+OyERHXJ88XJ1MXtsI9y9fGexlaXaVTPzBCRBwIxFkORuOb+uBqeu+RqnpgYTEZRUQ==" "integrity": "sha512-Ccy0NlLkzr0Ex2FKvh2X+OyERHXJ88XJ1MXtsI9y9fGexlaXaVTPzBCRBwIxFkORuOb+uBqeu+RqnpgYTEZRUQ=="
}, },

View File

@ -23,12 +23,14 @@
"@wangeditor/editor-for-vue": "^5.1.12", "@wangeditor/editor-for-vue": "^5.1.12",
"axios": "^0.26.0", "axios": "^0.26.0",
"core-js": "^3.6.5", "core-js": "^3.6.5",
"draggable-resizable-vue3": "^1.0.94-beta",
"echarts": "^5.3.3", "echarts": "^5.3.3",
"echarts-gl": "^2.0.9", "echarts-gl": "^2.0.9",
"el-table-infinite-scroll": "^3.0.6", "el-table-infinite-scroll": "^3.0.6",
"element-plus": "2.0.2", "element-plus": "2.0.2",
"file-saver": "^2.0.5", "file-saver": "^2.0.5",
"gifler": "^0.1.0", "gifler": "^0.1.0",
"html2pdf.js": "^0.10.1",
"image-compressor.js": "^1.1.4", "image-compressor.js": "^1.1.4",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"lunar-javascript": "^1.7.5", "lunar-javascript": "^1.7.5",

105
src/utils/export.js Normal file
View File

@ -0,0 +1,105 @@
import { saveAs } from 'file-saver';
import html2pdf from 'html2pdf.js';
const isDOM = (obj) => obj instanceof HTMLElement;
/**
* 导出 PDF
* @param {HTMLElement} dom 目标 DOM 元素
* @param {string} name 文件名
*/
export const downloadPDF = async (dom, name = '导出文件') => {
if(!isDOM(dom)) return;
try {
// 等待一段时间确保所有图表完全渲染
await new Promise(resolve => setTimeout(resolve, 1500));
// 强制重新渲染所有图表
const resizeEvent = new Event('resize');
window.dispatchEvent(resizeEvent);
// 再次等待图表重绘完成
await new Promise(resolve => setTimeout(resolve, 1000));
// 获取内容的实际高度
const contentHeight = dom.scrollHeight;
// 计算需要的页面尺寸(单位:毫米)
// 将像素转换为毫米1px ≈ 0.264583mm
const a4HeightMm = 297; // A4纸高度
const contentHeightMm = contentHeight * 0.264583;
const opt = {
margin: [10, 10, 20, 10], // [top, right, bottom, left] 格式
filename: name + '.pdf',
image: {
type: 'png',
quality: 1
},
html2canvas: {
scale: 1.5,
useCoRs: true,
allowTaint: true,
foreignObjectRendering: false,
logging: false,
width: dom.scrollWidth,
height: dom.scrollHeight + 100, // 增加额外高度,包含底部内容
scrollX: 0,
scrollY: 0
},
jsPDF: {
unit: 'mm',
format: [210, contentHeightMm + 40], // 自定义页面高度,确保所有内容都在一页
orientation: "portrait"
},
pagebreak: {
mode: "avoid", // 尽量避免在元素中间分页
before: '.analysis-report-box h2', // 在h2标题前避免分页
after: '.analysis-report-box h2', // 在h2标题后避免分页
avoid: '.analysis-report-box p' // 避免在段落中间分页
}
};
await html2pdf().set(opt).from(dom).save();
return true;
} catch (error) {
console.error('PDF导出失败:', error);
return false;
}
}
// 带样式的下载方法
export function downloadDocWithStyle(textContent) {
if (typeof textContent !== 'string') return;
const wordDocument = `
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" >
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>富文本导出</title>
<style>
/* 应用两端对齐样式 */
body {
text-align: justify;
text-justify: inter-character; /* 中文文本两端对齐 */
font-family: "Microsoft YaHei", Arial, sans-serif;
}
p {
text-align: justify;
text-justify: inter-character;
}
</style>
</head>
<body>
${textContent}
</body>
</html>
`;
const blob = new Blob([wordDocument], {
type: 'application/msword'
});
saveAs(blob, 'styled-document.doc');
};

View File

@ -1,5 +1,5 @@
<template> <template>
<div> <div class="analysis-report-box">
<!-- <div class="titleBox"> <!-- <div class="titleBox">
<PageTitle title="警情分析报告"> <PageTitle title="警情分析报告">
<el-button type="primary" @click="generatePDF()"> <el-button type="primary" @click="generatePDF()">
@ -23,7 +23,7 @@
<div style="background-color: #fff;color: black;padding: 15px;overflow: auto;" :style="{ height: tabHeight + 'px' }" <div style="background-color: #fff;color: black;padding: 15px;overflow: auto;" :style="{ height: tabHeight + 'px' }"
ref="tableBox"> ref="tableBox">
<div style="border-bottom: 1px #ccc solid;padding-bottom: 30px;"> <div style="border-bottom: 1px #ccc solid;padding-bottom: 30px;">
<h1 class="headline">{{ nd }}年度林芝市公安战术研判报告</h1> <h1 style="text-align: center;color: red;">{{ nd }}年度林芝市公安战术研判报告</h1>
<div <div
style="display: flex;align-items: center;justify-content: space-between; color: red;margin-top: 30px;padding: 0 30px;font-size: 18px;font-weight: 700;"> style="display: flex;align-items: center;justify-content: space-between; color: red;margin-top: 30px;padding: 0 30px;font-size: 18px;font-weight: 700;">
<div>{{ deptId.name }}</div> <div>{{ deptId.name }}</div>
@ -113,7 +113,7 @@
<p style="min-height: 100px;" v-loading="loading">{{ textContent }} </p> <p style="min-height: 100px;" v-loading="loading">{{ textContent }} </p>
</div> </div>
<el-button style="position: fixed;bottom: 0;left: 45%;" type="primary" size="default" <el-button :loading="downLoading" style="position: absolute;bottom: 0;left: 45%;" type="primary" size="default"
@click="downloadWithStyles">下载</el-button> @click="downloadWithStyles">下载</el-button>
</div> </div>
@ -128,7 +128,9 @@ import { getItem, setItem } from '@/utils/storage'
import { fxbgDywdtj, getDictItem, fxbgJqlxtj, fxbgJqlytj, fxbgYdfx, fxbgXsfx, fxgbCljgf, fxgbCzlfx, fxbgTj } from '@/api/semanticAnalysis' import { fxbgDywdtj, getDictItem, fxbgJqlxtj, fxbgJqlytj, fxbgYdfx, fxbgXsfx, fxgbCljgf, fxgbCzlfx, fxbgTj } from '@/api/semanticAnalysis'
import { completions } from '@/api/semanticAnalysis' import { completions } from '@/api/semanticAnalysis'
import { reactive, ref, onMounted, getCurrentInstance, nextTick, computed, watch } from "vue"; import { reactive, ref, onMounted, getCurrentInstance, nextTick, computed, watch } from "vue";
import { downloadDocWithStyle } from '@/utils/export.js'; // import { downloadDocWithStyle } from '@/utils/export.js';
import { downloadDocWithStyle, downloadPDF } from "@/utils/export.js"
import { ElMessage } from "element-plus";
const props = defineProps({ const props = defineProps({
// 数据 // 数据
@ -138,6 +140,7 @@ const props = defineProps({
} }
}) })
const loading = ref(false); const loading = ref(false);
const downLoading = ref(false)
const { proxy } = getCurrentInstance(); const { proxy } = getCurrentInstance();
const { D_GS_XS_LX } = proxy.$dict("D_GS_XS_LX"); //获取字典数据 const { D_GS_XS_LX } = proxy.$dict("D_GS_XS_LX"); //获取字典数据
const dictItemList = ref([]) const dictItemList = ref([])
@ -167,9 +170,74 @@ const tabHeightFn = () => {
tabHeightFn(); tabHeightFn();
}; };
}; };
const downloadDocWithStyle = () => { /** 以pdf形式下载 */
const downLoadPdf = async () => {
downLoading.value = true;
ElMessage.info('下载,会展开所有内容,是正常的。');
try {
// 强制刷新所有图表
const resizeEvent = new Event('resize');
window.dispatchEvent(resizeEvent);
// 等待图表重绘
await new Promise(resolve => setTimeout(resolve, 1000));
// 保存原始样式
const originalOverflow = tableBox.value.style.overflow;
const originalHeight = tableBox.value.style.height;
const originalPaddingBottom = tableBox.value.style.paddingBottom;
const originalMarginBottom = tableBox.value.style.marginBottom;
// 确保所有内容都可见,并增加底部间距
tableBox.value.style.overflow = 'visible';
tableBox.value.style.height = 'auto';
tableBox.value.style.paddingBottom = '50px'; // 增加底部内边距
tableBox.value.style.marginBottom = '30px'; // 增加底部外边距
// 为最后的总结分析部分添加特殊样式,避免分页截断
const summaryElements = tableBox.value.querySelectorAll('h2');
const lastH2 = summaryElements[summaryElements.length - 1];
if (lastH2 && lastH2.textContent.includes('总结分析')) {
lastH2.style.pageBreakBefore = 'always';
lastH2.style.pageBreakInside = 'avoid';
// 确保总结分析的段落不被分页截断
const nextP = lastH2.nextElementSibling;
if (nextP && nextP.tagName === 'P') {
nextP.style.pageBreakInside = 'avoid';
nextP.style.marginBottom = '30px';
}
}
await downloadPDF(tableBox.value, '警情分析报告');
// 恢复原始样式
tableBox.value.style.overflow = originalOverflow;
tableBox.value.style.height = originalHeight;
tableBox.value.style.paddingBottom = originalPaddingBottom;
tableBox.value.style.marginBottom = originalMarginBottom;
// 恢复总结分析部分的样式
if (lastH2) {
lastH2.style.pageBreakBefore = '';
lastH2.style.pageBreakInside = '';
const nextP = lastH2.nextElementSibling;
if (nextP && nextP.tagName === 'P') {
nextP.style.pageBreakInside = '';
nextP.style.marginBottom = '';
}
}
} catch (error) {
console.error('下载失败:', error);
} finally {
downLoading.value = false;
}
}
const downloadWithStyles = async () => {
if (!tableBox.value?.innerHTML) return; if (!tableBox.value?.innerHTML) return;
downloadDocWithStyle(tableBox.value?.innerHTML) // downloadDocWithStyle(tableBox.value?.innerHTML)
downLoadPdf()
} }
const pageData = reactive({ const pageData = reactive({
@ -381,6 +449,7 @@ const Time = () => {
Time() Time()
const funAll = () => { const funAll = () => {
// 使用Promise.all确保所有数据获取函数执行完成 // 使用Promise.all确保所有数据获取函数执行完成
downLoading.value = true
Promise.all([ Promise.all([
getfxbgDywdtj(), getfxbgDywdtj(),
getfxbgJqlxtj(), getfxbgJqlxtj(),
@ -391,6 +460,7 @@ const funAll = () => {
getfxbgTj() getfxbgTj()
]).then(() => { ]).then(() => {
extractTextContent() extractTextContent()
downLoading.value = false
}) })
} }
@ -422,7 +492,8 @@ watch(() => dictItemList.value, (val) => {
}, { deep: true }) }, { deep: true })
getDictItemList() getDictItemList()
const tableBox = ref(null); const tableBox = ref(null);
const textContent = ref('') const textContent = ref('');
const chartRefs = ref([]); // 存储所有图表组件的引用
// 获取p标签的所有文字内容去除所有<>包裹的内容 // 获取p标签的所有文字内容去除所有<>包裹的内容
const extractTextContent = () => { const extractTextContent = () => {
loading.value = true loading.value = true
@ -459,7 +530,7 @@ const extractTextContent = () => {
prompt: `# 角色定位\n你是一名资深警务人员尤其擅长对警情、案件、线索等非结构化文本数据进行阅读理解并总结各种对象之间的关联关系,对下面数据进行一个分析总结给出一个总结报告。\n${result}`, prompt: `# 角色定位\n你是一名资深警务人员尤其擅长对警情、案件、线索等非结构化文本数据进行阅读理解并总结各种对象之间的关联关系,对下面数据进行一个分析总结给出一个总结报告。\n${result}`,
max_tokens: 1000, max_tokens: 1000,
}).then(res => { }).then(res => {
textContent.value = res.choices[0].text textContent.value = res?.choices?.[0]?.text
}).finally(() => { }).finally(() => {
loading.value = false loading.value = false
}) })
@ -475,6 +546,10 @@ const extractTextContent = () => {
color: red; color: red;
} }
.analysis-report-box {
position: relative;
}
p { p {
text-indent: 2em; text-indent: 2em;
/* 首行缩进2个汉字 */ /* 首行缩进2个汉字 */