多出口节点重设计 + 全局错误提示
- 选择/随机/战斗节点改为每出口一行、右对齐、行高25px对齐右侧端口 - 去掉重复的选项合并预览,顶部改为紧凑角标 - 未捕获错误/操作失败统一 toast 提示,便于同事发现反馈
This commit is contained in:
@ -281,6 +281,13 @@
|
||||
clearTimeout(toastTimer);
|
||||
toastTimer = setTimeout(() => el.classList.remove("show"), 2600);
|
||||
}
|
||||
// 未捕获错误 → toast 提示(便于同事发现并反馈,而不是默默白屏)
|
||||
window.addEventListener("error", e => { try { toast("⚠ 页面出错:" + (e.message || "未知")); } catch (_) {} });
|
||||
window.addEventListener("unhandledrejection", e => {
|
||||
const m = String((e.reason && e.reason.message) || e.reason || "未知");
|
||||
if (m.includes("未授权")) return;
|
||||
try { toast("⚠ 操作失败:" + m); } catch (_) {}
|
||||
});
|
||||
|
||||
// ---------- 撤销 / 重做(Ctrl+Z / Ctrl+Y) ----------
|
||||
let undoStack = [], redoStack = [], snapTimer = null;
|
||||
|
||||
@ -41,11 +41,11 @@
|
||||
function getOutlets(node) {
|
||||
const k = node.kind;
|
||||
if (k === "choice" || k === "choice_once")
|
||||
return (node.options || []).map((o, i) => ({ label: "选" + (i + 1) + (o.text ? "·" + o.text.slice(0, 6) : ""), target: o.goto || "" }));
|
||||
return (node.options || []).map((o, i) => ({ label: (i + 1) + ". " + (o.text ? o.text.slice(0, 14) : "(空选项)"), target: o.goto || "" }));
|
||||
if (k === "random")
|
||||
return (node.branches || []).map((b) => ({ label: "权" + (b.weight != null ? b.weight : ""), target: b.goto || "" }));
|
||||
return (node.branches || []).map((b, i) => ({ label: (i + 1) + ". 权重 " + (b.weight != null ? b.weight : "?"), target: b.goto || "" }));
|
||||
if (k === "fight")
|
||||
return [{ label: "胜", target: node.win || "" }, { label: "败", target: node.lose || "" }];
|
||||
return [{ label: "① 胜", target: node.win || "" }, { label: "② 败", target: node.lose || "" }];
|
||||
if (isEnding(node)) return [];
|
||||
return [{ label: "next", target: node.next || "" }]; // 线性
|
||||
}
|
||||
@ -63,19 +63,25 @@
|
||||
// ---------- 节点 HTML ----------
|
||||
function nodeInner(ir, node, isStart) {
|
||||
const names = roleNames(ir), end = isEnding(node);
|
||||
const sm = summary(ir, names, end ? Object.assign({ kind: "ending" }, node) : node);
|
||||
let h = '<div class="nid">' + (isStart ? '<span class="startflag">▶ 开头</span> ' : '') + '#' + esc(node.id) + '</div>'
|
||||
+ '<div class="k">' + esc(sm[0]) + '</div>'
|
||||
+ '<div class="t">' + esc(sm[1] || "") + '</div>';
|
||||
const startTag = isStart ? '<span class="startflag">▶ 开头</span> ' : '';
|
||||
if (end) {
|
||||
const sm = summary(ir, names, Object.assign({ kind: "ending" }, node));
|
||||
const g = (node.grants && node.grants.length) ? node.grants.map(gr => grantStr(ir, names, gr)).join(",") : "无奖励";
|
||||
h += '<div class="rw">' + esc(g) + '</div>';
|
||||
} else {
|
||||
const outs = getOutlets(node);
|
||||
if (outs.length > 1)
|
||||
h += '<div class="outs">' + outs.map((o, i) => '<span>' + (i + 1) + "·" + esc(o.label) + '</span>').join("") + '</div>';
|
||||
return '<div class="nid">' + startTag + '#' + esc(node.id) + '</div>'
|
||||
+ '<div class="k">' + esc(sm[0]) + '</div><div class="t">' + esc(sm[1] || "") + '</div>'
|
||||
+ '<div class="rw">' + esc(g) + '</div>';
|
||||
}
|
||||
return h;
|
||||
const outs = getOutlets(node);
|
||||
// 多出口节点(选择/随机/战斗):顶部角标 + 每出口一行(右对齐,行高匹配端口间距 → 与黄点平齐)
|
||||
if (outs.length > 1) {
|
||||
const head = summary(ir, names, node)[0];
|
||||
return '<div class="ch-tag">' + (isStart ? '<span class="startflag">▶</span> ' : '') + '<span class="nid">#' + esc(node.id) + '</span> ' + esc(head) + '</div>'
|
||||
+ '<div class="ch-opts">' + outs.map(o => '<div class="ch-opt" title="' + esc(o.label) + '">' + esc(o.label) + '</div>').join("") + '</div>';
|
||||
}
|
||||
// 线性 / 单出口节点
|
||||
const sm = summary(ir, names, node);
|
||||
return '<div class="nid">' + startTag + '#' + esc(node.id) + '</div>'
|
||||
+ '<div class="k">' + esc(sm[0]) + '</div><div class="t">' + esc(sm[1] || "") + '</div>';
|
||||
}
|
||||
|
||||
// ---------- 自动布局:最长路径分层 + 每层顺序铺开 ----------
|
||||
|
||||
@ -117,6 +117,21 @@ button.mini { padding:2px 8px; font-size:12px; }
|
||||
#drawflow .drawflow-node.isstart { border-color:#7ad88a; }
|
||||
.drawflow-node .startflag { color:#7ad88a; font-weight:bold; }
|
||||
|
||||
/* 多出口节点:每出口一行,与右侧端口(间距25px、垂直居中)平齐 */
|
||||
#drawflow .kind-choice .drawflow_content_node,
|
||||
#drawflow .kind-choice_once .drawflow_content_node,
|
||||
#drawflow .kind-random .drawflow_content_node,
|
||||
#drawflow .kind-fight .drawflow_content_node { position:relative; }
|
||||
.ch-tag { position:absolute; top:-14px; left:0; right:0; font-size:11px; font-weight:bold;
|
||||
white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
|
||||
.ch-tag .nid { color:#6a6256; font-weight:normal; }
|
||||
#drawflow .kind-choice .ch-tag, #drawflow .kind-choice_once .ch-tag { color:#9ec0f0; }
|
||||
#drawflow .kind-fight .ch-tag { color:#d87878; }
|
||||
#drawflow .kind-random .ch-tag { color:#c0a0e0; }
|
||||
.ch-opts { margin-top:2px; } /* 微调对齐端口 top:2px */
|
||||
.ch-opt { height:25px; line-height:25px; text-align:right; font-size:12px; color:#dfe7f2;
|
||||
overflow:hidden; text-overflow:ellipsis; white-space:nowrap; padding-right:3px; }
|
||||
|
||||
/* ---- toast ---- */
|
||||
#toast { position:fixed; left:50%; bottom:38px; transform:translateX(-50%) translateY(10px);
|
||||
background:#2a2316; color:#f3dca0; border:1px solid #6a5630; border-radius:7px;
|
||||
|
||||
Reference in New Issue
Block a user