From d20172626221e98a0e3c8c492b367082b1c97381 Mon Sep 17 00:00:00 2001 From: Ethan-Zhang Date: Sun, 17 Aug 2025 07:49:44 +0800 Subject: [PATCH 1/4] =?UTF-8?q?Feat:=20=E5=90=88=E5=85=A5release-0.10.0?= =?UTF-8?q?=E5=8F=98=E6=9B=B4=EF=BC=8C=E4=B8=BArelease-0.10.1=E9=93=BA?= =?UTF-8?q?=E5=9E=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile | 30 +- build/linux/euler-copilot-web.spec | 9 +- build/linux/nginx.conf.local.tmpl | 8 +- deploy/nginx.conf.tmpl | 25 +- electron/main/common/locale.ts | 4 +- electron/main/common/platform.ts | 2 +- src/apis/appCenter/appCenterService.ts | 13 + src/apis/paths/account.ts | 1 + src/apis/paths/conversation.ts | 12 +- src/apis/paths/mcp.ts | 18 +- src/apis/paths/type.ts | 11 +- src/apis/workFlow/workFlowService.ts | 3 +- src/assets/svgs/file_download.svg | 13 + src/assets/svgs/file_download_active.svg | 13 + src/assets/svgs/file_download_hover.svg | 13 + src/assets/svgs/html.svg | 28 + src/assets/svgs/jpeg.svg | 27 + src/assets/svgs/json.svg | 28 + src/assets/svgs/png.svg | 28 + src/assets/svgs/pptx.svg | 28 + src/assets/svgs/txt_green.svg | 28 + src/assets/svgs/warning.svg | 8 + src/assets/svgs/yaml.svg | 28 + src/assets/svgs/zip.svg | 28 + src/components/commonFooter/CommonFooter.vue | 28 +- src/components/dialoguePanel/DialogueFlow.vue | 214 +++- .../dialoguePanel/DialoguePanel.scss | 6 - .../dialoguePanel/DialoguePanel.vue | 346 +++++- src/components/dialoguePanel/FlowCode.vue | 55 +- src/components/dialoguePanel/ParamsModel.vue | 137 +++ src/i18n/index.ts | 18 +- src/i18n/lang/en.ts | 528 ++++----- src/i18n/lang/zh-cn.ts | 101 +- src/store/account.ts | 3 + src/store/conversation.ts | 465 ++++---- src/store/historySession.ts | 5 +- src/store/lang.ts | 4 +- src/views/api/components/ActiveModel.vue | 167 +++ src/views/api/components/McpDrawer.vue | 40 +- src/views/api/components/PluginCard.vue | 3 +- src/views/api/index.vue | 219 +++- .../createapp/components/AgentAppConfig.vue | 11 +- src/views/createapp/components/DebugApp.vue | 492 ++++---- src/views/createapp/components/McpDrawer.vue | 129 ++- src/views/createapp/components/types.ts | 72 +- src/views/createapp/components/workFlow.vue | 28 +- .../components/workFlowConfig/BranchNode.vue | 262 ----- .../workFlowConfig/ChoiceBranchDrawer.vue | 2 +- .../VariableAssignNodeDrawer.vue | 2 +- .../workFlowConfig/workFlowDialog.vue | 4 +- .../workFlowConfig/yamlEditDrawer.vue | 14 +- src/views/createapp/index.vue | 10 + .../dialogue/components/DialogueSession.vue | 245 +++- .../dialogue/components/MultiSelectTags.vue | 5 + src/views/dialogue/components/TitleBar.vue | 4 +- src/views/dialogue/types.ts | 1 + .../analysis.md | 57 + ...nents_dialoguePanel__DialogueFlow.vue.diff | 318 ++++++ .../analysis.md | 22 + ...nts_dialoguePanel__DialoguePanel.scss.diff | 12 + .../analysis.md | 28 + ...ents_dialoguePanel__DialoguePanel.vue.diff | 84 ++ .../analysis.md | 49 + ...ws_createapp_components__DebugApp.vue.diff | 611 ++++++++++ .../analysis.md | 31 + ...ws_createapp_components__workFlow.vue.diff | 246 ++++ .../analysis.md | 28 + ...ts_workFlowConfig__workFlowDialog.vue.diff | 22 + .../analysis.md | 30 + ...ts_workFlowConfig__yamlEditDrawer.vue.diff | 1004 +++++++++++++++++ .../analysis.md | 40 + ...logue_components__DialogueSession.vue.diff | 460 ++++++++ ...30\346\233\264\346\200\273\347\273\223.md" | 432 +++++++ 73 files changed, 6178 insertions(+), 1322 deletions(-) create mode 100644 src/assets/svgs/file_download.svg create mode 100644 src/assets/svgs/file_download_active.svg create mode 100644 src/assets/svgs/file_download_hover.svg create mode 100644 src/assets/svgs/html.svg create mode 100644 src/assets/svgs/jpeg.svg create mode 100644 src/assets/svgs/json.svg create mode 100644 src/assets/svgs/png.svg create mode 100644 src/assets/svgs/pptx.svg create mode 100644 src/assets/svgs/txt_green.svg create mode 100644 src/assets/svgs/warning.svg create mode 100644 src/assets/svgs/yaml.svg create mode 100644 src/assets/svgs/zip.svg create mode 100644 src/components/dialoguePanel/ParamsModel.vue create mode 100644 src/views/api/components/ActiveModel.vue delete mode 100644 src/views/createapp/components/workFlowConfig/BranchNode.vue create mode 100644 web_diff/file_analyses/src_components_dialoguePanel_DialogueFlow.vue/analysis.md create mode 100644 web_diff/file_analyses/src_components_dialoguePanel_DialogueFlow.vue/src_components_dialoguePanel__DialogueFlow.vue.diff create mode 100644 web_diff/file_analyses/src_components_dialoguePanel_DialoguePanel.scss/analysis.md create mode 100644 web_diff/file_analyses/src_components_dialoguePanel_DialoguePanel.scss/src_components_dialoguePanel__DialoguePanel.scss.diff create mode 100644 web_diff/file_analyses/src_components_dialoguePanel_DialoguePanel.vue/analysis.md create mode 100644 web_diff/file_analyses/src_components_dialoguePanel_DialoguePanel.vue/src_components_dialoguePanel__DialoguePanel.vue.diff create mode 100644 web_diff/file_analyses/src_views_createapp_components_DebugApp.vue/analysis.md create mode 100644 web_diff/file_analyses/src_views_createapp_components_DebugApp.vue/src_views_createapp_components__DebugApp.vue.diff create mode 100644 web_diff/file_analyses/src_views_createapp_components_workFlow.vue/analysis.md create mode 100644 web_diff/file_analyses/src_views_createapp_components_workFlow.vue/src_views_createapp_components__workFlow.vue.diff create mode 100644 web_diff/file_analyses/src_views_createapp_components_workFlowConfig_workFlowDialog.vue/analysis.md create mode 100644 web_diff/file_analyses/src_views_createapp_components_workFlowConfig_workFlowDialog.vue/src_views_createapp_components_workFlowConfig__workFlowDialog.vue.diff create mode 100644 web_diff/file_analyses/src_views_createapp_components_workFlowConfig_yamlEditDrawer.vue/analysis.md create mode 100644 web_diff/file_analyses/src_views_createapp_components_workFlowConfig_yamlEditDrawer.vue/src_views_createapp_components_workFlowConfig__yamlEditDrawer.vue.diff create mode 100644 web_diff/file_analyses/src_views_dialogue_components_DialogueSession.vue/analysis.md create mode 100644 web_diff/file_analyses/src_views_dialogue_components_DialogueSession.vue/src_views_dialogue_components__DialogueSession.vue.diff create mode 100644 "web_diff/\345\217\230\346\233\264\346\200\273\347\273\223.md" diff --git a/Dockerfile b/Dockerfile index 556ab0e5..e477cfd2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,20 +1,33 @@ +# 第一阶段:构建前端项目 FROM node:22.14.0-alpine WORKDIR /opt -COPY . . -# ENV HTTPS_PROXY= -ENV ELECTRON_MIRROR="https://npmmirror.com/mirrors/electron/" -RUN npm install pnpm -g --registry=https://registry.npmmirror.com && \ - pnpm install --registry=https://registry.npmmirror.com && \ - pnpm run build - +# 更换Alpine为国内镜像源(解决apk安装失败问题) +RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories +COPY . . +# 配置国内镜像,加速Electron及依赖下载 +ENV ELECTRON_MIRROR="https://npmmirror.com/mirrors/electron/" \ + ELECTRON_BUILDER_BINARIES_MIRROR="https://npmmirror.com/mirrors/electron-builder-binaries/" + +# 安装必要工具(git+ssh客户端+CA证书),并配置Git协议转换 +RUN apk add --no-cache git openssh-client ca-certificates \ + && git config --global url."https://github.com/".insteadOf git@github.com: \ + && git config --global url."https://".insteadOf git:// \ + && npm install pnpm -g --registry=https://registry.npmmirror.com \ + && pnpm config set registry https://registry.npmmirror.com \ + && pnpm config set network-timeout 600000 \ + && pnpm install \ + && pnpm run build + +# 第二阶段:部署到nginx FROM hub.oepkgs.net/openeuler/openeuler:24.03-lts-sp2 ENV TZ Asia/Shanghai RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && \ echo $TZ > /etc/timezone +# 替换OpenEuler镜像源为华为云,加速依赖安装 RUN sed -i 's|repo.openeuler.org|repo.huaweicloud.com/openeuler|g' /etc/yum.repos.d/openEuler.repo && \ sed -i '/metalink/d' /etc/yum.repos.d/openEuler.repo && \ sed -i '/metadata_expire/d' /etc/yum.repos.d/openEuler.repo && \ @@ -22,6 +35,7 @@ RUN sed -i 's|repo.openeuler.org|repo.huaweicloud.com/openeuler|g' /etc/yum.repo yum install -y nginx shadow-utils passwd gettext && \ yum clean all +# 从构建阶段复制产物到nginx COPY --from=0 /opt/dist /usr/share/nginx/html COPY --from=0 /opt/public /usr/share/nginx/html COPY --from=0 /opt/deploy/nginx.conf.tmpl /opt/nginx.conf.tmpl @@ -29,5 +43,5 @@ COPY --from=0 /opt/deploy/start.sh /opt/start.sh EXPOSE 8080 WORKDIR /opt - ENTRYPOINT [ "bash", "./start.sh" ] + diff --git a/build/linux/euler-copilot-web.spec b/build/linux/euler-copilot-web.spec index 30f83bd6..0859c55c 100644 --- a/build/linux/euler-copilot-web.spec +++ b/build/linux/euler-copilot-web.spec @@ -15,8 +15,8 @@ BuildArch: aarch64 x86_64 Name: euler-copilot-web -Version: 0.9.6 -Release: 6%{?dist} +Version: 0.10.0 +Release: 1%{?dist} License: MulanPSL-2.0 Summary: openEuler 智能化解决方案 Web 前端 Source0: %{name}-%{version}.tar.gz @@ -315,6 +315,9 @@ fi %changelog +* Mon Sep 15 2025 openEuler - 0.10.0-1 +- 升级至 0.10.0 版本 + * Mon Jun 16 2025 openEuler - 0.9.6-6 - 优化 RPM 打包配置:启用自动 provide 并过滤 Electron 内部库 @@ -331,4 +334,4 @@ fi - 增加安装后提示信息 * Thu Apr 17 2025 openEuler - 0.9.6-1 -- Initial release +- Initial release \ No newline at end of file diff --git a/build/linux/nginx.conf.local.tmpl b/build/linux/nginx.conf.local.tmpl index b10a8d2c..9a5f99db 100644 --- a/build/linux/nginx.conf.local.tmpl +++ b/build/linux/nginx.conf.local.tmpl @@ -6,12 +6,16 @@ server { client_body_buffer_size 5120M; client_max_body_size 5120M; + add_header 'Access-Control-Allow-Origin' '*'; # 允许所有源 + add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE'; # 允许的HTTP方法 + add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization'; # 允许的请求头 + add_header 'Access-Control-Expose-Headers' 'Content-Length, Content-Range'; # 允许前端访问的响应头 add_header X-XSS-Protection "1; mode=block"; add_header X-Content-Type-Options nosniff; add_header Referrer-Policy "no-referrer"; add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; always"; add_header Cache-Control "no-cache"; - add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: base64;"; + add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src * data: base64;"; resolver 8.8.8.8 8.8.4.4 valid=60s; resolver_timeout 5s; @@ -140,4 +144,4 @@ server { expires 30d; add_header Cache-Control public; } -} +} \ No newline at end of file diff --git a/deploy/nginx.conf.tmpl b/deploy/nginx.conf.tmpl index c2ef1a64..6ec3bd94 100644 --- a/deploy/nginx.conf.tmpl +++ b/deploy/nginx.conf.tmpl @@ -63,12 +63,17 @@ http { server_name localhost; charset utf-8; + add_header 'Access-Control-Allow-Origin' '*'; # 允许所有源 + add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE'; # 允许的HTTP方法 + add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization'; # 允许的请求头 + add_header 'Access-Control-Expose-Headers' 'Content-Length, Content-Range'; # 允许前端访问的响应头 + add_header X-XSS-Protection "1; mode=block"; add_header X-Content-Type-Options nosniff; add_header Referrer-Policy "no-referrer"; add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; always"; add_header Cache-Control "no-cache"; - add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: base64;"; + add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src * data: base64;"; limit_conn limitperip 50; resolver 8.8.8.8 8.8.4.4 valid=60s; @@ -117,6 +122,22 @@ http { proxy_pass ${FRAMEWORK_URL}/api/; } + + + location /wtd/ { + proxy_set_header X-Forwarded-For $http_x_real_ip; + add_header Cache-Control "no-cache,no-store,must-revalidate"; + add_header X-Accel-Buffering no; + proxy_buffering off; + proxy_intercept_errors on; + + error_page 404 /404.html; + limit_req zone=ratelimit burst=15 nodelay; + proxy_read_timeout 500s; + proxy_connect_timeout 500s; + + proxy_pass ${FRAMEWORK_URL}/witchaind/; + } location /witchaind/ { proxy_set_header X-Forwarded-For $http_x_real_ip; @@ -149,4 +170,4 @@ http { internal; } } -} +} \ No newline at end of file diff --git a/electron/main/common/locale.ts b/electron/main/common/locale.ts index a2d6daa9..6ec8b07e 100644 --- a/electron/main/common/locale.ts +++ b/electron/main/common/locale.ts @@ -30,7 +30,7 @@ export function processZhLocale(appLocale: string): string { // country codes, assume they use Simplified Chinese. // For other cases, assume they use Traditional. if (['hans', 'cn', 'sg', 'my'].includes(region)) { - return 'zh_cn'; + return 'zh'; } return 'zh_tw'; @@ -84,4 +84,4 @@ export async function resolveNlsConfiguration( osLocale, resolvedLanguage: osLocale, }; -} +} \ No newline at end of file diff --git a/electron/main/common/platform.ts b/electron/main/common/platform.ts index 1cd6acc8..cbb9e61f 100644 --- a/electron/main/common/platform.ts +++ b/electron/main/common/platform.ts @@ -9,7 +9,7 @@ // See the Mulan PSL v2 for more details. import * as nls from './nls'; -export const LANGUAGE_DEFAULT = 'zh_cn'; +export const LANGUAGE_DEFAULT = 'zh'; let _isWindows = false; let _isMacintosh = false; diff --git a/src/apis/appCenter/appCenterService.ts b/src/apis/appCenter/appCenterService.ts index 025e7899..b5553932 100644 --- a/src/apis/appCenter/appCenterService.ts +++ b/src/apis/appCenter/appCenterService.ts @@ -108,6 +108,18 @@ export const getPartAppConfgUser = (): Promise< return get('/api/user'); }; +/** + * 修改用户设置 + * + * @param params + * @returns + */ +export const updateUserInfo = (params: { + autoExecute: boolean; +}): Promise<[any, FcResponse | undefined]> => { + return post(`/api/user`, {autoExecute: params.autoExecute}); +}; + export const appCenterApi = { queryAppList, createOrUpdateApp, @@ -116,4 +128,5 @@ export const appCenterApi = { releaseSingleAppData, changeSingleAppCollect, getPartAppConfgUser, + updateUserInfo, }; diff --git a/src/apis/paths/account.ts b/src/apis/paths/account.ts index 93411cdf..af452185 100644 --- a/src/apis/paths/account.ts +++ b/src/apis/paths/account.ts @@ -24,6 +24,7 @@ export const authorizeUser = (): Promise< organization: string; revision_number: string | null; is_admin: boolean; + auto_execute?: boolean; }> | undefined ), diff --git a/src/apis/paths/conversation.ts b/src/apis/paths/conversation.ts index 1527ebb8..5e2ed858 100644 --- a/src/apis/paths/conversation.ts +++ b/src/apis/paths/conversation.ts @@ -11,16 +11,20 @@ import { get, put, post, del } from 'src/apis/server'; import type { FcResponse } from 'src/apis/server'; import { ConversationRecordList, ConversationList } from './type'; +import i18n from 'src/i18n'; + const BASE_URL = '/api/conversation'; +const { t } = i18n.global; /** * 停止生成 * @returns */ -export const stopGeneration = (): Promise< +export const stopGeneration = (taskId: string): Promise< [any, FcResponse | undefined] > => { - return post(`/api/stop`); + let url = taskId === undefined ? '/api/stop' : `/api/stop?taskId=${taskId}`; + return post(url); }; /** @@ -41,11 +45,13 @@ export const createSession = ({ appId, debug = false, llm_id = '', + title = t('history.new_conversation'), kb_ids = [], }: { appId: string; debug?: boolean; llm_id?: string; + title?: string; kb_ids?: string[]; }): Promise< [ @@ -58,7 +64,7 @@ export const createSession = ({ ), ] > => { - return post(BASE_URL, { appId, debug }, { llm_id, kb_ids }); + return post(BASE_URL, { appId, debug }, { llm_id, kb_ids, title }); }; /** diff --git a/src/apis/paths/mcp.ts b/src/apis/paths/mcp.ts index 31ec1eb5..19313797 100644 --- a/src/apis/paths/mcp.ts +++ b/src/apis/paths/mcp.ts @@ -10,6 +10,8 @@ const getMcpList = (params: { keyword?: string; page?: number; pageSize?: number; + isInstall?: boolean | null; + isActive?: boolean | null; }) => { return get<{ currentPage: number; @@ -69,21 +71,26 @@ const createOrUpdateMcpService = (params: { name: string; overview: string; description: string; - config: string; + config: object; mcpType: 'stdio' | 'sse' | 'stream'; }) => { return post<{ service_id: string; name: string; - }>(`${MCP_BASE_URL}`, params); + }>(`${MCP_BASE_URL}`, params, { 'Content-Type': 'application/json' }); }; const deleteMcpService = (id: string) => { return del<{ serviceId: string }>(`${MCP_BASE_URL}/${id}`); }; -const activeMcpService = (id: string, active: boolean) => { - return post<{ serviceId: string }>(`${MCP_BASE_URL}/${id}`, { active }); +const activeMcpService = (id: string, active: boolean, mcpEnv?: any) => { + return post<{ serviceId: string }>(`${MCP_BASE_URL}/${id}`, { active, mcpEnv }, + { 'Content-Type': 'multipart/form-data' },); +}; + +const installMcpService = (id: string) => { + return post<{ serviceId: string }>(`${MCP_BASE_URL}/${id}/install`); }; // 定义一个名为uploadMcpIcon的函数,接收两个参数serviceId和icon @@ -96,7 +103,7 @@ const uploadMcpIcon = (params: { formData.append('icon', params.icon); // 使用post方法向MCP_BASE_URL/${id}发送请求,请求体为{ icon: params.icon } return post<{ icon: string }>( - `${MCP_BASE_URL}`, + `${MCP_BASE_URL}/icon/${params.serviceId}`, formData, { serviceId: params.serviceId, @@ -111,5 +118,6 @@ export const mcpApi = { createOrUpdateMcpService, deleteMcpService, activeMcpService, + installMcpService, uploadMcpIcon }; diff --git a/src/apis/paths/type.ts b/src/apis/paths/type.ts index d610be29..3305110a 100644 --- a/src/apis/paths/type.ts +++ b/src/apis/paths/type.ts @@ -45,6 +45,12 @@ export interface Metadata { inputTokens: number; outputTokens: number; timeCost: number; + footNoteMetadataList?:Array<{ + footSource: string; + footType: string; + insertPosition: number; + releatedId: string; + }> } // 定义问答对数据结构 @@ -57,7 +63,8 @@ export interface ConversationRecord { content: Content; metadata: Metadata; comment: string; - created_at: string; + createdAt: string; + document?: any[]; } // 定义对话内问答列表数据结构 @@ -234,4 +241,4 @@ export interface KnowledgeList { kbName: string; description: string; isUsed: boolean; -} +} \ No newline at end of file diff --git a/src/apis/workFlow/workFlowService.ts b/src/apis/workFlow/workFlowService.ts index 518437fc..02e4912b 100644 --- a/src/apis/workFlow/workFlowService.ts +++ b/src/apis/workFlow/workFlowService.ts @@ -13,8 +13,9 @@ import { export const queryAllFlowService = (params: { page: number; pageSize: number; + language?: string | undefined; }): Promise<[any, FcResponse | undefined]> => { - return get('/api/flow/service'); + return get(`/api/flow/service?language=${params.language}`); }; /** diff --git a/src/assets/svgs/file_download.svg b/src/assets/svgs/file_download.svg new file mode 100644 index 00000000..283d57e6 --- /dev/null +++ b/src/assets/svgs/file_download.svg @@ -0,0 +1,13 @@ + + + Created with Pixso. + + + + + + + + + + diff --git a/src/assets/svgs/file_download_active.svg b/src/assets/svgs/file_download_active.svg new file mode 100644 index 00000000..c072db44 --- /dev/null +++ b/src/assets/svgs/file_download_active.svg @@ -0,0 +1,13 @@ + + + Created with Pixso. + + + + + + + + + + diff --git a/src/assets/svgs/file_download_hover.svg b/src/assets/svgs/file_download_hover.svg new file mode 100644 index 00000000..09d8b74f --- /dev/null +++ b/src/assets/svgs/file_download_hover.svg @@ -0,0 +1,13 @@ + + + Created with Pixso. + + + + + + + + + + diff --git a/src/assets/svgs/html.svg b/src/assets/svgs/html.svg new file mode 100644 index 00000000..80482d0f --- /dev/null +++ b/src/assets/svgs/html.svg @@ -0,0 +1,28 @@ + + + Created with Pixso. + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/svgs/jpeg.svg b/src/assets/svgs/jpeg.svg new file mode 100644 index 00000000..64c1c5ad --- /dev/null +++ b/src/assets/svgs/jpeg.svg @@ -0,0 +1,27 @@ + + + Created with Pixso. + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/svgs/json.svg b/src/assets/svgs/json.svg new file mode 100644 index 00000000..0bbf3ab1 --- /dev/null +++ b/src/assets/svgs/json.svg @@ -0,0 +1,28 @@ + + + Created with Pixso. + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/svgs/png.svg b/src/assets/svgs/png.svg new file mode 100644 index 00000000..f3cf7bc4 --- /dev/null +++ b/src/assets/svgs/png.svg @@ -0,0 +1,28 @@ + + + Created with Pixso. + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/svgs/pptx.svg b/src/assets/svgs/pptx.svg new file mode 100644 index 00000000..76dbe7ba --- /dev/null +++ b/src/assets/svgs/pptx.svg @@ -0,0 +1,28 @@ + + + Created with Pixso. + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/svgs/txt_green.svg b/src/assets/svgs/txt_green.svg new file mode 100644 index 00000000..6256758d --- /dev/null +++ b/src/assets/svgs/txt_green.svg @@ -0,0 +1,28 @@ + + + Created with Pixso. + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/svgs/warning.svg b/src/assets/svgs/warning.svg new file mode 100644 index 00000000..71554801 --- /dev/null +++ b/src/assets/svgs/warning.svg @@ -0,0 +1,8 @@ + + + Created with Pixso. + + + + + diff --git a/src/assets/svgs/yaml.svg b/src/assets/svgs/yaml.svg new file mode 100644 index 00000000..57247604 --- /dev/null +++ b/src/assets/svgs/yaml.svg @@ -0,0 +1,28 @@ + + + Created with Pixso. + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/svgs/zip.svg b/src/assets/svgs/zip.svg new file mode 100644 index 00000000..a971b21a --- /dev/null +++ b/src/assets/svgs/zip.svg @@ -0,0 +1,28 @@ + + + Created with Pixso. + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/commonFooter/CommonFooter.vue b/src/components/commonFooter/CommonFooter.vue index 527eb53a..48979905 100644 --- a/src/components/commonFooter/CommonFooter.vue +++ b/src/components/commonFooter/CommonFooter.vue @@ -8,12 +8,33 @@ const agreeDialogVisiable = ref(false); // 协议内容 const agreement = ref(''); const policy = ref(''); + +/** + * 获取当前语言 + */ +const getLocale = () => { + const localLang = localStorage.getItem('copilot_language'); + let locale = 'zh'; // 默认值 + + if (localLang) { + try { + const parsed = JSON.parse(localLang); + // 处理可能的 language 值:zh, zh_cn, zh-tw 等 + if (parsed.language) { + locale = parsed.language.startsWith('zh') ? 'zh' : parsed.language; + } + } catch (e) { + console.error('解析语言设置失败:', e); + } + } + return locale; +}; + /** * 读取协议 */ const readAgreement = async () => { - const localLang = localStorage.getItem('copilot_language'); - const locale = (localLang && JSON.parse(localLang).language) || 'zh_cn'; + const locale = getLocale(); const response = locale === 'en' ? await import('src/conf/agreement-en.md?raw') @@ -23,8 +44,7 @@ const readAgreement = async () => { }; const readPolicy = async () => { - const localLang = localStorage.getItem('copilot_language'); - const locale = (localLang && JSON.parse(localLang).language) || 'zh_cn'; + const locale = getLocale(); const response = locale === 'en' ? await import('src/conf/policy-en.md?raw') diff --git a/src/components/dialoguePanel/DialogueFlow.vue b/src/components/dialoguePanel/DialogueFlow.vue index 210d7f0b..f6100ea6 100644 --- a/src/components/dialoguePanel/DialogueFlow.vue +++ b/src/components/dialoguePanel/DialogueFlow.vue @@ -1,9 +1,16 @@ + + diff --git a/src/i18n/index.ts b/src/i18n/index.ts index 6139cede..e830bea8 100644 --- a/src/i18n/index.ts +++ b/src/i18n/index.ts @@ -1,16 +1,28 @@ import { createI18n } from 'vue-i18n'; // 语言包 -import zh_cn from './lang/zh-cn'; +import zh from './lang/zh-cn'; import en from './lang/en'; const localLang = localStorage.getItem('copilot_language'); -const locale = (localLang && JSON.parse(localLang).language) || 'zh_cn'; +let locale = 'zh'; // 默认值 + +if (localLang) { + try { + const parsed = JSON.parse(localLang); + // 处理可能的 language 值:zh, zh_cn, zh-tw 等 + if (parsed.language) { + locale = parsed.language.startsWith('zh') ? 'zh' : parsed.language; + } + } catch (e) { + console.error('解析语言设置失败:', e); + } +} const i18n = createI18n({ legacy: false, // 设置为 false,启用 composition API 模式 locale, messages: { - zh_cn, + zh, en, }, }); diff --git a/src/i18n/lang/en.ts b/src/i18n/lang/en.ts index 5b6e518a..9277a060 100644 --- a/src/i18n/lang/en.ts +++ b/src/i18n/lang/en.ts @@ -7,90 +7,90 @@ export default { edit: 'Edit {name}', delete: 'Delete', search: 'Search', - null: 'No Data', + null: 'No data available.', createTime: 'Create Time', - operate: 'Operate', + operate: 'Operation', copy: 'Copy', icon: 'Icon', tip: 'Tip', - delete_success: 'Delete successfully', + delete_success: 'Deleted successfully.', }, settings: { model: 'Model', setting: 'Settings', model_setting: 'Models', - api_key_management: 'API KEY', + api_key_management: 'API Key', added_model: 'Added Models', - model_provider: 'Provider', - select_model: 'Select Model', - new_api_key: 'New API KEY', + model_provider: 'Providers', + select_model: 'Model', + new_api_key: 'New API Key', secret_key: 'Secret Key', secret_key_desc: 'Description', - confirm_to_delete: 'Confirm to delete this model?', + confirm_to_delete: 'Are you sure you want to delete this model?', placeHolder: { - url: 'Please enter the URL', - api_key: 'Please enter the API KEY', - model_name: 'Please enter the model', - max_token: 'Please enter the maximum number of tokens', + url: 'URL', + api_key: 'Enter the API key.', + model_name: 'Enter the model.', + max_token: 'Enter the maximum number of tokens.', }, }, home: { name: 'openEuler Intelligence', }, - tabs: { - work: 'Work', - private: 'Private', - collect: 'Collect', - like: 'Like', - }, + //tabs: { + //work: 'Work', + //private: 'Private', + //collect: 'Collect', + //like: 'Like', + //}, menu: { - dialogue: 'Dialogue', + dialogue: 'Chats',//前端整体统一使用Chat,infotip和错误信息酌情使用conversation,全局不使用dialog plugin_center: 'Plugin Center', app_center: 'App Center', - sql: 'witchainD', + sql: 'WitChainD',//WitChainD全局统一首字母大写 settings: 'Settings', }, semantic: { close: 'Close', - semantic_interface: 'Semantic Interface', - max_select_mcp_server: 'Choose up to {num} mcp services', - mcp_service: 'MCP Service', + semantic_interface: 'Semantic Interfaces',//tab名使用复数Semantic Interfaces,Create Plugin下拉选项使用单数Semantic Interface,需新增一个词条 + max_select_mcp_server: 'Select up to {num} MCP services.',//前端MCP统一用service,后端调用使用server + mcp_service: 'MCP Services', create_plugin: 'Create Plugin', - semantic_interface_center: 'Semantic interface center', + semantic_interface_center: 'Semantic Interface Center', all_select: 'All', - interface_name: 'Interface name', - interface_introduction: 'Interface introduction', + interface_name: 'Interface Name', + interface_introduction: 'Interface Introduction', username: 'Username', interface_search: 'Search', interface_upload: 'Upload', - all_interface: 'All interface', - my_upload: 'My upload', - my_favorite: 'My favorite', + //all_interface: 'All interface', + my_upload: 'My Uploads', + my_favorite: 'My Favorites', interface_edit: 'Edit', interface_delete: 'Delete', - no_data: 'No data', + no_data: 'No data available.', upload_semantic_interface: 'Upload Interface', edit_semantic_interface: 'Edit Interface', view_semantic_interface: 'View Interface', - choose_file: 'Choose File', - tip1: 'Drag file here', - tip2: 'Format: YAML, Size < 2M', + choose_file: 'Select File', + tip1: 'Drag a file here or click Select File.', + tip2: 'Format: YAML; Size < 2 MB', + interface_path: 'Interface Path', + interface_description: 'Interface Description', cancel: 'Cancel', submit: 'Submit', edit: 'Edit', - analyze: 'Analyze', - interface_path: 'Interface path', - interface_description: 'Interface description', - pleaseEnter: 'Please enter', + analyze: 'Parse', + pleaseEnter: 'Please enter.', save: 'Save', + publish_condition: 'Debug all workflows in the application successfully before publishing it.', + icon: 'Icon', + baseMessage: 'Basic Info', preview: 'Preview', publish: 'Publish', - publish_condition: - 'Need to debug all workflows in the application successfully before publishing the application', - icon: 'Icon', - baseMessage: 'Base Message', - copyFailed: 'Copy failed', - checkFormat: 'Please check the format', + copyFailed: 'Copy failed.', + checkFormat: 'Check the format.', + check_connect: 'Please check the connecting cable.', }, plugin_center: { plugin_name: 'Plugin Name', @@ -106,91 +106,97 @@ export default { mcp_type: 'MCP Type', activate: 'Activate', deactivate: 'Deactivate', + install: 'Install', + install_success: 'Install Success', + not_installed: 'Not Installed', + cancel: 'Cancel', + all_select: 'All', + installed: 'Installed', + not_active: 'Not Active', + active: 'Active', + active_mcp_service: 'Active MCP Service', }, upload_icon: 'Upload Icon', - please_upload_icon: 'Please upload icon', - please_input_mcp_name: 'Please input MCP name', - please_input_mcp_overview: 'Please input MCP overview', - please_select_mcp_description: 'Please select MCP description', - server_detail: 'Server Details', - server_tool: 'Server Tools', - server_description: 'Server Description', + please_upload_icon: 'Upload an icon.', + please_input_mcp_name: 'Enter an MCP name.', + please_input_mcp_overview: 'Enter an MCP overview.', + please_select_mcp_description: 'Enter an MCP description.', + server_detail: 'Service Details', + server_tool: 'Service Tools', + server_description: 'Service Description', tool_input_schema: 'Tool Input Schema', tool_output_schema: 'Tool Output Schema', - confirm_delete_interface: 'Are you sure to delete this interface?', - confirm_delete_server: 'Are you sure to delete this server?', - no_brief_description_yet: 'No brief description yet', + confirm_delete_interface: 'Are you sure you want to delete this interface?', + confirm_delete_server: 'Are you sure you want to delete this service?', + no_brief_description_yet: 'No description available.', }, app: { app_center: 'App Center', - flow: 'Flow', + flow: 'Workflow', agent: 'Agent', all_select: 'All', - app_name: 'App name', - app_introduction: 'App introduction', + app_name: 'App Name', + app_introduction: 'App Introduction', username: 'Username', app_search: 'Search', app_create: 'Create App', - all_app: 'All App', - my_created: 'My creations', - my_favorite: 'My favorite', + all_app: 'All', + my_created: 'My Creations', + my_favorite: 'My Favorites', app_edit: 'Edit', app_delete: 'Delete', - no_data: 'No data', + no_data: 'No data available.', upload_app: 'Upload App', - edit_app: 'Edit App', - view_app: 'View App', + //edit_app: 'Edit App', + //view_app: 'View App', cancel: 'Cancel', - submit: 'Submit', + submit: 'Confirm', edit: 'Edit', - analyze: 'Analyze', + analyze: 'Parse', unpublished: 'Unpublished', - publishSuccess: 'Publish successfully', - publishFailed: 'Publish failed', + publishSuccess: 'Published successfully.', + publishFailed: 'Publish failed.', create_app: 'Create App', app_published: 'Published', app_config: 'App Config', - workflow_app: 'WorkFlow App', - create_workflow_app: 'Create WorkFlow App', - workflow_app_desc: '(待翻译)建拖拽式工作流应用,精筑生产级AI', - mcp_app: 'MCP App', + workflow_app: 'Create Workflow App', + mcp_app: 'Create Agent App', create_mcp_app: 'Create MCP App', - mcp_app_desc: '(待翻译)创插件Agent应用,速构个人AI助手', - confirm_delete_app: 'Are you sure to delete this application?', - create_or_edit_workflow_first: 'Please create/edit a workflow first', + create_workflow_app: 'Create Workflow App', + mcp_app_desc: 'Build plugin/Agent apps to quickly create personal AI assistants', + workflow_app_desc: 'Create drag-and-drop workflow apps to craft production-grade AI solutions', + confirm_delete_app: 'Are you sure you want to delete this APP?', + create_or_edit_workflow_first: 'Create/Edit a workflow first.', ui_preview: 'UI Preview', appName: 'Agent Name', - appName_input: 'Please enter the agent name', + appName_input: 'Enter an agent name.', appDescription: 'Agent Description', - appDescription_input: 'Please enter the agent description', - modelSelected: 'Model Selection', - modelSelected_input: 'Please select a model', - multi_Dialogue: 'Multi-turn Dialogue', - multi_Dialogue_select: 'select the number of turns', - ability_Configuration: 'Ability Configuration', - MCPService: 'MCP Service', - MCPService_add: 'Add the MCP service', - permissionConfiguration: 'Permission Configuration', - permission: 'Permission', - permission_public: 'Public (visible to everyone)', - permission_private: 'Private (visible only to you)', - somePeople: 'Visible to some people', - updateSuccessfully: 'Update successfully', - pleasemodifyTheName: - 'The workflow name already exists under the current application, please modify the name and try again', - deleteWorkflowSuccessfully: 'Delete workflow successfully', - createSuccessfully: 'Create successfully', - createFailed: 'Create failed', - successfully: 'Successfully', - editable: 'Editable', - failed: 'Failed', - inputContent: 'Input content', - outputContent: 'Output content', - link: 'Related links', - addLink: 'Add link', - addFiveLinks: 'Up to 5 links can be added', - optional: 'Optional', - searchUser: 'Search user', + appDescription_input: 'Enter an agent description.', + modelSelected: 'Model', + modelSelected_input: 'Select a model.', + multi_Dialogue: 'Multi-turn Conversation', + multi_Dialogue_select: 'Number of Turns', + ability_Configuration: 'Capabilities', + MCPService: 'MCP Services', + MCPService_add: 'Add MCP Service', + permissionConfiguration: 'Permissions', + permission: 'Permissions', + permission_public: 'Public (visible to all)', + permission_private: 'Private (visible only to me)', + somePeople: 'Custom (visible to selected users)', + updateSuccessfully: 'Updated successfully.', + pleasemodifyTheName: 'The workflow name already exists. Modify the name and try again.', + deleteWorkflowSuccessfully: 'Workflow deleted successfully.', + createSuccessfully: 'Created successfully.', + //successfully: 'Done.', + //failed: 'Failed.', + inputContent: 'Input', + outputContent: 'Output', + link: 'Related Links', + addLink: 'Add Link', + addFiveLinks: 'Add up to 5 links.', + optional: 'Available', + searchUser: 'Search for users.', selected: 'Selected', }, main: { @@ -200,52 +206,50 @@ export default { describe2: ", and I'm happy to be of service.", left_describe: 'Popular Apps', os_knowledge: 'CVE Hotfix Assistant', - os_knowledge_describe: 'CVE Hotfix', + os_knowledge_describe: 'CVE hotfix', openEuler_expertise: 'Diagnostics Assistant', - openEuler_expertise_describe: 'Intelligent Diagnostics', + openEuler_expertise_describe: 'Intelligent diagnostics', beyond_openEuler: 'Tuning Assistant', - beyond_openEuler_describe: 'Intelligent Tuning', - openEuler_use_cases: 'Container Stack', - openEuler_use_cases_describe: 'AI Container Stack assistant', - question: 'Recommendation qustions', - addQuestion: 'Add question', - addFiveQuestions: 'Up to 5 questions can be added', - smart_shell_describe: - 'Experience the future of OSs with our smart shell! Use natural language to diagnose and optimize your system.', - try_app: 'Enter the App Center', + beyond_openEuler_describe: 'Intelligent tuning', + openEuler_use_cases: 'Container Stack Assistant', + openEuler_use_cases_describe: 'AI container stack assistant', + question: 'Suggested Questions', + addQuestion: 'Add Question', + addFiveQuestions: 'Add up to 5 questions.', + //smart_shell_describe: '欢迎探索首款自然语言交互的智能操作系统:一句话,即享智能诊断与优化', + try_app: 'Access App Center', refresh: 'Refresh', - query_interpretation: 'Query Interpretation', - Automatic: 'Automatic', - ask_me_anything: - 'Ask me anything about openEuler. Press Shift+Enter to start a new line.', - you_might_want_to_know: 'You might want to know:', + //query_interpretation: '请选择识别方式', + //Automatic: '自动识别', + ask_me_anything: 'Ask me anything about openEuler. Press Shift+Enter to start a new line.', + //you_might_want_to_know: 'You might want to know: ', close: 'Close', confirm: 'Confirm', - email1: 'Email:', + email1: 'Email: ', email2: 'contact@openeuler.io', - opinions: - 'AI-generated responses are for reference only and do not reflect the opinions of openEuler.', + opinions: 'AI-generated responses are provided for reference only and do not reflect the opinions of openEuler.', service_agreement: 'Service Agreement', privacy_policy: 'Privacy Policy', contact_us: 'Contact Us', - version: 'Version 0.9.6-Beta', + version: 'Version 0.10.0-Beta', }, history: { + new_conversation: 'New Chat', new_chat: 'New Chat', - latestConversation: 'This is the latest conversation', + latestConversation: 'This is the latest conversation.', recent_chats: 'Recent Chats', delete_chats: 'Delete Chats', find_recent_chats: 'Find recent chats.', rename: 'Rename', - delete: 'Delete', // 注意:这个字段可能与批量删除操作冲突,具体取决于应用逻辑 - delete_successfully: 'Delete successfully', - delete_failed: 'Delete failed', + delete: 'Delete', + delete_successfully: 'Deleted successfully.', + delete_failed: 'Delete failed.', cancel: 'Cancel', - confirmation_message: 'Delete Chats?', - confirmation_message1: 'Tip', select_all: 'Select All', - confirmation_content1: 'The selected ', - confirmation_content2: ' chats will be deleted.', + confirmation_message: 'Delete Chat', + confirmation_message1: 'Tip', + confirmation_content1: 'The selected', + confirmation_content2: 'chats will be deleted.', delete_content1: 'This chat', delete_content2: 'will be deleted.', ok: 'OK', @@ -256,39 +260,36 @@ export default { time_filter_last_30_days: 'Last 30 days', time_filter_last_6_months: 'Last 6 months', links: 'Links', - hiss_basic_software_service_capability_platform: - 'HiSS Basic Software Service Capability Platform', // 注意:这里我保留了原始的大小写,但通常我们会将首字母大写 + hiss_basic_software_service_capability_platform: 'HiSS Basic Software Service Capability Platform', collapse: 'Collapse', - no_chat_history: 'No chat history available.', // 假设需要一个字段来表示没有历史对话的情况 - expand: 'Expand', // 假设界面中有展开历史对话的功能 + no_chat_history: 'No chat history available.', + expand: 'Expand', myApp: 'My Apps', + auto_execute: 'Auto Execute', }, feedback: { - noCopyMessage: 'No information to copy', - feedbackSuccesful: 'Feedback succeeded.', - regenerate: 'Regenerate', // 这里我保留了原样,因为通常键名不加双引号 - try_ask_me: 'Try ask me:', - eulercopilot_is_thinking: 'openEuler Intelligence is thinking…', - generation_stopped: 'Generation stopped.', + noCopyMessage: 'No information to copy.', + feedbackSuccesful: 'Feedback sent. Thank you!', + regenerate: 'Regenerate', + try_ask_me: 'Try ask me: ', + eulercopilot_is_thinking: 'openEuler Intelligence is thinking...', + generation_stopped: 'Response stopped.', stop: 'Stop', - stopSuccessful: 'Stop Successfully', + stopSuccessful: 'Paused', systemBusy: 'The system is busy. Please try again later.', - onlySupport: - "I'm sorry, but for now, we only support questions related to the fields of openEuler and Linux.", + onlySupport: "I'm sorry, but for now, we only support questions related to openEuler and Linux.", copy: 'Copy', copied_successfully: 'Copied successfully.', - copied_failed: 'Copied failed', - edit_successful: 'Edit successful', - edit_failed: 'Edit failed', + copied_failed: 'Copy failed.', + edit_successful: 'Edited successfully.', + edit_failed: 'Edit failed.', good_answer: 'Good Answer', bad_answer: 'Bad Answer', your_feedback_helps_us_improve: 'Your feedback helps us improve.', - the_information_is_inappropriate_or_illegal: - 'The information is inappropriate or illegal.', + the_information_is_inappropriate_or_illegal: 'The information is inappropriate or illegal.', the_answer_is_not_helpful: 'The answer is not helpful.', - i_found_an_error: 'I found an error!', - enter_the_link_to_the_correct_answer: - 'Enter the link to the correct answer.', + i_found_an_error: 'I found an error.', + enter_the_link_to_the_correct_answer: 'Enter the URL to the correct answer.', describe_the_error: 'Describe the error.', submit: 'Submit', report: 'Report', @@ -298,164 +299,167 @@ export default { Report: { pornographic_content: 'Pornographic content', account_violation: 'Account violation', - politically_sensitive_content: 'Politically sensitive content', + politically_sensitive_content: 'Political content', violence_or_terrorism: 'Violence or terrorism', - defamation_or_rumor_spreading: 'Defamation or rumor spreading', + defamation_or_rumor_spreading: 'Defamation or misinformation', insult_to_heroes_or_martyrs: 'Insult to heroes or martyrs', spam: 'Spam', ethnic_or_religious_incitement: 'Ethnic or religious incitement', disturbing_content: 'Disturbing content', abuse_or_harassment: 'Abuse or harassment', gambling_or_fraud: 'Gambling or fraud', - consumer_manipulation: 'Consumer manipulation', + consumer_manipulation: 'Misleading promotions', harm_to_minors: 'Harm to minors', illegal_or_prohibited_items: 'Illegal or prohibited items', - other_violations: 'Other violations', + other_violations: 'Other', }, Login: { login: 'Log In', logout: 'Log Out', - login_now: 'Log In Now', // 假设这里“立即登录”需要更明确的英文表达 - account: 'Account', - enter_account: 'Please enter your account', - password: 'Password', - enter_password: 'Please enter your password', - incorrect_password: 'Incorrect password', - api_key_management: 'API Key Management', - no_api_key_available: 'No API key is available', - create_api_key: 'Create API Key', - api_key_display_once: - 'This API Key will be displayed only once. Please copy and save it securely.', - revoke: 'Revoke', // 为了与英文操作保持一致,我将“撤销”明确为对API Key的操作 + //login_now: '立即登录', + //account: '账号', + //enter_account: '请输入账号', + //password: '密码', + //enter_password: '请输入密码', + //incorrect_password: '密码输入有误', + //api_key_management: 'API key management', + //no_api_key_available: '暂无可用的 API Key', + //create_api_key: '新建 API Key', + //api_key_display_once: '此 API Key 只展示一次,请复制后妥善保存。', + revoke: 'Revoke', refresh: 'Refresh', - unauthorized: 'Unauthorized page, please login first', + unauthorized: 'Unauthorized page. Please log in first.', }, question: { - open_euler_community_edition_categories: 'Community Edition Categories', + open_euler_community_edition_categories: 'What are the categories of openEuler community distributions?', lts_release_cycle_and_support: - 'LTS Release Cycle and Community Support Duration', + 'What is the release cycle and community support duration for openEuler LTS versions?', innovation_release_cycle_and_support: - 'Innovation Release Cycle and Community Support Duration', + 'What is the release cycle and community support duration for openEuler innovation versions?', container_cloud_platform_solution: - 'Container Cloud Platform Solution (CCPS) of the openEuler Community', - sec_gear_main_functions: 'Main Functions of secGear', - dde_description: 'What is DDE?', - lustre_description: 'What is Lustre?', - open_euler_testing_management_platform: - 'Testing Management Platform of the openEuler Community', + 'What is the Container Cloud Platform Solution (CCPS) of the openEuler community?', + sec_gear_main_functions: 'What are the three main capabilities of secGear?', + dde_description: 'What is the DDE component?', open_euler_pkgship: 'What is pkgship in openEuler?', - open_euler_software_package_introduction_principles: - 'Introduction Principles of openEuler Software Packages', - download_rpm_without_installing: - 'How to download an RPM package to the local system without installing it in openEuler?', - count_the_occurrences_of_the_hello: - "Generate a shell command to count the occurrences of the 'hello' string in the 'test.txt' file.", - convert_uppercase_to_lowercase: - 'Give me a shell command to convert uppercase letters to lowercase in text files in the current directory and its subdirectories', - list_files_with_specific_permissions: - 'Give me a shell command to find and list files with specific permissions in the current directory', - search_error_keyword_with_context: - "Give me a shell command to search for the keyword 'error' in text files in the /home directory and its subdirectories, and output the matching lines along with the 3 lines before and after them to a file named 'result.txt'", - clear_dependencies_for_software_package: - 'How to clear dependencies for software packages in openEuler?', - gpgcheck_purpose_in_dnf: - 'What is the purpose of the gpgcheck parameter in DNF in openEuler?', - installonly_limit_function_in_dnf: - 'What is the function of the installonly_limit parameter in DNF in openEuler?', - clean_requirement_on_remove_function_in_dnf: - 'What is the function of the clean_requirement_on_remove parameter in DNF in openEuler?', - hunan_tobacco_monopoly_applications_on_openeuler: - 'What are the applications of Hunan Tobacco Monopoly based on openEuler?', - xsky_applications_on_openeuler: - 'What are the applications of XSKY based on openEuler?', }, upload: { upload_tip_text: - 'Maximum 10 files, 64 MB in total. Formats: pdf, docx, doc, txt, md, xlsx', - uploading: 'Upload...', - upload_fail: 'Upload failed', + 'Maximum 10 files, 64 MB in total. Formats: PDF, DOCX, DOC, TXT, MD, XLSX', + uploading: 'Uploading...', + upload_fail: 'Upload failed.', resolving: 'Parsing...', - resolve_fail: 'Parsing failed', - error_type_msg: 'Upload failed, Invalid file type.', - error_size_msg: - 'Upload failed, The number or size of files exceeds the upper limits.', - error_name_msg: 'Upload failed, Duplicate file names exit.', + resolve_fail: 'Parsing failed.', + error_type_msg: 'Upload failed: File type not supported.', + error_size_msg: 'Upload failed: File count or size exceeds the upper limit.', + error_name_msg: 'Upload failed: Duplicate file name exits.', aside_session_file_count_front: '', aside_session_file_count_back: 'files', + quote_front: 'Quote', + quote_back: 'pieces of knowledge base materials', + reference_source: 'Reference source', }, apikey: { - save_apikey: - 'This API KEY is only displayed once, please copy and save it properly', - no_apikey: 'No available API key', - create_apikey: 'Create new API key', + save_apikey: 'This API key is displayed only once. Please copy and keep it secure.', + no_apikey: 'No available API key.', + create_apikey: 'New API Key', cancel: 'Cancel', - confirm: 'Please enter the description of the API key', }, witChainD: { witChainD: 'WitChainD', - witChainD_id: 'id', - describe_the_witChainD: 'Please enter witChainD id', - find_witChainD: 'Please enter witChainD name/id', - knowledge: 'Knowledge', - fiveKnowledge: 'You can select up to 5 knowledge bases', + witChainD_id: 'ID', + describe_the_witChainD: 'Enter a WitChainD ID.', + find_witChainD: 'Enter a WitChainD name or ID.', + knowledge: 'Knowledge Base', + fiveKnowledge: 'Select up to 5 knowledge bases.', }, flow: { flow_start: 'Workflow in progress', - flow_end: 'Workflow completed', - flow_params_error: 'Missing parameters', + flow_end: 'Workflow completed.', + flow_cancel: 'Cancel running', + flow_risk: 'Risk statement', + flow_params_error: 'Parameter missing', flow_pause: 'Workflow paused', - edit_flow: 'Edit workflow', - edit_workflow: 'Edit workflow', - flow_name: 'Workflow name', - flow_description: 'Workflow description', - create_flow: 'Create workflow', - step_configuration: 'Step configuration', - debug_after_connection: - 'Debugging can only be performed after node connection is complete', - no_flow: 'No workflow', - choose_flow: 'Please choose a workflow', + edit_flow: 'Edit', + edit_workflow: 'Edit Workflow', + flow_name: 'Workflow Name', + flow_description: 'Workflow Description', + create_flow: 'Create Workflow', + step_configuration: 'Step Configuration', + debug_after_connection: 'Connect to a node before debugging.', + no_flow: 'No workflow available.', + choose_flow: 'Select a workflow.', debug: 'Debug', - enterWorkflowName: 'Please enter workflow name', + enterWorkflowName: 'Enter a workflow name.', + enterWorkflowDesc: 'Enter a workflow Description.', default: '', - success: 'successful', - error: 'failed', + success: 'Workflow succeeded', + error: 'Workflow failed', running: 'Running', pending: 'Running', result: 'Result', input: 'Input', output: 'Output', params: 'Parameters', - supplementaryParameters: 'Supplementary Parameters', - add_description: 'Add description...', - settings: 'Settings', - input_params: 'Input Fields', - param_name: 'Parameter Name', - param_label: 'Parameter Label', - select_type: 'Select Type', - required: 'Required', - default_value: 'Default Value', - no_input_params: 'No input parameters', - add_first_param: 'Add first parameter', - duplicate_param_key: 'Parameter name cannot be duplicate', - save_failed: 'Save failed', - add_param: 'Add Parameter', - edit_param: 'Edit Parameter', - param_type: 'Parameter Type', - param_settings: 'Parameter Settings', - param_name_required: 'Parameter name is required', - invalid_param_name: 'Invalid parameter name format', + supplementaryParameters: 'Additional Parameters', + parameterConfiguration: 'Parameter Configuration', + additionalNotes: 'Additional notes', }, pagination: { prev: 'Previous', next: 'Next', }, zoom: { - reduce: "Zoom Out", - amplify: "Zoom In", - adaptive: "Fit to Screen", - scaleTo50: "Zoom to 50%", - scaleTo100: "Zoom to 100%", - scaleTo150: "Zoom to 150%", - scaleTo200: "Zoom to 200%" + reduce: 'Zoom Out', + amplify: 'Zoom In', + adaptive: 'Fit to Screen', + scaleTo50: 'Zoom to 50%', + scaleTo100: 'Zoom to 100%', + scaleTo150: 'Zoom to 150%', + scaleTo200: 'Zoom to 200%', + }, + yaml: { + else: 'else', + if: 'if', + else_if: 'else if', + please_select: 'Please select', + please_select_condition: 'Please select the condition.', + parameter_value: 'Input or reference parameter value', + add_conditional_branches: 'Add conditional branches', + and: 'and', + or: 'or', + add: 'Add', + input_type_error: 'Input type error', + please_improve_the_conditions: 'Please improve the conditions.', + }, + opertion: { + api: 'HTTP request', + mcp: 'MCP', + sql: 'SQL query', + gcraph: 'Chart', + llm: 'Large model', + rag: 'Knowledge base', + suggestion: 'Question recommendation', + skip_round: 'Skip Round', + exit_loop: 'Exit Loop', + variable_assign: 'Variable Assignment', + file_extract: 'File Extractor', + equal: 'Equal', + not_equal: 'Not equal', + contains: 'Contains', + does_not_contain: 'Does not contain', + starts_with: 'Starts with', + ends_with: 'Ends with', + length_equal: 'Length equal', + length_greater_than: 'Length greater than', + length_greater_than_or_equal: 'Length greater than or equal', + length_less_than: 'Length less than', + length_less_than_or_equal: 'Length less than or equal', + regex_match: 'Regex match', + greater_than: 'Greater than', + less_than: 'Less than', + greater_than_or_equal: 'Greater than or equal', + less_than_or_equal: 'Less than or equal', + contains_key: 'Contains key', + does_not_contain_key: 'Does not contain key', } -}; +}; \ No newline at end of file diff --git a/src/i18n/lang/zh-cn.ts b/src/i18n/lang/zh-cn.ts index 2af28873..220efb4a 100644 --- a/src/i18n/lang/zh-cn.ts +++ b/src/i18n/lang/zh-cn.ts @@ -90,6 +90,7 @@ export default { publish: '发布', copyFailed: '复制失败', checkFormat: '请检查格式', + check_connect: '请检查连接线', }, plugin_center: { plugin_name: '插件名称', @@ -105,6 +106,15 @@ export default { mcp_type: 'MCP类型', activate: '激活', deactivate: '取消激活', + install: '安装', + install_success: '安装成功', + not_installed: '未安装', + cancel: '取消', + all_select: '全部', + installed: '已安装', + not_active: '未激活', + active: '已激活', + active_mcp_service: '激活MCP服务', }, upload_icon: '上传图标', please_upload_icon: '请上传图标', @@ -224,9 +234,10 @@ export default { service_agreement: '服务协议', privacy_policy: '隐私政策', contact_us: '联系我们', - version: '版本号0.9.6-内测版', + version: '版本号0.10.0-内测版', }, history: { + new_conversation: '新会话', new_chat: '新建对话', latestConversation: '已是最新对话', recent_chats: '历史记录', @@ -257,6 +268,7 @@ export default { no_chat_history: '暂无历史对话', expand: '展开', myApp: '我的应用', + auto_execute: '自动执行', }, feedback: { noCopyMessage: '无可复制的信息', @@ -372,6 +384,9 @@ export default { error_name_msg: '上传失败,存在重名文件。', aside_session_file_count_front: '共', aside_session_file_count_back: '个文档', + quote_front: '引用', + quote_back: '篇知识库资料', + reference_source: '引用来源', }, apikey: { save_apikey: '此 API KEY 只展示一次,请复制后妥善保存', @@ -391,6 +406,8 @@ export default { flow: { flow_start: '工作流进行中', flow_end: '工作流结束', + flow_cancel: '取消运行', + flow_risk: '风险提示', flow_params_error: '缺少参数', flow_pause: '工作流暂停', edit_flow: '编辑工作流', @@ -404,6 +421,7 @@ export default { choose_flow: '请选择工作流', debug: '调试', enterWorkflowName: '请输入工作流名称', + enterWorkflowDesc: '请输入工作流名称', default: '', success: '运行成功', error: '运行失败', @@ -414,36 +432,65 @@ export default { output: '输出', params: '参数', supplementaryParameters: '补充参数', - add_description: '添加描述...', - settings: '设置', - input_params: '输入字段', - param_name: '参数名', - param_label: '参数标签', - select_type: '选择类型', - required: '必填', - default_value: '默认值', - no_input_params: '暂无输入参数', - add_first_param: '添加第一个参数', - duplicate_param_key: '参数名不能重复', - save_failed: '保存失败', - add_param: '添加参数', - edit_param: '编辑参数', - param_type: '参数类型', - param_settings: '参数设置', - param_name_required: '参数名必填', - invalid_param_name: '参数名格式不正确', + parameterConfiguration: '参数配置', + additionalNotes: '补充说明', }, pagination: { prev: 'Previous', next: 'Next', }, zoom: { - reduce: "缩小", - amplify: "放大", - adaptive: "自适应", - scaleTo50: "缩放到50%", - scaleTo100: "缩放到100%", - scaleTo150: "缩放到150%", - scaleTo200: "缩放到200%" + reduce: '缩小', + amplify: '放大', + adaptive: '自适应', + scaleTo50: '缩放到50%', + scaleTo100: '缩放到100%', + scaleTo150: '缩放到150%', + scaleTo200: '缩放到200%', + }, + yaml: { + else: '否则', + if: '如果', + else_if: '否则如果', + please_select: '请选择', + please_select_condition: '请选择条件', + parameter_value: '输入或引用参数值', + add_conditional_branches: '添加条件分支', + and: '且', + or: '或', + add: '新增', + input_type_error: '输入类型错误', + please_improve_the_conditions: '请完善条件', + }, + opertion: { + api: 'HTTP请求', + mcp: 'MCP', + sql: 'SQL查询', + gcraph: '图表', + llm: '大模型', + rag: '知识库', + suggestion: '问题推荐', + skip_round: '跳过本轮', + exit_loop: '退出循环', + variable_assign: '变量赋值', + file_extract: '文件提取器', + equal: '等于', + not_equal: '不等于', + contains: '包含', + does_not_contain: '不包含', + starts_with: '起始等于', + ends_with: '结束等于', + length_equal: '长度等于', + length_greater_than: '长度大于', + length_greater_than_or_equal: '长度大于等于', + length_less_than: '长度小于', + length_less_than_or_equal: '长度小于等于', + regex_match: '正则匹配', + greater_than: '大于', + less_than: '小于', + greater_than_or_equal: '大于等于', + less_than_or_equal: '小于等于', + contains_key: '包含键', + does_not_contain_key: '不包含键', } -}; +}; \ No newline at end of file diff --git a/src/store/account.ts b/src/store/account.ts index fe7ab5e5..b13c06f5 100644 --- a/src/store/account.ts +++ b/src/store/account.ts @@ -25,11 +25,13 @@ export const useAccountStore = defineStore('account', () => { organization: string; user_sub: string; is_admin?: boolean; + auto_execute?: boolean; }>({ username: '', revsionNumber: null, organization: '', user_sub: '', // 用户唯一标识 + auto_execute: false, }); /** @@ -98,6 +100,7 @@ export const useAccountStore = defineStore('account', () => { userinfo.organization = organization; userinfo.revsionNumber = revision_number; userinfo.is_admin = res.result.is_admin; + userinfo.auto_execute = res.result.auto_execute; return true; } return false; diff --git a/src/store/conversation.ts b/src/store/conversation.ts index 999a2e5d..7eb25dba 100644 --- a/src/store/conversation.ts +++ b/src/store/conversation.ts @@ -22,7 +22,7 @@ import { FlowType, } from 'src/views/dialogue/types'; import { api } from 'src/apis'; -import { successMsg, errorMsg } from 'src/components/Message'; +import { successMsg } from 'src/components/Message'; import i18n from 'src/i18n'; import { Application } from 'src/apis/paths/type'; import { handleAuthorize } from 'src/apis/tools'; @@ -51,12 +51,12 @@ export const useSessionStore = defineStore('conversation', () => { // #endregion - const { language } = useLangStore(); - + const langStore = useLangStore(); // 是否暂停回答 const isPaused = ref(false); // 会话列表 const conversationList = ref([]); + const currentMessage = ref({}); const app = ref({ appId: '', name: '', @@ -66,7 +66,7 @@ export const useSessionStore = defineStore('conversation', () => { // ai回复是否还在生成中 const isAnswerGenerating = ref(false); - // 🔑 移除全局收集器,改为在每个conversationItem中存储文件 + const currentTaskId = ref(null); // 方法集合 - 用于处理不同类型的event message const dataTransfers = { @@ -87,7 +87,9 @@ export const useSessionStore = defineStore('conversation', () => { conversationItem: RobotConversationItem, message: Record, ) => { - conversationItem.message[conversationItem.currentInd] += message.content; + if (!conversationItem.files) { + conversationItem.files = []; + } conversationItem.files = [...conversationItem.files, message.content]; }, suggestionFunc: ( @@ -144,100 +146,12 @@ export const useSessionStore = defineStore('conversation', () => { (item) => item.id === flow.stepId, ); if (target) { - - // 统一的文件收集逻辑,存储到当前conversationItem.files中 - const addFileToConversationItem = (fileData: any) => { - // 确保conversationItem.files是数组 - if (!conversationItem.files) { - conversationItem.files = []; - } - - // 严格的去重检查:基于file_id和filename - const existingFile = conversationItem.files.find((item: any) => - item.file_id === fileData.file_id && item.filename === fileData.filename - ); - - if (!existingFile) { - const fileItem = { - file_id: fileData.file_id, - filename: fileData.filename, - file_type: fileData.file_type, - file_size: fileData.file_size, - variable_name: fileData.variable_name, - content: fileData.content, - step_name: target.title // 记录来源步骤 - }; - - conversationItem.files.push(fileItem); - return true; - } else { - return false; - } - }; - - // 🔑 检查不同的文件格式并收集 - let hasFileData = false; - - // 格式1:单个文件对象 - if (typeof message.content === 'object' && message.content && - (message.content as any).file_id && (message.content as any).filename && (message.content as any).content) { - addFileToConversationItem(message.content); - hasFileData = true; - } - // 格式2:多文件格式 {type: 'files', files: [...]} - else if (typeof message.content === 'object' && message.content && - (message.content as any).type === 'files' && (message.content as any).files && - Array.isArray((message.content as any).files)) { - (message.content as any).files.forEach((fileData: any) => { - if (fileData.file_id && fileData.filename && fileData.content) { - addFileToConversationItem(fileData); - hasFileData = true; - } - }); - } - // 格式3:旧格式文件 {files: [...]} - else if (typeof message.content === 'object' && message.content && - (message.content as any).files && Array.isArray((message.content as any).files)) { - (message.content as any).files.forEach((fileData: any) => { - if (fileData.file_id && fileData.filename && fileData.content) { - addFileToConversationItem(fileData); - hasFileData = true; - } - }); - } - - // 设置步骤输出显示 - if (hasFileData) { - // 保持原始文件格式,让FlowCode能够检测和显示 target.data.output = message.content; - } else { - // 普通数据输出 - target.data.output = message.content; - } - - target.status = flow.stepStatus; + target.status = 'success'; // 工作流添加每阶段的时间耗时 target['costTime'] = metadata.timeCost; - - // 修复:更新整体flowdata状态逻辑 - if (conversationItem.flowdata) { - if (flow.stepStatus === 'error') { - // 如果有错误,立即设置为错误状态 - conversationItem.flowdata.status = 'error'; - } else if (flow.stepStatus === 'success') { - // 如果步骤成功,检查是否所有步骤都完成了 - const allSteps = conversationItem.flowdata.data[0]; - const allCompleted = allSteps.every(step => - step.status === 'success' || step.status === 'error' - ); - - if (allCompleted) { - // 所有步骤都完成了,检查是否有错误 - const hasError = allSteps.some(step => step.status === 'error'); - conversationItem.flowdata.status = hasError ? 'error' : 'success'; - } - // 如果还有步骤未完成,保持running状态 - } + if (flow.step_status === 'error' && conversationItem.flowdata) { + conversationItem.flowdata.status = flow.stepStatus; } } }, @@ -246,15 +160,9 @@ export const useSessionStore = defineStore('conversation', () => { message: Record, isFlowDebug: boolean, ) => { - const content = (message.content || {}) as Record; const contentFlow = (content.flow || {}) as Record; const messageFlow = (message.flow || {}) as Record; - - // 🔑 关键修复:在 flow.stop 时就停止生成状态 - conversationItem.isFinish = true; - isAnswerGenerating.value = false; - if (isFlowDebug) { // 如果是工作流的调试功能-添加status/data conversationItem.flowdata = { @@ -265,16 +173,15 @@ export const useSessionStore = defineStore('conversation', () => { display: true, data: conversationItem?.flowdata?.data, }; - - $bus.emit('debugChatEnd'); } else if (content.type !== 'schema' && conversationItem.flowdata) { // 删除 end 逻辑 conversationItem.flowdata = { - id: contentFlow.stepId, - title: i18n.global.t('flow.flow_end'), - progress: contentFlow.stepProgress, - status: 'success', + id: messageFlow.stepId, + title: currentTaskId ? i18n.global.t('flow.flow_start') : i18n.global.t('flow.flow_end'), + progress: messageFlow.stepProgress, + status: currentTaskId ? messageFlow.flowStatus : 'success', display: true, + taskId: currentTaskId.value, data: conversationItem.flowdata.data, }; } else { @@ -287,67 +194,101 @@ export const useSessionStore = defineStore('conversation', () => { conversationItem.paramsList = content.data; } } - - }, - loopProgress: ( - conversationItem: RobotConversationItem, - message: Record, - ) => { - const content = (message.content || {}) as Record; - - // 更新循环进度显示,但不停止生成状态 - if (conversationItem.flowdata) { - conversationItem.flowdata.progress = `${content.iteration}/${content.total}`; - conversationItem.flowdata.status = 'running'; // 确保状态保持为运行中 - } - }, - loopCompleted: ( - conversationItem: RobotConversationItem, - message: Record, - isFlowDebug: boolean, - ) => { - const content = (message.content || {}) as Record; - - // 🔑 关键修改:循环完成时立即停止生成状态 - conversationItem.isFinish = true; - isAnswerGenerating.value = false; - - // 更新flowdata状态 - if (conversationItem.flowdata) { - conversationItem.flowdata.status = 'success'; - conversationItem.flowdata.progress = `${content.iteration_count}/${content.iteration_count}`; - } - - // 如果是工作流调试,发送完成事件 - if (isFlowDebug) { - $bus.emit('debugChatEnd'); - } }, dataDone: ( conversationItem: RobotConversationItem, isFlowDebug: boolean, ) => { - if (excelPath.value.length > 0) { conversationItem.message[conversationItem.currentInd] += `

下载地址:${excelPath.value}`; } - - // 🔑 只有在还没完成时才设置完成状态 - if (!conversationItem.isFinish) { - conversationItem.isFinish = true; - isAnswerGenerating.value = false; - - // 如果是工作流的调试功能-调试对话结束时-发送调试对话结束 - if (isFlowDebug) { - $bus.emit('debugChatEnd'); - } + conversationItem.isFinish = true; + isAnswerGenerating.value = false; + // 如果是工作流的调试功能-调试对话结束时-发送调试对话结束 + if (isFlowDebug) { + $bus.emit('debugChatEnd'); + } + }, + waitingForStart: ( + conversationItem: RobotConversationItem, + message: Record, + ) => { + const flow = (message.flow || {}) as Record; + const content = (message.content || {}) as Record; + conversationItem.flowdata = { + id: flow.stepId, + title: flow.stepName, + status: flow.stepStatus, + taskId: currentTaskId, + data: { + exData: content, + }, + }; + if (conversationItem.flowdata) { + conversationItem.flowdata.progress = flow.stepProgress; + conversationItem.flowdata.status = flow.stepStatus; + } + }, + waitingForParam: ( + conversationItem: RobotConversationItem, + message: Record, + ) => { + const flow = (message.flow || {}) as Record; + const content = (message.content || {}) as Record; + conversationItem.flowdata = { + id: flow.stepId, + title: flow.stepName, + status: flow.stepStatus, + taskId: currentTaskId, + data: { + exParam: content, + }, + }; + if (conversationItem.flowdata) { + conversationItem.flowdata.progress = flow.stepProgress; + conversationItem.flowdata.status = flow.stepStatus; } }, + flowCancel: ( + conversationItem: RobotConversationItem, + message: Record, + ) => { + const content = (message.content || {}) as Record; + const contentFlow = (content.flow || {}) as Record; + const messageFlow = (message.flow || {}) as Record; + + // 取消运行 + conversationItem.flowdata = { + id: contentFlow.stepId, + title: i18n.global.t('flow.flow_cancel'), + progress: contentFlow.stepProgress, + status: messageFlow.stepStatus, + display: true, + data: conversationItem?.flowdata?.data, + }; + }, + flowSuccess: ( + conversationItem: RobotConversationItem, + message: Record, + isFlowDebug: boolean, + ) => { + const content = (message.content || {}) as Record; + const contentFlow = (content.flow || {}) as Record; + const messageFlow = (message.flow || {}) as Record; + conversationItem.flowdata = { + id: contentFlow.stepId, + title: i18n.global.t('flow.flow_end'), + progress: contentFlow.stepProgress, + status: 'success', + display: true, + data: conversationItem?.flowdata?.data, + }; + }, }; // chat message回调 - const handleMsgDataShow = async ( + const handleMsgDataShow = ( params: Record, msgData: Record, conversationItem: RobotConversationItem, @@ -358,87 +299,27 @@ export const useSessionStore = defineStore('conversation', () => { return; } const rawMsgData = msgData.data as string; - if (rawMsgData === '[DONE]') { dataTransfers.dataDone(conversationItem, !!params.type); return; } - - - - // 🔑 重要修复:处理带详细信息的ERROR消息 - if (rawMsgData.startsWith('[ERROR]')) { - console.error('❌ 收到ERROR事件,停止对话生成:', rawMsgData); - conversationItem.isFinish = true; - isAnswerGenerating.value = false; - - // 🔑 重要:按正确顺序停止对话 - // 1. 首先中断前端fetchEventSource连接 - controller.abort(); - - // 2. 然后调用后端停止接口,清理后端WebSocket连接 - try { - const resp = await api.stopGeneration(); - if (resp?.[1]?.code === 200) { - // 后端停止成功 - } - } catch (stopError) { - console.error('调用停止接口失败:', stopError); - // 即使停止接口失败,也继续处理错误显示 - } - - // 提取错误信息 - const errorMessage = rawMsgData.replace('[ERROR]', '').trim(); - - // 🔑 修复:确保错误信息正确显示在对话内容中 - // 初始化message数组和currentInd,如果不存在的话 - if (!conversationItem.message || conversationItem.message.length === 0) { - conversationItem.message = ['']; - conversationItem.currentInd = 0; - } - - const currentIndex = conversationItem.currentInd || 0; - - // 确保currentIndex对应的message元素存在 - if (!conversationItem.message[currentIndex]) { - conversationItem.message[currentIndex] = ''; - } - - // 设置错误信息到对话内容中 - conversationItem.message[currentIndex] = errorMessage || '系统错误,请稍后再试'; - - // 🔑 重要:显示错误提示给用户 - errorMsg(errorMessage || '系统错误,请稍后再试'); + if (rawMsgData === '[ERROR]') { + dataTransfers.dataDone(conversationItem, !!params.type); return; } // 同一时间戳传来的decodeValue是含有三条信息的合并,so需要分割 - // 这里json解析,添加错误处理 - let message: any; - try { - message = JSON.parse(rawMsgData || '{}'); - } catch (parseError) { - console.error('📨 JSON解析失败:', { - rawData: rawMsgData, - error: parseError, - length: rawMsgData?.length - }); - // 如果解析失败,尝试处理为文本消息 - if (rawMsgData && rawMsgData.trim()) { - dataTransfers.textAdd(conversationItem, { - event: 'text.add', - content: { text: rawMsgData } - }); - } - return; - } + // 这里json解析 + const message = JSON.parse(rawMsgData || '{}'); const eventType = message['event']; if ('metadata' in message) { conversationItem.metadata = message.metadata; } + currentTaskId.value = message.taskId; if ('event' in message) { switch (eventType) { case 'text.add': + currentMessage.value = message; dataTransfers.textAdd(conversationItem, message); break; case 'heartbeat': @@ -448,7 +329,7 @@ export const useSessionStore = defineStore('conversation', () => { break; case 'document.add': // 遇到文档添加事件,先省略 - // dataTransfers.documentAdd(conversationItem, message); + dataTransfers.documentAdd(conversationItem, message); break; case 'Suggestion': dataTransfers.suggestionFunc(conversationItem, message); @@ -456,7 +337,7 @@ export const useSessionStore = defineStore('conversation', () => { case 'init': //初始化获取 metadata conversationItem.metadata = message.metadata; - conversationItem.createdAt = message.content.created_at; + conversationItem.createdAt = message.content.createdAt; conversationItem.groupId = message.groupId; break; case 'flow.start': @@ -469,17 +350,25 @@ export const useSessionStore = defineStore('conversation', () => { case 'step.output': dataTransfers.stepOutput(conversationItem, message); break; + case 'step.waiting_for_start': + // 事件流等待开始 + dataTransfers.waitingForStart(conversationItem, message); + break; + case 'step.waiting_for_param': + // 事件流等待参数 + dataTransfers.waitingForParam(conversationItem, message); + break; + case 'flow.cancel': + // 事件流取消 + dataTransfers.flowCancel(conversationItem, message); + break; case 'flow.stop': //时间流结束 dataTransfers.flowStop(conversationItem, message, !!params.type); break; - case 'loop.progress': - //循环进度更新 - dataTransfers.loopProgress(conversationItem, message); - break; - case 'loop.completed': - //循环完成 - dataTransfers.loopCompleted(conversationItem, message, !!params.type); + case 'flow.success': + //时间流结束 + dataTransfers.flowSuccess(conversationItem, message, !!params.type); break; default: break; @@ -512,7 +401,7 @@ export const useSessionStore = defineStore('conversation', () => { conversationId: params.conversationId, features: features, groupId: params.groupId, - language, + language: langStore.language, question: params.question, // record_id: params.qaRecordId, }), @@ -531,6 +420,7 @@ export const useSessionStore = defineStore('conversation', () => { }, conversationId: params.conversationId, debug: true, + language: langStore.language, question: params.question, }), openWhenHidden: true, @@ -541,6 +431,7 @@ export const useSessionStore = defineStore('conversation', () => { params: Record, innerParams: Record, fetchParams: Record, + isDebug: boolean, ) => { await fetchEventSource(url, { ...fetchParams, @@ -551,9 +442,10 @@ export const useSessionStore = defineStore('conversation', () => { flowId: '', params: innerParams || {}, }, + ...(isDebug && { debug: isDebug }), conversationId: params.conversationId, features: features, - language, + language: langStore.language, groupId: params.groupId, question: params.question, record_id: params.qaRecordId, @@ -579,13 +471,25 @@ export const useSessionStore = defineStore('conversation', () => { conversationId: params.conversationId, features: features, groupId: params.groupId, - language, + language: langStore.language, question: params.question, record_id: params.qaRecordId, }), openWhenHidden: true, }); }, + fetchWait: async ( + url: string, + params: Record, + innerParams: Record, + fetchParams: Record, + ) => { + await fetchEventSource(url, { + ...fetchParams, + body: JSON.stringify({ taskId: currentTaskId.value, params: params }), + openWhenHidden: true, + }); + }, }; const judgeResp = async (resp) => { @@ -617,6 +521,8 @@ export const useSessionStore = defineStore('conversation', () => { type?: any; }, ind?: number, + waitType?: string, + isDebug?: boolean, ): Promise => { const { currentSelectedSession } = useHistorySessionStore(); params.conversationId = currentSelectedSession; @@ -632,15 +538,7 @@ export const useSessionStore = defineStore('conversation', () => { if (params.params && typeof params.params === 'object') { pp = params.params; } else if (params.params && typeof params.params === 'string') { - try { - pp = Object(JSON.parse(params.params)); - } catch (parseError) { - console.error('📨 参数解析失败:', { - params: params.params, - error: parseError - }); - pp = {}; // 使用空对象作为默认值 - } + pp = Object(JSON.parse(params.params)); } isPaused.value = false; excelPath.value = ''; @@ -667,18 +565,23 @@ export const useSessionStore = defineStore('conversation', () => { resp = response; }, onmessage: async (ev) => { - await handleMsgDataShow(params, ev, conversationItem); + handleMsgDataShow(params, ev, conversationItem); }, }; - - if (params.user_selected_flow) { + if(isDebug){ + await funcFetch.fetchAppNew(streamUrl, params, pp, fetchParams, isDebug); + } else if (params.user_selected_flow) { // 之前的对话历史记录 await funcFetch.fetchHistory(streamUrl, params, pp, fetchParams); } else if (params.user_selected_app) { // 新的工作流调试记录 await funcFetch.fetchAppNew(streamUrl, params, pp, fetchParams); - // } else if (false) { - // //写传参数情况 + } else if (waitType) { + if (waitType === 'params') { + await funcFetch.fetchWait(streamUrl, params, pp, fetchParams); + } else { + await funcFetch.fetchWait(streamUrl, params.params, pp, fetchParams); + } } else { await funcFetch.fetchDefault(streamUrl, params, pp, fetchParams); } @@ -728,6 +631,8 @@ export const useSessionStore = defineStore('conversation', () => { * @param user_selected_flow * @param params * @param type + * @param waitType + * @param isDebug */ const sendQuestion = async ( groupId: string | undefined, @@ -738,6 +643,8 @@ export const useSessionStore = defineStore('conversation', () => { user_selected_flow?: string, params?: any, type?: any, + waitType?: string, + isDebug?: boolean, ): Promise => { const { updateSessionTitle, currentSelectedSession } = useHistorySessionStore(); @@ -761,7 +668,7 @@ export const useSessionStore = defineStore('conversation', () => { comment: 'none', }); targetItem.currentInd = targetItem.message.length - 1; //123 - } else { + } else if (!waitType) { // 初次生成 ,创建一个问题和一个回答 const ind = conversationList.value.length - 1; const messageList = new MessageArray(); @@ -810,7 +717,10 @@ export const useSessionStore = defineStore('conversation', () => { params: params || undefined, }; } - await getStream(getStreamParams, regenerateInd ?? undefined); + if (waitType) { + getStreamParams = params; + } + await getStream(getStreamParams, regenerateInd ?? undefined, waitType, isDebug); }; /** @@ -829,7 +739,7 @@ export const useSessionStore = defineStore('conversation', () => { targetItem.message[0] += '暂停生成'; targetItem.isFinish = true; cancel(); - const resp = await api.stopGeneration(); + const resp = await api.stopGeneration(currentMessage.value.taskId); if (resp?.[1]?.code === 200) { isAnswerGenerating.value = false; } @@ -899,6 +809,21 @@ export const useSessionStore = defineStore('conversation', () => { }; // #endregion + /** + * 处理历史对话数据中尾注位置 + */ + const handleMessage = (record: any): string => { + let message = record.content.answer; + record.metadata.footNoteMetadataList?.reverse().forEach((footNoteMetadata: any) => { + const insertFile = record.document?.filter((file: any) => file._id === footNoteMetadata.releatedId); + const insertNumber = insertFile[0]?.order; + if (!insertNumber) return; + const pos = footNoteMetadata.insertPosition; + message = message.slice(0, pos) + `[[${insertNumber}]]` + message.slice(pos) + }); + return message; + } + /** * 获取历史对话数据 * @param conversationId @@ -936,12 +861,12 @@ export const useSessionStore = defineStore('conversation', () => { cid: conversationList.value.length + 1, belong: 'user', message: record.content.question, - createdAt: record.created_at, + createdAt: record.createdAt, }, { cid: conversationList.value.length + 2, belong: 'robot', - message: [record.content.answer], + message: [handleMessage(record)], messageList, currentInd: 0, isAgainst: false, @@ -951,6 +876,7 @@ export const useSessionStore = defineStore('conversation', () => { conversationId: record.conversationId, groupId: record.groupId, metadata: record.metadata, + document: record.document, flowdata: record?.flow ? (generateFlowData(record.flow) as FlowType) : undefined, @@ -965,22 +891,31 @@ export const useSessionStore = defineStore('conversation', () => { const generateFlowData = (record: any): FlowDataType => { const flowData = { id: record.recordId, - title: record.id, - status: 'success', + title: record.flowName, + status: record.flowStatus, display: true, flowId: record.flowId, data: [[]] as any[], }; + // 看有没有取消的 + let isCancelled = false; for (let i = 0; i < record.steps.length; i++) { flowData.data[0].push({ id: record.steps[i].stepId, - title: record.steps[i].stepId, + title: record.steps[i].stepName, status: record.steps[i].stepStatus, data: { input: record.steps[i].input, output: record.steps[i].output, + ...(record.steps[i].exData && { exData: record.steps[i].exData }), }, }); + if (record.steps[i].stepStatus === 'cancelled') { + isCancelled = true; + } + } + if (isCancelled) { + flowData.status = 'cancelled'; } return flowData; }; @@ -990,18 +925,13 @@ export const useSessionStore = defineStore('conversation', () => { */ const stopDebug = async (): Promise => { isPaused.value = true; - - // 安全检查:确保 conversationList 不为空 - if (conversationList.value.length > 0) { - ( - conversationList.value[ - conversationList.value.length - 1 - ] as RobotConversationItem - ).isFinish = true; - } - + ( + conversationList.value[ + conversationList.value.length - 1 + ] as RobotConversationItem + ).isFinish = true; cancel(); - const resp = await api.stopGeneration(); + const resp = await api.stopGeneration(currentMessage.value.taskId); if (resp?.[1]?.code === 200) { isAnswerGenerating.value = false; } @@ -1018,6 +948,7 @@ export const useSessionStore = defineStore('conversation', () => { dialogueRef, app, appList, + currentMessage, sendQuestion, pausedStream, stopDebug, @@ -1027,4 +958,4 @@ export const useSessionStore = defineStore('conversation', () => { getConversation, cancel, }; -}); +}); \ No newline at end of file diff --git a/src/store/historySession.ts b/src/store/historySession.ts index 9b702d5e..eb9638ae 100644 --- a/src/store/historySession.ts +++ b/src/store/historySession.ts @@ -216,8 +216,9 @@ export const useHistorySessionStore = defineStore( /** * 创建一个新的会话 */ - const generateSession = async (): Promise => { - const [_, res] = await api.createSession(user_selected_app.value); + const generateSession = async (isDebug): Promise => { + const appId = user_selected_app.value ?? ''; + const [_, res] = await api.createSession({appId, debug: isDebug}); if (!_ && res) { currentSelectedSession.value = res.result.conversationId; await getHistorySession(); diff --git a/src/store/lang.ts b/src/store/lang.ts index 4c5c359b..bdfec2e9 100644 --- a/src/store/lang.ts +++ b/src/store/lang.ts @@ -9,7 +9,7 @@ export const useLangStore = defineStore( const i18n = useI18n(); const language = ref(); - const changeLanguage = (lang: 'zh_cn' | 'en') => { + const changeLanguage = (lang: 'zh' | 'en') => { language.value = lang; i18n.locale.value = language.value; if (ipcRenderer) { @@ -26,7 +26,7 @@ export const useLangStore = defineStore( electronProcess.env['EULERCOPILOT_NLS_CONFIG'] && changeLanguage(nlsConfig.userLocale); } else { - !language.value && changeLanguage('zh_cn'); + !language.value && changeLanguage('zh'); } }); return { diff --git a/src/views/api/components/ActiveModel.vue b/src/views/api/components/ActiveModel.vue new file mode 100644 index 00000000..835e22ce --- /dev/null +++ b/src/views/api/components/ActiveModel.vue @@ -0,0 +1,167 @@ + + + + diff --git a/src/views/api/components/McpDrawer.vue b/src/views/api/components/McpDrawer.vue index a44eff4a..e421cd9e 100644 --- a/src/views/api/components/McpDrawer.vue +++ b/src/views/api/components/McpDrawer.vue @@ -32,25 +32,20 @@ const COMMAND_TEMPLATE = { command: '', args: [], env: {}, - autoApprove: [], - autoInstall: true, - disabled: false, }; const URL_TEMPLATE = { + headers: {}, url: '', - env: {}, - autoApprove: [], - autoInstall: true, - disabled: false, }; +const STREAM = {}; const loading = ref(false); -const mcpConfigTemplate = { +const mcpConfigTemplate = ref({ stdio: COMMAND_TEMPLATE, sse: URL_TEMPLATE, - stream: URL_TEMPLATE, -}; + stream: STREAM, +}); const form = reactive({ icon: '', @@ -149,7 +144,7 @@ async function onConfirm(formEl: FormInstance | undefined) { icon: form.icon, name: form.name, description: form.description, - config: form.mcpConfig, + config: JSON.parse(form.mcpConfig), mcpType: form.type, }); @@ -178,7 +173,6 @@ async function onConfirm(formEl: FormInstance | undefined) { jsonEditorRef.value.setJsonValue('{\n \n}'); emits('success'); loading.value = false; - } catch (error) { console.error('Create or update MCP service failed:', error); ElMessage({ @@ -200,13 +194,23 @@ async function getMcpServiceDetail(serviceId: string) { form.description = description; form.type = mcpType; form.mcpConfig = data; - jsonEditorRef.value.setJsonValue(form.mcpConfig); + let json = {}; + if (mcpType === 'stdio') { + json.command = data.command; + json.args = data.args; + json.env = data.env; + } else if (mcpType === 'sse') { + json.headers = data.headers; + json.url = data.url; + } + jsonEditorRef.value.setJsonValue(JSON.stringify(json, null, 2)); + mcpConfigTemplate.value[mcpType] = json; } } async function setMcpConfig(type: string) { jsonEditorRef.value.setJsonValue( - JSON.stringify(mcpConfigTemplate[type], null, 2), + JSON.stringify(mcpConfigTemplate.value[type], null, 2), ); } @@ -222,6 +226,12 @@ watch( } getMcpServiceDetail(props.serviceId); } else { + // 初始化 + mcpConfigTemplate.value = { + stdio: COMMAND_TEMPLATE, + sse: URL_TEMPLATE, + stream: STREAM, + }; if (formRef.value) formRef.value.resetFields(); setMcpConfig(form.type); } @@ -240,7 +250,7 @@ watch( :model-value="visible" @close="emits('update:visible', false)" > - +

(); display: flex; flex-direction: column; gap: 4px; + width: 70%; &__top { min-height: 24px; @@ -66,7 +67,7 @@ const props = defineProps(); overflow: hidden; white-space: nowrap; text-overflow: ellipsis; - width: 80%; + width: 70%; font-weight: 700; } } diff --git a/src/views/api/index.vue b/src/views/api/index.vue index a566f503..c313e5ba 100644 --- a/src/views/api/index.vue +++ b/src/views/api/index.vue @@ -103,6 +103,30 @@ :lazy="true" > + + + + +
+
-
+ +
{{ t('plugin_center.mcp.install_failed') }}
+ +
+ +
+ {{ t('plugin_center.mcp.install_success') }} +
+
+ +
+ +
+ {{ t('plugin_center.mcp.not_installed') }} +
+
+ + \ No newline at end of file diff --git a/src/views/createapp/components/types.ts b/src/views/createapp/components/types.ts index ce88543b..9a4bd2b4 100644 --- a/src/views/createapp/components/types.ts +++ b/src/views/createapp/components/types.ts @@ -7,6 +7,8 @@ // IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR // PURPOSE. // See the Mulan PSL v2 for more details. +import i18n from 'src/i18n'; + import type { UserDialoguePanelType, RobotDialoguePanelType, @@ -93,40 +95,39 @@ export const nodeTypeToIcon = { // 这里是对应的图标 export const iconTypeList = [ - { name: 'HTTP请求', value: 'API', icon: API, class: 'otherNode' }, - { name: 'MCP', value: 'MCP', icon: API, class: 'otherNode' }, - { name: 'SQL查询', value: 'SQL', icon: API, class: 'otherNode' }, - { name: '图表', value: 'Graph', icon: API, class: 'otherNode' }, - { name: '代码执行', value: 'Code', icon: CODE, class: 'otherNode' }, - { name: '大模型', value: 'LLM', icon: LLM, class: 'systemNode' }, - { name: '知识库', value: 'RAG', icon: KENOWLEDGE_BASE, class: 'systemNode' }, + { name: i18n.global.t('opertion.api'), value: 'API', icon: API, class: 'otherNode' }, + { name: i18n.global.t('opertion.mcp'), value: 'MCP', icon: API, class: 'otherNode' }, + { name: i18n.global.t('opertion.sql'), value: 'SQL', icon: API, class: 'otherNode' }, + { name: i18n.global.t('opertion.gcraph'), value: 'Graph', icon: API, class: 'otherNode' }, + { name: i18n.global.t('opertion.llm'), value: 'LLM', icon: LLM, class: 'systemNode' }, + { name: i18n.global.t('opertion.rag'), value: 'RAG', icon: KENOWLEDGE_BASE, class: 'systemNode' }, { - name: '问题推荐', + name: i18n.global.t('opertion.suggestion'), value: 'Suggestion', icon: get_CVE_DETAIL, class: 'aposNode', }, // 循环控制节点 { - name: '跳过本轮', + name: i18n.global.t('opertion.skip_round'), value: 'continue', icon: REFRESH, class: 'systemNode', }, { - name: '退出循环', + name: i18n.global.t('opertion.exit_loop'), value: 'break', icon: STOP_FILLED, class: 'systemNode', }, { - name: '变量赋值', + name: i18n.global.t('opertion.variable_assign'), value: 'VariableAssign', icon: CODE, // 暂时使用CODE图标,与菜单保持一致 class: 'systemNode', }, { - name: '文件提取器', + name: i18n.global.t('opertion.file_extract'), value: 'FileExtract', icon: CODE, class: 'systemNode', @@ -187,3 +188,50 @@ export interface ListItem { id: string; value: string | null; } + +const opertionList = [ + // Number operations + {value: 'number_equal', label: i18n.global.t('opertion.equal'), str: '='}, + {value: 'number_not_equal', label: i18n.global.t('opertion.not_equal'), str: '≠'}, + {value: 'number_greater_than', label: i18n.global.t('opertion.greater_than'), str: '>'}, + {value: 'number_less_than', label: i18n.global.t('opertion.less_than'), str: '<'}, + {value: 'number_greater_than_or_equal', label: i18n.global.t('opertion.greater_than_or_equal'), str: '≥'}, + {value: 'number_less_than_or_equal', label: i18n.global.t('opertion.less_than_or_equal'), str: '≤'}, + + // String operations + {value: 'string_equal', label: i18n.global.t('opertion.equal'), str: '='}, + {value: 'string_not_equal', label: i18n.global.t('opertion.not_equal'), str: '≠'}, + {value: 'string_contains', label: i18n.global.t('opertion.contains'), str: ''}, + {value: 'string_not_contains', label: i18n.global.t('opertion.does_not_contain'), str: ''}, + {value: 'string_starts_with', label: i18n.global.t('opertion.starts_with'), str: '='}, + {value: 'string_ends_with', label: i18n.global.t('opertion.ends_with'), str: '='}, + {value: 'string_length_equal', label: i18n.global.t('opertion.length_equal'), str: '|...|='}, + {value: 'string_length_greater_than', label: i18n.global.t('opertion.length_greater_than'), str: '|...|>'}, + {value: 'string_length_greater_than_or_equal', label: i18n.global.t('opertion.length_greater_than_or_equal'), str: '|...|≥'}, + {value: 'string_length_less_than', label: i18n.global.t('opertion.length_less_than'), str: '|...|<'}, + {value: 'string_length_less_than_or_equal', label: i18n.global.t('opertion.length_less_than_or_equal'), str: '|...|≤'}, + {value: 'string_regex_match', label: i18n.global.t('opertion.regex_match'), str: '\\+'}, + + // List operations + {value: 'list_equal', label: i18n.global.t('opertion.equal'), str: '='}, + {value: 'list_not_equal', label: i18n.global.t('opertion.not_equal'), str: '≠'}, + {value: 'list_contains', label: i18n.global.t('opertion.contains'), str: ''}, + {value: 'list_not_contains', label: i18n.global.t('opertion.does_not_contain'), str: ''}, + {value: 'list_length_equal', label: i18n.global.t('opertion.length_equal'), str: '|...|='}, + {value: 'list_length_greater_than', label: i18n.global.t('opertion.length_greater_than'), str: '|...|>'}, + {value: 'list_length_greater_than_or_equal', label: i18n.global.t('opertion.length_greater_than_or_equal'), str: '|...|≥'}, + {value: 'list_length_less_than', label: i18n.global.t('opertion.length_less_than'), str: '|...|<'}, + {value: 'list_length_less_than_or_equal', label: i18n.global.t('opertion.length_less_than_or_equal'), str: '|...|≤'}, + + // Boolean operations + {value: 'bool_equal', label: i18n.global.t('opertion.equal'), str: '='}, + {value: 'bool_not_equal', label: i18n.global.t('opertion.not_equal'), str: '≠'}, + + // Dictionary operations + {value: 'dict_equal', label: i18n.global.t('opertion.equal'), str: '='}, + {value: 'dict_not_equal', label: i18n.global.t('opertion.not_equal'), str: '≠'}, + {value: 'dict_contains_key', label: i18n.global.t('opertion.contains_key'), str: ''}, + {value: 'dict_not_contains_key', label: i18n.global.t('opertion.does_not_contain_key'), str: ''} +] + + export const opertionListMap = new Map(opertionList.map(item => [item.value, item])); diff --git a/src/views/createapp/components/workFlow.vue b/src/views/createapp/components/workFlow.vue index 8e0a0475..7e6af886 100644 --- a/src/views/createapp/components/workFlow.vue +++ b/src/views/createapp/components/workFlow.vue @@ -6,7 +6,6 @@ import { ElMessage } from 'element-plus'; import { VueFlow, useVueFlow } from '@vue-flow/core'; import { Background } from '@vue-flow/background'; import { MiniMap } from '@vue-flow/minimap'; -import BranchNode from './workFlowConfig/BranchNode.vue'; import ChoiceBranchNode from './workFlowConfig/ChoiceBranchNode.vue'; import VariableAssignNode from './workFlowConfig/VariableAssignNode.vue'; import LoopNode from './workFlowConfig/LoopNode.vue'; @@ -48,8 +47,11 @@ import CustomLoading from '../../customLoading/index.vue'; import EditFlowName from './workFlowConfig/editFlowName.vue'; import NodeListPanel from './workFlowConfig/NodeListPanel.vue'; import EnvironmentVariableDrawer from './workFlowConfig/EnvironmentVariableDrawer.vue'; +import { useLangStore } from '@/store'; const { t } = useI18n(); +const langStore = useLangStore(); + const copilotAside = ref(); const isCopilotAsideVisible = ref(false); @@ -1085,6 +1087,7 @@ onMounted(() => { .queryAllFlowService({ page: 1, pageSize: 10, + language: langStore.language, }) .then((res) => { const services = res[1]?.result.services || []; @@ -1161,6 +1164,7 @@ const nodesChange = (nodes) => { removeSelectedNodes([getSelectedNodes.value[0]]); } if (nodes?.[0]?.type === 'remove') { + // TODO 为什么0.10.0删除了delNode? delNode(nodes[0].id); // 节点增加删除时直接将工作流debug状态置为false emits('updateFlowsDebug', false); @@ -2127,7 +2131,10 @@ const saveFlow = async (updateNodeParameter?, debug?) => { if (response[1]?.result) { queryFlow('update'); const updatedCurFlow = response[1].result.flow; - isNodeConnect.value = response[1].result.connectivity; + isNodeConnect.value = updatedCurFlow.connectivity; + if (!isNodeConnect.value) { + ElMessage.error(i18n.global.t('semantic.check_connect')); + } redrageFlow(updatedCurFlow?.nodes, updatedCurFlow?.edges); } @@ -2264,17 +2271,6 @@ defineExpose({ > - - -
- + - - - - + + + + @@ -56,18 +56,18 @@ - 变量管理 + {{ $t('flow.node_config.variable_management') }}
- +
- 选择工作流中的变量作为代码输入 + {{ $t('flow.node_config.input_variables_tip') }} - 添加变量 + {{ $t('flow.node_config.add_variable') }}
@@ -89,28 +89,28 @@ :show-actions="true" :show-variable-info="true" output-format="wrapped" - placeholder="输入节点内的变量名" + :placeholder="$t('flow.node_config.variable_placeholder')" @remove="removeInputVariable(index)" @variable-selected="(selectedVar, reference) => handleInputVariableSelected(selectedVar, index)" />
-

暂无输入变量

-

选择变量作为代码输入参数

+

{{ $t('flow.node_config.no_input_variables') }}

+

{{ $t('flow.node_config.input_variables_tip') }}

- +
- 定义节点的输出变量 + {{ $t('flow.node_config.output_variables_tip') }} - 添加变量 + {{ $t('flow.node_config.add_variable') }}
@@ -123,20 +123,20 @@
- - - - - + + + + +
-

暂无输出变量

-

输出变量应在代码的返回值中定义

+

{{ $t('flow.node_config.no_output_variables') }}

+

{{ $t('flow.node_config.output_variables_tip_code') }}

@@ -166,29 +166,29 @@ - 执行配置 + {{ $t('flow.node_config.execution_config') }}
- + - + {{ $t('flow.node_config.seconds') }} - + - MB + {{ $t('flow.node_config.mb') }} - + - 核心 + {{ $t('flow.node_config.cores') }}
@@ -208,7 +208,7 @@ - 代码编辑 + {{ $t('flow.node_config.code_editor') }}
@@ -230,9 +230,9 @@ @@ -929,12 +929,23 @@ watch(inputVariables, () => { .code-node-drawer { :deep(.el-drawer) { border-radius: 8px 0 0 8px; + background: var(--el-bg-color); + + body[theme='dark'] & { + background: #1f2329; + } } :deep(.el-drawer__header) { padding: 24px 24px 16px !important; margin-bottom: 0; border-bottom: 1px solid var(--el-border-color-lighter); + background: var(--el-bg-color); + + body[theme='dark'] & { + background: #1f2329; + border-bottom-color: var(--el-border-color); + } } :deep(.el-drawer__body) { @@ -944,6 +955,12 @@ watch(inputVariables, () => { :deep(.el-drawer__footer) { padding: 16px 24px; border-top: 1px solid var(--el-border-color-lighter); + background: var(--el-bg-color); + + body[theme='dark'] & { + background: #1f2329; + border-top-color: var(--el-border-color); + } } .drawer-header { @@ -953,6 +970,10 @@ watch(inputVariables, () => { font-size: 16px; font-weight: 500; color: var(--el-text-color-primary); + + body[theme='dark'] & { + color: #e4e8ee; + }; .node-icon { width: 24px; @@ -965,6 +986,11 @@ watch(inputVariables, () => { .drawer-body { height: 100%; overflow-y: auto; + background: var(--el-bg-color); + + body[theme='dark'] & { + background: #1f2329; + }; .code-content { :deep(.el-collapse-item__header) { @@ -972,6 +998,13 @@ watch(inputVariables, () => { font-weight: 500; background: var(--el-fill-color-extra-light); border-bottom: 1px solid var(--el-border-color-lighter); + color: var(--el-text-color-primary); + + body[theme='dark'] & { + background: var(--o-bash-bg, #2a2f37); + border-bottom-color: var(--el-border-color); + color: #e4e8ee; + } } :deep(.el-collapse-item__wrap) { @@ -980,6 +1013,11 @@ watch(inputVariables, () => { :deep(.el-collapse-item__content) { padding: 20px 24px; + background: var(--el-bg-color); + + body[theme='dark'] & { + background: #1f2329; + } } } } @@ -994,6 +1032,10 @@ watch(inputVariables, () => { margin-left: 8px; color: var(--el-text-color-secondary); font-size: 12px; + + body[theme='dark'] & { + color: #d3dce9; + } } } @@ -1016,49 +1058,81 @@ watch(inputVariables, () => { // 确保工具栏在暗色主题下正确显示 .editor-toolbar { + background: var(--el-fill-color-extra-light); + + body[theme='dark'] & { + background: var(--o-bash-bg, #2a2f37); + } + .toolbar-left { .language-selector { label { - color: #abb2bf; + color: var(--el-text-color-primary); + + body[theme='dark'] & { + color: #e4e8ee; + } } } .toolbar-actions { :deep(.el-button) { - color: #ffffff !important; // 使用白色文字确保清晰可见 - font-size: 13px !important; // 稍微增大字体 - font-weight: 500 !important; // 增加字体粗细 - padding: 6px 12px !important; // 增加内边距提升点击体验 - border-radius: 4px !important; // 添加圆角 - transition: all 0.2s ease !important; // 添加过渡动画 - border: 1px solid transparent !important; // 透明边框便于添加悬停效果 + color: var(--el-text-color-primary) !important; + font-size: 13px !important; + font-weight: 500 !important; + padding: 6px 12px !important; + border-radius: 4px !important; + transition: all 0.2s ease !important; + border: 1px solid transparent !important; background: transparent !important; + body[theme='dark'] & { + color: #e4e8ee !important; + } + span { - color: #ffffff !important; + color: var(--el-text-color-primary) !important; + + body[theme='dark'] & { + color: #e4e8ee !important; + } } &:hover { - color: #61afef !important; // 悬停时使用主题蓝色 - background: rgba(97, 175, 239, 0.15) !important; // 稍微增加背景透明度 - border-color: rgba(97, 175, 239, 0.3) !important; // 添加边框效果 - transform: translateY(-1px) !important; // 轻微上移效果 - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2) !important; // 添加阴影 + color: var(--el-color-primary) !important; + background: var(--el-color-primary-light-9) !important; + border-color: var(--el-color-primary-light-7) !important; + transform: translateY(-1px) !important; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1) !important; + + body[theme='dark'] & { + background: var(--flow-node-default-over-color, #25303e) !important; + border-color: var(--flow-node-boder-default-over, #314265) !important; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3) !important; + } span { - color: #61afef !important; + color: var(--el-color-primary) !important; } } &:active { - transform: translateY(0) !important; // 点击时回到原位 - background: rgba(97, 175, 239, 0.25) !important; + transform: translateY(0) !important; + background: var(--el-color-primary-light-8) !important; + + body[theme='dark'] & { + background: var(--flow-node-default-over-color, #25303e) !important; + } } &:focus { outline: none !important; - border-color: #61afef !important; - box-shadow: 0 0 0 2px rgba(97, 175, 239, 0.2) !important; + border-color: var(--el-color-primary) !important; + box-shadow: 0 0 0 2px var(--el-color-primary-light-8) !important; + + body[theme='dark'] & { + box-shadow: 0 0 0 2px var(--flow-node-boder-default-over, #314265) !important; + } } } } @@ -1082,10 +1156,18 @@ watch(inputVariables, () => { margin-bottom: 16px; padding-bottom: 8px; border-bottom: 1px solid var(--el-border-color-lighter); + + body[theme='dark'] & { + border-bottom-color: var(--el-border-color); + } span { font-weight: 500; color: var(--el-text-color-primary); + + body[theme='dark'] & { + color: #e4e8ee; + } } } @@ -1096,6 +1178,11 @@ watch(inputVariables, () => { background: var(--el-fill-color-extra-light); border-radius: 6px; border: 1px solid var(--el-border-color-lighter); + + body[theme='dark'] & { + background: var(--o-bash-bg, #2a2f37); + border-color: var(--el-border-color); + }; .variable-header { display: flex; @@ -1126,6 +1213,10 @@ watch(inputVariables, () => { text-align: center; padding: 40px 20px; color: var(--el-text-color-secondary); + + body[theme='dark'] & { + color: #d3dce9; + } p { margin: 0; @@ -1135,6 +1226,10 @@ watch(inputVariables, () => { font-size: 12px; margin-top: 8px; color: var(--el-text-color-placeholder); + + body[theme='dark'] & { + color: #8d98aa; + } } } } diff --git a/src/views/createapp/components/workFlowConfig/CommentNode.vue b/src/views/createapp/components/workFlowConfig/CommentNode.vue new file mode 100644 index 00000000..473ea9c3 --- /dev/null +++ b/src/views/createapp/components/workFlowConfig/CommentNode.vue @@ -0,0 +1,530 @@ + + + + + diff --git a/src/views/createapp/components/workFlowConfig/CustomNode.vue b/src/views/createapp/components/workFlowConfig/CustomNode.vue index 68f4bbb8..a6c648d6 100644 --- a/src/views/createapp/components/workFlowConfig/CustomNode.vue +++ b/src/views/createapp/components/workFlowConfig/CustomNode.vue @@ -444,7 +444,7 @@ const handleSourceHandleLeave = () => { /* 黑夜模式支持 - 使用用户指定的颜色规范 */ .dark .customNodeStyle { - background: #26262a !important; + background: #353f58 !important; border: 2px solid rgba(255, 255, 255, 0.08) !important; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3) !important; @@ -453,7 +453,7 @@ const handleSourceHandleLeave = () => { } .nodeBox { - background: #26262a !important; + background: #353f58 !important; .title { .label { @@ -499,14 +499,14 @@ const handleSourceHandleLeave = () => { } .handle-plus-button .plus-icon { - background: #26262a; + background: #353f58; border-color: #63b3ed; color: #63b3ed; box-shadow: 0 3px 8px rgba(99, 179, 237, 0.4); &:hover { background: #63b3ed; - color: #26262a; + color: #353f58; box-shadow: 0 5px 15px rgba(99, 179, 237, 0.6); } } diff --git a/src/views/createapp/components/workFlowConfig/CustomSaENode.vue b/src/views/createapp/components/workFlowConfig/CustomSaENode.vue index 81cdcdde..3ec36bb9 100644 --- a/src/views/createapp/components/workFlowConfig/CustomSaENode.vue +++ b/src/views/createapp/components/workFlowConfig/CustomSaENode.vue @@ -2,6 +2,9 @@ import { Position, Handle } from '@vue-flow/core'; import { ref, onMounted, watch, nextTick, computed } from 'vue'; import { Plus } from '@element-plus/icons-vue'; +import { useI18n } from 'vue-i18n'; + +const { t } = useI18n(); const props = defineProps({ id: { type: String, @@ -59,7 +62,7 @@ const setConnectStatus = (type) => { // 处理开始节点点击编辑 const handleStartNodeClick = () => { - const isStartNode = props.data.name === '开始' || props.data.name === 'start'; + const isStartNode = props.data.name === '开始' || props.data.name === 'start' || props.data.name === t('flow.node_names.start'); if (isStartNode) { // 使用nextTick确保DOM更新完成 nextTick(() => { @@ -119,12 +122,12 @@ const conversationVariables = computed(() => { // 判断是否为开始节点 const isStartNode = computed(() => { - return props.data.name === '开始' || props.data.name === 'start'; + return props.data.name === '开始' || props.data.name === 'start' || props.data.name === t('flow.node_names.start'); }); // 判断是否为结束节点 const isEndNode = computed(() => { - return props.data.name === '结束' || props.data.name === 'end'; + return props.data.name === '结束' || props.data.name === 'end' || props.data.name === t('flow.node_names.end'); }); // 获取变量类型的显示名称 @@ -206,19 +209,19 @@ watch(
-
{{ $t('main.start') }}
-
{{ $t('main.end') }}
+
{{ $t('main.start') }}
+
{{ $t('main.end') }}
-
+
@@ -457,11 +460,11 @@ watch( /* 黑夜模式支持 - 使用用户指定的颜色规范 */ .dark .nodeSaEBorder { - background: #26262a !important; + background: #353f58 !important; border: 2px solid rgba(255, 255, 255, 0.08) !important; .nodeSaEBorderBox { - background: #26262a !important; + background: #353f58 !important; } .saEText { @@ -492,12 +495,12 @@ watch( .handle-plus-button .plus-icon { color: #63b3ed; - background: #26262a; + background: #353f58; border-color: #63b3ed; &:hover { background: #63b3ed; - color: #26262a; + color: #353f58; } } } diff --git a/src/views/createapp/components/workFlowConfig/DirectReplyDrawer.vue b/src/views/createapp/components/workFlowConfig/DirectReplyDrawer.vue index 6ace1af8..9269c188 100644 --- a/src/views/createapp/components/workFlowConfig/DirectReplyDrawer.vue +++ b/src/views/createapp/components/workFlowConfig/DirectReplyDrawer.vue @@ -28,24 +28,24 @@ - 基本信息 + {{ $t('flow.node_config.basic_info') }}
- + - + @@ -60,7 +60,7 @@ - 回复内容 + {{ $t('app.outputContent') }}
@@ -69,13 +69,13 @@ v-model="nodeConfig.answer" :flow-id="flowId" :current-step-id="nodeId" - placeholder="请输入回复内容,可以使用变量插入功能..." + :placeholder="$t('app.inputContent') + '...'" @variable-inserted="handleFileVariableInserted" />
- {{ getCharCount() }} 字符 + {{ getCharCount() }} {{ $t('common.characters') }}
@@ -86,7 +86,7 @@ - 附件变量 + {{ $t('flow.file_variables') }} ({{ nodeConfig.fileVariables.length }})
@@ -103,7 +103,7 @@
{{ fileVar.displayName }}
{{ formatVariableDisplay(fileVar.variableName) }}
-
{{ fileVar.fileType === 'file' ? '单文件' : '多文件' }}
+
{{ fileVar.fileType === 'file' ? $t('flow.single_file') : $t('flow.multiple_files') }}
@@ -172,19 +172,19 @@ interface Props { const props = defineProps() const emit = defineEmits(['update:visible', 'saveNode']) -// const { t } = useI18n() // 暂时不使用 +const { t } = useI18n() // 响应式数据 const drawerVisible = ref(false) const activeName = ref(['basic', 'content']) -const nodeName = ref('回答') +const nodeName = ref(t('flow.answer')) const textEditorRef = ref() // 节点配置 const nodeConfig = ref({ - name: '回答', + name: t('flow.answer'), description: '', answer: '', fileVariables: [] @@ -225,7 +225,7 @@ const initializeNodeData = () => { } nodeConfig.value = { - name: props.nodeData.name || '回答', + name: props.nodeData.name || t('flow.answer'), description: props.nodeData.description || '', answer: props.nodeData.parameters?.input_parameters?.answer || '', fileVariables: fileVariables @@ -250,7 +250,7 @@ const handleFileVariableInserted = (variable: any) => { // 检查是否已存在相同的文件变量 const existingIndex = nodeConfig.value.fileVariables?.findIndex(f => f.variableName === variable.name) if (existingIndex !== undefined && existingIndex >= 0) { - ElMessage.warning(`文件变量 ${variable.name} 已存在,无需重复添加`) + ElMessage.warning(t('flow.file_variable_exists', { name: variable.name })) return } @@ -272,7 +272,7 @@ const handleFileVariableInserted = (variable: any) => { // 如果传入的是字符串变量名(向后兼容) else if (typeof variable === 'string') { // 这里需要根据变量名查找变量类型,暂时跳过 - console.log('收到字符串变量名:', variable) + console.log('Received string variable name:', variable) } } @@ -309,7 +309,7 @@ const removeFileVariable = (index: number) => { // 格式化变量显示 const formatVariableDisplay = (variableName: string) => { - return `变量: {{${variableName}}}` + return t('flow.variable_format', { name: variableName }) } const closeDrawer = () => { @@ -319,7 +319,7 @@ const closeDrawer = () => { const saveNode = () => { // 验证必填字段 if (!nodeConfig.value.name.trim()) { - ElMessage.error('请输入节点名称') + ElMessage.error(t('flow.node_config.node_name_required')) return } diff --git a/src/views/createapp/components/workFlowConfig/EnvironmentVariableDrawer.vue b/src/views/createapp/components/workFlowConfig/EnvironmentVariableDrawer.vue index 3f614702..370a5dc4 100644 --- a/src/views/createapp/components/workFlowConfig/EnvironmentVariableDrawer.vue +++ b/src/views/createapp/components/workFlowConfig/EnvironmentVariableDrawer.vue @@ -16,7 +16,7 @@
-
环境变量配置
+
{{ $t('flow.env_config') }}
@@ -25,13 +25,13 @@
@@ -39,14 +39,14 @@
-

环境变量列表

+

{{ $t('flow.env_list') }}

- 添加变量 + {{ $t('flow.add_variable') }}
@@ -80,8 +80,8 @@
-
暂无环境变量
-
点击"添加变量"按钮创建你的第一个环境变量
+
{{ $t('flow.no_env_variables') }}
+
{{ $t('flow.create_first_env_variable') }}
@@ -90,7 +90,7 @@ @@ -98,7 +98,7 @@ @@ -108,39 +108,39 @@ :rules="variableRules" ref="variableFormRef" > - + - - - - - - + + + + + + - + @@ -150,33 +150,33 @@ v-model="editingVariable.valueJson" type="textarea" :rows="4" - placeholder="请输入JSON格式的对象" + :placeholder="$t('flow.input_json_placeholder')" /> - + @@ -237,14 +237,14 @@ const variableFormRef = ref() // 表单验证规则 const variableRules = { name: [ - { required: true, message: '请输入变量名', trigger: 'blur' }, - { pattern: /^[a-zA-Z_][a-zA-Z0-9_]*$/, message: '变量名只能包含字母、数字和下划线,且不能以数字开头', trigger: 'blur' } + { required: true, message: t('flow.variable_name_required'), trigger: 'blur' }, + { pattern: /^[a-zA-Z_][a-zA-Z0-9_]*$/, message: t('flow.variable_name_pattern'), trigger: 'blur' } ], var_type: [ - { required: true, message: '请选择变量类型', trigger: 'change' } + { required: true, message: t('flow.variable_type_required'), trigger: 'change' } ], value: [ - { required: true, message: '请输入变量值', trigger: 'blur' } + { required: true, message: t('flow.variable_value_required'), trigger: 'blur' } ] } @@ -268,7 +268,7 @@ const getVariableTypeDisplay = (type: string): string => { // 格式化变量值显示 const formatVariableValue = (variable: Variable): string => { if (variable.value === null || variable.value === undefined) { - return '未设置' + return t('flow.not_set') } switch (variable.var_type) { @@ -287,7 +287,7 @@ const formatVariableValue = (variable: Variable): string => { return '{ ... }' } case 'secret': - return '****** (隐藏)' + return '****** (' + t('flow.hidden') + ')' default: const defaultValue = String(variable.value) return defaultValue.length > 50 ? `${defaultValue.substring(0, 50)}...` : defaultValue @@ -309,7 +309,7 @@ const getValueClass = (type: string): string => { // 加载环境变量列表 const loadEnvironmentVariables = async () => { if (!props.flowId) { - console.warn('没有flowId,跳过环境变量加载') + console.warn('No flowId, skipping environment variable loading') return } @@ -331,8 +331,8 @@ const loadEnvironmentVariables = async () => { environmentVariables.value = variables || [] } catch (error) { - console.error('❌ 加载环境变量失败:', error) - ElMessage.error('加载环境变量失败') + console.error('❌ Loading environment variables failed:', error) + ElMessage.error(t('flow.load_env_variables_failed')) environmentVariables.value = [] } finally { variablesLoading.value = false @@ -375,7 +375,7 @@ const saveEnvironmentVariable = async () => { try { variableData.value = JSON.parse(variableData.valueJson || '{}') } catch (error) { - ElMessage.error('JSON格式不正确,请检查输入') + ElMessage.error(t('flow.json_format_error')) return } } else if (variableData.var_type === 'boolean') { @@ -397,22 +397,22 @@ const saveEnvironmentVariable = async () => { }, variableData ) - ElMessage.success('环境变量更新成功') + ElMessage.success(t('flow.env_variable_update_success')) } else { // 创建变量 await createVariable({ ...variableData, flow_id: props.flowId }) - ElMessage.success('环境变量创建成功') + ElMessage.success(t('flow.env_variable_create_success')) } showVariableDialog.value = false await loadEnvironmentVariables() } catch (error) { - console.error('保存环境变量失败:', error) - ElMessage.error(isEditingVariable.value ? '更新环境变量失败' : '创建环境变量失败') + console.error('Save environment variable failed:', error) + ElMessage.error(isEditingVariable.value ? t('flow.env_variable_update_failed') : t('flow.env_variable_create_failed')) } } @@ -420,11 +420,11 @@ const saveEnvironmentVariable = async () => { const deleteEnvironmentVariable = async (variable: Variable) => { try { await ElMessageBox.confirm( - `确定要删除环境变量"${variable.name}"吗?此操作不可恢复。`, - '确认删除', + t('flow.confirm_delete_env_variable', { name: variable.name }), + t('flow.confirm_delete_title'), { - confirmButtonText: '确定', - cancelButtonText: '取消', + confirmButtonText: t('common.confirm'), + cancelButtonText: t('common.cancel'), type: 'warning', } ) @@ -435,14 +435,14 @@ const deleteEnvironmentVariable = async (variable: Variable) => { flow_id: props.flowId }) - ElMessage.success('环境变量删除成功') + ElMessage.success(t('flow.env_variable_delete_success')) showVariableDialog.value = false await loadEnvironmentVariables() } catch (error) { if (error !== 'cancel') { - console.error('删除环境变量失败:', error) - ElMessage.error('删除环境变量失败') + console.error('Delete environment variable failed:', error) + ElMessage.error(t('flow.env_variable_delete_failed')) } } } @@ -467,7 +467,7 @@ const closeDrawer = () => { } onMounted(() => { - console.log('🚀 EnvironmentVariableDrawer 已挂载') + console.log('🚀 EnvironmentVariableDrawer mounted') nextTick(() => { loadEnvironmentVariables() }) diff --git a/src/views/createapp/components/workFlowConfig/FileExtractorDrawer.vue b/src/views/createapp/components/workFlowConfig/FileExtractorDrawer.vue index b47cb5dc..5a0d87af 100644 --- a/src/views/createapp/components/workFlowConfig/FileExtractorDrawer.vue +++ b/src/views/createapp/components/workFlowConfig/FileExtractorDrawer.vue @@ -2,40 +2,40 @@
- +
-

基本信息

+

{{ t('flow.file_extractor.basic_info') }}

- - + + - +
- +
-

输入变量

+

{{ t('flow.file_extractor.input_variables') }}

- + @@ -57,9 +57,9 @@ @@ -4200,7 +4518,7 @@ export default { /* 深色主题支持 - 使用用户指定的颜色规范 */ .dark .loopNodeStyle { - background: #26262a !important; + background: #353f58 !important; border: 2px solid rgba(255, 255, 255, 0.08) !important; } @@ -4213,7 +4531,7 @@ export default { } .dark .loopInfo { - background: #26262a !important; + background: #353f58 !important; border-color: rgba(255, 255, 255, 0.08) !important; } @@ -4222,13 +4540,13 @@ export default { } .dark .infoValue { - background: #26262a !important; + background: #353f58 !important; border-color: rgba(255, 255, 255, 0.08) !important; color: #ffffff !important; } .dark .embeddedFlowCanvas { - background: #26262a !important; + background: #353f58 !important; border-color: rgba(255, 255, 255, 0.08) !important; } @@ -4251,11 +4569,11 @@ export default { /* 深色主题下的子画布节点样式 */ .dark .embeddedCustomNodeStyle { - background: #26262a !important; + background: #353f58 !important; border: 2px solid rgba(255, 255, 255, 0.08) !important; .nodeBox { - background: #26262a !important; + background: #353f58 !important; } .title .label { @@ -4285,11 +4603,11 @@ export default { .dark .embeddedCustomNodeStyle.vue-flow__node-end, .dark .embeddedCustomNodeStyle.vue-flow__node-break, .dark .embeddedCustomNodeStyle.vue-flow__node-continue { - background: #26262a !important; + background: #353f58 !important; border: 2px solid rgba(255, 255, 255, 0.08) !important; .nodeSaEBorderBox { - background: #26262a !important; + background: #353f58 !important; } .saEText { @@ -4299,7 +4617,7 @@ export default { /* 深色主题下的Choice节点样式 */ .dark .choiceBranchNodeStyle { - background: #26262a !important; + background: #353f58 !important; border: 2px solid rgba(255, 255, 255, 0.08) !important; .title .label { @@ -4307,7 +4625,7 @@ export default { } .branchItem { - background: #26262a !important; + background: #353f58 !important; border-color: rgba(255, 255, 255, 0.08) !important; .caseLabel { @@ -4324,7 +4642,7 @@ export default { } .emptyBranches { - background: #26262a !important; + background: #353f58 !important; border-color: rgba(255, 255, 255, 0.08) !important; .emptyText { @@ -4890,4 +5208,63 @@ export default { transition: border-color 0.2s ease; cursor: pointer; } + +/* 子画布右键菜单样式 - 与主画布保持一致 */ +.sub-canvas-context-menu { + pointer-events: auto; +} + +.sub-canvas-context-menu .custom-context-menu { + background: white; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); + border: 1px solid #e1e4e8; + border-radius: 8px; + padding: 4px 0; + min-width: 160px; +} + +.sub-canvas-context-menu .context-menu-item { + padding: 8px 16px; + font-size: 14px; + display: flex; + align-items: center; + gap: 8px; + cursor: pointer; + transition: all 0.2s ease; + color: #606266; +} + +.sub-canvas-context-menu .context-menu-item:hover { + background-color: #f5f7fa; + color: #409eff; +} + +.sub-canvas-context-menu .context-menu-item el-icon { + color: inherit; +} + +.sub-canvas-context-menu .context-menu-divider { + height: 1px; + background-color: #e1e4e8; + margin: 4px 0; +} + +/* 深色主题支持 - 与主画布保持一致 */ +.dark .sub-canvas-context-menu .custom-context-menu { + background-color: #2d3748; + border-color: #4a5568; +} + +.dark .sub-canvas-context-menu .context-menu-item { + color: #e2e8f0; +} + +.dark .sub-canvas-context-menu .context-menu-item:hover { + background-color: #4a5568; + color: #409eff; +} + +.dark .sub-canvas-context-menu .context-menu-divider { + background-color: #4a5568; +} diff --git a/src/views/createapp/components/workFlowConfig/LoopNodeDrawer.vue b/src/views/createapp/components/workFlowConfig/LoopNodeDrawer.vue index 38376a01..b13ffff6 100644 --- a/src/views/createapp/components/workFlowConfig/LoopNodeDrawer.vue +++ b/src/views/createapp/components/workFlowConfig/LoopNodeDrawer.vue @@ -1,7 +1,7 @@
- + - + @@ -47,7 +47,7 @@ - 循环设置 + {{ $t('flow.node_config.loop_settings') }}
@@ -57,7 +57,7 @@
-
循环变量
+
{{ $t('flow.node_config.loop_variables') }}
- 添加变量 + {{ $t('flow.node_config.add_variable') }}
@@ -85,7 +85,7 @@
@@ -94,16 +94,16 @@ - - - - - + + + + +
@@ -118,7 +118,7 @@ :current-step-id="currentStepId" :supported-scopes="['conversation', 'system', 'env']" :self-variables="variables" - self-scope-label="循环变量" + :self-scope-label="$t('flow.node_config.loop_variable_scope')" :show-variable-name="false" :show-label="false" :show-actions="false" @@ -127,7 +127,7 @@ :no-border-radius="true" :transparent-background="true" output-format="raw" - selector-placeholder="选择要引用的变量" + :selector-placeholder="$t('flow.node_config.select_reference_variable')" @variable-selected="updateVariables" />
@@ -138,7 +138,7 @@ @@ -146,7 +146,7 @@ @@ -168,7 +168,7 @@ v-model="variable.value" type="textarea" :rows="3" - placeholder="输入JSON格式的对象" + :placeholder="$t('flow.node_config.input_json_object')" size="small" @change="updateVariables" /> @@ -183,7 +183,7 @@ class="danger-button" @click="removeVariable(index)" type="button" - title="删除变量" + :title="$t('flow.node_config.delete_variable')" > @@ -200,7 +200,7 @@ class="add-first-variable-btn" > - 添加变量 + {{ $t('flow.node_config.add_variable') }}
@@ -211,7 +211,7 @@
-

循环终止条件

+

{{ $t('flow.node_config.loop_stop_condition') }}

@@ -227,7 +227,7 @@ :conversation-id="''" :current-step-id="currentStepId" :self-variables="variables" - self-scope-label="循环变量" + :self-scope-label="$t('flow.node_config.loop_variable_scope')" @remove-branch="handleRemoveBranch" @add-condition="handleAddConditionFromCard" @remove-condition="handleRemoveCondition" @@ -242,7 +242,7 @@ class="add-first-condition-btn" > - 添加终止条件 + {{ $t('flow.node_config.add_stop_condition') }}
@@ -250,7 +250,7 @@
-

最大循环次数

+

{{ $t('flow.node_config.max_iterations') }}

@@ -272,8 +272,8 @@ @@ -286,6 +286,7 @@ import { Plus, Delete } from '@element-plus/icons-vue'; import { IconCaretRight } from '@computing/opendesign-icons'; import VariableChooser from '../../../../components/VariableChooser.vue'; import ChoiceBranchCard from './ChoiceBranchCard.vue'; +import i18n from '@/i18n'; interface LoopVariable { name: string; @@ -346,7 +347,7 @@ const activeTab = ref('settings'); const variables = ref([]); const stopConditionBranch = ref({ branch_id: 'stop_condition_branch', - name: '循环终止条件', + name: i18n.global.t('flow.node_names.loop_condition'), logic: 'and', conditions: [], is_default: false @@ -355,43 +356,43 @@ const maxIteration = ref(10); // 为ChoiceBranchCard提供的数据 const valueTypeOptions = ref([ - { label: '字符串', value: 'string' }, - { label: '数值', value: 'number' }, - { label: '数组', value: 'list' }, - { label: '布尔值', value: 'bool' }, - { label: '对象', value: 'dict' } + { label: i18n.global.t('flow.node_config.type_string'), value: 'string' }, + { label: i18n.global.t('flow.node_config.type_number'), value: 'number' }, + { label: i18n.global.t('flow.node_config.type_list'), value: 'list' }, + { label: i18n.global.t('flow.node_config.type_boolean'), value: 'bool' }, + { label: i18n.global.t('flow.node_config.type_object'), value: 'dict' } ]); const operatorOptions = ref({ string: [ - { label: '等于', value: 'string_equal' }, - { label: '不等于', value: 'string_not_equal' }, - { label: '包含', value: 'string_contains' }, - { label: '不包含', value: 'string_not_contains' } + { label: i18n.global.t('opertion.string_equal'), value: 'string_equal' }, + { label: i18n.global.t('opertion.string_not_equal'), value: 'string_not_equal' }, + { label: i18n.global.t('opertion.string_contains'), value: 'string_contains' }, + { label: i18n.global.t('opertion.string_not_contains'), value: 'string_not_contains' } ], number: [ - { label: '等于', value: 'number_equal' }, - { label: '不等于', value: 'number_not_equal' }, - { label: '大于', value: 'number_greater_than' }, - { label: '小于', value: 'number_less_than' }, - { label: '大于等于', value: 'number_greater_than_or_equal' }, - { label: '小于等于', value: 'number_less_than_or_equal' } + { label: i18n.global.t('opertion.number_equal'), value: 'number_equal' }, + { label: i18n.global.t('opertion.number_not_equal'), value: 'number_not_equal' }, + { label: i18n.global.t('opertion.number_greater_than'), value: 'number_greater_than' }, + { label: i18n.global.t('opertion.number_less_than'), value: 'number_less_than' }, + { label: i18n.global.t('opertion.number_greater_than_or_equal'), value: 'number_greater_than_or_equal' }, + { label: i18n.global.t('opertion.number_less_than_or_equal'), value: 'number_less_than_or_equal' } ], list: [ - { label: '等于', value: 'list_equal' }, - { label: '不等于', value: 'list_not_equal' }, - { label: '包含', value: 'list_contains' }, - { label: '不包含', value: 'list_not_contains' } + { label: i18n.global.t('opertion.equal'), value: 'list_equal' }, + { label: i18n.global.t('opertion.not_equal'), value: 'list_not_equal' }, + { label: i18n.global.t('opertion.contains'), value: 'list_contains' }, + { label: i18n.global.t('opertion.does_not_contain'), value: 'list_not_contains' } ], bool: [ - { label: '等于', value: 'bool_equal' }, - { label: '不等于', value: 'bool_not_equal' } + { label: i18n.global.t('opertion.bool_equal'), value: 'bool_equal' }, + { label: i18n.global.t('opertion.bool_not_equal'), value: 'bool_not_equal' } ], dict: [ - { label: '等于', value: 'dict_equal' }, - { label: '不等于', value: 'dict_not_equal' }, - { label: '包含键', value: 'dict_contains_key' }, - { label: '不包含键', value: 'dict_not_contains_key' } + { label: i18n.global.t('opertion.dict_equal'), value: 'dict_equal' }, + { label: i18n.global.t('opertion.dict_not_equal'), value: 'dict_not_equal' }, + { label: i18n.global.t('opertion.dict_contains_key'), value: 'dict_contains_key' }, + { label: i18n.global.t('opertion.dict_not_contains_key'), value: 'dict_not_contains_key' } ] }); @@ -608,7 +609,7 @@ const handleSave = () => { ); if (duplicates.length > 0) { - ElMessage.error('变量名不能重复'); + ElMessage.error(i18n.global.t('flow.node_config.duplicate_variable_name')); return; } @@ -626,7 +627,7 @@ const handleSave = () => { variablesObj[variable.name] = { type: variable.type, value: variable.value, - description: `循环变量: ${variable.name}` + description: `${i18n.global.t('flow.node_config.loop_variable_scope')}: ${variable.name}` }; } } @@ -671,7 +672,7 @@ const handleSave = () => { emit('save', saveData); visible.value = false; - ElMessage.success('循环节点保存成功'); + ElMessage.success(i18n.global.t('flow.node_config.loop_node_save_success')); }; // 监听数据变化 @@ -736,18 +737,28 @@ onMounted(() => { .section-title { font-size: 16px; font-weight: 600; - color: #1f2937; + color: var(--el-text-color-primary); margin-bottom: 16px; + + body[theme='dark'] & { + color: #e4e8ee; + } } .description-content { padding: 12px 16px; - background-color: #f8f9fa; + background-color: var(--el-fill-color-extra-light); border-radius: 6px; - border: 1px solid #e9ecef; + border: 1px solid var(--el-border-color-light); font-size: 14px; line-height: 1.5; - color: #495057; + color: var(--el-text-color-primary); + + body[theme='dark'] & { + background-color: #1f2329; + border-color: var(--el-border-color); + color: #e4e8ee; + } } .description-section { @@ -772,8 +783,16 @@ onMounted(() => { height: 48px; line-height: 48px; padding: 0 16px; - background-color: #f8f9fa; - border-bottom: 1px solid #e4e7ed; + background-color: var(--el-fill-color-extra-light); + + body[theme='dark'] & { + background-color: #1f2329; + }; + border-bottom: 1px solid var(--el-border-color-light); + + body[theme='dark'] & { + border-bottom-color: var(--el-border-color); + }; .el-collapse-item__arrow { margin-right: 8px; @@ -791,7 +810,11 @@ onMounted(() => { .el-collapse-item__content { padding: 16px; - background-color: #ffffff; + background-color: var(--el-bg-color); + + body[theme='dark'] & { + background-color: #1f2329; + }; } } @@ -855,8 +878,12 @@ onMounted(() => { display: block; font-size: 12px; font-weight: 500; - color: #6b7280; + color: var(--el-text-color-secondary); margin-bottom: 6px; + + body[theme='dark'] & { + color: #d3dce9; + } } .variable-actions, @@ -878,12 +905,20 @@ onMounted(() => { .help-text { font-size: 12px; - color: #6b7280; + color: var(--el-text-color-secondary); + + body[theme='dark'] & { + color: #d3dce9; + } } .empty-state { text-align: center; - color: #9ca3af; + color: var(--el-text-color-secondary); + + body[theme='dark'] & { + color: #d3dce9; + } } .empty-text { @@ -893,11 +928,17 @@ onMounted(() => { .add-first-condition-btn { width: 100%; - background: white; - border: 2px dashed #d1d5db; + background: var(--el-bg-color); + border: 2px dashed var(--el-border-color); border-radius: 8px; padding: 16px; - color: #6b7280; + color: var(--el-text-color-secondary); + + body[theme='dark'] & { + background: #1f2329; + border-color: var(--el-border-color); + color: #d3dce9; + }; font-size: 14px; font-weight: 500; cursor: pointer; @@ -908,14 +949,19 @@ onMounted(() => { gap: 8px; &:hover { - border-color: #409eff; - color: #409eff; - background: #f0f8ff; + border-color: var(--el-color-primary); + color: var(--el-color-primary); + background: var(--el-color-primary-light-9); + + body[theme='dark'] & { + background: var(--flow-node-default-over-color, #25303e); + border-color: var(--flow-node-boder-default-over, #314265); + } } &:focus { outline: none; - border-color: #409eff; + border-color: var(--el-color-primary); box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2); } @@ -931,24 +977,37 @@ onMounted(() => { } .placeholder-text { - color: #9ca3af; + color: var(--el-text-color-secondary); font-size: 14px; margin: 0; + + body[theme='dark'] & { + color: #d3dce9; + } } .drawer-footer { padding: 16px 20px; - border-top: 1px solid #e1e4e8; + border-top: 1px solid var(--el-border-color-light); display: flex; justify-content: flex-end; gap: 12px; + background: var(--el-bg-color); + + body[theme='dark'] & { + border-top-color: var(--el-border-color); + } } /* 变量卡片样式 - 参考ChoiceBranchCard设计 */ .variables-card { border-radius: 8px; margin-bottom: 16px; - background: #ffffff; + background: var(--el-bg-color); + + body[theme='dark'] & { + background: #1f2329; + } } .variables-card .variables-section { @@ -964,9 +1023,13 @@ onMounted(() => { .variables-card .variables-title { font-weight: 600; - color: #1f2937; + color: var(--el-text-color-primary); font-size: 16px; margin-bottom: 16px; + + body[theme='dark'] & { + color: #e4e8ee; + } } .variables-card .add-btn { @@ -993,11 +1056,16 @@ onMounted(() => { .variables-card .variable-setting-col { flex: 1; min-width: 0; - background: #f8f9fa; - border: 1px solid #e4e7ed; + background: var(--el-fill-color-extra-light); + border: 1px solid var(--el-border-color-light); border-radius: 8px; overflow: hidden; transition: background-color 0.2s ease; + + body[theme='dark'] & { + background: var(--o-bash-bg, #2a2f37); + border-color: var(--el-border-color); + } } .variables-card .variable-delete-col { @@ -1016,11 +1084,15 @@ onMounted(() => { } .variables-card .variable-row:not(:last-child) { - border-bottom: 1px solid #e4e7ed; + border-bottom: 1px solid var(--el-border-color-light); margin-left: 2px; margin-right: 2px; padding-left: 0; padding-right: 0; + + body[theme='dark'] & { + border-bottom-color: var(--el-border-color); + } } .variables-card .variable-row.name-row { @@ -1029,8 +1101,12 @@ onMounted(() => { .variables-card .name-section { flex: 3; - border-right: 1px solid #e4e7ed; + border-right: 1px solid var(--el-border-color-light); padding-right: 12px; + + body[theme='dark'] & { + border-right-color: var(--el-border-color); + } } .variables-card .name-section :deep(.el-input__wrapper) { @@ -1083,19 +1159,29 @@ onMounted(() => { .variables-card .empty-state { text-align: center; - color: #9ca3af; + color: var(--el-text-color-secondary); + + body[theme='dark'] & { + color: #d3dce9; + } } .variables-card .add-first-variable-btn { width: 100%; - background: white; - border: 2px dashed #d1d5db; + background: var(--el-bg-color); + border: 2px dashed var(--el-border-color); border-radius: 8px; padding: 16px; - color: #6b7280; + color: var(--el-text-color-secondary); font-size: 14px; font-weight: 500; cursor: pointer; + + body[theme='dark'] & { + background: #1f2329; + border-color: var(--el-border-color); + color: #d3dce9; + }; transition: all 0.2s ease; display: flex; align-items: center; @@ -1104,14 +1190,18 @@ onMounted(() => { } .variables-card .add-first-variable-btn:hover { - border-color: #409eff; + border-color: var(--el-color-primary); color: #409eff; - background: #f0f8ff; + background: var(--el-color-primary-light-9); + + body[theme='dark'] & { + background: var(--flow-node-default-over-color, #25303e); + }; } .variables-card .add-first-variable-btn:focus { outline: none; - border-color: #409eff; + border-color: var(--el-color-primary); box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2); } @@ -1125,7 +1215,7 @@ onMounted(() => { } .variables-card .variables-section:has(.action-buttons .danger-button:hover) .variable-setting-col { - background-color: #eee8e9; + background-color: var(--el-color-danger-light-9); transition: none; } diff --git a/src/views/createapp/components/workFlowConfig/MCPNodeDrawer.vue b/src/views/createapp/components/workFlowConfig/MCPNodeDrawer.vue new file mode 100644 index 00000000..40fcc9a4 --- /dev/null +++ b/src/views/createapp/components/workFlowConfig/MCPNodeDrawer.vue @@ -0,0 +1,710 @@ + + + + + + + + \ No newline at end of file diff --git a/src/views/createapp/components/workFlowConfig/NodeListPanel.vue b/src/views/createapp/components/workFlowConfig/NodeListPanel.vue index d4138dc9..50d82f0f 100644 --- a/src/views/createapp/components/workFlowConfig/NodeListPanel.vue +++ b/src/views/createapp/components/workFlowConfig/NodeListPanel.vue @@ -13,7 +13,7 @@ v-if="searchKeyword.trim()" class="clear-icon" @click="resetSearch" - title="清空搜索" + :title="$t('common.clear_search')" > ✕
@@ -55,8 +55,11 @@ + + diff --git a/src/views/createapp/components/workFlowConfig/PluginListPanel.vue b/src/views/createapp/components/workFlowConfig/PluginListPanel.vue new file mode 100644 index 00000000..23f34d63 --- /dev/null +++ b/src/views/createapp/components/workFlowConfig/PluginListPanel.vue @@ -0,0 +1,860 @@ + + + + + diff --git a/src/views/createapp/components/workFlowConfig/PluginNode.vue b/src/views/createapp/components/workFlowConfig/PluginNode.vue new file mode 100644 index 00000000..d99ddf25 --- /dev/null +++ b/src/views/createapp/components/workFlowConfig/PluginNode.vue @@ -0,0 +1,727 @@ + + + + + \ No newline at end of file diff --git a/src/views/createapp/components/workFlowConfig/RAGNodeDrawer.vue b/src/views/createapp/components/workFlowConfig/RAGNodeDrawer.vue new file mode 100644 index 00000000..a24d74c2 --- /dev/null +++ b/src/views/createapp/components/workFlowConfig/RAGNodeDrawer.vue @@ -0,0 +1,910 @@ + + + + + + + + \ No newline at end of file diff --git a/src/views/createapp/components/workFlowConfig/SimpleCodeNodeDrawer.vue b/src/views/createapp/components/workFlowConfig/SimpleCodeNodeDrawer.vue index 2c1e7a65..1175bf5a 100644 --- a/src/views/createapp/components/workFlowConfig/SimpleCodeNodeDrawer.vue +++ b/src/views/createapp/components/workFlowConfig/SimpleCodeNodeDrawer.vue @@ -28,19 +28,19 @@ - 设置 + {{ $t('settings.setting') }}
- + - + { // 深色主题支持 .dark { .variable-assign-card { - background: #374151; + background: #1f2329; .operation-container { .operation-setting-col { diff --git a/src/views/createapp/components/workFlowConfig/VariableAssignNode.vue b/src/views/createapp/components/workFlowConfig/VariableAssignNode.vue index 9e12c8f2..d490f742 100644 --- a/src/views/createapp/components/workFlowConfig/VariableAssignNode.vue +++ b/src/views/createapp/components/workFlowConfig/VariableAssignNode.vue @@ -2,8 +2,11 @@ import { Position, Handle } from '@vue-flow/core'; import { ref, computed, watch } from 'vue'; import { Plus, Delete } from '@element-plus/icons-vue'; +import { useI18n } from 'vue-i18n'; import { getSrcIcon, getNodeClass } from '../types'; +const { t } = useI18n(); + const props = defineProps({ id: { type: String, @@ -117,19 +120,19 @@ const variableOperations = computed(() => { // 获取操作类型的显示名称 const getOperationDisplayName = (operation: string): string => { const operationMap: Record = { - 'overwrite': '覆盖', - 'clear': '清空', - 'add': '加法', - 'subtract': '减法', - 'multiply': '乘法', - 'divide': '除法', - 'modulo': '求余', - 'power': '乘幂', - 'sqrt': '开方', - 'append': '追加', - 'extend': '扩展', - 'pop_first': '移除首项', - 'pop_last': '移除尾项' + 'overwrite': t('flow.node_config.operation_overwrite'), + 'clear': t('flow.node_config.operation_clear'), + 'add': t('flow.node_config.operation_add'), + 'subtract': t('flow.node_config.operation_subtract'), + 'multiply': t('flow.node_config.operation_multiply'), + 'divide': t('flow.node_config.operation_divide'), + 'modulo': t('flow.node_config.operation_modulo'), + 'power': t('flow.node_config.operation_power'), + 'sqrt': t('flow.node_config.operation_sqrt'), + 'append': t('flow.node_config.operation_append'), + 'extend': t('flow.node_config.operation_extend'), + 'pop_first': t('flow.node_config.operation_pop_first'), + 'pop_last': t('flow.node_config.operation_pop_last') }; return operationMap[operation] || operation; }; @@ -184,7 +187,7 @@ watch( class="iconStyle" :src="getSrcIcon(props.data)" /> -
{{ data.name || '变量赋值' }}
+
{{ data.name || t('flow.node_names.variable_assign') }}
@@ -204,7 +207,7 @@ watch(
- 点击配置变量操作 + {{ t('flow.node_config.click_to_configure_operations') }}
@@ -222,14 +225,14 @@ watch(
@@ -272,7 +275,7 @@ watch( } &.node-selected .nodeContainer { - border-color: #409eff; + border-color: #409eff; box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2); &:hover { @@ -568,46 +571,25 @@ watch( -// 深色主题支持 - 使用用户指定的颜色规范 +// 深色主题支持 - 使用与其他Node一致的样式规范 .dark { - .variable-assign-border { - background: #26262a !important; - border: 2px solid rgba(255, 255, 255, 0.08) !important; - - &:hover { - border-color: #3182ce; + .customNodeStyle { + .nodeContainer { + background: #353f58 !important; + border: 2px solid rgba(255, 255, 255, 0.08) !important; + + &:hover { + border-color: #3182ce; + } } - &.node-selected { + &.node-selected .nodeContainer { border-color: #3182ce; box-shadow: 0 0 0 2px rgba(49, 130, 206, 0.2); } - &.running { - border-color: #3182ce; - background: linear-gradient(135deg, #1a365d 0%, #2c5282 100%); - } - - &.success { - border-color: #68d391; - background: linear-gradient(135deg, #1a202c 0%, #2d3748 100%); - } - - &.error { - border-color: #fc8181; - background: linear-gradient(135deg, #2d1b1b 0%, #3d2626 100%); - } - } - - .node-content { - .node-header { - .node-icon { - color: #63b3ed; - } - - .node-title { - color: #ffffff !important; - } + .label { + color: #ffffff !important; } .operations-list { @@ -633,6 +615,15 @@ watch( color: #718096; } } + + .nodeFooter { + background: rgba(45, 55, 72, 0.8); + border-top-color: rgba(255, 255, 255, 0.1); + + .nodeIdText { + color: #a0aec0; + } + } } .vue-flow__handle { @@ -657,5 +648,20 @@ watch( } } } + + .deleteButton { + background-color: #e53e3e; + color: #ffffff; + box-shadow: 0 4px 12px rgba(229, 62, 62, 0.4); + + &:hover { + background-color: #e53e3e; + box-shadow: 0 6px 20px rgba(229, 62, 62, 0.5); + } + + &:active { + background-color: #c53030; + } + } } diff --git a/src/views/createapp/components/workFlowConfig/VariableAssignNodeDrawer.vue b/src/views/createapp/components/workFlowConfig/VariableAssignNodeDrawer.vue index b9296c73..570f5d50 100644 --- a/src/views/createapp/components/workFlowConfig/VariableAssignNodeDrawer.vue +++ b/src/views/createapp/components/workFlowConfig/VariableAssignNodeDrawer.vue @@ -4,6 +4,7 @@ import { ElDrawer, ElButton, ElInput, ElForm, ElFormItem, ElMessage } from 'elem import { Plus } from '@element-plus/icons-vue'; import { v4 as uuidv4 } from 'uuid'; import VariableAssignCard from './VariableAssignCard.vue'; +import i18n from '@/i18n'; // 类型定义 interface VariableOperation { @@ -59,7 +60,7 @@ const localNodeData = ref({ // 表单验证规则 const rules = { name: [ - { required: true, message: '请输入节点名称', trigger: 'blur' } + { required: true, message: i18n.global.t('flow.node_config.node_name_placeholder'), trigger: 'blur' } ] }; @@ -79,7 +80,7 @@ watch( ([visible, nodeData]) => { if (visible && nodeData && typeof nodeData === 'object') { localNodeData.value = { - name: nodeData.name || '变量赋值', + name: nodeData.name || i18n.global.t('flow.node_names.variable_assign'), description: nodeData.description || '', callId: nodeData.callId || 'VariableAssign', parameters: { @@ -113,7 +114,7 @@ const addOperation = () => { // 删除操作 const removeOperation = (operationIndex: number) => { if (operations.value.length <= 1) { - ElMessage.warning('至少需要保留一个变量操作'); + ElMessage.warning(i18n.global.t('flow.node_config.at_least_one_operation')); return; } @@ -129,7 +130,7 @@ const updateOperation = (operationIndex: number, updatedOperation: VariableOpera const validateData = (): boolean => { // 检查节点名称 if (!localNodeData.value.name.trim()) { - ElMessage.error('请输入节点名称'); + ElMessage.error(i18n.global.t('flow.node_config.node_name_placeholder')); return false; } @@ -138,19 +139,19 @@ const validateData = (): boolean => { const operation = operations.value[i]; if (!operation.variable_name.trim()) { - ElMessage.error(`第 ${i + 1} 个操作:请选择变量`); + ElMessage.error(i18n.global.t('flow.node_config.operation_select_variable', { index: i + 1 })); return false; } if (!operation.operation) { - ElMessage.error(`第 ${i + 1} 个操作:请选择操作类型`); + ElMessage.error(i18n.global.t('flow.node_config.operation_select_type', { index: i + 1 })); return false; } // 检查需要值的操作是否提供了值 const operationsWithoutValue = ['clear', 'sqrt', 'pop_first', 'pop_last']; if (!operationsWithoutValue.includes(operation.operation) && !operation.value) { - ElMessage.error(`第 ${i + 1} 个操作:请输入操作值`); + ElMessage.error(i18n.global.t('flow.node_config.operation_input_value', { index: i + 1 })); return false; } } @@ -201,7 +202,7 @@ const conversationId = computed(() => { @@ -213,20 +214,20 @@ const conversationId = computed(() => { label-width="80px" class="node-form" > - + - + @@ -235,14 +236,14 @@ const conversationId = computed(() => {
-

变量操作

+

{{ $t('flow.node_config.variable_operations') }}

- 添加操作 + {{ $t('flow.node_config.add_operation') }}
@@ -266,8 +267,8 @@ const conversationId = computed(() => {
diff --git a/src/views/createapp/components/workFlowConfig/VariableBasedStartNodeDrawer.vue b/src/views/createapp/components/workFlowConfig/VariableBasedStartNodeDrawer.vue index 2a7eed44..26e155cd 100644 --- a/src/views/createapp/components/workFlowConfig/VariableBasedStartNodeDrawer.vue +++ b/src/views/createapp/components/workFlowConfig/VariableBasedStartNodeDrawer.vue @@ -13,7 +13,7 @@
-
开始
+
{{ $t('flow.node_names.start') }}
@@ -22,7 +22,7 @@
- 开始节点 + {{ $t('flow.node_names.start') }}
{{ nodeDescription }} @@ -31,7 +31,7 @@ v-else v-model="nodeDescription" type="textarea" - placeholder="开始节点" + :placeholder="$t('flow.node_names.start')" :rows="3" maxlength="200" show-word-limit @@ -45,15 +45,15 @@
-
设置
+
{{ $t('common.setting') }}
-
输入字段
-
设置的输入可在工作流程中使用
+
{{ $t('startNodeVariableManager.input_fields') }}
+
{{ $t('startNodeVariableManager.input_fields_hint') }}
-
暂无变量
+
{{ $t('startNodeVariableManager.no_variables') }}
@@ -121,44 +121,44 @@ - + - - - - - - - - - + + + + + + + + + - - - - - - - - + + + + + + + + - +
@@ -167,7 +167,7 @@ 文档
-
文档
+
{{ $t('startNodeVariableManager.document_category') }}
TXT, MD, MDX, MARKDOWN, PDF, HTML, XLSX, XLS, DOC, DOCX, CSV, EML, MSG, PPTX, PPT, XML, EPUB
@@ -180,7 +180,7 @@ 图片
-
图片
+
{{ $t('startNodeVariableManager.image_category') }}
JPG, JPEG, PNG, GIF, WEBP, SVG
@@ -193,7 +193,7 @@ 音频
-
音频
+
{{ $t('startNodeVariableManager.audio_category') }}
MP3, M4A, WAV, AMR, MPGA
@@ -206,7 +206,7 @@ 视频
-
视频
+
{{ $t('startNodeVariableManager.video_category') }}
MP4, MOV, MPEG, WEBM
@@ -219,11 +219,11 @@ 其他文件类型
-
其他文件类型
+
{{ $t('startNodeVariableManager.other_file_types') }}
@@ -234,27 +234,27 @@
-
上传文件类型
+
{{ $t('startNodeVariableManager.upload_file_types') }}
- 本地上传 + {{ $t('startNodeVariableManager.local_upload') }}
- URL上传 + {{ $t('startNodeVariableManager.url_upload') }}
-
文件上传限制
+
{{ $t('startNodeVariableManager.file_upload_limits') }}
- + - 文件类型固定为1个文件 + {{ $t('startNodeVariableManager.file_type_fixed') }}
- + - MB + {{ $t('startNodeVariableManager.mb_unit') }}
- + - 选中后,用户在对话时必须上传文件 + {{ $t('startNodeVariableManager.required_file_note') }}
- + @@ -312,7 +312,7 @@ @@ -323,7 +323,7 @@ v-else-if="editingVariable.var_type === 'secret'" v-model="editingVariable.value" type="password" - placeholder="请输入密钥值" + :placeholder="$t('startNodeVariableManager.enter_secret_value')" show-password /> @@ -333,14 +333,14 @@ v-model="editingVariable.valueJson" type="textarea" :rows="4" - placeholder="请输入JSON格式的对象值" + :placeholder="$t('startNodeVariableManager.enter_json_object')" />
- 文件类型变量将在对话时由用户上传 + {{ $t('startNodeVariableManager.file_type_tip') }}
@@ -348,7 +348,7 @@
- 字符串数组变量将在对话时由用户输入,默认为空数组 + {{ $t('startNodeVariableManager.string_array_tip') }}
@@ -356,7 +356,7 @@
- 数字数组变量将在对话时由用户输入,默认为空数组 + {{ $t('startNodeVariableManager.number_array_tip') }}
@@ -364,7 +364,7 @@
- 布尔值数组变量将在对话时由用户选择,默认为空数组 + {{ $t('startNodeVariableManager.boolean_array_tip') }}
@@ -372,7 +372,7 @@
- 文件列表类型变量将在对话时由用户上传,默认为空数组 + {{ $t('startNodeVariableManager.file_array_tip') }}
@@ -380,7 +380,7 @@
- 对象数组变量将在对话时由用户输入JSON格式数据,默认为空数组 + {{ $t('startNodeVariableManager.object_array_tip') }}
@@ -388,7 +388,7 @@
- 密钥数组变量将在对话时由用户输入,默认为空数组 + {{ $t('startNodeVariableManager.secret_array_tip') }}
@@ -396,36 +396,36 @@
- 任意类型数组变量将在对话时由用户输入,默认为空数组 + {{ $t('startNodeVariableManager.any_array_tip') }}
- + @@ -506,11 +506,11 @@ const variableFormRef = ref() // 表单验证规则 const variableFormRules = { name: [ - { required: true, message: '请输入变量名称', trigger: 'blur' }, - { pattern: /^[a-zA-Z_][a-zA-Z0-9_]*$/, message: '变量名只能包含字母、数字和下划线,且必须以字母或下划线开头', trigger: 'blur' } + { required: true, message: t('startNodeVariableManager.enter_variable_name_validation'), trigger: 'blur' }, + { pattern: /^[a-zA-Z_][a-zA-Z0-9_]*$/, message: t('startNodeVariableManager.variable_name_pattern_validation'), trigger: 'blur' } ], var_type: [ - { required: true, message: '请选择变量类型', trigger: 'change' } + { required: true, message: t('startNodeVariableManager.select_variable_type_validation'), trigger: 'change' } ] } @@ -593,7 +593,7 @@ const loadAllVariables = async () => { } catch (error) { console.error('❌ 变量加载过程发生未知错误:', error) - ElMessage.error('变量加载失败') + ElMessage.error(t('startNodeVariableManager.load_variables_failed')) } finally { variablesLoading.value = false } @@ -632,7 +632,7 @@ onUnmounted(() => { // 获取变量显示值 const getVariableDisplayValue = (value: any): string => { - if (value === null || value === undefined) return '(未设置)' + if (value === null || value === undefined) return t('startNodeVariableManager.not_set') if (typeof value === 'object') return JSON.stringify(value) return String(value) } @@ -704,9 +704,9 @@ const finishEditDesc = async () => { description: nodeDescription.value }) - ElMessage.success('描述保存成功') + ElMessage.success(t('startNodeVariableManager.description_save_success')) } catch (error) { - ElMessage.error('描述保存失败') + ElMessage.error(t('startNodeVariableManager.description_save_failed')) } } @@ -835,22 +835,22 @@ const editConversationVariable = (variable: Variable) => { const saveConversationVariable = async () => { // 详细的参数验证 if (!editingVariable.value) { - ElMessage.error('缺少变量数据') + ElMessage.error(t('startNodeVariableManager.missing_variable_data')) return } if (!props.flowId) { - ElMessage.error('缺少工作流ID (flowId),无法保存对话变量') + ElMessage.error(t('startNodeVariableManager.missing_flow_id')) return } if (!editingVariable.value.name || !editingVariable.value.name.trim()) { - ElMessage.error('变量名不能为空') + ElMessage.error(t('startNodeVariableManager.variable_name_empty')) return } if (!editingVariable.value.var_type) { - ElMessage.error('请选择变量类型') + ElMessage.error(t('startNodeVariableManager.select_variable_type_required')) return } @@ -867,7 +867,7 @@ const saveConversationVariable = async () => { if (editingVariable.value.value !== null && editingVariable.value.value !== undefined) { value = Number(editingVariable.value.value) if (isNaN(value)) { - ElMessage.error('请输入有效的数字') + ElMessage.error(t('startNodeVariableManager.enter_valid_number')) return } } else { @@ -888,7 +888,7 @@ const saveConversationVariable = async () => { try { value = JSON.parse(editingVariable.value.valueJson) } catch (error) { - ElMessage.error('JSON格式不正确,请检查对象值的语法') + ElMessage.error(t('startNodeVariableManager.json_format_incorrect')) return } } else { @@ -959,11 +959,11 @@ const saveConversationVariable = async () => { } const updateResult = await updateVariable(updateParams, updateData) - ElMessage.success('变量更新成功') + ElMessage.success(t('startNodeVariableManager.variable_update_success')) } else { // 创建变量 const createResult = await createVariable(variableData) - ElMessage.success('变量创建成功') + ElMessage.success(t('startNodeVariableManager.variable_create_success')) } handleVariableDialogClose() @@ -1020,7 +1020,7 @@ const deleteConversationVariable = async () => { flow_id: props.flowId }) - ElMessage.success('变量删除成功') + ElMessage.success(t('startNodeVariableManager.variable_delete_success')) // 在关闭对话框前先保存变量名(避免引用失效) const deletedVariableName = editingVariable.value.name @@ -1039,7 +1039,7 @@ const deleteConversationVariable = async () => { emits('variablesUpdated') } catch (error) { console.error('❌ 删除变量失败:', error) - ElMessage.error('删除变量失败') + ElMessage.error(t('startNodeVariableManager.delete_variable_failed')) } } @@ -1076,7 +1076,7 @@ const saveStartNodeConfig = () => { } emits('saveStartNode', nodeParams, props.nodeYamlId, nodeName.value, nodeDescription.value) - ElMessage.success('保存成功') + ElMessage.success(t('startNodeVariableManager.save_success')) closeDrawer() } @@ -1244,11 +1244,16 @@ const toggleUploadMethod = (method: 'manual' | 'url') => { padding: 16px 24px; border-bottom: 1px solid var(--o-border-color-light); margin-bottom: 0; + background: var(--el-bg-color); } .el-drawer__body { padding: 0; + background: var(--el-bg-color); } + + // 深色主题适配 + background: var(--el-bg-color); } .drawerHeader { @@ -1268,12 +1273,18 @@ const toggleUploadMethod = (method: 'manual' | 'url') => { .headerText { font-size: 16px; font-weight: 600; - color: var(--o-text-color-primary); + color: var(--el-text-color-primary); + + // 深色主题下确保文字可读 + body[theme='dark'] & { + color: #e4e8ee; + } } } .drawerBody { padding: 0; + background: var(--el-bg-color); .descriptionSection { margin: 20px 24px; @@ -1287,11 +1298,22 @@ const toggleUploadMethod = (method: 'manual' | 'url') => { text-align: center; font-size: 14px; transition: all 0.2s; + background: var(--el-fill-color-extra-light); + + body[theme='dark'] & { + background: var(--flow-bg-color, #343a43); + border-color: var(--el-border-color); + } &:hover { border-color: var(--el-color-primary); color: var(--el-color-primary); background: var(--el-color-primary-light-9); + + body[theme='dark'] & { + background: var(--flow-node-default-over-color, #25303e); + border-color: var(--flow-node-boder-default-over, #314265); + } } } @@ -1304,24 +1326,51 @@ const toggleUploadMethod = (method: 'manual' | 'url') => { font-size: 14px; line-height: 1.4; transition: all 0.2s; + color: var(--el-text-color-primary); + + body[theme='dark'] & { + background: var(--flow-bg-color, #343a43); + border-color: var(--el-border-color); + color: #e4e8ee; + } &:hover { border-color: var(--el-color-primary); background: var(--el-color-primary-light-9); + + body[theme='dark'] & { + background: var(--flow-node-default-over-color, #25303e); + border-color: var(--flow-node-boder-default-over, #314265); + } } } .descInput { margin-top: 8px; + + body[theme='dark'] & { + :deep(.el-textarea__inner) { + background: var(--flow-bg-color, #343a43) !important; + border-color: var(--el-border-color) !important; + color: #e4e8ee !important; + + &::placeholder { + color: var(--el-text-color-placeholder) !important; + } + } + } } } .tabContainer { + background: var(--el-bg-color); + .tabHeader { display: flex; border-bottom: 1px solid var(--el-border-color-light); padding: 0 24px; margin-bottom: 20px; + background: var(--el-bg-color); .tabItem { padding: 14px 16px; @@ -1345,6 +1394,7 @@ const toggleUploadMethod = (method: 'manual' | 'url') => { .inputFieldsSection { padding: 0 24px 24px; + background: var(--el-bg-color); .inputFieldsHeader { display: flex; @@ -1359,6 +1409,10 @@ const toggleUploadMethod = (method: 'manual' | 'url') => { font-weight: 600; color: var(--el-text-color-primary); margin-bottom: 6px; + + body[theme='dark'] & { + color: #e4e8ee; + } } .inputFieldsHint { @@ -1384,16 +1438,22 @@ const toggleUploadMethod = (method: 'manual' | 'url') => { color: var(--el-color-primary); background: var(--el-color-primary-light-9); transform: scale(1.1); + + body[theme='dark'] & { + background: var(--flow-node-default-over-color, #25303e); + } } &:active { transform: scale(0.95); } + } } .variableList { min-height: 100px; + background: var(--el-bg-color); .variableItem { display: flex; @@ -1405,18 +1465,34 @@ const toggleUploadMethod = (method: 'manual' | 'url') => { background: var(--el-fill-color-extra-light); transition: all 0.2s; + // 深色主题优化 + body[theme='dark'] & { + background: var(--flow-bg-color, #343a43); + border-color: var(--el-border-color); + } + &.editable { cursor: pointer; &:hover { background: var(--el-color-primary-light-9); border-color: var(--el-color-primary-light-7); + + body[theme='dark'] & { + background: var(--flow-node-default-over-color, #25303e); + border-color: var(--flow-node-boder-default-over, #314265); + } } } &.readonly { opacity: 0.8; background: var(--el-fill-color-lighter); + + body[theme='dark'] & { + background: var(--o-bash-bg, #2a2f37); + opacity: 0.7; + } } .variableIcon { @@ -1438,6 +1514,10 @@ const toggleUploadMethod = (method: 'manual' | 'url') => { font-family: 'Monaco', 'Consolas', monospace; line-height: 1.4; margin-bottom: 4px; + + body[theme='dark'] & { + color: #e4e8ee; + } } .variableType { @@ -1447,6 +1527,11 @@ const toggleUploadMethod = (method: 'manual' | 'url') => { padding: 2px 6px; border-radius: 4px; display: inline-block; + + body[theme='dark'] & { + background: var(--o-bash-bg, #2a2f37); + color: var(--el-text-color-secondary); + } } } } @@ -1455,6 +1540,7 @@ const toggleUploadMethod = (method: 'manual' | 'url') => { text-align: center; padding: 40px 20px; color: var(--el-text-color-secondary); + background: var(--el-bg-color); .emptyText { font-size: 14px; @@ -1472,7 +1558,8 @@ const toggleUploadMethod = (method: 'manual' | 'url') => { justify-content: flex-end; gap: 12px; padding: 16px 24px; - border-top: 1px solid var(--o-border-color-light); + border-top: 1px solid var(--el-border-color-light); + background: var(--el-bg-color); } // 透明遮罩样式 @@ -1544,9 +1631,19 @@ const toggleUploadMethod = (method: 'manual' | 'url') => { transition: all 0.2s; cursor: pointer; + body[theme='dark'] & { + background: var(--flow-bg-color, #343a43); + border-color: var(--el-border-color); + } + &:hover { border-color: var(--el-color-primary-light-7); background: var(--el-color-primary-light-9); + + body[theme='dark'] & { + background: var(--flow-node-default-over-color, #25303e); + border-color: var(--flow-node-boder-default-over, #314265); + } } &:active { @@ -1582,6 +1679,10 @@ const toggleUploadMethod = (method: 'manual' | 'url') => { font-weight: 600; color: var(--el-text-color-primary); margin-bottom: 4px; + + body[theme='dark'] & { + color: #e4e8ee; + } } .category-types { @@ -1595,6 +1696,10 @@ const toggleUploadMethod = (method: 'manual' | 'url') => { .el-input { font-size: 12px; + + :deep(.el-input__wrapper) { + background: var(--el-bg-color); + } } } } @@ -1611,6 +1716,10 @@ const toggleUploadMethod = (method: 'manual' | 'url') => { font-weight: 600; color: var(--el-text-color-primary); margin-bottom: 12px; + + body[theme='dark'] & { + color: #e4e8ee; + } } .upload-method-tabs { @@ -1626,7 +1735,12 @@ const toggleUploadMethod = (method: 'manual' | 'url') => { cursor: pointer; font-size: 13px; transition: all 0.2s; - background: white; + background: var(--el-bg-color); + color: var(--el-text-color-primary); + + body[theme='dark'] & { + color: #e4e8ee; + } &.active { border-color: var(--el-color-primary); @@ -1637,6 +1751,11 @@ const toggleUploadMethod = (method: 'manual' | 'url') => { &:hover:not(.active) { border-color: var(--el-color-primary-light-7); background: var(--el-color-primary-light-9); + + body[theme='dark'] & { + background: var(--flow-node-default-over-color, #25303e); + border-color: var(--flow-node-boder-default-over, #314265); + } } } } @@ -1649,6 +1768,10 @@ const toggleUploadMethod = (method: 'manual' | 'url') => { font-weight: 600; color: var(--el-text-color-primary); margin-bottom: 8px; + + body[theme='dark'] & { + color: #e4e8ee; + } } .upload-limit-item { @@ -1662,6 +1785,10 @@ const toggleUploadMethod = (method: 'manual' | 'url') => { color: var(--el-text-color-primary); font-weight: 500; min-width: 120px; + + body[theme='dark'] & { + color: #e4e8ee; + } } .unit-label { @@ -1696,6 +1823,11 @@ const toggleUploadMethod = (method: 'manual' | 'url') => { margin-bottom: 12px; color: var(--el-text-color-secondary); font-size: 13px; + + body[theme='dark'] & { + background: var(--o-bash-bg, #2a2f37); + border-color: var(--el-border-color); + } } } @@ -1712,9 +1844,12 @@ const toggleUploadMethod = (method: 'manual' | 'url') => { margin-bottom: 12px; color: var(--el-text-color-secondary); font-size: 13px; + + body[theme='dark'] & { + background: var(--o-bash-bg, #2a2f37); + border-color: var(--el-border-color); + } } - - } // 数组输入区域 @@ -1730,15 +1865,32 @@ const toggleUploadMethod = (method: 'manual' | 'url') => { margin-bottom: 12px; color: var(--el-text-color-secondary); font-size: 13px; + + body[theme='dark'] & { + background: var(--o-bash-bg, #2a2f37); + border-color: var(--el-border-color); + } } } // 变量对话框样式增强 :deep(.el-dialog) { + background: var(--el-bg-color); + + .el-dialog__header { + background: var(--el-bg-color); + border-bottom: 1px solid var(--el-border-color-light); + + .el-dialog__title { + color: var(--el-text-color-primary); + } + } + .el-dialog__body { padding: 20px 24px; max-height: 70vh; overflow-y: auto; + background: var(--el-bg-color); } .el-form-item { @@ -1747,11 +1899,16 @@ const toggleUploadMethod = (method: 'manual' | 'url') => { .el-form-item__label { font-weight: 600; color: var(--el-text-color-primary); + + body[theme='dark'] & { + color: #e4e8ee !important; + } } .el-form-item__content { .el-input__wrapper { transition: all 0.2s; + background: var(--el-bg-color); &:hover { box-shadow: 0 0 0 1px var(--el-color-primary-light-7); @@ -1760,15 +1917,26 @@ const toggleUploadMethod = (method: 'manual' | 'url') => { .el-select { width: 100%; + + .el-select__wrapper { + background: var(--el-bg-color); + } } .el-textarea__inner { transition: all 0.2s; + background: var(--el-bg-color); &:hover { border-color: var(--el-color-primary-light-7); } } + + .el-input-number { + .el-input__wrapper { + background: var(--el-bg-color); + } + } } } @@ -1776,10 +1944,16 @@ const toggleUploadMethod = (method: 'manual' | 'url') => { .el-button { border-style: dashed; transition: all 0.2s; + background: var(--el-bg-color); &:hover { border-color: var(--el-color-primary); color: var(--el-color-primary); + background: var(--el-color-primary-light-9); + + body[theme='dark'] & { + background: var(--flow-node-default-over-color, #25303e); + } } } } @@ -1789,18 +1963,22 @@ const toggleUploadMethod = (method: 'manual' | 'url') => { .variable-dialog { :deep(.el-dialog) { max-height: 90vh; + background: var(--el-bg-color); .el-dialog__header { border-bottom: 1px solid var(--el-border-color-light); + background: var(--el-bg-color); } .el-dialog__body { max-height: 70vh; overflow-y: auto; + background: var(--el-bg-color); } .el-dialog__footer { border-top: 1px solid var(--el-border-color-light); + background: var(--el-bg-color); } } } @@ -1814,6 +1992,11 @@ const toggleUploadMethod = (method: 'manual' | 'url') => { padding: 16px; background: var(--el-fill-color-extra-light); + body[theme='dark'] & { + background: var(--flow-bg-color, #343a43); + border-color: var(--el-border-color); + } + // 自定义滚动条样式 &::-webkit-scrollbar { width: 6px; @@ -1822,10 +2005,14 @@ const toggleUploadMethod = (method: 'manual' | 'url') => { &::-webkit-scrollbar-track { background: var(--el-fill-color-light); border-radius: 3px; + + body[theme='dark'] & { + background: var(--o-bash-bg, #2a2f37); + } } &::-webkit-scrollbar-thumb { - background: var(--el-border-color); + background: var(--o-scrollbar-thumb, var(--el-border-color)); border-radius: 3px; &:hover { diff --git a/src/views/createapp/components/workFlowConfig/insertNodeMenu.vue b/src/views/createapp/components/workFlowConfig/insertNodeMenu.vue index 76555e1d..ca75e658 100644 --- a/src/views/createapp/components/workFlowConfig/insertNodeMenu.vue +++ b/src/views/createapp/components/workFlowConfig/insertNodeMenu.vue @@ -12,54 +12,30 @@ @click.stop > - - - +
+ + \ No newline at end of file diff --git a/src/views/createapp/components/workFlowConfig/useDnD.js b/src/views/createapp/components/workFlowConfig/useDnD.js index f097b5f0..c4d80be8 100644 --- a/src/views/createapp/components/workFlowConfig/useDnD.js +++ b/src/views/createapp/components/workFlowConfig/useDnD.js @@ -255,13 +255,28 @@ function createNewNode(nodeMetaData, position, customNodeId = null) { const nodeId = customNodeId || getId(); const cleanNodeData = sanitizeNodeData(nodeMetaData, nodeId); + // 确定节点类型 + let nodeType = 'custom'; // 默认类型 + if (nodeMetaData.type === 'plugin-node') { + nodeType = 'plugin-node'; + } else if (nodeMetaData.callId === 'Choice') { + nodeType = 'Choice'; + } else if (nodeMetaData.callId === 'Loop') { + nodeType = 'Loop'; + } else if (nodeMetaData.callId === 'VariableAssign') { + nodeType = 'VariableAssign'; + } + return { id: nodeId, - type: nodeMetaData.callId === 'Choice' ? 'Choice' : - nodeMetaData.callId === 'Loop' ? 'Loop' : - nodeMetaData.callId === 'VariableAssign' ? 'VariableAssign' : 'custom', + type: nodeType, position, - data: { + data: nodeMetaData.type === 'plugin-node' ? { + // 插件节点的完整数据结构 + ...nodeMetaData.data, + nodeId: nodeId + } : { + // 常规节点的数据结构 name: cleanNodeData.name, description: cleanNodeData.description, nodeId: cleanNodeData.nodeId, diff --git a/src/views/dialogue/components/AppInitalPreview.vue b/src/views/dialogue/components/AppInitalPreview.vue index 326de5ed..c674c5b6 100644 --- a/src/views/dialogue/components/AppInitalPreview.vue +++ b/src/views/dialogue/components/AppInitalPreview.vue @@ -61,7 +61,7 @@ watch( background-color: var(--o-bg-color-base); border-radius: 8px; bottom: 0px; - box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); + box-shadow: var(--o-shadow-base); margin: 12px 88px; } @@ -77,10 +77,10 @@ watch( float: right; text-align: center; margin-right: 98px; - color: #8d98aa; + color: var(--o-text-color-secondary); .red-word { - color: #e02020; + color: var(--o-color-danger); } } diff --git a/src/views/dialogue/components/DialogueVariablePanel.vue b/src/views/dialogue/components/DialogueVariablePanel.vue index c3f1e9c9..640fb884 100644 --- a/src/views/dialogue/components/DialogueVariablePanel.vue +++ b/src/views/dialogue/components/DialogueVariablePanel.vue @@ -1024,8 +1024,8 @@ watch( + + diff --git a/src/views/settings/components/ModelCard.vue b/src/views/settings/components/ModelCard.vue index 5957e900..043f5f82 100644 --- a/src/views/settings/components/ModelCard.vue +++ b/src/views/settings/components/ModelCard.vue @@ -1,5 +1,5 @@