添加导出功能
This commit is contained in:
parent
7272342fe6
commit
98b85e8132
@ -50,7 +50,10 @@
|
||||
"echarts-wordcloud": "^2.1.0",
|
||||
"element-plus": "2.11.1",
|
||||
"fast-xml-parser": "^4.3.2",
|
||||
"file-saver": "^2.0.5",
|
||||
"highlight.js": "^11.9.0",
|
||||
"html-docx-js": "^0.3.1",
|
||||
"html-docx-js-typescript": "^0.1.5",
|
||||
"jsencrypt": "^3.3.2",
|
||||
"jsoneditor": "^10.1.3",
|
||||
"lodash-es": "^4.17.21",
|
||||
|
||||
248
pnpm-lock.yaml
generated
248
pnpm-lock.yaml
generated
@ -83,9 +83,18 @@ importers:
|
||||
fast-xml-parser:
|
||||
specifier: ^4.3.2
|
||||
version: 4.5.0
|
||||
file-saver:
|
||||
specifier: ^2.0.5
|
||||
version: 2.0.5
|
||||
highlight.js:
|
||||
specifier: ^11.9.0
|
||||
version: 11.10.0
|
||||
html-docx-js:
|
||||
specifier: ^0.3.1
|
||||
version: 0.3.1
|
||||
html-docx-js-typescript:
|
||||
specifier: ^0.1.5
|
||||
version: 0.1.5
|
||||
jsencrypt:
|
||||
specifier: ^3.3.2
|
||||
version: 3.3.2
|
||||
@ -2566,6 +2575,9 @@ packages:
|
||||
resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
browser-or-node@1.3.0:
|
||||
resolution: {integrity: sha512-0F2z/VSnLbmEeBcUrSuDH5l0HxTXdQQzLjkmBR4cYfvg1zJrKSlmIZFqyFR8oX0NrwPhy3c3HQ6i3OxMbew4Tg==}
|
||||
|
||||
browserslist-to-esbuild@2.1.1:
|
||||
resolution: {integrity: sha512-KN+mty6C3e9AN8Z5dI1xeN15ExcRNeISoC3g7V0Kax/MMF9MSoYA2G7lkTTcVUFntiEjkpI0HNgqJC1NjdyNUw==}
|
||||
engines: {node: '>=18'}
|
||||
@ -2758,6 +2770,9 @@ packages:
|
||||
core-js@3.39.0:
|
||||
resolution: {integrity: sha512-raM0ew0/jJUqkJ0E6e8UDtl+y/7ktFivgWvqw8dNSQeNWoSDLvQ1H/RN3aPXB9tBd4/FhyR4RDPGhsNIMsAn7g==}
|
||||
|
||||
core-util-is@1.0.3:
|
||||
resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==}
|
||||
|
||||
cosmiconfig-typescript-loader@5.1.0:
|
||||
resolution: {integrity: sha512-7PtBB+6FdsOvZyJtlF3hEPpACq7RQX6BVGsgC7/lfVXnKMvNCu/XY3ykreqG5w/rBNdu2z8LCIKoF3kpHHdHlA==}
|
||||
engines: {node: '>=v16'}
|
||||
@ -3426,6 +3441,9 @@ packages:
|
||||
resolution: {integrity: sha512-/pqPFG+FdxWQj+/WSuzXSDaNzxgTLr/OrR1QuqfEZzDakpdYE70PwUxL7BPUa8hpjbvY1+qvCl8k+8Tq34xJgg==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
file-saver@2.0.5:
|
||||
resolution: {integrity: sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==}
|
||||
|
||||
filelist@1.0.4:
|
||||
resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==}
|
||||
|
||||
@ -3671,6 +3689,12 @@ packages:
|
||||
htm@3.1.1:
|
||||
resolution: {integrity: sha512-983Vyg8NwUE7JkZ6NmOqpCZ+sh1bKv2iYTlUkzlWmA5JD2acKoxd4KVxbMmxX/85mtfdnDmTFoNKcg5DGAvxNQ==}
|
||||
|
||||
html-docx-js-typescript@0.1.5:
|
||||
resolution: {integrity: sha512-GNojWFDYbpHSIgKml6/0oAom8mtHrHRTWKMyLRdeJQHO/CyeM6H39DYgzYvPp4OhBp2Ti8dxMKFq0/FkpYD4bg==}
|
||||
|
||||
html-docx-js@0.3.1:
|
||||
resolution: {integrity: sha512-QSrMiRhxesqxYCa3f+2Z3ttIHPzSjDOL1tCOmIDIEET7HdabxXND6tAbsFMXAgRG4RADQ3wbl74ydMmjidaDPA==}
|
||||
|
||||
html-tags@3.3.1:
|
||||
resolution: {integrity: sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==}
|
||||
engines: {node: '>=8'}
|
||||
@ -3703,6 +3727,9 @@ packages:
|
||||
resolution: {integrity: sha512-InwqeHHN2XpumIkMvpl/DCJVrAHgCsG5+cn1XlnLWGwtZBm8QJfSusItfrwx81CTp5agNZqpKU2J/ccC5nGT4A==}
|
||||
engines: {node: '>= 4'}
|
||||
|
||||
immediate@3.0.6:
|
||||
resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==}
|
||||
|
||||
immer@9.0.21:
|
||||
resolution: {integrity: sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==}
|
||||
|
||||
@ -3904,6 +3931,9 @@ packages:
|
||||
resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
isarray@1.0.0:
|
||||
resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==}
|
||||
|
||||
isarray@2.0.5:
|
||||
resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==}
|
||||
|
||||
@ -3996,6 +4026,12 @@ packages:
|
||||
resolution: {integrity: sha512-5YRzlAQ7tuzV1nAJu3LvDlrKtBFIALHN2+a+I1MGJCt3ldRDBF/bZuvIPzae8Epot6KBXd0awRZZcuoeAsZ/mw==}
|
||||
hasBin: true
|
||||
|
||||
jszip@2.7.0:
|
||||
resolution: {integrity: sha512-JIsRKRVC3gTRo2vM4Wy9WBC3TRcfnIZU8k65Phi3izkvPH975FowRYtKGT6PxevA0XnJ/yO8b0QwV0ydVyQwfw==}
|
||||
|
||||
jszip@3.10.1:
|
||||
resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==}
|
||||
|
||||
katex@0.16.11:
|
||||
resolution: {integrity: sha512-RQrI8rlHY92OLf3rho/Ts8i/XvjgguEjOkO1BEXcU3N8BqPpSzBNwV/G0Ukr+P/l3ivvJUE/Fa/CwbS6HesGNQ==}
|
||||
hasBin: true
|
||||
@ -4026,6 +4062,9 @@ packages:
|
||||
lezer-feel@1.4.0:
|
||||
resolution: {integrity: sha512-kNxG7O38gwpuYy+C3JCRxQNTCE2qu9uTuH5dE3EGVnRhIQMe6rPDz0S8t3urLEOsMud6HI795m6zX2ujfUaqTw==}
|
||||
|
||||
lie@3.3.0:
|
||||
resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==}
|
||||
|
||||
lilconfig@3.1.2:
|
||||
resolution: {integrity: sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==}
|
||||
engines: {node: '>=14'}
|
||||
@ -4075,6 +4114,33 @@ packages:
|
||||
lodash: '*'
|
||||
lodash-es: '*'
|
||||
|
||||
lodash._arraycopy@3.0.0:
|
||||
resolution: {integrity: sha512-RHShTDnPKP7aWxlvXKiDT6IX2jCs6YZLCtNhOru/OX2Q/tzX295vVBK5oX1ECtN+2r86S0Ogy8ykP1sgCZAN0A==}
|
||||
|
||||
lodash._arrayeach@3.0.0:
|
||||
resolution: {integrity: sha512-Mn7HidOVcl3mkQtbPsuKR0Fj0N6Q6DQB77CtYncZcJc0bx5qv2q4Gl6a0LC1AN+GSxpnBDNnK3CKEm9XNA4zqQ==}
|
||||
|
||||
lodash._basecopy@3.0.1:
|
||||
resolution: {integrity: sha512-rFR6Vpm4HeCK1WPGvjZSJ+7yik8d8PVUdCJx5rT2pogG4Ve/2ZS7kfmO5l5T2o5V2mqlNIfSF5MZlr1+xOoYQQ==}
|
||||
|
||||
lodash._basefor@3.0.3:
|
||||
resolution: {integrity: sha512-6bc3b8grkpMgDcVJv9JYZAk/mHgcqMljzm7OsbmcE2FGUMmmLQTPHlh/dFqR8LA0GQ7z4K67JSotVKu5058v1A==}
|
||||
|
||||
lodash._bindcallback@3.0.1:
|
||||
resolution: {integrity: sha512-2wlI0JRAGX8WEf4Gm1p/mv/SZ+jLijpj0jyaE/AXeuQphzCgD8ZQW4oSpoN8JAopujOFGU3KMuq7qfHBWlGpjQ==}
|
||||
|
||||
lodash._createassigner@3.1.1:
|
||||
resolution: {integrity: sha512-LziVL7IDnJjQeeV95Wvhw6G28Z8Q6da87LWKOPWmzBLv4u6FAT/x5v00pyGW0u38UoogNF2JnD3bGgZZDaNEBw==}
|
||||
|
||||
lodash._getnative@3.9.1:
|
||||
resolution: {integrity: sha512-RrL9VxMEPyDMHOd9uFbvMe8X55X16/cGM5IgOKgRElQZutpX89iS6vwl64duTV1/16w5JY7tuFNXqoekmh1EmA==}
|
||||
|
||||
lodash._isiterateecall@3.0.9:
|
||||
resolution: {integrity: sha512-De+ZbrMu6eThFti/CSzhRvTKMgQToLxbij58LMfM8JnYDNSOjkjTCIaa8ixglOeGh2nyPlakbt5bJWJ7gvpYlQ==}
|
||||
|
||||
lodash._root@3.0.1:
|
||||
resolution: {integrity: sha512-O0pWuFSK6x4EXhM1dhZ8gchNtG7JMqBtrHdoUFUWXD7dJnNSUze1GuyQr5sOs0aCvgGeI3o/OJW8f4ca7FDxmQ==}
|
||||
|
||||
lodash.camelcase@4.3.0:
|
||||
resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==}
|
||||
|
||||
@ -4084,21 +4150,48 @@ packages:
|
||||
lodash.debounce@4.0.8:
|
||||
resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==}
|
||||
|
||||
lodash.escape@3.2.0:
|
||||
resolution: {integrity: sha512-n1PZMXgaaDWZDSvuNZ/8XOcYO2hOKDqZel5adtR30VKQAtoWs/5AOeFA0vPV8moiPzlqe7F4cP2tzpFewQyelQ==}
|
||||
|
||||
lodash.foreach@4.5.0:
|
||||
resolution: {integrity: sha512-aEXTF4d+m05rVOAUG3z4vZZ4xVexLKZGF0lIxuHZ1Hplpk/3B6Z1+/ICICYRLm7c41Z2xiejbkCkJoTlypoXhQ==}
|
||||
|
||||
lodash.isarguments@3.1.0:
|
||||
resolution: {integrity: sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==}
|
||||
|
||||
lodash.isarray@3.0.4:
|
||||
resolution: {integrity: sha512-JwObCrNJuT0Nnbuecmqr5DgtuBppuCvGD9lxjFpAzwnVtdGoDQ1zig+5W8k5/6Gcn0gZ3936HDAlGd28i7sOGQ==}
|
||||
|
||||
lodash.isplainobject@3.2.0:
|
||||
resolution: {integrity: sha512-P4wZnho5curNqeEq/x292Pb57e1v+woR7DJ84DURelKB46lby8aDEGVobSaYtzHdQBWQrJSdxcCwjlGOvvdIyg==}
|
||||
|
||||
lodash.isplainobject@4.0.6:
|
||||
resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==}
|
||||
|
||||
lodash.istypedarray@3.0.6:
|
||||
resolution: {integrity: sha512-lGWJ6N8AA3KSv+ZZxlTdn4f6A7kMfpJboeyvbFdE7IU9YAgweODqmOgdUHOA+c6lVWeVLysdaxciFXi+foVsWw==}
|
||||
|
||||
lodash.kebabcase@4.1.1:
|
||||
resolution: {integrity: sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==}
|
||||
|
||||
lodash.keys@3.1.2:
|
||||
resolution: {integrity: sha512-CuBsapFjcubOGMn3VD+24HOAPxM79tH+V6ivJL3CHYjtrawauDJHUk//Yew9Hvc6e9rbCrURGk8z6PC+8WJBfQ==}
|
||||
|
||||
lodash.keysin@3.0.8:
|
||||
resolution: {integrity: sha512-YDB/5xkL3fBKFMDaC+cfGV00pbiJ6XoJIfRmBhv7aR6wWtbCW6IzkiWnTfkiHTF6ALD7ff83dAtB3OEaSoyQPg==}
|
||||
|
||||
lodash.merge@3.3.2:
|
||||
resolution: {integrity: sha512-ZgGZpRhWLjivGUbjtApZR4HyLv/UAyoYqESVYkK4aLBJVHRrbFpG+GNnE9JPijliME4LkKM0SFI/WyOiBiv1+w==}
|
||||
|
||||
lodash.merge@4.6.2:
|
||||
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
|
||||
|
||||
lodash.mergewith@4.6.2:
|
||||
resolution: {integrity: sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==}
|
||||
|
||||
lodash.restparam@3.6.1:
|
||||
resolution: {integrity: sha512-L4/arjjuq4noiUJpt3yS6KIKDtJwNe2fIYgMqyYYKoeIfV1iEqvPwhCx23o+R9dzouGihDAPN1dTIRWa7zk8tw==}
|
||||
|
||||
lodash.snakecase@4.1.1:
|
||||
resolution: {integrity: sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==}
|
||||
|
||||
@ -4111,6 +4204,9 @@ packages:
|
||||
lodash.toarray@4.4.0:
|
||||
resolution: {integrity: sha512-QyffEA3i5dma5q2490+SgCvDN0pXLmRGSyAANuVi0HQ01Pkfr9fuoKQW8wm1wGBnJITs/mS7wQvS6VshUEBFCw==}
|
||||
|
||||
lodash.toplainobject@3.0.0:
|
||||
resolution: {integrity: sha512-wMI0Ju1bvSmnBS3EcRRH/3zDnZOPpDtMtNDzbbNMKuTrEpALsf+sPyMeogmv63Y11qZQO7H1xFzohIEGRMjPYA==}
|
||||
|
||||
lodash.truncate@4.4.2:
|
||||
resolution: {integrity: sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==}
|
||||
|
||||
@ -4459,6 +4555,9 @@ packages:
|
||||
package-manager-detector@0.2.5:
|
||||
resolution: {integrity: sha512-3dS7y28uua+UDbRCLBqltMBrbI+A5U2mI9YuxHRxIWYmLj3DwntEBmERYzIAQ4DMeuCUOBSak7dBHHoXKpOTYQ==}
|
||||
|
||||
pako@1.0.11:
|
||||
resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==}
|
||||
|
||||
parent-module@1.0.1:
|
||||
resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
|
||||
engines: {node: '>=6'}
|
||||
@ -4655,6 +4754,9 @@ packages:
|
||||
resolution: {integrity: sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
process-nextick-args@2.0.1:
|
||||
resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}
|
||||
|
||||
process@0.11.10:
|
||||
resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==}
|
||||
engines: {node: '>= 0.6.0'}
|
||||
@ -4701,6 +4803,9 @@ packages:
|
||||
react-is@18.3.1:
|
||||
resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==}
|
||||
|
||||
readable-stream@2.3.8:
|
||||
resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==}
|
||||
|
||||
readdirp@3.6.0:
|
||||
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
|
||||
engines: {node: '>=8.10.0'}
|
||||
@ -4825,6 +4930,9 @@ packages:
|
||||
resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==}
|
||||
engines: {node: '>=0.4'}
|
||||
|
||||
safe-buffer@5.1.2:
|
||||
resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==}
|
||||
|
||||
safe-json-parse@4.0.0:
|
||||
resolution: {integrity: sha512-RjZPPHugjK0TOzFrLZ8inw44s9bKox99/0AZW9o/BEQVrJfhI+fIHMErnPyRa89/yRXUUr93q+tiN6zhoVV4wQ==}
|
||||
|
||||
@ -4880,6 +4988,9 @@ packages:
|
||||
resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
setimmediate@1.0.5:
|
||||
resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==}
|
||||
|
||||
shebang-command@2.0.0:
|
||||
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
|
||||
engines: {node: '>=8'}
|
||||
@ -5010,6 +5121,9 @@ packages:
|
||||
resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
string_decoder@1.1.1:
|
||||
resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==}
|
||||
|
||||
strip-ansi@3.0.1:
|
||||
resolution: {integrity: sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@ -5168,6 +5282,9 @@ packages:
|
||||
tsconfig-paths@3.15.0:
|
||||
resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==}
|
||||
|
||||
tslib@1.14.1:
|
||||
resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==}
|
||||
|
||||
tslib@2.3.0:
|
||||
resolution: {integrity: sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==}
|
||||
|
||||
@ -8286,6 +8403,8 @@ snapshots:
|
||||
dependencies:
|
||||
fill-range: 7.1.1
|
||||
|
||||
browser-or-node@1.3.0: {}
|
||||
|
||||
browserslist-to-esbuild@2.1.1(browserslist@4.24.2):
|
||||
dependencies:
|
||||
browserslist: 4.24.2
|
||||
@ -8486,6 +8605,8 @@ snapshots:
|
||||
|
||||
core-js@3.39.0: {}
|
||||
|
||||
core-util-is@1.0.3: {}
|
||||
|
||||
cosmiconfig-typescript-loader@5.1.0(@types/node@20.17.9)(cosmiconfig@9.0.0(typescript@5.3.3))(typescript@5.3.3):
|
||||
dependencies:
|
||||
'@types/node': 20.17.9
|
||||
@ -9339,6 +9460,8 @@ snapshots:
|
||||
dependencies:
|
||||
flat-cache: 5.0.0
|
||||
|
||||
file-saver@2.0.5: {}
|
||||
|
||||
filelist@1.0.4:
|
||||
dependencies:
|
||||
minimatch: 5.1.6
|
||||
@ -9598,6 +9721,18 @@ snapshots:
|
||||
|
||||
htm@3.1.1: {}
|
||||
|
||||
html-docx-js-typescript@0.1.5:
|
||||
dependencies:
|
||||
browser-or-node: 1.3.0
|
||||
jszip: 3.10.1
|
||||
tslib: 1.14.1
|
||||
|
||||
html-docx-js@0.3.1:
|
||||
dependencies:
|
||||
jszip: 2.7.0
|
||||
lodash.escape: 3.2.0
|
||||
lodash.merge: 3.3.2
|
||||
|
||||
html-tags@3.3.1: {}
|
||||
|
||||
html-void-elements@3.0.0: {}
|
||||
@ -9625,6 +9760,8 @@ snapshots:
|
||||
|
||||
ignore@6.0.2: {}
|
||||
|
||||
immediate@3.0.6: {}
|
||||
|
||||
immer@9.0.21: {}
|
||||
|
||||
immutable@5.0.3: {}
|
||||
@ -9807,6 +9944,8 @@ snapshots:
|
||||
call-bound: 1.0.4
|
||||
get-intrinsic: 1.3.0
|
||||
|
||||
isarray@1.0.0: {}
|
||||
|
||||
isarray@2.0.5: {}
|
||||
|
||||
isexe@2.0.0: {}
|
||||
@ -9890,6 +10029,17 @@ snapshots:
|
||||
|
||||
jsonrepair@3.13.0: {}
|
||||
|
||||
jszip@2.7.0:
|
||||
dependencies:
|
||||
pako: 1.0.11
|
||||
|
||||
jszip@3.10.1:
|
||||
dependencies:
|
||||
lie: 3.3.0
|
||||
pako: 1.0.11
|
||||
readable-stream: 2.3.8
|
||||
setimmediate: 1.0.5
|
||||
|
||||
katex@0.16.11:
|
||||
dependencies:
|
||||
commander: 8.3.0
|
||||
@ -9926,6 +10076,10 @@ snapshots:
|
||||
'@lezer/lr': 1.4.2
|
||||
min-dash: 4.2.2
|
||||
|
||||
lie@3.3.0:
|
||||
dependencies:
|
||||
immediate: 3.0.6
|
||||
|
||||
lilconfig@3.1.2: {}
|
||||
|
||||
lines-and-columns@1.2.4: {}
|
||||
@ -9985,22 +10139,87 @@ snapshots:
|
||||
lodash: 4.17.21
|
||||
lodash-es: 4.17.21
|
||||
|
||||
lodash._arraycopy@3.0.0: {}
|
||||
|
||||
lodash._arrayeach@3.0.0: {}
|
||||
|
||||
lodash._basecopy@3.0.1: {}
|
||||
|
||||
lodash._basefor@3.0.3: {}
|
||||
|
||||
lodash._bindcallback@3.0.1: {}
|
||||
|
||||
lodash._createassigner@3.1.1:
|
||||
dependencies:
|
||||
lodash._bindcallback: 3.0.1
|
||||
lodash._isiterateecall: 3.0.9
|
||||
lodash.restparam: 3.6.1
|
||||
|
||||
lodash._getnative@3.9.1: {}
|
||||
|
||||
lodash._isiterateecall@3.0.9: {}
|
||||
|
||||
lodash._root@3.0.1: {}
|
||||
|
||||
lodash.camelcase@4.3.0: {}
|
||||
|
||||
lodash.clonedeep@4.5.0: {}
|
||||
|
||||
lodash.debounce@4.0.8: {}
|
||||
|
||||
lodash.escape@3.2.0:
|
||||
dependencies:
|
||||
lodash._root: 3.0.1
|
||||
|
||||
lodash.foreach@4.5.0: {}
|
||||
|
||||
lodash.isarguments@3.1.0: {}
|
||||
|
||||
lodash.isarray@3.0.4: {}
|
||||
|
||||
lodash.isplainobject@3.2.0:
|
||||
dependencies:
|
||||
lodash._basefor: 3.0.3
|
||||
lodash.isarguments: 3.1.0
|
||||
lodash.keysin: 3.0.8
|
||||
|
||||
lodash.isplainobject@4.0.6: {}
|
||||
|
||||
lodash.istypedarray@3.0.6: {}
|
||||
|
||||
lodash.kebabcase@4.1.1: {}
|
||||
|
||||
lodash.keys@3.1.2:
|
||||
dependencies:
|
||||
lodash._getnative: 3.9.1
|
||||
lodash.isarguments: 3.1.0
|
||||
lodash.isarray: 3.0.4
|
||||
|
||||
lodash.keysin@3.0.8:
|
||||
dependencies:
|
||||
lodash.isarguments: 3.1.0
|
||||
lodash.isarray: 3.0.4
|
||||
|
||||
lodash.merge@3.3.2:
|
||||
dependencies:
|
||||
lodash._arraycopy: 3.0.0
|
||||
lodash._arrayeach: 3.0.0
|
||||
lodash._createassigner: 3.1.1
|
||||
lodash._getnative: 3.9.1
|
||||
lodash.isarguments: 3.1.0
|
||||
lodash.isarray: 3.0.4
|
||||
lodash.isplainobject: 3.2.0
|
||||
lodash.istypedarray: 3.0.6
|
||||
lodash.keys: 3.1.2
|
||||
lodash.keysin: 3.0.8
|
||||
lodash.toplainobject: 3.0.0
|
||||
|
||||
lodash.merge@4.6.2: {}
|
||||
|
||||
lodash.mergewith@4.6.2: {}
|
||||
|
||||
lodash.restparam@3.6.1: {}
|
||||
|
||||
lodash.snakecase@4.1.1: {}
|
||||
|
||||
lodash.startcase@4.4.0: {}
|
||||
@ -10009,6 +10228,11 @@ snapshots:
|
||||
|
||||
lodash.toarray@4.4.0: {}
|
||||
|
||||
lodash.toplainobject@3.0.0:
|
||||
dependencies:
|
||||
lodash._basecopy: 3.0.1
|
||||
lodash.keysin: 3.0.8
|
||||
|
||||
lodash.truncate@4.4.2: {}
|
||||
|
||||
lodash.uniq@4.5.0: {}
|
||||
@ -10364,6 +10588,8 @@ snapshots:
|
||||
|
||||
package-manager-detector@0.2.5: {}
|
||||
|
||||
pako@1.0.11: {}
|
||||
|
||||
parent-module@1.0.1:
|
||||
dependencies:
|
||||
callsites: 3.1.0
|
||||
@ -10529,6 +10755,8 @@ snapshots:
|
||||
|
||||
prismjs@1.29.0: {}
|
||||
|
||||
process-nextick-args@2.0.1: {}
|
||||
|
||||
process@0.11.10: {}
|
||||
|
||||
progress@2.0.3: {}
|
||||
@ -10563,6 +10791,16 @@ snapshots:
|
||||
|
||||
react-is@18.3.1: {}
|
||||
|
||||
readable-stream@2.3.8:
|
||||
dependencies:
|
||||
core-util-is: 1.0.3
|
||||
inherits: 2.0.4
|
||||
isarray: 1.0.0
|
||||
process-nextick-args: 2.0.1
|
||||
safe-buffer: 5.1.2
|
||||
string_decoder: 1.1.1
|
||||
util-deprecate: 1.0.2
|
||||
|
||||
readdirp@3.6.0:
|
||||
dependencies:
|
||||
picomatch: 2.3.1
|
||||
@ -10714,6 +10952,8 @@ snapshots:
|
||||
has-symbols: 1.1.0
|
||||
isarray: 2.0.5
|
||||
|
||||
safe-buffer@5.1.2: {}
|
||||
|
||||
safe-json-parse@4.0.0:
|
||||
dependencies:
|
||||
rust-result: 1.0.0
|
||||
@ -10777,6 +11017,8 @@ snapshots:
|
||||
es-errors: 1.3.0
|
||||
es-object-atoms: 1.1.1
|
||||
|
||||
setimmediate@1.0.5: {}
|
||||
|
||||
shebang-command@2.0.0:
|
||||
dependencies:
|
||||
shebang-regex: 3.0.0
|
||||
@ -10928,6 +11170,10 @@ snapshots:
|
||||
define-properties: 1.2.1
|
||||
es-object-atoms: 1.1.1
|
||||
|
||||
string_decoder@1.1.1:
|
||||
dependencies:
|
||||
safe-buffer: 5.1.2
|
||||
|
||||
strip-ansi@3.0.1:
|
||||
dependencies:
|
||||
ansi-regex: 2.1.1
|
||||
@ -11112,6 +11358,8 @@ snapshots:
|
||||
minimist: 1.2.8
|
||||
strip-bom: 3.0.0
|
||||
|
||||
tslib@1.14.1: {}
|
||||
|
||||
tslib@2.3.0: {}
|
||||
|
||||
tslib@2.8.1: {}
|
||||
|
||||
@ -122,6 +122,14 @@
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
link
|
||||
@click.stop="handleExportReport(row)"
|
||||
v-hasPermi="['prison:evaluation-report:report:query']"
|
||||
>
|
||||
导出
|
||||
</el-button>
|
||||
<el-button
|
||||
type="warning"
|
||||
link
|
||||
@ -153,6 +161,9 @@
|
||||
|
||||
<!-- 新建报告对话框 -->
|
||||
<CreateReportDialog ref="createDialogRef" @success="loadReports" />
|
||||
|
||||
<!-- 导出报告对话框 -->
|
||||
<CreateReportOutput ref="exportDialogRef" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -163,6 +174,7 @@ import { PrisonerApi, type PrisonerVO } from '@/api/prison/prisoner'
|
||||
import { ReportApi, type ReportVO } from '@/api/prison/evaluation-report'
|
||||
import ReportEditDrawer from '../report/ReportEditDrawer.vue'
|
||||
import CreateReportDialog from '../report/CreateReportDialog.vue'
|
||||
import CreateReportOutput from '../report/CreateReportOutput.vue'
|
||||
|
||||
defineOptions({ name: 'PrisonerReportManage' })
|
||||
|
||||
@ -188,6 +200,7 @@ const reportList = ref<ReportVO[]>([])
|
||||
const selectedPrisoner = ref<PrisonerVO | null>(null)
|
||||
|
||||
const editDrawerRef = ref()
|
||||
const exportDialogRef = ref()
|
||||
const createDialogRef = ref()
|
||||
|
||||
/** 获取服刑人员列表 */
|
||||
@ -256,6 +269,11 @@ const handleEditReport = (row: ReportVO) => {
|
||||
editDrawerRef.value?.open(row.id!, selectedPrisoner.value!.id)
|
||||
}
|
||||
|
||||
/** 导出报告 */
|
||||
const handleExportReport = (row: ReportVO) => {
|
||||
exportDialogRef.value?.open(row.id!, selectedPrisoner.value!.id)
|
||||
}
|
||||
|
||||
/** 提交报告审核 */
|
||||
const handleSubmitReport = async (row: ReportVO) => {
|
||||
try {
|
||||
|
||||
297
src/views/prison/evaluation-report/report/CreateReportOutput.vue
Normal file
297
src/views/prison/evaluation-report/report/CreateReportOutput.vue
Normal file
@ -0,0 +1,297 @@
|
||||
<template>
|
||||
<Dialog :title="'评估报告'" v-model="dialogVisible" width="900px">
|
||||
<div v-loading="loading" class="report-edit-container" ref="previewRef">
|
||||
<template v-if="selectedReport">
|
||||
<!-- 基本信息区 -->
|
||||
<div class="basic-info-section">
|
||||
<span class="basic-info-item">服刑人员:{{ selectedReport.prisonerName }} ({{ selectedReport.prisonerNo }})</span>
|
||||
<span class="basic-info-item">监区:{{ selectedReport.areaName || '-' }}</span>
|
||||
<span class="basic-info-item">评估日期:{{ formatDateTime(selectedReport.evaluationDate, 'YYYY-MM-DD') }}</span>
|
||||
<span class="basic-info-item">模板:{{ selectedReport.templateName }}</span>
|
||||
<span class="basic-info-item">风险等级:{{ getDictLabel(DICT_TYPE.PRISON_RISK_LEVEL, selectedReport.riskLevel) }}</span>
|
||||
<span class="basic-info-item">状态:{{ getDictLabel(DICT_TYPE.PRISON_REPORT_STATUS, selectedReport.status) }}</span>
|
||||
</div>
|
||||
|
||||
<!-- 维度分析区 - 新版维度分析面板 -->
|
||||
<div class="dimension-section" v-if="dimensions.length > 0 && selectedReport">
|
||||
维度分析
|
||||
</div>
|
||||
<div v-for="item in dimensionAnalysisPanelRef" :key="item.id" class="dimension-item">
|
||||
<div class="dimension-item-title">{{ item.name }}</div>
|
||||
<div style="white-space: pre-line; line-height: 1.5;">{{ item.aiAnalysis?.replace(/##/g, '') }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template v-else-if="!loading">
|
||||
<el-empty description="报告不存在或已被删除" />
|
||||
</template>
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-button @click="handleClose">取消</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { DICT_TYPE, getDictLabel } from '@/utils/dict'
|
||||
import { formatDateTime } from '@/utils/formatTime'
|
||||
import download from '@/utils/download'
|
||||
import { ReportApi, ReportVO, DimensionDataApi, DimensionDataVO, DimensionApi, DimensionVO } from '@/api/prison/evaluation-report'
|
||||
import { PrisonerApi } from '@/api/prison/prisoner'
|
||||
import { asBlob } from 'html-docx-js-typescript'
|
||||
import { saveAs } from 'file-saver'
|
||||
|
||||
defineOptions({ name: 'CreateReportOutput' })
|
||||
|
||||
const { t } = useI18n()
|
||||
const message = useMessage()
|
||||
const router = useRouter()
|
||||
|
||||
// 抽屉状态
|
||||
const dialogVisible = ref(false)
|
||||
const drawerTitle = ref('编辑评估报告')
|
||||
const loading = ref(false)
|
||||
const saving = ref(false)
|
||||
const aiGenerating = ref(false)
|
||||
const aiGeneratingDimension = ref<number | undefined>(undefined)
|
||||
|
||||
// 数据状态
|
||||
const reportId = ref<number>()
|
||||
const selectedReport = ref<ReportVO | null>(null)
|
||||
const dimensionDataList = ref<DimensionDataVO[]>([])
|
||||
const dimensions = ref<any[]>([])
|
||||
const dimensionAnalysisPanelRef = ref<DimensionDataVO[]>([])
|
||||
const previewRef = ref<HTMLElement | null>(null)
|
||||
|
||||
/** 打开抽屉 */
|
||||
const open = async (id: number, prisonerId?: number) => {
|
||||
reportId.value = id
|
||||
dialogVisible.value = true
|
||||
await loadReportDetail(id)
|
||||
try {
|
||||
exportToWord()
|
||||
} catch {
|
||||
} finally {
|
||||
handleClose()
|
||||
}
|
||||
}
|
||||
|
||||
/** 加载报告详情 */
|
||||
const loadReportDetail = async (id: number) => {
|
||||
loading.value = true
|
||||
try {
|
||||
selectedReport.value = await ReportApi.getReport(id)
|
||||
dimensionDataList.value = await DimensionDataApi.getDimensionDataListByReportId(id)
|
||||
drawerTitle.value = selectedReport.value?.title || `${selectedReport.value?.prisonerName} - 评估报告`
|
||||
|
||||
// 监区信息兜底(报告未返回监区时,从罪犯档案补齐)
|
||||
if (selectedReport.value?.prisonerId && !selectedReport.value.areaName) {
|
||||
const prisoner = await PrisonerApi.get(selectedReport.value.prisonerId)
|
||||
if (prisoner?.prisonAreaName) {
|
||||
selectedReport.value.areaName = prisoner.prisonAreaName
|
||||
selectedReport.value.areaId = prisoner.prisonAreaId
|
||||
}
|
||||
}
|
||||
|
||||
// 加载维度配置
|
||||
if (selectedReport.value?.templateId) {
|
||||
try {
|
||||
const dimensionList = await DimensionApi.getDimensionsByTemplateId(selectedReport.value.templateId)
|
||||
if (dimensionList && dimensionList.length > 0) {
|
||||
console.log(dimensionList);
|
||||
dimensions.value = dimensionList
|
||||
} else {
|
||||
// 使用默认维度配置
|
||||
dimensions.value = getDefaultDimensions(selectedReport.value.templateId)
|
||||
}
|
||||
|
||||
} catch {
|
||||
dimensions.value = getDefaultDimensions(selectedReport.value.templateId)
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedReport.value?.id && dimensions.value.length > 0) {
|
||||
const list = await DimensionDataApi.getDimensionDataListByReportId(selectedReport.value.id)
|
||||
console.log(list, dimensions.value);
|
||||
dimensionAnalysisPanelRef.value = dimensions.value.map(item => {
|
||||
return {
|
||||
...item,
|
||||
aiAnalysis: list.find(analys => analys.dimensionId === item.id)?.aiAnalysis
|
||||
}
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
message.error(error?.msg || '加载报告失败')
|
||||
selectedReport.value = null
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 获取默认维度配置 */
|
||||
const getDefaultDimensions = (templateId: number): DimensionVO[] => {
|
||||
return [
|
||||
{ id: 1, templateId, name: '基本信息', dimensionType: 1, aiEnabled: 0, status: 0, dataSources: ['prisoner'] },
|
||||
{ id: 2, templateId, name: '犯罪情况分析', dimensionType: 1, aiEnabled: 1, status: 0, dataSources: ['prisoner', 'risk'] },
|
||||
{ id: 3, templateId, name: '服刑表现评估', dimensionType: 1, aiEnabled: 1, status: 0, dataSources: ['score', 'violation', 'reward'] },
|
||||
{ id: 4, templateId, name: '消费行为分析', dimensionType: 1, aiEnabled: 1, status: 0, dataSources: ['consumption'] },
|
||||
{ id: 5, templateId, name: '综合评估结论', dimensionType: 1, aiEnabled: 1, status: 0, dataSources: ['prisoner', 'psychology'] }
|
||||
]
|
||||
}
|
||||
|
||||
/** 关闭 */
|
||||
const handleClose = () => {
|
||||
dialogVisible.value = false
|
||||
selectedReport.value = null
|
||||
dimensionDataList.value = []
|
||||
}
|
||||
|
||||
/** 风险等级变化 */
|
||||
const handleRiskLevelChange = () => {
|
||||
// 风险等级变化会自动保存到 selectedReport
|
||||
}
|
||||
|
||||
/** 导出为Word文档 - 直接使用预览容器的HTML */
|
||||
const exportToWord = async () => {
|
||||
try {
|
||||
loading.value = true
|
||||
|
||||
if (!previewRef.value) {
|
||||
message.error('预览内容未加载完成')
|
||||
return
|
||||
}
|
||||
|
||||
// 获取预览容器的HTML内容
|
||||
let previewHTML = previewRef.value.innerHTML
|
||||
|
||||
// 将文本内容中的 \n 转换为 <br> 标签,使 Word 能够正确换行
|
||||
// 使用临时 DOM 解析 HTML,处理文本节点中的换行符
|
||||
const tempDiv = document.createElement('div')
|
||||
tempDiv.innerHTML = previewHTML
|
||||
|
||||
const processTextNodes = (node: Node) => {
|
||||
if (node.nodeType === Node.TEXT_NODE) {
|
||||
// 将文本节点中的 \n 替换为 <br>
|
||||
const text = node.textContent || ''
|
||||
if (text.includes('\n')) {
|
||||
const span = document.createElement('span')
|
||||
span.innerHTML = text.replace(/\n/g, '<br>')
|
||||
node.parentNode?.replaceChild(span, node)
|
||||
}
|
||||
} else if (node.nodeType === Node.ELEMENT_NODE) {
|
||||
// 递归处理子节点
|
||||
Array.from(node.childNodes).forEach(processTextNodes)
|
||||
}
|
||||
}
|
||||
|
||||
processTextNodes(tempDiv)
|
||||
previewHTML = tempDiv.innerHTML
|
||||
|
||||
// 构建完整的HTML文档,使用与预览页面一致的样式
|
||||
const fullHTML = `
|
||||
<html xmlns:o='urn:schemas-microsoft-com:office:office' xmlns:w='urn:schemas-microsoft-com:office:word'>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Microsoft YaHei', '微软雅黑', SimSun, Arial, sans-serif;
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
margin: 20px;
|
||||
padding: 20px;
|
||||
}
|
||||
.basic-info-section {
|
||||
padding: 15px 20px;
|
||||
color: black;
|
||||
font-size: 14px;
|
||||
}
|
||||
.basic-info-item{
|
||||
margin-right: 30px;
|
||||
}
|
||||
|
||||
.dimension-section {
|
||||
flex: 1;
|
||||
padding: 15px 20px 0;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: black;
|
||||
}
|
||||
.dimension-item {
|
||||
padding: 0 40px;
|
||||
font-size: 14px;
|
||||
color: black;
|
||||
}
|
||||
.dimension-item-title {
|
||||
font-size: 15px;
|
||||
padding: 15px 0;
|
||||
font-weight: 500;
|
||||
color: black;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
${previewHTML}
|
||||
</body>
|
||||
</html>
|
||||
`
|
||||
|
||||
// 转换为Blob (asBlob返回Promise)
|
||||
const converted = await asBlob(fullHTML)
|
||||
|
||||
// 下载文件
|
||||
const fileName = `${'评估报告'}_${new Date().toLocaleDateString('zh-CN')}.docx`
|
||||
saveAs(converted as Blob, fileName)
|
||||
|
||||
message.success('Word文档导出成功')
|
||||
} catch (error) {
|
||||
console.error('导出Word失败:', error)
|
||||
message.error('导出Word失败,请重试')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.report-edit-container {
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
|
||||
.drawer-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.basic-info-section {
|
||||
padding: 15px 20px;
|
||||
color: black;
|
||||
font-size: 14px;
|
||||
}
|
||||
.basic-info-item{
|
||||
margin-right: 30px;
|
||||
}
|
||||
|
||||
.dimension-section {
|
||||
flex: 1;
|
||||
padding: 15px 20px 0;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: black;
|
||||
}
|
||||
.dimension-item {
|
||||
padding: 0 40px;
|
||||
font-size: 14px;
|
||||
color: black;
|
||||
}
|
||||
.dimension-item-title {
|
||||
font-size: 15px;
|
||||
padding: 15px 0;
|
||||
font-weight: 500;
|
||||
color: black;
|
||||
}
|
||||
</style>
|
||||
@ -282,6 +282,15 @@
|
||||
>
|
||||
查看答案
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="scope.row.status === 3"
|
||||
link
|
||||
type="primary"
|
||||
size="small"
|
||||
@click="handleViewOutput(scope.row)"
|
||||
>
|
||||
导出
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="scope.row.status === 1"
|
||||
link
|
||||
@ -328,6 +337,9 @@
|
||||
|
||||
<!-- 代填弹窗 -->
|
||||
<AgentFillDialog ref="agentFillDialogRef" @success="loadPrisonerProgress" />
|
||||
|
||||
<!-- 问卷导出弹窗 -->
|
||||
<QuestionnaireOutput ref="outputDialogRef" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
@ -337,6 +349,7 @@ import { formatDateTime } from '@/utils/formatTime'
|
||||
import AnswerDetailDialog from '@/views/prison/questionnairerecord/AnswerDetailDialog.vue'
|
||||
import AgentFillDialog from './AgentFillDialog.vue'
|
||||
import { QuestionnaireTaskApi } from '@/api/prison/questionnaire-task'
|
||||
import QuestionnaireOutput from '@/views/prison/questionnairerecord/QuestionnaireOutputfile.vue'
|
||||
|
||||
defineOptions({ name: 'TaskDetailDialog' })
|
||||
|
||||
@ -527,6 +540,19 @@ const handleViewAnswer = (row: any) => {
|
||||
answerDetailDialogRef.value?.open(row.id)
|
||||
}
|
||||
|
||||
/** 导出答题详情 */
|
||||
const outputDialogRef = ref()
|
||||
const handleViewOutput = async (row: any) => {
|
||||
if (!row.id) {
|
||||
ElMessage.warning('该人员暂无答题记录')
|
||||
return
|
||||
}
|
||||
try {
|
||||
await outputDialogRef.value.open(row.id)
|
||||
} finally {
|
||||
}
|
||||
}
|
||||
|
||||
/** 通知人员 */
|
||||
const handleNotifyPrisoner = async (row: any) => {
|
||||
try {
|
||||
|
||||
574
src/views/prison/questionnairerecord/QuestionnaireOutputfile.vue
Normal file
574
src/views/prison/questionnairerecord/QuestionnaireOutputfile.vue
Normal file
@ -0,0 +1,574 @@
|
||||
<template>
|
||||
<Dialog style="display: none;" title="问卷预览" v-model="dialogVisible" width="900px" :fullscreen="false">
|
||||
<div ref="previewRef" class="questionnaire-preview" v-loading="loading">
|
||||
<!-- 问卷头部信息 -->
|
||||
<h1 class="preview-header">{{ recordInfo?.questionnaireName }}</h1>
|
||||
|
||||
<!-- 问卷说明 -->
|
||||
<div v-if="recordInfo?.description" class="preview-description">
|
||||
<div class="section-title">问卷说明</div>
|
||||
<div class="description-content" v-html="recordInfo.description"></div>
|
||||
</div>
|
||||
|
||||
<!-- 填写说明 -->
|
||||
<div v-if="recordInfo?.instruction" class="preview-instruction">
|
||||
<div class="section-title">填写说明</div>
|
||||
<div class="instruction-content">{{ recordInfo.instruction }}</div>
|
||||
</div>
|
||||
|
||||
<!-- 问题列表(按分区显示) -->
|
||||
<div class="preview-questions">
|
||||
<template v-for="partition in partitions" :key="partition.name || 'default'">
|
||||
<!-- 分区标题 -->
|
||||
<div v-if="partition.name" class="partition-title">
|
||||
{{ partition.name }}
|
||||
<span class="question-count">({{ partition.questions.length }} 道题)</span>
|
||||
</div>
|
||||
|
||||
<!-- 问题列表 -->
|
||||
<div class="question-items">
|
||||
<div
|
||||
v-for="(questionWithAnswer, index) in partition.questions"
|
||||
:key="questionWithAnswer.question.id"
|
||||
class="question-item"
|
||||
>
|
||||
<span class="question-index">{{ index + 1 }}.</span>
|
||||
<span class="question-title">{{ questionWithAnswer.question.title }}</span>
|
||||
|
||||
<!-- 帮助说明 -->
|
||||
<span v-if="questionWithAnswer.question.helpText" class="question-help-inline">
|
||||
{{ questionWithAnswer.question.helpText }}
|
||||
</span>
|
||||
|
||||
<!-- 单选/多选题 -->
|
||||
<span v-if="questionWithAnswer.question.type === 1 || questionWithAnswer.question.type === 2" class="question-options-inline">
|
||||
<span
|
||||
v-for="option in getQuestionOptions(questionWithAnswer.question)"
|
||||
:key="option.label"
|
||||
class="option-item"
|
||||
>
|
||||
<span v-if="questionWithAnswer.question.type === 1 && questionWithAnswer.answer?.answerText?.trim() === option.label">
|
||||
☑
|
||||
{{ option.label }}
|
||||
</span>
|
||||
<span v-if="questionWithAnswer.question.type === 1 && questionWithAnswer.answer?.answerText?.trim() !== option.label">
|
||||
☐
|
||||
{{ option.label }}
|
||||
</span>
|
||||
<span v-if="questionWithAnswer.question.type === 2 && getSelectedLabels(questionWithAnswer.answer).includes(option.label)">
|
||||
☑
|
||||
{{ option.label }}
|
||||
</span>
|
||||
<span v-if="questionWithAnswer.question.type === 2 && !getSelectedLabels(questionWithAnswer.answer).includes(option.label)">
|
||||
☐
|
||||
{{ option.label }}
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<!-- 填空题 -->
|
||||
<span v-else-if="questionWithAnswer.question.type === 3" class="question-input-inline">
|
||||
{{ getAnswerDisplayValue(questionWithAnswer.answer) }}
|
||||
</span>
|
||||
|
||||
<!-- 评分题 -->
|
||||
<span v-else-if="questionWithAnswer.question.type === 4" class="question-rating-inline">
|
||||
<span class="question-input-inline">{{ getAnswerDisplayValue(questionWithAnswer.answer) }}</span>
|
||||
</span>
|
||||
|
||||
<!-- 日期题 -->
|
||||
<span v-else-if="questionWithAnswer.question.type === 5" class="question-date-inline">
|
||||
<span class="date-info" v-if="getRangeValue(questionWithAnswer.question, 'min') || getRangeValue(questionWithAnswer.question, 'max')">
|
||||
日期范围:{{ getRangeValue(questionWithAnswer.question, 'min') || '无限制' }} ~ {{ getRangeValue(questionWithAnswer.question, 'max') || '无限制' }}
|
||||
</span>
|
||||
<span class="question-input-inline">{{ getAnswerDisplayValue(questionWithAnswer.answer) }}</span>
|
||||
</span>
|
||||
|
||||
<!-- 数字题 -->
|
||||
<span v-else-if="questionWithAnswer.question.type === 6" class="question-number-inline">
|
||||
<span class="question-input-inline">{{ getAnswerDisplayValue(questionWithAnswer.answer) }}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<el-empty v-if="partitions.length === 0 && !loading" description="暂无问题" />
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<el-button type="primary" @click="exportToWord" :loading="loading">导出Word</el-button>
|
||||
<el-button @click="dialogVisible = false">关 闭</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
import { QuestionnaireRecordApi, type QuestionnaireRecord } from '@/api/prison/questionnairerecord'
|
||||
import { AnswerApi, type Answer } from '@/api/prison/answer'
|
||||
import { QuestionApi, Question } from '@/api/prison/question'
|
||||
import { asBlob } from 'html-docx-js-typescript'
|
||||
import { saveAs } from 'file-saver'
|
||||
|
||||
defineOptions({ name: 'QuestionnaireOutput' })
|
||||
|
||||
const message = useMessage()
|
||||
const dialogVisible = ref(false)
|
||||
const loading = ref(false)
|
||||
const previewRef = ref<HTMLElement | null>(null)
|
||||
|
||||
const recordInfo = ref<QuestionnaireRecord | null>(null)
|
||||
const answers = ref<Answer[]>([])
|
||||
|
||||
const questions = ref<Question[]>([])
|
||||
const questionnaireInfo = ref<any>(null) // 问卷详细信息(包含description和instruction)
|
||||
|
||||
// 分区列表(带答案)
|
||||
interface QuestionWithAnswer {
|
||||
question: Question
|
||||
answer?: Answer
|
||||
index: number
|
||||
}
|
||||
|
||||
const partitions = computed(() => {
|
||||
const partMap = new Map<string, QuestionWithAnswer[]>()
|
||||
|
||||
questions.value.forEach((q, index) => {
|
||||
const partName = q.partName || ''
|
||||
const answer = answers.value.find(a => a.questionId === q.id)
|
||||
|
||||
if (!partMap.has(partName)) {
|
||||
partMap.set(partName, [])
|
||||
}
|
||||
partMap.get(partName)!.push({
|
||||
question: q,
|
||||
answer,
|
||||
index: index + 1
|
||||
})
|
||||
})
|
||||
|
||||
// 按分区排序
|
||||
const sortedParts = Array.from(partMap.entries())
|
||||
.sort((a, b) => {
|
||||
const sortA = a[1][0]?.question.partSort ?? 0
|
||||
const sortB = b[1][0]?.question.partSort ?? 0
|
||||
return sortA - sortB
|
||||
})
|
||||
|
||||
// 构建分区列表
|
||||
const result: Array<{ name: string; questions: QuestionWithAnswer[] }> = []
|
||||
|
||||
// 添加默认分区
|
||||
const defaultQuestions = sortedParts.find(([name]) => !name)
|
||||
if (defaultQuestions) {
|
||||
result.push({
|
||||
name: '',
|
||||
questions: defaultQuestions[1]
|
||||
})
|
||||
}
|
||||
|
||||
// 添加其他分区
|
||||
sortedParts
|
||||
.filter(([name]) => name)
|
||||
.forEach(([name, qs]) => {
|
||||
result.push({
|
||||
name,
|
||||
questions: qs
|
||||
})
|
||||
})
|
||||
|
||||
return result
|
||||
})
|
||||
|
||||
/** 根据问题ID获取答案 */
|
||||
const getAnswerByQuestionId = (questionId: number): Answer | undefined => {
|
||||
return answers.value.find(a => a.questionId === questionId)
|
||||
}
|
||||
|
||||
/** 获取答案显示值(兼容不同字段名) */
|
||||
const getAnswerDisplayValue = (answer?: Answer): string => {
|
||||
if (!answer) return ''
|
||||
// 优先使用 answerText,其次使用 optionIds
|
||||
return answer.answerText || answer.optionIds || '-'
|
||||
}
|
||||
|
||||
/** 问卷类型标签 */
|
||||
const getTypeLabel = (type: number) => {
|
||||
const options = getIntDictOptions(DICT_TYPE.PRISON_QUESTIONNAIRE_TYPE)
|
||||
return options.find(o => o.value === type)?.label || '未知'
|
||||
}
|
||||
|
||||
/** 获取问题选项 */
|
||||
const getQuestionOptions = (question: Question) => {
|
||||
if (!question.options) return []
|
||||
try {
|
||||
const parsed = JSON.parse(question.options)
|
||||
// 标准化选项格式,添加索引作为 value
|
||||
if (Array.isArray(parsed)) {
|
||||
return parsed.map((opt, index) => ({
|
||||
label: opt.label ?? opt.text ?? opt.name ?? String(opt),
|
||||
value: String(index), // 使用索引作为值
|
||||
score: opt.score ?? 0
|
||||
}))
|
||||
}
|
||||
return []
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
/** 获取范围值(用于日期、数字、评分题) */
|
||||
const getRangeValue = (question: Question, key: 'min' | 'max') => {
|
||||
if (!question.options) return undefined
|
||||
try {
|
||||
const obj = JSON.parse(question.options)
|
||||
return obj[key] || undefined
|
||||
} catch {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
/** 安全获取多选答案的标签数组 */
|
||||
const getSelectedLabels = (answer?: Answer): string[] => {
|
||||
if (!answer?.answerText) return []
|
||||
return answer.answerText.split(',').map(s => s.trim())
|
||||
}
|
||||
|
||||
/** 判断选项是否被选中(支持单选和多选)- 已废弃,改用模板直接绑定 */
|
||||
const isOptionSelected = (answer?: Answer, optionValue?: string, optionLabel?: string, questionType?: number): boolean => {
|
||||
if (!answer || !optionValue || !optionLabel) {
|
||||
return false
|
||||
}
|
||||
|
||||
// 多选(type === 2):answerText 是逗号分隔的标签列表
|
||||
if (questionType === 2 && answer.answerText && answer.answerText.includes(',')) {
|
||||
const answerText = answer.answerText
|
||||
const selectedLabels = answerText.split(',').map(s => s.trim())
|
||||
return selectedLabels.includes(optionLabel)
|
||||
}
|
||||
|
||||
// 单选(type === 1):answerText 等于选项标签(去除首尾空格后比较)
|
||||
if (questionType === 1 && answer.answerText) {
|
||||
return answer.answerText.trim() === optionLabel.trim()
|
||||
}
|
||||
|
||||
// 兼容:没有 questionType 时,根据是否有逗号判断
|
||||
if (answer.answerText && answer.answerText.includes(',')) {
|
||||
const answerText = answer.answerText
|
||||
const selectedLabels = answerText.split(',').map(s => s.trim())
|
||||
return selectedLabels.includes(optionLabel)
|
||||
}
|
||||
|
||||
if (answer.answerText?.trim() === optionLabel?.trim()) return true
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = async (recordId: number) => {
|
||||
dialogVisible.value = true
|
||||
loading.value = true
|
||||
|
||||
try {
|
||||
// 并行加载数据
|
||||
const [recordData, answerList] = await Promise.all([
|
||||
QuestionnaireRecordApi.getQuestionnaireRecord(recordId),
|
||||
AnswerApi.getAnswersByAssessmentRecordId(recordId)
|
||||
])
|
||||
|
||||
recordInfo.value = recordData
|
||||
answers.value = answerList
|
||||
|
||||
// 加载问题列表
|
||||
if (recordData.questionnaireId) {
|
||||
const questionsData = await QuestionApi.getQuestionPage({
|
||||
pageNo: 1,
|
||||
pageSize: 200,
|
||||
questionnaireId: recordData.questionnaireId
|
||||
})
|
||||
questions.value = questionsData.list
|
||||
}
|
||||
|
||||
// 加载问卷详细信息(用于导出Word时获取description和instruction)
|
||||
if (recordData.questionnaireId) {
|
||||
try {
|
||||
const { QuestionnaireApi } = await import('@/api/prison/questionnaire')
|
||||
questionnaireInfo.value = await QuestionnaireApi.getQuestionnaire(recordData.questionnaireId)
|
||||
} catch (error) {
|
||||
console.warn('加载问卷详细信息失败:', error)
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载答题详情失败:', error)
|
||||
} finally {
|
||||
loading.value = false
|
||||
exportToWord()
|
||||
dialogVisible.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 跳转到填写页面 */
|
||||
const openFillPage = () => {
|
||||
// TODO: 实现跳转到填写页面的逻辑
|
||||
message.info('填写页面功能待实现')
|
||||
}
|
||||
|
||||
/** 导出为Word文档 - 直接使用预览容器的HTML */
|
||||
const exportToWord = async () => {
|
||||
try {
|
||||
loading.value = true
|
||||
|
||||
if (!previewRef.value) {
|
||||
message.error('预览内容未加载完成')
|
||||
return
|
||||
}
|
||||
|
||||
// 获取预览容器的HTML内容
|
||||
const previewHTML = previewRef.value.innerHTML
|
||||
|
||||
// 构建完整的HTML文档,使用与预览页面一致的样式
|
||||
const fullHTML = `
|
||||
<html xmlns:o='urn:schemas-microsoft-com:office:office' xmlns:w='urn:schemas-microsoft-com:office:word'>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>${recordInfo.value?.questionnaireName || '问卷'}</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Microsoft YaHei', '微软雅黑', SimSun, Arial, sans-serif;
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
margin: 20px;
|
||||
padding: 20px;
|
||||
}
|
||||
h1 {
|
||||
font-weight: 500;
|
||||
color: #000;
|
||||
text-align: center;
|
||||
}
|
||||
.section-title {
|
||||
margin: 15px 0;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #000;
|
||||
}
|
||||
.description-content {
|
||||
color: #000;
|
||||
line-height: 1.8;
|
||||
font-size: 14px;
|
||||
}
|
||||
.instruction-content {
|
||||
color: #000;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
line-height: 1.8;
|
||||
}
|
||||
.partition-title {
|
||||
margin-bottom: 16px;
|
||||
margin-top: 24px;
|
||||
color: #000;
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
}
|
||||
.partition-title:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
.question-count {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
font-weight: normal;
|
||||
margin-left: 4px;
|
||||
}
|
||||
.question-item {
|
||||
font-size: 14px;
|
||||
color: #000;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 16px;
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
.question-index {
|
||||
margin-right: 4px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.question-title {
|
||||
margin-right: 8px;
|
||||
}
|
||||
.question-help-inline {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
color: #909399;
|
||||
font-size: 13px;
|
||||
padding: 4px 8px;
|
||||
background: #f4f4f5;
|
||||
border-radius: 4px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
.question-options-inline {
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
}
|
||||
.option-item {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.question-rating-inline,
|
||||
.question-date-inline,
|
||||
.question-number-inline {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
flex-wrap: wrap;
|
||||
margin-left: 8px;
|
||||
}
|
||||
.rating-info,
|
||||
.date-info,
|
||||
.number-info {
|
||||
display: inline-flex;
|
||||
gap: 16px;
|
||||
color: #909399;
|
||||
font-size: 13px;
|
||||
}
|
||||
.question-input-inline {
|
||||
color: #000;
|
||||
font-size: 14px;
|
||||
line-height: 2.2;
|
||||
text-decoration: underline;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
${previewHTML}
|
||||
</body>
|
||||
</html>
|
||||
`
|
||||
|
||||
// 转换为Blob (asBlob返回Promise)
|
||||
const converted = await asBlob(fullHTML)
|
||||
|
||||
// 下载文件
|
||||
const fileName = `${recordInfo.value?.questionnaireName || '问卷'}_${new Date().toLocaleDateString('zh-CN')}.docx`
|
||||
saveAs(converted as Blob, fileName)
|
||||
|
||||
message.success('Word文档导出成功')
|
||||
} catch (error) {
|
||||
console.error('导出Word失败:', error)
|
||||
message.error('导出Word失败,请重试')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({ open, exportToWord })
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.questionnaire-preview {
|
||||
padding: 20px;
|
||||
background: #f5f7fa;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-weight: 500;
|
||||
color: #000;
|
||||
text-align: center;
|
||||
}
|
||||
.section-title {
|
||||
margin: 15px 0;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #000;
|
||||
}
|
||||
.description-content {
|
||||
color: #000;
|
||||
line-height: 1.8;
|
||||
font-size: 14px;
|
||||
}
|
||||
.instruction-content {
|
||||
color: #000;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
line-height: 1.8;
|
||||
}
|
||||
.partition-title {
|
||||
margin-bottom: 16px;
|
||||
margin-top: 24px;
|
||||
color: #000;
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
}
|
||||
.partition-title:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
.question-count {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
font-weight: normal;
|
||||
margin-left: 4px;
|
||||
}
|
||||
.question-item {
|
||||
font-size: 14px;
|
||||
color: #000;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 16px;
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
.question-index {
|
||||
margin-right: 4px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.question-title {
|
||||
margin-right: 8px;
|
||||
}
|
||||
.question-help-inline {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
color: #909399;
|
||||
font-size: 13px;
|
||||
padding: 4px 8px;
|
||||
background: #f4f4f5;
|
||||
border-radius: 4px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
.question-options-inline {
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
}
|
||||
.option-item {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.question-rating-inline,
|
||||
.question-date-inline,
|
||||
.question-number-inline {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
flex-wrap: wrap;
|
||||
margin-left: 8px;
|
||||
}
|
||||
.rating-info,
|
||||
.date-info,
|
||||
.number-info {
|
||||
display: inline-flex;
|
||||
gap: 16px;
|
||||
color: #909399;
|
||||
font-size: 13px;
|
||||
}
|
||||
.question-input-inline {
|
||||
color: #000;
|
||||
font-size: 14px;
|
||||
line-height: 2.2;
|
||||
text-decoration: underline;
|
||||
}
|
||||
</style>
|
||||
@ -170,6 +170,15 @@
|
||||
>
|
||||
查看详情
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="scope.row.status === 3"
|
||||
link
|
||||
type="success"
|
||||
@click="handleViewOutput(scope.row.id)"
|
||||
v-hasPermi="['prison:questionnaire-record:query']"
|
||||
>
|
||||
导出
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="scope.row.status === 1"
|
||||
link
|
||||
@ -245,6 +254,9 @@
|
||||
|
||||
<!-- 答题详情弹窗 -->
|
||||
<AnswerDetailDialog ref="answerDetailDialogRef" />
|
||||
|
||||
<!-- 问卷导出弹窗 -->
|
||||
<QuestionnaireOutput ref="outputDialogRef" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
@ -258,6 +270,7 @@ import QuestionnaireRecordForm from './QuestionnaireRecordForm.vue'
|
||||
import InitiateAssessmentDialog from './InitiateAssessmentDialog.vue'
|
||||
import ManualScoreDialog from './ManualScoreDialog.vue'
|
||||
import AnswerDetailDialog from './AnswerDetailDialog.vue'
|
||||
import QuestionnaireOutput from './QuestionnaireOutputfile.vue'
|
||||
|
||||
/** 问卷答题记录/测评记录 列表 */
|
||||
defineOptions({ name: 'QuestionnaireRecord' })
|
||||
@ -367,6 +380,17 @@ const handleViewDetail = (id: number) => {
|
||||
answerDetailDialogRef.value.open(id)
|
||||
}
|
||||
|
||||
/** 导出答题详情 */
|
||||
const outputDialogRef = ref()
|
||||
const handleViewOutput = async (id: number) => {
|
||||
try {
|
||||
exportLoading.value = true
|
||||
await outputDialogRef.value.open(id)
|
||||
} finally {
|
||||
exportLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 人工评分 */
|
||||
const manualScoreDialogRef = ref()
|
||||
const handleManualScore = (row: QuestionnaireRecord) => {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user