This commit is contained in:
qsc
2026-09-04 22:38:16 +08:00
parent 3845950862
commit 9c9808adf7
30 changed files with 658 additions and 510 deletions
+119 -46
View File
@@ -1,5 +1,6 @@
const bridge = window.AstrBotPluginPage;
const state = { bindings: [], subscriptions: [], aliases: [], kungfu: [], servers: [], events: {} };
const editing = { aliasServer: null, kungfuPzid: null };
let toastTimer;
const byId = (id) => document.getElementById(id);
@@ -32,6 +33,42 @@ function emptyRow(columnCount, message) {
return row;
}
function aliasText(aliases) {
return aliases.length ? aliases.join("、") : "无";
}
function inlineAliasEditor(aliases, label, onSave, onCancel) {
const input = document.createElement("input");
input.className = "inline-editor";
input.value = aliases.join(", ");
input.placeholder = "多个别名用逗号分隔";
input.setAttribute("aria-label", label);
const saveButton = button("保存", "", async () => {
saveButton.disabled = true;
cancelButton.disabled = true;
const saved = await onSave(input.value);
if (!saved) {
saveButton.disabled = false;
cancelButton.disabled = false;
input.focus();
}
});
const cancelButton = button("取消", "", onCancel);
input.addEventListener("keydown", (event) => {
if (event.key === "Enter") {
event.preventDefault();
saveButton.click();
} else if (event.key === "Escape") {
event.preventDefault();
cancelButton.click();
}
});
queueMicrotask(() => input.focus());
return { input, controls: [saveButton, cancelButton] };
}
function bindingMap() {
return new Map(state.bindings.map((item) => [item.session_id, item.server]));
}
@@ -128,11 +165,22 @@ function renderSubscriptions() {
function renderAliases() {
const body = byId("aliases-body");
if (!state.aliases.length) {
body.replaceChildren(emptyRow(3, "暂无区服别名配置"));
const aliasesByServer = new Map(
state.aliases.map((item) => [item.server, item.aliases]),
);
const servers = [...new Set([
...state.servers,
...aliasesByServer.keys(),
])].sort((left, right) => left.localeCompare(right, "zh-CN"));
if (!servers.length) {
body.replaceChildren(emptyRow(3, "暂无标准区服数据"));
return;
}
body.replaceChildren(...state.aliases.map((item) => {
body.replaceChildren(...servers.map((serverName) => {
const item = {
server: serverName,
aliases: aliasesByServer.get(serverName) || [],
};
const row = document.createElement("tr");
const server = document.createElement("td");
const aliases = document.createElement("td");
@@ -141,19 +189,40 @@ function renderAliases() {
aliases.dataset.label = "别名";
actions.dataset.label = "操作";
server.textContent = item.server;
aliases.textContent = item.aliases.length ? item.aliases.join("、") : "无";
aliases.className = "alias-cell";
actions.className = "actions";
actions.append(
button("编辑", "", () => {
byId("alias-server").value = item.server;
byId("alias-values").value = item.aliases.join(", ");
byId("alias-values").focus();
}),
button("删除", "link-button--danger", async () => {
if (!window.confirm(`确认删除 ${item.server} 的全部别名?`)) return;
await mutate("aliases/delete", { server: item.server }, "别名已删除");
}),
);
if (editing.aliasServer === item.server) {
row.classList.add("is-editing");
const editor = inlineAliasEditor(
item.aliases,
`${item.server}的区服别名`,
async (value) => {
editing.aliasServer = null;
const saved = await mutate(
"aliases/save",
{ server: item.server, aliases: value },
"区服别名已保存",
);
if (!saved) {
editing.aliasServer = item.server;
renderAliases();
}
return saved;
},
() => {
editing.aliasServer = null;
renderAliases();
},
);
aliases.append(editor.input);
actions.append(...editor.controls);
} else {
aliases.textContent = aliasText(item.aliases);
actions.append(button("编辑", "", () => {
editing.aliasServer = item.server;
renderAliases();
}));
}
row.append(server, aliases, actions);
return row;
}));
@@ -162,30 +231,53 @@ function renderAliases() {
function renderKungfu() {
const body = byId("kungfu-body");
if (!state.kungfu.length) {
body.replaceChildren(emptyRow(4, "暂无心法配置"));
body.replaceChildren(emptyRow(3, "暂无心法配置"));
return;
}
body.replaceChildren(...state.kungfu.map((item) => {
const row = document.createElement("tr");
const pzid = document.createElement("td");
const name = document.createElement("td");
const aliases = document.createElement("td");
const actions = document.createElement("td");
pzid.dataset.label = "心法 ID";
name.dataset.label = "标准心法名";
aliases.dataset.label = "别名";
actions.dataset.label = "操作";
pzid.textContent = String(item.pzid);
name.textContent = item.name;
aliases.textContent = item.aliases.length ? item.aliases.join("、") : "无";
aliases.className = "alias-cell";
actions.className = "actions";
actions.append(button("编辑", "", () => {
byId("kungfu-pzid").value = String(item.pzid);
byId("kungfu-name").value = item.name;
byId("kungfu-aliases").value = item.aliases.join(", ");
byId("kungfu-aliases").focus();
}));
row.append(pzid, name, aliases, actions);
if (editing.kungfuPzid === item.pzid) {
row.classList.add("is-editing");
const editor = inlineAliasEditor(
item.aliases,
`${item.name}的心法别名`,
async (value) => {
editing.kungfuPzid = null;
const saved = await mutate(
"kungfu/save",
{ pzid: item.pzid, aliases: value },
"心法别名已保存",
);
if (!saved) {
editing.kungfuPzid = item.pzid;
renderKungfu();
}
return saved;
},
() => {
editing.kungfuPzid = null;
renderKungfu();
},
);
aliases.append(editor.input);
actions.append(...editor.controls);
} else {
aliases.textContent = aliasText(item.aliases);
actions.append(button("编辑", "", () => {
editing.kungfuPzid = item.pzid;
renderKungfu();
}));
}
row.append(name, aliases, actions);
return row;
}));
}
@@ -241,25 +333,6 @@ byId("binding-form").addEventListener("submit", async (event) => {
if (saved) event.currentTarget.reset();
});
byId("alias-form").addEventListener("submit", async (event) => {
event.preventDefault();
const saved = await mutate("aliases/save", {
server: byId("alias-server").value,
aliases: byId("alias-values").value,
}, "区服别名已保存");
if (saved) event.currentTarget.reset();
});
byId("kungfu-form").addEventListener("submit", async (event) => {
event.preventDefault();
const saved = await mutate("kungfu/save", {
pzid: byId("kungfu-pzid").value,
name: byId("kungfu-name").value,
aliases: byId("kungfu-aliases").value,
}, "心法配置已保存");
if (saved) event.currentTarget.reset();
});
byId("refresh").addEventListener("click", async (event) => {
const control = event.currentTarget;
control.disabled = true;
+7 -27
View File
@@ -76,17 +76,9 @@
</section>
<section class="panel" id="aliases-panel" role="tabpanel" hidden>
<form class="editor editor--aliases" id="alias-form">
<label>
<span>标准区服名</span>
<input id="alias-server" name="server" maxlength="64" list="server-options" placeholder="例如:梦江南" required />
</label>
<label>
<span>别名(多个别名用逗号分隔)</span>
<input id="alias-values" name="aliases" placeholder="例如:梦江、梦江南服、MJ" />
</label>
<button class="button button--primary" type="submit">保存别名</button>
</form>
<div class="section-intro">
<div><h2>区服别名</h2><p>标准区服名称为只读;点击对应行的“编辑”可修改别名。</p></div>
</div>
<div class="table-wrap">
<table>
<thead><tr><th>标准区服名</th><th>别名</th><th class="actions">操作</th></tr></thead>
@@ -96,24 +88,12 @@
</section>
<section class="panel" id="kungfu-panel" role="tabpanel" hidden>
<form class="editor editor--kungfu" id="kungfu-form">
<label>
<span>心法 IDJX3BOX 配装 ID</span>
<input id="kungfu-pzid" name="pzid" type="number" min="1" step="1" placeholder="例如:10014" required />
</label>
<label>
<span>标准心法名</span>
<input id="kungfu-name" name="name" maxlength="64" placeholder="例如:紫霞功" required />
</label>
<label>
<span>别名(最多 5 个,用逗号分隔)</span>
<input id="kungfu-aliases" name="aliases" placeholder="例如:气纯、紫霞" />
</label>
<button class="button button--primary" type="submit">保存心法</button>
</form>
<div class="section-intro">
<div><h2>心法别名</h2><p>标准心法名称为只读;点击对应行的“编辑”可修改最多 5 个别名。</p></div>
</div>
<div class="table-wrap">
<table>
<thead><tr><th>心法 ID</th><th>标准心法名</th><th>别名</th><th class="actions">操作</th></tr></thead>
<thead><tr><th>标准心法名</th><th>别名</th><th class="actions">操作</th></tr></thead>
<tbody id="kungfu-body"></tbody>
</table>
</div>
+6 -3
View File
@@ -59,8 +59,6 @@ h1 { margin: 0; font-size: clamp(26px, 3vw, 36px); line-height: 1.2; letter-spac
.panel { margin-top: 18px; background: var(--surface); border: 1px solid var(--border); border-radius: 12px; box-shadow: var(--shadow); overflow: hidden; }
.panel[hidden] { display: none; }
.editor { display: grid; grid-template-columns: minmax(260px, 1.35fr) minmax(210px, 1fr) auto; align-items: end; gap: 16px; padding: 22px; background: var(--surface-subtle); border-bottom: 1px solid var(--border); }
.editor--aliases { grid-template-columns: minmax(220px, .8fr) minmax(320px, 1.5fr) auto; }
.editor--kungfu { grid-template-columns: minmax(210px, .65fr) minmax(210px, .8fr) minmax(300px, 1.35fr) auto; }
label { display: grid; gap: 7px; color: var(--text); font-size: 13px; font-weight: 650; }
input { width: 100%; height: 42px; padding: 8px 12px; border: 1px solid var(--border); border-radius: 8px; background: var(--surface); color: var(--text); transition: border 160ms ease, box-shadow 160ms ease; }
input::placeholder { color: color-mix(in srgb, var(--muted) 76%, transparent); }
@@ -73,10 +71,14 @@ th, td { padding: 15px 20px; text-align: center; vertical-align: middle; border-
th { background: var(--surface-subtle); color: var(--muted); font-size: 13px; font-weight: 700; }
tbody tr:last-child td { border-bottom: 0; }
tbody tr:hover { background: color-mix(in srgb, var(--accent-soft) 42%, transparent); }
tbody tr.is-editing { background: color-mix(in srgb, var(--accent-soft) 62%, transparent); }
.alias-cell { min-width: 320px; }
.inline-editor { min-width: 260px; }
.actions { white-space: nowrap; }
.link-button { padding: 4px 7px; border: 0; border-radius: 5px; background: transparent; color: #3568ae; cursor: pointer; font-weight: 650; }
[data-theme="dark"] .link-button { color: #8db9f4; }
.link-button:hover { background: color-mix(in srgb, currentColor 10%, transparent); }
.link-button:disabled { cursor: wait; opacity: .55; }
.link-button--danger { color: var(--danger); }
.state { display: inline-flex; align-items: center; gap: 7px; font-weight: 650; }
.state::before { width: 8px; height: 8px; border-radius: 50%; background: currentColor; content: ""; }
@@ -97,7 +99,7 @@ tbody tr:hover { background: color-mix(in srgb, var(--accent-soft) 42%, transpar
.summary__item + .summary__item { border-top: 1px solid var(--border); border-left: 0; }
.tabs { gap: 16px; overflow-x: auto; }
.tab { flex: 0 0 auto; }
.editor, .editor--aliases, .editor--kungfu { grid-template-columns: 1fr; }
.editor { grid-template-columns: 1fr; }
.table-wrap { overflow: visible; }
table, tbody, tr, td { display: block; width: 100%; }
thead { display: none; }
@@ -106,6 +108,7 @@ tbody tr:hover { background: color-mix(in srgb, var(--accent-soft) 42%, transpar
td { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: 12px; padding: 8px 16px; border: 0; overflow-wrap: anywhere; }
td::before { color: var(--muted); content: attr(data-label); font-size: 12px; font-weight: 700; }
td.actions { grid-template-columns: max-content auto auto; align-items: center; width: 100%; white-space: normal; }
.alias-cell, .inline-editor { min-width: 0; }
td.empty { display: block; padding: 36px 16px; }
td.empty::before { content: none; }
}