125 lines
4.2 KiB
JavaScript
125 lines
4.2 KiB
JavaScript
/**
|
||
* 配置值校验工具
|
||
* 根据配置类型校验值的格式,编辑模式下额外校验值范围
|
||
*/
|
||
|
||
/**
|
||
* 支持的值类型列表
|
||
*/
|
||
const VALID_TYPES = ["string", "number", "boolean", "json"];
|
||
|
||
/**
|
||
* 校验配置值是否符合指定的类型
|
||
* @param {any} value - 配置值
|
||
* @param {string} type - 配置类型(string/number/boolean/json)
|
||
* @returns {{ valid: boolean, message: string }} 校验结果
|
||
*/
|
||
function validateByType(value, type) {
|
||
if (!VALID_TYPES.includes(type)) {
|
||
return { valid: false, message: `不支持的类型: ${type},支持的类型: ${VALID_TYPES.join(", ")}` };
|
||
}
|
||
|
||
switch (type) {
|
||
case "string":
|
||
if (typeof value !== "string") {
|
||
return { valid: false, message: "值必须是字符串类型" };
|
||
}
|
||
break;
|
||
case "number": {
|
||
if (value === null || value === undefined || String(value).trim() === "") {
|
||
return { valid: false, message: "值必须是有效的数字" };
|
||
}
|
||
const num = Number(value);
|
||
if (isNaN(num)) {
|
||
return { valid: false, message: "值必须是有效的数字" };
|
||
}
|
||
break;
|
||
}
|
||
case "boolean":
|
||
if (typeof value !== "boolean" && value !== "true" && value !== "false") {
|
||
return { valid: false, message: "值必须是布尔类型(true 或 false)" };
|
||
}
|
||
break;
|
||
case "json":
|
||
try {
|
||
JSON.parse(typeof value === "string" ? value : JSON.stringify(value));
|
||
} catch {
|
||
return { valid: false, message: "值必须是有效的 JSON 格式" };
|
||
}
|
||
break;
|
||
}
|
||
return { valid: true, message: "" };
|
||
}
|
||
|
||
/**
|
||
* 校验值范围约束(仅在编辑模式下生效)
|
||
* 支持:
|
||
* - 正则约束(string 类型常用):"/正则/flags",如 /^1[3-9]\d{9}$/(手机号)
|
||
* - 区间范围:"1-100" 或 "0.0-1.0"
|
||
* - 枚举范围:"枚举: A,B,C"
|
||
* - 描述性范围(如 "合法 IPv4 地址")仅作提示,不做强制校验
|
||
* @param {any} value - 配置值
|
||
* @param {string} valueRange - 值范围描述
|
||
* @returns {{ valid: boolean, message: string }} 校验结果
|
||
*/
|
||
function validateRange(value, valueRange) {
|
||
if (!valueRange || valueRange.trim() === "") {
|
||
return { valid: true, message: "" };
|
||
}
|
||
|
||
const range = valueRange.trim();
|
||
|
||
// 正则约束: "/pattern/flags",如 /^1[3-9]\d{9}$/(手机号)
|
||
if (range.startsWith("/")) {
|
||
// pattern 中未转义的 / 不允许(与 JS 正则字面量一致);flags 仅允许字母
|
||
const regexMatch = range.match(/^\/((?:\\.|[^\\/])*)\/([a-z]*)$/);
|
||
if (!regexMatch) {
|
||
return { valid: false, message: `正则格式无效: ${range},正确格式为 /pattern/flags` };
|
||
}
|
||
const pattern = regexMatch[1];
|
||
const flags = regexMatch[2] || "";
|
||
let re;
|
||
try {
|
||
re = new RegExp(pattern, flags);
|
||
} catch (e) {
|
||
return { valid: false, message: `正则表达式无效: /${pattern}/` };
|
||
}
|
||
// 保留 g/y 标志语义:test 前重置 lastIndex,避免状态残留
|
||
re.lastIndex = 0;
|
||
if (!re.test(String(value))) {
|
||
return { valid: false, message: `值不符合格式要求: /${pattern}/` };
|
||
}
|
||
return { valid: true, message: "" };
|
||
}
|
||
|
||
// 区间范围: "1-100" 或 "0.0-1.0"
|
||
const rangeMatch = range.match(/^(-?\d+(\.\d+)?)\s*-\s*(-?\d+(\.\d+)?)$/);
|
||
if (rangeMatch) {
|
||
const min = Number(rangeMatch[1]);
|
||
const max = Number(rangeMatch[3]);
|
||
if (min > max) {
|
||
return { valid: false, message: `范围下限 ${min} 大于上限 ${max}` };
|
||
}
|
||
const num = Number(value);
|
||
if (isNaN(num) || num < min || num > max) {
|
||
return { valid: false, message: `值必须在 ${min} 到 ${max} 之间` };
|
||
}
|
||
return { valid: true, message: "" };
|
||
}
|
||
|
||
// 枚举范围: "枚举: A,B,C"
|
||
const enumMatch = range.match(/^枚举[::]\s*(.+)$/);
|
||
if (enumMatch) {
|
||
const options = enumMatch[1].split(/[,,]/).map((s) => s.trim());
|
||
if (!options.includes(String(value).trim())) {
|
||
return { valid: false, message: `值必须是以下选项之一: ${options.join(", ")}` };
|
||
}
|
||
return { valid: true, message: "" };
|
||
}
|
||
|
||
// 其他描述性范围(如 "合法 IPv4 地址")目前不做强制校验,仅作提示
|
||
return { valid: true, message: "" };
|
||
}
|
||
|
||
module.exports = { VALID_TYPES, validateByType, validateRange };
|