diff --git a/admin/src/views/config/center/ContactFormPane.vue b/admin/src/views/config/center/ContactFormPane.vue new file mode 100644 index 0000000..e691e7b --- /dev/null +++ b/admin/src/views/config/center/ContactFormPane.vue @@ -0,0 +1,203 @@ + + + + + + + + + + + + + + + + 邮件通知使用的语言(暂未生效——引擎接线排期中) + + + + + + + 出问题?回滚 → + + + + + 保存 + + + + 重置出厂… + + + + + + + + diff --git a/admin/src/views/config/center/EngineFormPane.vue b/admin/src/views/config/center/EngineFormPane.vue new file mode 100644 index 0000000..2528239 --- /dev/null +++ b/admin/src/views/config/center/EngineFormPane.vue @@ -0,0 +1,236 @@ + + + + + + + + + + + + + + + + + + + + 产品字段 → 会话上下文种子键的映射;空行保存时自动剔除,清空全部行 = + 提交空映射。 + + + + + + + + 出问题?回滚 → + + + + + 保存 + + + + 重置出厂… + + + + + + + + diff --git a/admin/src/views/config/center/JsonGroupPane.vue b/admin/src/views/config/center/JsonGroupPane.vue deleted file mode 100644 index ec4113d..0000000 --- a/admin/src/views/config/center/JsonGroupPane.vue +++ /dev/null @@ -1,254 +0,0 @@ - - - - - - - {{ RESET_GROUP_LABELS[groupKey] }}({{ groupKey }} 组 · 整组 JSON 编辑) - - - 保存即写新版本并热生效 - - 重置出厂 - - - - - - - - - - - {{ field.key }} - {{ field.desc }} - - - - - - - - - 保存(整组提交,热生效) - - - - - - - - diff --git a/admin/src/views/config/center/KeyValueEditor.vue b/admin/src/views/config/center/KeyValueEditor.vue new file mode 100644 index 0000000..7c72946 --- /dev/null +++ b/admin/src/views/config/center/KeyValueEditor.vue @@ -0,0 +1,87 @@ + + + + + + → + + + 删除 + + + + + 添加一行 + + + + + + + diff --git a/admin/src/views/config/center/MiscFormPane.vue b/admin/src/views/config/center/MiscFormPane.vue new file mode 100644 index 0000000..13d4824 --- /dev/null +++ b/admin/src/views/config/center/MiscFormPane.vue @@ -0,0 +1,176 @@ + + + + + + + + + + 出问题?回滚 → + + + + + 保存 + + + + 重置出厂… + + + + + + + + diff --git a/admin/src/views/config/center/__tests__/model.test.ts b/admin/src/views/config/center/__tests__/model.test.ts index 66f13ad..a2359bc 100644 --- a/admin/src/views/config/center/__tests__/model.test.ts +++ b/admin/src/views/config/center/__tests__/model.test.ts @@ -44,8 +44,6 @@ import { formatKB, formatVersionTime, initWallEdit, - JSON_GROUP_FIELDS, - jsonGroupDraft, miscToForm, miscToPayload, normalizeAiCredsGroup, @@ -53,7 +51,6 @@ import { normalizePromptsGroup, normalizePushCredsGroup, parsePatternEntry, - parseJsonGroupDraft, poolLanguages, promptByteInfo, promptsOverCap, @@ -724,43 +721,6 @@ describe('scopedConfigJson / snapshotGroupKeys(两版对比的范围视图)' }) }) -describe('通用 JSON 组编辑(终审 I-1:contact/engine/misc)', () => { - it('jsonGroupDraft:组值 → 2 空格缩进等宽稿;null/undefined → 空对象', () => { - expect(jsonGroupDraft({ cc_completion: false })).toBe( - '{\n "cc_completion": false\n}' - ) - expect(jsonGroupDraft(null)).toBe('{}') - expect(jsonGroupDraft(undefined)).toBe('{}') - }) - - it('parseJsonGroupDraft:语法错误 / 非对象顶层 → 带原因拒绝;对象通过', () => { - const bad = parseJsonGroupDraft('{ "a": ') - expect(bad.ok).toBe(false) - if (!bad.ok) expect(bad.error).toContain('JSON 语法错误') - const notObject = parseJsonGroupDraft('[1, 2]') - expect(notObject.ok).toBe(false) - if (!notObject.ok) expect(notObject.error).toContain('顶层须为 JSON 对象') - const ok = parseJsonGroupDraft('{"cc_completion": false}') - expect(ok.ok).toBe(true) - if (ok.ok) expect(ok.value).toEqual({ cc_completion: false }) - }) - - it('JSON_GROUP_FIELDS:三组字段说明齐备(键与 DEFAULT_CONFIG 同名)', () => { - expect(Object.keys(JSON_GROUP_FIELDS).sort()).toEqual(['contact', 'engine', 'misc']) - expect(JSON_GROUP_FIELDS.contact.map((f) => f.key)).toEqual([ - 'cc_completion', - 'email_light_ask', - 'template_languages' - ]) - expect(JSON_GROUP_FIELDS.engine.map((f) => f.key)).toEqual([ - 'rate_limit_per_hour', - 'ws_conn_limit', - 'context_seed_keys' - ]) - expect(JSON_GROUP_FIELDS.misc.map((f) => f.key)).toEqual(['name_to_region_extras']) - }) -}) - describe('secretFieldState(密钥四件套展示态)', () => { it('未配置:input 空 + 无掩码', () => { const s = secretFieldState('', '', false) diff --git a/admin/src/views/config/center/index.vue b/admin/src/views/config/center/index.vue index db44268..a0a3ee5 100644 --- a/admin/src/views/config/center/index.vue +++ b/admin/src/views/config/center/index.vue @@ -30,6 +30,10 @@ ConfigCard(表单双列 grid + footer 回滚/备注/保存 + danger 重置行); 预览卡头加副标题;CredsPane @navigate 接线 onGroupSelect; 8 组全实装后 v-else 占位死分支剪除(activeGroupLabel/ALL_NAV_ITEMS 同清) + - Task 13 表单化(feature/config-ux Batch 3):contact/engine/misc 三组 + 退役通用 JSON 整组编辑(旧 JsonGroup 组件删除),换 ContactFormPane / + EngineFormPane / MiscFormPane 三表单卡(ConfigCard 基线 + KeyValueEditor + 行编辑——engine context_seed_keys / misc name_to_region_extras 共用) - 挂件实时预览:**本地简化预览组件**(标题/颜色气泡/头像/话题/占位的 静态模拟,零后端依赖、即时反映表单值;真机预览留给嵌入代码上真站) - 嵌入代码:域名取租户详情 sites[].domains(超管可枚举全租户,租户管理 @@ -477,8 +481,8 @@ - - + + 重试 - + + + + + + + + 重试 + + + + + + + + + + 重试 + + @@ -530,9 +578,10 @@ import PromptsPane from './PromptsPane.vue' import WallPane from './WallPane.vue' import CredsPane from './CredsPane.vue' - import JsonGroupPane from './JsonGroupPane.vue' + import ContactFormPane from './ContactFormPane.vue' + import EngineFormPane from './EngineFormPane.vue' + import MiscFormPane from './MiscFormPane.vue' import VersionsPane from './VersionsPane.vue' - import type { JsonGroupKey } from './model' import { confirmAndResetGroup } from './reset' defineOptions({ name: 'ConfigCenter' }) @@ -587,16 +636,6 @@ sub: '改坏了?这里一键回滚' } - /** 通用 JSON 组(终审 I-1:contact/engine/misc 走 JsonGroupPane 一份实现) */ - const JSON_GROUPS = new Set(['contact', 'engine', 'misc']) - - /** 模板绑定用的收窄视图(JSON 组分支下恒为该三键之一) */ - const activeJsonGroup = computed(() => - JSON_GROUPS.has(activeGroup.value) - ? (activeGroup.value as JsonGroupKey) - : 'contact' - ) - const activeGroup = ref('brand') function onGroupSelect(index: string): void { diff --git a/admin/src/views/config/center/model.ts b/admin/src/views/config/center/model.ts index 7a4f679..a9ae524 100644 --- a/admin/src/views/config/center/model.ts +++ b/admin/src/views/config/center/model.ts @@ -873,68 +873,6 @@ export function scopedConfigJson( return JSON.stringify(view, null, 2) } -// ---- 通用 JSON 组编辑(Phase 3 终审 I-1:contact / engine / misc) ------------------- - -/** 走通用 JSON pane 的组(无密钥字段、结构低频变更——表单化收益低) */ -export type JsonGroupKey = 'contact' | 'engine' | 'misc' - -/** 组取值 → 编辑器初稿(null 组 = 空对象;2 空格缩进等宽展示) */ -export function jsonGroupDraft( - group: Record | null | undefined -): string { - return JSON.stringify(group ?? {}, null, 2) -} - -/** 编辑器解析结果:ok=false 时 error 带可直接展示的中文原因 */ -export type JsonDraftParse = - | { ok: true; value: Record } - | { ok: false; error: string } - -/** 编辑稿预检:JSON 语法 + 顶层须为对象(后端 _check_* 类型校验兜底) */ -export function parseJsonGroupDraft(text: string): JsonDraftParse { - let value: unknown - try { - value = JSON.parse(text) - } catch (error) { - const detail = error instanceof Error ? error.message : String(error) - return { ok: false, error: `JSON 语法错误——${detail}` } - } - if (typeof value !== 'object' || value === null || Array.isArray(value)) { - return { ok: false, error: '顶层须为 JSON 对象({ ... })——整组替换语义' } - } - // typeof 收窄只到 object(无索引签名)——JSON.parse 产物必为普通对象,断言收形 - return { ok: true, value: value as Record } -} - -/** 三组的字段说明(pane 顶部展示;事实源 app/platform/defaults.py) */ -export const JSON_GROUP_FIELDS: Record< - JsonGroupKey, - Array<{ key: string; desc: string }> -> = { - contact: [ - { key: 'cc_completion', desc: 'bool——cc 区号补全判定树开关' }, - { key: 'email_light_ask', desc: 'bool——dual-contact 分步双要的邮箱轻补' }, - { - key: 'template_languages', - desc: 'string[]——邮件模板语言清单(运行期接线归 Phase 4,清理单 #8)' - } - ], - engine: [ - { key: 'rate_limit_per_hour', desc: '正整数——聊天限流(次/小时/IP)' }, - { key: 'ws_conn_limit', desc: '正整数——每 IP WebSocket 连接上限' }, - { - key: 'context_seed_keys', - desc: '映射——产品字段 → 会话上下文种子键(如 product_name → app_product_name)' - } - ], - misc: [ - { - key: 'name_to_region_extras', - desc: '映射——国名 → ISO 区域码扩展(resolve_region_code 消费)' - } - ] -} - // ---- contact / engine / misc 表单化(spec §5.4-5.7;JsonGroup 退役配套) ---------------- export interface KeyValueRow { @@ -956,6 +894,7 @@ export function validateKeyValueRows(rows: KeyValueRow[], label: string): string return errors } +/** 行→映射:空键行整行剔除(半行不报错,校验另有重复键拦截) */ function rowsToRecord(rows: KeyValueRow[]): Record { const out: Record = {} for (const row of rows) { @@ -965,6 +904,7 @@ function rowsToRecord(rows: KeyValueRow[]): Record { return out } +/** 映射→行:非字符串值静默剔除(后端 schema 使其不可达——防御读取) */ function recordToRows(value: unknown): KeyValueRow[] { const src = asRecord(value) ?? {} return Object.entries(src)
邮件通知使用的语言(暂未生效——引擎接线排期中)
+ 产品字段 → 会话上下文种子键的映射;空行保存时自动剔除,清空全部行 = + 提交空映射。 +