+104
-7
@@ -66,6 +66,20 @@ const MODE_NAMES = { plan: "规划", edit: "编辑", readonly: "只读" };
|
||||
*/
|
||||
const MAPPING_NAMES = { path: "路径", label: "标签" };
|
||||
|
||||
/**
|
||||
* string 类型值范围的常见格式正则预设(前端选择后填入值范围输入框,后端按 /正则/ 校验)
|
||||
* regex 为去掉首尾斜杠与修饰符的正则体
|
||||
*/
|
||||
const RANGE_PRESETS = {
|
||||
phone: { label: "手机号", regex: "^1[3-9]\\d{9}$" },
|
||||
email: { label: "邮箱", regex: "^[\\w.%+-]+@[\\w.-]+\\.[A-Za-z]{2,}$" },
|
||||
idcard: { label: "身份证", regex: "^\\d{17}[\\dXx]$" },
|
||||
ipv4: { label: "IPv4", regex: "^(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}$" },
|
||||
url: { label: "URL", regex: "^https?:\\/\\/[\\w.-]+(?:\\.[a-zA-Z]{2,})?(?:\\/[^\\s]*)?$" },
|
||||
port: { label: "端口号", regex: "^([1-9]\\d{0,3}|[1-5]\\d{4}|6[0-4]\\d{3}|65[0-4]\\d{2}|655[0-2]\\d|6553[0-5])$" },
|
||||
alphanumeric: { label: "字母数字", regex: "^[A-Za-z0-9]+$" },
|
||||
};
|
||||
|
||||
/**
|
||||
* 应用当前模式到界面(更新样式类、按钮状态、状态栏)
|
||||
* @returns {void}
|
||||
@@ -197,10 +211,16 @@ function renderDimensionBar() {
|
||||
return;
|
||||
}
|
||||
box.style.display = "";
|
||||
// 填充筛选下拉
|
||||
// 填充筛选下拉(用 DOM 构建避免属性上下文注入)
|
||||
const sel = document.getElementById(it.sel);
|
||||
const values = collectDimValues(it.field);
|
||||
sel.innerHTML = '<option value="">全部</option>' + values.map((v) => `<option value="${escapeHtml(v)}">${escapeHtml(v)}</option>`).join("");
|
||||
sel.innerHTML = '<option value="">全部</option>';
|
||||
values.forEach((v) => {
|
||||
const opt = document.createElement("option");
|
||||
opt.value = v;
|
||||
opt.textContent = v;
|
||||
sel.appendChild(opt);
|
||||
});
|
||||
sel.value = state.dimFilter[it.key] || "";
|
||||
// 展示映射方式
|
||||
document.getElementById(it.map).textContent = `[${MAPPING_NAMES[p[it.mapping]] || "路径"}]`;
|
||||
@@ -231,16 +251,28 @@ function renderConfigTable(configs, path) {
|
||||
tbody.innerHTML = "";
|
||||
|
||||
if (!configs || configs.length === 0) {
|
||||
tbody.innerHTML = '<tr class="empty-row"><td colspan="5">当前节点暂无配置项</td></tr>';
|
||||
tbody.innerHTML = '<tr class="empty-row"><td colspan="6">当前节点暂无配置项</td></tr>';
|
||||
return;
|
||||
}
|
||||
|
||||
configs.forEach((cfg) => {
|
||||
const exists = cfg.exists !== false;
|
||||
const tr = document.createElement("tr");
|
||||
if (!exists) tr.classList.add("row-not-exists");
|
||||
|
||||
const statusHtml = [];
|
||||
if (cfg.required === true) statusHtml.push('<span class="status-badge badge-required">必填</span>');
|
||||
statusHtml.push(
|
||||
exists
|
||||
? '<span class="status-badge badge-exists">存在</span>'
|
||||
: '<span class="status-badge badge-missing">不存在</span>'
|
||||
);
|
||||
|
||||
tr.innerHTML = `
|
||||
<td class="config-key-cell">${escapeHtml(cfg.key)}</td>
|
||||
<td class="config-value-cell">${escapeHtml(String(cfg.value))}</td>
|
||||
<td class="config-value-cell">${buildValueCell(cfg, exists)}</td>
|
||||
<td><span class="type-badge">${escapeHtml(cfg.type)}</span></td>
|
||||
<td class="config-status-cell">${statusHtml.join("")}</td>
|
||||
<td>${escapeHtml(cfg.description)}</td>
|
||||
<td>
|
||||
<button class="row-btn view" data-action="view" data-id="${cfg.id}">查看</button>
|
||||
@@ -252,6 +284,24 @@ function renderConfigTable(configs, path) {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建表格值列 HTML:存在且值非空显示实际值;值为空但配置了默认值时回退展示默认值;不存在显示 —
|
||||
* @param {Object} cfg - 配置项
|
||||
* @param {boolean} exists - 是否存在
|
||||
* @returns {string} 值列 HTML
|
||||
*/
|
||||
function buildValueCell(cfg, exists) {
|
||||
if (!exists) return '<span class="value-missing">—</span>';
|
||||
const value = String(cfg.value);
|
||||
if (value.trim() !== "") return escapeHtml(value);
|
||||
const hasDefault =
|
||||
cfg.defaultValue !== undefined && cfg.defaultValue !== null && String(cfg.defaultValue).trim() !== "";
|
||||
if (hasDefault) {
|
||||
return `<span class="value-default" title="配置值为空,回退展示默认值">默认 ${escapeHtml(String(cfg.defaultValue))}</span>`;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据当前项目维度开关,控制配置模态框中维度字段的显示
|
||||
* @returns {void}
|
||||
@@ -287,16 +337,36 @@ function openConfigModal(config, readonly) {
|
||||
document.getElementById("config-environment").value = preEnv;
|
||||
document.getElementById("config-group").value = preGroup;
|
||||
document.getElementById("config-value").value = config ? config.value : "";
|
||||
document.getElementById("config-default-value").value =
|
||||
config && config.defaultValue !== undefined && config.defaultValue !== null ? config.defaultValue : "";
|
||||
// 编辑模式下提示默认值(留空则回退展示默认值)
|
||||
document.getElementById("config-value").placeholder =
|
||||
config && config.defaultValue !== undefined && config.defaultValue !== null && String(config.defaultValue).trim() !== ""
|
||||
? `留空则使用默认值:${config.defaultValue}`
|
||||
: "配置值";
|
||||
document.getElementById("config-type").value = config ? config.type : "string";
|
||||
document.getElementById("config-value-range").value = config ? config.valueRange || "" : "";
|
||||
document.getElementById("config-description").value = config ? config.description : "";
|
||||
document.getElementById("config-purpose").value = config ? config.purpose || "" : "";
|
||||
document.getElementById("config-required").checked = config ? config.required === true : false;
|
||||
document.getElementById("config-exists").checked = config ? config.exists !== false : true;
|
||||
document.getElementById("config-range-preset").value = "";
|
||||
|
||||
updateDimensionFields();
|
||||
updateRangePresetVisibility();
|
||||
applyFieldPermissions(readonly);
|
||||
openModal("modal-config");
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据配置类型显示/隐藏“常见格式”正则预设下拉(仅 string 类型提供)
|
||||
* @returns {void}
|
||||
*/
|
||||
function updateRangePresetVisibility() {
|
||||
const isString = document.getElementById("config-type").value === "string";
|
||||
document.getElementById("config-range-preset-group").style.display = isString ? "" : "none";
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据模式和是否只读查看,设置配置模态框各字段的可编辑性
|
||||
* 规划模式:结构字段(类型/说明/用途/范围/键名/维度)可改,值不可改
|
||||
@@ -307,16 +377,21 @@ function openConfigModal(config, readonly) {
|
||||
*/
|
||||
function applyFieldPermissions(readonly) {
|
||||
const structFields = [
|
||||
"config-key", "config-type", "config-value-range",
|
||||
"config-key", "config-type", "config-value-range", "config-required",
|
||||
"config-default-value",
|
||||
"config-description", "config-purpose",
|
||||
"config-idc", "config-environment", "config-group",
|
||||
];
|
||||
const valueField = "config-value";
|
||||
const existsField = "config-exists";
|
||||
const presetField = "config-range-preset";
|
||||
const saveBtn = document.getElementById("btn-save-config");
|
||||
|
||||
const disableAll = (disabled) => {
|
||||
structFields.forEach((id) => (document.getElementById(id).disabled = disabled));
|
||||
document.getElementById(valueField).disabled = disabled;
|
||||
document.getElementById(existsField).disabled = disabled;
|
||||
document.getElementById(presetField).disabled = disabled;
|
||||
};
|
||||
|
||||
if (readonly || state.mode === "readonly") {
|
||||
@@ -328,14 +403,18 @@ function applyFieldPermissions(readonly) {
|
||||
saveBtn.style.display = "";
|
||||
|
||||
if (state.mode === "plan") {
|
||||
// 规划模式:可改结构,值不可改
|
||||
// 规划模式:可改结构(含是否必须),值/存在性不可改
|
||||
structFields.forEach((id) => (document.getElementById(id).disabled = false));
|
||||
document.getElementById(valueField).disabled = true;
|
||||
document.getElementById(existsField).disabled = true;
|
||||
document.getElementById(presetField).disabled = false;
|
||||
} else if (state.mode === "edit") {
|
||||
// 编辑模式:新增时结构可填;编辑已有项时仅值可改(键名/维度也允许调整路径)
|
||||
// 编辑模式:新增时结构可填;编辑已有项时仅值/存在性可改(键名/维度也允许调整路径)
|
||||
const isNew = !state.editingConfigId;
|
||||
document.getElementById("config-type").disabled = !isNew;
|
||||
document.getElementById("config-value-range").disabled = !isNew;
|
||||
document.getElementById("config-required").disabled = !isNew;
|
||||
document.getElementById("config-default-value").disabled = !isNew;
|
||||
document.getElementById("config-description").disabled = !isNew;
|
||||
document.getElementById("config-purpose").disabled = !isNew;
|
||||
document.getElementById("config-key").disabled = false;
|
||||
@@ -343,6 +422,8 @@ function applyFieldPermissions(readonly) {
|
||||
document.getElementById("config-environment").disabled = false;
|
||||
document.getElementById("config-group").disabled = false;
|
||||
document.getElementById(valueField).disabled = false;
|
||||
document.getElementById(existsField).disabled = false;
|
||||
document.getElementById(presetField).disabled = !isNew;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -359,6 +440,9 @@ function collectConfigForm() {
|
||||
value: document.getElementById("config-value").value,
|
||||
type: document.getElementById("config-type").value,
|
||||
valueRange: document.getElementById("config-value-range").value.trim(),
|
||||
defaultValue: document.getElementById("config-default-value").value,
|
||||
required: document.getElementById("config-required").checked,
|
||||
exists: document.getElementById("config-exists").checked,
|
||||
description: document.getElementById("config-description").value.trim(),
|
||||
purpose: document.getElementById("config-purpose").value.trim(),
|
||||
};
|
||||
@@ -632,6 +716,19 @@ function bindEvents() {
|
||||
});
|
||||
document.getElementById("btn-save-config").addEventListener("click", saveConfig);
|
||||
|
||||
// 类型切换:仅 string 类型展示“常见格式”预设
|
||||
document.getElementById("config-type").addEventListener("change", updateRangePresetVisibility);
|
||||
|
||||
// 常见格式预设:选择后自动填入值范围(/正则/ 格式)
|
||||
document.getElementById("config-range-preset").addEventListener("change", (e) => {
|
||||
const key = e.target.value;
|
||||
if (!key) return;
|
||||
const preset = RANGE_PRESETS[key];
|
||||
if (!preset) return;
|
||||
document.getElementById("config-value-range").value = `/${preset.regex}/`;
|
||||
toast(`已填入${preset.label}格式正则,可继续编辑`, "info");
|
||||
});
|
||||
|
||||
// 维度筛选下拉
|
||||
document.getElementById("dim-idc-select").addEventListener("change", (e) => {
|
||||
state.dimFilter.idc = e.target.value;
|
||||
|
||||
Reference in New Issue
Block a user