feat: 编辑框合并说明/用途字段,编辑模式下允许修改说明
build-and-deploy / build-and-deploy (push) Successful in 42s

This commit is contained in:
cheney
2026-09-17 10:37:07 +08:00
parent 6bbd4bc471
commit 26957bcb49
5 changed files with 54 additions and 29 deletions
-4
View File
@@ -295,10 +295,6 @@
<label>说明 <span class="required">*</span></label>
<textarea id="config-description" rows="2" placeholder="配置项用途说明"></textarea>
</div>
<div class="form-group">
<label>用途 <span class="hint">(规划模式)</span></label>
<textarea id="config-purpose" rows="2" placeholder="详细用途描述"></textarea>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-secondary" onclick="closeModal('modal-config')">取消</button>
+3 -7
View File
@@ -378,7 +378,6 @@ function openConfigModal(config, readonly) {
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 = "";
@@ -400,8 +399,8 @@ function updateRangePresetVisibility() {
/**
* 根据模式和是否只读查看,设置配置模态框各字段的可编辑性
* 规划模式:结构字段(类型/说明/用途/范围/键名/维度)可改,值不可改
* 编辑模式:值可改,已有项的结构字段锁定
* 规划模式:结构字段(类型/说明/范围/键名/维度)可改,值不可改
* 编辑模式:值、说明可改,已有项的其他结构字段锁定
* 只读查看:全部禁用
* @param {boolean} readonly - 是否只读查看
* @returns {void}
@@ -410,7 +409,7 @@ function applyFieldPermissions(readonly) {
const structFields = [
"config-key", "config-type", "config-value-range", "config-required",
"config-default-value",
"config-description", "config-purpose",
"config-description",
"config-idc", "config-environment", "config-group",
];
const valueField = "config-value";
@@ -446,8 +445,6 @@ function applyFieldPermissions(readonly) {
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;
document.getElementById("config-idc").disabled = false;
document.getElementById("config-environment").disabled = false;
@@ -475,7 +472,6 @@ function collectConfigForm() {
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(),
};
}
+1 -1
View File
@@ -401,7 +401,7 @@ function createMcpServer() {
"update_config",
{
description:
"修改配置项(只读模式下禁止)。plan 模式可修改 type/description/purpose/valueRange/required/key/维度;edit 模式可修改 value/exists/key/维度。可仅传需修改的字段。",
"修改配置项(只读模式下禁止)。plan 模式可修改 type/description/purpose/valueRange/required/key/维度;edit 模式可修改 value/exists/description/key/维度。可仅传需修改的字段。",
inputSchema: {
projectId: z.string().describe("项目 ID"),
configId: z.string().describe("配置项 ID"),
+3 -2
View File
@@ -239,7 +239,7 @@ function validateStructure(config, project) {
/**
* 更新配置项
* 规划模式:可修改 type、description、purpose、valueRange、required
* 编辑模式:可修改 value、exists
* 编辑模式:可修改 value、exists、description
* 只读模式:不可修改
* @param {string} projectId - 项目 ID
* @param {string} configId - 配置项 ID
@@ -279,11 +279,12 @@ async function updateConfig(projectId, configId, data, mode) {
if (data.group !== undefined) config.group = data.group;
validateStructure(config, project);
} else if (mode === "edit") {
// 编辑模式:修改值与存在性
// 编辑模式:修改值、存在性与说明
if (data.value !== undefined) config.value = data.value;
if (data.exists !== undefined) config.exists = data.exists !== false;
validateEditValue(config);
// 编辑模式下也可以修改 key 和维度(路径调整)
if (data.description !== undefined) config.description = String(data.description ?? "").trim();
if (data.key !== undefined) config.key = normalizeKey(data.key);
if (data.idc !== undefined) config.idc = data.idc;
if (data.environment !== undefined) config.environment = data.environment;
+32
View File
@@ -128,6 +128,38 @@ test("readonly 模式下拒绝项目/配置写操作,切换回 edit 后恢复"
await callTool("set_mode", { mode: "edit" });
});
test("edit 模式下可以修改配置说明", async () => {
await callTool("set_mode", { mode: "edit" });
const { text: createText } = await callTool("create_project", { name: "MCP-Unittest-Desc" });
const project = JSON.parse(createText.match(/\{[\s\S]*\}/)[0]);
const cfg = await callTool("create_config", {
projectId: project.id,
key: "db/host",
description: "原始说明",
value: "localhost",
});
const cfgObj = JSON.parse(cfg.text.match(/\{[\s\S]*\}/)[0]);
const updated = await callTool("update_config", {
projectId: project.id,
configId: cfgObj.id,
description: "新的说明",
});
assert.ok(!updated.isError, "edit 模式下应允许修改说明");
const updatedObj = JSON.parse(updated.text.match(/\{[\s\S]*\}/)[0]);
assert.strictEqual(updatedObj.description, "新的说明");
assert.strictEqual(updatedObj.value, "localhost", "仅改说明,值不应变化");
// 清理测试项目
const delProject = await callTool("delete_project", { projectId: project.id });
const confirmProject = await callTool("confirm_delete_project", {
confirmation_token: delProject.structuredContent.confirmation_token,
});
assert.strictEqual(confirmProject.structuredContent.status, "deleted");
});
test("配置删除同样为两步式确认", async () => {
const { text: createText } = await callTool("create_project", { name: "MCP-Unittest-Cfg" });
const project = JSON.parse(createText.match(/\{[\s\S]*\}/)[0]);