diff --git a/chat-client/package-lock.json b/chat-client/package-lock.json index edd5066..c89d77d 100644 --- a/chat-client/package-lock.json +++ b/chat-client/package-lock.json @@ -30,6 +30,7 @@ "element-plus": "^2.3.8", "file-saver": "^2.0.5", "highlight.js": "^11.11.1", + "html2pdf.js": "^0.12.1", "image-conversion": "^2.1.1", "js-cookie": "^3.0.5", "js-message": "^2.1.0", @@ -3752,6 +3753,11 @@ "integrity": "sha512-k7kRA033QNtC+gLc4VPlfnue58CM1iQLgn1IMAU8VPHGOj7oIHPp9UlhedEnD/Gl8evoCjwkZjlBORtZ3JByUA==", "dev": true }, + "node_modules/@types/pako": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/pako/-/pako-2.0.4.tgz", + "integrity": "sha512-VWDCbrLeVXJM9fihYodcLiIv0ku+AlOa/TQ1SvYOaBuyrSKgEcro95LJyIsJ4vSo6BXIxOKxiJAat04CmST9Fw==" + }, "node_modules/@types/parse-json": { "version": "4.0.2", "resolved": "https://registry.npmmirror.com/@types/parse-json/-/parse-json-4.0.2.tgz", @@ -3769,6 +3775,12 @@ "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", "dev": true }, + "node_modules/@types/raf": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/@types/raf/-/raf-3.4.3.tgz", + "integrity": "sha512-c4YAvMedbPZ5tEyxzQdMoOhhJ4RD3rngZIdwC2/qDN3d7JpEhB6fiBRKVY1lg5B7Wk+uPBjn5f39j1/2MY1oOw==", + "optional": true + }, "node_modules/@types/range-parser": { "version": "1.2.7", "resolved": "https://registry.npmmirror.com/@types/range-parser/-/range-parser-1.2.7.tgz", @@ -6391,6 +6403,14 @@ "node": ">=0.10.0" } }, + "node_modules/base64-arraybuffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", + "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==", + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmmirror.com/base64-js/-/base64-js-1.5.1.tgz", @@ -7450,6 +7470,25 @@ "simple-concat": "^1.0.0" } }, + "node_modules/canvg": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/canvg/-/canvg-3.0.11.tgz", + "integrity": "sha512-5ON+q7jCTgMp9cjpu4Jo6XbvfYwSB2Ow3kzHKfIyJfaCAOHLbdKPQqGKgfED/R5B+3TFFfe8pegYA+b423SRyA==", + "optional": true, + "dependencies": { + "@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" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/capital-case": { "version": "1.0.4", "resolved": "https://registry.npmmirror.com/capital-case/-/capital-case-1.0.4.tgz", @@ -8419,6 +8458,14 @@ "node": ">=12 || >=16" } }, + "node_modules/css-line-break": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz", + "integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==", + "dependencies": { + "utrie": "^1.0.2" + } + }, "node_modules/css-loader": { "version": "6.11.0", "resolved": "https://registry.npmmirror.com/css-loader/-/css-loader-6.11.0.tgz", @@ -11280,6 +11327,21 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, + "node_modules/fast-png": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/fast-png/-/fast-png-6.4.0.tgz", + "integrity": "sha512-kAqZq1TlgBjZcLr5mcN6NP5Rv4V2f22z00c3g8vRrwkcqjerx7BEhPbOnWCPqaHUl2XWQBJQvOT/FQhdMT7X/Q==", + "dependencies": { + "@types/pako": "^2.0.3", + "iobuffer": "^5.3.2", + "pako": "^2.1.0" + } + }, + "node_modules/fast-png/node_modules/pako": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", + "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==" + }, "node_modules/fast-uri": { "version": "3.0.6", "resolved": "https://registry.npmmirror.com/fast-uri/-/fast-uri-3.0.6.tgz", @@ -11363,6 +11425,11 @@ "pend": "~1.2.0" } }, + "node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==" + }, "node_modules/figures": { "version": "3.2.0", "resolved": "https://registry.npmmirror.com/figures/-/figures-3.2.0.tgz", @@ -12965,6 +13032,27 @@ "node": ">=6" } }, + "node_modules/html2canvas": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz", + "integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==", + "dependencies": { + "css-line-break": "^2.1.0", + "text-segmentation": "^1.0.3" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/html2pdf.js": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/html2pdf.js/-/html2pdf.js-0.12.1.tgz", + "integrity": "sha512-3rBWQ96H5oOU9jtoz3MnE/epGi27ig9h8aonBk4JTpvUERM3lMRxhIRckhJZEi4wE0YfRINoYOIDY0hLY0CHgQ==", + "dependencies": { + "html2canvas": "^1.0.0", + "jspdf": "^3.0.0" + } + }, "node_modules/htmlparser2": { "version": "8.0.2", "resolved": "https://registry.npmmirror.com/htmlparser2/-/htmlparser2-8.0.2.tgz", @@ -13715,6 +13803,11 @@ "node": ">=4" } }, + "node_modules/iobuffer": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/iobuffer/-/iobuffer-5.4.0.tgz", + "integrity": "sha512-DRebOWuqDvxunfkNJAlc3IzWIPD5xVxwUNbHr7xKB8E6aLJxIPfNX3CoMJghcFjpv6RWQsrcJbghtEwSPoJqMA==" + }, "node_modules/ipaddr.js": { "version": "2.2.0", "resolved": "https://registry.npmmirror.com/ipaddr.js/-/ipaddr.js-2.2.0.tgz", @@ -15661,6 +15754,22 @@ "node": ">=0.10.0" } }, + "node_modules/jspdf": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/jspdf/-/jspdf-3.0.3.tgz", + "integrity": "sha512-eURjAyz5iX1H8BOYAfzvdPfIKK53V7mCpBTe7Kb16PaM8JSXEcUQNBQaiWMI8wY5RvNOPj4GccMjTlfwRBd+oQ==", + "dependencies": { + "@babel/runtime": "^7.26.9", + "fast-png": "^6.2.0", + "fflate": "^0.8.1" + }, + "optionalDependencies": { + "canvg": "^3.0.11", + "core-js": "^3.6.0", + "dompurify": "^3.2.4", + "html2canvas": "^1.0.0-rc.5" + } + }, "node_modules/jszip": { "version": "3.10.1", "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", @@ -18641,6 +18750,12 @@ "dev": true, "optional": true }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", + "optional": true + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz", @@ -20408,6 +20523,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/raf": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", + "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", + "optional": true, + "dependencies": { + "performance-now": "^2.1.0" + } + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmmirror.com/randombytes/-/randombytes-2.1.0.tgz", @@ -20735,6 +20859,12 @@ "node": ">=4" } }, + "node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "optional": true + }, "node_modules/regex-not": { "version": "1.0.2", "resolved": "https://registry.npmmirror.com/regex-not/-/regex-not-1.0.2.tgz", @@ -21139,6 +21269,15 @@ "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", "dev": true }, + "node_modules/rgbcolor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/rgbcolor/-/rgbcolor-1.0.1.tgz", + "integrity": "sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==", + "optional": true, + "engines": { + "node": ">= 0.8.15" + } + }, "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmmirror.com/rimraf/-/rimraf-3.0.2.tgz", @@ -22461,6 +22600,15 @@ "node": ">=8" } }, + "node_modules/stackblur-canvas": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/stackblur-canvas/-/stackblur-canvas-2.7.0.tgz", + "integrity": "sha512-yf7OENo23AGJhBriGx0QivY5JP6Y1HbrrDI6WLt6C5auYZXlQrheoY8hD4ibekFKz1HOfE48Ww8kMWMnJD/zcQ==", + "optional": true, + "engines": { + "node": ">=0.1.14" + } + }, "node_modules/stackframe": { "version": "1.3.4", "resolved": "https://registry.npmmirror.com/stackframe/-/stackframe-1.3.4.tgz", @@ -23564,6 +23712,15 @@ "node": ">=0.10.0" } }, + "node_modules/svg-pathdata": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/svg-pathdata/-/svg-pathdata-6.0.3.tgz", + "integrity": "sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==", + "optional": true, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/svg-sprite-loader": { "version": "6.0.11", "resolved": "https://registry.npmmirror.com/svg-sprite-loader/-/svg-sprite-loader-6.0.11.tgz", @@ -24023,6 +24180,14 @@ "node": ">=8" } }, + "node_modules/text-segmentation": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz", + "integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==", + "dependencies": { + "utrie": "^1.0.2" + } + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmmirror.com/text-table/-/text-table-0.2.0.tgz", @@ -25294,6 +25459,14 @@ "node": ">= 0.4.0" } }, + "node_modules/utrie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz", + "integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==", + "dependencies": { + "base64-arraybuffer": "^1.0.2" + } + }, "node_modules/uuid": { "version": "3.4.0", "resolved": "https://registry.npmmirror.com/uuid/-/uuid-3.4.0.tgz", @@ -29618,6 +29791,11 @@ "integrity": "sha512-k7kRA033QNtC+gLc4VPlfnue58CM1iQLgn1IMAU8VPHGOj7oIHPp9UlhedEnD/Gl8evoCjwkZjlBORtZ3JByUA==", "dev": true }, + "@types/pako": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/pako/-/pako-2.0.4.tgz", + "integrity": "sha512-VWDCbrLeVXJM9fihYodcLiIv0ku+AlOa/TQ1SvYOaBuyrSKgEcro95LJyIsJ4vSo6BXIxOKxiJAat04CmST9Fw==" + }, "@types/parse-json": { "version": "4.0.2", "resolved": "https://registry.npmmirror.com/@types/parse-json/-/parse-json-4.0.2.tgz", @@ -29635,6 +29813,12 @@ "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", "dev": true }, + "@types/raf": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/@types/raf/-/raf-3.4.3.tgz", + "integrity": "sha512-c4YAvMedbPZ5tEyxzQdMoOhhJ4RD3rngZIdwC2/qDN3d7JpEhB6fiBRKVY1lg5B7Wk+uPBjn5f39j1/2MY1oOw==", + "optional": true + }, "@types/range-parser": { "version": "1.2.7", "resolved": "https://registry.npmmirror.com/@types/range-parser/-/range-parser-1.2.7.tgz", @@ -31566,6 +31750,11 @@ "pascalcase": "^0.1.1" } }, + "base64-arraybuffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", + "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==" + }, "base64-js": { "version": "1.5.1", "resolved": "https://registry.npmmirror.com/base64-js/-/base64-js-1.5.1.tgz", @@ -32391,6 +32580,22 @@ } } }, + "canvg": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/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" + } + }, "capital-case": { "version": "1.0.4", "resolved": "https://registry.npmmirror.com/capital-case/-/capital-case-1.0.4.tgz", @@ -33122,6 +33327,14 @@ "integrity": "sha512-IQOkD3hbR5KrN93MtcYuad6YPuTSUhntLHDuLEbFWE+ff2/XSZNdZG+LcbbIW5AXKg/WFIfYItIzVoHngHXZzA==", "dev": true }, + "css-line-break": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz", + "integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==", + "requires": { + "utrie": "^1.0.2" + } + }, "css-loader": { "version": "6.11.0", "resolved": "https://registry.npmmirror.com/css-loader/-/css-loader-6.11.0.tgz", @@ -35305,6 +35518,23 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, + "fast-png": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/fast-png/-/fast-png-6.4.0.tgz", + "integrity": "sha512-kAqZq1TlgBjZcLr5mcN6NP5Rv4V2f22z00c3g8vRrwkcqjerx7BEhPbOnWCPqaHUl2XWQBJQvOT/FQhdMT7X/Q==", + "requires": { + "@types/pako": "^2.0.3", + "iobuffer": "^5.3.2", + "pako": "^2.1.0" + }, + "dependencies": { + "pako": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", + "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==" + } + } + }, "fast-uri": { "version": "3.0.6", "resolved": "https://registry.npmmirror.com/fast-uri/-/fast-uri-3.0.6.tgz", @@ -35363,6 +35593,11 @@ "pend": "~1.2.0" } }, + "fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==" + }, "figures": { "version": "3.2.0", "resolved": "https://registry.npmmirror.com/figures/-/figures-3.2.0.tgz", @@ -36545,6 +36780,24 @@ } } }, + "html2canvas": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/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.12.1", + "resolved": "https://registry.npmjs.org/html2pdf.js/-/html2pdf.js-0.12.1.tgz", + "integrity": "sha512-3rBWQ96H5oOU9jtoz3MnE/epGi27ig9h8aonBk4JTpvUERM3lMRxhIRckhJZEi4wE0YfRINoYOIDY0hLY0CHgQ==", + "requires": { + "html2canvas": "^1.0.0", + "jspdf": "^3.0.0" + } + }, "htmlparser2": { "version": "8.0.2", "resolved": "https://registry.npmmirror.com/htmlparser2/-/htmlparser2-8.0.2.tgz", @@ -37106,6 +37359,11 @@ "p-is-promise": "^1.1.0" } }, + "iobuffer": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/iobuffer/-/iobuffer-5.4.0.tgz", + "integrity": "sha512-DRebOWuqDvxunfkNJAlc3IzWIPD5xVxwUNbHr7xKB8E6aLJxIPfNX3CoMJghcFjpv6RWQsrcJbghtEwSPoJqMA==" + }, "ipaddr.js": { "version": "2.2.0", "resolved": "https://registry.npmmirror.com/ipaddr.js/-/ipaddr.js-2.2.0.tgz", @@ -38517,6 +38775,20 @@ "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==", "dev": true }, + "jspdf": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/jspdf/-/jspdf-3.0.3.tgz", + "integrity": "sha512-eURjAyz5iX1H8BOYAfzvdPfIKK53V7mCpBTe7Kb16PaM8JSXEcUQNBQaiWMI8wY5RvNOPj4GccMjTlfwRBd+oQ==", + "requires": { + "@babel/runtime": "^7.26.9", + "canvg": "^3.0.11", + "core-js": "^3.6.0", + "dompurify": "^3.2.4", + "fast-png": "^6.2.0", + "fflate": "^0.8.1", + "html2canvas": "^1.0.0-rc.5" + } + }, "jszip": { "version": "3.10.1", "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", @@ -40791,6 +41063,12 @@ "dev": true, "optional": true }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", + "optional": true + }, "picocolors": { "version": "1.1.1", "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz", @@ -42005,6 +42283,15 @@ "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", "dev": true }, + "raf": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", + "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", + "optional": true, + "requires": { + "performance-now": "^2.1.0" + } + }, "randombytes": { "version": "2.1.0", "resolved": "https://registry.npmmirror.com/randombytes/-/randombytes-2.1.0.tgz", @@ -42253,6 +42540,12 @@ "regenerate": "^1.4.2" } }, + "regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "optional": true + }, "regex-not": { "version": "1.0.2", "resolved": "https://registry.npmmirror.com/regex-not/-/regex-not-1.0.2.tgz", @@ -42554,6 +42847,12 @@ "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", "dev": true }, + "rgbcolor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/rgbcolor/-/rgbcolor-1.0.1.tgz", + "integrity": "sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==", + "optional": true + }, "rimraf": { "version": "3.0.2", "resolved": "https://registry.npmmirror.com/rimraf/-/rimraf-3.0.2.tgz", @@ -43553,6 +43852,12 @@ } } }, + "stackblur-canvas": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/stackblur-canvas/-/stackblur-canvas-2.7.0.tgz", + "integrity": "sha512-yf7OENo23AGJhBriGx0QivY5JP6Y1HbrrDI6WLt6C5auYZXlQrheoY8hD4ibekFKz1HOfE48Ww8kMWMnJD/zcQ==", + "optional": true + }, "stackframe": { "version": "1.3.4", "resolved": "https://registry.npmmirror.com/stackframe/-/stackframe-1.3.4.tgz", @@ -44373,6 +44678,12 @@ } } }, + "svg-pathdata": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/svg-pathdata/-/svg-pathdata-6.0.3.tgz", + "integrity": "sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==", + "optional": true + }, "svg-sprite-loader": { "version": "6.0.11", "resolved": "https://registry.npmmirror.com/svg-sprite-loader/-/svg-sprite-loader-6.0.11.tgz", @@ -44713,6 +45024,14 @@ "minimatch": "^3.0.4" } }, + "text-segmentation": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz", + "integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==", + "requires": { + "utrie": "^1.0.2" + } + }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmmirror.com/text-table/-/text-table-0.2.0.tgz", @@ -45672,6 +45991,14 @@ "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", "dev": true }, + "utrie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz", + "integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==", + "requires": { + "base64-arraybuffer": "^1.0.2" + } + }, "uuid": { "version": "3.4.0", "resolved": "https://registry.npmmirror.com/uuid/-/uuid-3.4.0.tgz", diff --git a/chat-client/package.json b/chat-client/package.json index 95dde77..1da9652 100644 --- a/chat-client/package.json +++ b/chat-client/package.json @@ -45,6 +45,7 @@ "element-plus": "^2.3.8", "file-saver": "^2.0.5", "highlight.js": "^11.11.1", + "html2pdf.js": "^0.12.1", "image-conversion": "^2.1.1", "js-cookie": "^3.0.5", "js-message": "^2.1.0", diff --git a/chat-client/src/utils/exportPdf.ts b/chat-client/src/utils/exportPdf.ts new file mode 100644 index 0000000..c245886 --- /dev/null +++ b/chat-client/src/utils/exportPdf.ts @@ -0,0 +1,68 @@ +// src/utils/exportPdf.ts +import html2pdf from 'html2pdf.js' +import { nextTick } from 'vue' + +/** + * html2pdf 配置类型定义 + */ +export type Html2PdfOptions = { + margin?: number + filename?: string + image?: { type?: 'jpeg' | 'png'; quality?: number } + html2canvas?: { scale?: number; scrollY?: number; useCORS?: boolean; windowWidth?: number } + jsPDF?: { + unit?: string + format?: 'a3' | 'a4' | 'a5' | 'letter' | 'legal' | [number, number] + orientation?: 'portrait' | 'landscape' + } +} + +/** + * 导出指定 DOM 元素为 PDF(支持完整滚动内容) + * @param element 要导出的 DOM 元素 + * @param fileName 导出文件名(默认:export.pdf) + */ +export async function exportElementToPdf( + element: HTMLElement, + fileName = 'export.pdf' +): Promise { + if (!element) { + console.error('❌ exportElementToPdf: 未传入有效的 DOM 元素') + return + } + + // 等待 DOM 渲染完成 + await nextTick() + await new Promise(resolve => setTimeout(resolve, 50)) + + + // 获取元素尺寸 + const elementWidth = element.scrollWidth + const elementHeight = element.scrollHeight + + // html2pdf 配置 + const opt: Html2PdfOptions = { + margin: 10, // 页面边距,单位 px + filename: fileName, + image: { type: 'jpeg', quality: 0.98 }, + html2canvas: { + scale: 2, + scrollY: -window.scrollY, + useCORS: true, + windowWidth: elementWidth, + }, + jsPDF: { + unit: 'pt', // 使用 pt 单位更适合像素换算 + format: [elementWidth, elementHeight], + orientation: 'portrait', + }, + } + + console.log('📄 正在导出 PDF...') + try { + await html2pdf().set(opt).from(element).save() + console.log(`✅ PDF 导出成功:${fileName}`) + } catch (err) { + console.error('❌ PDF 导出失败:', err) + } +} diff --git a/chat-client/src/views/chatweb/components/ChatBox.vue b/chat-client/src/views/chatweb/components/ChatBox.vue index 600ac53..202d2f4 100644 --- a/chat-client/src/views/chatweb/components/ChatBox.vue +++ b/chat-client/src/views/chatweb/components/ChatBox.vue @@ -76,7 +76,7 @@
{ if (messages.value.length > 0) { setCurrentMessages(messages.value) } - + // 创建新会话初始消息 const initialMessages = [...props.initialMessages] - + const newChatIndex = await createNewChat(initialMessages) messages.value = initialMessages - + // 发射会话创建事件 emit('chatCreated', newChatIndex) - + // 滚动到底部显示新消息 await nextTick() throttledScrollToBottom() @@ -322,21 +322,21 @@ const handleCreateNewChat = async () => { // 切换会话 const handleSwitchChat = async (index: number) => { if (index === currentChatIndex.value) return - + // 保存当前会话消息 if (messages.value.length > 0) { setCurrentMessages(messages.value) } - + const newIndex = switchChat(index) - + // 短暂延迟以确保动画流畅 await nextTick() messages.value = [...getCurrentMessages()] - + // 发射会话切换事件 emit('chatSwitched', newIndex) - + // 滚动到底部 await nextTick() throttledScrollToBottom() @@ -345,12 +345,12 @@ const handleSwitchChat = async (index: number) => { // 发送消息 const handleSendMessage = async (messageText: string) => { if (!messageText.trim() || isLoading.value) return - + inputMessage.value = '' // 清空输入框 - + const currentSession = getCurrentSession() const conversationId = currentSession?.conversationId - + const result = await sendMessageCore( messageText, messages.value, @@ -371,7 +371,7 @@ const handleSendMessage = async (messageText: string) => { streamingScrollToBottom() } ) - + // 保存消息到当前会话 setCurrentMessages(messages.value) scrollToBottomForce() @@ -380,7 +380,7 @@ const handleSendMessage = async (messageText: string) => { // 中止流式回答 const handleStopStream = async () => { await stopStream(props.chatType, props.userId) - + // 移除当前消息的加载状态 if (messages.value.length > 0) { const lastMessage = messages.value[messages.value.length - 1] @@ -396,18 +396,18 @@ const handleStopStream = async () => { // 重新生成回答 const handleRegenerateMessage = async (index: number) => { if (index === 0) return - + // 获取前一条用户消息 const userMessage = messages.value[index - 1] if (!userMessage || !userMessage.isUser) return - + // 删除当前助手消息及之后的消息 messages.value = messages.value.slice(0, index) - + // 重新生成回答 const currentSession = getCurrentSession() const conversationId = currentSession?.conversationId - + await sendMessageCore( userMessage.text, messages.value, @@ -427,7 +427,7 @@ const handleRegenerateMessage = async (index: number) => { streamingScrollToBottom() } ) - + setCurrentMessages(messages.value) scrollToBottomForce() } @@ -435,11 +435,11 @@ const handleRegenerateMessage = async (index: number) => { // 保存编辑 const handleSaveEdit = async (newText: string) => { if (!newText.trim()) return - + // 重置编辑状态 editingMessageIndex.value = -1 editingMessageText.value = '' - + // 将编辑后的文本作为新的用户问题发送 await handleSendMessage(newText) } @@ -455,13 +455,13 @@ const handleSourceClick = async (source: TraceFile, conversationId?: string, mes // 构造完整的PDF预览URL const baseUrl = window.location.protocol + '//' + window.location.host const previewFileUrl = baseUrl + source.filePath - + console.log('预览文件:', source) console.log('预览URL:', previewFileUrl) - + // 调用文件预览模态框 filePreviewModals.value?.showPdfPreview(previewFileUrl) - + // 发射文件来源点击事件,让父组件处理 emit('sourceClick', source) } catch (error) { @@ -475,7 +475,7 @@ const handleContextFileClick = async (fileContext: TraceContext, fileType: 'exce try { // 将 \n 字符串转换为真正的换行符 const processedContent = fileContext.context.replace(/\\n/g, '\n') - + if (fileType === 'excel') { // 如果marked转换失败,尝试手动解析markdown表格 let htmlContent = '' @@ -486,15 +486,15 @@ const handleContextFileClick = async (fileContext: TraceContext, fileType: 'exce console.warn('marked解析失败,尝试手动解析:', error) htmlContent = parseMarkdownTable(processedContent) } - + // 如果HTML中没有table标签,尝试手动解析 if (!htmlContent.includes('${processedContent}` } - + filePreviewModals.value?.showMarkdownPreview(htmlContent, fileContext.fileName) } - + console.log(`预览${fileType}文件:`, fileContext.fileName) console.log('文件内容:', fileContext.context) } catch (error) { @@ -521,7 +521,7 @@ const handleContextFileClick = async (fileContext: TraceContext, fileType: 'exce // 处理推荐问题点击事件 const handleRecommendQuestionClick = (question: string) => { if (!question.trim() || isLoading.value) return - + // 发送问题 handleSendMessage(question) } @@ -530,6 +530,11 @@ const handleRecommendQuestionClick = (question: string) => { const getAssistantAvatar = () => { return props.assistantAvatar || new URL('@/assets/assistant.png', import.meta.url).href } +const messagesQuantity = computed(() => messages.value.length) +defineExpose({ + messagesContainer, + messagesQuantity +}) \ No newline at end of file + diff --git a/chat-client/src/views/chatweb/historicalRecords/components/details.vue b/chat-client/src/views/chatweb/historicalRecords/components/details.vue index d6c9d3e..abaa87f 100644 --- a/chat-client/src/views/chatweb/historicalRecords/components/details.vue +++ b/chat-client/src/views/chatweb/historicalRecords/components/details.vue @@ -6,11 +6,12 @@
{{ $t('vabI18n.HistoryRecords.HistoryDetails.count') }} {{ messages.length }} {{ $t('vabI18n.HistoryRecords.HistoryDetails.messageCount') }} {{ $t('vabI18n.HistoryRecords.HistoryDetails.exportMarkdown') }} + 导出为pdf
-
+
@@ -67,6 +68,8 @@ import { marked } from 'marked' import { convertEchartsMarkdown } from '@/api/echart' import { useI18n } from 'vue-i18n' + import html2pdf from 'html2pdf.js' + export default defineComponent({ name: 'HistoryDetails', props: { @@ -147,6 +150,67 @@ state.showThinking[index] = !state.showThinking[index] } + // DOM元素引用 + const timelineContainer = ref(null) + +// 声明 html2pdf 选项类型 + type Html2PdfOptions = { + margin?: number + filename?: string + image?: { type?: 'jpeg' | 'png'; quality?: number } + html2canvas?: { scale?: number; scrollY?: number; windowWidth?: number } + jsPDF?: { + unit?: string; + format?: 'a3' | 'a4' | 'a5' | 'letter' | 'legal' | [number, number]; + orientation?: 'portrait' | 'landscape' + } + } + + + const handleExportPdf = async () => { + if (!timelineContainer.value) return + + // 保存原始样式 + const originalMaxHeight = timelineContainer.value.style.maxHeight + const originalOverflow = timelineContainer.value.style.overflow + + // 展开所有内容 + timelineContainer.value.style.maxHeight = 'none' + timelineContainer.value.style.overflow = 'visible' + Object.keys(state.showThinking).forEach(k => state.showThinking[k] = true) + + await nextTick() + await new Promise(resolve => setTimeout(resolve, 50)) // 等待渲染 + + const elementHeight = timelineContainer.value.scrollHeight + const elementWidth = timelineContainer.value.scrollWidth + + const opt : Html2PdfOptions = { + margin: 0.3, + filename: `${props.conversationName}.pdf`, + image: { type: 'jpeg', quality: 0.98 }, + html2canvas: { scale: 2, scrollY: -window.scrollY, windowWidth: elementWidth }, + jsPDF: { + unit: 'in', + format: [elementWidth / 90, elementHeight / 90] as [number, number], + orientation: 'portrait' + } + } + + try { + await html2pdf().set(opt).from(timelineContainer.value).save() + } catch (err) { + console.error('PDF 导出失败:', err) + } finally { + // 恢复原样式 + timelineContainer.value.style.maxHeight = originalMaxHeight + timelineContainer.value.style.overflow = originalOverflow + } + } + + + + //导出为markdown格式 const handleExportMarkdown = async () => { // 原始 markdown 内容 @@ -182,6 +246,7 @@ return { ...toRefs(state), + timelineContainer, t, locale, conversationName: computed(() => props.conversationName), @@ -191,6 +256,7 @@ formatAnswer, toggleThinking, handleExportMarkdown, + handleExportPdf, } }, }) @@ -218,6 +284,7 @@ link.click() setTimeout(() => URL.revokeObjectURL(link.href), 10000) } +