@model UserScenariosViewModel
|
<!DOCTYPE html>
|
<html lang="zh-CN">
|
<head>
|
<meta charset="UTF-8" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<title>我的场景设置 - Board</title>
|
<link rel="stylesheet" href="@Model.BasePath/static/style.css" />
|
</head>
|
<body class="page-home page-user-scenarios">
|
<div class="shell">
|
<header class="topbar">
|
<div class="brand brand-nav">
|
<a href="@Model.BasePath/">Board</a>
|
<span class="brand-sep" aria-hidden="true">·</span>
|
<span class="brand-current">我的场景设置</span>
|
</div>
|
<div class="userbar">
|
<a class="userbar-back" href="@Model.BasePath/">返回首页</a>
|
<span class="userbar-sep" aria-hidden="true">|</span>
|
<span class="username">@Model.Username</span>
|
<button type="button" class="btn ghost" id="btnLogout">退出</button>
|
</div>
|
</header>
|
|
<div class="home-body cfg-body">
|
<section class="panel">
|
<h2>已有场景(仅本人可见)</h2>
|
@if (Model.UserScenarios.Count == 0)
|
{
|
<p class="muted">暂无自建场景</p>
|
}
|
else
|
{
|
<div class="user-scn-grid">
|
@foreach (var sc in Model.UserScenarios)
|
{
|
<div class="user-scn-card" data-scenario-id="@sc.Id">
|
<img class="user-scn-thumb" src="@(Model.BasePath + "/static/" + sc.Image)" alt="" loading="lazy" />
|
<div class="user-scn-name">@sc.Name</div>
|
<div class="user-scn-actions">
|
<button type="button" class="btn ghost user-scn-del">删除</button>
|
</div>
|
</div>
|
}
|
</div>
|
}
|
</section>
|
|
<section class="panel">
|
<h2>新增场景</h2>
|
<div class="user-scn-form">
|
<label class="user-scn-field">
|
<span>场景名称(本用户内不可重复)</span>
|
<input id="newScnName" type="text" maxlength="50" placeholder="例如:我的日报" />
|
</label>
|
<p class="muted user-scn-tip">选择 page → 设置顺序与停留时间(秒,@Model.StaytimeMin~@Model.StaytimeMax)。封面图默认取“排序最小”的第一个 page 的图标。</p>
|
<div class="user-scn-table-wrap">
|
<table class="user-scn-table">
|
<thead>
|
<tr>
|
<th scope="col">选</th>
|
<th scope="col">pageId</th>
|
<th scope="col">title</th>
|
<th scope="col">顺序</th>
|
<th scope="col">staytime(秒)</th>
|
</tr>
|
</thead>
|
<tbody>
|
@{
|
var idx = 0;
|
}
|
@foreach (var p in Model.AllPages)
|
{
|
idx++;
|
<tr data-page-id="@p.Id">
|
<td><input type="checkbox" class="pg-check" /></td>
|
<td><code>@p.Id</code></td>
|
<td>@p.Title</td>
|
<td><input type="number" class="pg-order" value="@idx" min="1" step="1" /></td>
|
<td><input type="number" class="pg-stay" value="10" min="@Model.StaytimeMin" max="@Model.StaytimeMax" step="1" /></td>
|
</tr>
|
}
|
</tbody>
|
</table>
|
</div>
|
<div class="user-scn-submit-row">
|
<button type="button" class="btn primary" id="btnCreateUserScn">创建</button>
|
<span id="createMsg" class="user-scn-msg" hidden></span>
|
</div>
|
</div>
|
</section>
|
</div>
|
</div>
|
|
<script>
|
(function () {
|
const base = "@Model.BasePath";
|
document.getElementById("btnLogout").addEventListener("click", async () => {
|
await fetch(base + "/api/logout", { method: "POST", credentials: "include" });
|
location.href = base + "/login";
|
});
|
|
function showMsg(el, text, ok) {
|
el.textContent = text;
|
el.hidden = false;
|
el.className = "user-scn-msg " + (ok ? "user-scn-msg--ok" : "user-scn-msg--err");
|
}
|
|
document.body.addEventListener("click", async (e) => {
|
const delBtn = e.target.closest(".user-scn-del");
|
if (!delBtn) return;
|
const card = delBtn.closest(".user-scn-card");
|
const scenarioId = card ? card.getAttribute("data-scenario-id") : "";
|
if (!scenarioId) return;
|
delBtn.disabled = true;
|
try {
|
const res = await fetch(base + "/api/user-scenarios/delete", {
|
method: "POST",
|
credentials: "include",
|
headers: { "Content-Type": "application/json" },
|
body: JSON.stringify({ scenarioId }),
|
});
|
const data = await res.json().catch(() => ({}));
|
if (!res.ok || !data.ok) {
|
alert(data.error || "删除失败");
|
return;
|
}
|
card.remove();
|
} finally {
|
delBtn.disabled = false;
|
}
|
});
|
|
document.getElementById("btnCreateUserScn").addEventListener("click", async () => {
|
const msgEl = document.getElementById("createMsg");
|
msgEl.hidden = true;
|
const name = String(document.getElementById("newScnName").value || "").trim();
|
if (!name) {
|
showMsg(msgEl, "场景名称不能为空", false);
|
return;
|
}
|
const rows = Array.from(document.querySelectorAll(".user-scn-table tbody tr"));
|
const selected = rows
|
.map((tr) => {
|
const pageId = tr.getAttribute("data-page-id");
|
const checked = tr.querySelector(".pg-check").checked;
|
if (!checked) return null;
|
const order = parseInt(String(tr.querySelector(".pg-order").value || ""), 10);
|
const staytime = parseInt(String(tr.querySelector(".pg-stay").value || ""), 10);
|
return { pageId, order, staytime };
|
})
|
.filter(Boolean);
|
if (!selected.length) {
|
showMsg(msgEl, "至少选择 1 个 page", false);
|
return;
|
}
|
for (const it of selected) {
|
if (!Number.isInteger(it.order) || it.order < 1) {
|
showMsg(msgEl, "顺序必须为 >= 1 的整数", false);
|
return;
|
}
|
if (!Number.isInteger(it.staytime) || it.staytime < @Model.StaytimeMin || it.staytime > @Model.StaytimeMax) {
|
showMsg(msgEl, "staytime 必须为 @Model.StaytimeMin~@Model.StaytimeMax 的整数(秒)", false);
|
return;
|
}
|
}
|
selected.sort((a, b) => a.order - b.order);
|
const pages = selected.map((x) => ({ pageId: x.pageId, staytime: x.staytime }));
|
|
const btn = document.getElementById("btnCreateUserScn");
|
btn.disabled = true;
|
try {
|
const res = await fetch(base + "/api/user-scenarios/create", {
|
method: "POST",
|
credentials: "include",
|
headers: { "Content-Type": "application/json" },
|
body: JSON.stringify({ name, pages }),
|
});
|
const data = await res.json().catch(() => ({}));
|
if (!res.ok || !data.ok) {
|
showMsg(msgEl, data.error || "创建失败", false);
|
return;
|
}
|
showMsg(msgEl, "已创建,刷新页面可看到", true);
|
} finally {
|
btn.disabled = false;
|
}
|
});
|
})();
|
</script>
|
</body>
|
</html>
|
|