Merge remote-tracking branch 'origin/feature/0902_antd组件替换' into test
# Conflicts: # src/App.vue # src/components/common-select/index.vue # src/layouts/components/navbar/components/task-center-modal/components/import-task/index.vue # src/views/components/login/index.vue # src/views/home/components/history-conversation-drawer/index.vue # src/views/login/style.scss # src/views/property-marketing/media-account/account-manage/components/add-account-modal/index.vue
This commit is contained in:
@ -1,6 +1,6 @@
|
||||
## template-admin-ts
|
||||
|
||||
基于 Arco Design Pro 的中后台管理模板
|
||||
基于 Ant Design Vue 的中后台管理模板
|
||||
|
||||
- vite 4.x
|
||||
- vue 3.x
|
||||
@ -10,7 +10,7 @@
|
||||
- axios
|
||||
- dayjs
|
||||
- lodash
|
||||
- arco-design
|
||||
- ant-design-vue
|
||||
- less
|
||||
- eslint + prettier
|
||||
|
||||
@ -183,7 +183,7 @@ export { default as Comp } from './comp/index.vue';
|
||||
</template>
|
||||
```
|
||||
|
||||
### `ui` 框架 [`ArcoVue`](https://arco.design/vue/docs/start)
|
||||
### `ui` 框架 [`Ant Design Vue`](https://antdv.com/)
|
||||
|
||||
- 自动导入组件,无需再次导入
|
||||
- 图标库已配置,同组件使用
|
||||
|
||||
@ -39,6 +39,7 @@
|
||||
"vue-draggable-next": "^2.2.1",
|
||||
"vue-draggable-plus": "^0.6.0",
|
||||
"vue-echarts": "^7.0.3",
|
||||
"vue-lazyload": "^3.0.0",
|
||||
"vue-router": "^4.4.0",
|
||||
"vuedraggable": "^4.1.0"
|
||||
},
|
||||
|
||||
108
pnpm-lock.yaml
generated
108
pnpm-lock.yaml
generated
@ -92,6 +92,9 @@ importers:
|
||||
vue-echarts:
|
||||
specifier: ^7.0.3
|
||||
version: 7.0.3(@vue/runtime-core@3.5.18)(echarts@5.6.0)(vue@3.5.18(typescript@4.9.5))
|
||||
vue-lazyload:
|
||||
specifier: ^3.0.0
|
||||
version: 3.0.0
|
||||
vue-router:
|
||||
specifier: ^4.4.0
|
||||
version: 4.5.1(vue@3.5.18(typescript@4.9.5))
|
||||
@ -447,133 +450,133 @@ packages:
|
||||
resolution: {integrity: sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==}
|
||||
|
||||
'@esbuild/android-arm64@0.16.17':
|
||||
resolution: {integrity: sha512-MIGl6p5sc3RDTLLkYL1MyL8BMRN4tLMRCn+yRJJmEDvYZ2M7tmAf80hx1kbNEUX2KJ50RRtxZ4JHLvCfuB6kBg==}
|
||||
resolution: {integrity: sha512-MIGl6p5sc3RDTLLkYL1MyL8BMRN4tLMRCn+yRJJmEDvYZ2M7tmAf80hx1kbNEUX2KJ50RRtxZ4JHLvCfuB6kBg==, tarball: https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.16.17.tgz}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [arm64]
|
||||
os: [android]
|
||||
|
||||
'@esbuild/android-arm@0.16.17':
|
||||
resolution: {integrity: sha512-N9x1CMXVhtWEAMS7pNNONyA14f71VPQN9Cnavj1XQh6T7bskqiLLrSca4O0Vr8Wdcga943eThxnVp3JLnBMYtw==}
|
||||
resolution: {integrity: sha512-N9x1CMXVhtWEAMS7pNNONyA14f71VPQN9Cnavj1XQh6T7bskqiLLrSca4O0Vr8Wdcga943eThxnVp3JLnBMYtw==, tarball: https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.16.17.tgz}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [arm]
|
||||
os: [android]
|
||||
|
||||
'@esbuild/android-x64@0.16.17':
|
||||
resolution: {integrity: sha512-a3kTv3m0Ghh4z1DaFEuEDfz3OLONKuFvI4Xqczqx4BqLyuFaFkuaG4j2MtA6fuWEFeC5x9IvqnX7drmRq/fyAQ==}
|
||||
resolution: {integrity: sha512-a3kTv3m0Ghh4z1DaFEuEDfz3OLONKuFvI4Xqczqx4BqLyuFaFkuaG4j2MtA6fuWEFeC5x9IvqnX7drmRq/fyAQ==, tarball: https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.16.17.tgz}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [x64]
|
||||
os: [android]
|
||||
|
||||
'@esbuild/darwin-arm64@0.16.17':
|
||||
resolution: {integrity: sha512-/2agbUEfmxWHi9ARTX6OQ/KgXnOWfsNlTeLcoV7HSuSTv63E4DqtAc+2XqGw1KHxKMHGZgbVCZge7HXWX9Vn+w==}
|
||||
resolution: {integrity: sha512-/2agbUEfmxWHi9ARTX6OQ/KgXnOWfsNlTeLcoV7HSuSTv63E4DqtAc+2XqGw1KHxKMHGZgbVCZge7HXWX9Vn+w==, tarball: https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.16.17.tgz}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@esbuild/darwin-x64@0.16.17':
|
||||
resolution: {integrity: sha512-2By45OBHulkd9Svy5IOCZt376Aa2oOkiE9QWUK9fe6Tb+WDr8hXL3dpqi+DeLiMed8tVXspzsTAvd0jUl96wmg==}
|
||||
resolution: {integrity: sha512-2By45OBHulkd9Svy5IOCZt376Aa2oOkiE9QWUK9fe6Tb+WDr8hXL3dpqi+DeLiMed8tVXspzsTAvd0jUl96wmg==, tarball: https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.16.17.tgz}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@esbuild/freebsd-arm64@0.16.17':
|
||||
resolution: {integrity: sha512-mt+cxZe1tVx489VTb4mBAOo2aKSnJ33L9fr25JXpqQqzbUIw/yzIzi+NHwAXK2qYV1lEFp4OoVeThGjUbmWmdw==}
|
||||
resolution: {integrity: sha512-mt+cxZe1tVx489VTb4mBAOo2aKSnJ33L9fr25JXpqQqzbUIw/yzIzi+NHwAXK2qYV1lEFp4OoVeThGjUbmWmdw==, tarball: https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.16.17.tgz}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [arm64]
|
||||
os: [freebsd]
|
||||
|
||||
'@esbuild/freebsd-x64@0.16.17':
|
||||
resolution: {integrity: sha512-8ScTdNJl5idAKjH8zGAsN7RuWcyHG3BAvMNpKOBaqqR7EbUhhVHOqXRdL7oZvz8WNHL2pr5+eIT5c65kA6NHug==}
|
||||
resolution: {integrity: sha512-8ScTdNJl5idAKjH8zGAsN7RuWcyHG3BAvMNpKOBaqqR7EbUhhVHOqXRdL7oZvz8WNHL2pr5+eIT5c65kA6NHug==, tarball: https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.16.17.tgz}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [x64]
|
||||
os: [freebsd]
|
||||
|
||||
'@esbuild/linux-arm64@0.16.17':
|
||||
resolution: {integrity: sha512-7S8gJnSlqKGVJunnMCrXHU9Q8Q/tQIxk/xL8BqAP64wchPCTzuM6W3Ra8cIa1HIflAvDnNOt2jaL17vaW+1V0g==}
|
||||
resolution: {integrity: sha512-7S8gJnSlqKGVJunnMCrXHU9Q8Q/tQIxk/xL8BqAP64wchPCTzuM6W3Ra8cIa1HIflAvDnNOt2jaL17vaW+1V0g==, tarball: https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.16.17.tgz}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-arm@0.16.17':
|
||||
resolution: {integrity: sha512-iihzrWbD4gIT7j3caMzKb/RsFFHCwqqbrbH9SqUSRrdXkXaygSZCZg1FybsZz57Ju7N/SHEgPyaR0LZ8Zbe9gQ==}
|
||||
resolution: {integrity: sha512-iihzrWbD4gIT7j3caMzKb/RsFFHCwqqbrbH9SqUSRrdXkXaygSZCZg1FybsZz57Ju7N/SHEgPyaR0LZ8Zbe9gQ==, tarball: https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.16.17.tgz}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-ia32@0.16.17':
|
||||
resolution: {integrity: sha512-kiX69+wcPAdgl3Lonh1VI7MBr16nktEvOfViszBSxygRQqSpzv7BffMKRPMFwzeJGPxcio0pdD3kYQGpqQ2SSg==}
|
||||
resolution: {integrity: sha512-kiX69+wcPAdgl3Lonh1VI7MBr16nktEvOfViszBSxygRQqSpzv7BffMKRPMFwzeJGPxcio0pdD3kYQGpqQ2SSg==, tarball: https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.16.17.tgz}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [ia32]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-loong64@0.16.17':
|
||||
resolution: {integrity: sha512-dTzNnQwembNDhd654cA4QhbS9uDdXC3TKqMJjgOWsC0yNCbpzfWoXdZvp0mY7HU6nzk5E0zpRGGx3qoQg8T2DQ==}
|
||||
resolution: {integrity: sha512-dTzNnQwembNDhd654cA4QhbS9uDdXC3TKqMJjgOWsC0yNCbpzfWoXdZvp0mY7HU6nzk5E0zpRGGx3qoQg8T2DQ==, tarball: https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.16.17.tgz}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [loong64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-mips64el@0.16.17':
|
||||
resolution: {integrity: sha512-ezbDkp2nDl0PfIUn0CsQ30kxfcLTlcx4Foz2kYv8qdC6ia2oX5Q3E/8m6lq84Dj/6b0FrkgD582fJMIfHhJfSw==}
|
||||
resolution: {integrity: sha512-ezbDkp2nDl0PfIUn0CsQ30kxfcLTlcx4Foz2kYv8qdC6ia2oX5Q3E/8m6lq84Dj/6b0FrkgD582fJMIfHhJfSw==, tarball: https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.16.17.tgz}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [mips64el]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-ppc64@0.16.17':
|
||||
resolution: {integrity: sha512-dzS678gYD1lJsW73zrFhDApLVdM3cUF2MvAa1D8K8KtcSKdLBPP4zZSLy6LFZ0jYqQdQ29bjAHJDgz0rVbLB3g==}
|
||||
resolution: {integrity: sha512-dzS678gYD1lJsW73zrFhDApLVdM3cUF2MvAa1D8K8KtcSKdLBPP4zZSLy6LFZ0jYqQdQ29bjAHJDgz0rVbLB3g==, tarball: https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.16.17.tgz}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-riscv64@0.16.17':
|
||||
resolution: {integrity: sha512-ylNlVsxuFjZK8DQtNUwiMskh6nT0vI7kYl/4fZgV1llP5d6+HIeL/vmmm3jpuoo8+NuXjQVZxmKuhDApK0/cKw==}
|
||||
resolution: {integrity: sha512-ylNlVsxuFjZK8DQtNUwiMskh6nT0vI7kYl/4fZgV1llP5d6+HIeL/vmmm3jpuoo8+NuXjQVZxmKuhDApK0/cKw==, tarball: https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.16.17.tgz}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-s390x@0.16.17':
|
||||
resolution: {integrity: sha512-gzy7nUTO4UA4oZ2wAMXPNBGTzZFP7mss3aKR2hH+/4UUkCOyqmjXiKpzGrY2TlEUhbbejzXVKKGazYcQTZWA/w==}
|
||||
resolution: {integrity: sha512-gzy7nUTO4UA4oZ2wAMXPNBGTzZFP7mss3aKR2hH+/4UUkCOyqmjXiKpzGrY2TlEUhbbejzXVKKGazYcQTZWA/w==, tarball: https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.16.17.tgz}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-x64@0.16.17':
|
||||
resolution: {integrity: sha512-mdPjPxfnmoqhgpiEArqi4egmBAMYvaObgn4poorpUaqmvzzbvqbowRllQ+ZgzGVMGKaPkqUmPDOOFQRUFDmeUw==}
|
||||
resolution: {integrity: sha512-mdPjPxfnmoqhgpiEArqi4egmBAMYvaObgn4poorpUaqmvzzbvqbowRllQ+ZgzGVMGKaPkqUmPDOOFQRUFDmeUw==, tarball: https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.16.17.tgz}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/netbsd-x64@0.16.17':
|
||||
resolution: {integrity: sha512-/PzmzD/zyAeTUsduZa32bn0ORug+Jd1EGGAUJvqfeixoEISYpGnAezN6lnJoskauoai0Jrs+XSyvDhppCPoKOA==}
|
||||
resolution: {integrity: sha512-/PzmzD/zyAeTUsduZa32bn0ORug+Jd1EGGAUJvqfeixoEISYpGnAezN6lnJoskauoai0Jrs+XSyvDhppCPoKOA==, tarball: https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.16.17.tgz}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [x64]
|
||||
os: [netbsd]
|
||||
|
||||
'@esbuild/openbsd-x64@0.16.17':
|
||||
resolution: {integrity: sha512-2yaWJhvxGEz2RiftSk0UObqJa/b+rIAjnODJgv2GbGGpRwAfpgzyrg1WLK8rqA24mfZa9GvpjLcBBg8JHkoodg==}
|
||||
resolution: {integrity: sha512-2yaWJhvxGEz2RiftSk0UObqJa/b+rIAjnODJgv2GbGGpRwAfpgzyrg1WLK8rqA24mfZa9GvpjLcBBg8JHkoodg==, tarball: https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.16.17.tgz}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [x64]
|
||||
os: [openbsd]
|
||||
|
||||
'@esbuild/sunos-x64@0.16.17':
|
||||
resolution: {integrity: sha512-xtVUiev38tN0R3g8VhRfN7Zl42YCJvyBhRKw1RJjwE1d2emWTVToPLNEQj/5Qxc6lVFATDiy6LjVHYhIPrLxzw==}
|
||||
resolution: {integrity: sha512-xtVUiev38tN0R3g8VhRfN7Zl42YCJvyBhRKw1RJjwE1d2emWTVToPLNEQj/5Qxc6lVFATDiy6LjVHYhIPrLxzw==, tarball: https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.16.17.tgz}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [x64]
|
||||
os: [sunos]
|
||||
|
||||
'@esbuild/win32-arm64@0.16.17':
|
||||
resolution: {integrity: sha512-ga8+JqBDHY4b6fQAmOgtJJue36scANy4l/rL97W+0wYmijhxKetzZdKOJI7olaBaMhWt8Pac2McJdZLxXWUEQw==}
|
||||
resolution: {integrity: sha512-ga8+JqBDHY4b6fQAmOgtJJue36scANy4l/rL97W+0wYmijhxKetzZdKOJI7olaBaMhWt8Pac2McJdZLxXWUEQw==, tarball: https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.16.17.tgz}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
'@esbuild/win32-ia32@0.16.17':
|
||||
resolution: {integrity: sha512-WnsKaf46uSSF/sZhwnqE4L/F89AYNMiD4YtEcYekBt9Q7nj0DiId2XH2Ng2PHM54qi5oPrQ8luuzGszqi/veig==}
|
||||
resolution: {integrity: sha512-WnsKaf46uSSF/sZhwnqE4L/F89AYNMiD4YtEcYekBt9Q7nj0DiId2XH2Ng2PHM54qi5oPrQ8luuzGszqi/veig==, tarball: https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.16.17.tgz}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [ia32]
|
||||
os: [win32]
|
||||
|
||||
'@esbuild/win32-x64@0.16.17':
|
||||
resolution: {integrity: sha512-y+EHuSchhL7FjHgvQL/0fnnFmO4T1bhvWANX6gcnqTjtnKWbTvUMCpGnv2+t+31d7RzyEAYAd4u2fnIhHL6N/Q==}
|
||||
resolution: {integrity: sha512-y+EHuSchhL7FjHgvQL/0fnnFmO4T1bhvWANX6gcnqTjtnKWbTvUMCpGnv2+t+31d7RzyEAYAd4u2fnIhHL6N/Q==, tarball: https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.16.17.tgz}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
@ -654,85 +657,91 @@ packages:
|
||||
engines: {node: ^14.16.0 || ^16.10.0 || ^17.0.0 || ^18.0.0 || ^19.0.0}
|
||||
|
||||
'@parcel/watcher-android-arm64@2.5.1':
|
||||
resolution: {integrity: sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==}
|
||||
resolution: {integrity: sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==, tarball: https://registry.npmmirror.com/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [android]
|
||||
|
||||
'@parcel/watcher-darwin-arm64@2.5.1':
|
||||
resolution: {integrity: sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==}
|
||||
resolution: {integrity: sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==, tarball: https://registry.npmmirror.com/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@parcel/watcher-darwin-x64@2.5.1':
|
||||
resolution: {integrity: sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==}
|
||||
resolution: {integrity: sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==, tarball: https://registry.npmmirror.com/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@parcel/watcher-freebsd-x64@2.5.1':
|
||||
resolution: {integrity: sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==}
|
||||
resolution: {integrity: sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==, tarball: https://registry.npmmirror.com/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [x64]
|
||||
os: [freebsd]
|
||||
|
||||
'@parcel/watcher-linux-arm-glibc@2.5.1':
|
||||
resolution: {integrity: sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==}
|
||||
resolution: {integrity: sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==, tarball: https://registry.npmmirror.com/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@parcel/watcher-linux-arm-musl@2.5.1':
|
||||
resolution: {integrity: sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==}
|
||||
resolution: {integrity: sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==, tarball: https://registry.npmmirror.com/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@parcel/watcher-linux-arm64-glibc@2.5.1':
|
||||
resolution: {integrity: sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==}
|
||||
resolution: {integrity: sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==, tarball: https://registry.npmmirror.com/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@parcel/watcher-linux-arm64-musl@2.5.1':
|
||||
resolution: {integrity: sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==}
|
||||
resolution: {integrity: sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==, tarball: https://registry.npmmirror.com/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@parcel/watcher-linux-x64-glibc@2.5.1':
|
||||
resolution: {integrity: sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==}
|
||||
resolution: {integrity: sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==, tarball: https://registry.npmmirror.com/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@parcel/watcher-linux-x64-musl@2.5.1':
|
||||
resolution: {integrity: sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==}
|
||||
resolution: {integrity: sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==, tarball: https://registry.npmmirror.com/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@parcel/watcher-win32-arm64@2.5.1':
|
||||
resolution: {integrity: sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==}
|
||||
resolution: {integrity: sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==, tarball: https://registry.npmmirror.com/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
'@parcel/watcher-win32-ia32@2.5.1':
|
||||
resolution: {integrity: sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==}
|
||||
resolution: {integrity: sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==, tarball: https://registry.npmmirror.com/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [ia32]
|
||||
os: [win32]
|
||||
|
||||
'@parcel/watcher-win32-x64@2.5.1':
|
||||
resolution: {integrity: sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==}
|
||||
resolution: {integrity: sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==, tarball: https://registry.npmmirror.com/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@parcel/watcher@2.5.1':
|
||||
resolution: {integrity: sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==}
|
||||
resolution: {integrity: sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==, tarball: https://registry.npmmirror.com/@parcel/watcher/-/watcher-2.5.1.tgz}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
|
||||
'@polka/url@1.0.0-next.21':
|
||||
@ -791,13 +800,13 @@ packages:
|
||||
resolution: {integrity: sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==}
|
||||
|
||||
'@types/sortablejs@1.15.8':
|
||||
resolution: {integrity: sha512-b79830lW+RZfwaztgs1aVPgbasJ8e7AXtZYHTELNXZPsERt4ymJdjV4OccDbHQAvHrCcFpbF78jkm0R6h/pZVg==}
|
||||
resolution: {integrity: sha512-b79830lW+RZfwaztgs1aVPgbasJ8e7AXtZYHTELNXZPsERt4ymJdjV4OccDbHQAvHrCcFpbF78jkm0R6h/pZVg==, tarball: https://registry.npmmirror.com/@types/sortablejs/-/sortablejs-1.15.8.tgz}
|
||||
|
||||
'@types/svgo@2.6.4':
|
||||
resolution: {integrity: sha512-l4cmyPEckf8moNYHdJ+4wkHvFxjyW6ulm9l4YGaOxeyBWPhBOT0gvni1InpFPdzx1dKf/2s62qGITwxNWnPQng==}
|
||||
|
||||
'@types/trusted-types@2.0.7':
|
||||
resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
|
||||
resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==, tarball: https://registry.npmmirror.com/@types/trusted-types/-/trusted-types-2.0.7.tgz}
|
||||
|
||||
'@types/web-bluetooth@0.0.16':
|
||||
resolution: {integrity: sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ==}
|
||||
@ -1678,7 +1687,7 @@ packages:
|
||||
resolution: {integrity: sha512-CPB+UL9XMT/Av+pJxCKGhdx+yg1hzplvFJQlJ2n68PyQGMz9L/E2zCyLdOL8uasbouTUgnPl+y0tccI/se+BEw==}
|
||||
|
||||
canvg@3.0.11:
|
||||
resolution: {integrity: sha512-5ON+q7jCTgMp9cjpu4Jo6XbvfYwSB2Ow3kzHKfIyJfaCAOHLbdKPQqGKgfED/R5B+3TFFfe8pegYA+b423SRyA==}
|
||||
resolution: {integrity: sha512-5ON+q7jCTgMp9cjpu4Jo6XbvfYwSB2Ow3kzHKfIyJfaCAOHLbdKPQqGKgfED/R5B+3TFFfe8pegYA+b423SRyA==, tarball: https://registry.npmmirror.com/canvg/-/canvg-3.0.11.tgz}
|
||||
engines: {node: '>=10.0.0'}
|
||||
|
||||
chalk@1.1.3:
|
||||
@ -1868,7 +1877,7 @@ packages:
|
||||
resolution: {integrity: sha512-3DdaFaU/Zf1AnpLiFDeNCD4TOWe3Zl2RZaTzUvWiIk5ERzcCodOE20Vqq4fzCbNoHURFHT4/us/Lfq+S2zyY4w==}
|
||||
|
||||
core-js@3.45.0:
|
||||
resolution: {integrity: sha512-c2KZL9lP4DjkN3hk/an4pWn5b5ZefhRJnAc42n6LJ19kSnbeRbdQZE5dSeE2LBol1OwJD3X1BQvFTAsa8ReeDA==}
|
||||
resolution: {integrity: sha512-c2KZL9lP4DjkN3hk/an4pWn5b5ZefhRJnAc42n6LJ19kSnbeRbdQZE5dSeE2LBol1OwJD3X1BQvFTAsa8ReeDA==, tarball: https://registry.npmmirror.com/core-js/-/core-js-3.45.0.tgz}
|
||||
|
||||
core-util-is@1.0.3:
|
||||
resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==}
|
||||
@ -2188,7 +2197,7 @@ packages:
|
||||
engines: {node: '>=0.12'}
|
||||
|
||||
errno@0.1.8:
|
||||
resolution: {integrity: sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==}
|
||||
resolution: {integrity: sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==, tarball: https://registry.npmmirror.com/errno/-/errno-0.1.8.tgz}
|
||||
hasBin: true
|
||||
|
||||
error-ex@1.3.2:
|
||||
@ -2605,7 +2614,7 @@ packages:
|
||||
resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
|
||||
|
||||
fsevents@2.3.2:
|
||||
resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
|
||||
resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==, tarball: https://registry.npmmirror.com/fsevents/-/fsevents-2.3.2.tgz}
|
||||
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
||||
os: [darwin]
|
||||
|
||||
@ -2769,7 +2778,7 @@ packages:
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
graceful-fs@4.2.10:
|
||||
resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==}
|
||||
resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==, tarball: https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.10.tgz}
|
||||
|
||||
grapheme-splitter@1.0.4:
|
||||
resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==}
|
||||
@ -3707,7 +3716,7 @@ packages:
|
||||
resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==}
|
||||
|
||||
make-dir@2.1.0:
|
||||
resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==}
|
||||
resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==, tarball: https://registry.npmmirror.com/make-dir/-/make-dir-2.1.0.tgz}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
make-iterator@1.0.1:
|
||||
@ -3828,7 +3837,7 @@ packages:
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
mime@1.6.0:
|
||||
resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==}
|
||||
resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==, tarball: https://registry.npmmirror.com/mime/-/mime-1.6.0.tgz}
|
||||
engines: {node: '>=4'}
|
||||
hasBin: true
|
||||
|
||||
@ -3957,7 +3966,7 @@ packages:
|
||||
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
|
||||
|
||||
needle@3.2.0:
|
||||
resolution: {integrity: sha512-oUvzXnyLiVyVGoianLijF9O/RecZUf7TkBfimjGrLM4eQhXyeJwM6GeAWccwfQ9aa4gMCZKqhAOuLaMIcQxajQ==}
|
||||
resolution: {integrity: sha512-oUvzXnyLiVyVGoianLijF9O/RecZUf7TkBfimjGrLM4eQhXyeJwM6GeAWccwfQ9aa4gMCZKqhAOuLaMIcQxajQ==, tarball: https://registry.npmmirror.com/needle/-/needle-3.2.0.tgz}
|
||||
engines: {node: '>= 4.4.x'}
|
||||
hasBin: true
|
||||
|
||||
@ -4863,7 +4872,7 @@ packages:
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
source-map@0.6.1:
|
||||
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
|
||||
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==, tarball: https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
source-map@0.7.4:
|
||||
@ -5547,6 +5556,9 @@ packages:
|
||||
peerDependencies:
|
||||
eslint: '>=6.0.0'
|
||||
|
||||
vue-lazyload@3.0.0:
|
||||
resolution: {integrity: sha512-h2keL/Rj550dLgesgOtXJS9qOiSMmuJNeVlfNAYV1/IYwOQYaWk5mFJlwRxmZDK9YC5gECcFLYYj7z1lKSf9ug==, tarball: https://registry.npmmirror.com/vue-lazyload/-/vue-lazyload-3.0.0.tgz}
|
||||
|
||||
vue-router@4.5.1:
|
||||
resolution: {integrity: sha512-ogAF3P97NPm8fJsE4by9dwSYtDwXIY1nFY9T6DyQnGHd1E2Da94w9JIolpe42LJGIl0DwOHBi8TcRPlPGwbTtw==}
|
||||
peerDependencies:
|
||||
@ -12409,6 +12421,8 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
vue-lazyload@3.0.0: {}
|
||||
|
||||
vue-router@4.5.1(vue@3.5.18(typescript@4.9.5)):
|
||||
dependencies:
|
||||
'@vue/devtools-api': 6.6.4
|
||||
|
||||
16
src/App.vue
16
src/App.vue
@ -1,23 +1,25 @@
|
||||
<template>
|
||||
<a-config-provider :locale="zhCN" size="small" :theme="redTheme">
|
||||
<router-view v-if="$route.path === '/login' || ['ExploreList', 'ExploreDetail', 'Trial'].includes($route.name)" />
|
||||
<ConfigProvider :locale="zhCN" :theme="redTheme">
|
||||
<router-view v-if="$route.path === '/login' || ['ExploreList', 'ExploreDetail'].includes($route.name)" />
|
||||
<LayoutBasic v-else />
|
||||
</a-config-provider>
|
||||
</ConfigProvider>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useUserStore } from '@/stores';
|
||||
import { useChatStore } from '@/stores/modules/chat';
|
||||
// import { useChatStore } from '@/stores/modules/chat';
|
||||
|
||||
import { initApp } from '@/utils/user';
|
||||
import { useSidebarStore } from '@/stores/modules/side-bar';
|
||||
|
||||
import zhCN from '@arco-design/web-vue/es/locale/lang/zh-cn';
|
||||
import { ConfigProvider } from 'ant-design-vue';
|
||||
import zhCN from 'ant-design-vue/es/locale/zh_CN';
|
||||
import 'dayjs/locale/zh-cn';
|
||||
|
||||
const userStore = useUserStore();
|
||||
const route = useRoute();
|
||||
// const route = useRoute();
|
||||
const sidebarStore = useSidebarStore();
|
||||
const chatStore = useChatStore();
|
||||
// const chatStore = useChatStore();
|
||||
|
||||
const redTheme = {
|
||||
token: {
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
|
||||
import router from '@/router';
|
||||
import { clearToken } from '@/utils/auth';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
/**
|
||||
* 处理业务逻辑定义的错误code
|
||||
@ -28,5 +28,5 @@ export const handleCodeError = (error: any) => {
|
||||
default:
|
||||
errMessage = error.msg || `未知错误-${error.code}`;
|
||||
}
|
||||
Message.error(errMessage);
|
||||
message.error(errMessage);
|
||||
};
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
import axios from 'axios';
|
||||
import { message } from 'ant-design-vue';
|
||||
import type { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';
|
||||
import { handleUserLogout, goUserLogin } from '@/utils/user';
|
||||
import { useEnterpriseStore } from '@/stores/modules/enterprise';
|
||||
@ -84,7 +85,7 @@ export class Request {
|
||||
break;
|
||||
}
|
||||
|
||||
AMessage.error(errMessage);
|
||||
message.error(errMessage);
|
||||
return Promise.reject(err.response);
|
||||
},
|
||||
);
|
||||
|
||||
@ -1,2 +0,0 @@
|
||||
export { default as TabBar } from './tab-bar/index.vue';
|
||||
export { default as ModalSimple } from './modal/index.vue';
|
||||
|
||||
@ -1,32 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import type { Component, DefineComponent } from 'vue';
|
||||
|
||||
import IconHover from '@arco-design/web-vue/es/_components/icon-hover';
|
||||
|
||||
defineProps<{
|
||||
title?: string;
|
||||
content?: string | (() => DefineComponent | Component);
|
||||
}>();
|
||||
|
||||
defineEmits(['close']);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<slot name="header">
|
||||
<div class="flex justify-end mb7">
|
||||
<slot name="close">
|
||||
<icon-hover @click="$emit('close')">
|
||||
<icon-close />
|
||||
</icon-hover>
|
||||
</slot>
|
||||
</div>
|
||||
</slot>
|
||||
<slot>
|
||||
<div class="flex flex-col text-center">
|
||||
<div v-if="title" class="mb4 text-lg font-600">{{ title }}</div>
|
||||
<template v-else />
|
||||
<component :is="content" v-if="typeof content === 'function'" />
|
||||
<div v-else>{{ content }}</div>
|
||||
</div>
|
||||
</slot>
|
||||
</template>
|
||||
@ -1,83 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import type { RouteLocationNormalized } from 'vue-router';
|
||||
import { listenerRouteChange, removeRouteListener } from '@/utils/route-listener';
|
||||
import { useAppStore, useTabBarStore } from '@/stores';
|
||||
import TabItem from './tab-item.vue';
|
||||
const appStore = useAppStore();
|
||||
const tabBarStore = useTabBarStore();
|
||||
const affixRef = ref();
|
||||
const tagList = computed(() => {
|
||||
return tabBarStore.getTabList;
|
||||
});
|
||||
const offsetTop = computed(() => {
|
||||
return appStore.navbar ? 60 : 0;
|
||||
});
|
||||
watch(
|
||||
() => appStore.navbar,
|
||||
() => {
|
||||
affixRef.value.updatePosition();
|
||||
},
|
||||
);
|
||||
listenerRouteChange((route: RouteLocationNormalized) => {
|
||||
if (!route.meta.noAffix && !tagList.value.some((tag) => tag.fullPath === route.fullPath)) {
|
||||
tabBarStore.updateTabList(route);
|
||||
}
|
||||
}, true);
|
||||
onUnmounted(() => {
|
||||
removeRouteListener();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="tab-bar-container">
|
||||
<a-affix ref="affixRef" :offset-top="offsetTop">
|
||||
<div class="tab-bar-box">
|
||||
<div class="tab-bar-scroll">
|
||||
<div class="tags-wrap">
|
||||
<tab-item v-for="(tag, index) in tagList" :key="tag.fullPath" :index="index" :item-data="tag" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="tag-bar-operation"></div>
|
||||
</div>
|
||||
</a-affix>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.tab-bar-container {
|
||||
position: relative;
|
||||
background-color: var(--color-bg-2);
|
||||
.tab-bar-box {
|
||||
display: flex;
|
||||
padding: 0 0 0 20px;
|
||||
background-color: var(--color-bg-2);
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
.tab-bar-scroll {
|
||||
height: 32px;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
.tags-wrap {
|
||||
padding: 4px 0;
|
||||
height: 48px;
|
||||
white-space: nowrap;
|
||||
overflow-x: auto;
|
||||
:deep(.arco-tag) {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
margin-right: 6px;
|
||||
cursor: pointer;
|
||||
&:first-child {
|
||||
.arco-tag-close-btn {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.tag-bar-operation {
|
||||
width: 100px;
|
||||
height: 32px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -1,177 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import type { PropType } from 'vue';
|
||||
import type { TagProps } from '@/stores/modules/tab-bar/types';
|
||||
|
||||
import { useTabBarStore } from '@/stores';
|
||||
import { DEFAULT_ROUTE_NAME, REDIRECT_ROUTE_NAME } from '@/router/constants';
|
||||
|
||||
const props = defineProps({
|
||||
itemData: {
|
||||
type: Object as PropType<TagProps>,
|
||||
default() {
|
||||
return [];
|
||||
},
|
||||
},
|
||||
index: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
});
|
||||
// eslint-disable-next-line no-shadow
|
||||
enum Eaction {
|
||||
reload = 'reload',
|
||||
current = 'current',
|
||||
left = 'left',
|
||||
right = 'right',
|
||||
others = 'others',
|
||||
all = 'all',
|
||||
}
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const tabBarStore = useTabBarStore();
|
||||
const goto = (tag: TagProps) => {
|
||||
router.push({ ...tag });
|
||||
};
|
||||
const tagList = computed(() => {
|
||||
return tabBarStore.getTabList;
|
||||
});
|
||||
const disabledReload = computed(() => {
|
||||
return props.itemData.fullPath !== route.fullPath;
|
||||
});
|
||||
const disabledCurrent = computed(() => {
|
||||
return props.index === 0;
|
||||
});
|
||||
const disabledLeft = computed(() => {
|
||||
return [0, 1].includes(props.index);
|
||||
});
|
||||
const disabledRight = computed(() => {
|
||||
return props.index === tagList.value.length - 1;
|
||||
});
|
||||
const tagClose = (tag: TagProps, idx: number) => {
|
||||
tabBarStore.deleteTag(idx, tag);
|
||||
if (props.itemData.fullPath === route.fullPath) {
|
||||
const latest = tagList.value[idx - 1]; // 获取队列的前一个tab
|
||||
router.push({ name: latest.name });
|
||||
}
|
||||
};
|
||||
const findCurrentRouteIndex = () => {
|
||||
return tagList.value.findIndex((el) => el.fullPath === route.fullPath);
|
||||
};
|
||||
const actionSelect = async (value: any) => {
|
||||
const { itemData, index } = props;
|
||||
const copyTagList = [...tagList.value];
|
||||
if (value === Eaction.current) {
|
||||
tagClose(itemData, index);
|
||||
} else if (value === Eaction.left) {
|
||||
const currentRouteIdx = findCurrentRouteIndex();
|
||||
copyTagList.splice(1, props.index - 1);
|
||||
tabBarStore.freshTabList(copyTagList);
|
||||
if (currentRouteIdx < index) {
|
||||
router.push({ name: itemData.name });
|
||||
}
|
||||
} else if (value === Eaction.right) {
|
||||
const currentRouteIdx = findCurrentRouteIndex();
|
||||
copyTagList.splice(props.index + 1);
|
||||
tabBarStore.freshTabList(copyTagList);
|
||||
if (currentRouteIdx > index) {
|
||||
router.push({ name: itemData.name });
|
||||
}
|
||||
} else if (value === Eaction.others) {
|
||||
const filterList = tagList.value.filter((el, idx) => {
|
||||
return idx === 0 || idx === props.index;
|
||||
});
|
||||
tabBarStore.freshTabList(filterList);
|
||||
router.push({ name: itemData.name });
|
||||
} else if (value === Eaction.reload) {
|
||||
tabBarStore.deleteCache(itemData);
|
||||
await router.push({
|
||||
name: REDIRECT_ROUTE_NAME,
|
||||
params: {
|
||||
path: route.fullPath,
|
||||
},
|
||||
});
|
||||
tabBarStore.addCache(itemData.name);
|
||||
} else {
|
||||
tabBarStore.resetTabList();
|
||||
router.push({ name: DEFAULT_ROUTE_NAME });
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a-dropdown trigger="contextMenu" :popup-max-height="false" @select="actionSelect">
|
||||
<span
|
||||
:class="[
|
||||
'arco-tag arco-tag-size-medium arco-tag-checked',
|
||||
{ 'link-activated': itemData.fullPath === $route.fullPath },
|
||||
]"
|
||||
@click="goto(itemData)"
|
||||
>
|
||||
<span class="tag-link">{{ itemData.title }}</span>
|
||||
<span
|
||||
class="arco-icon-hover arco-tag-icon-hover arco-icon-hover-size-medium arco-tag-close-btn"
|
||||
@click.stop="tagClose(itemData, index)"
|
||||
>
|
||||
<icon-close />
|
||||
</span>
|
||||
</span>
|
||||
<template #content>
|
||||
<a-doption :disabled="disabledReload" :value="Eaction.reload">
|
||||
<icon-refresh />
|
||||
<span>重新加载</span>
|
||||
</a-doption>
|
||||
<a-doption class="sperate-line" :disabled="disabledCurrent" :value="Eaction.current">
|
||||
<icon-close />
|
||||
<span>关闭当前标签页</span>
|
||||
</a-doption>
|
||||
<a-doption :disabled="disabledLeft" :value="Eaction.left">
|
||||
<icon-to-left />
|
||||
<span>关闭左侧标签页</span>
|
||||
</a-doption>
|
||||
<a-doption class="sperate-line" :disabled="disabledRight" :value="Eaction.right">
|
||||
<icon-to-right />
|
||||
<span>关闭右侧标签页</span>
|
||||
</a-doption>
|
||||
<a-doption :value="Eaction.others">
|
||||
<icon-swap />
|
||||
<span>关闭其它标签页</span>
|
||||
</a-doption>
|
||||
<a-doption :value="Eaction.all">
|
||||
<icon-folder-delete />
|
||||
<span>关闭全部标签页</span>
|
||||
</a-doption>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.tag-link {
|
||||
color: var(--color-text-2);
|
||||
text-decoration: none;
|
||||
}
|
||||
.link-activated {
|
||||
color: rgb(var(--link-6));
|
||||
.tag-link {
|
||||
color: rgb(var(--link-6));
|
||||
}
|
||||
& + .arco-tag-close-btn {
|
||||
color: rgb(var(--link-6));
|
||||
}
|
||||
}
|
||||
:deep(.arco-dropdown-option-content) {
|
||||
span {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
.arco-dropdown-open {
|
||||
.tag-link {
|
||||
color: rgb(var(--danger-6));
|
||||
}
|
||||
.arco-tag-close-btn {
|
||||
color: rgb(var(--danger-6));
|
||||
}
|
||||
}
|
||||
.sperate-line {
|
||||
border-bottom: 1px solid var(--color-neutral-3);
|
||||
}
|
||||
</style>
|
||||
@ -3,26 +3,29 @@
|
||||
* @Date: 2025-06-25 14:02:40
|
||||
-->
|
||||
<template>
|
||||
<a-select
|
||||
v-model="selectedValues"
|
||||
:multiple="multiple"
|
||||
size="medium"
|
||||
<Select
|
||||
v-model:value="selectedValues"
|
||||
:mode="multiple ? 'multiple' : undefined"
|
||||
size="middle"
|
||||
:placeholder="placeholder"
|
||||
:allow-clear="allClear"
|
||||
:allow-search="allowSearch"
|
||||
:max-tag-count="maxTagCount"
|
||||
:allowClear="allClear"
|
||||
:showSearch="allowSearch"
|
||||
showArrow
|
||||
:maxTagCount="maxTagCount"
|
||||
@change="handleChange"
|
||||
>
|
||||
<a-option v-for="(item, index) in options" :key="index" :value="item.id" :label="item.name">
|
||||
<Option v-for="(item, index) in options" :key="index" :value="item.id" :label="item.name">
|
||||
<div class="flex items-center">
|
||||
<img v-if="item.icon" :src="item.icon" class="w-16px h-16px mr-8px rounded-4px" />
|
||||
{{ item.name }}
|
||||
</div>
|
||||
</a-option>
|
||||
</a-select>
|
||||
</Option>
|
||||
</Select>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { Select } from 'ant-design-vue';
|
||||
const { Option } = Select;
|
||||
import { ref, watch } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
|
||||
@ -3,14 +3,15 @@
|
||||
* @Date: 2025-06-30 10:54:49
|
||||
-->
|
||||
<template>
|
||||
<a-modal
|
||||
v-model:visible="visible"
|
||||
<Modal
|
||||
v-model:open="visible"
|
||||
title="自定义列"
|
||||
width="960px"
|
||||
unmountOnClose
|
||||
titleAlign="start"
|
||||
class="custom-table-column-modal"
|
||||
@close="close"
|
||||
centered
|
||||
wrapClassName="custom-table-column-modal"
|
||||
@cancel="close"
|
||||
>
|
||||
<div class="modal-body">
|
||||
<!-- 左侧分组 -->
|
||||
@ -20,16 +21,16 @@
|
||||
<span class="text">{{ group.label }}</span>
|
||||
</div>
|
||||
<div class="fields">
|
||||
<a-checkbox
|
||||
<Checkbox
|
||||
v-for="option in group.columns"
|
||||
:key="option.value"
|
||||
:model-value="isCheck(option)"
|
||||
:checked="isCheck(option)"
|
||||
:value="option.value"
|
||||
:disabled="option.is_require === ENUM_STATUS.NO"
|
||||
@change="(checked) => onCheckChange(checked, option)"
|
||||
@change="(e) => onCheckChange(e.target.checked, option)"
|
||||
>
|
||||
{{ option.label }}
|
||||
</a-checkbox>
|
||||
</Checkbox>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -64,15 +65,16 @@
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<div style="text-align: right">
|
||||
<a-button class="mr-8px" size="medium" @click="close">取消</a-button>
|
||||
<a-button type="primary" size="medium" @click="onSubmit">确定</a-button>
|
||||
<div class="flex">
|
||||
<Button @click="close">取消</Button>
|
||||
<Button type="primary" @click="onSubmit">确定</Button>
|
||||
</div>
|
||||
</template>
|
||||
</a-modal>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { Checkbox, Modal, Button } from 'ant-design-vue';
|
||||
import { ref, defineExpose } from 'vue';
|
||||
import { VueDraggable } from 'vue-draggable-plus';
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
.custom-table-column-modal {
|
||||
.arco-modal-body {
|
||||
.ant-modal-body {
|
||||
.modal-body {
|
||||
height: 504px;
|
||||
border-radius: 8px;
|
||||
|
||||
@ -1,13 +1,31 @@
|
||||
<template>
|
||||
<a-modal modal-class="delete-modal" body-class="body" cancel-text="返回" ok-text="确定删除" v-bind="$attrs">
|
||||
<Modal wrapClassName="delete-modal" body-class="body" v-bind="$attrs" centered title="删除账号">
|
||||
<h2 class="delete-modal-title flex item-center">
|
||||
<img src="@/assets/warning.svg" alt="" />
|
||||
{{ $attrs.title }}
|
||||
{{ $attrs.content }}
|
||||
</h2>
|
||||
<slot></slot>
|
||||
</a-modal>
|
||||
<p class="delete-modal-content">删除后,该账号将无法登录您的企业。</p>
|
||||
<template #footer>
|
||||
<div style="text-align: right">
|
||||
<Button @click="close">返回</Button>
|
||||
<Button type="primary" danger @click="onSubmit">确定删除</Button>
|
||||
</div>
|
||||
</template>
|
||||
</Modal>
|
||||
</template>
|
||||
<script setup lang="ts"></script>
|
||||
<script setup lang="ts">
|
||||
import { Modal, Button } from 'ant-design-vue';
|
||||
|
||||
const emit = defineEmits(['close', 'ok']);
|
||||
|
||||
const close = () => {
|
||||
emit('close');
|
||||
};
|
||||
|
||||
const onSubmit = () => {
|
||||
emit('ok');
|
||||
};
|
||||
</script>
|
||||
<style lang="scss">
|
||||
:deep(.arco-btn-status-danger) {
|
||||
background-color: red !important;
|
||||
@ -18,7 +36,7 @@
|
||||
display: none;
|
||||
}
|
||||
.delete-modal-title {
|
||||
margin-top: 24px;
|
||||
// margin-top: 24px;
|
||||
font-family: $font-family-medium;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
@ -29,6 +47,14 @@
|
||||
margin-right: 12px;
|
||||
}
|
||||
}
|
||||
.delete-modal-content {
|
||||
margin-left: 34px;
|
||||
margin-top: 16px;
|
||||
font-family: $font-family-medium;
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
color: var(--Text-2, rgba(60, 64, 67, 1));
|
||||
}
|
||||
.arco-modal-footer {
|
||||
border-top: none;
|
||||
:first-child {
|
||||
@ -53,5 +79,6 @@
|
||||
}
|
||||
.body {
|
||||
padding: 0 24px;
|
||||
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -3,13 +3,13 @@
|
||||
* @Date: 2025-08-11 22:15:35
|
||||
-->
|
||||
<template>
|
||||
<a-popover
|
||||
:trigger="'hover'"
|
||||
class="hover-big-image-preview-popover"
|
||||
:position="props.position"
|
||||
:mouse-enter-delay="props.enterDelay"
|
||||
:mouse-leave-delay="props.leaveDelay"
|
||||
:disabled="!props.src"
|
||||
<Popover
|
||||
trigger="hover"
|
||||
:placement="props.position"
|
||||
:mouseEnterDelay="props.enterDelay / 1000"
|
||||
:mouseLeaveDelay="props.leaveDelay / 1000"
|
||||
:open="props.src ? undefined : false"
|
||||
overlayClassName="hover-big-image-preview-popover"
|
||||
>
|
||||
<template #content>
|
||||
<div class="preview-container">
|
||||
@ -18,17 +18,30 @@
|
||||
</template>
|
||||
|
||||
<slot />
|
||||
</a-popover>
|
||||
</Popover>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { Popover } from 'ant-design-vue';
|
||||
// import { computed, onMounted, ref, watch } from 'vue';
|
||||
// import type { ImageOrientation } from '@/utils/tools';
|
||||
// import { getImageOrientationByUrl } from '@/utils/tools';
|
||||
|
||||
interface Props {
|
||||
src: string;
|
||||
position?: 'top' | 'tl' | 'tr' | 'bottom' | 'bl' | 'br' | 'left' | 'lt' | 'lb' | 'right' | 'rt' | 'rb';
|
||||
position?:
|
||||
| 'top'
|
||||
| 'topLeft'
|
||||
| 'topRight'
|
||||
| 'bottom'
|
||||
| 'bottomLeft'
|
||||
| 'bottomRight'
|
||||
| 'left'
|
||||
| 'leftTop'
|
||||
| 'leftBottom'
|
||||
| 'right'
|
||||
| 'rightTop'
|
||||
| 'rightBottom';
|
||||
enterDelay?: number;
|
||||
leaveDelay?: number;
|
||||
}
|
||||
@ -37,6 +50,7 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
position: 'right',
|
||||
enterDelay: 100,
|
||||
leaveDelay: 200,
|
||||
src: '',
|
||||
});
|
||||
|
||||
// const orientation = ref<ImageOrientation>('landscape');
|
||||
@ -67,7 +81,11 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
|
||||
<style lang="scss">
|
||||
.hover-big-image-preview-popover {
|
||||
.arco-popover-popup-content {
|
||||
.ant-popover-content {
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.ant-popover-inner {
|
||||
padding: 16px !important;
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
|
||||
90
src/components/img-lazy-load/index.vue
Normal file
90
src/components/img-lazy-load/index.vue
Normal file
@ -0,0 +1,90 @@
|
||||
<template>
|
||||
<div class="img-lazy" v-lazy:background-image="imgSrc" :key="src" :class="imgClass" :style="style" />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, watch, computed } from 'vue';
|
||||
const emit = defineEmits(['click']);
|
||||
const props = defineProps({
|
||||
width: {
|
||||
type: [String, Number],
|
||||
},
|
||||
height: {
|
||||
type: [String, Number],
|
||||
},
|
||||
loadingSize: {
|
||||
type: [String],
|
||||
default: '5',
|
||||
},
|
||||
errorSize: {
|
||||
type: [String],
|
||||
default: '5',
|
||||
},
|
||||
fit: {
|
||||
type: [String],
|
||||
default: 'cover',
|
||||
},
|
||||
src: {
|
||||
type: String,
|
||||
},
|
||||
customImg: {
|
||||
type: String,
|
||||
},
|
||||
});
|
||||
|
||||
const style = computed(() => {
|
||||
return {
|
||||
'background-size': props.fit,
|
||||
width: props.width ? parseInt(props.width) + 'px' : undefined,
|
||||
height: props.height ? parseInt(props.height) + 'px' : undefined,
|
||||
};
|
||||
});
|
||||
|
||||
const imgClass = computed(() => {
|
||||
return {
|
||||
['loading-size-' + props.loadingSize]: true,
|
||||
};
|
||||
});
|
||||
|
||||
const imgSrc = computed(() => {
|
||||
return props.innerSrc || props.src;
|
||||
});
|
||||
const innerSrc = ref('');
|
||||
|
||||
watch(
|
||||
() => props.customImg,
|
||||
() => {
|
||||
innerSrc.value = '';
|
||||
if (props.customImg) {
|
||||
const img = new Image();
|
||||
img.src = props.src;
|
||||
img.onerror = () => {
|
||||
innerSrc.value = props.customImg;
|
||||
};
|
||||
img.onload = () => {
|
||||
innerSrc.value = props.src;
|
||||
};
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
onMounted(() => {});
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.img-lazy.block {
|
||||
display: block;
|
||||
}
|
||||
.img-lazy {
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
background-size: 100% 100%;
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
|
||||
@for $i from 0 to 10 {
|
||||
&.loading-size-#{$i}[lazy='loading'] {
|
||||
background-size: #{$i * 10 + '%'} !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -9,6 +9,7 @@
|
||||
<script setup lang="ts">
|
||||
import Modal from '@components/modal.vue';
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { getQueryParam } from '@/utils/helper';
|
||||
|
||||
import { getEnterpriseByInviteCode, joinEnterpriseByInviteCode } from '@/api/all';
|
||||
@ -26,7 +27,7 @@ async function getEnterprise() {
|
||||
|
||||
async function handleJoin() {
|
||||
await joinEnterpriseByInviteCode(inviteCode.value);
|
||||
AMessage.success('加入成功');
|
||||
message.success('加入成功');
|
||||
}
|
||||
// onMounted(() => {
|
||||
// getEnterprise();
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
<template>
|
||||
<a-modal title-align="start" modal-class="modal" body-class="body" v-bind="$attrs">
|
||||
<Modal title-align="start" wrapClassName="modal" cancelText="取消" okText="确定" body-class="body" v-bind="$attrs" centered>
|
||||
<slot></slot>
|
||||
</a-modal>
|
||||
</Modal>
|
||||
</template>
|
||||
<script setup lang="ts"></script>
|
||||
<script setup lang="ts">
|
||||
import { Modal } from 'ant-design-vue';
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.modal {
|
||||
.arco-modal-header {
|
||||
|
||||
@ -1,18 +1,16 @@
|
||||
<template>
|
||||
<a-upload
|
||||
:custom-request="customRequest"
|
||||
<Upload
|
||||
:customRequest="customRequest"
|
||||
action="/"
|
||||
:limit="limit"
|
||||
:maxCount="limit"
|
||||
:fileList="fileList"
|
||||
@change="onChange"
|
||||
@success="handleSuccess"
|
||||
@error="handleError"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
import { Upload, message } from 'ant-design-vue';
|
||||
import { fetchImageUploadFile, fetchUploadFile } from '@/api/all';
|
||||
import axios from 'axios';
|
||||
|
||||
@ -70,8 +68,10 @@ watch(
|
||||
);
|
||||
|
||||
let previousFileListLength = 0;
|
||||
//删除图片
|
||||
const onChange = (fileList) => {
|
||||
const onChange = (info) => {
|
||||
const { fileList } = info;
|
||||
|
||||
// 如果删除了文件
|
||||
if (fileList.length < previousFileListLength) {
|
||||
if (props.limit === 1) {
|
||||
if (fileList.length === 0) {
|
||||
@ -87,27 +87,34 @@ const onChange = (fileList) => {
|
||||
}
|
||||
}
|
||||
|
||||
// 处理上传成功的文件
|
||||
if (info.file.status === 'done' && info.file.response) {
|
||||
handleSuccess(info.file);
|
||||
} else if (info.file.status === 'error') {
|
||||
handleError(info.file.error);
|
||||
}
|
||||
|
||||
previousFileListLength = fileList.length;
|
||||
};
|
||||
|
||||
const beforeUpload = (file, files) => {
|
||||
if (props.limit > 0 && files.length >= props.limit) {
|
||||
Message.warning(`最多只能上传 ${props.limit} 张图片`);
|
||||
message.warning(`最多只能上传 ${props.limit} 张图片`);
|
||||
return false; // 阻止上传
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
const handleError = (error) => {
|
||||
Message.error('上传失败');
|
||||
message.error('上传失败');
|
||||
console.error(error);
|
||||
};
|
||||
|
||||
const customRequest = async (option) => {
|
||||
const { onProgress, onError, onSuccess, fileItem, name } = option;
|
||||
const { onProgress, onError, onSuccess, file, name } = option;
|
||||
try {
|
||||
// 1. 获取预签名上传URL
|
||||
const response = await fetchUploadFile({ suffix: getFileExtension(fileItem.file.name) });
|
||||
const response = await fetchUploadFile({ suffix: getFileExtension(file.name) });
|
||||
const preSignedUrl = response?.data?.upload_url;
|
||||
|
||||
if (!preSignedUrl) {
|
||||
@ -115,9 +122,9 @@ const customRequest = async (option) => {
|
||||
}
|
||||
console.log('preSignedUrl', preSignedUrl);
|
||||
// 2. 使用预签名URL上传文件
|
||||
const blob = new Blob([fileItem.file], { type: fileItem.file.type });
|
||||
const blob = new Blob([file], { type: file.type });
|
||||
await axios.put(preSignedUrl, blob, {
|
||||
headers: { 'Content-Type': fileItem.file.type },
|
||||
headers: { 'Content-Type': file.type },
|
||||
});
|
||||
|
||||
onSuccess(JSON.stringify(response));
|
||||
|
||||
@ -1,20 +1,19 @@
|
||||
<template>
|
||||
<a-upload
|
||||
:custom-request="customRequest"
|
||||
list-type="picture-card"
|
||||
<Upload
|
||||
:customRequest="customRequest"
|
||||
listType="picture-card"
|
||||
action="/"
|
||||
:limit="limit"
|
||||
:maxCount="limit"
|
||||
:fileList="fileList"
|
||||
image-preview
|
||||
:showUploadList="{ showPreviewIcon: true, showRemoveIcon: true }"
|
||||
@change="onChange"
|
||||
@success="handleSuccess"
|
||||
@error="handleError"
|
||||
@preview="handlePreview"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
import { Upload, message } from 'ant-design-vue';
|
||||
import { fetchImageUploadFile } from '@/api/all';
|
||||
import axios from 'axios';
|
||||
|
||||
@ -72,8 +71,14 @@ watch(
|
||||
);
|
||||
|
||||
let previousFileListLength = 0;
|
||||
//删除图片
|
||||
const onChange = (fileList) => {
|
||||
const handlePreview = (file) => {
|
||||
console.log('Preview file:', file);
|
||||
};
|
||||
|
||||
const onChange = (info) => {
|
||||
const { fileList } = info;
|
||||
|
||||
// 如果删除了文件
|
||||
if (fileList.length < previousFileListLength) {
|
||||
if (props.limit === 1) {
|
||||
if (fileList.length === 0) {
|
||||
@ -89,27 +94,34 @@ const onChange = (fileList) => {
|
||||
}
|
||||
}
|
||||
|
||||
// 处理上传成功的文件
|
||||
if (info.file.status === 'done' && info.file.response) {
|
||||
handleSuccess(info.file);
|
||||
} else if (info.file.status === 'error') {
|
||||
handleError(info.file.error);
|
||||
}
|
||||
|
||||
previousFileListLength = fileList.length;
|
||||
};
|
||||
|
||||
const beforeUpload = (file, files) => {
|
||||
if (props.limit > 0 && files.length >= props.limit) {
|
||||
Message.warning(`最多只能上传 ${props.limit} 张图片`);
|
||||
message.warning(`最多只能上传 ${props.limit} 张图片`);
|
||||
return false; // 阻止上传
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
const handleError = (error) => {
|
||||
Message.error('上传失败');
|
||||
message.error('上传失败');
|
||||
console.error(error);
|
||||
};
|
||||
|
||||
const customRequest = async (option) => {
|
||||
const { onProgress, onError, onSuccess, fileItem, name } = option;
|
||||
const { onProgress, onError, onSuccess, file, name } = option;
|
||||
try {
|
||||
// 1. 获取预签名上传URL
|
||||
const response = await fetchImageUploadFile({ suffix: getFileExtension(fileItem.file.name) });
|
||||
const response = await fetchImageUploadFile({ suffix: getFileExtension(file.name) });
|
||||
const preSignedUrl = response?.data?.upload_url;
|
||||
|
||||
if (!preSignedUrl) {
|
||||
@ -117,9 +129,9 @@ const customRequest = async (option) => {
|
||||
}
|
||||
console.log('preSignedUrl', preSignedUrl);
|
||||
// 2. 使用预签名URL上传文件
|
||||
const blob = new Blob([fileItem.file], { type: fileItem.file.type });
|
||||
const blob = new Blob([file], { type: file.type });
|
||||
await axios.put(preSignedUrl, blob, {
|
||||
headers: { 'Content-Type': fileItem.file.type },
|
||||
headers: { 'Content-Type': file.type },
|
||||
});
|
||||
|
||||
onSuccess(JSON.stringify(response));
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<script lang="tsx">
|
||||
import { Button } from '@arco-design/web-vue';
|
||||
import { Button } from 'ant-design-vue';
|
||||
import { Bubble } from '@/components/xt-chat/xt-bubble';
|
||||
|
||||
import Http from '@/api';
|
||||
@ -79,8 +79,8 @@ export default {
|
||||
<header class="header flex justify-end items-center mb-16px px-16px">
|
||||
{hasMediaCenter.value && (
|
||||
<Button
|
||||
type="outline"
|
||||
size="medium"
|
||||
type="primary"
|
||||
ghost
|
||||
class="mr-16px"
|
||||
v-slots={{ icon: () => <icon-plus size="14" /> }}
|
||||
onClick={onAddMediaCenter}
|
||||
@ -90,8 +90,8 @@ export default {
|
||||
)}
|
||||
|
||||
<Button
|
||||
type="outline"
|
||||
size="medium"
|
||||
type="primary"
|
||||
ghost
|
||||
class="mr-16px"
|
||||
v-slots={{ icon: () => <icon-plus size="14" /> }}
|
||||
onClick={onAddTaskManage}
|
||||
|
||||
@ -1,2 +1 @@
|
||||
export * from './responsive';
|
||||
export * from './modal';
|
||||
|
||||
@ -1,55 +0,0 @@
|
||||
/*
|
||||
* @Author: 田鑫
|
||||
* @Date: 2023-02-21 15:11:01
|
||||
* @LastEditors: 田鑫
|
||||
* @LastEditTime: 2023-02-21 15:11:02
|
||||
* @Description:
|
||||
*/
|
||||
import type { AppContext, Component, DefineComponent } from 'vue';
|
||||
import type { ModalConfig } from '@arco-design/web-vue';
|
||||
|
||||
import { ModalSimple } from '@/components/_base';
|
||||
|
||||
type CompType = DefineComponent | Component;
|
||||
interface SlotsType {
|
||||
default?: CompType;
|
||||
header?: CompType;
|
||||
close?: CompType;
|
||||
}
|
||||
|
||||
export const useModal = () => {
|
||||
const instance = getCurrentInstance();
|
||||
|
||||
const Modal = AModal;
|
||||
|
||||
Modal._context = instance?.appContext as AppContext;
|
||||
|
||||
Modal.simple = (config: ModalConfig, slots: SlotsType) => {
|
||||
const { title, content, ..._config } = config || {};
|
||||
|
||||
const modal = Modal.open({
|
||||
..._config,
|
||||
simple: false,
|
||||
footer: false,
|
||||
closable: false,
|
||||
content: () =>
|
||||
h(
|
||||
ModalSimple,
|
||||
{
|
||||
title,
|
||||
content,
|
||||
onClose: modal.close,
|
||||
},
|
||||
{
|
||||
default: slots?.default,
|
||||
header: slots?.header,
|
||||
close: slots?.close,
|
||||
},
|
||||
),
|
||||
});
|
||||
|
||||
return modal;
|
||||
};
|
||||
|
||||
return { Modal };
|
||||
};
|
||||
@ -1,172 +0,0 @@
|
||||
/*
|
||||
* @Author: 田鑫
|
||||
* @Date: 2023-02-16 15:02:51
|
||||
* @LastEditors: 田鑫
|
||||
* @LastEditTime: 2023-03-09 11:20:59
|
||||
* @Description: table-hooks
|
||||
*/
|
||||
import type { PaginationProps, TableBorder, TableColumnData, TableData, TableRowSelection } from '@arco-design/web-vue';
|
||||
type Size = 'mini' | 'small' | 'medium' | 'large';
|
||||
interface IDefaultProps {
|
||||
/** 是否显示边框 */
|
||||
bordered?: TableBorder;
|
||||
/** 是否显示选中效果 */
|
||||
hoverable?: boolean;
|
||||
/** 表格的大小 */
|
||||
size?: Size;
|
||||
/** 是否允许调整列宽 */
|
||||
'column-resizable'?: boolean;
|
||||
/** 是否为加载中状态 */
|
||||
loading?: boolean;
|
||||
/** 分页参数 */
|
||||
pagination?: PaginationProps;
|
||||
/** table数据类型 */
|
||||
data?: any[];
|
||||
/** 表头参数 */
|
||||
columns?: TableColumnData[];
|
||||
/** 表格行 key 的取值字段 */
|
||||
'row-key'?: string;
|
||||
/** 表格的行选择器配置 */
|
||||
'row-selection'?: TableRowSelection;
|
||||
'selected-keys'?: (string | number)[];
|
||||
[x: string]: any;
|
||||
}
|
||||
interface IPagination {
|
||||
/** 当前页数 */
|
||||
current?: number;
|
||||
/** 总页数默认是0条 */
|
||||
total?: number;
|
||||
}
|
||||
interface ITableResponse<T> {
|
||||
current?: number;
|
||||
records?: T[];
|
||||
size?: number;
|
||||
total?: number;
|
||||
[x: string]: any;
|
||||
}
|
||||
type GetListFunc<T> = (v: object) => Promise<ITableResponse<T>>;
|
||||
export default function useTableProps<T>(loadListFunc: GetListFunc<T>) {
|
||||
const defaultProps: IDefaultProps = {
|
||||
bordered: { cell: true },
|
||||
size: 'large',
|
||||
'column-resizable': true,
|
||||
loading: true,
|
||||
data: [] as any[],
|
||||
pagination: {
|
||||
current: 1,
|
||||
pageSize: 20,
|
||||
total: 0,
|
||||
showPageSize: true,
|
||||
showTotal: true,
|
||||
},
|
||||
hoverable: false,
|
||||
columns: [],
|
||||
};
|
||||
//* 属性组
|
||||
const propsRes = reactive<IDefaultProps>(defaultProps);
|
||||
//* 设置请求参数,如果出了分页参数还有搜索参数,在模板页面调用此方法,可以加入参数
|
||||
const loadListParams = reactive<object>({
|
||||
page: 1,
|
||||
size: 20,
|
||||
});
|
||||
/**
|
||||
* 单独设置默认属性
|
||||
* @param params
|
||||
*/
|
||||
const setProps = (params: IDefaultProps) => {
|
||||
if (Object.keys(params).length > 0) {
|
||||
Object.assign(defaultProps, params);
|
||||
}
|
||||
};
|
||||
/**
|
||||
* 设置表头数据
|
||||
* @param columns
|
||||
*/
|
||||
const setColumns = (columns: TableColumnData[]) => {
|
||||
propsRes.columns = columns;
|
||||
};
|
||||
/**
|
||||
* 设置loading
|
||||
* @param status
|
||||
*/
|
||||
const setLoading = (status: boolean) => {
|
||||
propsRes.loading = status;
|
||||
};
|
||||
/**
|
||||
* 设置分页
|
||||
* @param param0
|
||||
*/
|
||||
const setPagination = ({ current, total }: IPagination) => {
|
||||
propsRes.pagination!.current = current;
|
||||
total && (propsRes.pagination!.total = total);
|
||||
Object.assign(loadListParams, { page: current });
|
||||
};
|
||||
/**
|
||||
* 设置列表请求参数
|
||||
* @param params
|
||||
*/
|
||||
const setLoadListParams = <R>(params?: R) => {
|
||||
Object.assign(loadListParams, params);
|
||||
};
|
||||
/**
|
||||
* 加载列表
|
||||
* @returns
|
||||
*/
|
||||
const loadTableData = async (resetPageIndex = false) => {
|
||||
if (resetPageIndex) {
|
||||
setPagination({ current: 1 });
|
||||
}
|
||||
setLoading(true);
|
||||
try {
|
||||
const resData = await loadListFunc({
|
||||
...loadListParams,
|
||||
});
|
||||
console.log(resData);
|
||||
const response = resData as ITableResponse<T>;
|
||||
propsRes.data = response.records;
|
||||
setPagination({
|
||||
current: response.current,
|
||||
total: response.total,
|
||||
});
|
||||
setLoading(false);
|
||||
return resData;
|
||||
} catch (error) {
|
||||
setLoading(false);
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
// 事件触发组
|
||||
const propsEvent = reactive({
|
||||
// 排序触发
|
||||
sorterChange: (dataIndex: string, direction: string) => {
|
||||
console.log(dataIndex, direction);
|
||||
},
|
||||
// 分页触发
|
||||
pageChange: (current: number) => {
|
||||
setPagination({ current });
|
||||
loadTableData();
|
||||
},
|
||||
// 修改每页显示条数
|
||||
pageSizeChange: (size: number) => {
|
||||
propsRes.pagination!.pageSize = size;
|
||||
Object.assign(loadListParams, { size });
|
||||
loadTableData();
|
||||
},
|
||||
selectionChange: (rowKeys: (string | number)[]) => {
|
||||
propsRes['selected-keys'] = rowKeys;
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
propsRes,
|
||||
propsEvent,
|
||||
loadListParams,
|
||||
setProps,
|
||||
setColumns,
|
||||
setLoading,
|
||||
setPagination,
|
||||
loadTableData,
|
||||
setLoadListParams,
|
||||
};
|
||||
}
|
||||
@ -22,30 +22,28 @@ export function useTableSelectionWithPagination(options: UseTableSelectionWithPa
|
||||
const rowKey = options.rowKey || 'id';
|
||||
|
||||
const selectedRowKeys = ref<Array<string | number>>([]);
|
||||
const selectedRows = ref<any[]>([]);
|
||||
const selectedRows = ref<Array<any>>([]);
|
||||
|
||||
const pageInfo = ref(merge({}, DEFAULT_PAGE_INFO, options.pageInfo));
|
||||
|
||||
const dataSource = ref<any[]>([]);
|
||||
|
||||
// 单行选择
|
||||
const handleSelect = (selectedKeys: (string | number)[], rowKeyValue: string | number, record: any) => {
|
||||
const select = selectedKeys.includes(rowKeyValue);
|
||||
selectedRowKeys.value = selectedKeys;
|
||||
|
||||
const handleSelect = (record: any, select: boolean) => {
|
||||
const _targetKey = record[rowKey];
|
||||
if (select) {
|
||||
if (!selectedRows.value.some((v) => v[rowKey] === record[rowKey])) {
|
||||
selectedRows.value.push(record);
|
||||
}
|
||||
selectedRowKeys.value.push(_targetKey);
|
||||
} else {
|
||||
selectedRows.value = selectedRows.value.filter((v) => v[rowKey] !== record[rowKey]);
|
||||
selectedRows.value = selectedRows.value.filter((v) => v[rowKey] !== _targetKey);
|
||||
selectedRowKeys.value = selectedRowKeys.value.filter((key) => key !== _targetKey);
|
||||
}
|
||||
|
||||
options.onSelectChange?.();
|
||||
};
|
||||
|
||||
// 全选/取消全选
|
||||
const handleSelectAll = (checked: boolean) => {
|
||||
console.log('handleSelectAll', checked)
|
||||
const currentPageRows = dataSource.value;
|
||||
const currentPageKeys = currentPageRows.map((v) => v[rowKey]);
|
||||
|
||||
@ -62,18 +60,21 @@ export function useTableSelectionWithPagination(options: UseTableSelectionWithPa
|
||||
options.onSelectChange?.();
|
||||
};
|
||||
|
||||
const onPageChange = (page: number) => {
|
||||
const onPageChange = (page: number, pageSize: number) => {
|
||||
// console.log('onPageChange', page, pageSize);
|
||||
pageInfo.value.page = page;
|
||||
pageInfo.value.page_size = pageSize;
|
||||
options.onPageChange?.(page);
|
||||
};
|
||||
const onPageSizeChange = (size: number) => {
|
||||
pageInfo.value.page_size = size;
|
||||
pageInfo.value.page = 1;
|
||||
options.onPageSizeChange?.(size);
|
||||
const onPageSizeChange = (current: number, size: number) => {
|
||||
// console.log('onPageSizeChange', current, size);
|
||||
// pageInfo.value.page_size = size;
|
||||
// pageInfo.value.page = 1;
|
||||
// options.onPageSizeChange?.(size);
|
||||
};
|
||||
const resetPageInfo = () => {
|
||||
pageInfo.value = cloneDeep(DEFAULT_PAGE_INFO)
|
||||
}
|
||||
pageInfo.value = cloneDeep(DEFAULT_PAGE_INFO);
|
||||
};
|
||||
|
||||
const rowSelection = computed(() => ({
|
||||
type: 'checkbox',
|
||||
|
||||
@ -55,7 +55,7 @@ const checkHasInviteCode = () => {
|
||||
|
||||
<template>
|
||||
<Layout :class="['layout-wrap', { mobile: appStore.hideMenu }]" class="h-full flex flex-col w-full">
|
||||
<JoinModal v-model:visible="joinEnterpriseVisible" ref="joinModalRef" />
|
||||
<JoinModal v-model:open="joinEnterpriseVisible" centered ref="joinModalRef" />
|
||||
<Layout.Header class="layout-header-wrap">
|
||||
<Navbar />
|
||||
</Layout.Header>
|
||||
|
||||
@ -1,4 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { Button, Result } from 'ant-design-vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
const router = useRouter();
|
||||
const back = () => {
|
||||
router.replace('/');
|
||||
@ -7,9 +10,9 @@ const back = () => {
|
||||
|
||||
<template>
|
||||
<div class="content">
|
||||
<a-result class="result" status="404" subtitle="页面跑路了" />
|
||||
<div class="operation-row">
|
||||
<a-button key="back" type="primary" @click="back">返回</a-button>
|
||||
<Result class="result" status="404" sub-title="页面跑路了" />
|
||||
<div class="operation-row flex justify-center">
|
||||
<Button key="back" type="primary" @click="back">返回</Button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -19,8 +22,10 @@ const back = () => {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
margin-left: -95px;
|
||||
margin-top: -121px;
|
||||
text-align: center;
|
||||
transform: translate(-50%, -50%);
|
||||
padding: 32px 32px 24px;
|
||||
// margin-left: -95px;
|
||||
// margin-top: -121px;
|
||||
// text-align: center;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -3,13 +3,14 @@
|
||||
* @Date: 2025-06-26 17:23:52
|
||||
-->
|
||||
<template>
|
||||
<a-modal
|
||||
v-model:visible="visible"
|
||||
<Modal
|
||||
v-model:open="visible"
|
||||
width="400px"
|
||||
modal-class="exit-account-modal"
|
||||
wrapClassName="exit-account-modal"
|
||||
show-close="false"
|
||||
:footer="false"
|
||||
@close="onClose"
|
||||
:footer="null"
|
||||
@cancel="onClose"
|
||||
centered
|
||||
>
|
||||
<div class="flex items-center mb-16px">
|
||||
<img :src="icon1" width="20" height="20" class="mr-12px" />
|
||||
@ -17,15 +18,14 @@
|
||||
</div>
|
||||
<p class="m-0 p-0 mb-24px s2 ml-32px">退出登录后,你将无法收到该账号的通知</p>
|
||||
<div class="flex items-center justify-end">
|
||||
<a-button class="!rounded-4px" size="medium" @click="onClose">返回</a-button>
|
||||
<a-button type="primary" class="ml-16px danger-btn" status="danger" size="medium" @click="onLogout"
|
||||
>退出登录</a-button
|
||||
>
|
||||
<Button @click="onClose">返回</Button>
|
||||
<Button danger type="primary" @click="onLogout" class="ml-8px">退出登录</Button>
|
||||
</div>
|
||||
</a-modal>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { Modal, Button, message } from 'ant-design-vue';
|
||||
import { ref } from 'vue';
|
||||
import { fetchLogOut } from '@/api/all/login';
|
||||
import { handleUserLogout } from '@/utils/user';
|
||||
@ -46,7 +46,7 @@ async function onLogout() {
|
||||
const { code } = await fetchLogOut();
|
||||
if (code === 200) {
|
||||
handleUserLogout();
|
||||
AMessage.success('退出登录成功');
|
||||
message.success('退出登录成功');
|
||||
onClose();
|
||||
}
|
||||
}
|
||||
@ -56,14 +56,14 @@ defineExpose({ open });
|
||||
|
||||
<style lang="scss">
|
||||
.exit-account-modal {
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--BG-300, #e6e6e8) !important;
|
||||
background-color: var(--BG-white, #fff) !important;
|
||||
box-shadow: 0px 2px 10px 0px rgba(0, 0, 0, 0.1);
|
||||
.arco-modal-header {
|
||||
// border-radius: 8px;
|
||||
// border: 1px solid var(--BG-300, #e6e6e8) !important;
|
||||
// background-color: var(--BG-white, #fff) !important;
|
||||
// box-shadow: 0px 2px 10px 0px rgba(0, 0, 0, 0.1);
|
||||
.ant-modal-header {
|
||||
display: none;
|
||||
}
|
||||
.arco-modal-body {
|
||||
.ant-modal-body {
|
||||
padding: 24px;
|
||||
.s1 {
|
||||
color: var(--Text-1, #211f24);
|
||||
@ -81,15 +81,6 @@ defineExpose({ open });
|
||||
font-weight: 400;
|
||||
line-height: 20px; /* 166.667% */
|
||||
}
|
||||
.cancel-btn {
|
||||
border-radius: 4px;
|
||||
}
|
||||
.danger-btn {
|
||||
background: var(--Functional-Danger-6, #f64b31) !important;
|
||||
&:hover {
|
||||
background: var(--Functional-Danger-6, #f64b31) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -10,33 +10,31 @@
|
||||
<div class="agent-entry mx-16px" :class="isAgentRoute ? 'agent' : ''" @click="handleAgentClick"></div>
|
||||
|
||||
<!-- 头像设置 -->
|
||||
<a-dropdown trigger="click" class="layout-avatar-dropdown">
|
||||
<a-avatar class="cursor-pointer" :size="32">
|
||||
<img alt="avatar" src="@/assets/avatar.svg" />
|
||||
</a-avatar>
|
||||
<template #content>
|
||||
<div>
|
||||
<a-doption>
|
||||
<a-space class="flex justify-between w-100%" @click="setServerMenu">
|
||||
<Dropdown trigger="click" overlayClassName="layout-avatar-dropdown">
|
||||
<img alt="avatar" src="@/assets/avatar.svg" class="cursor-pointer w-32px h-32px rounded-50%" />
|
||||
<template #overlay>
|
||||
<Menu>
|
||||
<MenuItem>
|
||||
<div class="h-full flex justify-between items-center w-100%" @click="setServerMenu">
|
||||
<div class="flex items-center">
|
||||
<img :src="icon1" class="w-16px h-16px mr-8px" />
|
||||
<span>管理中心</span>
|
||||
</div>
|
||||
<icon-right size="12" />
|
||||
</a-space>
|
||||
</a-doption>
|
||||
<a-dsubmenu value="option-1" position="lt" trigger="hover" class="enterprises-dsubmenu">
|
||||
<a-doption class="enterprises-doption">
|
||||
<a-space class="flex justify-between w-100%">
|
||||
</div>
|
||||
</MenuItem>
|
||||
<MenuItem>
|
||||
<SubMenu value="option-1" position="lt" trigger="hover" popupClassName="enterprises-dsubmenu">
|
||||
<template #title>
|
||||
<div class="flex justify-between w-100% h-full items-center">
|
||||
<div class="flex items-center">
|
||||
<img :src="icon3" class="w-16px h-16px mr-8px" />
|
||||
<span>切换企业账号</span>
|
||||
</div>
|
||||
<icon-right size="12" />
|
||||
</a-space>
|
||||
</a-doption>
|
||||
<template #content>
|
||||
<a-doption
|
||||
</div>
|
||||
</template>
|
||||
<MenuItem
|
||||
v-for="(item, index) in enterprises"
|
||||
:key="index"
|
||||
class="rounded-8px hover:bg-#F2F3F5"
|
||||
@ -49,21 +47,21 @@
|
||||
<span>{{ item.name }}</span>
|
||||
<icon-check v-if="enterpriseInfo?.id === item.id" size="16" />
|
||||
</div>
|
||||
</a-doption>
|
||||
</template>
|
||||
</a-dsubmenu>
|
||||
<a-doption>
|
||||
<a-space class="flex justify-between w-100%" @click="clickExit">
|
||||
</MenuItem>
|
||||
</SubMenu>
|
||||
</MenuItem>
|
||||
<MenuItem>
|
||||
<div class="flex justify-between w-100% h-full items-center" @click="clickExit">
|
||||
<div class="flex items-center">
|
||||
<img :src="icon2" class="w-16px h-16px mr-8px" />
|
||||
<span>退出登录</span>
|
||||
</div>
|
||||
<icon-right size="12" />
|
||||
</a-space>
|
||||
</a-doption>
|
||||
</div>
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
</Dropdown>
|
||||
|
||||
<ExitAccountModal ref="exitAccountModalRef" />
|
||||
<DownloadCenterModal ref="downloadCenterModalRef" />
|
||||
@ -71,6 +69,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { Dropdown, Menu, MenuItem, SubMenu } from 'ant-design-vue';
|
||||
import router from '@/router';
|
||||
import { useEnterpriseStore } from '@/stores/modules/enterprise';
|
||||
import { useSidebarStore } from '@/stores/modules/side-bar';
|
||||
@ -137,20 +136,37 @@ const handleAgentClick = () => {
|
||||
<style lang="scss">
|
||||
.layout-avatar-dropdown,
|
||||
.enterprises-dsubmenu {
|
||||
.arco-dropdown {
|
||||
.ant-dropdown-menu {
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--BG-300, #e6e6e8);
|
||||
background: var(--BG-white, #fff);
|
||||
padding: 12px 0px;
|
||||
.arco-dropdown-option {
|
||||
.ant-dropdown-menu-item {
|
||||
padding: 0 12px;
|
||||
margin-bottom: 4px;
|
||||
&-content {
|
||||
|
||||
.ant-dropdown-menu-title-content {
|
||||
display: flex;
|
||||
height: 40px;
|
||||
width: 100%;
|
||||
padding: 10px 24px;
|
||||
align-items: center;
|
||||
.ant-dropdown-menu-submenu {
|
||||
width: 100%;
|
||||
.ant-dropdown-menu-submenu-title {
|
||||
padding: 0;
|
||||
&:hover {
|
||||
background: none;
|
||||
}
|
||||
.ant-dropdown-menu-title-content {
|
||||
padding: 0 !important;
|
||||
}
|
||||
}
|
||||
.ant-dropdown-menu-submenu-arrow {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
.menu-item-text {
|
||||
color: var(--Text-2, #3c4043);
|
||||
font-family: $font-family-regular;
|
||||
@ -159,13 +175,12 @@ const handleAgentClick = () => {
|
||||
font-weight: 400;
|
||||
line-height: 22px;
|
||||
}
|
||||
}
|
||||
.arco-dropdown-option-content {
|
||||
.ant-dropdown-menu-title-content {
|
||||
border-radius: 8px !important;
|
||||
}
|
||||
&:not(.arco-dropdown-option-disabled):hover {
|
||||
&:not(.ant-dropdown-menu-item):hover {
|
||||
background-color: transparent;
|
||||
.arco-dropdown-option-content {
|
||||
.ant-dropdown-menu-title-content {
|
||||
background: var(--BG-200, #f2f3f5);
|
||||
}
|
||||
}
|
||||
@ -175,29 +190,29 @@ const handleAgentClick = () => {
|
||||
.layout-avatar-dropdown,
|
||||
.enterprises-dsubmenu {
|
||||
width: 200px;
|
||||
.arco-dropdown {
|
||||
.ant-dropdown-menu {
|
||||
padding: 12px 4px;
|
||||
.arco-dropdown-option {
|
||||
.ant-dropdown-menu-item {
|
||||
padding: 0 !important;
|
||||
&-content {
|
||||
.ant-dropdown-menu-title-content {
|
||||
padding: 0 12px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
.arco-dropdown-option-suffix {
|
||||
.ant-dropdown-option-suffix {
|
||||
display: none;
|
||||
}
|
||||
.enterprises-doption {
|
||||
.arco-dropdown-option-content {
|
||||
padding: 0 !important;
|
||||
border-radius: 8px;
|
||||
}
|
||||
&:not(.arco-dropdown-option-disabled):hover {
|
||||
background-color: transparent;
|
||||
.arco-dropdown-option-content {
|
||||
background: var(--BG-200, #f2f3f5);
|
||||
}
|
||||
}
|
||||
}
|
||||
// .enterprises-doption {
|
||||
// .ant-dropdown-menu-title-content {
|
||||
// padding: 0 !important;
|
||||
// border-radius: 8px;
|
||||
// }
|
||||
// &:not(.ant-dropdown-option-disabled):hover {
|
||||
// background-color: transparent;
|
||||
// .ant-dropdown-menu-title-content {
|
||||
// background: var(--BG-200, #f2f3f5);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,24 +1,24 @@
|
||||
<template>
|
||||
<a-modal
|
||||
v-model:visible="visible"
|
||||
<Modal
|
||||
v-model:open="visible"
|
||||
:title="isBatch ? '批量删除下载记录' : '删除下载记录'"
|
||||
width="400px"
|
||||
@close="onClose"
|
||||
@cancel="onClose"
|
||||
centered
|
||||
>
|
||||
<div class="flex items-center">
|
||||
<img :src="icon1" width="20" height="20" class="mr-12px" />
|
||||
<span>确认删除 {{ accountName }} 这条记录吗?</span>
|
||||
</div>
|
||||
<template #footer>
|
||||
<a-button size="large" @click="onClose">取消</a-button>
|
||||
<a-button type="primary" class="ml-16px !bg-#f64b31 !border-none" status="danger" size="large" @click="onDelete"
|
||||
>确定</a-button
|
||||
>
|
||||
<Button @click="onClose">取消</Button>
|
||||
<Button type="primary" danger @click="onDelete">确定</Button>
|
||||
</template>
|
||||
</a-modal>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { Modal, Button, message } from 'ant-design-vue';
|
||||
import { ref } from 'vue';
|
||||
import { deleteTask, deleteBatchTasks } from '@/api/all/common';
|
||||
import icon1 from '@/assets/img/media-account/icon-warn-1.png';
|
||||
@ -52,7 +52,7 @@ async function onDelete() {
|
||||
|
||||
const { code } = await _fn(_params);
|
||||
if (code === 200) {
|
||||
AMessage.success('删除成功');
|
||||
message.success('删除成功');
|
||||
isBatch.value ? emits('batchUpdate') : emits('update');
|
||||
onClose();
|
||||
}
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
<script lang="jsx">
|
||||
import { ref, computed } from 'vue';
|
||||
import { Input, Table, TableColumn, Checkbox, Pagination, Button, Tooltip, Notification } from '@arco-design/web-vue';
|
||||
import { Button, Checkbox, Input, Tooltip, Table, Pagination, message, notification } from 'ant-design-vue';
|
||||
import { IconSearch, IconClose, IconQuestionCircle } from '@arco-design/web-vue/es/icon';
|
||||
|
||||
import NoData from '@/components/no-data';
|
||||
import { getTask, postRedoTask, postBatchDownload, batchQueryTaskStatus } from '@/api/all/common';
|
||||
import { INITIAL_FORM, TABLE_COLUMNS } from './constants';
|
||||
@ -21,8 +22,6 @@ export default {
|
||||
dataSource,
|
||||
pageInfo,
|
||||
onPageChange,
|
||||
onPageSizeChange,
|
||||
rowSelection,
|
||||
handleSelect,
|
||||
handleSelectAll,
|
||||
DEFAULT_PAGE_INFO,
|
||||
@ -30,9 +29,6 @@ export default {
|
||||
onPageChange: () => {
|
||||
getData();
|
||||
},
|
||||
onPageSizeChange: () => {
|
||||
getData();
|
||||
},
|
||||
});
|
||||
let queryTaskTimer = null;
|
||||
|
||||
@ -75,10 +71,12 @@ export default {
|
||||
getData();
|
||||
};
|
||||
|
||||
const handleSorterChange = (column, order) => {
|
||||
query.value.sort_column = column;
|
||||
query.value.sort_order = order === 'ascend' ? 'asc' : 'desc';
|
||||
const handleSorterChange = (pagination, filters, sorter) => {
|
||||
if (sorter && !Array.isArray(sorter) && sorter.columnKey) {
|
||||
query.value.sort_column = sorter.columnKey;
|
||||
query.value.sort_order = sorter.order === 'ascend' ? 'asc' : 'desc';
|
||||
reload();
|
||||
}
|
||||
};
|
||||
|
||||
const handleSearch = () => {
|
||||
@ -137,10 +135,10 @@ export default {
|
||||
completeTaskNum++;
|
||||
|
||||
const notificationId = downloadTaskInfos.value.find((v) => v.id === id)?.randomId;
|
||||
notificationId && Notification.remove(notificationId);
|
||||
notificationId && notification.close(notificationId);
|
||||
|
||||
if (status === 1) {
|
||||
AMessage.success('批量下载已完成,正在下载文件...');
|
||||
message.success('批量下载已完成,正在下载文件...');
|
||||
downloadByUrl(file);
|
||||
} else if (status === 2) {
|
||||
const onReDownload = () => {
|
||||
@ -208,11 +206,11 @@ export default {
|
||||
<div class="filter-row-item flex items-center">
|
||||
<span class="label">操作人员</span>
|
||||
<Input
|
||||
v-model={query.value.operator_name}
|
||||
v-model:value={query.value.operator_name}
|
||||
class="w-240px"
|
||||
placeholder="请输入操作人员"
|
||||
size="medium"
|
||||
allow-clear
|
||||
size="middle"
|
||||
allowClear
|
||||
onChange={handleSearch}
|
||||
v-slots={{
|
||||
prefix: () => <IconSearch />,
|
||||
@ -222,11 +220,11 @@ export default {
|
||||
<div class="filter-row-item flex items-center">
|
||||
<span class="label">所属模块</span>
|
||||
<Input
|
||||
v-model={query.value.module}
|
||||
v-model:value={query.value.module}
|
||||
class="w-240px"
|
||||
placeholder="请输入所属模块"
|
||||
size="medium"
|
||||
allow-clear
|
||||
size="middle"
|
||||
allowClear
|
||||
onChange={handleSearch}
|
||||
v-slots={{
|
||||
prefix: () => <IconSearch />,
|
||||
@ -239,17 +237,17 @@ export default {
|
||||
{dataSource.value.length > 0 && selectedRows.value.length > 0 && (
|
||||
<div
|
||||
class={[
|
||||
'tip-row flex justify-between px-16px py-10px w-100% mb-16px h-42px',
|
||||
'tip-row flex justify-between px-16px py-10px w-100% mb-16px h-48px items-center',
|
||||
selectedRows.value.length > 0 ? ' selected' : '',
|
||||
].join('')}
|
||||
>
|
||||
<div class="flex items-center">
|
||||
<div class="flex items-center">
|
||||
<Checkbox
|
||||
modelValue={checkedAll.value}
|
||||
checked={checkedAll.value}
|
||||
indeterminate={indeterminate.value}
|
||||
class="mr-8px"
|
||||
onChange={handleSelectAll}
|
||||
onChange={(e) => handleSelectAll(e.target.checked)}
|
||||
/>
|
||||
<span class="label mr-24px">
|
||||
已选
|
||||
@ -271,45 +269,44 @@ export default {
|
||||
{/* 表格 */}
|
||||
<Table
|
||||
ref="tableRef"
|
||||
data={dataSource.value}
|
||||
column-resizable
|
||||
row-key="id"
|
||||
row-selection={rowSelection.value}
|
||||
selected-keys={selectedRowKeys.value}
|
||||
dataSource={dataSource.value}
|
||||
rowKey="id"
|
||||
rowSelection={{
|
||||
selectedRowKeys: selectedRowKeys.value,
|
||||
onSelect: handleSelect,
|
||||
onSelectAll: handleSelectAll,
|
||||
}}
|
||||
pagination={false}
|
||||
scroll={{ x: '100%', y: '100%' }}
|
||||
class="w-100% flex-1 overflow-hidden"
|
||||
bordered
|
||||
onSorterChange={handleSorterChange}
|
||||
onSelect={handleSelect}
|
||||
onSelectAll={handleSelectAll}
|
||||
showSorterTooltip={false}
|
||||
onChange={handleSorterChange}
|
||||
v-slots={{
|
||||
empty: () => <NoData />,
|
||||
columns: () => (
|
||||
<>
|
||||
emptyText: () => <NoData />,
|
||||
}}
|
||||
>
|
||||
{TABLE_COLUMNS.map((column) => (
|
||||
<TableColumn
|
||||
<Table.Column
|
||||
key={column.dataIndex}
|
||||
data-index={column.dataIndex}
|
||||
dataIndex={column.dataIndex}
|
||||
fixed={column.fixed}
|
||||
width={column.width}
|
||||
min-width={column.minWidth}
|
||||
sortable={column.sortable}
|
||||
minWidth={column.minWidth}
|
||||
sorter={column.sortable}
|
||||
align={column.align}
|
||||
ellipsis
|
||||
tooltip
|
||||
v-slots={{
|
||||
title: () => (
|
||||
<div class="flex items-center">
|
||||
title={() => (
|
||||
<>
|
||||
<span class="cts mr-4px">{column.title}</span>
|
||||
{column.tooltip && (
|
||||
<Tooltip content={column.tooltip} position="top">
|
||||
<Tooltip title={column.tooltip} placement="top">
|
||||
<IconQuestionCircle class="tooltip-icon color-#737478" size={16} />
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
),
|
||||
cell: ({ record }) => {
|
||||
</>
|
||||
)}
|
||||
customRender={({ record }) => {
|
||||
if (column.dataIndex === 'status') {
|
||||
return (
|
||||
<div class={['status-box', `status-box-${record.status}`]}>
|
||||
@ -323,17 +320,15 @@ export default {
|
||||
} else {
|
||||
return formatTableField(column, record, true);
|
||||
}
|
||||
},
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
<TableColumn
|
||||
data-index="operation"
|
||||
<Table.Column
|
||||
dataIndex="operation"
|
||||
width={dataSource.value.some((record) => record.status !== enumTaskStatus.Exporting) ? 120 : 60}
|
||||
fixed="right"
|
||||
title="操作"
|
||||
v-slots={{
|
||||
cell: ({ record }) => (
|
||||
customRender={({ record }) => (
|
||||
<div class="flex items-center">
|
||||
<img
|
||||
src={icon1}
|
||||
@ -343,32 +338,27 @@ export default {
|
||||
onClick={() => handleDelete(record)}
|
||||
/>
|
||||
{record.status !== enumTaskStatus.Exporting && (
|
||||
<Button type="outline" size="mini" class="search-btn" onClick={() => handleDownload(record)}>
|
||||
<Button type="primary" ghost size="small" class="search-btn" onClick={() => handleDownload(record)}>
|
||||
{record.status === enumTaskStatus.Failed ? '重新导出' : '下载'}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
),
|
||||
}}
|
||||
)}
|
||||
/>
|
||||
</Table>
|
||||
|
||||
{/* 分页 */}
|
||||
{pageInfo.value.total > 0 && (
|
||||
<div class="flex justify-end my-16px">
|
||||
<Pagination
|
||||
total={pageInfo.value.total}
|
||||
size="mini"
|
||||
show-total
|
||||
show-jumper
|
||||
show-page-size
|
||||
size="small"
|
||||
showTotal={(total, range) => `共 ${total} 条记录`}
|
||||
showQuickJumper
|
||||
showSizeChanger
|
||||
current={pageInfo.value.page}
|
||||
page-size={pageInfo.value.page_size}
|
||||
pageSize={pageInfo.value.page_size}
|
||||
onChange={onPageChange}
|
||||
onPageSizeChange={onPageSizeChange}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@ -1,25 +1,25 @@
|
||||
<template>
|
||||
<a-modal
|
||||
v-model:visible="visible"
|
||||
<Modal
|
||||
v-model:open="visible"
|
||||
:title="isBatch ? '批量删除导入记录' : '删除导入记录'"
|
||||
width="400px"
|
||||
@close="onClose"
|
||||
centered
|
||||
@cancel="onClose"
|
||||
>
|
||||
<div class="flex items-center">
|
||||
<img :src="icon1" width="20" height="20" class="mr-12px" />
|
||||
<span>确认删除 {{ accountName }} 这条记录吗?</span>
|
||||
</div>
|
||||
<template #footer>
|
||||
<a-button size="large" @click="onClose">取消</a-button>
|
||||
<a-button type="primary" class="ml-16px !bg-#f64b31 !border-none" status="danger" size="large" @click="onDelete"
|
||||
>确定</a-button
|
||||
>
|
||||
<Button @click="onClose">取消</Button>
|
||||
<Button type="primary" danger @click="onDelete">确定</Button>
|
||||
</template>
|
||||
</a-modal>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { Modal, Button, message } from 'ant-design-vue';
|
||||
import { deleteTask, deleteBatchTasks } from '@/api/all/common';
|
||||
import icon1 from '@/assets/img/media-account/icon-warn-1.png';
|
||||
|
||||
@ -51,7 +51,7 @@ async function onDelete() {
|
||||
const _params = isBatch.value ? { ids: taskId.value } : taskId.value;
|
||||
const { code } = await _fn(_params);
|
||||
if (code === 200) {
|
||||
AMessage.success('删除成功');
|
||||
message.success('删除成功');
|
||||
isBatch.value ? emits('batchUpdate') : emits('update');
|
||||
onClose();
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<script lang="jsx">
|
||||
import { ref, computed } from 'vue';
|
||||
import { Input, Table, TableColumn, Checkbox, Pagination, Button, Tooltip, Notification } from '@arco-design/web-vue';
|
||||
import { Button, Tooltip, Table, Pagination } from 'ant-design-vue';
|
||||
import { IconSearch, IconClose, IconQuestionCircle } from '@arco-design/web-vue/es/icon';
|
||||
import NoData from '@/components/no-data';
|
||||
import { getTask } from '@/api/all/common';
|
||||
@ -21,8 +21,6 @@ export default {
|
||||
dataSource,
|
||||
pageInfo,
|
||||
onPageChange,
|
||||
onPageSizeChange,
|
||||
rowSelection,
|
||||
handleSelect,
|
||||
handleSelectAll,
|
||||
DEFAULT_PAGE_INFO,
|
||||
@ -30,9 +28,6 @@ export default {
|
||||
onPageChange: () => {
|
||||
getData();
|
||||
},
|
||||
onPageSizeChange: () => {
|
||||
getData();
|
||||
},
|
||||
});
|
||||
|
||||
const query = ref({ ...INITIAL_FORM });
|
||||
@ -72,10 +67,12 @@ export default {
|
||||
getData();
|
||||
};
|
||||
|
||||
const handleSorterChange = (column, order) => {
|
||||
query.value.sort_column = column;
|
||||
query.value.sort_order = order === 'ascend' ? 'asc' : 'desc';
|
||||
const handleSorterChange = (pagination, filters, sorter) => {
|
||||
if (sorter && sorter.columnKey) {
|
||||
query.value.sort_column = sorter.columnKey;
|
||||
query.value.sort_order = sorter.order === 'ascend' ? 'asc' : 'desc';
|
||||
reload();
|
||||
}
|
||||
};
|
||||
|
||||
const handleSearch = () => {
|
||||
@ -118,112 +115,46 @@ export default {
|
||||
|
||||
return () => (
|
||||
<div class="import-task-wrap">
|
||||
{/* 筛选行 */}
|
||||
{/* <div class="filter-row flex mb-16px">
|
||||
<div class="filter-row-item flex items-center">
|
||||
<span class="label">操作人员</span>
|
||||
<Input
|
||||
v-model={query.value.operator_name}
|
||||
class="w-240px"
|
||||
placeholder="请输入操作人员"
|
||||
size="medium"
|
||||
allow-clear
|
||||
onChange={handleSearch}
|
||||
v-slots={{
|
||||
prefix: () => <IconSearch />,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div class="filter-row-item flex items-center">
|
||||
<span class="label">所属模块</span>
|
||||
<Input
|
||||
v-model={query.value.module}
|
||||
class="w-240px"
|
||||
placeholder="请输入所属模块"
|
||||
size="medium"
|
||||
allow-clear
|
||||
onChange={handleSearch}
|
||||
v-slots={{
|
||||
prefix: () => <IconSearch />,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div> */}
|
||||
|
||||
{/* 已选提示行 */}
|
||||
{/* {dataSource.value.length > 0 && selectedRows.value.length > 0 && (
|
||||
<div
|
||||
class={[
|
||||
'tip-row flex justify-between px-16px py-10px w-100% mb-16px h-42px',
|
||||
selectedRows.value.length > 0 ? ' selected' : '',
|
||||
].join('')}
|
||||
>
|
||||
<div class="flex items-center">
|
||||
<div class="flex items-center">
|
||||
<Checkbox
|
||||
modelValue={checkedAll.value}
|
||||
indeterminate={indeterminate.value}
|
||||
class="mr-8px"
|
||||
onChange={handleSelectAll}
|
||||
/>
|
||||
<span class="label mr-24px">
|
||||
已选
|
||||
<span class="color-#6D4CFE">{selectedRows.value.length}</span>
|
||||
个文件
|
||||
</span>
|
||||
<span class="operation-btn" onClick={handleBatchDownload}>
|
||||
批量下载
|
||||
</span>
|
||||
<span class="operation-btn red" onClick={handleBatchDelete}>
|
||||
批量删除
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<IconClose size={16} class="cursor-pointer color-#737478" onClick={handleCloseTip} />
|
||||
</div>
|
||||
)} */}
|
||||
|
||||
{/* 表格 */}
|
||||
<Table
|
||||
ref="tableRef"
|
||||
data={dataSource.value}
|
||||
column-resizable
|
||||
row-key="id"
|
||||
selected-keys={selectedRowKeys.value}
|
||||
dataSource={dataSource.value}
|
||||
rowKey="id"
|
||||
rowSelection={{
|
||||
selectedRowKeys: selectedRowKeys.value,
|
||||
onSelect: handleSelect,
|
||||
onSelectAll: handleSelectAll,
|
||||
}}
|
||||
pagination={false}
|
||||
scroll={{ x: '100%', y: '100%' }}
|
||||
class="w-100% flex-1 overflow-hidden"
|
||||
bordered
|
||||
onSorterChange={handleSorterChange}
|
||||
onSelect={handleSelect}
|
||||
onSelectAll={handleSelectAll}
|
||||
showSorterTooltip={false}
|
||||
onChange={handleSorterChange}
|
||||
v-slots={{
|
||||
empty: () => <NoData />,
|
||||
columns: () => (
|
||||
<>
|
||||
}}
|
||||
>
|
||||
{TABLE_COLUMNS.map((column) => (
|
||||
<TableColumn
|
||||
<Table.Column
|
||||
key={column.dataIndex}
|
||||
data-index={column.dataIndex}
|
||||
dataIndex={column.dataIndex}
|
||||
fixed={column.fixed}
|
||||
width={column.width}
|
||||
min-width={column.minWidth}
|
||||
sortable={column.sortable}
|
||||
minWidth={column.minWidth}
|
||||
sorter={column.sortable}
|
||||
align={column.align}
|
||||
ellipsis
|
||||
tooltip
|
||||
v-slots={{
|
||||
title: () => (
|
||||
<div class="flex items-center">
|
||||
title={() => (
|
||||
<>
|
||||
<span class="cts mr-4px">{column.title}</span>
|
||||
{column.tooltip && (
|
||||
<Tooltip content={column.tooltip} position="top">
|
||||
<Tooltip title={column.tooltip} placement="top">
|
||||
<IconQuestionCircle class="tooltip-icon color-#737478" size={16} />
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
),
|
||||
cell: ({ record }) => {
|
||||
</>
|
||||
)}
|
||||
customRender={({ record }) => {
|
||||
if (column.dataIndex === 'status') {
|
||||
return (
|
||||
<div class={['status-box', `status-box-${record.status}`]}>
|
||||
@ -234,22 +165,18 @@ export default {
|
||||
return <span>{record.operator?.name || record.operator?.mobile}</span>;
|
||||
} else if (column.dataIndex === 'created_at') {
|
||||
return exactFormatTime(record.created_at, 'YYYY-MM-DD HH:mm:ss', 'YYYY-MM-DD HH:mm:ss');
|
||||
} else if (column.dataIndex === 'fail_number') {
|
||||
return <span class={`${record.fail_number > 0 ? 'color-#F64B31' : ''}`}>{formatTableField(column, record, true)}</span>;
|
||||
} else {
|
||||
return formatTableField(column, record, true);
|
||||
}
|
||||
},
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
<TableColumn
|
||||
data-index="operation"
|
||||
width={dataSource.value.some((record) => record.fail_number > 0) ? 180 : 60}
|
||||
<Table.Column
|
||||
dataIndex="operation"
|
||||
width={dataSource.value.some((record) => record.status === enumTaskStatus.Failed) ? 180 : 60}
|
||||
fixed="right"
|
||||
title="操作"
|
||||
v-slots={{
|
||||
cell: ({ record }) => (
|
||||
customRender={({ record }) => (
|
||||
<div class="flex items-center">
|
||||
<img
|
||||
src={icon1}
|
||||
@ -258,33 +185,28 @@ export default {
|
||||
class="mr-8px cursor-pointer"
|
||||
onClick={() => handleDelete(record)}
|
||||
/>
|
||||
{record.fail_number > 0 && (
|
||||
<Button type="outline" size="mini" class="search-btn" onClick={() => handleDownload(record)}>
|
||||
{record.status === enumTaskStatus.Failed && (
|
||||
<Button type="primary" ghost size="small" class="search-btn" onClick={() => handleDownload(record)}>
|
||||
下载问题表格
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
),
|
||||
}}
|
||||
)}
|
||||
/>
|
||||
</Table>
|
||||
|
||||
{/* 分页 */}
|
||||
{pageInfo.value.total > 0 && (
|
||||
<div class="flex justify-end my-16px">
|
||||
<Pagination
|
||||
total={pageInfo.value.total}
|
||||
size="mini"
|
||||
show-total
|
||||
show-jumper
|
||||
show-page-size
|
||||
size="small"
|
||||
showTotal={(total, range) => `共 ${total} 条记录`}
|
||||
showQuickJumper
|
||||
showSizeChanger
|
||||
current={pageInfo.value.page}
|
||||
page-size={pageInfo.value.page_size}
|
||||
pageSize={pageInfo.value.page_size}
|
||||
onChange={onPageChange}
|
||||
onPageSizeChange={onPageSizeChange}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@ -1,37 +1,39 @@
|
||||
<template>
|
||||
<a-modal
|
||||
v-model:visible="visible"
|
||||
<Modal
|
||||
v-model:open="visible"
|
||||
title="任务中心"
|
||||
modal-class="task-center-modal"
|
||||
wrapClassName="task-center-modal"
|
||||
width="860px"
|
||||
:mask-closable="false"
|
||||
:footer="false"
|
||||
@close="onClose"
|
||||
:footer="null"
|
||||
@cancel="onClose"
|
||||
centered
|
||||
>
|
||||
<a-tabs :active-key="activeTab" @tab-click="handleTabClick">
|
||||
<a-tab-pane key="0" title="导入"> </a-tab-pane>
|
||||
<a-tab-pane key="1" title="导出"> </a-tab-pane>
|
||||
</a-tabs>
|
||||
<Tabs v-model:activeKey="activeTab" @change="handleTabClick">
|
||||
<TabPane key="1" tab="导入"> </TabPane>
|
||||
<TabPane key="2" tab="导出"> </TabPane>
|
||||
</Tabs>
|
||||
<div class="content">
|
||||
<component :is="activeTab === '0' ? ImportTask : ExportTask" ref="componentRef" />
|
||||
<component :is="activeTab === '1' ? ImportTask : ExportTask" ref="componentRef" />
|
||||
</div>
|
||||
</a-modal>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { Notification } from '@arco-design/web-vue';
|
||||
import { Checkbox, Modal, Button, Tabs, notification } from 'ant-design-vue';
|
||||
const { TabPane } = Tabs;
|
||||
|
||||
import ExportTask from './components/export-task';
|
||||
import ImportTask from './components/import-task';
|
||||
|
||||
const visible = ref(false);
|
||||
const componentRef = ref(null);
|
||||
const activeTab = ref('0');
|
||||
const activeTab = ref('1');
|
||||
|
||||
let timer = null;
|
||||
|
||||
const handleTabClick = (key) => {
|
||||
activeTab.value = key;
|
||||
// activeTab.value = key;
|
||||
nextTick(() => {
|
||||
getData();
|
||||
});
|
||||
@ -42,20 +44,21 @@ const getData = () => {
|
||||
};
|
||||
|
||||
const open = () => {
|
||||
visible.value = true;
|
||||
nextTick(() => {
|
||||
getData();
|
||||
});
|
||||
|
||||
timer = setInterval(() => {
|
||||
getData();
|
||||
}, 10000);
|
||||
|
||||
visible.value = true;
|
||||
};
|
||||
const onClose = () => {
|
||||
activeTab.value = '0';
|
||||
|
||||
clearTimer();
|
||||
componentRef.value?.unloadComp?.();
|
||||
Notification.clear();
|
||||
notification.destroy();
|
||||
visible.value = false;
|
||||
};
|
||||
const clearTimer = () => {
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
.task-center-modal {
|
||||
.arco-modal-header {
|
||||
.ant-modal-header {
|
||||
border-bottom: none !important;
|
||||
.arco-modal-title {
|
||||
.ant-modal-title {
|
||||
color: var(--Text-1, #211f24);
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
@ -10,7 +10,7 @@
|
||||
font-family: $font-family-medium;
|
||||
}
|
||||
}
|
||||
.arco-modal-body {
|
||||
.ant-modal-body {
|
||||
padding: 0 !important;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
10
src/main.ts
10
src/main.ts
@ -7,17 +7,21 @@ import router from './router';
|
||||
import store from './stores';
|
||||
import * as directives from '@/directives';
|
||||
|
||||
import VueLazyLoad from "vue-lazyload";
|
||||
import NoData from '@/components/no-data/index.vue';
|
||||
import SvgIcon from '@/components/svg-icon/index.vue';
|
||||
|
||||
import '@/api/index';
|
||||
import '@arco-design/web-vue/dist/arco.css'; // Arco 默认样式
|
||||
import './core';
|
||||
import '@arco-design/web-vue/dist/arco.css'; // 已移除 Arco 样式
|
||||
|
||||
import 'normalize.css';
|
||||
import 'uno.css';
|
||||
import 'virtual:svg-icons-register';
|
||||
|
||||
import errorImage from '@/assets/img/error-img.png';
|
||||
import loadingImage from '@/assets/img/error-img.png';
|
||||
|
||||
// import '@/styles/vars.css'; // 优先加载
|
||||
|
||||
const app = createApp(App);
|
||||
@ -28,6 +32,10 @@ app.component('SvgIcon', SvgIcon);
|
||||
|
||||
app.use(store);
|
||||
app.use(router);
|
||||
app.use(VueLazyLoad, {
|
||||
error: errorImage,
|
||||
loading: loadingImage,
|
||||
});
|
||||
|
||||
Object.values(directives).forEach((directive) => {
|
||||
app.use(directive);
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
* @Date: 2025-06-22 22:59:16
|
||||
*/
|
||||
import type { Router } from 'vue-router';
|
||||
import { message } from 'ant-design-vue';
|
||||
import NProgress from 'nprogress';
|
||||
import { goUserLogin } from '@/utils/user';
|
||||
// import router from '@/router';
|
||||
@ -29,7 +30,7 @@ export default function setupUserLoginInfoGuard(router: Router) {
|
||||
// if (requiresAuth) {
|
||||
// const hasPermission = checkRoutePermission(routeName);
|
||||
// if (!hasPermission) {
|
||||
// AMessage.error('您没有权限访问该页面');
|
||||
// message.error('您没有权限访问该页面');
|
||||
// next('/');
|
||||
// return;
|
||||
// }
|
||||
|
||||
@ -1,89 +1,89 @@
|
||||
// import { IconBookmark } from '@arco-design/web-vue/es/icon';
|
||||
// import type { AppRouteRecordRaw } from '../types';
|
||||
// import { MENU_GROUP_IDS } from '@/router/constants';
|
||||
import { IconBookmark } from '@arco-design/web-vue/es/icon';
|
||||
import type { AppRouteRecordRaw } from '../types';
|
||||
import { MENU_GROUP_IDS } from '@/router/constants';
|
||||
|
||||
// const COMPONENTS: AppRouteRecordRaw[] = [
|
||||
// {
|
||||
// path: '/dataEngine',
|
||||
// name: 'DataEngine',
|
||||
// redirect: 'dataEngine/hotTranslation',
|
||||
// meta: {
|
||||
// locale: '全域数据引擎',
|
||||
// icon: IconBookmark,
|
||||
// requiresAuth: true,
|
||||
// requireLogin: true,
|
||||
// roles: ['*'],
|
||||
// id: MENU_GROUP_IDS.DATA_ENGINE_ID,
|
||||
// },
|
||||
// children: [
|
||||
// {
|
||||
// path: 'hotTranslation',
|
||||
// name: 'DataEngineHotTranslation',
|
||||
// meta: {
|
||||
// locale: '行业热门话题洞察',
|
||||
// requiresAuth: true,
|
||||
// requireLogin: true,
|
||||
// roles: ['*'],
|
||||
// },
|
||||
// component: () => import('@/views/components/dataEngine/hotTranslation.vue'),
|
||||
// },
|
||||
// {
|
||||
// path: 'hotCloud',
|
||||
// name: 'DataEngineHotCloud',
|
||||
// meta: {
|
||||
// locale: '行业词云',
|
||||
// requiresAuth: true,
|
||||
// requireLogin: true,
|
||||
// roles: ['*'],
|
||||
// },
|
||||
// component: () => import('@/views/components/dataEngine/hotCloud.vue'),
|
||||
// },
|
||||
// {
|
||||
// path: 'keyWord',
|
||||
// name: 'DataEngineKeyWord',
|
||||
// meta: {
|
||||
// locale: '行业关键词动向',
|
||||
// requiresAuth: true,
|
||||
// requireLogin: true,
|
||||
// roles: ['*'],
|
||||
// },
|
||||
// component: () => import('@/views/components/dataEngine/keyWord.vue'),
|
||||
// },
|
||||
// {
|
||||
// path: 'userPainPoints',
|
||||
// name: 'DataEngineUserPainPoints',
|
||||
// meta: {
|
||||
// locale: '用户痛点观察',
|
||||
// requiresAuth: true,
|
||||
// requireLogin: true,
|
||||
// roles: ['*'],
|
||||
// },
|
||||
// component: () => import('@/views/components/dataEngine/userPainPoints.vue'),
|
||||
// },
|
||||
// {
|
||||
// path: 'keyBrandMovement',
|
||||
// name: 'DataEngineKeyBrandMovement',
|
||||
// meta: {
|
||||
// locale: '重点品牌动向',
|
||||
// requiresAuth: true,
|
||||
// requireLogin: true,
|
||||
// roles: ['*'],
|
||||
// },
|
||||
// component: () => import('@/views/components/dataEngine/keyBrandMovement.vue'),
|
||||
// },
|
||||
// {
|
||||
// path: 'userPersona',
|
||||
// name: 'DataEngineUserPersona',
|
||||
// meta: {
|
||||
// locale: '用户画像',
|
||||
// requiresAuth: true,
|
||||
// requireLogin: true,
|
||||
// roles: ['*'],
|
||||
// },
|
||||
// component: () => import('@/views/components/dataEngine/userPersona.vue'),
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// ];
|
||||
const COMPONENTS: AppRouteRecordRaw[] = [
|
||||
{
|
||||
path: '/dataEngine',
|
||||
name: 'DataEngine',
|
||||
redirect: 'dataEngine/hotTranslation',
|
||||
meta: {
|
||||
locale: '全域数据引擎',
|
||||
icon: IconBookmark,
|
||||
requiresAuth: true,
|
||||
requireLogin: true,
|
||||
roles: ['*'],
|
||||
id: MENU_GROUP_IDS.DATA_ENGINE_ID,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'hotTranslation',
|
||||
name: 'DataEngineHotTranslation',
|
||||
meta: {
|
||||
locale: '行业热门话题洞察',
|
||||
requiresAuth: true,
|
||||
requireLogin: true,
|
||||
roles: ['*'],
|
||||
},
|
||||
component: () => import('@/views/components/dataEngine/hotTranslation.vue'),
|
||||
},
|
||||
{
|
||||
path: 'hotCloud',
|
||||
name: 'DataEngineHotCloud',
|
||||
meta: {
|
||||
locale: '行业词云',
|
||||
requiresAuth: true,
|
||||
requireLogin: true,
|
||||
roles: ['*'],
|
||||
},
|
||||
component: () => import('@/views/components/dataEngine/hotCloud.vue'),
|
||||
},
|
||||
{
|
||||
path: 'keyWord',
|
||||
name: 'DataEngineKeyWord',
|
||||
meta: {
|
||||
locale: '行业关键词动向',
|
||||
requiresAuth: true,
|
||||
requireLogin: true,
|
||||
roles: ['*'],
|
||||
},
|
||||
component: () => import('@/views/components/dataEngine/keyWord.vue'),
|
||||
},
|
||||
{
|
||||
path: 'userPainPoints',
|
||||
name: 'DataEngineUserPainPoints',
|
||||
meta: {
|
||||
locale: '用户痛点观察',
|
||||
requiresAuth: true,
|
||||
requireLogin: true,
|
||||
roles: ['*'],
|
||||
},
|
||||
component: () => import('@/views/components/dataEngine/userPainPoints.vue'),
|
||||
},
|
||||
{
|
||||
path: 'keyBrandMovement',
|
||||
name: 'DataEngineKeyBrandMovement',
|
||||
meta: {
|
||||
locale: '重点品牌动向',
|
||||
requiresAuth: true,
|
||||
requireLogin: true,
|
||||
roles: ['*'],
|
||||
},
|
||||
component: () => import('@/views/components/dataEngine/keyBrandMovement.vue'),
|
||||
},
|
||||
{
|
||||
path: 'userPersona',
|
||||
name: 'DataEngineUserPersona',
|
||||
meta: {
|
||||
locale: '用户画像',
|
||||
requiresAuth: true,
|
||||
requireLogin: true,
|
||||
roles: ['*'],
|
||||
},
|
||||
component: () => import('@/views/components/dataEngine/userPersona.vue'),
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
// export default COMPONENTS;
|
||||
export default COMPONENTS;
|
||||
|
||||
@ -7,9 +7,9 @@ import { MENU_GROUP_IDS } from '@/router/constants';
|
||||
|
||||
import IconRepository from '@/assets/svg/svg-repository.svg';
|
||||
import IconMediaAccount from '@/assets/svg/svg-mediaAccount.svg';
|
||||
import IconPutAccount from '@/assets/svg/svg-putAccount.svg';
|
||||
import IconIntelligentSolution from '@/assets/svg/svg-intelligentSolution.svg';
|
||||
import IconProjectManagement from '@/assets/svg/svg-projectManagement.svg';
|
||||
// import IconPutAccount from '@/assets/svg/svg-putAccount.svg';
|
||||
// import IconIntelligentSolution from '@/assets/svg/svg-intelligentSolution.svg';
|
||||
// import IconProjectManagement from '@/assets/svg/svg-projectManagement.svg';
|
||||
|
||||
const COMPONENTS: AppRouteRecordRaw[] = [
|
||||
{
|
||||
@ -184,77 +184,6 @@ const COMPONENTS: AppRouteRecordRaw[] = [
|
||||
},
|
||||
component: () => import('@/views/property-marketing/assignment-management/index.vue'),
|
||||
},
|
||||
{
|
||||
path: '/put-account',
|
||||
name: 'PutAccount',
|
||||
redirect: 'put-account/accountManagement',
|
||||
meta: {
|
||||
locale: '投放资源中心',
|
||||
icon: IconPutAccount,
|
||||
requiresAuth: true,
|
||||
requireLogin: true,
|
||||
roles: ['*'],
|
||||
id: MENU_GROUP_IDS.PROPERTY_ID,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'manage',
|
||||
name: 'PutAccountAccountManagement',
|
||||
meta: {
|
||||
locale: '账户管理',
|
||||
requiresAuth: true,
|
||||
requireLogin: true,
|
||||
roles: ['*'],
|
||||
},
|
||||
component: () => import('@/views/property-marketing/put-account/account-manage/index.vue'),
|
||||
},
|
||||
{
|
||||
path: 'data',
|
||||
name: 'PutAccountAccountData',
|
||||
meta: {
|
||||
locale: '账户数据',
|
||||
requiresAuth: true,
|
||||
requireLogin: true,
|
||||
roles: ['*'],
|
||||
},
|
||||
component: () => import('@/views/property-marketing/put-account/account-data/index.vue'),
|
||||
},
|
||||
{
|
||||
path: 'account-dashboard',
|
||||
name: 'PutAccountAccountDashboard',
|
||||
meta: {
|
||||
locale: '投放表现分析',
|
||||
requiresAuth: true,
|
||||
requireLogin: true,
|
||||
roles: ['*'],
|
||||
},
|
||||
component: () => import('@/views/property-marketing/put-account/account-dashboard/index.vue'),
|
||||
},
|
||||
{
|
||||
path: 'investmentGuidelines',
|
||||
name: 'PutAccountInvestmentGuidelines',
|
||||
meta: {
|
||||
locale: '投放指南',
|
||||
requiresAuth: true,
|
||||
requireLogin: true,
|
||||
roles: ['*'],
|
||||
},
|
||||
component: () => import('@/views/property-marketing/put-account/investment-guidelines/index.vue'),
|
||||
},
|
||||
{
|
||||
path: 'detail/:id',
|
||||
name: 'guideDetail',
|
||||
meta: {
|
||||
locale: '投放指南详情',
|
||||
requiresAuth: true,
|
||||
hideInMenu: true,
|
||||
roles: ['*'],
|
||||
activeMenu: 'PutAccountInvestmentGuidelines',
|
||||
},
|
||||
component: () => import('@/views/property-marketing/put-account/investment-guidelines/detail.vue'),
|
||||
},
|
||||
],
|
||||
},
|
||||
// {
|
||||
// path: '/intelligent-solution',
|
||||
// name: 'IntelligentSolution',
|
||||
@ -292,32 +221,32 @@ const COMPONENTS: AppRouteRecordRaw[] = [
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
{
|
||||
path: '/project-manage',
|
||||
name: 'ProjectManagement',
|
||||
redirect: 'project-manage/project-list',
|
||||
meta: {
|
||||
locale: '项目管理',
|
||||
icon: IconProjectManagement,
|
||||
requiresAuth: true,
|
||||
requireLogin: true,
|
||||
roles: ['*'],
|
||||
id: MENU_GROUP_IDS.PROPERTY_ID,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'project-list',
|
||||
name: 'ProjectList',
|
||||
meta: {
|
||||
locale: '项目列表',
|
||||
requiresAuth: true,
|
||||
requireLogin: true,
|
||||
roles: ['*'],
|
||||
},
|
||||
component: () => import('@/views/property-marketing/project-manage/project-list/index.vue'),
|
||||
},
|
||||
],
|
||||
},
|
||||
// {
|
||||
// path: '/project-manage',
|
||||
// name: 'ProjectManagement',
|
||||
// redirect: 'project-manage/project-list',
|
||||
// meta: {
|
||||
// locale: '项目管理',
|
||||
// icon: IconProjectManagement,
|
||||
// requiresAuth: true,
|
||||
// requireLogin: true,
|
||||
// roles: ['*'],
|
||||
// id: MENU_GROUP_IDS.PROPERTY_ID,
|
||||
// },
|
||||
// children: [
|
||||
// {
|
||||
// path: 'project-list',
|
||||
// name: 'ProjectList',
|
||||
// meta: {
|
||||
// locale: '项目列表',
|
||||
// requiresAuth: true,
|
||||
// requireLogin: true,
|
||||
// roles: ['*'],
|
||||
// },
|
||||
// component: () => import('@/views/property-marketing/project-manage/project-list/index.vue'),
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
];
|
||||
|
||||
export default COMPONENTS;
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
* @Date: 2025-06-23 03:56:22
|
||||
*/
|
||||
import { defineStore } from 'pinia';
|
||||
import type { AppState, RouteRecordNormalized, NotificationReturn } from './types';
|
||||
import type { AppState, RouteRecordNormalized } from './types';
|
||||
|
||||
import defaultSettings from '@/config/settings.json';
|
||||
import type { AppRouteRecordRaw } from '@/router/routes/types';
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
import type { RouteRecordNormalized } from 'vue-router';
|
||||
import type { NotificationReturn } from '@arco-design/web-vue/es/notification/interface';
|
||||
import type { AppRouteRecordRaw } from '@/router/routes/types';
|
||||
|
||||
export { RouteRecordNormalized, NotificationReturn };
|
||||
export { RouteRecordNormalized };
|
||||
|
||||
export interface AppState {
|
||||
theme: string;
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
.arco-modal {
|
||||
.arco-modal-header {
|
||||
.ant-modal {
|
||||
.ant-modal-header {
|
||||
border-bottom: 1px solid var(--Border-1, #d7d7d9);
|
||||
height: 56px;
|
||||
padding: 0 20px;
|
||||
.arco-modal-title {
|
||||
.ant-modal-title {
|
||||
font-family: $font-family-medium;
|
||||
color: #211f24;
|
||||
font-size: 16px;
|
||||
@ -13,11 +13,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
.arco-modal-body {
|
||||
.ant-modal-body {
|
||||
padding: 24px 20px;
|
||||
}
|
||||
|
||||
.arco-modal-footer {
|
||||
.ant-modal-footer {
|
||||
display: flex;
|
||||
height: 64px;
|
||||
padding: 0px 20px;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { Notification } from '@arco-design/web-vue';
|
||||
import { notification } from 'ant-design-vue';
|
||||
import { downloadByUrl } from '@/utils/tools';
|
||||
import { IconLoading } from '@arco-design/web-vue/es/icon';
|
||||
|
||||
@ -16,30 +16,30 @@ interface RenderNotificationData {
|
||||
|
||||
// 下载通知框
|
||||
export function showExportNotification(label: string, others: { id?: string, duration?: number }) {
|
||||
const { id = '', duration = 3000 } = others ?? {}
|
||||
Notification.warning({
|
||||
id,
|
||||
showIcon: false,
|
||||
closable: true,
|
||||
content: () => (
|
||||
const { id = '', duration = 3 } = others ?? {}
|
||||
notification.warning({
|
||||
key: id,
|
||||
icon: () => null,
|
||||
message: () => null,
|
||||
description: (
|
||||
<div class="flex items-center pr-16px">
|
||||
<IconLoading size={16} class="color-#6D4CFE mr-8px" />
|
||||
<p class="text-14px lh-22px font-400 color-#211F24">{label}</p>
|
||||
</div>
|
||||
),
|
||||
duration,
|
||||
class: 'px-16px py-9px w-450px rounded-2px bg-#F0EDFF',
|
||||
class: 'w-450px rounded-2px bg-#F0EDFF',
|
||||
});
|
||||
}
|
||||
|
||||
// 下载失败框
|
||||
export function showFailExportNotification(label: string, others: { id?: string, duration?: number, onReDownload?: Function }) {
|
||||
const { id = '', duration = 0, onReDownload } = others ?? {}
|
||||
Notification.warning({
|
||||
id,
|
||||
showIcon: false,
|
||||
closable: true,
|
||||
content: () => (
|
||||
notification.warning({
|
||||
key: id,
|
||||
icon: () => null,
|
||||
message: () => null,
|
||||
description: (
|
||||
<div class="flex items-center justify-between pr-16px">
|
||||
<div class="flex items-center mr-10px">
|
||||
<img src={icon3} width={16} height={16} class=" mr-8px" />
|
||||
@ -50,7 +50,7 @@ export function showFailExportNotification(label: string, others: { id?: string,
|
||||
</div>
|
||||
),
|
||||
duration,
|
||||
class: 'px-16px py-9px w-500px rounded-2px bg-#FFE9E7',
|
||||
class: 'w-500px rounded-2px bg-#FFE9E7',
|
||||
});
|
||||
}
|
||||
|
||||
@ -61,10 +61,10 @@ export const showImportResultNotification = (data: RenderNotificationData) => {
|
||||
file && downloadByUrl(file);
|
||||
};
|
||||
|
||||
Notification.warning({
|
||||
showIcon: false,
|
||||
closable: true,
|
||||
content: () => (
|
||||
notification.warning({
|
||||
icon: () => null,
|
||||
message: () => null,
|
||||
description: (
|
||||
<div>
|
||||
<div class="flex items-center mb-4px">
|
||||
<img src={hasError ? icon1 : icon2} width="16" height="16" class="mr-8px" />
|
||||
@ -89,6 +89,6 @@ export const showImportResultNotification = (data: RenderNotificationData) => {
|
||||
</div>
|
||||
),
|
||||
duration: 3000,
|
||||
class: `px-16px py-16px w-400px rounded-2px ${hasError ? 'bg-#FFF7E5' : 'bg-#EBF7F2'}`,
|
||||
class: `w-400px rounded-2px ${hasError ? 'bg-#FFF7E5' : 'bg-#EBF7F2'}`,
|
||||
});
|
||||
};
|
||||
@ -7,9 +7,9 @@
|
||||
</div>
|
||||
<div class="info-section">
|
||||
<div class="title-group">
|
||||
<a-tooltip :content="cozeInfo.name">
|
||||
<Tooltip :title="cozeInfo.name">
|
||||
<div class="title">{{ cozeInfo.name }}</div>
|
||||
</a-tooltip>
|
||||
</Tooltip>
|
||||
<div class="tag">
|
||||
<div>
|
||||
<img class="status-icon" :src="chatbotIcon" />
|
||||
@ -18,12 +18,8 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="usage-info">
|
||||
<a-space>
|
||||
<span class="count">{{ cozeInfo.views }}</span>
|
||||
</a-space>
|
||||
<a-space>
|
||||
<span class="label"> 次使用 </span>
|
||||
</a-space>
|
||||
</div>
|
||||
</div>
|
||||
<div class="description-section">
|
||||
@ -36,6 +32,7 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { defineProps } from 'vue';
|
||||
import { Tooltip } from 'ant-design-vue';
|
||||
import chatbotIcon from '@/assets/svg/chatbot.svg';
|
||||
|
||||
const props = defineProps({
|
||||
|
||||
@ -36,9 +36,9 @@
|
||||
<div class="body">
|
||||
<div class="">
|
||||
<div class="toggle-btn cursor-pointer" @click="toggleCollapse">
|
||||
<a-tooltip :content="isCollapsed ? '展开' : '折叠'">
|
||||
<Tooltip :title="isCollapsed ? '展开' : '折叠'">
|
||||
<img class="status-icon" :src="isCollapsed ? menuUnfold : menuFold" />
|
||||
</a-tooltip>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -55,6 +55,7 @@ import { getChatAgent } from '@/api/all/agent';
|
||||
import { useRouter } from 'vue-router';
|
||||
import menuFold from '@/assets/svg/menu-fold.svg';
|
||||
import menuUnfold from '@/assets/svg/menu-unfold.svg';
|
||||
import { Tooltip } from 'ant-design-vue';
|
||||
import { formatNumberShow } from '@/utils/tools';
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="agent-wrap relative h-full pl-16px">
|
||||
<a-input
|
||||
v-model="query.name"
|
||||
<Input
|
||||
v-model:value="query.name"
|
||||
@press-enter="getData()"
|
||||
placeholder="搜索智能体"
|
||||
size="large"
|
||||
@ -11,31 +11,32 @@
|
||||
<template #prefix>
|
||||
<icon-search @click="getData()" />
|
||||
</template>
|
||||
</a-input>
|
||||
</Input>
|
||||
<div v-for="(item, index) in list" :key="index">
|
||||
<p class="span-title w-fit mb-16px">{{ item.name }}</p>
|
||||
<a-row class="grid-demo" :gutter="[20, 16]" v-if="item.agent_products.length > 0">
|
||||
<a-col :xs="24"
|
||||
:sm="12"
|
||||
:md="8"
|
||||
:lg="5"
|
||||
:xl="6"
|
||||
:xxl="4"
|
||||
v-for="(product, k) in item.agent_products" :key="k">
|
||||
<Row class="grid-demo" :gutter="[20, 16]" v-if="item.agent_products.length > 0">
|
||||
<Col :xs="24" :sm="12" :md="8" :lg="5" :xl="6" :xxl="4" v-for="(product, k) in item.agent_products" :key="k">
|
||||
<div class="card-container cursor-pointer !h-252px" @click="goDetail(product?.type, product?.id)">
|
||||
<div class="card-image h-120px w-100% bg-cover bg-center mb-8px" v-image-main-color="product.image_url">
|
||||
<img class="object-contain h-full w-100% " :src="product?.image_url"/>
|
||||
<img class="object-contain h-full w-100%" :src="product?.image_url" />
|
||||
</div>
|
||||
|
||||
<div class="card-content w-full">
|
||||
<TextoverTips :context="product.name" class="card-title mb-4px !text-16px"/>
|
||||
<TextoverTips :context="product.description" class="card-description mb-8px color-#737478 text-14px lh-22px font-400" :line="2" />
|
||||
<TextOverTips :context="product.name" class="card-title mb-4px !text-16px" />
|
||||
<TextOverTips
|
||||
:context="product.description"
|
||||
class="card-description mb-8px color-#737478 text-14px lh-22px font-400"
|
||||
:line="2"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="card-footer">
|
||||
<div
|
||||
:class="['status-tag', product.type === 1 ? 'blue-tag' : 'red-tag']"
|
||||
:style="{ background: product.type === 1 ? 'var(--Functional-Blue-1, #F0EDFF)' : 'var(--Functional-Red-1, #FFE9E7)' }"
|
||||
:style="{
|
||||
background:
|
||||
product.type === 1 ? 'var(--Functional-Blue-1, #F0EDFF)' : 'var(--Functional-Red-1, #FFE9E7)',
|
||||
}"
|
||||
data-size="mini-20px"
|
||||
>
|
||||
<SvgIcon
|
||||
@ -48,13 +49,15 @@
|
||||
<div class="status-text">{{ product.type === 1 ? '对话式' : '工作流' }}</div>
|
||||
</div>
|
||||
<div class="usage-info">
|
||||
<div class="usage-count mr-2px">{{ formatNumberShow({ value: product?.views, showExactValue: true }) }}</div>
|
||||
<div class="usage-count mr-2px">
|
||||
{{ formatNumberShow({ value: product?.views, showExactValue: true }) }}
|
||||
</div>
|
||||
<div class="usage-label">次使用</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<NoData v-else />
|
||||
</div>
|
||||
@ -62,10 +65,11 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Input, Row, Col } from 'ant-design-vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { getAgentList } from '@/api/all/agent';
|
||||
import { formatNumberShow } from "@/utils/tools";
|
||||
import TextoverTips from "@/components/text-over-tips";
|
||||
import { formatNumberShow } from '@/utils/tools';
|
||||
import TextOverTips from '@/components/text-over-tips';
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
|
||||
@ -1,29 +1,26 @@
|
||||
<template>
|
||||
<div class="form-container">
|
||||
<a-form :model="formData" ref="formRef" layout="vertical">
|
||||
<a-form-item
|
||||
<Form :model="formData" ref="formRef" layout="vertical">
|
||||
<FormItem
|
||||
v-for="(field, index) in formFields"
|
||||
:key="index"
|
||||
:label="field.props.label"
|
||||
:field="field.props.name"
|
||||
:name="field.props.name"
|
||||
:rules="field.props.rules"
|
||||
:tooltip="field.props.tip"
|
||||
>
|
||||
<a-input
|
||||
<Input
|
||||
allowClear
|
||||
v-if="field.type === 'input'"
|
||||
v-model="formData[field.props.name]"
|
||||
v-model:value="formData[field.props.name]"
|
||||
:placeholder="field?.props?.placeholder"
|
||||
/>
|
||||
<a-textarea
|
||||
<TextArea
|
||||
v-if="field.type === 'textarea'"
|
||||
style="width: 500px; height: 200px"
|
||||
v-model="formData[field.props.name]"
|
||||
v-model:value="formData[field.props.name]"
|
||||
:placeholder="field?.props?.placeholder"
|
||||
/>
|
||||
<!-- <a-color-picker v-if="field.type === 'color_picker'"
|
||||
style="width: 500px; height: 200px"
|
||||
v-model="formData[field.props.name]" /> -->
|
||||
<ImageUpload
|
||||
v-if="field.type == 'upload_image'"
|
||||
v-model="formData[field.props.name]"
|
||||
@ -34,24 +31,28 @@
|
||||
v-model="formData[field.props.name]"
|
||||
:limit="field.props.limit"
|
||||
></FileUpload>
|
||||
<a-select
|
||||
<Select
|
||||
v-else-if="field.type === 'select'"
|
||||
v-model="formData[field.props.name]"
|
||||
v-model:value="formData[field.props.name]"
|
||||
:placeholder="field.placeholder"
|
||||
>
|
||||
<a-option v-for="(option, optIndex) in field.props.options" :key="optIndex" :value="option.value">
|
||||
<Option v-for="(option, optIndex) in field.props.options" :key="optIndex" :value="option.value">
|
||||
{{ option.label }}
|
||||
</a-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
<a-button class="submit-btn" type="primary" :disabled="loading" @click="handleSubmit">提交执行</a-button>
|
||||
</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
</Form>
|
||||
<Button class="submit-btn" type="primary" :disabled="loading" @click="handleSubmit">提交执行</Button>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { defineProps, defineEmits } from 'vue';
|
||||
import ImageUpload from '@/components/upload/ImageUpload.vue';
|
||||
import FileUpload from '@/components/upload/FileUpload.vue';
|
||||
import { Button, Input, Select, Row, Col, Form } from 'ant-design-vue';
|
||||
const { TextArea } = Input;
|
||||
const { Option } = Select;
|
||||
const { Item: FormItem } = Form;
|
||||
|
||||
const props = defineProps({
|
||||
formFields: {
|
||||
@ -79,24 +80,25 @@ const handleSubmit = async () => {
|
||||
|
||||
<style scoped lang="scss">
|
||||
.form-container {
|
||||
:deep(.arco-input-wrapper),
|
||||
:deep(.arco-textarea-wrapper) {
|
||||
:deep(.ant-input),
|
||||
:deep(.ant-input:focus),
|
||||
:deep(.ant-input-focused) {
|
||||
border-radius: 4px;
|
||||
border-color: #d7d7d9;
|
||||
background-color: #fff;
|
||||
height: 35px;
|
||||
width: 300px;
|
||||
|
||||
&:focus-within,
|
||||
&.arco-input-focus {
|
||||
&:focus,
|
||||
&:focus-within {
|
||||
background-color: var(--color-bg-2);
|
||||
border-color: rgb(var(--primary-6));
|
||||
box-shadow: 0 0 0 0 var(--color-primary-light-2);
|
||||
}
|
||||
|
||||
&.arco-textarea-wrapper {
|
||||
height: 60px;
|
||||
}
|
||||
|
||||
&.ant-textarea-wrapper {
|
||||
height: 60px;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
|
||||
@ -42,15 +42,14 @@
|
||||
{{ item.title }}
|
||||
</div>
|
||||
<div class="trigger-container">
|
||||
<a-trigger
|
||||
mouse-leave-delay="200"
|
||||
position="top"
|
||||
<Dropdown
|
||||
:mouseLeaveDelay="200"
|
||||
placement="top"
|
||||
trigger="hover"
|
||||
:auto-fit-position="false"
|
||||
:unmount-on-close="true"
|
||||
:overlayStyle="{ width: 'auto' }"
|
||||
>
|
||||
<SvgIcon size="12" name="svg-more" class="icon-more" />
|
||||
<template #content>
|
||||
<template #overlay>
|
||||
<div class="">
|
||||
<div class="history-item-dropdown">
|
||||
<div class="dropdown-item">
|
||||
@ -61,18 +60,19 @@
|
||||
</div>
|
||||
<div class="dropdown-item">
|
||||
<SvgIcon size="12" name="svg-delete" class="icon color-#6D4CFE" />
|
||||
<a-popconfirm
|
||||
<Popconfirm
|
||||
content="你确认删除该历史对话吗"
|
||||
@ok="deleteHistory(item.id, index)"
|
||||
type="error"
|
||||
@confirm="deleteHistory(item.id, index)"
|
||||
ok-text="确定"
|
||||
cancel-text="取消"
|
||||
>
|
||||
<div class="text delete">删除</div>
|
||||
</a-popconfirm>
|
||||
</Popconfirm>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</a-trigger>
|
||||
</Dropdown>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -83,9 +83,9 @@
|
||||
<div class="body">
|
||||
<div class="">
|
||||
<div class="toggle-btn cursor-pointer" @click="toggleCollapse">
|
||||
<a-tooltip :content="isCollapsed ? '展开' : '折叠'">
|
||||
<Tooltip :title="isCollapsed ? '展开' : '折叠'">
|
||||
<img class="status-icon" :src="isCollapsed ? menuUnfold : menuFold" />
|
||||
</a-tooltip>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -95,7 +95,7 @@
|
||||
<DynamicForm :formFields="formFields.form" :formData="formData" :loading="loading" @submit="handleSubmit" />
|
||||
</div>
|
||||
<div class="res h-full">
|
||||
<a-spin v-if="loading" class="spin-center" tip="生成中。。。" />
|
||||
<Spin v-if="loading" wrapperClassName="spin-center" tip="生成中。。。" />
|
||||
<div
|
||||
class="markdown-container"
|
||||
v-if="workFlowRes.output != '' && loading === false"
|
||||
@ -107,15 +107,16 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a-modal style="width: 500px" v-model:visible="editHistoryVisible">
|
||||
<Modal style="width: 500px" v-model:open="editHistoryVisible" centered>
|
||||
<template #title> Title</template>
|
||||
<div></div>
|
||||
</a-modal>
|
||||
</Modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive } from 'vue';
|
||||
import { Modal, Tooltip, message, Popconfirm, Spin, Dropdown } from 'ant-design-vue';
|
||||
import DynamicForm from './components/DynamicForm.vue';
|
||||
import {
|
||||
executeWorkFlow,
|
||||
@ -214,17 +215,17 @@ const handleTop = async (id, sort, event) => {
|
||||
|
||||
//置顶
|
||||
const topHistory = async (id, sort) => {
|
||||
const { code, message } = await topWorkflowHistoryApi(id);
|
||||
const { code, message: msg } = await topWorkflowHistoryApi(id);
|
||||
if (code === 200) {
|
||||
AMessage.success(message);
|
||||
message.success(msg);
|
||||
getWorkflowHistoryList();
|
||||
}
|
||||
};
|
||||
//取消置顶
|
||||
const canceltopHistory = async (id, sort) => {
|
||||
const { code, message } = await cancelTopWorkflowHistoryApi(id);
|
||||
const { code, message: msg } = await cancelTopWorkflowHistoryApi(id);
|
||||
if (code === 200) {
|
||||
AMessage.success(message);
|
||||
message.success(msg);
|
||||
getWorkflowHistoryList();
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,13 +1,15 @@
|
||||
<template>
|
||||
<a-select allow-search v-model="selectedValue" placeholder="请选择账号" allow-clear filterable @change="handleChange">
|
||||
<a-option v-for="account in filteredAccounts" :key="account.id" :value="account.id" :label="account.name">
|
||||
<Select showSearch v-model:value="selectedValue" placeholder="请选择账号" allowClear filterable @change="handleChange">
|
||||
<Option v-for="account in filteredAccounts" :key="account.id" :value="account.id" :label="account.name">
|
||||
{{ account.name }}
|
||||
</a-option>
|
||||
</a-select>
|
||||
</Option>
|
||||
</Select>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import { Select } from 'ant-design-vue';
|
||||
const { Option } = Select;
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { getPlacementAccountsList } from '@/api/all/propertyMarketing';
|
||||
|
||||
// 定义账号对象类型
|
||||
|
||||
@ -1,13 +1,15 @@
|
||||
<template>
|
||||
<a-select allow-search v-model="selectedValue" placeholder="请选择计划" allow-clear filterable @change="handleChange">
|
||||
<a-option v-for="item in listData" :key="item.id" :value="item.id" :label="item.name">
|
||||
<Select showSearch v-model:value="selectedValue" placeholder="请选择计划" allowClear filterable @change="handleChange">
|
||||
<Option v-for="item in listData" :key="item.id" :value="item.id" :label="item.name">
|
||||
{{ item.name }}
|
||||
</a-option>
|
||||
</a-select>
|
||||
</Option>
|
||||
</Select>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import { Select } from 'ant-design-vue';
|
||||
const { Option } = Select;
|
||||
import { ref, computed, onMounted, type PropType } from 'vue';
|
||||
import { getplacementAccountProjectsLlist } from '@/api/all/propertyMarketing';
|
||||
|
||||
interface Account {
|
||||
@ -17,7 +19,7 @@ interface Account {
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Array,
|
||||
type: Array as PropType<number[]>,
|
||||
default: () => [],
|
||||
},
|
||||
});
|
||||
@ -25,7 +27,7 @@ const props = defineProps({
|
||||
const emit = defineEmits(['update:modelValue', 'change']);
|
||||
|
||||
// 响应式数据
|
||||
const selectedValue = ref(props.modelValue);
|
||||
const selectedValue = ref<number | undefined>(props.modelValue?.[0]);
|
||||
const allAccounts = ref<Account[]>([]);
|
||||
const listData = ref<Account[]>([]);
|
||||
const loading = ref(false);
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
<template>
|
||||
<view>
|
||||
<topHeader ref="topHeaderRef" @search="search"></topHeader>
|
||||
<a-space direction="vertical" class="bg-#fff rounded-8px w-100% py-0 px-20px mb-24px">
|
||||
<div class="bg-#fff rounded-8px w-100% py-0 px-20px mb-24px">
|
||||
<div class="title-row">
|
||||
<span class="title mr-4px">行业词云</span>
|
||||
<a-tooltip>
|
||||
<template #content>基于行业内内容提取的高频词汇。</template>
|
||||
<Tooltip>
|
||||
<template #title>基于行业内内容提取的高频词汇。</template>
|
||||
<icon-question-circle size="16" class="!color-#737478" />
|
||||
</a-tooltip>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
<div class="multi-row-tag-cloud h-472px">
|
||||
@ -20,7 +20,7 @@
|
||||
class="tag-row"
|
||||
:style="{ justifyContent: row.align || 'center' }"
|
||||
>
|
||||
<a-tag
|
||||
<Tag
|
||||
v-for="(tag, tagIndex) in row.tags"
|
||||
:key="tagIndex"
|
||||
class="cursor-pointer"
|
||||
@ -39,16 +39,14 @@
|
||||
@mouseenter="hoverTag = tag"
|
||||
@mouseleave="hoverTag = null"
|
||||
>
|
||||
<a-space>
|
||||
<a-tooltip :content="`性价比:${Number(tag.rate * 100)}%`" position="tl">
|
||||
<a-space>{{ tag.term }}</a-space>
|
||||
</a-tooltip>
|
||||
</a-space>
|
||||
</a-tag>
|
||||
<Tooltip :title="`性价比:${Number(tag.rate * 100)}%`" placement="topLeft">
|
||||
<span>{{ tag.term }}</span>
|
||||
</Tooltip>
|
||||
</Tag>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</a-space>
|
||||
</div>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
@ -56,6 +54,7 @@
|
||||
import topHeader from './topHeader.vue';
|
||||
import { ref, computed } from 'vue';
|
||||
import { fetchindustryTerms } from '@/api/all/index';
|
||||
import { Tooltip, Tag } from 'ant-design-vue';
|
||||
|
||||
const topHeaderRef = ref();
|
||||
// 从topHeader获取统一的状态
|
||||
@ -170,7 +169,7 @@ const processTagData = (apiData) => {
|
||||
color: #6d4cfe !important;
|
||||
border-color: #6d4cfe !important;
|
||||
}
|
||||
:deep(.arco-modal-body) {
|
||||
:deep(.ant-modal-body) {
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
@ -199,7 +198,7 @@ const processTagData = (apiData) => {
|
||||
}
|
||||
|
||||
/* 悬停放大效果 */
|
||||
a-tag:hover {
|
||||
.ant-tag:hover {
|
||||
transform: scale(1.1);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
@ -2,63 +2,44 @@
|
||||
<view>
|
||||
<topHeader ref="topHeaderRef" @search="search"></topHeader>
|
||||
<!-- tabel -->
|
||||
<a-space
|
||||
direction="vertical"
|
||||
class="bg-#fff rounded-8px w-100% py-0 px-20px mb-24px"
|
||||
>
|
||||
<div class="bg-#fff rounded-8px w-100% py-0 px-20px mb-24px">
|
||||
<div class="title-row">
|
||||
<span class="title mr-4px">行业热门话题洞察</span>
|
||||
<a-tooltip>
|
||||
<template #content>基于社交内容平台的行业数据,分析用户关注的热门话题与趋势。</template>
|
||||
<Tooltip>
|
||||
<template #title>基于社交内容平台的行业数据,分析用户关注的热门话题与趋势。</template>
|
||||
<icon-question-circle size="16" class="!color-#737478" />
|
||||
</a-tooltip>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<a-table
|
||||
:columns="columns"
|
||||
:data="dataList"
|
||||
:filter-icon-align-left="alignLeft"
|
||||
:scroll="true"
|
||||
<Table
|
||||
:dataSource="dataList"
|
||||
:pagination="false"
|
||||
:showSorterTooltip="false"
|
||||
@change="handleChange"
|
||||
>
|
||||
<template #empty>
|
||||
<NoData />
|
||||
</template>
|
||||
<template #hotTitle>
|
||||
<a-space>
|
||||
<span>热度指数</span>
|
||||
<a-tooltip>
|
||||
<template #content>综合话题出现频次、互动数据(如点赞、收藏、评论)加权计算的热度得分。</template>
|
||||
<icon-question-circle size="14" class="!color-#737478" />
|
||||
</a-tooltip>
|
||||
</a-space>
|
||||
</template>
|
||||
<template #sentimentTitle>
|
||||
<a-space>
|
||||
<span>情感倾向</span>
|
||||
<a-tooltip>
|
||||
<template #content
|
||||
>统计该行业下全部内容的情绪分布,选取占比最高的情绪类型作为该话题的整体情感倾向。</template
|
||||
<Table.Column
|
||||
v-for="column in columns"
|
||||
:key="column.dataIndex"
|
||||
:title="column.title"
|
||||
:dataIndex="column.dataIndex"
|
||||
:width="column.width"
|
||||
:minWidth="column.minWidth"
|
||||
:sortable="column.sortable"
|
||||
>
|
||||
<icon-question-circle size="14" class="!color-#737478" />
|
||||
</a-tooltip>
|
||||
</a-space>
|
||||
</template>
|
||||
<template #rank="{ record }">
|
||||
<template v-if="column.slotName === 'rank'" #customRender="{ record }">
|
||||
<img v-if="record.rank == 1" :src="topImages[0]" style="width: 25px; height: 17px" />
|
||||
<img v-else-if="record.rank == 2" :src="topImages[1]" style="width: 25px; height: 17px" />
|
||||
<img v-else-if="record.rank == 3" :src="topImages[2]" style="width: 25px; height: 17px" />
|
||||
<span v-else>{{ record.rank }}</span>
|
||||
</template>
|
||||
<template #keywords="{ record }">
|
||||
<a-tag
|
||||
<template v-else-if="column.slotName === 'keywords'" #customRender="{ record }">
|
||||
<Tag
|
||||
v-for="item in record.keywords"
|
||||
:key="item"
|
||||
class="!rounded-2px !px-8px !py-1px !bg-#F2F3F5 !h-22px !color-#3C4043 mb-5px mr-5px"
|
||||
>{{ item }}</a-tag
|
||||
>{{ item }}</Tag
|
||||
>
|
||||
</template>
|
||||
<template #hot="{ record }">
|
||||
<template v-else-if="column.slotName === 'hot'" #customRender="{ record }">
|
||||
<img
|
||||
v-for="i in record.hot"
|
||||
:key="i"
|
||||
@ -67,23 +48,53 @@
|
||||
class="mr-2px"
|
||||
/>
|
||||
</template>
|
||||
<template #sentiment="{ record }">
|
||||
<template v-else-if="column.slotName === 'sentiment'" #customRender="{ record }">
|
||||
<img v-if="record.felling == '2'" src="@/assets/img/hottranslation/good.png" class="w-24px h-24px" />
|
||||
<img v-else-if="record.felling == '1'" src="@/assets/img/hottranslation/normal.png" class="w-24px h-24px" />
|
||||
<img v-else-if="record.felling == '0'" src="@/assets/img/hottranslation/poor.png" class="w-24px h-24px" />
|
||||
</template>
|
||||
|
||||
<template #optional="{ record }">
|
||||
<a-button type="outline" class="!rounded-4px" @click="gotoDetail(record)">详情</a-button>
|
||||
<template v-else-if="column.slotName === 'optional'" #customRender="{ record }">
|
||||
<Button type="primary" ghost @click="gotoDetail(record)">详情</Button>
|
||||
</template>
|
||||
</a-table>
|
||||
</a-space>
|
||||
<a-modal :visible="visible" unmountOnClose modal-class="hot-translation-modal" width="640px" @cancel="handleCancel">
|
||||
<template v-else-if="column.titleSlotName === 'hotTitle'" #title>
|
||||
<div class="flex items-center">
|
||||
<span class="mr-8px">热度指数</span>
|
||||
<Tooltip>
|
||||
<template #title>综合话题出现频次、互动数据(如点赞、收藏、评论)加权计算的热度得分。</template>
|
||||
<icon-question-circle size="14" class="!color-#737478" />
|
||||
</Tooltip>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="column.titleSlotName === 'sentimentTitle'" #title>
|
||||
<div class="flex items-center">
|
||||
<span class="mr-8px">情感倾向</span>
|
||||
<Tooltip>
|
||||
<template #title
|
||||
>统计该行业下全部内容的情绪分布,选取占比最高的情绪类型作为该话题的整体情感倾向。</template
|
||||
>
|
||||
<icon-question-circle size="14" class="!color-#737478" />
|
||||
</Tooltip>
|
||||
</div>
|
||||
</template>
|
||||
</Table.Column>
|
||||
<template #emptyText>
|
||||
<NoData />
|
||||
</template>
|
||||
</Table>
|
||||
</div>
|
||||
<Modal
|
||||
v-model:open="visible"
|
||||
unmountOnClose
|
||||
centered
|
||||
wrapClassName="hot-translation-modal"
|
||||
width="640px"
|
||||
@cancel="handleCancel"
|
||||
>
|
||||
<template #title>
|
||||
<span style="text-align: left; width: 100%">行业热门话题洞察</span>
|
||||
</template>
|
||||
<div>
|
||||
<a-space direction="vertical">
|
||||
<Space direction="vertical">
|
||||
<div class="mb-4px flex items-center">
|
||||
<p class="cts !mr-16px flex-shrink-0 w-48px">话题名称</p>
|
||||
<span class="cts">{{ topicInfo.name }}</span>
|
||||
@ -94,11 +105,11 @@
|
||||
</div>
|
||||
<div class="mb-4px flex items-center">
|
||||
<p class="cts !mr-16px flex-shrink-0 w-48px">关键词</p>
|
||||
<a-tag
|
||||
<Tag
|
||||
v-for="item in topicInfo.keywords"
|
||||
:key="item"
|
||||
class="mr-8px py-10px px-8px rounded-4px bg-#F2F3F5 cts !h-24px"
|
||||
>{{ item }}</a-tag
|
||||
>{{ item }}</Tag
|
||||
>
|
||||
</div>
|
||||
<div class="mb-4px flex items-center">
|
||||
@ -123,25 +134,27 @@
|
||||
<p class="!mr-16px w-48px cts relative top-2px">原始来源</p>
|
||||
<div class="flex flex-col">
|
||||
<div v-for="item in topicInfo.industry_topic_sources" :key="item" class="mb-18px flex items-center">
|
||||
<a-link style="background-color: initial" :href="item.link" target="_blank" class="!text-12px">{{
|
||||
<Link :href="item.link" target="_blank" class="!text-12px">{{
|
||||
item.title
|
||||
}}</a-link>
|
||||
}}</Link>
|
||||
<img src="@/assets/img/hottranslation/xhs.png" width="16" height="16" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-space>
|
||||
</Space>
|
||||
</div>
|
||||
<template #footer>
|
||||
<a-button size="large" @click="handleCancel">取消</a-button>
|
||||
<a-button type="primary" size="large" class="rounded-4px" @click="handleOk"> 确定 </a-button>
|
||||
<Button size="large" @click="handleCancel">取消</Button>
|
||||
<Button type="primary" size="large" @click="handleOk"> 确定 </Button>
|
||||
</template>
|
||||
</a-modal>
|
||||
</Modal>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import topHeader from './topHeader.vue';
|
||||
import { Modal, Button, Tooltip, Space, Table, Tag, Typography } from 'ant-design-vue';
|
||||
const { Link } = Typography;
|
||||
import { ref, computed } from 'vue';
|
||||
import { fetchIndustriesTree, fetchIndustryTopics, fetchIndustryTopicDetail } from '@/api/all/index';
|
||||
import star1 from '@/assets/img/hottranslation/star-fill1.png';
|
||||
@ -251,7 +264,7 @@ onMounted(() => {
|
||||
const getIndustriesTree = async () => {
|
||||
const res = await fetchIndustriesTree();
|
||||
industriesTree.value = res;
|
||||
selectedIndustry.value = res[0].id;
|
||||
selectedIndustry.value = res[0]?.id;
|
||||
getIndustryTopics();
|
||||
};
|
||||
|
||||
@ -320,7 +333,7 @@ const handleOk = () => {
|
||||
color: #6d4cfe !important;
|
||||
border-color: #6d4cfe !important;
|
||||
}
|
||||
:deep(.arco-modal-body) {
|
||||
:deep(.ant-modal-body) {
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
@ -374,7 +387,7 @@ const handleOk = () => {
|
||||
}
|
||||
}
|
||||
|
||||
.arco-modal-body {
|
||||
.ant-modal-body {
|
||||
padding: 12px 20px 0;
|
||||
.cts {
|
||||
color: var(--Text-2, #3c4043);
|
||||
|
||||
@ -3,102 +3,122 @@
|
||||
<view>
|
||||
<topHeader ref="topHeaderRef" @click="search"></topHeader>
|
||||
<!-- 重点品牌列表 -->
|
||||
<a-space
|
||||
<Space
|
||||
direction="vertical"
|
||||
class="bg-#fff rounded-8px w-100% py-0 px-20px mb-24px"
|
||||
>
|
||||
<div class="title-row">
|
||||
<span class="title mr-4px">重点品牌列表</span>
|
||||
<a-tooltip>
|
||||
<template #content
|
||||
<Tooltip>
|
||||
<template #title
|
||||
>基于该行业中近期提及频次高、用户互动活跃的品牌内容,筛选出关注度较高的代表性品牌。</template
|
||||
>
|
||||
<icon-question-circle size="16" class="!color-#737478" />
|
||||
</a-tooltip>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<a-table
|
||||
:columns="columns"
|
||||
:data="dataList"
|
||||
:filter-icon-align-left="alignLeft"
|
||||
:scroll="true"
|
||||
<Table
|
||||
:dataSource="dataList"
|
||||
:pagination="false"
|
||||
:showSorterTooltip="false"
|
||||
@change="handleChange"
|
||||
>
|
||||
<template #empty>
|
||||
<NoData />
|
||||
</template>
|
||||
<template #hotTitle>
|
||||
<a-space>
|
||||
<span>热度指数</span>
|
||||
<a-tooltip>
|
||||
<template #content>综合话题出现频次、互动数据(如点赞、收藏、评论)加权计算的热度得分。</template>
|
||||
<icon-question-circle size="14" class="!color-#737478" />
|
||||
</a-tooltip>
|
||||
</a-space>
|
||||
</template>
|
||||
<template #trendTitle>
|
||||
<a-space>
|
||||
<span>变化幅度</span>
|
||||
<a-tooltip>
|
||||
<template #content>仅基于品牌出现频次。</template>
|
||||
<icon-question-circle size="14" class="!color-#737478" />
|
||||
</a-tooltip>
|
||||
</a-space>
|
||||
</template>
|
||||
<template #volume_rateTitle>
|
||||
<a-space>
|
||||
<span>占总声量比例</span>
|
||||
<a-tooltip>
|
||||
<template #content>该品牌在当前周期内被提及的内容量,占整个行业内容总量的比例。</template>
|
||||
<icon-question-circle size="14" class="!color-#737478" />
|
||||
</a-tooltip>
|
||||
</a-space>
|
||||
</template>
|
||||
<template #rank="{ record }">
|
||||
<Table.Column
|
||||
v-for="column in columns"
|
||||
:key="column.dataIndex"
|
||||
:title="column.title"
|
||||
:dataIndex="column.dataIndex"
|
||||
:width="column.width"
|
||||
:minWidth="column.minWidth"
|
||||
:sortable="column.sortable"
|
||||
>
|
||||
<template v-if="column.slotName === 'rank'" #customRender="{ record }">
|
||||
<img v-if="record.rank == 1" :src="topImages[0]" style="width: 25px; height: 17px" />
|
||||
<img v-else-if="record.rank == 2" :src="topImages[1]" style="width: 25px; height: 17px" />
|
||||
<img v-else-if="record.rank == 3" :src="topImages[2]" style="width: 25px; height: 17px" />
|
||||
<span v-else>{{ record.rank }}</span>
|
||||
</template>
|
||||
<template #hot="{ record }">
|
||||
<template v-else-if="column.slotName === 'hot'" #customRender="{ record }">
|
||||
<img v-for="i in record.hot" :key="i" :src="starImages[i - 1]" style="width: 16px; height: 16px" />
|
||||
</template>
|
||||
<template #trend="{ record }">
|
||||
<template v-else-if="column.slotName === 'trend'" #customRender="{ record }">
|
||||
<div class="flex items-center" :class="record.trend > 0 ? 'color-#F64B31' : 'color-#25C883'">
|
||||
<icon-arrow-up v-if="record.trend > 0" size="16" />
|
||||
<icon-arrow-down v-else size="16" />
|
||||
{{ `${(record.trend * 100).toFixed(2)}%` }}
|
||||
</div>
|
||||
</template>
|
||||
<template #volumeRate="{ record }"> <a-statistic :value="record.volume_rate * 100" />% </template>
|
||||
</a-table>
|
||||
</a-space>
|
||||
<template v-else-if="column.slotName === 'volumeRate'" #customRender="{ record }">
|
||||
<Statistic :value="record.volume_rate * 100" />%
|
||||
</template>
|
||||
<template v-else-if="column.titleSlotName === 'hotTitle'" #title>
|
||||
<Space>
|
||||
<span>热度指数</span>
|
||||
<Tooltip>
|
||||
<template #title>综合话题出现频次、互动数据(如点赞、收藏、评论)加权计算的热度得分。</template>
|
||||
<icon-question-circle size="14" class="!color-#737478" />
|
||||
</Tooltip>
|
||||
</Space>
|
||||
</template>
|
||||
<template v-else-if="column.titleSlotName === 'trendTitle'" #title>
|
||||
<Space>
|
||||
<span>变化幅度</span>
|
||||
<Tooltip>
|
||||
<template #title>仅基于品牌出现频次。</template>
|
||||
<icon-question-circle size="14" class="!color-#737478" />
|
||||
</Tooltip>
|
||||
</Space>
|
||||
</template>
|
||||
<template v-else-if="column.titleSlotName === 'volume_rateTitle'" #title>
|
||||
<Space>
|
||||
<span>占总声量比例</span>
|
||||
<Tooltip>
|
||||
<template #title>该品牌在当前周期内被提及的内容量,占整个行业内容总量的比例。</template>
|
||||
<icon-question-circle size="14" class="!color-#737478" />
|
||||
</Tooltip>
|
||||
</Space>
|
||||
</template>
|
||||
</Table.Column>
|
||||
<template #emptyText>
|
||||
<NoData />
|
||||
</template>
|
||||
</Table>
|
||||
</Space>
|
||||
<!-- 舆情 & 敏感动态-->
|
||||
<a-space
|
||||
<Space
|
||||
direction="vertical"
|
||||
class="bg-#fff rounded-8px w-100% py-0 px-20px mb-24px"
|
||||
>
|
||||
<div class="title-row">
|
||||
<span class="title mr-4px">舆情 & 敏感动态</span>
|
||||
<a-tooltip>
|
||||
<template #content
|
||||
<Tooltip>
|
||||
<template #title
|
||||
>基于情绪分析与敏感词识别,对行业内容中的负面或争议性话题进行监测,辅助判断舆情风险动态。</template
|
||||
>
|
||||
<icon-question-circle size="16" class="!color-#737478" />
|
||||
</a-tooltip>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
<a-table :data="otherList" :columns="columns2" :pagination="false" :scroll="true" style="font-size: 12px">
|
||||
<template #empty>
|
||||
<Table :dataSource="otherList" :pagination="false" :showSorterTooltip="false" style="font-size: 12px">
|
||||
<Table.Column
|
||||
v-for="column in columns2"
|
||||
:key="column.dataIndex"
|
||||
:title="column.title"
|
||||
:dataIndex="column.dataIndex"
|
||||
:width="column.width"
|
||||
:minWidth="column.minWidth"
|
||||
:sortable="column.sortable"
|
||||
/>
|
||||
<template #emptyText>
|
||||
<NoData />
|
||||
</template>
|
||||
</a-table>
|
||||
</a-space>
|
||||
</Table>
|
||||
</Space>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import topHeader from './topHeader.vue';
|
||||
import { Tooltip, Space, Table, Statistic } from 'ant-design-vue';
|
||||
import { fetchFocusBrandsList, fetchEventDynamicsList } from '@/api/all/index';
|
||||
import { ref, onMounted, computed } from 'vue';
|
||||
import star1 from '@/assets/img/hottranslation/star-fill1.png';
|
||||
|
||||
@ -3,46 +3,46 @@
|
||||
<view>
|
||||
<topHeader ref="topHeaderRef" @search="search"></topHeader>
|
||||
<!-- 关键词热度榜 -->
|
||||
<a-space
|
||||
<Space
|
||||
direction="vertical"
|
||||
class="bg-#fff rounded-8px w-100% py-0 px-20px mb-20px"
|
||||
>
|
||||
<div class="title-row">
|
||||
<span class="title mr-4px">关键词热度榜</span>
|
||||
<a-tooltip>
|
||||
<template #content>基于该行业用户内容中提及频率较高的关键词,按热度进行排序,反映近期关注焦点。</template>
|
||||
<Tooltip>
|
||||
<template #title>基于该行业用户内容中提及频率较高的关键词,按热度进行排序,反映近期关注焦点。</template>
|
||||
<icon-question-circle size="16" class="!color-#737478" />
|
||||
</a-tooltip>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
<a-table
|
||||
<Table
|
||||
:columns="columns"
|
||||
:data="dataList"
|
||||
:filter-icon-align-left="alignLeft"
|
||||
:scroll="true"
|
||||
:dataSource="dataList"
|
||||
:scroll="{ x: true }"
|
||||
:pagination="false"
|
||||
:showSorterTooltip="false"
|
||||
@change="handleChange"
|
||||
>
|
||||
<template #empty>
|
||||
<template #emptyText>
|
||||
<NoData />
|
||||
</template>
|
||||
<template #heatLevel>
|
||||
<a-space>
|
||||
<Space>
|
||||
<span>热度指数</span>
|
||||
<a-tooltip>
|
||||
<template #content>综合话题出现频次、互动数据(如点赞、收藏、评论)加权计算的热度得分。</template>
|
||||
<Tooltip>
|
||||
<template #title>综合话题出现频次、互动数据(如点赞、收藏、评论)加权计算的热度得分。</template>
|
||||
<icon-question-circle size="14" class="!color-#737478" />
|
||||
</a-tooltip>
|
||||
</a-space>
|
||||
</Tooltip>
|
||||
</Space>
|
||||
</template>
|
||||
<template #trendTitle>
|
||||
<a-space>
|
||||
<Space>
|
||||
<span>变化幅度</span>
|
||||
<a-tooltip>
|
||||
<template #content>仅基于关键词出现频次。</template>
|
||||
<Tooltip>
|
||||
<template #title>仅基于关键词出现频次。</template>
|
||||
<icon-question-circle size="14" class="!color-#737478" />
|
||||
</a-tooltip>
|
||||
</a-space>
|
||||
</Tooltip>
|
||||
</Space>
|
||||
</template>
|
||||
|
||||
<template #rank="{ record }">
|
||||
@ -52,7 +52,7 @@
|
||||
<span v-else>{{ record.rank }}</span>
|
||||
</template>
|
||||
<template #keywords="{ record }">
|
||||
<a-tag v-for="item in record.keywords" :key="item" style="margin-right: 5px">{{ item }}</a-tag>
|
||||
<Tag v-for="item in record.keywords" :key="item" style="margin-right: 5px">{{ item }}</Tag>
|
||||
</template>
|
||||
<template #hot="{ record }">
|
||||
<img v-for="i in record.hot" :key="i" :src="starImages[i - 1]" style="width: 16px; height: 16px" />
|
||||
@ -69,21 +69,21 @@
|
||||
{{ `${(record.trend * 100).toFixed(2)}%` }}
|
||||
</div>
|
||||
</template>
|
||||
</a-table>
|
||||
</a-space>
|
||||
</Table>
|
||||
</Space>
|
||||
<!-- 行业情绪 -->
|
||||
<a-space
|
||||
<Space
|
||||
direction="vertical"
|
||||
class="bg-#fff rounded-8px w-100% py-0 px-20px mb-24px"
|
||||
>
|
||||
<div class="title-row">
|
||||
<span class="title mr-4px">行业情绪</span>
|
||||
<a-tooltip>
|
||||
<template #content
|
||||
<Tooltip>
|
||||
<template #title
|
||||
>对该行业下用户内容进行情绪分析,按情绪类别统计占比,提取占比最高者作为行业情绪代表。</template
|
||||
>
|
||||
<icon-question-circle size="16" class="!color-#737478" />
|
||||
</a-tooltip>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center w-100%">
|
||||
@ -103,17 +103,16 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<a-table
|
||||
<Table
|
||||
class="flex-1"
|
||||
:columns="columns2"
|
||||
:data="sortedRowData"
|
||||
:span-method="spanMethod"
|
||||
:filter-icon-align-left="alignLeft"
|
||||
:scroll="true"
|
||||
:dataSource="sortedRowData"
|
||||
:scroll="{ x: true }"
|
||||
:pagination="false"
|
||||
:showSorterTooltip="false"
|
||||
@change="handleChange"
|
||||
>
|
||||
<template #empty>
|
||||
<template #emptyText>
|
||||
<NoData />
|
||||
</template>
|
||||
<template #felling="{ record }">
|
||||
@ -122,33 +121,33 @@
|
||||
<span>{{ fellingStatus[record.felling].label }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</a-table>
|
||||
</Table>
|
||||
</div>
|
||||
</a-space>
|
||||
</Space>
|
||||
<!-- 新兴关键词 -->
|
||||
<a-space
|
||||
<Space
|
||||
direction="vertical"
|
||||
class="bg-#fff rounded-8px w-100% py-0 px-20px"
|
||||
>
|
||||
<div class="title-row">
|
||||
<span class="title mr-4px">新兴关键词</span>
|
||||
<a-tooltip>
|
||||
<template #content
|
||||
<Tooltip>
|
||||
<template #title
|
||||
>指当前周期中首次出现,或相较上一周期词频显著增长的关键词,反映近期出现的新关注点。</template
|
||||
>
|
||||
<icon-question-circle size="16" class="!color-#737478" />
|
||||
</a-tooltip>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
<a-table
|
||||
<Table
|
||||
:columns="columns3"
|
||||
:data="keywordList"
|
||||
:filter-icon-align-left="alignLeft"
|
||||
:scroll="true"
|
||||
:dataSource="keywordList"
|
||||
:scroll="{ x: true }"
|
||||
:pagination="false"
|
||||
:showSorterTooltip="false"
|
||||
@change="handleChange"
|
||||
>
|
||||
<template #empty>
|
||||
<template #emptyText>
|
||||
<NoData />
|
||||
</template>
|
||||
<template #rank="{ record }">
|
||||
@ -181,23 +180,23 @@
|
||||
<img v-for="i in record.hot" :key="i" :src="starImages[i - 1]" style="width: 16px; height: 16px" />
|
||||
</template>
|
||||
|
||||
<template #hotTitle="{ record }">
|
||||
<a-space>
|
||||
<template #hotTitle>
|
||||
<Space>
|
||||
<span>当前热度指数</span>
|
||||
<a-tooltip>
|
||||
<template #content>综合关键词出现频次、互动表现(如点赞、收藏、评论)加权计算的热度得分。</template>
|
||||
<Tooltip>
|
||||
<template #title>综合关键词出现频次、互动表现(如点赞、收藏、评论)加权计算的热度得分。</template>
|
||||
<icon-question-circle size="16" class="!color-#737478" />
|
||||
</a-tooltip>
|
||||
</a-space>
|
||||
</Tooltip>
|
||||
</Space>
|
||||
</template>
|
||||
<template #trendTitle="{ record }">
|
||||
<a-space>
|
||||
<template #trendTitle>
|
||||
<Space>
|
||||
<span>变化幅度</span>
|
||||
<a-tooltip>
|
||||
<template #content>仅基于关键词出现频次。</template>
|
||||
<Tooltip>
|
||||
<template #title>仅基于关键词出现频次。</template>
|
||||
<icon-question-circle size="16" class="!color-#737478" />
|
||||
</a-tooltip>
|
||||
</a-space>
|
||||
</Tooltip>
|
||||
</Space>
|
||||
</template>
|
||||
<template #tred="{ record }">
|
||||
<div class="flex items-center" :class="record.trend > 0 ? 'color-#F64B31' : 'color-#25C883'">
|
||||
@ -207,15 +206,16 @@
|
||||
</div>
|
||||
</template>
|
||||
<template #optional="{ record }">
|
||||
<a-button type="outline" @click="gotoDetail(record)">详情</a-button>
|
||||
<Button type="primary" ghost @click="gotoDetail(record)">详情</Button>
|
||||
</template>
|
||||
</a-table>
|
||||
</a-space>
|
||||
</Table>
|
||||
</Space>
|
||||
<!-- modal -->
|
||||
<a-modal
|
||||
:visible="visible"
|
||||
modal-class="keyword-modal"
|
||||
<Modal
|
||||
v-model:open="visible"
|
||||
wrapClassName="keyword-modal"
|
||||
unmountOnClose
|
||||
centered
|
||||
width="640px"
|
||||
@ok="handleOk"
|
||||
@cancel="handleCancel"
|
||||
@ -224,7 +224,7 @@
|
||||
<span style="text-align: left; width: 100%">新兴关键词</span>
|
||||
</template>
|
||||
<div>
|
||||
<a-space direction="vertical">
|
||||
<Space direction="vertical">
|
||||
<div class="mb-12px flex items-center">
|
||||
<p class="cts !mr-16px flex-shrink-0 w-83px">话题名称</p>
|
||||
<span class="cts">{{ topicInfo.name }}</span>
|
||||
@ -250,25 +250,27 @@
|
||||
<p class="!mr-16px w-83px cts relative top-2px">原始来源</p>
|
||||
<div class="flex flex-col">
|
||||
<div v-for="item in topicInfo.industry_new_keyword_sources" :key="item" class="mb-18px flex items-center">
|
||||
<a-link style="background-color: initial" :href="item.link" target="_blank" class="!text-12px">{{
|
||||
<Link :href="item.link" target="_blank" class="!text-12px">{{
|
||||
item.title
|
||||
}}</a-link>
|
||||
}}</Link>
|
||||
<img src="@/assets/img/hottranslation/xhs.png" width="16" height="16" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-space>
|
||||
</Space>
|
||||
</div>
|
||||
<template #footer>
|
||||
<a-button size="large" @click="handleCancel">取消</a-button>
|
||||
<a-button type="primary" size="large" class="rounded-4px" @click="handleOk"> 确定 </a-button>
|
||||
<Button size="large" @click="handleCancel">取消</Button>
|
||||
<Button type="primary" size="large" @click="handleOk"> 确定 </Button>
|
||||
</template>
|
||||
</a-modal>
|
||||
</Modal>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import topHeader from './topHeader.vue';
|
||||
import { Modal, Button, Tooltip, Space, Table, Tag, Typography } from 'ant-design-vue';
|
||||
const { Link } = Typography;
|
||||
import {
|
||||
fetchKeywordTrendsList,
|
||||
fetchIndustryEmotions,
|
||||
@ -690,7 +692,7 @@ onMounted(() => {
|
||||
}
|
||||
}
|
||||
|
||||
.arco-modal-body {
|
||||
.ant-modal-body {
|
||||
padding: 12px 20px 0;
|
||||
.cts {
|
||||
color: var(--Text-2, #3c4043);
|
||||
|
||||
@ -1,97 +1,100 @@
|
||||
<template>
|
||||
<view>
|
||||
<!-- 头部 -->
|
||||
<a-space
|
||||
<Space
|
||||
direction="vertical"
|
||||
class="bg-#fff rounded-8px mb-20px"
|
||||
style="background-color: #fff; width: 100%; padding: 24px; color: #737478; font-size: 14px"
|
||||
>
|
||||
<a-space align="start" style="width: 100%; align-items: flex-start" class="mb-12px">
|
||||
<Space align="start" style="width: 100%; align-items: flex-start" class="mb-12px">
|
||||
<span style="flex-shrink: 0; line-height: 28px" class="mr-32px">行业大类</span>
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 16px; width: 100%; align-items: flex-start">
|
||||
<a-tag
|
||||
<Tag
|
||||
v-for="item in industriesTree"
|
||||
:key="item.id"
|
||||
size="Medium"
|
||||
:checkable="true"
|
||||
:checked="selectedIndustry == item.id"
|
||||
style="padding: 10px 16px; border-radius: 30px; height: 28px"
|
||||
style="padding: 0 16px; border-radius: 30px; height: 28px"
|
||||
class="lh-28px cursor-pointer"
|
||||
:style="
|
||||
selectedIndustry == item.id
|
||||
? 'color: #6D4CFE; background-color: #F0EDFF'
|
||||
: 'color: #3C4043; background-color: #F7F8FA'
|
||||
"
|
||||
@check="handleIndustryCheck(item.id)"
|
||||
>{{ item.name }}</a-tag
|
||||
@click="handleIndustryCheck(item.id)"
|
||||
>{{ item.name }}</Tag
|
||||
>
|
||||
</div>
|
||||
</a-space>
|
||||
</Space>
|
||||
<!-- 二级类目 -->
|
||||
<a-space align="start" style="width: 100%; align-items: flex-start" class="mb-12px">
|
||||
<Space align="start" style="width: 100%; align-items: flex-start" class="mb-12px">
|
||||
<span style="flex-shrink: 0; line-height: 28px" class="mr-32px">二级类目</span>
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 16px; width: 100%; align-items: flex-start">
|
||||
<a-tag
|
||||
<Tag
|
||||
v-for="item in subCategories"
|
||||
:key="item.id"
|
||||
size="Medium"
|
||||
size="small"
|
||||
:checkable="true"
|
||||
:checked="selectedSubCategory == item.id"
|
||||
style="padding: 10px 16px; border-radius: 30px; height: 28px"
|
||||
style="padding: 0 16px; border-radius: 30px; height: 28px"
|
||||
class="lh-28px cursor-pointer"
|
||||
:style="
|
||||
selectedSubCategory == item.id
|
||||
? 'color: #6d4cfe; background-color: #f0edff'
|
||||
: 'color: #3C4043; background-color: #F7F8FA'
|
||||
"
|
||||
@check="handleSubCategoryCheck(item.id)"
|
||||
>{{ item.name }}</a-tag
|
||||
@click="handleSubCategoryCheck(item.id)"
|
||||
>{{ item.name }}</Tag
|
||||
>
|
||||
</div>
|
||||
</a-space>
|
||||
<!-- </a-space> -->
|
||||
<a-space align="start" style="width: 100%; align-items: flex-start" class="mb-12px">
|
||||
</Space>
|
||||
<!-- </Space> -->
|
||||
<Space align="start" style="width: 100%; align-items: flex-start" class="mb-12px">
|
||||
<span style="flex-shrink: 0; line-height: 28px" class="mr-32px">时间筛选</span>
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 16px; width: 100%; align-items: flex-start">
|
||||
<a-tag
|
||||
<Tag
|
||||
v-for="item in timePeriods"
|
||||
:key="item.value"
|
||||
size="Medium"
|
||||
:checkable="true"
|
||||
:checked="selectedTimePeriod == item.value"
|
||||
style="padding: 10px 16px; border-radius: 30px; height: 28px"
|
||||
class="lh-28px cursor-pointer"
|
||||
style="padding: 0 16px; border-radius: 30px; height: 28px"
|
||||
:style="
|
||||
selectedTimePeriod == item.value
|
||||
? 'color: #6d4cfe; background-color: #f0edff'
|
||||
: 'color: #3C4043; background-color: #F7F8FA'
|
||||
"
|
||||
@check="handleTimePeriodCheck(item.value)"
|
||||
@click="handleTimePeriodCheck(item.value)"
|
||||
>{{ item.label }}
|
||||
</a-tag>
|
||||
</Tag>
|
||||
</div>
|
||||
</a-space>
|
||||
</Space>
|
||||
<!-- 搜索区域 -->
|
||||
<a-space style="margin-left: 'auto'">
|
||||
<a-button type="primary" size="medium" @click="handleSearch">
|
||||
<Space style="margin-left: 'auto'">
|
||||
<Button type="primary" @click="handleSearch">
|
||||
<template #icon>
|
||||
<icon-search />
|
||||
<icon-search class="mr-8px"/>
|
||||
</template>
|
||||
<!-- Use the default slot to avoid extra spaces -->
|
||||
<template #default>搜索</template>
|
||||
</a-button>
|
||||
<a-button class="w-84px reset-btn" size="medium" @click="handleReset">
|
||||
</Button>
|
||||
<Button class="w-84px reset-btn" @click="handleReset">
|
||||
<template #icon>
|
||||
<icon-refresh />
|
||||
<icon-refresh class="mr-8px"/>
|
||||
</template>
|
||||
<template #default>重置</template>
|
||||
</a-button>
|
||||
</a-space>
|
||||
</a-space>
|
||||
</Button>
|
||||
</Space>
|
||||
</Space>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue';
|
||||
import { fetchIndustriesTree } from '@/api/all/index';
|
||||
|
||||
import { Button, Space, Tag } from 'ant-design-vue';
|
||||
const emit = defineEmits<(e: 'search') => void>();
|
||||
// 行业大类
|
||||
const industriesTree = ref([]);
|
||||
@ -139,6 +142,7 @@ const handleIndustryCheck = (id) => {
|
||||
};
|
||||
|
||||
const handleSubCategoryCheck = (id) => {
|
||||
console.log('handleSubCategoryCheck');
|
||||
selectedSubCategory.value = id;
|
||||
};
|
||||
|
||||
@ -185,7 +189,7 @@ const handleReset = () => {
|
||||
color: #6d4cfe !important;
|
||||
border-color: #6d4cfe !important;
|
||||
}
|
||||
:deep(.arco-modal-body) {
|
||||
:deep(.ant-modal-body) {
|
||||
padding: 0px;
|
||||
}
|
||||
.reset-btn {
|
||||
|
||||
@ -3,63 +3,71 @@
|
||||
<topHeader ref="topHeaderRef" @search="search"></topHeader>
|
||||
|
||||
<!-- 用户痛点观察 -->
|
||||
<a-space
|
||||
<Space
|
||||
direction="vertical"
|
||||
style="background-color: #fff; width: 100%; padding: 0 20px"
|
||||
class="bg-#fff rounded-8px mb-24px"
|
||||
>
|
||||
<div class="title-row">
|
||||
<span class="title mr-4px">用户痛点观察</span>
|
||||
<a-tooltip>
|
||||
<template #content
|
||||
<Tooltip>
|
||||
<template #title
|
||||
>基于用户内容中的情绪分析与表达模式,提取反复出现的负面倾向主题,反映典型使用痛点。</template
|
||||
>
|
||||
<icon-question-circle size="16" class="!color-#737478" />
|
||||
</a-tooltip>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<a-table
|
||||
:columns="columns"
|
||||
:data="dataList"
|
||||
:filter-icon-align-left="alignLeft"
|
||||
:scroll="true"
|
||||
<Table
|
||||
:dataSource="dataList"
|
||||
:pagination="false"
|
||||
:showSorterTooltip="false"
|
||||
@change="handleChange"
|
||||
>
|
||||
<template #empty>
|
||||
<NoData />
|
||||
</template>
|
||||
<template #rank="{ record }">
|
||||
<Table.Column
|
||||
v-for="column in columns"
|
||||
:key="column.dataIndex"
|
||||
:title="column.title"
|
||||
:dataIndex="column.dataIndex"
|
||||
:width="column.width"
|
||||
:minWidth="column.minWidth"
|
||||
:sortable="column.sortable"
|
||||
>
|
||||
<template v-if="column.slotName === 'rank'" #customRender="{ record }">
|
||||
<img v-if="record.rank == 1" :src="topImages[0]" style="width: 25px; height: 17px" />
|
||||
<img v-else-if="record.rank == 2" :src="topImages[1]" style="width: 25px; height: 17px" />
|
||||
<img v-else-if="record.rank == 3" :src="topImages[2]" style="width: 25px; height: 17px" />
|
||||
<span v-else>{{ record.rank }}</span>
|
||||
</template>
|
||||
<template #keywords="{ record }">
|
||||
<a-tag
|
||||
<template v-else-if="column.slotName === 'keywords'" #customRender="{ record }">
|
||||
<Tag
|
||||
v-for="item in record.keywords"
|
||||
:key="item"
|
||||
class="!rounded-2px !px-8px !py-1px !bg-#F2F3F5 !h-22px !color-#3C4043 mb-5px mr-5px"
|
||||
>{{ item }}</a-tag
|
||||
>{{ item }}</Tag
|
||||
>
|
||||
</template>
|
||||
<template #frequency="{ record }">
|
||||
<a-tag
|
||||
<template v-else-if="column.slotName === 'frequency'" #customRender="{ record }">
|
||||
<Tag
|
||||
:class="`!rounded-2px !px-8px !py-1px !bg-${frequencyStatus[record.frequency].bgColor} !h-22px !color-${
|
||||
frequencyStatus[record.frequency].color
|
||||
}`"
|
||||
>{{ frequencyStatus[record.frequency].label }}</a-tag
|
||||
>{{ frequencyStatus[record.frequency].label }}</Tag
|
||||
>
|
||||
</template>
|
||||
|
||||
<template #optional="{ record }">
|
||||
<a-button type="outline" class="!rounded-4px" @click="gotoDetail(record)">详情</a-button>
|
||||
<template v-else-if="column.slotName === 'optional'" #customRender="{ record }">
|
||||
<Button type="primary" ghost @click="gotoDetail(record)">详情</Button>
|
||||
</template>
|
||||
</a-table>
|
||||
</a-space>
|
||||
</Table.Column>
|
||||
<template #emptyText>
|
||||
<NoData />
|
||||
</template>
|
||||
</Table>
|
||||
</Space>
|
||||
|
||||
<a-modal
|
||||
:visible="visible"
|
||||
modal-class="user-pain-points-modal"
|
||||
<Modal
|
||||
v-model:open="visible"
|
||||
wrapClassName="user-pain-points-modal"
|
||||
centered
|
||||
unmountOnClose
|
||||
width="640px"
|
||||
@ok="handleOk"
|
||||
@ -69,27 +77,27 @@
|
||||
<span style="text-align: left; width: 100%">用户痛点观察</span>
|
||||
</template>
|
||||
<div>
|
||||
<a-space direction="vertical" style="font-size: 12px">
|
||||
<Space direction="vertical" style="font-size: 12px">
|
||||
<div class="mb-12px flex items-center">
|
||||
<p class="cts !mr-16px flex-shrink-0 w-60px">痛点</p>
|
||||
<span class="cts">{{ topicInfo.name }}</span>
|
||||
</div>
|
||||
<div class="mb-12px flex items-center">
|
||||
<p class="cts !mr-16px flex-shrink-0 w-60px">关键词</p>
|
||||
<a-tag
|
||||
<Tag
|
||||
v-for="item in topicInfo.keywords"
|
||||
:key="item"
|
||||
class="mr-8px py-10px px-8px rounded-4px bg-#F2F3F5 cts !h-24px"
|
||||
>{{ item }}</a-tag
|
||||
>{{ item }}</Tag
|
||||
>
|
||||
</div>
|
||||
<div class="mb-12px flex items-center">
|
||||
<p class="cts !mr-16px flex-shrink-0 w-60px">频次</p>
|
||||
<a-tag
|
||||
<Tag
|
||||
:class="`!rounded-2px !px-8px !py-1px !bg-${
|
||||
frequencyStatus[topicInfo.frequency].bgColor
|
||||
} !h-22px !color-${frequencyStatus[topicInfo.frequency].color}`"
|
||||
>{{ frequencyStatus[topicInfo.frequency].label }}</a-tag
|
||||
>{{ frequencyStatus[topicInfo.frequency].label }}</Tag
|
||||
>
|
||||
</div>
|
||||
<div class="mb-12px flex items-center">
|
||||
@ -100,25 +108,27 @@
|
||||
<p class="cts !mr-16px flex-shrink-0 w-60px">原始来源</p>
|
||||
<div class="flex flex-col">
|
||||
<div v-for="item in topicInfo.user_pain_point_sources" :key="item" class="mb-18px flex items-center">
|
||||
<a-link style="background-color: initial" :href="item.link" target="_blank" class="!text-12px">{{
|
||||
<Link :href="item.link" target="_blank" class="!text-12px">{{
|
||||
item.title
|
||||
}}</a-link>
|
||||
}}</Link>
|
||||
<img src="@/assets/img/hottranslation/xhs.png" width="16" height="16" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-space>
|
||||
</Space>
|
||||
</div>
|
||||
<template #footer>
|
||||
<a-button size="large" @click="handleCancel">取消</a-button>
|
||||
<a-button type="primary" size="large" class="rounded-4px" @click="handleOk"> 确定 </a-button>
|
||||
<Button size="large" @click="handleCancel">取消</Button>
|
||||
<Button type="primary" size="large" @click="handleOk"> 确定 </Button>
|
||||
</template>
|
||||
</a-modal>
|
||||
</Modal>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import topHeader from './topHeader.vue';
|
||||
import { Modal, Button, Tooltip, Space, Table, Tag, Typography } from 'ant-design-vue';
|
||||
const { Link } = Typography;
|
||||
import { fetchUserPainPointsDetail, fetchUserPainPointsList } from '@/api/all/index';
|
||||
import { ref, onMounted, computed } from 'vue';
|
||||
import top1 from '@/assets/img/captcha/top1.svg';
|
||||
@ -286,16 +296,16 @@ const search = () => {
|
||||
</style>
|
||||
<style lang="scss">
|
||||
.user-pain-points-modal {
|
||||
.arco-modal-header {
|
||||
.ant-modal-header {
|
||||
border-bottom: none;
|
||||
height: 56px;
|
||||
padding: 0 20px;
|
||||
.arco-modal-title {
|
||||
.ant-modal-title {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
}
|
||||
|
||||
.arco-modal-body {
|
||||
.ant-modal-body {
|
||||
padding: 12px 20px 0;
|
||||
.cts {
|
||||
color: var(--Text-2, #3c4043);
|
||||
@ -311,7 +321,7 @@ const search = () => {
|
||||
}
|
||||
}
|
||||
|
||||
.arco-modal-footer {
|
||||
.ant-modal-footer {
|
||||
display: flex;
|
||||
height: 64px;
|
||||
padding: 0px 20px;
|
||||
|
||||
@ -6,52 +6,52 @@
|
||||
<div class="bg-#fff rounded-8px w-100% py-0 px-20px w-600px mr-24px">
|
||||
<div class="title-row">
|
||||
<span class="title mr-4px">性别分布</span>
|
||||
<a-tooltip>
|
||||
<template #content>基于社交内容平台中用户资料、互动行为及语义特征进行智能识别与估算。</template>
|
||||
<Tooltip>
|
||||
<template #title>基于社交内容平台中用户资料、互动行为及语义特征进行智能识别与估算。</template>
|
||||
<icon-question-circle size="16" class="!color-#737478" />
|
||||
</a-tooltip>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<a-space v-if="genderData.length > 0">
|
||||
<Space v-if="genderData.length > 0">
|
||||
<div id="container" class="w-300px h-300px"></div>
|
||||
|
||||
<a-space direction="vertical" style="font-size: 14px">
|
||||
<a-space>
|
||||
<Space direction="vertical" style="font-size: 14px">
|
||||
<Space>
|
||||
<span style="width: 8px; height: 8px; background-color: #f64b31; border-radius: 50%"></span>
|
||||
<span>女性</span>
|
||||
<span>{{ (girlData.rate * 100).toFixed(2) }}%</span>
|
||||
<span>TGI</span>
|
||||
<span>{{ girlData.tgi }}</span>
|
||||
</a-space>
|
||||
<a-space>
|
||||
</Space>
|
||||
<Space>
|
||||
<span style="width: 8px; height: 8px; background-color: #2a59f3; border-radius: 50%"></span>
|
||||
<span>男性</span>
|
||||
<span>{{ (boyData.rate * 100).toFixed(2) }}%</span>
|
||||
<span>TGI</span>
|
||||
<span>{{ boyData.tgi }}</span>
|
||||
</a-space>
|
||||
</a-space>
|
||||
</a-space>
|
||||
</Space>
|
||||
</Space>
|
||||
</Space>
|
||||
<div v-else>
|
||||
<NoData class="w-100% h-100%" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- 2. 年龄分布 -->
|
||||
<div class="bg-#fff rounded-8px w-100% py-0 px-20px flex-1 flex flex-col">
|
||||
<a-space style="display: flex; justify-content: space-between; width: 100%; font-size: 12px">
|
||||
<Space style="display: flex; justify-content: space-between; width: 100%; font-size: 12px">
|
||||
<div class="title-row">
|
||||
<span class="title mr-4px">年龄分布</span>
|
||||
<a-tooltip>
|
||||
<template #content>基于社交平台的公开信息、内容偏好与行为模式,通过算法进行年龄段归类和统计。</template>
|
||||
<Tooltip>
|
||||
<template #title>基于社交平台的公开信息、内容偏好与行为模式,通过算法进行年龄段归类和统计。</template>
|
||||
<icon-question-circle size="16" class="!color-#737478" />
|
||||
</a-tooltip>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<a-space v-if="ageValueData.length > 0" align="center">
|
||||
<Space v-if="ageValueData.length > 0" align="center">
|
||||
<span style="width: 16px; height: 8px; background-color: #6d4cfe; border-radius: 2px"></span>
|
||||
<span style="color: #6d4cfe">占比</span>
|
||||
<span style="width: 16px; height: 8px; background-color: #f64b31; border-radius: 2px"></span>
|
||||
<span style="color: #f64b31">TGI比</span>
|
||||
</a-space>
|
||||
</a-space>
|
||||
</Space>
|
||||
</Space>
|
||||
<div v-if="ageValueData.length === 0" class="w-100% flex-1">
|
||||
<NoData />
|
||||
</div>
|
||||
@ -61,17 +61,17 @@
|
||||
<div class="bg-#fff rounded-8px w-100% py-0 px-20px flex-1 pb-20px">
|
||||
<div class="title-row">
|
||||
<span class="title mr-4px">地域分布</span>
|
||||
<a-tooltip>
|
||||
<template #content>基于社交平台的IP归属地、位置标签、内容发布地等数据推测用户活跃区域。</template>
|
||||
<Tooltip>
|
||||
<template #title>基于社交平台的IP归属地、位置标签、内容发布地等数据推测用户活跃区域。</template>
|
||||
<icon-question-circle size="16" class="!color-#737478" />
|
||||
</a-tooltip>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<a-space direction="vertical">
|
||||
<Space direction="vertical">
|
||||
<div id="chinaMap" style="height: 416px; width: 640px"></div>
|
||||
<a-space direction="vertical" style="font-size: 14px">
|
||||
<Space direction="vertical" style="font-size: 14px">
|
||||
<span class="cts">搜索指数</span>
|
||||
<a-space>
|
||||
<Space>
|
||||
<span class="cts">高</span>
|
||||
<span
|
||||
v-for="item in 5"
|
||||
@ -86,44 +86,38 @@
|
||||
}"
|
||||
></span>
|
||||
<span class="cts">低</span>
|
||||
</a-space>
|
||||
</a-space>
|
||||
</a-space>
|
||||
</Space>
|
||||
</Space>
|
||||
</Space>
|
||||
<div class="flex flex-col h-486px">
|
||||
<a-tabs default-active-key="1" class="h-100%" @change="tabChange">
|
||||
<a-tab-pane key="1" title="省份">
|
||||
<a-table :data="geoList" :pagination="false" class="h-100%" :scroll="{ y: '100%' }">
|
||||
<template #empty>
|
||||
<Tabs defaultActiveKey="1" class="h-100%" @change="tabChange" size="large">
|
||||
<TabPane key="1" tab="省份">
|
||||
<Table :dataSource="geoList" :pagination="false" class="h-100%" :scroll="{ y: '100%' }" :showSorterTooltip="false">
|
||||
<template #emptyText>
|
||||
<NoData />
|
||||
</template>
|
||||
<template #columns>
|
||||
<a-table-column title="排名" data-index="rank" />
|
||||
<a-table-column title="省份" data-index="geo" />
|
||||
<a-table-column title="分布占比" data-index="rate" />
|
||||
|
||||
<a-table-column title="TGI指数" data-index="tgi" />
|
||||
</template>
|
||||
</a-table>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="2" title="城市">
|
||||
<a-table :data="geoList" :pagination="false" class="h-100%" :scroll="{ y: '100%' }">
|
||||
<template #empty>
|
||||
<Column title="排名" dataIndex="rank" />
|
||||
<Column title="省份" dataIndex="geo" />
|
||||
<Column title="分布占比" dataIndex="rate" />
|
||||
<Column title="TGI指数" dataIndex="tgi" />
|
||||
</Table>
|
||||
</TabPane>
|
||||
<TabPane key="2" tab="城市">
|
||||
<Table :dataSource="geoList" :pagination="false" class="h-100%" :scroll="{ y: '100%' }" :showSorterTooltip="false">
|
||||
<template #emptyText>
|
||||
<NoData />
|
||||
</template>
|
||||
<template #columns>
|
||||
<a-table-column title="排名" data-index="rank" />
|
||||
<a-table-column title="城市" data-index="geo" />
|
||||
<a-table-column title="分布占比" data-index="rate">
|
||||
<template #cell="{ record }">
|
||||
<Column title="排名" dataIndex="rank" />
|
||||
<Column title="城市" dataIndex="geo" />
|
||||
<Column title="分布占比" dataIndex="rate">
|
||||
<template #customRender="{ record }">
|
||||
<span class="cts">{{ (record.rate * 100).toFixed(2) }}%</span>
|
||||
</template>
|
||||
</a-table-column>
|
||||
|
||||
<a-table-column title="TGI指数" data-index="tgi" />
|
||||
</template>
|
||||
</a-table>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</Column>
|
||||
<Column title="TGI指数" dataIndex="tgi" />
|
||||
</Table>
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -133,11 +127,15 @@
|
||||
<script setup>
|
||||
import topHeader from './topHeader.vue';
|
||||
import { fetchAgeDistributionsList, fetchGeoDistributionsList, fetchGenderDistributionsList } from '@/api/all/index';
|
||||
import { ref, onMounted, computed } from 'vue';
|
||||
import { ref, onMounted, computed, watch, nextTick } from 'vue';
|
||||
import * as echarts from 'echarts';
|
||||
import chinaJson from '@/assets/maps/china.json';
|
||||
|
||||
echarts.registerMap('china', chinaJson);
|
||||
import { Tabs, Tooltip, Space, Table } from 'ant-design-vue';
|
||||
const { TabPane } = Tabs;
|
||||
const { Column } = Table;
|
||||
|
||||
const scope = ref(1); // 地域范围,1-省,2-市
|
||||
const chartInstance = (ref < echarts.ECharts) | (null > null);
|
||||
const topHeaderRef = ref();
|
||||
|
||||
@ -9,7 +9,7 @@
|
||||
<img src="@/assets/img/Frame.svg" class="w-480 h-480 mr-40" alt="" />
|
||||
</div>
|
||||
<div class="flex items-center w-400 h-100%">
|
||||
<a-space
|
||||
<Space
|
||||
direction="vertical"
|
||||
size="large"
|
||||
align="center"
|
||||
@ -17,30 +17,27 @@
|
||||
>
|
||||
<img src="@/assets/img/icon-logo.png" alt="" width="96" height="24" class="mb-8px" />
|
||||
<span class="text-4 color-#737478">AI营销工具</span>
|
||||
<a-form ref="formRef" :model="loginForm" :rules="formRules" auto-label-width class="w-320 mt-48px form-wrap">
|
||||
<a-form-item field="mobile" hide-label>
|
||||
<a-input
|
||||
v-model="loginForm.mobile"
|
||||
<Form ref="formRef" :model="loginForm" :rules="formRules" auto-label-width class="w-320 mt-48px form-wrap">
|
||||
<FormItem name="mobile">
|
||||
<Input
|
||||
v-model:value="loginForm.mobile"
|
||||
placeholder="输入手机号"
|
||||
class="form-input border border-solid border-#d7d7d9 x w-100% h-48px text-14 rounded-4px color-#333 bg-#fff"
|
||||
class="form-input border border-solid !border-#d7d7d9 w-100% h-48px !text-14px rounded-4px color-#333 bg-#fff"
|
||||
clearable
|
||||
allow-clear
|
||||
@blur="validateField('mobile')"
|
||||
@input="clearError('mobile')"
|
||||
allowClear
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item field="captcha" hide-label>
|
||||
</FormItem>
|
||||
<FormItem name="captcha">
|
||||
<div
|
||||
class="form-input border border-solid border-#d7d7d9 w-100% h-48px text-14 rounded-4px color-#333 bg-#fff flex justify-between items-center"
|
||||
class="form-input border border-solid !border-#d7d7d9 w-100% h-48px !text-14px rounded-4px color-#333 bg-#fff flex justify-between items-center"
|
||||
>
|
||||
<a-input
|
||||
v-model="loginForm.captcha"
|
||||
<Input
|
||||
v-model:value="loginForm.captcha"
|
||||
placeholder="验证码"
|
||||
style="background-color: #fff; border: none"
|
||||
allow-clear
|
||||
maxlength="6"
|
||||
@blur="validateField('captcha')"
|
||||
@input="clearError('captcha')"
|
||||
style="background-color: #fff; border: none !important;"
|
||||
allowClear
|
||||
class="form-input"
|
||||
:maxlength="6"
|
||||
/>
|
||||
<span
|
||||
class="w-120 font-400 text-right mr-4 text-16px"
|
||||
@ -52,27 +49,27 @@
|
||||
>{{ countdown > 0 ? `${countdown}s` : hasGetCode ? '重新发送' : '发送验证码' }}</span
|
||||
>
|
||||
</div>
|
||||
</a-form-item>
|
||||
<a-form-item hide-label class="mt-68px mb-16px">
|
||||
<a-button
|
||||
</FormItem>
|
||||
<FormItem class="mt-68px mb-16px">
|
||||
<Button
|
||||
type="primary"
|
||||
class="w-480 h-48 !text-16px !rounded-8px"
|
||||
class="w-full h-48 !text-16px !rounded-8px"
|
||||
:class="disabledSubmitBtn ? 'cursor-no-drop' : 'cursor-pointer'"
|
||||
:disabled="disabledSubmitBtn"
|
||||
@click="handleSubmit"
|
||||
>
|
||||
{{ isLogin ? '登录' : '注册并开通企业账号' }}
|
||||
</a-button>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
<a-space class="text-12px color-#737478 justify-start items-center">
|
||||
<a-checkbox v-model="hasCheck" class="!text-12px mr-8px"></a-checkbox>
|
||||
</Button>
|
||||
</FormItem>
|
||||
</Form>
|
||||
<Space class="text-12px color-#737478 justify-start items-center">
|
||||
<Checkbox v-model:checked="hasCheck" class="!text-12px mr-8px"></Checkbox>
|
||||
<span class="text-12px color-#737478">{{ isLogin ? '登录' : '注册' }}即代表同意</span>
|
||||
<a-link href="link" class="form-link color-#211F24" target="_blank">用户协议</a-link>
|
||||
<Link href="link" class="form-link color-#211F24" target="_blank">用户协议</Link>
|
||||
<span class="text-12px color-#737478">和</span>
|
||||
<a-link href="link" class="form-link color-#211f24" target="_blank">隐私政策</a-link>
|
||||
</a-space>
|
||||
</a-space>
|
||||
<Link href="link" class="form-link color-#211f24" target="_blank">隐私政策</Link>
|
||||
</Space>
|
||||
</Space>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@ -85,18 +82,18 @@
|
||||
@submit="handleVerificationSubmit"
|
||||
@cancel="isVerificationVisible = false"
|
||||
/>
|
||||
<a-modal :visible="visible" unmountOnClose hide-cancel @ok="handleOk" @cancel="handleCancel">
|
||||
<Modal v-model:open="visible" centered unmountOnClose @cancel="handleCancel">
|
||||
<template #title>
|
||||
<span style="text-align: left; width: 100%">选择账号</span>
|
||||
</template>
|
||||
<div class="account-bind-container">
|
||||
<a-card :bordered="false" class="bind-card">
|
||||
<Card :bordered="false" class="bind-card">
|
||||
<div class="bind-header">
|
||||
<a-typography-text class="mobile-number">{{ mobileNumber }} 已在以下企业绑定了账号</a-typography-text>
|
||||
<Typography.Text class="mobile-number">{{ mobileNumber }} 已在以下企业绑定了账号</Typography.Text>
|
||||
</div>
|
||||
|
||||
<a-list :bordered="false" :split="false" class="account-list">
|
||||
<a-list-item
|
||||
<List :bordered="false" :split="false" class="account-list">
|
||||
<List.Item
|
||||
v-for="(account, index) in accounts"
|
||||
:key="index"
|
||||
class="account-item"
|
||||
@ -107,22 +104,29 @@
|
||||
}"
|
||||
@click="selectAccount(account, index)"
|
||||
>
|
||||
<a-list-item-meta>
|
||||
<List.Item.Meta>
|
||||
<template #title>
|
||||
<div style="display: flex; align-items: center; gap: 12px">
|
||||
<a-checkbox :model-value="selectedAccountIndex === index" />
|
||||
<a-typography-text>{{ account.name || '-' }}</a-typography-text>
|
||||
<Checkbox :checked="selectedAccountIndex === index" />
|
||||
<Typography.Text>{{ account.name || '-' }}</Typography.Text>
|
||||
</div>
|
||||
</template>
|
||||
</a-list-item-meta>
|
||||
</a-list-item>
|
||||
</a-list>
|
||||
</a-card>
|
||||
</List.Item.Meta>
|
||||
</List.Item>
|
||||
</List>
|
||||
</Card>
|
||||
</div>
|
||||
</a-modal>
|
||||
<template #footer>
|
||||
<div class="flex">
|
||||
<Button type="primary" @click="handleOk">确定</Button>
|
||||
</div>
|
||||
</template>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Checkbox, Modal, Button, Form, FormItem, Input, Space, message, Typography, Card, List } from 'ant-design-vue';
|
||||
const { Link } = Typography;
|
||||
import PuzzleVerification from './components/PuzzleVerification.vue';
|
||||
import { fetchLoginCaptCha, fetchAuthorizationsCaptcha, fetchProfileInfo } from '@/api/all/login';
|
||||
import { joinEnterpriseByInviteCode } from '@/api/all';
|
||||
@ -159,15 +163,14 @@ const formRules = {
|
||||
mobile: [
|
||||
{
|
||||
required: true,
|
||||
message: '请填写手机号',
|
||||
trigger: ['blur', 'change'],
|
||||
},
|
||||
{
|
||||
validator: (value: string, callback: (error?: string) => void) => {
|
||||
validator: (_rule: any, value: string) => {
|
||||
if (!value) {
|
||||
return Promise.reject('请填写手机号');
|
||||
}
|
||||
if (!/^1[3-9]\d{9}$/.test(value)) {
|
||||
callback('手机号格式不正确');
|
||||
return Promise.reject('手机号格式不正确');
|
||||
} else {
|
||||
callback();
|
||||
return Promise.resolve();
|
||||
}
|
||||
},
|
||||
trigger: ['blur', 'change'],
|
||||
@ -176,15 +179,14 @@ const formRules = {
|
||||
captcha: [
|
||||
{
|
||||
required: true,
|
||||
message: '请填写验证码',
|
||||
trigger: ['blur', 'change'],
|
||||
},
|
||||
{
|
||||
validator: (value: string, callback: (error?: string) => void) => {
|
||||
validator: (_rule: any, value: string) => {
|
||||
if (!value) {
|
||||
return Promise.reject('请填写验证码');
|
||||
}
|
||||
if (!/^\d{6}$/.test(value)) {
|
||||
callback('验证码必须是6位数字');
|
||||
return Promise.reject('验证码必须是6位数字');
|
||||
} else {
|
||||
callback();
|
||||
return Promise.resolve();
|
||||
}
|
||||
},
|
||||
trigger: ['blur', 'change'],
|
||||
@ -216,7 +218,7 @@ const selectAccount = (account: any, index: any) => {
|
||||
};
|
||||
|
||||
const validateField = (field: string) => {
|
||||
formRef.value.validateField(field);
|
||||
formRef.value.validateFields(field);
|
||||
};
|
||||
|
||||
const clearError = (field: string) => {
|
||||
@ -239,11 +241,9 @@ const getCode = async () => {
|
||||
// 先重置验证状态
|
||||
formRef.value.clearValidate('mobile');
|
||||
|
||||
const result = await formRef.value.validateField('mobile');
|
||||
// 只有当验证通过时才会显示滑块验证
|
||||
if (result === true || result === undefined) {
|
||||
formRef.value.validateFields('mobile').then(() => {
|
||||
isVerificationVisible.value = true;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 验证码验证通过后
|
||||
@ -252,9 +252,9 @@ const handleVerificationSubmit = async () => {
|
||||
startCountdown();
|
||||
|
||||
try {
|
||||
const { code, message } = await fetchLoginCaptCha({ mobile: loginForm.mobile });
|
||||
const { code, message: msg } = await fetchLoginCaptCha({ mobile: loginForm.mobile });
|
||||
if (code === 200) {
|
||||
AMessage.success(message);
|
||||
message.success(msg);
|
||||
}
|
||||
} catch (error) {
|
||||
// 重置倒计时
|
||||
@ -292,7 +292,7 @@ const handleSubmit = async () => {
|
||||
await formRef.value.validate();
|
||||
|
||||
if (!hasCheck.value) {
|
||||
AMessage.error('请先勾选同意用户协议');
|
||||
message.error('请先勾选同意用户协议');
|
||||
return;
|
||||
}
|
||||
|
||||
@ -301,14 +301,14 @@ const handleSubmit = async () => {
|
||||
|
||||
if (code === 200) {
|
||||
// 处理登录成功逻辑
|
||||
AMessage.success(isLogin.value ? '登录成功' : '注册成功');
|
||||
message.success(isLogin.value ? '登录成功' : '注册成功');
|
||||
userStore.setToken(data.access_token);
|
||||
|
||||
const { invite_code } = route.query;
|
||||
if (invite_code) {
|
||||
const { code } = await joinEnterpriseByInviteCode(invite_code as string);
|
||||
if (code === 200) {
|
||||
AMessage.success('加入企业成功');
|
||||
message.success('加入企业成功');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -2,40 +2,44 @@
|
||||
<div class="bg-#fff rounded-8px w-100% py-0 px-20px pb-24px">
|
||||
<div class="title-row">
|
||||
<span class="title">账号管理</span>
|
||||
<a-button type="outline" class="add-account-button" @click="handleAddAccount">添加子账号</a-button>
|
||||
<Button type="primary" ghost class="add-account-button" @click="handleAddAccount">添加子账号</Button>
|
||||
</div>
|
||||
<a-table
|
||||
:columns="columns"
|
||||
:data="dataSource"
|
||||
<Table
|
||||
:dataSource="dataSource"
|
||||
:pagination="pagination"
|
||||
:showSorterTooltip="false"
|
||||
class="mt-8px"
|
||||
@page-change="handlePageChange"
|
||||
@page-size-change="handlePageSizeChange"
|
||||
@change="handleTableChange"
|
||||
>
|
||||
<template #empty>
|
||||
<NoData />
|
||||
</template>
|
||||
<template #mobile="{ record }">
|
||||
<Table.Column title="手机号" dataIndex="mobile">
|
||||
<template #customRender="{ record }">
|
||||
<div class="flex item-center pt-13px pb-13px">
|
||||
<span class="mr-4px">{{ record.mobile }}</span>
|
||||
<a-tag v-if="record.type === 0" class="primary-account">主账号</a-tag>
|
||||
<a-tag v-else class="sub-account">子账号</a-tag>
|
||||
<Tag v-if="record.type === 0" class="primary-account">主账号</Tag>
|
||||
<Tag v-else class="sub-account">子账号</Tag>
|
||||
</div>
|
||||
</template>
|
||||
<template #action="{ record }">
|
||||
<a-button
|
||||
</Table.Column>
|
||||
<Table.Column title="操作" dataIndex="action" width="120">
|
||||
<template #customRender="{ record }">
|
||||
<Button
|
||||
v-if="record.type !== 0"
|
||||
class="delete-button"
|
||||
size="mini"
|
||||
type="outline"
|
||||
status="danger"
|
||||
size="small"
|
||||
type="primary"
|
||||
ghost
|
||||
danger
|
||||
@click="openDeleteModal(record)"
|
||||
>
|
||||
删除
|
||||
</a-button>
|
||||
</Button>
|
||||
</template>
|
||||
</a-table>
|
||||
<Modal v-model:visible="addAccountVisible" width="480px" title="添加子账号" :okText="okText" @ok="handleOk">
|
||||
</Table.Column>
|
||||
<template #emptyText>
|
||||
<NoData />
|
||||
</template>
|
||||
</Table>
|
||||
<Modal v-model:open="addAccountVisible" centered width="480px" title="添加子账号" :okText="okText" @ok="handleOk" >
|
||||
<div v-if="canAddAccount" class="add-account-container">
|
||||
<h2 class="add-account-title">生成企业专属链接,成员通过访问即可注册并加入企业账号。</h2>
|
||||
<p class="add-account-subtitle">子账号可独立登录,权限继承主账号配置。</p>
|
||||
@ -52,15 +56,15 @@
|
||||
<p class="cannot-add-account-subtitle">如需添加更多子账号,您可联系销售人员进行购买和权限扩展。</p>
|
||||
</div>
|
||||
</Modal>
|
||||
<CustomerServiceModal v-model:visible="customerServiceVisible" />
|
||||
<DeleteModal v-model:visible="deleteVisible" :title="deleteTitle" @ok="handleDelete">
|
||||
<p class="delete-modal-content">删除后,该账号将无法登录您的企业。</p>
|
||||
<CustomerServiceModal v-model:open="customerServiceVisible" centered/>
|
||||
<DeleteModal v-model:open="deleteVisible" centered :content="deleteTitle" @ok="handleDelete" @cancel="deleteVisible = false">
|
||||
</DeleteModal>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import Container from '@/components/container.vue';
|
||||
import { ref, onMounted, reactive, computed } from 'vue';
|
||||
import { Button, Table, message, Tag } from 'ant-design-vue';
|
||||
|
||||
import { fetchSubAccountPage, removeEnterpriseAccount, getEnterpriseInviteCode } from '@/api/all';
|
||||
import Modal from '@/components/modal.vue';
|
||||
import DeleteModal from '@/components/delete-modal.vue';
|
||||
@ -82,10 +86,10 @@ const columns = [
|
||||
const dataSource = ref([]);
|
||||
const pagination = reactive({
|
||||
total: 0,
|
||||
showPageSize: true,
|
||||
showTotal: true,
|
||||
defaultCurrent: 1,
|
||||
defaultPageSize: 10,
|
||||
showSizeChanger: true,
|
||||
showTotal: (total: number, range: [number, number]) => `共 ${total} 条记录`,
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
});
|
||||
|
||||
const params = reactive({
|
||||
@ -120,13 +124,12 @@ const currentSelectAccount = ref();
|
||||
|
||||
const { copy, copied, isSupported } = useClipboard({ source: inviteUrl });
|
||||
|
||||
function handlePageChange(current: number) {
|
||||
params.page = current;
|
||||
getSubAccount();
|
||||
}
|
||||
|
||||
function handlePageSizeChange(pageSize: number) {
|
||||
params.page_size = pageSize;
|
||||
function handleTableChange(paginationInfo: any, filters: any, sorter: any) {
|
||||
params.page = paginationInfo.current;
|
||||
params.page_size = paginationInfo.pageSize;
|
||||
// 更新分页状态
|
||||
pagination.current = paginationInfo.current;
|
||||
pagination.pageSize = paginationInfo.pageSize;
|
||||
getSubAccount();
|
||||
}
|
||||
|
||||
@ -155,13 +158,13 @@ function handleOk() {
|
||||
return;
|
||||
}
|
||||
if (!isSupported) {
|
||||
AMessage.error('您的浏览器不支持复制,请手动复制!');
|
||||
message.error('您的浏览器不支持复制,请手动复制!');
|
||||
}
|
||||
copy(inviteUrl.value);
|
||||
if (!copied) {
|
||||
AMessage.error('复制失败,请手动复制!');
|
||||
message.error('复制失败,请手动复制!');
|
||||
}
|
||||
AMessage.success('复制成功!');
|
||||
message.success('复制成功!');
|
||||
}
|
||||
|
||||
function openDeleteModal(record: { id: number; mobile: string }) {
|
||||
@ -172,7 +175,7 @@ function openDeleteModal(record: { id: number; mobile: string }) {
|
||||
|
||||
async function handleDelete() {
|
||||
await removeEnterpriseAccount(currentSelectAccount.value.id);
|
||||
AMessage.success('移除成功!');
|
||||
message.success('移除成功!');
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
@ -275,14 +278,7 @@ onMounted(() => {
|
||||
color: var(--Text-2, rgba(60, 64, 67, 1));
|
||||
}
|
||||
}
|
||||
.delete-modal-content {
|
||||
margin-left: 34px;
|
||||
margin-top: 16px;
|
||||
font-family: $font-family-medium;
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
color: var(--Text-2, rgba(60, 64, 67, 1));
|
||||
}
|
||||
|
||||
.title-row {
|
||||
display: flex;
|
||||
height: 64px;
|
||||
|
||||
@ -3,39 +3,53 @@
|
||||
<div class="title-row">
|
||||
<span class="title">企业信息</span>
|
||||
</div>
|
||||
<a-table :columns="columns" :data="dataSource" :pagination="false" class="mt-8px">
|
||||
<template #empty>
|
||||
<NoData />
|
||||
</template>
|
||||
<template #info="{ record }">
|
||||
<Table :dataSource="dataSource" :pagination="false" :showSorterTooltip="false" class="mt-8px">
|
||||
<Table.Column title="企业信息" dataIndex="info">
|
||||
<template #customRender="{ record }">
|
||||
{{ record.name }}
|
||||
</template>
|
||||
<template #action>
|
||||
<a-button class="edit-button" size="mini" type="outline" @click="handleUpdate">修改</a-button>
|
||||
</Table.Column>
|
||||
<Table.Column title="操作" dataIndex="action" width="120">
|
||||
<template #customRender>
|
||||
<Button class="edit-button" size="small" type="primary" ghost @click="handleUpdate">修改</Button>
|
||||
</template>
|
||||
</a-table>
|
||||
<Modal v-model:visible="infoVisible" width="480px" title="修改企业名称" :okText="okText" @ok="handleOk">
|
||||
</Table.Column>
|
||||
<template #emptyText>
|
||||
<NoData />
|
||||
</template>
|
||||
</Table>
|
||||
<Modal
|
||||
v-model:open="infoVisible"
|
||||
width="480px"
|
||||
centered
|
||||
title="修改企业名称"
|
||||
:okText="okText"
|
||||
@ok="handleOk"
|
||||
cancelText="取消"
|
||||
>
|
||||
<p class="tips">
|
||||
企业名称只能修改2次,请谨慎操作。<span
|
||||
>(剩余{{ enterpriseInfo!.update_name_quota - enterpriseInfo!.used_update_name_count }}次)
|
||||
</span>
|
||||
</p>
|
||||
<a-form
|
||||
<Form
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
class="form"
|
||||
:label-col-props="{ span: 6, offset: 0 }"
|
||||
:wrapper-col-props="{ span: 18, offset: 0 }"
|
||||
label-align="left"
|
||||
>
|
||||
<a-form-item required field="name" label="新企业名称">
|
||||
<a-input v-model.trim="form.name" size="small" :disabled="!canUpdate" placeholder="请输入新企业名称" />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
<FormItem required name="name" label="新企业名称">
|
||||
<Input v-model:value.trim="form.name" size="small" :disabled="!canUpdate" placeholder="请输入新企业名称" />
|
||||
</FormItem>
|
||||
</Form>
|
||||
</Modal>
|
||||
<CustomerServiceModal v-model:visible="customerServiceVisible" />
|
||||
<CustomerServiceModal v-model:open="customerServiceVisible" centered />
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { Button, Form, FormItem, Input, Table, message } from 'ant-design-vue';
|
||||
import Container from '@/components/container.vue';
|
||||
import Modal from '@/components/modal.vue';
|
||||
import { ref, reactive, computed } from 'vue';
|
||||
@ -48,6 +62,16 @@ const form = reactive({
|
||||
name: '',
|
||||
});
|
||||
|
||||
const rules = {
|
||||
name: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入新企业名称',
|
||||
trigger: ['blur'],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const enterpriseInfo = computed(() => {
|
||||
return store.enterpriseInfo ?? {};
|
||||
});
|
||||
@ -98,7 +122,7 @@ async function handleOk() {
|
||||
await updateEnterpriseName({ name: form.name });
|
||||
store.setEnterpriseName(form.name);
|
||||
store.incUsedUpdateNameCount();
|
||||
AMessage.success('修改成功!');
|
||||
message.success('修改成功!');
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@ -3,32 +3,37 @@
|
||||
<div class="title-row">
|
||||
<span class="title">个人信息</span>
|
||||
</div>
|
||||
<a-table :columns="columns" :data="dataSource" :pagination="false" class="mt-8px">
|
||||
<template #empty>
|
||||
<NoData />
|
||||
</template>
|
||||
<template #info="{ record }">
|
||||
<Table :dataSource="dataSource" :pagination="false" :showSorterTooltip="false" class="mt-8px">
|
||||
<Table.Column title="用户信息" dataIndex="info">
|
||||
<template #customRender="{ record }">
|
||||
<div class="pt-3px pb-3px">
|
||||
<a-avatar :image-url="record.head_image" :size="32" />
|
||||
<Avatar :src="record.head_image" :size="32" />
|
||||
{{ record.name || '-' }}
|
||||
<icon-edit size="13" class="ml-8px" @click="openEditInfoModal" />
|
||||
</div>
|
||||
</template>
|
||||
<template #mobile="{ record }">
|
||||
</Table.Column>
|
||||
<Table.Column title="手机号" dataIndex="mobile">
|
||||
<template #customRender="{ record }">
|
||||
{{ record.mobile.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2') }}
|
||||
<icon-edit size="13" class="ml-8px" @click="openEditMobileModal" />
|
||||
</template>
|
||||
</a-table>
|
||||
<Modal v-model:visible="infoVisible" title="修改用户信息" @ok="handleSubmitUserInfo">
|
||||
<a-form
|
||||
</Table.Column>
|
||||
<template #emptyText>
|
||||
<NoData />
|
||||
</template>
|
||||
</Table>
|
||||
<Modal v-model:open="infoVisible" centered title="修改用户信息" @ok="handleSubmitUserInfo">
|
||||
<Form
|
||||
class="form"
|
||||
:rules="rules"
|
||||
:model="userInfoForm"
|
||||
:label-col-props="{ span: 3, offset: 0 }"
|
||||
:wrapper-col-props="{ span: 21, offset: 0 }"
|
||||
>
|
||||
<a-form-item field="head_image" label="头像">
|
||||
<FormItem name="head_image" label="头像">
|
||||
<div class="flex items-center">
|
||||
<a-avatar :image-url="userInfoForm.file_url" :size="48" />
|
||||
<Avatar :src="userInfoForm.file_url" :size="48" />
|
||||
<span class="upload-button" @click="triggerFileInput">
|
||||
<input
|
||||
ref="uploadInputRef"
|
||||
@ -37,40 +42,40 @@
|
||||
style="display: none"
|
||||
@change="handleFileChange"
|
||||
/>
|
||||
<a-button><icon-upload />上传新头像</a-button>
|
||||
<Button><icon-upload />上传新头像</Button>
|
||||
</span>
|
||||
</div>
|
||||
</a-form-item>
|
||||
<a-form-item field="name" label="昵称">
|
||||
<a-input v-model.trim="userInfoForm.name" placeholder="请输入昵称" />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</FormItem>
|
||||
<FormItem name="name" label="昵称">
|
||||
<Input v-model:value="userInfoForm.name" placeholder="请输入昵称" />
|
||||
</FormItem>
|
||||
</Form>
|
||||
</Modal>
|
||||
<Modal v-model:visible="imageVisible" title="头像裁剪">
|
||||
<Modal v-model:open="imageVisible" centered title="头像裁剪">
|
||||
<VueCropper></VueCropper>
|
||||
</Modal>
|
||||
<Modal v-model:visible="mobileVisible" title="修改手机号" @ok="handleUpdateMobile">
|
||||
<a-form
|
||||
<Modal v-model:open="mobileVisible" centered title="修改手机号" @ok="handleUpdateMobile">
|
||||
<Form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
class="form"
|
||||
:rules="formRules"
|
||||
:label-col-props="{ span: 5, offset: 0 }"
|
||||
:wrapper-col-props="{ span: 19, offset: 0 }"
|
||||
label-align="left"
|
||||
labelAlign="right"
|
||||
:labelCol="{ span: 4 }"
|
||||
:wrapperCol="{ span: 20 }"
|
||||
>
|
||||
<a-form-item required field="mobile" label="新手机号">
|
||||
<a-input v-model.trim="form.mobile" size="small" placeholder="请输入新的手机号" />
|
||||
</a-form-item>
|
||||
<a-form-item required field="captcha" label="获取验证码">
|
||||
<a-input v-model.trim="form.captcha" size="small" placeholder="请输入验证码">
|
||||
<FormItem required name="mobile" label="新手机号">
|
||||
<Input v-model:value="form.mobile" size="small" placeholder="请输入新的手机号" />
|
||||
</FormItem>
|
||||
<FormItem required name="captcha" label="获取验证码">
|
||||
<Input v-model:value="form.captcha" size="small" placeholder="请输入验证码">
|
||||
<template #suffix>
|
||||
<span v-if="countdown <= 0" @click="sendCaptcha">发送验证码</span>
|
||||
<span v-else>{{ countdown }}s</span>
|
||||
</template>
|
||||
</a-input>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</Input>
|
||||
</FormItem>
|
||||
</Form>
|
||||
<PuzzleVerification
|
||||
:show="verificationVisible"
|
||||
@submit="handleVerificationSubmit"
|
||||
@ -80,6 +85,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { Button, Form, FormItem, Input, Table, message, Avatar } from 'ant-design-vue';
|
||||
import Container from '@/components/container.vue';
|
||||
import Modal from '@/components/modal.vue';
|
||||
import PuzzleVerification from '@/views/login/components/PuzzleVerification.vue';
|
||||
@ -124,35 +130,32 @@ const dataSource = computed(() => {
|
||||
const formRules = {
|
||||
mobile: [
|
||||
{
|
||||
required: true,
|
||||
message: '请填写手机号',
|
||||
trigger: ['blur', 'change'],
|
||||
},
|
||||
{
|
||||
validator: (value: string, callback: (error?: string) => void) => {
|
||||
if (!/^1[3-9]\d{9}$/.test(value)) {
|
||||
callback('手机号格式不正确');
|
||||
} else {
|
||||
callback();
|
||||
validator: (_rule: any, value: string) => {
|
||||
if (!value) {
|
||||
return Promise.reject('请填写手机号');
|
||||
}
|
||||
if (!/^1[3-9]\d{9}$/.test(value)) {
|
||||
return Promise.reject('手机号格式不正确');
|
||||
}
|
||||
return Promise.resolve();
|
||||
},
|
||||
required: true,
|
||||
trigger: ['blur', 'change'],
|
||||
},
|
||||
],
|
||||
captcha: [
|
||||
{
|
||||
required: true,
|
||||
message: '请填写验证码',
|
||||
trigger: ['blur', 'change'],
|
||||
},
|
||||
{
|
||||
validator: (value: string, callback: (error?: string) => void) => {
|
||||
if (!/^\d{6}$/.test(value)) {
|
||||
callback('验证码必须是6位数字');
|
||||
} else {
|
||||
callback();
|
||||
validator: (rule, value) => {
|
||||
if (!value) {
|
||||
return Promise.reject('请填写验证码');
|
||||
}
|
||||
if (!/^\d{6}$/.test(value)) {
|
||||
return Promise.reject('验证码必须是6位数字');
|
||||
}
|
||||
return Promise.resolve();
|
||||
},
|
||||
|
||||
trigger: ['blur', 'change'],
|
||||
},
|
||||
],
|
||||
@ -205,7 +208,7 @@ function openEditMobileModal() {
|
||||
|
||||
async function handleSubmitUserInfo() {
|
||||
await updateMyInfo(userInfoForm);
|
||||
AMessage.success('修改成功!');
|
||||
message.success('修改成功!');
|
||||
}
|
||||
|
||||
async function sendCaptcha() {
|
||||
@ -215,7 +218,7 @@ async function sendCaptcha() {
|
||||
verificationVisible.value = true;
|
||||
isSendCaptcha.value = true;
|
||||
}
|
||||
AMessage.error('请填写正确的手机号!');
|
||||
message.error('请填写正确的手机号!');
|
||||
} catch (error) {
|
||||
console.log('手机号验证失败:', error);
|
||||
}
|
||||
@ -233,7 +236,7 @@ function beginCountdown() {
|
||||
|
||||
async function handleVerificationSubmit() {
|
||||
await sendUpdateMobileCaptcha({ mobile: form.mobile });
|
||||
AMessage.success('发送成功');
|
||||
message.success('发送成功');
|
||||
verificationVisible.value = false;
|
||||
countdown.value = 60;
|
||||
beginCountdown();
|
||||
@ -241,13 +244,13 @@ async function handleVerificationSubmit() {
|
||||
|
||||
async function handleUpdateMobile() {
|
||||
if (!isSendCaptcha.value) {
|
||||
AMessage.error('请先获取验证码!');
|
||||
message.error('请先获取验证码!');
|
||||
return false;
|
||||
}
|
||||
const res = await formRef.value.validate();
|
||||
if (res === true || res === undefined) {
|
||||
await updateMobile(form);
|
||||
AMessage.success('修改成功!');
|
||||
message.success('修改成功!');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,34 +0,0 @@
|
||||
<!--
|
||||
* @Author: 田鑫
|
||||
* @Date: 2023-03-05 15:13:44
|
||||
* @LastEditors: 田鑫
|
||||
* @LastEditTime: 2023-03-05 15:43:18
|
||||
* @Description: 模拟登录鉴权页
|
||||
-->
|
||||
|
||||
<template>
|
||||
<a-modal title="登录鉴权页" :visible="true" :footer="false">
|
||||
<div w100 align-center>
|
||||
<a-button w-160 @click="login" :loading="loading" type="primary">登录</a-button>
|
||||
</div>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const loading = ref(false);
|
||||
|
||||
function login() {
|
||||
loading.value = true;
|
||||
localStorage.setItem('satoken', '123asdzxc');
|
||||
AMessage.success('登录鉴权成功,准备跳转');
|
||||
setTimeout(() => {
|
||||
loading.value = false;
|
||||
router.push({ name: route.query?.redirect ? route.query?.redirect : 'dashboard' });
|
||||
}, 1500);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
</style>
|
||||
@ -1,19 +0,0 @@
|
||||
<!--
|
||||
* @Author: 田鑫
|
||||
* @Date: 2023-03-05 14:27:21
|
||||
* @LastEditors: 田鑫
|
||||
* @LastEditTime: 2023-03-05 15:14:15
|
||||
* @Description:
|
||||
-->
|
||||
<template>
|
||||
<a-modal title="选择企业:" :visible="true">
|
||||
<a-select v-model="enterprise" placeholder="请选择您的企业"></a-select>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
const enterprise = ref('');
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
</style>
|
||||
@ -6,7 +6,7 @@
|
||||
<div class="m-auto mt-24px max-w-1000px">
|
||||
<Container title="推荐产品" class="container-body">
|
||||
<div class="grid grid-cols-3 gap-20px">
|
||||
<Product v-for="product in products" :key="product.id" :product="product" @refresh="getProductList" />
|
||||
<!-- <Product v-for="product in products" :key="product.id" :product="product" @refresh="getProductList" /> -->
|
||||
</div>
|
||||
<NoData v-if="products.length === 0" />
|
||||
</Container>
|
||||
@ -20,7 +20,7 @@
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import Container from '@/components/container.vue';
|
||||
import Product from '@/views/components/workplace/modules/product.vue';
|
||||
// import Product from '@/views/components/workplace/modules/product.vue';
|
||||
import Case from '@/views/components/workplace/modules/case.vue';
|
||||
import { fetchProductList, fetchSuccessCaseList } from '@/api/all/index';
|
||||
import { ref, onMounted } from 'vue';
|
||||
|
||||
@ -2,16 +2,15 @@
|
||||
<div class="container">
|
||||
<div class="flex arco-row-justify-space-between">
|
||||
<img class="avatar" :src="props.product.image" :alt="props.product.name" />
|
||||
<a-tag v-if="props.product.status === Status.Enable" class="status status-enable">已开通</a-tag>
|
||||
<a-tag v-if="props.product.status === Status.Disable" class="status status-disable">未开通</a-tag>
|
||||
<a-tag v-if="props.product.status === Status.EXPIRED" class="status status-expired">已到期</a-tag>
|
||||
<a-tag v-if="props.product.status === Status.TRIAL_ENDS" class="status status-expired">试用结束</a-tag>
|
||||
<a-countdown
|
||||
<Tag v-if="props.product.status === Status.Enable" class="status status-enable">已开通</Tag>
|
||||
<Tag v-if="props.product.status === Status.Disable" class="status status-disable">未开通</Tag>
|
||||
<Tag v-if="props.product.status === Status.EXPIRED" class="status status-expired">已到期</Tag>
|
||||
<Tag v-if="props.product.status === Status.TRIAL_ENDS" class="status status-expired">试用结束</Tag>
|
||||
<Countdown
|
||||
v-if="props.product.status === Status.ON_TRIAL"
|
||||
class="status-on-trill"
|
||||
title="试用中"
|
||||
:value="1000 * (props.product.expired_at ?? 0)"
|
||||
:now="now()"
|
||||
format="D天H时m分s秒"
|
||||
/>
|
||||
</div>
|
||||
@ -22,47 +21,50 @@
|
||||
</p>
|
||||
</div>
|
||||
<div class="footer flex arco-row-justify-start">
|
||||
<a-button
|
||||
<Button
|
||||
v-if="props.product.status === Status.Enable || props.product.status === Status.ON_TRIAL"
|
||||
type="primary"
|
||||
size="mini"
|
||||
size="small"
|
||||
class="mr-8px"
|
||||
@click="gotoModule(props.product.id)"
|
||||
>
|
||||
进入模块
|
||||
</a-button>
|
||||
<a-button
|
||||
</Button>
|
||||
<Button
|
||||
v-if="props.product.status === Status.TRIAL_ENDS || props.product.status === Status.EXPIRED"
|
||||
size="mini"
|
||||
size="small"
|
||||
type="primary"
|
||||
class="mr-8px"
|
||||
@click="visible = true"
|
||||
>
|
||||
立即购买
|
||||
</a-button>
|
||||
<a-button
|
||||
</Button>
|
||||
<Button
|
||||
v-if="props.product.status === Status.ON_TRIAL"
|
||||
class="mr-8px"
|
||||
size="mini"
|
||||
type="outline"
|
||||
size="small"
|
||||
type="primary"
|
||||
ghost
|
||||
@click="visible = true"
|
||||
>
|
||||
升级购买
|
||||
</a-button>
|
||||
<a-button
|
||||
</Button>
|
||||
<Button
|
||||
v-if="props.product.status === Status.TRIAL_ENDS || props.product.status === Status.EXPIRED"
|
||||
class="mr-8px"
|
||||
size="mini"
|
||||
type="outline"
|
||||
size="small"
|
||||
type="primary"
|
||||
ghost
|
||||
@click="visible = true"
|
||||
>
|
||||
联系客服
|
||||
</a-button>
|
||||
<a-popconfirm focusLock title="试用产品" content="确定试用该产品吗?" @ok="handleTrial(props.product.id)">
|
||||
<a-button v-if="props.product.status === Status.Disable" size="mini" type="outline"> 免费试用7天 </a-button>
|
||||
</a-popconfirm>
|
||||
</Button>
|
||||
<Popconfirm title="试用产品" ok-text="确定" cancel-text="取消" @confirm="handleTrial(props.product.id)">
|
||||
<template #description>确定试用该产品吗?</template>
|
||||
<Button v-if="props.product.status === Status.Disable" size="small" type="default" ghost> 免费试用7天 </Button>
|
||||
</Popconfirm>
|
||||
</div>
|
||||
<CustomerServiceModal v-model:visible="visible" />
|
||||
<CustomerServiceModal v-model:open="visible" centered/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -71,6 +73,8 @@ import { now } from '@vueuse/core';
|
||||
import { trialProduct } from '@/api/all';
|
||||
import { useRouter } from 'vue-router';
|
||||
import CustomerServiceModal from '@/components/customer-service-modal.vue';
|
||||
import { Button, message, Tag, Statistic, Popconfirm } from 'ant-design-vue';
|
||||
const { Countdown } = Statistic;
|
||||
|
||||
import { useSidebarStore } from '@/stores/modules/side-bar';
|
||||
import { useEnterpriseStore } from '@/stores/modules/enterprise';
|
||||
@ -110,7 +114,7 @@ const handleTrial = async (id: any) => {
|
||||
if (code === 200) {
|
||||
getUserEnterpriseInfo();
|
||||
|
||||
AMessage.success('试用成功!');
|
||||
message.success('试用成功!');
|
||||
emit('refresh');
|
||||
}
|
||||
};
|
||||
@ -174,7 +178,7 @@ const gotoModule = (menuId: number) => {
|
||||
border-radius: 4px;
|
||||
background: rgba(255, 245, 222, 1);
|
||||
|
||||
:deep(.arco-statistic-title) {
|
||||
:deep(.ant-statistic-title) {
|
||||
font-family: $font-family-medium;
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
@ -184,7 +188,7 @@ const gotoModule = (menuId: number) => {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
:deep(.arco-statistic-value) {
|
||||
:deep(.ant-statistic-content) {
|
||||
font-family: $font-family-medium;
|
||||
font-weight: 400;
|
||||
font-size: 10px;
|
||||
|
||||
@ -1,20 +1,19 @@
|
||||
<template>
|
||||
<a-modal v-model:visible="visible" title="删除对话" width="400px" @close="onClose">
|
||||
<Modal v-model:open="visible" title="删除对话" width="400px" @cancel="onClose" centered>
|
||||
<div class="flex items-center">
|
||||
<img :src="icon1" width="20" height="20" class="mr-12px" />
|
||||
<span>确认删除对话吗?删除后,聊天记录将不可恢复。</span>
|
||||
</div>
|
||||
<template #footer>
|
||||
<a-button size="large" @click="onClose">取消</a-button>
|
||||
<a-button type="primary" class="ml-16px !bg-#f64b31 !border-none" status="danger" size="large" @click="onDelete"
|
||||
>确定</a-button
|
||||
>
|
||||
<Button @click="onClose">取消</Button>
|
||||
<Button type="primary" danger @click="onDelete">确定</Button>
|
||||
</template>
|
||||
</a-modal>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { Modal, Button, message } from 'ant-design-vue';
|
||||
import { deleteHistoryItem } from '@/api/all/chat';
|
||||
import icon1 from '@/assets/img/media-account/icon-warn-1.png';
|
||||
|
||||
@ -41,7 +40,7 @@ const open = (record) => {
|
||||
async function onDelete() {
|
||||
const { code } = await deleteHistoryItem(conversationId.value);
|
||||
if (code === 200) {
|
||||
AMessage.success('删除成功');
|
||||
message.success('删除成功');
|
||||
emits('delete', conversationId.value);
|
||||
onClose();
|
||||
}
|
||||
|
||||
@ -1,18 +1,19 @@
|
||||
<template>
|
||||
<a-modal v-model:visible="visible" title="确定删除评论?" width="400px" @close="onClose">
|
||||
<Modal v-model:open="visible" title="确定删除评论?" width="400px" @cancel="onClose" centered>
|
||||
<div class="flex items-center">
|
||||
<img :src="icon1" width="20" height="20" class="mr-12px" />
|
||||
<span>删除的评论将从对话中消失,但仍在被引用的评论中可见</span>
|
||||
</div>
|
||||
<template #footer>
|
||||
<a-button size="large" @click="onClose">取消</a-button>
|
||||
<a-button type="primary" class="ml-16px !border-none" size="large" @click="onDelete">删除</a-button>
|
||||
<Button @click="onClose">取消</Button>
|
||||
<Button type="primary" danger @click="onDelete">确定</Button>
|
||||
</template>
|
||||
</a-modal>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { Modal, Button } from 'ant-design-vue';
|
||||
import icon1 from '@/assets/img/media-account/icon-warn-1.png';
|
||||
|
||||
const emits = defineEmits(['delete', 'close']);
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
<script lang="jsx">
|
||||
import { Image, Spin, Button, Input, Textarea, Affix } from '@arco-design/web-vue';
|
||||
import { Button, Input, Image } from 'ant-design-vue';
|
||||
const { TextArea } = Input;
|
||||
import TextOverTips from '@/components/text-over-tips';
|
||||
import SvgIcon from '@/components/svg-icon/index.vue';
|
||||
import DeleteCommentModal from './delete-comment-modal.vue';
|
||||
@ -125,23 +126,23 @@ export default {
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Textarea
|
||||
<TextArea
|
||||
ref={textAreaRef}
|
||||
auto-size
|
||||
class={`max-h-220px overflow-y-auto ${isReplay.value ? 'pt-38px' : ''}`}
|
||||
autoSize
|
||||
class={`max-h-220px overflow-y-auto textarea-box ${isReplay.value ? 'pt-38px' : ''}`}
|
||||
size="large"
|
||||
placeholder="输入评论"
|
||||
v-model={comment.value}
|
||||
v-model:value={comment.value}
|
||||
onPressEnter={onComment}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{comment.value && (
|
||||
<div class="flex justify-end mt-12px">
|
||||
<Button type="outline" class="mr-12px rounded-8px" size="medium" onClick={onClearComment}>
|
||||
<Button type="primary" ghost class="cancel-btn mr-12px !rounded-8px" onClick={onClearComment}>
|
||||
取消
|
||||
</Button>
|
||||
<Button type="primary" class="rounded-8px" size="medium" onClick={onComment}>
|
||||
<Button type="primary" class="!rounded-8px" onClick={onComment}>
|
||||
发送
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@ -19,6 +19,9 @@
|
||||
font-family: $font-family-medium;
|
||||
}
|
||||
}
|
||||
.cancel-btn {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
.ai-text {
|
||||
font-family: $font-family-medium;
|
||||
font-size: 16px;
|
||||
@ -30,7 +33,7 @@
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
:deep(.arco-textarea-wrapper) {
|
||||
.textarea-box {
|
||||
min-height: 38px;
|
||||
display: flex;
|
||||
border-color: transparent !important;
|
||||
@ -39,14 +42,11 @@
|
||||
background-color: #fff;
|
||||
color: #211f24 !important;
|
||||
transition: all 0.3s;
|
||||
.arco-textarea-mirror,
|
||||
.arco-textarea {
|
||||
padding: 8px 16px !important;
|
||||
}
|
||||
&:hover {
|
||||
border-color: #6d4cfe !important;
|
||||
}
|
||||
&.arco-textarea-focus {
|
||||
&:focus-within {
|
||||
border-color: #6d4cfe !important;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
<script lang="jsx">
|
||||
import { Image, Spin, Button } from '@arco-design/web-vue';
|
||||
import { Button } from 'ant-design-vue';
|
||||
import { Spin } from 'ant-design-vue';
|
||||
import AiSuggest from './components/ai-suggest/';
|
||||
|
||||
import { getShareWorksList, getShareWorksDetail, patchShareWorksConfirm } from '@/api/all/generationWorkshop.ts';
|
||||
@ -186,7 +187,7 @@ export default {
|
||||
下一条
|
||||
</Button>
|
||||
)}
|
||||
<Button type="outline" size="large" class="mr-12px" onClick={onBackList}>
|
||||
<Button type="primary" ghost size="large" class="mr-12px" onClick={onBackList}>
|
||||
返回列表
|
||||
</Button>
|
||||
{renderConfirmBtn()}
|
||||
@ -220,7 +221,7 @@ export default {
|
||||
</div>
|
||||
</header>
|
||||
{loading.value ? (
|
||||
<Spin spinning={loading.value} class="flex-1 w-full flex justify-center items-center" size={60} />
|
||||
<Spin spinning={loading.value} wrapperClassName="flex-1 w-full flex justify-center items-center" size="large" />
|
||||
) : (
|
||||
<section class={`page-wrap relative ${isExpand.value ? 'expand' : ''}`}>
|
||||
<div class="fold-box cursor-pointer" onClick={() => (isExpand.value = true)}>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<script lang="jsx">
|
||||
import TextOverTips from '@/components/text-over-tips';
|
||||
import { Image, Spin } from '@arco-design/web-vue';
|
||||
import { Image, Spin } from 'ant-design-vue';
|
||||
|
||||
import { exactFormatTime } from '@/utils/tools';
|
||||
import { handleUserHome } from '@/utils/user.ts';
|
||||
@ -52,7 +52,7 @@ export default {
|
||||
</header>
|
||||
<section class="page-wrapper flex justify-center">
|
||||
{loading.value ? (
|
||||
<Spin spinning={loading.value} class="w-full flex justify-center items-center" size={60} />
|
||||
<Spin spinning={loading.value} wrapperClassName="w-full flex justify-center items-center" size="large" />
|
||||
) : (
|
||||
<div class="explore-container">
|
||||
<div class="explore-list-wrap pt-24px pb-28px">
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<script lang="tsx">
|
||||
import { Tabs, TabPane } from 'ant-design-vue';
|
||||
import { Tabs, TabPane, Button } from 'ant-design-vue';
|
||||
import ManuscriptList from './manuscript/list/index.vue';
|
||||
import ManuscriptCheckList from './manuscript/check-list/index.vue';
|
||||
import ShareManuscriptModal from '@/views/material-center/components/finished-products/manuscript/components/share-manuscript-modal/index.vue';
|
||||
@ -33,17 +33,17 @@ export default defineComponent({
|
||||
v-slots={{
|
||||
rightExtra: () => (
|
||||
<div class="flex items-center">
|
||||
<a-button type="outline" size="medium" onClick={handleShareModal}>
|
||||
<Button type="primary" ghost size="medium" onClick={handleShareModal}>
|
||||
分享内容稿件
|
||||
</a-button>
|
||||
</Button>
|
||||
{showManuscriptList.value && (
|
||||
<a-button
|
||||
<Button
|
||||
type="primary"
|
||||
size="medium"
|
||||
class="ml-12px"
|
||||
onClick={openUploadModal}
|
||||
v-slots={{
|
||||
icon: () => <icon-plus size="16" />,
|
||||
icon: () => <icon-plus size="16" class="mr-8px" />,
|
||||
default: () => '上传内容稿件',
|
||||
}}
|
||||
/>
|
||||
|
||||
@ -8,42 +8,42 @@
|
||||
<div class="filter-row">
|
||||
<div class="filter-row-item">
|
||||
<span class="label">内容稿件标题</span>
|
||||
<a-space size="medium">
|
||||
<a-input
|
||||
v-model="query.title"
|
||||
<Space size="medium">
|
||||
<Input
|
||||
v-model:value="query.title"
|
||||
class="!w-240px"
|
||||
placeholder="请输入内容稿件标题"
|
||||
size="medium"
|
||||
allow-clear
|
||||
allowClear
|
||||
@change="handleSearch"
|
||||
>
|
||||
<template #prefix>
|
||||
<icon-search />
|
||||
</template>
|
||||
</a-input>
|
||||
</a-space>
|
||||
</Input>
|
||||
</Space>
|
||||
</div>
|
||||
<div class="filter-row-item">
|
||||
<span class="label">序号</span>
|
||||
<a-space size="medium">
|
||||
<a-input
|
||||
v-model="query.uid"
|
||||
<Space size="medium">
|
||||
<Input
|
||||
v-model:value="query.uid"
|
||||
class="!w-160px"
|
||||
placeholder="请输入序号"
|
||||
size="medium"
|
||||
allow-clear
|
||||
allowClear
|
||||
@change="handleSearch"
|
||||
>
|
||||
<template #prefix>
|
||||
<icon-search />
|
||||
</template>
|
||||
</a-input>
|
||||
</a-space>
|
||||
</Input>
|
||||
</Space>
|
||||
</div>
|
||||
<div class="filter-row-item" v-if="query.audit_status === AuditStatus.Pending">
|
||||
<span class="label">上传时间</span>
|
||||
<a-range-picker
|
||||
v-model="created_at"
|
||||
<DatePicker.RangePicker
|
||||
v-model:value="created_at"
|
||||
size="medium"
|
||||
allow-clear
|
||||
format="YYYY-MM-DD"
|
||||
@ -54,25 +54,25 @@
|
||||
<template v-if="[AuditStatus.Auditing, AuditStatus.Passed].includes(query.audit_status)">
|
||||
<div class="filter-row-item">
|
||||
<span class="label">审核平台</span>
|
||||
<a-select
|
||||
v-model="query.audit_platform"
|
||||
size="medium"
|
||||
<Select
|
||||
v-model:value="query.audit_platform"
|
||||
size="middle"
|
||||
placeholder="全部"
|
||||
allow-clear
|
||||
allowClear
|
||||
@change="handleSearch"
|
||||
class="!w-160px"
|
||||
>
|
||||
<a-option v-for="(item, index) in PLATFORMS" :key="index" :value="item.value" :label="item.label">{{
|
||||
<Option v-for="(item, index) in PLATFORMS" :key="index" :value="item.value" :label="item.label">{{
|
||||
item.label
|
||||
}}</a-option>
|
||||
</a-select>
|
||||
}}</Option>
|
||||
</Select>
|
||||
</div>
|
||||
<div class="filter-row-item">
|
||||
<span class="label">审核时间</span>
|
||||
<a-range-picker
|
||||
v-model="audit_started_at"
|
||||
<DatePicker.RangePicker
|
||||
v-model:value="audit_started_at"
|
||||
size="medium"
|
||||
allow-clear
|
||||
allowClear
|
||||
format="YYYY-MM-DD"
|
||||
class="!w-280px"
|
||||
@change="(value) => onDateChange(value, 'audit_started_at')"
|
||||
@ -81,27 +81,31 @@
|
||||
</template>
|
||||
|
||||
<div class="filter-row-item">
|
||||
<a-button type="outline" class="mr-12px" size="medium" @click="handleSearch">
|
||||
<Button type="primary" ghost class="mr-12px" size="medium" @click="handleSearch">
|
||||
<template #icon>
|
||||
<icon-search />
|
||||
<icon-search class="mr-8px"/>
|
||||
</template>
|
||||
<template #default>搜索</template>
|
||||
</a-button>
|
||||
<a-button size="medium" @click="handleReset">
|
||||
</Button>
|
||||
<Button size="medium" @click="handleReset">
|
||||
<template #icon>
|
||||
<icon-refresh />
|
||||
<icon-refresh class="mr-8px" />
|
||||
</template>
|
||||
<template #default>重置</template>
|
||||
</a-button>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { Button, Input, Select, Space, DatePicker } from 'ant-design-vue';
|
||||
const { Option } = Select;
|
||||
import { defineEmits, defineProps } from 'vue';
|
||||
import { PLATFORMS } from '@/views/material-center/components/finished-products/manuscript/check-list/constants';
|
||||
import { AuditStatus } from '@/views/material-center/components/finished-products/constants';
|
||||
import { ref, nextTick } from 'vue';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
const props = defineProps({
|
||||
query: {
|
||||
@ -110,7 +114,7 @@ const props = defineProps({
|
||||
},
|
||||
});
|
||||
|
||||
const emits = defineEmits('search', 'reset', 'update:query');
|
||||
const emits = defineEmits(['search', 'reset', 'update:query']);
|
||||
const created_at = ref([]);
|
||||
const audit_started_at = ref([]);
|
||||
|
||||
@ -137,6 +141,7 @@ const onDateChange = (value, type) => {
|
||||
|
||||
const handleReset = () => {
|
||||
created_at.value = [];
|
||||
audit_started_at.value = [];
|
||||
emits('reset');
|
||||
};
|
||||
</script>
|
||||
|
||||
@ -1,25 +1,27 @@
|
||||
<template>
|
||||
<a-modal
|
||||
v-model:visible="visible"
|
||||
<Modal
|
||||
v-model:open="visible"
|
||||
title="删除内容稿件"
|
||||
width="480px"
|
||||
@close="onClose"
|
||||
centered
|
||||
@cancel="onClose"
|
||||
>
|
||||
<div class="flex items-center">
|
||||
<img :src="icon1" width="20" height="20" class="mr-12px" />
|
||||
<span>确认删除 {{ projectName }} 这个内容稿件吗?</span>
|
||||
</div>
|
||||
<template #footer>
|
||||
<a-button size="medium" @click="onClose">取消</a-button>
|
||||
<a-button type="primary" class="ml-16px" status="danger" size="medium" @click="onDelete"
|
||||
>确认删除</a-button
|
||||
<Button size="medium" @click="onClose">取消</Button>
|
||||
<Button type="primary" class="ml-16px" danger size="medium" @click="onDelete"
|
||||
>确认删除</Button
|
||||
>
|
||||
</template>
|
||||
</a-modal>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { Button, Modal, message } from 'ant-design-vue';
|
||||
import { deleteWork } from '@/api/all/generationWorkshop';
|
||||
import icon1 from '@/assets/img/media-account/icon-warn-1.png';
|
||||
|
||||
@ -48,7 +50,7 @@ const open = (record) => {
|
||||
async function onDelete() {
|
||||
const { code } = await deleteWork(projectId.value);
|
||||
if (code === 200) {
|
||||
AMessage.success('删除成功');
|
||||
message.success('删除成功');
|
||||
update()
|
||||
onClose();
|
||||
}
|
||||
|
||||
@ -1,45 +1,38 @@
|
||||
<template>
|
||||
<a-table
|
||||
<Table
|
||||
ref="tableRef"
|
||||
:data="dataSource"
|
||||
row-key="id"
|
||||
column-resizable
|
||||
:dataSource="dataSource"
|
||||
rowKey="id"
|
||||
:pagination="false"
|
||||
:scroll="{ x: '100%' }"
|
||||
class="flex-1 manuscript-table w-100%"
|
||||
bordered
|
||||
:row-selection="rowSelection"
|
||||
:selected-row-keys="selectedRowKeys"
|
||||
@sorter-change="handleSorterChange"
|
||||
@select="(selectedKeys, rowKeyValue, record) => emits('select', selectedKeys, rowKeyValue, record)"
|
||||
@select-all="(check) => emits('selectAll', check)"
|
||||
:rowSelection="rowSelection"
|
||||
:showSorterTooltip="false"
|
||||
@change="handleTableChange"
|
||||
>
|
||||
<template #empty>
|
||||
<template #emptyText>
|
||||
<NoData text="暂无稿件" />
|
||||
</template>
|
||||
<template #columns>
|
||||
<a-table-column
|
||||
<Column
|
||||
v-for="column in tableColumns"
|
||||
:key="column.dataIndex"
|
||||
:data-index="column.dataIndex"
|
||||
:dataIndex="column.dataIndex"
|
||||
:fixed="column.fixed"
|
||||
:width="column.width"
|
||||
:min-width="column.minWidth"
|
||||
:sortable="column.sortable"
|
||||
:minWidth="column.minWidth"
|
||||
:sorter="column.sortable"
|
||||
:align="column.align"
|
||||
ellipsis
|
||||
tooltip
|
||||
:ellipsis="true"
|
||||
>
|
||||
<template #title>
|
||||
<div class="flex items-center">
|
||||
<span class="cts mr-4px">{{ column.title }}</span>
|
||||
<a-tooltip v-if="column.tooltip" :content="column.tooltip" position="top">
|
||||
<Tooltip v-if="column.tooltip" :title="column.tooltip" placement="top">
|
||||
<icon-question-circle class="tooltip-icon color-#737478" size="16" />
|
||||
</a-tooltip>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</template>
|
||||
|
||||
<template v-if="column.dataIndex === 'customer_opinion'" #cell="{ record }">
|
||||
<template v-if="column.dataIndex === 'customer_opinion'" #customRender="{ record }">
|
||||
<p
|
||||
class="h-28px px-8px flex items-center rounded-2px w-fit"
|
||||
:style="{ background: getCustomerOpinionInfo(record.customer_opinion)?.bg }"
|
||||
@ -49,7 +42,7 @@
|
||||
}}</span>
|
||||
</p>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'platform'" #cell="{ record }">
|
||||
<template v-else-if="column.dataIndex === 'platform'" #customRender="{ record }">
|
||||
<template v-if="!PLATFORMS.find((item) => item.value === record.platform)"> - </template>
|
||||
<img
|
||||
v-else
|
||||
@ -59,75 +52,66 @@
|
||||
:src="PLATFORMS.find((item) => item.value === record.platform)?.icon"
|
||||
/>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'compliance_level'" #cell="{ record }">
|
||||
<template v-else-if="column.dataIndex === 'compliance_level'" #customRender="{ record }">
|
||||
<span class="cts num !color-#6D4CFE">{{
|
||||
record.ai_review?.compliance_level ? `${record.ai_review?.compliance_level}%` : '-'
|
||||
}}</span>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'title'" #cell="{ record }">
|
||||
<template v-else-if="column.dataIndex === 'title'" #customRender="{ record }">
|
||||
<TextOverTips :context="record.title" :line="3" class="title" @click="onDetail(record)" />
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'type'" #cell="{ record }">
|
||||
<template v-else-if="column.dataIndex === 'type'" #customRender="{ record }">
|
||||
<div class="flex items-center">
|
||||
<img
|
||||
:src="record.type === EnumManuscriptType.Image ? icon2 : icon3"
|
||||
width="16"
|
||||
height="16"
|
||||
class="mr-4px"
|
||||
/>
|
||||
<img :src="record.type === EnumManuscriptType.Image ? icon2 : icon3" width="16" height="16" class="mr-4px" />
|
||||
<span class="cts" :class="record.type === EnumManuscriptType.Image ? '!color-#25C883' : '!color-#6D4CFE'">{{
|
||||
record.type === EnumManuscriptType.Image ? '图文' : '视频'
|
||||
}}</span>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="['uploader', 'last_modifier'].includes(column.dataIndex)" #cell="{ record }">
|
||||
<template v-else-if="['uploader', 'last_modifier'].includes(column.dataIndex)" #customRender="{ record }">
|
||||
{{ record[column.dataIndex].name || record[column.dataIndex].mobile }}
|
||||
</template>
|
||||
<template
|
||||
#cell="{ record }"
|
||||
v-else-if="
|
||||
['created_at', 'last_modified_at', 'audit_started_at', 'audit_passed_at'].includes(column.dataIndex)
|
||||
"
|
||||
#customRender="{ record }"
|
||||
v-else-if="['created_at', 'last_modified_at', 'audit_started_at', 'audit_passed_at'].includes(column.dataIndex)"
|
||||
>
|
||||
<span class="cts num">{{ exactFormatTime(record[column.dataIndex]) }}</span>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'cover'" #cell="{ record }">
|
||||
<template v-else-if="column.dataIndex === 'cover'" #customRender="{ record }">
|
||||
<HoverImagePreview :src="record.cover">
|
||||
<a-image :width="64" :height="64" :src="record.cover" class="!rounded-6px" fit="cover">
|
||||
<template #error>
|
||||
<img :src="icon4" class="w-full h-full" />
|
||||
</template>
|
||||
</a-image>
|
||||
<ImgLazyLoad :width="64" :height="64" :src="record.cover" class="!rounded-6px" />
|
||||
</HoverImagePreview>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'operation'" #cell="{ record }">
|
||||
<template v-else-if="column.dataIndex === 'operation'" #customRender="{ record }">
|
||||
<div class="flex items-center">
|
||||
<img class="mr-8px cursor-pointer" :src="icon1" width="14" height="14" @click="onDelete(record)" />
|
||||
<a-button type="outline" size="mini" @click="onShare(record)" v-if="audit_status === AuditStatus.Passed"
|
||||
>分享</a-button
|
||||
<Button type="primary" ghost size="small" @click="onShare(record)" v-if="audit_status === AuditStatus.Passed"
|
||||
>分享</Button
|
||||
>
|
||||
<a-button
|
||||
type="outline"
|
||||
size="mini"
|
||||
<Button
|
||||
type="primary"
|
||||
ghost
|
||||
size="small"
|
||||
@click="onCheck(record)"
|
||||
v-else-if="audit_status === AuditStatus.Pending"
|
||||
>审核</a-button
|
||||
>审核</Button
|
||||
>
|
||||
<a-button type="outline" size="mini" @click="onScan(record)" v-else>查看</a-button>
|
||||
<Button type="primary" ghost size="small" @click="onScan(record)" v-else>查看</Button>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else #cell="{ record }">
|
||||
<template v-else #customRender="{ record }">
|
||||
{{ formatTableField(column, record, true) }}
|
||||
</template>
|
||||
</a-table-column>
|
||||
</template>
|
||||
</a-table>
|
||||
</Column>
|
||||
</Table>
|
||||
|
||||
<ShareModal ref="shareModalRef" />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { Button, Tooltip, Table, Image } from 'ant-design-vue';
|
||||
const { Column } = Table;
|
||||
import { formatTableField, exactFormatTime } from '@/utils/tools';
|
||||
import { EnumManuscriptType } from '@/views/material-center/components/finished-products/manuscript/list/constants';
|
||||
import { patchWorkAuditsAudit } from '@/api/all/generationWorkshop';
|
||||
@ -139,16 +123,17 @@ import { AuditStatus } from '@/views/material-center/components/finished-product
|
||||
|
||||
import { slsWithCatch } from '@/utils/stroage.ts';
|
||||
|
||||
import TextOverTips from '@/components/text-over-tips';
|
||||
import TextOverTips from '@/components/text-over-tips'
|
||||
import ImgLazyLoad from "@/components/img-lazy-load";
|
||||
import ShareModal from '@/views/material-center/components/finished-products/manuscript/components/share-manuscript-modal/share-modal.vue';
|
||||
import HoverImagePreview from '@/components/hover-image-preview';
|
||||
|
||||
import icon1 from '@/assets/img/media-account/icon-delete.png';
|
||||
import icon2 from '@/assets/img/creative-generation-workshop/icon-photo.png';
|
||||
import icon3 from '@/assets/img/creative-generation-workshop/icon-video.png';
|
||||
import icon4 from '@/assets/img/error-img.png';
|
||||
// import icon4 from '@/assets/img/error-img.png';
|
||||
|
||||
const emits = defineEmits([ 'sorterChange', 'delete', 'select', 'selectAll']);
|
||||
const emits = defineEmits(['sorterChange', 'delete', 'select', 'selectAll']);
|
||||
const router = useRouter();
|
||||
|
||||
const props = defineProps({
|
||||
@ -160,10 +145,6 @@ const props = defineProps({
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
rowSelection: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
selectedRowKeys: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
@ -176,8 +157,20 @@ const props = defineProps({
|
||||
const tableRef = ref(null);
|
||||
const shareModalRef = ref(null);
|
||||
|
||||
const handleSorterChange = (column, order) => {
|
||||
emits('sorterChange', column, order === 'ascend' ? 'asc' : 'desc');
|
||||
const handleTableChange = (pagination, filters, sorter) => {
|
||||
if (sorter && sorter.field) {
|
||||
emits('sorterChange', sorter.field, sorter.order === 'ascend' ? 'asc' : 'desc');
|
||||
}
|
||||
};
|
||||
|
||||
const rowSelection = {
|
||||
selectedRowKeys: computed(() => props.selectedRowKeys),
|
||||
onSelect: (record, selected) => {
|
||||
emits('select', record, selected);
|
||||
},
|
||||
onSelectAll: (selected) => {
|
||||
emits('selectAll', selected);
|
||||
},
|
||||
};
|
||||
const onDelete = (item) => {
|
||||
emits('delete', item);
|
||||
|
||||
@ -219,7 +219,7 @@ export const INITIAL_QUERY = {
|
||||
title: '',
|
||||
created_at: [],
|
||||
audit_started_at: [],
|
||||
audit_platform: '',
|
||||
audit_platform: undefined,
|
||||
sort_column: undefined,
|
||||
sort_order: undefined,
|
||||
};
|
||||
|
||||
@ -13,28 +13,29 @@
|
||||
class="flex justify-end mb-12px"
|
||||
v-if="[AuditStatus.Pending, AuditStatus.Auditing].includes(query.audit_status)"
|
||||
>
|
||||
<a-button
|
||||
type="outline"
|
||||
<Button
|
||||
type="primary"
|
||||
ghost
|
||||
class="w-fit"
|
||||
size="medium"
|
||||
@click="handleBatchCheck"
|
||||
v-if="query.audit_status === AuditStatus.Pending"
|
||||
>批量审核</a-button
|
||||
>批量审核</Button
|
||||
>
|
||||
<a-button
|
||||
type="outline"
|
||||
<Button
|
||||
type="primary"
|
||||
ghost
|
||||
class="w-fit"
|
||||
size="medium"
|
||||
@click="handleBatchView"
|
||||
v-if="query.audit_status === AuditStatus.Auditing"
|
||||
>批量查看</a-button
|
||||
>批量查看</Button
|
||||
>
|
||||
</div>
|
||||
|
||||
<ManuscriptCheckTable
|
||||
:key="query.audit_status"
|
||||
:tableColumns="tableColumns"
|
||||
:rowSelection="rowSelection"
|
||||
:selectedRowKeys="selectedRowKeys"
|
||||
:dataSource="dataSource"
|
||||
:audit_status="query.audit_status"
|
||||
@ -44,16 +45,15 @@
|
||||
@selectAll="handleSelectAll"
|
||||
/>
|
||||
<div v-if="pageInfo.total > 0" class="pagination-row">
|
||||
<a-pagination
|
||||
<Pagination
|
||||
:total="pageInfo.total"
|
||||
size="mini"
|
||||
show-total
|
||||
show-jumper
|
||||
show-page-size
|
||||
size="small"
|
||||
:showTotal="(total, range) => `共 ${total} 条`"
|
||||
showSizeChanger
|
||||
showQuickJumper
|
||||
:current="pageInfo.page"
|
||||
:page-size="pageInfo.page_size"
|
||||
:pageSize="pageInfo.page_size"
|
||||
@change="onPageChange"
|
||||
@page-size-change="onPageSizeChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -62,7 +62,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<script lang="jsx" setup>
|
||||
import { Button, Message as AMessage } from '@arco-design/web-vue';
|
||||
import { Button, Pagination, message } from 'ant-design-vue';
|
||||
import FilterBlock from './components/filter-block';
|
||||
import ManuscriptCheckTable from './components/manuscript-check-table';
|
||||
import DeleteManuscriptModal from './components/manuscript-check-table/delete-manuscript-modal.vue';
|
||||
@ -83,22 +83,16 @@ const props = defineProps({
|
||||
const {
|
||||
dataSource,
|
||||
pageInfo,
|
||||
rowSelection,
|
||||
onPageChange,
|
||||
onPageSizeChange,
|
||||
resetPageInfo,
|
||||
selectedRowKeys,
|
||||
selectedRows,
|
||||
handleSelect,
|
||||
handleSelectAll,
|
||||
DEFAULT_PAGE_INFO,
|
||||
} = useTableSelectionWithPagination({
|
||||
onPageChange: () => {
|
||||
getData();
|
||||
},
|
||||
onPageSizeChange: () => {
|
||||
getData();
|
||||
},
|
||||
});
|
||||
const router = useRouter();
|
||||
const tableColumns = ref([]);
|
||||
@ -140,7 +134,7 @@ const handleSorterChange = (column, order) => {
|
||||
};
|
||||
const handleBatchCheck = () => {
|
||||
if (!selectedRows.value.length) {
|
||||
AMessage.warning('请选择需审核的内容稿件');
|
||||
message.warning('请选择需审核的内容稿件');
|
||||
return;
|
||||
}
|
||||
|
||||
@ -151,7 +145,7 @@ const handleBatchCheck = () => {
|
||||
};
|
||||
const handleBatchView = () => {
|
||||
if (!selectedRows.value.length) {
|
||||
AMessage.warning('请选择需查看的内容稿件');
|
||||
message.warning('请选择需查看的内容稿件');
|
||||
return;
|
||||
}
|
||||
|
||||
@ -175,7 +169,6 @@ const handleDelete = (item) => {
|
||||
deleteManuscriptModalRef.value?.open({ id, name: `“${title}”` });
|
||||
};
|
||||
|
||||
|
||||
watch(
|
||||
() => props.audit_status,
|
||||
(newVal) => {
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
<template>
|
||||
<a-modal
|
||||
v-model:visible="visible"
|
||||
<Modal
|
||||
v-model:open="visible"
|
||||
:title="action === 'exit' ? '退出审核' : '切换内容稿件'"
|
||||
width="480px"
|
||||
@close="onClose"
|
||||
centered
|
||||
@cancel="onClose"
|
||||
>
|
||||
<div class="flex items-center">
|
||||
<img :src="icon1" width="20" height="20" class="mr-12px" />
|
||||
@ -14,16 +15,17 @@
|
||||
}}</span>
|
||||
</div>
|
||||
<template #footer>
|
||||
<a-button size="medium" @click="onClose">继续编辑</a-button>
|
||||
<a-button type="primary" class="ml-8px" size="medium" @click="onConfirm">
|
||||
<Button size="medium" @click="onClose">继续编辑</Button>
|
||||
<Button type="primary" class="ml-8px" size="medium" @click="onConfirm">
|
||||
{{ action === 'exit' ? '确认退出' : '确认切换' }}
|
||||
</a-button>
|
||||
</Button>
|
||||
</template>
|
||||
</a-modal>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { Button, Modal } from 'ant-design-vue';
|
||||
import icon1 from '@/assets/img/media-account/icon-warn-1.png';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
<template>
|
||||
<a-modal
|
||||
v-model:visible="visible"
|
||||
<Modal
|
||||
v-model:open="visible"
|
||||
title="提示"
|
||||
width="480px"
|
||||
@close="onClose"
|
||||
modal-class="upload-success11-modal"
|
||||
centered
|
||||
wrapClassName="upload-success11-modal"
|
||||
:footer="null"
|
||||
>
|
||||
<div class="flex items-center flex-col justify-center">
|
||||
@ -13,14 +14,12 @@
|
||||
<p class="text-14px lh-22px font-400 color-#737478 ld">想让内容更抓眼球、更吸流量吗?</p>
|
||||
<p class="text-14px lh-22px font-400 color-#737478 ld">试试「内容稿件分析」功能吧!</p>
|
||||
</div>
|
||||
<!-- <template #footer>
|
||||
<a-button type="primary" class="ml-8px" size="medium" @click="onConfirm">内容稿件分析</a-button>
|
||||
</template> -->
|
||||
</a-modal>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { Button, Modal } from 'ant-design-vue';
|
||||
import icon1 from '@/assets/img/media-account/icon-feedback-success.png';
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<script lang="jsx">
|
||||
import { Drawer, Image } from '@arco-design/web-vue';
|
||||
import { Drawer, Image } from 'ant-design-vue';
|
||||
import TextOverTips from '@/components/text-over-tips';
|
||||
|
||||
import icon1 from '@/assets/img/error-img.png';
|
||||
@ -32,12 +32,12 @@ export default {
|
||||
return () => (
|
||||
<Drawer
|
||||
title="审核列表"
|
||||
visible={visible.value}
|
||||
v-model:open={visible.value}
|
||||
width={420}
|
||||
class="check-list-drawer-xt"
|
||||
footer={false}
|
||||
header={false}
|
||||
onCancel={onClose}
|
||||
rootClassName="check-list-drawer-xt"
|
||||
footer={null}
|
||||
closable={false}
|
||||
onClose={onClose}
|
||||
>
|
||||
<div class="flex justify-between items-center h-56px px-24px">
|
||||
<div class="flex items-center">
|
||||
@ -61,13 +61,12 @@ export default {
|
||||
height={48}
|
||||
preview={false}
|
||||
src={item.cover}
|
||||
class="!rounded-4px mr-8px"
|
||||
fit="cover"
|
||||
class="!rounded-4px"
|
||||
v-slots={{
|
||||
error: () => <img src={icon1} class="w-full h-full" />,
|
||||
}}
|
||||
/>
|
||||
<div class="flex-1 overflow-hidden flex flex-col items-start">
|
||||
<div class="flex-1 overflow-hidden flex flex-col items-start ml-8px">
|
||||
<TextOverTips context={item.title} class={`cts !color-#211F24 title mb-4px !text-14px`} />
|
||||
<p class="cts !text-14px">{`合规程度:${
|
||||
item.ai_review?.compliance_level ? `${item.ai_review?.compliance_level}%` : '-'
|
||||
|
||||
@ -1,10 +1,12 @@
|
||||
.check-list-drawer-xt {
|
||||
.arco-drawer-mask {
|
||||
box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.15);
|
||||
.ant-drawer-mask {
|
||||
background-color: transparent;
|
||||
}
|
||||
.arco-drawer {
|
||||
box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.15);
|
||||
.arco-drawer-body {
|
||||
.ant-drawer-header {
|
||||
display: none;
|
||||
}
|
||||
.ant-drawer-body {
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@ -41,5 +43,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,10 +14,10 @@ export const TAB_LIST = [
|
||||
label: '文本',
|
||||
value: enumTab.TEXT,
|
||||
},
|
||||
// {
|
||||
// label: '图片',
|
||||
// value: enumTab.IMAGE,
|
||||
// },
|
||||
{
|
||||
label: '图片',
|
||||
value: enumTab.IMAGE,
|
||||
},
|
||||
];
|
||||
|
||||
export enum Enum_Level {
|
||||
|
||||
@ -24,6 +24,8 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Input } from 'ant-design-vue';
|
||||
const {TextArea} = Input
|
||||
import { ref, computed, watch, defineProps, defineEmits, onMounted, onUnmounted } from 'vue';
|
||||
import { escapeRegExp } from './constants';
|
||||
|
||||
|
||||
@ -1,23 +1,13 @@
|
||||
<script lang="jsx">
|
||||
import axios from 'axios';
|
||||
import { Swiper, SwiperSlide } from 'swiper/vue';
|
||||
import { Button, Form, Input, FormItem, Tabs, message, Image, Upload, Spin } from 'ant-design-vue';
|
||||
import { IconLoading } from '@arco-design/web-vue/es/icon';
|
||||
import {
|
||||
Image,
|
||||
Form,
|
||||
FormItem,
|
||||
Input,
|
||||
Textarea,
|
||||
Button,
|
||||
Tabs,
|
||||
Upload,
|
||||
TabPane,
|
||||
Spin,
|
||||
Message as AMessage,
|
||||
} from '@arco-design/web-vue';
|
||||
import TextOverTips from '@/components/text-over-tips';
|
||||
import HighlightTextarea from './highlight-textarea';
|
||||
|
||||
const { TabPane } = Tabs;
|
||||
|
||||
import 'swiper/css';
|
||||
import 'swiper/css/navigation';
|
||||
import { Navigation } from 'swiper/modules';
|
||||
@ -87,7 +77,7 @@ export default {
|
||||
|
||||
const onAgainCheck = () => {
|
||||
if (!isTextTab.value && !props.modelValue.files?.length) {
|
||||
AMessage.warning('请先上传需审核图片');
|
||||
message.warning('请先上传需审核图片');
|
||||
return;
|
||||
}
|
||||
emit('againCheck');
|
||||
@ -99,15 +89,7 @@ export default {
|
||||
activeTab.value = key;
|
||||
};
|
||||
const validate = () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
formRef.value?.validate((errors) => {
|
||||
if (errors) {
|
||||
reject();
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
return formRef.value?.validate();
|
||||
};
|
||||
const reset = () => {
|
||||
formRef.value?.resetFields?.();
|
||||
@ -139,24 +121,19 @@ export default {
|
||||
<Upload
|
||||
ref={uploadRef}
|
||||
action="/"
|
||||
draggable
|
||||
class="w-fit"
|
||||
custom-request={(option) => uploadImage(option, action)}
|
||||
customRequest={(option) => uploadImage(option, action)}
|
||||
accept=".jpg,.jpeg,.png,.gif,.webp"
|
||||
show-file-list={false}
|
||||
showUploadList={false}
|
||||
multiple
|
||||
>
|
||||
{{
|
||||
'upload-button': () => <UploadBtn />,
|
||||
}}
|
||||
<UploadBtn />
|
||||
</Upload>
|
||||
);
|
||||
};
|
||||
|
||||
const uploadImage = async (option, action = 'upload') => {
|
||||
const {
|
||||
fileItem: { file },
|
||||
} = option;
|
||||
const { file } = option;
|
||||
|
||||
const { name, size, type } = file;
|
||||
const response = await getImagePreSignedUrl({ suffix: getFileExtension(name) });
|
||||
@ -190,11 +167,11 @@ export default {
|
||||
const renderFooterRow = () => {
|
||||
return (
|
||||
<>
|
||||
<Button class="mr-12px" size="medium" onClick={onAgainCheck} disabled={isDisabled.value}>
|
||||
<Button class="mr-12px" onClick={onAgainCheck} disabled={isDisabled.value}>
|
||||
再次审核
|
||||
</Button>
|
||||
{isTextTab.value ? (
|
||||
<Button size="medium" type="outline" class="w-123px check-btn" onClick={onAiReplace} disabled={isDisabled.value}>
|
||||
<Button type="primary" ghost class="w-123px check-btn" onClick={onAiReplace} disabled={isDisabled.value}>
|
||||
{aiReplaceLoading.value ? (
|
||||
<>
|
||||
<IconLoading size={14} />
|
||||
@ -210,7 +187,7 @@ export default {
|
||||
) : (
|
||||
<div class="w-88px">
|
||||
{renderUpload(
|
||||
<Button size="medium" type="outline">
|
||||
<Button type="primary" ghost>
|
||||
图片替换
|
||||
</Button>,
|
||||
'replaceImage',
|
||||
@ -223,17 +200,17 @@ export default {
|
||||
const renderTextForm = () => {
|
||||
return (
|
||||
<Form ref={formRef} model={props.modelValue} rules={FORM_RULES} layout="vertical" auto-label-width>
|
||||
<FormItem label="标题" field="title" required>
|
||||
<FormItem label="标题" name="title" required>
|
||||
<Input
|
||||
v-model={props.modelValue.title}
|
||||
v-model:value={props.modelValue.title}
|
||||
placeholder="请输入标题"
|
||||
size="large"
|
||||
maxLength={30}
|
||||
show-word-limit
|
||||
maxlength={30}
|
||||
showCount
|
||||
disabled={isDisabled.value}
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem label="作品描述" field="content" class="flex-1 content-form-item">
|
||||
<FormItem label="作品描述" name="content" class="flex-1 content-form-item">
|
||||
<HighlightTextarea
|
||||
v-model={props.modelValue.content}
|
||||
disabled={isDisabled.value}
|
||||
@ -409,9 +386,9 @@ export default {
|
||||
<div class="right-box">
|
||||
<p class="cts bold !text-16px !lh-24px !color-#211F24 mb-16px">审核结果</p>
|
||||
<Spin
|
||||
loading={true}
|
||||
spinning={true}
|
||||
tip={`${isTextTab.value ? '文本' : '图片'}检测中`}
|
||||
size={72}
|
||||
size="large"
|
||||
class="h-298px !flex flex-col justify-center items-center"
|
||||
/>
|
||||
</div>
|
||||
@ -421,7 +398,7 @@ export default {
|
||||
<div class="right-box">
|
||||
<p class="cts bold !text-16px !lh-24px !color-#211F24 mb-16px">审核结果</p>
|
||||
{props.getDataLoading ? (
|
||||
<Spin loading={true} size={72} class="h-298px !flex justify-center items-center" />
|
||||
<Spin spinning={true} size="large" class="h-298px !flex justify-center items-center" />
|
||||
) : (
|
||||
renderCheckSuccessBox()
|
||||
)}
|
||||
@ -440,12 +417,11 @@ export default {
|
||||
<div class="h-full w-full px-24px pt-16px pb-24px content-wrap flex">
|
||||
<div class="flex-2 left-box mr-24px flex flex-col">
|
||||
<div class="flex-1 mb-12px rounded-8px border-1px pt-8px flex flex-col pb-16px bg-#F7F8FA border-#E6E6E8 border-solid">
|
||||
<Tabs v-model={activeTab.value} onTabClick={handleTabClick} class="mb-16px">
|
||||
<Tabs activeKey={activeTab.value} onChange={handleTabClick} class="mb-16px">
|
||||
{TAB_LIST.map((item) => (
|
||||
<TabPane
|
||||
key={item.value}
|
||||
v-slots={{
|
||||
title: () => (
|
||||
tab={
|
||||
<div class="flex items-center relative">
|
||||
<span>{item.label}</span>
|
||||
{
|
||||
@ -454,8 +430,7 @@ export default {
|
||||
// )
|
||||
}
|
||||
</div>
|
||||
),
|
||||
}}
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</Tabs>
|
||||
|
||||
@ -23,47 +23,36 @@
|
||||
}
|
||||
|
||||
.left-box {
|
||||
:deep(.arco-tabs) {
|
||||
.arco-tabs-nav {
|
||||
.arco-tabs-tab {
|
||||
height: 40px;
|
||||
// padding: 0 8px;
|
||||
margin: 0 16px;
|
||||
}
|
||||
&::before {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.arco-tabs-content {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
:deep(.arco-form) {
|
||||
:deep(.ant-form) {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.arco-form-item {
|
||||
.ant-form-item {
|
||||
margin-bottom: 24px;
|
||||
.arco-form-item-label-col {
|
||||
.arco-form-item-label {
|
||||
.ant-form-item-label-col {
|
||||
.ant-form-item-label {
|
||||
color: #939499;
|
||||
}
|
||||
}
|
||||
}
|
||||
.content-form-item {
|
||||
margin-bottom: 0;
|
||||
|
||||
.ant-row {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.arco-form-item-wrapper-col {
|
||||
.ant-form-item-control-input {
|
||||
flex: 1;
|
||||
.arco-form-item-content-wrapper,
|
||||
.arco-form-item-content,
|
||||
.arco-textarea-wrapper {
|
||||
.ant-form-item-control-input-content,
|
||||
.ant-form-item-control-input-content,
|
||||
.ant-textarea-wrapper {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.upload-box {
|
||||
display: flex;
|
||||
width: 100px;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<script lang="jsx">
|
||||
import { Image } from '@arco-design/web-vue';
|
||||
import { Image } from 'ant-design-vue';
|
||||
import { Swiper, SwiperSlide } from 'swiper/vue';
|
||||
import TextOverTips from '@/components/text-over-tips';
|
||||
|
||||
@ -56,13 +56,13 @@ export default {
|
||||
height={48}
|
||||
preview={false}
|
||||
src={item.cover}
|
||||
class="!rounded-4px mr-8px"
|
||||
class="!rounded-4px"
|
||||
fit="cover"
|
||||
v-slots={{
|
||||
error: () => <img src={icon1} class="w-full h-full" />,
|
||||
}}
|
||||
/>
|
||||
<div class="flex-1 overflow-hidden flex flex-col items-start">
|
||||
<div class="flex-1 overflow-hidden flex flex-col items-start ml-8px">
|
||||
<TextOverTips context={item.title} class={`cts !color-#211F24 title mb-4px`} />
|
||||
<p class="cts">{`合规程度:${item.ai_review?.compliance_level ? `${item.ai_review?.compliance_level}%` : '-'}`}</p>
|
||||
</div>
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
<script lang="jsx">
|
||||
import { Button, Message as AMessage, Spin } from '@arco-design/web-vue';
|
||||
import { Button, message } from 'ant-design-vue';
|
||||
import { Spin } from 'ant-design-vue';
|
||||
import CancelCheckModal from './cancel-check-modal.vue';
|
||||
import CheckSuccessModal from './check-success-modal.vue';
|
||||
import HeaderCard from './components/header-card';
|
||||
@ -135,14 +136,14 @@ export default {
|
||||
};
|
||||
const onSave = async () => {
|
||||
if (!selectCardInfo.value.title) {
|
||||
AMessage.warning('标题不能为空');
|
||||
message.warning('标题不能为空');
|
||||
}
|
||||
|
||||
contentCardRef.value?.validate().then(async () => {
|
||||
const { code, data } = await putWorkAuditsUpdate(selectCardInfo.value);
|
||||
if (code === 200) {
|
||||
isSaved.value = true;
|
||||
AMessage.success('当前内容稿件已保存');
|
||||
message.success('当前内容稿件已保存');
|
||||
}
|
||||
});
|
||||
};
|
||||
@ -181,13 +182,13 @@ export default {
|
||||
const renderFooterRow = () => {
|
||||
return (
|
||||
<>
|
||||
<Button size="medium" type="outline" class="mr-12px" onClick={onExit}>
|
||||
<Button type="primary" ghost class="mr-12px" onClick={onExit}>
|
||||
退出
|
||||
</Button>
|
||||
<Button size="medium" type="outline" class="mr-12px" onClick={onSave}>
|
||||
<Button type="primary" ghost class="mr-12px" onClick={onSave}>
|
||||
保存
|
||||
</Button>
|
||||
<Button type="primary" size="medium" onClick={onSubmit} loading={submitLoading.value}>
|
||||
<Button type="primary" onClick={onSubmit} loading={submitLoading.value}>
|
||||
{submitLoading.value ? '通过审核中...' : '通过审核'}
|
||||
</Button>
|
||||
</>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<a-form-item field="files">
|
||||
<FormItem name="files">
|
||||
<template #label>
|
||||
<div class="flex items-center">
|
||||
<span class="cts !color-#211F24 mr-4px">图片</span>
|
||||
@ -23,32 +23,31 @@
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<a-upload
|
||||
<Upload
|
||||
v-if="files.length < 18"
|
||||
ref="uploadRef"
|
||||
action="/"
|
||||
draggable
|
||||
:custom-request="(option) => emit('upload', option)"
|
||||
:customRequest="(option) => emit('upload', option)"
|
||||
accept=".jpg,.jpeg,.png,.gif,.webp"
|
||||
:show-file-list="false"
|
||||
:showUploadList="false"
|
||||
multiple
|
||||
class="!flex !items-center"
|
||||
:limit="18 - files.length"
|
||||
>
|
||||
<template #upload-button>
|
||||
<template #default>
|
||||
<div class="upload-box">
|
||||
<icon-plus size="14" class="mb-16px color-#3C4043" />
|
||||
<span class="cts !color-#211F24">上传图片</span>
|
||||
</div>
|
||||
</template>
|
||||
</a-upload>
|
||||
</Upload>
|
||||
</VueDraggable>
|
||||
</div>
|
||||
</a-form-item>
|
||||
</FormItem>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { VueDraggable } from 'vue-draggable-plus';
|
||||
import { FormItem, Upload } from 'ant-design-vue';
|
||||
|
||||
const props = defineProps({
|
||||
files: {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<script lang="jsx">
|
||||
import axios from 'axios';
|
||||
import { Form, FormItem, Input, Textarea, Upload, Message as AMessage, Button } from '@arco-design/web-vue';
|
||||
import { Button, Form, FormItem, Input, message, Upload } from 'ant-design-vue';
|
||||
// import CommonSelect from '@/components/common-select';
|
||||
// import { VueDraggable } from 'vue-draggable-plus';
|
||||
import TextOverTips from '@/components/text-over-tips';
|
||||
@ -11,6 +11,8 @@ import { formatFileSize, getVideoInfo, formatDuration, formatUploadSpeed } from
|
||||
import { EnumManuscriptType } from '@/views/material-center/components/finished-products/manuscript/list/constants.ts';
|
||||
import { getImagePreSignedUrl, getVideoPreSignedUrl } from '@/api/all/common';
|
||||
|
||||
const { TextArea } = Input;
|
||||
|
||||
// import icon1 from '@/assets/img/creative-generation-workshop/icon-close.png';
|
||||
|
||||
// 表单验证规则
|
||||
@ -128,9 +130,7 @@ export default {
|
||||
formData.value.videoInfo.uploadStatus = ENUM_UPLOAD_STATUS.UPLOADING;
|
||||
emit('updateVideoInfo', formData.value.videoInfo);
|
||||
|
||||
const {
|
||||
fileItem: { file },
|
||||
} = option;
|
||||
const { file } = option;
|
||||
setVideoInfo(file);
|
||||
|
||||
const response = await getVideoPreSignedUrl({ suffix: getFileExtension(file.name) });
|
||||
@ -161,13 +161,11 @@ export default {
|
||||
};
|
||||
// 文件上传处理
|
||||
const uploadImage = async (option) => {
|
||||
const {
|
||||
fileItem: { file },
|
||||
} = option;
|
||||
const { file } = option;
|
||||
|
||||
// 验证文件数量
|
||||
if (formData.value.files?.length >= 18) {
|
||||
AMessage.error('最多只能上传18张图片!');
|
||||
message.error('最多只能上传18张图片!');
|
||||
return;
|
||||
}
|
||||
const { name, size, type } = file;
|
||||
@ -189,15 +187,7 @@ export default {
|
||||
};
|
||||
|
||||
const validate = () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
formRef.value?.validate((errors) => {
|
||||
if (errors) {
|
||||
reject(formData.value);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
return formRef.value?.validate();
|
||||
};
|
||||
|
||||
const resetForm = () => {
|
||||
@ -211,25 +201,18 @@ export default {
|
||||
<Upload
|
||||
ref={uploadRef}
|
||||
action="/"
|
||||
draggable
|
||||
custom-request={uploadVideo}
|
||||
customRequest={uploadVideo}
|
||||
accept=".mp4,.mov,.avi,.flv,.wmv"
|
||||
show-file-list={false}
|
||||
showUploadList={false}
|
||||
>
|
||||
{{
|
||||
'upload-button': () => {
|
||||
if (formData.value.videoInfo.uploadStatus === ENUM_UPLOAD_STATUS.DEFAULT) {
|
||||
return (
|
||||
{formData.value.videoInfo.uploadStatus === ENUM_UPLOAD_STATUS.DEFAULT ? (
|
||||
<div class="upload-box">
|
||||
<icon-plus size="14" class="mb-16px color-#3C4043" />
|
||||
<span class="cts !color-#211F24">上传视频</span>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return <Button type="text">替换视频</Button>;
|
||||
}
|
||||
},
|
||||
}}
|
||||
) : (
|
||||
<Button type="text">替换视频</Button>
|
||||
)}
|
||||
</Upload>
|
||||
);
|
||||
};
|
||||
@ -238,7 +221,7 @@ export default {
|
||||
const isEnd = formData.value.videoInfo.uploadStatus === ENUM_UPLOAD_STATUS.END;
|
||||
return (
|
||||
<FormItem
|
||||
field="files"
|
||||
name="files"
|
||||
v-slots={{
|
||||
label: () => (
|
||||
<div class="flex items-center">
|
||||
@ -315,30 +298,31 @@ export default {
|
||||
|
||||
return () => (
|
||||
<Form ref={formRef} model={formData.value} rules={props.rules} layout="vertical" auto-label-width>
|
||||
<FormItem label="标题" field="title" required>
|
||||
<FormItem label="标题" name="title" required>
|
||||
<Input
|
||||
v-model={formData.value.title}
|
||||
v-model:value={formData.value.title}
|
||||
onInput={() => {
|
||||
console.log('onInput');
|
||||
onChange();
|
||||
emit('reValidate');
|
||||
}}
|
||||
placeholder="请输入标题"
|
||||
size="large"
|
||||
class="!w-500px"
|
||||
maxLength={30}
|
||||
show-word-limit
|
||||
maxlength={30}
|
||||
showCount
|
||||
/>
|
||||
</FormItem>
|
||||
|
||||
<FormItem label="作品描述" field="content">
|
||||
<Textarea
|
||||
v-model={formData.value.content}
|
||||
<FormItem label="作品描述" name="content">
|
||||
<TextArea
|
||||
v-model:value={formData.value.content}
|
||||
onInput={onChange}
|
||||
placeholder="请输入作品描述"
|
||||
size="large"
|
||||
class="textarea-box !w-784px"
|
||||
show-word-limit
|
||||
max-length={1000}
|
||||
showCount
|
||||
maxlength={1000}
|
||||
/>
|
||||
</FormItem>
|
||||
{isVideo.value ? (
|
||||
@ -352,7 +336,7 @@ export default {
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* <FormItem label="所属项目" field="project_ids">
|
||||
{/* <FormItem label="所属项目" name="project_ids">
|
||||
<CommonSelect
|
||||
v-model={formData.value.project_ids}
|
||||
onChange={() => emit('change')}
|
||||
|
||||
@ -31,8 +31,6 @@
|
||||
}
|
||||
}
|
||||
.textarea-box {
|
||||
:deep(.arco-textarea) {
|
||||
height: 140px;
|
||||
max-height: 298px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
export const INITIAL_FORM = {
|
||||
audit_status: '',
|
||||
audit_status: undefined,
|
||||
sort_column: undefined,
|
||||
sort_order: undefined,
|
||||
};
|
||||
|
||||
@ -1,22 +1,15 @@
|
||||
<script lang="jsx">
|
||||
import {
|
||||
Input,
|
||||
Table,
|
||||
Modal,
|
||||
TableColumn,
|
||||
Checkbox,
|
||||
Pagination,
|
||||
Button,
|
||||
Tooltip,
|
||||
Notification,
|
||||
} from '@arco-design/web-vue';
|
||||
import { Button, Modal, Tooltip, Table, Pagination } from 'ant-design-vue';
|
||||
import CommonSelect from '@/components/common-select';
|
||||
import TextOverTips from '@/components/text-over-tips';
|
||||
import ShareModal from '@/views/material-center/components/finished-products/manuscript/components/share-manuscript-modal/share-modal';
|
||||
|
||||
import { INITIAL_FORM, TABLE_COLUMNS } from './constants';
|
||||
import { formatTableField, exactFormatTime } from '@/utils/tools';
|
||||
import { CHECK_STATUS, EnumManuscriptType } from '@/views/material-center/components/finished-products/manuscript/list/constants';
|
||||
import {
|
||||
CHECK_STATUS,
|
||||
EnumManuscriptType,
|
||||
} from '@/views/material-center/components/finished-products/manuscript/list/constants';
|
||||
import { useTableSelectionWithPagination } from '@/hooks/useTableSelectionWithPagination';
|
||||
import { getWorksPage, getWriterLinksGenerate } from '@/api/all/generationWorkshop.ts';
|
||||
|
||||
@ -31,8 +24,6 @@ export default {
|
||||
dataSource,
|
||||
pageInfo,
|
||||
onPageChange,
|
||||
onPageSizeChange,
|
||||
rowSelection,
|
||||
handleSelect,
|
||||
handleSelectAll,
|
||||
DEFAULT_PAGE_INFO,
|
||||
@ -40,9 +31,6 @@ export default {
|
||||
onPageChange: () => {
|
||||
getData();
|
||||
},
|
||||
onPageSizeChange: () => {
|
||||
getData();
|
||||
},
|
||||
});
|
||||
const visible = ref(false);
|
||||
const query = ref(cloneDeep(INITIAL_FORM));
|
||||
@ -86,42 +74,44 @@ export default {
|
||||
reset();
|
||||
};
|
||||
|
||||
const renderColumn = () => {
|
||||
return TABLE_COLUMNS.map((column) => (
|
||||
<TableColumn
|
||||
key={column.dataIndex}
|
||||
data-index={column.dataIndex}
|
||||
fixed={column.fixed}
|
||||
width={column.width}
|
||||
min-width={column.minWidth}
|
||||
sortable={column.sortable}
|
||||
align={column.align}
|
||||
ellipsis
|
||||
tooltip
|
||||
v-slots={{
|
||||
title: () => (
|
||||
<div class="flex items-center">
|
||||
<span class="cts mr-4px">{column.title}</span>
|
||||
{column.tooltip && (
|
||||
<Tooltip content={column.tooltip} position="top">
|
||||
<IconQuestionCircle class="tooltip-icon color-#737478" size={16} />
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
),
|
||||
cell: ({ record }) => renderCell(record),
|
||||
}}
|
||||
/>
|
||||
));
|
||||
};
|
||||
// const renderColumn = () => {
|
||||
// return TABLE_COLUMNS.map((column) => (
|
||||
// <TableColumn
|
||||
// key={column.dataIndex}
|
||||
// data-index={column.dataIndex}
|
||||
// fixed={column.fixed}
|
||||
// width={column.width}
|
||||
// min-width={column.minWidth}
|
||||
// sortable={column.sortable}
|
||||
// align={column.align}
|
||||
// ellipsis
|
||||
// tooltip
|
||||
// v-slots={{
|
||||
// title: () => (
|
||||
// <>
|
||||
// <span class="cts mr-4px">{column.title}</span>
|
||||
// {column.tooltip && (
|
||||
// <Tooltip title={column.tooltip} placement="top">
|
||||
// <IconQuestionCircle class="tooltip-icon color-#737478" size={16} />
|
||||
// </Tooltip>
|
||||
// )}
|
||||
// </>
|
||||
// ),
|
||||
// cell: ({ record }) => renderCell(record),
|
||||
// }}
|
||||
// />
|
||||
// ));
|
||||
// };
|
||||
|
||||
const onShare = () => {
|
||||
shareModalRef.value?.open(selectedRowKeys.value);
|
||||
};
|
||||
const handleSorterChange = (column, order) => {
|
||||
query.value.sort_column = column;
|
||||
query.value.sort_order = order === 'ascend' ? 'asc' : 'desc';
|
||||
const handleSorterChange = (pagination, filters, sorter) => {
|
||||
if (sorter && !Array.isArray(sorter) && sorter.columnKey) {
|
||||
query.value.sort_column = sorter.columnKey;
|
||||
query.value.sort_order = sorter.order === 'ascend' ? 'asc' : 'desc';
|
||||
reload();
|
||||
}
|
||||
};
|
||||
const getStatusInfo = (audit_status) => {
|
||||
return CHECK_STATUS.find((v) => v.id === audit_status) ?? {};
|
||||
@ -132,12 +122,13 @@ export default {
|
||||
return () => (
|
||||
<>
|
||||
<Modal
|
||||
v-model:visible={visible.value}
|
||||
v-model:open={visible.value}
|
||||
title="分享内容稿件"
|
||||
width="920px"
|
||||
onClose={onClose}
|
||||
unmount-on-close
|
||||
modal-class="share-manuscript-modal"
|
||||
onCancel={onClose}
|
||||
centered
|
||||
destroyOnClose
|
||||
wrapClassName="share-manuscript-modal"
|
||||
v-slots={{
|
||||
footer: () => (
|
||||
<div class="flex justify-between w-full items-center">
|
||||
@ -145,16 +136,8 @@ export default {
|
||||
已选择 <span class="cts color-#211F24 bold">{selectedRows.value.length}</span> 个
|
||||
</p>
|
||||
<div class="flex items-center">
|
||||
<Button size="medium" onClick={onClose}>
|
||||
取消
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
class="ml-16px"
|
||||
size="medium"
|
||||
onClick={onShare}
|
||||
disabled={!selectedRows.value.length}
|
||||
>
|
||||
<Button onClick={onClose}>取消</Button>
|
||||
<Button type="primary" class="ml-16px" onClick={onShare} disabled={!selectedRows.value.length}>
|
||||
分享
|
||||
</Button>
|
||||
</div>
|
||||
@ -176,45 +159,44 @@ export default {
|
||||
</div>
|
||||
<Table
|
||||
ref={tableRef}
|
||||
data={dataSource.value}
|
||||
row-key="id"
|
||||
column-resizable
|
||||
row-selection={rowSelection.value}
|
||||
selected-keys={selectedRowKeys.value}
|
||||
dataSource={dataSource.value}
|
||||
rowKey="id"
|
||||
rowSelection={{
|
||||
selectedRowKeys: selectedRowKeys.value,
|
||||
onSelect: handleSelect,
|
||||
onSelectAll: handleSelectAll,
|
||||
}}
|
||||
pagination={false}
|
||||
scroll={{ x: '100%', y: '100%' }}
|
||||
class="overflow-hidden"
|
||||
bordered
|
||||
onSorterChange={handleSorterChange}
|
||||
onSelect={handleSelect}
|
||||
onSelectAll={handleSelectAll}
|
||||
showSorterTooltip={false}
|
||||
onChange={handleSorterChange}
|
||||
v-slots={{
|
||||
empty: () => <NoData />,
|
||||
columns: () => (
|
||||
<>
|
||||
emptyText: () => <NoData />,
|
||||
}}
|
||||
>
|
||||
{TABLE_COLUMNS.map((column) => (
|
||||
<TableColumn
|
||||
<Table.Column
|
||||
key={column.dataIndex}
|
||||
data-index={column.dataIndex}
|
||||
dataIndex={column.dataIndex}
|
||||
fixed={column.fixed}
|
||||
width={column.width}
|
||||
min-width={column.minWidth}
|
||||
sortable={column.sortable}
|
||||
minWidth={column.minWidth}
|
||||
sorter={column.sortable}
|
||||
align={column.align}
|
||||
ellipsis
|
||||
tooltip
|
||||
v-slots={{
|
||||
title: () => (
|
||||
<div class="flex items-center">
|
||||
title={() => (
|
||||
<>
|
||||
<span class="cts mr-4px bold color-#211F24">{column.title}</span>
|
||||
{column.tooltip && (
|
||||
<Tooltip content={column.tooltip} position="top">
|
||||
<Tooltip title={column.tooltip} placement="top">
|
||||
<IconQuestionCircle class="tooltip-icon color-#737478" size={16} />
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
),
|
||||
cell: ({ record }) => {
|
||||
</>
|
||||
)}
|
||||
customRender={({ record }) => {
|
||||
if (column.dataIndex === 'audit_status') {
|
||||
return (
|
||||
<div
|
||||
@ -227,7 +209,7 @@ export default {
|
||||
</div>
|
||||
);
|
||||
} else if (column.dataIndex === 'title') {
|
||||
return <TextOverTips context={record.title} />;
|
||||
return <TextOverTips context={record.title} class="!text-12px" />;
|
||||
} else if (column.dataIndex === 'type') {
|
||||
return (
|
||||
<div class="flex items-center">
|
||||
@ -249,36 +231,27 @@ export default {
|
||||
} else if (column.dataIndex === 'last_modified_at') {
|
||||
return (
|
||||
<span class="cts num">
|
||||
{exactFormatTime(
|
||||
record.last_modified_at,
|
||||
'YYYY-MM-DD HH:mm:ss',
|
||||
'YYYY-MM-DD HH:mm:ss',
|
||||
)}
|
||||
{exactFormatTime(record.last_modified_at, 'YYYY-MM-DD HH:mm:ss', 'YYYY-MM-DD HH:mm:ss')}
|
||||
</span>
|
||||
);
|
||||
} else {
|
||||
return formatTableField(column, record, true);
|
||||
}
|
||||
},
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</Table>
|
||||
{pageInfo.value.total > 0 && (
|
||||
<div class="flex justify-end mt-16px">
|
||||
<Pagination
|
||||
total={pageInfo.value.total}
|
||||
size="mini"
|
||||
show-total
|
||||
show-jumper
|
||||
show-page-size
|
||||
size="small"
|
||||
showTotal={(total, range) => `共 ${total} 条`}
|
||||
showQuickJumper
|
||||
showSizeChanger
|
||||
current={pageInfo.value.page}
|
||||
page-size={pageInfo.value.page_size}
|
||||
pageSize={pageInfo.value.page_size}
|
||||
onChange={onPageChange}
|
||||
onPageSizeChange={onPageSizeChange}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<script lang="jsx">
|
||||
import { Modal, Form, FormItem, Input, Button, Message as AMessage } from '@arco-design/web-vue';
|
||||
import { Button, Modal, Form, FormItem, Input, Tooltip, message } from 'ant-design-vue';
|
||||
import CommonSelect from '@/components/common-select';
|
||||
|
||||
import { useClipboard } from '@vueuse/core';
|
||||
@ -65,8 +65,7 @@ export default {
|
||||
};
|
||||
|
||||
const onGenerateLink = () => {
|
||||
formRef.value.validate().then(async (errors) => {
|
||||
if (!errors) {
|
||||
formRef.value.validate().then(async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
const { code, data } = await postShareLinksGenerate(formData.value);
|
||||
@ -77,13 +76,12 @@ export default {
|
||||
path: `/explore/list/${data.code}`,
|
||||
}).href;
|
||||
copy(generateFullUrl(url));
|
||||
AMessage.success('链接已复制!');
|
||||
message.success('链接已复制!');
|
||||
emit('close');
|
||||
}
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
const open = (workIds) => {
|
||||
@ -96,27 +94,33 @@ export default {
|
||||
});
|
||||
return () => (
|
||||
<Modal
|
||||
v-model:visible={visible.value}
|
||||
v-model:open={visible.value}
|
||||
title="分享内容稿件"
|
||||
width="480px"
|
||||
onClose={onClose}
|
||||
unmount-on-close
|
||||
onCancel={onClose}
|
||||
destroyOnClose
|
||||
centered
|
||||
auto-label-width
|
||||
v-slots={{
|
||||
footer: () => (
|
||||
<>
|
||||
<Button size="medium" onClick={onClose}>
|
||||
取消
|
||||
</Button>
|
||||
<Button type="primary" class="ml-16px" size="medium" onClick={onGenerateLink} disabled={loading.value}>
|
||||
<Button onClick={onClose}>取消</Button>
|
||||
<Button type="primary" class="ml-16px" onClick={onGenerateLink} disabled={loading.value}>
|
||||
{loading.value ? '生成中...' : '生成链接'}
|
||||
</Button>
|
||||
</>
|
||||
),
|
||||
}}
|
||||
>
|
||||
<Form ref={formRef} rules={rules} model={formData.value} auto-label-width>
|
||||
<FormItem label="有效期" prop="days" row-class="!items-center">
|
||||
<Form
|
||||
ref={formRef}
|
||||
rules={rules}
|
||||
model={formData.value}
|
||||
labelAlign="right"
|
||||
labelCol={{ span: 5 }}
|
||||
wrapperCol={{ span: 19 }}
|
||||
>
|
||||
<FormItem label="有效期" name="days" row-class="!items-center">
|
||||
<CommonSelect
|
||||
v-model={formData.value.days}
|
||||
options={OPTIONS}
|
||||
@ -128,21 +132,20 @@ export default {
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
label="分享对象"
|
||||
prop="receiver"
|
||||
name="receiver"
|
||||
row-class="!items-center"
|
||||
v-slots={{
|
||||
label: () => (
|
||||
<div class="flex items-center">
|
||||
<span>分享对象</span>
|
||||
<a-tooltip content="可填写客户名称、昵称等,非必填" position="top">
|
||||
<Tooltip title="可填写客户名称、昵称等,非必填" placement="top">
|
||||
<icon-question-circle class="tooltip-icon color-#737478 ml-4px" size="14" />
|
||||
</a-tooltip>
|
||||
</Tooltip>
|
||||
</div>
|
||||
),
|
||||
}}
|
||||
>
|
||||
<Input v-model={formData.value.receiver} class="!w-240px" size="large" placeholder="请输入分享对象" />
|
||||
<Input v-model:value={formData.value.receiver} class="!w-240px" size="large" placeholder="请输入分享对象" />
|
||||
</FormItem>
|
||||
</Form>
|
||||
</Modal>
|
||||
|
||||
@ -18,49 +18,36 @@
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
.arco-modal-body {
|
||||
.ant-modal-body {
|
||||
height: 464px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
.arco-scrollbar-track {
|
||||
.ant-scrollbar-track {
|
||||
display: none !important;
|
||||
}
|
||||
.arco-table {
|
||||
.arco-table-container {
|
||||
.arco-table-element {
|
||||
thead {
|
||||
.arco-table-tr {
|
||||
.arco-table-th {
|
||||
.arco-table-cell {
|
||||
.ant-table {
|
||||
.ant-table-thead {
|
||||
.ant-table-cell {
|
||||
padding: 10px 16px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
tbody {
|
||||
.arco-table-tr {
|
||||
.arco-table-td {
|
||||
.arco-table-cell {
|
||||
padding: 6px 16px;
|
||||
.arco-table-cell-content,
|
||||
.arco-table-td-content {
|
||||
.ant-table-body {
|
||||
.ant-table-cell {
|
||||
padding: 6px 16px !important;
|
||||
.ant-table-cell-content {
|
||||
font-size: 12px;
|
||||
line-height: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.arco-pagination {
|
||||
.arco-pagination-total,
|
||||
.arco-pagination-jumper-prepend {
|
||||
.ant-pagination {
|
||||
.ant-pagination-total-text,
|
||||
.ant-pagination-options-quick-jumper {
|
||||
font-size: 14px;
|
||||
}
|
||||
.arco-pagination-jumper-prepend {
|
||||
.ant-pagination-options-quick-jumper {
|
||||
font-family: $font-family-regular;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,16 +1,5 @@
|
||||
<script lang="jsx">
|
||||
import {
|
||||
Modal,
|
||||
Form,
|
||||
FormItem,
|
||||
Input,
|
||||
RadioGroup,
|
||||
Radio,
|
||||
Upload,
|
||||
Button,
|
||||
Message as AMessage,
|
||||
Textarea,
|
||||
} from '@arco-design/web-vue';
|
||||
import { Modal, Button, Form, FormItem, RadioGroup, Radio, Input, message, Upload } from 'ant-design-vue';
|
||||
import { useClipboard } from '@vueuse/core';
|
||||
import { getWriterLinksGenerate, getTemplateUrl, postWorksByLink, postWorksByFile } from '@/api/all/generationWorkshop';
|
||||
import { generateFullUrl } from '@/utils/tools';
|
||||
@ -18,6 +7,9 @@ import { slsWithCatch } from '@/utils/stroage.ts';
|
||||
|
||||
import TextOverTips from '@/components/text-over-tips';
|
||||
import icon1 from '@/assets/img/media-account/icon-feedback-fail.png';
|
||||
import icon2 from '@/assets/img/media-account/icon-download.png';
|
||||
|
||||
const { TextArea } = Input;
|
||||
|
||||
// 状态枚举
|
||||
const TASK_STATUS = {
|
||||
@ -41,7 +33,7 @@ const INITIAL_FORM = {
|
||||
|
||||
export default {
|
||||
setup(props, { emit, expose }) {
|
||||
const update = inject('update');
|
||||
// const update = inject('update');
|
||||
const router = useRouter();
|
||||
|
||||
// 响应式状态
|
||||
@ -119,8 +111,7 @@ export default {
|
||||
handleHandwriteSubmit();
|
||||
return;
|
||||
}
|
||||
formRef.value?.validate(async (errors) => {
|
||||
if (!errors) {
|
||||
formRef.value?.validate().then(async () => {
|
||||
taskStatus.value = TASK_STATUS.LOADING;
|
||||
const { link } = form.value;
|
||||
const { code, data } = await postWorksByLink({ link });
|
||||
@ -128,7 +119,6 @@ export default {
|
||||
taskStatus.value = TASK_STATUS.SUCCESS;
|
||||
works.value = data ? [data] : [];
|
||||
}
|
||||
}
|
||||
});
|
||||
}, 300);
|
||||
|
||||
@ -140,27 +130,25 @@ export default {
|
||||
// 手写提交处理
|
||||
const handleHandwriteSubmit = () => {
|
||||
if (!form.value.writerLink) {
|
||||
AMessage.warning('请输入上传链接!');
|
||||
message.warning('请输入上传链接!');
|
||||
return;
|
||||
}
|
||||
|
||||
copy(form.value.writerLink);
|
||||
AMessage.success('复制成功!');
|
||||
message.success('复制成功!');
|
||||
onClose();
|
||||
};
|
||||
|
||||
// 取消上传
|
||||
const onCancelUpload = () => {
|
||||
taskStatus.value = TASK_STATUS.DEFAULT;
|
||||
AMessage.info('已取消上传');
|
||||
message.info('已取消上传');
|
||||
};
|
||||
|
||||
// 文件上传处理
|
||||
const handleUpload = async (option) => {
|
||||
taskStatus.value = TASK_STATUS.LOADING;
|
||||
const {
|
||||
fileItem: { file },
|
||||
} = option;
|
||||
const { file } = option;
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
@ -222,9 +210,9 @@ export default {
|
||||
|
||||
// 渲染链接上传表单
|
||||
const renderLinkForm = () => (
|
||||
<FormItem label="链接地址" field="link" required>
|
||||
<Textarea
|
||||
v-model={form.value.link}
|
||||
<FormItem label="链接地址" name="link" required>
|
||||
<TextArea
|
||||
v-model:value={form.value.link}
|
||||
size="large"
|
||||
placeholder="请输入飞书链接地址"
|
||||
autoSize={{ minRows: 5, maxRows: 8 }}
|
||||
@ -234,8 +222,8 @@ export default {
|
||||
|
||||
// 渲染手写上传表单
|
||||
const renderHandwriteForm = () => (
|
||||
<FormItem label="上传链接" field="writerLink">
|
||||
<Input v-model={form.value.writerLink} placeholder="请输入上传链接" disabled size="large" />
|
||||
<FormItem label="上传链接" name="writerLink">
|
||||
<Input v-model:value={form.value.writerLink} placeholder="请输入上传链接" disabled size="large" />
|
||||
</FormItem>
|
||||
);
|
||||
|
||||
@ -245,24 +233,20 @@ export default {
|
||||
<div class="flex flex-col w-full">
|
||||
<Upload
|
||||
action="/"
|
||||
draggable
|
||||
multiple
|
||||
customRequest={handleUpload}
|
||||
class="w-full"
|
||||
accept=".xlsx,.xls,.docx,.doc,.mp4,.mov,.avi,.flv,.wmv,.m4v"
|
||||
show-file-list={false}
|
||||
showUploadList={false}
|
||||
>
|
||||
{{
|
||||
'upload-button': () => (
|
||||
<div class="upload-box">
|
||||
<icon-plus size="14" class="mb-16px" />
|
||||
<span class="text mb-4px">点击或拖拽文件到此处上传</span>
|
||||
<span class="tip">支持文档(文本+图), 视频批量上传</span>
|
||||
</div>
|
||||
),
|
||||
}}
|
||||
</Upload>
|
||||
<div class="flex items-center cursor-pointer mt-8px" onClick={handleDownloadTemplate}>
|
||||
<icon-download size="14" class="mr-4px !color-#6D4CFE" />
|
||||
<img src={icon2} width="16" height="16" class="mr-4px" />
|
||||
<span class="cts color-#6D4CFE">下载示例文档</span>
|
||||
</div>
|
||||
</div>
|
||||
@ -332,36 +316,30 @@ export default {
|
||||
const renderFooterButtons = () => {
|
||||
const buttonMap = {
|
||||
[TASK_STATUS.LOADING]: () => (
|
||||
<Button type="primary" size="medium" onClick={onCancelUpload}>
|
||||
<Button type="primary" onClick={onCancelUpload}>
|
||||
取消上传
|
||||
</Button>
|
||||
),
|
||||
[TASK_STATUS.DEFAULT]: () => (
|
||||
<>
|
||||
<Button size="medium" onClick={onClose}>
|
||||
取消
|
||||
</Button>
|
||||
<Button type="primary" size="medium" onClick={onSubmit}>
|
||||
<Button onClick={onClose}>取消</Button>
|
||||
<Button type="primary" onClick={onSubmit}>
|
||||
{isHandwrite.value ? '复制链接' : '确认'}
|
||||
</Button>
|
||||
</>
|
||||
),
|
||||
[TASK_STATUS.FAILED]: () => (
|
||||
<>
|
||||
<Button size="medium" onClick={onClose}>
|
||||
取消
|
||||
</Button>
|
||||
<Button type="primary" size="medium" onClick={onClose}>
|
||||
<Button onClick={onClose}>取消</Button>
|
||||
<Button type="primary" onClick={onClose}>
|
||||
重新上传
|
||||
</Button>
|
||||
</>
|
||||
),
|
||||
[TASK_STATUS.SUCCESS]: () => (
|
||||
<>
|
||||
<Button size="medium" onClick={onClose}>
|
||||
取消
|
||||
</Button>
|
||||
<Button type="primary" size="medium" onClick={goUpload}>
|
||||
<Button onClick={onClose}>取消</Button>
|
||||
<Button type="primary" onClick={goUpload}>
|
||||
确认
|
||||
</Button>
|
||||
</>
|
||||
@ -375,17 +353,16 @@ export default {
|
||||
|
||||
return () => (
|
||||
<Modal
|
||||
v-model:visible={visible.value}
|
||||
centered
|
||||
v-model:open={visible.value}
|
||||
title={getTitle()}
|
||||
modal-class="upload-manuscript-modal"
|
||||
wrapClassName="upload-manuscript-modal"
|
||||
width="500px"
|
||||
mask-closable={false}
|
||||
unmount-on-close
|
||||
onClose={onClose}
|
||||
footer={!(isDefault.value && isLocal.value)}
|
||||
v-slots={{
|
||||
footer: () => renderFooterButtons(),
|
||||
}}
|
||||
maskClosable={false}
|
||||
destroyOnClose
|
||||
centered
|
||||
onCancel={onClose}
|
||||
footer={isDefault.value && isLocal.value ? null : renderFooterButtons()}
|
||||
>
|
||||
<Form
|
||||
ref={formRef}
|
||||
@ -398,7 +375,7 @@ export default {
|
||||
>
|
||||
{isDefault.value && (
|
||||
<FormItem label="上传方式">
|
||||
<RadioGroup v-model={uploadType.value} onChange={onUploadTypeChange}>
|
||||
<RadioGroup v-model:value={uploadType.value} onChange={onUploadTypeChange}>
|
||||
<Radio value={UPLOAD_TYPE.LINK}>链接上传</Radio>
|
||||
<Radio value={UPLOAD_TYPE.LOCAL}>本地上传</Radio>
|
||||
<Radio value={UPLOAD_TYPE.HANDWRITE}>写手上传</Radio>
|
||||
|
||||
@ -28,4 +28,8 @@
|
||||
border: 1px dashed var(--Border-1, #d7d7d9);
|
||||
background: var(--BG-200, #f2f3f5);
|
||||
}
|
||||
.ant-upload {
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<script lang="jsx">
|
||||
import { Button, Message as AMessage, Spin } from '@arco-design/web-vue';
|
||||
import { Button, Spin, message } from 'ant-design-vue';
|
||||
|
||||
import { useRouter, useRoute } from 'vue-router';
|
||||
import { AuditStatus } from '@/views/material-center/components/finished-products/constants';
|
||||
@ -119,12 +119,12 @@ export default {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button size="medium" type="outline" class="mr-12px" onClick={onBack}>
|
||||
<Button type="primary" ghost class="mr-12px" onClick={onBack}>
|
||||
退出
|
||||
</Button>
|
||||
<Button
|
||||
size="medium"
|
||||
type="outline"
|
||||
type="primary"
|
||||
ghost
|
||||
class="mr-12px"
|
||||
onClick={() =>
|
||||
router.push({
|
||||
@ -138,7 +138,7 @@ export default {
|
||||
编辑
|
||||
</Button>
|
||||
{audit_status !== AuditStatus.Passed && (
|
||||
<Button type="primary" size="medium" onClick={_fn}>
|
||||
<Button type="primary" onClick={_fn}>
|
||||
去审核
|
||||
</Button>
|
||||
)}
|
||||
@ -157,7 +157,7 @@ export default {
|
||||
});
|
||||
|
||||
return () => (
|
||||
<Spin loading={loading.value} class="manuscript-detail-wrap" size={50}>
|
||||
<Spin spinning={loading.value} wrapperClassName="manuscript-detail-wrap" size="large">
|
||||
<div class="h-full w-full flex flex-col">
|
||||
<div class="flex items-center mb-8px">
|
||||
<span class="cts color-#4E5969 cursor-pointer" onClick={onBack}>
|
||||
|
||||
@ -1,18 +1,19 @@
|
||||
<template>
|
||||
<a-modal v-model:visible="visible" title="退出编辑" width="480px" @close="onClose">
|
||||
<Modal v-model:open="visible" title="退出编辑" centered width="480px" @cancel="onClose">
|
||||
<div class="flex items-center">
|
||||
<img :src="icon1" width="20" height="20" class="mr-12px" />
|
||||
<span>内容已修改尚未保存,若退出编辑,本次修改将不保存。</span>
|
||||
</div>
|
||||
<template #footer>
|
||||
<a-button size="medium" @click="onClose">继续编辑</a-button>
|
||||
<a-button type="primary" class="ml-8px" size="medium" @click="onConfirm">确认退出</a-button>
|
||||
<Button size="medium" @click="onClose">继续编辑</Button>
|
||||
<Button type="primary" class="ml-8px" size="medium" @click="onConfirm">确认退出</Button>
|
||||
</template>
|
||||
</a-modal>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { Button, Modal } from 'ant-design-vue';
|
||||
import icon1 from '@/assets/img/media-account/icon-warn-1.png';
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<script lang="jsx">
|
||||
import { Button, Message as AMessage } from '@arco-design/web-vue';
|
||||
import { Button, message } from 'ant-design-vue';
|
||||
import EditForm, { ENUM_UPLOAD_STATUS, INITIAL_VIDEO_INFO } from '../components/edit-form';
|
||||
import CancelEditModal from './cancel-edit-modal.vue';
|
||||
|
||||
@ -43,14 +43,14 @@ export default {
|
||||
const onSave = async (check = false) => {
|
||||
formRef.value?.validate().then(async () => {
|
||||
if (dataSource.value.videoInfo.uploadStatus === ENUM_UPLOAD_STATUS.UPLOADING) {
|
||||
AMessage.warning('有视频正在上传中,请等待上传完成后再提交');
|
||||
message.warning('有视频正在上传中,请等待上传完成后再提交');
|
||||
return;
|
||||
}
|
||||
|
||||
const filteredWorks = omit(dataSource.value, 'videoInfo');
|
||||
const { code, data } = await putWorksUpdate({ id: workId.value, ...filteredWorks });
|
||||
if (code === 200) {
|
||||
AMessage.success('保存成功');
|
||||
message.success('保存成功');
|
||||
isSaved.value = true;
|
||||
|
||||
if (check) {
|
||||
|
||||
@ -3,18 +3,17 @@
|
||||
<div class="filter-row">
|
||||
<div class="filter-row-item">
|
||||
<span class="label">内容稿件标题</span>
|
||||
<a-input
|
||||
v-model="query.title"
|
||||
<Input
|
||||
v-model:value="query.title"
|
||||
class="!w-240px"
|
||||
placeholder="请输入内容稿件标题"
|
||||
size="medium"
|
||||
allow-clear
|
||||
allowClear
|
||||
@change="handleSearch"
|
||||
>
|
||||
<template #prefix>
|
||||
<icon-search />
|
||||
</template>
|
||||
</a-input>
|
||||
</Input>
|
||||
</div>
|
||||
<!-- <div class="filter-row-item">
|
||||
<span class="label">所属项目</span>
|
||||
@ -28,20 +27,17 @@
|
||||
</div> -->
|
||||
<div class="filter-row-item">
|
||||
<span class="label">序号</span>
|
||||
<a-space size="medium">
|
||||
<a-input
|
||||
v-model="query.uid"
|
||||
<Input
|
||||
v-model:value="query.uid"
|
||||
class="!w-160px"
|
||||
placeholder="请输入序号"
|
||||
size="medium"
|
||||
allow-clear
|
||||
allowClear
|
||||
@change="handleSearch"
|
||||
>
|
||||
<template #prefix>
|
||||
<icon-search />
|
||||
</template>
|
||||
</a-input>
|
||||
</a-space>
|
||||
</Input>
|
||||
</div>
|
||||
<div class="filter-row-item">
|
||||
<span class="label">审核状态</span>
|
||||
@ -56,8 +52,8 @@
|
||||
</div>
|
||||
<div class="filter-row-item">
|
||||
<span class="label">上传时间</span>
|
||||
<a-range-picker
|
||||
v-model="created_at"
|
||||
<DatePicker.RangePicker
|
||||
v-model:value="created_at"
|
||||
size="medium"
|
||||
allow-clear
|
||||
format="YYYY-MM-DD"
|
||||
@ -66,24 +62,25 @@
|
||||
/>
|
||||
</div>
|
||||
<div class="filter-row-item">
|
||||
<a-button type="outline" class="mr-12px" size="medium" @click="handleSearch">
|
||||
<Button type="primary" ghost class="mr-12px" size="medium" @click="handleSearch">
|
||||
<template #icon>
|
||||
<icon-search />
|
||||
<icon-search class="mr-8px" />
|
||||
</template>
|
||||
<template #default>搜索</template>
|
||||
</a-button>
|
||||
<a-button size="medium" @click="handleReset">
|
||||
</Button>
|
||||
<Button size="medium" @click="handleReset">
|
||||
<template #icon>
|
||||
<icon-refresh />
|
||||
<icon-refresh class="mr-8px" />
|
||||
</template>
|
||||
<template #default>重置</template>
|
||||
</a-button>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { Button, Input, DatePicker } from 'ant-design-vue';
|
||||
import { defineEmits, defineProps } from 'vue';
|
||||
import { CHECK_STATUS } from '@/views/material-center/components/finished-products/manuscript/list/constants';
|
||||
import CommonSelect from '@/components/common-select';
|
||||
@ -96,7 +93,7 @@ const props = defineProps({
|
||||
},
|
||||
});
|
||||
|
||||
const emits = defineEmits('search', 'reset', 'update:query');
|
||||
const emits = defineEmits(['search', 'reset', 'update:query']);
|
||||
|
||||
const created_at = ref([]);
|
||||
// const projects = ref([]);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user