/* ==================== 视图二:项目设置(tab 切换) ==================== */ function renderProjectDetail() { if (!state.project || !state.project.projectId) { switchView("projects"); return; } $("#view-projectDetail").innerHTML = '
'; Core.api("project/info", { projectId: state.project.projectId }) .then(function (info) { state.detail = info || {}; var base = info.Base || {}; setActiveProject({ projectId: base.project_id || state.project.projectId, name: base.name || state.project.name }); // 加载项目资料(新表) return Core.api("project/data/load", { projectId: state.project.projectId }); }) .then(function (resp) { state.projectData = resp || {}; // resp 即为栏目扁平 map(保存什么就读到什么) var data = resp || {}; state.pjiSections = buildSectionsFromData(data); // 保存初始快照(用于重置) state.pjiInitialSections = JSON.parse(JSON.stringify(state.pjiSections)); renderProjectDetailForm(); }) .catch(function (e) { $("#view-projectDetail").innerHTML = '
\u26a0
' + Core.esc(e.message) + "
"; }); } /* ---------- 主 Tab 定义 ---------- */ var PD_TABS = [ ["base", "\u57fa\u7840\u4fe1\u606f"], ["projectInfo", "\u9879\u76ee\u8d44\u6599"], ["settings", "\u8bbe\u7f6e"] ]; /* ---------- 栏目类型选项(与维度格式一致) ---------- */ var PJI_TYPE_OPTIONS = [ ["string", "\u63cf\u8ff0"], ["object", "\u8be6\u7ec6\u4fe1\u606f"], ["array", "\u591a\u6761\u76ee"], ["list", "\u5217\u8868"] ]; var PJI_TYPE_LABELS = { string: "\u63cf\u8ff0", object: "\u8be6\u7ec6\u4fe1\u606f", array: "\u591a\u6761\u76ee", list: "\u5217\u8868" }; function renderProjectDetailForm() { var base = state.detail.Base || {}; var sup = state.detail.ModelInfo || {}; var hasDimEditor = typeof dimBlock === "function"; // ===== 顶部卡片 ===== var html = '
' + '
\u9879\u76ee\u8bbe\u7f6e #' + (base.project_id || state.project.projectId) + "
"; // ===== Tab 栏 ===== html += '
'; PD_TABS.forEach(function (t) { html += '
' + t[1] + "
"; }); html += "
"; // ===== Tab 1:基础信息 ===== html += '
'; // 项目基本信息 html += '
\u9879\u76ee\u4fe1\u606f' + '
' + '
' + '
' + "
"; // 行业模板应用 html += '
' + '
\u884c\u4e1a\u6a21\u677f
' + '
\u4ece\u884c\u4e1a\u6a21\u677f\u91cd\u65b0\u590d\u5236\u7ef4\u5ea6\u5185\u5bb9\uff08\u8986\u76d6\u4e0b\u65b9\u7ef4\u5ea6\u6a21\u677f\u5b57\u6bb5\uff0c\u4e0d\u5f71\u54cd\u5176\u4ed6\u6570\u636e\uff09
' + '
' + '
'; // 维度子 tab html += '
'; html += '
\u7ef4\u5ea6\u6a21\u677f
'; html += '
'; DIM_NAMES.forEach(function (d, i) { var label = d[1].replace(/ \u6a21\u677f$/, ""); html += '
' + label + "
"; }); html += "
"; // 维度内容区 DIM_NAMES.forEach(function (d, i) { var hidden = i === 0 ? "" : ' style="display:none"'; html += '"; }); html += "
"; // end pd-tab-base // ===== Tab 2:项目资料(动态编辑,初始无栏目) ===== html += ''; // end pd-tab-projectInfo // ===== Tab 3:设置(模型配置 + 微信接入 Token) ===== html += ''; // end pd-tab-settings html += "
"; // end card $("#view-projectDetail").innerHTML = html; loadIndustryOptions($("#pd-industry"), base.industry_id); // ===== 渲染项目资料动态栏目 ===== // 复位编辑器上下文为项目资料默认(防止客户画像弹窗遗留的目标污染本页) setPjiEditorTarget(null); drawPjiSections(); // ===== 主 Tab 切换 ===== $$("#pd-tabs .tab").forEach(function (t) { t.onclick = function () { $$("#pd-tabs .tab").forEach(function (x) { x.classList.toggle("active", x === t); }); var tab = t.dataset.tab; $("#pd-tab-base").style.display = tab === "base" ? "" : "none"; $("#pd-tab-projectInfo").style.display = tab === "projectInfo" ? "" : "none"; $("#pd-tab-settings").style.display = tab === "settings" ? "" : "none"; }; }); // ===== 检查是否有待切换到设置 tab 的请求 ===== if (state._pendingSettingsTab) { state._pendingSettingsTab = false; var settingsTab = $("#pd-tabs .tab[data-tab='settings']"); if (settingsTab) settingsTab.click(); } // ===== 维度子 Tab 切换 ===== $$("#pd-dim-tabs .tab").forEach(function (t) { t.onclick = function () { $$("#pd-dim-tabs .tab").forEach(function (x) { x.classList.toggle("active", x === t); }); $$(".pd-dim-panel").forEach(function (p) { p.style.display = p.dataset.dim === t.dataset.dim ? "" : "none"; }); }; }); // ===== 绑定维度结构化编辑器事件 ===== if (hasDimEditor) { $$(".dim-editor").forEach(function (editor) { bindDimEditor(editor); }); $$(".dim-gen").forEach(function (btn) { btn.onclick = function () { var dim = btn.dataset.dim; var editor = btn.closest(".dim-editor"); generateDialog({ dimension: dim, name: base.name || "", desc: base.desc || "", advicerDesc: base.advicer_desc || "", onApply: function (content) { var wrap = $(".dim-row-wrap", editor); var ta = $(".dim-input", editor); var rows = parseDimJson(content); if (wrap) { wrap.innerHTML = rows.length ? rows.map(function (r, i) { return dimRowHtml(i, r); }).join("") : '
\u6682\u65e0\u5b57\u6bb5\u5b9a\u4e49
'; wrap.classList.remove("hidden"); ta.classList.add("hidden"); $$(".dim-row", wrap).forEach(function (row) { bindDimRowEvents(row, editor); }); var toggleBtn = $(".dim-toggle-json", editor); if (toggleBtn) toggleBtn.textContent = "{ } JSON"; } if (ta) ta.value = content; Core.toast(dim + " \u5df2\u586b\u5165\u8868\u5355", "ok"); } }); }; }); } // ===== 保存基础信息 ===== $("#pd-save-all").onclick = function () { var payload = { projectId: base.project_id || state.project.projectId, name: $("#pd-name").value.trim() }; if (hasDimEditor) { $$(".dim-editor").forEach(function (ed) { var field = ed.dataset.field; var ta = $(".dim-input", ed); var wrap = $(".dim-row-wrap", ed); if (wrap && !wrap.classList.contains("hidden")) { payload[field] = serializeDimEditor(ed); } else if (ta) { payload[field] = ta.value; } }); $$(".pd-dim").forEach(function (ta) { payload[ta.dataset.field] = ta.value; }); } else { $$(".pd-dim").forEach(function (ta) { payload[ta.dataset.field] = ta.value; }); } Core.loading(true, "\u4fdd\u5b58\u4e2d\u2026"); Core.api("project/base/update", payload) .then(function () { Core.loading(false); Core.toast("\u4fdd\u5b58\u6210\u529f", "ok"); setActiveProject({ projectId: payload.projectId, name: payload.name }); renderProjectDetail(); }) .catch(function (e) { Core.loading(false); Core.toast(e.message, "err"); }); }; // ===== 应用行业模板 ===== $("#pd-copy").onclick = function () { var industryId = Number($("#pd-industry").value); if (!industryId) { Core.toast("\u8bf7\u9009\u62e9\u884c\u4e1a\u6a21\u677f", "err"); return; } Core.confirm("\u786e\u5b9a\u4ece\u8be5\u884c\u4e1a\u6a21\u677f\u590d\u5236\u7ef4\u5ea6\u5185\u5bb9\uff1f\u5c06\u8986\u76d6\u5f53\u524d\u9879\u76ee\u7ef4\u5ea6\u6a21\u677f\u3002").then(function (ok) { if (!ok) return; Core.loading(true, "\u590d\u5236\u6a21\u677f\u4e2d\u2026"); Core.api("project/template/copy", { projectId: state.project.projectId, industryId: industryId }) .then(function () { Core.loading(false); Core.toast("\u6a21\u677f\u5df2\u5e94\u7528", "ok"); renderProjectDetail(); }) .catch(function (e) { Core.loading(false); Core.toast(e.message, "err"); }); }); }; // ===== 保存项目资料 ===== $("#pji-save").onclick = function () { var sections = collectPjiSectionsData(); var dataObj = {}; sections.forEach(function (s) { dataObj[s.key] = s.content; }); Core.loading(true, "\u4fdd\u5b58\u4e2d\u2026"); Core.api("project/data/save", { projectId: state.project.projectId, data: dataObj }) .then(function () { Core.loading(false); Core.toast("\u9879\u76ee\u8d44\u6599\u5df2\u4fdd\u5b58", "ok"); state.pjiSections = sections; state.pjiInitialSections = JSON.parse(JSON.stringify(sections)); }) .catch(function (e) { Core.loading(false); Core.toast(e.message, "err"); }); }; // ===== 重置(撤销本次修改,恢复为接口数据) ===== $("#pji-reset").onclick = function () { Core.confirm("\u786e\u5b9a\u91cd\u7f6e\uff1f\u5c06\u64a4\u9500\u672c\u6b21\u6240\u6709\u65b0\u589e\u548c\u4fee\u6539\uff0c\u6062\u590d\u4e3a\u63a5\u53e3\u6570\u636e\u3002").then(function (ok) { if (!ok) return; // 先清空 DOM,防止 drawPjiSections 内的 sync 读到旧数据 var sectionsEl = $("#pji-sections"); if (sectionsEl) sectionsEl.innerHTML = ""; state.pjiSections = []; Core.loading(true, "\u91cd\u65b0\u52a0\u8f7d\u4e2d\u2026"); Core.api("project/data/load", { projectId: state.project.projectId }) .then(function (resp) { Core.loading(false); state.projectData = resp || {}; state.pjiSections = buildSectionsFromData(resp || {}); state.pjiInitialSections = JSON.parse(JSON.stringify(state.pjiSections)); drawPjiSections(); Core.toast("\u5df2\u91cd\u7f6e\u4e3a\u63a5\u53e3\u6570\u636e", "ok"); }) .catch(function (e) { Core.loading(false); Core.toast(e.message, "err"); }); }); }; // ===== 新增栏目 ===== $("#pji-add-section").onclick = function () { addPjiSectionDialog(); }; // ===== AI 预设 ===== $("#pji-ai-preset").onclick = function () { aiPresetDialog(); }; // ===== 设置 Tab:加载模型配置选项 ===== loadModelsupOptions($("#pd-settings-model"), base.model_sup_id); // ===== 设置 Tab:保存微信接入 Token ===== $("#pd-save-wx").onclick = function () { var wxToken = $("#pd-wx-token").value.trim(); Core.loading(true, "保存中…"); Core.api("project/base/update", { projectId: state.project.projectId, wxToken: wxToken }) .then(function () { Core.loading(false); Core.toast("微信接入配置已保存", "ok"); }) .catch(function (e) { Core.loading(false); Core.toast(e.message, "err"); }); }; // ===== 设置 Tab:保存模型配置 ===== $("#pd-save-settings-model").onclick = function () { var modelSupId = Number($("#pd-settings-model").value) || 0; Core.loading(true, "保存中…"); Core.api("project/base/update", { projectId: state.project.projectId, modelSupId: modelSupId }) .then(function () { Core.loading(false); Core.toast("模型配置已保存", "ok"); renderProjectDetail(); }) .catch(function (e) { Core.loading(false); Core.toast(e.message, "err"); }); }; } /* ==================== 项目资料动态栏目(维度格式) ==================== */ /* * 栏目编辑器上下文:默认读写项目资料页的 state.pjiSections + #pji-sections。 * 通过 setPjiEditorTarget 可将「数据源 + 容器」指向别处(如客户画像弹窗内的编辑器), * 复用同一套渲染/绑定/收集逻辑;传 null 复位为项目资料默认。 */ var __pjiTarget = null; function setPjiEditorTarget(root, get, set) { __pjiTarget = root ? { root: root, get: get, set: set } : null; } function pjiContainer() { if (__pjiTarget && __pjiTarget.root) return __pjiTarget.root.querySelector("#pji-sections"); return document.querySelector("#pji-sections"); } function pjiGetSections() { if (__pjiTarget && __pjiTarget.get) return __pjiTarget.get() || []; return state.pjiSections || []; } function pjiSetSections(v) { if (__pjiTarget && __pjiTarget.set) { __pjiTarget.set(v); return; } state.pjiSections = v; } /** 将 MongoDB 返回的 [{Key,Value}] 或其他格式统一转为 [{k,v}] */ function normalizeKvPairs(data) { if (!data) return []; if (Array.isArray(data)) { return data.map(function (item) { if (item && typeof item === "object") { // 支持 {k,v} 和 {Key,Value} 两种格式 if (item.Key !== undefined) return { k: String(item.Key), v: String(item.Value || "") }; if (item.k !== undefined) return { k: String(item.k), v: String(item.v || "") }; } return { k: String(item), v: "" }; }); } if (typeof data === "object") { return Object.keys(data).map(function (k) { return { k: k, v: String(data[k] || "") }; }); } return []; } /** 从数据值推断栏目类型 */ function inferSectionType(val) { if (typeof val === "string") return "string"; if (Array.isArray(val)) { if (val.length && typeof val[0] === "object" && val[0] !== null) return "list"; return "array"; } if (typeof val === "object" && val !== null) return "object"; return "string"; } /** 从接口返回的扁平数据还原栏目数组 */ function buildSectionsFromData(data) { var result = []; Object.keys(data).forEach(function (key) { var val = data[key]; var type = inferSectionType(val); result.push({ key: key, label: key, type: type, content: parseSectionContent(val, type) }); }); return result; } /** 解析栏目内容为编辑器所需格式 */ function parseSectionContent(content, type) { if (content == null || content === "") return type === "string" ? "" : []; if (type === "string") return typeof content === "string" ? content : JSON.stringify(content); if (typeof content === "string") { try { content = JSON.parse(content); } catch (e) { return type === "array" ? [content] : []; } } if (type === "object") return normalizeKvPairs(content); if (type === "list") { // list: 数组,每个元素是一组 [{k,v}] 或 [{Key,Value}] if (Array.isArray(content)) { return content.map(function (entry) { return normalizeKvPairs(entry); }); } return typeof parseExample === "function" ? parseExample(content, "list") : []; } if (type === "array") return typeof parseExample === "function" ? parseExample(content, "array") : []; return []; } /** 生成栏目内容编辑器 HTML */ function sectionContentHtml(type, content) { if (type === "string") { return ''; } if (type === "object") { var pairs = (Array.isArray(content) ? content : []).map(function (p) { return kvRowHtml(p.k, p.v); }).join(""); return '
' + pairs + '
'; } if (type === "array") { var items = (Array.isArray(content) ? content : []).map(function (v, i) { return '
' + '
'; }).join(""); return '
' + items + '
'; } if (type === "list") { var entries = (Array.isArray(content) ? content : []).map(function (pairs, idx) { var kvHtml = (Array.isArray(pairs) ? pairs : []).map(function (p) { return kvRowHtml(p.k, p.v); }).join(""); return '
' + '
#' + (idx + 1) + '' + '
' + kvHtml + '
'; }).join(""); return '
' + entries + '
'; } return ""; } /** 将任意格式内容统一转为字符串表示 */ function contentToString(content, type) { if (content == null || content === "") return ""; if (type === "string") return typeof content === "string" ? content : JSON.stringify(content); if (type === "object") return normalizeKvPairs(content).map(function (p) { return p.k + ": " + p.v; }).join("\n"); if (type === "array") { if (Array.isArray(content)) return content.map(function (v) { return String(v || ""); }).join("\n"); if (typeof content === "string") return content; return String(content); } if (type === "list") { if (Array.isArray(content)) return content.map(function (entry) { return normalizeKvPairs(entry).map(function (p) { return p.k + ": " + p.v; }).join(", "); }).join("\n"); return typeof content === "string" ? content : JSON.stringify(content); } return ""; } /** 将字符串内容解析为目标类型格式 */ function stringToType(str, toType) { if (str == null || str === "") return toType === "string" ? "" : []; if (toType === "string") return str; if (toType === "object") { var lines = str.split("\n").filter(function (s) { return s.trim(); }); if (!lines.length) return []; return lines.map(function (line) { var idx = line.indexOf(":"); if (idx > 0) return { k: line.substring(0, idx).trim(), v: line.substring(idx + 1).trim() }; return { k: line.trim(), v: "" }; }); } if (toType === "array") { return str.split("\n").filter(function (s) { return s.trim(); }); } if (toType === "list") { var listLines = str.split("\n").filter(function (s) { return s.trim(); }); if (!listLines.length) return []; return [listLines.map(function (line) { var idx = line.indexOf(":"); if (idx > 0) return { k: line.substring(0, idx).trim(), v: line.substring(idx + 1).trim() }; return { k: line.trim(), v: "" }; })]; } return []; } /** 将内容从一种类型转换为另一种类型(统一走字符串中转) */ function convertSectionContent(content, fromType, toType) { if (fromType === toType) return content; // 先统一转为字符串,再从字符串转为目标类型 var str = contentToString(content, fromType); return stringToType(str, toType); } /** 从单个卡片 DOM 直接读取内容(不依赖 state) */ function readCardContentFromDom(card, type) { if (!card) return type === "string" ? "" : []; if (type === "string") { var inp = $(".pji-content", card); return inp ? inp.value : ""; } if (type === "object") { var obj = {}; $$(".dr-kv-row", card).forEach(function (r) { var k = $(".dr-kv-key", r).value.trim(); var v = $(".dr-kv-val", r).value.trim(); if (k) obj[k] = v; }); return obj; } if (type === "array") { var arr = []; $$(".dr-example-item", card).forEach(function (inp) { var v = inp.value.trim(); if (v) arr.push(v); }); return arr; } if (type === "list") { var list = []; $$(".dr-list-entry", card).forEach(function (entry) { var obj = {}; $$(".dr-kv-row", entry).forEach(function (r) { var k = $(".dr-kv-key", r).value.trim(); var v = $(".dr-kv-val", r).value.trim(); if (k) obj[k] = v; }); if (Object.keys(obj).length) list.push(obj); }); return list; } return type === "string" ? "" : []; } /** 从 DOM 同步栏目内容到当前数据源(防止重绘丢失数据) */ function syncPjiSectionsFromDom() { var sections = pjiGetSections(); var cont = pjiContainer(); var cards = $$(".pji-section-card", cont || document); if (!cards.length || !sections.length) return; cards.forEach(function (card) { var idx = Number(card.dataset.idx); var sec = sections[idx]; if (!sec) return; if (sec.type === "string") { var inp = $(".pji-content", card); if (inp) sec.content = inp.value; } else if (sec.type === "object") { var obj = {}; $$(".dr-kv-row", card).forEach(function (r) { var k = $(".dr-kv-key", r).value.trim(); var v = $(".dr-kv-val", r).value.trim(); if (k) obj[k] = v; }); sec.content = obj; } else if (sec.type === "array") { var arr = []; $$(".dr-example-item", card).forEach(function (inp) { var v = inp.value.trim(); if (v) arr.push(v); }); sec.content = arr; } else if (sec.type === "list") { var list = []; $$(".dr-list-entry", card).forEach(function (entry) { var obj = {}; $$(".dr-kv-row", entry).forEach(function (r) { var k = $(".dr-kv-key", r).value.trim(); var v = $(".dr-kv-val", r).value.trim(); if (k) obj[k] = v; }); if (Object.keys(obj).length) list.push(obj); }); sec.content = list; } }); } /** 绘制所有栏目(渲染到当前上下文容器) */ function drawPjiSections() { var container = pjiContainer(); if (!container) return; // 重绘前先从 DOM 同步数据,防止已填内容丢失 syncPjiSectionsFromDom(); var sections = pjiGetSections(); if (!sections.length) { container.innerHTML = '
\u2726
\u6682\u65e0\u680f\u76ee\uff0c\u70b9\u51fb\u4e0a\u65b9\u300c+ \u65b0\u589e\u680f\u76ee\u300d\u6dfb\u52a0
'; return; } var typeOptsHtml = PJI_TYPE_OPTIONS.map(function (o) { return ''; }).join(""); var html = ""; sections.forEach(function (sec, idx) { var content = parseSectionContent(sec.content, sec.type); html += '
' + '
' + '' + '' + '' + '' + "
" + '
' + sectionContentHtml(sec.type, content) + "
" + "
"; }); container.innerHTML = html; // 设置下拉框当前值 $$(".pji-type-select", container).forEach(function (sel) { sel.value = sections[Number(sel.dataset.idx)].type; }); bindPjiSectionEvents(); } /** 绑定栏目内事件(增删改) */ function bindPjiSectionEvents() { var container = pjiContainer(); if (!container) return; var sections = pjiGetSections(); // 点击栏目名称 → 内联编辑 $$(".pji-section-label", container).forEach(function (span) { span.onclick = function () { var idx = Number(span.dataset.idx); var sec = sections[idx]; if (!sec) return; // 先同步当前 DOM 数据 syncPjiSectionsFromDom(); var input = document.createElement("input"); input.className = "input sm"; input.style.cssText = "width:160px;padding:2px 8px"; input.value = sec.label; span.replaceWith(input); input.focus(); input.select(); var finish = function () { var newLabel = input.value.trim(); if (newLabel) { sec.label = newLabel; // 同步更新 key(存储字段名) sec.key = newLabel.replace(/[^a-zA-Z0-9\u4e00-\u9fff]/g, "").replace(/^[0-9]+/, "") || ("sec_" + Date.now()); } pjiSetSections(sections); drawPjiSections(); }; input.onblur = finish; input.onkeydown = function (e) { if (e.key === "Enter") { e.preventDefault(); finish(); } if (e.key === "Escape") { pjiSetSections(sections); drawPjiSections(); } }; }; }); // 删除栏目 $$(".pji-del", container).forEach(function (btn) { btn.onclick = function () { var idx = Number(btn.dataset.idx); Core.confirm("\u786e\u5b9a\u5220\u9664\u680f\u76ee\u300c" + sections[idx].label + "\u300d\uff1f").then(function (ok) { if (!ok) return; sections.splice(idx, 1); pjiSetSections(sections); drawPjiSections(); }); }; }); // 下拉框切换类型(保留内容转换) $$(".pji-type-select", container).forEach(function (sel) { sel.onchange = function () { var idx = Number(sel.dataset.idx); var sec = sections[idx]; if (!sec) return; var oldType = sec.type; var newType = sel.value; if (oldType === newType) return; // 直接从当前卡片 DOM 读取内容(不走 syncPjiSectionsFromDom,避免干扰) var card = sel.closest(".pji-section-card"); var domContent = readCardContentFromDom(card, oldType); // 转换内容格式 sec.content = convertSectionContent(domContent, oldType, newType); sec.type = newType; pjiSetSections(sections); drawPjiSections(); }; }); // 每个栏目卡片内的编辑器事件 $$(".pji-section-card", container).forEach(function (card) { var idx = Number(card.dataset.idx); var sec = sections[idx]; if (!sec || sec.type === "string") return; if (sec.type === "object") { // KV 添加字段 $$(".dr-kv-add", card).forEach(function (b) { b.onclick = function () { b.insertAdjacentHTML("beforebegin", kvRowHtml("", "")); }; }); // KV 删除字段 card.addEventListener("click", function (e) { if (e.target.classList.contains("dr-kv-del")) { e.target.closest(".dr-kv-row").remove(); } }); } if (sec.type === "array") { // 添加条目 $$(".dr-example-add", card).forEach(function (b) { b.onclick = function () { var wrap = b.closest(".dr-example-multi"); var count = $$(".dr-example-item", wrap).length; var div = document.createElement("div"); div.className = "dr-example-row"; div.innerHTML = '' + ''; wrap.insertBefore(div, b); }; }); // 删除条目 card.addEventListener("click", function (e) { if (e.target.classList.contains("dr-example-del")) { e.target.closest(".dr-example-row").remove(); } }); } if (sec.type === "list") { // 添加条目 $$(".dr-list-entry-add", card).forEach(function (b) { b.onclick = function () { var editor = b.closest(".dr-list-editor"); var lastIdx = $$(".dr-list-entry", editor).length; var div = document.createElement("div"); div.className = "dr-list-entry"; div.dataset.entry = lastIdx; div.innerHTML = '
#' + (lastIdx + 1) + '' + '
' + kvRowHtml("", "") + ''; editor.insertBefore(div, b); // 绑定新条目内的事件 $$(".dr-kv-add", div).forEach(function (kvAdd) { kvAdd.onclick = function () { kvAdd.insertAdjacentHTML("beforebegin", kvRowHtml("", "")); }; }); }; }); // 删除条目 card.addEventListener("click", function (e) { if (e.target.classList.contains("dr-list-entry-del")) { e.target.closest(".dr-list-entry").remove(); } }); // 条目内 KV 添加(委托,排除非 list 区域) card.addEventListener("click", function (e) { if (e.target.classList.contains("dr-kv-add") && !e.target.classList.contains("dr-list-entry-add")) { var entry = e.target.closest(".dr-list-entry"); if (entry) { e.preventDefault(); e.stopPropagation(); e.target.insertAdjacentHTML("beforebegin", kvRowHtml("", "")); } } }); // 条目内 KV 删除 card.addEventListener("click", function (e) { if (e.target.classList.contains("dr-kv-del")) { var entry = e.target.closest(".dr-list-entry"); if (entry) { e.preventDefault(); e.stopPropagation(); e.target.closest(".dr-kv-row").remove(); } } }); } }); } /** 收集所有栏目数据用于保存 */ function collectPjiSectionsData() { var sections = pjiGetSections(); var cont = pjiContainer(); var result = []; $$(".pji-section-card", cont || document).forEach(function (card) { var idx = Number(card.dataset.idx); var sec = sections[idx]; if (!sec) return; var content; if (sec.type === "string") { var inp = $(".pji-content", card); content = inp ? inp.value : ""; } else if (sec.type === "object") { content = {}; $$(".dr-kv-row", card).forEach(function (r) { var k = $(".dr-kv-key", r).value.trim(); var v = $(".dr-kv-val", r).value.trim(); if (k) content[k] = v; }); } else if (sec.type === "array") { content = []; $$(".dr-example-item", card).forEach(function (inp) { var v = inp.value.trim(); if (v) content.push(v); }); } else if (sec.type === "list") { content = []; $$(".dr-list-entry", card).forEach(function (entry) { var obj = {}; $$(".dr-kv-row", entry).forEach(function (r) { var k = $(".dr-kv-key", r).value.trim(); var v = $(".dr-kv-val", r).value.trim(); if (k) obj[k] = v; }); if (Object.keys(obj).length) content.push(obj); }); } result.push({ key: sec.key, label: sec.label, type: sec.type, content: content }); }); return result; } /** 新增栏目对话框 */ function addPjiSectionDialog() { var typeOpts = PJI_TYPE_OPTIONS.map(function (o) { return '"; }).join(""); var m = Core.modal({ title: "\u65b0\u589e\u680f\u76ee", body: '
' + '
' + '
\u680f\u76ee\u6807\u8bc6\u7b26\u5c06\u6839\u636e\u540d\u79f0\u81ea\u52a8\u751f\u6210\uff0c\u5185\u5bb9\u683c\u5f0f\u4e0e\u7ef4\u5ea6\u683c\u5f0f\u4e00\u81f4\u3002
', footer: '' }); $(".mf-cancel", m.root).onclick = m.close; $(".mf-ok", m.root).onclick = function () { var label = $("#ns-label", m.root).value.trim(); if (!label) { Core.toast("\u8bf7\u8f93\u5165\u680f\u76ee\u540d\u79f0", "err"); return; } var type = $("#ns-type", m.root).value; var key = label.replace(/[^a-zA-Z0-9\u4e00-\u9fff]/g, "").replace(/^[0-9]+/, "") || ("sec_" + Date.now()); var sections = pjiGetSections(); sections.push({ key: key, label: label, type: type, content: type === "string" ? "" : [] }); pjiSetSections(sections); m.close(); drawPjiSections(); Core.toast("\u680f\u76ee\u300c" + label + "\u300d\u5df2\u6dfb\u52a0", "ok"); }; } /** AI 预设项目资料对话框 */ function aiPresetDialog() { var base = state.detail.Base || {}; // 拉取模型配置列表 Core.api("modelsup/list", { page: 1, page_size: 50 }).then(function (data) { var sups = (data && data.list) || []; if (sups.length === 0) { Core.toast("尚未配置模型提供方,请先到「模型配置」添加", "err"); return; } var options = sups.map(function (s) { var selected = (s.sup_id === base.model_sup_id) ? " selected" : ""; return '"; }).join(""); var m = Core.modal({ title: "✦ AI 预设项目资料", width: "wide", body: '
AI 将根据项目名称和描述,自动生成 2~4 个项目资料栏目。生成后可在编辑器中继续调整。
' + '
' + '
' + '
' + '
' + '
' + '
' + '
', footer: '' + '' + '' }); var resultEl = $("#aip-result", m.root); $(".mf-close", m.root).onclick = m.close; $(".mf-gen", m.root).onclick = function () { var supId = Number($("#aip-sup", m.root).value); if (!supId) { Core.toast("请选择模型配置", "err"); return; } Core.loading(true, "AI 生成中,请稍候…"); Core.api("project/data/generate", { projectId: state.project.projectId, supId: supId }) .then(function (data) { Core.loading(false); // data 是后端返回的展平 map,直接格式化显示 resultEl.value = Core.prettyJson(data); $(".mf-apply", m.root).disabled = !resultEl.value.trim(); Core.toast("生成完成", "ok"); }) .catch(function (e) { Core.loading(false); Core.toast(e.message, "err"); }); }; $(".mf-apply", m.root).onclick = function () { var content = resultEl.value.trim(); if (!content) return; try { var dataObj = JSON.parse(content); // 将展平数据转为栏目数组 var newSections = buildSectionsFromData(dataObj); if (!newSections.length) { Core.toast("未解析到有效栏目", "err"); return; } // 先同步当前 DOM 数据,再追加新栏目 syncPjiSectionsFromDom(); var existing = state.pjiSections || []; state.pjiSections = existing.concat(newSections); drawPjiSections(); m.close(); Core.toast("已添加 " + newSections.length + " 个栏目,请检查后保存", "ok"); } catch (e) { Core.toast("解析生成结果失败:" + e.message, "err"); } }; }).catch(function (e) { Core.toast(e.message, "err"); }); }