wangzhibo
2026-05-09 53418655c061ac26f66058253f39200aced1ab1d
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
@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>