Commit 0f954b20 authored by nick zheng's avatar nick zheng

Merge branch 'beta' into 'master'

feat: 插件配置及使用

See merge request !141
parents 336c88db c4f583b1
......@@ -8,7 +8,7 @@
name="viewport"
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
/>
<link rel="stylesheet" href="//at.alicdn.com/t/c/font_4711453_qvvmczfd1pl.css" />
<link rel="stylesheet" href="//at.alicdn.com/t/c/font_4711453_mmmsje9d36.css" />
<title>Model Link</title>
</head>
......
......@@ -37,6 +37,7 @@
"mitt": "^3.0.1",
"nanoid": "^5.0.7",
"pinia": "^2.2.2",
"qs": "^6.14.0",
"spark-md5": "^3.0.2",
"type-fest": "^4.26.1",
"validator": "^13.12.0",
......@@ -52,6 +53,7 @@
"@types/howler": "^2.2.12",
"@types/lodash-es": "^4.17.12",
"@types/node": "^20.16.5",
"@types/qs": "^6.9.18",
"@types/spark-md5": "^3.0.4",
"@types/validator": "^13.12.2",
"@typescript-eslint/parser": "^7.18.0",
......
......@@ -71,6 +71,9 @@ importers:
pinia:
specifier: ^2.2.2
version: 2.2.2(typescript@5.6.2)(vue@3.5.13(typescript@5.6.2))
qs:
specifier: ^6.14.0
version: 6.14.0
spark-md5:
specifier: ^3.0.2
version: 3.0.2
......@@ -111,6 +114,9 @@ importers:
'@types/node':
specifier: ^20.16.5
version: 20.16.5
'@types/qs':
specifier: ^6.9.18
version: 6.9.18
'@types/spark-md5':
specifier: ^3.0.4
version: 3.0.4
......@@ -119,10 +125,10 @@ importers:
version: 13.12.2
'@typescript-eslint/parser':
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':
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':
specifier: ^4.6.2
version: 4.6.2(vite@5.4.6(@types/node@20.16.5)(sass@1.79.1))(vue@3.5.13(typescript@5.6.2))
......@@ -134,16 +140,16 @@ importers:
version: 10.4.20(postcss@8.4.47)
eslint:
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:
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:
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:
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:
specifier: ^15.9.0
version: 15.9.0
......@@ -200,7 +206,7 @@ importers:
version: 5.6.2
typescript-eslint:
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:
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))
......@@ -215,13 +221,13 @@ importers:
version: 5.4.6(@types/node@20.16.5)(sass@1.79.1)
vite-plugin-checker:
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))
vite-svg-loader:
specifier: ^5.1.0
version: 5.1.0(vue@3.5.13(typescript@5.6.2))
vue-eslint-parser:
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:
specifier: ^2.0.29
version: 2.0.29(typescript@5.6.2)
......@@ -1039,6 +1045,9 @@ packages:
'@types/node@20.16.5':
resolution: {integrity: sha512-VwYCweNo3ERajwy0IUlqqcyZ8/A7Zwa9ZP3MnENWcB11AejO+tLy3pu850goUW2FC/IJMdZUfKpX/yxL1gymCA==}
'@types/qs@6.9.18':
resolution: {integrity: sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA==}
'@types/spark-md5@3.0.4':
resolution: {integrity: sha512-qtOaDz+IXiNndPgYb6t1YoutnGvFRtWSNzpVjkAPCfB2UzTyybuD4Tjgs7VgRawum3JnJNRwNQd4N//SvrHg1Q==}
......@@ -1429,6 +1438,14 @@ packages:
resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==}
engines: {node: '>=8'}
call-bind-apply-helpers@1.0.1:
resolution: {integrity: sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==}
engines: {node: '>= 0.4'}
call-bound@1.0.3:
resolution: {integrity: sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==}
engines: {node: '>= 0.4'}
callsites@3.1.0:
resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
engines: {node: '>=6'}
......@@ -1669,6 +1686,10 @@ packages:
resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==}
engines: {node: '>=8'}
dunder-proto@1.0.1:
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
engines: {node: '>= 0.4'}
duplexer@0.1.2:
resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==}
......@@ -1699,6 +1720,18 @@ packages:
error-ex@1.3.2:
resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==}
es-define-property@1.0.1:
resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==}
engines: {node: '>= 0.4'}
es-errors@1.3.0:
resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
engines: {node: '>= 0.4'}
es-object-atoms@1.1.1:
resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
engines: {node: '>= 0.4'}
esbuild@0.21.5:
resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==}
engines: {node: '>=12'}
......@@ -1924,6 +1957,14 @@ packages:
resolution: {integrity: sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA==}
engines: {node: '>=18'}
get-intrinsic@1.2.7:
resolution: {integrity: sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==}
engines: {node: '>= 0.4'}
get-proto@1.0.1:
resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==}
engines: {node: '>= 0.4'}
get-stream@8.0.1:
resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==}
engines: {node: '>=16'}
......@@ -1983,6 +2024,10 @@ packages:
globjoin@0.1.4:
resolution: {integrity: sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==}
gopd@1.2.0:
resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
engines: {node: '>= 0.4'}
graceful-fs@4.2.11:
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
......@@ -2001,6 +2046,10 @@ packages:
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
engines: {node: '>=8'}
has-symbols@1.1.0:
resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==}
engines: {node: '>= 0.4'}
hasown@2.0.2:
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
engines: {node: '>= 0.4'}
......@@ -2311,6 +2360,10 @@ packages:
engines: {node: '>= 18'}
hasBin: true
math-intrinsics@1.1.0:
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
engines: {node: '>= 0.4'}
mathml-tag-names@2.1.3:
resolution: {integrity: sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==}
......@@ -2429,6 +2482,10 @@ packages:
nth-check@2.1.1:
resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
object-inspect@1.13.3:
resolution: {integrity: sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==}
engines: {node: '>= 0.4'}
ofetch@1.3.4:
resolution: {integrity: sha512-KLIET85ik3vhEfS+3fDlc/BAZiAp+43QEC/yCo5zkNoY2YaKvNkOaFr/6wCFgFH1kuYQM5pMNi0Tg8koiIemtw==}
......@@ -2660,6 +2717,10 @@ packages:
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
engines: {node: '>=6'}
qs@6.14.0:
resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==}
engines: {node: '>=0.6'}
queue-microtask@1.2.3:
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
......@@ -2754,6 +2815,22 @@ packages:
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
engines: {node: '>=8'}
side-channel-list@1.0.0:
resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==}
engines: {node: '>= 0.4'}
side-channel-map@1.0.1:
resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==}
engines: {node: '>= 0.4'}
side-channel-weakmap@1.0.2:
resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==}
engines: {node: '>= 0.4'}
side-channel@1.1.0:
resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==}
engines: {node: '>= 0.4'}
signal-exit@4.1.0:
resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
engines: {node: '>=14'}
......@@ -3822,9 +3899,9 @@ snapshots:
'@esbuild/win32-x64@0.23.1':
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:
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-community/regexpp@4.11.1': {}
......@@ -4048,21 +4125,23 @@ snapshots:
dependencies:
undici-types: 6.19.8
'@types/qs@6.9.18': {}
'@types/spark-md5@3.0.4': {}
'@types/validator@13.12.2': {}
'@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:
'@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/type-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@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@2.0.0-beta.3))(typescript@5.6.2)
'@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
ignore: 5.3.2
natural-compare: 1.4.0
......@@ -4072,14 +4151,14 @@ snapshots:
transitivePeerDependencies:
- 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:
'@typescript-eslint/scope-manager': 7.18.0
'@typescript-eslint/types': 7.18.0
'@typescript-eslint/typescript-estree': 7.18.0(typescript@5.6.2)
'@typescript-eslint/visitor-keys': 7.18.0
debug: 4.3.7
eslint: 9.10.0(jiti@1.21.6)
eslint: 9.10.0(jiti@2.0.0-beta.3)
optionalDependencies:
typescript: 5.6.2
transitivePeerDependencies:
......@@ -4090,12 +4169,12 @@ snapshots:
'@typescript-eslint/types': 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:
'@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
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)
optionalDependencies:
typescript: 5.6.2
......@@ -4119,13 +4198,13 @@ snapshots:
transitivePeerDependencies:
- 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:
'@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/types': 7.18.0
'@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:
- supports-color
- typescript
......@@ -4174,17 +4253,17 @@ snapshots:
'@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:
'@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:
- eslint
- supports-color
- 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:
'@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/core': 0.61.9
magic-string: 0.30.11
......@@ -4616,6 +4695,16 @@ snapshots:
cac@6.7.14: {}
call-bind-apply-helpers@1.0.1:
dependencies:
es-errors: 1.3.0
function-bind: 1.1.2
call-bound@1.0.3:
dependencies:
call-bind-apply-helpers: 1.0.1
get-intrinsic: 1.2.7
callsites@3.1.0: {}
caniuse-lite@1.0.30001660: {}
......@@ -4843,6 +4932,12 @@ snapshots:
dependencies:
is-obj: 2.0.0
dunder-proto@1.0.1:
dependencies:
call-bind-apply-helpers: 1.0.1
es-errors: 1.3.0
gopd: 1.2.0
duplexer@0.1.2: {}
echarts@5.5.1:
......@@ -4866,6 +4961,14 @@ snapshots:
dependencies:
is-arrayish: 0.2.1
es-define-property@1.0.1: {}
es-errors@1.3.0: {}
es-object-atoms@1.1.1:
dependencies:
es-errors: 1.3.0
esbuild@0.21.5:
optionalDependencies:
'@esbuild/aix-ppc64': 0.21.5
......@@ -4935,29 +5038,29 @@ snapshots:
optionalDependencies:
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:
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:
eslint: 9.10.0(jiti@1.21.6)
eslint: 9.10.0(jiti@2.0.0-beta.3)
prettier: 3.3.3
prettier-linter-helpers: 1.0.0
synckit: 0.9.1
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:
'@eslint-community/eslint-utils': 4.4.0(eslint@9.10.0(jiti@1.21.6))
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@2.0.0-beta.3)
globals: 13.24.0
natural-compare: 1.4.0
nth-check: 2.1.1
postcss-selector-parser: 6.1.2
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
transitivePeerDependencies:
- supports-color
......@@ -4976,9 +5079,9 @@ snapshots:
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:
'@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/config-array': 0.18.0
'@eslint/eslintrc': 3.1.0
......@@ -5013,7 +5116,7 @@ snapshots:
strip-ansi: 6.0.1
text-table: 0.2.0
optionalDependencies:
jiti: 1.21.6
jiti: 2.0.0-beta.3
transitivePeerDependencies:
- supports-color
......@@ -5151,6 +5254,24 @@ snapshots:
get-east-asian-width@1.2.0: {}
get-intrinsic@1.2.7:
dependencies:
call-bind-apply-helpers: 1.0.1
es-define-property: 1.0.1
es-errors: 1.3.0
es-object-atoms: 1.1.1
function-bind: 1.1.2
get-proto: 1.0.1
gopd: 1.2.0
has-symbols: 1.1.0
hasown: 2.0.2
math-intrinsics: 1.1.0
get-proto@1.0.1:
dependencies:
dunder-proto: 1.0.1
es-object-atoms: 1.1.1
get-stream@8.0.1: {}
get-tsconfig@4.8.1:
......@@ -5208,6 +5329,8 @@ snapshots:
globjoin@0.1.4: {}
gopd@1.2.0: {}
graceful-fs@4.2.11: {}
graphemer@1.4.0: {}
......@@ -5220,6 +5343,8 @@ snapshots:
has-flag@4.0.0: {}
has-symbols@1.1.0: {}
hasown@2.0.2:
dependencies:
function-bind: 1.1.2
......@@ -5483,6 +5608,8 @@ snapshots:
marked@15.0.0: {}
math-intrinsics@1.1.0: {}
mathml-tag-names@2.1.3: {}
mdn-data@2.0.28: {}
......@@ -5588,6 +5715,8 @@ snapshots:
dependencies:
boolbase: 1.0.0
object-inspect@1.13.3: {}
ofetch@1.3.4:
dependencies:
destr: 2.0.3
......@@ -5748,6 +5877,10 @@ snapshots:
punycode@2.3.1: {}
qs@6.14.0:
dependencies:
side-channel: 1.1.0
queue-microtask@1.2.3: {}
readdirp@3.6.0:
......@@ -5838,6 +5971,34 @@ snapshots:
shebang-regex@3.0.0: {}
side-channel-list@1.0.0:
dependencies:
es-errors: 1.3.0
object-inspect: 1.13.3
side-channel-map@1.0.1:
dependencies:
call-bound: 1.0.3
es-errors: 1.3.0
get-intrinsic: 1.2.7
object-inspect: 1.13.3
side-channel-weakmap@1.0.2:
dependencies:
call-bound: 1.0.3
es-errors: 1.3.0
get-intrinsic: 1.2.7
object-inspect: 1.13.3
side-channel-map: 1.0.1
side-channel@1.1.0:
dependencies:
es-errors: 1.3.0
object-inspect: 1.13.3
side-channel-list: 1.0.0
side-channel-map: 1.0.1
side-channel-weakmap: 1.0.2
signal-exit@4.1.0: {}
sirv@2.0.4:
......@@ -6098,12 +6259,12 @@ snapshots:
type-fest@4.26.1: {}
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:
'@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/parser': 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@1.21.6))(typescript@5.6.2)
eslint: 9.10.0(jiti@1.21.6)
'@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@2.0.0-beta.3))(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@2.0.0-beta.3)
optionalDependencies:
typescript: 5.6.2
transitivePeerDependencies:
......@@ -6237,7 +6398,7 @@ snapshots:
evtd: 0.2.4
vue: 3.5.13(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:
'@babel/code-frame': 7.24.7
ansi-escapes: 4.3.2
......@@ -6255,7 +6416,8 @@ snapshots:
vscode-languageserver-textdocument: 1.0.12
vscode-uri: 3.0.8
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
stylelint: 16.9.0(typescript@5.6.2)
typescript: 5.6.2
......@@ -6308,10 +6470,10 @@ snapshots:
dependencies:
vue: 3.5.13(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:
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-visitor-keys: 3.4.3
espree: 9.6.1
......
import qs from 'qs'
import { request } from '@/utils/request'
export function fetchPluginCategoryKeyList<T>() {
......@@ -5,5 +6,14 @@ export function fetchPluginCategoryKeyList<T>() {
}
export function fetchPluginCategoryDetailInfoList<T>(payload: object) {
return request.post<T>('/bizAgentApplicationPluginRest/getList.json', null, { params: payload })
return request.post<T>(
'/bizAgentApplicationPluginRest/getList.json',
{ pagingInfo: { pageNo: 1, pageSize: 9999 } },
{
params: payload,
paramsSerializer: function (params) {
return qs.stringify(params, { arrayFormat: 'repeat' })
},
},
)
}
......@@ -66,6 +66,12 @@ export function fetchCustomEventSource(config: {
return
}
// 插件调用
if (data.function && data.function.name) {
config.onMessage(data.function)
return
}
config.onMessage(data.message)
} catch (err) {
config.onRequestError(err)
......
import { UploadFileInfo } from 'naive-ui'
import { reactive, ref } from 'vue'
import { UploadStatus } from '@/enums/upload-status'
import { fetchUpload } from '@/apis/upload'
import i18n from '@/locales'
const { t } = i18n.global
interface UploadImageItem {
id: string
url: string
status: 'pending' | 'uploading' | 'finished' | 'error' | 'removed'
}
export function useUploadImage() {
const uploadImageList = ref<UploadImageItem[]>([])
function handleLimitUploadImage(data: { file: UploadFileInfo }) {
if (data.file.file && data.file.file?.size > 1024 * 1024 * 5) {
window.$message.error(t('common_module.dialogue_module.upload_image_size_error_message'))
return false
}
const imageTypeList = ['image/png', 'image/jpeg', 'image/jpg', 'image/webp']
if (data.file && data.file.type && !imageTypeList.includes(data.file.type)) {
window.$message.error(t('common_module.dialogue_module.upload_image_format_error_message'))
return false
}
}
function handleUploadImage(data: { file: UploadFileInfo }) {
uploadImageList.value = []
if (data.file.file) {
const formData = new FormData()
formData.append('file', data.file.file)
const currentUploadImage = reactive({
id: data.file.id,
status: UploadStatus.UPLOADING,
url: '',
})
uploadImageList.value.push(currentUploadImage)
fetchUpload<string>(formData)
.then((res) => {
if (res.code === 0) {
currentUploadImage.status = UploadStatus.FINISHED
currentUploadImage.url = res.data
}
})
.catch(() => {
currentUploadImage.status = UploadStatus.ERROR
})
}
}
function handleRemoveUploadImage(id: string) {
uploadImageList.value = uploadImageList.value.filter((fileItem) => fileItem.id !== id)
}
return {
uploadImageList,
handleLimitUploadImage,
handleUploadImage,
handleRemoveUploadImage,
}
}
......@@ -137,6 +137,9 @@ common_module:
or: 'or'
search_keyword_empty_tip: 'The search content cannot be empty'
added: 'Added'
plugin_in_progress: '{pluginName} Plugin in Progress'
plugin_executed_successfully: '{pluginName} Plugin Executed Successfully'
upload_image: 'Upload Image'
dialogue_module:
continue_question_message: 'You can keep asking questions'
......@@ -151,6 +154,10 @@ common_module:
overwrite_file_tip: 'The newly uploaded file overwrites the original file, whether to continue uploading'
stop_playing_and_then_operate: 'When the audio is playing, stop playing and then perform the operation'
do_not_operate_until_the_reply_is_complete: 'Do not operate until the reply is complete'
upload_image_limit: 'Only a single image can be uploaded in png, jpg, jpeg, webp format, up to 5MB'
overwrite_image_tip: 'The newly uploaded image overwrites the original image, whether to continue uploading'
upload_image_size_error_message: 'The size of the uploaded image cannot exceed 5 MB'
upload_image_format_error_message: 'Only png, jpg, jpeg, webp format images can be uploaded, please upload again'
data_table_module:
action: 'Controls'
......@@ -294,6 +301,11 @@ personal_space_module:
agent_system_input_placeholder: 'Please enter the task objectives that you want the character to complete, the component capabilities that they have, and the requirements and limitations on the output answers'
ability_expand: 'Capacity expansion'
plugin: 'Plugin'
plugin_desc: 'Plugin enable intelligences to call external APIs, expanding their capabilities and usage scenarios'
add_plugin: 'Add Plugin'
add_plugin_successfully: 'Plugin {0} was added successfully'
remove_plugin_successfully: 'Plugin {0} was removed successfully'
knowledge: 'Knowledge'
knowledge_base: 'Knowledge base'
knowledge_base_desc: 'Reference text data, tabular knowledge data (including FAQ questions, multi-column index questions) and web data to achieve knowledge base questions and answers. The application can be associated with a maximum of 5 knowledge bases. Please fill in the detailed description of the knowledge base to improve the accuracy of questions and answers'
......
......@@ -136,6 +136,9 @@ common_module:
or: '或'
search_keyword_empty_tip: '搜索内容不能为空'
added: '已添加'
plugin_in_progress: '{pluginName}插件执行中...'
plugin_executed_successfully: '{pluginName}插件执行成功'
upload_image: '上传图片'
dialogue_module:
continue_question_message: '你可以继续提问'
......@@ -150,6 +153,10 @@ common_module:
overwrite_file_tip: '新上传的文件会覆盖原有文件,是否继续上传'
stop_playing_and_then_operate: '音频播放中,请停止播放后再操作'
do_not_operate_until_the_reply_is_complete: '回复完成后再操作'
upload_image_limit: '仅支持上传一张图片,支持png、jpg、jpeg、webp格式,最大5MB'
overwrite_image_tip: '新上传的图片会覆盖原有图片,是否继续上传'
upload_image_size_error_message: '上传图片大小不能超过5M'
upload_image_format_error_message: '只能上传png、jpg、jpeg、webp格式图片,请重新上传'
data_table_module:
action: '操作'
......@@ -292,6 +299,11 @@ personal_space_module:
agent_system_input_placeholder: '请输入希望角色完成的任务目标、具备的组件能力以及对输出答案的要求与限制等'
ability_expand: '能力扩展'
plugin: '插件'
plugin_desc: '插件能够让智能体调用外部 API,扩展智能体的能力和使用场景'
add_plugin: '添加插件'
add_plugin_successfully: '插件 {0} 添加成功'
remove_plugin_successfully: '插件 {0} 移除成功'
knowledge: '知识'
knowledge_base: '知识库'
knowledge_base_desc: '引用文本数据、表格型知识数据(含FAQ问答,多列索引问答)以及网页数据,实现知识库问答,应用最多可关联5个知识库,请详细填写知识库描述信息以提高问答准确率'
......
......@@ -136,6 +136,9 @@ common_module:
or: '或'
search_keyword_empty_tip: '搜索內容不能為空'
added: '已添加'
plugin_in_progress: '{pluginName}插件執行中...'
plugin_executed_successfully: '{pluginName}插件執行成功'
upload_image: '上傳圖片'
dialogue_module:
continue_question_message: '你可以繼續提問'
......@@ -150,6 +153,10 @@ common_module:
overwrite_file_tip: '新上傳的文件會覆蓋原有文件,是否繼續上傳'
stop_playing_and_then_operate: '音頻播放中,請停止播放後再操作'
do_not_operate_until_the_reply_is_complete: '回覆完成後再操作'
upload_image_limit: '僅支持上傳一張圖片,支持png、jpg、jpeg、webp格式,最大5MB'
overwrite_image_tip: '新上傳的圖片會覆蓋原有圖片,是否繼續上傳'
upload_image_size_error_message: '上傳圖片大小不能超過5M'
upload_image_format_error_message: '只能上傳png、jpg、jpeg、webp格式圖片,請重新上傳'
data_table_module:
action: '操作'
......@@ -292,6 +299,11 @@ personal_space_module:
agent_system_input_placeholder: '請輸入希望角色完成的任務目標、具備的組件能力以及對輸出答案的要求與限制等'
ability_expand: '能力擴展'
plugin: '插件'
plugin_desc: '插件能夠讓智能體調用外部 API,擴展智能體的能力和使用場景'
add_plugin: '添加插件'
add_plugin_successfully: '插件 {0} 添加成功'
remove_plugin_successfully: '插件 {0} 移除成功'
knowledge: '知識'
knowledge_base: '知識庫'
knowledge_base_desc: '引用文本數據、表格型知識數據(含FAQ問答,多列索引問答)以及網頁數據,實現知識庫問答,應用最多可關聯5個知識庫,請詳細填寫知識庫描述信息以提高問答準確率'
......
<script setup lang="ts">
import { computed, nextTick, ref, shallowRef, toValue, useTemplateRef } from 'vue'
import { computed, nextTick, ref, shallowRef, toValue, useTemplateRef, watch } from 'vue'
import type { AgentApplicationRecordItem, MessageItemInterface } from '../types'
import { fetchAgentApplicationSelectList, fetchFileUpload } from '@/apis/home-agent'
import { nanoid } from 'nanoid'
import { CloseSmall } from '@icon-park/vue-next'
import fetchEventStreamSource from '../utils/fetch-event-stream-source'
import { throttle } from 'lodash-es'
import { useI18n } from 'vue-i18n'
import { useUserStore } from '@/store/modules/user'
import { ChannelType } from '@/enums/channel'
import { UploadStatus } from '@/enums/upload-status'
import { useUploadImage } from '@/composables/useUploadImage'
import { useSystemLanguageStore } from '@/store/modules/system-language'
interface Props {
currentSessionId: string
......@@ -36,6 +40,10 @@ const { t } = useI18n()
const inputFileRef = useTemplateRef<HTMLInputElement | null>('inputFileRef')
const userStore = useUserStore()
const systemLanguageStore = useSystemLanguageStore()
const { uploadImageList, handleLimitUploadImage, handleUploadImage, handleRemoveUploadImage } = useUploadImage()
let fileUploadController = shallowRef<AbortController | null>(null)
const isShowApplicationSelectMenu = ref(false)
......@@ -51,9 +59,20 @@ const currentInputFileInfo = ref({
})
const isQuestionSubmitBtnDisabled = computed(() => {
return questionContent.value.trim().length === 0 || isAgentResponding.value || currentInputFileInfo.value.uploading
return (
questionContent.value.trim().length === 0 ||
isAgentResponding.value ||
currentInputFileInfo.value.uploading ||
uploadImageList.value.some((imageItem) => imageItem.status !== UploadStatus.FINISHED)
)
})
const isHasUploadImage = computed(() => {
return uploadImageList.value.length > 0
})
const isEnglishLanguage = computed(() => systemLanguageStore.currentLanguageInfo.key === 'en')
;(function () {
getAgentApplicationSelectList()
})()
......@@ -66,6 +85,15 @@ const messageListScrollToBottomThrottle = throttle(
{ leading: true, trailing: true },
)
watch(
() => uploadImageList.value.length,
() => {
nextTick(() => {
emit('messageListScrollToBottom')
})
},
)
function getAgentApplicationSelectList() {
fetchAgentApplicationSelectList<AgentApplicationRecordItem[]>().then((res) => {
agentApplicationSelectList.value = res.data
......@@ -134,6 +162,7 @@ function questionSubmit() {
isAnswerLoading: false,
avatar: '',
name: '',
imageUrl: uploadImageList.value?.[0]?.url || '',
})
const agentAvatar = currentAgentApplication.value.agentAvatar
......@@ -148,6 +177,7 @@ function questionSubmit() {
isAnswerLoading: true,
avatar: agentAvatar,
name: agentName,
pluginName: '',
})
setTimeout(() => {
......@@ -168,6 +198,7 @@ function questionSubmit() {
input: questionContent.value.trim(), //提问文本
fileUrls: currentInputFileInfo.value.url ? [currentInputFileInfo.value.url] : [],
channel: ChannelType.index,
imageUrl: uploadImageList.value?.[0]?.url || '', // 图片链接
},
{
onmessage: (message) => {
......@@ -179,6 +210,16 @@ function questionSubmit() {
messageListScrollToBottomThrottle()
},
onFunction: (name) => {
emit('updateSpecifyMessageItem', currentLatestMessageItemKeyMap.value.get('assistant')!, {
pluginName: name,
})
nextTick(() => {
emit('messageListScrollToBottom')
})
},
onend: () => {
setTimeout(() => {
emit('updateSpecifyMessageItem', currentLatestMessageItemKeyMap.value.get('assistant')!, {
......@@ -206,13 +247,14 @@ function questionSubmit() {
)
questionContent.value = ''
uploadImageList.value = []
}
function handleQuestionSubmitEnter(event: KeyboardEvent) {
if (event.key === 'Enter' && !event.shiftKey) {
event.preventDefault()
if (questionContent.value.trim().length > 0 && !isAgentResponding.value && !currentInputFileInfo.value.uploading) {
if (!isQuestionSubmitBtnDisabled.value) {
questionSubmit()
}
}
......@@ -292,6 +334,17 @@ function handleFileUploadReplace() {
handleFileUploadPopup()
}
function handleSelectImage(cb: () => void) {
if (uploadImageList.value.length > 0) {
window.$message.ctWarning('', t('common_module.dialogue_module.overwrite_image_tip')).then(() => {
cb()
})
return
}
cb()
}
defineExpose({
clearSessionReferenceFile: handleFileUploadCancel,
})
......@@ -411,7 +464,7 @@ defineExpose({
class="mr-[5px] h-[18px] w-[18px] bg-[url('https://gsst-poe-sit.gz.bcebos.com/icon/doc.svg')] bg-contain bg-no-repeat"
></div>
<div class="w-[260px] text-[14px]">
<div class="text-[14px]" :class="isEnglishLanguage ? 'w-[110px]' : 'w-[210px]'">
<n-ellipsis :tooltip="{ width: 400 }">
{{ currentInputFileInfo.fileName }}
</n-ellipsis>
......@@ -443,6 +496,32 @@ defineExpose({
</div>
</Transition>
<n-upload
:show-file-list="false"
accept="image/png, image/jpeg, image/jpg, image/webp"
abstract
@before-upload="handleLimitUploadImage"
@change="handleUploadImage"
>
<n-upload-trigger #="{ handleClick }" abstract>
<n-popover style="width: 210px" trigger="hover">
<template #trigger>
<n-button
class="upload-image-btn !mr-[14px] !h-[34px] !rounded-[10px] !p-0"
@click="handleSelectImage(handleClick)"
>
<div class="box-border flex w-full items-center justify-between px-[12px]">
<i class="iconfont icon-upload-image mr-[5px] text-[14px]"></i>
<span class="text-[14px]">{{ t('common_module.upload_image') }}</span>
</div>
</n-button>
</template>
<span>{{ t('common_module.dialogue_module.upload_image_limit') }}</span>
</n-popover>
</n-upload-trigger>
</n-upload>
<n-button class="application-select-btn !h-[34px] !rounded-[10px] !p-0" @click="handleCreateNewSession">
<div class="box-border flex w-full items-center justify-between px-[12px]">
<i class="iconfont icon-session mr-[5px] text-[14px]"></i>
......@@ -460,23 +539,53 @@ defineExpose({
</div>
</div>
<div>
<div class="relative">
<n-input
v-model:value.trim="questionContent"
class="content-input"
type="textarea"
:autosize="{ minRows: 5, maxRows: 5 }"
:autosize="{ minRows: isHasUploadImage ? 3 : 5, maxRows: isHasUploadImage ? 3 : 5 }"
:class="isHasUploadImage ? 'carry-image' : ''"
:placeholder="t('home_module.please_enter_a_question')"
@keydown="handleQuestionSubmitEnter"
>
<template #suffix>
<div class="flex h-full items-end pb-[10px]">
<div :class="isHasUploadImage ? 'absolute bottom-[9px] right-[11px]' : 'flex h-full items-end pb-[10px]'">
<n-button type="primary" :disabled="isQuestionSubmitBtnDisabled" @click="() => questionSubmit()">
<i class="iconfont icon-send-icon"></i>
</n-button>
</div>
</template>
</n-input>
<div v-show="isHasUploadImage" class="absolute bottom-[9px] left-[11px] flex gap-[11px]">
<div
v-for="uploadImageItem in uploadImageList"
:key="uploadImageItem.id"
class="border-inactive-border-color relative h-[66px] w-[66px] rounded-[10px] border bg-white"
:class="{ 'border-[#F25744]!': uploadImageItem.status === UploadStatus.ERROR }"
>
<div
class="absolute right-[-4px] top-[-4px] flex h-4 w-4 cursor-pointer items-center justify-center rounded-full bg-[rgba(0,0,0,0.55)] hover:opacity-80"
@click="handleRemoveUploadImage(uploadImageItem.id)"
>
<CloseSmall theme="outline" size="16" fill="#fff" />
</div>
<div class="flex h-full w-full items-center justify-center overflow-hidden rounded-[10px]">
<n-spin v-show="uploadImageItem.status === UploadStatus.UPLOADING" :size="20" />
<n-image
v-show="uploadImageItem.status === UploadStatus.FINISHED"
width="100"
object-fit="contain"
:src="uploadImageItem.url"
preview-disabled
class="h-full w-full flex-shrink-0"
/>
</div>
</div>
</div>
</div>
<div class="mt-[20px] text-center text-[13px] text-[#999]">
......@@ -492,6 +601,11 @@ defineExpose({
}
}
:deep(.carry-image .n-input-wrapper) {
padding-bottom: 84px;
}
:deep(.upload-image-btn),
:deep(.application-select-btn) {
.n-button__border {
border-color: #9ea3ff;
......
<script setup lang="ts">
import { computed, readonly } from 'vue'
import { CheckOne } from '@icon-park/vue-next'
import type { MessageItemInterface } from '../types'
import { useUserStore } from '@/store/modules/user'
import MessageBubbleLoading from './message-bubble-loading.vue'
......@@ -38,6 +39,7 @@ const name = computed(() => {
<div class="mb-[7px] text-[12px] text-[#999]">
{{ name }}
</div>
<div
class="box-content min-h-[21px] min-w-[10px] max-w-full rounded-[10px] border border-[#9EA3FF] px-[15px] py-[14px] text-justify"
:class="{
......@@ -46,6 +48,11 @@ const name = computed(() => {
'!min-w-[80px]': messageItem.isAnswerLoading,
}"
>
<img
v-show="!isAgentMessage && messageItem.imageUrl"
:src="messageItem.imageUrl"
class="max-h-[120px]! mb-[11px] rounded-[10px] object-contain"
/>
<div
v-if="messageItem.isAnswerLoading && !messageItem.content"
class="flex h-[21px] items-center justify-center"
......@@ -65,6 +72,25 @@ const name = computed(() => {
</div>
</template>
</div>
<div
v-show="isAgentMessage && messageItem.pluginName"
class="mt-[7px] flex items-center gap-[5px] font-['Microsoft_YaHei_UI'] text-[#999]"
>
<div
v-show="!messageItem.content"
class="bg-px-plugin_loading-gif h-[14px] w-[14px] bg-contain bg-center bg-no-repeat"
/>
<CheckOne v-show="messageItem.content" theme="outline" size="16" fill="#40bd23" />
<span class="leading-5">
{{
!messageItem.content
? t('common_module.plugin_in_progress', { pluginName: messageItem.pluginName })
: t('common_module.plugin_executed_successfully', { pluginName: messageItem.pluginName })
}}
</span>
</div>
</div>
</div>
</div>
......
......@@ -30,6 +30,7 @@ const currentAgentApplication = ref<AgentApplicationRecordItem>({
agentDesc: '',
creator: '',
publishedTime: '',
points: 0,
})
const currentSessionId = ref('')
......@@ -161,6 +162,7 @@ function onGetMessageRecordList(recordId: string) {
agentTitle: string
content: string
timestamp: number
imageUrl: string
}[]
>(recordId)
.then((res) => {
......@@ -176,6 +178,7 @@ function onGetMessageRecordList(recordId: string) {
name: recordItem.agentTitle,
avatar: recordItem.agentAvatar,
timestamp: recordItem.timestamp,
imageUrl: recordItem?.imageUrl || '',
},
]
})
......
......@@ -16,4 +16,6 @@ export interface MessageItemInterface {
isAnswerLoading: boolean
avatar: string
name: string
pluginName?: string
imageUrl?: string
}
......@@ -5,6 +5,7 @@ import { languageKeyTransform } from '@/utils/language-key-transform'
import { fetchEventSource } from '@microsoft/fetch-event-source'
interface Options {
onFunction?: (name: string) => void
onmessage?: (message: string) => void
onend?: () => void
onclose?: () => void
......@@ -15,6 +16,7 @@ export default function fetchEventStreamSource(
url: string,
payload: object = {},
options: Options = {
onFunction: (_name: string) => {},
onmessage: (_message: string) => {},
onend: () => {},
onclose: () => {},
......@@ -46,6 +48,11 @@ export default function fetchEventStreamSource(
try {
const data = JSON.parse(e.data)
if (data.function && data.function.name) {
options.onFunction && options.onFunction(data.function.name)
return
}
if (data.code === 0 || data.code === '0') {
data.message && options.onmessage && options.onmessage(data.message)
} else {
......
<script setup lang="ts">
import { computed, ref } from 'vue'
import { computed, ref, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import { nanoid } from 'nanoid'
import { throttle } from 'lodash-es'
import { CloseSmall } from '@icon-park/vue-next'
import CMessage from './c-message'
import { MessageItemInterface, MultiModelDialogueItem, QuestionMessageItem } from '../types'
import { fetchEventStreamSource } from '../utils/fetch-event-stream-source'
......@@ -10,6 +11,7 @@ import { UploadStatus } from '@/enums/upload-status'
import { ChannelType } from '@/enums/channel'
import { useDialogueFile } from '@/composables/useDialogueFile'
import { useUserStore } from '@/store/modules/user'
import { useUploadImage } from '@/composables/useUploadImage'
const { t } = useI18n()
......@@ -33,6 +35,7 @@ const emit = defineEmits<{
const userStore = useUserStore()
const { uploadFileList, handleLimitUpload, handleUpload, handleRemoveFile } = useDialogueFile()
const { uploadImageList, handleLimitUploadImage, handleUploadImage, handleRemoveUploadImage } = useUploadImage()
const multiModelDialogueList = defineModel<MultiModelDialogueItem[]>('multiModelDialogueList', { required: true })
......@@ -51,7 +54,10 @@ const isAllowClearAllMessage = computed(() => {
})
const isInputMessageDisabled = computed(() => {
return uploadFileList.value.some((fileItem) => fileItem.status !== UploadStatus.FINISHED)
return (
uploadFileList.value.some((fileItem) => fileItem.status !== UploadStatus.FINISHED) ||
uploadImageList.value.some((imageItem) => imageItem.status !== UploadStatus.FINISHED)
)
})
const isUploadFileDisabled = computed(() => {
......@@ -64,7 +70,14 @@ const uploadFileIcon = (type: string) => {
const messageListScrollToBottomThrottle = throttle(() => {
emit('messageListScrollToBottom')
}, 1000)
}, 300)
watch(
() => uploadImageList.value.length,
() => {
emit('messageListScrollToBottom')
},
)
function handleClearAllMessage() {
isAllowClearAllMessage.value && emit('clearAllMessage')
......@@ -79,6 +92,8 @@ function messageItemFactory() {
timestamp: Date.now(),
isTextContentLoading: false,
isAnswerResponseLoading: false,
pluginName: '',
imageUrl: '',
} as MessageItemInterface
}
......@@ -99,7 +114,11 @@ function handleQuestionSubmit() {
const questionMessageId = nanoid()
emit('addQuestionMessageItem', questionMessageId, { ...messageItemFactory(), content: questionContent.value })
emit('addQuestionMessageItem', questionMessageId, {
...messageItemFactory(),
content: questionContent.value,
imageUrl: uploadImageList.value?.[0]?.url || '',
})
emit('messageListScrollToBottom')
......@@ -115,7 +134,7 @@ function handleQuestionSubmit() {
type: 'text',
text: messageItem.content,
image_url: {
url: '',
url: messageItem?.imageUrl || '',
},
},
],
......@@ -123,7 +142,6 @@ function handleQuestionSubmit() {
})
})
questionContent.value = ''
modelItem.isAnswerResponseWait = true
const answerMessageId = nanoid()
......@@ -161,6 +179,12 @@ function handleQuestionSubmit() {
return
}
if (data && data.name) {
emit('updateMessageItem', answerMessageId, { pluginName: data.name }, modelIndex)
emit('messageListScrollToBottom')
return
}
if (data) {
messageContent += data
......@@ -189,8 +213,12 @@ function handleQuestionSubmit() {
modelItem.controller = null
modelItem.isAnswerResponseWait = false
userStore.fetchUpdateEquityInfo()
emit('messageListScrollToBottom')
},
})
questionContent.value = ''
uploadImageList.value = []
})
}
......@@ -209,6 +237,17 @@ function handleSelectFile(cb: () => void) {
cb()
}
function handleSelectUploadImage(cb: () => void) {
if (uploadImageList.value.length > 0) {
window.$message.ctWarning('', t('common_module.dialogue_module.overwrite_image_tip')).then(() => {
cb()
})
return
}
cb()
}
</script>
<template>
......@@ -232,6 +271,29 @@ function handleSelectFile(cb: () => void) {
<span class="text-xs"> {{ t('common_module.dialogue_module.clear_message_popover_message') }}</span>
</n-popover>
<n-upload
:show-file-list="false"
accept="image/png, image/jpeg, image/jpg, image/webp"
abstract
@before-upload="handleLimitUploadImage"
@change="handleUploadImage"
>
<n-upload-trigger #="{ handleClick }" abstract>
<n-popover style="width: 210px" trigger="hover">
<template #trigger>
<div
class="border-inactive-border-color text-font-color hover:text-theme-color flex h-[54px] w-[54px] shrink-0 cursor-pointer items-center justify-center rounded-full border bg-white"
@click="handleSelectUploadImage(handleClick)"
>
<i class="iconfont icon-upload-image text-xl leading-none" />
</div>
</template>
<span class="text-xs"> {{ t('common_module.dialogue_module.upload_image_limit') }} </span>
</n-popover>
</n-upload-trigger>
</n-upload>
<n-upload
:show-file-list="false"
accept=".doc, .pdf, .docx, .txt, .md"
......@@ -258,6 +320,35 @@ function handleSelectFile(cb: () => void) {
</div>
<div class="flex flex-col gap-1">
<div class="mb-1.5 flex gap-[14px]">
<div
v-for="uploadImageItem in uploadImageList"
:key="uploadImageItem.id"
class="border-inactive-border-color relative h-[66px] w-[66px] rounded-[10px] border bg-white"
:class="{ 'border-[#F25744]!': uploadImageItem.status === UploadStatus.ERROR }"
>
<div
class="absolute right-[-4px] top-[-4px] flex h-4 w-4 cursor-pointer items-center justify-center rounded-full bg-[rgba(0,0,0,0.55)] hover:opacity-80"
@click="handleRemoveUploadImage(uploadImageItem.id)"
>
<CloseSmall theme="outline" size="16" fill="#fff" />
</div>
<div class="flex h-full w-full items-center justify-center overflow-hidden rounded-[10px]">
<n-spin v-show="uploadImageItem.status === UploadStatus.UPLOADING" :size="20" />
<n-image
v-show="uploadImageItem.status === UploadStatus.FINISHED"
width="100"
object-fit="contain"
:src="uploadImageItem.url"
preview-disabled
class="h-full w-full flex-shrink-0"
/>
</div>
</div>
</div>
<ul v-show="uploadFileList.length > 0" class="mb-1.5 grid gap-1.5">
<li
v-for="uploadFileItem in uploadFileList"
......
<script setup lang="ts">
import { computed, readonly } from 'vue'
import { useI18n } from 'vue-i18n'
import { CheckOne } from '@icon-park/vue-next'
import type { MessageItemInterface } from '../types'
import MarkdownRender from '@/components/markdown-render/markdown-render.vue'
import MessageBubbleLoading from './message-bubble-loading.vue'
......@@ -45,6 +46,12 @@ const assistantAvatarUrl = computed(() => {
'!min-w-[80px]': messageItem.isTextContentLoading,
}"
>
<img
v-show="!isAssistant && messageItem.imageUrl"
:src="messageItem.imageUrl"
class="max-h-[120px]! mb-2 rounded-[10px] object-contain"
/>
<div v-if="messageItem.isTextContentLoading" class="flex h-[21px] w-[30px] items-center justify-center">
<MessageBubbleLoading :active-color="isAssistant ? '#fff' : '#192338'" />
</div>
......@@ -65,6 +72,25 @@ const assistantAvatarUrl = computed(() => {
</div>
</div>
</div>
<div
v-show="isAssistant && messageItem.pluginName"
class="mt-[7px] flex items-center gap-[5px] font-['Microsoft_YaHei_UI'] text-[#999]"
>
<div
v-show="messageItem.isTextContentLoading"
class="bg-px-plugin_loading-gif h-[14px] w-[14px] bg-contain bg-center bg-no-repeat"
/>
<CheckOne v-show="!messageItem.isTextContentLoading" theme="outline" size="16" fill="#40bd23" />
<span class="leading-5">
{{
messageItem.isTextContentLoading
? t('common_module.plugin_in_progress', { pluginName: messageItem.pluginName })
: t('common_module.plugin_executed_successfully', { pluginName: messageItem.pluginName })
}}
</span>
</div>
</div>
</div>
</div>
......
......@@ -162,7 +162,7 @@ async function handleGetLargeModelInfo() {
// 返回应用设置页
function handleBackAgentSetting() {
handleBlockMessageResponse()
router.replace({ name: 'PersonalAppSetting', params: { agentId: agentId.value } })
router.back()
}
// 添加模型
......
......@@ -33,6 +33,8 @@ export interface MessageItemInterface {
timestamp: number
isTextContentLoading: boolean
isAnswerResponseLoading: boolean
pluginName?: string
imageUrl?: string
}
export interface LargeModelItem {
......
......@@ -65,6 +65,12 @@ export function fetchEventStreamSource(config: {
return
}
// 插件调用
if (data.function && data.function.name) {
config.onMessage(data.function)
return
}
config.onMessage(data.message)
} catch (err) {
config.onRequestError(err)
......
<script setup lang="ts">
import { computed, inject, onMounted, onUnmounted, ref, useTemplateRef, watch } from 'vue'
import { computed, inject, onMounted, onUnmounted, reactive, ref, useTemplateRef, watch } from 'vue'
import { Emitter } from 'mitt'
import { useI18n } from 'vue-i18n'
import { nanoid } from 'nanoid'
import { CloseSmall } from '@icon-park/vue-next'
import OverwriteMessageTipModal from './overwrite-message-tip-modal.vue'
import { fetchCustomEventSource } from '@/composables/useEventSource'
import { usePersonalAppConfigStore } from '@/store/modules/personal-app-config'
......@@ -12,6 +13,7 @@ import { TEXTTOSPEECH_WS_URL } from '@/config/base-url'
import WebSocketCtr from '@/utils/web-socket-ctr'
import { ChannelType } from '@/enums/channel'
import { useUserStore } from '@/store/modules/user'
import { useUploadImage } from '@/composables/useUploadImage'
interface Props {
messageList: Map<string, ConversationMessageItem>
......@@ -39,7 +41,13 @@ const emit = defineEmits<{
const personalAppConfigStore = usePersonalAppConfigStore()
const userStore = useUserStore()
const overwriteMessageTipOptions = reactive({
title: '',
content: '',
})
const { uploadFileList, handleLimitUpload, handleUpload, handleRemoveFile } = useDialogueFile()
const { uploadImageList, handleLimitUploadImage, handleUploadImage, handleRemoveUploadImage } = useUploadImage()
const messageTipModalRef = useTemplateRef<InstanceType<typeof OverwriteMessageTipModal>>('messageTipModalRef')
const isAnswerResponseLoading = defineModel<boolean>('isAnswerResponseLoading', { required: true })
......@@ -78,7 +86,8 @@ const isSendBtnDisabled = computed(() => {
const isInputMessageDisabled = computed(() => {
return (
uploadFileList.value.some((fileItem) => fileItem.status !== UploadStatus.FINISHED) ||
!personalAppConfigStore.baseInfo.agentId
!personalAppConfigStore.baseInfo.agentId ||
uploadImageList.value.some((imageItem) => imageItem.status !== UploadStatus.FINISHED)
)
})
......@@ -101,6 +110,13 @@ watch(
},
)
watch(
() => uploadImageList.value.length,
() => {
emit('updatePageScroll')
},
)
onUnmounted(() => {
blockMessageResponse()
emitter?.off('selectQuestion')
......@@ -125,6 +141,8 @@ function messageItemFactory(): ConversationMessageItem {
isVoicePlaying: false,
voiceFragmentUrlList: [],
isVoiceEnabled: false,
pluginName: '',
imageUrl: '',
}
}
......@@ -160,14 +178,18 @@ function handleMessageSend() {
type: string
text: string
image_url: {
url: ''
url: string
}
}[]
role: string
}[] = []
emit('updateContinuousQuestionStatus', personalAppConfigStore.commConfig.continuousQuestionStatus)
emit('addMessageItem', latestUserMessageKey, { ...messageItemFactory(), textContent: inputMessageContent.value })
emit('addMessageItem', latestUserMessageKey, {
...messageItemFactory(),
textContent: inputMessageContent.value,
imageUrl: uploadImageList.value?.[0]?.url || '',
})
emit('updatePageScroll')
props.messageList.forEach((messageItem) => {
......@@ -177,7 +199,7 @@ function handleMessageSend() {
type: 'text',
text: messageItem.textContent,
image_url: {
url: '',
url: messageItem.imageUrl || '',
},
},
],
......@@ -186,7 +208,6 @@ function handleMessageSend() {
})
isAnswerResponseLoading.value = true
inputMessageContent.value = ''
isAnswerResponseWait.value = true
currentReplyContentSentenceExtractIndex.value = 0
......@@ -237,6 +258,12 @@ function handleMessageSend() {
return
}
if (data && data.name) {
emit('updateSpecifyMessageItem', latestAssistantMessageKey, { pluginName: data.name })
emit('updatePageScroll')
return
}
if (data) {
replyTextContent += data
......@@ -269,6 +296,9 @@ function handleMessageSend() {
userStore.fetchUpdateEquityInfo()
},
})
inputMessageContent.value = ''
uploadImageList.value = []
}
function errorMessageResponse() {
......@@ -296,6 +326,21 @@ function blockMessageResponse() {
function handleSelectFile(cb: () => void) {
if (isUploadFileDisabled.value) {
overwriteMessageTipOptions.title = t('common_module.dialogue_module.overwrite_file_tip')
messageTipModalRef.value?.handleShowModal().then(() => {
cb()
})
return
}
cb()
}
function handleSelectImage(cb: () => void) {
if (uploadImageList.value.length > 0) {
overwriteMessageTipOptions.title = t('common_module.dialogue_module.overwrite_image_tip')
messageTipModalRef.value?.handleShowModal().then(() => {
cb()
})
......@@ -416,6 +461,34 @@ defineExpose({
<div class="mb-3 mt-5 px-5">
<div class="flex items-end gap-2.5">
<div class="flex-1">
<div class="mb-1.5 flex gap-[14px]">
<div
v-for="uploadImageItem in uploadImageList"
:key="uploadImageItem.id"
class="border-inactive-border-color relative h-[66px] w-[66px] rounded-[10px] border bg-white"
:class="{ 'border-[#F25744]!': uploadImageItem.status === UploadStatus.ERROR }"
>
<div
class="absolute right-[-4px] top-[-4px] flex h-4 w-4 cursor-pointer items-center justify-center rounded-full bg-[rgba(0,0,0,0.55)] hover:opacity-80"
@click="handleRemoveUploadImage(uploadImageItem.id)"
>
<CloseSmall theme="outline" size="16" fill="#fff" />
</div>
<div class="flex h-full w-full items-center justify-center overflow-hidden rounded-[10px]">
<n-spin v-show="uploadImageItem.status === UploadStatus.UPLOADING" :size="20" />
<n-image
v-show="uploadImageItem.status === UploadStatus.FINISHED"
object-fit="contain"
:src="uploadImageItem.url"
preview-disabled
class="h-full w-full flex-shrink-0"
/>
</div>
</div>
</div>
<ul v-show="uploadFileList.length > 0" class="mb-1.5 grid gap-1.5">
<li
v-for="uploadFileItem in uploadFileList"
......@@ -511,6 +584,29 @@ defineExpose({
</n-upload-trigger>
</n-upload>
<n-upload
:show-file-list="false"
accept="image/png, image/jpeg, image/jpg, image/webp"
abstract
@before-upload="handleLimitUploadImage"
@change="handleUploadImage"
>
<n-upload-trigger #="{ handleClick }" abstract>
<n-popover style="width: 210px" trigger="hover">
<template #trigger>
<div
class="h-7.5 w-7.5 hover:text-theme-color text-font-color mb-1 flex cursor-pointer items-center justify-center rounded-full bg-white"
@click="handleSelectImage(handleClick)"
>
<i class="iconfont icon-upload-image flex h-4 w-4 items-center justify-center" />
</div>
</template>
<span class="text-xs"> {{ t('common_module.dialogue_module.upload_image_limit') }} </span>
</n-popover>
</n-upload-trigger>
</n-upload>
<n-popover trigger="hover">
<template #trigger>
<div
......@@ -538,5 +634,5 @@ defineExpose({
</div>
</div>
<OverwriteMessageTipModal ref="messageTipModalRef" />
<OverwriteMessageTipModal ref="messageTipModalRef" :modal-options="overwriteMessageTipOptions" />
</template>
<script setup lang="ts">
import { computed } from 'vue'
import { useI18n } from 'vue-i18n'
import { CheckOne } from '@icon-park/vue-next'
import CustomLoading from './custom-loading.vue'
import { usePersonalAppConfigStore } from '@/store/modules/personal-app-config'
import MarkdownRender from '@/components/markdown-render/markdown-render.vue'
......@@ -72,6 +73,31 @@ function handleAudioControl() {
class="min-w-[80px] max-w-full flex-wrap rounded-xl border border-[#e8e9eb] px-4 py-[11px]"
:class="role === 'user' ? 'bg-[#4b87ff] text-white' : 'bg-white text-[#333]'"
>
<img
v-show="role === 'user' && messageItem.imageUrl"
:src="messageItem.imageUrl"
class="max-h-[120px]! mb-1.5 rounded-[10px] object-contain"
/>
<div
v-show="role === 'assistant' && messageItem.pluginName"
class="mb-[8px] flex items-center gap-[5px] font-['Microsoft_YaHei_UI'] text-[#999]"
>
<div
v-show="messageItem.isTextContentLoading"
class="bg-px-plugin_loading-gif h-[14px] w-[14px] bg-contain bg-center bg-no-repeat"
/>
<CheckOne v-show="!messageItem.isTextContentLoading" theme="outline" size="16" fill="#40bd23" />
<span class="leading-5">
{{
messageItem.isTextContentLoading
? t('common_module.plugin_in_progress', { pluginName: messageItem.pluginName })
: t('common_module.plugin_executed_successfully', { pluginName: messageItem.pluginName })
}}
</span>
</div>
<div v-if="messageItem.isTextContentLoading" class="py-1.5 pl-4">
<CustomLoading />
</div>
......
<script setup lang="ts">
import { computed, reactive, ref, watch } from 'vue'
import { computed, ref, watch } from 'vue'
import { useI18n } from 'vue-i18n'
interface ModalOptionInterface {
title: string
content: string
}
interface Props {
modalOptions: ModalOptionInterface
}
defineProps<Props>()
const { t } = useI18n()
const isShowModal = ref(false)
const pageWidth = ref(window.innerWidth)
const modalOptions = reactive({
title: t('common_module.dialogue_module.overwrite_file_tip'),
content: '',
})
let _modalStatusResolve = (_value?: any) => {}
let _modalStatusReject = (_reason?: any) => {}
......
......@@ -23,6 +23,7 @@ import {
} from '@/apis/agent-application'
import { fetchCustomEventSource } from '@/composables/useEventSource'
import AgentModelSetting from './components/agent-model-setting.vue'
import AgentPlugin from './components/agent-plugin.vue'
import AgentAssociatedKnowledge from './components/agent-associated-knowledge.vue'
import AgentMemorySetting from './components/agent-memory-setting.vue'
import AgentDialogueSetting from './components/agent-dialogue-setting.vue'
......@@ -35,7 +36,7 @@ const router = useRouter()
const personalAppConfigStore = usePersonalAppConfigStore()
const userStore = useUserStore()
const { baseInfo, commConfig, commModelConfig, knowledgeConfig } = storeToRefs(personalAppConfigStore)
const { baseInfo, commConfig, commModelConfig, knowledgeConfig, unitIds } = storeToRefs(personalAppConfigStore)
const emitter = inject<Emitter<MittEvents>>('emitter')
......@@ -378,7 +379,7 @@ async function handleEquityInfoValidate() {
</script>
<template>
<div class="flex h-full flex-[3_3_0%] flex-col">
<div class="flex h-full flex-[3_3_0%] flex-col overflow-hidden">
<div
class="flex h-[56px] w-full items-center justify-between border-r border-[#e8e9eb] px-5 text-[#333] shadow-[inset_0_-1px_#e8e9eb]"
>
......@@ -605,6 +606,8 @@ async function handleEquityInfoValidate() {
</div>
<div class="flex-1 overflow-auto">
<AgentPlugin v-model:unit-ids="unitIds" />
<AgentAssociatedKnowledge v-model:knowledge-config="knowledgeConfig" />
<AgentMemorySetting
......
<script setup lang="ts">
import { computed, ref, watch } from 'vue'
import { Search, Close } from '@icon-park/vue-next'
import { useI18n } from 'vue-i18n'
import { fetchPluginCategoryDetailInfoList, fetchPluginCategoryKeyList } from '@/apis/plugin-center'
import { useSystemLanguageStore } from '@/store/modules/system-language'
interface Props {
isShowModal: boolean
modalTitle: string
}
interface PluginClassificationItemInterface {
classificationName: string
classification: string
}
interface PluginItemInterface {
pluginId: string
title: string
points: number
description: string
icon: string
}
interface Emits {
(e: 'update:isShowModal', value: boolean): void
(e: 'close'): void
}
const { t } = useI18n()
const props = defineProps<Props>()
const emit = defineEmits<Emits>()
const systemLanguageStore = useSystemLanguageStore()
const pluginIdList = defineModel<string[]>('pluginIdList', { required: true })
const pluginClassificationListLoading = ref(false)
const pluginListLoading = ref(false)
const isSearchEmptyList = ref(false)
const searchPluginQuery = ref('')
const pluginClassificationList = ref<PluginClassificationItemInterface[]>([])
const currentPluginClassification = ref<PluginClassificationItemInterface>({
classificationName: t('common_module.all'),
classification: '',
})
const pluginList = ref<PluginItemInterface[]>([])
const defaultPluginIconUrl = 'https://gsst-poe-sit.gz.bcebos.com/data/20250115/1736924150557.png'
const showModal = computed({
get() {
return props.isShowModal
},
set(value: boolean) {
emit('update:isShowModal', value)
},
})
const emptyPluginListText = computed(() => {
return isSearchEmptyList.value ? t('common_module.search_empty_data') : t('common_module.empty_data')
})
const isAddedAgentPluginId = computed(() => (pluginId: string) => {
return pluginIdList.value.includes(pluginId)
})
const isEnglishLanguage = computed(() => systemLanguageStore.currentLanguageInfo.key === 'en')
watch(
() => props.isShowModal,
(newVal) => {
newVal && handleGetPluginClassificationList()
},
)
async function handleGetPluginClassificationList() {
pluginClassificationListLoading.value = true
const res = await fetchPluginCategoryKeyList<PluginClassificationItemInterface[]>()
if (res.code === 0) {
pluginClassificationList.value = [
{
classificationName: t('common_module.all'),
classification: '',
},
]
pluginClassificationList.value.push(...res.data)
pluginClassificationListLoading.value = false
await handleGetPluginList()
}
}
async function handleGetPluginList() {
pluginListLoading.value = true
const payload = {
classification: currentPluginClassification.value.classification,
pluginIds: [],
query: searchPluginQuery.value,
}
const res = await fetchPluginCategoryDetailInfoList<{ pluginInfos: PluginItemInterface[] }[]>(payload)
if (res.code === 0) {
pluginList.value = []
res.data.forEach((item) => {
item.pluginInfos.forEach((pluginItem) => {
pluginList.value.push(pluginItem)
})
})
pluginListLoading.value = false
isSearchEmptyList.value = !!searchPluginQuery.value && pluginList.value.length === 0
}
}
async function handleClearPluginQuery() {
searchPluginQuery.value = ''
await handleGetPluginList()
}
async function handleChangePluginClassification(pluginClassificationItem: PluginClassificationItemInterface) {
currentPluginClassification.value = pluginClassificationItem
await handleGetPluginList()
}
function handleAddAgentPlugin(pluginId: string, pluginTitle: string) {
pluginIdList.value.push(pluginId)
window.$message.success(
t('personal_space_module.agent_module.agent_setting_module.agent_config_module.add_plugin_successfully', [
pluginTitle,
]),
)
}
function handleRemoveAgentPlugin(pluginId: string, pluginTitle: string) {
pluginIdList.value = pluginIdList.value.filter((id) => id !== pluginId)
window.$message.success(
t('personal_space_module.agent_module.agent_setting_module.agent_config_module.remove_plugin_successfully', [
pluginTitle,
]),
)
}
function handleCloseModal() {
emit('close')
}
</script>
<template>
<n-modal
v-model:show="showModal"
class="h-[700px] w-[954px] rounded-[10px] bg-white"
:auto-focus="false"
@after-leave="handleCloseModal"
>
<div class="flex">
<div class="w-[218px] flex-shrink-0 p-6 shadow-[0_2px_8px_0_rgba(0,0,0,0.1)]">
<p class="text-[18px] leading-none text-[#333]">{{ modalTitle }}</p>
<n-input
v-model:value="searchPluginQuery"
:placeholder="t('common_module.search')"
class="my-[18px]"
clearable
@clear="handleClearPluginQuery"
@keyup.enter="handleGetPluginList"
>
<template #prefix>
<Search theme="outline" size="16" fill="#999" class="cursor-pointer" />
</template>
</n-input>
<n-scrollbar v-if="!pluginClassificationListLoading" style="max-height: calc(100% - 86px)">
<ul class="flex flex-col gap-[2px]">
<li
v-for="pluginClassificationItem in pluginClassificationList"
:key="pluginClassificationItem.classification"
:class="
currentPluginClassification.classification === pluginClassificationItem.classification
? 'text-theme-color bg-active-color'
: 'text-[#333]'
"
class="hover:bg-active-color rounded-theme flex h-10 w-full cursor-pointer items-center px-[13px]"
@click="handleChangePluginClassification(pluginClassificationItem)"
>
<n-ellipsis>
{{ pluginClassificationItem.classificationName }}
</n-ellipsis>
</li>
</ul>
</n-scrollbar>
<div v-show="pluginClassificationListLoading" class="flex flex-col gap-[2px]">
<n-skeleton v-for="i in 3" :key="i" height="40px" width="100%" class="rounded-theme" />
</div>
</div>
<div class="flex-1 overflow-hidden p-6">
<div class="mb-[18px] flex items-center justify-between">
<span>{{ currentPluginClassification.classificationName }}</span>
<div
class="rounded-theme cursor-pointer p-[3px] hover:bg-[#dedede]"
@click="emit('update:isShowModal', false)"
>
<Close theme="outline" size="16" fill="#666" />
</div>
</div>
<n-virtual-list
v-show="!pluginListLoading && pluginList.length > 0"
style="max-height: 612px"
:item-size="106"
:items="pluginList"
key-field="pluginId"
item-resizable
items-style="padding-right: 12px;"
>
<template #default="{ item }: { item: PluginItemInterface }">
<div
:key="item.pluginId"
class="border-inactive-border-color mb-4.5 hover:border-theme-color flex h-[88px] items-center gap-[14px] overflow-hidden rounded-[10px] border px-5 last:mb-0 hover:bg-[#F6F7FF]"
>
<div
class="h-[38px] w-[38px] flex-shrink-0 bg-contain bg-center bg-no-repeat"
:style="
item.icon
? { backgroundImage: `url(${item.icon})` }
: { backgroundImage: `url(${defaultPluginIconUrl})` }
"
/>
<div class="flex flex-1 items-center justify-between gap-[14px] overflow-hidden">
<div class="flex h-full flex-1 flex-col justify-between overflow-hidden">
<div class="w-full text-[14px]">
<n-ellipsis class="w-full text-[#333]" :tooltip="{ 'content-style': 'max-width: 600px;' }">
{{ item.title }}
</n-ellipsis>
</div>
<div class="w-full text-[12px]">
<n-ellipsis class="w-full text-[#333]" :tooltip="{ 'content-style': 'max-width: 600px;' }">
{{ item.description }}
</n-ellipsis>
</div>
</div>
<div class="flex flex-shrink-0 items-center gap-[14px]">
<span class="text-[12px] text-[#999]">
{{ t('common_module.points_per_time', { count: (item.points * 1).toFixed(2) }) }}
</span>
<n-button
v-show="!isAddedAgentPluginId(item.pluginId)"
type="primary"
:border="false"
class="h-[34px]!"
:class="isEnglishLanguage ? 'w-[86px]!' : 'px-5!'"
@click="handleAddAgentPlugin(item.pluginId, item.title)"
>
{{ t('common_module.add') }}
</n-button>
<n-button
v-show="isAddedAgentPluginId(item.pluginId)"
type="error"
ghost
class="h-[34px]!"
:class="isEnglishLanguage ? 'w-[86px]!' : 'px-5!'"
@click="handleRemoveAgentPlugin(item.pluginId, item.title)"
>
{{ t('common_module.remove') }}
</n-button>
</div>
</div>
</div>
</template>
</n-virtual-list>
<div v-show="pluginListLoading" class="flex flex-col gap-[14px]">
<n-skeleton v-for="i in 3" :key="i" height="88px" width="100%" class="rounded-[10px]" />
</div>
<div v-show="!pluginListLoading && pluginList.length === 0" class="flex h-full items-center justify-center">
<div class="flex flex-col items-center justify-center">
<div
class="mb-5 h-[68px] w-[68px]"
:class="isSearchEmptyList ? 'bg-px-search_empty_list-png' : 'bg-px-empty_list-png'"
/>
<p class="text-gray-font-color select-none">{{ emptyPluginListText }}</p>
</div>
</div>
</div>
</div>
<template #footer />
</n-modal>
</template>
<script setup lang="ts">
import { computed, ref, watch } from 'vue'
import { Plus, RightOne } from '@icon-park/vue-next'
import { useI18n } from 'vue-i18n'
import AddAgentPluginModal from './add-agent-plugin-modal.vue'
import { PersonalAppConfigState } from '@/store/types/personal-app-config'
import { fetchPluginCategoryDetailInfoList } from '@/apis/plugin-center'
interface PluginItemInterface {
pluginId: string
title: string
points: number
description: string
icon: string
}
const { t } = useI18n()
const unitIds = defineModel<PersonalAppConfigState['unitIds']>('unitIds', { required: true })
const pluginExpandedNames = ref<string[]>([])
const selectedPluginList = ref<PluginItemInterface[]>([])
const isShowAddAgentPluginModal = ref(false)
const hoverUnitId = ref('')
const defaultPluginIconUrl = 'https://gsst-poe-sit.gz.bcebos.com/data/20250115/1736924150557.png'
const isHoverPluginItem = computed(() => (pluginId: string) => {
return hoverUnitId.value === pluginId
})
watch(
() => unitIds.value,
async (newUnitIds) => {
if (newUnitIds.length > 0) {
await handleGetPluginList()
pluginExpandedNames.value = ['plugin']
}
},
{ once: true, immediate: true },
)
function handleUpdatePluginExpandedNames(expandedNames: string[]) {
pluginExpandedNames.value = expandedNames
}
function handleShowAddAgentPluginModel() {
isShowAddAgentPluginModal.value = true
}
async function handleGetPluginList() {
const payload = {
classification: '',
pluginIds: unitIds.value,
}
const res = await fetchPluginCategoryDetailInfoList<{ pluginInfos: PluginItemInterface[] }[]>(payload)
if (res.code === 0) {
selectedPluginList.value = []
res.data.forEach((pluginClassificationItem) => {
pluginClassificationItem.pluginInfos.forEach((pluginItem) => {
selectedPluginList.value.push(pluginItem)
})
})
}
}
function handleMouseoverPluginItem(pluginId: string) {
hoverUnitId.value = pluginId
}
function handleMouseleavePluginItem() {
hoverUnitId.value = ''
}
function handleDeleteUnitId(pluginId: string) {
unitIds.value = unitIds.value.filter((id) => id !== pluginId)
selectedPluginList.value = selectedPluginList.value.filter((pluginItem) => pluginItem.pluginId !== pluginId)
}
async function handleCloseAgentPluginModal() {
if (!unitIds.value.length) {
selectedPluginList.value = []
return
}
await handleGetPluginList()
pluginExpandedNames.value = ['plugin']
}
</script>
<template>
<section class="border-b border-[#e8e9eb] px-5">
<div class="pt-4">
<h2 class="mb-3 text-[#84868c]">
{{ t('personal_space_module.agent_module.agent_setting_module.agent_config_module.plugin') }}
</h2>
<NCollapse
:expanded-names="pluginExpandedNames"
:trigger-areas="['main', 'arrow']"
@update:expanded-names="handleUpdatePluginExpandedNames"
>
<template #arrow>
<RightOne theme="filled" size="17" fill="#333" :stroke-width="3" />
</template>
<NCollapseItem
:title="t('personal_space_module.agent_module.agent_setting_module.agent_config_module.plugin')"
name="plugin"
class="my-[13px]!"
>
<template #header-extra>
<NTooltip trigger="hover">
<template #trigger>
<Plus
theme="outline"
size="22"
:stroke-width="3"
class="text-theme-color cursor-pointer"
@click="handleShowAddAgentPluginModel"
/>
</template>
{{ t('personal_space_module.agent_module.agent_setting_module.agent_config_module.add_plugin') }}
</NTooltip>
</template>
<ul v-show="selectedPluginList.length" class="flex flex-col gap-2">
<li
v-for="pluginItem in selectedPluginList"
:key="pluginItem.pluginId"
class="flex h-[66px] items-center gap-[12px] overflow-hidden rounded-[10px] border border-[#f2f5f9] px-4 hover:bg-[#f2f5f9]"
@mouseover="handleMouseoverPluginItem(pluginItem.pluginId)"
@mouseleave="handleMouseleavePluginItem"
>
<div
class="h-[36px] w-[36px] flex-shrink-0 bg-contain bg-center bg-no-repeat"
:style="
pluginItem.icon
? { backgroundImage: `url(${pluginItem.icon})` }
: { backgroundImage: `url(${defaultPluginIconUrl})` }
"
/>
<div class="flex h-full flex-1 flex-col justify-center overflow-hidden">
<div class="w-full text-[14px]">
<n-ellipsis class="text-font-color w-full" :tooltip="{ 'content-style': 'max-width: 600px;' }">
{{ pluginItem.title }}
</n-ellipsis>
</div>
<div class="w-full text-[12px]">
<n-ellipsis class="text-gray-font-color w-full" :tooltip="{ 'content-style': 'max-width: 600px;' }">
{{ pluginItem.description }}
</n-ellipsis>
</div>
</div>
<n-tooltip placement="top">
<template #trigger>
<div
v-show="isHoverPluginItem(pluginItem.pluginId)"
class="flex cursor-pointer items-center justify-center"
@click="handleDeleteUnitId(pluginItem.pluginId)"
>
<i class="hover:text-error-font-color text-font-color iconfont icon-reduce outline-none" />
</div>
</template>
<span>{{ t('common_module.remove') }}</span>
</n-tooltip>
</li>
</ul>
<div v-show="selectedPluginList.length === 0">
<span class="text-xs text-[#84868c]">
{{ t('personal_space_module.agent_module.agent_setting_module.agent_config_module.plugin_desc') }}
</span>
</div>
</NCollapseItem>
</NCollapse>
</div>
</section>
<AddAgentPluginModal
v-model:is-show-modal="isShowAddAgentPluginModal"
v-model:plugin-id-list="unitIds"
:modal-title="t('personal_space_module.agent_module.agent_setting_module.agent_config_module.add_plugin')"
@close="handleCloseAgentPluginModal"
/>
</template>
......@@ -102,7 +102,7 @@ onMounted(() => {
})
function handleBackPreviousPage() {
router.replace({ name: 'PersonalSpaceApp' })
router.back()
}
function handleDropdownSelect(key: string) {
......
<script setup lang="ts">
import { computed, inject, onMounted, onUnmounted, ref } from 'vue'
import { computed, inject, onMounted, onUnmounted, ref, watch } from 'vue'
import { Emitter } from 'mitt'
import { useI18n } from 'vue-i18n'
import { nanoid } from 'nanoid'
import { useRoute } from 'vue-router'
import { CloseSmall } from '@icon-park/vue-next'
import { fetchCustomEventSource } from '@/composables/useEventSource'
import { useUserStore } from '@/store/modules/user'
import { UploadStatus } from '@/enums/upload-status'
......@@ -12,6 +13,7 @@ import { useLayoutConfig } from '@/composables/useLayoutConfig'
import { TEXTTOSPEECH_WS_URL } from '@/config/base-url'
import WebSocketCtr from '@/utils/web-socket-ctr'
import { ChannelType } from '@/enums/channel'
import { useUploadImage } from '@/composables/useUploadImage'
interface Props {
agentId: string
......@@ -49,6 +51,7 @@ const { isMobile } = useLayoutConfig()
const userStore = useUserStore()
const { uploadFileList, handleLimitUpload, handleUpload, handleRemoveFile } = useDialogueFile()
const { uploadImageList, handleLimitUploadImage, handleUploadImage, handleRemoveUploadImage } = useUploadImage()
const isAnswerResponseLoading = defineModel<boolean>('isAnswerResponseLoading', { required: true })
......@@ -87,13 +90,23 @@ const isCreateContinueQuestions = computed(() => {
})
const isInputMessageDisabled = computed(() => {
return uploadFileList.value.some((fileItem) => fileItem.status !== UploadStatus.FINISHED)
return (
uploadFileList.value.some((fileItem) => fileItem.status !== UploadStatus.FINISHED) ||
uploadImageList.value.some((imageItem) => imageItem.status !== UploadStatus.FINISHED)
)
})
const isUploadFileDisabled = computed(() => {
return uploadFileList.value.length === 1
})
watch(
() => uploadImageList.value.length,
() => {
emit('updatePageScroll')
},
)
const uploadFileIcon = (type: string) => {
return `https://gsst-poe-sit.gz.bcebos.com/icon/${type}.svg`
}
......@@ -126,6 +139,8 @@ function messageItemFactory(): ConversationMessageItem {
isVoiceLoading: false,
isVoicePlaying: false,
voiceFragmentUrlList: [],
pluginName: '',
imageUrl: '',
}
}
......@@ -162,7 +177,11 @@ function handleMessageSend() {
currentLatestMessageItemKeyMap.value.set('assistant', latestAssistantMessageKey)
emit('resetContinueQuestionList')
emit('addMessageItem', latestUserMessageKey, { ...messageItemFactory(), textContent: inputMessageContent.value })
emit('addMessageItem', latestUserMessageKey, {
...messageItemFactory(),
textContent: inputMessageContent.value,
imageUrl: uploadImageList.value?.[0]?.url || '',
})
emit('updatePageScroll')
emit('addMessageItem', latestAssistantMessageKey, {
......@@ -174,12 +193,9 @@ function handleMessageSend() {
})
emit('updatePageScroll')
const input = inputMessageContent.value
let replyTextContent = ''
isAnswerResponseLoading.value = true
isAnswerResponseWait.value = true
inputMessageContent.value = ''
currentReplyContentSentenceExtractIndex.value = 0
sentenceFragmentSerialNo.value = 0
sentenceExtractCheckEnabled.value = false
......@@ -195,8 +211,9 @@ function handleMessageSend() {
agentId: props.agentId,
dialogsId: props.dialogsId,
fileUrls: uploadFileList.value.map((item) => item.url),
input,
input: inputMessageContent.value,
channel: query.channel || ChannelType.link_share,
imageUrl: uploadImageList.value?.[0]?.url || '',
},
controller,
onMessage: (data: any) => {
......@@ -214,6 +231,12 @@ function handleMessageSend() {
return
}
if (data && data.name) {
emit('updateSpecifyMessageItem', latestAssistantMessageKey, { pluginName: data.name })
emit('updatePageScroll')
return
}
if (data) {
replyTextContent += data
......@@ -246,6 +269,9 @@ function handleMessageSend() {
userStore.fetchUpdateEquityInfo()
},
})
inputMessageContent.value = ''
uploadImageList.value = []
}
function errorMessageResponse() {
......@@ -286,6 +312,17 @@ function handleSelectFile(cb: () => void) {
cb()
}
function handleSelectImage(cb: () => void) {
if (uploadImageList.value.length > 0) {
window.$message.ctWarning('', t('common_module.dialogue_module.overwrite_file_tip')).then(() => {
cb()
})
return
}
cb()
}
function sentenceExtract(messageId: string) {
const symbolRegExp = /[。!?;.!?;]/g
let sentenceDraft = assistantFullAnswerContent.value
......@@ -397,6 +434,34 @@ defineExpose({
<div class="my-5">
<div class="flex items-end gap-2.5">
<div class="flex flex-1 flex-col">
<div class="mb-1.5 flex gap-[14px]">
<div
v-for="uploadImageItem in uploadImageList"
:key="uploadImageItem.id"
class="border-inactive-border-color relative h-[66px] w-[66px] rounded-[10px] border bg-white"
:class="{ 'border-[#F25744]!': uploadImageItem.status === UploadStatus.ERROR }"
>
<div
class="absolute right-[-4px] top-[-4px] flex h-4 w-4 cursor-pointer items-center justify-center rounded-full bg-[rgba(0,0,0,0.55)] hover:opacity-80"
@click="handleRemoveUploadImage(uploadImageItem.id)"
>
<CloseSmall theme="outline" size="16" fill="#fff" />
</div>
<div class="flex h-full w-full items-center justify-center overflow-hidden rounded-[10px]">
<n-spin v-show="uploadImageItem.status === UploadStatus.UPLOADING" :size="20" />
<n-image
v-show="uploadImageItem.status === UploadStatus.FINISHED"
object-fit="contain"
:src="uploadImageItem.url"
preview-disabled
class="h-full w-full flex-shrink-0"
/>
</div>
</div>
</div>
<ul v-show="uploadFileList.length > 0" class="mb-1.5 grid gap-1.5">
<li
v-for="uploadFileItem in uploadFileList"
......@@ -509,6 +574,29 @@ defineExpose({
</n-upload-trigger>
</n-upload>
<n-upload
:show-file-list="false"
accept="image/png, image/jpeg, image/jpg, image/webp"
abstract
@before-upload="handleLimitUploadImage"
@change="handleUploadImage"
>
<n-upload-trigger #="{ handleClick }" abstract>
<n-popover style="width: 210px" trigger="hover">
<template #trigger>
<div
class="h-7.5 w-7.5 hover:text-theme-color text-font-color mb-1 flex cursor-pointer items-center justify-center rounded-full bg-white"
@click="handleSelectImage(handleClick)"
>
<i class="iconfont icon-upload-image flex h-4 w-4 items-center justify-center" />
</div>
</template>
<span class="text-xs"> {{ t('common_module.dialogue_module.upload_image_limit') }} </span>
</n-popover>
</n-upload-trigger>
</n-upload>
<n-popover trigger="hover">
<template #trigger>
<div
......
<script setup lang="ts">
import { computed } from 'vue'
import { useI18n } from 'vue-i18n'
import { CheckOne } from '@icon-park/vue-next'
import CustomLoading from './custom-loading.vue'
import MusicWavesLoading from './music-waves-loading.vue'
import MarkdownRender from '@/components/markdown-render/markdown-render.vue'
......@@ -101,6 +102,31 @@ function handleAudioControl() {
isMobile ? 'max-w-[calc(100%-20px)]' : 'max-w-full',
]"
>
<img
v-show="role === 'user' && messageItem.imageUrl"
:src="messageItem.imageUrl"
class="max-h-[120px]! mb-1.5 rounded-[10px] object-contain"
/>
<div
v-show="role === 'assistant' && messageItem.pluginName"
class="mb-[8px] flex items-center gap-[5px] font-['Microsoft_YaHei_UI'] text-[#999]"
>
<div
v-show="messageItem.isTextContentLoading"
class="bg-px-plugin_loading-gif h-[14px] w-[14px] bg-contain bg-center bg-no-repeat"
/>
<CheckOne v-show="!messageItem.isTextContentLoading" theme="outline" size="16" fill="#40bd23" />
<span class="leading-5">
{{
messageItem.isTextContentLoading
? t('common_module.plugin_in_progress', { pluginName: messageItem.pluginName })
: t('common_module.plugin_executed_successfully', { pluginName: messageItem.pluginName })
}}
</span>
</div>
<div v-if="messageItem.isTextContentLoading" class="py-1.5 pl-4">
<CustomLoading />
</div>
......
......@@ -9,4 +9,6 @@ declare interface ConversationMessageItem {
isVoicePlaying: boolean
voiceFragmentUrlList: string[]
isVoiceEnabled?: boolean
pluginName?: string
imageUrl?: string
}
......@@ -136,6 +136,9 @@ declare namespace I18n {
or: string
search_keyword_empty_tip: string
added: string
plugin_in_progress: string
plugin_executed_successfully: string
upload_image: string
dialogue_module: {
continue_question_message: string
......@@ -150,6 +153,10 @@ declare namespace I18n {
overwrite_file_tip: string
stop_playing_and_then_operate: string
do_not_operate_until_the_reply_is_complete: string
upload_image_limit: string
overwrite_image_tip: string
upload_image_size_error_message: string
upload_image_format_error_message: string
}
data_table_module: {
......@@ -293,6 +300,11 @@ declare namespace I18n {
agent_system_input_placeholder: string
ability_expand: string
plugin: string
plugin_desc: string
add_plugin: string
add_plugin_successfully: string
remove_plugin_successfully: string
knowledge: string
knowledge_base: string
knowledge_base_desc: string
......
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