feat(admin): 搜索提供商下拉/多选/表格显示中文名(值仍是 ASCII id)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled

主人报障:「为什么中文搜索接口……下拉菜单里全给的英文名?你甚至不愿意带个括号
把中文名写上去」。根因不是漏写文案,而是整条链路没有「显示名」的位置:字段
声明只有 optionsFrom,候选动作回的就是 KnownNames() 的纯 id 数组。

- 后端:候选改为对象数组 {value,label};label 表与接入位表同源(键用
  NameSearXNG 等常量),未知 id 原样回。value 逐字等于 KnownNames()。
- 前端:下拉/多选「显示 label、写 value」;表格单元格按 value→label 索引显示,
  查不到回落显示原值。多选从裸输入框改成「候选开关 + 落盘值输入框」。
  解码器本就兼容裸字符串/{value,label}/{id} 三形状 ⇒ 其他插件零影响。

中文名逐字:SearXNG 本地兜底(searxng)/ 智谱 BigModel(zhipu)/
阿里云 IQS 信息查询(aliyun_iqs)/ 博查 Bocha(bocha)/
火山引擎·豆包搜索(volcengine)/ 百度千帆 AI 搜索(baidu_qianfan)。

验收(主会话复跑 + 亲自变异):go build ./... + vet 绿;search 包测试绿;
tsc --noEmit 0 错;新渲染测试 26 checks + 既有 plugin panel 55 / data 90 /
schema values 40 / channel config 70 全绿。我用 sed 把 values.ts 的
option.value 全换成 option.label ⇒ 渲染测试红(「点选把候选项的 id 追加到
降级链末尾」got="bocha, 智谱 BigModel(zhipu)")—— 这正是会把配置文件写坏的
错法;还原后 md5 一致且绿。

⚠️ 契约面变更(我在 Docs/api-client.md 登记):plugin.providers 候选响应形状
字符串数组 → 对象数组;label 仅供显示、value 才是落盘值。
⚠️ 已知边界:viewer 角色看不到 form 区块 ⇒ 表格对 viewer 仍显示裸 id(未做)。
⚠️ 真机浏览器点击未跑(项目无 jsdom)⇒ 建议刷新管理页点一次确认观感。
This commit is contained in:
AskaEth committed 2026-10-07 10:08:19 +08:00
1 parent 8051906afa
commit c46053e276
12 files changed
+500 -49

No files matched your search

+20 -2
View File
@@ -15,7 +15,7 @@
* (design/09 §5.4.2 的哨兵语义)。
*/
import type { SchemaField, SchemaFieldType } from "./fieldTypes";
import type { SchemaField, SchemaFieldType, SchemaOption } from "./fieldTypes";
/** 表单值一律以字符串承载(与渠道配置表单同一口径):控件层只认 string,
* 提交时再按声明的 type 还原成后端要的类型。 */
@@ -29,6 +29,24 @@ export function fieldLabel(f: SchemaField): string {
return label !== "" ? label : f.key;
}
/** 多选字段值的分隔口径(逗号 / 中文逗号 / 换行)。**只此一处**:装载、提交解析与
* 「点选候选开关」三处共用,各写一份就会出现「面板看着有值、后端收到空数组」。 */
export function splitListValue(text: string): string[] {
return text.split(/[,,\n]/).map((s) => s.trim()).filter(Boolean);
}
/** 候选开关的取值:点一下把候选项的 **`value`(英文 id)** 加入 / 移出多选值。
*
* **写进去的必须是 `value`,不是 `label`(中文显示名)** —— 这个字段的值会落进
* `web-search.json` 的降级链,后端按名字取 provider,写中文名等于写坏配置。参数收整个
* 候选项(只读它的 `value`)就是为了让这条分离在函数签名上看得见。
* 顺序 = 加入顺序(降级链按序生效),因此从值末尾追加、移出时保持其余相对次序。 */
export function toggleListOption(text: string, option: SchemaOption): string {
const items = splitListValue(text);
const next = items.includes(option.value) ? items.filter((x) => x !== option.value) : [...items, option.value];
return next.join(", ");
}
/** 敏感字段:读侧永不回显明文,写侧留空 = 不改动(design/09 §5.4.2)。 */
export function isSensitive(f: SchemaField): boolean {
return f.type === "secret";
@@ -129,7 +147,7 @@ export function parseFieldValue(f: SchemaField, raw: string): { ok: true; value:
case "switch":
return { ok: true, value: text === "true" };
case "multiSelect":
return { ok: true, value: text.split(/[,,\n]/).map((s) => s.trim()).filter(Boolean) };
return { ok: true, value: splitListValue(text) };
case "kv": {
let parsed: unknown;
try {