first commit
This commit is contained in:
commit
fda3eb683c
50 files changed
+12264
No files matched your search
@@ -0,0 +1,44 @@
|
||||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>后台管理 - Echo Tavern</title>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}" />
|
||||
</head>
|
||||
<body class="admin-page" data-user='{{ user | tojson | safe if user else "null" }}' data-theme="{{ user.theme if user else 'dopamine' }}">
|
||||
<header class="lobby-topbar">
|
||||
<div class="brand-block">
|
||||
<h1>后台管理</h1>
|
||||
<p>管理员控制台:用户权限、房间状态、游戏卡治理</p>
|
||||
</div>
|
||||
<div class="top-actions">
|
||||
<span class="user-badge {% if user.avatar_url %}has-avatar{% endif %}" style="--avatar-color: {{ user.avatar_color }}; --avatar-url: url('{{ user.avatar_url }}')">
|
||||
<i></i>{{ user.username }}(管理员)
|
||||
</span>
|
||||
<a class="link-btn" href="{{ url_for('index') }}">返回大厅</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="admin-grid">
|
||||
<section class="card admin-panel">
|
||||
<h2>用户权限管理</h2>
|
||||
<div id="users-table" class="table-wrap"></div>
|
||||
</section>
|
||||
|
||||
<section class="card admin-panel">
|
||||
<h2>房间与游戏监控</h2>
|
||||
<div id="rooms-table" class="table-wrap"></div>
|
||||
</section>
|
||||
|
||||
<section class="card admin-panel">
|
||||
<h2>游戏卡治理</h2>
|
||||
<div id="cards-table" class="table-wrap"></div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<section id="status" class="status-banner"></section>
|
||||
<script src="{{ url_for('static', filename='js/theme.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/admin.js') }}"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,235 @@
|
||||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>游戏卡编辑器 - Echo Tavern</title>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}" />
|
||||
</head>
|
||||
<body class="card-editor-page" data-user='{{ user | tojson | safe if user else "null" }}' data-card-id="{{ card_id }}" data-theme="{{ user.theme if user else 'dopamine' }}">
|
||||
<header class="lobby-topbar">
|
||||
<div class="brand-block">
|
||||
<h1 id="editor-page-title">游戏卡编辑器</h1>
|
||||
<p id="editor-page-subtitle">创建可复用的多人文字冒险卡,支持多状态条、结束条件与扩展系统。</p>
|
||||
</div>
|
||||
<div class="top-actions">
|
||||
<a class="link-btn" href="{{ url_for('plaza_page') }}">返回广场</a>
|
||||
<a class="link-btn" href="{{ url_for('index') }}">返回大厅</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="card-editor-shell">
|
||||
<section class="card editor-form-card">
|
||||
<section class="assist-panel">
|
||||
<h3>AI 辅助写卡</h3>
|
||||
<label for="assist-concept">创意草案</label>
|
||||
<textarea id="assist-concept" maxlength="660" placeholder="例如:蒸汽飞艇陷入时空风暴,玩家要在资源耗尽前决定返航或闯入未知空域。"></textarea>
|
||||
<div class="assist-grid">
|
||||
<div>
|
||||
<label for="assist-tone">风格</label>
|
||||
<input id="assist-tone" maxlength="72" placeholder="例如:热血科幻" />
|
||||
</div>
|
||||
<div>
|
||||
<label for="assist-difficulty">难度</label>
|
||||
<input id="assist-difficulty" maxlength="36" placeholder="例如:中高" />
|
||||
</div>
|
||||
</div>
|
||||
<button id="assist-btn" type="button" class="secondary-btn">AI 生成草稿并填充</button>
|
||||
<div id="assist-loading" class="assist-loading" hidden>
|
||||
<span class="assist-spinner" aria-hidden="true"></span>
|
||||
<span>AI 正在生成游戏卡草稿,请稍候</span>
|
||||
<span class="assist-dots" aria-hidden="true"><i></i><i></i><i></i></span>
|
||||
</div>
|
||||
|
||||
<label for="assist-modify-instruction">AI 修改指令(增量修改,不覆盖整卡)</label>
|
||||
<textarea id="assist-modify-instruction" maxlength="960" placeholder="例如:我想增加一个天赋系统,每个玩家开局可选 1 个天赋,并在系统规则里写清楚。"></textarea>
|
||||
<button id="assist-modify-btn" type="button" class="secondary-btn">AI 按指令修改当前内容</button>
|
||||
<div id="assist-modify-loading" class="assist-loading" hidden>
|
||||
<span class="assist-spinner" aria-hidden="true"></span>
|
||||
<span>AI 正在按指令增量修改,请稍候</span>
|
||||
<span class="assist-dots" aria-hidden="true"><i></i><i></i><i></i></span>
|
||||
</div>
|
||||
<ul id="assist-notes" class="compact-list"></ul>
|
||||
</section>
|
||||
|
||||
<form id="card-form" class="editor-form-grid">
|
||||
<h2>基础信息</h2>
|
||||
<label for="card-title">标题</label>
|
||||
<input id="card-title" maxlength="84" required placeholder="例如:零号机库" />
|
||||
|
||||
<label for="card-subtitle">副标题</label>
|
||||
<input id="card-subtitle" maxlength="84" placeholder="例如:战斗生存 / 阵营博弈" />
|
||||
|
||||
<label for="card-summary">简介</label>
|
||||
<textarea id="card-summary" maxlength="420" required placeholder="一句话说明世界、目标与玩法"></textarea>
|
||||
|
||||
<label for="card-cover">封面 URL</label>
|
||||
<input id="card-cover" placeholder="推荐 16:9 图片" />
|
||||
|
||||
<label for="card-room-bg">房间背景图 URL</label>
|
||||
<input id="card-room-bg" placeholder="游戏中全屏背景,不填则使用封面" />
|
||||
|
||||
<label for="card-opening">开场描述</label>
|
||||
<textarea id="card-opening" maxlength="660" required placeholder="玩家进入房间后第一段看到的叙述"></textarea>
|
||||
|
||||
<label for="card-goals">新手引导目标(逗号或换行)</label>
|
||||
<textarea id="card-goals" maxlength="840" placeholder="先确认局势, 与关键 NPC 对话, 提交第一轮行动"></textarea>
|
||||
|
||||
<h2>界面与分区</h2>
|
||||
<label for="card-inventory-label">背包区名称</label>
|
||||
<input id="card-inventory-label" maxlength="48" placeholder="例如:装备库" />
|
||||
|
||||
<label for="card-clues-label">线索区名称</label>
|
||||
<input id="card-clues-label" maxlength="48" placeholder="例如:战况简报" />
|
||||
|
||||
<label for="card-choices-label">快速行动区名称</label>
|
||||
<input id="card-choices-label" maxlength="48" placeholder="例如:AI 战术建议" />
|
||||
|
||||
<label for="card-scene-label">场景字段名称</label>
|
||||
<input id="card-scene-label" maxlength="48" placeholder="例如:当前战区" />
|
||||
|
||||
<label for="card-hero-status-label">主状态条名称</label>
|
||||
<input id="card-hero-status-label" maxlength="48" placeholder="例如:前线压力" />
|
||||
|
||||
<label for="card-hero-status-key">主状态条字段 key</label>
|
||||
<input id="card-hero-status-key" maxlength="72" placeholder="例如:front_pressure(留空默认第一个状态)" />
|
||||
|
||||
<label for="card-hero-status-color">主状态条颜色</label>
|
||||
<input id="card-hero-status-color" type="color" value="#ffb500" />
|
||||
|
||||
<h2>数据作用域(共享/个人)</h2>
|
||||
<label for="card-data-scope-json">数据作用域 JSON</label>
|
||||
<textarea id="card-data-scope-json" class="code-input" spellcheck="false" placeholder='{
|
||||
"metrics": "per_player",
|
||||
"inventory": "per_player",
|
||||
"clues": "shared"
|
||||
}'></textarea>
|
||||
<p class="json-help">
|
||||
作用:决定哪些数据是“每位玩家独立”,哪些是“全队共享”。可选值只有 <code>shared</code> 或 <code>per_player</code>。
|
||||
推荐:线索 <code>shared</code>,背包和状态 <code>per_player</code>。
|
||||
</p>
|
||||
|
||||
<label for="card-visibility-json">可见性 JSON</label>
|
||||
<textarea id="card-visibility-json" class="code-input" spellcheck="false" placeholder='{
|
||||
"show_all_player_metrics": true,
|
||||
"show_all_player_inventory": true
|
||||
}'></textarea>
|
||||
<p class="json-help">
|
||||
作用:控制房间是否展示“全员状态”和“全员背包”看板。填 <code>true</code>/<code>false</code>。
|
||||
</p>
|
||||
|
||||
<h2>数值系统</h2>
|
||||
<label for="card-metrics-json">状态字段 JSON(支持任意范围,不限 0-10)</label>
|
||||
<textarea id="card-metrics-json" class="code-input" spellcheck="false" placeholder='[
|
||||
{"key":"hp","label":"队伍生命","min":0,"max":200,"initial":160,"color":"#ff5a4d"},
|
||||
{"key":"morale","label":"士气","min":-50,"max":50,"initial":0,"color":"#ffd166"},
|
||||
{"key":"threat","label":"威胁等级","min":0,"max":99,"initial":20,"color":"#6aa3ff"}
|
||||
]'></textarea>
|
||||
<p class="json-help">
|
||||
字段说明:<code>key</code> 英文唯一标识(如 hp)、<code>label</code> 中文名、<code>min/max</code> 范围、<code>initial</code> 初始值、<code>color</code> 颜色。
|
||||
</p>
|
||||
|
||||
<label for="card-status-tracks-json">显示状态条 JSON(可多条)</label>
|
||||
<textarea id="card-status-tracks-json" class="code-input" spellcheck="false" placeholder='[
|
||||
{"key":"hp","label":"队伍生命","min":0,"max":200,"color":"#ff5a4d"},
|
||||
{"key":"threat","label":"威胁等级","min":0,"max":99,"color":"#6aa3ff"}
|
||||
]'></textarea>
|
||||
<p class="json-help">
|
||||
这里决定“顶部会显示哪些状态条”。<code>key</code> 必须来自状态字段 JSON。你可以展示 1 条或多条。
|
||||
</p>
|
||||
|
||||
<label for="card-end-conditions-json">结束条件 JSON(由游戏卡定义)</label>
|
||||
<textarea id="card-end-conditions-json" class="code-input" spellcheck="false" placeholder='[
|
||||
{"id":"victory","metric":"threat","op":"<=","value":5,"flag":"core_boss_defeated","flag_equals":true,"ending_state":"victory","ending_summary":"核心敌人被击破,战区恢复秩序。"},
|
||||
{"id":"wipe","metric":"hp","op":"<=","value":0,"ending_state":"defeat","ending_summary":"队伍全灭,行动失败。"}
|
||||
]'></textarea>
|
||||
<p class="json-help">
|
||||
每条结束条件满足后会立即结束游戏。支持比较符:<code>>=</code>、<code><=</code>、<code>></code>、<code><</code>、<code>==</code>、<code>!=</code>。
|
||||
</p>
|
||||
|
||||
<label for="card-systems-json">扩展系统 JSON(战斗/骰子/技能等)</label>
|
||||
<textarea id="card-systems-json" class="code-input" spellcheck="false" placeholder='{
|
||||
"combat": {"enabled": true, "turn_order": "initiative", "damage_model": "flat+roll"},
|
||||
"dice": {"enabled": true, "formula": "d20"},
|
||||
"skills": ["射击","医疗","潜行"]
|
||||
}'></textarea>
|
||||
<p class="json-help">
|
||||
这是“高自由扩展区”。可写战斗、技能、资源、法术、阵营等结构,AI 会读取并在叙述中遵循。
|
||||
</p>
|
||||
|
||||
<h2>内容包</h2>
|
||||
<label for="card-genre-tags">题材标签(逗号分隔)</label>
|
||||
<input id="card-genre-tags" maxlength="540" placeholder="例如:科幻, 战斗, 合作, 生存" />
|
||||
|
||||
<label for="card-feature-badges">功能标签(逗号分隔)</label>
|
||||
<input id="card-feature-badges" maxlength="660" placeholder="例如:多状态条, 战斗系统, 阵营冲突, 资源管理" />
|
||||
|
||||
<label for="card-world-lore">世界观设定</label>
|
||||
<textarea id="card-world-lore" maxlength="3600"></textarea>
|
||||
|
||||
<label for="card-core-conflict">核心冲突</label>
|
||||
<textarea id="card-core-conflict" maxlength="1800"></textarea>
|
||||
|
||||
<label for="card-player-roles">玩家角色(逗号或换行)</label>
|
||||
<textarea id="card-player-roles" maxlength="1080"></textarea>
|
||||
|
||||
<label for="card-interaction-modes">互动模式(逗号或换行)</label>
|
||||
<textarea id="card-interaction-modes" maxlength="1080"></textarea>
|
||||
|
||||
<label for="card-system-rules">主持规则(逗号或换行)</label>
|
||||
<textarea id="card-system-rules" maxlength="2100"></textarea>
|
||||
|
||||
<label for="card-npc-roster">NPC 名册(逗号或换行)</label>
|
||||
<textarea id="card-npc-roster" maxlength="2400"></textarea>
|
||||
|
||||
<label for="card-scene-blueprint">场景蓝图(逗号或换行)</label>
|
||||
<textarea id="card-scene-blueprint" maxlength="2100"></textarea>
|
||||
|
||||
<label for="card-ending-catalog">结局目录(逗号或换行)</label>
|
||||
<textarea id="card-ending-catalog" maxlength="1500"></textarea>
|
||||
|
||||
<label for="card-hidden-truths">隐藏真相池(逗号或换行)</label>
|
||||
<textarea id="card-hidden-truths" maxlength="1500"></textarea>
|
||||
|
||||
<button id="save-card-btn" type="submit">保存游戏卡</button>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<aside class="card editor-guide-card">
|
||||
<h2>详细编辑说明</h2>
|
||||
<ol class="guide-list">
|
||||
<li>先写基础信息:标题、简介、开场描述。开场必须清楚“玩家要做什么”。</li>
|
||||
<li>再定数据作用域:哪些字段是个人,哪些字段是共享。</li>
|
||||
<li>然后定义状态字段(metrics):这是所有数值系统的基础。</li>
|
||||
<li>再定义状态条(status_tracks):决定房间顶部展示哪些状态。</li>
|
||||
<li>最后写结束条件(end_conditions):明确“什么情况下结局触发并终止游戏”。</li>
|
||||
<li>如果要做战斗/技能/职业,放在 systems_json,并在 system_rules 里写主持规则。</li>
|
||||
<li>已发布卡可持续修改,修改后会用于新开房间(旧房间不回溯覆盖)。</li>
|
||||
</ol>
|
||||
|
||||
<h3>完整字段模板(可直接复制)</h3>
|
||||
<ul class="compact-list">
|
||||
<li><strong>数据作用域:</strong><code>{"metrics":"per_player","inventory":"per_player","clues":"shared"}</code></li>
|
||||
<li><strong>状态字段:</strong><code>{"key":"hp","label":"生命","min":0,"max":100,"initial":80}</code></li>
|
||||
<li><strong>状态条:</strong><code>{"key":"hp","label":"生命","min":0,"max":100,"color":"#ff5a4d"}</code></li>
|
||||
<li><strong>结束条件:</strong><code>{"metric":"hp","op":"<=","value":0,"ending_state":"defeat"}</code></li>
|
||||
<li><strong>战斗扩展:</strong><code>{"combat":{"enabled":true,"turn_order":"initiative"}}</code></li>
|
||||
</ul>
|
||||
|
||||
<h3>战斗系统建议(可直接用)</h3>
|
||||
<ul class="compact-list">
|
||||
<li>在 metrics 增加:<code>hp</code>、<code>armor</code>、<code>energy</code>、<code>threat</code>。</li>
|
||||
<li>在 systems_json 写:攻击公式、暴击规则、先攻顺序、技能列表。</li>
|
||||
<li>在 system_rules 写:每轮每人 1 次主行动 + 1 次辅助行动。</li>
|
||||
<li>在 end_conditions 写:我方 hp 归零失败、敌方威胁归零胜利。</li>
|
||||
</ul>
|
||||
|
||||
<p class="helper-text" id="card-meta-note"></p>
|
||||
</aside>
|
||||
</main>
|
||||
|
||||
<section id="status" class="status-banner"></section>
|
||||
<script src="{{ url_for('static', filename='js/theme.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/card_editor.js') }}"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,98 @@
|
||||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Echo Tavern - 大厅</title>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}" />
|
||||
</head>
|
||||
<body class="lobby-page" data-user='{{ user | tojson | safe if user else "null" }}' data-theme="{{ user.theme if user else 'dopamine' }}">
|
||||
<header class="lobby-topbar">
|
||||
<div class="brand-block">
|
||||
<h1>Echo Tavern</h1>
|
||||
<p>多人联机派对文字冒险</p>
|
||||
</div>
|
||||
<div class="top-actions" id="top-actions">
|
||||
{% if user %}
|
||||
<span id="top-user-badge" class="user-badge {% if user.avatar_url %}has-avatar{% endif %}" style="--avatar-color: {{ user.avatar_color }}; --avatar-url: url('{{ user.avatar_url }}')" data-avatar-url="{{ user.avatar_url }}">
|
||||
<i></i>{{ user.username }}
|
||||
</span>
|
||||
{% if user.role == 'admin' %}
|
||||
<a class="link-btn" href="{{ url_for('admin_page') }}">管理后台</a>
|
||||
{% endif %}
|
||||
<a class="link-btn" href="{{ url_for('plaza_page') }}">游戏卡广场</a>
|
||||
<button id="logout-btn" class="ghost-btn" type="button">退出登录</button>
|
||||
{% else %}
|
||||
<a class="link-btn" href="{{ url_for('login_page') }}">登录</a>
|
||||
<a class="link-btn" href="{{ url_for('register_page') }}">注册</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="lobby-grid">
|
||||
<section class="card">
|
||||
<h2>创建房间</h2>
|
||||
<form id="create-form">
|
||||
<p class="helper-text" id="create-name-hint">将使用用户名创建房间</p>
|
||||
|
||||
<label for="create-card">选择游戏卡</label>
|
||||
<select id="create-card" name="card_id"></select>
|
||||
<div id="create-card-gallery" class="card-select-grid"></div>
|
||||
<article id="selected-card-detail" class="selected-card-detail"></article>
|
||||
|
||||
<button type="submit">创建并进入</button>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<section class="card">
|
||||
<h2>加入房间</h2>
|
||||
<form id="join-form">
|
||||
<label for="join-room">房间码</label>
|
||||
<input id="join-room" name="room_id" maxlength="6" placeholder="例如:A1B2C3" required />
|
||||
<button type="submit">加入游戏</button>
|
||||
</form>
|
||||
<p class="helper-text">将使用用户名加入。支持断线重连:输入同房间码会优先恢复你在该房间的身份。</p>
|
||||
</section>
|
||||
|
||||
<section class="card">
|
||||
<h2>快速重连</h2>
|
||||
<ul id="rejoin-list" class="compact-list"></ul>
|
||||
</section>
|
||||
|
||||
<section class="card" id="profile-card">
|
||||
<h2>账号个性化</h2>
|
||||
<form id="profile-form">
|
||||
<p class="helper-text">游戏内统一使用登录账号:<strong id="profile-username">{{ user.username if user else '' }}</strong></p>
|
||||
|
||||
<label for="profile-color">头像主色</label>
|
||||
<div class="color-row">
|
||||
<input id="profile-color" type="color" value="{{ user.avatar_color if user else '#0070f2' }}" />
|
||||
<span id="profile-color-preview" class="color-preview"></span>
|
||||
</div>
|
||||
|
||||
<label for="profile-avatar-url">头像图片 URL(可选)</label>
|
||||
<input id="profile-avatar-url" maxlength="280" placeholder="例如:https://... 或 /static/..." />
|
||||
|
||||
<label for="profile-bio">个性签名</label>
|
||||
<textarea id="profile-bio" maxlength="160" placeholder="一句话介绍你的玩法风格"></textarea>
|
||||
|
||||
<label for="profile-theme">界面主题</label>
|
||||
<select id="profile-theme">
|
||||
<option value="dopamine">多巴胺主题</option>
|
||||
<option value="minimal">简约主题</option>
|
||||
<option value="midnight">午夜霓虹</option>
|
||||
<option value="mint">薄荷雾光</option>
|
||||
<option value="sunset">落日胶片</option>
|
||||
</select>
|
||||
|
||||
<button type="submit">保存个性化</button>
|
||||
</form>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<section id="status" class="status-banner"></section>
|
||||
|
||||
<script src="{{ url_for('static', filename='js/theme.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/lobby.js') }}"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,26 @@
|
||||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>登录 - Echo Tavern</title>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}" />
|
||||
</head>
|
||||
<body class="auth-page" data-theme="{{ user.theme if user else 'dopamine' }}" data-user='{{ user | tojson | safe if user else "null" }}'>
|
||||
<main class="auth-card">
|
||||
<h1>登录 Echo Tavern</h1>
|
||||
<form id="login-form">
|
||||
<label for="username">登录账号</label>
|
||||
<input id="username" name="username" required placeholder="请输入用户名" />
|
||||
<label for="password">密码</label>
|
||||
<input id="password" name="password" type="password" required placeholder="至少 6 位" />
|
||||
<button type="submit">登录</button>
|
||||
</form>
|
||||
<p class="auth-links">没有账号?<a href="{{ url_for('register_page') }}">去注册</a></p>
|
||||
<p class="auth-links"><a href="{{ url_for('index') }}">返回大厅</a></p>
|
||||
<section id="status" class="status-banner"></section>
|
||||
</main>
|
||||
<script src="{{ url_for('static', filename='js/theme.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/auth.js') }}"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,52 @@
|
||||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>游戏卡广场 - Echo Tavern</title>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}" />
|
||||
</head>
|
||||
<body class="plaza-page" data-user='{{ user | tojson | safe if user else "null" }}' data-theme="{{ user.theme if user else 'dopamine' }}">
|
||||
<header class="lobby-topbar">
|
||||
<div class="brand-block">
|
||||
<h1>游戏卡广场</h1>
|
||||
<p>浏览剧本卡、查看封面与简介、快速开局。复杂创卡和编辑在独立界面完成。</p>
|
||||
</div>
|
||||
<div class="top-actions">
|
||||
{% if user and user.role == 'admin' %}
|
||||
<a class="link-btn" href="{{ url_for('admin_page') }}">管理后台</a>
|
||||
{% endif %}
|
||||
{% if user %}
|
||||
<a class="link-btn" href="{{ url_for('card_new_page') }}">新建游戏卡</a>
|
||||
{% endif %}
|
||||
<a class="link-btn" href="{{ url_for('index') }}">返回大厅</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="plaza-grid">
|
||||
<section class="card plaza-cards">
|
||||
<h2>可游玩游戏卡</h2>
|
||||
<p class="helper-text">点击卡片即可查看详情;你自己发布的卡支持直接编辑。</p>
|
||||
<div id="cards-grid" class="cards-grid"></div>
|
||||
</section>
|
||||
<section class="card plaza-create">
|
||||
<h2>创作入口</h2>
|
||||
<p class="helper-text">复杂配置(多状态条、结束条件、战斗系统)已迁移到独立编辑器。</p>
|
||||
{% if user %}
|
||||
<a class="link-btn" href="{{ url_for('card_new_page') }}">进入游戏卡编辑器</a>
|
||||
<p class="helper-text">在广场里点击你自己的卡片可直接进入编辑。</p>
|
||||
{% else %}
|
||||
<p class="helper-text">请先登录后创建或编辑游戏卡。</p>
|
||||
{% endif %}
|
||||
|
||||
<h2 style="margin-top: 1rem;">联机广场</h2>
|
||||
<p class="helper-text">房主将房间设为公开后,会显示在这里。未开始可加入,进行中可观战。</p>
|
||||
<ul id="public-rooms-list" class="compact-list"></ul>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<section id="status" class="status-banner"></section>
|
||||
<script src="{{ url_for('static', filename='js/theme.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/plaza.js') }}"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,26 @@
|
||||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>注册 - Echo Tavern</title>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}" />
|
||||
</head>
|
||||
<body class="auth-page" data-theme="{{ user.theme if user else 'dopamine' }}" data-user='{{ user | tojson | safe if user else "null" }}'>
|
||||
<main class="auth-card">
|
||||
<h1>创建账号</h1>
|
||||
<form id="register-form">
|
||||
<label for="username">登录账号</label>
|
||||
<input id="username" name="username" required placeholder="将作为游戏内公开名称使用" />
|
||||
<label for="password">密码</label>
|
||||
<input id="password" name="password" type="password" required placeholder="至少 6 位" />
|
||||
<button type="submit">注册并登录</button>
|
||||
</form>
|
||||
<p class="auth-links">已有账号?<a href="{{ url_for('login_page') }}">去登录</a></p>
|
||||
<p class="auth-links"><a href="{{ url_for('index') }}">返回大厅</a></p>
|
||||
<section id="status" class="status-banner"></section>
|
||||
</main>
|
||||
<script src="{{ url_for('static', filename='js/theme.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/auth.js') }}"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,161 @@
|
||||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Echo Tavern - 房间 {{ room_id }}</title>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}" />
|
||||
</head>
|
||||
<body class="room-page"
|
||||
data-room-id="{{ room_id }}"
|
||||
data-player-id="{{ player_id }}"
|
||||
data-player-name="{{ player_name }}"
|
||||
data-spectator="{{ '1' if spectator else '0' }}"
|
||||
data-theme="{{ user.theme if user else 'dopamine' }}"
|
||||
data-user='{{ user | tojson | safe if user else "null" }}'>
|
||||
<header class="room-topbar room-topbar-immersive">
|
||||
<div class="room-title">
|
||||
<h1>Echo Tavern</h1>
|
||||
<p>房间码 <strong id="room-code">{{ room_id }}</strong> · 玩家 <strong>{{ player_name }}</strong></p>
|
||||
</div>
|
||||
<div class="header-actions">
|
||||
<a class="link-btn back-home-link" href="/">返回主页</a>
|
||||
<button id="invite-btn" class="secondary-btn" type="button">邀请链接</button>
|
||||
<button id="public-btn" class="secondary-btn" type="button">公开房间</button>
|
||||
<button id="ready-btn" type="button">准备</button>
|
||||
<button id="start-btn" type="button">房主开始</button>
|
||||
<button id="delete-room-btn" class="ghost-btn" type="button">删除房间</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="room-shell">
|
||||
<section class="story-stage panel">
|
||||
<div id="scene-hero" class="scene-hero immersive-hero">
|
||||
<div class="scene-overlay">
|
||||
<h2 id="scene-title">当前场景</h2>
|
||||
<p id="scene-description">正在加载场景...</p>
|
||||
<div id="hero-status-list" class="hero-status-list"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section class="story-major-block">
|
||||
<h3>剧情进展</h3>
|
||||
<div id="story-log" class="log story-major"></div>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<aside class="command-stage panel">
|
||||
<section class="quick-panel">
|
||||
<h2 id="choices-title">AI 快速行动</h2>
|
||||
<div id="choice-chips" class="quick-grid"></div>
|
||||
<form id="action-form" onsubmit="return false;">
|
||||
<label for="action-input">自定义行动</label>
|
||||
<textarea id="action-input" maxlength="280" placeholder="例如:检查登记台抽屉里的账本"></textarea>
|
||||
<button type="submit">提交行动</button>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<section class="chat-panel">
|
||||
<form id="chat-form" onsubmit="return false;">
|
||||
<label for="chat-input">队内讨论</label>
|
||||
<input id="chat-input" maxlength="280" placeholder="例如:我问老板,你搜柜台" />
|
||||
<button type="submit">发送讨论</button>
|
||||
</form>
|
||||
<h3>讨论与系统提示</h3>
|
||||
<div id="chat-log" class="log small"></div>
|
||||
</section>
|
||||
|
||||
<section class="meta-panel">
|
||||
<div class="meta-grid">
|
||||
<article class="meta-block">
|
||||
<h3>队伍成员</h3>
|
||||
<ul id="players-list"></ul>
|
||||
</article>
|
||||
|
||||
<article class="meta-block">
|
||||
<h3>局内状态</h3>
|
||||
<div id="status-panel" class="status-grid"></div>
|
||||
</article>
|
||||
|
||||
<article class="meta-block">
|
||||
<h3>全员状态</h3>
|
||||
<ul id="all-status-list"></ul>
|
||||
</article>
|
||||
|
||||
<article class="meta-block">
|
||||
<div class="meta-block-head">
|
||||
<h3 id="clues-title">线索墙</h3>
|
||||
<button id="clues-toggle-btn" class="ghost-btn slim hidden" type="button">展开线索</button>
|
||||
</div>
|
||||
<ul id="clues-list" class="list-collapsible"></ul>
|
||||
</article>
|
||||
|
||||
<article class="meta-block">
|
||||
<h3 id="inventory-title">我的背包</h3>
|
||||
<ul id="inventory-list"></ul>
|
||||
</article>
|
||||
|
||||
<article class="meta-block">
|
||||
<h3>全员背包</h3>
|
||||
<ul id="all-inventory-list"></ul>
|
||||
</article>
|
||||
|
||||
<article class="meta-block">
|
||||
<h3>私密信息</h3>
|
||||
<ul id="private-list"></ul>
|
||||
</article>
|
||||
|
||||
<article class="meta-block">
|
||||
<h3>本轮已提交</h3>
|
||||
<ul id="pending-actions"></ul>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
</aside>
|
||||
</main>
|
||||
|
||||
<section id="ending-overlay" class="ending-overlay hidden">
|
||||
<div class="ending-card">
|
||||
<h2 id="ending-title">本局结束</h2>
|
||||
<p id="ending-summary">结局已触发。</p>
|
||||
<a class="link-btn" href="/">返回大厅</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="invite-modal" class="invite-modal hidden" aria-hidden="true">
|
||||
<div class="invite-panel">
|
||||
<div class="invite-header">
|
||||
<h2>邀请队友</h2>
|
||||
<button id="invite-close-btn" type="button" class="ghost-btn slim">关闭</button>
|
||||
</div>
|
||||
<p class="invite-tip">未开局可登录加入,开局后可直接观战。</p>
|
||||
|
||||
<div class="invite-grid">
|
||||
<article class="invite-card">
|
||||
<h3>加入链接</h3>
|
||||
<p id="invite-join-link" class="invite-link">加载中...</p>
|
||||
<div class="invite-actions">
|
||||
<button id="invite-copy-join-btn" type="button">复制加入链接</button>
|
||||
<a id="invite-open-join-btn" class="link-btn" href="#" target="_blank" rel="noopener noreferrer">打开链接</a>
|
||||
</div>
|
||||
<img id="invite-join-qr" class="invite-qr" alt="加入链接二维码" />
|
||||
</article>
|
||||
|
||||
<article class="invite-card">
|
||||
<h3>观战链接</h3>
|
||||
<p id="invite-spectate-link" class="invite-link">加载中...</p>
|
||||
<div class="invite-actions">
|
||||
<button id="invite-copy-spectate-btn" type="button">复制观战链接</button>
|
||||
<a id="invite-open-spectate-btn" class="link-btn" href="#" target="_blank" rel="noopener noreferrer">打开链接</a>
|
||||
</div>
|
||||
<img id="invite-spectate-qr" class="invite-qr" alt="观战链接二维码" />
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<script src="{{ url_for('static', filename='js/socket.io.min.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/theme.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/room.js') }}"></script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in new issue
Block a user