Commit 942e65b9 authored by nick zheng's avatar nick zheng

feat: 新增知识库管理

parent 147e7f9d
...@@ -7,6 +7,7 @@ import Components from 'unplugin-vue-components/vite' ...@@ -7,6 +7,7 @@ import Components from 'unplugin-vue-components/vite'
import { NaiveUiResolver } from 'unplugin-vue-components/resolvers' import { NaiveUiResolver } from 'unplugin-vue-components/resolvers'
import VueI18nPlugin from '@intlify/unplugin-vue-i18n/vite' import VueI18nPlugin from '@intlify/unplugin-vue-i18n/vite'
import UnoCSS from 'unocss/vite' import UnoCSS from 'unocss/vite'
import VueJsx from '@vitejs/plugin-vue-jsx'
export function setupPlugins(isBuild: boolean, envConf: ViteEnv, pathResolve: (dir: string) => string): PluginOption[] { export function setupPlugins(isBuild: boolean, envConf: ViteEnv, pathResolve: (dir: string) => string): PluginOption[] {
const lifecycle = process.env.npm_lifecycle_event const lifecycle = process.env.npm_lifecycle_event
...@@ -27,6 +28,7 @@ export function setupPlugins(isBuild: boolean, envConf: ViteEnv, pathResolve: (d ...@@ -27,6 +28,7 @@ export function setupPlugins(isBuild: boolean, envConf: ViteEnv, pathResolve: (d
include: [pathResolve('./src/locales/langs/**')], include: [pathResolve('./src/locales/langs/**')],
}), }),
UnoCSS(), UnoCSS(),
VueJsx(),
] ]
if (envConf.VITE_VITEST && !isBuild) { if (envConf.VITE_VITEST && !isBuild) {
......
...@@ -50,6 +50,7 @@ ...@@ -50,6 +50,7 @@
"@typescript-eslint/parser": "^7.18.0", "@typescript-eslint/parser": "^7.18.0",
"@unocss/eslint-config": "^0.61.9", "@unocss/eslint-config": "^0.61.9",
"@vitejs/plugin-vue": "^4.6.2", "@vitejs/plugin-vue": "^4.6.2",
"@vitejs/plugin-vue-jsx": "^4.0.1",
"autoprefixer": "^10.4.20", "autoprefixer": "^10.4.20",
"eslint": "^9.10.0", "eslint": "^9.10.0",
"eslint-config-prettier": "^9.1.0", "eslint-config-prettier": "^9.1.0",
......
...@@ -98,28 +98,31 @@ importers: ...@@ -98,28 +98,31 @@ importers:
version: 13.12.2 version: 13.12.2
'@typescript-eslint/parser': '@typescript-eslint/parser':
specifier: ^7.18.0 specifier: ^7.18.0
version: 7.18.0(eslint@9.10.0(jiti@1.21.6))(typescript@5.6.2) version: 7.18.0(eslint@9.10.0(jiti@2.0.0-beta.3))(typescript@5.6.2)
'@unocss/eslint-config': '@unocss/eslint-config':
specifier: ^0.61.9 specifier: ^0.61.9
version: 0.61.9(eslint@9.10.0(jiti@1.21.6))(typescript@5.6.2) version: 0.61.9(eslint@9.10.0(jiti@2.0.0-beta.3))(typescript@5.6.2)
'@vitejs/plugin-vue': '@vitejs/plugin-vue':
specifier: ^4.6.2 specifier: ^4.6.2
version: 4.6.2(vite@5.4.6(@types/node@20.16.5)(sass@1.79.1))(vue@3.5.12(typescript@5.6.2)) version: 4.6.2(vite@5.4.6(@types/node@20.16.5)(sass@1.79.1))(vue@3.5.12(typescript@5.6.2))
'@vitejs/plugin-vue-jsx':
specifier: ^4.0.1
version: 4.0.1(vite@5.4.6(@types/node@20.16.5)(sass@1.79.1))(vue@3.5.12(typescript@5.6.2))
autoprefixer: autoprefixer:
specifier: ^10.4.20 specifier: ^10.4.20
version: 10.4.20(postcss@8.4.47) version: 10.4.20(postcss@8.4.47)
eslint: eslint:
specifier: ^9.10.0 specifier: ^9.10.0
version: 9.10.0(jiti@1.21.6) version: 9.10.0(jiti@2.0.0-beta.3)
eslint-config-prettier: eslint-config-prettier:
specifier: ^9.1.0 specifier: ^9.1.0
version: 9.1.0(eslint@9.10.0(jiti@1.21.6)) version: 9.1.0(eslint@9.10.0(jiti@2.0.0-beta.3))
eslint-plugin-prettier: eslint-plugin-prettier:
specifier: ^5.2.1 specifier: ^5.2.1
version: 5.2.1(eslint-config-prettier@9.1.0(eslint@9.10.0(jiti@1.21.6)))(eslint@9.10.0(jiti@1.21.6))(prettier@3.3.3) version: 5.2.1(eslint-config-prettier@9.1.0(eslint@9.10.0(jiti@2.0.0-beta.3)))(eslint@9.10.0(jiti@2.0.0-beta.3))(prettier@3.3.3)
eslint-plugin-vue: eslint-plugin-vue:
specifier: ^9.28.0 specifier: ^9.28.0
version: 9.28.0(eslint@9.10.0(jiti@1.21.6)) version: 9.28.0(eslint@9.10.0(jiti@2.0.0-beta.3))
globals: globals:
specifier: ^15.9.0 specifier: ^15.9.0
version: 15.9.0 version: 15.9.0
...@@ -176,7 +179,7 @@ importers: ...@@ -176,7 +179,7 @@ importers:
version: 5.6.2 version: 5.6.2
typescript-eslint: typescript-eslint:
specifier: ^7.18.0 specifier: ^7.18.0
version: 7.18.0(eslint@9.10.0(jiti@1.21.6))(typescript@5.6.2) version: 7.18.0(eslint@9.10.0(jiti@2.0.0-beta.3))(typescript@5.6.2)
unocss: unocss:
specifier: ^0.61.9 specifier: ^0.61.9
version: 0.61.9(postcss@8.4.47)(rollup@4.21.3)(vite@5.4.6(@types/node@20.16.5)(sass@1.79.1)) version: 0.61.9(postcss@8.4.47)(rollup@4.21.3)(vite@5.4.6(@types/node@20.16.5)(sass@1.79.1))
...@@ -191,10 +194,10 @@ importers: ...@@ -191,10 +194,10 @@ importers:
version: 5.4.6(@types/node@20.16.5)(sass@1.79.1) version: 5.4.6(@types/node@20.16.5)(sass@1.79.1)
vite-plugin-checker: vite-plugin-checker:
specifier: ^0.7.2 specifier: ^0.7.2
version: 0.7.2(eslint@9.10.0(jiti@1.21.6))(optionator@0.9.4)(stylelint@16.9.0(typescript@5.6.2))(typescript@5.6.2)(vite@5.4.6(@types/node@20.16.5)(sass@1.79.1))(vue-tsc@2.0.29(typescript@5.6.2)) version: 0.7.2(eslint@9.10.0(jiti@2.0.0-beta.3))(meow@13.2.0)(optionator@0.9.4)(stylelint@16.9.0(typescript@5.6.2))(typescript@5.6.2)(vite@5.4.6(@types/node@20.16.5)(sass@1.79.1))(vue-tsc@2.0.29(typescript@5.6.2))
vue-eslint-parser: vue-eslint-parser:
specifier: ^9.4.3 specifier: ^9.4.3
version: 9.4.3(eslint@9.10.0(jiti@1.21.6)) version: 9.4.3(eslint@9.10.0(jiti@2.0.0-beta.3))
vue-tsc: vue-tsc:
specifier: ^2.0.29 specifier: ^2.0.29
version: 2.0.29(typescript@5.6.2) version: 2.0.29(typescript@5.6.2)
...@@ -1172,6 +1175,13 @@ packages: ...@@ -1172,6 +1175,13 @@ packages:
peerDependencies: peerDependencies:
vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0-0 vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0-0
'@vitejs/plugin-vue-jsx@4.0.1':
resolution: {integrity: sha512-7mg9HFGnFHMEwCdB6AY83cVK4A6sCqnrjFYF4WIlebYAQVVJ/sC/CiTruVdrRlhrFoeZ8rlMxY9wYpPTIRhhAg==}
engines: {node: ^18.0.0 || >=20.0.0}
peerDependencies:
vite: ^5.0.0
vue: ^3.0.0
'@vitejs/plugin-vue@4.6.2': '@vitejs/plugin-vue@4.6.2':
resolution: {integrity: sha512-kqf7SGFoG+80aZG6Pf+gsZIVvGSCKE98JbiWqcCV9cThtg91Jav0yvYFC9Zb+jKetNGF6ZKeoaxgZfND21fWKw==} resolution: {integrity: sha512-kqf7SGFoG+80aZG6Pf+gsZIVvGSCKE98JbiWqcCV9cThtg91Jav0yvYFC9Zb+jKetNGF6ZKeoaxgZfND21fWKw==}
engines: {node: ^14.18.0 || >=16.0.0} engines: {node: ^14.18.0 || >=16.0.0}
...@@ -1188,6 +1198,22 @@ packages: ...@@ -1188,6 +1198,22 @@ packages:
'@volar/typescript@2.4.5': '@volar/typescript@2.4.5':
resolution: {integrity: sha512-mcT1mHvLljAEtHviVcBuOyAwwMKz1ibXTi5uYtP/pf4XxoAzpdkQ+Br2IC0NPCvLCbjPZmbf3I0udndkfB1CDg==} resolution: {integrity: sha512-mcT1mHvLljAEtHviVcBuOyAwwMKz1ibXTi5uYtP/pf4XxoAzpdkQ+Br2IC0NPCvLCbjPZmbf3I0udndkfB1CDg==}
'@vue/babel-helper-vue-transform-on@1.2.5':
resolution: {integrity: sha512-lOz4t39ZdmU4DJAa2hwPYmKc8EsuGa2U0L9KaZaOJUt0UwQNjNA3AZTq6uEivhOKhhG1Wvy96SvYBoFmCg3uuw==}
'@vue/babel-plugin-jsx@1.2.5':
resolution: {integrity: sha512-zTrNmOd4939H9KsRIGmmzn3q2zvv1mjxkYZHgqHZgDrXz5B1Q3WyGEjO2f+JrmKghvl1JIRcvo63LgM1kH5zFg==}
peerDependencies:
'@babel/core': ^7.0.0-0
peerDependenciesMeta:
'@babel/core':
optional: true
'@vue/babel-plugin-resolve-type@1.2.5':
resolution: {integrity: sha512-U/ibkQrf5sx0XXRnUZD1mo5F7PkpKyTbfXM3a3rC4YnUz6crHEz9Jg09jzzL6QYlXNto/9CePdOg/c87O4Nlfg==}
peerDependencies:
'@babel/core': ^7.0.0-0
'@vue/compiler-core@3.5.12': '@vue/compiler-core@3.5.12':
resolution: {integrity: sha512-ISyBTRMmMYagUxhcpyEH0hpXRd/KqDU4ymofPgl2XAkY9ZhQ+h0ovEZJIiPop13UmR/54oA2cgMDjgroRelaEw==} resolution: {integrity: sha512-ISyBTRMmMYagUxhcpyEH0hpXRd/KqDU4ymofPgl2XAkY9ZhQ+h0ovEZJIiPop13UmR/54oA2cgMDjgroRelaEw==}
...@@ -3703,9 +3729,9 @@ snapshots: ...@@ -3703,9 +3729,9 @@ snapshots:
'@esbuild/win32-x64@0.23.1': '@esbuild/win32-x64@0.23.1':
optional: true optional: true
'@eslint-community/eslint-utils@4.4.0(eslint@9.10.0(jiti@1.21.6))': '@eslint-community/eslint-utils@4.4.0(eslint@9.10.0(jiti@2.0.0-beta.3))':
dependencies: dependencies:
eslint: 9.10.0(jiti@1.21.6) eslint: 9.10.0(jiti@2.0.0-beta.3)
eslint-visitor-keys: 3.4.3 eslint-visitor-keys: 3.4.3
'@eslint-community/regexpp@4.11.1': {} '@eslint-community/regexpp@4.11.1': {}
...@@ -3948,15 +3974,15 @@ snapshots: ...@@ -3948,15 +3974,15 @@ snapshots:
'@types/web-bluetooth@0.0.20': {} '@types/web-bluetooth@0.0.20': {}
'@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@7.18.0(eslint@9.10.0(jiti@1.21.6))(typescript@5.6.2))(eslint@9.10.0(jiti@1.21.6))(typescript@5.6.2)': '@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@7.18.0(eslint@9.10.0(jiti@2.0.0-beta.3))(typescript@5.6.2))(eslint@9.10.0(jiti@2.0.0-beta.3))(typescript@5.6.2)':
dependencies: dependencies:
'@eslint-community/regexpp': 4.11.1 '@eslint-community/regexpp': 4.11.1
'@typescript-eslint/parser': 7.18.0(eslint@9.10.0(jiti@1.21.6))(typescript@5.6.2) '@typescript-eslint/parser': 7.18.0(eslint@9.10.0(jiti@2.0.0-beta.3))(typescript@5.6.2)
'@typescript-eslint/scope-manager': 7.18.0 '@typescript-eslint/scope-manager': 7.18.0
'@typescript-eslint/type-utils': 7.18.0(eslint@9.10.0(jiti@1.21.6))(typescript@5.6.2) '@typescript-eslint/type-utils': 7.18.0(eslint@9.10.0(jiti@2.0.0-beta.3))(typescript@5.6.2)
'@typescript-eslint/utils': 7.18.0(eslint@9.10.0(jiti@1.21.6))(typescript@5.6.2) '@typescript-eslint/utils': 7.18.0(eslint@9.10.0(jiti@2.0.0-beta.3))(typescript@5.6.2)
'@typescript-eslint/visitor-keys': 7.18.0 '@typescript-eslint/visitor-keys': 7.18.0
eslint: 9.10.0(jiti@1.21.6) eslint: 9.10.0(jiti@2.0.0-beta.3)
graphemer: 1.4.0 graphemer: 1.4.0
ignore: 5.3.2 ignore: 5.3.2
natural-compare: 1.4.0 natural-compare: 1.4.0
...@@ -3966,14 +3992,14 @@ snapshots: ...@@ -3966,14 +3992,14 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@typescript-eslint/parser@7.18.0(eslint@9.10.0(jiti@1.21.6))(typescript@5.6.2)': '@typescript-eslint/parser@7.18.0(eslint@9.10.0(jiti@2.0.0-beta.3))(typescript@5.6.2)':
dependencies: dependencies:
'@typescript-eslint/scope-manager': 7.18.0 '@typescript-eslint/scope-manager': 7.18.0
'@typescript-eslint/types': 7.18.0 '@typescript-eslint/types': 7.18.0
'@typescript-eslint/typescript-estree': 7.18.0(typescript@5.6.2) '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.6.2)
'@typescript-eslint/visitor-keys': 7.18.0 '@typescript-eslint/visitor-keys': 7.18.0
debug: 4.3.7 debug: 4.3.7
eslint: 9.10.0(jiti@1.21.6) eslint: 9.10.0(jiti@2.0.0-beta.3)
optionalDependencies: optionalDependencies:
typescript: 5.6.2 typescript: 5.6.2
transitivePeerDependencies: transitivePeerDependencies:
...@@ -3984,12 +4010,12 @@ snapshots: ...@@ -3984,12 +4010,12 @@ snapshots:
'@typescript-eslint/types': 7.18.0 '@typescript-eslint/types': 7.18.0
'@typescript-eslint/visitor-keys': 7.18.0 '@typescript-eslint/visitor-keys': 7.18.0
'@typescript-eslint/type-utils@7.18.0(eslint@9.10.0(jiti@1.21.6))(typescript@5.6.2)': '@typescript-eslint/type-utils@7.18.0(eslint@9.10.0(jiti@2.0.0-beta.3))(typescript@5.6.2)':
dependencies: dependencies:
'@typescript-eslint/typescript-estree': 7.18.0(typescript@5.6.2) '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.6.2)
'@typescript-eslint/utils': 7.18.0(eslint@9.10.0(jiti@1.21.6))(typescript@5.6.2) '@typescript-eslint/utils': 7.18.0(eslint@9.10.0(jiti@2.0.0-beta.3))(typescript@5.6.2)
debug: 4.3.7 debug: 4.3.7
eslint: 9.10.0(jiti@1.21.6) eslint: 9.10.0(jiti@2.0.0-beta.3)
ts-api-utils: 1.3.0(typescript@5.6.2) ts-api-utils: 1.3.0(typescript@5.6.2)
optionalDependencies: optionalDependencies:
typescript: 5.6.2 typescript: 5.6.2
...@@ -4013,13 +4039,13 @@ snapshots: ...@@ -4013,13 +4039,13 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@typescript-eslint/utils@7.18.0(eslint@9.10.0(jiti@1.21.6))(typescript@5.6.2)': '@typescript-eslint/utils@7.18.0(eslint@9.10.0(jiti@2.0.0-beta.3))(typescript@5.6.2)':
dependencies: dependencies:
'@eslint-community/eslint-utils': 4.4.0(eslint@9.10.0(jiti@1.21.6)) '@eslint-community/eslint-utils': 4.4.0(eslint@9.10.0(jiti@2.0.0-beta.3))
'@typescript-eslint/scope-manager': 7.18.0 '@typescript-eslint/scope-manager': 7.18.0
'@typescript-eslint/types': 7.18.0 '@typescript-eslint/types': 7.18.0
'@typescript-eslint/typescript-estree': 7.18.0(typescript@5.6.2) '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.6.2)
eslint: 9.10.0(jiti@1.21.6) eslint: 9.10.0(jiti@2.0.0-beta.3)
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
- typescript - typescript
...@@ -4068,17 +4094,17 @@ snapshots: ...@@ -4068,17 +4094,17 @@ snapshots:
'@unocss/core@0.61.9': {} '@unocss/core@0.61.9': {}
'@unocss/eslint-config@0.61.9(eslint@9.10.0(jiti@1.21.6))(typescript@5.6.2)': '@unocss/eslint-config@0.61.9(eslint@9.10.0(jiti@2.0.0-beta.3))(typescript@5.6.2)':
dependencies: dependencies:
'@unocss/eslint-plugin': 0.61.9(eslint@9.10.0(jiti@1.21.6))(typescript@5.6.2) '@unocss/eslint-plugin': 0.61.9(eslint@9.10.0(jiti@2.0.0-beta.3))(typescript@5.6.2)
transitivePeerDependencies: transitivePeerDependencies:
- eslint - eslint
- supports-color - supports-color
- typescript - typescript
'@unocss/eslint-plugin@0.61.9(eslint@9.10.0(jiti@1.21.6))(typescript@5.6.2)': '@unocss/eslint-plugin@0.61.9(eslint@9.10.0(jiti@2.0.0-beta.3))(typescript@5.6.2)':
dependencies: dependencies:
'@typescript-eslint/utils': 7.18.0(eslint@9.10.0(jiti@1.21.6))(typescript@5.6.2) '@typescript-eslint/utils': 7.18.0(eslint@9.10.0(jiti@2.0.0-beta.3))(typescript@5.6.2)
'@unocss/config': 0.61.9 '@unocss/config': 0.61.9
'@unocss/core': 0.61.9 '@unocss/core': 0.61.9
magic-string: 0.30.11 magic-string: 0.30.11
...@@ -4209,6 +4235,16 @@ snapshots: ...@@ -4209,6 +4235,16 @@ snapshots:
- rollup - rollup
- supports-color - supports-color
'@vitejs/plugin-vue-jsx@4.0.1(vite@5.4.6(@types/node@20.16.5)(sass@1.79.1))(vue@3.5.12(typescript@5.6.2))':
dependencies:
'@babel/core': 7.25.2
'@babel/plugin-transform-typescript': 7.25.2(@babel/core@7.25.2)
'@vue/babel-plugin-jsx': 1.2.5(@babel/core@7.25.2)
vite: 5.4.6(@types/node@20.16.5)(sass@1.79.1)
vue: 3.5.12(typescript@5.6.2)
transitivePeerDependencies:
- supports-color
'@vitejs/plugin-vue@4.6.2(vite@5.4.6(@types/node@20.16.5)(sass@1.79.1))(vue@3.5.12(typescript@5.6.2))': '@vitejs/plugin-vue@4.6.2(vite@5.4.6(@types/node@20.16.5)(sass@1.79.1))(vue@3.5.12(typescript@5.6.2))':
dependencies: dependencies:
vite: 5.4.6(@types/node@20.16.5)(sass@1.79.1) vite: 5.4.6(@types/node@20.16.5)(sass@1.79.1)
...@@ -4226,6 +4262,36 @@ snapshots: ...@@ -4226,6 +4262,36 @@ snapshots:
path-browserify: 1.0.1 path-browserify: 1.0.1
vscode-uri: 3.0.8 vscode-uri: 3.0.8
'@vue/babel-helper-vue-transform-on@1.2.5': {}
'@vue/babel-plugin-jsx@1.2.5(@babel/core@7.25.2)':
dependencies:
'@babel/helper-module-imports': 7.24.7
'@babel/helper-plugin-utils': 7.24.8
'@babel/plugin-syntax-jsx': 7.24.7(@babel/core@7.25.2)
'@babel/template': 7.25.0
'@babel/traverse': 7.25.6
'@babel/types': 7.25.8
'@vue/babel-helper-vue-transform-on': 1.2.5
'@vue/babel-plugin-resolve-type': 1.2.5(@babel/core@7.25.2)
html-tags: 3.3.1
svg-tags: 1.0.0
optionalDependencies:
'@babel/core': 7.25.2
transitivePeerDependencies:
- supports-color
'@vue/babel-plugin-resolve-type@1.2.5(@babel/core@7.25.2)':
dependencies:
'@babel/code-frame': 7.24.7
'@babel/core': 7.25.2
'@babel/helper-module-imports': 7.24.7
'@babel/helper-plugin-utils': 7.24.8
'@babel/parser': 7.25.6
'@vue/compiler-sfc': 3.5.6
transitivePeerDependencies:
- supports-color
'@vue/compiler-core@3.5.12': '@vue/compiler-core@3.5.12':
dependencies: dependencies:
'@babel/parser': 7.25.8 '@babel/parser': 7.25.8
...@@ -4757,29 +4823,29 @@ snapshots: ...@@ -4757,29 +4823,29 @@ snapshots:
optionalDependencies: optionalDependencies:
source-map: 0.6.1 source-map: 0.6.1
eslint-config-prettier@9.1.0(eslint@9.10.0(jiti@1.21.6)): eslint-config-prettier@9.1.0(eslint@9.10.0(jiti@2.0.0-beta.3)):
dependencies: dependencies:
eslint: 9.10.0(jiti@1.21.6) eslint: 9.10.0(jiti@2.0.0-beta.3)
eslint-plugin-prettier@5.2.1(eslint-config-prettier@9.1.0(eslint@9.10.0(jiti@1.21.6)))(eslint@9.10.0(jiti@1.21.6))(prettier@3.3.3): eslint-plugin-prettier@5.2.1(eslint-config-prettier@9.1.0(eslint@9.10.0(jiti@2.0.0-beta.3)))(eslint@9.10.0(jiti@2.0.0-beta.3))(prettier@3.3.3):
dependencies: dependencies:
eslint: 9.10.0(jiti@1.21.6) eslint: 9.10.0(jiti@2.0.0-beta.3)
prettier: 3.3.3 prettier: 3.3.3
prettier-linter-helpers: 1.0.0 prettier-linter-helpers: 1.0.0
synckit: 0.9.1 synckit: 0.9.1
optionalDependencies: optionalDependencies:
eslint-config-prettier: 9.1.0(eslint@9.10.0(jiti@1.21.6)) eslint-config-prettier: 9.1.0(eslint@9.10.0(jiti@2.0.0-beta.3))
eslint-plugin-vue@9.28.0(eslint@9.10.0(jiti@1.21.6)): eslint-plugin-vue@9.28.0(eslint@9.10.0(jiti@2.0.0-beta.3)):
dependencies: dependencies:
'@eslint-community/eslint-utils': 4.4.0(eslint@9.10.0(jiti@1.21.6)) '@eslint-community/eslint-utils': 4.4.0(eslint@9.10.0(jiti@2.0.0-beta.3))
eslint: 9.10.0(jiti@1.21.6) eslint: 9.10.0(jiti@2.0.0-beta.3)
globals: 13.24.0 globals: 13.24.0
natural-compare: 1.4.0 natural-compare: 1.4.0
nth-check: 2.1.1 nth-check: 2.1.1
postcss-selector-parser: 6.1.2 postcss-selector-parser: 6.1.2
semver: 7.6.3 semver: 7.6.3
vue-eslint-parser: 9.4.3(eslint@9.10.0(jiti@1.21.6)) vue-eslint-parser: 9.4.3(eslint@9.10.0(jiti@2.0.0-beta.3))
xml-name-validator: 4.0.0 xml-name-validator: 4.0.0
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
...@@ -4798,9 +4864,9 @@ snapshots: ...@@ -4798,9 +4864,9 @@ snapshots:
eslint-visitor-keys@4.0.0: {} eslint-visitor-keys@4.0.0: {}
eslint@9.10.0(jiti@1.21.6): eslint@9.10.0(jiti@2.0.0-beta.3):
dependencies: dependencies:
'@eslint-community/eslint-utils': 4.4.0(eslint@9.10.0(jiti@1.21.6)) '@eslint-community/eslint-utils': 4.4.0(eslint@9.10.0(jiti@2.0.0-beta.3))
'@eslint-community/regexpp': 4.11.1 '@eslint-community/regexpp': 4.11.1
'@eslint/config-array': 0.18.0 '@eslint/config-array': 0.18.0
'@eslint/eslintrc': 3.1.0 '@eslint/eslintrc': 3.1.0
...@@ -4835,7 +4901,7 @@ snapshots: ...@@ -4835,7 +4901,7 @@ snapshots:
strip-ansi: 6.0.1 strip-ansi: 6.0.1
text-table: 0.2.0 text-table: 0.2.0
optionalDependencies: optionalDependencies:
jiti: 1.21.6 jiti: 2.0.0-beta.3
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
...@@ -5907,12 +5973,12 @@ snapshots: ...@@ -5907,12 +5973,12 @@ snapshots:
type-fest@0.21.3: {} type-fest@0.21.3: {}
typescript-eslint@7.18.0(eslint@9.10.0(jiti@1.21.6))(typescript@5.6.2): typescript-eslint@7.18.0(eslint@9.10.0(jiti@2.0.0-beta.3))(typescript@5.6.2):
dependencies: dependencies:
'@typescript-eslint/eslint-plugin': 7.18.0(@typescript-eslint/parser@7.18.0(eslint@9.10.0(jiti@1.21.6))(typescript@5.6.2))(eslint@9.10.0(jiti@1.21.6))(typescript@5.6.2) '@typescript-eslint/eslint-plugin': 7.18.0(@typescript-eslint/parser@7.18.0(eslint@9.10.0(jiti@2.0.0-beta.3))(typescript@5.6.2))(eslint@9.10.0(jiti@2.0.0-beta.3))(typescript@5.6.2)
'@typescript-eslint/parser': 7.18.0(eslint@9.10.0(jiti@1.21.6))(typescript@5.6.2) '@typescript-eslint/parser': 7.18.0(eslint@9.10.0(jiti@2.0.0-beta.3))(typescript@5.6.2)
'@typescript-eslint/utils': 7.18.0(eslint@9.10.0(jiti@1.21.6))(typescript@5.6.2) '@typescript-eslint/utils': 7.18.0(eslint@9.10.0(jiti@2.0.0-beta.3))(typescript@5.6.2)
eslint: 9.10.0(jiti@1.21.6) eslint: 9.10.0(jiti@2.0.0-beta.3)
optionalDependencies: optionalDependencies:
typescript: 5.6.2 typescript: 5.6.2
transitivePeerDependencies: transitivePeerDependencies:
...@@ -6048,7 +6114,7 @@ snapshots: ...@@ -6048,7 +6114,7 @@ snapshots:
evtd: 0.2.4 evtd: 0.2.4
vue: 3.5.12(typescript@5.6.2) vue: 3.5.12(typescript@5.6.2)
vite-plugin-checker@0.7.2(eslint@9.10.0(jiti@1.21.6))(optionator@0.9.4)(stylelint@16.9.0(typescript@5.6.2))(typescript@5.6.2)(vite@5.4.6(@types/node@20.16.5)(sass@1.79.1))(vue-tsc@2.0.29(typescript@5.6.2)): vite-plugin-checker@0.7.2(eslint@9.10.0(jiti@2.0.0-beta.3))(meow@13.2.0)(optionator@0.9.4)(stylelint@16.9.0(typescript@5.6.2))(typescript@5.6.2)(vite@5.4.6(@types/node@20.16.5)(sass@1.79.1))(vue-tsc@2.0.29(typescript@5.6.2)):
dependencies: dependencies:
'@babel/code-frame': 7.24.7 '@babel/code-frame': 7.24.7
ansi-escapes: 4.3.2 ansi-escapes: 4.3.2
...@@ -6066,7 +6132,8 @@ snapshots: ...@@ -6066,7 +6132,8 @@ snapshots:
vscode-languageserver-textdocument: 1.0.12 vscode-languageserver-textdocument: 1.0.12
vscode-uri: 3.0.8 vscode-uri: 3.0.8
optionalDependencies: optionalDependencies:
eslint: 9.10.0(jiti@1.21.6) eslint: 9.10.0(jiti@2.0.0-beta.3)
meow: 13.2.0
optionator: 0.9.4 optionator: 0.9.4
stylelint: 16.9.0(typescript@5.6.2) stylelint: 16.9.0(typescript@5.6.2)
typescript: 5.6.2 typescript: 5.6.2
...@@ -6114,10 +6181,10 @@ snapshots: ...@@ -6114,10 +6181,10 @@ snapshots:
dependencies: dependencies:
vue: 3.5.12(typescript@5.6.2) vue: 3.5.12(typescript@5.6.2)
vue-eslint-parser@9.4.3(eslint@9.10.0(jiti@1.21.6)): vue-eslint-parser@9.4.3(eslint@9.10.0(jiti@2.0.0-beta.3)):
dependencies: dependencies:
debug: 4.3.7 debug: 4.3.7
eslint: 9.10.0(jiti@1.21.6) eslint: 9.10.0(jiti@2.0.0-beta.3)
eslint-scope: 7.2.2 eslint-scope: 7.2.2
eslint-visitor-keys: 3.4.3 eslint-visitor-keys: 3.4.3
espree: 9.6.1 espree: 9.6.1
......
import { request } from '@/utils/request'
/* 知识库部分开始 */
/**
* @query trainStatus 训练状态
* @query search 搜索值
* @returns 获取知识库列表
*/
export function fetchGetKnowledgeList<T>(trainStatus: string, search: string, payload: object) {
return request.post<T>(`/knowledgeRest/getKnowledgeList.json?trainStatus=${trainStatus}&search=${search}`, payload)
}
/**
* @param payload { decs: string, knowledgeName: string }
* @returns 新建知识库
*/
export function fetchCreateKnowledge<T>(payload: object) {
return request.post<T>('/knowledgeRest/createKnowledge.json', payload)
}
/**
* @param payload { id:number, decs: string, knowledgeName: string }
* @returns 更新知识库
*/
export function fetchUpdateKnowledgeInfo<T>(payload: object) {
return request.post<T>('/knowledgeRest/updateKnowledgeInfo.json', payload)
}
/**
* @query knowledgeInfoId 知识库Id
* @returns 通过Id获取知识库详情
*/
export function fetchGetKnowledgeDetail<T>(knowledgeInfoId: number) {
return request.post<T>(`/knowledgeRest/getKnowledgeDetail.json?knowledgeInfoId=${knowledgeInfoId}`)
}
/**
* @query knowledgeInfoId 知识库Id
* @query isOpen 是否开启 Y | N
* @returns 开关知识库
*/
export function fetchEnableKnowledgeInfo(knowledgeInfoId: number, isOpen: string) {
return request.post(`/knowledgeRest/enableKnowledgeInfo.json?knowledgeInfoId=${knowledgeInfoId}&isOpen=${isOpen}`)
}
/**
* @query knowledgeInfoId 知识库Id
* @returns 批量删除知识库
*/
export function fetchDelKnowledgeById<T>(knowledgeInfoId: number) {
return request.post<T>(`/knowledgeRest/deleteKnowledgeInfo.json?knowledgeInfoId=${knowledgeInfoId}`)
}
/**
* @query formData 上传文档内容
* @returns 上传知识库文档
*/
export function fetchUploadKnowledgeDocument<T>(formData: FormData) {
return request.post<T>('/knowledgeRest/uploadDocument.json', formData, {
headers: {
'Content-Type': 'multipart/form-data',
},
timeout: 300000,
})
}
/**
* @params { knowledgeInfoId: number, kdIds: number[], segmentationConfig: SegmentationConfigInterface }
* @returns 训练知识库
*/
export function fetchTrainKnowledge<T>(payload: object) {
return request.post<T>('/knowledgeRest/trainKnowledge.json', payload)
}
/* 知识库部分结束 */
/* 知识库文档部分开始 */
/**
* @query knowledgeInfoId 知识库Id
* @query search 搜索值
* @returns 获取知识库文档列表
*/
export function fetchGetKnowledgeDocumentList<T>(knowledgeInfoId: number, search: string) {
return request.post<T>(`/knowledgeRest/searchDocuments.json?knowledgeInfoId=${knowledgeInfoId}&search=${search}`)
}
/**
* @query knowledgeInfoId 知识库Id
* @params kdIds 知识库文档Id
* @returns 批量删除知识库文档
*/
export function fetchBatchDelKnowledgeDocument(knowledgeInfoId: number, payload: number[]) {
return request.post(`/knowledgeRest/batchDelDocument.json?knowledgeInfoId=${knowledgeInfoId}`, payload)
}
/**
* @query knowledgeInfoId 知识库Id
* @query kdId 知识库文档Id
* @returns 删除知识库文档
*/
export function fetchDelKnowledgeDocument(knowledgeInfoId: number, kdId: number) {
return request.post(`/knowledgeRest/delDocument.json?knowledgeInfoId=${knowledgeInfoId}&kdId=${kdId}`)
}
/**
* @params kdIds 知识库文档Id
* @returns 根据kdIds获取知识库文档列表
*/
export function fetchGetKnowledgeDocumentListByKdIds<T>(kdIds: number[]) {
return request.post<T>(`/knowledgeRest/getListByKdIds.json`, kdIds)
}
/* 知识库文档片段开始 */
/**
* @query query 模糊搜索
* @query kdId 知识库文档Id
* @params payload { page: number, pageSize: number }
* @returns 获取知识库分片列表
*/
export function fetchGetKnowledgeChunkList<T>(query: string, kdId: number, payload: object) {
return request.post<T>(`/knowledgeRest/getChunks.json?query=${query}&kdIds=${kdId}`, payload)
}
/**
* @params payload { kdId: number, chunkContent: string, chunkSort: number }
* @returns 新增知识库分片
*/
export function fetchAddKnowledgeChunk<T>(payload: { kdId: number; chunkContent: string; chunkSort: number }) {
return request.post<T>('/knowledgeRest/addKnowledgeChunk.json', payload)
}
/**
* @params payload { kdId: number, chunkContent: string, chunkRelationId: string }
* @returns 更新知识库分片
*/
export function fetchUpdateKnowledgeChunk<T>(payload: { kdId: number; chunkContent: string; chunkRelationId: string }) {
return request.post<T>('/knowledgeRest/updateKnowledgeChunkDoc.json', payload)
}
/**
* @params payload { kdId: number, chunkRelationId: string }
* @returns 删除知识库分片
*/
export function fetchDeleteKnowledgeChunk<T>(payload: { kdId: number; chunkRelationId: string }) {
return request.post<T>('/knowledgeRest/deleteKnowledgeChunk.json', payload)
}
/**
* @params payload { kdId: number, chunkRelationId: string, isOpen: 'Y' | 'N' }
* @returns 开关知识库分片
*/
export function fetchOpenKnowledgeChunk<T>(payload: { kdId: number; chunkRelationId: string; isOpen: 'Y' | 'N' }) {
return request.post<T>('/knowledgeRest/openKnowledgeChunk.json', payload)
}
/* 知识库文档部分结束 */
<?xml version="1.0" encoding="UTF-8"?>
<svg width="18px" height="18px" viewBox="0 0 18 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>编组</title>
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="个人空间-删除切片" transform="translate(-831.000000, -492.000000)" fill-rule="nonzero">
<g id="编组" transform="translate(831.000000, 492.000000)">
<path d="M9,18 C6.612215,18.0030765 4.32175345,17.0539566 2.6359938,15.3628792 C0.945884088,13.6774855 -0.0027748396,11.3879687 0,9.00112665 C-0.00320129147,6.61331659 0.945937676,4.32280901 2.63712078,2.63712016 C4.3223293,0.946597065 6.61186896,-0.00247695124 8.99887335,0 C11.3866587,-0.00307708581 13.6771202,0.946042805 15.3628798,2.63712016 C17.05368,4.32260425 18.002777,6.61260485 18,9 C18.0030771,11.387785 17.0539572,13.6782466 15.3628798,15.3640062 C13.6771202,17.0550836 11.3866587,18.0042034 8.99887335,18.0011264 L9,18 Z" id="路径" fill="#F25744"></path>
<path d="M9.00000032,3.3797942 C9.31104437,3.3797942 9.74154737,3.65026729 9.74154737,4.01427899 L9.74154737,10.5879021 C9.74154737,10.9507869 9.31104435,11.2381645 9.00000032,11.2381645 C8.68895629,11.2381645 8.25845325,10.9507869 8.25845325,10.5879021 L8.25845325,4.01427899 C8.25845325,3.65026729 8.68895627,3.3797942 9.00000032,3.3797942 Z" id="路径" fill="#FFFFFF"></path>
<path d="M9.09917378,14.5818882 C8.47676476,14.5818882 7.97220256,14.077326 7.97220256,13.4549169 C7.97220256,12.8325079 8.47676476,12.3279457 9.09917378,12.3279457 C9.7215828,12.3279457 10.226145,12.8325079 10.226145,13.4549169 C10.226145,14.077326 9.7215828,14.5818882 9.09917378,14.5818882 L9.09917378,14.5818882 Z" id="路径" fill="#FFFFFF"></path>
</g>
</g>
</g>
</svg>
\ No newline at end of file
...@@ -34,8 +34,51 @@ export default [ ...@@ -34,8 +34,51 @@ export default [
}, },
component: () => import('@/views/personal-space/personal-app/personal-app.vue'), component: () => import('@/views/personal-space/personal-app/personal-app.vue'),
}, },
{
path: '/personalSpace/knowledge',
name: 'PersonalSpaceKnowledge',
meta: {
rank: 1001,
title: 'router_title_module.knowledge',
belong: 'PersonalSpace',
},
component: () => import('@/views/personal-space/personal-knowledge/personal-knowledge.vue'),
},
], ],
}, },
{
path: '/personalSpace/knowledge/document/:id',
name: 'KnowledgeDocument',
meta: {
rank: 1001,
title: 'router_title_module.knowledge_document_list',
belong: 'PersonalSpace',
},
component: () => import('@/views/personal-space/personal-knowledge/personal-document.vue'),
},
{
path: '/personalSpace/knowledge/document/detail/:kdId',
name: 'KnowledgeDocumentDetail',
meta: {
rank: 1001,
title: 'router_title_module.knowledge_document_detail',
belong: 'PersonalSpace',
},
component: () => import('@/views/personal-space/personal-knowledge/document-detail.vue'),
},
{
path: '/personalSpace/knowledge/upload/:id/:type?',
name: 'UploadKnowledge',
meta: {
rank: 1001,
title: 'router_title_module.upload_knowledge_document',
belong: 'PersonalSpace',
},
component: () => import('@/views/personal-space/personal-knowledge/upload-knowledge/upload-knowledge.vue'),
},
], ],
}, },
{ {
......
import { NSwitch, NPopconfirm } from 'naive-ui'
import { KnowledgeDocumentItem, KnowledgeItem } from './knowledge-type'
import { formatDateTime } from '@/utils/date-formatter'
import i18n from '@/locales'
const t = i18n.global.t
export function createKnowledgeColumn(
handleKnowledgeTableAction: (actionType: string, knowledgeId: number, KnowledgeItem?: KnowledgeItem) => void,
) {
return [
{
title: () => <span>{t('personal_space_module.knowledge_module.knowledge_name')}</span>,
key: 'knowledgeName',
align: 'left',
ellipsis: {
tooltip: true,
},
width: 210,
fixed: 'left',
render(row: KnowledgeItem) {
return row.knowledgeName || '--'
},
},
{
title: () => <span>{t('personal_space_module.knowledge_module.knowledge_desc')}</span>,
key: 'knowledgeDesc',
align: 'left',
ellipsis: {
tooltip: true,
},
width: 380,
render(row: KnowledgeItem) {
return row.desc || '--'
},
},
{
title: () => <span>{t('common_module.modified_time')}</span>,
key: 'modifiedTime',
align: 'left',
ellipsis: {
tooltip: true,
},
width: 170,
render(row: KnowledgeItem) {
return formatDateTime(row.modifiedTime) || '--'
},
},
{
title: () => <span>{t('common_module.is_open')}</span>,
key: 'isOpen',
align: 'left',
ellipsis: {
tooltip: true,
},
width: 150,
render(row: KnowledgeItem) {
return (
<NSwitch
value={row.isOpen === 'Y'}
onUpdateValue={() => handleKnowledgeTableAction('updateOpen', row.id, row)}
/>
)
},
},
{
title: () => <span>{t('common_module.data_table_module.action')}</span>,
key: 'action',
align: 'left',
ellipsis: {
tooltip: true,
},
width: 190,
fixed: 'right',
render(row: KnowledgeItem) {
return (
<div>
<span
className='text-theme-color mr-5 cursor-pointer hover:opacity-80'
onClick={() => handleKnowledgeTableAction('view', row.id)}
>
{t('common_module.data_table_module.view')}
</span>
<NPopconfirm
negative-text={t('common_module.cancel_btn_text')}
positive-text={t('common_module.confirm_btn_text')}
onNegativeClick={() => {}}
onPositiveClick={() => handleKnowledgeTableAction('delete', row.id)}
>
{{
trigger: () => (
<span className='text-error-font-color mr-5 cursor-pointer hover:opacity-80'>
{t('common_module.data_table_module.delete')}
</span>
),
default: () => (
<span> {t('personal_space_module.knowledge_module.delete_knowledge_dialog_content')}</span>
),
icon: () => (
<div class='bg-background-color h-4 w-4 rounded-full bg-[url(@/assets/svgs/warning.svg)] bg-contain' />
),
}}
</NPopconfirm>
</div>
)
},
},
]
}
export function createKnowledgeDocumentColumn(
handleKnowledgeDocumentTableAction: (actionType: string, knowledgeDocumentItem: KnowledgeDocumentItem) => void,
) {
return [
{
type: 'selection',
fixed: 'left',
},
{
title: () => <span>{t('personal_space_module.knowledge_module.knowledge_document_name')}</span>,
key: 'documentName',
align: 'left',
ellipsis: {
tooltip: true,
},
width: 190,
render(row: KnowledgeDocumentItem) {
return row.documentName || '--'
},
},
{
title: () => <span>{t('personal_space_module.knowledge_module.knowledge_document_char_count')}</span>,
key: 'charCount',
align: 'left',
ellipsis: {
tooltip: true,
},
width: 330,
render(row: KnowledgeDocumentItem) {
return `${row.charCount || 0}` + t('common_module.char')
},
},
{
title: () => <span>{t('personal_space_module.knowledge_module.knowledge_document_format')}</span>,
key: 'format',
align: 'left',
ellipsis: {
tooltip: true,
},
width: 220,
render(row: KnowledgeDocumentItem) {
return row.documentName.split('.')[1].toUpperCase() || '--'
},
},
{
title: () => <span>{t('common_module.modified_time')}</span>,
key: 'uploadTime',
align: 'left',
ellipsis: {
tooltip: true,
},
width: 300,
render(row: KnowledgeDocumentItem) {
return formatDateTime(row.uploadTime) || '--'
},
},
{
title: () => <span>{t('common_module.is_open')}</span>,
key: 'trainStatus',
align: 'left',
width: 180,
render(row: KnowledgeDocumentItem) {
const trainStatusMap = {
Unopened: { text: 'common_module.studying', color: '#0B7DFF' },
Line: { text: 'common_module.studying', color: '#0B7DFF' },
Training: { text: 'common_module.studying', color: '#0B7DFF' },
Complete: { text: 'common_module.available', color: '#0DD623' },
Fail: { text: 'common_module.unavailable', color: '#F25744' },
}
const state = trainStatusMap[row.trainStatus]
return (
<div class='flex items-center'>
{['Unopened', 'Line', 'Training'].includes(row.trainStatus) && (
<i class='iconfont icon-study mr-1.5 text-[#0B7DFF]' />
)}
{['Complete', 'Fail'].includes(row.trainStatus) && (
<div class='mr-2.5 h-3 w-3 rounded-full' style={{ backgroundColor: state.color }} />
)}
<span>{t(state.text)}</span>
</div>
)
},
},
{
title: () => <span>{t('common_module.data_table_module.action')}</span>,
key: 'action',
align: 'left',
width: 250,
fixed: 'right',
render(row: KnowledgeDocumentItem) {
return (
<div>
{row.trainStatus !== 'Fail' && (
<span
class={[
row.trainStatus === 'Complete'
? 'mr-5 cursor-pointer text-[#0B7DFF] hover:opacity-80'
: 'text-gray-font-color pointer-events-none mr-5 cursor-not-allowed',
]}
onClick={() => handleKnowledgeDocumentTableAction('view', row)}
>
{t('personal_space_module.knowledge_module.view_knowledge_document_fragment')}
</span>
)}
{row.trainStatus === 'Fail' && (
<span
class='mr-5 cursor-pointer text-[#0B7DFF] hover:opacity-80'
onClick={() => handleKnowledgeDocumentTableAction('train', row)}
>
{t('personal_space_module.knowledge_module.training_knowledge_document')}
</span>
)}
<NPopconfirm
negative-text={t('common_module.cancel_btn_text')}
positive-text={t('common_module.confirm_btn_text')}
onNegativeClick={() => {}}
onPositiveClick={() => handleKnowledgeDocumentTableAction('delete', row)}
>
{{
trigger: () => (
<span
class={[
row.trainStatus === 'Complete'
? 'text-error-font-color mr-5 cursor-pointer hover:opacity-80'
: 'text-gray-font-color pointer-events-none mr-5 cursor-not-allowed',
]}
>
{t('common_module.data_table_module.delete')}
</span>
),
default: () => (
<span> {t('personal_space_module.knowledge_module.delete_knowledge_dialog_content')}</span>
),
icon: () => (
<div class='bg-background-color h-4 w-4 rounded-full bg-[url(@/assets/svgs/warning.svg)] bg-contain' />
),
}}
</NPopconfirm>
</div>
)
},
},
]
}
<script setup lang="ts">
import { computed, ref, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import { FormInst } from 'naive-ui'
import CustomModal from '@/components/custom-modal/custom-modal.vue'
import { KnowledgeChunkItem } from '../knowledge-type'
interface Props {
isShowModal: boolean
btnLoading: boolean
chunkItem: KnowledgeChunkItem
}
interface Emits {
(e: 'update:isShowModal', value: boolean): void
(e: 'confirm', chunkContent: string, chunkSort: number): void
}
const { t } = useI18n()
const props = defineProps<Props>()
const emit = defineEmits<Emits>()
const defaultKnowledgeChunkData: KnowledgeChunkItem = {
knowledgeId: '',
chunkContent: '',
chunkRelationId: '',
chunkSort: 0,
isOpen: 'Y',
}
const knowledgeChunkFormRef = ref<FormInst | null>(null)
const knowledgeChunkData = ref<KnowledgeChunkItem>({ ...defaultKnowledgeChunkData })
const knowledgeChunkRules = {
chunkContent: [
{
required: true,
message: () => t('personal_space_module.knowledge_module.knowledge_chunk_content_input_rule'),
trigger: 'blur',
},
],
}
const showModal = computed({
get() {
return props.isShowModal
},
set(value: boolean) {
emit('update:isShowModal', value)
},
})
const isDisabledConfirmBtn = computed(() => {
return !knowledgeChunkData.value.chunkContent
})
watch(
() => showModal.value,
(newVal) => {
newVal && (knowledgeChunkData.value = JSON.parse(JSON.stringify(props.chunkItem)))
},
)
function handleConfirm() {
knowledgeChunkFormRef.value?.validate((valid) => {
if (!valid) {
const { chunkContent, chunkSort } = knowledgeChunkData.value
emit('confirm', chunkContent, chunkSort)
}
})
}
</script>
<template>
<CustomModal
v-model:is-show="showModal"
:title="t('personal_space_module.knowledge_module.add_knowledge_chunk_modal_title')"
:width="808"
:height="710"
:btn-disabled="isDisabledConfirmBtn"
:btn-loading="btnLoading"
@confirm="handleConfirm"
>
<template #content>
<n-form
ref="knowledgeChunkFormRef"
:model="knowledgeChunkData"
:rules="knowledgeChunkRules"
:label-width="100"
label-placement="top"
>
<n-form-item :show-label="false" path="chunkContent">
<n-input
v-model:value="knowledgeChunkData.chunkContent"
type="textarea"
:placeholder="t('personal_space_module.knowledge_module.knowledge_chunk_content_input_placeholder')"
:rows="23"
/>
</n-form-item>
</n-form>
</template>
</CustomModal>
</template>
<script setup lang="ts">
import { computed, h, readonly, ref, watch } from 'vue'
import { FormInst } from 'naive-ui'
import { useI18n } from 'vue-i18n'
import { FileAddition } from '@icon-park/vue-next'
import CustomModal from '@/components/custom-modal/custom-modal.vue'
const { t } = useI18n()
export interface KnowledgeFormDataInterface {
knowledgeType: string
knowledgeName: string
knowledgeDesc: string
knowledgeImportType: string
}
interface Props {
isShowModal: boolean
btnLoading: boolean
}
interface Emits {
(e: 'update:isShowModal', value: boolean): void
(e: 'confirm', value: KnowledgeFormDataInterface): void
}
const props = defineProps<Props>()
const emit = defineEmits<Emits>()
const createKnowledgeFormRef = ref<FormInst | null>(null)
const defaultKnowledgeFormData = {
knowledgeType: 'text',
knowledgeName: '',
knowledgeDesc: '',
knowledgeImportType: 'local-document',
}
const createKnowledgeFormData = ref<KnowledgeFormDataInterface>({ ...defaultKnowledgeFormData })
const createKnowledgeFormDataRules = {
knowledgeType: [
{ required: true, message: () => t('personal_space_module.knowledge_module.knowledge_type_rule'), trigger: 'blur' },
],
knowledgeName: [
{
required: true,
message: () => t('personal_space_module.knowledge_module.knowledge_name_rule'),
trigger: 'blur',
},
],
knowledgeImportType: [
{
required: true,
message: () => t('personal_space_module.knowledge_module.knowledge_import_type_rule'),
trigger: 'blur',
},
],
}
const knowledgeTypeList = readonly([
{
id: 'KJ7rCkq3HNOk',
icon: 'https://gsst-poe-sit.gz.bcebos.com/data/20241012/1728700558225.png',
type: 'text',
name: 'personal_space_module.knowledge_module.knowledge_document_text_type',
},
])
const importKnowledgeFileList = readonly([
{
id: 'ewwBbe9di5Ym',
icon: () => h(FileAddition, { theme: 'outline', size: '16', fill: '#333' }),
type: 'local-document',
title: 'personal_space_module.knowledge_module.import_local_document_knowledge',
desc: 'personal_space_module.knowledge_module.import_local_document_knowledge_desc',
},
])
const showModal = computed({
get() {
return props.isShowModal
},
set(value: boolean) {
emit('update:isShowModal', value)
},
})
const isDisabledConfirmBtn = computed(() => {
return !createKnowledgeFormData.value.knowledgeName
})
watch(
() => props.isShowModal,
() => {
createKnowledgeFormData.value = { ...defaultKnowledgeFormData }
},
)
function handleNext() {
createKnowledgeFormRef.value?.validate((errors) => {
if (!errors) {
emit('confirm', createKnowledgeFormData.value)
}
})
}
</script>
<template>
<CustomModal
v-model:is-show="showModal"
:height="562"
:width="437"
:title="t('personal_space_module.knowledge_module.create_knowledge_modal_title')"
:confirm-btn-text="t('common_module.next_btn_text')"
:btn-disabled="isDisabledConfirmBtn"
:btn-loading="btnLoading"
@confirm="handleNext"
>
<template #content>
<NForm
ref="createKnowledgeFormRef"
:model="createKnowledgeFormData"
:rules="createKnowledgeFormDataRules"
:label-width="100"
label-placement="top"
>
<NFormItem :show-label="false" path="knowledgeType">
<div class="flex w-full flex-col">
<div class="grid grid-cols-3 gap-2">
<div
v-for="knowledgeTypeItem in knowledgeTypeList"
:key="knowledgeTypeItem.id"
:class="
createKnowledgeFormData.knowledgeType === knowledgeTypeItem.type
? 'border-theme-color text-theme-color bg-[#ecedfa]'
: 'text-font-color bg-white'
"
class="hover:border-theme-color hover:text-theme-color flex h-[66px] cursor-pointer flex-col items-center justify-center rounded-lg border"
>
<img :src="knowledgeTypeItem.icon" class="h-6 w-6" />
<span class="mt-2 text-sm leading-[14px]">{{ t(knowledgeTypeItem.name) }}</span>
</div>
</div>
</div>
</NFormItem>
<NFormItem :label="t('common_module.name')" show-require-mark path="knowledgeName">
<NInput
v-model:value="createKnowledgeFormData.knowledgeName"
maxlength="15"
show-count
type="text"
:placeholder="t('personal_space_module.knowledge_module.knowledge_name_input_placeholder')"
/>
</NFormItem>
<NFormItem :label="t('common_module.desc')">
<NInput
v-model:value="createKnowledgeFormData.knowledgeDesc"
maxlength="500"
show-count
type="textarea"
:placeholder="t('personal_space_module.knowledge_module.knowledge_desc_input_placeholder')"
/>
</NFormItem>
<NFormItem
:label="t('personal_space_module.knowledge_module.knowledge_import_type_label')"
:show-require-mark="false"
path="knowledgeImportType"
>
<div class="grid w-full grid-cols-2 gap-2">
<div
v-for="importKnowledgeFileItem in importKnowledgeFileList"
:key="importKnowledgeFileItem.id"
class="flex h-[64px] cursor-pointer flex-col items-start justify-center rounded-lg border px-[14px]"
:class="
createKnowledgeFormData.knowledgeImportType === importKnowledgeFileItem.type
? 'border-theme-color bg-[#ecedfa]'
: 'border-inactive-border-color bg-[#FAFAFA]'
"
>
<div class="flex items-center">
<component :is="importKnowledgeFileItem.icon()" />
<span class="text-font-color ml-1 line-clamp-1">{{ t(importKnowledgeFileItem.title) }}</span>
</div>
<NPopover trigger="hover">
<template #trigger>
<span class="text-gray-font-color mt-1.5 line-clamp-1 text-xs">
{{ t(importKnowledgeFileItem.desc) }}
</span>
</template>
<span>{{ t(importKnowledgeFileItem.desc) }}</span>
</NPopover>
</div>
</div>
</NFormItem>
</NForm>
</template>
</CustomModal>
</template>
<script setup lang="ts">
import { computed, ref, watch } from 'vue'
import { FormInst } from 'naive-ui'
import { useI18n } from 'vue-i18n'
import CustomModal from '@/components/custom-modal/custom-modal.vue'
import { KnowledgeChunkItem } from '../knowledge-type'
interface Props {
isShowModal: boolean
btnLoading: boolean
totalChunk: number
chunkItem: KnowledgeChunkItem
}
interface Emits {
(e: 'update:isShowModal', value: boolean): void
(e: 'confirm', chunkRelationId: string, chunkContent: string): void
}
const { t } = useI18n()
const props = defineProps<Props>()
const emit = defineEmits<Emits>()
const defaultKnowledgeChunkData: KnowledgeChunkItem = {
knowledgeId: '',
chunkContent: '',
chunkRelationId: '',
chunkSort: 0,
isOpen: 'Y',
}
const knowledgeChunkFormRef = ref<FormInst | null>(null)
const knowledgeChunkData = ref<KnowledgeChunkItem>({ ...defaultKnowledgeChunkData })
const knowledgeChunkRules = {
chunkContent: [
{
required: true,
message: () => t('personal_space_module.knowledge_module.knowledge_chunk_content_input_rule'),
trigger: 'blur',
},
],
}
const showModal = computed({
get() {
return props.isShowModal
},
set(value: boolean) {
emit('update:isShowModal', value)
},
})
const isDisabledConfirmBtn = computed(() => {
return !knowledgeChunkData.value.chunkContent
})
watch(
() => showModal.value,
(newVal) => {
newVal && (knowledgeChunkData.value = JSON.parse(JSON.stringify(props.chunkItem)))
},
)
function handleConfirm() {
knowledgeChunkFormRef.value?.validate((valid) => {
if (!valid) {
const { chunkRelationId, chunkContent } = knowledgeChunkData.value
emit('confirm', chunkRelationId, chunkContent)
}
})
}
</script>
<template>
<CustomModal
v-model:is-show="showModal"
title=""
:width="808"
:height="710"
:btn-disabled="isDisabledConfirmBtn"
:btn-loading="btnLoading"
@confirm="handleConfirm"
>
<template #header>
<div>
<span class="bg-background-color rounded-full px-4 py-2 text-[15px]">
{{ knowledgeChunkData.chunkSort }} / {{ totalChunk }}
</span>
<span class="ml-[18px] text-[15px]">
{{ t('common_module.char') }}{{ knowledgeChunkData.chunkContent.length }}
</span>
</div>
</template>
<template #content>
<n-form
ref="knowledgeChunkFormRef"
:model="knowledgeChunkData"
:rules="knowledgeChunkRules"
:label-width="100"
label-placement="top"
>
<n-form-item :show-label="false" path="chunkContent">
<n-input
v-model:value="knowledgeChunkData.chunkContent"
type="textarea"
:placeholder="t('personal_space_module.knowledge_module.knowledge_chunk_content_input_placeholder')"
:rows="23"
/>
</n-form-item>
</n-form>
</template>
</CustomModal>
</template>
<script setup lang="ts">
import { computed, ref, watch } from 'vue'
import { FormInst } from 'naive-ui'
import { useI18n } from 'vue-i18n'
import CustomModal from '@/components/custom-modal/custom-modal.vue'
const { t } = useI18n()
export interface KnowledgeFormDataInterface {
knowledgeName: string
desc: string
}
interface Props {
isShowModal: boolean
btnLoading: boolean
knowledgeData: KnowledgeFormDataInterface
}
interface Emits {
(e: 'update:isShowModal', value: boolean): void
(e: 'confirm', value: KnowledgeFormDataInterface): void
}
const props = defineProps<Props>()
const emit = defineEmits<Emits>()
const defaultKnowledgeFormData = {
knowledgeName: '',
desc: '',
}
const knowledgeFormRef = ref<FormInst | null>(null)
const knowledgeFormData = ref<KnowledgeFormDataInterface>({ ...defaultKnowledgeFormData })
const knowledgeFormDataRules = {
knowledgeName: [
{ required: true, message: () => t('personal_space_module.knowledge_module.knowledge_name_rule'), trigger: 'blur' },
],
}
const showModal = computed({
get() {
return props.isShowModal
},
set(value: boolean) {
emit('update:isShowModal', value)
},
})
const isDisabledConfirmBtn = computed(() => {
return !knowledgeFormData.value.knowledgeName
})
watch(
() => showModal.value,
(newVal) => {
newVal && (knowledgeFormData.value = JSON.parse(JSON.stringify(props.knowledgeData)))
},
)
function handleConfirm() {
knowledgeFormRef.value?.validate((errors) => {
if (!errors) {
emit('confirm', knowledgeFormData.value)
}
})
}
</script>
<template>
<CustomModal
v-model:is-show="showModal"
:height="364"
:width="437"
:title="t('personal_space_module.knowledge_module.edit_knowledge_modal_title')"
:btn-disabled="isDisabledConfirmBtn"
:btn-loading="btnLoading"
@confirm="handleConfirm"
>
<template #content>
<NForm
ref="knowledgeFormRef"
:model="knowledgeFormData"
:rules="knowledgeFormDataRules"
:label-width="100"
label-placement="top"
>
<NFormItem :label="t('common_module.name')" show-require-mark path="knowledgeName">
<NInput
v-model:value="knowledgeFormData.knowledgeName"
maxlength="15"
show-count
type="text"
:placeholder="t('personal_space_module.knowledge_module.knowledge_name_input_placeholder')"
/>
</NFormItem>
<NFormItem :label="t('common_module.desc')">
<NInput
v-model:value="knowledgeFormData.desc"
maxlength="500"
show-count
type="textarea"
:placeholder="t('personal_space_module.knowledge_module.knowledge_desc_input_placeholder')"
/>
</NFormItem>
</NForm>
</template>
</CustomModal>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { useI18n } from 'vue-i18n'
import { KnowledgeChunkItem } from '../knowledge-type'
interface Props {
chunkItem: KnowledgeChunkItem
totalChunk: number
}
const { t } = useI18n()
defineProps<Props>()
const emit = defineEmits<{
(e: 'edit', value: KnowledgeChunkItem): void
(e: 'upAddChunk', chunkSort: number): void
(e: 'downAddChunk', chunkSort: number): void
(e: 'delete', chunkRelationId: string): void
(e: 'updateOpen', value: KnowledgeChunkItem): void
}>()
const isShowKnowledgeChunkAction = ref(false)
</script>
<template>
<div class="group w-full rounded-[20px] bg-white px-5 py-[18px]" @mouseleave="isShowKnowledgeChunkAction = false">
<div
class="text-gray-font-color border-inactive-border-color flex h-10 select-none items-center justify-between border-b pb-[14px] text-[13px]"
>
<span>{{ chunkItem.chunkSort }} / {{ totalChunk }}</span>
<div
class="hidden items-center gap-[10px] group-hover:flex"
:class="isShowKnowledgeChunkAction ? 'flex' : 'hidden group-hover:flex'"
>
<n-popover trigger="hover">
<template #trigger>
<i
class="iconfont icon-edit-document hover:text-font-color hover:bg-background-color flex h-6 w-6 cursor-pointer items-center justify-center rounded-full text-sm"
@click="emit('edit', chunkItem)"
/>
</template>
<span> {{ t('common_module.data_table_module.edit') }}</span>
</n-popover>
<n-popover trigger="hover">
<template #trigger>
<i
class="iconfont icon-add-chunk-up hover:text-font-color hover:bg-background-color flex h-6 w-6 cursor-pointer items-center justify-center rounded-full text-sm"
@click="emit('upAddChunk', chunkItem.chunkSort)"
/>
</template>
<span> {{ t('personal_space_module.knowledge_module.add_chunk_up_message') }} </span>
</n-popover>
<n-popover trigger="hover">
<template #trigger>
<i
class="iconfont icon-add-chunk-up hover:text-font-color hover:bg-background-color flex h-6 w-6 rotate-180 cursor-pointer items-center justify-center rounded-full text-sm"
@click="emit('downAddChunk', chunkItem.chunkSort + 1)"
/>
</template>
<span> {{ t('personal_space_module.knowledge_module.add_chunk_down_message') }} </span>
</n-popover>
<n-popconfirm
placement="bottom"
:negative-text="t('common_module.cancel_btn_text')"
:positive-text="t('common_module.confirm_btn_text')"
@negative-click="() => {}"
@positive-click="emit('delete', chunkItem.chunkRelationId)"
@mouseenter="isShowKnowledgeChunkAction = true"
>
<template #icon>
<div class="bg-background-color h-4 w-4 rounded-full bg-[url(@/assets/svgs/warning.svg)] bg-contain" />
</template>
<template #trigger>
<n-popover trigger="hover">
<template #trigger>
<i
class="iconfont icon-delete hover:text-font-color hover:bg-background-color flex h-6 w-6 cursor-pointer items-center justify-center rounded-full text-sm"
/>
</template>
<span> {{ t('common_module.data_table_module.delete') }}</span>
</n-popover>
</template>
{{ t('personal_space_module.knowledge_module.delete_knowledge_chunk_content_message') }}
</n-popconfirm>
<n-switch :value="chunkItem.isOpen === 'Y'" @update:value="emit('updateOpen', chunkItem)" />
</div>
</div>
<div class="text-font-color mt-[14px] whitespace-pre-wrap">
{{ chunkItem.chunkContent }}
</div>
</div>
</template>
<script setup lang="ts">
import { computed, onMounted, ref } from 'vue'
import { useRouter } from 'vue-router'
import { useI18n } from 'vue-i18n'
import { ScrollbarInst } from 'naive-ui'
import { Left, Search } from '@icon-park/vue-next'
import { KnowledgeChunkItem, KnowledgeDocumentItem } from './knowledge-type'
import KnowledgeChuckItem from './components/knowledge-chuck-item.vue'
import {
fetchAddKnowledgeChunk,
fetchDeleteKnowledgeChunk,
fetchGetKnowledgeChunkList,
fetchGetKnowledgeDocumentListByKdIds,
fetchOpenKnowledgeChunk,
fetchUpdateKnowledgeChunk,
} from '@/apis/knowledge'
import CustomPagination, { PaginationInfo } from '@/components/custom-pagination/custom-pagination.vue'
import EditKnowledgeChunkModal from './components/edit-knowledge-chunk-modal.vue'
import AddKnowledgeChunkModal from './components/add-knowledge-chunk-modal.vue'
const { t } = useI18n()
const router = useRouter()
const currentKdId = ref(0)
const currentKnowledgeDocumentName = ref('')
const currentKnowledgeDocumentUrl = ref('')
const scrollBarRef = ref<ScrollbarInst | null>(null)
const searchKnowledgeChunkValue = ref('')
const isSearchEmptyList = ref(false)
const totalChunk = ref(0)
const knowledgeChunkList = ref<KnowledgeChunkItem[]>([])
const knowledgeChunkListLoading = ref(false)
const pagingInfo = ref<PaginationInfo>({
pageNo: 1,
pageSize: 10,
totalPages: 0,
totalRows: 0,
})
const defaultKnowledgeChunkData: KnowledgeChunkItem = {
knowledgeId: '',
chunkContent: '',
chunkRelationId: '',
chunkSort: 0,
isOpen: 'Y',
}
const isShowEditKnowledgeChunkModal = ref(false)
const updateKnowledgeChunkBtnLoading = ref(false)
const isShowAddKnowledgeChunkModal = ref(false)
const addKnowledgeChunkBtnLoading = ref(false)
const currentKnowledgeChunkInfo = ref<KnowledgeChunkItem>({ ...defaultKnowledgeChunkData })
const documentIcon = computed(() => (documentUrl: string) => {
const type = documentUrl.substring(documentUrl.lastIndexOf('.') + 1)
return `https://gsst-poe-sit.gz.bcebos.com/icon/${type}.png`
})
const emptyKnowledgeChunkListText = computed(() => {
return isSearchEmptyList.value ? t('common_module.search_empty_data') : t('common_module.empty_data')
})
onMounted(async () => {
if (!router.currentRoute.value.params.kdId) {
window.$message.warning(t('personal_space_module.knowledge_module.not_find_knowledge_document_message'))
router.replace({ name: 'PersonalSpaceKnowledge' })
return
}
currentKdId.value = Number(router.currentRoute.value.params.kdId)
await handleGetKnowledgeDocumentDetail()
await handleGetKnowledgeChunkList()
})
function handleGetKnowledgeDocumentDetail() {
fetchGetKnowledgeDocumentListByKdIds<KnowledgeDocumentItem[]>([currentKdId.value])
.then((res) => {
if (res.code === 0) {
currentKnowledgeDocumentName.value = res.data[0].documentName
currentKnowledgeDocumentUrl.value = res.data[0].documentUrl
}
})
.catch(() => {
window.$message.warning(t('personal_space_module.knowledge_module.not_find_knowledge_document_message'))
router.replace({ name: 'PersonalSpaceKnowledge' })
})
}
async function handleGetKnowledgeChunkList() {
knowledgeChunkListLoading.value = true
const res = await fetchGetKnowledgeChunkList<{ totalChunk: number; chunkInfos: KnowledgeChunkItem[] }>(
searchKnowledgeChunkValue.value,
currentKdId.value,
{
pagingInfo: pagingInfo.value,
},
)
if (res.code === 0) {
totalChunk.value = res.data.totalChunk
knowledgeChunkList.value = res.data.chunkInfos || []
pagingInfo.value = res.pagingInfo as PaginationInfo
knowledgeChunkListLoading.value = false
isSearchEmptyList.value = !!searchKnowledgeChunkValue.value && pagingInfo.value.totalRows === 0
scrollBarRef.value?.scrollTo({ top: 0 })
}
}
function handleBackKnowledgeDocumentList() {
router.back()
}
async function handleGetKnowledgeChunkListUpdatePageNo(pageNo: number) {
pagingInfo.value.pageNo = pageNo
await handleGetKnowledgeChunkList()
}
async function handleGetKnowledgeChunkListUpdatePageSize(pageSize: number) {
pagingInfo.value.pageNo = 1
pagingInfo.value.pageSize = pageSize
await handleGetKnowledgeChunkList()
}
async function handleSearchKnowledgeChunkList() {
pagingInfo.value.pageNo = 1
await handleGetKnowledgeChunkList()
}
function handleEditKnowledgeChunkModal(chunkItem: KnowledgeChunkItem) {
isShowEditKnowledgeChunkModal.value = true
currentKnowledgeChunkInfo.value = chunkItem
}
async function handleUpdateKnowledgeChunk(chunkRelationId: string, chunkContent: string) {
updateKnowledgeChunkBtnLoading.value = true
const res = await fetchUpdateKnowledgeChunk({
kdId: currentKdId.value,
chunkRelationId,
chunkContent,
}).finally(() => (updateKnowledgeChunkBtnLoading.value = false))
if (res.code === 0) {
window.$message.success(t('common_module.edit_success_message'))
isShowEditKnowledgeChunkModal.value = false
await handleGetKnowledgeChunkList()
}
}
function handleShowUpAddChunkModal(chunkSort: number) {
isShowAddKnowledgeChunkModal.value = true
currentKnowledgeChunkInfo.value = { ...defaultKnowledgeChunkData, chunkSort }
}
function handleShowDownAddChunkModal(chunkSort: number) {
isShowAddKnowledgeChunkModal.value = true
currentKnowledgeChunkInfo.value = { ...defaultKnowledgeChunkData, chunkSort }
}
async function handleAddKnowledgeChunk(chunkContent: string, chunkSort: number) {
addKnowledgeChunkBtnLoading.value = true
const res = await fetchAddKnowledgeChunk({
kdId: currentKdId.value,
chunkContent,
chunkSort,
}).finally(() => {
addKnowledgeChunkBtnLoading.value = false
})
if (res.code === 0) {
window.$message.success(t('common_module.add_success_message'))
isShowAddKnowledgeChunkModal.value = false
await handleGetKnowledgeChunkList()
}
}
async function handleDeleteKnowledgeChunk(chunkRelationId: string) {
const res = await fetchDeleteKnowledgeChunk({
kdId: currentKdId.value,
chunkRelationId,
})
if (res.code === 0) {
window.$message.success(t('common_module.delete_success_message'))
await handleGetKnowledgeChunkList()
}
}
async function handleUpdateOpenKnowledgeChunk(chunkItem: KnowledgeChunkItem) {
const { chunkRelationId, isOpen } = chunkItem
const res = await fetchOpenKnowledgeChunk({
chunkRelationId,
isOpen: isOpen === 'Y' ? 'N' : 'Y',
kdId: currentKdId.value,
})
if (res.code === 0) {
window.$message.success(t('common_module.edit_success_message'))
await handleGetKnowledgeChunkList()
}
}
</script>
<template>
<div class="flex h-full flex-col overflow-hidden">
<div class="my-6">
<div class="flex h-9 items-center">
<Left theme="outline" size="20" fill="#333" class="cursor-pointer" @click="handleBackKnowledgeDocumentList" />
<img :src="documentIcon(currentKnowledgeDocumentUrl)" class="ml-[2px] h-9 w-9" />
<span class="text-font-color ml-[5px] line-clamp-1 select-none break-all text-lg">
{{ currentKnowledgeDocumentName }}
</span>
</div>
<div class="ml-[66px] mt-1">
<ul class="flex gap-2">
<li class="border-inactive-border-color select-none rounded-full border px-4 py-1 leading-[18px]">
{{ totalChunk }}
{{ t('personal_space_module.knowledge_module.segment') }}
</li>
<li class="border-inactive-border-color select-none rounded-full border px-4 py-1 leading-[18px]">
{{ t('personal_space_module.knowledge_module.auto_segment') }}
</li>
</ul>
</div>
</div>
<div class="mb-[18px] flex justify-end">
<n-input
v-model:value="searchKnowledgeChunkValue"
:placeholder="t('personal_space_module.knowledge_module.search_knowledge_chunk_placeholder')"
class="w-[214px]!"
@keyup.enter="handleSearchKnowledgeChunkList"
>
<template #suffix>
<Search
theme="outline"
size="16"
fill="#999"
class="cursor-pointer"
@click="handleSearchKnowledgeChunkList"
/>
</template>
</n-input>
</div>
<n-spin :show="knowledgeChunkListLoading" class="w-full flex-1 overflow-hidden" content-class="flex w-full h-full">
<n-scrollbar
v-if="!knowledgeChunkListLoading && knowledgeChunkList.length"
ref="scrollBarRef"
class="grid w-full flex-1 overflow-hidden"
>
<div class="grid gap-5">
<KnowledgeChuckItem
v-for="chunkItem in knowledgeChunkList"
:key="chunkItem.chunkRelationId"
:chunk-item="chunkItem"
:total-chunk="totalChunk"
@edit="handleEditKnowledgeChunkModal"
@up-add-chunk="handleShowUpAddChunkModal"
@down-add-chunk="handleShowDownAddChunkModal"
@delete="handleDeleteKnowledgeChunk"
@update-open="handleUpdateOpenKnowledgeChunk"
/>
</div>
</n-scrollbar>
<div
v-show="!knowledgeChunkListLoading && knowledgeChunkList.length === 0"
class="flex flex-1 items-center justify-center"
>
<div class="flex flex-col items-center justify-center">
<div
class="mb-5 h-[68px] w-[68px] bg-contain"
:class="
isSearchEmptyList
? `bg-[url('@/assets/images/search-empty-list.png')]`
: `bg-[url('@/assets/images/empty-list.png')]`
"
/>
<p class="text-gray-font-color select-none">{{ emptyKnowledgeChunkListText }}</p>
</div>
</div>
</n-spin>
<div class="mt-4 flex justify-end">
<CustomPagination
:paging-info="pagingInfo"
@update-page-no="handleGetKnowledgeChunkListUpdatePageNo"
@update-page-size="handleGetKnowledgeChunkListUpdatePageSize"
/>
</div>
<EditKnowledgeChunkModal
v-model:is-show-modal="isShowEditKnowledgeChunkModal"
:chunk-item="currentKnowledgeChunkInfo"
:total-chunk="totalChunk"
:btn-loading="updateKnowledgeChunkBtnLoading"
@confirm="handleUpdateKnowledgeChunk"
/>
<AddKnowledgeChunkModal
v-model:is-show-modal="isShowAddKnowledgeChunkModal"
:chunk-item="currentKnowledgeChunkInfo"
:total-chunk="totalChunk"
:btn-loading="addKnowledgeChunkBtnLoading"
@confirm="handleAddKnowledgeChunk"
/>
</div>
</template>
export enum TrainStatus {
UNOPENED = 'Unopened',
LINE = 'Line',
TRAINING = 'Training',
COMPLETE = 'Complete',
FAIL = 'Fail',
}
export interface KnowledgeItem {
id: number
knowledgeName: string
desc: string
trainStatus: TrainStatus
isOpen: 'Y' | 'N'
modifiedTime: Date
}
export interface SegmentationConfigInterface {
segmentationType: 'Default'
chunkSize: number
scrapSize: number
repetitionRate: number
relationInfo: string[]
regex: string
punctuations: string
}
export interface KnowledgeDocumentItem {
kdId: number
knowledgeType: string
documentName: string
documentUrl: string
charCount: number
uploadTime: Date
isEnable: 'Y' | 'N'
knowledgeId: string
trainStatus: TrainStatus
segmentationConfig: SegmentationConfigInterface
}
export interface KnowledgeChunkItem {
knowledgeId: string
chunkContent: string
chunkRelationId: string
chunkSort: number
isOpen: 'Y' | 'N'
}
<script setup lang="ts">
import { computed, onMounted, ref, watch } from 'vue'
import { useRouter } from 'vue-router'
import { useI18n } from 'vue-i18n'
import { Left, Search } from '@icon-park/vue-next'
import { useIntervalFn } from '@vueuse/core'
import { createKnowledgeDocumentColumn } from './columns'
import { KnowledgeDocumentItem, TrainStatus } from './knowledge-type'
import {
fetchBatchDelKnowledgeDocument,
fetchDelKnowledgeDocument,
fetchGetKnowledgeDetail,
fetchGetKnowledgeDocumentList,
fetchUpdateKnowledgeInfo,
} from '@/apis/knowledge'
import useTableScrollY from '@/composables/useTableScrollY'
import EditKnowledgeModal, { KnowledgeFormDataInterface } from './components/edit-knowledge-modal.vue'
const { t } = useI18n()
const router = useRouter()
const { pageContentWrapRef, tableContentY } = useTableScrollY(48 + 48)
const knowledgeDocumentColumn = createKnowledgeDocumentColumn(handleClickKnowledgeDocumentTableAction)
const knowledgeDocumentTableLoading = ref(false)
const knowledgeDocumentList = ref<KnowledgeDocumentItem[]>([])
const checkedKdIdList = ref<number[]>([])
const searchDocumentInputValue = ref('')
const currentKnowledgeId = ref(0)
const isSearchEmptyList = ref(false)
const isShowEditKnowledgeModal = ref(false)
const updateKnowledgeInfoBtnLoading = ref(false)
const currentKnowledgeData = ref<KnowledgeFormDataInterface>({
knowledgeName: '',
desc: '',
})
const isDisabledBatchDelBtn = computed(() => {
return checkedKdIdList.value.length <= 0
})
const isKnowledgeDocumentFinish = computed(() => {
return knowledgeDocumentList.value.every((documentItem) => {
return [TrainStatus.FAIL, TrainStatus.COMPLETE].includes(documentItem.trainStatus)
})
})
const emptyTableDataText = computed(() => {
return isSearchEmptyList.value
? t('common_module.search_empty_data')
: t('personal_space_module.knowledge_module.empty_knowledge_document_list')
})
watch(
() => isKnowledgeDocumentFinish.value,
(newVal) => {
newVal ? pauseIntervalFn() : resumeIntervalFn()
},
{ deep: true },
)
const { pause: pauseIntervalFn, resume: resumeIntervalFn } = useIntervalFn(
async () => {
if (knowledgeDocumentTableLoading.value) return
const res = await fetchGetKnowledgeDocumentList<KnowledgeDocumentItem[]>(
currentKnowledgeId.value,
searchDocumentInputValue.value,
)
if (res.code === 0) {
knowledgeDocumentList.value = res.data
isSearchEmptyList.value = !!searchDocumentInputValue.value && res.data.length === 0
}
},
2000,
{ immediateCallback: false, immediate: false },
)
onMounted(async () => {
if (!router.currentRoute.value.params.id) {
window.$message.warning(t('personal_space_module.knowledge_module.not_find_knowledge_message'))
router.replace({ name: 'PersonalSpaceKnowledge' })
return
}
currentKnowledgeId.value = Number(router.currentRoute.value.params.id)
await handleGetKnowledgeDetail()
await handleGetKnowledgeDocumentList()
})
async function handleGetKnowledgeDetail() {
const res = await fetchGetKnowledgeDetail<KnowledgeFormDataInterface>(currentKnowledgeId.value)
if (res.code === 0) {
currentKnowledgeData.value = res.data
}
}
async function handleGetKnowledgeDocumentList() {
knowledgeDocumentTableLoading.value = true
const res = await fetchGetKnowledgeDocumentList<KnowledgeDocumentItem[]>(
currentKnowledgeId.value,
searchDocumentInputValue.value,
)
if (res.code === 0) {
knowledgeDocumentList.value = res.data
knowledgeDocumentTableLoading.value = false
checkedKdIdList.value = []
isSearchEmptyList.value = !!searchDocumentInputValue.value && res.data.length === 0
}
}
function handleClickKnowledgeDocumentTableAction(actionType: string, knowledgeDocumentItem: KnowledgeDocumentItem) {
switch (actionType) {
case 'view':
handleToViewDocumentDetail(knowledgeDocumentItem.kdId)
break
case 'delete':
handleDeleteKnowledgeDocument(knowledgeDocumentItem.kdId)
break
}
}
function handleToViewDocumentDetail(kdId: number) {
router.push({
name: 'KnowledgeDocumentDetail',
params: { kdId },
})
}
async function handleDeleteKnowledgeDocument(kdId: number) {
const res = await fetchDelKnowledgeDocument(currentKnowledgeId.value, kdId)
if (res.code === 0) {
window.$message.success(t('common_module.delete_success_message'))
await handleGetKnowledgeDocumentList()
}
}
function handleShowEditKnowledgeModal() {
isShowEditKnowledgeModal.value = true
}
async function handleUpdateKnowledgeInfo(knowledgeData: KnowledgeFormDataInterface) {
updateKnowledgeInfoBtnLoading.value = true
const res = await fetchUpdateKnowledgeInfo<KnowledgeFormDataInterface>({
id: currentKnowledgeId.value,
knowledgeName: knowledgeData.knowledgeName,
desc: knowledgeData.desc,
}).finally(() => (updateKnowledgeInfoBtnLoading.value = false))
if (res.code === 0) {
window.$message.success(t('common_module.save_success_message'))
currentKnowledgeData.value = res.data
isShowEditKnowledgeModal.value = false
}
}
function handleUpdateCheckedKdId(kdIdList: number[]) {
checkedKdIdList.value = kdIdList
}
async function handleBatchDelDocument() {
const res = await fetchBatchDelKnowledgeDocument(currentKnowledgeId.value, checkedKdIdList.value)
if (res.code === 0) {
window.$message.success(t('common_module.delete_success_message'))
await handleGetKnowledgeDocumentList()
}
}
function handleToUploadDocument() {
router.push({
name: 'UploadKnowledge',
params: { id: router.currentRoute.value.params.id, type: 'text-local-document' },
})
}
function handleBackKnowledgeList() {
router.replace({ name: 'PersonalSpaceKnowledge' })
}
</script>
<template>
<div class="flex h-full w-full flex-col overflow-hidden">
<div class="my-3 flex h-8 items-center">
<Left theme="outline" size="20" fill="#333" class="cursor-pointer" @click="handleBackKnowledgeList" />
<div class="flex items-center">
<span class="text-font-color ml-[2px] h-8 select-none text-lg leading-[30px]">
{{ currentKnowledgeData.knowledgeName }}
</span>
<i
class="iconfont icon-edit text-gray-font-color ml-[5px] cursor-pointer text-[15px] leading-none"
@click="handleShowEditKnowledgeModal"
/>
</div>
</div>
<div class="mb-[18px] flex justify-end">
<NInput
v-model:value="searchDocumentInputValue"
:placeholder="t('personal_space_module.knowledge_module.search_knowledge_document_placeholder')"
class="w-[214px]!"
@keyup.enter="handleGetKnowledgeDocumentList"
>
<template #suffix>
<Search
theme="outline"
size="16"
fill="#999"
class="cursor-pointer"
@click="handleGetKnowledgeDocumentList"
/>
</template>
</NInput>
<NButton class="ml-[14px]!" type="info" :disabled="isDisabledBatchDelBtn" @click="handleBatchDelDocument">
{{ t('personal_space_module.knowledge_module.batch_delete_knowledge_document_btn_text') }}
</NButton>
<NButton type="primary" :bordered="false" class="ml-[14px]!" @click="handleToUploadDocument">
{{ t('personal_space_module.knowledge_module.upload_knowledge_document_btn_text') }}
</NButton>
</div>
<div ref="pageContentWrapRef" class="flex-1 overflow-hidden rounded-[20px] bg-white p-6">
<div :style="{ height: tableContentY + 48 + 'px' }">
<NDataTable
:loading="knowledgeDocumentTableLoading"
:columns="knowledgeDocumentColumn"
:bordered="false"
:single-line="false"
:data="knowledgeDocumentList"
:row-key="(row: KnowledgeDocumentItem) => row.kdId"
:max-height="tableContentY"
:scroll-x="1526"
:checked-row-keys="checkedKdIdList"
@update:checked-row-keys="handleUpdateCheckedKdId"
>
<template #empty>
<div :style="{ height: tableContentY + 'px' }" class="flex items-center justify-center">
<div class="flex flex-col items-center justify-center">
<div
class="mb-5 h-[68px] w-[68px] bg-contain"
:class="
isSearchEmptyList
? `bg-[url('@/assets/images/search-empty-list.png')]`
: `bg-[url('@/assets/images/empty-document-list.png')]`
"
/>
<p class="select-none text-[#84868c]">{{ emptyTableDataText }}</p>
</div>
</div>
</template>
</NDataTable>
</div>
</div>
<EditKnowledgeModal
v-model:is-show-modal="isShowEditKnowledgeModal"
:btn-loading="updateKnowledgeInfoBtnLoading"
:knowledge-data="currentKnowledgeData"
@confirm="handleUpdateKnowledgeInfo"
/>
</div>
</template>
<script setup lang="ts">
import { computed, onMounted, ref } from 'vue'
import { useRouter } from 'vue-router'
import { useI18n } from 'vue-i18n'
import { Search } from '@icon-park/vue-next'
import { createKnowledgeColumn } from './columns.tsx'
import { KnowledgeItem } from './knowledge-type.ts'
import CustomPagination, { PaginationInfo } from '@/components/custom-pagination/custom-pagination.vue'
import useTableScrollY from '@/composables/useTableScrollY.ts'
import { fetchDelKnowledgeById, fetchEnableKnowledgeInfo, fetchGetKnowledgeList } from '@/apis/knowledge.ts'
const { t } = useI18n()
const router = useRouter()
const { pageContentWrapRef, tableContentY } = useTableScrollY(48 + 32 + 16 + 16 + 28)
const knowledgeColumns = createKnowledgeColumn(handleClickKnowledgeTableAction)
const pagingInfo = ref<PaginationInfo>({
pageNo: 1,
pageSize: 10,
totalPages: 0,
totalRows: 0,
})
const knowledgeListTableLoading = ref(false)
const knowledgeList = ref<KnowledgeItem[]>([])
const searchKnowledgeInputValue = ref('')
const isSearchEmptyList = ref(false)
const isLoadingPagination = computed(() => {
return tableContentY.value > 0
})
const emptyTableDataText = computed(() => {
return isSearchEmptyList.value ? t('common_module.search_empty_data') : t('common_module.empty_data')
})
onMounted(async () => {
await handleGetKnowledgeList()
})
async function handleGetKnowledgeList() {
knowledgeListTableLoading.value = true
const res = await fetchGetKnowledgeList<KnowledgeItem[]>('', searchKnowledgeInputValue.value, {
pagingInfo: pagingInfo.value,
})
if (res.code === 0) {
knowledgeList.value = res.data
pagingInfo.value = res.pagingInfo as PaginationInfo
knowledgeListTableLoading.value = false
isSearchEmptyList.value = !!searchKnowledgeInputValue.value && pagingInfo.value.totalRows === 0
}
}
function handleClickKnowledgeTableAction(actionType: string, knowledgeId: number, knowledgeItem?: KnowledgeItem) {
switch (actionType) {
case 'updateOpen':
handleUpdateKnowledgeOpen(knowledgeId, knowledgeItem!)
break
case 'view':
handleViewDocumentList(knowledgeId)
break
case 'delete':
handleDeleteKnowledge(knowledgeId)
break
}
}
async function handleUpdateKnowledgeOpen(knowledgeId: number, knowledgeItem: KnowledgeItem) {
const isOpen = knowledgeItem.isOpen === 'Y' ? 'N' : 'Y'
const res = await fetchEnableKnowledgeInfo(knowledgeId, isOpen)
if (res.code === 0) {
knowledgeItem.isOpen = isOpen
await handleGetKnowledgeList()
}
}
function handleViewDocumentList(knowledgeId: number) {
router.push({
name: 'KnowledgeDocument',
params: { id: knowledgeId },
})
}
async function handleDeleteKnowledge(knowledgeId: number) {
const res = await fetchDelKnowledgeById(knowledgeId)
if (res.code === 0) {
window.$message.success(t('common_module.delete_success_message'))
await handleGetKnowledgeList()
}
}
function handleSearchKnowledge() {
pagingInfo.value.pageNo = 1
handleGetKnowledgeList()
}
async function handleGetKnowledgeListUpdatePageNo(pageNo: number) {
pagingInfo.value.pageNo = pageNo
await handleGetKnowledgeList()
}
async function handleGetKnowledgeListUpdatePageSize(pageSize: number) {
pagingInfo.value.pageNo = 1
pagingInfo.value.pageSize = pageSize
await handleGetKnowledgeList()
}
</script>
<template>
<div ref="pageContentWrapRef" class="h-full">
<div class="mb-4 flex justify-end">
<NInput
v-model:value="searchKnowledgeInputValue"
:placeholder="t('personal_space_module.knowledge_module.search_knowledge_placeholder')"
class="w-[256px]!"
@keyup.enter="handleSearchKnowledge"
>
<template #suffix>
<Search theme="outline" size="16" fill="#999" class="cursor-pointer" @click="handleSearchKnowledge" />
</template>
</NInput>
</div>
<div class="mb-4" :style="{ height: tableContentY + 48 + 'px' }">
<NDataTable
:loading="knowledgeListTableLoading"
:bordered="true"
:bottom-bordered="true"
:single-line="false"
:data="knowledgeList"
:columns="knowledgeColumns"
:max-height="tableContentY"
:scroll-x="1100"
>
<template #empty>
<div :style="{ height: tableContentY + 'px' }" class="flex items-center justify-center">
<div class="flex flex-col items-center justify-center">
<div
class="mb-5 h-[68px] w-[68px] bg-contain"
:class="
isSearchEmptyList
? `bg-[url('@/assets/images/search-empty-list.png')]`
: `bg-[url('@/assets/images/empty-list.png')]`
"
/>
<p class="text-gray-font-color select-none">{{ emptyTableDataText }}</p>
</div>
</div>
</template>
</NDataTable>
</div>
<footer v-show="isLoadingPagination" class="flex justify-end">
<CustomPagination
:paging-info="pagingInfo"
@update-page-no="handleGetKnowledgeListUpdatePageNo"
@update-page-size="handleGetKnowledgeListUpdatePageSize"
/>
</footer>
</div>
</template>
<script setup lang="ts">
import { onMounted, ref } from 'vue'
import { useRouter } from 'vue-router'
import { useI18n } from 'vue-i18n'
import { Left } from '@icon-park/vue-next'
import UploadLocalDocument from './upload-local-document/index.vue'
import { fetchGetKnowledgeDetail, fetchUpdateKnowledgeInfo } from '@/apis/knowledge'
import EditKnowledgeModal, { KnowledgeFormDataInterface } from '../components/edit-knowledge-modal.vue'
const { t } = useI18n()
const router = useRouter()
const currentKnowledgeId = ref(0)
const uploadKnowledgeType = ref('')
const isShowEditKnowledgeModal = ref(false)
const updateKnowledgeInfoBtnLoading = ref(false)
const currentKnowledgeData = ref<KnowledgeFormDataInterface>({
knowledgeName: '',
desc: '',
})
onMounted(async () => {
if (!router.currentRoute.value.params.id) {
window.$message.warning(t('personal_space_module.knowledge_module.not_find_knowledge_message'))
router.replace({ name: 'PersonalSpaceKnowledge' })
return
}
uploadKnowledgeType.value = router.currentRoute.value.params.type as string
currentKnowledgeId.value = Number(router.currentRoute.value.params.id)
await handleGetKnowledgeDetail()
})
async function handleGetKnowledgeDetail() {
const res = await fetchGetKnowledgeDetail<KnowledgeFormDataInterface>(currentKnowledgeId.value)
currentKnowledgeData.value = res.data
}
function handleBackKnowledgeList() {
router.back()
}
function handleShowEditKnowledgeModal() {
isShowEditKnowledgeModal.value = true
}
async function handleUpdateKnowledgeInfo(knowledgeData: KnowledgeFormDataInterface) {
updateKnowledgeInfoBtnLoading.value = true
const res = await fetchUpdateKnowledgeInfo<KnowledgeFormDataInterface>({
id: currentKnowledgeId.value,
knowledgeName: knowledgeData.knowledgeName,
desc: knowledgeData.desc,
}).finally(() => (updateKnowledgeInfoBtnLoading.value = false))
if (res.code === 0) {
window.$message.success(t('common_module.save_success_message'))
currentKnowledgeData.value = res.data
isShowEditKnowledgeModal.value = false
}
}
</script>
<template>
<div class="flex h-full w-full flex-col overflow-hidden">
<div class="my-3 flex h-8 items-center">
<Left theme="outline" size="20" fill="#333" class="cursor-pointer" @click="handleBackKnowledgeList" />
<div class="flex items-center">
<span class="text-font-color ml-[2px] h-8 select-none text-lg leading-[30px]">
{{ currentKnowledgeData.knowledgeName }}
</span>
<i
class="iconfont icon-edit text-gray-font-color ml-[5px] cursor-pointer text-[15px] leading-none"
@click="handleShowEditKnowledgeModal"
/>
</div>
</div>
<UploadLocalDocument v-if="uploadKnowledgeType === 'text-local-document'" />
<EditKnowledgeModal
v-model:is-show-modal="isShowEditKnowledgeModal"
:btn-loading="updateKnowledgeInfoBtnLoading"
:knowledge-data="currentKnowledgeData"
@confirm="handleUpdateKnowledgeInfo"
/>
</div>
</template>
<script setup lang="ts">
import { computed, onMounted, ref, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import { useIntervalFn } from '@vueuse/core'
import { fetchGetKnowledgeDocumentListByKdIds } from '@/apis/knowledge'
interface Props {
kdIds: number[]
}
interface Emit {
(e: 'confirm'): void
}
enum TrainStatus {
UNOPENED = 'Unopened',
LINE = 'Line',
TRAINING = 'Training',
COMPLETE = 'Complete',
FAIL = 'Fail',
}
interface TrainFileItem {
kdId: number
documentName: string
documentUrl: string
trainStatus: TrainStatus
knowledgeId: string
documentSize: string
}
const { t } = useI18n()
const props = defineProps<Props>()
const emit = defineEmits<Emit>()
const trainFileList = ref<TrainFileItem[]>([])
onMounted(() => {
!!props.kdIds.length && handleGetKnowledgeDocumentList()
})
const trainFileText = computed(() => (status: TrainStatus) => {
switch (status) {
case TrainStatus.COMPLETE:
return 'personal_space_module.knowledge_module.upload_document_module.process_success'
case TrainStatus.FAIL:
return 'personal_space_module.knowledge_module.upload_document_module.process_fail'
default:
return 'personal_space_module.knowledge_module.upload_document_module.processed'
}
})
const trainFileFontColor = computed(() => (status: TrainStatus) => {
switch (status) {
case TrainStatus.COMPLETE:
return '#0B7DFF'
case TrainStatus.FAIL:
return '#F25744'
default:
return '#333333'
}
})
const trainFileIcon = computed(() => (documentUrl: string) => {
const type = documentUrl.substring(documentUrl.lastIndexOf('.') + 1)
return `https://gsst-poe-sit.gz.bcebos.com/icon/${type}.png`
})
const isTrainFileListFinish = computed(() => {
return trainFileList.value.every((trainFileItem) => {
return [TrainStatus.FAIL, TrainStatus.COMPLETE].includes(trainFileItem.trainStatus)
})
})
watch(
() => isTrainFileListFinish.value,
(newVal) => {
newVal ? pauseIntervalFn() : resumeIntervalFn()
},
{ deep: true },
)
const { pause: pauseIntervalFn, resume: resumeIntervalFn } = useIntervalFn(
async () => {
handleGetKnowledgeDocumentList()
},
2000,
{ immediateCallback: false, immediate: false },
)
async function handleGetKnowledgeDocumentList() {
const res = await fetchGetKnowledgeDocumentListByKdIds<TrainFileItem[]>(props.kdIds)
if (res.code === 0) {
trainFileList.value = res.data
}
}
function handleConfirm() {
emit('confirm')
}
</script>
<template>
<ul class="grid gap-[18px]">
<li
v-for="trainFileItem in trainFileList"
:key="trainFileItem.kdId"
class="border-inactive-border-color relative h-[90px] select-none overflow-hidden rounded-[10px] border bg-white p-6"
>
<div class="flex h-full items-center justify-between">
<div class="flex items-center">
<img :src="trainFileIcon(trainFileItem.documentUrl)" class="mr-[15px] h-9 w-9" />
<div class="flex h-9 flex-col justify-between">
<span class="text-font-color line-clamp-1 break-all text-sm leading-[14px]">
{{ trainFileItem.documentName }}
</span>
<span class="text-gray-font-color line-clamp-1 break-all text-[13px] leading-[13px]">
{{ trainFileItem.documentSize }}
</span>
</div>
</div>
<span :style="{ color: trainFileFontColor(trainFileItem.trainStatus) }">
{{ t(trainFileText(trainFileItem.trainStatus)) }}
</span>
</div>
<div
v-show="![TrainStatus.COMPLETE, TrainStatus.FAIL].includes(trainFileItem.trainStatus)"
class="animate-training absolute left-0 top-[-1px] h-[90px] w-full bg-gradient-to-r from-transparent via-[#9EA0FF] to-transparent"
/>
</li>
</ul>
<div class="mt-14 flex items-center justify-end">
<span class="text-gray-font-color mr-2.5 select-none text-xs">
{{ t('personal_space_module.knowledge_module.upload_document_module.data_process_tip_message') }}
</span>
<NButton type="primary" :bordered="false" @click="handleConfirm">
{{ t('common_module.confirm_btn_text') }}
</NButton>
</div>
</template>
<script lang="ts" setup>
import { onMounted, ref } from 'vue'
import { useRouter } from 'vue-router'
import { useI18n } from 'vue-i18n'
import CustomIcon from '@/components/custom-icon/custom-icon.vue'
import UploadFile from './upload-file.vue'
import SegmentSetting from './segment-setting.vue'
import DataProcess from './data-process.vue'
import { fetchTrainKnowledge } from '@/apis/knowledge'
interface StepItem {
stepId: number
title: string
}
const { t } = useI18n()
const router = useRouter()
const currentKnowledgeId = ref(0)
const currentStepId = ref(1) // 当前步骤Id
const currentKdIdList = ref<number[]>([])
const stepList = ref<StepItem[]>([
{
stepId: 1,
title: 'common_module.upload',
},
{
stepId: 2,
title: 'personal_space_module.knowledge_module.upload_document_module.segment_setting',
},
{
stepId: 3,
title: 'personal_space_module.knowledge_module.upload_document_module.data_process',
},
])
onMounted(() => {
if (!router.currentRoute.value.params.id) {
window.$message.warning('知识库不存在')
router.replace({ name: 'PersonalSpaceKnowledge' })
return
}
currentKnowledgeId.value = Number(router.currentRoute.value.params.id)
})
function handleToUploadFile() {
currentStepId.value = 1
}
function handleToSegmentSetting(kdId: number[]) {
currentStepId.value = 2
currentKdIdList.value = kdId
}
async function handleToDataProcess() {
const res = await fetchTrainKnowledge({
knowledgeInfoId: currentKnowledgeId.value,
kdIds: currentKdIdList.value,
segmentationConfig: {
segmentationType: 'DEFAULT',
chunkSize: 300,
scrapSize: 80,
repetitionRate: 5,
relationInfo: [''],
regex: '',
punctuations: [''],
},
})
if (res.code === 0) {
currentStepId.value = 3
}
}
function handleToKnowledgeList() {
router.replace({ name: 'KnowledgeDocument', params: { id: currentKnowledgeId.value } })
}
</script>
<template>
<div class="flex flex-1 flex-col overflow-hidden">
<ul class="mx-auto my-10 flex w-[870px] shrink-0 duration-500 ease-in">
<li
v-for="(stepItem, index) in stepList"
:key="stepItem.stepId"
class="relative flex flex-1 shrink-0 flex-col items-center justify-center"
>
<div
v-show="stepItem.stepId !== 1"
class="absolute left-0 top-[24px] w-[calc((100%-48px)/2)] border-t"
:class="[currentStepId >= stepItem.stepId ? 'border-[#9EA3FF]' : 'border-inactive-border-color']"
/>
<div class="flex flex-col items-center justify-center">
<div
class="relative mb-3 flex h-12 w-12 items-center justify-center rounded-full border text-lg"
:class="{
'border-[#6F77FF] bg-[#6F77FF]': stepItem.stepId === currentStepId,
'border-inactive-border-color text-gray-font-color': stepItem.stepId > currentStepId,
'bg-[#9ea3ff]': stepItem.stepId < currentStepId,
}"
>
<span
v-show="currentStepId <= stepItem.stepId"
class="select-none text-xl"
:class="stepItem.stepId === currentStepId ? 'text-white' : 'text-gray-font-color'"
>
{{ index + 1 }}
</span>
<CustomIcon
v-show="currentStepId > stepItem.stepId"
icon="mingcute:check-fill"
class="text-[22px] text-white"
/>
</div>
<span
class="text-base"
:class="[currentStepId === stepItem.stepId ? 'text-theme-color' : 'text-gray-font-color']"
>
{{ t(stepItem.title) }}
</span>
</div>
<div
v-show="index < stepList.length - 1"
class="absolute right-0 top-[24px] w-[calc((100%-48px)/2)] border-t"
:class="[currentStepId > stepItem.stepId ? 'border-[#9EA3FF]' : 'border-inactive-border-color']"
/>
</li>
</ul>
<div class="mx-10 flex-1 overflow-hidden">
<NScrollbar class="max-h-full! px-10">
<div v-show="currentStepId === 1">
<UploadFile @next="handleToSegmentSetting" />
</div>
<div v-show="currentStepId === 2">
<SegmentSetting @prev="handleToUploadFile" @next="handleToDataProcess" />
</div>
<div v-if="currentStepId === 3">
<DataProcess :kd-ids="currentKdIdList" @confirm="handleToKnowledgeList" />
</div>
</NScrollbar>
</div>
</div>
</template>
<script setup lang="ts">
import { useI18n } from 'vue-i18n'
interface Emit {
(e: 'prev'): void
(e: 'next'): void
}
const { t } = useI18n()
const emit = defineEmits<Emit>()
function handlePrev() {
emit('prev')
}
function handleNext() {
emit('next')
}
</script>
<template>
<div>
<div>
<div
class="border-theme-color bg-active-color flex h-[90px] w-full cursor-pointer flex-col justify-between rounded-[10px] border px-6 py-5"
>
<span class="text-font-color">
{{ t('personal_space_module.knowledge_module.upload_document_module.default_segment_setting_title') }}
</span>
<span class="text-gray-font-color text-[13px]">
{{ t('personal_space_module.knowledge_module.upload_document_module.default_segment_setting_desc') }}
</span>
</div>
</div>
<div class="mt-14 flex justify-end">
<NButton @click="handlePrev">{{ t('common_module.prev_btn_text') }}</NButton>
<NButton type="primary" class="ml-5!" :bordered="false" @click="handleNext">
{{ t('common_module.next_btn_text') }}
</NButton>
</div>
</div>
</template>
<script setup lang="ts">
import { computed, ref } from 'vue'
import { UploadFileInfo } from 'naive-ui'
import { useI18n } from 'vue-i18n'
import { UploadOne } from '@icon-park/vue-next'
import CustomIcon from '@/components/custom-icon/custom-icon.vue'
import { fetchUploadKnowledgeDocument } from '@/apis/knowledge'
interface Emit {
(e: 'next', value: number[]): void
}
enum FileItemStatus {
PENDING = 'pending',
UPLOADING = 'uploading',
FINISHED = 'finished',
REMOVED = 'removed',
ERROR = 'error',
}
interface FileItem {
id: string
name: string
size: number
status: FileItemStatus
kdId: number
type?: string
}
const { t } = useI18n()
const emit = defineEmits<Emit>()
const uploadFileList = ref<FileItem[]>([])
const uploadFileIcon = (type: string) => {
return `https://gsst-poe-sit.gz.bcebos.com/icon/${type}.png`
}
const isDisabledNextBtn = computed(() => {
return (
uploadFileList.value.length > 0 && uploadFileList.value.every((item) => item.status === FileItemStatus.FINISHED)
)
})
const uploadFileSize = computed(() => (fileSize: number) => {
if (fileSize == 0) return '0B'
const binarySize = 1024
const sizes = ['B', 'KB', 'MB', 'GB', 'TB']
const unit = Math.floor(Math.log(fileSize) / Math.log(binarySize))
return (fileSize / Math.pow(binarySize, unit)).toPrecision(3) + ' ' + sizes[unit]
})
// 上传文件前限制
function handleLimitUpload(data: { file: UploadFileInfo }) {
const allowTypeList = ['md', 'doc', 'docx', 'pdf', 'txt']
if (data.file.file && !allowTypeList.includes(data.file.file?.name.split('.')[1].toLowerCase())) {
window.$message.error(
t('personal_space_module.knowledge_module.upload_document_module.upload_format_error_message'),
)
return false
}
if (data.file.file && data.file.file?.size === 0) {
window.$message.error(
t('personal_space_module.knowledge_module.upload_document_module.empty_document_content_message'),
)
const fileData = {
id: data.file.id,
name: data.file.name,
status: FileItemStatus.ERROR,
size: data.file.file?.size || 0,
type: data.file.file?.name.split('.')[1].toLowerCase(),
kdId: 0,
}
uploadFileList.value.push(fileData)
return false
}
if (data.file.file && data.file.file?.size > 5 * 1024 * 1024) {
window.$message.error(t('personal_space_module.knowledge_module.upload_document_module.upload_size_error_message'))
const fileData = {
id: data.file.id,
name: data.file.name,
status: FileItemStatus.ERROR,
size: data.file.file?.size || 0,
type: data.file.file?.name.split('.')[1].toLowerCase(),
kdId: 0,
}
uploadFileList.value.push(fileData)
return false
}
return true
}
// 上传文件
async function handleUpload(file: any) {
if (file.event) {
const formData = new FormData()
formData.append('documentFiles', file.file.file)
const fileData = {
id: file.file.id,
name: file.file.name,
status: FileItemStatus.UPLOADING,
size: file.file?.file?.size || 0,
type: file.file?.name.split('.')[1].toLowerCase(),
kdId: 0,
}
uploadFileList.value.push(fileData)
fetchUploadKnowledgeDocument<{ kdId: number }[]>(formData)
.then((res) => {
if (res.code === 0) {
uploadFileList.value.forEach((fileItem) => {
if (fileItem.id === file.file.id) {
fileItem.status = FileItemStatus.FINISHED
fileItem.kdId = res.data?.[0]?.kdId
}
})
}
})
.catch(() => {
uploadFileList.value.forEach((fileItem) => {
if (fileItem.id === file.file.id) {
fileItem.status = FileItemStatus.ERROR
}
})
})
}
}
function handleRemoveFile(id: string) {
uploadFileList.value = uploadFileList.value.filter((item) => item.id !== id)
}
function handleNextStep() {
const kdIds = uploadFileList.value.map((item) => item.kdId)
emit('next', kdIds)
}
</script>
<template>
<div class="w-full">
<NUpload
multiple
directory-dnd
:show-file-list="false"
:disabled="uploadFileList.length >= 5"
accept=".doc, .pdf, .docx, .txt, .md"
@before-upload="handleLimitUpload"
@change="handleUpload"
>
<NUploadDragger>
<div class="mb-3 flex justify-center">
<UploadOne theme="outline" size="36" fill="#333" />
</div>
<NText class="text-base">
{{ t('personal_space_module.knowledge_module.upload_document_module.upload_action_tip_message') }}
</NText>
<p class="text-gray-font-color mt-3 text-sm">
{{ t('personal_space_module.knowledge_module.upload_document_module.upload_limit_tip_message') }}
</p>
</NUploadDragger>
</NUpload>
<div class="mt-[18px]">
<ul class="grid grid-cols-2 gap-4">
<li
v-for="uploadFileItem in uploadFileList"
:key="uploadFileItem.id"
class="group relative h-[92px] w-full overflow-hidden rounded-[10px] border bg-white p-6"
:class="uploadFileItem.status === 'error' ? 'border-error-font-color' : 'border-transparent'"
>
<div class="flex items-center justify-between">
<div class="flex items-center">
<img :src="uploadFileIcon(uploadFileItem.type!)" class="h-9 w-9" />
<div class="mx-4 flex flex-col">
<NPopover trigger="hover">
<template #trigger>
<span class="text-font-color mb-2 line-clamp-1 select-none break-all text-base leading-4">
{{ uploadFileItem.name }}
</span>
</template>
<span>{{ uploadFileItem.name }}</span>
</NPopover>
<div
v-if="uploadFileItem.status === FileItemStatus.FINISHED"
class="text-gray-font-color line-clamp-1 select-none break-all"
>
<span class="inline-block group-hover:hidden">{{ uploadFileSize(uploadFileItem.size) }}</span>
<span class="hidden group-hover:inline-block">{{ t('common_module.upload_success_message') }}</span>
</div>
<div
v-else-if="uploadFileItem.status === FileItemStatus.UPLOADING"
class="text-gray-font-color line-clamp-1 select-none break-all"
>
<span>{{ t('common_module.uploading') }}</span>
</div>
<span v-else class="text-error-font-color line-clamp-1 select-none break-all">
{{ t('personal_space_module.knowledge_module.upload_document_module.upload_error_message') }}
</span>
</div>
</div>
<div
v-show="[FileItemStatus.FINISHED, FileItemStatus.ERROR].includes(uploadFileItem.status)"
class="hidden group-hover:block"
>
<NPopover trigger="hover">
<template #trigger>
<CustomIcon
icon="material-symbols:close"
class="cursor-pointer text-base outline-none hover:opacity-80"
:class="
uploadFileItem.status === FileItemStatus.ERROR ? 'text-error-font-color' : 'text-font-color'
"
@click="handleRemoveFile(uploadFileItem.id)"
/>
</template>
<span>{{ t('common_module.delete') }}</span>
</NPopover>
</div>
</div>
<div
v-show="[FileItemStatus.UPLOADING].includes(uploadFileItem.status)"
class="animate-training absolute left-0 top-[-1px] h-[92px] w-full bg-gradient-to-r from-transparent via-[#9EA0FF] to-transparent"
/>
</li>
</ul>
</div>
<div class="mt-14 flex justify-end">
<NButton type="primary" :disabled="!isDisabledNextBtn" :bordered="false" @click="handleNextStep">
{{ t('common_module.next_btn_text') }}
</NButton>
</div>
</div>
</template>
...@@ -2,11 +2,11 @@ ...@@ -2,11 +2,11 @@
import { h, readonly, ref, watch } from 'vue' import { h, readonly, ref, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router' import { useRoute, useRouter } from 'vue-router'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { Plus, ApplicationTwo } from '@icon-park/vue-next' import { Plus, ApplicationTwo, NotebookOne } from '@icon-park/vue-next'
// import CreateKnowledgeModal, { import CreateKnowledgeModal, {
// KnowledgeFormDataInterface, KnowledgeFormDataInterface,
// } from './personal-knowledge/components/create-knowledge-modal.vue' } from './personal-knowledge/components/create-knowledge-modal.vue'
// import { fetchCreateKnowledge } from '@/apis/knowledge' import { fetchCreateKnowledge } from '@/apis/knowledge'
const { t } = useI18n() const { t } = useI18n()
...@@ -20,10 +20,10 @@ const personalSpaceModuleList = [ ...@@ -20,10 +20,10 @@ const personalSpaceModuleList = [
routeName: 'PersonalSpaceApp', routeName: 'PersonalSpaceApp',
label: 'common_module.agent', label: 'common_module.agent',
}, },
// { {
// routeName: 'PersonalSpaceKnowledge', routeName: 'PersonalSpaceKnowledge',
// label: 'common_module.knowledge', label: 'common_module.knowledge',
// }, },
] ]
const addPersonalSpaceOptions = readonly([ const addPersonalSpaceOptions = readonly([
...@@ -33,15 +33,15 @@ const addPersonalSpaceOptions = readonly([ ...@@ -33,15 +33,15 @@ const addPersonalSpaceOptions = readonly([
icon: () => h(ApplicationTwo, { theme: 'outline', size: 16, fill: '#333' }), icon: () => h(ApplicationTwo, { theme: 'outline', size: 16, fill: '#333' }),
}, },
// { {
// label: () => h('span', {}, t('common_module.knowledge')), label: () => h('span', {}, t('common_module.knowledge')),
// key: 'addKnowledge', key: 'addKnowledge',
// icon: () => h(NotebookOne, { theme: 'outline', size: 16, fill: '#333' }), icon: () => h(NotebookOne, { theme: 'outline', size: 16, fill: '#333' }),
// }, },
]) ])
const showCreateKnowledgeModal = ref(false) const showCreateKnowledgeModal = ref(false)
// const createKnowledgeBtnLoading = ref(false) const createKnowledgeBtnLoading = ref(false)
watch( watch(
() => currentRoute.fullPath, () => currentRoute.fullPath,
...@@ -65,26 +65,26 @@ function handleSelectAddType(type: string) { ...@@ -65,26 +65,26 @@ function handleSelectAddType(type: string) {
} }
} }
// async function handleCreateKnowledgeNextStep(createKnowledgeData: KnowledgeFormDataInterface) { async function handleCreateKnowledgeNextStep(createKnowledgeData: KnowledgeFormDataInterface) {
// createKnowledgeBtnLoading.value = true createKnowledgeBtnLoading.value = true
// const res = await fetchCreateKnowledge<{ id: number }>({ const res = await fetchCreateKnowledge<{ id: number }>({
// knowledgeName: createKnowledgeData.knowledgeName, knowledgeName: createKnowledgeData.knowledgeName,
// desc: createKnowledgeData.knowledgeDesc, desc: createKnowledgeData.knowledgeDesc,
// }) })
// if (res.code === 0) { if (res.code === 0) {
// router.push({ router.push({
// name: 'UploadKnowledge', name: 'UploadKnowledge',
// params: { params: {
// id: res.data.id, id: res.data.id,
// type: createKnowledgeData.knowledgeType + '-' + createKnowledgeData.knowledgeImportType, type: createKnowledgeData.knowledgeType + '-' + createKnowledgeData.knowledgeImportType,
// }, },
// }) })
// showCreateKnowledgeModal.value = false showCreateKnowledgeModal.value = false
// createKnowledgeBtnLoading.value = false createKnowledgeBtnLoading.value = false
// } }
// } }
</script> </script>
<template> <template>
...@@ -131,11 +131,11 @@ function handleSelectAddType(type: string) { ...@@ -131,11 +131,11 @@ function handleSelectAddType(type: string) {
</RouterView> </RouterView>
</div> </div>
<!-- <CreateKnowledgeModal <CreateKnowledgeModal
v-model:is-show-modal="showCreateKnowledgeModal" v-model:is-show-modal="showCreateKnowledgeModal"
:btn-loading="createKnowledgeBtnLoading" :btn-loading="createKnowledgeBtnLoading"
@confirm="handleCreateKnowledgeNextStep" @confirm="handleCreateKnowledgeNextStep"
/> --> />
</div> </div>
</template> </template>
......
...@@ -7,5 +7,8 @@ ...@@ -7,5 +7,8 @@
{ {
"path": "./tsconfig.node.json" "path": "./tsconfig.node.json"
} }
] ],
"compilerOptions": {
"jsx": "preserve",
}
} }
...@@ -6,6 +6,14 @@ declare interface Window { ...@@ -6,6 +6,14 @@ declare interface Window {
$notification: import('naive-ui').NotificationProviderInst $notification: import('naive-ui').NotificationProviderInst
} }
declare namespace JSX {
interface IntrinsicElements {
div: DivElement
span: SpanElement
i: IElement
}
}
type Recordable<T = any> = Record<string, T> type Recordable<T = any> = Record<string, T>
declare type AnyObject = { [k: string]: any } declare type AnyObject = { [k: string]: any }
...@@ -22,6 +22,8 @@ export default defineConfig({ ...@@ -22,6 +22,8 @@ export default defineConfig({
'font-color': '#333333', 'font-color': '#333333',
'gray-font-color': '#999999', 'gray-font-color': '#999999',
'error-font-color': '#F25744', 'error-font-color': '#F25744',
'inactive-border-color': '#CCCCCC',
'background-color': '#F3F5F8',
}, },
height: { height: {
navbar: '56px', navbar: '56px',
...@@ -33,15 +35,18 @@ export default defineConfig({ ...@@ -33,15 +35,18 @@ export default defineConfig({
animation: { animation: {
keyframes: { keyframes: {
'card-reverse': `{ 0% { transform: rotateY(0deg); } 100% { transform: rotateY(1turn); } }`, 'card-reverse': `{ 0% { transform: rotateY(0deg); } 100% { transform: rotateY(1turn); } }`,
training: `{ 0% { transform: translateX(-100%) } 100% { transform: translateX(100%); } }`,
}, },
durations: { durations: {
'card-reverse': '1s', 'card-reverse': '1s',
training: '1.2s',
}, },
timingFns: { timingFns: {
'card-reverse': 'ease-in-out', 'card-reverse': 'ease-in-out',
}, },
counts: { counts: {
'card-reverse': '1', 'card-reverse': '1',
training: 'infinite',
}, },
}, },
}, },
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment