init local code
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
import { ThemeParamVo, ThemeVo } from "@/types/cms";
|
||||
|
||||
/**
|
||||
* themeParams 纯函数模块(方案 §6.3,评审 R10/R6):schema → 表单模型映射、
|
||||
* 值校验(color 正则 / select 枚举 / image 仅 /upload/**)、themeParams ↔ configJson 装拆。
|
||||
* 与后端合并语义(方案 §5.3)保持一致:仅替换 theme_params 键、保留其余键、丢弃未声明键。
|
||||
* vitest 单测对象(§8.4 T28-T30),不依赖组件挂载基建。
|
||||
*/
|
||||
|
||||
/** color 合法值:#RGB/#RRGGBB/#RRGGBBAA(与后端 ThemeParam.COLOR_PATTERN 一致) */
|
||||
export const COLOR_RE = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/;
|
||||
|
||||
/** param key 约束(与后端 KEY_PATTERN 一致) */
|
||||
export const KEY_RE = /^[a-zA-Z][a-zA-Z0-9_]{0,31}$/;
|
||||
|
||||
export interface FormModelValue {
|
||||
[key: string]: string;
|
||||
}
|
||||
|
||||
/** 单值校验(与后端 ThemeParam.isValidValue 同语义;null/空一律不合法) */
|
||||
export function isValidParamValue(param: ThemeParamVo, value: string | undefined | null): boolean {
|
||||
if (value === undefined || value === null || value === "") return false;
|
||||
switch (param.type) {
|
||||
case "color":
|
||||
return COLOR_RE.test(value);
|
||||
case "image":
|
||||
// B-7:与后端对齐——附件 webUrl 相对形态含 context-path(/cms/upload/**)
|
||||
return value.startsWith("/cms/upload/");
|
||||
case "select":
|
||||
return !!param.options?.some(o => o.value === value);
|
||||
case "text":
|
||||
return value.length <= 256;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/** 取参数生效值:显式值合法用之;否则回退 defaultValue(缺省也视为合法默认) */
|
||||
export function effectiveValue(param: ThemeParamVo, model: FormModelValue): string {
|
||||
const raw = model[param.key];
|
||||
if (isValidParamValue(param, raw)) return raw as string;
|
||||
return param.defaultValue ?? "";
|
||||
}
|
||||
|
||||
/** T28: schema → 表单模型(声明过的 key 全部带上生效值) */
|
||||
export function formModelFromTheme(theme: ThemeVo, existing: FormModelValue = {}): FormModelValue {
|
||||
const model: FormModelValue = {};
|
||||
theme.params.forEach(p => {
|
||||
const raw = existing[p.key];
|
||||
model[p.key] = isValidParamValue(p, raw) ? (raw as string) : (p.defaultValue ?? "");
|
||||
});
|
||||
return model;
|
||||
}
|
||||
|
||||
/** T28: 值校验兜底 —— 收集当前表单模型里的非法值 key(空值不报,默认值兜底) */
|
||||
export function invalidParamKeys(theme: ThemeVo, model: FormModelValue): string[] {
|
||||
const result: string[] = [];
|
||||
theme.params.forEach(p => {
|
||||
const v = model[p.key];
|
||||
if (v !== undefined && v !== "" && !isValidParamValue(p, v)) {
|
||||
result.push(p.key);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
/** T30a: 服务端合并语义(与后端 §5.3 一致)—— 仅替换 theme_params、保留其余键、丢弃未声明/非法值 */
|
||||
export function mergeThemeParamsIntoConfigJson(configJson: string | undefined | null,
|
||||
theme: ThemeVo,
|
||||
model: FormModelValue): string {
|
||||
let config: Record<string, unknown> = {};
|
||||
if (configJson && configJson.trim()) {
|
||||
try {
|
||||
const parsed = JSON.parse(configJson);
|
||||
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
||||
config = { ...parsed };
|
||||
}
|
||||
} catch {
|
||||
// 非合法 JSON 按空配置处理(与后端 ExtJsonUtils.parse 语义一致)
|
||||
}
|
||||
}
|
||||
const filtered: Record<string, string> = {};
|
||||
theme.params.forEach(p => {
|
||||
const v = model[p.key];
|
||||
if (isValidParamValue(p, v)) {
|
||||
filtered[p.key] = v as string;
|
||||
}
|
||||
});
|
||||
if (Object.keys(filtered).length === 0) {
|
||||
delete config.theme_params;
|
||||
} else {
|
||||
config.theme_params = filtered;
|
||||
}
|
||||
return JSON.stringify(config);
|
||||
}
|
||||
|
||||
/** T30b: configJson → themeParams(编辑回显:仅取声明过的 key,非法值丢弃) */
|
||||
export function themeParamsFromConfigJson(configJson: string | undefined | null,
|
||||
theme: ThemeVo): FormModelValue {
|
||||
const model: FormModelValue = {};
|
||||
if (!configJson) return model;
|
||||
try {
|
||||
const parsed = JSON.parse(configJson);
|
||||
const tp = parsed && typeof parsed === "object" && !Array.isArray(parsed)
|
||||
? (parsed as Record<string, unknown>).theme_params
|
||||
: undefined;
|
||||
if (tp && typeof tp === "object") {
|
||||
theme.params.forEach(p => {
|
||||
const raw = (tp as Record<string, unknown>)[p.key];
|
||||
const v = typeof raw === "string" ? raw : "";
|
||||
if (isValidParamValue(p, v)) model[p.key] = v;
|
||||
});
|
||||
}
|
||||
} catch {
|
||||
// 非合法 JSON 按无参数处理
|
||||
}
|
||||
return model;
|
||||
}
|
||||
|
||||
/** 主题是否声明了某类型参数(组件渲染分支用) */
|
||||
export function hasParamType(theme: ThemeVo | undefined | null, type: string): boolean {
|
||||
return !!theme?.params?.some(p => p.type === type);
|
||||
}
|
||||
Reference in New Issue
Block a user