Merge branch 'main' of https://gitea-inner.fontree.cn/scout666/chat-pc into yink
This commit is contained in:
commit
7999499702
11
package.json
11
package.json
@ -15,10 +15,21 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@ant-design/icons-vue": "^7.0.1",
|
||||
"@floating-ui/dom": "^1.7.2",
|
||||
"@highlightjs/vue-plugin": "^2.1.0",
|
||||
"@iconify-json/ion": "^1.2.3",
|
||||
"@kangc/v-md-editor": "^2.3.18",
|
||||
"@onlyoffice/document-editor-vue": "^1.5.0",
|
||||
"@tiptap/core": "^2.23.1",
|
||||
"@tiptap/extension-blockquote": "^2.23.1",
|
||||
"@tiptap/extension-emoji": "^2.23.1",
|
||||
"@tiptap/extension-image": "^2.23.1",
|
||||
"@tiptap/extension-link": "^2.23.1",
|
||||
"@tiptap/extension-mention": "^2.23.1",
|
||||
"@tiptap/extension-placeholder": "^2.23.1",
|
||||
"@tiptap/pm": "^2.23.1",
|
||||
"@tiptap/starter-kit": "^2.23.1",
|
||||
"@tiptap/vue-3": "^2.23.1",
|
||||
"@vicons/fluent": "^0.13.0",
|
||||
"@vicons/ionicons4": "^0.13.0",
|
||||
"@vicons/ionicons5": "^0.13.0",
|
||||
|
695
pnpm-lock.yaml
695
pnpm-lock.yaml
@ -11,6 +11,9 @@ importers:
|
||||
'@ant-design/icons-vue':
|
||||
specifier: ^7.0.1
|
||||
version: 7.0.1(vue@3.5.17(typescript@5.2.2))
|
||||
'@floating-ui/dom':
|
||||
specifier: ^1.7.2
|
||||
version: 1.7.2
|
||||
'@highlightjs/vue-plugin':
|
||||
specifier: ^2.1.0
|
||||
version: 2.1.0(highlight.js@11.11.1)(vue@3.5.17(typescript@5.2.2))
|
||||
@ -23,6 +26,36 @@ importers:
|
||||
'@onlyoffice/document-editor-vue':
|
||||
specifier: ^1.5.0
|
||||
version: 1.5.0(vue@3.5.17(typescript@5.2.2))
|
||||
'@tiptap/core':
|
||||
specifier: ^2.23.1
|
||||
version: 2.23.1(@tiptap/pm@2.23.1)
|
||||
'@tiptap/extension-blockquote':
|
||||
specifier: ^2.23.1
|
||||
version: 2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))
|
||||
'@tiptap/extension-emoji':
|
||||
specifier: ^2.23.1
|
||||
version: 2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))(@tiptap/pm@2.23.1)(@tiptap/suggestion@2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))(@tiptap/pm@2.23.1))(emojibase@16.0.0)
|
||||
'@tiptap/extension-image':
|
||||
specifier: ^2.23.1
|
||||
version: 2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))
|
||||
'@tiptap/extension-link':
|
||||
specifier: ^2.23.1
|
||||
version: 2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))(@tiptap/pm@2.23.1)
|
||||
'@tiptap/extension-mention':
|
||||
specifier: ^2.23.1
|
||||
version: 2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))(@tiptap/pm@2.23.1)(@tiptap/suggestion@2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))(@tiptap/pm@2.23.1))
|
||||
'@tiptap/extension-placeholder':
|
||||
specifier: ^2.23.1
|
||||
version: 2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))(@tiptap/pm@2.23.1)
|
||||
'@tiptap/pm':
|
||||
specifier: ^2.23.1
|
||||
version: 2.23.1
|
||||
'@tiptap/starter-kit':
|
||||
specifier: ^2.23.1
|
||||
version: 2.23.1
|
||||
'@tiptap/vue-3':
|
||||
specifier: ^2.23.1
|
||||
version: 2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))(@tiptap/pm@2.23.1)(vue@3.5.17(typescript@5.2.2))
|
||||
'@vicons/fluent':
|
||||
specifier: ^0.13.0
|
||||
version: 0.13.0
|
||||
@ -538,6 +571,15 @@ packages:
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@floating-ui/core@1.7.2':
|
||||
resolution: {integrity: sha512-wNB5ooIKHQc+Kui96jE/n69rHFWAVoxn5CAzL1Xdd8FG03cgY3MLO+GF9U3W737fYDSgPWA6MReKhBQBop6Pcw==}
|
||||
|
||||
'@floating-ui/dom@1.7.2':
|
||||
resolution: {integrity: sha512-7cfaOQuCS27HD7DX+6ib2OrnW+b4ZBwDNnCcT0uTyidcmyWb03FnQqJybDBoCnpdxwBSfA94UAYlRCt7mV+TbA==}
|
||||
|
||||
'@floating-ui/utils@0.2.10':
|
||||
resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==}
|
||||
|
||||
'@hapi/hoek@9.3.0':
|
||||
resolution: {integrity: sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==}
|
||||
|
||||
@ -711,6 +753,12 @@ packages:
|
||||
'@polka/url@1.0.0-next.29':
|
||||
resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==}
|
||||
|
||||
'@popperjs/core@2.11.8':
|
||||
resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==}
|
||||
|
||||
'@remirror/core-constants@3.0.0':
|
||||
resolution: {integrity: sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg==}
|
||||
|
||||
'@rollup/pluginutils@5.2.0':
|
||||
resolution: {integrity: sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
@ -850,6 +898,173 @@ packages:
|
||||
resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
'@tiptap/core@2.23.1':
|
||||
resolution: {integrity: sha512-EURGKGsEPrwxvOPi9gA+BsczvsECJNV+xgTAGWHmEtU4YJ0AulYrCX3b7FK+aiduVhThIHDoG/Mmvmb/HPLRhQ==}
|
||||
peerDependencies:
|
||||
'@tiptap/pm': ^2.7.0
|
||||
|
||||
'@tiptap/extension-blockquote@2.23.1':
|
||||
resolution: {integrity: sha512-GI3s+uFU88LWRaDG20Z9yIu2av3Usn8kw2lkm2ntwX1K6/mQBS/zkGhWr/FSwWOlMtTzYFxF4Ttb0e+hn67A/A==}
|
||||
peerDependencies:
|
||||
'@tiptap/core': ^2.7.0
|
||||
|
||||
'@tiptap/extension-bold@2.23.1':
|
||||
resolution: {integrity: sha512-OM4RxuZeOqpYRN1G/YpXSE8tZ3sVtT2XlO3qKa74qf+htWz8W3x4X0oQCrHrRTDSAA1wbmeZU3QghAIHnbvP/A==}
|
||||
peerDependencies:
|
||||
'@tiptap/core': ^2.7.0
|
||||
|
||||
'@tiptap/extension-bubble-menu@2.23.1':
|
||||
resolution: {integrity: sha512-tupuvrlZMTziVZXJuCVjLwllUnux/an9BtTYHpoRyLX9Hg0v7Kh39k9x58zJaoW8Q/Oc/qxPhbJpyOqhE1rLeg==}
|
||||
peerDependencies:
|
||||
'@tiptap/core': ^2.7.0
|
||||
'@tiptap/pm': ^2.7.0
|
||||
|
||||
'@tiptap/extension-bullet-list@2.23.1':
|
||||
resolution: {integrity: sha512-0g9U42m+boLJZP3x9KoJHDCp9WD5abaVdqNbTg9sFPDNsepb7Zaeu8AEB+yZLP/fuTI1I4ko6qkdr3UaaIYcmA==}
|
||||
peerDependencies:
|
||||
'@tiptap/core': ^2.7.0
|
||||
|
||||
'@tiptap/extension-code-block@2.23.1':
|
||||
resolution: {integrity: sha512-eYzJVUR13BhSE/TYAMZihGBId+XiwhnTPqGcSFo+zx89It/vxwDLvAUn0PReMNI7ULKPTw8orUt2fVKSarb2DQ==}
|
||||
peerDependencies:
|
||||
'@tiptap/core': ^2.7.0
|
||||
'@tiptap/pm': ^2.7.0
|
||||
|
||||
'@tiptap/extension-code@2.23.1':
|
||||
resolution: {integrity: sha512-3IOdE40m0UTR2+UXui69o/apLtutAbtzfgmMxD6q0qlRvVqz99QEfk9RPHDNlUqJtYCL4TD+sj7UclBsDdgVXA==}
|
||||
peerDependencies:
|
||||
'@tiptap/core': ^2.7.0
|
||||
|
||||
'@tiptap/extension-document@2.23.1':
|
||||
resolution: {integrity: sha512-2nkIkGVsaMJkpd024E6vXK+5XNz8VOVWp/pM6bbXpuv0HnGPrfLdh4ruuFc+xTQ3WPOmpSu8ygtujt4I1o9/6g==}
|
||||
peerDependencies:
|
||||
'@tiptap/core': ^2.7.0
|
||||
|
||||
'@tiptap/extension-dropcursor@2.23.1':
|
||||
resolution: {integrity: sha512-GyVp+o/RVrKlLdrQvtIpJGphFGogiPjcPCkAFcrfY1vDY1EYxfVZELC96gG1mUT1BO8FUD3hmbpkWi9l8/6O4A==}
|
||||
peerDependencies:
|
||||
'@tiptap/core': ^2.7.0
|
||||
'@tiptap/pm': ^2.7.0
|
||||
|
||||
'@tiptap/extension-emoji@2.23.1':
|
||||
resolution: {integrity: sha512-bqTn+hbq0bDIcrPIIjVq3GndJ/PYQfReMDlyTv0mUCtRbP7zReJ1oFx02d25RmwgS6XL3U8WW4kEFomhliwWSQ==}
|
||||
peerDependencies:
|
||||
'@tiptap/core': ^2.7.0
|
||||
'@tiptap/pm': ^2.7.0
|
||||
'@tiptap/suggestion': ^2.7.0
|
||||
|
||||
'@tiptap/extension-floating-menu@2.23.1':
|
||||
resolution: {integrity: sha512-GMWkpH+p/OUOk1Y5UGOnKuHSDEVBN7DhYIJiWt5g9LK/mpPeuqoCmQg3RQDgjtZXb74SlxLK2pS/3YcAnemdfQ==}
|
||||
peerDependencies:
|
||||
'@tiptap/core': ^2.7.0
|
||||
'@tiptap/pm': ^2.7.0
|
||||
|
||||
'@tiptap/extension-gapcursor@2.23.1':
|
||||
resolution: {integrity: sha512-iP+TiFIGZEbOvYAs04pI14mLI4xqbt64Da91TgMF1FNZUrG+9eWKjqbcHLQREuK3Qnjn5f0DI4nOBv61FlnPmA==}
|
||||
peerDependencies:
|
||||
'@tiptap/core': ^2.7.0
|
||||
'@tiptap/pm': ^2.7.0
|
||||
|
||||
'@tiptap/extension-hard-break@2.23.1':
|
||||
resolution: {integrity: sha512-YF66EVxnBxt1bHPx6fUUSSXK1Vg+/9baJ0AfJ12hCSPCgSjUclRuNmWIH5ikVfByOmPV1xlrN9wryLoSEBcNRQ==}
|
||||
peerDependencies:
|
||||
'@tiptap/core': ^2.7.0
|
||||
|
||||
'@tiptap/extension-heading@2.23.1':
|
||||
resolution: {integrity: sha512-5BPoli9wudiAOgSyK8309jyRhFyu5vd02lNChfpHwxUudzIJ/L+0E6FcwrDcw+yXh23cx7F5SSjtFQ7AobxlDQ==}
|
||||
peerDependencies:
|
||||
'@tiptap/core': ^2.7.0
|
||||
|
||||
'@tiptap/extension-history@2.23.1':
|
||||
resolution: {integrity: sha512-1rp2CRjM+P58oGEgeUUDSk0ch67ngIGbGJOOjiBGKU9GIVhI2j4uSwsYTAa9qYMjMUI6IyH1xJpsY2hLKcBOtg==}
|
||||
peerDependencies:
|
||||
'@tiptap/core': ^2.7.0
|
||||
'@tiptap/pm': ^2.7.0
|
||||
|
||||
'@tiptap/extension-horizontal-rule@2.23.1':
|
||||
resolution: {integrity: sha512-uHEF0jpmhtgAxjKw8/s5ipEeTnu99f9RVMGAlmcthJ5Fx9TzH0MvtH4dtBNEu5MXC7+0bNsnncWo125AAbCohg==}
|
||||
peerDependencies:
|
||||
'@tiptap/core': ^2.7.0
|
||||
'@tiptap/pm': ^2.7.0
|
||||
|
||||
'@tiptap/extension-image@2.23.1':
|
||||
resolution: {integrity: sha512-8j2FLBWKq6j27aQcOAlmyKixxHflW8j5FhxLgPPS6hithgFQVET4OYH+1c6r7Qd/T4YoAqt/0PmNZ/gYWI9gzg==}
|
||||
peerDependencies:
|
||||
'@tiptap/core': ^2.7.0
|
||||
|
||||
'@tiptap/extension-italic@2.23.1':
|
||||
resolution: {integrity: sha512-a+cPzffaC/1AKMmZ1Ka6l81xmTgcalf8NXfBuFCUTf5r7uI9NIgXnLo9hg+jR9F4K+bwhC4/UbMvQQzAjh0c0A==}
|
||||
peerDependencies:
|
||||
'@tiptap/core': ^2.7.0
|
||||
|
||||
'@tiptap/extension-link@2.23.1':
|
||||
resolution: {integrity: sha512-zMD0V8djkvwRYACzd8EvFXXNLQH5poJt6aHC9/8uest6njRhlrRjSjwG5oa+xHW4A76XfAH0A5BPj6ZxZnAUQg==}
|
||||
peerDependencies:
|
||||
'@tiptap/core': ^2.7.0
|
||||
'@tiptap/pm': ^2.7.0
|
||||
|
||||
'@tiptap/extension-list-item@2.23.1':
|
||||
resolution: {integrity: sha512-wVrRp6KAiyjFVFGmn+ojisP64Bsd+ZPdqQBYVbebBx1skZeW0uhG60d7vUkWHi0gCgxHZDfvDbXpfnOD0INRWw==}
|
||||
peerDependencies:
|
||||
'@tiptap/core': ^2.7.0
|
||||
|
||||
'@tiptap/extension-mention@2.23.1':
|
||||
resolution: {integrity: sha512-YaHg2ZqNb2Vur5hBoswzu8pJ3kV54PPonR8gHgelD83T1s8vBqdl03WL2NUVciovEbENnirePLDfhKaFjRXSzQ==}
|
||||
peerDependencies:
|
||||
'@tiptap/core': ^2.7.0
|
||||
'@tiptap/pm': ^2.7.0
|
||||
'@tiptap/suggestion': ^2.7.0
|
||||
|
||||
'@tiptap/extension-ordered-list@2.23.1':
|
||||
resolution: {integrity: sha512-Zp+qognyNgoaJ9bxkBwIuWJEnQ67RdsHXzv3YOdeGRbkUhd8LT6OL7P0mAuNbMBU8MwHxyJ7C7NsyzwzuVbFzA==}
|
||||
peerDependencies:
|
||||
'@tiptap/core': ^2.7.0
|
||||
|
||||
'@tiptap/extension-paragraph@2.23.1':
|
||||
resolution: {integrity: sha512-LLEPizt1ALE7Ek6prlJ1uhoUCT8C/a3PdZpCh3DshM1L3Kv9TENlaJL2GhFl8SVUCwHmWHvXg30+4tIRFBedaQ==}
|
||||
peerDependencies:
|
||||
'@tiptap/core': ^2.7.0
|
||||
|
||||
'@tiptap/extension-placeholder@2.23.1':
|
||||
resolution: {integrity: sha512-zSkCljVpxJh3GHW7ppFNYhHPjYKmS3Tw0e74BOGzb5TqP57GRvnPgDGg4fr6kDsSWMo9minnNM3PDA7kNT+PRQ==}
|
||||
peerDependencies:
|
||||
'@tiptap/core': ^2.7.0
|
||||
'@tiptap/pm': ^2.7.0
|
||||
|
||||
'@tiptap/extension-strike@2.23.1':
|
||||
resolution: {integrity: sha512-hAT9peYkKezRGp/EcPQKtyYQT+2XGUbb26toTr9XIBQIeQCuCpT+FirPrDMrMVWPwcJt7Rv+AzoVjDuBs9wE0A==}
|
||||
peerDependencies:
|
||||
'@tiptap/core': ^2.7.0
|
||||
|
||||
'@tiptap/extension-text-style@2.23.1':
|
||||
resolution: {integrity: sha512-fZn1GePlL27pUFfKXKoRZo4L4pZP9dUjNNiS/eltLpbi/SenJ15UKhAoHtN1KQvNGJsWkYN49FjnnltU8qvQ+Q==}
|
||||
peerDependencies:
|
||||
'@tiptap/core': ^2.7.0
|
||||
|
||||
'@tiptap/extension-text@2.23.1':
|
||||
resolution: {integrity: sha512-XK0D/eyS1Vm5yUrCtkS0AfgyKLJqpi8nJivCOux/JLhhC4x87R1+mI8NoFDYZJ5ic/afREPSBB8jORqOi0qIHg==}
|
||||
peerDependencies:
|
||||
'@tiptap/core': ^2.7.0
|
||||
|
||||
'@tiptap/pm@2.23.1':
|
||||
resolution: {integrity: sha512-iAx4rP0k4Xd9Ywh+Gpaz5IWfY2CYRpiwVXWekTHLlNRFtrVIWVpMxaQr2mvRU2g0Ca6rz5w3KzkHBMqrI3dIBA==}
|
||||
|
||||
'@tiptap/starter-kit@2.23.1':
|
||||
resolution: {integrity: sha512-rrImwzJbKSHoFa+WdNU4I0evXcMiQ4yRm737sxvNJwYItT6fXIxrbRT7nJDmtYu2TflcfT1KklEnSrzz1hhYRw==}
|
||||
|
||||
'@tiptap/suggestion@2.23.1':
|
||||
resolution: {integrity: sha512-ryAsYhXmehwQLJnGFw9VHfQMANeWgFzDQUfgg0vcjbHJAa0t3qBsSOUP2RKOuErQmCvlgY/cgxO/6Q0l5IzsIA==}
|
||||
peerDependencies:
|
||||
'@tiptap/core': ^2.7.0
|
||||
'@tiptap/pm': ^2.7.0
|
||||
|
||||
'@tiptap/vue-3@2.23.1':
|
||||
resolution: {integrity: sha512-wBF9LxI96qMXE0Z+3C8GU4mmDRbCapcnjb/euQ1NeihqBwK6qdd5g8IIgGm+rQCSY8hCJBUTWJtfn7vJAasSDQ==}
|
||||
peerDependencies:
|
||||
'@tiptap/core': ^2.7.0
|
||||
'@tiptap/pm': ^2.7.0
|
||||
vue: ^3.0.0
|
||||
|
||||
'@tsconfig/node18@18.2.4':
|
||||
resolution: {integrity: sha512-5xxU8vVs9/FNcvm3gE07fPbn9tl6tqGGWA9tSlwsUEkBxtRnTsNmwrV8gasZ9F/EobaSv9+nu8AxUKccw77JpQ==}
|
||||
|
||||
@ -883,15 +1098,24 @@ packages:
|
||||
'@types/katex@0.16.7':
|
||||
resolution: {integrity: sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ==}
|
||||
|
||||
'@types/linkify-it@5.0.0':
|
||||
resolution: {integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==}
|
||||
|
||||
'@types/lodash-es@4.17.12':
|
||||
resolution: {integrity: sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==}
|
||||
|
||||
'@types/lodash@4.17.18':
|
||||
resolution: {integrity: sha512-KJ65INaxqxmU6EoCiJmRPZC9H9RVWCRd349tXM2M3O5NA7cY6YL7c0bHAHQ93NOfTObEQ004kd2QVHs/r0+m4g==}
|
||||
|
||||
'@types/markdown-it@14.1.2':
|
||||
resolution: {integrity: sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==}
|
||||
|
||||
'@types/mdast@3.0.15':
|
||||
resolution: {integrity: sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ==}
|
||||
|
||||
'@types/mdurl@2.0.0':
|
||||
resolution: {integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==}
|
||||
|
||||
'@types/minimatch@5.1.2':
|
||||
resolution: {integrity: sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==}
|
||||
|
||||
@ -1485,6 +1709,9 @@ packages:
|
||||
cose-base@1.0.3:
|
||||
resolution: {integrity: sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==}
|
||||
|
||||
crelt@1.0.6:
|
||||
resolution: {integrity: sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==}
|
||||
|
||||
cross-env@7.0.3:
|
||||
resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==}
|
||||
engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'}
|
||||
@ -1811,9 +2038,21 @@ packages:
|
||||
elkjs@0.9.3:
|
||||
resolution: {integrity: sha512-f/ZeWvW/BCXbhGEf1Ujp29EASo/lk1FDnETgNKwJrsVvGZhUWCZyg3xLJjAsxfOmt8KjswHmI5EwCQcPMpOYhQ==}
|
||||
|
||||
emoji-regex@10.4.0:
|
||||
resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==}
|
||||
|
||||
emoji-regex@8.0.0:
|
||||
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
|
||||
|
||||
emojibase-data@15.3.2:
|
||||
resolution: {integrity: sha512-TpDyTDDTdqWIJixV5sTA6OQ0P0JfIIeK2tFRR3q56G9LK65ylAZ7z3KyBXokpvTTJ+mLUXQXbLNyVkjvnTLE+A==}
|
||||
peerDependencies:
|
||||
emojibase: '*'
|
||||
|
||||
emojibase@16.0.0:
|
||||
resolution: {integrity: sha512-Nw2m7JLIO4Ou2X/yZPRNscHQXVbbr6SErjkJ7EooG7MbR3yDZszCv9KTizsXFc7yZl0n3WF+qUKIC/Lw6H9xaQ==}
|
||||
engines: {node: '>=18.12.0'}
|
||||
|
||||
enhanced-resolve@5.18.2:
|
||||
resolution: {integrity: sha512-6Jw4sE1maoRJo3q8MsSIn2onJFbLTOjY9hlx4DZXmOKvLRd1Ok2kXmAGXaafL2+ijsJZ1ClYbl/pmqr9+k4iUQ==}
|
||||
engines: {node: '>=10.13.0'}
|
||||
@ -1881,6 +2120,10 @@ packages:
|
||||
resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==}
|
||||
engines: {node: '>=0.8.0'}
|
||||
|
||||
escape-string-regexp@4.0.0:
|
||||
resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
escape-string-regexp@5.0.0:
|
||||
resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==}
|
||||
engines: {node: '>=12'}
|
||||
@ -2248,6 +2491,9 @@ packages:
|
||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||
hasBin: true
|
||||
|
||||
is-emoji-supported@0.0.5:
|
||||
resolution: {integrity: sha512-WOlXUhDDHxYqcSmFZis+xWhhqXiK2SU0iYiqmth5Ip0FHLZQAt9rKL5ahnilE8/86WH8tZ3bmNNNC+bTzamqlw==}
|
||||
|
||||
is-extendable@0.1.1:
|
||||
resolution: {integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@ -2442,6 +2688,12 @@ packages:
|
||||
linkify-it@3.0.3:
|
||||
resolution: {integrity: sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==}
|
||||
|
||||
linkify-it@5.0.0:
|
||||
resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==}
|
||||
|
||||
linkifyjs@4.3.1:
|
||||
resolution: {integrity: sha512-DRSlB9DKVW04c4SUdGvKK5FR6be45lTU9M76JnngqPeeGDqPwYc0zdUErtsNVMtxPXgUWV4HbXbnC4sNyBxkYg==}
|
||||
|
||||
loader-runner@4.3.0:
|
||||
resolution: {integrity: sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==}
|
||||
engines: {node: '>=6.11.5'}
|
||||
@ -2516,6 +2768,10 @@ packages:
|
||||
resolution: {integrity: sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==}
|
||||
hasBin: true
|
||||
|
||||
markdown-it@14.1.0:
|
||||
resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==}
|
||||
hasBin: true
|
||||
|
||||
markdown-it@8.4.2:
|
||||
resolution: {integrity: sha512-GcRz3AWTqSUphY3vsUqQSFMbgR38a4Lh3GWlHRh/7MRwz8mcu9n2IO7HOh+bXHrR9kOPDl5RNCaEsrneb+xhHQ==}
|
||||
hasBin: true
|
||||
@ -2536,6 +2792,9 @@ packages:
|
||||
mdurl@1.0.1:
|
||||
resolution: {integrity: sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==}
|
||||
|
||||
mdurl@2.0.0:
|
||||
resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==}
|
||||
|
||||
memorystream@0.3.1:
|
||||
resolution: {integrity: sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==}
|
||||
engines: {node: '>= 0.10.0'}
|
||||
@ -2766,6 +3025,9 @@ packages:
|
||||
resolution: {integrity: sha512-cxN6aIDPz6rm8hbebcP7vrQNhvRcveZoJU72Y7vskh4oIm+BZwBECnx5nTmrlres1Qapvx27Qo1Auukpf8PKXw==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
orderedmap@2.1.1:
|
||||
resolution: {integrity: sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==}
|
||||
|
||||
package-manager-detector@1.3.0:
|
||||
resolution: {integrity: sha512-ZsEbbZORsyHuO00lY1kV3/t72yp6Ysay6Pd17ZAlNGuGwmWDLCJxFpRs0IzfXfj1o4icJOkUEioexFHzyPurSQ==}
|
||||
|
||||
@ -2885,12 +3147,74 @@ packages:
|
||||
resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
prosemirror-changeset@2.3.1:
|
||||
resolution: {integrity: sha512-j0kORIBm8ayJNl3zQvD1TTPHJX3g042et6y/KQhZhnPrruO8exkTgG8X+NRpj7kIyMMEx74Xb3DyMIBtO0IKkQ==}
|
||||
|
||||
prosemirror-collab@1.3.1:
|
||||
resolution: {integrity: sha512-4SnynYR9TTYaQVXd/ieUvsVV4PDMBzrq2xPUWutHivDuOshZXqQ5rGbZM84HEaXKbLdItse7weMGOUdDVcLKEQ==}
|
||||
|
||||
prosemirror-commands@1.7.1:
|
||||
resolution: {integrity: sha512-rT7qZnQtx5c0/y/KlYaGvtG411S97UaL6gdp6RIZ23DLHanMYLyfGBV5DtSnZdthQql7W+lEVbpSfwtO8T+L2w==}
|
||||
|
||||
prosemirror-dropcursor@1.8.2:
|
||||
resolution: {integrity: sha512-CCk6Gyx9+Tt2sbYk5NK0nB1ukHi2ryaRgadV/LvyNuO3ena1payM2z6Cg0vO1ebK8cxbzo41ku2DE5Axj1Zuiw==}
|
||||
|
||||
prosemirror-gapcursor@1.3.2:
|
||||
resolution: {integrity: sha512-wtjswVBd2vaQRrnYZaBCbyDqr232Ed4p2QPtRIUK5FuqHYKGWkEwl08oQM4Tw7DOR0FsasARV5uJFvMZWxdNxQ==}
|
||||
|
||||
prosemirror-history@1.4.1:
|
||||
resolution: {integrity: sha512-2JZD8z2JviJrboD9cPuX/Sv/1ChFng+xh2tChQ2X4bB2HeK+rra/bmJ3xGntCcjhOqIzSDG6Id7e8RJ9QPXLEQ==}
|
||||
|
||||
prosemirror-inputrules@1.5.0:
|
||||
resolution: {integrity: sha512-K0xJRCmt+uSw7xesnHmcn72yBGTbY45vm8gXI4LZXbx2Z0jwh5aF9xrGQgrVPu0WbyFVFF3E/o9VhJYz6SQWnA==}
|
||||
|
||||
prosemirror-keymap@1.2.3:
|
||||
resolution: {integrity: sha512-4HucRlpiLd1IPQQXNqeo81BGtkY8Ai5smHhKW9jjPKRc2wQIxksg7Hl1tTI2IfT2B/LgX6bfYvXxEpJl7aKYKw==}
|
||||
|
||||
prosemirror-markdown@1.13.2:
|
||||
resolution: {integrity: sha512-FPD9rHPdA9fqzNmIIDhhnYQ6WgNoSWX9StUZ8LEKapaXU9i6XgykaHKhp6XMyXlOWetmaFgGDS/nu/w9/vUc5g==}
|
||||
|
||||
prosemirror-menu@1.2.5:
|
||||
resolution: {integrity: sha512-qwXzynnpBIeg1D7BAtjOusR+81xCp53j7iWu/IargiRZqRjGIlQuu1f3jFi+ehrHhWMLoyOQTSRx/IWZJqOYtQ==}
|
||||
|
||||
prosemirror-model@1.25.1:
|
||||
resolution: {integrity: sha512-AUvbm7qqmpZa5d9fPKMvH1Q5bqYQvAZWOGRvxsB6iFLyycvC9MwNemNVjHVrWgjaoxAfY8XVg7DbvQ/qxvI9Eg==}
|
||||
|
||||
prosemirror-schema-basic@1.2.4:
|
||||
resolution: {integrity: sha512-ELxP4TlX3yr2v5rM7Sb70SqStq5NvI15c0j9j/gjsrO5vaw+fnnpovCLEGIcpeGfifkuqJwl4fon6b+KdrODYQ==}
|
||||
|
||||
prosemirror-schema-list@1.5.1:
|
||||
resolution: {integrity: sha512-927lFx/uwyQaGwJxLWCZRkjXG0p48KpMj6ueoYiu4JX05GGuGcgzAy62dfiV8eFZftgyBUvLx76RsMe20fJl+Q==}
|
||||
|
||||
prosemirror-state@1.4.3:
|
||||
resolution: {integrity: sha512-goFKORVbvPuAQaXhpbemJFRKJ2aixr+AZMGiquiqKxaucC6hlpHNZHWgz5R7dS4roHiwq9vDctE//CZ++o0W1Q==}
|
||||
|
||||
prosemirror-tables@1.7.1:
|
||||
resolution: {integrity: sha512-eRQ97Bf+i9Eby99QbyAiyov43iOKgWa7QCGly+lrDt7efZ1v8NWolhXiB43hSDGIXT1UXgbs4KJN3a06FGpr1Q==}
|
||||
|
||||
prosemirror-trailing-node@3.0.0:
|
||||
resolution: {integrity: sha512-xiun5/3q0w5eRnGYfNlW1uU9W6x5MoFKWwq/0TIRgt09lv7Hcser2QYV8t4muXbEr+Fwo0geYn79Xs4GKywrRQ==}
|
||||
peerDependencies:
|
||||
prosemirror-model: ^1.22.1
|
||||
prosemirror-state: ^1.4.2
|
||||
prosemirror-view: ^1.33.8
|
||||
|
||||
prosemirror-transform@1.10.4:
|
||||
resolution: {integrity: sha512-pwDy22nAnGqNR1feOQKHxoFkkUtepoFAd3r2hbEDsnf4wp57kKA36hXsB3njA9FtONBEwSDnDeCiJe+ItD+ykw==}
|
||||
|
||||
prosemirror-view@1.40.0:
|
||||
resolution: {integrity: sha512-2G3svX0Cr1sJjkD/DYWSe3cfV5VPVTBOxI9XQEGWJDFEpsZb/gh4MV29ctv+OJx2RFX4BLt09i+6zaGM/ldkCw==}
|
||||
|
||||
proxy-from-env@1.1.0:
|
||||
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
|
||||
|
||||
prr@1.0.1:
|
||||
resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==}
|
||||
|
||||
punycode.js@2.3.1:
|
||||
resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
quansync@0.2.10:
|
||||
resolution: {integrity: sha512-t41VRkMYbkHyCYmOvx/6URnN80H7k4X0lLdBMGsz+maAwrJQYB1djpV6vHrQIBE0WBSGqhtEHrK9U3DWWH8v7A==}
|
||||
|
||||
@ -2980,6 +3304,9 @@ packages:
|
||||
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
|
||||
hasBin: true
|
||||
|
||||
rope-sequence@1.3.4:
|
||||
resolution: {integrity: sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==}
|
||||
|
||||
run-applescript@7.0.0:
|
||||
resolution: {integrity: sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==}
|
||||
engines: {node: '>=18'}
|
||||
@ -3223,6 +3550,9 @@ packages:
|
||||
resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
|
||||
tippy.js@6.3.7:
|
||||
resolution: {integrity: sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==}
|
||||
|
||||
to-object-path@0.3.0:
|
||||
resolution: {integrity: sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@ -3274,6 +3604,9 @@ packages:
|
||||
uc.micro@1.0.6:
|
||||
resolution: {integrity: sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==}
|
||||
|
||||
uc.micro@2.1.0:
|
||||
resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==}
|
||||
|
||||
ufo@1.6.1:
|
||||
resolution: {integrity: sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==}
|
||||
|
||||
@ -3538,6 +3871,9 @@ packages:
|
||||
peerDependencies:
|
||||
vue: ^3.0.11
|
||||
|
||||
w3c-keyname@2.2.8:
|
||||
resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==}
|
||||
|
||||
wait-on@6.0.1:
|
||||
resolution: {integrity: sha512-zht+KASY3usTY5u2LgaNqn/Cd8MukxLGjdcZxT2ns5QzDmTFc4XoWBgC+C/na+sMRZTuVygQoMYwdcVjHnYIVw==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
@ -3956,6 +4292,17 @@ snapshots:
|
||||
'@esbuild/win32-x64@0.25.5':
|
||||
optional: true
|
||||
|
||||
'@floating-ui/core@1.7.2':
|
||||
dependencies:
|
||||
'@floating-ui/utils': 0.2.10
|
||||
|
||||
'@floating-ui/dom@1.7.2':
|
||||
dependencies:
|
||||
'@floating-ui/core': 1.7.2
|
||||
'@floating-ui/utils': 0.2.10
|
||||
|
||||
'@floating-ui/utils@0.2.10': {}
|
||||
|
||||
'@hapi/hoek@9.3.0': {}
|
||||
|
||||
'@hapi/topo@5.1.0':
|
||||
@ -4123,6 +4470,10 @@ snapshots:
|
||||
|
||||
'@polka/url@1.0.0-next.29': {}
|
||||
|
||||
'@popperjs/core@2.11.8': {}
|
||||
|
||||
'@remirror/core-constants@3.0.0': {}
|
||||
|
||||
'@rollup/pluginutils@5.2.0(rollup@4.44.0)':
|
||||
dependencies:
|
||||
'@types/estree': 1.0.8
|
||||
@ -4208,6 +4559,193 @@ snapshots:
|
||||
|
||||
'@sindresorhus/merge-streams@4.0.0': {}
|
||||
|
||||
'@tiptap/core@2.23.1(@tiptap/pm@2.23.1)':
|
||||
dependencies:
|
||||
'@tiptap/pm': 2.23.1
|
||||
|
||||
'@tiptap/extension-blockquote@2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))':
|
||||
dependencies:
|
||||
'@tiptap/core': 2.23.1(@tiptap/pm@2.23.1)
|
||||
|
||||
'@tiptap/extension-bold@2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))':
|
||||
dependencies:
|
||||
'@tiptap/core': 2.23.1(@tiptap/pm@2.23.1)
|
||||
|
||||
'@tiptap/extension-bubble-menu@2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))(@tiptap/pm@2.23.1)':
|
||||
dependencies:
|
||||
'@tiptap/core': 2.23.1(@tiptap/pm@2.23.1)
|
||||
'@tiptap/pm': 2.23.1
|
||||
tippy.js: 6.3.7
|
||||
|
||||
'@tiptap/extension-bullet-list@2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))':
|
||||
dependencies:
|
||||
'@tiptap/core': 2.23.1(@tiptap/pm@2.23.1)
|
||||
|
||||
'@tiptap/extension-code-block@2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))(@tiptap/pm@2.23.1)':
|
||||
dependencies:
|
||||
'@tiptap/core': 2.23.1(@tiptap/pm@2.23.1)
|
||||
'@tiptap/pm': 2.23.1
|
||||
|
||||
'@tiptap/extension-code@2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))':
|
||||
dependencies:
|
||||
'@tiptap/core': 2.23.1(@tiptap/pm@2.23.1)
|
||||
|
||||
'@tiptap/extension-document@2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))':
|
||||
dependencies:
|
||||
'@tiptap/core': 2.23.1(@tiptap/pm@2.23.1)
|
||||
|
||||
'@tiptap/extension-dropcursor@2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))(@tiptap/pm@2.23.1)':
|
||||
dependencies:
|
||||
'@tiptap/core': 2.23.1(@tiptap/pm@2.23.1)
|
||||
'@tiptap/pm': 2.23.1
|
||||
|
||||
'@tiptap/extension-emoji@2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))(@tiptap/pm@2.23.1)(@tiptap/suggestion@2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))(@tiptap/pm@2.23.1))(emojibase@16.0.0)':
|
||||
dependencies:
|
||||
'@tiptap/core': 2.23.1(@tiptap/pm@2.23.1)
|
||||
'@tiptap/pm': 2.23.1
|
||||
'@tiptap/suggestion': 2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))(@tiptap/pm@2.23.1)
|
||||
emoji-regex: 10.4.0
|
||||
emojibase-data: 15.3.2(emojibase@16.0.0)
|
||||
is-emoji-supported: 0.0.5
|
||||
transitivePeerDependencies:
|
||||
- emojibase
|
||||
|
||||
'@tiptap/extension-floating-menu@2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))(@tiptap/pm@2.23.1)':
|
||||
dependencies:
|
||||
'@tiptap/core': 2.23.1(@tiptap/pm@2.23.1)
|
||||
'@tiptap/pm': 2.23.1
|
||||
tippy.js: 6.3.7
|
||||
|
||||
'@tiptap/extension-gapcursor@2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))(@tiptap/pm@2.23.1)':
|
||||
dependencies:
|
||||
'@tiptap/core': 2.23.1(@tiptap/pm@2.23.1)
|
||||
'@tiptap/pm': 2.23.1
|
||||
|
||||
'@tiptap/extension-hard-break@2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))':
|
||||
dependencies:
|
||||
'@tiptap/core': 2.23.1(@tiptap/pm@2.23.1)
|
||||
|
||||
'@tiptap/extension-heading@2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))':
|
||||
dependencies:
|
||||
'@tiptap/core': 2.23.1(@tiptap/pm@2.23.1)
|
||||
|
||||
'@tiptap/extension-history@2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))(@tiptap/pm@2.23.1)':
|
||||
dependencies:
|
||||
'@tiptap/core': 2.23.1(@tiptap/pm@2.23.1)
|
||||
'@tiptap/pm': 2.23.1
|
||||
|
||||
'@tiptap/extension-horizontal-rule@2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))(@tiptap/pm@2.23.1)':
|
||||
dependencies:
|
||||
'@tiptap/core': 2.23.1(@tiptap/pm@2.23.1)
|
||||
'@tiptap/pm': 2.23.1
|
||||
|
||||
'@tiptap/extension-image@2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))':
|
||||
dependencies:
|
||||
'@tiptap/core': 2.23.1(@tiptap/pm@2.23.1)
|
||||
|
||||
'@tiptap/extension-italic@2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))':
|
||||
dependencies:
|
||||
'@tiptap/core': 2.23.1(@tiptap/pm@2.23.1)
|
||||
|
||||
'@tiptap/extension-link@2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))(@tiptap/pm@2.23.1)':
|
||||
dependencies:
|
||||
'@tiptap/core': 2.23.1(@tiptap/pm@2.23.1)
|
||||
'@tiptap/pm': 2.23.1
|
||||
linkifyjs: 4.3.1
|
||||
|
||||
'@tiptap/extension-list-item@2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))':
|
||||
dependencies:
|
||||
'@tiptap/core': 2.23.1(@tiptap/pm@2.23.1)
|
||||
|
||||
'@tiptap/extension-mention@2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))(@tiptap/pm@2.23.1)(@tiptap/suggestion@2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))(@tiptap/pm@2.23.1))':
|
||||
dependencies:
|
||||
'@tiptap/core': 2.23.1(@tiptap/pm@2.23.1)
|
||||
'@tiptap/pm': 2.23.1
|
||||
'@tiptap/suggestion': 2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))(@tiptap/pm@2.23.1)
|
||||
|
||||
'@tiptap/extension-ordered-list@2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))':
|
||||
dependencies:
|
||||
'@tiptap/core': 2.23.1(@tiptap/pm@2.23.1)
|
||||
|
||||
'@tiptap/extension-paragraph@2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))':
|
||||
dependencies:
|
||||
'@tiptap/core': 2.23.1(@tiptap/pm@2.23.1)
|
||||
|
||||
'@tiptap/extension-placeholder@2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))(@tiptap/pm@2.23.1)':
|
||||
dependencies:
|
||||
'@tiptap/core': 2.23.1(@tiptap/pm@2.23.1)
|
||||
'@tiptap/pm': 2.23.1
|
||||
|
||||
'@tiptap/extension-strike@2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))':
|
||||
dependencies:
|
||||
'@tiptap/core': 2.23.1(@tiptap/pm@2.23.1)
|
||||
|
||||
'@tiptap/extension-text-style@2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))':
|
||||
dependencies:
|
||||
'@tiptap/core': 2.23.1(@tiptap/pm@2.23.1)
|
||||
|
||||
'@tiptap/extension-text@2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))':
|
||||
dependencies:
|
||||
'@tiptap/core': 2.23.1(@tiptap/pm@2.23.1)
|
||||
|
||||
'@tiptap/pm@2.23.1':
|
||||
dependencies:
|
||||
prosemirror-changeset: 2.3.1
|
||||
prosemirror-collab: 1.3.1
|
||||
prosemirror-commands: 1.7.1
|
||||
prosemirror-dropcursor: 1.8.2
|
||||
prosemirror-gapcursor: 1.3.2
|
||||
prosemirror-history: 1.4.1
|
||||
prosemirror-inputrules: 1.5.0
|
||||
prosemirror-keymap: 1.2.3
|
||||
prosemirror-markdown: 1.13.2
|
||||
prosemirror-menu: 1.2.5
|
||||
prosemirror-model: 1.25.1
|
||||
prosemirror-schema-basic: 1.2.4
|
||||
prosemirror-schema-list: 1.5.1
|
||||
prosemirror-state: 1.4.3
|
||||
prosemirror-tables: 1.7.1
|
||||
prosemirror-trailing-node: 3.0.0(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.40.0)
|
||||
prosemirror-transform: 1.10.4
|
||||
prosemirror-view: 1.40.0
|
||||
|
||||
'@tiptap/starter-kit@2.23.1':
|
||||
dependencies:
|
||||
'@tiptap/core': 2.23.1(@tiptap/pm@2.23.1)
|
||||
'@tiptap/extension-blockquote': 2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))
|
||||
'@tiptap/extension-bold': 2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))
|
||||
'@tiptap/extension-bullet-list': 2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))
|
||||
'@tiptap/extension-code': 2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))
|
||||
'@tiptap/extension-code-block': 2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))(@tiptap/pm@2.23.1)
|
||||
'@tiptap/extension-document': 2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))
|
||||
'@tiptap/extension-dropcursor': 2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))(@tiptap/pm@2.23.1)
|
||||
'@tiptap/extension-gapcursor': 2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))(@tiptap/pm@2.23.1)
|
||||
'@tiptap/extension-hard-break': 2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))
|
||||
'@tiptap/extension-heading': 2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))
|
||||
'@tiptap/extension-history': 2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))(@tiptap/pm@2.23.1)
|
||||
'@tiptap/extension-horizontal-rule': 2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))(@tiptap/pm@2.23.1)
|
||||
'@tiptap/extension-italic': 2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))
|
||||
'@tiptap/extension-list-item': 2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))
|
||||
'@tiptap/extension-ordered-list': 2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))
|
||||
'@tiptap/extension-paragraph': 2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))
|
||||
'@tiptap/extension-strike': 2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))
|
||||
'@tiptap/extension-text': 2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))
|
||||
'@tiptap/extension-text-style': 2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))
|
||||
'@tiptap/pm': 2.23.1
|
||||
|
||||
'@tiptap/suggestion@2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))(@tiptap/pm@2.23.1)':
|
||||
dependencies:
|
||||
'@tiptap/core': 2.23.1(@tiptap/pm@2.23.1)
|
||||
'@tiptap/pm': 2.23.1
|
||||
|
||||
'@tiptap/vue-3@2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))(@tiptap/pm@2.23.1)(vue@3.5.17(typescript@5.2.2))':
|
||||
dependencies:
|
||||
'@tiptap/core': 2.23.1(@tiptap/pm@2.23.1)
|
||||
'@tiptap/extension-bubble-menu': 2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))(@tiptap/pm@2.23.1)
|
||||
'@tiptap/extension-floating-menu': 2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))(@tiptap/pm@2.23.1)
|
||||
'@tiptap/pm': 2.23.1
|
||||
vue: 3.5.17(typescript@5.2.2)
|
||||
|
||||
'@tsconfig/node18@18.2.4': {}
|
||||
|
||||
'@types/d3-scale-chromatic@3.1.0': {}
|
||||
@ -4243,16 +4781,25 @@ snapshots:
|
||||
|
||||
'@types/katex@0.16.7': {}
|
||||
|
||||
'@types/linkify-it@5.0.0': {}
|
||||
|
||||
'@types/lodash-es@4.17.12':
|
||||
dependencies:
|
||||
'@types/lodash': 4.17.18
|
||||
|
||||
'@types/lodash@4.17.18': {}
|
||||
|
||||
'@types/markdown-it@14.1.2':
|
||||
dependencies:
|
||||
'@types/linkify-it': 5.0.0
|
||||
'@types/mdurl': 2.0.0
|
||||
|
||||
'@types/mdast@3.0.15':
|
||||
dependencies:
|
||||
'@types/unist': 2.0.11
|
||||
|
||||
'@types/mdurl@2.0.0': {}
|
||||
|
||||
'@types/minimatch@5.1.2': {}
|
||||
|
||||
'@types/ms@2.1.0': {}
|
||||
@ -5058,6 +5605,8 @@ snapshots:
|
||||
dependencies:
|
||||
layout-base: 1.0.2
|
||||
|
||||
crelt@1.0.6: {}
|
||||
|
||||
cross-env@7.0.3:
|
||||
dependencies:
|
||||
cross-spawn: 7.0.6
|
||||
@ -5390,8 +5939,16 @@ snapshots:
|
||||
|
||||
elkjs@0.9.3: {}
|
||||
|
||||
emoji-regex@10.4.0: {}
|
||||
|
||||
emoji-regex@8.0.0: {}
|
||||
|
||||
emojibase-data@15.3.2(emojibase@16.0.0):
|
||||
dependencies:
|
||||
emojibase: 16.0.0
|
||||
|
||||
emojibase@16.0.0: {}
|
||||
|
||||
enhanced-resolve@5.18.2:
|
||||
dependencies:
|
||||
graceful-fs: 4.2.11
|
||||
@ -5479,6 +6036,8 @@ snapshots:
|
||||
|
||||
escape-string-regexp@1.0.5: {}
|
||||
|
||||
escape-string-regexp@4.0.0: {}
|
||||
|
||||
escape-string-regexp@5.0.0: {}
|
||||
|
||||
eslint-scope@5.1.1:
|
||||
@ -5864,6 +6423,8 @@ snapshots:
|
||||
|
||||
is-docker@3.0.0: {}
|
||||
|
||||
is-emoji-supported@0.0.5: {}
|
||||
|
||||
is-extendable@0.1.1: {}
|
||||
|
||||
is-extendable@1.0.1:
|
||||
@ -6033,6 +6594,12 @@ snapshots:
|
||||
dependencies:
|
||||
uc.micro: 1.0.6
|
||||
|
||||
linkify-it@5.0.0:
|
||||
dependencies:
|
||||
uc.micro: 2.1.0
|
||||
|
||||
linkifyjs@4.3.1: {}
|
||||
|
||||
loader-runner@4.3.0: {}
|
||||
|
||||
local-pkg@1.1.1:
|
||||
@ -6100,6 +6667,15 @@ snapshots:
|
||||
mdurl: 1.0.1
|
||||
uc.micro: 1.0.6
|
||||
|
||||
markdown-it@14.1.0:
|
||||
dependencies:
|
||||
argparse: 2.0.1
|
||||
entities: 4.5.0
|
||||
linkify-it: 5.0.0
|
||||
mdurl: 2.0.0
|
||||
punycode.js: 2.3.1
|
||||
uc.micro: 2.1.0
|
||||
|
||||
markdown-it@8.4.2:
|
||||
dependencies:
|
||||
argparse: 1.0.10
|
||||
@ -6135,6 +6711,8 @@ snapshots:
|
||||
|
||||
mdurl@1.0.1: {}
|
||||
|
||||
mdurl@2.0.0: {}
|
||||
|
||||
memorystream@0.3.1: {}
|
||||
|
||||
merge-stream@2.0.0: {}
|
||||
@ -6489,6 +7067,8 @@ snapshots:
|
||||
is-inside-container: 1.0.0
|
||||
is-wsl: 3.1.0
|
||||
|
||||
orderedmap@2.1.1: {}
|
||||
|
||||
package-manager-detector@1.3.0: {}
|
||||
|
||||
parchment@1.1.4: {}
|
||||
@ -6575,11 +7155,116 @@ snapshots:
|
||||
|
||||
prismjs@1.30.0: {}
|
||||
|
||||
prosemirror-changeset@2.3.1:
|
||||
dependencies:
|
||||
prosemirror-transform: 1.10.4
|
||||
|
||||
prosemirror-collab@1.3.1:
|
||||
dependencies:
|
||||
prosemirror-state: 1.4.3
|
||||
|
||||
prosemirror-commands@1.7.1:
|
||||
dependencies:
|
||||
prosemirror-model: 1.25.1
|
||||
prosemirror-state: 1.4.3
|
||||
prosemirror-transform: 1.10.4
|
||||
|
||||
prosemirror-dropcursor@1.8.2:
|
||||
dependencies:
|
||||
prosemirror-state: 1.4.3
|
||||
prosemirror-transform: 1.10.4
|
||||
prosemirror-view: 1.40.0
|
||||
|
||||
prosemirror-gapcursor@1.3.2:
|
||||
dependencies:
|
||||
prosemirror-keymap: 1.2.3
|
||||
prosemirror-model: 1.25.1
|
||||
prosemirror-state: 1.4.3
|
||||
prosemirror-view: 1.40.0
|
||||
|
||||
prosemirror-history@1.4.1:
|
||||
dependencies:
|
||||
prosemirror-state: 1.4.3
|
||||
prosemirror-transform: 1.10.4
|
||||
prosemirror-view: 1.40.0
|
||||
rope-sequence: 1.3.4
|
||||
|
||||
prosemirror-inputrules@1.5.0:
|
||||
dependencies:
|
||||
prosemirror-state: 1.4.3
|
||||
prosemirror-transform: 1.10.4
|
||||
|
||||
prosemirror-keymap@1.2.3:
|
||||
dependencies:
|
||||
prosemirror-state: 1.4.3
|
||||
w3c-keyname: 2.2.8
|
||||
|
||||
prosemirror-markdown@1.13.2:
|
||||
dependencies:
|
||||
'@types/markdown-it': 14.1.2
|
||||
markdown-it: 14.1.0
|
||||
prosemirror-model: 1.25.1
|
||||
|
||||
prosemirror-menu@1.2.5:
|
||||
dependencies:
|
||||
crelt: 1.0.6
|
||||
prosemirror-commands: 1.7.1
|
||||
prosemirror-history: 1.4.1
|
||||
prosemirror-state: 1.4.3
|
||||
|
||||
prosemirror-model@1.25.1:
|
||||
dependencies:
|
||||
orderedmap: 2.1.1
|
||||
|
||||
prosemirror-schema-basic@1.2.4:
|
||||
dependencies:
|
||||
prosemirror-model: 1.25.1
|
||||
|
||||
prosemirror-schema-list@1.5.1:
|
||||
dependencies:
|
||||
prosemirror-model: 1.25.1
|
||||
prosemirror-state: 1.4.3
|
||||
prosemirror-transform: 1.10.4
|
||||
|
||||
prosemirror-state@1.4.3:
|
||||
dependencies:
|
||||
prosemirror-model: 1.25.1
|
||||
prosemirror-transform: 1.10.4
|
||||
prosemirror-view: 1.40.0
|
||||
|
||||
prosemirror-tables@1.7.1:
|
||||
dependencies:
|
||||
prosemirror-keymap: 1.2.3
|
||||
prosemirror-model: 1.25.1
|
||||
prosemirror-state: 1.4.3
|
||||
prosemirror-transform: 1.10.4
|
||||
prosemirror-view: 1.40.0
|
||||
|
||||
prosemirror-trailing-node@3.0.0(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.40.0):
|
||||
dependencies:
|
||||
'@remirror/core-constants': 3.0.0
|
||||
escape-string-regexp: 4.0.0
|
||||
prosemirror-model: 1.25.1
|
||||
prosemirror-state: 1.4.3
|
||||
prosemirror-view: 1.40.0
|
||||
|
||||
prosemirror-transform@1.10.4:
|
||||
dependencies:
|
||||
prosemirror-model: 1.25.1
|
||||
|
||||
prosemirror-view@1.40.0:
|
||||
dependencies:
|
||||
prosemirror-model: 1.25.1
|
||||
prosemirror-state: 1.4.3
|
||||
prosemirror-transform: 1.10.4
|
||||
|
||||
proxy-from-env@1.1.0: {}
|
||||
|
||||
prr@1.0.1:
|
||||
optional: true
|
||||
|
||||
punycode.js@2.3.1: {}
|
||||
|
||||
quansync@0.2.10: {}
|
||||
|
||||
queue-microtask@1.2.3: {}
|
||||
@ -6688,6 +7373,8 @@ snapshots:
|
||||
'@rollup/rollup-win32-x64-msvc': 4.44.0
|
||||
fsevents: 2.3.3
|
||||
|
||||
rope-sequence@1.3.4: {}
|
||||
|
||||
run-applescript@7.0.0: {}
|
||||
|
||||
run-parallel@1.2.0:
|
||||
@ -6929,6 +7616,10 @@ snapshots:
|
||||
fdir: 6.4.6(picomatch@4.0.2)
|
||||
picomatch: 4.0.2
|
||||
|
||||
tippy.js@6.3.7:
|
||||
dependencies:
|
||||
'@popperjs/core': 2.11.8
|
||||
|
||||
to-object-path@0.3.0:
|
||||
dependencies:
|
||||
kind-of: 3.2.2
|
||||
@ -6969,6 +7660,8 @@ snapshots:
|
||||
|
||||
uc.micro@1.0.6: {}
|
||||
|
||||
uc.micro@2.1.0: {}
|
||||
|
||||
ufo@1.6.1: {}
|
||||
|
||||
unconfig@0.3.13:
|
||||
@ -7270,6 +7963,8 @@ snapshots:
|
||||
vooks: 0.2.12(vue@3.5.17(typescript@5.2.2))
|
||||
vue: 3.5.17(typescript@5.2.2)
|
||||
|
||||
w3c-keyname@2.2.8: {}
|
||||
|
||||
wait-on@6.0.1:
|
||||
dependencies:
|
||||
axios: 0.25.0
|
||||
|
@ -2,12 +2,14 @@ import { post, get, upload } from '@/utils/request'
|
||||
|
||||
//ES搜索-主页搜索什么都有、指定用户、指定群、群与用户概览
|
||||
export const ServeSeachQueryAll = (data = {}) => {
|
||||
return post('/api/v1/elasticsearch/query-all/v2', data)
|
||||
return post('/api/v1/elasticsearch/query-all', data)
|
||||
// return post('/api/v1/elasticsearch/query-all/v2', data)
|
||||
}
|
||||
|
||||
// ES搜索用户数据
|
||||
export const ServeQueryUser = (data) => {
|
||||
return post('/api/v1/elasticsearch/query-user/v2', data)
|
||||
return post('/api/v1/elasticsearch/query-user', data)
|
||||
// return post('/api/v1/elasticsearch/query-user/v2', data)
|
||||
}
|
||||
|
||||
// ES搜索群组数据
|
||||
|
@ -33,7 +33,6 @@ const props = defineProps({
|
||||
const emit = defineEmits(['editor-event'])
|
||||
const userStore = useUserStore()
|
||||
const dialogueStore = useDialogueStore()
|
||||
console.log('dialogueStore', dialogueStore.talk.talk_type)
|
||||
const editorDraftStore = useEditorDraftStore()
|
||||
const editorRef = ref(null)
|
||||
const content = ref('')
|
||||
@ -172,7 +171,6 @@ const updateMentionPosition = (range) => {
|
||||
}
|
||||
}
|
||||
const insertMention = (member, clonedRange) => {
|
||||
console.log('插入mention', member);
|
||||
const selection = window.getSelection();
|
||||
if (!clonedRange || !selection || !editorRef.value) return;
|
||||
const range = clonedRange;
|
||||
@ -565,20 +563,20 @@ const parseEditorContent = () => {
|
||||
currentTextBuffer += '\n';
|
||||
break;
|
||||
case 'IMG':
|
||||
flushTextBufferIfNeeded();
|
||||
const src = node.getAttribute('src');
|
||||
const alt = node.getAttribute('alt');
|
||||
const isEmojiPic = node.classList.contains('editor-emoji');
|
||||
const isTextEmojiPlaceholder = node.classList.contains('emoji');
|
||||
if (isTextEmojiPlaceholder && alt) {
|
||||
if ((isEmojiPic || isTextEmojiPlaceholder) && alt) {
|
||||
currentTextBuffer += alt;
|
||||
} else if (src) {
|
||||
flushTextBufferIfNeeded();
|
||||
items.push({
|
||||
type: 3,
|
||||
content: src,
|
||||
isEmoji: isEmojiPic,
|
||||
width: node.getAttribute('data-original-width') || node.width || null,
|
||||
height: node.getAttribute('data-original-height') || node.height || null,
|
||||
isEmoji: false,
|
||||
width: node.getAttribute('data-original-width') || node.width,
|
||||
height: node.getAttribute('data-original-height') || node.height
|
||||
});
|
||||
}
|
||||
break;
|
||||
@ -887,12 +885,8 @@ const insertImageEmoji = (imgSrc, altText) => {
|
||||
img.className = 'editor-emoji';
|
||||
img.setAttribute('data-role', 'emoji');
|
||||
range.insertNode(img);
|
||||
const spaceNode = document.createTextNode('\u00A0');
|
||||
range.setStartAfter(img);
|
||||
range.collapse(true);
|
||||
range.insertNode(spaceNode);
|
||||
range.setStartAfter(spaceNode);
|
||||
range.collapse(true);
|
||||
if (selection) {
|
||||
selection.removeAllRanges();
|
||||
selection.addRange(range);
|
||||
|
161
src/components/editor/MentionList.vue
Normal file
161
src/components/editor/MentionList.vue
Normal file
@ -0,0 +1,161 @@
|
||||
<template>
|
||||
<div class="dropdown-menu">
|
||||
<n-virtual-list
|
||||
ref="virtualListRef"
|
||||
style="max-height: 240px"
|
||||
:item-size="50"
|
||||
:items="props.items"
|
||||
>
|
||||
<template #default="{ item }">
|
||||
<button
|
||||
:class="{ 'is-selected': props.items[selectedIndex] === item }"
|
||||
@click="selectItem(item)"
|
||||
>
|
||||
<img :src="item.avatar" class="avatar" />
|
||||
<span class="nickname">{{ item.nickname }}</span>
|
||||
</button>
|
||||
</template>
|
||||
</n-virtual-list>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch, defineProps, defineExpose } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
items: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
command: {
|
||||
type: Function,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
|
||||
const selectedIndex = ref(0)
|
||||
const virtualListRef = ref(null)
|
||||
|
||||
watch(
|
||||
() => props.items,
|
||||
() => {
|
||||
selectedIndex.value = 0
|
||||
}
|
||||
)
|
||||
|
||||
const onKeyDown = ({ event }) => {
|
||||
console.log('event',event)
|
||||
if (event.key === 'ArrowUp') {
|
||||
upHandler()
|
||||
return true
|
||||
}
|
||||
|
||||
if (event.key === 'ArrowDown') {
|
||||
downHandler()
|
||||
return true
|
||||
}
|
||||
|
||||
if (event.key === 'Enter') {
|
||||
enterHandler()
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
const upHandler = () => {
|
||||
selectedIndex.value =
|
||||
(selectedIndex.value + props.items.length - 1) % props.items.length
|
||||
virtualListRef.value?.scrollTo({ index: selectedIndex.value })
|
||||
}
|
||||
|
||||
const downHandler = () => {
|
||||
selectedIndex.value = (selectedIndex.value + 1) % props.items.length
|
||||
virtualListRef.value?.scrollTo({ index: selectedIndex.value })
|
||||
}
|
||||
|
||||
const enterHandler = () => {
|
||||
selectItem(props.items[selectedIndex.value])
|
||||
}
|
||||
|
||||
const selectItem = item => {
|
||||
if (item) {
|
||||
props.command({ id: item.id, label: item.nickname })
|
||||
}
|
||||
}
|
||||
defineExpose({
|
||||
onKeyDown
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.dropdown-menu {
|
||||
background: var(--white, #fff);
|
||||
border: 1px solid var(--gray-1, #e0e0e0);
|
||||
border-radius: 0.7rem;
|
||||
box-shadow: var(--shadow, 0 2px 12px 0 rgba(0, 0, 0, 0.1));
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.1rem;
|
||||
overflow: auto;
|
||||
padding: 0.4rem;
|
||||
position: relative;
|
||||
max-height: 200px;
|
||||
width: 200px;
|
||||
button {
|
||||
align-items: center;
|
||||
background-color: transparent;
|
||||
display: flex;
|
||||
gap: 0.25rem;
|
||||
text-align: left;
|
||||
width: 100%;
|
||||
padding: 5px 10px;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover,
|
||||
&:hover.is-selected {
|
||||
background-color: var(--gray-3, #f5f7fa);
|
||||
}
|
||||
|
||||
&.is-selected {
|
||||
background-color: var(--gray-2, #f0f0f0);
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 50%;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.nickname {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 暗色模式下的样式调整 */
|
||||
html[theme-mode='dark'] {
|
||||
.dropdown-menu {
|
||||
background-color: #1e1e1e;
|
||||
border-color: #333;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.3);
|
||||
|
||||
button {
|
||||
&:hover,
|
||||
&:hover.is-selected {
|
||||
background-color: #2c2c2c;
|
||||
}
|
||||
|
||||
&.is-selected {
|
||||
background-color: #333;
|
||||
}
|
||||
|
||||
.nickname {
|
||||
color: #e0e0e0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
1066
src/components/editor/TiptapEditor.vue
Normal file
1066
src/components/editor/TiptapEditor.vue
Normal file
File diff suppressed because it is too large
Load Diff
115
src/components/editor/suggestion.js
Normal file
115
src/components/editor/suggestion.js
Normal file
@ -0,0 +1,115 @@
|
||||
import { computePosition, flip, shift } from '@floating-ui/dom'
|
||||
import { posToDOMRect, VueRenderer } from '@tiptap/vue-3'
|
||||
|
||||
import MentionList from './MentionList.vue'
|
||||
import { defAvatar } from '@/constant/default'
|
||||
|
||||
const updatePosition = (editor, element) => {
|
||||
const virtualElement = {
|
||||
getBoundingClientRect: () => posToDOMRect(editor.view, editor.state.selection.from, editor.state.selection.to),
|
||||
}
|
||||
|
||||
computePosition(virtualElement, element, {
|
||||
placement: 'bottom-start',
|
||||
strategy: 'absolute',
|
||||
middleware: [shift(), flip()],
|
||||
}).then(({ x, y, strategy }) => {
|
||||
element.style.position = strategy
|
||||
if (window.__POWERED_BY_WUJIE__) {
|
||||
element.style.left = `${x + 200}px`
|
||||
element.style.top = `${y + 100}px`
|
||||
} else {
|
||||
element.style.left = `${x}px`
|
||||
element.style.top = `${y}px`
|
||||
}
|
||||
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
export default {
|
||||
items: ({ query, editor, props }) => {
|
||||
if (!props.members || !props.members.length) {
|
||||
return []
|
||||
}
|
||||
|
||||
let list = [...props.members]
|
||||
|
||||
// 如果是群组管理员,添加"所有人"选项
|
||||
if (props.isGroupManager) {
|
||||
list.unshift({ id: 0, nickname: '所有人', avatar: defAvatar })
|
||||
}
|
||||
|
||||
const filteredItems = list.filter(
|
||||
(item) => item.nickname.toLowerCase().includes(query.toLowerCase())
|
||||
)
|
||||
|
||||
// 如果没有匹配项,返回空数组以关闭弹窗
|
||||
if (filteredItems.length === 0) {
|
||||
return []
|
||||
}
|
||||
|
||||
return filteredItems
|
||||
},
|
||||
|
||||
render: () => {
|
||||
let component
|
||||
|
||||
return {
|
||||
onStart: props => {
|
||||
// 如果没有匹配项,不创建弹窗
|
||||
if (!props.items || props.items.length === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
component = new VueRenderer(MentionList, {
|
||||
// Vue 3 props格式
|
||||
props,
|
||||
editor: props.editor,
|
||||
})
|
||||
|
||||
if (!props.clientRect) {
|
||||
return
|
||||
}
|
||||
|
||||
component.element.style.position = 'absolute'
|
||||
|
||||
document.body.appendChild(component.element)
|
||||
|
||||
updatePosition(props.editor, component.element)
|
||||
},
|
||||
|
||||
onUpdate(props) {
|
||||
component.updateProps(props)
|
||||
|
||||
if (props.items.length === 0) {
|
||||
this.onExit()
|
||||
return
|
||||
}
|
||||
|
||||
if (!props.clientRect) {
|
||||
return
|
||||
}
|
||||
|
||||
updatePosition(props.editor, component.element)
|
||||
},
|
||||
|
||||
onKeyDown(props) {
|
||||
if (props.event.key === 'Escape') {
|
||||
this.onExit()
|
||||
return true
|
||||
}
|
||||
if(!component?.props.items?.length){
|
||||
return false
|
||||
}
|
||||
return component.ref.onKeyDown(props)
|
||||
},
|
||||
|
||||
onExit() {
|
||||
console.log('component.element',component.element)
|
||||
component.element.remove()
|
||||
component.destroy()
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
@ -1,70 +1,172 @@
|
||||
<template>
|
||||
<span>
|
||||
<template v-for="(part, index) in parts" :key="index">
|
||||
<span v-if="part.highlighted" :class="highlightClass">
|
||||
{{ part.text }}
|
||||
<template v-if="isHtml">
|
||||
<span v-html="highlightedHtml" />
|
||||
</template>
|
||||
<template v-else>
|
||||
<span class="text-content">
|
||||
<template v-for="(part, index) in parts" :key="index">
|
||||
<span
|
||||
v-if="part.highlighted"
|
||||
:class="highlightClass"
|
||||
v-html="textReplaceEmoji(part.text)"
|
||||
/>
|
||||
<span v-else v-html="textReplaceEmoji(part.text)" />
|
||||
</template>
|
||||
</span>
|
||||
<span v-else>{{ part.text }}</span>
|
||||
</template>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import { textReplaceEmoji } from '@/utils/emojis'
|
||||
|
||||
const props = defineProps({
|
||||
text: {
|
||||
type: String,
|
||||
required: true,
|
||||
required: true
|
||||
},
|
||||
searchText: {
|
||||
type: String,
|
||||
default: '',
|
||||
default: ''
|
||||
},
|
||||
highlightClass: {
|
||||
type: String,
|
||||
default: 'highlight',
|
||||
},
|
||||
default: 'highlight'
|
||||
}
|
||||
})
|
||||
|
||||
// 检测是否为HTML内容
|
||||
const isHtml = computed(() => {
|
||||
return /<[^>]*>/g.test(props.text)
|
||||
})
|
||||
|
||||
const escapedSearchText = computed(() =>
|
||||
String(props.searchText).replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'),
|
||||
String(props.searchText).replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&')
|
||||
)
|
||||
|
||||
const pattern = computed(() => new RegExp(escapedSearchText.value, 'gi'))
|
||||
|
||||
const parts = computed(() => {
|
||||
if (!props.searchText || !props.text)
|
||||
return [{ text: props.text, highlighted: false }];
|
||||
if (!props.searchText || !props.text) return [{ text: props.text, highlighted: false }]
|
||||
|
||||
const result = [];
|
||||
let currentIndex = 0;
|
||||
const escapedSearchTextValue = escapedSearchText.value;
|
||||
const searchPattern = new RegExp(`(${escapedSearchTextValue})`, 'gi');
|
||||
const result = []
|
||||
let currentIndex = 0
|
||||
const escapedSearchTextValue = escapedSearchText.value
|
||||
const searchPattern = new RegExp(`(${escapedSearchTextValue})`, 'gi')
|
||||
|
||||
props.text.replace(searchPattern, (match, p1, offset) => {
|
||||
// 添加非高亮文本
|
||||
if (currentIndex < offset) {
|
||||
result.push({ text: props.text.slice(currentIndex, offset), highlighted: false });
|
||||
result.push({ text: props.text.slice(currentIndex, offset), highlighted: false })
|
||||
}
|
||||
// 添加高亮文本
|
||||
result.push({ text: p1, highlighted: true });
|
||||
result.push({ text: p1, highlighted: true })
|
||||
// 更新当前索引
|
||||
currentIndex = offset + p1.length;
|
||||
return p1; // 这个返回值不影响最终结果,只是replace方法的要求
|
||||
});
|
||||
currentIndex = offset + p1.length
|
||||
return p1 // 这个返回值不影响最终结果,只是replace方法的要求
|
||||
})
|
||||
|
||||
// 添加剩余的非高亮文本(如果有的话)
|
||||
if (currentIndex < props.text.length) {
|
||||
result.push({ text: props.text.slice(currentIndex), highlighted: false });
|
||||
result.push({ text: props.text.slice(currentIndex), highlighted: false })
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
return result
|
||||
})
|
||||
|
||||
// 处理特殊字符的函数
|
||||
const processSpecialChars = (text) => {
|
||||
return (
|
||||
text
|
||||
// 处理换行符
|
||||
.replace(/\n/g, '<br>')
|
||||
// 处理制表符
|
||||
.replace(/\t/g, ' ')
|
||||
// 处理连续空格(保留第一个,其余转换为 )
|
||||
.replace(/ {2,}/g, (match) => {
|
||||
return ' '.repeat(match.length)
|
||||
})
|
||||
// 处理不可见字符(零宽空格等)
|
||||
.replace(/[\u200B-\u200D\uFEFF]/g, '')
|
||||
)
|
||||
}
|
||||
|
||||
// 处理HTML内容的高亮 - 使用字符串处理方法
|
||||
const highlightedHtml = computed(() => {
|
||||
if (!props.searchText || !props.text) {
|
||||
// 先处理特殊字符,再处理表情
|
||||
return textReplaceEmoji(processSpecialChars(props.text))
|
||||
}
|
||||
|
||||
// 对于富文本,使用一个更安全的方法
|
||||
// 将HTML内容按标签分割,只对文本部分进行高亮
|
||||
|
||||
// 分割HTML字符串,保护标签
|
||||
const parts = []
|
||||
let lastIndex = 0
|
||||
const tagRegex = /<[^>]*>/g
|
||||
let tagMatch
|
||||
|
||||
// 重置正则表达式的lastIndex
|
||||
tagRegex.lastIndex = 0
|
||||
|
||||
while ((tagMatch = tagRegex.exec(props.text)) !== null) {
|
||||
// 添加标签前的文本
|
||||
if (tagMatch.index > lastIndex) {
|
||||
const textBeforeTag = props.text.slice(lastIndex, tagMatch.index)
|
||||
if (textBeforeTag) {
|
||||
// 先处理特殊字符,再处理高亮
|
||||
const processedText = processSpecialChars(textBeforeTag)
|
||||
const searchPattern = new RegExp(`(${escapedSearchText.value})`, 'gi')
|
||||
const highlightedText = processedText.replace(
|
||||
searchPattern,
|
||||
`<span class="${props.highlightClass}">$1</span>`
|
||||
)
|
||||
parts.push(highlightedText)
|
||||
}
|
||||
}
|
||||
|
||||
// 添加标签本身(不处理)
|
||||
parts.push(tagMatch[0])
|
||||
lastIndex = tagMatch.index + tagMatch[0].length
|
||||
}
|
||||
|
||||
// 添加最后一个标签后的文本
|
||||
if (lastIndex < props.text.length) {
|
||||
const textAfterLastTag = props.text.slice(lastIndex)
|
||||
if (textAfterLastTag) {
|
||||
// 先处理特殊字符,再处理高亮
|
||||
const processedText = processSpecialChars(textAfterLastTag)
|
||||
const searchPattern = new RegExp(`(${escapedSearchText.value})`, 'gi')
|
||||
const highlightedText = processedText.replace(
|
||||
searchPattern,
|
||||
`<span class="${props.highlightClass}">$1</span>`
|
||||
)
|
||||
parts.push(highlightedText)
|
||||
}
|
||||
}
|
||||
|
||||
let html = parts.join('')
|
||||
// 最后处理表情
|
||||
return textReplaceEmoji(html)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.highlight {
|
||||
color: #7a58de;
|
||||
}
|
||||
|
||||
.text-content {
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
:deep(.emoji) {
|
||||
vertical-align: text-bottom!important;
|
||||
margin: 0 5px !important;
|
||||
width: 22px !important;
|
||||
height: 22px !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -137,7 +137,7 @@
|
||||
@click="toDialogueByMember(item)"
|
||||
:searchResultKey="'search_by_member_condition'"
|
||||
:searchItem="item"
|
||||
:searchText="state.searchText"
|
||||
:searchText="props?.searchRecordByConditionText"
|
||||
:searchRecordDetail="true"
|
||||
></searchItem>
|
||||
</div>
|
||||
@ -305,6 +305,7 @@ import { parseTime } from '@/utils/datetime'
|
||||
import { fileFormatSize, fileSuffix } from '@/utils/strings'
|
||||
import { NImage, NInfiniteScroll, NScrollbar, NIcon, NDatePicker } from 'naive-ui'
|
||||
import { MessageComponents } from '@/constant/message'
|
||||
import { checkFileCanPreview } from '@/utils/helper/form'
|
||||
|
||||
const emits = defineEmits([
|
||||
'clearSearchMemberByAlphabet',
|
||||
@ -667,15 +668,23 @@ const queryAllSearch = () => {
|
||||
|
||||
//文件类型图标
|
||||
const fileTypeAvatar = (fileType) => {
|
||||
//PDF文件扩展名映射
|
||||
const PDF_EXTENSIONS = ['PDF', 'pdf']
|
||||
// Excel文件扩展名映射
|
||||
const EXCEL_EXTENSIONS = ['XLS', 'XLSX', 'CSV', 'xls', 'xlsx', 'csv']
|
||||
// Word文件扩展名映射
|
||||
const WORD_EXTENSIONS = ['DOC', 'DOCX', 'RTF', 'DOT', 'DOTX', 'doc', 'docx', 'rtf', 'dot', 'dotx']
|
||||
// PPT文件扩展名映射
|
||||
const PPT_EXTENSIONS = ['PPT', 'PPTX', 'PPS', 'PPSX', 'ppt', 'pptx', 'pps', 'ppsx']
|
||||
let file_type_avatar = fileType_Files
|
||||
if (fileType) {
|
||||
if (fileType === 'ppt' || fileType === 'pptx') {
|
||||
if (PPT_EXTENSIONS.includes(fileType)) {
|
||||
file_type_avatar = fileType_PPT
|
||||
} else if (fileType === 'pdf') {
|
||||
} else if (PDF_EXTENSIONS.includes(fileType)) {
|
||||
file_type_avatar = fileType_PDF
|
||||
} else if (fileType === 'doc' || fileType === 'docx') {
|
||||
} else if (WORD_EXTENSIONS.includes(fileType)) {
|
||||
file_type_avatar = fileType_WORD
|
||||
} else if (fileType === 'xls' || fileType === 'xlsx') {
|
||||
} else if (EXCEL_EXTENSIONS.includes(fileType)) {
|
||||
file_type_avatar = fileType_EXCEL
|
||||
} else {
|
||||
file_type_avatar = fileType_Files
|
||||
@ -693,11 +702,15 @@ const previewPDF = (item) => {
|
||||
// downloadAndOpenFile(item)
|
||||
// })
|
||||
// }
|
||||
window.open(
|
||||
`${import.meta.env.VITE_PAGE_URL}/office?url=${item.extra.path}`,
|
||||
'_blank',
|
||||
'width=1200,height=900,left=200,top=200,toolbar=no,menubar=no,scrollbars=yes,resizable=yes,location=no,status=no'
|
||||
)
|
||||
if (checkFileCanPreview(item?.extra?.path || '')) {
|
||||
window.open(
|
||||
`${import.meta.env.VITE_PAGE_URL}/office?url=${item.extra.path}`,
|
||||
'_blank',
|
||||
'width=1200,height=900,left=200,top=200,toolbar=no,menubar=no,scrollbars=yes,resizable=yes,location=no,status=no'
|
||||
)
|
||||
} else {
|
||||
toDialogueByMember(item)
|
||||
}
|
||||
}
|
||||
|
||||
const downloadAndOpenFile = (item) => {
|
||||
@ -951,11 +964,11 @@ body:deep(.round-3) {
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
border-bottom: 1px solid #f8f8f8;
|
||||
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(70, 41, 157, 0.1)
|
||||
background-color: rgba(70, 41, 157, 0.1);
|
||||
}
|
||||
|
||||
|
||||
.attachment-avatar {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
@ -69,9 +69,17 @@
|
||||
class="text-[12px] font-regular"
|
||||
:text="resultDetail"
|
||||
:searchText="props.searchText"
|
||||
v-if="props.searchItem?.msg_type !== 3 && props.searchItem?.msg_type !== 6"
|
||||
v-if="
|
||||
props.searchItem?.msg_type !== 3 &&
|
||||
props.searchItem?.msg_type !== 5 &&
|
||||
props.searchItem?.msg_type !== 6
|
||||
"
|
||||
/>
|
||||
<div class="message-component-wrapper" v-if="props.searchItem?.msg_type === 3" @click.stop>
|
||||
<div
|
||||
class="message-component-wrapper"
|
||||
v-if="props.searchItem?.msg_type === 3 || props.searchItem?.msg_type === 5"
|
||||
@click.stop
|
||||
>
|
||||
<component
|
||||
:is="MessageComponents[props.searchItem?.msg_type] || 'unknown-message'"
|
||||
:extra="resultDetail"
|
||||
@ -122,6 +130,7 @@ import { ref, watch, computed, onMounted, onUnmounted, reactive, defineProps } f
|
||||
import HighlightText from './highLightText.vue'
|
||||
import { beautifyTime } from '@/utils/datetime'
|
||||
import { ChatMsgTypeMapping, MessageComponents } from '@/constant/message'
|
||||
import { checkFileCanPreview } from '@/utils/helper/form'
|
||||
const props = defineProps({
|
||||
searchItem: Object | Number,
|
||||
searchResultKey: {
|
||||
@ -291,7 +300,9 @@ const resultDetail = computed(() => {
|
||||
result_detail =
|
||||
props.searchItem?.msg_type === 1
|
||||
? props.searchItem?.extra?.content
|
||||
: props.searchItem?.msg_type === 3 || props.searchItem?.msg_type === 6
|
||||
: props.searchItem?.msg_type === 3 ||
|
||||
props.searchItem?.msg_type === 5 ||
|
||||
props.searchItem?.msg_type === 6
|
||||
? props.searchItem?.extra
|
||||
: ChatMsgTypeMapping[props.searchItem?.msg_type]
|
||||
break
|
||||
@ -310,11 +321,16 @@ const previewPDF = (item) => {
|
||||
// downloadAndOpenFile(item)
|
||||
// })
|
||||
// }
|
||||
window.open(
|
||||
`${import.meta.env.VITE_PAGE_URL}/office?url=${item}`,
|
||||
'_blank',
|
||||
'width=1200,height=900,left=200,top=200,toolbar=no,menubar=no,scrollbars=yes,resizable=yes,location=no,status=no'
|
||||
)
|
||||
if (checkFileCanPreview(item || '')) {
|
||||
window.open(
|
||||
`${import.meta.env.VITE_PAGE_URL}/office?url=${item}`,
|
||||
'_blank',
|
||||
'width=1200,height=900,left=200,top=200,toolbar=no,menubar=no,scrollbars=yes,resizable=yes,location=no,status=no'
|
||||
)
|
||||
} else {
|
||||
//由于聊天记录本身有跳转到指定位置的逻辑,所以这里不需要再做跳转
|
||||
window['$message'].warning('暂不支持在线预览该类型文件')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@ -377,7 +393,8 @@ const previewPDF = (item) => {
|
||||
}
|
||||
.file-message-wrapper {
|
||||
.condition-each-result-attachments {
|
||||
width: 289px;
|
||||
min-width: 289px;
|
||||
max-width: 660px;
|
||||
height: 62px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
@ -447,6 +464,7 @@ const previewPDF = (item) => {
|
||||
span {
|
||||
color: #191919;
|
||||
word-break: break-all;
|
||||
max-width: 660px;
|
||||
}
|
||||
.searchRecordDetail-fastLocal {
|
||||
display: none;
|
||||
|
@ -17,7 +17,7 @@ let textContent = props.extra?.content || ''
|
||||
textContent = textReplaceLink(textContent)
|
||||
|
||||
if (props.data.talk_type == 2) {
|
||||
textContent = textReplaceMention(textContent, float==='right'?'#fff':'#462AA0')
|
||||
textContent = textReplaceMention(textContent, float==='right'?'#462AA0':'#fff',float==='right'?'#EEE9F9':'#462AA0')
|
||||
}
|
||||
|
||||
textContent = textReplaceEmoji(textContent)
|
||||
|
@ -186,7 +186,14 @@ class Talk extends Base {
|
||||
|
||||
//群解散时,需要更新群成员权限
|
||||
if ([1106].includes(record.msg_type)) {
|
||||
//更新会话信息
|
||||
useDialogueStore().updateDismiss(true)
|
||||
//更新会话列表中的会话信息
|
||||
useTalkStore().updateItem({
|
||||
index_name: this.getIndexName(),
|
||||
is_dismiss: 1,
|
||||
group_member_num: 0
|
||||
})
|
||||
}
|
||||
|
||||
//群成员被移出时,需要更新群成员权限
|
||||
@ -197,6 +204,12 @@ class Talk extends Base {
|
||||
)
|
||||
if (isMeQuit) {
|
||||
useDialogueStore().updateQuit(true)
|
||||
//更新会话列表中的会话信息
|
||||
useTalkStore().updateItem({
|
||||
index_name: this.getIndexName(),
|
||||
is_quit: 1,
|
||||
group_member_num: 0
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -124,48 +124,72 @@ export const useTalkRecord = (uid: number) => {
|
||||
|
||||
// 加载数据列表
|
||||
const load = async (params: Params) => {
|
||||
// 使用性能标记测量加载时间
|
||||
const startTime = performance.now()
|
||||
|
||||
const request = {
|
||||
talk_type: params.talk_type,
|
||||
receiver_id: params.receiver_id,
|
||||
cursor: loadConfig.cursor,
|
||||
limit: 30
|
||||
}
|
||||
|
||||
// 如果不是从本地数据库加载的,则设置加载状态为0(加载中)
|
||||
if (loadConfig.status !== 2 && loadConfig.status !== 3) {
|
||||
loadConfig.status = 0
|
||||
}
|
||||
|
||||
// 记录当前滚动高度,用于后续保持滚动位置
|
||||
let scrollHeight = 0
|
||||
const el = document.getElementById('imChatPanel')
|
||||
if (el) {
|
||||
scrollHeight = el.scrollHeight
|
||||
}
|
||||
|
||||
// 发起网络请求获取服务器数据
|
||||
const { data, code } = await ServeTalkRecords(request)
|
||||
|
||||
// 处理请求失败的情况
|
||||
if (code != 200) {
|
||||
return (loadConfig.status = (loadConfig.status === 2 || loadConfig.status === 3) ? loadConfig.status : 1) // 如果已经从本地加载了数据,保持原状态
|
||||
// 如果已经从本地加载了数据,保持原状态
|
||||
loadConfig.status = (loadConfig.status === 2 || loadConfig.status === 3) ? loadConfig.status : 1
|
||||
return
|
||||
}
|
||||
|
||||
// 防止对话切换过快,数据渲染错误
|
||||
if (
|
||||
request.talk_type != loadConfig.talk_type ||
|
||||
request.receiver_id != loadConfig.receiver_id
|
||||
) {
|
||||
return (location.msgid = '')
|
||||
if (request.talk_type != loadConfig.talk_type || request.receiver_id != loadConfig.receiver_id) {
|
||||
location.msgid = ''
|
||||
return
|
||||
}
|
||||
|
||||
const items = (data.items || []).map((item: ITalkRecord) => formatTalkRecord(uid, item))
|
||||
|
||||
// 同步到本地数据库
|
||||
try {
|
||||
const { batchAddOrUpdateMessages } = await import('@/utils/db')
|
||||
await batchAddOrUpdateMessages(data.items || [], params.talk_type, params.receiver_id, true, 'sequence')
|
||||
console.log('聊天记录已同步到本地数据库')
|
||||
} catch (error) {
|
||||
console.error('同步聊天记录到本地数据库失败:', error)
|
||||
// 优化:使用批量处理而不是map,减少内存分配
|
||||
const serverItems = data.items || []
|
||||
const items = new Array(serverItems.length)
|
||||
for (let i = 0; i < serverItems.length; i++) {
|
||||
items[i] = formatTalkRecord(uid, serverItems[i])
|
||||
}
|
||||
|
||||
// 同步到本地数据库(异步操作,不阻塞UI更新)
|
||||
const syncToLocalDB = async () => {
|
||||
try {
|
||||
const syncStartTime = performance.now()
|
||||
const { batchAddOrUpdateMessages } = await import('@/utils/db')
|
||||
await batchAddOrUpdateMessages(serverItems, params.talk_type, params.receiver_id, true, 'sequence')
|
||||
const syncEndTime = performance.now()
|
||||
console.log(`聊天记录已同步到本地数据库,耗时: ${(syncEndTime - syncStartTime).toFixed(2)}ms`)
|
||||
} catch (error) {
|
||||
console.error('同步聊天记录到本地数据库失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 启动异步同步过程
|
||||
syncToLocalDB()
|
||||
|
||||
// 如果是从本地数据库加载的数据,且服务器返回的数据与本地数据相同,则不需要更新UI
|
||||
if ((loadConfig.status === 2 || loadConfig.status === 3) && request.cursor === 0) {
|
||||
try {
|
||||
const compareStartTime = performance.now()
|
||||
|
||||
// 获取最新的本地数据库消息进行比较
|
||||
const { getMessages } = await import('@/utils/db')
|
||||
const localMessages = await getMessages(
|
||||
@ -173,80 +197,121 @@ export const useTalkRecord = (uid: number) => {
|
||||
uid,
|
||||
params.receiver_id,
|
||||
items.length || 30, // 获取与服务器返回数量相同的消息
|
||||
0 // 从第一页开始
|
||||
0, // 从第一页开始
|
||||
'sequence' // 明确指定排序字段
|
||||
)
|
||||
|
||||
// 格式化本地消息,确保与服务器消息结构一致
|
||||
const formattedLocalMessages = localMessages.map((item: ITalkRecord) => formatTalkRecord(uid, item))
|
||||
|
||||
|
||||
// 改进比较逻辑:检查消息数量和所有消息的ID是否匹配
|
||||
if (formattedLocalMessages.length === items.length && formattedLocalMessages.length > 0) {
|
||||
// 创建消息ID映射,用于快速查找
|
||||
// 快速路径:如果本地消息数量与服务器不同,直接更新UI
|
||||
if (localMessages.length !== items.length) {
|
||||
console.log('本地数据与服务器数据数量不一致,更新UI')
|
||||
} else if (items.length > 0) {
|
||||
// 优化:使用位图标记需要更新的消息,减少内存使用
|
||||
const needsUpdate = new Uint8Array(items.length)
|
||||
let updateCount = 0
|
||||
|
||||
// 优化:使用哈希表存储消息ID到索引的映射,加速查找
|
||||
const serverMsgMap = new Map()
|
||||
items.forEach(item => serverMsgMap.set(item.msg_id, item))
|
||||
|
||||
// 检查每条本地消息是否与服务器消息匹配
|
||||
const allMatch = formattedLocalMessages.every(localMsg => {
|
||||
const serverMsg = serverMsgMap.get(localMsg.msg_id)
|
||||
// 检查消息是否存在且关键状态是否一致(考虑撤回、已读等状态变化)
|
||||
return serverMsg &&
|
||||
serverMsg.is_revoke === localMsg.is_revoke &&
|
||||
serverMsg.is_read === localMsg.is_read &&
|
||||
(serverMsg.send_status === localMsg.send_status ||
|
||||
(!serverMsg.send_status && !localMsg.send_status)) &&
|
||||
serverMsg.content === localMsg.content
|
||||
})
|
||||
|
||||
if (allMatch) {
|
||||
console.log('本地数据与服务器数据一致,无需更新UI')
|
||||
return
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
serverMsgMap.set(items[i].msg_id, i)
|
||||
}
|
||||
|
||||
// 优化:首先检查首尾消息,如果它们匹配,再使用抽样检查中间消息
|
||||
const firstLocalMsg = localMessages[0]
|
||||
const lastLocalMsg = localMessages[localMessages.length - 1]
|
||||
|
||||
const firstServerIdx = serverMsgMap.get(firstLocalMsg.msg_id)
|
||||
const lastServerIdx = serverMsgMap.get(lastLocalMsg.msg_id)
|
||||
|
||||
// 如果首尾消息ID存在于服务器数据中,进行详细比较
|
||||
if (firstServerIdx !== undefined && lastServerIdx !== undefined) {
|
||||
// 根据用户建议,只比较msg_id和is_revoke字段
|
||||
// 因为消息ID是唯一的,内容变化主要是由撤回操作引起的
|
||||
const compareMessage = (localMsg, serverMsg) => {
|
||||
// 消息ID已在外部比较过,这里只需检查is_revoke状态
|
||||
return localMsg.is_revoke === serverMsg.is_revoke
|
||||
}
|
||||
|
||||
const firstMatch = compareMessage(firstLocalMsg, items[firstServerIdx])
|
||||
const lastMatch = compareMessage(lastLocalMsg, items[lastServerIdx])
|
||||
|
||||
// 如果首尾消息匹配,进行全量检查所有消息
|
||||
if (firstMatch && lastMatch) {
|
||||
// 全量检查策略:检查所有消息
|
||||
// 由于一次只有30条消息,全量检查不会带来太大的性能负担
|
||||
let allMatch = true
|
||||
|
||||
// 遍历所有本地消息,与服务器消息进行比较
|
||||
for (let i = 0; i < localMessages.length; i++) {
|
||||
const localMsg = localMessages[i]
|
||||
const serverIdx = serverMsgMap.get(localMsg.msg_id)
|
||||
|
||||
// 如果消息ID不存在于服务器数据中,或者消息内容不匹配
|
||||
if (serverIdx === undefined || !compareMessage(localMsg, items[serverIdx])) {
|
||||
allMatch = false
|
||||
console.log(`消息不匹配,索引: ${i}, 消息ID: ${localMsg.msg_id}`)
|
||||
break // 一旦发现不匹配,立即退出循环
|
||||
}
|
||||
}
|
||||
|
||||
if (allMatch) {
|
||||
const compareEndTime = performance.now()
|
||||
console.log(`本地数据与服务器数据一致(全量检查),无需更新UI,比较耗时: ${(compareEndTime - compareStartTime).toFixed(2)}ms`)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log('本地数据与服务器数据不一致,更新UI')
|
||||
}
|
||||
|
||||
// 数据不一致,需要更新UI
|
||||
console.log('本地数据与服务器数据不一致,更新UI')
|
||||
} catch (error) {
|
||||
console.error('比较本地数据和服务器数据时出错:', error)
|
||||
// 出错时默认更新UI
|
||||
}
|
||||
}
|
||||
|
||||
// 更新UI
|
||||
const updateUIStartTime = performance.now()
|
||||
|
||||
if (request.cursor == 0) {
|
||||
// 判断是否是初次加载
|
||||
dialogueStore.clearDialogueRecord()
|
||||
}
|
||||
|
||||
// 反转消息顺序并添加到对话记录
|
||||
dialogueStore.unshiftDialogueRecord(items.reverse())
|
||||
|
||||
// 更新加载状态
|
||||
loadConfig.status = items.length >= request.limit ? 1 : 2
|
||||
|
||||
loadConfig.cursor = data.cursor
|
||||
|
||||
nextTick(() => {
|
||||
// 使用requestAnimationFrame代替nextTick,提高滚动性能
|
||||
requestAnimationFrame(() => {
|
||||
const el = document.getElementById('imChatPanel')
|
||||
if (el) {
|
||||
if (request.cursor == 0) {
|
||||
// el.scrollTop = el.scrollHeight
|
||||
|
||||
// setTimeout(() => {
|
||||
// el.scrollTop = el.scrollHeight + 1000
|
||||
// }, 500)
|
||||
console.log('滚动到底部')
|
||||
|
||||
// 在初次加载完成后恢复上传任务
|
||||
// 确保在所有聊天记录加载完成后再恢复上传任务
|
||||
dialogueStore.restoreUploadTasks()
|
||||
|
||||
// 使用优化的滚动函数
|
||||
scrollToBottom()
|
||||
} else {
|
||||
// 保持滚动位置
|
||||
el.scrollTop = el.scrollHeight - scrollHeight
|
||||
}
|
||||
}
|
||||
|
||||
// 如果有需要定位的消息ID,执行定位
|
||||
if (location.msgid) {
|
||||
onJumpMessage(location.msgid)
|
||||
}
|
||||
|
||||
const updateUIEndTime = performance.now()
|
||||
const totalEndTime = performance.now()
|
||||
|
||||
console.log(`UI更新耗时: ${(updateUIEndTime - updateUIStartTime).toFixed(2)}ms`)
|
||||
console.log(`load函数总耗时: ${(totalEndTime - startTime).toFixed(2)}ms`)
|
||||
})
|
||||
}
|
||||
|
||||
@ -261,27 +326,85 @@ export const useTalkRecord = (uid: number) => {
|
||||
return Math.max(...records.value.map((item) => item.sequence))
|
||||
}
|
||||
|
||||
// 本地数据库加载缓存,用于优化短时间内的重复加载
|
||||
const localDBCache = {
|
||||
key: '', // 缓存键:talk_type-receiver_id
|
||||
data: null, // 缓存的消息数据
|
||||
timestamp: 0, // 缓存时间戳
|
||||
ttl: 2000 // 缓存有效期(毫秒)
|
||||
}
|
||||
|
||||
// 从本地数据库加载聊天记录
|
||||
const loadFromLocalDB = async (params: Params) => {
|
||||
try {
|
||||
// 使用性能标记测量加载时间
|
||||
const startTime = performance.now()
|
||||
|
||||
// 生成缓存键
|
||||
const cacheKey = `${params.talk_type}-${params.receiver_id}`
|
||||
|
||||
// 检查缓存是否有效
|
||||
const now = Date.now()
|
||||
if (localDBCache.key === cacheKey &&
|
||||
localDBCache.data &&
|
||||
now - localDBCache.timestamp < localDBCache.ttl) {
|
||||
console.log('使用缓存的本地数据库消息')
|
||||
|
||||
// 清空现有记录
|
||||
dialogueStore.clearDialogueRecord()
|
||||
|
||||
// 直接使用缓存数据
|
||||
dialogueStore.unshiftDialogueRecord([...localDBCache.data]) // 创建副本避免引用问题
|
||||
|
||||
// 设置加载状态为完成(3表示从本地数据库加载完成)
|
||||
loadConfig.status = 3
|
||||
|
||||
// 恢复上传任务
|
||||
dialogueStore.restoreUploadTasks()
|
||||
|
||||
// 使用requestAnimationFrame优化滚动性能
|
||||
requestAnimationFrame(() => {
|
||||
scrollToBottom()
|
||||
})
|
||||
|
||||
const endTime = performance.now()
|
||||
console.log(`从缓存加载聊天记录耗时: ${(endTime - startTime).toFixed(2)}ms,加载了${localDBCache.data.length}条记录`)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// 导入 getMessages 函数
|
||||
const { getMessages } = await import('@/utils/db')
|
||||
// 从本地数据库获取聊天记录
|
||||
|
||||
// 从本地数据库获取聊天记录,使用sequence作为排序字段以提高性能
|
||||
const localMessages = await getMessages(
|
||||
params.talk_type,
|
||||
uid,
|
||||
params.receiver_id,
|
||||
params.limit || 30,
|
||||
0 // 从第一页开始
|
||||
// 不传入 maxSequence 参数,获取最新的消息
|
||||
0, // 从第一页开始
|
||||
'sequence' // 明确指定排序字段
|
||||
)
|
||||
|
||||
// 如果有本地数据
|
||||
if (localMessages && localMessages.length > 0) {
|
||||
// 清空现有记录
|
||||
dialogueStore.clearDialogueRecord()
|
||||
|
||||
// 格式化并添加记录
|
||||
const formattedMessages = localMessages.map((item: ITalkRecord) => formatTalkRecord(uid, item))
|
||||
// 优化:预分配数组大小,减少内存重分配
|
||||
const formattedMessages = new Array(localMessages.length)
|
||||
|
||||
// 优化:使用批量处理而不是map,减少内存分配和GC压力
|
||||
for (let i = 0; i < localMessages.length; i++) {
|
||||
formattedMessages[i] = formatTalkRecord(uid, localMessages[i])
|
||||
}
|
||||
|
||||
// 更新缓存
|
||||
localDBCache.key = cacheKey
|
||||
localDBCache.data = formattedMessages
|
||||
localDBCache.timestamp = now
|
||||
|
||||
// 批量添加记录
|
||||
dialogueStore.unshiftDialogueRecord(formattedMessages)
|
||||
|
||||
// 设置加载状态为完成(3表示从本地数据库加载完成)
|
||||
@ -290,17 +413,27 @@ export const useTalkRecord = (uid: number) => {
|
||||
// 恢复上传任务
|
||||
dialogueStore.restoreUploadTasks()
|
||||
|
||||
// 滚动到底部
|
||||
nextTick(() => {
|
||||
// 使用requestAnimationFrame优化滚动性能
|
||||
requestAnimationFrame(() => {
|
||||
scrollToBottom()
|
||||
})
|
||||
|
||||
const endTime = performance.now()
|
||||
console.log(`从本地数据库加载聊天记录耗时: ${(endTime - startTime).toFixed(2)}ms,加载了${localMessages.length}条记录`)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// 无数据时清除缓存
|
||||
localDBCache.key = ''
|
||||
localDBCache.data = null
|
||||
|
||||
return false
|
||||
} catch (error) {
|
||||
console.error('从本地数据库加载聊天记录失败:', error)
|
||||
// 出错时清除缓存
|
||||
localDBCache.key = ''
|
||||
localDBCache.data = null
|
||||
return false
|
||||
}
|
||||
}
|
||||
@ -311,6 +444,10 @@ export const useTalkRecord = (uid: number) => {
|
||||
* @param options 可选,{ specifiedMsg } 指定消息对象
|
||||
*/
|
||||
const onLoad = async (params: Params, options?: LoadOptions) => {
|
||||
// 使用性能标记测量加载时间
|
||||
const startTime = performance.now()
|
||||
|
||||
// 检查会话是否变更,如果变更则重置配置
|
||||
if (
|
||||
params.talk_type !== loadConfig.talk_type ||
|
||||
params.receiver_id !== loadConfig.receiver_id
|
||||
@ -324,8 +461,10 @@ export const useTalkRecord = (uid: number) => {
|
||||
|
||||
// 新增:支持指定消息定位模式,参数以传入为准合并
|
||||
if (options?.specifiedMsg?.cursor !== undefined) {
|
||||
// 特殊消息定位模式
|
||||
loadConfig.specialParams = { ...options.specifiedMsg } // 记录特殊参数,供分页加载用
|
||||
loadConfig.status = 0 // 复用主流程 loading 状态
|
||||
|
||||
// 以 params 为基础,合并 specifiedMsg 的所有字段(只要有就覆盖)
|
||||
const contextParams = {
|
||||
...params,
|
||||
@ -333,20 +472,36 @@ export const useTalkRecord = (uid: number) => {
|
||||
}
|
||||
//msg_id是用来做定位的,不做参数,所以这里清空
|
||||
contextParams.msg_id = ''
|
||||
ServeTalkRecords(contextParams).then(({ data, code }) => {
|
||||
console.log('data',data)
|
||||
|
||||
// 使用Promise.all并行处理数据库操作和网络请求
|
||||
const serverDataPromise = ServeTalkRecords(contextParams)
|
||||
|
||||
// 记录当前滚动高度
|
||||
const el = document.getElementById('imChatPanel')
|
||||
const scrollHeight = el?.scrollHeight || 0
|
||||
|
||||
try {
|
||||
// 等待服务器响应
|
||||
const { data, code } = await serverDataPromise
|
||||
|
||||
if (code !== 200) {
|
||||
loadConfig.status = 2
|
||||
return
|
||||
}
|
||||
// 记录当前滚动高度
|
||||
const el = document.getElementById('imChatPanel')
|
||||
const scrollHeight = el?.scrollHeight || 0
|
||||
|
||||
|
||||
console.log('data', data)
|
||||
|
||||
// 优化:使用批量处理而不是map,减少内存分配
|
||||
const items = new Array(data.items?.length || 0)
|
||||
for (let i = 0; i < (data.items?.length || 0); i++) {
|
||||
items[i] = formatTalkRecord(uid, data.items[i])
|
||||
}
|
||||
|
||||
// 根据方向和类型处理数据
|
||||
if (contextParams.direction === 'down' && !contextParams.type) {
|
||||
dialogueStore.clearDialogueRecord()
|
||||
}
|
||||
const items = (data.items || []).map((item: ITalkRecord) => formatTalkRecord(uid, item))
|
||||
|
||||
if (contextParams.type && contextParams.type === 'loadMore') {
|
||||
dialogueStore.addDialogueRecordForLoadMore(items)
|
||||
} else {
|
||||
@ -354,12 +509,14 @@ export const useTalkRecord = (uid: number) => {
|
||||
contextParams.direction === 'down' ? items : items.reverse()
|
||||
)
|
||||
}
|
||||
|
||||
if (
|
||||
contextParams.direction === 'up' ||
|
||||
(contextParams.direction === 'down' && !contextParams.type)
|
||||
) {
|
||||
loadConfig.status = items[0].sequence == 1 || data.length === 0 ? 2 : 1
|
||||
loadConfig.status = items[0]?.sequence == 1 || data.length === 0 ? 2 : 1
|
||||
}
|
||||
|
||||
loadConfig.cursor = data.cursor
|
||||
|
||||
// 使用 requestAnimationFrame 来确保在下一帧渲染前设置滚动位置
|
||||
@ -375,7 +532,7 @@ export const useTalkRecord = (uid: number) => {
|
||||
} else if (contextParams.type && contextParams.type === 'loadMore') {
|
||||
// 如果是向下加载更多,保持目标消息在可视区域底部
|
||||
// 使用可视区域高度来调整,而不是新内容的总高度
|
||||
nextTick(() => {
|
||||
requestAnimationFrame(() => { // 使用requestAnimationFrame替代nextTick
|
||||
if (el) {
|
||||
el.scrollTop = scrollHeight - el.clientHeight
|
||||
}
|
||||
@ -383,8 +540,8 @@ export const useTalkRecord = (uid: number) => {
|
||||
} else if (target && msgId) {
|
||||
// 只有在有目标元素且有 msg_id 时才执行定位逻辑
|
||||
// 如果是定位到特定消息,计算并滚动到目标位置
|
||||
// 使用 nextTick 确保 DOM 完全渲染后再计算位置
|
||||
nextTick(() => {
|
||||
// 使用 requestAnimationFrame 确保 DOM 完全渲染后再计算位置
|
||||
requestAnimationFrame(() => {
|
||||
const el = document.getElementById('imChatPanel')
|
||||
const target = document.getElementById(msgId)
|
||||
|
||||
@ -431,23 +588,39 @@ export const useTalkRecord = (uid: number) => {
|
||||
scrollToBottom()
|
||||
}
|
||||
}
|
||||
|
||||
const endTime = performance.now()
|
||||
console.log(`特殊消息定位模式加载耗时: ${(endTime - startTime).toFixed(2)}ms`)
|
||||
})
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('特殊消息定位模式加载失败:', error)
|
||||
loadConfig.status = 2
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// 普通模式
|
||||
loadConfig.specialParams = undefined // 普通模式清空
|
||||
|
||||
// 设置初始加载状态为0(加载中)
|
||||
loadConfig.status = 0
|
||||
|
||||
// 先从本地数据库加载数据
|
||||
const hasLocalData = await loadFromLocalDB(params)
|
||||
|
||||
// 无论是否有本地数据,都从服务器获取最新数据
|
||||
// 原有逻辑
|
||||
console.log('onLoad()执行load')
|
||||
load(params)
|
||||
// 使用Promise.all并行处理本地数据库加载和网络请求准备
|
||||
try {
|
||||
// 先从本地数据库加载数据
|
||||
const hasLocalData = await loadFromLocalDB(params)
|
||||
|
||||
// 无论是否有本地数据,都从服务器获取最新数据
|
||||
console.log('onLoad()执行load')
|
||||
await load(params)
|
||||
|
||||
const endTime = performance.now()
|
||||
console.log(`普通模式加载总耗时: ${(endTime - startTime).toFixed(2)}ms`)
|
||||
} catch (error) {
|
||||
console.error('加载聊天记录失败:', error)
|
||||
loadConfig.status = 2
|
||||
}
|
||||
}
|
||||
|
||||
// 向上加载更多(兼容特殊参数模式)
|
||||
|
@ -181,7 +181,7 @@ export const useTalkStore = defineStore('talk', {
|
||||
|
||||
// 更新状态和本地数据库
|
||||
this.items = serverItems
|
||||
|
||||
console.log('serverItems',serverItems)
|
||||
// 将最新的会话列表保存到本地数据库
|
||||
for (const item of serverItems) {
|
||||
await addOrUpdateConversation(item)
|
||||
|
@ -18,7 +18,7 @@ export function isLoggedIn() {
|
||||
*/
|
||||
export function getAccessToken() {
|
||||
// return storage.get(AccessToken) || ''
|
||||
return JSON.parse(localStorage.getItem('token'))||'79b5c732d96d2b27a48a99dfd4a5566c43aaa5796242e854ebe3ffc198d6876b9628e7b764d9af65ab5dbb2d517ced88170491b74b048c0ba827c0d3741462cb89dc59ed46653a449af837a8262941caaef1334d640773710f8cd96473bacfb190cba595a5d6a9c87d70f0999a3ebb41147213b31b4bdccffca66a56acf3baab5af0154f0dce360079f37709f78e13711036899344bddb0fb4cf0f2890287cb62c3fcbe33368caa5e213624577be8b8420ab75b1f50775ee16142a4321c5d56995f37354a66a969da98d95ba6e65d142ed097e04b411c1ebad2f62866d0ec7e1838420530a9941dbbcd00490199f8b897a4f2416a772eacd03215226020e2e551cdac98368e42541ee3082dc07317d4ecc6a5dfbbe2a28f8c48ccfae7bc6046c3b9b79c0eb3a1ec4c25f5d766a2f8f01f64da8f70f7dbf63e124ffcf72398d86'
|
||||
return JSON.parse(localStorage.getItem('token'))||'46d71a72d8d845ad7ed23eba9bdde260e635407190c2ce1bf7fd22088e41682ea07773ec65cae8946d2003f264d55961f96e0fc5da10eb96d3a348c1664e9644ce2108c311309f398ae8ea1b8200bfd490e5cb6e8c52c9e5d493cbabb163368f8351420451a631dbfa749829ee4cda49b77b5ed2d3dced5d0f2b7dd9ee76ba5465c84a17c23af040cd92b6b2a4ea48befbb5c729dcdad0a9c9668befe84074cc24f78899c1d947f8e7f94c7eda5325b8ed698df729e76febb98549ef3482ae942fb4f4a1c92d21836fa784728f0c5483aab2760a991b6b36e6b10c84f840a6433a6ecc31dee36e8f1c6158818bc89d220365eb2ca93ef31880576e2aa3ca8c45a705b447d40e300a54644829e2da528ea463bd2581a396336ed74880960d35716f5f7594e5b8cbb597027c6133b97b12df23427ca728fd2625977a0658ab470d'
|
||||
}
|
||||
|
||||
/**
|
||||
|
200
src/utils/db.js
200
src/utils/db.js
@ -1,11 +1,30 @@
|
||||
|
||||
import Dexie from 'dexie';
|
||||
|
||||
/**
|
||||
* 聊天历史数据库
|
||||
* 版本5-6: 修复会话表主键问题
|
||||
* - 版本5: 删除旧的会话表结构
|
||||
* - 版本6: 使用index_name作为主键重新创建会话表
|
||||
*
|
||||
* 注意: Dexie不支持直接更改主键,必须通过删除并重建表的方式实现
|
||||
*/
|
||||
export const db = new Dexie('chatHistory');
|
||||
|
||||
// 定义数据库表结构和索引
|
||||
// 版本3:优化了索引,提高了查询和排序性能
|
||||
db.version(4).stores({
|
||||
// 版本6:修复主键更改问题
|
||||
// 版本5:删除旧的会话表
|
||||
db.version(5).stores({
|
||||
conversations: null
|
||||
}).upgrade(function(trans) {
|
||||
// 确保物理删除表
|
||||
if (trans.idbtrans.db.objectStoreNames.contains('conversations')) {
|
||||
trans.idbtrans.db.deleteObjectStore('conversations');
|
||||
}
|
||||
});
|
||||
|
||||
// 版本6:使用新的主键结构重新创建会话表
|
||||
db.version(6).stores({
|
||||
/**
|
||||
* 聊天记录表
|
||||
* - msg_id: 消息唯一ID (主键)
|
||||
@ -18,12 +37,20 @@ db.version(4).stores({
|
||||
|
||||
/**
|
||||
* 会话表
|
||||
* - ++id: 自增主键
|
||||
* - &index_name: 唯一索引 (talk_type + '_' + receiver_id)
|
||||
* - index_name: 主键 (talk_type + '_' + receiver_id)
|
||||
* - updated_at: 索引,用于排序
|
||||
* - is_top: 索引,用于置顶排序
|
||||
*/
|
||||
conversations: 'id, &index_name, talk_type, receiver_id, updated_at, unread_num, is_top',
|
||||
conversations: 'index_name, talk_type, receiver_id, updated_at, unread_num, is_top',
|
||||
});
|
||||
|
||||
// 清理旧版本数据
|
||||
db.on('versionchange', function(event) {
|
||||
if (event.oldVersion < 6 && event.newVersion >= 6) {
|
||||
console.log('数据库版本升级到6,清理旧数据');
|
||||
db.conversations.clear();
|
||||
console.log('会话表数据已清理,主键结构已更新');
|
||||
}
|
||||
});
|
||||
|
||||
db.on('ready', () => {
|
||||
@ -87,31 +114,71 @@ export async function addMessage(message) {
|
||||
/**
|
||||
* 批量添加或更新聊天记录
|
||||
* @param {Array<object>} messages - 消息对象数组
|
||||
* @param {number} talkType - 会话类型
|
||||
* @param {number} receiverId - 接收者ID
|
||||
* @param {boolean} [updateConversation=true] - 是否更新会话信息
|
||||
* @param {string} [sortField='created_at'] - 排序字段
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export async function batchAddOrUpdateMessages(messages) {
|
||||
export async function batchAddOrUpdateMessages(messages, talkType, receiverId, updateConversation = true, sortField = 'created_at') {
|
||||
try {
|
||||
if (!Array.isArray(messages) || messages.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const messagesToStore = messages.map(message => {
|
||||
if (!message.msg_id) {
|
||||
message.msg_id = generateUUID();
|
||||
// 使用批处理优化性能
|
||||
return await db.transaction('rw', db.messages, db.conversations, async () => {
|
||||
// 预处理消息数据,避免在循环中多次创建对象
|
||||
const now = new Date().toISOString().replace('T', ' ').substring(0, 19);
|
||||
|
||||
// 使用for循环替代map,减少内存分配
|
||||
const messagesToStore = new Array(messages.length);
|
||||
for (let i = 0; i < messages.length; i++) {
|
||||
const message = messages[i];
|
||||
// 确保必要字段存在
|
||||
if (!message.msg_id) {
|
||||
message.msg_id = generateUUID();
|
||||
}
|
||||
if (!message.created_at) {
|
||||
message.created_at = now;
|
||||
}
|
||||
// 确保talk_type和receiver_id字段存在
|
||||
if (talkType && !message.talk_type) {
|
||||
message.talk_type = talkType;
|
||||
}
|
||||
if (receiverId && !message.receiver_id) {
|
||||
message.receiver_id = receiverId;
|
||||
}
|
||||
messagesToStore[i] = message;
|
||||
}
|
||||
if (!message.created_at) {
|
||||
message.created_at = new Date().toISOString().replace('T', ' ').substring(0, 19);
|
||||
|
||||
// 使用bulkPut批量插入/更新,提高性能
|
||||
await db.messages.bulkPut(messagesToStore);
|
||||
|
||||
// 只有在需要时才更新会话信息
|
||||
if (updateConversation && messagesToStore.length > 0) {
|
||||
// 根据排序字段找出最新消息
|
||||
let latestMessage;
|
||||
if (sortField === 'sequence') {
|
||||
// 按sequence排序找出最大的
|
||||
latestMessage = messagesToStore.reduce((max, current) => {
|
||||
return (current.sequence > (max.sequence || 0)) ? current : max;
|
||||
}, messagesToStore[0]);
|
||||
} else {
|
||||
// 默认按created_at排序
|
||||
latestMessage = messagesToStore.reduce((latest, current) => {
|
||||
if (!latest.created_at) return current;
|
||||
if (!current.created_at) return latest;
|
||||
return new Date(current.created_at) > new Date(latest.created_at) ? current : latest;
|
||||
}, messagesToStore[0]);
|
||||
}
|
||||
|
||||
// 异步更新会话最后消息,不阻塞主流程
|
||||
updateConversationLastMessage(latestMessage).catch(err => {
|
||||
console.error('更新会话最后消息失败:', err);
|
||||
});
|
||||
}
|
||||
return message;
|
||||
});
|
||||
|
||||
await db.messages.bulkPut(messagesToStore);
|
||||
|
||||
// 更新最后一条消息到会话
|
||||
const latestMessage = messagesToStore[messagesToStore.length - 1];
|
||||
if (latestMessage) {
|
||||
await updateConversationLastMessage(latestMessage);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('批量添加或更新消息失败:', error);
|
||||
throw error;
|
||||
@ -125,35 +192,78 @@ export async function batchAddOrUpdateMessages(messages) {
|
||||
* @param {number} receiverId - 接收者ID (私聊为对方用户ID,群聊为群ID)
|
||||
* @param {number} [limit=30] - 限制返回的记录数量
|
||||
* @param {number|null} [maxSequence=null] - 最大sequence值,用于分页加载更早的消息
|
||||
* @param {string} [sortField='sequence'] - 排序字段,默认按sequence排序
|
||||
* @returns {Promise<Array<object>>} 消息列表 (按sequence升序排列)
|
||||
*/
|
||||
export async function getMessages(talkType, userId, receiverId, limit = 30, maxSequence = null) {
|
||||
export async function getMessages(talkType, userId, receiverId, limit = 30, maxSequence = null, sortField = 'sequence') {
|
||||
try {
|
||||
// 使用缓存优化重复查询
|
||||
const cacheKey = `${talkType}_${receiverId}_${limit}_${maxSequence}_${sortField}`;
|
||||
const cachedResult = messageCache.get(cacheKey);
|
||||
|
||||
// 如果缓存存在且未过期,直接返回缓存结果
|
||||
if (cachedResult && (Date.now() - cachedResult.timestamp < 2000)) { // 2秒缓存
|
||||
return cachedResult.data;
|
||||
}
|
||||
|
||||
let collection;
|
||||
|
||||
// 优化查询策略
|
||||
if (maxSequence !== null) {
|
||||
// 加载更多:查询 sequence 小于 maxSequence 的消息
|
||||
// 使用复合索引优化查询
|
||||
collection = db.messages
|
||||
.where('[talk_type+receiver_id+sequence]')
|
||||
.between([talkType, receiverId, 0], [talkType, receiverId, maxSequence], true, false);
|
||||
} else {
|
||||
// 首次加载:查询指定会话的所有消息
|
||||
collection = db.messages.where({ '[talk_type+receiver_id]': [talkType, receiverId] });
|
||||
// 使用复合索引优化查询
|
||||
collection = db.messages.where('[talk_type+receiver_id]').equals([talkType, receiverId]);
|
||||
}
|
||||
|
||||
// 1. reverse() - 利用索引倒序排列,获取最新的消息
|
||||
// 2. limit() - 限制数量,实现分页
|
||||
// 3. toArray() - 执行查询
|
||||
const messages = await collection.reverse().limit(limit).toArray();
|
||||
|
||||
// 再次 reverse() - 将获取到的分页消息按时间正序排列,以便于在界面上显示
|
||||
return messages.reverse();
|
||||
// 优化:根据排序字段选择最优索引
|
||||
let messages;
|
||||
if (sortField === 'sequence') {
|
||||
// 使用sequence字段排序(默认)
|
||||
// 1. reverse() - 利用索引倒序排列,获取最新的消息
|
||||
// 2. limit() - 限制数量,实现分页
|
||||
// 3. toArray() - 执行查询,一次性获取所有数据减少IO操作
|
||||
messages = await collection.reverse().limit(limit).toArray();
|
||||
// 再次 reverse() - 将获取到的分页消息按时间正序排列,以便于在界面上显示
|
||||
messages = messages.reverse();
|
||||
} else if (sortField === 'created_at') {
|
||||
// 使用created_at字段排序
|
||||
messages = await collection.toArray();
|
||||
// 在内存中排序,避免数据库排序开销
|
||||
messages.sort((a, b) => {
|
||||
const dateA = new Date(a.created_at || 0);
|
||||
const dateB = new Date(b.created_at || 0);
|
||||
return dateA - dateB; // 升序排列
|
||||
});
|
||||
// 限制返回数量
|
||||
messages = messages.slice(-limit);
|
||||
} else {
|
||||
// 默认排序逻辑
|
||||
messages = await collection.reverse().limit(limit).toArray();
|
||||
messages = messages.reverse();
|
||||
}
|
||||
|
||||
// 缓存查询结果
|
||||
messageCache.set(cacheKey, {
|
||||
data: messages,
|
||||
timestamp: Date.now()
|
||||
});
|
||||
|
||||
return messages;
|
||||
} catch (error) {
|
||||
console.error('获取消息失败:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// 简单的内存缓存实现
|
||||
const messageCache = new Map();
|
||||
|
||||
/**
|
||||
* 标记指定会话的所有消息为已读
|
||||
* @param {number} talkType - 会话类型
|
||||
@ -219,14 +329,24 @@ export async function deleteMessage(msgId) {
|
||||
/**
|
||||
* 添加或更新会话
|
||||
* @param {object} conversation - 会话对象
|
||||
* @returns {Promise<number>} 会话ID
|
||||
* @returns {Promise<string>} 会话索引名称
|
||||
*/
|
||||
export async function addOrUpdateConversation(conversation) {
|
||||
try {
|
||||
// put 方法会根据唯一索引 index_name 自动判断是添加还是更新
|
||||
// 确保 index_name 存在,这是会话表的主键
|
||||
if (!conversation.index_name && conversation.talk_type && conversation.receiver_id) {
|
||||
conversation.index_name = `${conversation.talk_type}_${conversation.receiver_id}`;
|
||||
}
|
||||
|
||||
if (!conversation.index_name) {
|
||||
throw new Error('无法添加会话:缺少必要的index_name或无法生成');
|
||||
}
|
||||
|
||||
// 使用 put 方法,如果主键已存在则更新,否则添加
|
||||
return await db.conversations.put(conversation);
|
||||
} catch (error) {
|
||||
console.error('添加或更新会话失败:', error);
|
||||
console.error('错误详情:', error.message, error.stack);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
@ -274,7 +394,7 @@ export async function getConversations(includeEmpty = false) {
|
||||
export async function getConversation(talkType, receiverId) {
|
||||
try {
|
||||
const indexName = `${talkType}_${receiverId}`;
|
||||
return await db.conversations.get({ index_name: indexName });
|
||||
return await db.conversations.get(indexName);
|
||||
} catch (error) {
|
||||
console.error('获取会话失败:', error);
|
||||
throw error;
|
||||
@ -291,11 +411,11 @@ export async function getConversation(talkType, receiverId) {
|
||||
export async function updateConversationUnreadNum(talkType, receiverId, unreadNum = null) {
|
||||
try {
|
||||
const indexName = `${talkType}_${receiverId}`;
|
||||
const conversation = await db.conversations.get({ index_name: indexName });
|
||||
const conversation = await db.conversations.get(indexName);
|
||||
|
||||
if (conversation) {
|
||||
const newUnreadNum = unreadNum === null ? (conversation.unread_num || 0) + 1 : unreadNum;
|
||||
return await db.conversations.update(conversation.id, { unread_num: newUnreadNum });
|
||||
return await db.conversations.update(indexName, { unread_num: newUnreadNum });
|
||||
}
|
||||
return 0;
|
||||
} catch (error) {
|
||||
@ -316,18 +436,18 @@ export function clearConversationUnreadNum(talkType, receiverId) {
|
||||
|
||||
/**
|
||||
* 删除会话及其相关的消息
|
||||
* @param {number} conversationId - 会话ID
|
||||
* @param {string} indexName - 会话索引名称
|
||||
* @param {boolean} [deleteMessages=false] - 是否同时删除相关的消息记录
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export async function deleteConversation(conversationId, deleteMessages = false) {
|
||||
export async function deleteConversation(indexName, deleteMessages = false) {
|
||||
try {
|
||||
await db.transaction('rw', db.conversations, db.messages, async () => {
|
||||
const conversation = await db.conversations.get(conversationId);
|
||||
const conversation = await db.conversations.get(indexName);
|
||||
if (!conversation) return;
|
||||
|
||||
// 删除会话
|
||||
await db.conversations.delete(conversationId);
|
||||
await db.conversations.delete(indexName);
|
||||
|
||||
// 如果需要,删除关联的消息
|
||||
if (deleteMessages) {
|
||||
@ -352,7 +472,7 @@ export async function updateConversationLastMessage(message) {
|
||||
const targetReceiverId = talk_type === TalkType.PRIVATE ? (user_id === receiver_id ? user_id : receiver_id) : receiver_id;
|
||||
const indexName = `${talk_type}_${targetReceiverId}`;
|
||||
|
||||
const conversation = await db.conversations.get({ index_name: indexName });
|
||||
const conversation = await db.conversations.get(indexName);
|
||||
if (!conversation) return 0;
|
||||
|
||||
let msgText = '';
|
||||
@ -367,7 +487,7 @@ export async function updateConversationLastMessage(message) {
|
||||
default: msgText = '[未知消息]';
|
||||
}
|
||||
|
||||
return await db.conversations.update(conversation.id, {
|
||||
return await db.conversations.update(indexName, {
|
||||
msg_text: msgText,
|
||||
content: message.content || '',
|
||||
updated_at: message.created_at,
|
||||
|
@ -345,3 +345,29 @@ export const formatNumberWithCommas = (num) => {
|
||||
}
|
||||
return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
||||
};
|
||||
// 判断文件是否可以预览
|
||||
export const checkFileCanPreview = (path) => {
|
||||
if (!path) {
|
||||
return false
|
||||
}
|
||||
//PDF文件扩展名映射
|
||||
const PDF_EXTENSIONS = ['PDF', 'pdf']
|
||||
// Excel文件扩展名映射
|
||||
const EXCEL_EXTENSIONS = ['XLS', 'XLSX', 'CSV', 'xls', 'xlsx', 'csv']
|
||||
// Word文件扩展名映射
|
||||
const WORD_EXTENSIONS = ['DOC', 'DOCX', 'RTF', 'DOT', 'DOTX', 'doc', 'docx', 'rtf', 'dot', 'dotx']
|
||||
// PPT文件扩展名映射
|
||||
const PPT_EXTENSIONS = ['PPT', 'PPTX', 'PPS', 'PPSX', 'ppt', 'pptx', 'pps', 'ppsx']
|
||||
|
||||
// 获取文件扩展名
|
||||
function getFileExtension(filepath) {
|
||||
const parts = filepath?.split('.')
|
||||
return parts?.length > 1 ? parts?.pop()?.toUpperCase() : ''
|
||||
}
|
||||
|
||||
const extension = getFileExtension(path)
|
||||
return PDF_EXTENSIONS.includes(extension) ||
|
||||
EXCEL_EXTENSIONS.includes(extension) ||
|
||||
WORD_EXTENSIONS.includes(extension) ||
|
||||
PPT_EXTENSIONS.includes(extension)
|
||||
}
|
||||
|
@ -42,9 +42,9 @@ export function textReplaceLink(text, color = '#409eff') {
|
||||
* @param {String} text 文本
|
||||
* @param {String} color 超链接颜色
|
||||
*/
|
||||
export function textReplaceMention(text, color = '#2196F3') {
|
||||
export function textReplaceMention(text, color = '#2196F3',bg) {
|
||||
return text.replace(new RegExp(/@\S+/, 'g'), ($0, $1) => {
|
||||
return `<span style="color:${color};">${$0}</span>`
|
||||
return `<span style="color:${color};background:${bg};border-radius:2px;padding:0 5px">${$0}</span>`
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -518,7 +518,7 @@ const checkVisibleElements = () => {
|
||||
prev.talk_type === doReadItem.talk_type && prev.receiver_id === doReadItem.receiver_id
|
||||
)
|
||||
if (!prevItem || !doReadItem.msg_ids.every((id) => prevItem.msg_ids.includes(id))) {
|
||||
console.error('====发送了新版已读回执=====', doReadItem)
|
||||
// console.error('====发送了新版已读回执=====', doReadItem)
|
||||
ws.emit('im.message.new.read', doReadItem)
|
||||
}
|
||||
})
|
||||
|
@ -13,6 +13,7 @@ import { ServePublishMessage, ServeSendVote } from '@/api/chat'
|
||||
import { throttle, getVideoImage } from '@/utils/common'
|
||||
import { parseTime } from '@/utils/datetime'
|
||||
import Editor from '@/components/editor/Editor.vue'
|
||||
import TiptapEditor from '@/components/editor/TiptapEditor.vue'
|
||||
import MultiSelectFooter from './MultiSelectFooter.vue'
|
||||
import HistoryRecord from '@/components/talk/HistoryRecord.vue'
|
||||
import {scrollToBottom} from '@/utils/dom.ts'
|
||||
@ -294,9 +295,9 @@ onMounted(() => {
|
||||
<template>
|
||||
<footer class="el-footer">
|
||||
<MultiSelectFooter v-if="dialogueStore.isOpenMultiSelect" />
|
||||
|
||||
<!-- <Editor v-else @editor-event="onEditorEvent" :vote="talk_type == 2" :members="members" /> -->
|
||||
<CustomEditor v-else @editor-event="onEditorEvent" :vote="talk_type == 2" :members="members" />
|
||||
<!-- <CustomEditor v-else @editor-event="onEditorEvent" :vote="talk_type == 2" :members="members" /> -->
|
||||
<TiptapEditor v-else @editor-event="onEditorEvent" :vote="talk_type == 2" :members="members" />
|
||||
</footer>
|
||||
|
||||
<HistoryRecord
|
||||
|
@ -1,4 +1,5 @@
|
||||
<script lang="ts" setup>
|
||||
import { reactive, computed } from 'vue'
|
||||
import { Peoples, Announcement, MenuUnfoldOne, MenuFoldOne } from '@icon-park/vue-next'
|
||||
import { useDialogueStore } from '@/store'
|
||||
|
||||
@ -30,6 +31,10 @@ defineProps({
|
||||
})
|
||||
|
||||
const dialogueStore = useDialogueStore()
|
||||
const dialogueParams = reactive({
|
||||
isDismiss: computed(() => dialogueStore.isDismiss),
|
||||
isQuit: computed(() => dialogueStore.isQuit)
|
||||
})
|
||||
const emit = defineEmits(['evnet'])
|
||||
|
||||
const onSetMenu = () => {
|
||||
@ -83,7 +88,7 @@ const onSetMenu = () => {
|
||||
:size="18"
|
||||
class="icon"
|
||||
@click="emit('evnet', 'group')"
|
||||
v-show="!dialogueStore.isDismiss && !dialogueStore.isQuit"
|
||||
v-show="!dialogueParams.isDismiss && !dialogueParams.isQuit"
|
||||
>
|
||||
<img
|
||||
style="width: 20px; height: 20px;"
|
||||
|
@ -46,9 +46,9 @@ export default defineConfig(({ mode }) => {
|
||||
vueJsx({}),
|
||||
compressPlugin(),
|
||||
UnoCSS(),
|
||||
vueDevTools({
|
||||
launchEditor: 'trae',
|
||||
})
|
||||
// vueDevTools({
|
||||
// launchEditor: 'trae',
|
||||
// })
|
||||
],
|
||||
define: {
|
||||
__APP_ENV__: env.APP_ENV
|
||||
|
Loading…
Reference in New Issue
Block a user