diff --git a/admin/src/views/config/center/__tests__/model.test.ts b/admin/src/views/config/center/__tests__/model.test.ts index a9845e9..816de42 100644 --- a/admin/src/views/config/center/__tests__/model.test.ts +++ b/admin/src/views/config/center/__tests__/model.test.ts @@ -52,6 +52,7 @@ import { rowsToEntries, rowToSingleEntry, scopedConfigJson, + secretFieldState, snapshotGroupKeys, touchstoneSentences, utf8Bytes, @@ -745,3 +746,24 @@ describe('通用 JSON 组编辑(终审 I-1:contact/engine/misc)', () => { expect(JSON_GROUP_FIELDS.misc.map((f) => f.key)).toEqual(['name_to_region_extras']) }) }) + +describe('secretFieldState(密钥四件套展示态)', () => { + it('未配置:input 空 + 无掩码', () => { + const s = secretFieldState('', '', false) + expect(s).toMatchObject({ badge: 'unset', badgeType: 'info', badgeLabel: '未配置' }) + expect(s.placeholder).toContain('未设置') + }) + it('已配置:掩码哨兵且未动', () => { + const s = secretFieldState('', '__SET__', false) + expect(s).toMatchObject({ badge: 'configured', badgeType: 'success', badgeLabel: '已配置' }) + expect(s.placeholder).toContain('留空保持不变') + }) + it('pending-new:输入了新值(优先级最高)', () => { + const s = secretFieldState('new-secret', '__SET__', true) + expect(s).toMatchObject({ badge: 'pending-new', badgeType: 'warning', badgeLabel: '已输入新值 · 未保存' }) + }) + it('pending-clear:清除了已配置值', () => { + const s = secretFieldState('', '__SET__', true) + expect(s).toMatchObject({ badge: 'pending-clear', badgeType: 'warning', badgeLabel: '将清除 · 未保存' }) + }) +}) diff --git a/admin/src/views/config/center/model.ts b/admin/src/views/config/center/model.ts index 6e0541a..6eff903 100644 --- a/admin/src/views/config/center/model.ts +++ b/admin/src/views/config/center/model.ts @@ -23,7 +23,7 @@ * 密钥哨兵交互见 src/api/mask.ts(GET '__SET__' → 空输入 + 占位符; * 不动保存回传哨兵保持库值;「清除」= 提交空串)。 */ -import { MASKED_SENTINEL, secretToSubmit } from '@/api/mask' +import { MASKED_SENTINEL, isMaskedSentinel, secretToSubmit } from '@/api/mask' import type { ConfigGroupKey } from '@/api/config' // ---- 提示词四件套 ----------------------------------------------------------------- @@ -569,6 +569,44 @@ export function normalizeAiCredsGroup( } } +// ---- SecretField 展示态(视觉基线 §4.2;提交值仍走 secretToSubmit) ----------------- + +/** 密钥字段徽章四态:已配置 / 未配置 / 已输入新值(未保存) / 将清除(未保存) */ +export type SecretFieldBadge = 'configured' | 'unset' | 'pending-new' | 'pending-clear' + +export interface SecretFieldState { + badge: SecretFieldBadge + /** ElTag type 映射(configured=success / unset=info / pending-*=warning) */ + badgeType: 'success' | 'info' | 'warning' + badgeLabel: string + placeholder: string +} + +/** 输入值 + GET 掩码态 + 清除开关 → 徽章/占位文案(纯展示;优先级 新值 > 清除 > 已配置 > 未配置) */ +export function secretFieldState( + input: string, + masked: string, + cleared: boolean +): SecretFieldState { + if (input !== '') { + return { + badge: 'pending-new', badgeType: 'warning', badgeLabel: '已输入新值 · 未保存', placeholder: '' + } + } + if (cleared) { + return { + badge: 'pending-clear', badgeType: 'warning', badgeLabel: '将清除 · 未保存', placeholder: '' + } + } + if (isMaskedSentinel(masked)) { + return { + badge: 'configured', badgeType: 'success', badgeLabel: '已配置', + placeholder: '已设置,输入新值可更换(留空保持不变)' + } + } + return { badge: 'unset', badgeType: 'info', badgeLabel: '未配置', placeholder: '未设置,输入新值' } +} + /** * 邮件表单 → PUT config/email 整组载荷。密钥走 secretToSubmit(未动 → * 哨兵保持库值;「清除」按钮已把 maskedState 置 '',提交空串 = 显式清空)。