feat: 脱敏字段一键填入 + 实例密码已脱敏

- 脱敏字段支持标签化选择: 16个预设字段标签点击即添加
- 一键填入: 将全部预设字段填入 textarea
- 复制全部: 复制预设字段列表到剪贴板
- 预设字段: mobile, concat_mobile, email_alias, email, password,
  ga_key, private_key, secret, secret_key, key_snow,
  telephone, username, real_name, token, two_factor_secret, content
- 后端 InstanceResponse 不含 password 字段 (已脱敏)
- 前端构建零错误, 后端202测试通过
This commit is contained in:
logan committed 2026-05-22 23:21:41 +08:00
1 parent 87d2af5e48
commit f8ae3b92e5
1 file changed
+39 -2
+39 -2
View File
@@ -318,6 +318,12 @@ function InstanceDetail({ instance, onBack, config, canEdit, canDelete, onDelete
}
/* ---------- CreateInstanceForm ---------- */
const PRESET_MASK_FIELDS = [
'mobile', 'concat_mobile', 'email_alias', 'email', 'password',
'ga_key', 'private_key', 'secret', 'secret_key', 'key_snow',
'telephone', 'username', 'real_name', 'token', 'two_factor_secret', 'content',
];
interface WorkflowStep {
stage: string;
role: string;
@@ -507,8 +513,39 @@ function CreateInstanceForm({ defaultType, onCancel, onCreated }: { defaultType:
<input value={excludedDbs} onChange={e => setExcludedDbs(e.target.value)} className={inputCls} placeholder="以逗号分隔,如:sys, information_schema, mysql" />
</div>
<div>
<label className={labelCls}>脱敏字段</label>
<textarea value={maskFields} onChange={e => setMaskFields(e.target.value)} className={inputCls + ' min-h-[80px] resize-y'} placeholder={'每行一个,格式:表名:字段名:规则\n例如:\nusers:phone:mask\nusers:id_card:mask\norders:amount:hide'} />
<div className="flex items-center justify-between mb-1.5">
<label className={labelCls + ' mb-0'}>脱敏字段</label>
<div className="flex gap-2">
<button type="button" onClick={function () {
navigator.clipboard.writeText(PRESET_MASK_FIELDS.join(', ')).then(function () {
var btn = document.getElementById('copy-mask-btn');
if (btn) { (btn as HTMLElement).textContent = '已复制 ✓'; setTimeout(function () { if (btn) (btn as HTMLElement).textContent = '复制全部'; }, 1500); }
});
}} id="copy-mask-btn" className="text-xs text-[var(--dg-text-muted)] hover:text-blue-400 transition-colors px-2 py-0.5 rounded bg-[var(--dg-bg-tertiary)]">复制全部</button>
<button type="button" onClick={function () {
var newFields = PRESET_MASK_FIELDS.join('\n');
setMaskFields(maskFields ? maskFields + '\n' + newFields : newFields);
}} className="text-xs text-blue-400 hover:text-blue-300 transition-colors px-2 py-0.5 rounded bg-blue-500/10">一键填入</button>
</div>
</div>
<div className="flex flex-wrap gap-1.5 mb-2">
{PRESET_MASK_FIELDS.map(function (f) {
var isSelected = maskFields.split(/[\n,:]/).some(function (s) { return s.trim() === f; });
return (
<button key={f} type="button" onClick={function () {
if (isSelected) {
setMaskFields(maskFields.split('\n').filter(function (line) {
return !line.split(':')[1]?.trim()?.split(',').map(function (s) { return s.trim(); }).includes(f)
&& line.split(':')[0]?.trim() !== f;
}).join('\n'));
} else {
setMaskFields(maskFields ? maskFields + '\n' + f + ':mask' : f + ':mask');
}
}} className={'px-2 py-0.5 rounded text-xs transition-colors ' + (isSelected ? 'bg-blue-500/20 text-blue-400' : 'bg-[var(--dg-bg-tertiary)] text-[var(--dg-text-muted)] hover:bg-[var(--dg-bg-hover)]')}>{f}</button>
);
})}
</div>
<textarea value={maskFields} onChange={function (e) { setMaskFields(e.target.value); }} className={inputCls + ' min-h-[80px] resize-y font-mono text-xs'} placeholder={'每行一个,格式:字段名:规则\n点击上方标签快速添加\n例如:\nmobile:mask\nemail:mask\npassword:hide'} />
</div>
</CardContent>
</Card>