diff --git a/package.json b/package.json
index 869d6e90..e0c443e3 100644
--- a/package.json
+++ b/package.json
@@ -27,7 +27,6 @@
"alova": "^2.6.0",
"dayjs": "^1.11.8",
"element-plus": "^2.3.6",
- "gsap": "^3.12.1",
"lodash": "^4.17.21",
"pinia": "^2.1.3",
"pinia-plugin-persistedstate": "^3.1.0",
@@ -47,6 +46,7 @@
"@vue/eslint-config-prettier": "^7.1.0",
"@vue/eslint-config-typescript": "^11.0.3",
"@vue/tsconfig": "^0.1.3",
+ "@vueuse/core": "^10.2.0",
"autoprefixer": "^10.4.14",
"eslint": "^8.42.0",
"eslint-plugin-vue": "^9.14.1",
@@ -71,6 +71,7 @@
"unplugin-vue-components": "^0.24.1",
"vite": "4.3.5",
"vite-plugin-mkcert": "^1.16.0",
- "vue-tsc": "^1.6.5"
+ "vue-tsc": "^1.6.5",
+ "xgplayer": "^3.0.5"
}
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 208109ab..690ad67e 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -1,4 +1,4 @@
-lockfileVersion: '6.0'
+lockfileVersion: '6.1'
settings:
autoInstallPeers: true
@@ -23,9 +23,6 @@ dependencies:
element-plus:
specifier: ^2.3.6
version: registry.npmmirror.com/element-plus@2.3.6(vue@3.3.4)
- gsap:
- specifier: ^3.12.1
- version: registry.npmmirror.com/gsap@3.12.1
lodash:
specifier: ^4.17.21
version: registry.npmmirror.com/lodash@4.17.21
@@ -79,6 +76,9 @@ devDependencies:
'@vue/tsconfig':
specifier: ^0.1.3
version: registry.npmmirror.com/@vue/tsconfig@0.1.3(@types/node@18.16.16)
+ '@vueuse/core':
+ specifier: ^10.2.0
+ version: registry.npmmirror.com/@vueuse/core@10.2.0(vue@3.3.4)
autoprefixer:
specifier: ^10.4.14
version: registry.npmmirror.com/autoprefixer@10.4.14(postcss@8.4.24)
@@ -138,7 +138,7 @@ devDependencies:
version: registry.npmmirror.com/typescript@5.1.3
unplugin-auto-import:
specifier: ^0.15.3
- version: registry.npmmirror.com/unplugin-auto-import@0.15.3
+ version: registry.npmmirror.com/unplugin-auto-import@0.15.3(@vueuse/core@10.2.0)
unplugin-icons:
specifier: ^0.15.3
version: registry.npmmirror.com/unplugin-icons@0.15.3
@@ -154,6 +154,9 @@ devDependencies:
vue-tsc:
specifier: ^1.6.5
version: registry.npmmirror.com/vue-tsc@1.6.5(typescript@5.1.3)
+ xgplayer:
+ specifier: ^3.0.5
+ version: registry.npmmirror.com/xgplayer@3.0.5(core-js@3.31.0)
packages:
@@ -1573,6 +1576,12 @@ packages:
version: 0.0.16
dev: false
+ registry.npmmirror.com/@types/web-bluetooth@0.0.17:
+ resolution: {integrity: sha512-4p9vcSmxAayx72yn70joFoL44c9MO/0+iVEBIQXe3v2h2SiAsEIo/G5v6ObFWvNKRFjbrVadNf9LqEEZeQPzdA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@types/web-bluetooth/-/web-bluetooth-0.0.17.tgz}
+ name: '@types/web-bluetooth'
+ version: 0.0.17
+ dev: true
+
registry.npmmirror.com/@typescript-eslint/eslint-plugin@5.59.9(@typescript-eslint/parser@5.59.9)(eslint@8.42.0)(typescript@5.1.3):
resolution: {integrity: sha512-4uQIBq1ffXd2YvF7MAvehWKW3zVv/w+mSfRAu+8cKbfj3nwzyqJLNcZJpQ/WZ1HLbJDiowwmQ6NO+63nCA+fqA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.9.tgz}
id: registry.npmmirror.com/@typescript-eslint/eslint-plugin/5.59.9
@@ -1993,6 +2002,21 @@ packages:
'@types/node': registry.npmmirror.com/@types/node@18.16.16
dev: true
+ registry.npmmirror.com/@vueuse/core@10.2.0(vue@3.3.4):
+ resolution: {integrity: sha512-aHBnoCteIS3hFu7ZZkVB93SanVDY6t4TIb7XDLxJT/HQdAZz+2RdIEJ8rj5LUoEJr7Damb5+sJmtpCwGez5ozQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@vueuse/core/-/core-10.2.0.tgz}
+ id: registry.npmmirror.com/@vueuse/core/10.2.0
+ name: '@vueuse/core'
+ version: 10.2.0
+ dependencies:
+ '@types/web-bluetooth': registry.npmmirror.com/@types/web-bluetooth@0.0.17
+ '@vueuse/metadata': registry.npmmirror.com/@vueuse/metadata@10.2.0
+ '@vueuse/shared': registry.npmmirror.com/@vueuse/shared@10.2.0(vue@3.3.4)
+ vue-demi: registry.npmmirror.com/vue-demi@0.14.5(vue@3.3.4)
+ transitivePeerDependencies:
+ - '@vue/composition-api'
+ - vue
+ dev: true
+
registry.npmmirror.com/@vueuse/core@9.13.0(vue@3.3.4):
resolution: {integrity: sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@vueuse/core/-/core-9.13.0.tgz}
id: registry.npmmirror.com/@vueuse/core/9.13.0
@@ -2008,12 +2032,30 @@ packages:
- vue
dev: false
+ registry.npmmirror.com/@vueuse/metadata@10.2.0:
+ resolution: {integrity: sha512-IR7Mkq6QSgZ38q/2ZzOt+Zz1OpcEsnwE64WBumDQ+RGKrosFCtUA2zgRrOqDEzPBXrVB+4HhFkwDjQMu0fDBKw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@vueuse/metadata/-/metadata-10.2.0.tgz}
+ name: '@vueuse/metadata'
+ version: 10.2.0
+ dev: true
+
registry.npmmirror.com/@vueuse/metadata@9.13.0:
resolution: {integrity: sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@vueuse/metadata/-/metadata-9.13.0.tgz}
name: '@vueuse/metadata'
version: 9.13.0
dev: false
+ registry.npmmirror.com/@vueuse/shared@10.2.0(vue@3.3.4):
+ resolution: {integrity: sha512-dIeA8+g9Av3H5iF4NXR/sft4V6vys76CpZ6hxwj8eMXybXk2WRl3scSsOVi+kQ9SX38COR7AH7WwY83UcuxbSg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@vueuse/shared/-/shared-10.2.0.tgz}
+ id: registry.npmmirror.com/@vueuse/shared/10.2.0
+ name: '@vueuse/shared'
+ version: 10.2.0
+ dependencies:
+ vue-demi: registry.npmmirror.com/vue-demi@0.14.5(vue@3.3.4)
+ transitivePeerDependencies:
+ - '@vue/composition-api'
+ - vue
+ dev: true
+
registry.npmmirror.com/@vueuse/shared@9.13.0(vue@3.3.4):
resolution: {integrity: sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@vueuse/shared/-/shared-9.13.0.tgz}
id: registry.npmmirror.com/@vueuse/shared/9.13.0
@@ -2600,6 +2642,13 @@ packages:
version: 1.9.0
dev: true
+ registry.npmmirror.com/core-js@3.31.0:
+ resolution: {integrity: sha512-NIp2TQSGfR6ba5aalZD+ZQ1fSxGhDo/s1w0nx3RYzf2pnJxt7YynxFlFScP6eV7+GZsKO95NSjGxyJsU3DZgeQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/core-js/-/core-js-3.31.0.tgz}
+ name: core-js
+ version: 3.31.0
+ requiresBuild: true
+ dev: true
+
registry.npmmirror.com/cosmiconfig-typescript-loader@4.3.0(@types/node@18.16.16)(cosmiconfig@8.2.0)(ts-node@10.9.1)(typescript@4.8.4):
resolution: {integrity: sha512-NTxV1MFfZDLPiBMjxbHRwSh5LaLcPMwNdCutmnHJCKoVnlvldPWlllonKwrsRJ5pYZBIBGRWWU2tfvzxgeSW5Q==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-4.3.0.tgz}
id: registry.npmmirror.com/cosmiconfig-typescript-loader/4.3.0
@@ -2690,6 +2739,23 @@ packages:
name: csstype
version: 3.1.2
+ registry.npmmirror.com/d@1.0.1:
+ resolution: {integrity: sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/d/-/d-1.0.1.tgz}
+ name: d
+ version: 1.0.1
+ dependencies:
+ es5-ext: registry.npmmirror.com/es5-ext@0.10.62
+ type: registry.npmmirror.com/type@1.2.0
+ dev: true
+
+ registry.npmmirror.com/danmu.js@1.1.8:
+ resolution: {integrity: sha512-GIFSHqJ+HFTGLLaL2BHMPBaOuPY1bWPwC0Pvi/V06uMIoxNTyEGxMuoO2SzNHsDvKC/r252zR9T/Gwx93AaKfw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/danmu.js/-/danmu.js-1.1.8.tgz}
+ name: danmu.js
+ version: 1.1.8
+ dependencies:
+ event-emitter: registry.npmmirror.com/event-emitter@0.3.5
+ dev: true
+
registry.npmmirror.com/dargs@7.0.0:
resolution: {integrity: sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/dargs/-/dargs-7.0.0.tgz}
name: dargs
@@ -2763,6 +2829,12 @@ packages:
engines: {node: '>=0.4.0'}
dev: true
+ registry.npmmirror.com/delegate@3.2.0:
+ resolution: {integrity: sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/delegate/-/delegate-3.2.0.tgz}
+ name: delegate
+ version: 3.2.0
+ dev: true
+
registry.npmmirror.com/deprecation@2.3.1:
resolution: {integrity: sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/deprecation/-/deprecation-2.3.1.tgz}
name: deprecation
@@ -2838,6 +2910,12 @@ packages:
is-obj: registry.npmmirror.com/is-obj@2.0.0
dev: true
+ registry.npmmirror.com/downloadjs@1.4.7:
+ resolution: {integrity: sha512-LN1gO7+u9xjU5oEScGFKvXhYf7Y/empUIIEAGBs1LzUq/rg5duiDrkuH5A2lQGd5jfMOb9X9usDa2oVXwJ0U/Q==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/downloadjs/-/downloadjs-1.4.7.tgz}
+ name: downloadjs
+ version: 1.4.7
+ dev: true
+
registry.npmmirror.com/duplexer@0.1.2:
resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/duplexer/-/duplexer-0.1.2.tgz}
name: duplexer
@@ -2975,6 +3053,37 @@ packages:
is-symbol: registry.npmmirror.com/is-symbol@1.0.4
dev: true
+ registry.npmmirror.com/es5-ext@0.10.62:
+ resolution: {integrity: sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/es5-ext/-/es5-ext-0.10.62.tgz}
+ name: es5-ext
+ version: 0.10.62
+ engines: {node: '>=0.10'}
+ requiresBuild: true
+ dependencies:
+ es6-iterator: registry.npmmirror.com/es6-iterator@2.0.3
+ es6-symbol: registry.npmmirror.com/es6-symbol@3.1.3
+ next-tick: registry.npmmirror.com/next-tick@1.1.0
+ dev: true
+
+ registry.npmmirror.com/es6-iterator@2.0.3:
+ resolution: {integrity: sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/es6-iterator/-/es6-iterator-2.0.3.tgz}
+ name: es6-iterator
+ version: 2.0.3
+ dependencies:
+ d: registry.npmmirror.com/d@1.0.1
+ es5-ext: registry.npmmirror.com/es5-ext@0.10.62
+ es6-symbol: registry.npmmirror.com/es6-symbol@3.1.3
+ dev: true
+
+ registry.npmmirror.com/es6-symbol@3.1.3:
+ resolution: {integrity: sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/es6-symbol/-/es6-symbol-3.1.3.tgz}
+ name: es6-symbol
+ version: 3.1.3
+ dependencies:
+ d: registry.npmmirror.com/d@1.0.1
+ ext: registry.npmmirror.com/ext@1.7.0
+ dev: true
+
registry.npmmirror.com/esbuild@0.17.19:
resolution: {integrity: sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/esbuild/-/esbuild-0.17.19.tgz}
name: esbuild
@@ -3226,6 +3335,21 @@ packages:
engines: {node: '>=0.10.0'}
dev: true
+ registry.npmmirror.com/event-emitter@0.3.5:
+ resolution: {integrity: sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/event-emitter/-/event-emitter-0.3.5.tgz}
+ name: event-emitter
+ version: 0.3.5
+ dependencies:
+ d: registry.npmmirror.com/d@1.0.1
+ es5-ext: registry.npmmirror.com/es5-ext@0.10.62
+ dev: true
+
+ registry.npmmirror.com/eventemitter3@4.0.7:
+ resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/eventemitter3/-/eventemitter3-4.0.7.tgz}
+ name: eventemitter3
+ version: 4.0.7
+ dev: true
+
registry.npmmirror.com/execa@5.1.1:
resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/execa/-/execa-5.1.1.tgz}
name: execa
@@ -3260,6 +3384,14 @@ packages:
strip-final-newline: registry.npmmirror.com/strip-final-newline@3.0.0
dev: true
+ registry.npmmirror.com/ext@1.7.0:
+ resolution: {integrity: sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/ext/-/ext-1.7.0.tgz}
+ name: ext
+ version: 1.7.0
+ dependencies:
+ type: registry.npmmirror.com/type@2.7.2
+ dev: true
+
registry.npmmirror.com/fast-deep-equal@3.1.3:
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz}
name: fast-deep-equal
@@ -3669,12 +3801,6 @@ packages:
version: 1.4.0
dev: true
- registry.npmmirror.com/gsap@3.12.1:
- resolution: {integrity: sha512-FXtb2YbBE9l8I9Pl5DFLpCMedaiMPztRlr0Ln0CMSnJn+pbTaeKlzgth8cLNPc7PzNwIZe+SEQiBBAWaBKJdVA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/gsap/-/gsap-3.12.1.tgz}
- name: gsap
- version: 3.12.1
- dev: false
-
registry.npmmirror.com/gzip-size@6.0.0:
resolution: {integrity: sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/gzip-size/-/gzip-size-6.0.0.tgz}
name: gzip-size
@@ -4728,6 +4854,12 @@ packages:
version: 1.4.0
dev: true
+ registry.npmmirror.com/next-tick@1.1.0:
+ resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/next-tick/-/next-tick-1.1.0.tgz}
+ name: next-tick
+ version: 1.1.0
+ dev: true
+
registry.npmmirror.com/nice-try@1.0.5:
resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/nice-try/-/nice-try-1.0.5.tgz}
name: nice-try
@@ -6332,6 +6464,18 @@ packages:
engines: {node: '>=8'}
dev: true
+ registry.npmmirror.com/type@1.2.0:
+ resolution: {integrity: sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/type/-/type-1.2.0.tgz}
+ name: type
+ version: 1.2.0
+ dev: true
+
+ registry.npmmirror.com/type@2.7.2:
+ resolution: {integrity: sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/type/-/type-2.7.2.tgz}
+ name: type
+ version: 2.7.2
+ dev: true
+
registry.npmmirror.com/typed-array-length@1.0.4:
resolution: {integrity: sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/typed-array-length/-/typed-array-length-1.0.4.tgz}
name: typed-array-length
@@ -6407,8 +6551,9 @@ packages:
engines: {node: '>= 10.0.0'}
dev: true
- registry.npmmirror.com/unplugin-auto-import@0.15.3:
+ registry.npmmirror.com/unplugin-auto-import@0.15.3(@vueuse/core@10.2.0):
resolution: {integrity: sha512-RLT8SqbPn4bT7yBshZId0uPSofKWnwr66RyDaxWaFb/+f7OTDOWAsVNz+hOQLBWSjvbekr2xZY9ccS8TDHJbCQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/unplugin-auto-import/-/unplugin-auto-import-0.15.3.tgz}
+ id: registry.npmmirror.com/unplugin-auto-import/0.15.3
name: unplugin-auto-import
version: 0.15.3
engines: {node: '>=14'}
@@ -6423,6 +6568,7 @@ packages:
dependencies:
'@antfu/utils': registry.npmmirror.com/@antfu/utils@0.7.4
'@rollup/pluginutils': registry.npmmirror.com/@rollup/pluginutils@5.0.2
+ '@vueuse/core': registry.npmmirror.com/@vueuse/core@10.2.0(vue@3.3.4)
local-pkg: registry.npmmirror.com/local-pkg@0.4.3
magic-string: registry.npmmirror.com/magic-string@0.30.0
minimatch: registry.npmmirror.com/minimatch@9.0.1
@@ -6626,7 +6772,6 @@ packages:
optional: true
dependencies:
vue: registry.npmmirror.com/vue@3.3.4
- dev: false
registry.npmmirror.com/vue-eslint-parser@9.3.0(eslint@8.42.0):
resolution: {integrity: sha512-48IxT9d0+wArT1+3wNIy0tascRoywqSUe2E1YalIC1L8jsUGe5aJQItWfRok7DVFGz3UYvzEI7n5wiTXsCMAcQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/vue-eslint-parser/-/vue-eslint-parser-9.3.0.tgz}
@@ -6840,6 +6985,34 @@ packages:
optional: true
dev: true
+ registry.npmmirror.com/xgplayer-subtitles@3.0.5(core-js@3.31.0):
+ resolution: {integrity: sha512-NH7UjkK+2gy15DZsB4SFNUkdcgLMm6THVEMzl026pxEQyqXr0F65fMhx3nzFGZLzL2TSRpQHPJCeBNqGY0uWEg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/xgplayer-subtitles/-/xgplayer-subtitles-3.0.5.tgz}
+ id: registry.npmmirror.com/xgplayer-subtitles/3.0.5
+ name: xgplayer-subtitles
+ version: 3.0.5
+ peerDependencies:
+ core-js: '>=3.12.1'
+ dependencies:
+ core-js: registry.npmmirror.com/core-js@3.31.0
+ eventemitter3: registry.npmmirror.com/eventemitter3@4.0.7
+ dev: true
+
+ registry.npmmirror.com/xgplayer@3.0.5(core-js@3.31.0):
+ resolution: {integrity: sha512-QmKcc2l/ETF6JYOYbYz8dm1OWOwft/6AuUy6RF8ms+3T05sqMXlAqEoXPVnCmiHs4BB4W2LSXsxyQXX6+xNBTw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/xgplayer/-/xgplayer-3.0.5.tgz}
+ id: registry.npmmirror.com/xgplayer/3.0.5
+ name: xgplayer
+ version: 3.0.5
+ peerDependencies:
+ core-js: '>=3.12.1'
+ dependencies:
+ core-js: registry.npmmirror.com/core-js@3.31.0
+ danmu.js: registry.npmmirror.com/danmu.js@1.1.8
+ delegate: registry.npmmirror.com/delegate@3.2.0
+ downloadjs: registry.npmmirror.com/downloadjs@1.4.7
+ eventemitter3: registry.npmmirror.com/eventemitter3@4.0.7
+ xgplayer-subtitles: registry.npmmirror.com/xgplayer-subtitles@3.0.5(core-js@3.31.0)
+ dev: true
+
registry.npmmirror.com/xml-name-validator@4.0.0:
resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz}
name: xml-name-validator
diff --git a/src/assets/avatars/default.png b/src/assets/avatars/default.png
deleted file mode 100644
index 90113952..00000000
Binary files a/src/assets/avatars/default.png and /dev/null differ
diff --git a/src/assets/iconfont/index-color.css b/src/assets/iconfont/index-color.css
new file mode 100644
index 00000000..89408aa7
--- /dev/null
+++ b/src/assets/iconfont/index-color.css
@@ -0,0 +1,143 @@
+/* stylelint-disable */
+/* eslint-disable */
+/* prettier-ignore */
+@font-face {
+ font-family: "mallchatcolor"; /* Project id 4141022 */
+ /* Color fonts */
+ src:
+ url('//at.alicdn.com/t/c/font_4141022_j7dvq8wg0dr.woff2?t=1688220706250') format('woff2'),
+ url('//at.alicdn.com/t/c/font_4141022_j7dvq8wg0dr.woff?t=1688220706250') format('woff'),
+ url('//at.alicdn.com/t/c/font_4141022_j7dvq8wg0dr.ttf?t=1688220706250') format('truetype');
+}
+
+.mallchatcolor {
+ font-family: 'mallchatcolor' !important;
+ font-size: 16px;
+ font-style: normal;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+.icon-happy1:before {
+ content: '\e620';
+}
+
+.icon-laughing:before {
+ content: '\e62b';
+}
+
+.icon-other:before {
+ content: '\e67a';
+}
+
+.icon-posun:before {
+ content: '\e67e';
+}
+
+.icon-zip:before {
+ content: '\e60c';
+}
+
+.icon-wenjianjia1:before {
+ content: '\e64d';
+}
+
+.icon-at:before {
+ content: '\e629';
+}
+
+.icon-wenjianjia2:before {
+ content: '\e647';
+}
+
+.icon-tupian:before {
+ content: '\e679';
+}
+
+.icon-qcloud:before {
+ content: '\e600';
+}
+
+.icon-yuque:before {
+ content: '\e7c0';
+}
+
+.icon-dazed:before {
+ content: '\e60f';
+}
+
+.icon-happy:before {
+ content: '\e621';
+}
+
+.icon-secret:before {
+ content: '\e63c';
+}
+
+.icon-shocked:before {
+ content: '\e63e';
+}
+
+.icon-smiling:before {
+ content: '\e642';
+}
+
+.icon-yawn:before {
+ content: '\e64e';
+}
+
+.icon-github:before {
+ content: '\e709';
+}
+
+.icon-weixin:before {
+ content: '\e60d';
+}
+
+.icon-bilibili:before {
+ content: '\e60e';
+}
+
+.icon-doc:before {
+ content: '\e601';
+}
+
+.icon-code:before {
+ content: '\e602';
+}
+
+.icon-ppt:before {
+ content: '\e603';
+}
+
+.icon-txt:before {
+ content: '\e604';
+}
+
+.icon-java:before {
+ content: '\e605';
+}
+
+.icon-pdf:before {
+ content: '\e606';
+}
+
+.icon-jpg:before {
+ content: '\e607';
+}
+
+.icon-mp3:before {
+ content: '\e608';
+}
+
+.icon-mp4:before {
+ content: '\e609';
+}
+
+.icon-wenjianjia:before {
+ content: '\e60a';
+}
+
+.icon-xls:before {
+ content: '\e60b';
+}
diff --git a/src/assets/iconfont/index.css b/src/assets/iconfont/index.css
new file mode 100644
index 00000000..39928c0f
--- /dev/null
+++ b/src/assets/iconfont/index.css
@@ -0,0 +1,85 @@
+/* stylelint-disable */
+/* eslint-disable */
+/* prettier-ignore */
+@font-face {
+ font-family: "mallchat"; /* Project id 4142069 */
+ src: url('//at.alicdn.com/t/c/font_4142069_q7wdf7ccbx.woff2?t=1688219879090') format('woff2'),
+ url('//at.alicdn.com/t/c/font_4142069_q7wdf7ccbx.woff?t=1688219879090') format('woff'),
+ url('//at.alicdn.com/t/c/font_4142069_q7wdf7ccbx.ttf?t=1688219879090') format('truetype');
+}
+
+.mallchat {
+ font-family: 'mallchat' !important;
+ font-size: 16px;
+ font-style: normal;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+.icon-tupian1:before {
+ content: '\e640';
+}
+
+.icon-jianpan:before {
+ content: '\e6d8';
+}
+
+.icon-saying:before {
+ content: '\e62f';
+}
+
+.icon-xiazai:before {
+ content: '\e616';
+}
+
+.icon-dislike:before {
+ content: '\e67c';
+}
+
+.icon-huojian:before {
+ content: '\e68b';
+}
+
+.icon-like:before {
+ content: '\e85c';
+}
+
+.icon-reply:before {
+ content: '\e716';
+}
+
+.icon-totop:before {
+ content: '\e63a';
+}
+
+.icon-zhankai:before {
+ content: '\e657';
+}
+
+.icon-chehui:before {
+ content: '\e665';
+}
+
+.icon-shanchu:before {
+ content: '\e6bb';
+}
+
+.icon-voice:before {
+ content: '\e85e';
+}
+
+.icon-avatar:before {
+ content: '\e648';
+}
+
+.icon-loading:before {
+ content: '\e6de';
+}
+
+.icon-copy:before {
+ content: '\e62d';
+}
+
+.icon-lahei:before {
+ content: '\e651';
+}
diff --git a/src/assets/icons/icon_bilibili.svg b/src/assets/icons/icon_bilibili.svg
deleted file mode 100644
index 5b5ebda4..00000000
--- a/src/assets/icons/icon_bilibili.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/src/assets/icons/icon_github.svg b/src/assets/icons/icon_github.svg
deleted file mode 100644
index 3aad2ba0..00000000
--- a/src/assets/icons/icon_github.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/src/assets/icons/icon_like.svg b/src/assets/icons/icon_like.svg
deleted file mode 100644
index 029f1cef..00000000
--- a/src/assets/icons/icon_like.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/src/assets/icons/icon_liked.svg b/src/assets/icons/icon_liked.svg
deleted file mode 100644
index daaf6ab0..00000000
--- a/src/assets/icons/icon_liked.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/src/assets/icons/icon_reply.svg b/src/assets/icons/icon_reply.svg
deleted file mode 100644
index 6c3fe220..00000000
--- a/src/assets/icons/icon_reply.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/src/assets/icons/icon_tencent_cloud.svg b/src/assets/icons/icon_tencent_cloud.svg
deleted file mode 100644
index 03c4dc8e..00000000
--- a/src/assets/icons/icon_tencent_cloud.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/src/assets/icons/icon_tiktok.svg b/src/assets/icons/icon_tiktok.svg
deleted file mode 100644
index f9f91fb0..00000000
--- a/src/assets/icons/icon_tiktok.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/src/assets/icons/icon_wechat.svg b/src/assets/icons/icon_wechat.svg
deleted file mode 100644
index 37c9fca6..00000000
--- a/src/assets/icons/icon_wechat.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/src/assets/icons/icon_yuque.png b/src/assets/icons/icon_yuque.png
deleted file mode 100755
index cf35622e..00000000
Binary files a/src/assets/icons/icon_yuque.png and /dev/null differ
diff --git a/src/assets/operate-icons/to_top.svg b/src/assets/operate-icons/to_top.svg
deleted file mode 100644
index 78645853..00000000
--- a/src/assets/operate-icons/to_top.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/src/components.d.ts b/src/components.d.ts
index cd4a456c..d5cfb6fe 100644
--- a/src/components.d.ts
+++ b/src/components.d.ts
@@ -9,44 +9,37 @@ export {}
declare module '@vue/runtime-core' {
export interface GlobalComponents {
- AnimateCount: typeof import('./components/AnimateCount.vue')['default']
+ Avatar: typeof import('./components/avatar/index.vue')['default']
ElAlert: typeof import('element-plus/es')['ElAlert']
ElAvatar: typeof import('element-plus/es')['ElAvatar']
ElButton: typeof import('element-plus/es')['ElButton']
ElDialog: typeof import('element-plus/es')['ElDialog']
ElIcon: typeof import('element-plus/es')['ElIcon']
+ ElImage: typeof import('element-plus/es')['ElImage']
ElInput: typeof import('element-plus/es')['ElInput']
ElPopover: typeof import('element-plus/es')['ElPopover']
ElTooltip: typeof import('element-plus/es')['ElTooltip']
- IconCommunity: typeof import('./components/icons/IconCommunity.vue')['default']
- IconCopy: typeof import('./components/icons/IconCopy.vue')['default']
- IconDelete: typeof import('./components/icons/IconDelete.vue')['default']
- IconDislike: typeof import('./components/icons/iconDislike.vue')['default']
- IconDocumentation: typeof import('./components/icons/IconDocumentation.vue')['default']
- IconEcosystem: typeof import('./components/icons/IconEcosystem.vue')['default']
- IconLike: typeof import('./components/icons/iconLike.vue')['default']
- IconMore: typeof import('./components/icons/IconMore.vue')['default']
- IconRecall: typeof import('./components/icons/IconRecall.vue')['default']
- IconReply: typeof import('./components/icons/IconReply.vue')['default']
- IconShield: typeof import('./components/icons/IconShield.vue')['default']
- IconSupport: typeof import('./components/icons/IconSupport.vue')['default']
- IconTooling: typeof import('./components/icons/IconTooling.vue')['default']
+ File: typeof import('./components/RenderMessage/file.vue')['default']
+ Icon: typeof import('./components/Icon/index.vue')['default']
IEpArrowDownBold: typeof import('~icons/ep/arrow-down-bold')['default']
IEpChatDotRound: typeof import('~icons/ep/chat-dot-round')['default']
IEpClose: typeof import('~icons/ep/close')['default']
IEpFemale: typeof import('~icons/ep/female')['default']
- IEpFold: typeof import('~icons/ep/fold')['default']
IEpInfoFilled: typeof import('~icons/ep/info-filled')['default']
IEpLoading: typeof import('~icons/ep/loading')['default']
IEpLock: typeof import('~icons/ep/lock')['default']
IEpMale: typeof import('~icons/ep/male')['default']
IEpSuccessFilled: typeof import('~icons/ep/success-filled')['default']
- LikeButton: typeof import('./components/LikeButton/index.vue')['default']
+ Image: typeof import('./components/RenderMessage/image.vue')['default']
LoginBox: typeof import('./components/LoginBox/index.vue')['default']
- RenderMsg: typeof import('./components/RenderMsg.vue')['default']
+ RenderMessage: typeof import('./components/RenderMessage/index.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
+ Text: typeof import('./components/RenderMessage/text.vue')['default']
UserSettingBox: typeof import('./components/UserSettingBox/index.vue')['default']
+ Video: typeof import('./components/RenderMessage/video.vue')['default']
+ VideoPlayer: typeof import('./components/VideoPlayer/index.vue')['default']
+ Voice: typeof import('./components/RenderMessage/voice.vue')['default']
}
export interface ComponentCustomProperties {
vLoading: typeof import('element-plus/es')['ElLoadingDirective']
diff --git a/src/components/AnimateCount.vue b/src/components/AnimateCount.vue
deleted file mode 100644
index 0de50573..00000000
--- a/src/components/AnimateCount.vue
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
-
- {{ tweened.number.toFixed(0) }}
-
-
diff --git a/src/components/Icon/index.vue b/src/components/Icon/index.vue
new file mode 100644
index 00000000..850135f4
--- /dev/null
+++ b/src/components/Icon/index.vue
@@ -0,0 +1,47 @@
+
+
+
+
+
diff --git a/src/components/RenderMessage/file.vue b/src/components/RenderMessage/file.vue
new file mode 100644
index 00000000..8388adfc
--- /dev/null
+++ b/src/components/RenderMessage/file.vue
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+ {{ body?.fileName || '未知文件' }}
+ {{ formatBytes(body?.size) }}
+
+
+
+
+
diff --git a/src/components/RenderMessage/image.vue b/src/components/RenderMessage/image.vue
new file mode 100644
index 00000000..2cd58ee9
--- /dev/null
+++ b/src/components/RenderMessage/image.vue
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+ Loading...
+
+
+
+
+
+ 加载失败
+
+
+
+
diff --git a/src/components/RenderMessage/index.vue b/src/components/RenderMessage/index.vue
new file mode 100644
index 00000000..115c73e5
--- /dev/null
+++ b/src/components/RenderMessage/index.vue
@@ -0,0 +1,31 @@
+
+
+
+
+
diff --git a/src/components/RenderMessage/text.vue b/src/components/RenderMessage/text.vue
new file mode 100644
index 00000000..d145b1c3
--- /dev/null
+++ b/src/components/RenderMessage/text.vue
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+ {{ item.trim() }}
+ {{ urlMap[item] }}
+ 暂无描述
+
+
+ {{ item }}
+
+ {{ item }}
+
+
+
diff --git a/src/components/RenderMessage/video.vue b/src/components/RenderMessage/video.vue
new file mode 100644
index 00000000..feae55f4
--- /dev/null
+++ b/src/components/RenderMessage/video.vue
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
diff --git a/src/components/RenderMessage/voice.vue b/src/components/RenderMessage/voice.vue
new file mode 100644
index 00000000..9c66d1c9
--- /dev/null
+++ b/src/components/RenderMessage/voice.vue
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
{{ progress || body?.second }}s
+
+
diff --git a/src/components/RenderMsg.vue b/src/components/RenderMsg.vue
deleted file mode 100644
index 6fc2ea01..00000000
--- a/src/components/RenderMsg.vue
+++ /dev/null
@@ -1,41 +0,0 @@
-
-
-
-
-
- {{ urlMap[item] }}
-
- {{ item }}
-
-
diff --git a/src/components/UserSettingBox/index.vue b/src/components/UserSettingBox/index.vue
index a370c1c8..b75f51b1 100644
--- a/src/components/UserSettingBox/index.vue
+++ b/src/components/UserSettingBox/index.vue
@@ -4,7 +4,7 @@ import { useRequest } from 'alova'
import { ElMessage } from 'element-plus'
import { Select, CloseBold, EditPen } from '@element-plus/icons-vue'
import { useUserStore } from '@/stores/user'
-import { SexType, IsYet } from '@/services/types'
+import { SexEnum, IsYetEnum } from '@/enums'
import type { BadgeType } from '@/services/types'
import apis from '@/services/apis'
import { judgeClient } from '@/utils/detectDevice'
@@ -44,7 +44,7 @@ watchEffect(() => {
})
const currentBadge = computed(() =>
- badgeList.value.find((item) => item.obtain === IsYet.Yes && item.wearing === IsYet.Yes),
+ badgeList.value.find((item) => item.obtain === IsYetEnum.YES && item.wearing === IsYetEnum.YES),
)
// 佩戴卸下徽章
@@ -111,15 +111,15 @@ const onCancelEditName = async () => {
size="20"
color="var(--font-main)"
class="setting-avatar-sex"
- v-if="userInfo.sex && [SexType.Man, SexType.Female].includes(userInfo.sex)"
+ v-if="userInfo.sex && [SexEnum.MAN, SexEnum.REMALE].includes(userInfo.sex)"
:style="{
backgroundColor: `var(${
- userInfo.sex === SexType.Man ? '--avatar-sex-bg-man' : '--avatar-sex-bg-female'
+ userInfo.sex === SexEnum.MAN ? '--avatar-sex-bg-man' : '--avatar-sex-bg-female'
})`,
}"
>
-
-
+
+
@@ -183,20 +183,20 @@ const onCancelEditName = async () => {
-
+
佩戴
-
+
diff --git a/src/components/VideoPlayer/index.vue b/src/components/VideoPlayer/index.vue
new file mode 100644
index 00000000..fef37b42
--- /dev/null
+++ b/src/components/VideoPlayer/index.vue
@@ -0,0 +1,29 @@
+
+
+
+
+
diff --git a/src/components/avatar/index.vue b/src/components/avatar/index.vue
new file mode 100644
index 00000000..4f38733f
--- /dev/null
+++ b/src/components/avatar/index.vue
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/avatar/styles.scss b/src/components/avatar/styles.scss
new file mode 100644
index 00000000..4bd6ef78
--- /dev/null
+++ b/src/components/avatar/styles.scss
@@ -0,0 +1,48 @@
+.avatar {
+ position: relative;
+ box-sizing: border-box;
+ line-height: 1;
+ white-space: nowrap;
+ vertical-align: middle;
+ cursor: pointer;
+ user-select: none;
+ -webkit-user-drag: none;
+
+ .status {
+ position: absolute;
+ right: 0;
+ bottom: 0;
+ z-index: 1;
+ width: 30%;
+ height: 30%;
+ background-color: var(--color-online);
+ border-radius: 50%;
+ }
+
+ img {
+ width: 100%;
+ height: 100%;
+ -webkit-user-drag: none;
+ }
+}
+
+.downline {
+ filter: grayscale(1);
+}
+
+.avatar-circle {
+ border-radius: 50%;
+
+ img {
+ border-radius: 50%;
+ }
+}
+
+.avatar-square {
+ border-radius: 8px;
+
+ img {
+ overflow: hidden;
+ border-radius: 8px;
+ }
+}
diff --git a/src/components/icons/IconCommunity.vue b/src/components/icons/IconCommunity.vue
deleted file mode 100644
index 2dc8b055..00000000
--- a/src/components/icons/IconCommunity.vue
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
diff --git a/src/components/icons/IconCopy.vue b/src/components/icons/IconCopy.vue
deleted file mode 100644
index d83bb3cf..00000000
--- a/src/components/icons/IconCopy.vue
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
diff --git a/src/components/icons/IconDelete.vue b/src/components/icons/IconDelete.vue
deleted file mode 100644
index aa1ab245..00000000
--- a/src/components/icons/IconDelete.vue
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
diff --git a/src/components/icons/IconDocumentation.vue b/src/components/icons/IconDocumentation.vue
deleted file mode 100644
index 6d4791cf..00000000
--- a/src/components/icons/IconDocumentation.vue
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
diff --git a/src/components/icons/IconEcosystem.vue b/src/components/icons/IconEcosystem.vue
deleted file mode 100644
index c3a4f078..00000000
--- a/src/components/icons/IconEcosystem.vue
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
diff --git a/src/components/icons/IconMore.vue b/src/components/icons/IconMore.vue
deleted file mode 100644
index 7b8f4602..00000000
--- a/src/components/icons/IconMore.vue
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
diff --git a/src/components/icons/IconRecall.vue b/src/components/icons/IconRecall.vue
deleted file mode 100644
index c5f03c9b..00000000
--- a/src/components/icons/IconRecall.vue
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
diff --git a/src/components/icons/IconReply.vue b/src/components/icons/IconReply.vue
deleted file mode 100644
index 0f50c93c..00000000
--- a/src/components/icons/IconReply.vue
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
diff --git a/src/components/icons/IconShield.vue b/src/components/icons/IconShield.vue
deleted file mode 100644
index 220f48ed..00000000
--- a/src/components/icons/IconShield.vue
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
diff --git a/src/components/icons/IconSupport.vue b/src/components/icons/IconSupport.vue
deleted file mode 100644
index 7452834d..00000000
--- a/src/components/icons/IconSupport.vue
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
diff --git a/src/components/icons/IconTooling.vue b/src/components/icons/IconTooling.vue
deleted file mode 100644
index 660598d7..00000000
--- a/src/components/icons/IconTooling.vue
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
-
diff --git a/src/components/icons/iconDislike.vue b/src/components/icons/iconDislike.vue
deleted file mode 100644
index 4f4e1519..00000000
--- a/src/components/icons/iconDislike.vue
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/src/components/icons/iconLike.vue b/src/components/icons/iconLike.vue
deleted file mode 100644
index 41fd66a3..00000000
--- a/src/components/icons/iconLike.vue
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
diff --git a/src/enums/index.ts b/src/enums/index.ts
new file mode 100644
index 00000000..a995bb22
--- /dev/null
+++ b/src/enums/index.ts
@@ -0,0 +1,68 @@
+/**
+ * 全局枚举文件
+ * 如果枚举值需要在全局使用,那么请在此文件中定义。其他枚举值请在对应的文件中定义。
+ * 定义规则:
+ * 枚举名:XxxEnum
+ * 枚举值:全部大写,单词间用下划线分割
+ */
+/** -------------------------------------- */
+
+/**
+ * 消息类型
+ */
+export enum MsgEnum {
+ /** 未知 */
+ UNKNOWN,
+ /** 文本 */
+ TEXT,
+ /** 撤回 */
+ RECALL,
+ /** 图片 */
+ IMAGE,
+ /** 文件 */
+ FILE,
+ /** 语音 */
+ VOICE,
+ /** 视频 */
+ VIDEO,
+}
+
+/**
+ * 在线状态
+ */
+export enum OnlineEnum {
+ /** 在线 */
+ ONLINE = 1,
+ /** 离线 */
+ OFFLINE,
+}
+
+/**
+ * 操作类型
+ */
+export enum ActEnum {
+ /** 确认 */
+ Confirm = 1,
+ /** 取消 */
+ Cancel,
+}
+
+export enum SexEnum {
+ MAN = 1,
+ REMALE,
+}
+
+export enum PowerEnum {
+ USER,
+ ADMIN,
+}
+
+export enum IsYetEnum {
+ NO,
+ YES,
+}
+
+export enum MarkEnum {
+ LIKE = 1,
+ DISLIKE,
+}
diff --git a/src/hooks/useLikeToggle.ts b/src/hooks/useLikeToggle.ts
index 2a2049f1..dd076a5e 100644
--- a/src/hooks/useLikeToggle.ts
+++ b/src/hooks/useLikeToggle.ts
@@ -1,6 +1,6 @@
import { computed } from 'vue'
import apis from '@/services/apis'
-import { ActType, MarkType, IsYet } from '@/services/types'
+import { ActEnum, IsYetEnum, MarkEnum } from '@/enums'
import type { MsgType } from '@/services/types'
/**
@@ -9,8 +9,8 @@ import type { MsgType } from '@/services/types'
* @description 引入该Hook后,可直接使用isLike(是否已点赞)、isDisLike(是否已倒赞)、onLike(点赞方法)、onDisLike(倒赞方法)
*/
export const useLikeToggle = (message: MsgType) => {
- const isLike = computed(() => message.messageMark.userLike === IsYet.Yes)
- const isDisLike = computed(() => message.messageMark.userDislike === IsYet.Yes)
+ const isLike = computed(() => message.messageMark.userLike === IsYetEnum.YES)
+ const isDisLike = computed(() => message.messageMark.userDislike === IsYetEnum.YES)
const likeCount = computed(() => message.messageMark.likeCount)
const dislikeCount = computed(() => message.messageMark.dislikeCount)
@@ -19,17 +19,17 @@ export const useLikeToggle = (message: MsgType) => {
* @description 根据是否已经点赞控制更新点赞状态
*/
const onLike = async () => {
- const actType = isLike.value ? ActType.Cancel : ActType.Confirm
- await apis.markMsg({ actType, markType: MarkType.Like, msgId: message.id }).send()
+ const actType = isLike.value ? ActEnum.Cancel : ActEnum.Confirm
+ await apis.markMsg({ actType, markType: MarkEnum.LIKE, msgId: message.id }).send()
// 根据actType类型去更新本地点赞状态-点赞数
const { likeCount } = message.messageMark
- const isConfirm = actType === ActType.Confirm
- message.messageMark.userLike = isConfirm ? IsYet.Yes : IsYet.No
+ const isConfirm = actType === ActEnum.Confirm
+ message.messageMark.userLike = isConfirm ? IsYetEnum.YES : IsYetEnum.NO
message.messageMark.likeCount = isConfirm ? likeCount + 1 : likeCount - 1
// 互斥操作
if (isDisLike.value) {
- message.messageMark.userDislike = IsYet.No
+ message.messageMark.userDislike = IsYetEnum.NO
message.messageMark.dislikeCount = dislikeCount.value - 1
}
}
@@ -39,17 +39,17 @@ export const useLikeToggle = (message: MsgType) => {
* @description 根据是否已经倒赞控制更新倒赞状态
*/
const onDisLike = async () => {
- const actType = isDisLike.value ? ActType.Cancel : ActType.Confirm
- await apis.markMsg({ actType, markType: MarkType.DisLike, msgId: message.id }).send()
+ const actType = isDisLike.value ? ActEnum.Cancel : ActEnum.Confirm
+ await apis.markMsg({ actType, markType: MarkEnum.DISLIKE, msgId: message.id }).send()
// 根据actType类型去更新本地倒赞状态-倒赞数
const { dislikeCount } = message.messageMark
- const isConfirm = actType === ActType.Confirm
- message.messageMark.userDislike = isConfirm ? IsYet.Yes : IsYet.No
+ const isConfirm = actType === ActEnum.Confirm
+ message.messageMark.userDislike = isConfirm ? IsYetEnum.YES : IsYetEnum.NO
message.messageMark.dislikeCount = isConfirm ? dislikeCount + 1 : dislikeCount - 1
// 互斥操作
if (isLike.value) {
- message.messageMark.userLike = IsYet.No
+ message.messageMark.userLike = IsYetEnum.NO
message.messageMark.likeCount = likeCount.value - 1
}
}
diff --git a/src/hooks/useMockMessage.ts b/src/hooks/useMockMessage.ts
new file mode 100644
index 00000000..dbc113da
--- /dev/null
+++ b/src/hooks/useMockMessage.ts
@@ -0,0 +1,57 @@
+import type { MessageType } from '@/services/types'
+import { computed } from 'vue'
+
+/**
+ * Mock 消息 Hook
+ */
+export const useMockMessage = () => {
+ // 获取本地存储的用户信息
+ const userInfo = computed(() => JSON.parse(localStorage.getItem('USER_INFO') || '{}'))
+
+ /**
+ * 模拟消息生成
+ * @param type 消息类型
+ * @param body 消息体
+ * @param messageMark 互动信息
+ * @returns 服务器格式消息
+ */
+ const mockMessage = (type: number, body: any, messageMark?: any): MessageType => {
+ const currentTimeStamp: number = Date.now()
+ const random: number = Math.floor(Math.random() * 15)
+ // 唯一id 后五位时间戳+随机数
+ const uniqueId: number = Number(String(currentTimeStamp).slice(-7) + random)
+ const content = type === 1 ? body.content : null
+ const { uid, name: username, avatar } = userInfo.value
+
+ const data = {
+ fromUser: {
+ username,
+ uid,
+ avatar,
+ locPlace: 'XX',
+ },
+ message: {
+ id: uniqueId,
+ sendTime: Number(currentTimeStamp),
+ content,
+ urlTitleMap: {},
+ type: type,
+ body,
+ messageMark: {
+ likeCount: 0,
+ userLike: 0,
+ dislikeCount: 0,
+ userDislike: 0,
+ ...messageMark,
+ },
+ },
+ sendTime: String(currentTimeStamp),
+ loading: true,
+ }
+ return data
+ }
+
+ return {
+ mockMessage,
+ }
+}
diff --git a/src/hooks/useRecording.ts b/src/hooks/useRecording.ts
new file mode 100644
index 00000000..d69a10d3
--- /dev/null
+++ b/src/hooks/useRecording.ts
@@ -0,0 +1,91 @@
+import { ref } from 'vue'
+import { createEventHook } from '@vueuse/core'
+import { ElMessage } from 'element-plus'
+
+/**
+ * wav音频录制Hook
+ */
+export const useRecording = () => {
+ const isRecording = ref(false) // 是否正在录制
+ const mediaRecorder = ref(null) // 录制器
+ const audioUrl = ref(null) // 音频地址
+ const file = ref(null) // 录制的WAV格式File
+ const timer = ref(null) // 计时器
+ const second = ref(0) // 当前秒数
+
+ const onEnd = createEventHook()
+
+ /**
+ * 开始录制
+ * @return File 录制的WAV音频
+ **/
+ const start = () => {
+ if (isRecording.value) return // 如果正在录制,就不再录制
+ reset() // 重置
+
+ const stream = navigator.mediaDevices.getUserMedia({ audio: true })
+ stream.then((stream) => {
+ mediaRecorder.value = new MediaRecorder(stream)
+ mediaRecorder.value.start()
+ isRecording.value = true
+ // 开始计时
+ timer.value = setInterval(() => {
+ second.value++
+ // 最多录制60秒
+ if (second.value >= 59) {
+ stop()
+ }
+ }, 1000)
+
+ const audioChunks: Blob[] = []
+ mediaRecorder.value.addEventListener('dataavailable', (event) => {
+ audioChunks.push(event.data)
+ })
+ mediaRecorder.value.addEventListener('stop', () => {
+ const blob = new Blob(audioChunks, { type: 'audio/wav' })
+ audioUrl.value = URL.createObjectURL(blob)
+ file.value = new File([blob], 'audio.wav', { type: 'audio/wav' })
+
+ timer.value && clearInterval(timer.value) // 停止计时
+ if (second.value < 2) {
+ ElMessage.warning('录制时间太短')
+ return
+ }
+ onEnd.trigger(file.value)
+ second.value = 0
+ })
+ })
+ }
+
+ /**
+ * 停止录制
+ */
+
+ const stop = () => {
+ isRecording.value = false
+ mediaRecorder.value?.stop()
+ }
+
+ /**
+ * 重置
+ **/
+ const reset = () => {
+ isRecording.value = false
+ mediaRecorder.value = null // 事件监听器会被自动清除
+ audioUrl.value = null
+ file.value = null
+ second.value = 0
+ timer.value && clearInterval(timer.value)
+ }
+
+ return {
+ isRecording,
+ audioUrl,
+ second,
+ file,
+ start,
+ stop,
+ reset,
+ onEnd: onEnd.on,
+ }
+}
diff --git a/src/hooks/useUpload.ts b/src/hooks/useUpload.ts
new file mode 100644
index 00000000..37ab79f6
--- /dev/null
+++ b/src/hooks/useUpload.ts
@@ -0,0 +1,160 @@
+import { ref } from 'vue'
+import { createEventHook } from '@vueuse/core'
+import apis from '@/services/apis'
+import { ElMessage } from 'element-plus'
+
+/** 文件信息类型 */
+export type FileInfoType = {
+ name: string
+ type: string
+ size: number
+ suffix: string
+ width?: number
+ height?: number
+ downloadUrl?: string
+ second?: number
+}
+
+const Max = 30 // 单位M
+const MAX_FILE_SIZE = Max * 1024 * 1024 // 最大上传限制
+
+/**
+ * 文件上传Hook
+ */
+export const useUpload = () => {
+ const isUploading = ref(false) // 是否正在上传
+ const progress = ref(0) // 进度
+ const fileInfo = ref(null) // 文件信息
+
+ const { on: onChange, trigger } = createEventHook()
+ const onStart = createEventHook()
+
+ /**
+ * 上传文件
+ * @param url 上传链接
+ * @param file 文件
+ */
+ const upload = async (url: string, file: File) => {
+ isUploading.value = true
+
+ const xhr = new XMLHttpRequest()
+ xhr.open('PUT', url, true)
+ xhr.setRequestHeader('Content-Type', file.type)
+ xhr.upload.onprogress = function (e) {
+ progress.value = Math.round((e.loaded / e.total) * 100)
+ }
+ xhr.onload = function () {
+ isUploading.value = false
+ if (xhr.status === 200) {
+ trigger('success')
+ } else {
+ trigger('fail')
+ }
+ }
+ xhr.send(file)
+ }
+
+ /**
+ * 获取图片宽高
+ */
+ const getImgWH = (file: File) => {
+ const img = new Image()
+ const tempUrl = URL.createObjectURL(file)
+ img.src = tempUrl
+ return new Promise((resolve, reject) => {
+ img.onload = function () {
+ resolve({ width: img.width, height: img.height, tempUrl })
+ }
+ img.onerror = function () {
+ URL.revokeObjectURL(tempUrl) // 释放临时URL资源
+ reject({ width: 0, height: 0, url: null })
+ }
+ })
+ }
+
+ /**
+ * 获取音频时长
+ */
+ const getAudioDuration = (file: File) => {
+ return new Promise((resolve, reject) => {
+ const audio = new Audio()
+ const tempUrl = URL.createObjectURL(file)
+ audio.src = tempUrl
+ // 计算音频的时长
+ const countAudioTime = async () => {
+ while (isNaN(audio.duration) || audio.duration === Infinity) {
+ // 防止浏览器卡死
+ await new Promise((resolve) => setTimeout(resolve, 100))
+ // 随机进度条位置
+ audio.currentTime = 10000000 * Math.random()
+ }
+ // 取整
+ const second = Math.round(audio.duration || 0)
+ resolve({ second, tempUrl })
+ }
+ countAudioTime()
+ audio.onerror = function () {
+ reject({ second: 0, tempUrl })
+ }
+ })
+ }
+
+ /**
+ * 解析文件
+ * @param file 文件
+ * @returns 文件大小、文件类型、文件名、文件后缀...
+ */
+ const parseFile = async (file: File, addParams: Record = {}) => {
+ const { name, size, type } = file
+ const suffix = name.split('.').pop() || ''
+ const baseInfo = { name, size, type, suffix, ...addParams }
+
+ if (type.includes('image')) {
+ const { width, height, tempUrl } = (await getImgWH(file)) as any
+ return { ...baseInfo, suffix, width, height, tempUrl }
+ }
+
+ if (type.includes('audio')) {
+ const { second, tempUrl } = (await getAudioDuration(file)) as any
+ return { second, tempUrl, ...baseInfo }
+ }
+
+ return baseInfo
+ }
+
+ /**
+ * 上传文件
+ * @param file 文件
+ */
+ const uploadFile = async (file: File, addParams?: Record) => {
+ if (isUploading.value) return
+ const info = await parseFile(file, addParams)
+
+ // 限制文件大小
+ if (info.size > MAX_FILE_SIZE) {
+ ElMessage.warning(`文件不得大于 ${Max} MB`)
+ return
+ }
+
+ const { downloadUrl, uploadUrl } = await apis
+ .getUploadUrl({ fileName: info.name, scene: '1' })
+ .send()
+
+ if (uploadUrl && downloadUrl) {
+ fileInfo.value = { ...info, downloadUrl }
+ onStart.trigger(fileInfo)
+ upload(uploadUrl, file)
+ } else {
+ trigger('fail')
+ }
+ }
+
+ return {
+ fileInfo,
+ isUploading,
+ progress,
+ onStart: onStart.on,
+ onChange,
+ uploadFile,
+ }
+}
diff --git a/src/main.ts b/src/main.ts
index c184d80f..dfd35626 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -1,33 +1,25 @@
import { createApp } from 'vue'
+import { createPinia } from 'pinia'
import dayjs from 'dayjs'
-import 'dayjs/locale/zh-cn'
import weekday from 'dayjs/plugin/weekday'
-import '@imengyu/vue3-context-menu/lib/vue3-context-menu.css'
-// import 'element-plus/dist/index.css'
-import { createPinia } from 'pinia'
+import vLogin from './directives/v-login'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
-
-import App from './App.vue'
import router from './router'
+import App from './App.vue'
+import 'dayjs/locale/zh-cn'
-import '@/utils/websocket'
import './styles/main.css'
-import vLogin from './directives/v-login'
-
-// 设置 dayjs 语言
-dayjs.locale('zh-cn')
-// 设置一周起始位周一
-dayjs.extend(weekday)
+import '@/utils/websocket'
+import '@imengyu/vue3-context-menu/lib/vue3-context-menu.css'
-const app = createApp(App)
+dayjs.locale('zh-cn') // 设置 dayjs 语言
+dayjs.extend(weekday) // 设置一周起始位周一
-// app.use(ElementPlus)
const pinia = createPinia()
-pinia.use(piniaPluginPersistedstate)
+pinia.use(piniaPluginPersistedstate) // 数据持久化
+
+const app = createApp(App)
app.use(pinia)
app.use(router)
-
-// 没登录就要求先登录的指令。
-app.directive('login', vLogin)
-
+app.directive('login', vLogin) // 登录权限指令-未登录先登录
app.mount('#app')
diff --git a/src/services/apis.ts b/src/services/apis.ts
index f3deeaee..4857d90c 100644
--- a/src/services/apis.ts
+++ b/src/services/apis.ts
@@ -51,4 +51,7 @@ export default {
recallMsg: (data: { msgId: number; roomId: number }) => putRequest(urls.recallMsg, data),
/** 拉黑用户 */
blockUser: (data: { uid: number }) => putRequest(urls.blockUser, data),
+ /** 获取临时上传链接 */
+ getUploadUrl: (params: any) =>
+ getRequest<{ downloadUrl: string; uploadUrl: string }>(urls.fileUpload, { params }),
}
diff --git a/src/services/types.ts b/src/services/types.ts
index cd964ce7..a4fa225d 100644
--- a/src/services/types.ts
+++ b/src/services/types.ts
@@ -3,6 +3,7 @@
* 注意:请使用TSDoc规范进行注释,以便在使用时能够获得良好提示。
* @see TSDoc规范https://tsdoc.org/
**/
+import type { OnlineEnum, MsgEnum, ActEnum, SexEnum, IsYetEnum, MarkEnum } from '@/enums'
/***/
export type ListResponse = {
@@ -13,11 +14,6 @@ export type ListResponse = {
list: T[]
}
-export enum OnlineStatus {
- Online = 1,
- Offline,
-}
-
export type CacheBadgeReq = {
/** 最后更新时间 更新超过 10 分钟异步去更新。 */
lastModifyTime?: number
@@ -68,7 +64,7 @@ export type CacheUserItem = {
export type UserItem = {
/** 在线状态 */
- activeStatus: OnlineStatus
+ activeStatus: OnlineEnum
/** 头像 */
avatar: string
/** 最后一次上下线时间 */
@@ -99,36 +95,15 @@ export type MessageReplyType = {
username: string
}
-export enum ActType {
- /** 确认 */
- Confirm = 1,
- /** 取消 */
- Cancel,
-}
-export enum MarkType {
- Like = 1,
- DisLike,
-}
-
export type MarkMsgReq = {
// actType 动作类型 1确认 2取消
- actType: ActType
+ actType: ActEnum
// 标记类型 1点赞 2举报
- markType: MarkType
+ markType: MarkEnum
// 消息 ID
msgId: number
}
-export enum SexType {
- Man = 1,
- Female,
-}
-
-export enum PowerType {
- User,
- Admin,
-}
-
export type UserInfoType = {
/** 用户唯一标识 */
uid: number
@@ -139,19 +114,13 @@ export type UserInfoType = {
/** 剩余改名次数 */
modifyNameChance: number
/** 性别 1为男性,2为女性 */
- sex: SexType
+ sex: SexEnum
/** 徽章,本地字段,有值用本地,无值用远端 */
badge?: string
/** 权限 */
power?: number
}
-// 是否拥有 0否 1是
-export enum IsYet {
- No,
- Yes,
-}
-
export type BadgeType = {
// 徽章描述
describe: string
@@ -160,9 +129,9 @@ export type BadgeType = {
// 徽章图标
img: string
// 是否拥有 0否 1是
- obtain: IsYet
+ obtain: IsYetEnum
// 是否佩戴 0否 1是
- wearing: IsYet
+ wearing: IsYetEnum
}
export type MarkItemType = {
@@ -171,11 +140,11 @@ export type MarkItemType = {
/** 消息id */
msgId: number
/** 操作类型 1点赞 2举报 */
- markType: MarkType
+ markType: MarkEnum
/** 数量 */
markCount: number
/** 动作类型 1确认 2取消 */
- actType: ActType
+ actType: ActEnum
}
export type RevokedMsgType = {
@@ -187,13 +156,6 @@ export type RevokedMsgType = {
recallUid?: number
}
-export enum MsgTypeType {
- /** 文本 */
- Text = 1,
- /** 撤回 */
- Recall,
-}
-
// -------------------- ⬇消息体类型定义⬇ ----------------
/**
@@ -208,6 +170,8 @@ export type MessageType = {
sendTime: string
/** 时间段(可选) */
timeBlock?: string
+ /** 是否加载中 */
+ loading?: boolean
}
/**
@@ -245,44 +209,75 @@ export type MessageMarkType = {
dislikeCount: number
}
+/** 图片消息体 */
+export type ImageBody = {
+ size: number
+ url: string
+ width: number
+ height: number
+}
+/** 语音消息体 */
+export type VoiceBody = {
+ size: number
+ second: number
+ url: string
+}
+/** 视频 */
+export type VideoBody = {
+ size: number
+ url: string
+ thumbWidth?: number
+ thumbHeight?: number
+ thumbUrl?: string
+}
+/** 文件消息体 */
+export type FileBody = {
+ size: number
+ fileName: string
+ url: string
+}
+/** 文本消息体 */
+export type TextBody = {
+ /** 消息内容 */
+ content: string
+ /** 回复 */
+ reply: ReplyType
+ /**
+ * 消息链接映射
+ * @deprecated 即将废弃?
+ */
+ urlTitleMap: Record
+}
+
/**
* 消息内容
*/
export type MsgType = {
+ /** 消息ID */
id: number
- type: number
- /** TODO:不同消息类型不同Body 未来后端增加其它类型后变更为`联合类型`, `条件类型` */
- body: BodyType | any
+ /** 消息类型 */
+ type: MsgEnum
+ /** 动态消息体-`根据消息类型变化` */
+ body: TextBody | ImageBody | VoiceBody | VideoBody | FileBody | any
+ /** 发送时间戳 */
sendTime: number
+ /** 消息互动信息 */
messageMark: MessageMarkType
}
-/**
- * 消息
- */
-export type BodyType = {
- /** 消息内容 */
- content: string
- /** 回复 */
- reply: {
- id: number
- username: string
- type: number
- /** 根据不同类型回复的消息展示也不同-`过渡版` */
- body: any
- /**
- * 是否可消息跳转
- * @enum {number} `0`否 `1`是
- */
- canCallback: number
- /** 跳转间隔的消息条数 */
- gapCount: number
- }
+export type ReplyType = {
+ id: number
+ username: string
+ type: MsgEnum
+ /** 根据不同类型回复的消息展示也不同-`过渡版` */
+ body: any
/**
- * 消息链接映射
- * @deprecated 即将废弃?
+ * 是否可消息跳转
+ * @enum {number} `0`否 `1`是
*/
- urlTitleMap: Record
+ canCallback: number
+ /** 跳转间隔的消息条数 */
+ gapCount: number
}
/**
@@ -292,12 +287,14 @@ export type MessageReq = {
/** 会话id */
roomId: number
/** 消息类型 */
- msgType: number
+ msgType: MsgEnum
/** 消息体 */
body: {
/** 文本消息内容 */
- content: string
+ content?: string
/** 回复的消息id */
replyMsgId?: number
+ /** 任意 */
+ [key: string]: any
}
}
diff --git a/src/services/urls.ts b/src/services/urls.ts
index 97f970d9..9d0dbecc 100644
--- a/src/services/urls.ts
+++ b/src/services/urls.ts
@@ -23,4 +23,6 @@ export default {
blockUser: `${prefix}/capi/user/black`,
// 撤回消息
recallMsg: `${prefix}/capi/chat/msg/recall`,
+ // 文件上传
+ fileUpload: `${prefix}/capi/oss/upload/url`,
}
diff --git a/src/stores/chat.ts b/src/stores/chat.ts
index 5fd13ebe..59ca79f4 100644
--- a/src/stores/chat.ts
+++ b/src/stores/chat.ts
@@ -2,7 +2,7 @@ import { ref, reactive, computed } from 'vue'
import { defineStore } from 'pinia'
import apis from '@/services/apis'
import type { MessageType, MarkItemType, RevokedMsgType, CacheUserReq } from '@/services/types'
-import { MarkType } from '@/services/types'
+import { MarkEnum } from '@/enums'
import { computedTimeBlock } from '@/utils/computedTime'
import { useCachedStore } from '@/stores/cached'
import { useUserStore } from '@/stores/user'
@@ -150,9 +150,9 @@ export const useChatStore = defineStore('chat', () => {
const msgItem = messageMap.get(msgId)
if (msgItem) {
- if (markType === MarkType.Like) {
+ if (markType === MarkEnum.LIKE) {
msgItem.message.messageMark.likeCount = markCount
- } else if (markType === MarkType.DisLike) {
+ } else if (markType === MarkEnum.DISLIKE) {
msgItem.message.messageMark.dislikeCount = markCount
}
}
@@ -164,7 +164,9 @@ export const useChatStore = defineStore('chat', () => {
const message = messageMap.get(msgId)
if (message) {
message.message.type = 2
- message.message.body = `撤回了一条消息` // 后期根据本地用户数据修改
+ const userName = message.fromUser.username || ''
+
+ message.message.body = `"${userName}"撤回了一条消息` // 后期根据本地用户数据修改
}
// 更新与这条撤回消息有关的消息
const messageList = replyMapping.get(msgId)
@@ -179,6 +181,11 @@ export const useChatStore = defineStore('chat', () => {
const deleteMsg = (msgId: number) => {
messageMap.delete(msgId)
}
+ // 更新消息
+ const updateMsg = (msgId: number, newMessage: MessageType) => {
+ deleteMsg(msgId)
+ pushMsg(newMessage)
+ }
return {
getMsgIndex,
@@ -188,6 +195,7 @@ export const useChatStore = defineStore('chat', () => {
clearNewMsgCount,
updateMarkCount,
updateRecallStatus,
+ updateMsg,
chatListToBottomAction,
newMsgCount,
isLoading,
diff --git a/src/stores/group.ts b/src/stores/group.ts
index a20f285c..cdb50a62 100644
--- a/src/stores/group.ts
+++ b/src/stores/group.ts
@@ -5,27 +5,18 @@ import { useCachedStore } from '@/stores/cached'
import type { UserItem } from '@/services/types'
import { pageSize } from './chat'
import cloneDeep from 'lodash/cloneDeep'
-import { OnlineStatus } from '@/services/types'
+import { OnlineEnum } from '@/enums'
import type { CacheUserReq } from '@/services/types'
import { uniqueUserList } from '@/utils/unique'
const sorAction = (pre: UserItem, next: UserItem) => {
- if (pre.activeStatus === OnlineStatus.Online && next.activeStatus === OnlineStatus.Online) {
+ if (pre.activeStatus === OnlineEnum.ONLINE && next.activeStatus === OnlineEnum.ONLINE) {
return next.lastOptTime < pre.lastOptTime ? -1 : 1
- } else if (
- pre.activeStatus !== OnlineStatus.Online &&
- next.activeStatus !== OnlineStatus.Online
- ) {
+ } else if (pre.activeStatus !== OnlineEnum.ONLINE && next.activeStatus !== OnlineEnum.ONLINE) {
return next.lastOptTime < pre.lastOptTime ? -1 : 1
- } else if (
- pre.activeStatus === OnlineStatus.Online &&
- next.activeStatus !== OnlineStatus.Online
- ) {
+ } else if (pre.activeStatus === OnlineEnum.ONLINE && next.activeStatus !== OnlineEnum.ONLINE) {
return -1
- } else if (
- pre.activeStatus !== OnlineStatus.Online &&
- next.activeStatus === OnlineStatus.Online
- ) {
+ } else if (pre.activeStatus !== OnlineEnum.ONLINE && next.activeStatus === OnlineEnum.ONLINE) {
return 1
} else {
return next.lastOptTime < pre.lastOptTime ? -1 : 1
diff --git a/src/styles/base.css b/src/styles/base.css
index aabddbab..0977ce19 100644
--- a/src/styles/base.css
+++ b/src/styles/base.css
@@ -87,14 +87,34 @@ a:active {
cursor: pointer;
}
-.msg-content-link {
- display: inline-block;
- margin-right: 4px;
- margin-left: 4px;
- font-size: 14px;
+.ellipsis-1 {
+ display: -webkit-box;
+ -webkit-box-orient: vertical;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ -webkit-line-clamp: 1;
+ line-clamp: 1;
}
-.msg-content-link:hover {
- color: currentcolor;
- text-decoration: underline;
+.ellipsis-2 {
+ display: -webkit-box;
+ -webkit-box-orient: vertical;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ -webkit-line-clamp: 2;
+ line-clamp: 2;
+}
+
+.icon-spin {
+ animation: icon-loading 1s infinite cubic-bezier(0, 0, 1, 1);
+}
+
+@keyframes icon-loading {
+ 0% {
+ transform: rotate(0);
+ }
+
+ 100% {
+ transform: rotate(360deg);
+ }
}
diff --git a/src/styles/main.css b/src/styles/main.css
index d85f5448..60328265 100644
--- a/src/styles/main.css
+++ b/src/styles/main.css
@@ -1,4 +1,6 @@
@import './base.css';
+@import '@/assets/iconfont/index.css';
+@import '@/assets/iconfont/index-color.css';
#app {
min-height: 100vh;
diff --git a/src/utils/index.ts b/src/utils/index.ts
new file mode 100644
index 00000000..257d004c
--- /dev/null
+++ b/src/utils/index.ts
@@ -0,0 +1,93 @@
+import { MsgEnum } from '@/enums'
+/**
+ * 文件大小格式化
+ */
+export const formatBytes = (bytes: number): string => {
+ if (bytes === 0 || !bytes) {
+ return '0 B'
+ }
+
+ const units = ['B', 'KB', 'MB', 'GB', 'TB']
+ const base = 1024
+ const unitIndex = Math.floor(Math.log(bytes) / Math.log(base))
+ const size = parseFloat((bytes / Math.pow(base, unitIndex)).toFixed(2))
+
+ return size + ' ' + units[unitIndex]
+}
+
+/** 注意!这是文件图标映射关系表,如有修改需求-请联系前端管理同学 */
+const fileSuffixMap: Record = {
+ 'jpg': 'jpg',
+ 'jpeg': 'jpg',
+ 'png': 'jpg',
+ 'webp': 'jpg',
+ 'mp4': 'mp4',
+ 'mov': 'mp4',
+ 'avi': 'mp4',
+ 'rmvb': 'mp4',
+ 'doc': 'doc',
+ 'docx': 'doc',
+ 'mp3': 'mp3',
+ 'wav': 'mp3',
+ 'aac': 'mp3',
+ 'flac': 'mp3',
+ 'pdf': 'pdf',
+ 'ppt': 'ppt',
+ 'pptx': 'ppt',
+ 'xls': 'xls',
+ 'xlsx': 'xls',
+ 'zip': 'zip',
+ 'rar': 'zip',
+ '7z': 'zip',
+ 'txt': 'txt',
+}
+/**
+ * 获取文件对应的Icon
+ * @param fileName 文件名
+ * @returns Icon
+ */
+export const getFileSuffix = (fileName: string): string => {
+ if (!fileName) return 'other'
+
+ const suffix = fileName.toLowerCase().split('.').pop()
+ if (!suffix) return 'other'
+
+ return fileSuffixMap[suffix] || 'other'
+}
+
+/**
+ * 转换文件类型
+ * @param suffix 文件后缀
+ */
+export const convertFileType = (suffix: string) => {
+ if (['png', 'jpg', 'jpeg', 'gif', 'webp'].includes(suffix)) {
+ return MsgEnum.IMAGE
+ } else if ([''].includes(suffix)) {
+ // TODO: 视频消息 暂不支持-所有视频消息都会被当做文件处理
+ return MsgEnum.VIDEO
+ } else if (['mp3', 'wav'].includes(suffix)) {
+ return MsgEnum.VOICE
+ } else {
+ return MsgEnum.FILE
+ }
+}
+
+// 生成消息体
+export const generateBody = (fileInfo: any, isMock?: boolean) => {
+ const { size, suffix, width, height, downloadUrl, name, second, tempUrl } = fileInfo
+ const type = convertFileType(suffix)
+ const url = isMock ? tempUrl : downloadUrl
+ const baseBody = { size, url }
+ let body = {}
+
+ if (type === MsgEnum.IMAGE) {
+ body = { ...baseBody, width, height }
+ } else if (type === MsgEnum.VOICE) {
+ body = { ...baseBody, second }
+ } else if (type === MsgEnum.VIDEO) {
+ body = { ...baseBody, thumbWidth: null, thumbHeight: null, thumbUrl: null }
+ } else if (type === MsgEnum.FILE) {
+ body = { ...baseBody, fileName: name, url: downloadUrl }
+ }
+ return { body, type }
+}
diff --git a/src/utils/websocket.ts b/src/utils/websocket.ts
index 0e89ea11..20b363b6 100644
--- a/src/utils/websocket.ts
+++ b/src/utils/websocket.ts
@@ -11,7 +11,7 @@ import type {
OnStatusChangeType,
} from './wsType'
import type { MessageType, MarkItemType, RevokedMsgType } from '@/services/types'
-import { OnlineStatus } from '@/services/types'
+import { OnlineEnum } from '@/enums'
import { computedToken } from '@/services/request'
import { worker } from './initWorker'
import shakeTitle from '@/utils/shakeTitle'
@@ -149,7 +149,7 @@ class WS {
// 自己更新自己上线
groupStore.batchUpdateUserStatus([
{
- activeStatus: OnlineStatus.Online,
+ activeStatus: OnlineEnum.ONLINE,
avatar: rest.avatar,
lastOptTime: Date.now(),
name: rest.name,
diff --git a/src/views/Home/components/ChatBox/MsgInput/index.vue b/src/views/Home/components/ChatBox/MsgInput/index.vue
index 93e6c86d..1c84b525 100644
--- a/src/views/Home/components/ChatBox/MsgInput/index.vue
+++ b/src/views/Home/components/ChatBox/MsgInput/index.vue
@@ -1,6 +1,6 @@
-