Commit cc6ad537 by weiw

fix:文件预览

parent 126e2b3a
......@@ -14,13 +14,16 @@
"axios": "^1.7.3",
"clsx": "^2.1.1",
"date-fns": "^3.6.0",
"docx-preview": "^0.3.6",
"github-markdown-css": "^5.8.0",
"pdfjs-dist": "^5.4.149",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-draggable": "^4.4.6",
"react-hot-toast": "^2.4.1",
"react-joyride": "^2.9.3",
"react-markdown": "^9.0.1",
"react-pdf": "^10.1.0",
"react-photo-view": "^1.2.6",
"react-redux": "^9.1.2",
"react-router-dom": "^6.26.0",
......@@ -28,7 +31,8 @@
"rehype-raw": "^7.0.0",
"rehype-sanitize": "^6.0.0",
"remark-gfm": "^4.0.0",
"tailwind-merge": "^2.4.0"
"tailwind-merge": "^2.4.0",
"xlsx": "^0.18.5"
},
"devDependencies": {
"@antfu/eslint-config": "^2.24.1",
......@@ -2937,6 +2941,191 @@
"@module-federation/sdk": "0.17.1"
}
},
"node_modules/@napi-rs/canvas": {
"version": "0.1.78",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas/-/canvas-0.1.78.tgz",
"integrity": "sha512-YaBHJvT+T1DoP16puvWM6w46Lq3VhwKIJ8th5m1iEJyGh7mibk5dT7flBvMQ1EH1LYmMzXJ+OUhu+8wQ9I6u7g==",
"license": "MIT",
"optional": true,
"workspaces": [
"e2e/*"
],
"engines": {
"node": ">= 10"
},
"optionalDependencies": {
"@napi-rs/canvas-android-arm64": "0.1.78",
"@napi-rs/canvas-darwin-arm64": "0.1.78",
"@napi-rs/canvas-darwin-x64": "0.1.78",
"@napi-rs/canvas-linux-arm-gnueabihf": "0.1.78",
"@napi-rs/canvas-linux-arm64-gnu": "0.1.78",
"@napi-rs/canvas-linux-arm64-musl": "0.1.78",
"@napi-rs/canvas-linux-riscv64-gnu": "0.1.78",
"@napi-rs/canvas-linux-x64-gnu": "0.1.78",
"@napi-rs/canvas-linux-x64-musl": "0.1.78",
"@napi-rs/canvas-win32-x64-msvc": "0.1.78"
}
},
"node_modules/@napi-rs/canvas-android-arm64": {
"version": "0.1.78",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-android-arm64/-/canvas-android-arm64-0.1.78.tgz",
"integrity": "sha512-N1ikxztjrRmh8xxlG5kYm1RuNr8ZW1EINEDQsLhhuy7t0pWI/e7SH91uFVLZKCMDyjel1tyWV93b5fdCAi7ggw==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@napi-rs/canvas-darwin-arm64": {
"version": "0.1.78",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-arm64/-/canvas-darwin-arm64-0.1.78.tgz",
"integrity": "sha512-FA3aCU3G5yGc74BSmnLJTObnZRV+HW+JBTrsU+0WVVaNyVKlb5nMvYAQuieQlRVemsAA2ek2c6nYtHh6u6bwFw==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@napi-rs/canvas-darwin-x64": {
"version": "0.1.78",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-x64/-/canvas-darwin-x64-0.1.78.tgz",
"integrity": "sha512-xVij69o9t/frixCDEoyWoVDKgE3ksLGdmE2nvBWVGmoLu94MWUlv2y4Qzf5oozBmydG5Dcm4pRHFBM7YWa1i6g==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@napi-rs/canvas-linux-arm-gnueabihf": {
"version": "0.1.78",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm-gnueabihf/-/canvas-linux-arm-gnueabihf-0.1.78.tgz",
"integrity": "sha512-aSEXrLcIpBtXpOSnLhTg4jPsjJEnK7Je9KqUdAWjc7T8O4iYlxWxrXFIF8rV8J79h5jNdScgZpAUWYnEcutR3g==",
"cpu": [
"arm"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@napi-rs/canvas-linux-arm64-gnu": {
"version": "0.1.78",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-gnu/-/canvas-linux-arm64-gnu-0.1.78.tgz",
"integrity": "sha512-dlEPRX1hLGKaY3UtGa1dtkA1uGgFITn2mDnfI6YsLlYyLJQNqHx87D1YTACI4zFCUuLr/EzQDzuX+vnp9YveVg==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@napi-rs/canvas-linux-arm64-musl": {
"version": "0.1.78",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-musl/-/canvas-linux-arm64-musl-0.1.78.tgz",
"integrity": "sha512-TsCfjOPZtm5Q/NO1EZHR5pwDPSPjPEttvnv44GL32Zn1uvudssjTLbvaG1jHq81Qxm16GTXEiYLmx4jOLZQYlg==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@napi-rs/canvas-linux-riscv64-gnu": {
"version": "0.1.78",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-riscv64-gnu/-/canvas-linux-riscv64-gnu-0.1.78.tgz",
"integrity": "sha512-+cpTTb0GDshEow/5Fy8TpNyzaPsYb3clQIjgWRmzRcuteLU+CHEU/vpYvAcSo7JxHYPJd8fjSr+qqh+nI5AtmA==",
"cpu": [
"riscv64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@napi-rs/canvas-linux-x64-gnu": {
"version": "0.1.78",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-gnu/-/canvas-linux-x64-gnu-0.1.78.tgz",
"integrity": "sha512-wxRcvKfvYBgtrO0Uy8OmwvjlnTcHpY45LLwkwVNIWHPqHAsyoTyG/JBSfJ0p5tWRzMOPDCDqdhpIO4LOgXjeyg==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@napi-rs/canvas-linux-x64-musl": {
"version": "0.1.78",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-musl/-/canvas-linux-x64-musl-0.1.78.tgz",
"integrity": "sha512-vQFOGwC9QDP0kXlhb2LU1QRw/humXgcbVp8mXlyBqzc/a0eijlLF9wzyarHC1EywpymtS63TAj8PHZnhTYN6hg==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@napi-rs/canvas-win32-x64-msvc": {
"version": "0.1.78",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-x64-msvc/-/canvas-win32-x64-msvc-0.1.78.tgz",
"integrity": "sha512-/eKlTZBtGUgpRKalzOzRr6h7KVSuziESWXgBcBnXggZmimwIJWPJlEcbrx5Tcwj8rPuZiANXQOG9pPgy9Q4LTQ==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@napi-rs/image": {
"version": "1.11.2",
"resolved": "https://registry.npmmirror.com/@napi-rs/image/-/image-1.11.2.tgz",
......@@ -7395,6 +7584,15 @@
"node": ">=0.4.0"
}
},
"node_modules/adler-32": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/adler-32/-/adler-32-1.3.1.tgz",
"integrity": "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==",
"license": "Apache-2.0",
"engines": {
"node": ">=0.8"
}
},
"node_modules/ahooks": {
"version": "3.9.0",
"resolved": "https://registry.npmmirror.com/ahooks/-/ahooks-3.9.0.tgz",
......@@ -7867,6 +8065,19 @@
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/cfb": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/cfb/-/cfb-1.2.2.tgz",
"integrity": "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==",
"license": "Apache-2.0",
"dependencies": {
"adler-32": "~1.3.0",
"crc-32": "~1.2.0"
},
"engines": {
"node": ">=0.8"
}
},
"node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz",
......@@ -8118,6 +8329,15 @@
"node": ">=6"
}
},
"node_modules/codepage": {
"version": "1.15.0",
"resolved": "https://registry.npmjs.org/codepage/-/codepage-1.15.0.tgz",
"integrity": "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==",
"license": "Apache-2.0",
"engines": {
"node": ">=0.8"
}
},
"node_modules/color": {
"version": "4.2.3",
"resolved": "https://registry.npmmirror.com/color/-/color-4.2.3.tgz",
......@@ -8326,6 +8546,12 @@
"url": "https://opencollective.com/core-js"
}
},
"node_modules/core-util-is": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
"license": "MIT"
},
"node_modules/cors": {
"version": "2.8.5",
"resolved": "https://registry.npmmirror.com/cors/-/cors-2.8.5.tgz",
......@@ -8367,6 +8593,18 @@
}
}
},
"node_modules/crc-32": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz",
"integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==",
"license": "Apache-2.0",
"bin": {
"crc32": "bin/crc32.njs"
},
"engines": {
"node": ">=0.8"
}
},
"node_modules/cross-spawn": {
"version": "7.0.6",
"resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.6.tgz",
......@@ -8675,6 +8913,15 @@
"node": ">=6.0.0"
}
},
"node_modules/docx-preview": {
"version": "0.3.6",
"resolved": "https://registry.npmjs.org/docx-preview/-/docx-preview-0.3.6.tgz",
"integrity": "sha512-gKVPE18hlpfuhQHiptsw1rbOwzQeGSwK10/w7hv1ZMEqHmjtCuTpz6AUMfu1twIPGxgpcsMXThKI6B6WsP3L1w==",
"license": "Apache-2.0",
"dependencies": {
"jszip": ">=3.0.0"
}
},
"node_modules/dom-serializer": {
"version": "2.0.0",
"resolved": "https://registry.npmmirror.com/dom-serializer/-/dom-serializer-2.0.0.tgz",
......@@ -10412,6 +10659,15 @@
"node": ">= 6"
}
},
"node_modules/frac": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/frac/-/frac-1.1.2.tgz",
"integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==",
"license": "Apache-2.0",
"engines": {
"node": ">=0.8"
}
},
"node_modules/framer-motion": {
"version": "11.18.2",
"resolved": "https://registry.npmmirror.com/framer-motion/-/framer-motion-11.18.2.tgz",
......@@ -11081,6 +11337,12 @@
"node": ">= 4"
}
},
"node_modules/immediate": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
"integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==",
"license": "MIT"
},
"node_modules/immer": {
"version": "10.1.1",
"resolved": "https://registry.npmmirror.com/immer/-/immer-10.1.1.tgz",
......@@ -11403,6 +11665,12 @@
"node": ">=8"
}
},
"node_modules/isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
"license": "MIT"
},
"node_modules/isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz",
......@@ -11622,6 +11890,18 @@
"graceful-fs": "^4.1.6"
}
},
"node_modules/jszip": {
"version": "3.10.1",
"resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
"integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==",
"license": "(MIT OR GPL-3.0-or-later)",
"dependencies": {
"lie": "~3.3.0",
"pako": "~1.0.2",
"readable-stream": "~2.3.6",
"setimmediate": "^1.0.5"
}
},
"node_modules/keyv": {
"version": "4.5.4",
"resolved": "https://registry.npmmirror.com/keyv/-/keyv-4.5.4.tgz",
......@@ -11644,6 +11924,15 @@
"node": ">= 0.8.0"
}
},
"node_modules/lie": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
"integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
"license": "MIT",
"dependencies": {
"immediate": "~3.0.5"
}
},
"node_modules/lilconfig": {
"version": "3.1.3",
"resolved": "https://registry.npmmirror.com/lilconfig/-/lilconfig-3.1.3.tgz",
......@@ -11941,6 +12230,24 @@
"@jridgewell/sourcemap-codec": "^1.5.0"
}
},
"node_modules/make-cancellable-promise": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/make-cancellable-promise/-/make-cancellable-promise-2.0.0.tgz",
"integrity": "sha512-3SEQqTpV9oqVsIWqAcmDuaNeo7yBO3tqPtqGRcKkEo0lrzD3wqbKG9mkxO65KoOgXqj+zH2phJ2LiAsdzlogSw==",
"license": "MIT",
"funding": {
"url": "https://github.com/wojtekmaj/make-cancellable-promise?sponsor=1"
}
},
"node_modules/make-event-props": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/make-event-props/-/make-event-props-2.0.0.tgz",
"integrity": "sha512-G/hncXrl4Qt7mauJEXSg3AcdYzmpkIITTNl5I+rH9sog5Yw0kK6vseJjCaPfOXqOqQuPUP89Rkhfz5kPS8ijtw==",
"license": "MIT",
"funding": {
"url": "https://github.com/wojtekmaj/make-event-props?sponsor=1"
}
},
"node_modules/markdown-table": {
"version": "3.0.4",
"resolved": "https://registry.npmmirror.com/markdown-table/-/markdown-table-3.0.4.tgz",
......@@ -13204,6 +13511,23 @@
"node": ">= 0.6"
}
},
"node_modules/merge-refs": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/merge-refs/-/merge-refs-2.0.0.tgz",
"integrity": "sha512-3+B21mYK2IqUWnd2EivABLT7ueDhb0b8/dGK8LoFQPrU61YITeCMn14F7y7qZafWNZhUEKb24cJdiT5Wxs3prg==",
"license": "MIT",
"funding": {
"url": "https://github.com/wojtekmaj/merge-refs?sponsor=1"
},
"peerDependencies": {
"@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/merge-stream": {
"version": "2.0.0",
"resolved": "https://registry.npmmirror.com/merge-stream/-/merge-stream-2.0.0.tgz",
......@@ -14276,6 +14600,12 @@
"quansync": "^0.2.7"
}
},
"node_modules/pako": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
"license": "(MIT AND Zlib)"
},
"node_modules/parent-module": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/parent-module/-/parent-module-1.0.1.tgz",
......@@ -14473,6 +14803,18 @@
"dev": true,
"license": "MIT"
},
"node_modules/pdfjs-dist": {
"version": "5.4.149",
"resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-5.4.149.tgz",
"integrity": "sha512-Xe8/1FMJEQPUVSti25AlDpwpUm2QAVmNOpFP0SIahaPIOKBKICaefbzogLdwey3XGGoaP4Lb9wqiw2e9Jqp0LA==",
"license": "Apache-2.0",
"engines": {
"node": ">=20.16.0 || >=22.3.0"
},
"optionalDependencies": {
"@napi-rs/canvas": "^0.1.77"
}
},
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz",
......@@ -14742,6 +15084,12 @@
"node": ">= 0.8.0"
}
},
"node_modules/process-nextick-args": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
"license": "MIT"
},
"node_modules/prop-types": {
"version": "15.8.1",
"resolved": "https://registry.npmmirror.com/prop-types/-/prop-types-15.8.1.tgz",
......@@ -15042,6 +15390,47 @@
"@types/unist": "*"
}
},
"node_modules/react-pdf": {
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/react-pdf/-/react-pdf-10.1.0.tgz",
"integrity": "sha512-iUI1YqWgwwZcsXjrehTp3Yi8nT/bvTaWULaRMMyJWvoqqSlopk4LQQ9GDqUnDtX3gzT2glrqrLbjIPl56a+Q3w==",
"license": "MIT",
"dependencies": {
"clsx": "^2.0.0",
"dequal": "^2.0.3",
"make-cancellable-promise": "^2.0.0",
"make-event-props": "^2.0.0",
"merge-refs": "^2.0.0",
"pdfjs-dist": "5.3.93",
"tiny-invariant": "^1.0.0",
"warning": "^4.0.0"
},
"funding": {
"url": "https://github.com/wojtekmaj/react-pdf?sponsor=1"
},
"peerDependencies": {
"@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/react-pdf/node_modules/pdfjs-dist": {
"version": "5.3.93",
"resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-5.3.93.tgz",
"integrity": "sha512-w3fQKVL1oGn8FRyx5JUG5tnbblggDqyx2XzA5brsJ5hSuS+I0NdnJANhmeWKLjotdbPQucLBug5t0MeWr0AAdg==",
"license": "Apache-2.0",
"engines": {
"node": ">=20.16.0 || >=22.3.0"
},
"optionalDependencies": {
"@napi-rs/canvas": "^0.1.71"
}
},
"node_modules/react-photo-view": {
"version": "1.2.7",
"resolved": "https://registry.npmmirror.com/react-photo-view/-/react-photo-view-1.2.7.tgz",
......@@ -15263,6 +15652,27 @@
"node": ">=8"
}
},
"node_modules/readable-stream": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
"license": "MIT",
"dependencies": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.3",
"isarray": "~1.0.0",
"process-nextick-args": "~2.0.0",
"safe-buffer": "~5.1.1",
"string_decoder": "~1.1.1",
"util-deprecate": "~1.0.1"
}
},
"node_modules/readable-stream/node_modules/safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
"license": "MIT"
},
"node_modules/readdirp": {
"version": "3.6.0",
"resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz",
......@@ -15989,6 +16399,12 @@
"node": ">= 0.8.0"
}
},
"node_modules/setimmediate": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
"integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==",
"license": "MIT"
},
"node_modules/setprototypeof": {
"version": "1.2.0",
"resolved": "https://registry.npmmirror.com/setprototypeof/-/setprototypeof-1.2.0.tgz",
......@@ -16389,6 +16805,18 @@
"dev": true,
"license": "CC0-1.0"
},
"node_modules/ssf": {
"version": "0.11.2",
"resolved": "https://registry.npmjs.org/ssf/-/ssf-0.11.2.tgz",
"integrity": "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==",
"license": "Apache-2.0",
"dependencies": {
"frac": "~1.1.2"
},
"engines": {
"node": ">=0.8"
}
},
"node_modules/stable-hash-x": {
"version": "0.2.0",
"resolved": "https://registry.npmmirror.com/stable-hash-x/-/stable-hash-x-0.2.0.tgz",
......@@ -16416,6 +16844,21 @@
"node": ">= 0.6"
}
},
"node_modules/string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"license": "MIT",
"dependencies": {
"safe-buffer": "~5.1.0"
}
},
"node_modules/string_decoder/node_modules/safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
"license": "MIT"
},
"node_modules/string-argv": {
"version": "0.3.2",
"resolved": "https://registry.npmmirror.com/string-argv/-/string-argv-0.3.2.tgz",
......@@ -16984,6 +17427,12 @@
"node": ">=0.8"
}
},
"node_modules/tiny-invariant": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
"integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==",
"license": "MIT"
},
"node_modules/tinyexec": {
"version": "0.3.2",
"resolved": "https://registry.npmmirror.com/tinyexec/-/tinyexec-0.3.2.tgz",
......@@ -17681,6 +18130,15 @@
"url": "https://opencollective.com/eslint"
}
},
"node_modules/warning": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
"integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
"license": "MIT",
"dependencies": {
"loose-envify": "^1.0.0"
}
},
"node_modules/watchpack": {
"version": "2.4.4",
"resolved": "https://registry.npmmirror.com/watchpack/-/watchpack-2.4.4.tgz",
......@@ -17883,6 +18341,24 @@
"node": ">= 8"
}
},
"node_modules/wmf": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wmf/-/wmf-1.0.2.tgz",
"integrity": "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==",
"license": "Apache-2.0",
"engines": {
"node": ">=0.8"
}
},
"node_modules/word": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/word/-/word-0.3.0.tgz",
"integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==",
"license": "Apache-2.0",
"engines": {
"node": ">=0.8"
}
},
"node_modules/word-wrap": {
"version": "1.2.5",
"resolved": "https://registry.npmmirror.com/word-wrap/-/word-wrap-1.2.5.tgz",
......@@ -18027,6 +18503,27 @@
}
}
},
"node_modules/xlsx": {
"version": "0.18.5",
"resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.18.5.tgz",
"integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==",
"license": "Apache-2.0",
"dependencies": {
"adler-32": "~1.3.0",
"cfb": "~1.2.1",
"codepage": "~1.15.0",
"crc-32": "~1.2.1",
"ssf": "~0.11.2",
"wmf": "~1.0.1",
"word": "~0.3.0"
},
"bin": {
"xlsx": "bin/xlsx.njs"
},
"engines": {
"node": ">=0.8"
}
},
"node_modules/xml-name-validator": {
"version": "4.0.0",
"resolved": "https://registry.npmmirror.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz",
......
......@@ -46,13 +46,16 @@
"axios": "^1.7.3",
"clsx": "^2.1.1",
"date-fns": "^3.6.0",
"docx-preview": "^0.3.6",
"github-markdown-css": "^5.8.0",
"pdfjs-dist": "^5.4.149",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-draggable": "^4.4.6",
"react-hot-toast": "^2.4.1",
"react-joyride": "^2.9.3",
"react-markdown": "^9.0.1",
"react-pdf": "^10.1.0",
"react-photo-view": "^1.2.6",
"react-redux": "^9.1.2",
"react-router-dom": "^6.26.0",
......@@ -60,7 +63,8 @@
"rehype-raw": "^7.0.0",
"rehype-sanitize": "^6.0.0",
"remark-gfm": "^4.0.0",
"tailwind-merge": "^2.4.0"
"tailwind-merge": "^2.4.0",
"xlsx": "^0.18.5"
},
"devDependencies": {
"@antfu/eslint-config": "^2.24.1",
......
// src/components/FilePreviewModal/DocxPreview.tsx
import React, { useEffect, useRef } from 'react'
import { renderAsync } from 'docx-preview'
interface DocxPreviewProps {
src: string
className?: string
onRendered?: () => void
onError?: (error: any) => void
}
export const DocxPreview: React.FC<DocxPreviewProps> = ({ src, className = '', onRendered, onError }) => {
const containerRef = useRef<HTMLDivElement>(null)
useEffect(() => {
if (src && containerRef.current) {
fetch(src)
.then(response => response.arrayBuffer())
.then((arrayBuffer) => {
const blob = new Blob([arrayBuffer], {
type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
})
// 清空容器
if (containerRef.current) {
containerRef.current.innerHTML = ''
}
return renderAsync(blob, containerRef.current!, undefined, {
className,
inWrapper: true,
breakPages: true,
ignoreWidth: false,
ignoreHeight: false,
ignoreFonts: false,
})
})
.then(() => {
onRendered?.()
})
.catch((error) => {
console.error('DOCX 渲染失败:', error)
onError?.(error)
})
}
}, [src, className, onRendered, onError])
return (
<div
ref={containerRef}
className={className}
style={{
overflow: 'auto',
height: '100%',
padding: '1rem',
backgroundColor: '#fff',
maxWidth: '100%',
boxSizing: 'border-box',
}}
/>
)
}
// src/components/FilePreviewModal/ExcelPreview.tsx
import { useEffect, useState } from 'react'
import * as XLSX from 'xlsx'
interface ExcelPreviewProps {
src: string
options?: { xls?: boolean }
className?: string
onRendered?: () => void
onError?: (error: any) => void
}
// 将默认 props 提取为常量
const DEFAULT_OPTIONS = { xls: false }
export const ExcelPreview: React.FC<ExcelPreviewProps> = ({
src,
options = DEFAULT_OPTIONS,
className = '',
onRendered,
onError,
}) => {
const [html, setHtml] = useState<string>('')
useEffect(() => {
if (src) {
fetch(src)
.then(response => response.arrayBuffer())
.then((arrayBuffer) => {
try {
const workbook = XLSX.read(arrayBuffer, {
type: 'array',
cellStyles: true,
cellHTML: true,
})
const firstSheetName = workbook.SheetNames[0]
const worksheet = workbook.Sheets[firstSheetName]
// 转换为 HTML 并添加基本样式
const htmlString = XLSX.utils.sheet_to_html(worksheet, {
editable: false,
})
// 添加基本样式使表格更美观并避免横向滚动
const styledHtml = htmlString
.replace('<table', '<table style="width: 100%; table-layout: fixed; border-collapse: collapse;"')
.replace(/<td/g, '<td style="border: 1px solid #d1d5db; padding: 4px 8px; word-wrap: break-word; overflow-wrap: break-word;"')
.replace(/<th/g, '<th style="border: 1px solid #d1d5db; padding: 4px 8px; background-color: #f3f4f6; font-weight: bold; word-wrap: break-word; overflow-wrap: break-word;"')
setHtml(styledHtml)
onRendered?.()
}
catch (error) {
onError?.(error)
}
})
.catch((error) => {
onError?.(error)
})
}
}, [src, options, onRendered, onError])
return (
<div className={`${className} overflow-auto p-4 bg-white`}>
<div
dangerouslySetInnerHTML={{ __html: html || '<p>无法加载 Excel 内容</p>' }}
style={{ minWidth: '100%' }}
/>
</div>
)
}
// src/components/FilePreviewModal/PdfPreview.tsx
import React, { useState } from 'react'
import { Document, Page, pdfjs } from 'react-pdf'
// 设置 PDF.js worker
pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.min.js`
interface PdfPreviewProps {
src: string
className?: string
onLoaded?: () => void
onError?: (error: any) => void
}
export const PdfPreview: React.FC<PdfPreviewProps> = ({ src, className = '', onLoaded, onError }) => {
const [numPages, setNumPages] = useState<number | null>(null)
const [pageNumber, setPageNumber] = useState<number>(1)
const [containerWidth, setContainerWidth] = useState<number>(800)
function onDocumentLoadSuccess({ numPages }: { numPages: number }) {
setNumPages(numPages)
setPageNumber(1)
onLoaded?.()
}
function changePage(offset: number) {
setPageNumber(prevPageNumber => prevPageNumber + offset)
}
function previousPage() {
changePage(-1)
}
function nextPage() {
changePage(1)
}
return (
<div className={`${className} flex flex-col h-full`}>
<div className="flex-grow overflow-auto flex items-center justify-center bg-gray-50 p-2">
<div
className="w-full flex justify-center"
ref={(el) => {
if (el) {
setContainerWidth(el.clientWidth)
}
}}
>
<Document
file={src}
onLoadSuccess={onDocumentLoadSuccess}
onLoadError={onError}
className="flex flex-col items-center"
>
<Page
pageNumber={pageNumber}
width={Math.min(containerWidth - 32, 800)} // 减去padding
className="shadow-md"
/>
</Document>
</div>
</div>
{numPages !== null && numPages > 1 && (
<div className="pdf-navigation flex justify-center items-center py-3 bg-white border-t">
<button
type="button" // 添加 type 属性
onClick={previousPage}
disabled={pageNumber <= 1}
className="px-3 py-1 mx-1 bg-gray-200 rounded disabled:opacity-50 text-sm"
>
上一页
</button>
<span className="mx-3 text-sm">
{' '}
{pageNumber}
{' '}
页,共
{' '}
{numPages}
{' '}
</span>
<button
type="button" // 添加 type 属性
onClick={nextPage}
disabled={pageNumber >= numPages}
className="px-3 py-1 mx-1 bg-gray-200 rounded disabled:opacity-50 text-sm"
>
下一页
</button>
</div>
)}
</div>
)
}
// src/components/FilePreviewModal/index.tsx
import { Button, Modal, ModalBody, ModalContent, ModalFooter, ModalHeader, Spinner } from '@heroui/react'
import { useEffect, useState } from 'react'
import { DocxPreview } from './DocxPreview'
import { ExcelPreview } from './ExcelPreview'
import { PdfPreview } from './PdfPreview'
interface FilePreviewModalProps {
isOpen: boolean
onClose: () => void
doc: any
docUrl?: string
}
export const FilePreviewModal: React.FC<FilePreviewModalProps> = ({ isOpen, onClose, doc, docUrl }) => {
const [loading, setLoading] = useState(false)
const [fileType, setFileType] = useState<string>('')
// 确定文件类型
useEffect(() => {
if (doc?.documentAlias) {
const name = doc.documentAlias.toLowerCase()
if (name.endsWith('.docx'))
setFileType('docx')
else if (name.endsWith('.doc'))
setFileType('doc')
else if (name.endsWith('.xlsx'))
setFileType('xlsx')
else if (name.endsWith('.xls'))
setFileType('xls')
else if (name.endsWith('.pdf'))
setFileType('pdf')
else setFileType('')
}
}, [doc])
const handleDownload = () => {
if (docUrl) {
const link = document.createElement('a')
link.href = docUrl
link.download = doc?.documentAlias || 'document'
link.target = '_blank'
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
}
}
const handleDocumentRendered = () => {
setLoading(false)
}
const handleDocumentError = (error: any) => {
console.error('文档预览出错:', error)
setLoading(false)
}
const renderPreview = () => {
if (!docUrl) {
return (
<div className="flex flex-col items-center justify-center h-96">
<p>无法预览该文件</p>
</div>
)
}
switch (fileType) {
case 'docx':
return (
<div className="h-[70vh] max-h-[70vh] overflow-auto">
<DocxPreview
src={docUrl}
className="w-full min-h-full"
onRendered={handleDocumentRendered}
onError={handleDocumentError}
/>
</div>
)
case 'doc':
return (
<div className="flex flex-col items-center justify-center h-96">
<div className="text-center">
<p className="mb-4">DOC 格式文件无法在线预览</p>
<div className="flex gap-3 justify-center">
<Button color="primary" onPress={() => window.open(docUrl, '_blank')} size="sm">
在新窗口打开
</Button>
<Button color="secondary" onPress={handleDownload} size="sm">
下载文件
</Button>
</div>
</div>
</div>
)
case 'xls':
return (
<div className="h-[70vh] max-h-[70vh] overflow-auto">
<ExcelPreview
src={docUrl}
options={{ xls: true }}
className="w-full min-h-full"
onRendered={handleDocumentRendered}
onError={handleDocumentError}
/>
</div>
)
case 'xlsx':
return (
<div className="h-[70vh] max-h-[70vh] overflow-auto">
<ExcelPreview
src={docUrl}
className="w-full min-h-full"
onRendered={handleDocumentRendered}
onError={handleDocumentError}
/>
</div>
)
case 'pdf':
return (
<div className="h-[70vh] max-h-[70vh] overflow-auto">
<PdfPreview
src={docUrl}
className="w-full min-h-full"
onLoaded={handleDocumentRendered}
onError={handleDocumentError}
/>
</div>
)
default:
return (
<div className="flex flex-col items-center justify-center h-96">
<div className="text-center">
<p className="mb-4">该文件格式无法在线预览</p>
<Button color="primary" onPress={() => window.open(docUrl, '_blank')} size="sm">
在新窗口打开
</Button>
</div>
</div>
)
}
}
return (
<Modal
isOpen={isOpen}
onClose={onClose}
size="3xl"
classNames={{
base: 'max-h-[90vh] max-w-[50vw]',
body: 'py-4',
header: 'border-b border-divider',
footer: 'border-t border-divider',
}}
>
<ModalContent>
<ModalHeader className="flex items-center justify-between">
<span className="text-lg font-semibold truncate max-w-md">{doc?.documentAlias || '文件预览'}</span>
</ModalHeader>
<ModalBody className="p-0">
{loading && fileType !== '' && (
<div className="flex justify-center items-center h-64">
<Spinner size="lg" />
</div>
)}
{renderPreview()}
</ModalBody>
<ModalFooter className="flex justify-end gap-2">
{docUrl && (
<Button color="primary" onPress={handleDownload} size="sm">
下载文件
</Button>
)}
</ModalFooter>
</ModalContent>
</Modal>
)
}
// src/pages/Chat/components/ChatItem/ChatAnswerAttchment.tsx
import { Button, Link } from '@heroui/react'
import { motion } from 'framer-motion'
import { useState } from 'react'
import type { Answer, Attachment } from '@/types/chat'
import AnswerProDetailIcon from '@/assets/svg/answerProDetail.svg?react'
import CardNavImg from '@/assets/card-nav.png'
......@@ -8,13 +10,23 @@ import CardDetailImg from '@/assets/card-detail.png'
import CardPlansImg from '@/assets/card-book1111.png'
import CardProductCompareImg from '@/assets/card-product2222.png'
import { fetchGetDocumentLink } from '@/api/common'
import { FilePreviewModal } from '@/components/FilePreviewModal'
interface ChatAnswerAttachmentProps {
answer: Answer
isLastAnswer?: boolean
onSubmitQuestion?: (question: string, productCode?: string) => void
}
export const ChatAnswerAttachment: React.FC<ChatAnswerAttachmentProps> = ({ answer, isLastAnswer, onSubmitQuestion }) => {
export const ChatAnswerAttachment: React.FC<ChatAnswerAttachmentProps> = ({
answer,
isLastAnswer,
onSubmitQuestion,
}) => {
const [previewModalOpen, setPreviewModalOpen] = useState(false)
const [currentDoc, setCurrentDoc] = useState<any>(null)
const [docUrl, setDocUrl] = useState<string | undefined>(undefined)
const handleClickBoxItem = (produceName: string, productCode: string) => {
if (onSubmitQuestion) {
onSubmitQuestion(produceName, productCode)
......@@ -26,39 +38,57 @@ export const ChatAnswerAttachment: React.FC<ChatAnswerAttachmentProps> = ({ answ
}
const handleClickDocLink = async (doc: any) => {
try {
const docId = `${doc.knowledgeName}/${doc.documentStoreKey}`
const res = await fetchGetDocumentLink(docId)
if (res.data) {
setCurrentDoc(doc)
setDocUrl(res.data.docUrl)
setPreviewModalOpen(true)
}
}
catch (error) {
console.error('获取文档链接失败:', error)
// 如果获取预览链接失败,直接打开新窗口
const docId = `${doc.knowledgeName}/${doc.documentStoreKey}`
const res = await fetchGetDocumentLink(docId)
if (res.data) {
const link = document.createElement('a')
link.href = res.data.docUrl
link.target = '_blank'
link.rel = 'noopener noreferrer' // 安全考虑
link.style.display = 'none'
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
window.open(res.data.docUrl, '_blank')
}
}
}
const closePreviewModal = () => {
setPreviewModalOpen(false)
setCurrentDoc(null)
setDocUrl(null)
}
return (
(
<>
<div className="cardList flex flex-col gap-[20px]">
{answer.cardList && answer.cardList.map((attachment, index) => {
{answer.cardList
&& answer.cardList.map((attachment, index) => {
if (attachment?.type) {
// 使用唯一标识符而不是索引作为 key
const key = `${attachment.type}_${attachment.id || index}`
return (
(
<div key={`${attachment.type}_${index}`}>
<div key={key}>
{/* 附件:product-detail */}
{attachment.type === 'product-detail' && (
<div className="bg-[#29B6FD0A] text-[14px] text-primary py-[4px] px-[16px] w-fit flex items-center">
<AnswerProDetailIcon />
{/* <span className="ml-[6px]">{attachment.name}</span> */}
<div className="ml-[6px] max-w-full sm:w-full text-nowrap text-ellipsis overflow-hidden">
{attachment.name}
</div>
</div>
)}
{/* 附件:引用文件 */}
{attachment.type === 'reference' && attachment.content?.docList && attachment.content.docList.length !== 0 && (
{attachment.type === 'reference'
&& attachment.content?.docList
&& attachment.content.docList.length !== 0 && (
<div>
<p className="text-[14px] text-[#8D9795] mb-[12px]">
已为您找到
......@@ -66,59 +96,73 @@ export const ChatAnswerAttachment: React.FC<ChatAnswerAttachmentProps> = ({ answ
篇资料作为参考:
</p>
<div className="flex flex-col gap-[9px]">
{ attachment.content.docList.map(doc => (
<Link className="cursor-pointer" onPress={() => handleClickDocLink(doc)} size="sm" key={doc.documentStoreKey} isExternal showAnchorIcon underline="hover">
{attachment.content.docList.map(doc => (
<Link
className="cursor-pointer"
onPress={() => handleClickDocLink(doc)}
size="sm"
key={doc.documentStoreKey}
isExternal
showAnchorIcon
underline="hover"
>
{doc.documentAlias}
</Link>
))}
</div>
</div>
)}
{/* 附件:选择 box */}
{
attachment.type === 'box' && attachment.content.productList.length !== 0 && (
{attachment.type === 'box' && attachment.content.productList.length !== 0 && (
<div>
<div className="mb-[12px]">{attachment.description}</div>
<ul
className="flex flex-col gap-[8px]"
>
<ul className="flex flex-col gap-[8px]">
{attachment.content.productList.map(product => (
// <div key={product.productCode}>{product.productName}</div>
(
<motion.li
key={product.productCode}
<motion.li key={product.productCode}>
<Button
onPress={() => handleClickBoxItem(product.productName, product.productCode)}
isDisabled={!isLastAnswer}
color="primary"
variant="light"
className="text-left bg-[#F7FCFF] w-full text-[#333] rounded-[23px] data-[hover=true]:bg-[#E5F6FF] data-[hover=true]:text-primary"
>
<Button onPress={() => handleClickBoxItem(product.productName, product.productCode)} isDisabled={!isLastAnswer} color="primary" variant="light" className="text-left bg-[#F7FCFF] w-full text-[#333] rounded-[23px] data-[hover=true]:bg-[#E5F6FF] data-[hover=true]:text-primary">
<div className="w-full text-nowrap text-ellipsis overflow-hidden">
<span className="ml-[8px]">{product.productName}</span>
</div>
</Button>
</motion.li>
)
))}
</ul>
</div>
)
}
{
attachment.type?.includes('card-') && (
)}
{attachment.type?.includes('card-') && (
<div onClick={() => handleClickCard(attachment)}>
{attachment.type === 'card-nav' && <img className="w-full max-w-[400px] cursor-pointer" src={CardNavImg} alt="" />}
{attachment.type === 'card-detail' && <img className="w-full max-w-[400px] cursor-pointer" src={CardDetailImg} alt="" />}
{attachment.type === 'card-calculation' && <img className="w-full max-w-[400px] cursor-pointer" src={CardCalculation} alt="" />}
{attachment.type === 'card-product-compare' && <img className="w-full max-w-[400px] cursor-pointer" src={CardProductCompareImg} alt="" />}
{attachment.type === 'card-plans' && <img className="w-full max-w-[400px] cursor-pointer" src={CardPlansImg} alt="" />}
{attachment.type === 'card-nav' && (
<img className="w-full max-w-[400px] cursor-pointer" src={CardNavImg} alt="" />
)}
{attachment.type === 'card-detail' && (
<img className="w-full max-w-[400px] cursor-pointer" src={CardDetailImg} alt="" />
)}
{attachment.type === 'card-calculation' && (
<img className="w-full max-w-[400px] cursor-pointer" src={CardCalculation} alt="" />
)}
{attachment.type === 'card-product-compare' && (
<img className="w-full max-w-[400px] cursor-pointer" src={CardProductCompareImg} alt="" />
)}
{attachment.type === 'card-plans' && (
<img className="w-full max-w-[400px] cursor-pointer" src={CardPlansImg} alt="" />
)}
</div>
)
}
)}
</div>
)
)
}
return null
})}
</div>
)
{/* 文件预览弹窗 */}
<FilePreviewModal isOpen={previewModalOpen} onClose={closePreviewModal} doc={currentDoc} docUrl={docUrl} />
</>
)
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment