From 83808cc5b7ed3f90318a436657588e67e2b0ec76 Mon Sep 17 00:00:00 2001 From: wizardchen Date: Thu, 28 May 2026 20:53:34 +0800 Subject: [PATCH] feat(frontend): improve agent and KB editor ID display and intent prompts UX Expose copyable resource IDs in edit modals and replace the intent prompt dropdown with independent toggle buttons so multi-intent selection wraps cleanly. --- frontend/src/i18n/locales/en-US.ts | 5 + frontend/src/i18n/locales/ko-KR.ts | 5 + frontend/src/i18n/locales/ru-RU.ts | 5 + frontend/src/i18n/locales/zh-CN.ts | 5 + frontend/src/views/agent/AgentEditorModal.vue | 264 ++++++++++++------ .../knowledge/KnowledgeBaseEditorModal.vue | 76 +++++ 6 files changed, 269 insertions(+), 91 deletions(-) diff --git a/frontend/src/i18n/locales/en-US.ts b/frontend/src/i18n/locales/en-US.ts index bc2c4d17..80d5669f 100755 --- a/frontend/src/i18n/locales/en-US.ts +++ b/frontend/src/i18n/locales/en-US.ts @@ -1900,6 +1900,8 @@ export default { basic: { title: 'Basic Information', description: 'Configure the knowledge base name and description', + kbId: 'Knowledge Base ID', + kbIdDesc: 'Use this ID to target the knowledge base in API integrations', typeLabel: 'Knowledge Base Type', typeDocument: 'Document-based', typeFAQ: 'FAQ Q&A', @@ -4280,9 +4282,12 @@ export default { }, intentPrompts: { title: 'Intent Prompts', + sectionDesc: 'Configure intent-specific system prompts; defaults apply when not customized', intentLabel: 'Intent', intentDescription: 'Select the intent-specific system prompt to edit', promptPlaceholder: 'Enter a custom system prompt...', + customized: 'Customized', + empty: 'No intent templates available', }, selection: { all: 'All', diff --git a/frontend/src/i18n/locales/ko-KR.ts b/frontend/src/i18n/locales/ko-KR.ts index 37310de5..a24e8651 100755 --- a/frontend/src/i18n/locales/ko-KR.ts +++ b/frontend/src/i18n/locales/ko-KR.ts @@ -2722,6 +2722,8 @@ export default { basic: { title: "기본 정보", description: "지식베이스의 이름과 설명 정보 설정", + kbId: "지식베이스 ID", + kbIdDesc: "API 연동에서 이 ID로 지식베이스를 지정할 수 있습니다", typeLabel: "지식베이스 유형", typeDocument: "문서", typeFAQ: "Q&A", @@ -4342,9 +4344,12 @@ export default { }, intentPrompts: { title: '의도 프롬프트', + sectionDesc: '쿼리 의도별 시스템 프롬프트를 설정합니다. 사용자 정의가 없으면 기본 템플릿이 적용됩니다', intentLabel: '의도', intentDescription: '편집할 의도별 시스템 프롬프트를 선택하세요', promptPlaceholder: '사용자 정의 시스템 프롬프트 입력...', + customized: '사용자 정의됨', + empty: '사용 가능한 의도 템플릿이 없습니다', }, selection: { all: '전체', diff --git a/frontend/src/i18n/locales/ru-RU.ts b/frontend/src/i18n/locales/ru-RU.ts index caad0424..09bb042b 100755 --- a/frontend/src/i18n/locales/ru-RU.ts +++ b/frontend/src/i18n/locales/ru-RU.ts @@ -2235,6 +2235,8 @@ export default { basic: { title: 'Основная информация', description: 'Укажите название и описание базы знаний', + kbId: 'ID базы знаний', + kbIdDesc: 'Используйте этот ID для указания базы знаний в API-интеграциях', typeLabel: 'Тип базы знаний', typeDocument: 'Документальная', typeFAQ: 'FAQ (вопрос-ответ)', @@ -3842,9 +3844,12 @@ export default { }, intentPrompts: { title: 'Промпты намерений', + sectionDesc: 'Настройте системные промпты для разных намерений запроса; по умолчанию используются шаблоны системы', intentLabel: 'Намерение', intentDescription: 'Выберите системный промпт для редактирования', promptPlaceholder: 'Введите пользовательский системный промпт...', + customized: 'Настроено', + empty: 'Нет доступных шаблонов намерений', }, selection: { all: 'Все', diff --git a/frontend/src/i18n/locales/zh-CN.ts b/frontend/src/i18n/locales/zh-CN.ts index fe84d91b..0f0888d0 100755 --- a/frontend/src/i18n/locales/zh-CN.ts +++ b/frontend/src/i18n/locales/zh-CN.ts @@ -2697,6 +2697,8 @@ export default { basic: { title: "基本信息", description: "设置知识库的名称和描述信息", + kbId: "知识库 ID", + kbIdDesc: "API 集成时可使用此 ID 指定知识库", typeLabel: "知识库类型", typeDocument: "文档", typeFAQ: "问答", @@ -4274,9 +4276,12 @@ export default { }, intentPrompts: { title: "意图提示词", + sectionDesc: "为不同查询意图配置专属系统提示词;未自定义时使用系统默认模板", intentLabel: "意图", intentDescription: "选择要编辑的意图专属系统提示词", promptPlaceholder: "输入自定义系统提示词...", + customized: "已自定义", + empty: "暂无可用的意图模板", }, selection: { all: "全部", diff --git a/frontend/src/views/agent/AgentEditorModal.vue b/frontend/src/views/agent/AgentEditorModal.vue index 91013d7c..3e0e1957 100644 --- a/frontend/src/views/agent/AgentEditorModal.vue +++ b/frontend/src/views/agent/AgentEditorModal.vue @@ -32,17 +32,19 @@
-

{{ $t('agent.editor.basicInfo') }}

+
+

{{ $t('agent.editor.basicInfo') }}

+ + + + + +

{{ $t('agent.editor.basicInfoDesc') }}

- -
- - {{ $t('agentEditor.builtinHint') }} -
-
@@ -50,13 +52,12 @@

{{ $t('agent.editor.agentIdDesc') }}

-
- +
+ {{ props.agent.id }} - - + +
@@ -246,60 +247,72 @@
+

{{ $t('agentEditor.intentPrompts.sectionDesc') }}

-
- {{ $t('agentEditor.intentPrompts.intentLabel') }} - - - {{ template.name || template.id }} - - - {{ currentIntentTemplateDesc - }} +
+ {{ $t('agentEditor.intentPrompts.empty') }}
+
@@ -1862,9 +1875,17 @@ const currentIntentTemplate = computed(() => ); const currentIntentTemplateDesc = computed(() => - currentIntentTemplate.value?.description || t('agentEditor.intentPrompts.intentDescription'), + currentIntentTemplate.value?.description || '', ); +const isIntentCustomized = (intentId: string) => { + const overrides = formData.value.config.intent_prompts || {}; + const override = overrides[intentId]; + if (!override?.trim()) return false; + const template = intentPromptTemplates.value.find((item) => item.id === intentId); + return override.trim() !== (template?.content || '').trim(); +}; + const filteredIntentPlaceholders = computed(() => { if (!intentPromptPopup.value.prefix) { return placeholderData.value.system_prompt; @@ -3812,6 +3833,17 @@ const handleSave = async () => { .section-header { margin-bottom: 32px; + .section-header-title { + display: flex; + align-items: center; + gap: 8px; + margin-bottom: 8px; + + h2 { + margin: 0; + } + } + h2 { font-size: 20px; font-weight: 600; @@ -3984,16 +4016,40 @@ const handleSave = async () => { } } -.agent-id-control { +.agent-id-field { display: flex; align-items: center; - gap: 8px; + gap: 4px; width: 100%; -} + padding: 6px 8px 6px 12px; + background: var(--td-bg-color-secondarycontainer); + border: 1px solid var(--td-component-stroke); + border-radius: 6px; -.agent-id-input { - flex: 1; - min-width: 0; + .agent-id-value { + flex: 1; + min-width: 0; + margin: 0; + padding: 0; + background: none; + border: none; + font-family: var(--app-font-family-mono); + font-size: 13px; + line-height: 1.5; + color: var(--td-text-color-primary); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + .agent-id-copy { + flex-shrink: 0; + color: var(--td-text-color-secondary); + + &:hover { + color: var(--td-brand-color); + } + } } .settings-footer { @@ -4516,29 +4572,53 @@ const handleSave = async () => { width: 100%; display: flex; flex-direction: column; + gap: 10px; +} + +.intent-toggle-group { + display: flex; + flex-wrap: wrap; gap: 8px; } -.intent-selector-row { - display: flex; +.intent-toggle-group :deep(.intent-toggle-btn--active) { + background-color: rgba(7, 192, 95, 0.1); + border-color: var(--td-brand-color); + color: var(--td-brand-color); + font-weight: 500; + + &:hover, + &:focus-visible { + background-color: rgba(7, 192, 95, 0.14); + border-color: var(--td-brand-color); + color: var(--td-brand-color); + } +} + +.intent-toggle-btn { + max-width: 100%; +} + +.intent-toggle-label { + display: inline-flex; align-items: center; - gap: 12px; - flex-wrap: wrap; + gap: 6px; + white-space: nowrap; } -.intent-label { - font-size: 12px; - color: var(--td-text-color-secondary); +.intent-toggle-dot { + flex-shrink: 0; + width: 5px; + height: 5px; + border-radius: 50%; + background: currentColor; } -.intent-select { - width: 150px; -} - -.intent-desc { +.intent-active-desc { + margin: 0; font-size: 12px; color: var(--td-text-color-placeholder); - line-height: 1.4; + line-height: 1.5; } // 系统提示词输入框样式 @@ -4663,22 +4743,24 @@ const handleSave = async () => { } } -// 内置智能体提示 -.builtin-agent-notice { - display: flex; +.builtin-agent-hint { + display: inline-flex; align-items: center; - gap: 8px; - padding: 12px 16px; - background: var(--td-warning-color-light); - border: 1px solid var(--td-warning-color-focus); - border-radius: 8px; - margin-bottom: 16px; - color: var(--td-warning-color); - font-size: 14px; + color: var(--td-text-color-placeholder); + font-size: 18px; + line-height: 1; + cursor: help; + transition: color 0.2s; - .t-icon { - font-size: 16px; - flex-shrink: 0; + &:hover, + &:focus-visible { + color: var(--td-warning-color); + outline: none; + } + + &:focus-visible { + border-radius: 2px; + box-shadow: 0 0 0 2px var(--td-warning-color-focus); } } diff --git a/frontend/src/views/knowledge/KnowledgeBaseEditorModal.vue b/frontend/src/views/knowledge/KnowledgeBaseEditorModal.vue index 02b66824..50ba6fc4 100644 --- a/frontend/src/views/knowledge/KnowledgeBaseEditorModal.vue +++ b/frontend/src/views/knowledge/KnowledgeBaseEditorModal.vue @@ -41,6 +41,20 @@

{{ $t('knowledgeEditor.basic.description') }}

+
+ +

{{ $t('knowledgeEditor.basic.kbIdDesc') }}

+
+ {{ props.kbId }} + + + + + +
+
+
() +const copyKbId = async () => { + const id = props.kbId + if (!id) return + + try { + if (navigator.clipboard?.writeText) { + await navigator.clipboard.writeText(id) + } else { + const textarea = document.createElement('textarea') + textarea.value = id + textarea.setAttribute('readonly', '') + textarea.style.position = 'fixed' + textarea.style.opacity = '0' + document.body.appendChild(textarea) + textarea.select() + document.execCommand('copy') + document.body.removeChild(textarea) + } + MessagePlugin.success(t('common.copied')) + } catch { + MessagePlugin.error(t('common.copyFailed')) + } +} + const currentSection = ref('basic') const saving = ref(false) const loading = ref(false) @@ -1498,6 +1536,44 @@ watch( color: var(--td-text-color-placeholder); } +.kb-id-field { + display: flex; + align-items: center; + gap: 4px; + width: 100%; + max-width: 480px; + margin-top: 8px; + padding: 6px 8px 6px 12px; + background: var(--td-bg-color-secondarycontainer); + border: 1px solid var(--td-component-stroke); + border-radius: 6px; + + .kb-id-value { + flex: 1; + min-width: 0; + margin: 0; + padding: 0; + background: none; + border: none; + font-family: var(--app-font-family-mono); + font-size: 13px; + line-height: 1.5; + color: var(--td-text-color-primary); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + .kb-id-copy { + flex-shrink: 0; + color: var(--td-text-color-secondary); + + &:hover { + color: var(--td-brand-color); + } + } +} + .granularity-radio-group { margin-top: 4px; }