diff --git a/Dockerfile b/Dockerfile index 556ab0e587bb0b68c6c16b4fa63ca7cc7ade5b45..e477cfd2d2962d9303a2ea461c344df061f17a1a 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 30f83bd6496845a8a41bcde2e007840e9e112dae..0859c55c92c9fca07c1f219fcfc7d0c57e1c6fbd 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 b10a8d2c058fdc127ba387f33094221454acf34a..9a5f99dbf09f4b0cfd3732e00a44a18b41e68b9a 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/dark-mode-fix-summary.md b/dark-mode-fix-summary.md new file mode 100644 index 0000000000000000000000000000000000000000..5f3dbdca01e077ce16b1ea5a585bbf460def5ab9 --- /dev/null +++ b/dark-mode-fix-summary.md @@ -0,0 +1,56 @@ +# 黑夜模式样式优先级修复总结 + +## 问题分析 +由于Vue组件使用`scoped`样式,组件内部定义的样式具有更高的CSS特异性,导致外部的黑夜模式样式无法覆盖原有的样式。 + +## 解决方案 +使用`!important`声明来强制覆盖组件内部样式,确保黑夜模式下的样式能够正确应用。 + +## 修复的文件和样式 + +### 1. LoopNode.vue +- ✅ `.loopNodeStyle` 背景色和边框 +- ✅ `.title .label` 标题文字颜色 +- ✅ `.title .iconStyle` 图标颜色 +- ✅ `.loopInfo` 信息区域背景和边框 +- ✅ `.infoLabel` 和 `.infoValue` 文字和背景 +- ✅ `.embeddedFlowCanvas` 子画布背景和边框 + +### 2. CustomNode.vue +- ✅ `.customNodeStyle` 主容器背景和边框 +- ✅ `.nodeBox` 内容区域背景 +- ✅ `.title .label` 标题文字颜色 +- ✅ `.nodeFooter` 底部区域样式 + +### 3. ChoiceBranchNode.vue +- ✅ `.choiceBranchNodeStyle` 主容器样式 +- ✅ `.title .label` 标题文字颜色 + +### 4. VariableAssignNode.vue +- ✅ `.variable-assign-border` 主容器样式 +- ✅ `.node-title` 标题文字颜色 + +### 5. CustomSaENode.vue +- ✅ `.nodeSaEBorder` 主容器样式 +- ✅ `.label` 和 `.iconStyle` 文字和图标颜色 + +### 6. workFlowArrange.scss +- ✅ 全局深色主题样式增强 +- ✅ CSS变量系统使用用户指定的颜色规范 + +## 最终样式规范 +```scss +// 黑夜模式颜色规范 +background: #26262a !important; +border: 2px solid rgba(255, 255, 255, 0.08) !important; +color: #ffffff !important; +``` + +## 验证方法 +1. 切换到黑夜模式 +2. 检查所有节点容器背景是否为 `#26262a` +3. 检查所有节点边框是否为半透明白色 +4. 检查所有标题文字是否为白色 +5. 验证LoopNode的子画布样式与主画布一致 + +所有修改都使用了`!important`声明来确保样式优先级足够高,能够覆盖组件内部的scoped样式。 diff --git a/deploy/nginx.conf.tmpl b/deploy/nginx.conf.tmpl index c2ef1a64218811aeb6ac1dd4357072b3fc2af2a3..6ec3bd9409fd0f169f7093607357bda2696f03bd 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 a2d6daa99b739b11a014a09b2e91c464c8aa50f3..6ec8b07ea5320c96e65e0548c08636316e9b68ac 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 1cd6acc8c47a0cf694670540552a6103b77b686f..cbb9e61ff35a9a6e7ac9d34b778d38568fdc0b32 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/public/styles/base/index.scss b/public/styles/base/index.scss index 4dbcb7afe3ce35a3ced158ec0bd07915a5ecbf1c..a585ef3cf9e7da52527d11aed34b278a33bf7af9 100644 --- a/public/styles/base/index.scss +++ b/public/styles/base/index.scss @@ -22,8 +22,4 @@ ::-webkit-scrollbar-thumb:hover { background-color: var(--o-scrollbar-thumb) !important; /* 鼠标悬停时的滚动条按钮颜色 */ -} -div{ - // 全局 div 默认光标 - cursor: default !important; } \ No newline at end of file diff --git a/src/apis/appCenter/appCenterService.ts b/src/apis/appCenter/appCenterService.ts index 025e7899e39561faa46629f785fa4dfb8ede9d72..b5553932922e06370bb80ccd1ce174e3dd58f77b 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 93411cdfd4d012974ef357b27183497015f9d8b4..c465a147b48174a8956eac27c53651ce42350657 100644 --- a/src/apis/paths/account.ts +++ b/src/apis/paths/account.ts @@ -7,7 +7,7 @@ // IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR // PURPOSE. // See the Mulan PSL v2 for more details. -import { get, post } from 'src/apis/server'; +import { get, post, put } from 'src/apis/server'; import type { FcResponse } from 'src/apis/server'; /** @@ -24,6 +24,7 @@ export const authorizeUser = (): Promise< organization: string; revision_number: string | null; is_admin: boolean; + auto_execute?: boolean; }> | undefined ), @@ -133,6 +134,41 @@ function queryAuthUrl(action: string) { }>('/api/auth/redirect', { action }); } +/** + * 获取用户偏好设置 + * @returns + */ +export const getUserPreferences = (): Promise< + [ + any, + ( + | FcResponse<{ + reasoningModelPreference?: any; + embeddingModelPreference?: any; + rerankerPreference?: any; + chainOfThoughtPreference?: boolean; + }> + | undefined + ), + ] +> => { + return get('/api/user/preferences'); +}; + +/** + * 更新用户偏好设置 + * @param preferences + * @returns + */ +export const updateUserPreferences = (preferences: { + reasoningModelPreference?: any; + embeddingModelPreference?: any; + rerankerPreference?: any; + chainOfThoughtPreference?: boolean; +}): Promise<[any, FcResponse | undefined]> => { + return put('/api/user/preferences', preferences); +}; + export const accountApi = { authorizeUser, authorizeLogout, @@ -141,4 +177,6 @@ export const accountApi = { refreshToken, updateRevision, queryAuthUrl, + getUserPreferences, + updateUserPreferences, }; diff --git a/src/apis/paths/conversation.ts b/src/apis/paths/conversation.ts index 1527ebb87d3dc6a3506cabdf95f16dd24023858d..b97d083ca7adeebdb1458688cb4c5d31310b7dfd 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`); + const 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/knowledge.ts b/src/apis/paths/knowledge.ts index b5ccbe2c51d57e67d086ad0a8734b263c48f4ab7..ed1a3a1d7153d30fc5f31b7437ce502df0957a68 100644 --- a/src/apis/paths/knowledge.ts +++ b/src/apis/paths/knowledge.ts @@ -32,7 +32,15 @@ export const getConvKnowledgeList = (params: { return get('/api/knowledge', params); }; +export const getTeamKnowledgeList = (params?: { + kbId?: string; + kbName?: string; +}): Promise<[any, FcResponse<{}> | undefined]> => { + return get('/api/knowledge/team', params); +}; + export const knowledgeApi = { updateKnowledgeList, getConvKnowledgeList, + getTeamKnowledgeList, }; diff --git a/src/apis/paths/llm.ts b/src/apis/paths/llm.ts index aa20aa4ecd3310b91a262f346d833f5af5e06acd..0a64684f58b06f83c4b04c449ce88b9563275908 100644 --- a/src/apis/paths/llm.ts +++ b/src/apis/paths/llm.ts @@ -32,8 +32,35 @@ const getAddedModels = () => { return get('/api/llm'); }; +/** + * 获取 Embedding 配置 + * @returns + */ +const getEmbeddingConfig = () => { + return get<{ + type: string; + endpoint: string; + api_key: string; + model: string; + icon?: string; + }>('/api/llm/embedding'); +}; + +/** + * 获取 Reranker 配置 + * @returns + */ +const getRerankerConfig = () => { + return get<{ + type: string; + name: string; + }[]>('/api/llm/reranker'); +}; + export const llmApi = { getAddedModels, getLLMList, updateLLMList, + getEmbeddingConfig, + getRerankerConfig, }; diff --git a/src/apis/paths/mcp.ts b/src/apis/paths/mcp.ts index 31ec1eb56c97ad0331a06ccb4762bd0762ecdc04..1931379789366ecf35f8f71ef4629a2fdde5a537 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 d610be29dfbe9ff96ffe175f54a8727bb5f05bbc..3305110aef86088393ba4793a2995d860688718b 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 518437fc1b29841eb7d128a24c32b98ce028c9c1..02e4912bbbd4b2e3220f1bbc6fb6f16026790a4b 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/styles/base/index.scss b/src/assets/styles/base/index.scss index 795e3f627bbf9797f5040b7afc586efd5604ab78..34fa254f144dc7c4378cf183e6b96df41afc0ebe 100644 --- a/src/assets/styles/base/index.scss +++ b/src/assets/styles/base/index.scss @@ -23,10 +23,6 @@ background-color: var(--o-scrollbar-thumb) !important; /* 鼠标悬停时的滚动条按钮颜色 */ } -div{ - // 全局 div 默认光标 - cursor: default !important; -} textarea::-webkit-input-placeholder { color: var(--o-think-header-text); font-size: 14px; diff --git a/src/assets/styles/variables/theme.scss b/src/assets/styles/variables/theme.scss index e760f795345c8762bb8bc1bce4724fd4d35cfc05..f115d3686c99919861c2de82505d5545f467a0e0 100644 --- a/src/assets/styles/variables/theme.scss +++ b/src/assets/styles/variables/theme.scss @@ -85,12 +85,41 @@ body[theme='dark'] { --flow-debug-hover: url('../../assets/svgs/dark_debug_hover.svg'); --flow-debug-active: url('../../assets/svgs/dark_debug_active.svg'); --flow-debug-dis: url('../../assets/svgs/dark_debug_dis.svg'); + + // 环境变量按钮专用变量 - 使用纯色背景 + --flow-env-default: #374151; + --flow-env-hover: #4b5563; + --flow-env-active: #374151; + --flow-env-dis: #6b7280; // 这里是不同种类的展开收起图标 --expand-fold-default: url('../../assets/svgs/dark_expand_fold.svg'); --expand-fold-hover: url('../../assets/svgs/dark_expand_fold_hover.svg'); --expand-fold-active: url('../../assets/svgs/dark_expand_fold_active.svg'); --o-scrollbar-thumb: #576372; --o-apiBox-bg: rgb(253, 254, 255); + + // 深色模式下的组件颜色变量 + --o-bg-color-base: #1f2329; + --o-text-color-primary: #e4e8ee; + --o-text-color-secondary: #a3a6ad; + --o-text-color-regular: #8d98aa; + --o-border-color: #3e4551; + --o-border-color-light: #3e4551; + --o-border-color-lighter: #2a2f37; + --o-color-primary: #6395fd; + --o-color-primary-light-3: #79a6ff; + --o-color-primary-light-7: #4d7fff; + --o-color-primary-light-8: #3d73ff; + --o-color-primary-light-9: #2d66ff; + --o-fill-color-extra-light: #2a2f37; + --o-fill-color-light: #343a43; + --o-color-success: #67c23a; + --o-color-success-light-9: #1f2b1a; + --o-color-warning: #e6a23c; + --o-color-warning-light-9: #2b261a; + --o-color-danger: #f56c6c; + --o-color-danger-light-9: #2b1f1f; + --o-shadow-base: 0 4px 12px rgba(0, 0, 0, 0.3); } body[theme='light'] { @@ -187,6 +216,12 @@ body[theme='light'] { --flow-debug-hover: url('../../assets/svgs/light_debug_hover.svg'); --flow-debug-active: url('../../assets/svgs/light_debug_active.svg'); --flow-debug-dis: url('../../assets/svgs/light_debug_dis.svg'); + + // 环境变量按钮专用变量 - 使用纯色背景 + --flow-env-default: #ffffff; + --flow-env-hover: #f8f9fa; + --flow-env-active: #e9ecef; + --flow-env-dis: #f8f9fa; // 这里是不同种类的展开收起图标 --expand-fold-default: url('../../assets/svgs/light_expand_fold.svg'); --expand-fold-hover: url('../../assets/svgs/light_expand_fold_hover.svg'); @@ -194,6 +229,29 @@ body[theme='light'] { --o-scrollbar-thumb: #c3cedf; --o-apiBox-bg: rgb(253, 254, 255); --el-drawer-padding-primary: 24px !important; + + // 浅色模式下的组件颜色变量 + --o-bg-color-base: #ffffff; + --o-text-color-primary: #303133; + --o-text-color-secondary: #606266; + --o-text-color-regular: #909399; + --o-border-color: #dcdfe6; + --o-border-color-light: #dcdfe6; + --o-border-color-lighter: #e4e7ed; + --o-color-primary: #409eff; + --o-color-primary-light-3: #79bbff; + --o-color-primary-light-7: #c6e2ff; + --o-color-primary-light-8: #ecf2ff; + --o-color-primary-light-9: #f2f6fc; + --o-fill-color-extra-light: #f8f9fa; + --o-fill-color-light: #f5f7fa; + --o-color-success: #67c23a; + --o-color-success-light-9: #f0f9ff; + --o-color-warning: #e6a23c; + --o-color-warning-light-9: #fdf6ec; + --o-color-danger: #f56c6c; + --o-color-danger-light-9: #fef0f0; + --o-shadow-base: 0 4px 12px rgba(0, 0, 0, 0.08); } body { diff --git a/src/assets/svgs/file_download.svg b/src/assets/svgs/file_download.svg new file mode 100644 index 0000000000000000000000000000000000000000..283d57e696c0cc738161a593fe5a5af61eb8b81f --- /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 0000000000000000000000000000000000000000..c072db44196d37edee48be9499d18f0e67744582 --- /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 0000000000000000000000000000000000000000..09d8b74fef906ba4786dc0809cf4e1bdf238b54b --- /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 0000000000000000000000000000000000000000..80482d0f812be11cf7b2d6f20227eab884ac2226 --- /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 0000000000000000000000000000000000000000..64c1c5ad07225905221df8e791f97fb0c0c00fc1 --- /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 0000000000000000000000000000000000000000..0bbf3ab1e8f6388db6e32321c578141429db1d3a --- /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 0000000000000000000000000000000000000000..f3cf7bc416c2ce7407dfca546b9e8f9fcf7e1012 --- /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 0000000000000000000000000000000000000000..76dbe7ba5d9e0dc2ce4e77413a5816e0d5b86e80 --- /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 0000000000000000000000000000000000000000..6256758d15ae950973284b32f03dfe85b650b995 --- /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 0000000000000000000000000000000000000000..71554801a5aa7005cbc4643970ba1f70daac34a2 --- /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 0000000000000000000000000000000000000000..572476042d634abda0338cd4d76eb9826a1d74cd --- /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 0000000000000000000000000000000000000000..a971b21aef314924421a19def5be7eeb52599b33 --- /dev/null +++ b/src/assets/svgs/zip.svg @@ -0,0 +1,28 @@ + + + Created with Pixso. + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/CodeMonacoEditor.vue b/src/components/CodeMonacoEditor.vue index 9a386aa56f92e0466005af75ba0550c81ac7c239..a1b07f9d5ed3e23b639d9e1be86345543e51c357 100644 --- a/src/components/CodeMonacoEditor.vue +++ b/src/components/CodeMonacoEditor.vue @@ -4,7 +4,7 @@
- +
- 插入模板 + {{ $t('flow.insert_template') }} - 格式化 + {{ $t('flow.format_code') }} - 复制 + {{ $t('common.copy') }} - {{ isFullScreen ? '退出全屏' : '全屏' }} + {{ isFullScreen ? $t('flow.exit_fullscreen') : $t('flow.fullscreen') }}
@@ -45,7 +45,7 @@ v-model="selectedSymbol" @change="navigateToSymbol" size="small" - placeholder="跳转到..." + :placeholder="$t('flow.navigate_to')" style="width: 200px" clearable filterable @@ -79,6 +79,7 @@ import { ref, onMounted, onBeforeUnmount, watch, computed, nextTick } from 'vue' import { ElSelect, ElOption, ElButton, ElMessage } from 'element-plus' import { CopyDocument, FullScreen, Minus } from '@element-plus/icons-vue' +import { useI18n } from 'vue-i18n' import * as monaco from 'monaco-editor' interface Props { @@ -96,7 +97,7 @@ const props = withDefaults(defineProps(), { language: 'python', minHeight: 200, maxHeight: 600, - placeholder: '请输入代码...', + placeholder: 'Please enter code...', disabled: false, customTemplate: undefined }) @@ -107,6 +108,7 @@ const emit = defineEmits<{ 'language-change': [language: string] }>() +const { t } = useI18n() const editorContainer = ref() const currentLanguage = ref(props.language) const selectedSymbol = ref('') @@ -138,23 +140,23 @@ const containerHeight = computed(() => { }) // 代码模板 -const codeTemplates = { +const codeTemplates = computed(() => ({ python: `def main(**kwargs): """ - 代码执行主函数 + ${t('flow.code_main_function')} Args: - **kwargs: 输入变量字典 + **kwargs: ${t('flow.input_variables_dict')} Returns: - dict: 输出变量字典 + dict: ${t('flow.output_variables_dict')} """ - # 获取输入变量 - # 示例:value = kwargs.get('input_variable_name', default_value) + # ${t('flow.get_input_variables')} + # ${t('flow.example_get_variable')} - # 在这里编写您的 Python 代码 + # ${t('flow.write_python_code_here')} result = { - # 定义输出变量 + # ${t('flow.define_output_variables')} # 'output_variable_name': value } @@ -162,18 +164,18 @@ const codeTemplates = { javascript: `function main(variables = {}) { /** - * 代码执行主函数 + * ${t('flow.code_main_function')} * - * @param {Object} variables - 输入变量对象 - * @returns {Object} 输出变量对象 + * @param {Object} variables - ${t('flow.input_variables_object')} + * @returns {Object} ${t('flow.output_variables_object')} */ - // 获取输入变量 - // 示例:const value = variables.input_variable_name || default_value; + // ${t('flow.get_input_variables')} + // ${t('flow.example_js_get_variable')} - // 在这里编写您的 JavaScript 代码 + // ${t('flow.write_js_code_here')} const result = { - // 定义输出变量 + // ${t('flow.define_output_variables')} // output_variable_name: value }; @@ -182,17 +184,17 @@ const codeTemplates = { shell: `#!/bin/bash -# 代码执行主函数 -# 输入变量通过环境变量传递:INPUT_VARIABLE_NAME -# 输出变量以JSON格式打印到stdout +# ${t('flow.code_main_function')} +# ${t('flow.shell_input_env_vars')} +# ${t('flow.shell_output_json')} main() { - # 获取输入变量 - # 示例:local value="\${INPUT_VARIABLE_NAME:-default_value}" + # ${t('flow.get_input_variables')} + # ${t('flow.example_shell_get_variable')} - # 在这里编写您的 Bash 代码 + # ${t('flow.write_bash_code_here')} - # 输出JSON格式结果 + # ${t('flow.output_json_result')} cat << EOF { "output_variable_name": "value" @@ -200,9 +202,9 @@ main() { EOF } -# 调用主函数 +# ${t('flow.call_main_function')} main` -} +})) // 初始化Monaco Editor const initMonacoEditor = async () => { @@ -261,7 +263,7 @@ const initMonacoEditor = async () => { // 创建编辑器 editor = monaco.editor.create(editorContainer.value, { - value: props.modelValue || codeTemplates[currentLanguage.value] || '', + value: props.modelValue || codeTemplates.value[currentLanguage.value] || '', language: currentLanguage.value, theme: 'atom-one-dark-pro', automaticLayout: true, @@ -381,7 +383,7 @@ const updateSymbols = async () => { symbols.value = foundSymbols } catch (error) { - console.warn('获取文档符号失败:', error) + console.warn('Failed to get document symbols:', error) symbols.value = [] } } @@ -441,11 +443,11 @@ const handleLanguageChange = (newLanguage: string) => { // 插入模板 const insertTemplate = () => { // 优先使用传入的自定义模板,否则使用默认模板 - const template = props.customTemplate || codeTemplates[currentLanguage.value] + const template = props.customTemplate || codeTemplates.value[currentLanguage.value] if (editor && template) { editor.setValue(template) emit('update:modelValue', template) - ElMessage.success('模板已插入') + ElMessage.success(t('flow.template_inserted')) } } @@ -453,7 +455,7 @@ const insertTemplate = () => { const formatCode = () => { if (editor) { editor.getAction('editor.action.formatDocument')?.run() - ElMessage.success('代码已格式化') + ElMessage.success(t('flow.code_formatted')) } } @@ -463,10 +465,10 @@ const copyCode = async () => { const code = editor.getValue() try { await navigator.clipboard.writeText(code) - ElMessage.success('代码已复制到剪贴板') + ElMessage.success(t('flow.code_copied')) } catch (err) { - console.error('复制失败:', err) - ElMessage.error('复制失败,请手动复制') + console.error('Copy failed:', err) + ElMessage.error(t('flow.copy_failed')) } } } @@ -477,7 +479,7 @@ const toggleFullScreen = () => { if (isFullScreen.value) { // 进入全屏模式 - 立即应用所有样式避免动画过程 - ElMessage.info('按 ESC 或 F11 键退出全屏模式') + ElMessage.info(t('flow.fullscreen_exit_tip')) // 先应用全屏样式,再添加类名,确保无缝切换 addFullscreenStyles() diff --git a/src/components/NodeVariableConfig.vue b/src/components/NodeVariableConfig.vue index 42cf8ac4ea088f31c8e2df61e87b27c989b4fa7b..64a60b26274b1e72c44273ccd6823aa671ece3ca 100644 --- a/src/components/NodeVariableConfig.vue +++ b/src/components/NodeVariableConfig.vue @@ -1,5 +1,6 @@ \ No newline at end of file diff --git a/src/components/commonFooter/CommonFooter.vue b/src/components/commonFooter/CommonFooter.vue index 527eb53a372eb681a3b5da62bb6f11eab9ead125..4897990521b8093d4f3043be304b8e71310a090f 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 210d7f0b3cced1cc96fa9425a79f34bc444cf1f0..cfd18a8dba6a83182b84154e0d9e64b30a7f665d 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 6139cede76c42987bc0e10ac5cde63c50e1474fe..e830bea8206a4657683e879d921705650b61a436 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 5b6e518af81551e6dc6910a194962e2a4e414d77..f3ac75843b6e7e174b4545dbee57763e69298f32 100644 --- a/src/i18n/lang/en.ts +++ b/src/i18n/lang/en.ts @@ -7,90 +7,106 @@ 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.', + clear_search: 'Clear search', + no_data: 'No Data', + characters: 'characters', + setting: '设置', + reply: 'Reply', }, 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.', }, + model_preferences_settings: 'Model Preferences Settings', + reasoning_model_preference: 'Reasoning Model Preference', + embedding_model_preference: 'Embedding Model Preference', + reranker_preference: 'Reranker Preference', + chain_of_thought_preference: 'Chain of Thought Preference', }, 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.', + component_name: 'component name', + component_introduction: 'component introduction', + selectedModel: 'select model', + key: 'key', + value: 'value', + rag_query_variables: 'query' }, plugin_center: { plugin_name: 'Plugin Name', @@ -106,91 +122,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 +222,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 +276,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 +315,1019 @@ 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.', + }, + // API Call Drawer specific translations + apiCall: { + basic_info: 'Basic Information', + request_headers: 'Request Headers', + query_params: 'Query Parameters', + request_body: 'Request Body', + output_variables: 'Output Variables', + node_name_placeholder: 'Enter node name', + node_description_placeholder: 'Enter node description', + api_url_placeholder: 'Enter API address', + key_placeholder: 'Enter key', + value_placeholder: 'Enter value', + json_body_placeholder: 'Enter JSON format request body', + url_validation_message: 'URL must start with http:// or https://', + add_header: 'Add Header', + add_query_param: 'Add Query Parameter', + add_form_data: 'Add Form Data', + add_multipart_data: 'Add Data', + }, + // LLM Node Drawer specific translations + llmNode: { + basic_info: 'Basic Information', + prompt_config: 'Prompt Configuration', + parameter_config: 'Parameter Configuration', + output_variables: 'Output Variables', + node_name_placeholder: 'Enter node name', + node_description_placeholder: 'Enter node description', + model_placeholder: 'Please select model', + system_prompt_label: 'System Prompt', + system_prompt_placeholder: 'Enter system prompt', + user_prompt_label: 'User Prompt', + user_prompt_placeholder: 'Enter user prompt', + question_label: 'Question', + question_placeholder: 'Enter question', + temperature_label: 'Temperature', + context_label: 'Context', + frequency_penalty_label: 'Frequency Penalty', + }, + // MCP Node Drawer specific translations + mcpNode: { + basic_info: 'Basic Information', + output_variables: 'Output Variables', + node_name_placeholder: 'Enter node name', + node_description_placeholder: 'Enter node description', + mcp_tools_label: 'MCP Tools', + mcp_tools_placeholder: 'Please select MCP tools', + max_steps_label: 'Max Steps', + query_mcp_service_failed: 'Failed to query MCP services', + }, + // RAG Node Drawer specific translations + ragNode: { + basic_info: 'Basic Information', + knowledge_config: 'Knowledge Base Configuration', + advanced_options: 'Advanced Options', + output_variables: 'Output Variables', + node_name_placeholder: 'Enter node name', + node_description_placeholder: 'Enter node description', + query_placeholder: 'Enter question to query knowledge base', + knowledge_base_label: 'Knowledge Base', + knowledge_base_placeholder: 'Please select knowledge base', + search_method_label: 'Search Method', + search_method_placeholder: 'Please select search method', + is_rerank_label: 'Enable Rerank', + is_compress_label: 'Enable Compression', + is_classify_by_doc_label: 'Classify by Document', + is_related_surrounding_label: 'Related Context', + keyword_search: 'Keyword Search', + vector_search: 'Vector Search', + hybrid_search: 'Hybrid Search', + doc2chunk_search: 'Document Chunk Search', + doc2chunk_bfs_search: 'Document Chunk Search (BFS)', + enhanced_by_llm_search: 'LLM Enhanced Search', + query_knowledge_base_failed: 'Failed to query knowledge base', + no_knowledge_base_available: 'No knowledge base available', }, 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', + nodes: 'Nodes', + plugins: 'Plugins', + node_names: { + start: 'Start', + end: 'End', + loop: 'Loop', + choice: 'Choice', + variable_assign: 'Variable Assignment', + file_extractor: 'File Extractor', + skip_round: 'Skip Round', + exit_loop: 'Exit Loop', + loop_subflow: 'Loop Subflow', + loop_condition: 'Loop Condition', + }, + // File Extractor Configuration + file_extractor: { + title: 'Configure File Extractor', + basic_info: 'Basic Information', + node_name: 'Node Name', + node_name_placeholder: 'Enter node name', + node_description: 'Node Description', + node_description_placeholder: 'Enter node description', + input_variables: 'Input Variables', + parse_method: 'Parse Method', + parse_method_placeholder: 'Select file parsing method', + parse_method_description: 'Extract text content from files, supports parsing of various file formats', + input_file: 'Input File', + supported_file_types: 'Supported file types: {types}', + select_file_variable: 'Select file variable', + select_file_or_array_variable: 'Select file or file array variable', + output_variables: 'Output Variables', + variable_name: 'Variable Name', + type: 'Type', + description: 'Description', + text_output_description: 'Extracted text content', + error_output_description: 'Error message (if any)', + string_type: 'String', + cancel: 'Cancel', + save: 'Save', + // Validation messages + node_name_required: 'Please enter node name', + parse_method_required: 'Please select file parsing method', + input_file_required: 'Please select input file variable', + // Error messages + load_parse_methods_failed: 'Failed to load parse method list', + }, + node_groups: { + tool: 'Tools', + transform: 'Transform', + logic: 'Logic', + default: 'Default', + }, + node_config: { + basic_info: 'Basic Information', + node_name: 'Node Name', + node_name_placeholder: 'Enter node name', + node_name_required: 'Please enter node name', + node_description: 'Node Description', + node_description_placeholder: 'Enter node description', + security_level: 'Security Level', + security_level_placeholder: 'Select security level', + security_level_low: 'Low Security Level (Fast Execution)', + security_level_high: 'High Security Level (Complete Isolation)', + variable_management: 'Variable Management', + loop_settings: 'Loop Settings', + loop_variables: 'Loop Variables', + add_variable: 'Add Variable', + input_variable_name: 'Input variable name', + select_type: 'Select type', + type_string: 'String', + type_number: 'Number', + type_boolean: 'Boolean', + type_object: 'Object', + type_list: 'Array', + type_reference: 'Reference Variable', + select_reference_variable: 'Select variable to reference', + input_string_value: 'Enter string value', + input_number_value: 'Enter number value', + select_boolean_value: 'Select boolean value', + input_json_object: 'Enter JSON formatted object', + delete_variable: 'Delete variable', + loop_stop_condition: 'Loop Stop Condition', + add_stop_condition: 'Add Stop Condition', + max_iterations: 'Maximum Iterations', + duplicate_variable_name: 'Variable names cannot be duplicated', + loop_node_save_success: 'Loop node saved successfully', + no_input_variables: 'No input variables', + no_output_variables: 'No output variables', + input_variables_tip: 'Select variables as code input parameters', + output_variables_tip: 'These variables will be created after code execution', + input_variables: 'Input Variables', + output_variables: 'Output Variables', + variable_placeholder: 'Enter variable name in node', + loop_variable_scope: 'Loop Variables', + output_variables_tip_code: 'Output variables should be defined in code return value', + execution_config: 'Execution Configuration', + timeout_seconds: 'Timeout', + memory_limit: 'Memory Limit', + cpu_limit: 'CPU Limit', + seconds: 'seconds', + mb: 'MB', + cores: 'cores', + code_editor: 'Code Editor', + at_least_one_operation: 'At least one variable operation must be retained', + operation_select_variable: 'Operation {index}: Please select variable', + operation_select_type: 'Operation {index}: Please select operation type', + operation_input_value: 'Operation {index}: Please input operation value', + variable_operations: 'Variable Operations', + add_operation: 'Add Operation', + variable_assign_config: 'Variable Assignment Node Configuration', + }, + answer: 'Answer', + file_variables: 'File Variables', + single_file: 'Single File', + multiple_files: 'Multiple Files', + remove_file_variable: 'Remove File Variable', + file_variable_exists: 'File variable {name} already exists, no need to add again', + variable_format: 'Variable: {{name}}', + env_config: 'Environment Variable Configuration', + env_description_title: 'Environment Variable Description', + env_description_content: 'Environment variables are bound to the current workflow and can only be read during workflow execution, not modified. Suitable for storing configuration information, keys, constants, etc.', + env_list: 'Environment Variable List', + no_env_variables: 'No environment variables', + create_first_env_variable: 'Click "Add Variable" button to create your first environment variable', + edit_env_variable: 'Edit Environment Variable', + add_env_variable: 'Add Environment Variable', + variable_name: 'Variable Name', + variable_name_placeholder: 'Enter variable name (letters, numbers, underscores supported)', + variable_type: 'Variable Type', + select_variable_type: 'Select variable type', + type_string_desc: 'String (string)', + type_number_desc: 'Number (number)', + type_boolean_desc: 'Boolean (boolean)', + type_object_desc: 'JSON Object (object)', + variable_value: 'Variable Value', + input_string_placeholder: 'Enter string value', + input_number_placeholder: 'Enter number value', + select_boolean_placeholder: 'Select boolean value', + input_json_placeholder: 'Enter JSON formatted object', + description: 'Description', + variable_description_placeholder: 'Enter variable description (optional)', + variable_name_required: 'Please enter variable name', + variable_name_pattern: 'Variable name can only contain letters, numbers and underscores, and cannot start with a number', + variable_type_required: 'Please select variable type', + variable_value_required: 'Please enter variable value', + not_set: 'Not set', + hidden: 'Hidden', + load_env_variables_failed: 'Failed to load environment variables', + json_format_error: 'JSON format is incorrect, please check input', + env_variable_update_success: 'Environment variable updated successfully', + env_variable_create_success: 'Environment variable created successfully', + env_variable_update_failed: 'Failed to update environment variable', + env_variable_create_failed: 'Failed to create environment variable', + confirm_delete_env_variable: 'Are you sure you want to delete environment variable "{name}"? This operation cannot be undone.', + confirm_delete_title: 'Confirm Delete', + env_variable_delete_success: 'Environment variable deleted successfully', + env_variable_delete_failed: 'Failed to delete environment variable', + language: 'Language', + insert_template: 'Insert Template', + format_code: 'Format', + fullscreen: 'Fullscreen', + exit_fullscreen: 'Exit Fullscreen', + navigate_to: 'Navigate to...', + template_inserted: 'Template inserted', + code_formatted: 'Code formatted', + code_copied: 'Code copied to clipboard', + copy_failed: 'Copy failed, please copy manually', + fullscreen_exit_tip: 'Press ESC or F11 to exit fullscreen mode', + code_main_function: 'Code execution main function', + input_variables_dict: 'Input variables dictionary', + output_variables_dict: 'Output variables dictionary', + get_input_variables: 'Get input variables', + example_get_variable: 'Example: value = kwargs.get(\'input_variable_name\', default_value)', + write_python_code_here: 'Write your Python code here', + define_output_variables: 'Define output variables', + input_variables_object: 'Input variables object', + output_variables_object: 'Output variables object', + example_js_get_variable: 'Example: const value = variables.input_variable_name || default_value;', + write_js_code_here: 'Write your JavaScript code here', + shell_input_env_vars: 'Input variables passed via environment variables: INPUT_VARIABLE_NAME', + shell_output_json: 'Output variables printed to stdout in JSON format', + example_shell_get_variable: 'Example: local value="${INPUT_VARIABLE_NAME:-default_value}"', + write_bash_code_here: 'Write your Bash code here', + output_json_result: 'Output JSON format result', + call_main_function: 'Call main function', }, 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', + del_branch: 'Remove Branch', + add_condition: 'Add Condition', + del_condition: 'Remove Condition', + 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', + // Complete operator mappings (with type prefix) + string_equal: 'Equal', + string_not_equal: 'Not equal', + string_contains: 'Contains', + string_not_contains: 'Does not contain', + string_starts_with: 'Starts with', + string_ends_with: 'Ends with', + string_length_equal: 'Length equal', + string_length_greater_than: 'Length greater than', + string_length_greater_than_or_equal: 'Length greater than or equal', + string_length_less_than: 'Length less than', + string_length_less_than_or_equal: 'Length less than or equal', + string_regex_match: 'Regex match', + number_equal: 'Equal', + number_not_equal: 'Not equal', + number_greater_than: 'Greater than', + number_greater_than_or_equal: 'Greater than or equal', + number_less_than: 'Less than', + number_less_than_or_equal: 'Less than or equal', + bool_equal: 'Equal', + bool_not_equal: 'Not equal', + dict_equal: 'Equal', + dict_not_equal: 'Not equal', + dict_contains_key: 'Contains key', + dict_not_contains_key: 'Does not contain key', + // Variable operation type mappings + overwrite: 'Overwrite', + clear: 'Clear', + add: 'Add', + subtract: 'Subtract', + multiply: 'Multiply', + divide: 'Divide', + modulo: 'Modulo', + power: 'Power', + sqrt: 'Square root', + append: 'Append', + extend: 'Extend', + pop_first: 'Remove first', + pop_last: 'Remove last', + }, + nodeVariableConfig: { + variable_config: 'Variable Configuration', + variable_syntax_tooltip: 'Use variable syntax like {{system.query}} to reference variables', + custom_fields: 'Custom Fields', + field_name_placeholder: 'Field Name', + field_value_placeholder: 'Field Value (supports variable references)', + add_custom_field: 'Add Custom Field', + // LLM node configuration + llm: { + system_prompt_label: 'System Prompt', + system_prompt_placeholder: 'Enter system prompt, variables like {{system.query}} can be used', + system_prompt_tooltip: 'Define AI assistant role and behavior, supports variable references', + user_prompt_label: 'User Prompt', + user_prompt_placeholder: 'Enter user prompt template', + user_prompt_tooltip: 'User input template, usually contains user query variables', + api_key_label: 'API Key', + api_key_placeholder: 'Select API key variable', + api_key_tooltip: 'API key for calling LLM service', + temperature_label: 'Temperature', + temperature_placeholder: '0.7', + temperature_tooltip: 'Control randomness of generated text, range 0-1', + max_tokens_label: 'Max Tokens', + max_tokens_placeholder: '2048', + max_tokens_tooltip: 'Limit maximum length of generated content' + }, + // Condition node configuration + condition: { + condition_expression_label: 'Condition Expression', + condition_expression_placeholder: 'e.g.: {{score}} > 0.8', + condition_expression_tooltip: 'Supports JavaScript expressions, variables can be used for conditional judgment', + true_branch_label: 'When True', + true_branch_placeholder: 'Next node ID', + true_branch_tooltip: 'Node to jump to when condition is true', + false_branch_label: 'When False', + false_branch_placeholder: 'Next node ID', + false_branch_tooltip: 'Node to jump to when condition is false' + }, + // Variable assignment node configuration + variable_assignment: { + target_variable_label: 'Target Variable', + target_variable_placeholder: 'Variable Name', + target_variable_tooltip: 'Name of the variable to assign', + source_expression_label: 'Assignment Expression', + source_expression_placeholder: '{{system.query}} + " processed"', + source_expression_tooltip: 'Supports variable references and simple expressions', + variable_type_label: 'Variable Type', + variable_type_tooltip: 'Data type of the new variable', + type_string: 'String', + type_number: 'Number', + type_boolean: 'Boolean', + type_object: 'Object' + }, + // HTTP request node configuration + http_request: { + url_label: 'Request URL', + url_placeholder: 'https://api.example.com/data', + url_tooltip: 'Target address for HTTP request', + method_label: 'Request Method', + method_tooltip: 'HTTP request method', + headers_label: 'Request Headers', + headers_placeholder: '{"Content-Type": "application/json"}', + headers_tooltip: 'HTTP request headers, supports variable references', + body_label: 'Request Body', + body_placeholder: '{"query": "{{system.query}}"}', + body_tooltip: 'Request body content, supports variable references' + } + }, + startNodeEditor: { + title: 'Edit Start Node', + basic_config: 'Basic Configuration', + variable_management: 'Variable Management', + node_description: 'Node Description', + node_description_placeholder: 'Enter node description', + input_parameters: 'Input Parameters', + output_parameters: 'Output Parameters', + parameter_value_placeholder: 'Enter parameter value', + add_input_parameter: 'Add Input Parameter', + add_output_parameter: 'Add Output Parameter', + delete: 'Delete', + save: 'Save', + cancel: 'Cancel', + loading: 'Initializing...', + no_conversation: 'Conversation ID is required to manage variables', + // Messages + messages: { + init_form_failed: 'Failed to initialize form data', + load_variables_failed: 'Failed to load conversation variables', + editor_not_initialized: 'Editor not fully initialized, please try again later', + save_success: 'Saved successfully', + save_failed: 'Save failed, please check your input', + variables_updated: 'Variables updated', + update_variables_failed: 'Failed to update variables', + init_editor_failed: 'Failed to initialize editor', + update_form_failed: 'Failed to update form data', + component_init_failed: 'Component initialization failed' + } + }, + startNodeVariableManager: { + // Tabs + system_variables: 'System Variables', + conversation_variables: 'Conversation Variables', + input_fields: 'Input Fields', + input_fields_hint: 'Configured inputs can be used in the workflow', + no_variables: 'No Variables', + // Dialog titles + edit_variable: 'Edit Variable', + add_variable_dialog: 'Add Variable', + // Form labels + variable_name_label: 'Variable Name', + variable_type_label: 'Variable Type', + variable_value_label: 'Variable Value', + description_label: 'Description', + supported_file_types_label: 'Supported File Types', + // Variable type groups + basic_types: 'Basic Types', + array_types: 'Array Types', + // Basic type options + string_type: 'String', + number_type: 'Number', + boolean_type: 'Boolean', + object_type: 'Object', + secret_type: 'Secret', + file_type: 'File', + // Array type options + array_any: 'Array[Any]', + array_string: 'Array[String]', + array_number: 'Array[Number]', + array_object: 'Array[Object]', + array_file: 'Array[File]', + array_boolean: 'Array[Boolean]', + array_secret: 'Array[Secret]', + // File type categories + document_category: 'Document', + image_category: 'Image', + audio_category: 'Audio', + video_category: 'Video', + other_file_types: 'Other File Types', + // Upload related + upload_file_types: 'Upload File Types', + local_upload: 'Local Upload', + url_upload: 'URL Upload', + file_upload_limits: 'File Upload Limits', + max_files: 'Max Files:', + max_file_size: 'Max File Size:', + required_file: 'Required File:', + file_type_fixed: 'File type is fixed to 1 file', + // Placeholder texts + enter_variable_name: 'Enter variable name', + select_variable_type: 'Select variable type', + enter_string_value: 'Enter string value', + enter_number_value: 'Enter number value', + select_boolean_value: 'Select boolean value', + enter_secret_value: 'Enter secret value', + enter_json_object: 'Enter JSON format object value', + enter_variable_description: 'Enter variable description (optional)', + file_extension_placeholder: '+ + file extension, e.g. .doc', + // Tip messages + file_type_tip: 'File type variables will be uploaded by users during conversation', + string_array_tip: 'String array variables will be input by users during conversation, default is empty array', + number_array_tip: 'Number array variables will be input by users during conversation, default is empty array', + boolean_array_tip: 'Boolean array variables will be selected by users during conversation, default is empty array', + file_array_tip: 'File list type variables will be uploaded by users during conversation, default is empty array', + object_array_tip: 'Object array variables will be input by users in JSON format during conversation, default is empty array', + secret_array_tip: 'Secret array variables will be input by users during conversation, default is empty array', + any_array_tip: 'Any type array variables will be input by users during conversation, default is empty array', + required_file_note: 'When checked, users must upload files during conversation', + // Button texts + cancel: 'Cancel', + delete: 'Delete', + save: 'Save', + // Unit text + mb_unit: 'MB', + // Validation rules + enter_variable_name_validation: 'Please enter variable name', + variable_name_pattern_validation: 'Variable name can only contain letters, numbers and underscores, and must start with a letter or underscore', + select_variable_type_validation: 'Please select variable type', + // Error messages + missing_variable_data: 'Missing variable data', + missing_flow_id: 'Missing workflow ID (flowId), unable to save conversation variable', + variable_name_empty: 'Variable name cannot be empty', + select_variable_type_required: 'Please select variable type', + enter_valid_number: 'Please enter a valid number', + json_format_incorrect: 'JSON format is incorrect, please check object value syntax', + variable_update_success: 'Variable updated successfully', + variable_create_success: 'Variable created successfully', + variable_delete_success: 'Variable deleted successfully', + save_variable_failed: 'Failed to save variable', + delete_variable_failed: 'Failed to delete variable', + load_variables_failed: 'Failed to load variables', + description_save_success: 'Description saved successfully', + description_save_failed: 'Failed to save description', + save_success: 'Saved successfully', + // Other labels + not_set: '(Not set)', + // System variables related + system_readonly: 'System Variables (Read-only)', + system_description: 'These variables are automatically provided by the system and contain context information for the current workflow execution', + available_system_variables: 'Available System Variables', + // Conversation variables related + conversation_description: 'These variables are valid in the current conversation and can be used in various steps of the workflow', + add_variable: 'Add Variable', + // Table column headers + variable_name: 'Variable Name', + type: 'Type', + description: 'Description', + value: 'Value', + reference_syntax: 'Reference Syntax', + operations: 'Operations', + // System variable descriptions + system_var_descriptions: { + query: 'Current user query content', + files: 'List of files uploaded by user', + dialogue_count: 'Current conversation round count', + app_id: 'Current application ID', + flow_id: 'Current workflow ID', + user_id: 'Current user ID', + session_id: 'Current session ID', + timestamp: 'Current timestamp', + default: 'System variable' + }, + // Variable types + variable_types: { + string: 'String', + number: 'Number', + boolean: 'Boolean', + object: 'Object', + secret: 'Secret', + group: 'Group', + file: 'File', + 'array[any]': 'Any Array', + 'array[string]': 'String Array', + 'array[number]': 'Number Array', + 'array[object]': 'Object Array', + 'array[file]': 'File Array', + 'array[boolean]': 'Boolean Array', + 'array[secret]': 'Secret Array' + }, + // Empty state + no_conversation_variables: 'No conversation variables', + empty_hint: 'Click the "Add Variable" button above to create your first conversation variable', + // Dialog + add_conversation_variable: 'Add Conversation Variable', + // Placeholders + placeholders: { + variable_name: 'Enter variable name (e.g., user_name)', + select_variable_type: 'Select variable type', + variable_value: 'Enter variable value', + boolean_value: 'Select boolean value', + json_object: 'Enter JSON format object', + string_array: 'Enter comma-separated strings, e.g., item1,item2,item3', + json_array: 'Enter JSON array format, e.g., [1,2,3] or [{},{}]', + file_default: 'Optional: Enter default value or description', + description: 'Enter variable description (optional)' + }, + // Confirm delete + confirm_delete: 'Are you sure you want to delete this variable?', + // Validation rules + validation: { + name_required: 'Please enter variable name', + name_pattern: 'Variable name can only contain letters, numbers and underscores, and cannot start with a number', + type_required: 'Please select variable type', + value_required: 'Please enter variable value' + }, + // Messages + messages: { + system_variables_loaded: 'System variables loaded successfully', + load_system_variables_failed: 'Failed to load system variables', + load_conversation_variables_failed: 'Failed to load conversation variables', + load_variable_types_failed: 'Failed to load variable types', + invalid_number: 'Please enter a valid number', + invalid_json: 'Please enter valid JSON format', + invalid_number_array: 'Please enter a valid number array, e.g., [1,2,3]', + invalid_boolean_array: 'Please enter a valid boolean array, e.g., [true,false]', + invalid_object_array: 'Please enter a valid object array', + variable_created: 'Variable created successfully', + create_variable_failed: 'Failed to create variable', + variable_deleted: 'Variable deleted successfully', + delete_variable_failed: 'Failed to delete variable' + } + }, + variableChooser: { + // Labels + variable_name_label: 'Variable Name:', + select_variable_label: 'Select Variable:', + source_variable_label: 'Source Variable:', + type_label: 'Type:', + scope_label: 'Scope:', + description_label: 'Description:', + // Buttons and actions + clear_variable_selection: 'Clear variable selection', + // Placeholders + enter_variable_name: 'Enter variable name', + select_variable: 'Select variable', + // Variable types + variable_types: { + string: 'String', + number: 'Number', + boolean: 'Boolean', + object: 'Object', + secret: 'Secret', + file: 'File', + 'array[any]': 'Array', + 'array[string]': 'String Array', + 'array[number]': 'Number Array', + 'array[object]': 'Object Array', + 'array[file]': 'File Array', + 'array[boolean]': 'Boolean Array', + 'array[secret]': 'Secret Array' + }, + // Scopes + scopes: { + system: 'System Variables', + user: 'User Variables', + env: 'Environment Variables', + conversation: 'Conversation Variables' + }, + // Description templates + current_node_variable: 'Current node variable: {name}', + conversation_variable: 'Conversation variable: {name}', + scope_variable: '{scope} variable: {name}', + // Error messages + find_variable_failed: 'Failed to find variable:', + variable_detail_query_failed: 'Variable detail query failed, using default info:' + }, + variableInsertDropdown: { + // Titles and labels + select_variable_to_insert: 'Select variable to insert', + search_variables_placeholder: 'Search variables...', + searching: 'Searching...', + // Empty states + no_variables_found: 'No matching variables found', + no_available_variables: 'No available variables', + ensure_variables_defined: 'Please ensure variables are defined in relevant scopes' + }, + variableSelector: { + // Placeholders + select_variable: 'Select variable', + search_variables_placeholder: 'Search variables...', + // Scope labels + scopes: { + self: 'Current Node Variables', + system: 'System Variables', + user: 'User Variables', + env: 'Environment Variables', + conversation: 'Conversation Variables' + }, + // Variable types + variable_types: { + string: 'String', + number: 'Number', + boolean: 'Boolean', + object: 'Object', + secret: 'Secret', + file: 'File', + 'array[any]': 'Array', + 'array[string]': 'String Array', + 'array[number]': 'Number Array', + 'array[object]': 'Object Array', + 'array[file]': 'File Array', + 'array[boolean]': 'Boolean Array', + 'array[secret]': 'Secret Array' + }, + // Template text + node_output: 'Node {nodeName} Output', + // Empty states + no_available_variables: 'No available variables', + define_variables_hint: 'You can define conversation variables in the start node', + // Console messages + loading_variables: 'VariableSelector loading variables, current parameters:', + conversation_variables_query: 'Conversation variables query with current_step_id:', + variables_loaded_result: 'Variables loading result:', + variables_load_failed: 'Variables loading failed:', + variables_loading_complete: 'VariableSelector variables loading complete:', + total_count: 'Total', + system_variables_count: 'System Variables', + user_variables_count: 'User Variables', + env_variables_count: 'Environment Variables', + conversation_variables_count: 'Conversation Variables', + predecessor_variables_count: 'Predecessor Variables', + variables_load_error: 'Variables loading failed:', + api_path_check: 'Suggestion: Check if API path is correct and backend service is running', + flow_id_check: 'Suggestion: Check if flowId parameter is passed correctly', + load_variable_types_failed: 'Failed to load variable types:' + }, + contextMenu: { + add_node: 'Add Node', + add_comment: 'Add Comment', + debug_workflow: 'Debug', + export_yaml: 'Export YAML', + import_yaml: 'Import YAML', + }, + choiceBranch: { + // Basic information + basic_info: 'Basic Information', + node_name: 'Node Name', + node_description: 'Node Description', + branch_config: 'Branch Configuration', + // Branch types + branch_types: { + if: 'IF', + elif: 'ELIF', + else: 'ELSE' + }, + // Data types + data_types: { + string: 'String', + number: 'Number', + list: 'Array', + bool: 'Boolean', + dict: 'Object', + reference: 'Variable Reference' + }, + // Operators + operators: { + // String operators + string_equal: 'Equals', + string_not_equal: 'Not Equals', + string_contains: 'Contains', + string_not_contains: 'Not Contains', + string_starts_with: 'Starts With', + string_ends_with: 'Ends With', + string_length_equal: 'Length Equals', + string_length_greater_than: 'Length Greater Than', + string_length_greater_than_or_equal: 'Length Greater Than or Equal', + string_length_less_than: 'Length Less Than', + string_length_less_than_or_equal: 'Length Less Than or Equal', + string_regex_match: 'Regex Match', + // Number operators + number_equal: 'Equals', + number_not_equal: 'Not Equals', + number_greater_than: 'Greater Than', + number_less_than: 'Less Than', + number_greater_than_or_equal: 'Greater Than or Equal', + number_less_than_or_equal: 'Less Than or Equal', + // List operators + list_equal: 'Equals', + list_not_equal: 'Not Equals', + list_contains: 'Contains', + list_not_contains: 'Not Contains', + list_length_equal: 'Length Equals', + list_length_greater_than: 'Length Greater Than', + list_length_greater_than_or_equal: 'Length Greater Than or Equal', + list_length_less_than: 'Length Less Than', + list_length_less_than_or_equal: 'Length Less Than or Equal', + // Boolean operators + bool_equal: 'Equals', + bool_not_equal: 'Not Equals', + // Dictionary operators + dict_equal: 'Equals', + dict_not_equal: 'Not Equals', + dict_contains_key: 'Contains Key', + dict_not_contains_key: 'Not Contains Key', + // Generic operators (simplified) + eq: 'Equals', + ne: 'Not Equals', + gt: 'Greater Than', + gte: 'Greater Than or Equal', + lt: 'Less Than', + lte: 'Less Than or Equal', + contains: 'Contains', + not_contains: 'Not Contains', + starts_with: 'Starts With', + ends_with: 'Ends With', + is_empty: 'Is Empty', + is_not_empty: 'Is Not Empty', + length_eq: 'Length Equals', + length_gt: 'Length Greater Than', + length_lt: 'Length Less Than', + has_key: 'Has Key', + not_has_key: 'Not Has Key' + }, + // Logic operators + logic_operators: { + and: 'AND', + or: 'OR', + and_text: 'AND', + or_text: 'OR' + }, + // Placeholders + placeholders: { + enter_node_name: 'Enter node name', + enter_node_description: 'Enter node description', + select_left_variable: 'Select left variable', + select_right_variable: 'Select right variable', + data_type: 'Type', + operator: 'Operator', + select_boolean: 'Select boolean', + enter_value: 'Enter {type}', + enter_generic_value: 'value' + }, + // Button text + buttons: { + variable_reference: 'Variable Reference', + input_value: 'Input Value', + add_condition: 'Add Condition', + delete_branch: 'Delete Branch', + add_elif: 'ELIF', + cancel: 'Cancel', + save: 'Save' + }, + // Titles and labels + titles: { + config_condition_branch: 'Configure Condition Branch', + case_number: 'CASE {number}', + condition_branch: 'Condition Branch', + delete_condition: 'Delete Condition', + selected_branch_id: 'Selected Branch ID' + }, + // Tips + tips: { + else_description: 'Used to define the branch executed when IF/ELIF conditions are not met', + click_edit_add_branch: 'Click edit to add branch conditions', + other_cases: 'All other cases' + }, + // Validation and error messages + validation: { + enter_node_name: 'Please enter node name', + need_at_least_one_branch: 'At least one branch is required', + branch_missing_left_variable: '{branchName} branch has unfilled left variable', + branch_missing_right_value: '{branchName} branch has unfilled right value', + branch_missing_operator: '{branchName} branch has unselected operator', + need_valid_branch: 'At least one valid branch is required', + condition_cannot_delete: 'Condition cannot be deleted', + each_branch_need_condition: 'Each branch needs at least one condition', + keep_at_least_one_branch: 'At least one conditional branch must be kept', + confirm_delete_branch: 'Are you sure you want to delete this branch?', + confirm_delete_title: 'Confirm Delete', + data_error_multiple_default: 'Data error: Cannot have multiple default branches' + }, + // Status display + status: { + condition_not_set: 'Condition not set', + no_condition: 'Condition not set', + branch_prefix: 'Branch' + }, + // Console messages + console: { + init_form_data: 'Initialize form data - original choices:', + branch_separation_result: 'Branch separation result:', + non_default_branch: 'Non-default branch', + found_multiple_default: 'Found multiple default branches, keeping only the first:', + has_non_default_branch: 'Has non-default branches, updating names', + no_non_default_branch: 'No non-default branches, creating empty IF branch', + no_default_branch: 'No default branch, creating ELSE branch', + has_default_branch: 'Has default branch:', + final_form_data: 'Final formData:', + save_final_data: 'Save final data:', + operator_mapping_not_found: 'Operator mapping not found:' + } } -}; +}; \ No newline at end of file diff --git a/src/i18n/lang/zh-cn.ts b/src/i18n/lang/zh-cn.ts index 2af288735256caff488a7fe4fdd71158be682131..2e82ec05fa4c65937704a718ea3cd3ad8b5c1781 100644 --- a/src/i18n/lang/zh-cn.ts +++ b/src/i18n/lang/zh-cn.ts @@ -14,6 +14,10 @@ export default { icon: '图标', tip: '提示', delete_success: '删除成功', + clear_search: '清空搜索', + characters: '字符', + setting: '设置', + reply: '回复', }, settings: { model: '模型', @@ -33,6 +37,11 @@ export default { model_name: '请输入模型', max_token: '请输入最大 Token 数', }, + model_preferences_settings: '模型偏好设置', + reasoning_model_preference: '推理模型偏好', + embedding_model_preference: 'Embedding模型偏好', + reranker_preference: 'Reranker偏好', + chain_of_thought_preference: '思维链偏好', }, home: { name: 'openEuler 智能化解决方案', @@ -90,6 +99,13 @@ export default { publish: '发布', copyFailed: '复制失败', checkFormat: '请检查格式', + check_connect: '请检查连接线', + component_name: '组件名称', + component_introduction: '组件描述', + selectedModel: '选择模型', + key: '键', + value: '值', + rag_query_variables: '问题' }, plugin_center: { plugin_name: '插件名称', @@ -105,6 +121,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 +249,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 +283,7 @@ export default { no_chat_history: '暂无历史对话', expand: '展开', myApp: '我的应用', + auto_execute: '自动执行', }, feedback: { noCopyMessage: '无可复制的信息', @@ -372,6 +399,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 只展示一次,请复制后妥善保存', @@ -388,9 +418,86 @@ export default { find_witChainD: '请输入知识库名称/ID', fiveKnowledge: '最多只能选择5个知识库', }, + // API Call Drawer specific translations + apiCall: { + basic_info: '基本信息', + request_headers: '请求头', + query_params: '查询参数', + request_body: '请求体', + output_variables: '输出变量', + node_name_placeholder: '请输入节点名称', + node_description_placeholder: '请输入节点描述', + api_url_placeholder: '请输入API地址', + key_placeholder: '请输入键', + value_placeholder: '请输入值', + json_body_placeholder: '请输入JSON格式的请求体', + url_validation_message: 'URL必须以 http:// 或 https:// 开头', + add_header: '添加 Header', + add_query_param: '添加查询参数', + add_form_data: '添加表单数据', + add_multipart_data: '添加数据', + }, + // LLM Node Drawer specific translations + llmNode: { + basic_info: '基本信息', + prompt_config: '提示词配置', + parameter_config: '参数配置', + output_variables: '输出变量', + node_name_placeholder: '请输入节点名称', + node_description_placeholder: '请输入节点描述', + model_placeholder: '请选择模型', + system_prompt_label: '系统提示词', + system_prompt_placeholder: '请输入系统提示词', + user_prompt_label: '用户提示词', + user_prompt_placeholder: '请输入用户提示词', + question_label: '问题', + question_placeholder: '请输入问题', + temperature_label: '温度', + context_label: '上下文', + frequency_penalty_label: '频率惩罚', + }, + // MCP Node Drawer specific translations + mcpNode: { + basic_info: '基本信息', + output_variables: '输出变量', + node_name_placeholder: '请输入节点名称', + node_description_placeholder: '请输入节点描述', + mcp_tools_label: 'MCP工具', + mcp_tools_placeholder: '请选择MCP工具', + max_steps_label: '最大步骤数', + query_mcp_service_failed: '查询MCP服务失败', + }, + // RAG Node Drawer specific translations + ragNode: { + basic_info: '基本信息', + knowledge_config: '知识库配置', + advanced_options: '高级选项', + output_variables: '输出变量', + node_name_placeholder: '请输入节点名称', + node_description_placeholder: '请输入节点描述', + query_placeholder: '请输入要查询知识库的问题', + knowledge_base_label: '知识库', + knowledge_base_placeholder: '请选择知识库', + search_method_label: '检索方法', + search_method_placeholder: '请选择检索方法', + is_rerank_label: '是否重排', + is_compress_label: '是否压缩', + is_classify_by_doc_label: '是否按文档分类', + is_related_surrounding_label: '是否关联上下文', + keyword_search: '关键词检索', + vector_search: '向量化检索', + hybrid_search: '混合检索', + doc2chunk_search: '文档分块检索', + doc2chunk_bfs_search: '文档分块检索(bfs)', + enhanced_by_llm_search: '大模型增强检索', + query_knowledge_base_failed: '查询知识库失败', + no_knowledge_base_available: '暂无可用知识库', + }, flow: { flow_start: '工作流进行中', flow_end: '工作流结束', + flow_cancel: '取消运行', + flow_risk: '风险提示', flow_params_error: '缺少参数', flow_pause: '工作流暂停', edit_flow: '编辑工作流', @@ -404,6 +511,7 @@ export default { choose_flow: '请选择工作流', debug: '调试', enterWorkflowName: '请输入工作流名称', + enterWorkflowDesc: '请输入工作流名称', default: '', success: '运行成功', error: '运行失败', @@ -414,36 +522,842 @@ 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: '补充说明', + nodes: '节点', + plugins: '插件', + node_names: { + start: '开始', + end: '结束', + loop: '循环', + choice: '选择', + variable_assign: '变量赋值', + file_extractor: '文件提取器', + skip_round: '跳过本轮', + exit_loop: '退出循环', + loop_subflow: '循环子工作流', + loop_condition: '循环终止条件', + }, + // 文件提取器配置 + file_extractor: { + title: '配置文件提取器', + basic_info: '基本信息', + node_name: '节点名称', + node_name_placeholder: '请输入节点名称', + node_description: '节点描述', + node_description_placeholder: '请输入节点描述', + input_variables: '输入变量', + parse_method: '解析方法', + parse_method_placeholder: '请选择文件解析方法', + parse_method_description: '从文件中提取文本内容,支持多种文件格式的解析', + input_file: '输入文件', + supported_file_types: '支持的文件类型:{types}', + select_file_variable: '选择文件变量', + select_file_or_array_variable: '选择文件或文件数组变量', + output_variables: '输出变量', + variable_name: '变量名', + type: '类型', + description: '描述', + text_output_description: '提取的文本内容', + error_output_description: '错误信息(如果有的话)', + string_type: '字符串', + cancel: '取消', + save: '保存', + // 验证消息 + node_name_required: '请填写节点名称', + parse_method_required: '请选择文件解析方法', + input_file_required: '请选择输入文件变量', + // 错误消息 + load_parse_methods_failed: '获取解析方法列表失败', + }, + node_groups: { + tool: '工具', + transform: '转换', + logic: '逻辑', + default: '默认', + }, + node_config: { + basic_info: '基本信息', + node_name: '节点名称', + node_name_placeholder: '请输入节点名称', + node_name_required: '请输入节点名称', + node_description: '节点描述', + node_description_placeholder: '请输入节点描述', + security_level: '安全等级', + security_level_placeholder: '请选择安全等级', + security_level_low: '低安全级别(快速执行)', + security_level_high: '高安全级别(完全隔离)', + variable_management: '变量管理', + loop_settings: '循环设置', + loop_variables: '循环变量', + add_variable: '添加变量', + input_variable_name: '输入变量名', + select_type: '选择类型', + type_string: '字符串', + type_number: '数字', + type_boolean: '布尔值', + type_object: '对象', + type_list: '数组', + type_reference: '引用变量', + select_reference_variable: '选择要引用的变量', + input_string_value: '输入字符串值', + input_number_value: '输入数字值', + select_boolean_value: '选择布尔值', + input_json_object: '输入JSON格式的对象', + delete_variable: '删除变量', + loop_stop_condition: '循环终止条件', + add_stop_condition: '添加终止条件', + max_iterations: '最大循环次数', + duplicate_variable_name: '变量名不能重复', + loop_node_save_success: '循环节点保存成功', + no_input_variables: '暂无输入变量', + no_output_variables: '暂无输出变量', + input_variables_tip: '选择变量作为代码输入参数', + output_variables_tip: '代码执行后将创建这些变量', + input_variables: '输入变量', + output_variables: '输出变量', + variable_placeholder: '输入节点内的变量名', + loop_variable_scope: '循环变量', + output_variables_tip_code: '输出变量应在代码的返回值中定义', + execution_config: '执行配置', + timeout_seconds: '超时时间', + memory_limit: '内存限制', + cpu_limit: 'CPU限制', + seconds: '秒', + mb: 'MB', + cores: '核心', + code_editor: '代码编辑', + at_least_one_operation: '至少需要保留一个变量操作', + operation_select_variable: '第 {index} 个操作:请选择变量', + operation_select_type: '第 {index} 个操作:请选择操作类型', + operation_input_value: '第 {index} 个操作:请输入操作值', + variable_operations: '变量操作', + add_operation: '添加操作', + variable_assign_config: '变量赋值节点配置', + }, + answer: '回答', + file_variables: '附件变量', + single_file: '单文件', + multiple_files: '多文件', + remove_file_variable: '移除附件变量', + file_variable_exists: '文件变量 {name} 已存在,无需重复添加', + variable_format: '变量: {{name}}', + env_config: '环境变量配置', + env_description_title: '环境变量说明', + env_description_content: '环境变量与当前工作流绑定,在流程运行期间只能读取,不能修改。适用于存储配置信息、密钥、常量等。', + env_list: '环境变量列表', + no_env_variables: '暂无环境变量', + create_first_env_variable: '点击"添加变量"按钮创建你的第一个环境变量', + edit_env_variable: '编辑环境变量', + add_env_variable: '添加环境变量', + variable_name: '变量名', + variable_name_placeholder: '请输入变量名(支持字母、数字、下划线)', + variable_type: '变量类型', + select_variable_type: '请选择变量类型', + type_string_desc: '字符串 (string)', + type_number_desc: '数字 (number)', + type_boolean_desc: '布尔值 (boolean)', + type_object_desc: 'JSON对象 (object)', + variable_value: '变量值', + input_string_placeholder: '请输入字符串值', + input_number_placeholder: '请输入数字值', + select_boolean_placeholder: '请选择布尔值', + input_json_placeholder: '请输入JSON格式的对象', + description: '描述', + variable_description_placeholder: '请输入变量描述(可选)', + variable_name_required: '请输入变量名', + variable_name_pattern: '变量名只能包含字母、数字和下划线,且不能以数字开头', + variable_type_required: '请选择变量类型', + variable_value_required: '请输入变量值', + not_set: '未设置', + hidden: '隐藏', + load_env_variables_failed: '加载环境变量失败', + json_format_error: 'JSON格式不正确,请检查输入', + env_variable_update_success: '环境变量更新成功', + env_variable_create_success: '环境变量创建成功', + env_variable_update_failed: '更新环境变量失败', + env_variable_create_failed: '创建环境变量失败', + confirm_delete_env_variable: '确定要删除环境变量"{name}"吗?此操作不可恢复。', + confirm_delete_title: '确认删除', + env_variable_delete_success: '环境变量删除成功', + env_variable_delete_failed: '删除环境变量失败', + language: '语言', + insert_template: '插入模板', + format_code: '格式化', + fullscreen: '全屏', + exit_fullscreen: '退出全屏', + navigate_to: '跳转到...', + template_inserted: '模板已插入', + code_formatted: '代码已格式化', + code_copied: '代码已复制到剪贴板', + copy_failed: '复制失败,请手动复制', + fullscreen_exit_tip: '按 ESC 或 F11 键退出全屏模式', + code_main_function: '代码执行主函数', + input_variables_dict: '输入变量字典', + output_variables_dict: '输出变量字典', + get_input_variables: '获取输入变量', + example_get_variable: '示例:value = kwargs.get(\'input_variable_name\', default_value)', + write_python_code_here: '在这里编写您的 Python 代码', + define_output_variables: '定义输出变量', + input_variables_object: '输入变量对象', + output_variables_object: '输出变量对象', + example_js_get_variable: '示例:const value = variables.input_variable_name || default_value;', + write_js_code_here: '在这里编写您的 JavaScript 代码', + shell_input_env_vars: '输入变量通过环境变量传递:INPUT_VARIABLE_NAME', + shell_output_json: '输出变量以JSON格式打印到stdout', + example_shell_get_variable: '示例:local value="${INPUT_VARIABLE_NAME:-default_value}"', + write_bash_code_here: '在这里编写您的 Bash 代码', + output_json_result: '输出JSON格式结果', + call_main_function: '调用主函数', }, 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: '添加条件分支', + del_branch: '移除分支', + add_condition: '添加条件', + del_condition: '移除条件', + 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: '不包含键', + // 完整的操作符映射(带类型前缀) + string_equal: '等于', + string_not_equal: '不等于', + string_contains: '包含', + string_not_contains: '不包含', + string_starts_with: '开始于', + string_ends_with: '结束于', + string_length_equal: '长度等于', + string_length_greater_than: '长度大于', + string_length_greater_than_or_equal: '长度大于等于', + string_length_less_than: '长度小于', + string_length_less_than_or_equal: '长度小于等于', + string_regex_match: '正则匹配', + number_equal: '等于', + number_not_equal: '不等于', + number_greater_than: '大于', + number_greater_than_or_equal: '大于等于', + number_less_than: '小于', + number_less_than_or_equal: '小于等于', + bool_equal: '等于', + bool_not_equal: '不等于', + dict_equal: '等于', + dict_not_equal: '不等于', + dict_contains_key: '包含键', + dict_not_contains_key: '不包含键', + // 变量操作类型映射 + overwrite: '覆盖', + clear: '清空', + add: '加法', + subtract: '减法', + multiply: '乘法', + divide: '除法', + modulo: '求余', + power: '乘幂', + sqrt: '开方', + append: '追加', + extend: '扩展', + pop_first: '移除首项', + pop_last: '移除尾项', + }, + nodeVariableConfig: { + variable_config: '变量配置', + variable_syntax_tooltip: '使用变量语法如 {{system.query}} 来引用变量', + custom_fields: '自定义字段', + field_name_placeholder: '字段名', + field_value_placeholder: '字段值(支持变量引用)', + add_custom_field: '添加自定义字段', + // LLM节点配置 + llm: { + system_prompt_label: '系统提示词', + system_prompt_placeholder: '请输入系统提示词,可使用变量如 {{system.query}}', + system_prompt_tooltip: '定义AI助手的角色和行为,支持使用变量引用', + user_prompt_label: '用户提示词', + user_prompt_placeholder: '请输入用户提示词模板', + user_prompt_tooltip: '用户输入的模板,通常包含用户查询变量', + api_key_label: 'API密钥', + api_key_placeholder: '选择API密钥变量', + api_key_tooltip: '用于调用LLM服务的API密钥', + temperature_label: '温度参数', + temperature_placeholder: '0.7', + temperature_tooltip: '控制生成文本的随机性,范围0-1', + max_tokens_label: '最大Token数', + max_tokens_placeholder: '2048', + max_tokens_tooltip: '限制生成内容的最大长度' + }, + // 条件节点配置 + condition: { + condition_expression_label: '条件表达式', + condition_expression_placeholder: '如: {{score}} > 0.8', + condition_expression_tooltip: '支持JavaScript表达式,可使用变量进行条件判断', + true_branch_label: '条件为真时', + true_branch_placeholder: '下一个节点ID', + true_branch_tooltip: '条件为真时跳转的节点', + false_branch_label: '条件为假时', + false_branch_placeholder: '下一个节点ID', + false_branch_tooltip: '条件为假时跳转的节点' + }, + // 变量赋值节点配置 + variable_assignment: { + target_variable_label: '目标变量', + target_variable_placeholder: '变量名', + target_variable_tooltip: '要赋值的变量名', + source_expression_label: '赋值表达式', + source_expression_placeholder: '{{system.query}} + " processed"', + source_expression_tooltip: '支持变量引用和简单表达式', + variable_type_label: '变量类型', + variable_type_tooltip: '新变量的数据类型', + type_string: '字符串', + type_number: '数字', + type_boolean: '布尔值', + type_object: '对象' + }, + // HTTP请求节点配置 + http_request: { + url_label: '请求URL', + url_placeholder: 'https://api.example.com/data', + url_tooltip: 'HTTP请求的目标地址', + method_label: '请求方法', + method_tooltip: 'HTTP请求方法', + headers_label: '请求头', + headers_placeholder: '{"Content-Type": "application/json"}', + headers_tooltip: 'HTTP请求头,支持变量引用', + body_label: '请求体', + body_placeholder: '{"query": "{{system.query}}"}', + body_tooltip: '请求体内容,支持变量引用' + } + }, + startNodeEditor: { + title: '编辑开始节点', + basic_config: '基本配置', + variable_management: '变量管理', + node_description: '节点描述', + node_description_placeholder: '请输入节点描述', + input_parameters: '输入参数', + output_parameters: '输出参数', + parameter_value_placeholder: '请输入参数值', + add_input_parameter: '添加输入参数', + add_output_parameter: '添加输出参数', + delete: '删除', + save: '保存', + cancel: '取消', + loading: '正在初始化...', + no_conversation: '需要会话ID才能管理变量', + // 消息提示 + messages: { + init_form_failed: '初始化表单数据失败', + load_variables_failed: '加载对话变量失败', + editor_not_initialized: '编辑器未完全初始化,请稍后再试', + save_success: '保存成功', + save_failed: '保存失败,请检查输入内容', + variables_updated: '变量已更新', + update_variables_failed: '更新变量失败', + init_editor_failed: '初始化编辑器失败', + update_form_failed: '更新表单数据失败', + component_init_failed: '组件初始化失败' + } + }, + startNodeVariableManager: { + // 标签页 + system_variables: '系统变量', + conversation_variables: '对话变量', + input_fields: '输入字段', + input_fields_hint: '设置的输入可在工作流程中使用', + no_variables: '暂无变量', + // 弹窗标题 + edit_variable: '编辑变量', + add_variable_dialog: '添加变量', + // 表单标签 + variable_name_label: '变量名称', + variable_type_label: '变量类型', + variable_value_label: '变量值', + description_label: '描述', + supported_file_types_label: '支持的文件类型', + // 变量类型分组 + basic_types: '基础类型', + array_types: '数组类型', + // 基础类型选项 + string_type: '字符串', + number_type: '数字', + boolean_type: '布尔值', + object_type: '对象', + secret_type: '密钥', + file_type: '文件', + // 数组类型选项 + array_any: '数组[任意]', + array_string: '数组[字符串]', + array_number: '数组[数字]', + array_object: '数组[对象]', + array_file: '数组[文件]', + array_boolean: '数组[布尔值]', + array_secret: '数组[密钥]', + // 文件类型分类 + document_category: '文档', + image_category: '图片', + audio_category: '音频', + video_category: '视频', + other_file_types: '其他文件类型', + // 上传相关 + upload_file_types: '上传文件类型', + local_upload: '本地上传', + url_upload: 'URL上传', + file_upload_limits: '文件上传限制', + max_files: '最大文件数:', + max_file_size: '单个文件最大大小:', + required_file: '必填文件:', + file_type_fixed: '文件类型固定为1个文件', + // 占位符文本 + enter_variable_name: '请输入变量名称', + select_variable_type: '选择变量类型', + enter_string_value: '请输入字符串值', + enter_number_value: '请输入数字值', + select_boolean_value: '选择布尔值', + enter_secret_value: '请输入密钥值', + enter_json_object: '请输入JSON格式的对象值', + enter_variable_description: '请输入变量描述(可选)', + file_extension_placeholder: '+ + 文件扩展名,例如 .doc', + // 提示信息 + file_type_tip: '文件类型变量将在对话时由用户上传', + string_array_tip: '字符串数组变量将在对话时由用户输入,默认为空数组', + number_array_tip: '数字数组变量将在对话时由用户输入,默认为空数组', + boolean_array_tip: '布尔值数组变量将在对话时由用户选择,默认为空数组', + file_array_tip: '文件列表类型变量将在对话时由用户上传,默认为空数组', + object_array_tip: '对象数组变量将在对话时由用户输入JSON格式数据,默认为空数组', + secret_array_tip: '密钥数组变量将在对话时由用户输入,默认为空数组', + any_array_tip: '任意类型数组变量将在对话时由用户输入,默认为空数组', + required_file_note: '选中后,用户在对话时必须上传文件', + // 按钮文本 + cancel: '取消', + delete: '删除', + save: '保存', + // 单位文本 + mb_unit: 'MB', + // 验证规则 + enter_variable_name_validation: '请输入变量名称', + variable_name_pattern_validation: '变量名只能包含字母、数字和下划线,且必须以字母或下划线开头', + select_variable_type_validation: '请选择变量类型', + // 错误消息 + missing_variable_data: '缺少变量数据', + missing_flow_id: '缺少工作流ID (flowId),无法保存对话变量', + variable_name_empty: '变量名不能为空', + select_variable_type_required: '请选择变量类型', + enter_valid_number: '请输入有效的数字', + json_format_incorrect: 'JSON格式不正确,请检查对象值的语法', + variable_update_success: '变量更新成功', + variable_create_success: '变量创建成功', + variable_delete_success: '变量删除成功', + save_variable_failed: '保存变量失败', + delete_variable_failed: '删除变量失败', + load_variables_failed: '变量加载失败', + description_save_success: '描述保存成功', + description_save_failed: '描述保存失败', + save_success: '保存成功', + // 其他标签 + not_set: '(未设置)', + // 系统变量相关 + system_readonly: '系统变量 (只读)', + system_description: '这些变量由系统自动提供,包含当前工作流执行的上下文信息', + available_system_variables: '可用的系统变量', + // 对话变量相关 + conversation_description: '这些变量在当前对话中有效,可以在工作流的各个步骤中使用', + add_variable: '新增变量', + // 表格列标题 + variable_name: '变量名', + type: '类型', + description: '描述', + value: '值', + reference_syntax: '引用语法', + operations: '操作', + // 系统变量描述 + system_var_descriptions: { + query: '用户当前的查询内容', + files: '用户上传的文件列表', + dialogue_count: '当前对话轮数', + app_id: '当前应用ID', + flow_id: '当前工作流ID', + user_id: '当前用户ID', + session_id: '当前会话ID', + timestamp: '当前时间戳', + default: '系统变量' + }, + // 变量类型 + variable_types: { + string: '字符串', + number: '数字', + boolean: '布尔值', + object: '对象', + secret: '密钥', + group: '分组', + file: '文件', + 'array[any]': '任意数组', + 'array[string]': '字符串数组', + 'array[number]': '数字数组', + 'array[object]': '对象数组', + 'array[file]': '文件数组', + 'array[boolean]': '布尔数组', + 'array[secret]': '密钥数组' + }, + // 空状态 + no_conversation_variables: '暂无对话变量', + empty_hint: '点击上方"新增变量"按钮创建第一个对话变量', + // 对话框 + add_conversation_variable: '新增对话变量', + // 占位符 + placeholders: { + variable_name: '请输入变量名(如:user_name)', + select_variable_type: '请选择变量类型', + variable_value: '请输入变量值', + boolean_value: '请选择布尔值', + json_object: '请输入JSON格式的对象', + string_array: '请输入逗号分隔的字符串,如:item1,item2,item3', + json_array: '请输入JSON数组格式,如:[1,2,3] 或 [{},{}]', + file_default: '可选:输入默认值或描述', + description: '请输入变量描述(可选)' + }, + // 确认删除 + confirm_delete: '确定要删除这个变量吗?', + // 验证规则 + validation: { + name_required: '请输入变量名称', + name_pattern: '变量名只能包含字母、数字和下划线,且不能以数字开头', + type_required: '请选择变量类型', + value_required: '请输入变量值' + }, + // 消息提示 + messages: { + system_variables_loaded: '系统变量加载成功', + load_system_variables_failed: '加载系统变量失败', + load_conversation_variables_failed: '加载对话变量失败', + load_variable_types_failed: '加载变量类型失败', + invalid_number: '请输入有效的数字', + invalid_json: '请输入有效的JSON格式', + invalid_number_array: '请输入有效的数字数组,如:[1,2,3]', + invalid_boolean_array: '请输入有效的布尔数组,如:[true,false]', + invalid_object_array: '请输入有效的对象数组', + variable_created: '变量创建成功', + create_variable_failed: '创建变量失败', + variable_deleted: '变量删除成功', + delete_variable_failed: '删除变量失败' + } + }, + variableChooser: { + // 标签 + variable_name_label: '变量名:', + select_variable_label: '选择变量:', + source_variable_label: '源变量:', + type_label: '类型:', + scope_label: '作用域:', + description_label: '描述:', + // 按钮和操作 + clear_variable_selection: '清空变量选择', + // 占位符 + enter_variable_name: '输入变量名称', + select_variable: '选择变量', + // 变量类型 + variable_types: { + string: '字符串', + number: '数字', + boolean: '布尔值', + object: '对象', + secret: '密钥', + file: '文件', + 'array[any]': '数组', + 'array[string]': '字符串数组', + 'array[number]': '数字数组', + 'array[object]': '对象数组', + 'array[file]': '文件数组', + 'array[boolean]': '布尔数组', + 'array[secret]': '密钥数组' + }, + // 作用域 + scopes: { + system: '系统变量', + user: '用户变量', + env: '环境变量', + conversation: '对话变量' + }, + // 描述模板 + current_node_variable: '当前节点变量: {name}', + conversation_variable: '对话变量: {name}', + scope_variable: '{scope}变量: {name}', + // 错误消息 + find_variable_failed: '查找变量失败:', + variable_detail_query_failed: '变量详细信息查询失败,使用默认信息:' + }, + variableInsertDropdown: { + // 标题和标签 + select_variable_to_insert: '选择要插入的变量', + search_variables_placeholder: '搜索变量...', + searching: '搜索中...', + // 空状态 + no_variables_found: '未找到匹配的变量', + no_available_variables: '暂无可用变量', + ensure_variables_defined: '请确保相关作用域中已定义变量' + }, + variableSelector: { + // 占位符 + select_variable: '选择变量', + search_variables_placeholder: '搜索变量...', + // 作用域标签 + scopes: { + self: '当前节点变量', + system: '系统变量', + user: '用户变量', + env: '环境变量', + conversation: '对话变量' + }, + // 变量类型 + variable_types: { + string: '字符串', + number: '数字', + boolean: '布尔值', + object: '对象', + secret: '密钥', + file: '文件', + 'array[any]': '数组', + 'array[string]': '字符串数组', + 'array[number]': '数字数组', + 'array[object]': '对象数组', + 'array[file]': '文件数组', + 'array[boolean]': '布尔数组', + 'array[secret]': '密钥数组' + }, + // 模板文本 + node_output: '节点 {nodeName} 输出', + // 空状态 + no_available_variables: '暂无可用变量', + define_variables_hint: '您可以在开始节点中定义对话变量', + // 控制台消息 + loading_variables: 'VariableSelector开始加载变量,当前参数:', + conversation_variables_query: '对话变量查询带有current_step_id:', + variables_loaded_result: '变量加载结果:', + variables_load_failed: '变量加载失败:', + variables_loading_complete: 'VariableSelector变量加载完成:', + total_count: '总数', + system_variables_count: '系统变量', + user_variables_count: '用户变量', + env_variables_count: '环境变量', + conversation_variables_count: '对话变量', + predecessor_variables_count: '前置节点变量', + variables_load_error: '变量加载失败:', + api_path_check: '建议检查: API路径是否正确,后端服务是否启动', + flow_id_check: '建议检查: flowId参数是否正确传递', + load_variable_types_failed: '加载变量类型失败:' + }, + contextMenu: { + add_node: '添加节点', + add_comment: '添加注释', + debug_workflow: '调试', + export_yaml: '导出YAML', + import_yaml: '导入YAML', + }, + choiceBranch: { + // 基本信息 + basic_info: '基本信息', + node_name: '节点名称', + node_description: '节点描述', + branch_config: '分支配置', + // 分支类型 + branch_types: { + if: 'IF', + elif: 'ELIF', + else: 'ELSE' + }, + // 数据类型 + data_types: { + string: '字符串', + number: '数值', + list: '数组', + bool: '布尔值', + dict: '对象', + reference: '引用变量' + }, + // 操作符 + operators: { + // 字符串操作符 + string_equal: '等于', + string_not_equal: '不等于', + string_contains: '包含', + string_not_contains: '不包含', + string_starts_with: '开始于', + string_ends_with: '结束于', + string_length_equal: '长度等于', + string_length_greater_than: '长度大于', + string_length_greater_than_or_equal: '长度大于等于', + string_length_less_than: '长度小于', + string_length_less_than_or_equal: '长度小于等于', + string_regex_match: '正则匹配', + // 数字操作符 + number_equal: '等于', + number_not_equal: '不等于', + number_greater_than: '大于', + number_less_than: '小于', + number_greater_than_or_equal: '大于等于', + number_less_than_or_equal: '小于等于', + // 列表操作符 + list_equal: '等于', + list_not_equal: '不等于', + list_contains: '包含', + list_not_contains: '不包含', + list_length_equal: '长度等于', + list_length_greater_than: '长度大于', + list_length_greater_than_or_equal: '长度大于等于', + list_length_less_than: '长度小于', + list_length_less_than_or_equal: '长度小于等于', + // 布尔操作符 + bool_equal: '等于', + bool_not_equal: '不等于', + // 字典操作符 + dict_equal: '等于', + dict_not_equal: '不等于', + dict_contains_key: '包含键', + dict_not_contains_key: '不包含键', + // 通用操作符(简化版) + eq: '等于', + ne: '不等于', + gt: '大于', + gte: '大于等于', + lt: '小于', + lte: '小于等于', + contains: '包含', + not_contains: '不包含', + starts_with: '开始于', + ends_with: '结束于', + is_empty: '为空', + is_not_empty: '不为空', + length_eq: '长度等于', + length_gt: '长度大于', + length_lt: '长度小于', + has_key: '包含键', + not_has_key: '不包含键' + }, + // 逻辑运算符 + logic_operators: { + and: '且 (AND)', + or: '或 (OR)', + and_text: '且', + or_text: '或' + }, + // 占位符 + placeholders: { + enter_node_name: '请输入节点名称', + enter_node_description: '请输入节点描述', + select_left_variable: '选择左值变量', + select_right_variable: '选择右值变量', + data_type: '类型', + operator: '操作符', + select_boolean: '选择布尔值', + enter_value: '输入{type}', + enter_generic_value: '值' + }, + // 按钮文本 + buttons: { + variable_reference: '变量引用', + input_value: '输入值', + add_condition: '添加条件', + delete_branch: '删除分支', + add_elif: 'ELIF', + cancel: '取消', + save: '保存' + }, + // 标题和标签 + titles: { + config_condition_branch: '配置条件分支', + case_number: 'CASE {number}', + condition_branch: '条件分支', + delete_condition: '删除条件', + selected_branch_id: '选中的分支ID' + }, + // 提示信息 + tips: { + else_description: '用于定义当IF/ELIF条件均不满足时执行的分支', + click_edit_add_branch: '点击编辑添加分支条件', + other_cases: '其他所有情况' + }, + // 验证和错误信息 + validation: { + enter_node_name: '请填写节点名称', + need_at_least_one_branch: '至少需要一个分支', + branch_missing_left_variable: '{branchName} 分支存在未填写的左值变量', + branch_missing_right_value: '{branchName} 分支存在未填写的右值', + branch_missing_operator: '{branchName} 分支存在未选择的操作符', + need_valid_branch: '至少需要一个有效的分支', + condition_cannot_delete: '条件不可删除', + each_branch_need_condition: '每个分支至少需要一个条件', + keep_at_least_one_branch: '至少需要保留一个条件分支', + confirm_delete_branch: '确定要删除这个分支吗?', + confirm_delete_title: '确认删除', + data_error_multiple_default: '数据错误:不能有多个默认分支' + }, + // 状态显示 + status: { + condition_not_set: '条件未设置', + no_condition: '条件未设置', + branch_prefix: '分支' + }, + // 控制台信息 + console: { + init_form_data: '初始化表单数据 - 原始choices:', + branch_separation_result: '分支分离结果:', + non_default_branch: '非默认分支', + found_multiple_default: '发现多个默认分支,只保留第一个:', + has_non_default_branch: '有非默认分支,更新名称', + no_non_default_branch: '没有非默认分支,创建一个空的IF分支', + no_default_branch: '没有默认分支,创建ELSE分支', + has_default_branch: '已有默认分支:', + final_form_data: '最终formData:', + save_final_data: '保存的最终数据:', + operator_mapping_not_found: '未找到操作符映射:' + } } -}; +}; \ No newline at end of file diff --git a/src/store/account.ts b/src/store/account.ts index fe7ab5e5e0841d560a3b121776702fd527705370..b13c06f59d8713de010e146491bdafddfcbe51ae 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 999a2e5d5c375ad7f208c515c34de459a54929c5..343ad3b1104c1b98d484485f81b3a893b156e7f5 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.value ? i18n.global.t('flow.flow_start') : i18n.global.t('flow.flow_end'), + progress: messageFlow.stepProgress, + status: currentTaskId.value ? 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 9b702d5e0527149a8893773ea7da63ef157b9d70..eb9638aea3f1a8e33f3de8469a2938a84055e683 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 4c5c359b3e47fa3a1a9330351196c91a42d1cecc..bdfec2e9f35755f7787d2cf9a450b28b4610f5ef 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 0000000000000000000000000000000000000000..835e22ceb05d94d029b978f63297fb26775ec4e6 --- /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 a44eff4a07646c559d7f9fb9f33e564b653c460b..e421cd9e29aef5a1c066cadada5802bbb3fe0372 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 a566f503aadc2508f54681e7026f09bc8b64b6b6..13b5e8e601d7781a39630dab4baee8c1b616fb73 100644 --- a/src/views/api/index.vue +++ b/src/views/api/index.vue @@ -103,13 +103,37 @@ :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 ce88543b8ae2334781778a6e0901644887ff0946..3e147fc81b11b0344a40655c1d59457dd6db911e 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, @@ -36,6 +38,21 @@ import REFRESH from '@/assets/svgs/Refresh.svg'; import STOP_FILLED from '@/assets/svgs/StopFilled.svg'; // 引入图片--文件处理相关图标 import DOCUMENT from '@/assets/svgs/document.svg'; +// 节点类型枚举 +export enum NodeType { + API = 'API', + LLM = 'LLM', + RAG = 'RAG', + MCP = 'MCP', + SQL = 'SQL', + Graph = 'Graph', + VariableAssign = 'VariableAssign', + FileExtract = 'FileExtract', + Choice = 'Choice', + Loop = 'Loop', + Code = 'Code', +} + // 工具类型 export type LinkType = 'redirect' | 'action'; @@ -93,40 +110,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 +203,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 8e0a04759d778dc2995eea90f355c1767c728a9b..52b4680bcf43e4c4f28a1496aaaf78d92ad0b210 100644 --- a/src/views/createapp/components/workFlow.vue +++ b/src/views/createapp/components/workFlow.vue @@ -1,18 +1,19 @@ + + + + + \ No newline at end of file diff --git a/src/views/createapp/components/workFlowConfig/BranchNode.vue b/src/views/createapp/components/workFlowConfig/BranchNode.vue deleted file mode 100644 index 40c45f4fbdd41792024188ec14525fef3ef281d8..0000000000000000000000000000000000000000 --- a/src/views/createapp/components/workFlowConfig/BranchNode.vue +++ /dev/null @@ -1,262 +0,0 @@ - - - - - diff --git a/src/views/createapp/components/workFlowConfig/ChoiceBranchCard.vue b/src/views/createapp/components/workFlowConfig/ChoiceBranchCard.vue index 8722fb8651796855a6b353a2d65f76c75d565fe2..fa7b6b9b60b92500eaca5e20ebcbfe4e7e0f13ab 100644 --- a/src/views/createapp/components/workFlowConfig/ChoiceBranchCard.vue +++ b/src/views/createapp/components/workFlowConfig/ChoiceBranchCard.vue @@ -3,6 +3,7 @@ import { ref, computed } from 'vue'; import { ElMessage, ElMessageBox, ElButton, ElSelect, ElOption, ElInput, ElSwitch } from 'element-plus'; import { Plus, Delete, Refresh } from '@element-plus/icons-vue'; import { v4 as uuidv4 } from 'uuid'; +import { useI18n } from 'vue-i18n'; import VariableChooser from '@/components/VariableChooser.vue'; // 类型定义 @@ -96,61 +97,63 @@ const props = defineProps({ const emit = defineEmits(['removeBranch', 'addCondition', 'removeCondition', 'toggleLogic', 'updateCondition']); +const { t } = useI18n(); + // 数据类型选项(基于后端schemas) -const dataTypeOptions = [ - { label: '字符串', value: 'string' }, - { label: '数值', value: 'number' }, - { label: '数组', value: 'list' }, - { label: '布尔值', value: 'bool' }, - { label: '对象', value: 'dict' }, -]; +const dataTypeOptions = computed(() => [ + { label: t('choiceBranch.data_types.string'), value: 'string' }, + { label: t('choiceBranch.data_types.number'), value: 'number' }, + { label: t('choiceBranch.data_types.list'), value: 'list' }, + { label: t('choiceBranch.data_types.bool'), value: 'bool' }, + { label: t('choiceBranch.data_types.dict'), value: 'dict' }, +]); // 基于后端schemas的操作符配置 -const operatorOptionsByType = { +const operatorOptionsByType = computed(() => ({ string: [ - { label: '等于', value: 'string_equal' }, - { label: '不等于', value: 'string_not_equal' }, - { label: '包含', value: 'string_contains' }, - { label: '不包含', value: 'string_not_contains' }, - { label: '开始于', value: 'string_starts_with' }, - { label: '结束于', value: 'string_ends_with' }, - { label: '长度等于', value: 'string_length_equal' }, - { label: '长度大于', value: 'string_length_greater_than' }, - { label: '长度大于等于', value: 'string_length_greater_than_or_equal' }, - { label: '长度小于', value: 'string_length_less_than' }, - { label: '长度小于等于', value: 'string_length_less_than_or_equal' }, - { label: '正则匹配', value: 'string_regex_match' }, + { label: t('choiceBranch.operators.string_equal'), value: 'string_equal' }, + { label: t('choiceBranch.operators.string_not_equal'), value: 'string_not_equal' }, + { label: t('choiceBranch.operators.string_contains'), value: 'string_contains' }, + { label: t('choiceBranch.operators.string_not_contains'), value: 'string_not_contains' }, + { label: t('choiceBranch.operators.string_starts_with'), value: 'string_starts_with' }, + { label: t('choiceBranch.operators.string_ends_with'), value: 'string_ends_with' }, + { label: t('choiceBranch.operators.string_length_equal'), value: 'string_length_equal' }, + { label: t('choiceBranch.operators.string_length_greater_than'), value: 'string_length_greater_than' }, + { label: t('choiceBranch.operators.string_length_greater_than_or_equal'), value: 'string_length_greater_than_or_equal' }, + { label: t('choiceBranch.operators.string_length_less_than'), value: 'string_length_less_than' }, + { label: t('choiceBranch.operators.string_length_less_than_or_equal'), value: 'string_length_less_than_or_equal' }, + { label: t('choiceBranch.operators.string_regex_match'), value: 'string_regex_match' }, ], 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: t('choiceBranch.operators.number_equal'), value: 'number_equal' }, + { label: t('choiceBranch.operators.number_not_equal'), value: 'number_not_equal' }, + { label: t('choiceBranch.operators.number_greater_than'), value: 'number_greater_than' }, + { label: t('choiceBranch.operators.number_less_than'), value: 'number_less_than' }, + { label: t('choiceBranch.operators.number_greater_than_or_equal'), value: 'number_greater_than_or_equal' }, + { label: t('choiceBranch.operators.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: '长度等于', value: 'list_length_equal' }, - { label: '长度大于', value: 'list_length_greater_than' }, - { label: '长度大于等于', value: 'list_length_greater_than_or_equal' }, - { label: '长度小于', value: 'list_length_less_than' }, - { label: '长度小于等于', value: 'list_length_less_than_or_equal' }, + { label: t('choiceBranch.operators.list_equal'), value: 'list_equal' }, + { label: t('choiceBranch.operators.list_not_equal'), value: 'list_not_equal' }, + { label: t('choiceBranch.operators.list_contains'), value: 'list_contains' }, + { label: t('choiceBranch.operators.list_not_contains'), value: 'list_not_contains' }, + { label: t('choiceBranch.operators.list_length_equal'), value: 'list_length_equal' }, + { label: t('choiceBranch.operators.list_length_greater_than'), value: 'list_length_greater_than' }, + { label: t('choiceBranch.operators.list_length_greater_than_or_equal'), value: 'list_length_greater_than_or_equal' }, + { label: t('choiceBranch.operators.list_length_less_than'), value: 'list_length_less_than' }, + { label: t('choiceBranch.operators.list_length_less_than_or_equal'), value: 'list_length_less_than_or_equal' }, ], bool: [ - { label: '等于', value: 'bool_equal' }, - { label: '不等于', value: 'bool_not_equal' }, + { label: t('choiceBranch.operators.bool_equal'), value: 'bool_equal' }, + { label: t('choiceBranch.operators.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: t('choiceBranch.operators.dict_equal'), value: 'dict_equal' }, + { label: t('choiceBranch.operators.dict_not_equal'), value: 'dict_not_equal' }, + { label: t('choiceBranch.operators.dict_contains_key'), value: 'dict_contains_key' }, + { label: t('choiceBranch.operators.dict_not_contains_key'), value: 'dict_not_contains_key' }, ], -}; +})); // 获取分支标题 const getBranchTitle = computed(() => { @@ -162,7 +165,7 @@ const getBranchTitle = computed(() => { // 获取 Case 编号 const getCaseNumber = (index: number) => { - return `CASE ${index + 1}`; + return t('choiceBranch.titles.case_number', { number: index + 1 }); }; // 计算 conditions-bracket 的高度 @@ -198,7 +201,7 @@ const getConnectingLineHeight = computed(() => { // 获取操作符选项 const getOperatorOptions = (dataType: string) => { - return operatorOptionsByType[dataType] || operatorOptionsByType.string; + return operatorOptionsByType.value[dataType] || operatorOptionsByType.value.string; }; // 添加条件 @@ -211,12 +214,12 @@ const removeCondition = (conditionIndex: number) => { if (props.choice.conditions.length <= 1) { // 只有一个条件时,检查是否允许删除条件 if (!props.showConditionDelete) { - ElMessage.warning('条件不可删除'); + ElMessage.warning(t('choiceBranch.validation.condition_cannot_delete')); return; } // 如果can-delete为false且showConditionDelete为true,仍然不允许删除最后一个条件 if (!props.canDelete) { - ElMessage.warning('每个分支至少需要一个条件'); + ElMessage.warning(t('choiceBranch.validation.each_branch_need_condition')); return; } } @@ -231,12 +234,12 @@ const toggleLogic = () => { // 删除分支 const removeBranch = async () => { if (!props.canDelete) { - ElMessage.warning('至少需要保留一个条件分支'); + ElMessage.warning(t('choiceBranch.validation.keep_at_least_one_branch')); return; } try { - await ElMessageBox.confirm('确定要删除这个分支吗?', '确认删除', { + await ElMessageBox.confirm(t('choiceBranch.validation.confirm_delete_branch'), t('choiceBranch.validation.confirm_delete_title'), { type: 'warning', }); @@ -372,7 +375,7 @@ const handleRightValueChange = (conditionIndex: number, value: any) => { :self-variables="selfVariables" :self-scope-label="selfScopeLabel" output-format="raw" - selector-placeholder="选择左值变量" + :selector-placeholder="t('choiceBranch.placeholders.select_left_variable')" @variable-selected="(variable, reference) => handleLeftVariableSelected(conditionIndex, variable, reference)" />
@@ -382,7 +385,7 @@ const handleRightValueChange = (conditionIndex: number, value: any) => { @update:model-value="handleDataTypeChange(conditionIndex, $event)" size="small" class="data-type-select" - placeholder="类型" + :placeholder="t('choiceBranch.placeholders.data_type')" > { @update:model-value="handleOperatorChange(conditionIndex, $event)" size="small" class="operator-select" - placeholder="操作符" + :placeholder="t('choiceBranch.placeholders.operator')" > { :self-variables="selfVariables" :self-scope-label="selfScopeLabel" output-format="raw" - selector-placeholder="选择右值变量" + :selector-placeholder="t('choiceBranch.placeholders.select_right_variable')" @variable-selected="(variable, reference) => handleRightVariableSelected(conditionIndex, variable, reference)" />
@@ -442,7 +445,7 @@ const handleRightValueChange = (conditionIndex: number, value: any) => { @update:model-value="handleRightValueChange(conditionIndex, $event)" size="small" style="flex: 1;" - placeholder="选择布尔值" + :placeholder="t('choiceBranch.placeholders.select_boolean')" > @@ -454,7 +457,7 @@ const handleRightValueChange = (conditionIndex: number, value: any) => { :model-value="condition.right.value" @update:model-value="handleRightValueChange(conditionIndex, $event)" size="small" - :placeholder="`输入${dataTypeOptions.find(t => t.value === condition.dataType)?.label || '值'}`" + :placeholder="t('choiceBranch.placeholders.enter_value', { type: dataTypeOptions.find(opt => opt.value === condition.dataType)?.label || t('choiceBranch.placeholders.enter_generic_value') })" style="flex: 1;" />
@@ -467,7 +470,7 @@ const handleRightValueChange = (conditionIndex: number, value: any) => { @click="handleRightReferenceToggle(conditionIndex, !condition.isRightReference)" :icon="Refresh" > - {{ condition.isRightReference ? '变量引用' : '输入值' }} + {{ condition.isRightReference ? t('choiceBranch.buttons.variable_reference') : t('choiceBranch.buttons.input_value') }}
@@ -478,7 +481,7 @@ const handleRightValueChange = (conditionIndex: number, value: any) => { class="danger-button" @click="removeCondition(conditionIndex)" type="button" - title="删除条件" + :title="t('choiceBranch.titles.delete_condition')" > @@ -493,7 +496,7 @@ const handleRightValueChange = (conditionIndex: number, value: any) => { :icon="Plus" @click="addCondition" > - 添加条件 + {{ t('choiceBranch.buttons.add_condition') }}
@@ -515,17 +518,27 @@ const handleRightValueChange = (conditionIndex: number, value: any) => { .branch-card { border-radius: 8px; margin-bottom: 16px; - background: #ffffff; + background: var(--el-bg-color); + + body[theme='dark'] & { + background: #1f2329; + border-color: var(--el-border-color); + }; &.default-branch { - border-color: #f56c6c; - background: #fef0f0; + border-color: var(--el-color-danger); + background: var(--el-color-danger-light-9); } .branch-header { padding: 16px; - border-bottom: 1px solid #e4e7ed; - background: #f8f9fa; + border-bottom: 1px solid var(--el-border-color-light); + background: var(--el-fill-color-extra-light); + + body[theme='dark'] & { + background: var(--o-bash-bg, #2a2f37); + border-bottom-color: var(--el-border-color); + }; display: flex; align-items: center; justify-content: space-between; @@ -538,11 +551,11 @@ const handleRightValueChange = (conditionIndex: number, value: any) => { .main-title { font-size: 18px; font-weight: 700; - color: #409eff; + color: var(--el-color-primary); padding: 6px 12px; - background: #ecf5ff; + background: var(--el-color-primary-light-9); border-radius: 6px; - border: 1px solid #d9ecff; + border: 1px solid var(--el-color-primary-light-7); min-width: 60px; text-align: center; } @@ -550,7 +563,11 @@ const handleRightValueChange = (conditionIndex: number, value: any) => { .sub-title { font-size: 14px; font-weight: 600; - color: #606266; + color: var(--el-text-color-primary); + + body[theme='dark'] & { + color: #e4e8ee; + } } } @@ -571,14 +588,22 @@ const handleRightValueChange = (conditionIndex: number, value: any) => { .conditions-title { font-weight: 600; - color: black; + color: var(--el-text-color-primary); font-size: 16px; + + body[theme='dark'] & { + color: #e4e8ee; + } } .conditions-subtitle { font-weight: 500; - color: #676f83; + color: var(--el-text-color-secondary); font-size: 12px; + + body[theme='dark'] & { + color: #d3dce9; + } } } @@ -606,9 +631,16 @@ const handleRightValueChange = (conditionIndex: number, value: any) => { font-weight: 600; font-size: 10px; padding: 4px 8px; - background-color: white; + background-color: var(--el-bg-color); + color: var(--el-text-color-primary); z-index: 1; + body[theme='dark'] & { + background-color: #1f2329; + color: #e4e8ee; + border-color: var(--el-border-color); + }; + // 调整图标大小 :deep(.el-icon) { font-size: 10px; @@ -652,8 +684,13 @@ const handleRightValueChange = (conditionIndex: number, value: any) => { .condition-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); + + body[theme='dark'] & { + background: var(--o-bash-bg, #2a2f37); + border-color: var(--el-border-color); + }; border-radius: 8px; overflow: hidden; transition: background-color 0.2s ease; @@ -684,7 +721,11 @@ const handleRightValueChange = (conditionIndex: number, value: any) => { .condition-number { font-size: 12px; font-weight: 600; - color: #909399; + color: var(--el-text-color-secondary); + + body[theme='dark'] & { + color: #d3dce9; + } } } @@ -696,7 +737,11 @@ const handleRightValueChange = (conditionIndex: number, value: any) => { padding: 6px 0 6px 0; &:not(:last-child) { - border-bottom: 1px solid #e4e7ed; + border-bottom: 1px solid var(--el-border-color-light); + + body[theme='dark'] & { + border-bottom-color: var(--el-border-color); + }; margin-left: 2px; margin-right: 2px; padding-left: 0; @@ -708,13 +753,21 @@ const handleRightValueChange = (conditionIndex: number, value: any) => { .variable-section { flex: 3; // VariableChooser 占3份空间 - border-right: 1px solid #e4e7ed; + border-right: 1px solid var(--el-border-color-light); + + body[theme='dark'] & { + border-right-color: var(--el-border-color); + }; padding-right: 12px; } .data-type-select { flex: 0 0 25%; - border-right: 1px solid #e4e7ed; + border-right: 1px solid var(--el-border-color-light); + + body[theme='dark'] & { + border-right-color: var(--el-border-color); + }; padding-right: 12px; :deep(.el-select__wrapper) { @@ -746,7 +799,11 @@ const handleRightValueChange = (conditionIndex: number, value: any) => { .value-section { flex: 1; min-width: 0; - border-right: 1px solid #e4e7ed; + border-right: 1px solid var(--el-border-color-light); + + body[theme='dark'] & { + border-right-color: var(--el-border-color); + }; padding-right: 12px; .reference-input, @@ -767,7 +824,7 @@ const handleRightValueChange = (conditionIndex: number, value: any) => { .el-button { border: none; background: transparent; - color: #409eff; + color: var(--el-color-primary); font-size: 12px; padding: 4px 8px; border-radius: 0; @@ -800,91 +857,7 @@ const handleRightValueChange = (conditionIndex: number, value: any) => { } } -// 深色主题支持 -.dark { - .branch-card { - background: #374151; - border-color: #4b5563; - - &.default-branch { - border-color: #f87171; - background: #450a0a; - } - - .branch-header { - background: #4b5563; - border-color: #6b7280; - - .branch-title-section { - .main-title { - color: #3b82f6; - background: #1e3a8a; - border-color: #1e40af; - } - - .sub-title { - color: #e5e7eb; - } - } - } - - .conditions-container { - .conditions-bracket { - .bracket-line { - background: #3b82f6; - } - } - - .conditions-list { - .condition-item { - background: #4b5563; - border-color: #6b7280; - - .condition-setting-col { - background: #4b5563; - border-color: #6b7280; - - .condition-row { - &:not(:last-child) { - border-bottom-color: #6b7280; - } - - &.variable-row { - .variable-section { - border-right-color: #6b7280; - } - - .data-type-select { - border-right-color: #6b7280; - } - - .operator-select :deep(.el-select__wrapper) { - border-right-color: #6b7280; - } - } - - &.value-row { - .value-section { - border-right-color: #6b7280; - } - - .toggle-section .el-button { - color: #3b82f6; - - &:hover { - background: rgba(59, 130, 246, 0.2); - } - } - } - } - } - } - } - } - - - } -} +// 深色主题适配已集成到主样式中 // 确保VariableChooser组件样式适配 :deep(.variable-chooser) { diff --git a/src/views/createapp/components/workFlowConfig/ChoiceBranchDrawer.vue b/src/views/createapp/components/workFlowConfig/ChoiceBranchDrawer.vue index 26fa75dc79e23c601b47891211a7984ecaec4776..550d3bac7d461e3e7e158ab5e6c7d5b46650f17d 100644 --- a/src/views/createapp/components/workFlowConfig/ChoiceBranchDrawer.vue +++ b/src/views/createapp/components/workFlowConfig/ChoiceBranchDrawer.vue @@ -77,68 +77,68 @@ const formData = ref({ }); // 操作符选项 -const operatorOptions = { +const operatorOptions = computed(() => ({ string: [ - { label: '等于', value: 'eq' }, - { label: '不等于', value: 'ne' }, - { label: '包含', value: 'contains' }, - { label: '不包含', value: 'not_contains' }, - { label: '开始于', value: 'starts_with' }, - { label: '结束于', value: 'ends_with' }, - { label: '为空', value: 'is_empty' }, - { label: '不为空', value: 'is_not_empty' }, + { label: t('choiceBranch.operators.eq'), value: 'eq' }, + { label: t('choiceBranch.operators.ne'), value: 'ne' }, + { label: t('choiceBranch.operators.contains'), value: 'contains' }, + { label: t('choiceBranch.operators.not_contains'), value: 'not_contains' }, + { label: t('choiceBranch.operators.starts_with'), value: 'starts_with' }, + { label: t('choiceBranch.operators.ends_with'), value: 'ends_with' }, + { label: t('choiceBranch.operators.is_empty'), value: 'is_empty' }, + { label: t('choiceBranch.operators.is_not_empty'), value: 'is_not_empty' }, ], number: [ - { label: '等于', value: 'eq' }, - { label: '不等于', value: 'ne' }, - { label: '大于', value: 'gt' }, - { label: '大于等于', value: 'gte' }, - { label: '小于', value: 'lt' }, - { label: '小于等于', value: 'lte' }, + { label: t('choiceBranch.operators.eq'), value: 'eq' }, + { label: t('choiceBranch.operators.ne'), value: 'ne' }, + { label: t('choiceBranch.operators.gt'), value: 'gt' }, + { label: t('choiceBranch.operators.gte'), value: 'gte' }, + { label: t('choiceBranch.operators.lt'), value: 'lt' }, + { label: t('choiceBranch.operators.lte'), value: 'lte' }, ], bool: [ - { label: '等于', value: 'eq' }, - { label: '不等于', value: 'ne' }, + { label: t('choiceBranch.operators.eq'), value: 'eq' }, + { label: t('choiceBranch.operators.ne'), value: 'ne' }, ], list: [ - { label: '包含', value: 'contains' }, - { label: '不包含', value: 'not_contains' }, - { label: '为空', value: 'is_empty' }, - { label: '不为空', value: 'is_not_empty' }, - { label: '长度等于', value: 'length_eq' }, - { label: '长度大于', value: 'length_gt' }, - { label: '长度小于', value: 'length_lt' }, + { label: t('choiceBranch.operators.contains'), value: 'contains' }, + { label: t('choiceBranch.operators.not_contains'), value: 'not_contains' }, + { label: t('choiceBranch.operators.is_empty'), value: 'is_empty' }, + { label: t('choiceBranch.operators.is_not_empty'), value: 'is_not_empty' }, + { label: t('choiceBranch.operators.length_eq'), value: 'length_eq' }, + { label: t('choiceBranch.operators.length_gt'), value: 'length_gt' }, + { label: t('choiceBranch.operators.length_lt'), value: 'length_lt' }, ], dict: [ - { label: '包含键', value: 'has_key' }, - { label: '不包含键', value: 'not_has_key' }, - { label: '为空', value: 'is_empty' }, - { label: '不为空', value: 'is_not_empty' }, + { label: t('choiceBranch.operators.has_key'), value: 'has_key' }, + { label: t('choiceBranch.operators.not_has_key'), value: 'not_has_key' }, + { label: t('choiceBranch.operators.is_empty'), value: 'is_empty' }, + { label: t('choiceBranch.operators.is_not_empty'), value: 'is_not_empty' }, ], -}; +})); // 值类型选项 -const valueTypeOptions = [ - { label: '字符串', value: 'string' }, - { label: '数字', value: 'number' }, - { label: '布尔值', value: 'bool' }, - { label: '列表', value: 'list' }, - { label: '字典', value: 'dict' }, - { label: '引用变量', value: 'reference' }, -]; +const valueTypeOptions = computed(() => [ + { label: t('choiceBranch.data_types.string'), value: 'string' }, + { label: t('choiceBranch.data_types.number'), value: 'number' }, + { label: t('choiceBranch.data_types.bool'), value: 'bool' }, + { label: t('choiceBranch.data_types.list'), value: 'list' }, + { label: t('choiceBranch.data_types.dict'), value: 'dict' }, + { label: t('choiceBranch.data_types.reference'), value: 'reference' }, +]); // 逻辑运算符选项 -const logicOptions = [ - { label: '且 (AND)', value: 'and' }, - { label: '或 (OR)', value: 'or' }, -]; +const logicOptions = computed(() => [ + { label: t('choiceBranch.logic_operators.and'), value: 'and' }, + { label: t('choiceBranch.logic_operators.or'), value: 'or' }, +]); // 初始化表单数据 const initFormData = () => { if (props.nodeData) { const allChoices = JSON.parse(JSON.stringify(props.nodeData.parameters?.input_parameters?.choices || [])); - console.log('[ChoiceBranchDrawer] 初始化表单数据 - 原始choices:', { + console.log('[ChoiceBranchDrawer]', t('choiceBranch.console.init_form_data'), { allChoices, choicesCount: allChoices.length, nodeData: props.nodeData @@ -148,7 +148,7 @@ const initFormData = () => { let defaultBranches = allChoices.filter(choice => choice.is_default); let nonDefaultChoices = allChoices.filter(choice => !choice.is_default); - console.log('[ChoiceBranchDrawer] 分支分离结果:', { + console.log('[ChoiceBranchDrawer]', t('choiceBranch.console.branch_separation_result'), { defaultBranches, nonDefaultChoices, defaultCount: defaultBranches.length, @@ -157,7 +157,7 @@ const initFormData = () => { // 详细打印每个非默认分支的条件信息 nonDefaultChoices.forEach((choice, index) => { - console.log(`[ChoiceBranchDrawer] 非默认分支 ${index}:`, { + console.log(`[ChoiceBranchDrawer] ${t('choiceBranch.console.non_default_branch')} ${index}:`, { branch_id: choice.branch_id, name: choice.name, is_default: choice.is_default, @@ -168,13 +168,13 @@ const initFormData = () => { // 验证默认分支数量 if (defaultBranches.length > 1) { - console.warn('[ChoiceBranchDrawer] 发现多个默认分支,只保留第一个:', defaultBranches); - ElMessage.error('数据错误:不能有多个默认分支'); + console.warn('[ChoiceBranchDrawer]', t('choiceBranch.console.found_multiple_default'), defaultBranches); + ElMessage.error(t('choiceBranch.validation.data_error_multiple_default')); defaultBranches = [defaultBranches[0]]; // 只保留第一个默认分支 } formData.value = { - name: props.nodeData.name || '条件分支', + name: props.nodeData.name || t('choiceBranch.titles.condition_branch'), description: props.nodeData.description || '', choices: nonDefaultChoices, defaultBranch: defaultBranches.length > 0 ? defaultBranches[0] : null, @@ -189,7 +189,7 @@ const initFormData = () => { }) .map((choice, index) => ({ branch_id: choice.branch_id || uuidv4(), - name: choice.name || `分支 ${index + 1}`, + name: choice.name || `${t('choiceBranch.status.branch_prefix')} ${index + 1}`, logic: choice.logic || 'and', conditions: (() => { // 处理条件数组 @@ -204,7 +204,7 @@ const initFormData = () => { type: condition.right?.type || 'string', value: condition.right?.value || '', }, - operate: condition.operator || condition.operate || 'string_equal', + operate: condition.operate || 'string_equal', dataType: condition.dataType || 'string', isRightReference: condition.isRightReference !== undefined ? condition.isRightReference : false, })); @@ -245,10 +245,10 @@ const initFormData = () => { // 更新非默认分支名称为 IF/ELIF(如果有有效分支) if (formData.value.choices.length > 0) { - console.log('[ChoiceBranchDrawer] 有非默认分支,更新名称'); + console.log('[ChoiceBranchDrawer]', t('choiceBranch.console.has_non_default_branch')); updateBranchNames(); } else { - console.log('[ChoiceBranchDrawer] 没有非默认分支,创建一个空的IF分支'); + console.log('[ChoiceBranchDrawer]', t('choiceBranch.console.no_non_default_branch')); // 如果没有非默认分支,创建一个空的IF分支,这样用户就不需要手动点击+ELIF按钮 formData.value.choices = [ { @@ -263,7 +263,7 @@ const initFormData = () => { // 如果没有默认分支,创建一个 if (!formData.value.defaultBranch) { - console.log('[ChoiceBranchDrawer] 没有默认分支,创建ELSE分支'); + console.log('[ChoiceBranchDrawer]', t('choiceBranch.console.no_default_branch')); formData.value.defaultBranch = { branch_id: 'else_' + uuidv4(), name: 'ELSE', @@ -272,10 +272,10 @@ const initFormData = () => { is_default: true, }; } else { - console.log('[ChoiceBranchDrawer] 已有默认分支:', formData.value.defaultBranch); + console.log('[ChoiceBranchDrawer]', t('choiceBranch.console.has_default_branch'), formData.value.defaultBranch); } - console.log('[ChoiceBranchDrawer] 最终formData:', { + console.log('[ChoiceBranchDrawer]', t('choiceBranch.console.final_form_data'), { choices: formData.value.choices, defaultBranch: formData.value.defaultBranch, choicesCount: formData.value.choices.length @@ -283,7 +283,7 @@ const initFormData = () => { } else { // 如果没有nodeData,创建空的表单数据,不自动创建IF分支 formData.value = { - name: '条件分支', + name: t('choiceBranch.titles.condition_branch'), description: '', choices: [], // 不自动创建IF分支 defaultBranch: { @@ -409,13 +409,13 @@ const updateCondition = (choiceIndex, conditionIndex, condition) => { const validateForm = () => { // 验证基本信息 if (!formData.value.name || formData.value.name.trim() === '') { - ElMessage.error('请填写节点名称'); + ElMessage.error(t('choiceBranch.validation.enter_node_name')); return false; } // 验证是否至少有一个分支(默认分支) if (!formData.value.defaultBranch && formData.value.choices.length === 0) { - ElMessage.error('至少需要一个分支'); + ElMessage.error(t('choiceBranch.validation.need_at_least_one_branch')); return false; } @@ -435,15 +435,15 @@ const validateForm = () => { // 对于有条件的分支,验证条件完整性 for (const condition of validConditions) { if (!condition.left.value || condition.left.value === '') { - ElMessage.error(`${choice.name} 分支存在未填写的左值变量`); + ElMessage.error(t('choiceBranch.validation.branch_missing_left_variable', { branchName: choice.name })); return false; } if (!condition.right.value || condition.right.value === '') { - ElMessage.error(`${choice.name} 分支存在未填写的右值`); + ElMessage.error(t('choiceBranch.validation.branch_missing_right_value', { branchName: choice.name })); return false; } if (!condition.operate) { - ElMessage.error(`${choice.name} 分支存在未选择的操作符`); + ElMessage.error(t('choiceBranch.validation.branch_missing_operator', { branchName: choice.name })); return false; } } @@ -479,7 +479,7 @@ const saveNode = () => { // 如果没有任何有效的非默认分支,确保至少有默认分支 if (allChoices.length === 0) { - ElMessage.error('至少需要一个有效的分支'); + ElMessage.error(t('choiceBranch.validation.need_valid_branch')); return; } @@ -523,13 +523,13 @@ const saveNode = () => { output_parameters: { branch_id: { type: 'string', - description: '选中的分支ID' + description: t('choiceBranch.titles.selected_branch_id') } }, }, }; - console.log('[ChoiceBranchDrawer] 保存的最终数据:', { + console.log('[ChoiceBranchDrawer]', t('choiceBranch.console.save_final_data'), { processedChoices, validChoicesCount: validChoices.length, totalChoicesCount: allChoices.length @@ -593,26 +593,26 @@ const formatValue = (value, type) => {
-

基本信息

+

{{ $t('choiceBranch.basic_info') }}

- - + + - + @@ -621,7 +621,7 @@ const formatValue = (value, type) => {
-

分支配置

+

{{ $t('choiceBranch.branch_config') }}

@@ -648,7 +648,7 @@ const formatValue = (value, type) => {
@@ -658,7 +658,7 @@ const formatValue = (value, type) => {

ELSE

-

用于定义当IF/ELIF条件均不满足时执行的分支

+

{{ $t('choiceBranch.tips.else_description') }}

@@ -667,8 +667,8 @@ const formatValue = (value, type) => { @@ -685,6 +685,11 @@ const formatValue = (value, type) => { height: 100%; overflow-y: auto; padding: 20px; + background: var(--el-bg-color); + + body[theme='dark'] & { + background: #1f2329; + } } .section { @@ -693,8 +698,12 @@ const formatValue = (value, type) => { .section-title { font-size: 16px; font-weight: 600; - color: #303133; + color: var(--el-text-color-primary); margin: 0 0 16px 0; + + body[theme='dark'] & { + color: #e4e8ee; + } } .section-header { @@ -712,8 +721,8 @@ const formatValue = (value, type) => { .add-branch-btn { width: calc(100% - 20px); padding: 12px 16px; - background-color: #f2f4f7; - color: black; + background-color: var(--el-fill-color-extra-light); + color: var(--el-text-color-primary); border: none; border-radius: 6px; font-size: 14px; @@ -724,13 +733,24 @@ const formatValue = (value, type) => { gap: 6px; transition: all 0.2s; + body[theme='dark'] & { + background-color: #1f2329; + color: #e4e8ee; + border: 1px solid var(--el-border-color); + } + svg { width: 14px; height: 14px; } &:hover { - background-color: #e5e7eb; + background-color: var(--el-fill-color-light); + + body[theme='dark'] & { + background-color: var(--flow-node-default-over-color, #25303e); + border-color: var(--flow-node-boder-default-over, #314265); + } } &:active { @@ -741,23 +761,35 @@ const formatValue = (value, type) => { .divider { height: 1px; - background: #e4e7ed; + background: var(--el-border-color-light); margin: 24px 0; + + body[theme='dark'] & { + background: var(--el-border-color); + } } .default-branch-section { .else-title { font-size: 16px; font-weight: 600; - color: #303133; + color: var(--el-text-color-primary); margin: 0 0 8px 0; + + body[theme='dark'] & { + color: #e4e8ee; + } } .else-description { font-size: 14px; - color: #909399; + color: var(--el-text-color-secondary); margin: 0; line-height: 1.5; + + body[theme='dark'] & { + color: #d3dce9; + } } } @@ -766,28 +798,17 @@ const formatValue = (value, type) => { .drawer-footer { padding: 16px 20px; - border-top: 1px solid #e4e7ed; - background: #f8f9fa; + border-top: 1px solid var(--el-border-color-light); + background: var(--el-bg-color); display: flex; justify-content: flex-end; gap: 12px; -} - -// 深色主题支持 -.dark { - .choice-branch-drawer { - .drawer-content { - background: #1a1a1a; - } - - .section-title { - color: #e5e7eb; - } - - .drawer-footer { - background: #374151; - border-color: #4b5563; - } + + body[theme='dark'] & { + background: #1f2329; + border-top-color: var(--el-border-color); } } + +// 深色主题适配已集成到主样式中 \ No newline at end of file diff --git a/src/views/createapp/components/workFlowConfig/ChoiceBranchNode.vue b/src/views/createapp/components/workFlowConfig/ChoiceBranchNode.vue index da1f9efd040be374567d7b3482747c1483697167..15a6393b0c36bc3335177890a1191b3d3148c908 100644 --- a/src/views/createapp/components/workFlowConfig/ChoiceBranchNode.vue +++ b/src/views/createapp/components/workFlowConfig/ChoiceBranchNode.vue @@ -65,7 +65,7 @@ const branches = computed(() => { return choices.map((choice, index) => { return { id: choice.branch_id || `branch_${index}`, - name: choice.name || `分支 ${index + 1}`, + name: choice.name || `${t('choiceBranch.status.branch_prefix')} ${index + 1}`, isDefault: choice.is_default || false, conditions: choice.conditions || [], logic: choice.logic || 'and' @@ -169,40 +169,8 @@ const setConnectStatus = (type) => { }; // 操作符中文映射 -const operatorLabels = { - 'string_equal': '等于', - 'string_not_equal': '不等于', - 'string_contains': '包含', - 'string_not_contains': '不包含', - 'string_starts_with': '开始于', - 'string_ends_with': '结束于', - 'string_length_equal': '长度等于', - 'string_length_greater_than': '长度大于', - 'string_length_greater_than_or_equal': '长度大于等于', - 'string_length_less_than': '长度小于', - 'string_length_less_than_or_equal': '长度小于等于', - 'string_regex_match': '正则匹配', - 'number_equal': '等于', - 'number_not_equal': '不等于', - 'number_greater_than': '大于', - 'number_less_than': '小于', - 'number_greater_than_or_equal': '大于等于', - 'number_less_than_or_equal': '小于等于', - 'list_equal': '等于', - 'list_not_equal': '不等于', - 'list_contains': '包含', - 'list_not_contains': '不包含', - 'list_length_equal': '长度等于', - 'list_length_greater_than': '长度大于', - 'list_length_greater_than_or_equal': '长度大于等于', - 'list_length_less_than': '长度小于', - 'list_length_less_than_or_equal': '长度小于等于', - 'bool_equal': '等于', - 'bool_not_equal': '不等于', - 'dict_equal': '等于', - 'dict_not_equal': '不等于', - 'dict_contains_key': '包含键', - 'dict_not_contains_key': '不包含键', +const getOperatorLabel = (operatorKey: string): string => { + return t(`choiceBranch.operators.${operatorKey}`, operatorKey); }; // 解析变量引用,提取变量名 @@ -254,7 +222,7 @@ const formatValueByType = (value, dataType, isReference = false) => { // 格式化条件显示 const formatConditions = (conditions, logic) => { if (!conditions || conditions.length === 0) { - return '条件未设置'; + return t('choiceBranch.status.condition_not_set'); } // 过滤掉无效条件(left.value为null的条件) @@ -266,7 +234,7 @@ const formatConditions = (conditions, logic) => { }); if (validConditions.length === 0) { - return '条件未设置'; + return t('choiceBranch.status.condition_not_set'); } const conditionTexts = validConditions.map(condition => { @@ -275,7 +243,7 @@ const formatConditions = (conditions, logic) => { // 获取操作符中文 const operatorKey = condition.operator || condition.operate; - let operatorLabel = operatorLabels[operatorKey]; + let operatorLabel = getOperatorLabel(operatorKey); // 如果没有找到映射,提供简单的后备显示 if (!operatorLabel) { @@ -312,14 +280,14 @@ const formatConditions = (conditions, logic) => { return `${leftValue} ${operatorLabel} ${rightValue}`; }); - const logicText = logic === 'and' ? ' 且 ' : ' 或 '; + const logicText = logic === 'and' ? ` ${t('choiceBranch.logic_operators.and_text')} ` : ` ${t('choiceBranch.logic_operators.or_text')} `; return conditionTexts.join(logicText); }; // 格式化条件显示为HTML(带标签样式) const formatConditionsHtml = (conditions, logic) => { if (!conditions || conditions.length === 0) { - return '条件未设置'; + return `${t('choiceBranch.status.condition_not_set')}`; } // 过滤掉无效条件(left.value为null的条件) @@ -331,7 +299,7 @@ const formatConditionsHtml = (conditions, logic) => { }); if (validConditions.length === 0) { - return '条件未设置'; + return `${t('choiceBranch.status.condition_not_set')}`; } const conditionTexts = validConditions.map(condition => { @@ -341,11 +309,11 @@ const formatConditionsHtml = (conditions, logic) => { // 获取操作符中文 - 添加操作符样式 const operatorKey = condition.operator || condition.operate; - let operatorLabel = operatorLabels[operatorKey]; + let operatorLabel = getOperatorLabel(operatorKey); // 如果没有找到映射,提供简单的后备显示 - if (!operatorLabel) { - console.warn(`[Choice显示] 未找到操作符映射: ${operatorKey}`); + if (operatorLabel === operatorKey) { + console.warn(`[Choice显示]`, t('choiceBranch.console.operator_mapping_not_found'), operatorKey); switch(operatorKey) { case 'number_less_than_or_equal': operatorLabel = '<='; @@ -386,7 +354,7 @@ const formatConditionsHtml = (conditions, logic) => { return `${leftHtml} ${operatorHtml} ${rightHtml}`; }); - const logicText = logic === 'and' ? ' ' : ' '; + const logicText = logic === 'and' ? ` ${t('choiceBranch.logic_operators.and_text')} ` : ` ${t('choiceBranch.logic_operators.or_text')} `; return conditionTexts.join(logicText); }; @@ -520,7 +488,7 @@ const handleBranchInsertNode = (event: Event, branchId: string) => {
- 其他所有情况 + {{ t('choiceBranch.tips.other_cases') }}
@@ -549,7 +517,7 @@ const handleBranchInsertNode = (event: Event, branchId: string) => {
-
点击编辑添加分支条件
+
{{ t('choiceBranch.tips.click_edit_add_branch') }}
@@ -604,7 +572,7 @@ const handleBranchInsertNode = (event: Event, branchId: string) => { border-radius: 8px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); min-width: 320px; - transition: all 0.3s ease; + transition: transform 0.3s ease, box-shadow 0.3s ease; &:hover { border-color: #6395fd; @@ -1091,14 +1059,14 @@ const handleBranchInsertNode = (event: Event, branchId: string) => { } } -// 深色主题支持 +// 深色主题支持 - 使用用户指定的颜色规范 .dark { .choiceBranchNodeStyle { - background: #1f2937; - border-color: #374151; + background: #353f58 !important; + border: 2px solid rgba(255, 255, 255, 0.08) !important; .title .label { - color: #f3f4f6; + color: #ffffff !important; } .branchItem { diff --git a/src/views/createapp/components/workFlowConfig/CodeNodeDrawer.vue b/src/views/createapp/components/workFlowConfig/CodeNodeDrawer.vue index f3c84466074a549992bd8a57606390a90ce5ea7b..aed04e7c427fc45ef1be74855ecc54b50bd057cd 100644 --- a/src/views/createapp/components/workFlowConfig/CodeNodeDrawer.vue +++ b/src/views/createapp/components/workFlowConfig/CodeNodeDrawer.vue @@ -28,22 +28,22 @@ - 基本信息 + {{ $t('flow.node_config.basic_info') }}
- + - - - - + + + + @@ -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 0000000000000000000000000000000000000000..473ea9c30166aac7b28077c2620c80faa6d5ec5c --- /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 a72bb42dd40ace25b91b7a1db02707607eb18787..a6c648d6749d718d2b7d1d451ae2df527c185089 100644 --- a/src/views/createapp/components/workFlowConfig/CustomNode.vue +++ b/src/views/createapp/components/workFlowConfig/CustomNode.vue @@ -441,4 +441,74 @@ const handleSourceHandleLeave = () => { visibility: visible; transition: opacity 0.2s ease 0s, visibility 0s ease 0s; /* 立即显示 */ } + +/* 黑夜模式支持 - 使用用户指定的颜色规范 */ +.dark .customNodeStyle { + 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; + + &:hover { + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4) !important; + } + + .nodeBox { + background: #353f58 !important; + + .title { + .label { + color: #ffffff !important; + } + } + + &:hover { + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25) !important; + } + } + + .nodeFooter { + background: rgba(255, 255, 255, 0.05) !important; + border-top-color: rgba(255, 255, 255, 0.08) !important; + + .nodeIdText { + color: rgba(255, 255, 255, 0.6) !important; + } + + .copydocument { + color: rgba(255, 255, 255, 0.6) !important; + + &:hover { + color: rgba(255, 255, 255, 0.8) !important; + } + } + } + + .deleteButton { + background-color: #e53e3e; + color: #ffffff; + box-shadow: 0 4px 12px rgba(229, 62, 62, 0.4); + + &:hover { + background-color: #c53030; + box-shadow: 0 6px 20px rgba(229, 62, 62, 0.5); + } + + &:active { + background-color: #9c2222; + } + } + + .handle-plus-button .plus-icon { + background: #353f58; + border-color: #63b3ed; + color: #63b3ed; + box-shadow: 0 3px 8px rgba(99, 179, 237, 0.4); + + &:hover { + background: #63b3ed; + 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 c3a238fe33742da5c3de26f5455f93845ae013b0..3ec36bb92fd8ddde8e9c0927831c5a0dae4ca961 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') }}
-
+
@@ -247,7 +250,7 @@ watch( box-sizing: border-box; border-radius: 10px; cursor: pointer; // 添加鼠标指针 - transition: all 0.3s ease; // 添加过渡效果 + transition: transform 0.3s ease, box-shadow 0.3s ease; // 只对变换和阴影添加过渡效果 &:hover { transform: translateY(-1px); @@ -454,4 +457,51 @@ watch( visibility: visible; transition: opacity 0.2s ease 0s, visibility 0s ease 0s; /* 立即显示 */ } + +/* 黑夜模式支持 - 使用用户指定的颜色规范 */ +.dark .nodeSaEBorder { + background: #353f58 !important; + border: 2px solid rgba(255, 255, 255, 0.08) !important; + + .nodeSaEBorderBox { + background: #353f58 !important; + } + + .saEText { + color: #ffffff !important; + } + + .variableItem { + background: rgba(99, 179, 237, 0.15) !important; + border-color: rgba(99, 179, 237, 0.3) !important; + } + + .variablePrefix { + color: #63b3ed !important; + } + + .label { + color: #ffffff !important; + } + + .iconStyle { + color: #ffffff !important; + } + + .variable-count { + color: #ffffff; + background: rgba(99, 179, 237, 0.2); + } + + .handle-plus-button .plus-icon { + color: #63b3ed; + background: #353f58; + border-color: #63b3ed; + + &:hover { + background: #63b3ed; + color: #353f58; + } + } +} diff --git a/src/views/createapp/components/workFlowConfig/DirectReplyDrawer.vue b/src/views/createapp/components/workFlowConfig/DirectReplyDrawer.vue index 6ace1af81222e064fd80469aa1093c3d83558c15..9269c1885b28a2b99a271068b2704fc0ae603d23 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 3f6147021923d6032839d1d3e89f72da0a6cd586..370a5dc4d389539ff20f659dde67de411f60d385 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 b47cb5dc6a6819f2391c2c5a1b18958d5b2a9cb8..5a0d87af2103202f521ab6175731bddb8dffb2d9 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 @@ @@ -3177,7 +3498,7 @@ export default { border-radius: 12px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); min-width: 320px; - transition: all 0.2s ease; + transition: transform 0.2s ease, box-shadow 0.2s ease; } .loopNodeStyle.node-selected { @@ -4195,38 +4516,42 @@ export default { box-shadow: 0 2px 8px rgba(99, 149, 253, 0.3); } -/* 深色主题支持 */ +/* 深色主题支持 - 使用用户指定的颜色规范 */ .dark .loopNodeStyle { - background: #1f2937; - border-color: #374151; + background: #353f58 !important; + border: 2px solid rgba(255, 255, 255, 0.08) !important; } .dark .title .label { - color: #f3f4f6; + color: #ffffff !important; } .dark .title .iconStyle { - color: #a5b4fc; + color: #ffffff !important; } .dark .loopInfo { - background: #374151; - border-color: #4b5563; + background: #353f58 !important; + border-color: rgba(255, 255, 255, 0.08) !important; } .dark .infoLabel { - color: #9ca3af; + color: #ffffff !important; } .dark .infoValue { - background: #4b5563; - border-color: #6b7280; - color: #f3f4f6; + background: #353f58 !important; + border-color: rgba(255, 255, 255, 0.08) !important; + color: #ffffff !important; } .dark .embeddedFlowCanvas { - background: #374151; - border-color: #4b5563; + background: #353f58 !important; + border-color: rgba(255, 255, 255, 0.08) !important; +} + +.dark .vue-flow-like-canvas { + background: #262626 !important; } /* 深色主题下的Handle样式 */ @@ -4242,6 +4567,129 @@ export default { border-color: #374151; } +/* 深色主题下的子画布节点样式 */ +.dark .embeddedCustomNodeStyle { + background: #353f58 !important; + border: 2px solid rgba(255, 255, 255, 0.08) !important; + + .nodeBox { + background: #353f58 !important; + } + + .title .label { + color: #ffffff !important; + } + + .nodeFooter { + background: rgba(255, 255, 255, 0.05) !important; + border-top-color: rgba(255, 255, 255, 0.08) !important; + + .nodeIdText { + color: rgba(255, 255, 255, 0.6) !important; + } + + .copydocument { + color: rgba(255, 255, 255, 0.6) !important; + + &:hover { + color: rgba(255, 255, 255, 0.8) !important; + } + } + } +} + +/* 深色主题下的开始/结束节点样式 */ +.dark .embeddedCustomNodeStyle.vue-flow__node-start, +.dark .embeddedCustomNodeStyle.vue-flow__node-end, +.dark .embeddedCustomNodeStyle.vue-flow__node-break, +.dark .embeddedCustomNodeStyle.vue-flow__node-continue { + background: #353f58 !important; + border: 2px solid rgba(255, 255, 255, 0.08) !important; + + .nodeSaEBorderBox { + background: #353f58 !important; + } + + .saEText { + color: #ffffff !important; + } +} + +/* 深色主题下的Choice节点样式 */ +.dark .choiceBranchNodeStyle { + background: #353f58 !important; + border: 2px solid rgba(255, 255, 255, 0.08) !important; + + .title .label { + color: #ffffff !important; + } + + .branchItem { + background: #353f58 !important; + border-color: rgba(255, 255, 255, 0.08) !important; + + .caseLabel { + color: rgba(255, 255, 255, 0.8) !important; + } + + .branchCondition { + color: #ffffff !important; + + .defaultText { + color: rgba(255, 255, 255, 0.6) !important; + } + } + } + + .emptyBranches { + background: #353f58 !important; + border-color: rgba(255, 255, 255, 0.08) !important; + + .emptyText { + color: rgba(255, 255, 255, 0.6) !important; + } + } +} + +/* 深色主题下的VariableAssign节点样式 */ +.dark .embeddedCustomNodeStyle .operations-list { + .operation-item { + background: rgba(99, 179, 237, 0.15) !important; + border-color: rgba(99, 179, 237, 0.3) !important; + + .variable-name { + color: #63b3ed !important; + } + + .operation-type { + background: rgba(255, 255, 255, 0.1) !important; + color: rgba(255, 255, 255, 0.8) !important; + } + } +} + +.dark .embeddedCustomNodeStyle .no-operations { + .placeholder-text { + color: rgba(255, 255, 255, 0.6) !important; + } +} + +/* 深色主题下的子画布Handle样式 */ +.dark .vue-flow-like-canvas .vue-flow__nodes .vue-flow__node.embeddedCustomNodeStyle .vue-flow__handle { + background: #6395fd !important; + border-color: #374151 !important; +} + +.dark .vue-flow-like-canvas .vue-flow__nodes .vue-flow__node.embeddedCustomNodeStyle .vue-flow__handle.connecting { + background: #4f46e5 !important; + border-color: #374151 !important; +} + +.dark .vue-flow-like-canvas .vue-flow__nodes .vue-flow__node.embeddedCustomNodeStyle:hover .vue-flow__handle { + background: #4f46e5 !important; + border-color: #374151 !important; +} + /* 禁用Choice节点内部的nodeBox点击事件,让容器统一处理 */ .choice-node-no-click .nodeBox { pointer-events: none; @@ -4760,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 38376a018c6601588f555c7b12b679749e740101..b13ffff62f331e4d96e362af89ff63acd739258f 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 0000000000000000000000000000000000000000..40fcc9a43705c74cd994c492bb2326b2c0104ddd --- /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 372205970e8bb5c0ca33c641decb092c62a21647..50d82f0f4e37e5ae16cef2da29a8fcd6dd08a1aa 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 0000000000000000000000000000000000000000..23f34d633efc78a0a817baf1415144d5dc29990b --- /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 0000000000000000000000000000000000000000..d99ddf253df36a2992270a63cc3855fdefdbf4dc --- /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 0000000000000000000000000000000000000000..a24d74c213987852f3d3792590d4cc77fd0effdc --- /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 2c1e7a6523420f5162b98dd6eba5181ee48d3a3a..1175bf5affdf5bb694fd09beb1c79412c1a13c0c 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 b8421b2fc4530466c1cb6cae052eca99283161bf..d490f74254cdb3e1bc2c973b136f6befd3e86198 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(
@@ -268,11 +271,11 @@ watch( border-radius: 8px; min-width: 200px; cursor: pointer; - transition: all 0.3s ease; + transition: transform 0.3s ease, box-shadow 0.3s ease; } &.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: #2d3748; - border-color: #4a5568; - - &: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: #e2e8f0; - } + .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 74a98aedef58b956702400e2ecd91beeef524df9..570f5d5030f290d39f4905851a0d1f74c333ede8 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,8 +202,8 @@ 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 2a7eed44c8e4fc6ee068348570e50ea2581f1093..26e155cddd0a5932749e897bd2ad34da1eda423e 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 76555e1d5c7e6e9fa05970309ab3710b10b26350..ca75e658aee4dcc6541c1f28bac24d77ea3c4180 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 f097b5f02425cd8b1367e6b97f4d27f8a08922f5..c4d80be81add356bbe391514a04fed3104b713f5 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/createapp/components/workFlowConfig/workFlowDialog.vue b/src/views/createapp/components/workFlowConfig/workFlowDialog.vue index c69cb3b081499742aa9d6440eb4990c64c96916d..52fe8e9c43c9d1dc6172c31f45842148e994e61d 100644 --- a/src/views/createapp/components/workFlowConfig/workFlowDialog.vue +++ b/src/views/createapp/components/workFlowConfig/workFlowDialog.vue @@ -132,7 +132,7 @@ const handleSubmit = (formEl: FormInstance | undefined) => { apiId: 'startId', nodeId: 'Empty', serviceId: 'start', - name: '开始', + name: i18n.global.t('main.start'), callId: 'start', description: 'startNode', editable: false, @@ -147,7 +147,7 @@ const handleSubmit = (formEl: FormInstance | undefined) => { apiId: 'endId', nodeId: 'Empty', serviceId: 'end', - name: '结束', + name: i18n.global.t('main.end'), callId: 'end', description: 'endNode', editable: false, diff --git a/src/views/createapp/components/workFlowConfig/yamlEditDrawer.vue b/src/views/createapp/components/workFlowConfig/yamlEditDrawer.vue index 5360c68d5af93514daa06cfabf36c08d69e1e418..2a56ed57232bfc5c6e594ccb671e483aea656290 100644 --- a/src/views/createapp/components/workFlowConfig/yamlEditDrawer.vue +++ b/src/views/createapp/components/workFlowConfig/yamlEditDrawer.vue @@ -158,9 +158,19 @@ const yamlExpress = ref([ }, ]); const yamlBaseInfoRule = ref({ - name: [{ required: true, message: '请输入工作流名称', trigger: 'blur' }], + name: [ + { + required: true, + message: i18n.global.t('flow.enterWorkflowName'), + trigger: 'blur', + }, + ], description: [ - { required: true, message: '请输入工作流描述', trigger: 'blur' }, + { + required: true, + message: i18n.global.t('flow.enterWorkflowDesc'), + trigger: 'blur', + }, ], }); const activeName = ref([ diff --git a/src/views/createapp/index.vue b/src/views/createapp/index.vue index 987f71d3b73cbfb9ce22ff72ccb4f492b18e096e..45819b263359693a6c4103476782a16b04bf2dc5 100644 --- a/src/views/createapp/index.vue +++ b/src/views/createapp/index.vue @@ -39,6 +39,10 @@ onMounted(() => { if (currentAppType) { createAppType.value = currentAppType; } + // 如果是agent直接可以发布 + if(appType.value === 'agent') { + publishValidate.value = true; + } }); onUnmounted(() => { @@ -142,6 +146,7 @@ const saveApp = async (type: 'agent' | 'flow') => { appType: type, icon: formData.icon, name: formData.name, + llm: formData.model, description: formData.description, dialogRounds: formData.dialogRounds, mcpService: formData.mcps, @@ -171,6 +176,10 @@ const getPublishStatus = (status) => { } }; +const saveConfig = () => { + saveApp(appType.value as 'agent' | 'flow'); +}; + const handleJumperAppCenter = () => { router.push('/app?to=createdByMe'); }; @@ -256,6 +265,7 @@ function onDebugSuccess(status: boolean) { v-else-if="appType === 'agent'" :handleValidateContent="handleValidateContent" :onDebug="onDebugSuccess" + :saveConfig="saveConfig" />
-import { computed, onMounted, ref, watch } from 'vue'; +import { computed, onMounted, ref, watch, nextTick } from 'vue'; import DialoguePanel from 'src/components/dialoguePanel/DialoguePanel.vue'; import UploadFileGroup from 'src/components/uploadFile/UploadFileGroup.vue'; import InitalPanel from 'src/views/dialogue/components/InitalPanel.vue'; @@ -14,6 +14,8 @@ import { UploadTypeName, UploadStatus } from 'src/components/uploadFile/type'; import CommonFooter from 'src/components/commonFooter/CommonFooter.vue'; import { api } from 'src/apis'; import { useHistorySessionStore } from 'src/store/historySession'; +import { ElMessage } from 'element-plus'; +import { useAccountStore } from 'src/store'; import { successMsg, errorMsg } from 'src/components/Message'; import i18n from 'src/i18n'; import DialogueVariablePanel from './DialogueVariablePanel.vue'; @@ -28,6 +30,9 @@ export interface DialogueSession { createAppForm: any; } +const autoExecuteRef = ref(false); +const { userinfo } = storeToRefs(useAccountStore()); + const props = withDefaults(defineProps(), {}); const Form = ref(props.createAppForm); const AppForm = ref(props.createAppForm); @@ -36,11 +41,21 @@ const { pausedStream } = useSessionStore(); const themeStore = useChangeThemeStore(); const isCreateApp = ref(props?.isCreateApp); const selectedLLM = ref({}); +const curFileList = ref>([]); const handleChangeMode = (val: string) => { selectedLLM.value = val; }; const llmOptions = ref([]); const { app } = storeToRefs(useSessionStore()); +const showFileSource = ref(false); + +const closeShowFileSource = () => { + showFileSource.value = false; +}; +const openShowFileSource = (fileList: Array) => { + showFileSource.value = true; + curFileList.value = fileList; +}; // 对话输入内容 const dialogueInput = ref(''); @@ -55,8 +70,9 @@ const currentFlowId = ref(''); // 对话列表 const { sendQuestion } = useSessionStore(); -const { conversationList, isAnswerGenerating, dialogueRef } = - storeToRefs(useSessionStore()); +const { conversationList, isAnswerGenerating, dialogueRef } = storeToRefs( + useSessionStore(), +); const { generateSession } = useHistorySessionStore(); const { currentSelectedSession } = storeToRefs(useHistorySessionStore()); @@ -291,7 +307,7 @@ const handleKeydown = (event: KeyboardEvent) => { * * @param item */ -const getItem = (item: ConversationItem, field: string): T | undefined => { +const getItem = (item: ConversationItem, field: string): T | undefined => { if (field in item) { return (item as RobotConversationItem)[field] as T; @@ -361,6 +377,7 @@ const isAllowToSend = computed(() => { // 会话切换时 watch(currentSelectedSession, async (newVal) => { if (!newVal) return; + closeShowFileSource(); const newExistList = existUploadMap.get(newVal); const newFileView = uploadViewsMap.get(newVal); let curPolling = pollingMap.get(newVal); @@ -679,7 +696,12 @@ const getProviderLLM = async () => { } }; -// 🔑 移除全局收集器定义,改为使用per-QA的files字段 +const autoExecuteChange = async (value) => { + autoExecuteRef.value = value; + await nextTick(); + Object.assign(userinfo.value, { auto_execute: value }); + api.updateUserInfo({ autoExecute: value }); +}; onMounted(() => { // 数据初始化 @@ -687,19 +709,28 @@ onMounted(() => { if (!inputRef.value) return; inputRef.value.focus(); getProviderLLM(); - - // 🔑 移除全局收集器初始化,改为使用per-QA的files字段 - + // 🔑 重要修复:只在没有现有变量配置时才加载 // 避免重复加载导致覆盖用户已输入的变量状态 if (user_selected_app.value && conversationVariables.value.length === 0) { loadConversationVariables(); } else if (user_selected_app.value && conversationVariables.value.length > 0) { } - - }); +watch( + [userinfo], + () => { + if (userinfo.value.auto_execute) { + autoExecuteRef.value = userinfo.value.auto_execute; + } + }, + { + immediate: true, + deep: true, + }, +); + watch(selectLLM, (newValue) => { if (newValue) { selectedLLM.value.modalName = newValue.modelName; @@ -757,7 +788,7 @@ const getappMode = (appId: string) => { watch( () => user_selected_app, (val, oldVal) => { - if (app.value) { + if (app.value.appId) { user_selected_app.value = app.value.appId; } if (user_selected_app.value && !isCreateApp.value) { @@ -824,6 +855,89 @@ watch( deep: true, }, ); + +function downloadFun(url: string) { + const token = localStorage.getItem('ECSESSION'); + if (!token) { + ElMessage.error(`Token is not available yet`); + return; + } + fetch(url, { + headers: { + Authorization: `Bearer ${token}`, + }, + }) + .then((response) => { + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + // 提取 Content-Disposition 头部 + const contentDisposition = response.headers.get('Content-Disposition'); + let fileName = 'default-filename'; + // 解析文件名(处理编码及格式) + if (contentDisposition) { + const fileNameMatch = contentDisposition.match( + /filename\*?=(?:UTF-8'')?"?([^";]+)"?/i, + ); + if (fileNameMatch && fileNameMatch[1]) { + fileName = decodeURIComponent(fileNameMatch[1].replace(/"/g, '')); + } + } + return response.blob().then((blob) => ({ blob, fileName })); + }) + .then(({ blob, fileName }) => { + const a = document.createElement('a'); + a.href = URL.createObjectURL(blob); + a.download = fileName; + a.style.display = 'none'; + a.click(); + URL.revokeObjectURL(a.href); + }) + .catch((error) => { + ElMessage.error(`下载失败: ${error}`); + }); +} +const downLoadSourceFile = (file: any) => { + if (!file) return; + const url = `${window.origin}/witchaind/api/doc/download?docId=${file.documentId}`; + downloadFun(url); +}; + +const formatDate = (timestamp: number) => { + const date = new Date(timestamp * 1000); // 秒转毫秒 + const year = date.getFullYear(); + const month = String(date.getMonth() + 1).padStart(2, '0'); + const day = String(date.getDate()).padStart(2, '0'); + return `${year}-${month}-${day}`; +}; +/** + * @description 处理并过滤文件列表,将文件列表中的字段名统一为指定格式 + * @param {ConversationItem} ConversationItem - 对话项对象 + * @param {string} str - 字段名 + * @returns {Array} 格式化后的文件列表 + */ +const getFormatFileList = (ConversationItem, str) => { + let fileList: any = getItem(ConversationItem, str); + if (!fileList || fileList?.length === 0) return; + let newFileList: any = []; + fileList?.forEach((file) => { + if (file.associated === 'answer') { + newFileList.push({ + documentId: file._id, + documentName: file.name, + documentAbstract: file.abstract, + documentType: file.type, + documentSize: file.size, + sourceUrl: file.sourceUrl, + documentOrder: file.order, + createdAt: file.created_at, + documentAuthor: file.author, + }); + } + }); + return newFileList; +}; +