import { Icon } from "../../components/Icon"; import { type PlatformField } from "./channelFields"; import { modeHint, modeSummary, requiredFieldKeys, visibleFields } from "./channelFieldRules"; import { FieldShell, MaskedSecretFooter, SchemaControl, } from "../schema/fields"; import type { SchemaField, SchemaFieldType } from "../schema/fieldTypes"; /** qq-obv11 这类「字段含义随 transport × mode 变化」的平台,字段不能平铺 —— 平铺会 * 同时制造「填了用不上」和「没填起不来」两种事故。这里只渲染当前组合下真正生效的字段, * 并逐字段给出「谁连谁 / 空值代表什么 / 现在是否必填」。 * * 校验规则(必填、清空、写回补丁)全在 `channelFieldRules.ts` 的纯函数里;**呈现**复用 * `../schema/fields.tsx` 的通用控件(与插件面板同一份渲染,design/09 §5.7 C)—— * 本组件只做「渠道元数据 → 通用 schema」的适配与规则结果的分发,不再自己画控件。 */ export interface PlatformConnFieldsProps { platform: string; values: Readonly>; onChange: (key: string, value: string) => void; /** 盘上已有值的键(含只有掩码可看的秘密):用来显示「已配置」并算作必填已满足。 */ configuredKeys: readonly string[]; /** 本次保存被拦下时点名的必填字段键(红框 + 一句话)。 */ missingKeys: readonly string[]; /** 主人显式要求清除的掩码秘密键(面板不持有原文,留空 ≠ 清除)。 */ clearedKeys: readonly string[]; onToggleClear: (key: string, cleared: boolean) => void; } /** 渠道字段声明 → 通用 schema。分支顺序与旧渲染器一致(有候选先给下拉), * 因此 `channelConfigForm.render.test.ts` 断言的 HTML 形状逐字不变。 */ export function platformFieldToSchema(f: PlatformField): SchemaField { const options = f.options ?? []; const type: SchemaFieldType = options.length > 0 ? "select" : f.secret === true ? "secret" : "text"; return { key: f.key, label: f.label, type, placeholder: f.placeholder, options, default: f.defaultValue, guide: f.guide, }; } export function PlatformConnFields({ platform, values, onChange, configuredKeys, missingKeys, clearedKeys, onToggleClear, }: PlatformConnFieldsProps): React.ReactNode { const fields = visibleFields(platform, values); if (fields.length === 0) return null; const hint = modeHint(platform, values); const summary = modeSummary(platform, values); // 必填清单一次算完(当前组合下哪些字段必填),避免逐字段重复判定。 const required = new Set(requiredFieldKeys(platform, values)); const missing = new Set(missingKeys); const configured = new Set(configuredKeys); const cleared = new Set(clearedKeys); return (
{hint ? (
{hint}
) : null}
{fields.map((f) => { const isRequired = required.has(f.key); const isMissing = missing.has(f.key); const clearRequested = cleared.has(f.key); // 掩码秘密:面板只看到「已配置」,原文拿不回来 —— 留空保持原值,要删掉必须显式开关。 const maskedSecret = f.secret === true && configured.has(f.key); return ( onToggleClear(f.key, next)} /> } > onChange(f.key, v)} disabled={clearRequested} invalid={isMissing && isRequired} /> ); })}
); }