feat:增加导出和快速评论功能

This commit is contained in:
2025-10-20 15:45:58 +08:00
parent 1fc1a31641
commit 4e83531621
6 changed files with 360 additions and 96 deletions

View File

@@ -2,7 +2,7 @@
"name": "blog-ui",
"private": true,
"version": "0.0.1",
"scripts": {
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
@@ -16,6 +16,7 @@
"dayjs": "^1.11.13",
"element-plus": "~2.9.7",
"font-awesome": "^4.7.0",
"html2pdf.js": "^0.12.1",
"md-editor-v3": "^4.21.3",
"pinia": "^2.3.1",
"ua-parser-js": "^2.0.3",

173
pnpm-lock.yaml generated
View File

@@ -32,6 +32,9 @@ importers:
font-awesome:
specifier: ^4.7.0
version: 4.7.0
html2pdf.js:
specifier: ^0.12.1
version: 0.12.1
md-editor-v3:
specifier: ^4.21.3
version: 4.21.3(vue@3.5.12(typescript@5.9.2))
@@ -861,6 +864,12 @@ packages:
'@types/node@18.19.126':
resolution: {integrity: sha512-8AXQlBfrGmtYJEJUPs63F/uZQqVeFiN9o6NUjbDJYfxNxFnArlZufANPw4h6dGhYGKxcyw+TapXFvEsguzIQow==}
'@types/pako@2.0.4':
resolution: {integrity: sha512-VWDCbrLeVXJM9fihYodcLiIv0ku+AlOa/TQ1SvYOaBuyrSKgEcro95LJyIsJ4vSo6BXIxOKxiJAat04CmST9Fw==}
'@types/raf@3.4.3':
resolution: {integrity: sha512-c4YAvMedbPZ5tEyxzQdMoOhhJ4RD3rngZIdwC2/qDN3d7JpEhB6fiBRKVY1lg5B7Wk+uPBjn5f39j1/2MY1oOw==}
'@types/resize-observer-browser@0.1.11':
resolution: {integrity: sha512-cNw5iH8JkMkb3QkCoe7DaZiawbDQEUX8t7iuQaRTyLOyQCR2h+ibBD4GJt7p5yhUHrlOeL7ZtbxNHeipqNsBzQ==}
@@ -870,6 +879,9 @@ packages:
'@types/svgo@2.6.4':
resolution: {integrity: sha512-l4cmyPEckf8moNYHdJ+4wkHvFxjyW6ulm9l4YGaOxeyBWPhBOT0gvni1InpFPdzx1dKf/2s62qGITwxNWnPQng==}
'@types/trusted-types@2.0.7':
resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
'@types/web-bluetooth@0.0.16':
resolution: {integrity: sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ==}
@@ -1180,6 +1192,10 @@ packages:
balanced-match@1.0.2:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
base64-arraybuffer@1.0.2:
resolution: {integrity: sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==}
engines: {node: '>= 0.6.0'}
base@0.11.2:
resolution: {integrity: sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==}
engines: {node: '>=0.10.0'}
@@ -1256,6 +1272,10 @@ packages:
caniuse-lite@1.0.30001743:
resolution: {integrity: sha512-e6Ojr7RV14Un7dz6ASD0aZDmQPT/A+eZU+nuTNfjqmRrmkmQlnTNWH0SKmqagx9PeW87UVqapSurtAXifmtdmw==}
canvg@3.0.11:
resolution: {integrity: sha512-5ON+q7jCTgMp9cjpu4Jo6XbvfYwSB2Ow3kzHKfIyJfaCAOHLbdKPQqGKgfED/R5B+3TFFfe8pegYA+b423SRyA==}
engines: {node: '>=10.0.0'}
chalk@1.1.3:
resolution: {integrity: sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==}
engines: {node: '>=0.10.0'}
@@ -1346,6 +1366,9 @@ packages:
copy-to-clipboard@3.3.3:
resolution: {integrity: sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==}
core-js@3.46.0:
resolution: {integrity: sha512-vDMm9B0xnqqZ8uSBpZ8sNtRtOdmfShrvT6h2TuQGLs0Is+cR0DYbj/KWP6ALVNbWPpqA/qPLoOuppJN07humpA==}
cors@2.8.5:
resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==}
engines: {node: '>= 0.10'}
@@ -1360,6 +1383,9 @@ packages:
crypto-js@4.2.0:
resolution: {integrity: sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==}
css-line-break@2.1.0:
resolution: {integrity: sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==}
css-select@4.3.0:
resolution: {integrity: sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==}
@@ -1514,6 +1540,9 @@ packages:
resolution: {integrity: sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==}
engines: {node: '>= 4'}
dompurify@3.3.0:
resolution: {integrity: sha512-r+f6MYR1gGN1eJv0TVQbhA7if/U7P87cdPl3HN5rikqaBSBxLiCb/b9O+2eG0cxz0ghyU+mU1QkbsOwERMYlWQ==}
domutils@1.7.0:
resolution: {integrity: sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==}
@@ -1788,12 +1817,18 @@ packages:
fast-levenshtein@2.0.6:
resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
fast-png@6.4.0:
resolution: {integrity: sha512-kAqZq1TlgBjZcLr5mcN6NP5Rv4V2f22z00c3g8vRrwkcqjerx7BEhPbOnWCPqaHUl2XWQBJQvOT/FQhdMT7X/Q==}
fast-uri@3.1.0:
resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==}
fastq@1.19.1:
resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==}
fflate@0.8.2:
resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==}
file-entry-cache@6.0.1:
resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==}
engines: {node: ^10.12.0 || >=12.0.0}
@@ -1991,6 +2026,13 @@ packages:
resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==}
hasBin: true
html2canvas@1.4.1:
resolution: {integrity: sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==}
engines: {node: '>=8.0.0'}
html2pdf.js@0.12.1:
resolution: {integrity: sha512-3rBWQ96H5oOU9jtoz3MnE/epGi27ig9h8aonBk4JTpvUERM3lMRxhIRckhJZEi4wE0YfRINoYOIDY0hLY0CHgQ==}
htmlparser2@3.10.1:
resolution: {integrity: sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==}
@@ -2032,6 +2074,9 @@ packages:
resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==}
engines: {node: '>= 0.4'}
iobuffer@5.4.0:
resolution: {integrity: sha512-DRebOWuqDvxunfkNJAlc3IzWIPD5xVxwUNbHr7xKB8E6aLJxIPfNX3CoMJghcFjpv6RWQsrcJbghtEwSPoJqMA==}
is-accessor-descriptor@1.0.1:
resolution: {integrity: sha512-YBUanLI8Yoihw923YeFUS5fs0fF2f5TSFTNiYAAzhhDscDa3lEqYuz1pDOEP5KvX94I9ey3vsqjJcLVFVU+3QA==}
engines: {node: '>= 0.10'}
@@ -2247,6 +2292,9 @@ packages:
jsonfile@6.2.0:
resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==}
jspdf@3.0.3:
resolution: {integrity: sha512-eURjAyz5iX1H8BOYAfzvdPfIKK53V7mCpBTe7Kb16PaM8JSXEcUQNBQaiWMI8wY5RvNOPj4GccMjTlfwRBd+oQ==}
keyv@4.5.4:
resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
@@ -2519,6 +2567,9 @@ packages:
package-json-from-dist@1.0.1:
resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==}
pako@2.1.0:
resolution: {integrity: sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==}
parent-module@1.0.1:
resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
engines: {node: '>=6'}
@@ -2559,6 +2610,9 @@ packages:
pathe@0.2.0:
resolution: {integrity: sha512-sTitTPYnn23esFR3RlqYBWn4c45WGeLcsKzQiUpXJAyfcWkolvlYpV8FLo7JishK946oQwMFUCHXQ9AjGPKExw==}
performance-now@2.1.0:
resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==}
picocolors@1.1.1:
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
@@ -2688,6 +2742,9 @@ packages:
queue-microtask@1.2.3:
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
raf@3.4.1:
resolution: {integrity: sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==}
read-cache@1.0.0:
resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==}
@@ -2707,6 +2764,9 @@ packages:
resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==}
engines: {node: '>= 0.4'}
regenerator-runtime@0.13.11:
resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==}
regex-not@1.0.2:
resolution: {integrity: sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==}
engines: {node: '>=0.10.0'}
@@ -2752,6 +2812,10 @@ packages:
resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==}
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
rgbcolor@1.0.1:
resolution: {integrity: sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==}
engines: {node: '>= 0.8.15'}
rimraf@3.0.2:
resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==}
deprecated: Rimraf versions prior to v4 are no longer supported
@@ -2892,6 +2956,10 @@ packages:
resolution: {integrity: sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==}
deprecated: 'Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility'
stackblur-canvas@2.7.0:
resolution: {integrity: sha512-yf7OENo23AGJhBriGx0QivY5JP6Y1HbrrDI6WLt6C5auYZXlQrheoY8hD4ibekFKz1HOfE48Ww8kMWMnJD/zcQ==}
engines: {node: '>=0.1.14'}
static-extend@0.1.2:
resolution: {integrity: sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==}
engines: {node: '>=0.10.0'}
@@ -2982,6 +3050,10 @@ packages:
svg-baker@1.7.0:
resolution: {integrity: sha512-nibslMbkXOIkqKVrfcncwha45f97fGuAOn1G99YwnwTj8kF9YiM6XexPcUso97NxOm6GsP0SIvYVIosBis1xLg==}
svg-pathdata@6.0.3:
resolution: {integrity: sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==}
engines: {node: '>=12.0.0'}
svgo@2.8.0:
resolution: {integrity: sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==}
engines: {node: '>=10.13.0'}
@@ -2996,6 +3068,9 @@ packages:
engines: {node: '>=14.0.0'}
hasBin: true
text-segmentation@1.0.3:
resolution: {integrity: sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==}
text-table@0.2.0:
resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}
@@ -3141,6 +3216,9 @@ packages:
resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==}
engines: {node: '>= 0.4.0'}
utrie@1.0.2:
resolution: {integrity: sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==}
v-calendar@3.1.2:
resolution: {integrity: sha512-QDWrnp4PWCpzUblctgo4T558PrHgHzDtQnTeUNzKxfNf29FkCeFpwGd9bKjAqktaa2aJLcyRl45T5ln1ku34kg==}
peerDependencies:
@@ -4180,6 +4258,11 @@ snapshots:
dependencies:
undici-types: 5.26.5
'@types/pako@2.0.4': {}
'@types/raf@3.4.3':
optional: true
'@types/resize-observer-browser@0.1.11': {}
'@types/semver@7.7.1': {}
@@ -4188,6 +4271,9 @@ snapshots:
dependencies:
'@types/node': 18.19.126
'@types/trusted-types@2.0.7':
optional: true
'@types/web-bluetooth@0.0.16': {}
'@types/web-bluetooth@0.0.21': {}
@@ -4572,6 +4658,8 @@ snapshots:
balanced-match@1.0.2: {}
base64-arraybuffer@1.0.2: {}
base@0.11.2:
dependencies:
cache-base: 1.0.1
@@ -4670,6 +4758,18 @@ snapshots:
caniuse-lite@1.0.30001743: {}
canvg@3.0.11:
dependencies:
'@babel/runtime': 7.28.4
'@types/raf': 3.4.3
core-js: 3.46.0
raf: 3.4.1
regenerator-runtime: 0.13.11
rgbcolor: 1.0.1
stackblur-canvas: 2.7.0
svg-pathdata: 6.0.3
optional: true
chalk@1.1.3:
dependencies:
ansi-styles: 2.2.1
@@ -4779,6 +4879,9 @@ snapshots:
dependencies:
toggle-selection: 1.0.6
core-js@3.46.0:
optional: true
cors@2.8.5:
dependencies:
object-assign: 4.1.1
@@ -4794,6 +4897,10 @@ snapshots:
crypto-js@4.2.0: {}
css-line-break@2.1.0:
dependencies:
utrie: 1.0.2
css-select@4.3.0:
dependencies:
boolbase: 1.0.0
@@ -4936,6 +5043,11 @@ snapshots:
dependencies:
domelementtype: 2.3.0
dompurify@3.3.0:
optionalDependencies:
'@types/trusted-types': 2.0.7
optional: true
domutils@1.7.0:
dependencies:
dom-serializer: 0.2.2
@@ -5356,12 +5468,20 @@ snapshots:
fast-levenshtein@2.0.6: {}
fast-png@6.4.0:
dependencies:
'@types/pako': 2.0.4
iobuffer: 5.4.0
pako: 2.1.0
fast-uri@3.1.0: {}
fastq@1.19.1:
dependencies:
reusify: 1.1.0
fflate@0.8.2: {}
file-entry-cache@6.0.1:
dependencies:
flat-cache: 3.2.0
@@ -5579,6 +5699,16 @@ snapshots:
he@1.2.0: {}
html2canvas@1.4.1:
dependencies:
css-line-break: 2.1.0
text-segmentation: 1.0.3
html2pdf.js@0.12.1:
dependencies:
html2canvas: 1.4.1
jspdf: 3.0.3
htmlparser2@3.10.1:
dependencies:
domelementtype: 1.3.1
@@ -5618,6 +5748,8 @@ snapshots:
hasown: 2.0.2
side-channel: 1.1.0
iobuffer@5.4.0: {}
is-accessor-descriptor@1.0.1:
dependencies:
hasown: 2.0.2
@@ -5822,6 +5954,17 @@ snapshots:
optionalDependencies:
graceful-fs: 4.2.11
jspdf@3.0.3:
dependencies:
'@babel/runtime': 7.28.4
fast-png: 6.4.0
fflate: 0.8.2
optionalDependencies:
canvg: 3.0.11
core-js: 3.46.0
dompurify: 3.3.0
html2canvas: 1.4.1
keyv@4.5.4:
dependencies:
json-buffer: 3.0.1
@@ -6114,6 +6257,8 @@ snapshots:
package-json-from-dist@1.0.1: {}
pako@2.1.0: {}
parent-module@1.0.1:
dependencies:
callsites: 3.1.0
@@ -6141,6 +6286,9 @@ snapshots:
pathe@0.2.0: {}
performance-now@2.1.0:
optional: true
picocolors@1.1.1: {}
picomatch@2.3.1: {}
@@ -6255,6 +6403,11 @@ snapshots:
queue-microtask@1.2.3: {}
raf@3.4.1:
dependencies:
performance-now: 2.1.0
optional: true
read-cache@1.0.0:
dependencies:
pify: 2.3.0
@@ -6282,6 +6435,9 @@ snapshots:
get-proto: 1.0.1
which-builtin-type: 1.2.1
regenerator-runtime@0.13.11:
optional: true
regex-not@1.0.2:
dependencies:
extend-shallow: 3.0.2
@@ -6318,6 +6474,9 @@ snapshots:
reusify@1.1.0: {}
rgbcolor@1.0.1:
optional: true
rimraf@3.0.2:
dependencies:
glob: 7.2.3
@@ -6510,6 +6669,9 @@ snapshots:
stable@0.1.8: {}
stackblur-canvas@2.7.0:
optional: true
static-extend@0.1.2:
dependencies:
define-property: 0.2.5
@@ -6625,6 +6787,9 @@ snapshots:
transitivePeerDependencies:
- supports-color
svg-pathdata@6.0.3:
optional: true
svgo@2.8.0:
dependencies:
'@trysound/sax': 0.2.0
@@ -6670,6 +6835,10 @@ snapshots:
transitivePeerDependencies:
- ts-node
text-segmentation@1.0.3:
dependencies:
utrie: 1.0.2
text-table@0.2.0: {}
thenify-all@1.6.0:
@@ -6834,6 +7003,10 @@ snapshots:
utils-merge@1.0.1: {}
utrie@1.0.2:
dependencies:
base64-arraybuffer: 1.0.2
v-calendar@3.1.2(@popperjs/core@2.11.8)(vue@3.5.12(typescript@5.9.2)):
dependencies:
'@popperjs/core': 2.11.8

View File

@@ -6,9 +6,25 @@
</div>
<div class="blog-md card-item">
<MdPreview editorId="blog-id"
v-model="blogStore.currentBlog.content"
:theme="appStore.isDark ? 'dark' : 'light'"/>
<div class="toolbar">
<el-tooltip content="导出为pdf">
<el-button @click="exportToPDF" type="primary" circle size="small">
<el-icon><Document /></el-icon>
</el-button>
</el-tooltip>
<el-tooltip content="快速评论">
<el-button @click="scrollToComment" type="success" circle size="small">
<el-icon><ChatDotRound /></el-icon>
</el-button>
</el-tooltip>
</div>
<div ref="previewRef">
<MdPreview editorId="blog-id"
v-model="blogStore.currentBlog.content"
:theme="appStore.isDark ? 'dark' : 'light'"/>
</div>
</div>
<div class="blog-copyright">
@@ -67,14 +83,14 @@
</el-row>
</div>
<blog-comment-edit :parent-id="0"/>
<blog-comment-edit :parent-id="0" ref="blogCommentEditRef"/>
<blog-comment />
</div>
</template>
<script setup lang="ts">
import { ArrowLeft, ArrowRight } from '@element-plus/icons-vue'
import { ArrowLeft, ArrowRight, ChatDotRound, Document } from '@element-plus/icons-vue'
import BlogLabel from '@/components/Content/BlogLabel.vue'
import BlogCommentEdit from '@/components/Comment/BlogCommentEdit.vue'
import BlogComment from '@/components/Comment/BlogComment.vue'
@@ -84,13 +100,17 @@ import { useBlogStore } from '@/store/blog.ts'
import { useAppStore } from '@/store/app'
import { getBasicBlog } from '@/utils/blog/blogUtil.ts'
import { useRoute } from 'vue-router'
import { onMounted, watch } from 'vue'
import { onMounted, watch, ref } from 'vue'
import html2pdf from 'html2pdf.js'
import { ElLoading, ElMessage } from 'element-plus'
const blogStore = useBlogStore()
const appStore = useAppStore()
const route = useRoute()
const currentUrl = new URL(window.location.href)
const previewRef = ref(null)
const blogCommentEditRef = ref(null)
onMounted(async () => {
const blogId = route.params.id as number
@@ -108,47 +128,69 @@ const refreshBlog = async (blogId: number) => {
await blogStore.queryBlogById(blogId)
await blogStore.queryBlogAdjacent(blogId)
}
const exportToPDF = async () => {
// 先显示提示消息
ElMessage.success('正在准备导出,请稍候...')
// 使用微任务确保消息能显示出来
// JavaScript是单线程的 后面的 html2pdf()是一个 CPU 密集型任务
// 如果不加上延迟此时ElMessage.success()会触发UI更新但浏览器还没渲染因为 JS 还在执行同步代码
// 因此ElMessage.success()是不会显示的
await new Promise((resolve) => setTimeout(resolve, 50))
// 启动全屏加载指示器
const loading = ElLoading.service({
lock: true,
text: '正在生成PDF这可能需要一些时间...',
background: 'rgba(255, 255, 255, 0.9)'
})
try {
// 再次延迟确保加载动画显示
await new Promise((resolve) => setTimeout(resolve, 100))
const element = previewRef.value
const opt = {
margin: 10,
filename: `${blogStore.currentBlog.title}.pdf`,
image: { type: 'jpeg', quality: 0.98 },
html2canvas: {
scale: 2,
logging: true, // 开启日志便于调试
useCORS: true // 允许跨域资源
},
jsPDF: {
unit: 'mm',
format: 'a4',
orientation: 'portrait'
}
}
// 使用Promise确保导出完成
await new Promise((resolve, reject) => {
html2pdf().from(element).set(opt).save()
.then(resolve)
.catch(reject)
})
ElMessage.success('导出成功!')
} catch (error) {
ElMessage.error(`导出失败: ${error.message}`)
} finally {
loading.close()
}
}
const scrollToComment = () => {
blogCommentEditRef.value?.$el.scrollIntoView({
behavior: 'smooth',
block: 'start'
})
}
</script>
<style scoped lang="scss">
@use "@/styles/blog/blog.detail.module.scss";
.blog-navigation {
width: 100%;
background-color: var(--color-copyright-bg);
.nav-item {
padding: 10px;
display: flex;
align-items: center;
gap: 5px;
}
.label {
color: var(--el-text-color-secondary);
font-size: 14px;
}
.blog-navigation-title {
color: var(--el-text-color-primary);
text-decoration: none;
transition: color 0.3s;
font-weight: 500;
&:hover {
color: var(--el-color-primary);
text-decoration: underline;
}
}
.prev {
flex-direction: row;
text-align: left;
}
.next {
flex-direction: row-reverse;
text-align: right;
}
}
</style>

View File

@@ -14,58 +14,97 @@
width: 100%;
padding: 10px;
overflow: auto;
position: relative;
.md-editor {
height: 100%;
}
.toolbar {
position: absolute;
top: 5px;
right: 5px;
z-index: 1;
}
}
.blog-title {
width: 100%;
display: grid;
grid-template-rows: 1fr 1fr;
align-items: center;
gap: 1vh;
.title {
background-color: blog.$theme-color;
padding: 5px;
border: 2px solid blog.$theme-color;
border-radius: 10px;
text-align: center;
font-size: blog.$blog-title-font-size;
font-family: "华文楷体", serif;
}
}
.blog-copyright {
width: 100%;
background-color: var(--color-copyright-bg);
padding: 10px;
border-left: 3px solid #FD2C17;
font-size: blog.$small-font-size;
display: flex;
flex-direction: column;
gap: 1vh;
strong {
margin-right: 0.2vw;
}
}
.blog-ending {
span {
color: #CFCFCF;
}
}
.blog-navigation {
width: 100%;
background-color: var(--color-copyright-bg);
.nav-item {
padding: 10px;
display: flex;
align-items: center;
gap: 5px;
}
.label {
color: var(--el-text-color-secondary);
font-size: 14px;
}
.blog-navigation-title {
color: var(--el-text-color-primary);
text-decoration: none;
transition: color 0.3s;
font-weight: 500;
&:hover {
color: var(--el-color-primary);
text-decoration: underline;
}
}
.prev {
flex-direction: row;
text-align: left;
}
.next {
flex-direction: row-reverse;
text-align: right;
}
}
}
.blog-title {
width: 100%;
display: grid;
grid-template-rows: 1fr 1fr;
align-items: center;
gap: 1vh;
.title {
background-color: blog.$theme-color;
padding: 5px;
border: 2px solid blog.$theme-color;
border-radius: 10px;
text-align: center;
font-size: blog.$blog-title-font-size;
font-family: "华文楷体", serif;
}
}
.blog-copyright {
width: 100%;
background-color: var(--color-copyright-bg);
padding: 10px;
border-left: 3px solid #FD2C17;
font-size: blog.$small-font-size;
display: flex;
flex-direction: column;
gap: 1vh;
strong {
margin-right: 0.2vw;
}
}
.blog-ending {
span {
color: #CFCFCF;
}
}
.blog-detail-mobile {
padding: 10px;
display: flex;
flex-direction: column;
gap: 2vh;
}

View File

@@ -243,9 +243,11 @@ $sub-title-font-size: 1rem;
.md-editor-catalog {
height: 100%;
width: 100%;
padding: 0 5px;
overflow: auto;
.md-editor-catalog-active {
border-radius: 5px;
background-color: var(--color-copyright-bg);
font-weight: bold;
}

View File

@@ -17,7 +17,14 @@
"src/*"
],
"vue3-common/*": ["node_modules/vue3-common/dist/*"]
}
},
"lib": [
"decorators",
"dom",
"es5",
"scripthost",
"es2015.promise"
]
},
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"]
}