feat: init project with pages and index

This commit is contained in:
2026-02-15 22:50:30 +08:00
commit f407288701
79 changed files with 25642 additions and 0 deletions

1500
index.html Normal file

File diff suppressed because it is too large Load Diff

1
pages/_b64chunk.txt Normal file
View File

@@ -0,0 +1 @@
PCEtLSDlrqLmiLfliIbmnpDpobUgLS0+CjxzdHlsZT4KICAucGFnZS1jdXN0LWFuYWx5c2lzIC5jYS1zdGF0cyB7IGRpc3BsYXk6Z3JpZDsgZ3JpZC10ZW1wbGF0ZS1jb2x1bW5zOnJlcGVhdCg0LDFmcik7IGdhcDoxMnB4OyBtYXJnaW4tYm90dG9tOjE2cHg7IH0KICAucGFnZS1jdXN0LWFuYWx5c2lzIC5jYS1zdGF0IHsKICAgIGJhY2tncm91bmQ6I2ZmZjsgYm9yZGVyLXJhZGl1czoxMHB4OyBwYWRkaW5nOjE2cHggMjBweDsKICAgIGJveC1zaGFkb3c6dmFyKC0tZy1zaGFkb3ctc20pOyB0cmFuc2l0aW9uOnZhcigtLWctdHJhbnNpdGlvbik7CiAgfQogIC5wYWdlLWN1c3QtYW5hbHlzaXMgLmNhLXN0YXQ6aG92ZXIgeyBib3gtc2hhZG93OnZhcigtLWctc2hhZG93LW1kKTsgdHJhbnNmb3JtOnRyYW5zbGF0ZVkoLTFweCk7IH0KICAucGFnZS1jdXN0LWFuYWx5c2lzIC5jYS1zdGF0IC5sYWJlbCB7IGZvbnQtc2l6ZToxM3B4OyBjb2xvcjp2YXIoLS1nLXRleHQtbXV0ZWQpOyBtYXJnaW4tYm90dG9tOjZweDsgfQogIC5wYWdlLWN1c3QtYW5hbHlzaXMgLmNhLXN0YXQgLnZhbCB7IGZvbnQtc2l6ZToyNHB4OyBmb250LXdlaWdodDo3MDA7IGNvbG9yOnZhcigtLWctdGV4dCk7IH0KICAucGFnZS1jdXN0LWFuYWx5c2lzIC5jYS1zdGF0IC52YWwuYmx1ZSB7IGNvbG9yOnZhcigtLXByaW1hcnkpOyB9CiAgLnBhZ2UtY3VzdC1hbmFseXNpcyAuY2Etc3RhdCAudmFsLmdyZWVuIHsgY29sb3I6dmFyKC0tZy1zdWNjZXNzKTsgfQogIC5wYWdlLWN1c3QtYW5hbHlzaXMgLmNhLXN0YXQgLnZhbC5vcmFuZ2UgeyBjb2xvcjp2YXIoLS1nLXdhcm5pbmcpOyB9CiAgLnBhZ2UtY3VzdC1hbmFseXNpcyAuY2Etc3RhdCAuc3ViIHsgZm9udC1zaXplOjEycHg7IGNvbG9yOnZhcigtLWctdGV4dC1tdXRlZCk7IG1hcmdpbi10b3A6NHB4OyB9Cg==

5
pages/_b64write.py Normal file
View File

@@ -0,0 +1,5 @@
import pathlib,base64,sys
p=pathlib.Path(sys.argv[1])
d=base64.b64decode(sys.argv[2])
p.write_text(d.decode(chr(117)+chr(116)+chr(102)+chr(45)+chr(56)),encoding=chr(117)+chr(116)+chr(102)+chr(45)+chr(56))
print(p.name,p.stat().st_size)

View File

5
pages/_gen.py Normal file
View File

@@ -0,0 +1,5 @@
import pathlib,base64,sys
p=pathlib.Path(sys.argv[1])
d=base64.b64decode(sys.argv[2])
p.write_text(d.decode(chr(117)+chr(116)+chr(102)+chr(45)+chr(56)),encoding=chr(117)+chr(116)+chr(102)+chr(45)+chr(56))
print(chr(99)+chr(114)+chr(101)+chr(97)+chr(116)+chr(101)+chr(100),p.stat().st_size)

256
pages/ch-delivery.html Normal file
View File

@@ -0,0 +1,256 @@
<!-- 配送调度 — ch-delivery.html -->
<style>
.chd-seg-wrap { margin-bottom: 20px; }
.chd-stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; margin-bottom: 20px; }
.chd-stat-card { background: #fff; border-radius: var(--g-radius); padding: 18px 20px; border: 1px solid var(--g-border); box-shadow: var(--g-shadow-sm); transition: var(--g-transition); }
.chd-stat-card:hover { box-shadow: var(--g-shadow-md); transform: translateY(-1px); }
.chd-stat-label { font-size: 13px; color: var(--g-text-muted); margin-bottom: 6px; display: flex; align-items: center; gap: 6px; }
.chd-stat-label i { width: 16px; height: 16px; }
.chd-stat-val { font-size: 24px; font-weight: 700; color: var(--g-text); }
.chd-stat-val.green { color: var(--g-success); }
.chd-stat-val.orange { color: var(--g-warning); }
/* 地图占位 */
.chd-map-placeholder {
height: 240px; border: 2px dashed #e5e7eb; border-radius: var(--g-radius-lg, 12px);
display: flex; flex-direction: column; align-items: center; justify-content: center;
color: var(--g-text-muted); gap: 8px; margin-bottom: 20px; background: #fafafa;
}
.chd-map-placeholder i { width: 32px; height: 32px; opacity: .5; }
/* 在途订单卡片 */
.chd-order-card {
background: #fff; border-radius: var(--g-radius); border: 1px solid var(--g-border);
padding: 16px 20px; margin-bottom: 10px; transition: var(--g-transition);
display: flex; align-items: center; gap: 16px;
}
.chd-order-card:hover { box-shadow: var(--g-shadow-md); }
.chd-order-card.urgent { border-left: 3px solid var(--g-danger); background: #fff5f5; }
.chd-order-info { flex: 1; min-width: 0; }
.chd-order-top { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; font-size: 13px; }
.chd-order-no { font-family: 'SF Mono','Cascadia Code','Consolas',monospace; font-size: 12px; color: var(--g-text-secondary); }
.chd-order-mid { font-size: 13px; color: var(--g-text-secondary); display: flex; gap: 16px; }
.chd-order-mid span { display: flex; align-items: center; gap: 4px; }
.chd-order-mid i { width: 14px; height: 14px; }
.chd-order-actions { display: flex; gap: 8px; flex-shrink: 0; }
/* 配送商卡片 */
.chd-provider-card {
background: #fff; border-radius: var(--g-radius); border: 1px solid var(--g-border);
padding: 20px; margin-bottom: 12px; transition: var(--g-transition);
}
.chd-provider-card:hover { box-shadow: var(--g-shadow-md); }
.chd-provider-hd { display: flex; align-items: center; gap: 14px; }
.chd-provider-logo { width: 40px; height: 40px; border-radius: 10px; display: flex; align-items: center; justify-content: center; font-weight: 700; font-size: 16px; color: #fff; flex-shrink: 0; }
.chd-provider-name { font-size: 15px; font-weight: 600; color: var(--g-text); }
.chd-provider-desc { font-size: 12px; color: var(--g-text-muted); margin-top: 2px; }
.chd-provider-data { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; margin: 16px 0; padding: 14px 0; border-top: 1px solid var(--g-border); border-bottom: 1px solid var(--g-border); }
.chd-provider-data dt { font-size: 12px; color: var(--g-text-muted); margin-bottom: 4px; }
.chd-provider-data dd { font-size: 16px; font-weight: 700; color: var(--g-text); margin: 0; }
.chd-provider-ft { display: flex; align-items: center; justify-content: space-between; }
.chd-provider-fee { font-size: 12px; color: var(--g-text-muted); }
.chd-provider-card.disabled { opacity: .55; }
/* 调度规则 */
.chd-rules { background: #fff; border-radius: var(--g-radius); border: 1px solid var(--g-border); padding: 20px; margin-top: 20px; }
.chd-rule-item { display: flex; align-items: center; gap: 10px; padding: 10px 0; border-bottom: 1px solid #f3f4f6; font-size: 13px; color: var(--g-text); }
.chd-rule-item:last-child { border-bottom: none; }
.chd-rule-num { width: 24px; height: 24px; border-radius: 50%; background: var(--primary-light, #e8f0fe); color: var(--primary); font-size: 12px; font-weight: 700; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
.chd-section-hd { font-size: 15px; font-weight: 600; color: var(--g-text); padding-left: 10px; border-left: 3px solid var(--primary); margin-bottom: 16px; }
/* 费用分析 */
.chd-bar-chart { margin-bottom: 24px; }
.chd-bar-row { display: flex; align-items: center; gap: 12px; margin-bottom: 10px; }
.chd-bar-label { width: 90px; font-size: 13px; color: var(--g-text); font-weight: 500; text-align: right; flex-shrink: 0; }
.chd-bar-track { flex: 1; height: 28px; background: #f3f4f6; border-radius: 6px; overflow: hidden; position: relative; }
.chd-bar-fill { height: 100%; border-radius: 6px; display: flex; align-items: center; padding-left: 10px; font-size: 12px; font-weight: 600; color: #fff; transition: width .6s ease; }
.chd-bar-val { font-size: 13px; font-weight: 600; color: var(--g-text); width: 70px; text-align: right; flex-shrink: 0; }
</style>
<!-- 分段Tab -->
<div class="chd-seg-wrap">
<div class="g-seg">
<div class="g-seg-item active" onclick="chdSwitchTab(this,'chd-tab-realtime')" data-tab="realtime">实时配送</div>
<div class="g-seg-item" onclick="chdSwitchTab(this,'chd-tab-provider')" data-tab="provider">运力管理</div>
<div class="g-seg-item" onclick="chdSwitchTab(this,'chd-tab-cost')" data-tab="cost">费用分析</div>
</div>
</div>
<!-- Tab1: 实时配送 -->
<div id="chd-tab-realtime">
<div class="chd-stats">
<div class="chd-stat-card"><div class="chd-stat-label"><i data-lucide="bike"></i>配送中</div><div class="chd-stat-val">12</div></div>
<div class="chd-stat-card"><div class="chd-stat-label"><i data-lucide="clock"></i>待取餐</div><div class="chd-stat-val orange">3</div></div>
<div class="chd-stat-card"><div class="chd-stat-label"><i data-lucide="timer"></i>平均配送时长</div><div class="chd-stat-val">28<span style="font-size:14px;font-weight:400;color:var(--g-text-muted);">分钟</span></div></div>
<div class="chd-stat-card"><div class="chd-stat-label"><i data-lucide="target"></i>今日准时率</div><div class="chd-stat-val green">94.2%</div></div>
</div>
<div class="chd-map-placeholder">
<i data-lucide="map-pin"></i>
<span style="font-size:14px;font-weight:500;">配送地图区域</span>
<span style="font-size:12px;">实时显示骑手位置与配送路线</span>
</div>
<div class="chd-section-hd">在途订单</div>
<div class="chd-order-card urgent">
<div class="chd-order-info">
<div class="chd-order-top"><span class="chd-order-no">#ORD20260212045</span><span class="g-tag g-tag-red">即将超时</span><span class="g-tag" style="background:#fff7e6;color:#d48806;">美团配送</span></div>
<div class="chd-order-mid"><span><i data-lucide="user"></i>王师傅 138****6789</span><span><i data-lucide="clock"></i>取餐 12:20 → 预计 12:50</span></div>
</div>
<div class="chd-order-actions"><a class="g-action" style="color:var(--g-danger);">催单</a><a class="g-action">联系骑手</a></div>
</div>
<div class="chd-order-card">
<div class="chd-order-info">
<div class="chd-order-top"><span class="chd-order-no">#ORD20260212048</span><span class="g-tag g-tag-blue">配送中</span><span class="g-tag" style="background:#fff7e6;color:#d48806;">美团配送</span></div>
<div class="chd-order-mid"><span><i data-lucide="user"></i>李骑手 139****2345</span><span><i data-lucide="clock"></i>取餐 12:30 → 预计 13:00</span></div>
</div>
<div class="chd-order-actions"><a class="g-action">联系骑手</a></div>
</div>
<div class="chd-order-card">
<div class="chd-order-info">
<div class="chd-order-top"><span class="chd-order-no">#ORD20260212051</span><span class="g-tag g-tag-blue">配送中</span><span class="g-tag" style="background:#fee2e2;color:#dc2626;">达达快送</span></div>
<div class="chd-order-mid"><span><i data-lucide="user"></i>张师傅 137****8901</span><span><i data-lucide="clock"></i>取餐 12:35 → 预计 13:10</span></div>
</div>
<div class="chd-order-actions"><a class="g-action">联系骑手</a></div>
</div>
<div class="chd-order-card">
<div class="chd-order-info">
<div class="chd-order-top"><span class="chd-order-no">#ORD20260212053</span><span class="g-tag g-tag-orange">待取餐</span><span class="g-tag" style="background:#f0f0f0;color:#333;">顺丰同城</span></div>
<div class="chd-order-mid"><span><i data-lucide="user"></i>陈骑手 136****5678</span><span><i data-lucide="clock"></i>预计取餐 12:45</span></div>
</div>
<div class="chd-order-actions"><a class="g-action">催取餐</a><a class="g-action">联系骑手</a></div>
</div>
<div class="chd-order-card">
<div class="chd-order-info">
<div class="chd-order-top"><span class="chd-order-no">#ORD20260212055</span><span class="g-tag g-tag-orange">待取餐</span><span class="g-tag" style="background:#fff7e6;color:#d48806;">美团配送</span></div>
<div class="chd-order-mid"><span><i data-lucide="user"></i>赵骑手 135****4321</span><span><i data-lucide="clock"></i>预计取餐 12:48</span></div>
</div>
<div class="chd-order-actions"><a class="g-action">催取餐</a><a class="g-action">联系骑手</a></div>
</div>
<div class="chd-order-card">
<div class="chd-order-info">
<div class="chd-order-top"><span class="chd-order-no">#ORD20260212057</span><span class="g-tag g-tag-blue">配送中</span><span class="g-tag" style="background:#fee2e2;color:#dc2626;">达达快送</span></div>
<div class="chd-order-mid"><span><i data-lucide="user"></i>刘师傅 133****7654</span><span><i data-lucide="clock"></i>取餐 12:25 → 预计 12:55</span></div>
</div>
<div class="chd-order-actions"><a class="g-action">联系骑手</a></div>
</div>
</div>
<!-- Tab2: 运力管理 -->
<div id="chd-tab-provider" style="display:none;">
<p style="font-size:13px;color:var(--g-text-secondary);margin-bottom:20px;line-height:1.6;">管理已接入的配送服务商,系统将根据距离、费用、运力自动选择最优配送方案。</p>
<div class="chd-provider-card">
<div class="chd-provider-hd">
<div class="chd-provider-logo" style="background:#faad14;"></div>
<div style="flex:1;"><div class="chd-provider-name">美团配送</div><div class="chd-provider-desc">美团专送骑手覆盖3km</div></div>
<button class="g-toggle on" onclick="toggleSwitch(this)"></button>
</div>
<div class="chd-provider-data">
<div><dt>今日派单</dt><dd>38</dd></div>
<div><dt>平均时长</dt><dd>26分钟</dd></div>
<div><dt>准时率</dt><dd>96%</dd></div>
</div>
<div class="chd-provider-ft"><span class="chd-provider-fee">基础费 ¥5 + 超3km每km加 ¥2</span></div>
</div>
<div class="chd-provider-card">
<div class="chd-provider-hd">
<div class="chd-provider-logo" style="background:#f5222d;"></div>
<div style="flex:1;"><div class="chd-provider-name">达达快送</div><div class="chd-provider-desc">即时配送覆盖5km</div></div>
<button class="g-toggle on" onclick="toggleSwitch(this)"></button>
</div>
<div class="chd-provider-data">
<div><dt>今日派单</dt><dd>22</dd></div>
<div><dt>平均时长</dt><dd>32分钟</dd></div>
<div><dt>准时率</dt><dd>91%</dd></div>
</div>
<div class="chd-provider-ft"><span class="chd-provider-fee">基础费 ¥6 + 超3km每km加 ¥1.5</span></div>
</div>
<div class="chd-provider-card">
<div class="chd-provider-hd">
<div class="chd-provider-logo" style="background:#1a1a2e;"></div>
<div style="flex:1;"><div class="chd-provider-name">顺丰同城</div><div class="chd-provider-desc">高品质配送覆盖10km</div></div>
<button class="g-toggle on" onclick="toggleSwitch(this)"></button>
</div>
<div class="chd-provider-data">
<div><dt>今日派单</dt><dd>8</dd></div>
<div><dt>平均时长</dt><dd>35分钟</dd></div>
<div><dt>准时率</dt><dd>98%</dd></div>
</div>
<div class="chd-provider-ft"><span class="chd-provider-fee">基础费 ¥8 + 超3km每km加 ¥2.5</span></div>
</div>
<div class="chd-provider-card disabled">
<div class="chd-provider-hd">
<div class="chd-provider-logo" style="background:color-mix(in srgb, var(--primary) 80%, #fff);"></div>
<div style="flex:1;"><div class="chd-provider-name">自配送</div><div class="chd-provider-desc">自有骑手配送</div></div>
<button class="g-toggle" onclick="toggleSwitch(this)"></button>
</div>
<div class="chd-provider-data">
<div><dt>今日派单</dt><dd></dd></div>
<div><dt>平均时长</dt><dd></dd></div>
<div><dt>准时率</dt><dd></dd></div>
</div>
<div class="chd-provider-ft"><span class="chd-provider-fee">启用后可配置自有骑手和配送范围</span></div>
</div>
<div class="chd-rules">
<div class="chd-section-hd">智能调度规则</div>
<div class="chd-rule-item"><span class="chd-rule-num">1</span>3km内优先使用<strong style="color:var(--primary);margin:0 4px;">美团配送</strong>(费用最低)</div>
<div class="chd-rule-item"><span class="chd-rule-num">2</span>3~5km使用<strong style="color:var(--primary);margin:0 4px;">达达快送</strong></div>
<div class="chd-rule-item"><span class="chd-rule-num">3</span>5km以上使用<strong style="color:var(--primary);margin:0 4px;">顺丰同城</strong></div>
<div style="margin-top:14px;"><button class="g-btn g-btn-sm">编辑规则</button></div>
</div>
</div>
<!-- Tab3: 费用分析 -->
<div id="chd-tab-cost" style="display:none;">
<div class="chd-stats" style="grid-template-columns:repeat(3,1fr);">
<div class="chd-stat-card"><div class="chd-stat-label"><i data-lucide="receipt"></i>本月配送总费用</div><div class="chd-stat-val">&yen;4,860</div></div>
<div class="chd-stat-card"><div class="chd-stat-label"><i data-lucide="divide"></i>单均配送费</div><div class="chd-stat-val">&yen;5.2</div></div>
<div class="chd-stat-card"><div class="chd-stat-label"><i data-lucide="trending-down"></i>环比变化</div><div class="chd-stat-val green">-3.8%</div></div>
</div>
<div class="g-card" style="padding:20px;margin-bottom:20px;">
<div class="chd-section-hd">各配送商费用对比</div>
<div class="chd-bar-chart">
<div class="chd-bar-row"><span class="chd-bar-label">美团配送</span><div class="chd-bar-track"><div class="chd-bar-fill" style="width:100%;background:#faad14;">&yen;2,180</div></div><span class="chd-bar-val">44.9%</span></div>
<div class="chd-bar-row"><span class="chd-bar-label">达达快送</span><div class="chd-bar-track"><div class="chd-bar-fill" style="width:71%;background:#f5222d;">&yen;1,560</div></div><span class="chd-bar-val">32.1%</span></div>
<div class="chd-bar-row"><span class="chd-bar-label">顺丰同城</span><div class="chd-bar-track"><div class="chd-bar-fill" style="width:38%;background:#1a1a2e;">&yen;820</div></div><span class="chd-bar-val">16.9%</span></div>
<div class="chd-bar-row"><span class="chd-bar-label">自配送</span><div class="chd-bar-track"><div class="chd-bar-fill" style="width:14%;background:var(--primary);">&yen;300</div></div><span class="chd-bar-val">6.2%</span></div>
</div>
</div>
<div class="g-card" style="padding:20px;">
<div class="chd-section-hd">近7天配送费趋势</div>
<table class="g-table">
<thead>
<tr><th>日期</th><th style="text-align:right;">美团配送</th><th style="text-align:right;">达达快送</th><th style="text-align:right;">顺丰同城</th><th style="text-align:right;">自配送</th><th style="text-align:right;">合计</th></tr>
</thead>
<tbody>
<tr><td>2026-02-12</td><td style="text-align:right;">¥320</td><td style="text-align:right;">¥228</td><td style="text-align:right;">¥120</td><td style="text-align:right;">¥45</td><td style="text-align:right;font-weight:600;">¥713</td></tr>
<tr><td>2026-02-11</td><td style="text-align:right;">¥298</td><td style="text-align:right;">¥215</td><td style="text-align:right;">¥108</td><td style="text-align:right;">¥40</td><td style="text-align:right;font-weight:600;">¥661</td></tr>
<tr><td>2026-02-10</td><td style="text-align:right;">¥335</td><td style="text-align:right;">¥240</td><td style="text-align:right;">¥135</td><td style="text-align:right;">¥50</td><td style="text-align:right;font-weight:600;">¥760</td></tr>
<tr><td>2026-02-09</td><td style="text-align:right;">¥310</td><td style="text-align:right;">¥220</td><td style="text-align:right;">¥115</td><td style="text-align:right;">¥42</td><td style="text-align:right;font-weight:600;">¥687</td></tr>
<tr><td>2026-02-08</td><td style="text-align:right;">¥345</td><td style="text-align:right;">¥248</td><td style="text-align:right;">¥128</td><td style="text-align:right;">¥48</td><td style="text-align:right;font-weight:600;">¥769</td></tr>
<tr><td>2026-02-07</td><td style="text-align:right;">¥288</td><td style="text-align:right;">¥205</td><td style="text-align:right;">¥98</td><td style="text-align:right;">¥38</td><td style="text-align:right;font-weight:600;">¥629</td></tr>
<tr><td>2026-02-06</td><td style="text-align:right;">¥284</td><td style="text-align:right;">¥204</td><td style="text-align:right;">¥116</td><td style="text-align:right;">¥37</td><td style="text-align:right;font-weight:600;">¥641</td></tr>
</tbody>
</table>
</div>
</div>
<script>
function chdSwitchTab(el, tabId) {
el.parentElement.querySelectorAll('.g-seg-item').forEach(function(item) { item.classList.remove('active'); });
el.classList.add('active');
document.getElementById('chd-tab-realtime').style.display = tabId === 'chd-tab-realtime' ? '' : 'none';
document.getElementById('chd-tab-provider').style.display = tabId === 'chd-tab-provider' ? '' : 'none';
document.getElementById('chd-tab-cost').style.display = tabId === 'chd-tab-cost' ? '' : 'none';
}
function toggleSwitch(el) { el.classList.toggle('on'); }
if (typeof lucide !== 'undefined') { lucide.createIcons(); }
</script>

422
pages/ch-menu-sync.html Normal file
View File

@@ -0,0 +1,422 @@
<!-- 菜单同步页 -->
<style>
.cms-page { font-size: 13px; color: var(--g-text); }
/* ===== 统计条 ===== */
.cms-stats {
display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; margin-bottom: 16px;
}
.cms-stat-card {
background: #fff; border-radius: var(--g-radius); border: 1px solid var(--g-border);
padding: 16px 20px; display: flex; align-items: center; gap: 14px;
transition: box-shadow var(--g-transition);
}
.cms-stat-card:hover { box-shadow: var(--g-shadow-md); }
.cms-stat-icon {
width: 42px; height: 42px; border-radius: 10px; display: flex;
align-items: center; justify-content: center; flex-shrink: 0;
}
.cms-stat-icon.total { background: color-mix(in srgb, var(--primary) 10%, #fff); color: var(--primary); }
.cms-stat-icon.synced { background: #f6ffed; color: var(--g-success); }
.cms-stat-icon.pending { background: #fff7e6; color: var(--g-warning); }
.cms-stat-icon.failed { background: #fff1f0; color: var(--g-danger); }
.cms-stat-info { flex: 1; }
.cms-stat-label { font-size: 12px; color: var(--g-text-muted); margin-bottom: 4px; }
.cms-stat-value { font-size: 22px; font-weight: 700; color: var(--g-text); line-height: 1; }
.cms-stat-value.danger { color: var(--g-danger); }
/* ===== 工具栏 ===== */
.cms-toolbar {
background: #fff; border-radius: var(--g-radius); border: 1px solid var(--g-border);
padding: 14px 20px; display: flex; align-items: center; gap: 10px;
flex-wrap: wrap; margin-bottom: 14px;
}
.cms-select {
height: 34px; border-radius: var(--g-radius-sm); border: 1px solid #e5e7eb;
padding: 0 10px; font-size: 13px; outline: none; min-width: 140px;
cursor: pointer; transition: all var(--g-transition); color: var(--g-text-secondary);
}
.cms-select:focus { border-color: var(--primary); box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 12%, transparent); }
.cms-search {
height: 34px; border-radius: var(--g-radius-sm); border: 1px solid #e5e7eb;
padding: 0 10px 0 32px; font-size: 13px; outline: none; width: 200px;
transition: all var(--g-transition); color: var(--g-text); background: #fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23999' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'/%3E%3Cpath d='m21 21-4.3-4.3'/%3E%3C/svg%3E") 9px center no-repeat;
}
.cms-search:focus { border-color: var(--primary); box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 12%, transparent); }
.cms-spacer { flex: 1; }
/* ===== 表格卡片 ===== */
.cms-table-card {
background: #fff; border-radius: var(--g-radius); border: 1px solid var(--g-border);
overflow: hidden;
}
.cms-table-wrap { overflow-x: auto; }
/* 平台价格单元格 */
.cms-price-cell { display: flex; align-items: center; gap: 5px; }
.cms-price-ok { color: var(--g-success); width: 14px; height: 14px; flex-shrink: 0; }
.cms-price-warn { color: var(--g-warning); width: 14px; height: 14px; flex-shrink: 0; cursor: help; }
.cms-price-fail { color: var(--g-danger); width: 14px; height: 14px; flex-shrink: 0; }
.cms-price-na { color: var(--g-text-muted); }
/* 操作列 */
.cms-actions { display: flex; align-items: center; gap: 12px; }
/* ===== 抽屉内 ===== */
.cms-readonly {
height: 34px; line-height: 34px; padding: 0 11px; background: var(--g-bg-subtle);
border: 1px solid var(--g-border); border-radius: var(--g-radius-sm);
font-size: 13px; color: var(--g-text);
}
.cms-platform-row {
display: flex; align-items: center; gap: 12px; padding: 12px 0;
border-bottom: 1px solid #f5f5f5;
}
.cms-platform-row:last-child { border-bottom: none; }
.cms-platform-name {
width: 72px; flex-shrink: 0; font-size: 13px; font-weight: 500; color: var(--g-text);
display: flex; align-items: center; gap: 6px;
}
.cms-platform-name i { width: 16px; height: 16px; color: var(--g-text-muted); }
.cms-platform-price { flex: 1; }
.cms-platform-price .g-input { width: 100%; }
.cms-platform-price.disabled .g-input {
background: #f5f5f5; color: var(--g-text-muted); pointer-events: none;
}
.cms-follow-hint { font-size: 11px; color: var(--g-text-muted); margin-top: 4px; }
</style>
<div class="cms-page">
<!-- 统计条 -->
<div class="cms-stats">
<div class="cms-stat-card">
<div class="cms-stat-icon total"><i data-lucide="utensils-crossed" style="width:20px;height:20px"></i></div>
<div class="cms-stat-info">
<div class="cms-stat-label">本店菜品总数</div>
<div class="cms-stat-value">48</div>
</div>
</div>
<div class="cms-stat-card">
<div class="cms-stat-icon synced"><i data-lucide="check-circle-2" style="width:20px;height:20px"></i></div>
<div class="cms-stat-info">
<div class="cms-stat-label">已全平台同步</div>
<div class="cms-stat-value">42</div>
</div>
</div>
<div class="cms-stat-card">
<div class="cms-stat-icon pending"><i data-lucide="clock" style="width:20px;height:20px"></i></div>
<div class="cms-stat-info">
<div class="cms-stat-label">待同步</div>
<div class="cms-stat-value">4</div>
</div>
</div>
<div class="cms-stat-card">
<div class="cms-stat-icon failed"><i data-lucide="alert-circle" style="width:20px;height:20px"></i></div>
<div class="cms-stat-info">
<div class="cms-stat-label">同步失败</div>
<div class="cms-stat-value danger">2</div>
</div>
</div>
</div>
<!-- 工具栏 -->
<div class="cms-toolbar">
<select class="cms-select">
<option>全部分类</option>
<option>热销套餐</option>
<option>主食</option>
<option>小吃</option>
<option>饮品</option>
<option>甜品</option>
</select>
<select class="cms-select">
<option>全部状态</option>
<option>已同步</option>
<option>待同步</option>
<option>同步失败</option>
<option>部分同步</option>
</select>
<input class="cms-search" type="text" placeholder="搜索菜品名称">
<div class="cms-spacer"></div>
<button class="g-btn g-btn-primary" onclick="alert('已发起全量同步')">
<i data-lucide="refresh-cw" style="width:14px;height:14px"></i>
一键全量同步
</button>
</div>
<!-- 表格 -->
<div class="cms-table-card">
<div class="cms-table-wrap">
<table class="g-table">
<thead>
<tr>
<th style="min-width:160px">菜品名称</th>
<th style="min-width:80px">分类</th>
<th style="min-width:90px">本店价格</th>
<th style="min-width:110px">美团价格</th>
<th style="min-width:110px">饿了么价格</th>
<th style="min-width:110px">小程序价格</th>
<th style="min-width:90px">同步状态</th>
<th style="min-width:120px">操作</th>
</tr>
</thead>
<tbody>
<!-- 1. 宫保鸡丁套餐 - 已同步 -->
<tr>
<td style="font-weight:500">宫保鸡丁套餐</td>
<td><span class="g-tag g-tag-orange">热销套餐</span></td>
<td>&yen;32.00</td>
<td><span class="cms-price-cell">&yen;32.00 <i data-lucide="check" class="cms-price-ok"></i></span></td>
<td><span class="cms-price-cell">&yen;32.00 <i data-lucide="check" class="cms-price-ok"></i></span></td>
<td><span class="cms-price-cell">&yen;32.00 <i data-lucide="check" class="cms-price-ok"></i></span></td>
<td><span class="g-tag g-tag-green">已同步</span></td>
<td class="cms-actions">
<a class="g-action" onclick="alert('同步中...')">同步</a>
<a class="g-action" onclick="openDrawer('宫保鸡丁套餐','32.00','32.00','32.00','32.00')">编辑价格</a>
</td>
</tr>
<!-- 2. 麻辣香锅(中份) - 已同步 -->
<tr>
<td style="font-weight:500">麻辣香锅(中份)</td>
<td><span class="g-tag g-tag-blue">主食</span></td>
<td>&yen;48.00</td>
<td><span class="cms-price-cell">&yen;48.00 <i data-lucide="check" class="cms-price-ok"></i></span></td>
<td><span class="cms-price-cell">&yen;48.00 <i data-lucide="check" class="cms-price-ok"></i></span></td>
<td><span class="cms-price-cell">&yen;48.00 <i data-lucide="check" class="cms-price-ok"></i></span></td>
<td><span class="g-tag g-tag-green">已同步</span></td>
<td class="cms-actions">
<a class="g-action" onclick="alert('同步中...')">同步</a>
<a class="g-action" onclick="openDrawer('麻辣香锅(中份)','48.00','48.00','48.00','48.00')">编辑价格</a>
</td>
</tr>
<!-- 3. 牛肉面套餐 - 已同步 -->
<tr>
<td style="font-weight:500">牛肉面套餐</td>
<td><span class="g-tag g-tag-blue">主食</span></td>
<td>&yen;28.00</td>
<td><span class="cms-price-cell">&yen;28.00 <i data-lucide="check" class="cms-price-ok"></i></span></td>
<td><span class="cms-price-cell">&yen;28.00 <i data-lucide="check" class="cms-price-ok"></i></span></td>
<td><span class="cms-price-cell">&yen;28.00 <i data-lucide="check" class="cms-price-ok"></i></span></td>
<td><span class="g-tag g-tag-green">已同步</span></td>
<td class="cms-actions">
<a class="g-action" onclick="alert('同步中...')">同步</a>
<a class="g-action" onclick="openDrawer('牛肉面套餐','28.00','28.00','28.00','28.00')">编辑价格</a>
</td>
</tr>
<!-- 4. 小龙虾(2斤) - 部分同步,饿了么价格不一致 -->
<tr>
<td style="font-weight:500">小龙虾(2斤)</td>
<td><span class="g-tag g-tag-orange">热销套餐</span></td>
<td>&yen;128.00</td>
<td><span class="cms-price-cell">&yen;128.00 <i data-lucide="check" class="cms-price-ok"></i></span></td>
<td><span class="cms-price-cell">&yen;138.00 <i data-lucide="alert-triangle" class="cms-price-warn" title="与本店价格不一致"></i></span></td>
<td><span class="cms-price-cell">&yen;128.00 <i data-lucide="check" class="cms-price-ok"></i></span></td>
<td><span class="g-tag g-tag-blue">部分同步</span></td>
<td class="cms-actions">
<a class="g-action" onclick="alert('同步中...')">同步</a>
<a class="g-action" onclick="openDrawer('小龙虾(2斤)','128.00','128.00','138.00','128.00')">编辑价格</a>
</td>
</tr>
<!-- 5. 烤鱼套餐 - 已同步 -->
<tr>
<td style="font-weight:500">烤鱼套餐</td>
<td><span class="g-tag g-tag-blue">主食</span></td>
<td>&yen;68.00</td>
<td><span class="cms-price-cell">&yen;68.00 <i data-lucide="check" class="cms-price-ok"></i></span></td>
<td><span class="cms-price-cell">&yen;68.00 <i data-lucide="check" class="cms-price-ok"></i></span></td>
<td><span class="cms-price-cell">&yen;68.00 <i data-lucide="check" class="cms-price-ok"></i></span></td>
<td><span class="g-tag g-tag-green">已同步</span></td>
<td class="cms-actions">
<a class="g-action" onclick="alert('同步中...')">同步</a>
<a class="g-action" onclick="openDrawer('烤鱼套餐','68.00','68.00','68.00','68.00')">编辑价格</a>
</td>
</tr>
<!-- 6. 炸鸡翅(6只) - 待同步 -->
<tr>
<td style="font-weight:500">炸鸡翅(6只)</td>
<td><span class="g-tag g-tag-purple">小吃</span></td>
<td>&yen;22.00</td>
<td><span class="cms-price-cell cms-price-na">&mdash;</span></td>
<td><span class="cms-price-cell cms-price-na">&mdash;</span></td>
<td><span class="cms-price-cell cms-price-na">&mdash;</span></td>
<td><span class="g-tag g-tag-orange">待同步</span></td>
<td class="cms-actions">
<a class="g-action" onclick="alert('同步中...')">同步</a>
<a class="g-action" onclick="openDrawer('炸鸡翅(6只)','22.00','','','')">编辑价格</a>
</td>
</tr>
<!-- 7. 珍珠奶茶 - 待同步 -->
<tr>
<td style="font-weight:500">珍珠奶茶</td>
<td><span class="g-tag g-tag-gray">饮品</span></td>
<td>&yen;15.00</td>
<td><span class="cms-price-cell cms-price-na">&mdash;</span></td>
<td><span class="cms-price-cell cms-price-na">&mdash;</span></td>
<td><span class="cms-price-cell cms-price-na">&mdash;</span></td>
<td><span class="g-tag g-tag-orange">待同步</span></td>
<td class="cms-actions">
<a class="g-action" onclick="alert('同步中...')">同步</a>
<a class="g-action" onclick="openDrawer('珍珠奶茶','15.00','','','')">编辑价格</a>
</td>
</tr>
<!-- 8. 提拉米苏 - 同步失败 -->
<tr>
<td style="font-weight:500">提拉米苏</td>
<td><span class="g-tag g-tag-gray">甜品</span></td>
<td>&yen;28.00</td>
<td><span class="cms-price-cell">&yen;28.00 <i data-lucide="x" class="cms-price-fail"></i></span></td>
<td><span class="cms-price-cell">&yen;28.00 <i data-lucide="x" class="cms-price-fail"></i></span></td>
<td><span class="cms-price-cell">&yen;28.00 <i data-lucide="x" class="cms-price-fail"></i></span></td>
<td><span class="g-tag g-tag-red">同步失败</span></td>
<td class="cms-actions">
<a class="g-action" onclick="alert('重新同步中...')">同步</a>
<a class="g-action" onclick="openDrawer('提拉米苏','28.00','28.00','28.00','28.00')">编辑价格</a>
</td>
</tr>
<!-- 9. 美式咖啡 - 已同步 -->
<tr>
<td style="font-weight:500">美式咖啡</td>
<td><span class="g-tag g-tag-gray">饮品</span></td>
<td>&yen;18.00</td>
<td><span class="cms-price-cell">&yen;18.00 <i data-lucide="check" class="cms-price-ok"></i></span></td>
<td><span class="cms-price-cell">&yen;18.00 <i data-lucide="check" class="cms-price-ok"></i></span></td>
<td><span class="cms-price-cell">&yen;18.00 <i data-lucide="check" class="cms-price-ok"></i></span></td>
<td><span class="g-tag g-tag-green">已同步</span></td>
<td class="cms-actions">
<a class="g-action" onclick="alert('同步中...')">同步</a>
<a class="g-action" onclick="openDrawer('美式咖啡','18.00','18.00','18.00','18.00')">编辑价格</a>
</td>
</tr>
<!-- 10. 4人聚餐套餐 - 同步失败 -->
<tr>
<td style="font-weight:500">4人聚餐套餐</td>
<td><span class="g-tag g-tag-orange">热销套餐</span></td>
<td>&yen;168.00</td>
<td><span class="cms-price-cell">&yen;168.00 <i data-lucide="x" class="cms-price-fail"></i></span></td>
<td><span class="cms-price-cell">&yen;168.00 <i data-lucide="x" class="cms-price-fail"></i></span></td>
<td><span class="cms-price-cell">&yen;168.00 <i data-lucide="x" class="cms-price-fail"></i></span></td>
<td><span class="g-tag g-tag-red">同步失败</span></td>
<td class="cms-actions">
<a class="g-action" onclick="alert('重新同步中...')">同步</a>
<a class="g-action" onclick="openDrawer('4人聚餐套餐','168.00','168.00','168.00','168.00')">编辑价格</a>
</td>
</tr>
</tbody>
</table>
</div>
<!-- 分页 -->
<div style="padding: 12px 20px;">
<div class="g-pagination">
<span>共 48 条</span>
<button class="g-page-btn" disabled>&laquo;</button>
<button class="g-page-btn active">1</button>
<button class="g-page-btn">2</button>
<button class="g-page-btn">3</button>
<button class="g-page-btn">4</button>
<button class="g-page-btn">5</button>
<button class="g-page-btn">&raquo;</button>
</div>
</div>
</div>
</div>
<!-- 编辑价格抽屉 -->
<div class="g-drawer-mask" id="cmsDrawerMask" onclick="closeDrawer()"></div>
<div class="g-drawer" id="cmsDrawer" style="width:460px">
<div class="g-drawer-hd">
<div class="g-drawer-title">编辑价格</div>
<button class="g-drawer-close" onclick="closeDrawer()">&times;</button>
</div>
<div class="g-drawer-bd">
<!-- 菜品名称(只读) -->
<div class="g-form-group">
<label class="g-form-label">菜品名称</label>
<div class="cms-readonly" id="cmsName">宫保鸡丁套餐</div>
</div>
<!-- 本店价格 -->
<div class="g-form-group">
<label class="g-form-label required">本店价格</label>
<input class="g-input" id="cmsBasePrice" type="number" step="0.01" placeholder="请输入本店价格">
</div>
<div class="g-divider"></div>
<!-- 各平台价格 -->
<div class="g-form-group">
<label class="g-form-label" style="margin-bottom:12px; font-size:14px; font-weight:600;">各平台价格设置</label>
<div class="cms-follow-hint" style="margin-bottom:12px; margin-top:-8px;">关闭开关则该平台不上架此菜品,开启后默认跟随本店价格</div>
<!-- 美团 -->
<div class="cms-platform-row">
<div class="cms-platform-name">
<i data-lucide="shopping-bag"></i> 美团
</div>
<div class="g-toggle on" id="toggleMeituan" onclick="togglePlatform(event.currentTarget, 'priceMeituan')"></div>
<div class="cms-platform-price" id="priceMeituanWrap">
<input class="g-input" id="priceMeituan" type="number" step="0.01" placeholder="跟随本店价格">
</div>
</div>
<!-- 饿了么 -->
<div class="cms-platform-row">
<div class="cms-platform-name">
<i data-lucide="bike"></i> 饿了么
</div>
<div class="g-toggle on" id="toggleEleme" onclick="togglePlatform(event.currentTarget, 'priceEleme')"></div>
<div class="cms-platform-price" id="priceElemeWrap">
<input class="g-input" id="priceEleme" type="number" step="0.01" placeholder="跟随本店价格">
</div>
</div>
<!-- 小程序 -->
<div class="cms-platform-row">
<div class="cms-platform-name">
<i data-lucide="smartphone"></i> 小程序
</div>
<div class="g-toggle on" id="toggleMini" onclick="togglePlatform(event.currentTarget, 'priceMini')"></div>
<div class="cms-platform-price" id="priceMiniWrap">
<input class="g-input" id="priceMini" type="number" step="0.01" placeholder="跟随本店价格">
</div>
</div>
</div>
</div>
<div class="g-drawer-ft">
<button class="g-btn" onclick="closeDrawer()">取消</button>
<button class="g-btn g-btn-primary" onclick="closeDrawer()">保存并同步</button>
</div>
</div>
<script>
/* 抽屉开关 */
function openDrawer(name, base, mt, ele, mini) {
document.getElementById('cmsName').textContent = name;
document.getElementById('cmsBasePrice').value = base;
document.getElementById('priceMeituan').value = mt;
document.getElementById('priceEleme').value = ele;
document.getElementById('priceMini').value = mini;
document.getElementById('cmsDrawerMask').classList.add('open');
document.getElementById('cmsDrawer').classList.add('open');
}
function closeDrawer() {
document.getElementById('cmsDrawerMask').classList.remove('open');
document.getElementById('cmsDrawer').classList.remove('open');
}
/* 平台开关 */
function togglePlatform(el, inputId) {
el.classList.toggle('on');
var wrap = document.getElementById(inputId + 'Wrap');
if (el.classList.contains('on')) {
wrap.classList.remove('disabled');
} else {
wrap.classList.add('disabled');
}
}
/* 初始化图标 */
if (typeof lucide !== 'undefined') { lucide.createIcons(); }
</script>

441
pages/ch-orders.html Normal file
View File

@@ -0,0 +1,441 @@
<!-- 渠道订单聚合 — ch-orders.html -->
<style>
/* ---- page-private: cho- prefix ---- */
/* 统计卡片 */
.cho-stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; margin-bottom: 16px; }
.cho-stat-card {
background: #fff; border-radius: var(--g-radius); border: 1px solid var(--g-border);
padding: 18px 20px; transition: box-shadow var(--g-transition);
}
.cho-stat-card:hover { box-shadow: var(--g-shadow-md); }
.cho-stat-label { font-size: 13px; color: var(--g-text-muted); margin-bottom: 8px; display: flex; align-items: center; gap: 6px; }
.cho-stat-label i { width: 15px; height: 15px; }
.cho-stat-val { font-size: 26px; font-weight: 700; color: var(--g-text); }
.cho-stat-trend { font-size: 12px; margin-left: 8px; font-weight: 500; }
.cho-stat-trend.up { color: var(--g-success); }
.cho-stat-trend.down { color: var(--g-danger); }
.cho-stat-card.urgent { border-left: 3px solid var(--g-warning); }
.cho-stat-card.urgent .cho-stat-val { color: var(--g-warning); }
/* 平台pills */
.cho-platforms { display: flex; gap: 8px; margin-bottom: 14px; flex-wrap: wrap; }
.cho-plat-pill {
display: inline-flex; align-items: center; gap: 6px; padding: 6px 16px;
border-radius: 20px; border: 1px solid #d9d9d9; font-size: 13px;
cursor: pointer; transition: all var(--g-transition); user-select: none; background: #fff;
}
.cho-plat-pill:hover { border-color: var(--primary); }
.cho-plat-pill.active { background: color-mix(in srgb, var(--primary) 8%, #fff); border-color: var(--primary); color: var(--primary); font-weight: 600; }
.cho-plat-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
.cho-dot-all { background: var(--primary); }
.cho-dot-meituan { background: #ffc107; }
.cho-dot-eleme { background: #2196f3; }
.cho-dot-mini { background: #722ed1; }
/* 工具栏 */
.cho-toolbar {
background: #fff; border-radius: var(--g-radius); padding: 12px 16px;
display: flex; flex-wrap: wrap; gap: 10px; align-items: center;
border: 1px solid var(--g-border); margin-bottom: 14px;
}
.cho-search-wrap { position: relative; width: 200px; }
.cho-search-wrap .g-input { padding-left: 30px; width: 100%; }
.cho-search-wrap i { position: absolute; left: 8px; top: 50%; transform: translateY(-50%); color: var(--g-text-muted); pointer-events: none; }
.cho-toolbar-right { margin-left: auto; }
/* 订单卡片列表 */
.cho-order-list { display: flex; flex-direction: column; gap: 10px; }
.cho-order-card {
background: #fff; border-radius: var(--g-radius); border: 1px solid var(--g-border);
padding: 16px 20px; transition: box-shadow var(--g-transition); position: relative;
cursor: pointer;
}
.cho-order-card:hover { box-shadow: var(--g-shadow-md); }
.cho-order-card.cho-urgent { border-left: 3px solid var(--g-warning); }
.cho-order-row { display: flex; align-items: center; gap: 10px; }
.cho-order-row + .cho-order-row { margin-top: 10px; }
.cho-order-no { font-family: 'Cascadia Code', 'Fira Code', monospace; font-size: 13px; color: var(--g-text); font-weight: 600; }
.cho-order-time { margin-left: auto; font-size: 12px; color: var(--g-text-muted); flex-shrink: 0; }
.cho-order-items { flex: 1; font-size: 13px; color: var(--g-text-secondary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
.cho-order-amount { font-size: 15px; font-weight: 700; color: var(--g-text); margin-left: auto; flex-shrink: 0; }
.cho-order-customer { font-size: 12px; color: var(--g-text-muted); display: flex; align-items: center; gap: 4px; }
.cho-order-customer i { width: 13px; height: 13px; }
.cho-order-addr { font-size: 12px; color: var(--g-text-muted); flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-left: 12px; }
.cho-order-actions { margin-left: auto; display: flex; gap: 8px; align-items: center; flex-shrink: 0; }
/* 已完成/已取消卡片弱化 */
.cho-order-card.cho-dimmed { opacity: 0.6; }
.cho-order-card.cho-dimmed:hover { opacity: 0.8; }
/* 平台tag自定义色 */
.cho-tag-meituan { background: #fff8e1; color: #f57f17; }
.cho-tag-eleme { background: #e3f2fd; color: #1565c0; }
.cho-tag-mini { background: #f3e5f5; color: #7b1fa2; }
/* 抽屉 */
.cho-drawer { width: 480px; }
.cho-section { margin-bottom: 22px; }
.cho-section-hd {
font-size: 14px; font-weight: 600; color: var(--g-text);
padding-left: 10px; border-left: 3px solid var(--primary); margin-bottom: 14px;
}
.cho-info-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px 20px; font-size: 13px; }
.cho-info-label { color: var(--g-text-muted); }
.cho-info-val { color: var(--g-text); }
.cho-info-full { grid-column: 1 / -1; }
.cho-prod-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.cho-prod-table th {
text-align: left; padding: 8px 10px; font-size: 12px; font-weight: 500;
color: var(--g-text-muted); background: var(--g-bg-subtle); border-bottom: 1px solid var(--g-border);
}
.cho-prod-table td { padding: 8px 10px; border-bottom: 1px solid #f5f5f5; color: var(--g-text); }
.cho-sum-block { font-size: 13px; padding: 12px 10px 0; display: flex; flex-direction: column; gap: 6px; }
.cho-sum-row { display: flex; justify-content: space-between; color: var(--g-text-secondary); }
.cho-sum-total {
display: flex; justify-content: space-between; font-weight: 600; color: var(--g-text);
border-top: 1px solid var(--g-border); padding-top: 8px; margin-top: 4px;
}
</style>
<!-- 顶部统计卡片 -->
<div class="cho-stats">
<div class="cho-stat-card">
<div class="cho-stat-label"><i data-lucide="receipt"></i> 今日总单量</div>
<div class="cho-stat-val">186 <span class="cho-stat-trend up"><i data-lucide="trending-up" style="width:13px;height:13px;vertical-align:-2px;"></i> +8%</span></div>
</div>
<div class="cho-stat-card urgent">
<div class="cho-stat-label"><i data-lucide="alert-circle"></i> 待接单</div>
<div class="cho-stat-val">5</div>
</div>
<div class="cho-stat-card">
<div class="cho-stat-label"><i data-lucide="bike"></i> 配送中</div>
<div class="cho-stat-val">12</div>
</div>
<div class="cho-stat-card">
<div class="cho-stat-label"><i data-lucide="banknote"></i> 今日营收</div>
<div class="cho-stat-val" style="font-size:22px;">&yen;12,480</div>
</div>
</div>
<!-- 平台来源pills -->
<div class="cho-platforms">
<div class="cho-plat-pill active" onclick="choSwitchPlatform(event.currentTarget)">
<span class="cho-plat-dot cho-dot-all"></span> 全部 (186)
</div>
<div class="cho-plat-pill" onclick="choSwitchPlatform(event.currentTarget)">
<span class="cho-plat-dot cho-dot-meituan"></span> 美团外卖 (68)
</div>
<div class="cho-plat-pill" onclick="choSwitchPlatform(event.currentTarget)">
<span class="cho-plat-dot cho-dot-eleme"></span> 饿了么 (52)
</div>
<div class="cho-plat-pill" onclick="choSwitchPlatform(event.currentTarget)">
<span class="cho-plat-dot cho-dot-mini"></span> 自有小程序 (66)
</div>
</div>
<!-- 工具栏 -->
<div class="cho-toolbar">
<select class="g-select" style="width:130px;">
<option>全部状态</option>
<option>待接单</option>
<option>已接单</option>
<option>配送中</option>
<option>已完成</option>
<option>已取消</option>
</select>
<select class="g-select" style="width:110px;">
<option>今天</option>
<option>昨天</option>
<option>近7天</option>
</select>
<div class="cho-search-wrap">
<i data-lucide="search" style="width:14px;height:14px;"></i>
<input class="g-input" placeholder="搜索订单号/手机号">
</div>
<div class="cho-toolbar-right">
<button class="g-btn g-btn-primary"><i data-lucide="check-check" style="width:14px;height:14px;"></i> 批量接单</button>
</div>
</div>
<!-- 订单卡片列表 -->
<div class="cho-order-list">
<!-- 1. 待接单-美团 -->
<div class="cho-order-card cho-urgent" onclick="choOpenDrawer('MT20260213001')">
<div class="cho-order-row">
<span class="cho-order-no">#MT20260213001</span>
<span class="g-tag cho-tag-meituan">美团外卖</span>
<span class="g-tag g-tag-orange">待接单</span>
<span class="cho-order-time">12:32:15</span>
</div>
<div class="cho-order-row">
<span class="cho-order-items">宫保鸡丁套餐x1、米饭x1 等2件</span>
<span class="cho-order-amount">&yen;58.00</span>
</div>
<div class="cho-order-row">
<span class="cho-order-customer"><i data-lucide="user"></i> 张**</span>
<span class="cho-order-addr">朝阳区建国路88号院3号楼1单元502</span>
<div class="cho-order-actions" onclick="event.stopPropagation()">
<button class="g-btn g-btn-primary g-btn-sm">接单</button>
<a class="g-action g-action-danger">拒单</a>
</div>
</div>
</div>
<!-- 2. 待接单-饿了么 -->
<div class="cho-order-card cho-urgent" onclick="choOpenDrawer('EL20260213002')">
<div class="cho-order-row">
<span class="cho-order-no">#EL20260213002</span>
<span class="g-tag cho-tag-eleme">饿了么</span>
<span class="g-tag g-tag-orange">待接单</span>
<span class="cho-order-time">12:28:40</span>
</div>
<div class="cho-order-row">
<span class="cho-order-items">麻辣香锅(中辣)x1、可乐x1 等2件</span>
<span class="cho-order-amount">&yen;42.50</span>
</div>
<div class="cho-order-row">
<span class="cho-order-customer"><i data-lucide="user"></i> 李**</span>
<span class="cho-order-addr">海淀区中关村大街1号银谷大厦8层</span>
<div class="cho-order-actions" onclick="event.stopPropagation()">
<button class="g-btn g-btn-primary g-btn-sm">接单</button>
<a class="g-action g-action-danger">拒单</a>
</div>
</div>
</div>
<!-- 3. 待接单-小程序 -->
<div class="cho-order-card cho-urgent" onclick="choOpenDrawer('WX20260213003')">
<div class="cho-order-row">
<span class="cho-order-no">#WX20260213003</span>
<span class="g-tag cho-tag-mini">小程序</span>
<span class="g-tag g-tag-orange">待接单</span>
<span class="cho-order-time">12:25:08</span>
</div>
<div class="cho-order-row">
<span class="cho-order-items">牛肉面套餐x1、卤蛋x1 等2件</span>
<span class="cho-order-amount">&yen;35.00</span>
</div>
<div class="cho-order-row">
<span class="cho-order-customer"><i data-lucide="user"></i> 王**</span>
<span class="cho-order-addr">西城区金融街7号英蓝国际金融中心B座</span>
<div class="cho-order-actions" onclick="event.stopPropagation()">
<button class="g-btn g-btn-primary g-btn-sm">接单</button>
<a class="g-action g-action-danger">拒单</a>
</div>
</div>
</div>
<!-- 4. 已接单-美团 -->
<div class="cho-order-card" onclick="choOpenDrawer('MT20260213004')">
<div class="cho-order-row">
<span class="cho-order-no">#MT20260213004</span>
<span class="g-tag cho-tag-meituan">美团外卖</span>
<span class="g-tag g-tag-blue">已接单</span>
<span class="cho-order-time">12:15:30</span>
</div>
<div class="cho-order-row">
<span class="cho-order-items">4人聚餐套餐x1、啤酒x4 等5件</span>
<span class="cho-order-amount">&yen;126.00</span>
</div>
<div class="cho-order-row">
<span class="cho-order-customer"><i data-lucide="user"></i> 赵**</span>
<span class="cho-order-addr">朝阳区望京SOHO T1 12层</span>
<div class="cho-order-actions" onclick="event.stopPropagation()">
<button class="g-btn g-btn-sm"><i data-lucide="phone-call" style="width:13px;height:13px;"></i> 呼叫配送</button>
</div>
</div>
</div>
<!-- 5. 配送中-饿了么 -->
<div class="cho-order-card" onclick="choOpenDrawer('EL20260213005')">
<div class="cho-order-row">
<span class="cho-order-no">#EL20260213005</span>
<span class="g-tag cho-tag-eleme">饿了么</span>
<span class="g-tag g-tag-blue">配送中</span>
<span class="cho-order-time">11:58:12</span>
</div>
<div class="cho-order-row">
<span class="cho-order-items">烤鱼套餐(微辣)x1、凉菜拼盘x1 等3件</span>
<span class="cho-order-amount">&yen;68.00</span>
</div>
<div class="cho-order-row">
<span class="cho-order-customer"><i data-lucide="user"></i> 刘**</span>
<span class="cho-order-addr">丰台区丽泽商务区16号院平安幸福中心</span>
<div class="cho-order-actions" onclick="event.stopPropagation()">
<a class="g-action">催配送</a>
<a class="g-action">查看骑手</a>
</div>
</div>
</div>
<!-- 6. 配送中-小程序 -->
<div class="cho-order-card" onclick="choOpenDrawer('WX20260213006')">
<div class="cho-order-row">
<span class="cho-order-no">#WX20260213006</span>
<span class="g-tag cho-tag-mini">小程序</span>
<span class="g-tag g-tag-blue">配送中</span>
<span class="cho-order-time">11:45:50</span>
</div>
<div class="cho-order-row">
<span class="cho-order-items">拿铁咖啡x2、提拉米苏x1 等3件</span>
<span class="cho-order-amount">&yen;45.00</span>
</div>
<div class="cho-order-row">
<span class="cho-order-customer"><i data-lucide="user"></i> 陈**</span>
<span class="cho-order-addr">东城区东直门外大街48号东方银座C座</span>
<div class="cho-order-actions" onclick="event.stopPropagation()">
<a class="g-action">催配送</a>
<a class="g-action">查看骑手</a>
</div>
</div>
</div>
<!-- 7. 已完成-美团 -->
<div class="cho-order-card cho-dimmed" onclick="choOpenDrawer('MT20260213007')">
<div class="cho-order-row">
<span class="cho-order-no">#MT20260213007</span>
<span class="g-tag cho-tag-meituan">美团外卖</span>
<span class="g-tag g-tag-green">已完成</span>
<span class="cho-order-time">11:20:05</span>
</div>
<div class="cho-order-row">
<span class="cho-order-items">小龙虾套餐(十三香)x1、啤酒x2 等3件</span>
<span class="cho-order-amount">&yen;89.00</span>
</div>
<div class="cho-order-row">
<span class="cho-order-customer"><i data-lucide="user"></i> 孙**</span>
<span class="cho-order-addr">朝阳区三里屯太古里南区S2栋</span>
<div class="cho-order-actions" onclick="event.stopPropagation()">
<a class="g-action">查看详情</a>
</div>
</div>
</div>
<!-- 8. 已完成-饿了么 -->
<div class="cho-order-card cho-dimmed" onclick="choOpenDrawer('EL20260213008')">
<div class="cho-order-row">
<span class="cho-order-no">#EL20260213008</span>
<span class="g-tag cho-tag-eleme">饿了么</span>
<span class="g-tag g-tag-green">已完成</span>
<span class="cho-order-time">10:55:30</span>
</div>
<div class="cho-order-row">
<span class="cho-order-items">蛋炒饭x1、紫菜蛋花汤x1 等2件</span>
<span class="cho-order-amount">&yen;32.00</span>
</div>
<div class="cho-order-row">
<span class="cho-order-customer"><i data-lucide="user"></i> 周**</span>
<span class="cho-order-addr">海淀区五道口华清嘉园12号楼</span>
<div class="cho-order-actions" onclick="event.stopPropagation()">
<a class="g-action">查看详情</a>
</div>
</div>
</div>
</div>
<!-- 分页 -->
<div class="g-pagination">
<span>共 186 条</span>
<button class="g-page-btn" disabled>&lt;</button>
<button class="g-page-btn active">1</button>
<button class="g-page-btn">2</button>
<button class="g-page-btn">3</button>
<button class="g-page-btn">...</button>
<button class="g-page-btn">10</button>
<button class="g-page-btn">&gt;</button>
</div>
<!-- 订单详情抽屉 -->
<div class="g-drawer-mask" id="choDrawerMask" onclick="choCloseDrawer()"></div>
<div class="g-drawer cho-drawer" id="choDrawer">
<div class="g-drawer-hd">
<span class="g-drawer-title" id="choDrawerTitle">订单详情</span>
<button class="g-drawer-close" onclick="choCloseDrawer()"><i data-lucide="x" style="width:18px;height:18px;"></i></button>
</div>
<div class="g-drawer-bd">
<!-- 基本信息 -->
<div class="cho-section">
<div class="cho-section-hd">基本信息</div>
<div class="cho-info-grid">
<div><span class="cho-info-label">订单号:</span><span class="cho-info-val" style="font-family:monospace;">#MT20260213001</span></div>
<div><span class="cho-info-label">来源平台:</span><span class="g-tag cho-tag-meituan">美团外卖</span></div>
<div><span class="cho-info-label">订单状态:</span><span class="g-tag g-tag-orange">待接单</span></div>
<div><span class="cho-info-label">下单时间:</span><span class="cho-info-val">2026-02-13 12:32:15</span></div>
</div>
</div>
<!-- 菜品明细 -->
<div class="cho-section">
<div class="cho-section-hd">菜品明细</div>
<table class="cho-prod-table">
<thead>
<tr><th>菜品</th><th>数量</th><th>单价</th><th style="text-align:right;">小计</th></tr>
</thead>
<tbody>
<tr><td>宫保鸡丁套餐</td><td>1</td><td>&yen;45.00</td><td style="text-align:right;">&yen;45.00</td></tr>
<tr><td>米饭(加量)</td><td>1</td><td>&yen;3.00</td><td style="text-align:right;">&yen;3.00</td></tr>
</tbody>
</table>
<div class="cho-sum-block">
<div class="cho-sum-row"><span>商品总额</span><span>&yen;48.00</span></div>
<div class="cho-sum-row"><span>配送费</span><span>&yen;5.00</span></div>
<div class="cho-sum-row"><span>包装费</span><span>&yen;2.00</span></div>
<div class="cho-sum-row"><span>优惠减免</span><span style="color:var(--g-danger);">-&yen;7.00</span></div>
<div class="cho-sum-total"><span>实付金额</span><span style="color:var(--primary);font-size:15px;">&yen;58.00</span></div>
</div>
</div>
<!-- 收货信息 -->
<div class="cho-section">
<div class="cho-section-hd">收货信息</div>
<div class="cho-info-grid">
<div><span class="cho-info-label">收货人:</span><span class="cho-info-val">张**</span></div>
<div><span class="cho-info-label">联系电话:</span><span class="cho-info-val">138****6789</span></div>
<div class="cho-info-full"><span class="cho-info-label">收货地址:</span><span class="cho-info-val">北京市朝阳区建国路88号院3号楼1单元502</span></div>
</div>
</div>
<!-- 配送信息 -->
<div class="cho-section">
<div class="cho-section-hd">配送信息</div>
<div class="cho-info-grid">
<div><span class="cho-info-label">配送方式:</span><span class="cho-info-val">美团专送</span></div>
<div><span class="cho-info-label">预计送达:</span><span class="cho-info-val">13:10 前</span></div>
<div><span class="cho-info-label">骑手姓名:</span><span class="cho-info-val" style="color:var(--g-text-muted);">待分配</span></div>
<div><span class="cho-info-label">骑手电话:</span><span class="cho-info-val" style="color:var(--g-text-muted);">--</span></div>
</div>
</div>
</div>
<div class="g-drawer-ft" id="choDrawerFt">
<button class="g-btn" onclick="choCloseDrawer()">关闭</button>
<button class="g-btn g-btn-primary">接单</button>
</div>
</div>
<script>
/* 平台pill切换 */
function choSwitchPlatform(el) {
document.querySelectorAll('.cho-plat-pill').forEach(function(p) { p.classList.remove('active'); });
el.classList.add('active');
}
/* 抽屉开关 */
function choOpenDrawer(orderNo) {
document.getElementById('choDrawerTitle').textContent = '订单详情 #' + orderNo;
document.getElementById('choDrawerMask').classList.add('open');
document.getElementById('choDrawer').classList.add('open');
if (typeof lucide !== 'undefined') lucide.createIcons();
}
function choCloseDrawer() {
document.getElementById('choDrawerMask').classList.remove('open');
document.getElementById('choDrawer').classList.remove('open');
}
/* 初始化图标 */
if (typeof lucide !== 'undefined') { lucide.createIcons(); }
</script>

308
pages/ch-platform.html Normal file
View File

@@ -0,0 +1,308 @@
<!-- 渠道管理 - 平台接入 -->
<style>
.chp-page { display:flex; flex-direction:column; gap:20px; }
/* 顶部统计条 */
.chp-stats { display:grid; grid-template-columns:repeat(4,1fr); gap:14px; }
.chp-stat-item {
background:#fff; border-radius:10px; padding:18px 22px;
box-shadow:var(--g-shadow-sm); transition:var(--g-transition);
display:flex; align-items:center; gap:14px;
}
.chp-stat-item:hover { box-shadow:var(--g-shadow-md); transform:translateY(-1px); }
.chp-stat-icon {
width:42px; height:42px; border-radius:10px; display:flex;
align-items:center; justify-content:center; flex-shrink:0;
}
.chp-stat-icon.blue { background:color-mix(in srgb, var(--primary) 12%, #fff); color:var(--primary); }
.chp-stat-icon.green { background:color-mix(in srgb, var(--g-success) 12%, #fff); color:var(--g-success); }
.chp-stat-icon.orange { background:color-mix(in srgb, var(--g-warning) 12%, #fff); color:var(--g-warning); }
.chp-stat-icon.teal { background:color-mix(in srgb, #06b6d4 12%, #fff); color:#06b6d4; }
.chp-stat-label { font-size:13px; color:var(--g-text-muted); margin-bottom:4px; }
.chp-stat-value { font-size:22px; font-weight:700; color:var(--g-text); }
.chp-stat-value .unit { font-size:13px; font-weight:400; color:var(--g-text-muted); margin-left:2px; }
.chp-sync-dot {
display:inline-block; width:7px; height:7px; border-radius:50%;
background:var(--g-success); margin-right:6px; vertical-align:middle;
animation:chp-pulse 2s infinite;
}
@keyframes chp-pulse {
0%,100% { opacity:1; }
50% { opacity:.4; }
}
/* Section 标题 */
.chp-section-hd {
font-size:15px; font-weight:600; color:var(--g-text);
padding-left:10px; border-left:3px solid var(--primary); margin-bottom:16px;
}
/* 已接入渠道卡片网格 */
.chp-channel-grid { display:grid; grid-template-columns:repeat(2,1fr); gap:16px; }
.chp-channel-card {
background:#fff; border-radius:10px; padding:22px 24px;
box-shadow:var(--g-shadow-sm); transition:var(--g-transition);
display:flex; flex-direction:column; gap:16px;
}
.chp-channel-card:hover { box-shadow:var(--g-shadow-md); transform:translateY(-2px); }
.chp-card-top { display:flex; align-items:center; justify-content:space-between; }
.chp-card-top-left { display:flex; align-items:center; gap:12px; }
.chp-logo {
width:40px; height:40px; border-radius:10px; display:flex;
align-items:center; justify-content:center;
font-size:18px; font-weight:700; flex-shrink:0;
}
.chp-logo.meituan { background:#FFC107; color:#1a1a2e; }
.chp-logo.eleme { background:#2563eb; color:#fff; }
.chp-logo.mini { background:var(--primary); color:#fff; }
.chp-logo.gray { background:#e5e7eb; color:#9ca3af; }
.chp-platform-name { font-size:15px; font-weight:600; color:var(--g-text); }
.chp-shop-name { font-size:12px; color:var(--g-text-muted); margin-top:2px; }
.chp-data-row {
display:grid; grid-template-columns:repeat(3,1fr); gap:12px;
background:#f8f9fb; border-radius:var(--g-radius); padding:14px 16px;
}
.chp-data-item { text-align:center; }
.chp-data-label { font-size:11px; color:var(--g-text-muted); margin-bottom:4px; }
.chp-data-val { font-size:18px; font-weight:700; color:var(--g-text); }
.chp-data-val .small-unit { font-size:12px; font-weight:400; color:var(--g-text-muted); }
.chp-sync-time { font-size:12px; color:var(--g-text-muted); display:flex; align-items:center; gap:4px; }
.chp-card-actions {
display:flex; align-items:center; gap:12px;
padding-top:14px; border-top:1px solid var(--g-border);
}
.chp-card-actions .spacer { flex:1; }
/* 待接入渠道 */
.chp-pending-grid { display:grid; grid-template-columns:repeat(2,1fr); gap:16px; }
.chp-pending-card {
border:2px dashed #d9d9d9; border-radius:10px; padding:22px 24px;
display:flex; align-items:center; gap:16px; background:#fafafa;
transition:var(--g-transition);
}
.chp-pending-card:hover { border-color:var(--primary); background:#fff; }
.chp-pending-info { flex:1; }
.chp-pending-name { font-size:14px; font-weight:600; color:var(--g-text); margin-bottom:4px; }
.chp-pending-desc { font-size:12px; color:var(--g-text-muted); }
.chp-btn-outline {
padding:6px 16px; border-radius:var(--g-radius-sm); font-size:13px;
border:1px solid var(--primary); color:var(--primary); background:#fff;
cursor:pointer; transition:var(--g-transition); white-space:nowrap; font-weight:500;
}
.chp-btn-outline:hover { background:var(--primary-light); }
/* 底部提示条 */
.chp-info-bar {
background:color-mix(in srgb, var(--primary) 6%, #fff);
border:1px solid color-mix(in srgb, var(--primary) 15%, #fff);
border-radius:var(--g-radius); padding:14px 18px;
font-size:13px; color:var(--g-text-secondary);
display:flex; align-items:center; gap:8px;
}
.chp-info-bar i { color:var(--primary); flex-shrink:0; }
</style>
<div class="chp-page">
<!-- 顶部统计条 -->
<div class="chp-stats">
<div class="chp-stat-item">
<div class="chp-stat-icon blue"><i data-lucide="link" style="width:20px;height:20px"></i></div>
<div>
<div class="chp-stat-label">已接入渠道</div>
<div class="chp-stat-value">3<span class="unit"></span></div>
</div>
</div>
<div class="chp-stat-item">
<div class="chp-stat-icon orange"><i data-lucide="shopping-bag" style="width:20px;height:20px"></i></div>
<div>
<div class="chp-stat-label">今日全渠道订单</div>
<div class="chp-stat-value">186<span class="unit"></span></div>
</div>
</div>
<div class="chp-stat-item">
<div class="chp-stat-icon green"><i data-lucide="banknote" style="width:20px;height:20px"></i></div>
<div>
<div class="chp-stat-label">今日全渠道营收</div>
<div class="chp-stat-value">¥12,480</div>
</div>
</div>
<div class="chp-stat-item">
<div class="chp-stat-icon teal"><i data-lucide="refresh-cw" style="width:20px;height:20px"></i></div>
<div>
<div class="chp-stat-label">数据最后同步</div>
<div class="chp-stat-value" style="font-size:16px;"><span class="chp-sync-dot"></span>2 分钟前</div>
</div>
</div>
</div>
<!-- 已接入渠道 -->
<div>
<div class="chp-section-hd">已接入渠道</div>
<div class="chp-channel-grid">
<!-- 美团外卖 -->
<div class="chp-channel-card">
<div class="chp-card-top">
<div class="chp-card-top-left">
<div class="chp-logo meituan"></div>
<div>
<div class="chp-platform-name">美团外卖</div>
<div class="chp-shop-name">老三家·朝阳店</div>
</div>
</div>
<span class="g-tag g-tag-green">已连接</span>
</div>
<div class="chp-data-row">
<div class="chp-data-item">
<div class="chp-data-label">今日订单</div>
<div class="chp-data-val">68<span class="small-unit"></span></div>
</div>
<div class="chp-data-item">
<div class="chp-data-label">今日营收</div>
<div class="chp-data-val">¥4,580</div>
</div>
<div class="chp-data-item">
<div class="chp-data-label">好评率</div>
<div class="chp-data-val">4.8<span class="small-unit"></span></div>
</div>
</div>
<div class="chp-sync-time">
<i data-lucide="clock" style="width:13px;height:13px"></i> 最后同步3 分钟前
</div>
<div class="chp-card-actions">
<button class="g-btn g-btn-sm"><i data-lucide="refresh-cw" style="width:13px;height:13px;margin-right:4px;vertical-align:-2px"></i>同步数据</button>
<a class="g-action" href="javascript:;">查看详情</a>
<span class="spacer"></span>
<a class="g-action g-action-danger" href="javascript:;">断开连接</a>
</div>
</div>
<!-- 饿了么 -->
<div class="chp-channel-card">
<div class="chp-card-top">
<div class="chp-card-top-left">
<div class="chp-logo eleme">饿</div>
<div>
<div class="chp-platform-name">饿了么</div>
<div class="chp-shop-name">老三家餐饮(朝阳路)</div>
</div>
</div>
<span class="g-tag g-tag-green">已连接</span>
</div>
<div class="chp-data-row">
<div class="chp-data-item">
<div class="chp-data-label">今日订单</div>
<div class="chp-data-val">52<span class="small-unit"></span></div>
</div>
<div class="chp-data-item">
<div class="chp-data-label">今日营收</div>
<div class="chp-data-val">¥3,420</div>
</div>
<div class="chp-data-item">
<div class="chp-data-label">好评率</div>
<div class="chp-data-val">4.6<span class="small-unit"></span></div>
</div>
</div>
<div class="chp-sync-time">
<i data-lucide="clock" style="width:13px;height:13px"></i> 最后同步5 分钟前
</div>
<div class="chp-card-actions">
<button class="g-btn g-btn-sm"><i data-lucide="refresh-cw" style="width:13px;height:13px;margin-right:4px;vertical-align:-2px"></i>同步数据</button>
<a class="g-action" href="javascript:;">查看详情</a>
<span class="spacer"></span>
<a class="g-action g-action-danger" href="javascript:;">断开连接</a>
</div>
</div>
<!-- 自有小程序 -->
<div class="chp-channel-card">
<div class="chp-card-top">
<div class="chp-card-top-left">
<div class="chp-logo mini"></div>
<div>
<div class="chp-platform-name">自有小程序</div>
<div class="chp-shop-name">老三家官方小程序</div>
</div>
</div>
<span class="g-tag g-tag-blue">同步中</span>
</div>
<div class="chp-data-row">
<div class="chp-data-item">
<div class="chp-data-label">今日订单</div>
<div class="chp-data-val">66<span class="small-unit"></span></div>
</div>
<div class="chp-data-item">
<div class="chp-data-label">今日营收</div>
<div class="chp-data-val">¥4,480</div>
</div>
<div class="chp-data-item">
<div class="chp-data-label">好评率</div>
<div class="chp-data-val">4.9<span class="small-unit"></span></div>
</div>
</div>
<div class="chp-sync-time">
<i data-lucide="clock" style="width:13px;height:13px"></i> 最后同步:实时同步
</div>
<div class="chp-card-actions">
<button class="g-btn g-btn-sm"><i data-lucide="refresh-cw" style="width:13px;height:13px;margin-right:4px;vertical-align:-2px"></i>同步数据</button>
<a class="g-action" href="javascript:;">查看详情</a>
<span class="spacer"></span>
<a class="g-action g-action-danger" href="javascript:;">断开连接</a>
</div>
</div>
</div>
</div>
<!-- 待接入渠道 -->
<div>
<div class="chp-section-hd">更多渠道</div>
<div class="chp-pending-grid">
<div class="chp-pending-card">
<div class="chp-logo gray" style="font-size:16px;"></div>
<div class="chp-pending-info">
<div class="chp-pending-name">抖音团购</div>
<div class="chp-pending-desc">接入抖音本地生活,获取短视频流量</div>
</div>
<button class="chp-btn-outline">立即接入</button>
</div>
<div class="chp-pending-card">
<div class="chp-logo gray" style="font-size:16px;"></div>
<div class="chp-pending-info">
<div class="chp-pending-name">快手本地生活</div>
<div class="chp-pending-desc">接入快手同城,覆盖下沉市场</div>
</div>
<button class="chp-btn-outline">立即接入</button>
</div>
<div class="chp-pending-card">
<div class="chp-logo gray" style="font-size:16px;"></div>
<div class="chp-pending-info">
<div class="chp-pending-name">大众点评</div>
<div class="chp-pending-desc">同步大众点评店铺,管理团购套餐</div>
</div>
<button class="chp-btn-outline">立即接入</button>
</div>
</div>
</div>
<!-- 底部提示条 -->
<div class="chp-info-bar">
<i data-lucide="info" style="width:16px;height:16px"></i>
接入新渠道后,该渠道的订单将自动同步到「订单聚合」页面,菜单可通过「菜单同步」统一管理。
</div>
</div>
<script>
if (typeof lucide !== 'undefined') { lucide.createIcons(); }
</script>

136
pages/cpl-expiry.html Normal file
View File

@@ -0,0 +1,136 @@
<!-- 到期提醒页 -->
<style>
.cle-status-cards{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;margin-bottom:20px;}
.cle-status-card{border-radius:var(--g-radius);padding:20px;display:flex;align-items:center;gap:14px;box-shadow:var(--g-shadow-sm);transition:var(--g-transition);}
.cle-status-card:hover{box-shadow:var(--g-shadow-md);}
.cle-status-card.red{background:#fff1f0;border-left:4px solid var(--g-danger);}
.cle-status-card.orange{background:#fff7e6;border-left:4px solid var(--g-warning);}
.cle-status-card.green{background:#f6ffed;border-left:4px solid var(--g-success);}
.cle-sc-icon{width:44px;height:44px;border-radius:10px;display:flex;align-items:center;justify-content:center;background:rgba(255,255,255,.7);}
.cle-sc-icon svg{width:22px;height:22px;}
.cle-status-card.red .cle-sc-icon svg{color:var(--g-danger);}
.cle-status-card.orange .cle-sc-icon svg{color:var(--g-warning);}
.cle-status-card.green .cle-sc-icon svg{color:var(--g-success);}
.cle-sc-num{font-size:26px;font-weight:700;color:var(--g-text);}
.cle-sc-label{font-size:13px;color:var(--g-text-secondary);margin-top:2px;}
.cle-section-hd{font-size:15px;font-weight:600;color:var(--g-text);padding-left:10px;border-left:3px solid var(--primary);margin-bottom:16px;}
.cle-timeline{position:relative;padding-left:20px;}
.cle-timeline::before{content:'';position:absolute;left:8px;top:0;bottom:0;width:2px;background:#e5e7eb;}
.cle-tl-item{position:relative;padding:0 0 24px 20px;}
.cle-tl-dot{position:absolute;left:-16px;top:6px;width:12px;height:12px;border-radius:50%;border:2px solid #fff;box-shadow:0 0 0 2px currentColor;}
.cle-tl-item.red .cle-tl-dot{color:var(--g-danger);background:var(--g-danger);}
.cle-tl-item.orange .cle-tl-dot{color:var(--g-warning);background:var(--g-warning);}
.cle-tl-date{font-size:12px;color:var(--g-text-muted);margin-bottom:6px;}
.cle-tl-card{background:#fff;border-radius:var(--g-radius);padding:14px 18px;box-shadow:var(--g-shadow-sm);display:flex;align-items:center;gap:12px;flex-wrap:wrap;transition:var(--g-transition);}
.cle-tl-card:hover{box-shadow:var(--g-shadow-md);}
.cle-tl-item.red .cle-tl-card{border-left:3px solid var(--g-danger);}
.cle-tl-item.orange .cle-tl-card{border-left:3px solid var(--g-warning);}
.cle-tl-name{font-size:14px;font-weight:600;color:var(--g-text);}
.cle-tl-remain{font-size:13px;font-weight:600;margin-left:auto;}
.cle-tl-remain.red{color:var(--g-danger);}
.cle-tl-remain.orange{color:var(--g-warning);}
.cle-setting-row{display:flex;align-items:center;gap:16px;margin-bottom:14px;font-size:13px;color:var(--g-text);}
.cle-setting-row label{min-width:100px;font-weight:500;color:var(--g-text-secondary);}
.cle-notify-list{display:flex;flex-direction:column;gap:12px;}
.cle-notify-item{display:flex;align-items:center;justify-content:space-between;padding:10px 0;border-bottom:1px solid #f3f4f6;}
.cle-notify-item:last-child{border-bottom:none;}
.cle-notify-label{font-size:13px;color:var(--g-text);}
@media(max-width:700px){.cle-status-cards{grid-template-columns:1fr;}}
</style>
<!-- 状态卡片 -->
<div class="cle-status-cards">
<div class="cle-status-card red">
<div class="cle-sc-icon"><i data-lucide="alert-circle"></i></div>
<div><div class="cle-sc-num">1</div><div class="cle-sc-label">已过期</div></div>
</div>
<div class="cle-status-card orange">
<div class="cle-sc-icon"><i data-lucide="clock"></i></div>
<div><div class="cle-sc-num">3</div><div class="cle-sc-label">30天内到期</div></div>
</div>
<div class="cle-status-card green">
<div class="cle-sc-icon"><i data-lucide="check-circle"></i></div>
<div><div class="cle-sc-num">8</div><div class="cle-sc-label">状态正常</div></div>
</div>
</div>
<!-- 即将到期列表 -->
<div class="g-card" style="margin-bottom:20px;">
<div class="cle-section-hd">即将到期项目</div>
<div class="cle-timeline">
<div class="cle-tl-item red">
<div class="cle-tl-dot"></div>
<div class="cle-tl-date">到期日2026-02-10</div>
<div class="cle-tl-card">
<span class="cle-tl-name">健康证 - 李娜</span>
<span class="g-tag g-tag-red">健康证</span>
<span class="cle-tl-remain red">已过期 3 天</span>
<button class="g-btn g-btn-danger g-btn-sm">去处理</button>
</div>
</div>
<div class="cle-tl-item orange">
<div class="cle-tl-dot"></div>
<div class="cle-tl-date">到期日2026-03-15</div>
<div class="cle-tl-card">
<span class="cle-tl-name">食品经营许可证</span>
<span class="g-tag g-tag-orange">证照</span>
<span class="cle-tl-remain orange">剩余 30 天</span>
<button class="g-btn g-btn-sm">去处理</button>
</div>
</div>
<div class="cle-tl-item orange">
<div class="cle-tl-dot"></div>
<div class="cle-tl-date">到期日2026-03-30</div>
<div class="cle-tl-card">
<span class="cle-tl-name">消防安全检查合格证</span>
<span class="g-tag g-tag-orange">证照</span>
<span class="cle-tl-remain orange">剩余 45 天</span>
<button class="g-btn g-btn-sm">去处理</button>
</div>
</div>
<div class="cle-tl-item orange">
<div class="cle-tl-dot"></div>
<div class="cle-tl-date">到期日2026-02-28</div>
<div class="cle-tl-card">
<span class="cle-tl-name">花生油 批次BN20260110</span>
<span class="g-tag g-tag-blue">食材</span>
<span class="cle-tl-remain orange">剩余 15 天</span>
<button class="g-btn g-btn-sm">去处理</button>
</div>
</div>
</div>
</div>
<!-- 提醒设置 -->
<div class="g-card">
<div class="cle-section-hd">提醒设置</div>
<div class="cle-setting-row">
<label>提前提醒天数</label>
<select class="g-select" style="width:140px;">
<option>7 天</option>
<option>15 天</option>
<option selected>30 天</option>
<option>60 天</option>
</select>
</div>
<div class="g-divider" style="margin:12px 0;"></div>
<div class="cle-notify-list">
<div class="cle-notify-item">
<span class="cle-notify-label">站内通知</span>
<div class="g-toggle on" onclick="toggleSwitch(this)"><div class="g-toggle-thumb"></div></div>
</div>
<div class="cle-notify-item">
<span class="cle-notify-label">微信推送</span>
<div class="g-toggle on" onclick="toggleSwitch(this)"><div class="g-toggle-thumb"></div></div>
</div>
<div class="cle-notify-item">
<span class="cle-notify-label">短信通知</span>
<div class="g-toggle" onclick="toggleSwitch(this)"><div class="g-toggle-thumb"></div></div>
</div>
</div>
</div>
<script>
function toggleSwitch(el){el.classList.toggle('on');}
if(typeof lucide!=='undefined'){lucide.createIcons();}
</script>

142
pages/cpl-inspect.html Normal file
View File

@@ -0,0 +1,142 @@
<!-- 安全巡检页 -->
<style>
.cli-toolbar{display:flex;align-items:center;gap:12px;margin-bottom:16px;padding:12px 16px;background:#fff;border-radius:var(--g-radius);box-shadow:var(--g-shadow-sm);flex-wrap:wrap;}
.cli-toolbar .g-select{height:34px;font-size:13px;}
.cli-stats{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;margin-bottom:20px;}
.cli-stat{background:#fff;border-radius:var(--g-radius);padding:18px 20px;box-shadow:var(--g-shadow-sm);display:flex;align-items:center;gap:14px;transition:var(--g-transition);}
.cli-stat:hover{box-shadow:var(--g-shadow-md);}
.cli-stat-icon{width:44px;height:44px;border-radius:10px;display:flex;align-items:center;justify-content:center;}
.cli-stat-icon svg{width:22px;height:22px;}
.cli-stat-icon.blue{background:var(--primary-light);color:var(--primary);}
.cli-stat-icon.orange{background:#fff7e6;color:var(--g-warning);}
.cli-stat-icon.green{background:#f6ffed;color:var(--g-success);}
.cli-stat-num{font-size:24px;font-weight:700;color:var(--g-text);}
.cli-stat-label{font-size:13px;color:var(--g-text-secondary);margin-top:2px;}
.cli-mono{font-family:'Courier New',monospace;font-size:12px;color:var(--g-text-secondary);}
.cli-issue-zero{color:var(--g-success);font-weight:600;}
.cli-issue-bad{color:var(--g-danger);font-weight:600;}
</style>
<!-- 工具栏 -->
<div class="cli-toolbar">
<select class="g-select">
<option>全部门店</option>
<option>朝阳店</option>
<option>海淀店</option>
<option>望京店</option>
</select>
<select class="g-select">
<option>2026-02</option>
<option>2026-01</option>
<option>2025-12</option>
</select>
<button class="g-btn g-btn-primary" style="margin-left:auto;"><i data-lucide="plus" style="width:14px;height:14px;margin-right:4px;vertical-align:-2px;"></i>新建巡检</button>
</div>
<!-- 本月统计 -->
<div class="cli-stats">
<div class="cli-stat">
<div class="cli-stat-icon blue"><i data-lucide="clipboard-check"></i></div>
<div><div class="cli-stat-num">8</div><div class="cli-stat-label">已完成巡检</div></div>
</div>
<div class="cli-stat">
<div class="cli-stat-icon orange"><i data-lucide="alert-triangle"></i></div>
<div><div class="cli-stat-num" style="color:var(--g-warning)">3</div><div class="cli-stat-label">发现问题</div></div>
</div>
<div class="cli-stat">
<div class="cli-stat-icon green"><i data-lucide="check-circle"></i></div>
<div><div class="cli-stat-num" style="color:var(--g-success)">2</div><div class="cli-stat-label">整改完成</div></div>
</div>
</div>
<!-- 巡检记录表格 -->
<div class="g-card">
<table class="g-table">
<thead>
<tr>
<th>巡检编号</th>
<th>门店</th>
<th>巡检日期</th>
<th>巡检人</th>
<th>检查项数</th>
<th>问题数</th>
<th>状态</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr>
<td class="cli-mono">XJ20260213001</td>
<td>朝阳店</td>
<td>2026-02-13</td>
<td>王建国</td>
<td>25</td>
<td><span class="cli-issue-zero">0</span></td>
<td><span class="g-tag g-tag-green">合格</span></td>
<td><a class="g-action" href="javascript:;">查看报告</a></td>
</tr>
<tr>
<td class="cli-mono">XJ20260212002</td>
<td>海淀店</td>
<td>2026-02-12</td>
<td>李明</td>
<td>25</td>
<td><span class="cli-issue-bad">2</span></td>
<td><span class="g-tag g-tag-orange">待整改</span></td>
<td><a class="g-action" href="javascript:;">查看报告</a></td>
</tr>
<tr>
<td class="cli-mono">XJ20260210003</td>
<td>望京店</td>
<td>2026-02-10</td>
<td>赵丽</td>
<td>25</td>
<td><span class="cli-issue-bad">1</span></td>
<td><span class="g-tag g-tag-blue">已整改</span></td>
<td><a class="g-action" href="javascript:;">查看报告</a></td>
</tr>
<tr>
<td class="cli-mono">XJ20260208004</td>
<td>朝阳店</td>
<td>2026-02-08</td>
<td>王建国</td>
<td>25</td>
<td><span class="cli-issue-zero">0</span></td>
<td><span class="g-tag g-tag-green">合格</span></td>
<td><a class="g-action" href="javascript:;">查看报告</a></td>
</tr>
<tr>
<td class="cli-mono">XJ20260205005</td>
<td>海淀店</td>
<td>2026-02-05</td>
<td>李明</td>
<td>25</td>
<td><span class="cli-issue-bad">1</span></td>
<td><span class="g-tag g-tag-blue">已整改</span></td>
<td><a class="g-action" href="javascript:;">查看报告</a></td>
</tr>
<tr>
<td class="cli-mono">XJ20260203006</td>
<td>望京店</td>
<td>2026-02-03</td>
<td>赵丽</td>
<td>25</td>
<td><span class="cli-issue-zero">0</span></td>
<td><span class="g-tag g-tag-green">合格</span></td>
<td><a class="g-action" href="javascript:;">查看报告</a></td>
</tr>
</tbody>
</table>
</div>
<!-- 分页 -->
<div class="g-pagination" style="margin-top:16px;">
<button class="g-page-btn" disabled><i data-lucide="chevron-left" style="width:14px;height:14px;"></i></button>
<button class="g-page-btn active">1</button>
<button class="g-page-btn">2</button>
<button class="g-page-btn"><i data-lucide="chevron-right" style="width:14px;height:14px;"></i></button>
</div>
<script>
if(typeof lucide!=='undefined'){lucide.createIcons();}
</script>

133
pages/cpl-license.html Normal file
View File

@@ -0,0 +1,133 @@
<!-- 证照管理页 -->
<style>
.cll-stats{display:grid;grid-template-columns:repeat(4,1fr);gap:16px;margin-bottom:20px;}
.cll-stat{background:#fff;border-radius:var(--g-radius);padding:18px 20px;box-shadow:var(--g-shadow-sm);display:flex;align-items:center;gap:14px;transition:var(--g-transition);}
.cll-stat:hover{box-shadow:var(--g-shadow-md);}
.cll-stat-icon{width:44px;height:44px;border-radius:10px;display:flex;align-items:center;justify-content:center;}
.cll-stat-icon svg{width:22px;height:22px;}
.cll-stat-icon.blue{background:var(--primary-light);color:var(--primary);}
.cll-stat-icon.orange{background:#fff7e6;color:var(--g-warning);}
.cll-stat-icon.red{background:#fff1f0;color:var(--g-danger);}
.cll-stat-icon.gray{background:#f5f5f5;color:var(--g-text-muted);}
.cll-stat-num{font-size:24px;font-weight:700;color:var(--g-text);}
.cll-stat-label{font-size:13px;color:var(--g-text-secondary);margin-top:2px;}
.cll-section-hd{font-size:15px;font-weight:600;color:var(--g-text);padding-left:10px;border-left:3px solid var(--primary);margin-bottom:16px;display:flex;align-items:center;justify-content:space-between;}
.cll-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:16px;margin-bottom:20px;}
.cll-cert{background:#fff;border-radius:var(--g-radius);padding:18px 20px;box-shadow:var(--g-shadow-sm);display:flex;gap:16px;position:relative;transition:var(--g-transition);border:1px solid var(--g-border);}
.cll-cert:hover{box-shadow:var(--g-shadow-md);}
.cll-cert.cll-expired{border-color:var(--g-danger);background:#fffbfb;}
.cll-cert.cll-pending{border:1px dashed var(--g-text-muted);background:#fafafa;}
.cll-cert-icon{width:64px;height:64px;border-radius:var(--g-radius);display:flex;align-items:center;justify-content:center;flex-shrink:0;}
.cll-cert-icon svg{width:28px;height:28px;}
.cll-cert-icon.blue{background:var(--primary-light);color:var(--primary);}
.cll-cert-icon.green{background:#f6ffed;color:var(--g-success);}
.cll-cert-icon.orange{background:#fff7e6;color:var(--g-warning);}
.cll-cert-icon.red{background:#fff1f0;color:var(--g-danger);}
.cll-cert-icon.gray{background:#f5f5f5;color:var(--g-text-muted);}
.cll-cert-info{flex:1;min-width:0;}
.cll-cert-name{font-size:15px;font-weight:600;color:var(--g-text);margin-bottom:4px;}
.cll-cert-no{font-size:12px;color:var(--g-text-muted);font-family:'Courier New',monospace;margin-bottom:4px;}
.cll-cert-date{font-size:12px;color:var(--g-text-secondary);margin-bottom:8px;}
.cll-cert-actions{position:absolute;top:16px;right:18px;display:flex;gap:10px;}
@media(max-width:800px){.cll-grid{grid-template-columns:1fr;}.cll-stats{grid-template-columns:repeat(2,1fr);}}
</style>
<!-- 统计栏 -->
<div class="cll-stats">
<div class="cll-stat">
<div class="cll-stat-icon blue"><i data-lucide="file-text"></i></div>
<div><div class="cll-stat-num">12</div><div class="cll-stat-label">证照总数</div></div>
</div>
<div class="cll-stat">
<div class="cll-stat-icon orange"><i data-lucide="clock"></i></div>
<div><div class="cll-stat-num" style="color:var(--g-warning)">2</div><div class="cll-stat-label">即将到期</div></div>
</div>
<div class="cll-stat">
<div class="cll-stat-icon red"><i data-lucide="alert-triangle"></i></div>
<div><div class="cll-stat-num" style="color:var(--g-danger)">1</div><div class="cll-stat-label">已过期</div></div>
</div>
<div class="cll-stat">
<div class="cll-stat-icon gray"><i data-lucide="upload"></i></div>
<div><div class="cll-stat-num">3</div><div class="cll-stat-label">待上传</div></div>
</div>
</div>
<!-- 证照列表 -->
<div class="g-card">
<div class="cll-section-hd">
<span>证照列表</span>
<button class="g-btn g-btn-primary g-btn-sm"><i data-lucide="plus" style="width:14px;height:14px;margin-right:4px;vertical-align:-2px;"></i>添加证照</button>
</div>
<div class="cll-grid">
<!-- 营业执照 -->
<div class="cll-cert">
<div class="cll-cert-icon blue"><i data-lucide="building-2"></i></div>
<div class="cll-cert-info">
<div class="cll-cert-name">营业执照</div>
<div class="cll-cert-no">91110105MA01ABCD5X</div>
<div class="cll-cert-date">2025-03-01 至 2028-03-01</div>
<span class="g-tag g-tag-green">有效</span>
</div>
<div class="cll-cert-actions"><a class="g-action" href="javascript:;">查看</a><a class="g-action" href="javascript:;">更新</a></div>
</div>
<!-- 食品经营许可证 -->
<div class="cll-cert">
<div class="cll-cert-icon orange"><i data-lucide="utensils"></i></div>
<div class="cll-cert-info">
<div class="cll-cert-name">食品经营许可证</div>
<div class="cll-cert-no">JY11105xxxxxx</div>
<div class="cll-cert-date">2023-03-15 至 2026-03-15</div>
<span class="g-tag g-tag-orange">即将到期</span>
</div>
<div class="cll-cert-actions"><a class="g-action" href="javascript:;">查看</a><a class="g-action" href="javascript:;">更新</a></div>
</div>
<!-- 食品安全管理员证 -->
<div class="cll-cert">
<div class="cll-cert-icon green"><i data-lucide="shield-check"></i></div>
<div class="cll-cert-info">
<div class="cll-cert-name">食品安全管理员证</div>
<div class="cll-cert-no">AQGL2025001234</div>
<div class="cll-cert-date">2025-06-01 至 2028-06-01</div>
<span class="g-tag g-tag-green">有效</span>
</div>
<div class="cll-cert-actions"><a class="g-action" href="javascript:;">查看</a><a class="g-action" href="javascript:;">更新</a></div>
</div>
<!-- 健康证-张伟 -->
<div class="cll-cert">
<div class="cll-cert-icon green"><i data-lucide="heart-pulse"></i></div>
<div class="cll-cert-info">
<div class="cll-cert-name">健康证 - 张伟</div>
<div class="cll-cert-no">JKZ110105202500567</div>
<div class="cll-cert-date">2025-08-10 至 2026-08-10</div>
<span class="g-tag g-tag-green">有效</span>
</div>
<div class="cll-cert-actions"><a class="g-action" href="javascript:;">查看</a><a class="g-action" href="javascript:;">更新</a></div>
</div>
<!-- 健康证-李娜(已过期) -->
<div class="cll-cert cll-expired">
<div class="cll-cert-icon red"><i data-lucide="heart-pulse"></i></div>
<div class="cll-cert-info">
<div class="cll-cert-name">健康证 - 李娜</div>
<div class="cll-cert-no">JKZ110105202400321</div>
<div class="cll-cert-date">2024-02-10 至 2025-02-10</div>
<span class="g-tag g-tag-red">已过期</span>
</div>
<div class="cll-cert-actions"><a class="g-action" href="javascript:;">查看</a><a class="g-action" href="javascript:;">更新</a></div>
</div>
<!-- 消防安全检查合格证(待上传) -->
<div class="cll-cert cll-pending">
<div class="cll-cert-icon gray"><i data-lucide="flame"></i></div>
<div class="cll-cert-info">
<div class="cll-cert-name">消防安全检查合格证</div>
<div class="cll-cert-no"></div>
<div class="cll-cert-date"></div>
<span class="g-tag g-tag-gray">待上传</span>
</div>
<div class="cll-cert-actions"><a class="g-action" href="javascript:;">上传</a></div>
</div>
</div>
</div>
<script>
if(typeof lucide!=='undefined'){lucide.createIcons();}
</script>

131
pages/cpl-trace.html Normal file
View File

@@ -0,0 +1,131 @@
<!-- 食品溯源页 -->
<style>
.clt-toolbar{display:flex;align-items:center;gap:12px;margin-bottom:16px;padding:12px 16px;background:#fff;border-radius:var(--g-radius);box-shadow:var(--g-shadow-sm);flex-wrap:wrap;}
.clt-toolbar .g-select,.clt-toolbar .g-input{height:34px;font-size:13px;}
.clt-toolbar .g-input{min-width:200px;}
.clt-mono{font-family:'Courier New',monospace;font-size:12px;color:var(--g-text-secondary);}
.clt-trace-code{font-family:'Courier New',monospace;font-size:12px;color:var(--primary);cursor:pointer;}
.clt-trace-code:hover{text-decoration:underline;}
</style>
<!-- 筛选栏 -->
<div class="clt-toolbar">
<select class="g-select">
<option>全部分类</option>
<option>肉类</option>
<option>蔬菜</option>
<option>水产</option>
<option>调料</option>
<option>粮油</option>
</select>
<select class="g-select">
<option>全部供应商</option>
<option>鑫达肉业</option>
<option>湖北水产</option>
<option>绿源农场</option>
<option>鲁花集团</option>
<option>贵州老干妈</option>
<option>五常米业</option>
</select>
<input class="g-input" placeholder="搜索食材名称" style="margin-left:auto;">
</div>
<!-- 表格 -->
<div class="g-card">
<table class="g-table">
<thead>
<tr>
<th>食材名称</th>
<th>分类</th>
<th>供应商</th>
<th>产地</th>
<th>批次号</th>
<th>入库日期</th>
<th>保质期至</th>
<th>溯源码</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr>
<td>五花肉</td>
<td><span class="g-tag g-tag-red">肉类</span></td>
<td>鑫达肉业</td>
<td>山东</td>
<td class="clt-mono">BN20260210001</td>
<td>2026-02-10</td>
<td>2026-02-17</td>
<td><span class="clt-trace-code">TC80261001</span></td>
<td><a class="g-action" href="javascript:;">查看</a><a class="g-action" href="javascript:;" style="margin-left:8px;">打印</a></td>
</tr>
<tr>
<td>小龙虾</td>
<td><span class="g-tag g-tag-blue">水产</span></td>
<td>湖北水产</td>
<td>湖北</td>
<td class="clt-mono">BN20260209003</td>
<td>2026-02-09</td>
<td>2026-02-14</td>
<td><span class="clt-trace-code">TC80261002</span></td>
<td><a class="g-action" href="javascript:;">查看</a><a class="g-action" href="javascript:;" style="margin-left:8px;">打印</a></td>
</tr>
<tr>
<td>生菜</td>
<td><span class="g-tag g-tag-green">蔬菜</span></td>
<td>绿源农场</td>
<td>北京</td>
<td class="clt-mono">BN20260213001</td>
<td>2026-02-13</td>
<td>2026-02-18</td>
<td><span class="clt-trace-code">TC80261003</span></td>
<td><a class="g-action" href="javascript:;">查看</a><a class="g-action" href="javascript:;" style="margin-left:8px;">打印</a></td>
</tr>
<tr>
<td>花生油</td>
<td><span class="g-tag g-tag-orange">粮油</span></td>
<td>鲁花集团</td>
<td>山东</td>
<td class="clt-mono">BN20260110005</td>
<td>2026-01-10</td>
<td>2027-01-10</td>
<td><span class="clt-trace-code">TC80261004</span></td>
<td><a class="g-action" href="javascript:;">查看</a><a class="g-action" href="javascript:;" style="margin-left:8px;">打印</a></td>
</tr>
<tr>
<td>老干妈</td>
<td><span class="g-tag g-tag-purple">调料</span></td>
<td>贵州老干妈</td>
<td>贵州</td>
<td class="clt-mono">BN20260205008</td>
<td>2026-02-05</td>
<td>2027-02-05</td>
<td><span class="clt-trace-code">TC80261005</span></td>
<td><a class="g-action" href="javascript:;">查看</a><a class="g-action" href="javascript:;" style="margin-left:8px;">打印</a></td>
</tr>
<tr>
<td>大米</td>
<td><span class="g-tag g-tag-orange">粮油</span></td>
<td>五常米业</td>
<td>黑龙江</td>
<td class="clt-mono">BN20260201010</td>
<td>2026-02-01</td>
<td>2026-08-01</td>
<td><span class="clt-trace-code">TC80261006</span></td>
<td><a class="g-action" href="javascript:;">查看</a><a class="g-action" href="javascript:;" style="margin-left:8px;">打印</a></td>
</tr>
</tbody>
</table>
</div>
<!-- 分页 -->
<div class="g-pagination" style="margin-top:16px;">
<button class="g-page-btn" disabled><i data-lucide="chevron-left" style="width:14px;height:14px;"></i></button>
<button class="g-page-btn active">1</button>
<button class="g-page-btn">2</button>
<button class="g-page-btn">3</button>
<button class="g-page-btn"><i data-lucide="chevron-right" style="width:14px;height:14px;"></i></button>
</div>
<script>
if(typeof lucide!=='undefined'){lucide.createIcons();}
</script>

166
pages/cust-analysis.html Normal file
View File

@@ -0,0 +1,166 @@
<!-- 客户分析页 -->
<style>
.page-cust-analysis .ca-stats { display:grid; grid-template-columns:repeat(4,1fr); gap:12px; margin-bottom:16px; }
.page-cust-analysis .ca-stat {
background:#fff; border-radius:10px; padding:16px 20px;
box-shadow:var(--g-shadow-sm); transition:var(--g-transition);
}
.page-cust-analysis .ca-stat:hover { box-shadow:var(--g-shadow-md); transform:translateY(-1px); }
.page-cust-analysis .ca-stat .label { font-size:13px; color:var(--g-text-muted); margin-bottom:6px; }
.page-cust-analysis .ca-stat .val { font-size:24px; font-weight:700; color:var(--g-text); }
.page-cust-analysis .ca-stat .val.blue { color:var(--primary); }
.page-cust-analysis .ca-stat .val.green { color:var(--g-success); }
.page-cust-analysis .ca-stat .val.orange { color:var(--g-warning); }
.page-cust-analysis .ca-stat .sub { font-size:12px; color:var(--g-text-muted); margin-top:4px; }
.page-cust-analysis .ca-grid { display:grid; grid-template-columns:1fr 1fr; gap:14px; }
.page-cust-analysis .ca-card { background:#fff; border-radius:10px; padding:20px; box-shadow:var(--g-shadow-sm); }
.page-cust-analysis .ca-card.full { grid-column:1/-1; }
.page-cust-analysis .ca-card-title { font-size:15px; font-weight:600; color:var(--g-text); padding-left:10px; border-left:3px solid var(--primary); margin-bottom:16px; }
.page-cust-analysis .ca-bars { display:flex; align-items:flex-end; gap:6px; height:140px; }
.page-cust-analysis .ca-bar-col { flex:1; display:flex; flex-direction:column; align-items:center; gap:4px; }
.page-cust-analysis .ca-bar { width:100%; border-radius:4px 4px 0 0; transition:var(--g-transition); cursor:pointer; }
.page-cust-analysis .ca-bar:hover { opacity:.85; }
.page-cust-analysis .ca-bar-lbl { font-size:11px; color:var(--g-text-muted); }
.page-cust-analysis .ca-bar-val { font-size:11px; color:var(--g-text-secondary); font-weight:500; }
.page-cust-analysis .ca-donut-wrap { display:flex; align-items:center; gap:24px; }
.page-cust-analysis .ca-donut {
width:130px; height:130px; border-radius:50%; position:relative; flex-shrink:0;
background:conic-gradient(var(--primary) 0deg 158deg, var(--g-success) 158deg 270deg, var(--g-warning) 270deg 331deg, #e5e7eb 331deg 360deg);
}
.page-cust-analysis .ca-donut-hole {
position:absolute; inset:25px; border-radius:50%; background:#fff;
display:flex; flex-direction:column; align-items:center; justify-content:center;
}
.page-cust-analysis .ca-donut-hole .num { font-size:18px; font-weight:700; color:var(--g-text); }
.page-cust-analysis .ca-donut-hole .lbl { font-size:11px; color:var(--g-text-muted); }
.page-cust-analysis .ca-legend { font-size:13px; }
.page-cust-analysis .ca-legend-item { display:flex; align-items:center; gap:8px; padding:6px 0; }
.page-cust-analysis .ca-legend-dot { width:10px; height:10px; border-radius:3px; flex-shrink:0; }
.page-cust-analysis .ca-rfm { display:grid; grid-template-columns:auto repeat(4,1fr); gap:2px; font-size:12px; }
.page-cust-analysis .ca-rfm-header { background:#f8f9fb; padding:8px; text-align:center; font-weight:600; color:#6b7280; }
.page-cust-analysis .ca-rfm-label { background:#f8f9fb; padding:8px 10px; font-weight:600; color:#6b7280; white-space:nowrap; }
.page-cust-analysis .ca-rfm-cell { padding:10px 8px; text-align:center; border-radius:4px; font-weight:500; cursor:pointer; transition:var(--g-transition); }
.page-cust-analysis .ca-rfm-cell:hover { transform:scale(1.05); }
.page-cust-analysis .ca-rfm-cell.hot { background:color-mix(in srgb, var(--primary) 30%, #fff); color:var(--primary); }
.page-cust-analysis .ca-rfm-cell.warm { background:color-mix(in srgb, var(--primary) 15%, #fff); color:var(--primary); }
.page-cust-analysis .ca-rfm-cell.cool { background:color-mix(in srgb, var(--g-warning) 15%, #fff); color:var(--g-warning); }
.page-cust-analysis .ca-rfm-cell.cold { background:#f8f9fb; color:var(--g-text-muted); }
.page-cust-analysis .ca-dist-row { display:flex; align-items:center; gap:10px; padding:8px 0; font-size:13px; }
.page-cust-analysis .ca-dist-label { width:90px; color:var(--g-text-secondary); flex-shrink:0; }
.page-cust-analysis .ca-dist-bar { flex:1; height:20px; background:#f0f0f0; border-radius:4px; overflow:hidden; }
.page-cust-analysis .ca-dist-bar-inner { height:100%; border-radius:4px; transition:var(--g-transition); }
.page-cust-analysis .ca-dist-val { width:60px; text-align:right; font-weight:500; color:var(--g-text); }
</style>
<div class="page-cust-analysis">
<div style="display:flex;align-items:center;gap:12px;margin-bottom:16px;background:#fff;border-radius:10px;padding:10px 14px;box-shadow:var(--g-shadow-sm);">
<span style="font-size:13px;color:var(--g-text-secondary);">统计周期</span>
<div class="g-seg">
<div class="g-seg-item">近7天</div>
<div class="g-seg-item active">近30天</div>
<div class="g-seg-item">近90天</div>
<div class="g-seg-item">近1年</div>
</div>
<div style="flex:1;"></div>
<button class="g-btn"><i data-lucide="download" style="width:14px;height:14px;"></i>导出报表</button>
</div>
<div class="ca-stats">
<div class="ca-stat"><div class="label">客户总数</div><div class="val blue">1,286</div><div class="sub">较上月 +10.2%</div></div>
<div class="ca-stat"><div class="label">本月新增</div><div class="val green">+128</div><div class="sub">日均 4.3 人</div></div>
<div class="ca-stat"><div class="label">活跃率</div><div class="val orange">36%</div><div class="sub">463 / 1,286</div></div>
<div class="ca-stat"><div class="label">平均客户价值</div><div class="val">&#165;286</div><div class="sub">生命周期价值</div></div>
</div>
<div class="ca-grid">
<div class="ca-card full">
<div class="ca-card-title">客户增长趋势</div>
<div class="ca-bars">
<div class="ca-bar-col"><div class="ca-bar-val">68</div><div class="ca-bar" style="height:48px;background:var(--primary);opacity:.7;"></div><span class="ca-bar-lbl">3月</span></div>
<div class="ca-bar-col"><div class="ca-bar-val">82</div><div class="ca-bar" style="height:58px;background:var(--primary);opacity:.7;"></div><span class="ca-bar-lbl">4月</span></div>
<div class="ca-bar-col"><div class="ca-bar-val">95</div><div class="ca-bar" style="height:67px;background:var(--primary);opacity:.7;"></div><span class="ca-bar-lbl">5月</span></div>
<div class="ca-bar-col"><div class="ca-bar-val">110</div><div class="ca-bar" style="height:78px;background:var(--primary);opacity:.7;"></div><span class="ca-bar-lbl">6月</span></div>
<div class="ca-bar-col"><div class="ca-bar-val">88</div><div class="ca-bar" style="height:62px;background:var(--primary);opacity:.7;"></div><span class="ca-bar-lbl">7月</span></div>
<div class="ca-bar-col"><div class="ca-bar-val">102</div><div class="ca-bar" style="height:72px;background:var(--primary);opacity:.7;"></div><span class="ca-bar-lbl">8月</span></div>
<div class="ca-bar-col"><div class="ca-bar-val">76</div><div class="ca-bar" style="height:54px;background:var(--primary);opacity:.7;"></div><span class="ca-bar-lbl">9月</span></div>
<div class="ca-bar-col"><div class="ca-bar-val">118</div><div class="ca-bar" style="height:84px;background:var(--primary);opacity:.7;"></div><span class="ca-bar-lbl">10月</span></div>
<div class="ca-bar-col"><div class="ca-bar-val">90</div><div class="ca-bar" style="height:64px;background:var(--primary);opacity:.7;"></div><span class="ca-bar-lbl">11月</span></div>
<div class="ca-bar-col"><div class="ca-bar-val">135</div><div class="ca-bar" style="height:96px;background:var(--primary);opacity:.7;"></div><span class="ca-bar-lbl">12月</span></div>
<div class="ca-bar-col"><div class="ca-bar-val">111</div><div class="ca-bar" style="height:79px;background:var(--primary);opacity:.7;"></div><span class="ca-bar-lbl">1月</span></div>
<div class="ca-bar-col"><div class="ca-bar-val">128</div><div class="ca-bar" style="height:91px;background:var(--primary);"></div><span class="ca-bar-lbl">2月</span></div>
</div>
</div>
<div class="ca-card">
<div class="ca-card-title">新老客占比</div>
<div class="ca-donut-wrap">
<div class="ca-donut"><div class="ca-donut-hole"><div class="num">1,286</div><div class="lbl">总客户</div></div></div>
<div class="ca-legend">
<div class="ca-legend-item"><span class="ca-legend-dot" style="background:var(--primary);"></span><span style="flex:1;">老客户复购2次+</span><span style="font-weight:600;">44%</span></div>
<div class="ca-legend-item"><span class="ca-legend-dot" style="background:var(--g-success);"></span><span style="flex:1;">活跃新客</span><span style="font-weight:600;">31%</span></div>
<div class="ca-legend-item"><span class="ca-legend-dot" style="background:var(--g-warning);"></span><span style="flex:1;">沉睡客户</span><span style="font-weight:600;">17%</span></div>
<div class="ca-legend-item"><span class="ca-legend-dot" style="background:#e5e7eb;"></span><span style="flex:1;">流失客户</span><span style="font-weight:600;">8%</span></div>
</div>
</div>
</div>
<div class="ca-card">
<div class="ca-card-title">客单价分布</div>
<div class="ca-dist-row"><span class="ca-dist-label">0 - 30元</span><div class="ca-dist-bar"><div class="ca-dist-bar-inner" style="width:25%;background:color-mix(in srgb, var(--primary) 50%, #fff);"></div></div><span class="ca-dist-val">18%</span></div>
<div class="ca-dist-row"><span class="ca-dist-label">30 - 60元</span><div class="ca-dist-bar"><div class="ca-dist-bar-inner" style="width:45%;background:color-mix(in srgb, var(--primary) 65%, #fff);"></div></div><span class="ca-dist-val">32%</span></div>
<div class="ca-dist-row"><span class="ca-dist-label">60 - 100元</span><div class="ca-dist-bar"><div class="ca-dist-bar-inner" style="width:55%;background:var(--primary);"></div></div><span class="ca-dist-val">28%</span></div>
<div class="ca-dist-row"><span class="ca-dist-label">100 - 150元</span><div class="ca-dist-bar"><div class="ca-dist-bar-inner" style="width:20%;background:color-mix(in srgb, var(--primary) 80%, #fff);"></div></div><span class="ca-dist-val">14%</span></div>
<div class="ca-dist-row"><span class="ca-dist-label">150元以上</span><div class="ca-dist-bar"><div class="ca-dist-bar-inner" style="width:12%;background:color-mix(in srgb, var(--primary) 40%, #fff);"></div></div><span class="ca-dist-val">8%</span></div>
</div>
<div class="ca-card full">
<div class="ca-card-title">RFM 客户分层</div>
<div class="ca-rfm">
<div class="ca-rfm-header"></div>
<div class="ca-rfm-header">高频高额</div><div class="ca-rfm-header">高频低额</div><div class="ca-rfm-header">低频高额</div><div class="ca-rfm-header">低频低额</div>
<div class="ca-rfm-label">近期活跃</div>
<div class="ca-rfm-cell hot">186人<br><span style="font-size:11px;opacity:.7;">重要价值</span></div>
<div class="ca-rfm-cell warm">142人<br><span style="font-size:11px;opacity:.7;">潜力客户</span></div>
<div class="ca-rfm-cell warm">98人<br><span style="font-size:11px;opacity:.7;">新客培育</span></div>
<div class="ca-rfm-cell cool">37人<br><span style="font-size:11px;opacity:.7;">一般维护</span></div>
<div class="ca-rfm-label">中期沉默</div>
<div class="ca-rfm-cell warm">124人<br><span style="font-size:11px;opacity:.7;">重要挽留</span></div>
<div class="ca-rfm-cell cool">168人<br><span style="font-size:11px;opacity:.7;">一般发展</span></div>
<div class="ca-rfm-cell cool">86人<br><span style="font-size:11px;opacity:.7;">一般保持</span></div>
<div class="ca-rfm-cell cold">52人<br><span style="font-size:11px;opacity:.7;">低优先级</span></div>
<div class="ca-rfm-label">长期流失</div>
<div class="ca-rfm-cell cool">76人<br><span style="font-size:11px;opacity:.7;">重要召回</span></div>
<div class="ca-rfm-cell cold">112人<br><span style="font-size:11px;opacity:.7;">即将流失</span></div>
<div class="ca-rfm-cell cold">98人<br><span style="font-size:11px;opacity:.7;">基本流失</span></div>
<div class="ca-rfm-cell cold">107人<br><span style="font-size:11px;opacity:.7;">已流失</span></div>
</div>
</div>
<div class="ca-card full">
<div class="ca-card-title">高价值客户 TOP 10</div>
<table style="width:100%;border-collapse:collapse;font-size:13px;">
<thead><tr>
<th style="background:#f8f9fb;padding:8px 12px;text-align:left;font-weight:600;color:#6b7280;border-bottom:1px solid #e5e7eb;">排名</th>
<th style="background:#f8f9fb;padding:8px 12px;text-align:left;font-weight:600;color:#6b7280;border-bottom:1px solid #e5e7eb;">客户</th>
<th style="background:#f8f9fb;padding:8px 12px;text-align:left;font-weight:600;color:#6b7280;border-bottom:1px solid #e5e7eb;">累计消费</th>
<th style="background:#f8f9fb;padding:8px 12px;text-align:left;font-weight:600;color:#6b7280;border-bottom:1px solid #e5e7eb;">下单次数</th>
<th style="background:#f8f9fb;padding:8px 12px;text-align:left;font-weight:600;color:#6b7280;border-bottom:1px solid #e5e7eb;">客单价</th>
<th style="background:#f8f9fb;padding:8px 12px;text-align:left;font-weight:600;color:#6b7280;border-bottom:1px solid #e5e7eb;">最近下单</th>
<th style="background:#f8f9fb;padding:8px 12px;text-align:left;font-weight:600;color:#6b7280;border-bottom:1px solid #e5e7eb;">标签</th>
</tr></thead>
<tbody>
<tr style="border-bottom:1px solid #f3f4f6;"><td style="padding:10px 12px;font-weight:700;color:var(--primary);">1</td><td style="padding:10px 12px;">王大力</td><td style="padding:10px 12px;font-weight:600;">&#165;4,210</td><td style="padding:10px 12px;">35</td><td style="padding:10px 12px;">&#165;120</td><td style="padding:10px 12px;color:var(--g-text-secondary);">02-12</td><td style="padding:10px 12px;"><span class="g-tag g-tag-orange">高价值</span></td></tr>
<tr style="border-bottom:1px solid #f3f4f6;"><td style="padding:10px 12px;font-weight:700;color:var(--primary);">2</td><td style="padding:10px 12px;">张小明</td><td style="padding:10px 12px;font-weight:600;">&#165;3,628</td><td style="padding:10px 12px;">42</td><td style="padding:10px 12px;">&#165;86</td><td style="padding:10px 12px;color:var(--g-text-secondary);">02-14</td><td style="padding:10px 12px;"><span class="g-tag g-tag-orange">高价值</span></td></tr>
<tr style="border-bottom:1px solid #f3f4f6;"><td style="padding:10px 12px;font-weight:700;color:var(--primary);">3</td><td style="padding:10px 12px;">周雅琴</td><td style="padding:10px 12px;font-weight:600;">&#165;3,150</td><td style="padding:10px 12px;">30</td><td style="padding:10px 12px;">&#165;105</td><td style="padding:10px 12px;color:var(--g-text-secondary);">02-13</td><td style="padding:10px 12px;"><span class="g-tag g-tag-orange">高价值</span></td></tr>
<tr style="border-bottom:1px solid #f3f4f6;"><td style="padding:10px 12px;color:var(--g-text-secondary);">4</td><td style="padding:10px 12px;">吴强</td><td style="padding:10px 12px;font-weight:600;">&#165;2,890</td><td style="padding:10px 12px;">26</td><td style="padding:10px 12px;">&#165;111</td><td style="padding:10px 12px;color:var(--g-text-secondary);">02-11</td><td style="padding:10px 12px;"><span class="g-tag g-tag-blue">活跃</span></td></tr>
<tr style="border-bottom:1px solid #f3f4f6;"><td style="padding:10px 12px;color:var(--g-text-secondary);">5</td><td style="padding:10px 12px;">李婷婷</td><td style="padding:10px 12px;font-weight:600;">&#165;2,156</td><td style="padding:10px 12px;">28</td><td style="padding:10px 12px;">&#165;77</td><td style="padding:10px 12px;color:var(--g-text-secondary);">02-13</td><td style="padding:10px 12px;"><span class="g-tag g-tag-blue">活跃</span></td></tr>
</tbody>
</table>
</div>
</div>
</div>

435
pages/cust-list.html Normal file
View File

@@ -0,0 +1,435 @@
<!-- 客户列表页 -->
<style>
.page-cust-list .cl-filter { display:flex; gap:12px; flex-wrap:wrap; align-items:center; margin-bottom:16px; box-shadow:var(--g-shadow-sm); border-radius:10px; padding:10px 14px; background:#fff; }
.page-cust-list .cl-filter input,
.page-cust-list .cl-filter select {
height:34px; padding:0 10px; border:1px solid #e5e7eb; border-radius:8px;
font-size:13px; outline:none; transition:var(--g-transition); background:#fff;
}
.page-cust-list .cl-filter input:focus,
.page-cust-list .cl-filter select:focus { border-color:var(--primary); box-shadow:0 0 0 3px color-mix(in srgb, var(--primary) 12%, transparent); }
.page-cust-list .cl-filter input { width:220px; }
.page-cust-list .cl-stats { display:grid; grid-template-columns:repeat(4,1fr); gap:12px; margin-bottom:16px; }
.page-cust-list .cl-stat {
background:#fff; border-radius:10px; padding:16px 20px;
box-shadow:var(--g-shadow-sm); transition:var(--g-transition);
}
.page-cust-list .cl-stat:hover { box-shadow:var(--g-shadow-md); transform:translateY(-1px); }
.page-cust-list .cl-stat .label { font-size:13px; color:var(--g-text-muted); margin-bottom:6px; }
.page-cust-list .cl-stat .val { font-size:24px; font-weight:700; color:var(--g-text); }
.page-cust-list .cl-stat .val.blue { color:var(--primary); }
.page-cust-list .cl-stat .val.green { color:var(--g-success); }
.page-cust-list .cl-stat .val.orange { color:var(--g-warning); }
.page-cust-list .cl-stat .sub { font-size:12px; color:var(--g-text-muted); margin-top:4px; }
.page-cust-list .cl-table-wrap {
background:#fff; border-radius:10px; box-shadow:var(--g-shadow-sm); overflow:hidden;
}
.page-cust-list table { width:100%; border-collapse:collapse; font-size:13px; }
.page-cust-list thead th {
background:#f8f9fb; padding:10px 12px; text-align:left;
font-weight:600; color:#6b7280; border-bottom:1px solid #e5e7eb; white-space:nowrap;
}
.page-cust-list tbody td { padding:12px; border-bottom:1px solid #f3f4f6; color:var(--g-text); }
.page-cust-list tbody tr:hover { background:color-mix(in srgb, var(--primary) 3%, #fff); }
.page-cust-list tbody tr:last-child td { border-bottom:none; }
.page-cust-list .cl-cust-cell { display:flex; align-items:center; gap:10px; }
.page-cust-list .cl-avatar {
width:36px; height:36px; border-radius:50%; display:flex; align-items:center; justify-content:center;
flex-shrink:0; font-size:14px; font-weight:600; color:#fff;
}
.page-cust-list .cl-cust-info .name { font-weight:500; color:var(--g-text); }
.page-cust-list .cl-cust-info .phone { font-size:12px; color:var(--g-text-muted); margin-top:2px; }
.page-cust-list .cl-bar { display:inline-block; height:6px; border-radius:3px; background:var(--primary); opacity:.7; }
.page-cust-list .cl-pagination {
display:flex; align-items:center; justify-content:flex-end;
padding:12px 16px; gap:6px; font-size:13px; color:#4b5563; background:#fafbfc;
}
.page-cust-list .cl-page-btn {
min-width:34px; height:34px; border:1px solid #e5e7eb; border-radius:8px;
background:#fff; cursor:pointer; display:flex; align-items:center;
justify-content:center; font-size:13px; transition:var(--g-transition);
}
.page-cust-list .cl-page-btn:hover { border-color:var(--primary); color:var(--primary); }
.page-cust-list .cl-page-btn.active { background:var(--primary); color:#fff; border-color:var(--primary); }
</style>
<div class="page-cust-list">
<!-- 筛选栏 -->
<div class="cl-filter">
<input type="text" placeholder="搜索客户姓名 / 手机号" />
<select>
<option value="">客户标签</option>
<option>高价值</option>
<option>活跃客户</option>
<option>沉睡客户</option>
<option>新客户</option>
</select>
<select>
<option value="">下单次数</option>
<option>1次</option>
<option>2-5次</option>
<option>6-10次</option>
<option>10次以上</option>
</select>
<select>
<option value="">注册时间</option>
<option>最近7天</option>
<option>最近30天</option>
<option>最近90天</option>
</select>
<button class="g-btn">查询</button>
<button class="g-btn">重置</button>
<div style="flex:1;"></div>
<button class="g-btn">
<i data-lucide="download" style="width:14px;height:14px;"></i>导出
</button>
</div>
<!-- 统计卡片 -->
<div class="cl-stats">
<div class="cl-stat">
<div class="label">客户总数</div>
<div class="val blue">1,286</div>
<div class="sub">累计注册客户</div>
</div>
<div class="cl-stat">
<div class="label">本月新增</div>
<div class="val green">+128</div>
<div class="sub">较上月 +15.3%</div>
</div>
<div class="cl-stat">
<div class="label">活跃客户</div>
<div class="val orange">463</div>
<div class="sub">近30天有下单</div>
</div>
<div class="cl-stat">
<div class="label">客均消费</div>
<div class="val">&#165;86.5</div>
<div class="sub">近30天平均</div>
</div>
</div>
<!-- 表格 -->
<div class="cl-table-wrap">
<table>
<thead>
<tr>
<th style="width:200px;">客户信息</th>
<th>下单次数</th>
<th>累计消费</th>
<th>客单价</th>
<th>最近下单</th>
<th>客户标签</th>
<th style="width:100px;">操作</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<div class="cl-cust-cell">
<div class="cl-avatar" style="background:#3b82f6"></div>
<div class="cl-cust-info">
<div class="name">张小明</div>
<div class="phone">138****6621</div>
</div>
</div>
</td>
<td><strong>42</strong> <span class="cl-bar" style="width:42px;"></span></td>
<td style="font-weight:600;">&#165;3,628.00</td>
<td>&#165;86.4</td>
<td>2026-02-14</td>
<td><span class="g-tag g-tag-orange">高价值</span><span class="g-tag g-tag-blue">活跃</span></td>
<td><a class="g-action" onclick="openClDrawer('张小明')">查看</a></td>
</tr>
<tr>
<td>
<div class="cl-cust-cell">
<div class="cl-avatar" style="background:#8b5cf6"></div>
<div class="cl-cust-info">
<div class="name">李婷婷</div>
<div class="phone">139****2208</div>
</div>
</div>
</td>
<td><strong>28</strong> <span class="cl-bar" style="width:28px;"></span></td>
<td style="font-weight:600;">&#165;2,156.00</td>
<td>&#165;77.0</td>
<td>2026-02-13</td>
<td><span class="g-tag g-tag-blue">活跃</span></td>
<td><a class="g-action" onclick="openClDrawer('李婷婷')">查看</a></td>
</tr>
<tr>
<td>
<div class="cl-cust-cell">
<div class="cl-avatar" style="background:#f59e0b"></div>
<div class="cl-cust-info">
<div class="name">王大力</div>
<div class="phone">136****9903</div>
</div>
</div>
</td>
<td><strong>35</strong> <span class="cl-bar" style="width:35px;"></span></td>
<td style="font-weight:600;">&#165;4,210.50</td>
<td>&#165;120.3</td>
<td>2026-02-12</td>
<td><span class="g-tag g-tag-orange">高价值</span><span class="g-tag g-tag-blue">活跃</span></td>
<td><a class="g-action" onclick="openClDrawer('王大力')">查看</a></td>
</tr>
<tr>
<td>
<div class="cl-cust-cell">
<div class="cl-avatar" style="background:#22c55e"></div>
<div class="cl-cust-info">
<div class="name">赵敏</div>
<div class="phone">158****4412</div>
</div>
</div>
</td>
<td><strong>8</strong> <span class="cl-bar" style="width:8px;"></span></td>
<td style="font-weight:600;">&#165;592.00</td>
<td>&#165;74.0</td>
<td>2026-02-10</td>
<td><span class="g-tag g-tag-green">新客户</span></td>
<td><a class="g-action" onclick="openClDrawer('赵敏')">查看</a></td>
</tr>
<tr>
<td>
<div class="cl-cust-cell">
<div class="cl-avatar" style="background:#ef4444"></div>
<div class="cl-cust-info">
<div class="name">陈志远</div>
<div class="phone">137****7756</div>
</div>
</div>
</td>
<td><strong>15</strong> <span class="cl-bar" style="width:15px;"></span></td>
<td style="font-weight:600;">&#165;1,380.00</td>
<td>&#165;92.0</td>
<td>2026-01-18</td>
<td><span class="g-tag g-tag-gray">沉睡</span></td>
<td><a class="g-action" onclick="openClDrawer('陈志远')">查看</a></td>
</tr>
<tr>
<td>
<div class="cl-cust-cell">
<div class="cl-avatar" style="background:#ec4899"></div>
<div class="cl-cust-info">
<div class="name">刘思琪</div>
<div class="phone">155****3301</div>
</div>
</div>
</td>
<td><strong>22</strong> <span class="cl-bar" style="width:22px;"></span></td>
<td style="font-weight:600;">&#165;1,870.00</td>
<td>&#165;85.0</td>
<td>2026-02-14</td>
<td><span class="g-tag g-tag-blue">活跃</span></td>
<td><a class="g-action" onclick="openClDrawer('刘思琪')">查看</a></td>
</tr>
<tr>
<td>
<div class="cl-cust-cell">
<div class="cl-avatar" style="background:#6366f1"></div>
<div class="cl-cust-info">
<div class="name">孙浩然</div>
<div class="phone">182****0087</div>
</div>
</div>
</td>
<td><strong>3</strong> <span class="cl-bar" style="width:3px;"></span></td>
<td style="font-weight:600;">&#165;186.00</td>
<td>&#165;62.0</td>
<td>2026-02-08</td>
<td><span class="g-tag g-tag-green">新客户</span></td>
<td><a class="g-action" onclick="openClDrawer('孙浩然')">查看</a></td>
</tr>
<tr style="opacity:.55;">
<td>
<div class="cl-cust-cell">
<div class="cl-avatar" style="background:#9ca3af"></div>
<div class="cl-cust-info">
<div class="name">周建国</div>
<div class="phone">133****5590</div>
</div>
</div>
</td>
<td><strong>6</strong> <span class="cl-bar" style="width:6px;"></span></td>
<td style="font-weight:600;">&#165;420.00</td>
<td>&#165;70.0</td>
<td>2025-11-02</td>
<td><span class="g-tag g-tag-red">流失</span></td>
<td><a class="g-action" onclick="openClDrawer('周建国')">查看</a></td>
</tr>
</tbody>
</table>
<div class="cl-pagination">
<span>共 1,286 条</span>
<button class="cl-page-btn">&lt;</button>
<button class="cl-page-btn active">1</button>
<button class="cl-page-btn">2</button>
<button class="cl-page-btn">3</button>
<span>...</span>
<button class="cl-page-btn">129</button>
<button class="cl-page-btn">&gt;</button>
<span style="margin-left:8px;">10 条/页</span>
</div>
</div>
</div>
<!-- 客户详情抽屉 -->
<div class="g-drawer-mask" id="clDrawerMask" onclick="closeClDrawer()"></div>
<div class="g-drawer" id="clDrawer" style="width:520px">
<div class="g-drawer-hd">
<div class="g-drawer-title" id="clDrawerTitle">客户详情</div>
<button class="g-drawer-close" onclick="closeClDrawer()"><i data-lucide="x" style="width:18px;height:18px;"></i></button>
</div>
<div class="g-drawer-bd">
<!-- 基本信息 -->
<div style="display:flex;align-items:center;gap:14px;margin-bottom:20px;">
<div style="width:52px;height:52px;border-radius:50%;background:#3b82f6;display:flex;align-items:center;justify-content:center;color:#fff;font-size:20px;font-weight:600;"></div>
<div>
<div style="font-size:16px;font-weight:600;color:var(--g-text);" id="clName">张小明</div>
<div style="font-size:13px;color:var(--g-text-muted);margin-top:2px;">138****6621 · 注册于 2025-03-12</div>
</div>
<div style="margin-left:auto;display:flex;gap:6px;">
<span class="g-tag g-tag-orange">高价值</span>
<span class="g-tag g-tag-blue">活跃</span>
</div>
</div>
<!-- 消费概览 -->
<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:10px;margin-bottom:24px;">
<div style="background:#f8f9fb;border-radius:8px;padding:12px;text-align:center;">
<div style="font-size:12px;color:var(--g-text-muted);">累计下单</div>
<div style="font-size:20px;font-weight:700;color:var(--g-text);margin-top:4px;">42 次</div>
</div>
<div style="background:#f8f9fb;border-radius:8px;padding:12px;text-align:center;">
<div style="font-size:12px;color:var(--g-text-muted);">累计消费</div>
<div style="font-size:20px;font-weight:700;color:var(--primary);margin-top:4px;">&#165;3,628</div>
</div>
<div style="background:#f8f9fb;border-radius:8px;padding:12px;text-align:center;">
<div style="font-size:12px;color:var(--g-text-muted);">复购率</div>
<div style="font-size:20px;font-weight:700;color:var(--g-success);margin-top:4px;">78%</div>
</div>
</div>
<!-- 消费偏好 -->
<div style="font-size:15px;font-weight:600;color:var(--g-text);padding-left:10px;border-left:3px solid var(--primary);margin-bottom:14px;">消费偏好</div>
<div style="display:flex;gap:8px;flex-wrap:wrap;margin-bottom:20px;">
<span class="g-tag g-tag-blue">奶茶类</span>
<span class="g-tag g-tag-green">小食类</span>
<span class="g-tag g-tag-purple">果茶类</span>
</div>
<div style="font-size:13px;color:var(--g-text-secondary);margin-bottom:20px;">
<div style="display:flex;justify-content:space-between;margin-bottom:6px;"><span>偏好下单时段</span><span style="font-weight:500;">14:00 - 16:00</span></div>
<div style="display:flex;justify-content:space-between;margin-bottom:6px;"><span>平均客单价</span><span style="font-weight:500;">&#165;86.4</span></div>
<div style="display:flex;justify-content:space-between;"><span>常用配送方式</span><span style="font-weight:500;">外卖配送</span></div>
</div>
<!-- 常购商品 -->
<div style="font-size:15px;font-weight:600;color:var(--g-text);padding-left:10px;border-left:3px solid var(--primary);margin-bottom:14px;">常购商品 TOP5</div>
<div style="margin-bottom:20px;">
<div style="display:flex;align-items:center;gap:10px;padding:8px 0;border-bottom:1px solid #f3f4f6;font-size:13px;">
<span style="width:20px;text-align:center;color:var(--g-text-muted);">1</span>
<span style="flex:1;color:var(--g-text);">珍珠奶茶(大杯)</span>
<span style="color:var(--g-text-secondary);">18次</span>
</div>
<div style="display:flex;align-items:center;gap:10px;padding:8px 0;border-bottom:1px solid #f3f4f6;font-size:13px;">
<span style="width:20px;text-align:center;color:var(--g-text-muted);">2</span>
<span style="flex:1;color:var(--g-text);">芒果鲜果茶</span>
<span style="color:var(--g-text-secondary);">12次</span>
</div>
<div style="display:flex;align-items:center;gap:10px;padding:8px 0;border-bottom:1px solid #f3f4f6;font-size:13px;">
<span style="width:20px;text-align:center;color:var(--g-text-muted);">3</span>
<span style="flex:1;color:var(--g-text);">薯条(大份)</span>
<span style="color:var(--g-text-secondary);">9次</span>
</div>
<div style="display:flex;align-items:center;gap:10px;padding:8px 0;border-bottom:1px solid #f3f4f6;font-size:13px;">
<span style="width:20px;text-align:center;color:var(--g-text-muted);">4</span>
<span style="flex:1;color:var(--g-text);">杨枝甘露</span>
<span style="color:var(--g-text-secondary);">7次</span>
</div>
<div style="display:flex;align-items:center;gap:10px;padding:8px 0;font-size:13px;">
<span style="width:20px;text-align:center;color:var(--g-text-muted);">5</span>
<span style="flex:1;color:var(--g-text);">鸡米花</span>
<span style="color:var(--g-text-secondary);">6次</span>
</div>
</div>
<!-- 近期消费趋势 -->
<div style="font-size:15px;font-weight:600;color:var(--g-text);padding-left:10px;border-left:3px solid var(--primary);margin-bottom:14px;">近6月消费趋势</div>
<div style="display:flex;align-items:flex-end;gap:8px;height:100px;margin-bottom:6px;">
<div style="flex:1;display:flex;flex-direction:column;align-items:center;gap:4px;">
<div style="width:100%;background:color-mix(in srgb, var(--primary) 70%, transparent);border-radius:4px 4px 0 0;height:45px;"></div>
<span style="font-size:11px;color:var(--g-text-muted);">9月</span>
</div>
<div style="flex:1;display:flex;flex-direction:column;align-items:center;gap:4px;">
<div style="width:100%;background:color-mix(in srgb, var(--primary) 70%, transparent);border-radius:4px 4px 0 0;height:60px;"></div>
<span style="font-size:11px;color:var(--g-text-muted);">10月</span>
</div>
<div style="flex:1;display:flex;flex-direction:column;align-items:center;gap:4px;">
<div style="width:100%;background:color-mix(in srgb, var(--primary) 70%, transparent);border-radius:4px 4px 0 0;height:38px;"></div>
<span style="font-size:11px;color:var(--g-text-muted);">11月</span>
</div>
<div style="flex:1;display:flex;flex-direction:column;align-items:center;gap:4px;">
<div style="width:100%;background:color-mix(in srgb, var(--primary) 70%, transparent);border-radius:4px 4px 0 0;height:72px;"></div>
<span style="font-size:11px;color:var(--g-text-muted);">12月</span>
</div>
<div style="flex:1;display:flex;flex-direction:column;align-items:center;gap:4px;">
<div style="width:100%;background:color-mix(in srgb, var(--primary) 70%, transparent);border-radius:4px 4px 0 0;height:55px;"></div>
<span style="font-size:11px;color:var(--g-text-muted);">1月</span>
</div>
<div style="flex:1;display:flex;flex-direction:column;align-items:center;gap:4px;">
<div style="width:100%;background:var(--primary);border-radius:4px 4px 0 0;height:85px;"></div>
<span style="font-size:11px;color:var(--g-text-muted);">2月</span>
</div>
</div>
<!-- 最近订单 -->
<div style="font-size:15px;font-weight:600;color:var(--g-text);padding-left:10px;border-left:3px solid var(--primary);margin-bottom:14px;margin-top:20px;">最近订单</div>
<div style="font-size:13px;">
<div style="display:flex;justify-content:space-between;align-items:center;padding:10px 0;border-bottom:1px solid #f3f4f6;">
<div>
<div style="color:var(--g-text);font-weight:500;">珍珠奶茶 x1、薯条 x1</div>
<div style="color:var(--g-text-muted);font-size:12px;margin-top:2px;">2026-02-14 15:23</div>
</div>
<div style="font-weight:600;color:var(--g-text);">&#165;38.00</div>
</div>
<div style="display:flex;justify-content:space-between;align-items:center;padding:10px 0;border-bottom:1px solid #f3f4f6;">
<div>
<div style="color:var(--g-text);font-weight:500;">芒果鲜果茶 x2</div>
<div style="color:var(--g-text-muted);font-size:12px;margin-top:2px;">2026-02-12 14:08</div>
</div>
<div style="font-weight:600;color:var(--g-text);">&#165;52.00</div>
</div>
<div style="display:flex;justify-content:space-between;align-items:center;padding:10px 0;">
<div>
<div style="color:var(--g-text);font-weight:500;">杨枝甘露 x1、鸡米花 x1</div>
<div style="color:var(--g-text-muted);font-size:12px;margin-top:2px;">2026-02-10 16:45</div>
</div>
<div style="font-weight:600;color:var(--g-text);">&#165;45.00</div>
</div>
</div>
</div>
<div class="g-drawer-ft">
<button class="g-btn" onclick="closeClDrawer()">关闭</button>
<button class="g-btn g-btn-primary" onclick="navigateTo('客户画像')">查看完整画像</button>
</div>
</div>
<script>
function openClDrawer(name) {
document.getElementById('clDrawerMask').classList.add('open');
document.getElementById('clDrawer').classList.add('open');
document.getElementById('clName').textContent = name;
}
function closeClDrawer() {
document.getElementById('clDrawerMask').classList.remove('open');
document.getElementById('clDrawer').classList.remove('open');
}
</script>

109
pages/cust-profile.html Normal file
View File

@@ -0,0 +1,109 @@
<!-- 客户画像页 -->
<style>
.page-cust-profile .cp-header { display:flex; align-items:center; gap:16px; background:#fff; border-radius:10px; padding:20px; box-shadow:var(--g-shadow-sm); margin-bottom:16px; }
.page-cust-profile .cp-avatar { width:56px; height:56px; border-radius:50%; display:flex; align-items:center; justify-content:center; font-size:22px; font-weight:700; color:#fff; background:#3b82f6; flex-shrink:0; }
.page-cust-profile .cp-name { font-size:18px; font-weight:700; color:var(--g-text); }
.page-cust-profile .cp-meta { font-size:13px; color:var(--g-text-muted); margin-top:4px; }
.page-cust-profile .cp-kpis { display:grid; grid-template-columns:repeat(5,1fr); gap:12px; margin-bottom:16px; }
.page-cust-profile .cp-kpi { background:#fff; border-radius:10px; padding:16px; text-align:center; box-shadow:var(--g-shadow-sm); transition:var(--g-transition); }
.page-cust-profile .cp-kpi:hover { box-shadow:var(--g-shadow-md); transform:translateY(-1px); }
.page-cust-profile .cp-kpi .num { font-size:22px; font-weight:700; color:var(--g-text); }
.page-cust-profile .cp-kpi .num.blue { color:var(--primary); }
.page-cust-profile .cp-kpi .lbl { font-size:12px; color:var(--g-text-muted); margin-top:4px; }
.page-cust-profile .cp-grid { display:grid; grid-template-columns:1fr 1fr; gap:14px; }
.page-cust-profile .cp-card { background:#fff; border-radius:10px; padding:20px; box-shadow:var(--g-shadow-sm); }
.page-cust-profile .cp-card.full { grid-column:1/-1; }
.page-cust-profile .cp-card-title { font-size:15px; font-weight:600; color:var(--g-text); padding-left:10px; border-left:3px solid var(--primary); margin-bottom:16px; }
.page-cust-profile .cp-pref-row { display:flex; justify-content:space-between; padding:10px 0; border-bottom:1px solid #f3f4f6; font-size:13px; }
.page-cust-profile .cp-pref-row:last-child { border-bottom:none; }
.page-cust-profile .cp-pref-label { color:var(--g-text-secondary); }
.page-cust-profile .cp-pref-val { color:var(--g-text); font-weight:500; }
.page-cust-profile .cp-top-item { display:flex; align-items:center; gap:10px; padding:8px 0; border-bottom:1px solid #f3f4f6; font-size:13px; }
.page-cust-profile .cp-top-item:last-child { border-bottom:none; }
.page-cust-profile .cp-top-rank { width:22px; height:22px; border-radius:50%; display:flex; align-items:center; justify-content:center; font-size:11px; font-weight:700; flex-shrink:0; }
.page-cust-profile .cp-top-rank.gold { background:#fef3c7; color:#d97706; }
.page-cust-profile .cp-top-rank.silver { background:#f3f4f6; color:#6b7280; }
.page-cust-profile .cp-top-bar { flex:1; height:6px; background:#f0f0f0; border-radius:3px; overflow:hidden; }
.page-cust-profile .cp-top-bar-inner { height:100%; border-radius:3px; background:var(--primary); }
.page-cust-profile .cp-bars { display:flex; align-items:flex-end; gap:8px; height:120px; }
.page-cust-profile .cp-bar-col { flex:1; display:flex; flex-direction:column; align-items:center; gap:4px; }
.page-cust-profile .cp-bar { width:100%; border-radius:4px 4px 0 0; background:var(--primary); transition:var(--g-transition); cursor:pointer; }
.page-cust-profile .cp-bar:hover { opacity:.8; }
.page-cust-profile .cp-bar-lbl { font-size:11px; color:var(--g-text-muted); }
.page-cust-profile .cp-bar-val { font-size:11px; color:var(--g-text-secondary); font-weight:500; }
</style>
<div class="page-cust-profile">
<div class="cp-header">
<div class="cp-avatar"></div>
<div>
<div class="cp-name">王大力</div>
<div class="cp-meta">138****6601 · 首次下单 2025-06-18 · 来源:小程序</div>
</div>
<div style="margin-left:auto;display:flex;gap:8px;">
<span class="g-tag g-tag-orange">高价值</span>
<span class="g-tag g-tag-blue">活跃</span>
</div>
</div>
<div class="cp-kpis">
<div class="cp-kpi"><div class="num blue">35</div><div class="lbl">下单次数</div></div>
<div class="cp-kpi"><div class="num">¥4,210</div><div class="lbl">累计消费</div></div>
<div class="cp-kpi"><div class="num">¥120</div><div class="lbl">客单价</div></div>
<div class="cp-kpi"><div class="num" style="color:var(--g-success);">68%</div><div class="lbl">复购率</div></div>
<div class="cp-kpi"><div class="num">4.2天</div><div class="lbl">平均下单间隔</div></div>
</div>
<div class="cp-grid">
<div class="cp-card">
<div class="cp-card-title">消费偏好</div>
<div class="cp-pref-row"><span class="cp-pref-label">偏好品类</span><span class="cp-pref-val">快餐、饮品</span></div>
<div class="cp-pref-row"><span class="cp-pref-label">常用口味</span><span class="cp-pref-val">微辣、少糖</span></div>
<div class="cp-pref-row"><span class="cp-pref-label">下单高峰</span><span class="cp-pref-val">11:30-13:00、18:00-19:30</span></div>
<div class="cp-pref-row"><span class="cp-pref-label">配送方式</span><span class="cp-pref-val">外卖配送 (86%)、自提 (14%)</span></div>
<div class="cp-pref-row"><span class="cp-pref-label">支付方式</span><span class="cp-pref-val">微信支付</span></div>
<div class="cp-pref-row"><span class="cp-pref-label">平均配送距离</span><span class="cp-pref-val">2.3 km</span></div>
</div>
<div class="cp-card">
<div class="cp-card-title">常购商品 TOP 5</div>
<div class="cp-top-item"><span class="cp-top-rank gold">1</span><span style="width:100px;">香辣鸡腿堡</span><div class="cp-top-bar"><div class="cp-top-bar-inner" style="width:90%;"></div></div><span style="width:40px;text-align:right;color:var(--g-text-secondary);">18次</span></div>
<div class="cp-top-item"><span class="cp-top-rank gold">2</span><span style="width:100px;">珍珠奶茶(大)</span><div class="cp-top-bar"><div class="cp-top-bar-inner" style="width:75%;"></div></div><span style="width:40px;text-align:right;color:var(--g-text-secondary);">15次</span></div>
<div class="cp-top-item"><span class="cp-top-rank gold">3</span><span style="width:100px;">薯条(中)</span><div class="cp-top-bar"><div class="cp-top-bar-inner" style="width:60%;"></div></div><span style="width:40px;text-align:right;color:var(--g-text-secondary);">12次</span></div>
<div class="cp-top-item"><span class="cp-top-rank silver">4</span><span style="width:100px;">麻辣烫套餐</span><div class="cp-top-bar"><div class="cp-top-bar-inner" style="width:45%;"></div></div><span style="width:40px;text-align:right;color:var(--g-text-secondary);">9次</span></div>
<div class="cp-top-item"><span class="cp-top-rank silver">5</span><span style="width:100px;">可乐(中)</span><div class="cp-top-bar"><div class="cp-top-bar-inner" style="width:35%;"></div></div><span style="width:40px;text-align:right;color:var(--g-text-secondary);">7次</span></div>
</div>
<div class="cp-card full">
<div class="cp-card-title">月度消费趋势</div>
<div class="cp-bars">
<div class="cp-bar-col"><div class="cp-bar-val">¥280</div><div class="cp-bar" style="height:47px;opacity:.6;"></div><span class="cp-bar-lbl">3月</span></div>
<div class="cp-bar-col"><div class="cp-bar-val">¥350</div><div class="cp-bar" style="height:58px;opacity:.6;"></div><span class="cp-bar-lbl">4月</span></div>
<div class="cp-bar-col"><div class="cp-bar-val">¥420</div><div class="cp-bar" style="height:70px;opacity:.7;"></div><span class="cp-bar-lbl">5月</span></div>
<div class="cp-bar-col"><div class="cp-bar-val">¥310</div><div class="cp-bar" style="height:52px;opacity:.6;"></div><span class="cp-bar-lbl">6月</span></div>
<div class="cp-bar-col"><div class="cp-bar-val">¥480</div><div class="cp-bar" style="height:80px;opacity:.7;"></div><span class="cp-bar-lbl">7月</span></div>
<div class="cp-bar-col"><div class="cp-bar-val">¥390</div><div class="cp-bar" style="height:65px;opacity:.7;"></div><span class="cp-bar-lbl">8月</span></div>
<div class="cp-bar-col"><div class="cp-bar-val">¥260</div><div class="cp-bar" style="height:43px;opacity:.6;"></div><span class="cp-bar-lbl">9月</span></div>
<div class="cp-bar-col"><div class="cp-bar-val">¥520</div><div class="cp-bar" style="height:87px;opacity:.8;"></div><span class="cp-bar-lbl">10月</span></div>
<div class="cp-bar-col"><div class="cp-bar-val">¥340</div><div class="cp-bar" style="height:57px;opacity:.6;"></div><span class="cp-bar-lbl">11月</span></div>
<div class="cp-bar-col"><div class="cp-bar-val">¥450</div><div class="cp-bar" style="height:75px;opacity:.7;"></div><span class="cp-bar-lbl">12月</span></div>
<div class="cp-bar-col"><div class="cp-bar-val">¥380</div><div class="cp-bar" style="height:63px;opacity:.7;"></div><span class="cp-bar-lbl">1月</span></div>
<div class="cp-bar-col"><div class="cp-bar-val">¥210</div><div class="cp-bar" style="height:35px;"></div><span class="cp-bar-lbl">2月</span></div>
</div>
</div>
<div class="cp-card full">
<div class="cp-card-title">最近订单</div>
<table style="width:100%;border-collapse:collapse;font-size:13px;">
<thead><tr>
<th style="background:#f8f9fb;padding:8px 12px;text-align:left;font-weight:600;color:#6b7280;border-bottom:1px solid #e5e7eb;">订单号</th>
<th style="background:#f8f9fb;padding:8px 12px;text-align:left;font-weight:600;color:#6b7280;border-bottom:1px solid #e5e7eb;">金额</th>
<th style="background:#f8f9fb;padding:8px 12px;text-align:left;font-weight:600;color:#6b7280;border-bottom:1px solid #e5e7eb;">商品</th>
<th style="background:#f8f9fb;padding:8px 12px;text-align:left;font-weight:600;color:#6b7280;border-bottom:1px solid #e5e7eb;">方式</th>
<th style="background:#f8f9fb;padding:8px 12px;text-align:left;font-weight:600;color:#6b7280;border-bottom:1px solid #e5e7eb;">状态</th>
<th style="background:#f8f9fb;padding:8px 12px;text-align:left;font-weight:600;color:#6b7280;border-bottom:1px solid #e5e7eb;">时间</th>
</tr></thead>
<tbody>
<tr><td style="padding:10px 12px;border-bottom:1px solid #f3f4f6;">#20260212035</td><td style="padding:10px 12px;border-bottom:1px solid #f3f4f6;">¥128</td><td style="padding:10px 12px;border-bottom:1px solid #f3f4f6;">香辣鸡腿堡 x1、珍珠奶茶 x1、薯条 x1</td><td style="padding:10px 12px;border-bottom:1px solid #f3f4f6;"><span class="g-tag g-tag-blue">外卖</span></td><td style="padding:10px 12px;border-bottom:1px solid #f3f4f6;"><span style="color:var(--g-success);font-weight:600;">已完成</span></td><td style="padding:10px 12px;border-bottom:1px solid #f3f4f6;color:var(--g-text-muted);">02-12 18:32</td></tr>
<tr><td style="padding:10px 12px;border-bottom:1px solid #f3f4f6;">#20260210021</td><td style="padding:10px 12px;border-bottom:1px solid #f3f4f6;">¥86</td><td style="padding:10px 12px;border-bottom:1px solid #f3f4f6;">麻辣烫套餐 x1、可乐 x1</td><td style="padding:10px 12px;border-bottom:1px solid #f3f4f6;"><span class="g-tag g-tag-blue">外卖</span></td><td style="padding:10px 12px;border-bottom:1px solid #f3f4f6;"><span style="color:var(--g-success);font-weight:600;">已完成</span></td><td style="padding:10px 12px;border-bottom:1px solid #f3f4f6;color:var(--g-text-muted);">02-10 12:15</td></tr>
<tr><td style="padding:10px 12px;border-bottom:1px solid #f3f4f6;">#20260208018</td><td style="padding:10px 12px;border-bottom:1px solid #f3f4f6;">¥152</td><td style="padding:10px 12px;border-bottom:1px solid #f3f4f6;">香辣鸡腿堡 x2、珍珠奶茶 x2</td><td style="padding:10px 12px;border-bottom:1px solid #f3f4f6;"><span class="g-tag g-tag-green">自提</span></td><td style="padding:10px 12px;border-bottom:1px solid #f3f4f6;"><span style="color:var(--g-success);font-weight:600;">已完成</span></td><td style="padding:10px 12px;border-bottom:1px solid #f3f4f6;color:var(--g-text-muted);">02-08 19:47</td></tr>
<tr><td style="padding:10px 12px;">#20260205009</td><td style="padding:10px 12px;">¥95</td><td style="padding:10px 12px;">薯条 x1、鸡翅桶 x1、可乐 x1</td><td style="padding:10px 12px;"><span class="g-tag g-tag-blue">外卖</span></td><td style="padding:10px 12px;"><span style="color:var(--g-success);font-weight:600;">已完成</span></td><td style="padding:10px 12px;color:var(--g-text-muted);">02-05 11:20</td></tr>
</tbody>
</table>
</div>
</div>
</div>

227
pages/dashboard.html Normal file
View File

@@ -0,0 +1,227 @@
<!-- 工作台 Dashboard -->
<style>
.dashboard { display:flex; flex-direction:column; gap:16px; }
.dash-row { display:grid; gap:16px; }
.dash-row.kpi { grid-template-columns:repeat(4,1fr); }
.dash-row.mid { grid-template-columns:2fr 1fr; }
.dash-row.bot { grid-template-columns:1fr 1fr 1fr; }
.kpi-card { border-radius:10px; padding:20px 24px; color:#fff; position:relative; overflow:hidden; min-height:120px; display:flex; flex-direction:column; justify-content:space-between; box-shadow:var(--g-shadow-sm); transition:var(--g-transition); }
.kpi-card:hover { box-shadow:var(--g-shadow-md); transform:translateY(-2px); }
.kpi-card::before { content:""; position:absolute; top:-30px; right:-30px; width:100px; height:100px; border-radius:50%; background:rgba(255,255,255,0.1); }
.kpi-card::after { content:""; position:absolute; bottom:-20px; right:30px; width:60px; height:60px; border-radius:50%; background:rgba(255,255,255,0.08); }
.kpi-card.c1 { background:linear-gradient(135deg,#1890ff,#096dd9); }
.kpi-card.c2 { background:linear-gradient(135deg,#22c55e,#16a34a); }
.kpi-card.c3 { background:linear-gradient(135deg,#f59e0b,#d97706); }
.kpi-card.c4 { background:linear-gradient(135deg,#722ed1,#531dab); }
.kpi-top { display:flex; align-items:center; justify-content:space-between; }
.kpi-label { font-size:13px; opacity:0.85; }
.kpi-icon { width:40px; height:40px; border-radius:10px; background:rgba(255,255,255,0.2); display:flex; align-items:center; justify-content:center; }
.kpi-value { font-size:34px; font-weight:800; letter-spacing:-1px; margin:8px 0 4px; }
.kpi-sub { font-size:12px; opacity:0.75; display:flex; align-items:center; gap:4px; }
.kpi-sub .up { color:#b7eb8f; }
.kpi-sub .down { color:#fecaca; }
.dash-card-more { font-size:12px; color:#9ca3af; font-weight:400; cursor:pointer; transition:var(--g-transition); }
.dash-card-more:hover { color:var(--primary); }
.bar-chart { display:flex; align-items:flex-end; gap:10px; height:200px; padding-top:10px; }
.bar-chart-wrap { display:flex; height:200px; }
.bar-y-axis { display:flex; flex-direction:column; justify-content:space-between; padding-right:8px; font-size:11px; color:#9ca3af; text-align:right; min-width:36px; padding-bottom:22px; }
.bar-chart-body { flex:1; display:flex; align-items:flex-end; gap:10px; }
.bar-col { flex:1; display:flex; flex-direction:column; align-items:center; gap:6px; }
.bar-wrap { width:100%; display:flex; gap:4px; justify-content:center; height:170px; align-items:flex-end; }
.bar { width:18px; border-radius:4px 4px 0 0; transition:height 0.6s cubic-bezier(0.2,0,0,1); cursor:pointer; }
.bar:hover { opacity:0.85; }
.bar.income { background:linear-gradient(180deg,#1890ff,#69c0ff); }
.bar.orders { background:linear-gradient(180deg,#22c55e,#86efac); }
.bar-label { font-size:11px; color:#9ca3af; }
.donut-wrap { display:flex; align-items:center; gap:24px; padding:10px 0; }
.donut { width:140px; height:140px; border-radius:50%; position:relative; flex-shrink:0; }
.donut-center { position:absolute; inset:30px; background:#fff; border-radius:50%; display:flex; flex-direction:column; align-items:center; justify-content:center; }
.donut-center .num { font-size:24px; font-weight:800; color:#1a1a2e; }
.donut-center .txt { font-size:11px; color:#9ca3af; }
.donut-legend { display:flex; flex-direction:column; gap:10px; flex:1; }
.legend-item { display:flex; align-items:center; gap:8px; font-size:13px; color:#1a1a2e; }
.legend-dot { width:8px; height:8px; border-radius:2px; flex-shrink:0; }
.legend-val { margin-left:auto; font-weight:600; }
.rank-list { display:flex; flex-direction:column; gap:12px; }
.rank-item { display:flex; align-items:center; gap:10px; }
.rank-num { width:20px; height:20px; border-radius:4px; display:flex; align-items:center; justify-content:center; font-size:11px; font-weight:600; color:#fff; flex-shrink:0; }
.rank-num.t1 { background:linear-gradient(135deg,#ef4444,#f97316); }
.rank-num.t2 { background:linear-gradient(135deg,#f59e0b,#fbbf24); }
.rank-num.t3 { background:linear-gradient(135deg,#1890ff,#69c0ff); }
.rank-num.tn { background:#e5e7eb; color:#4b5563; }
.rank-name { font-size:13px; color:#1a1a2e; flex:1; }
.rank-bar-bg { width:100px; height:6px; background:#e5e7eb; border-radius:3px; overflow:hidden; }
.rank-bar-fill { height:100%; border-radius:3px; background:linear-gradient(90deg,#1890ff,#69c0ff); }
.rank-count { font-size:12px; color:#9ca3af; width:40px; text-align:right; font-weight:600; }
.feed-list { display:flex; flex-direction:column; max-height:320px; overflow-y:auto; }
.feed-list::-webkit-scrollbar { width:3px; }
.feed-list::-webkit-scrollbar-thumb { background:#e5e7eb; border-radius:2px; }
.feed-item { display:flex; align-items:center; gap:10px; padding:10px 0; border-bottom:1px solid #f3f4f6; transition:var(--g-transition); }
.feed-item:hover { background:color-mix(in srgb, var(--primary) 3%, #fff); }
.feed-item:last-child { border-bottom:none; }
.feed-avatar { width:32px; height:32px; border-radius:50%; background:#f8f9fb; display:flex; align-items:center; justify-content:center; font-size:12px; flex-shrink:0; color:#4b5563; }
.feed-info { flex:1; }
.feed-info .top { font-size:13px; color:#1a1a2e; display:flex; justify-content:space-between; }
.feed-info .bot { font-size:11px; color:#9ca3af; margin-top:3px; }
.feed-badge { padding:2px 8px; border-radius:6px; font-size:11px; white-space:nowrap; font-weight:600; }
.feed-badge.new { background:#eff6ff; color:#2563eb; }
.feed-badge.cooking { background:#fffbeb; color:#d97706; }
.feed-badge.delivering { background:#f0fdf4; color:#16a34a; }
.feed-badge.done { background:#f8f9fb; color:#9ca3af; }
.alert-list { display:flex; flex-direction:column; gap:8px; }
.alert-item { display:flex; align-items:center; gap:10px; padding:10px 12px; border-radius:8px; font-size:13px; transition:var(--g-transition); cursor:pointer; }
.alert-item:hover { filter:brightness(0.97); transform:translateX(2px); }
.alert-item.warn { background:#fffbeb; border:1px solid #fde68a; color:#92400e; }
.alert-item.danger { background:#fef2f2; border:1px solid #fecaca; color:#991b1b; }
.alert-item.info { background:#eff6ff; border:1px solid #bfdbfe; color:#1e40af; }
.alert-icon { flex-shrink:0; }
.quota-item { display:flex; flex-direction:column; gap:6px; margin-bottom:14px; }
.quota-item:last-child { margin-bottom:0; }
.quota-head { display:flex; justify-content:space-between; font-size:13px; }
.quota-name { color:#1a1a2e; font-weight:500; }
.quota-val { color:#9ca3af; }
.quota-bar { height:8px; background:#e5e7eb; border-radius:4px; overflow:hidden; }
.quota-fill { height:100%; border-radius:4px; transition:width 0.6s; }
.quota-fill.ok { background:linear-gradient(90deg,#1890ff,#69c0ff); }
.quota-fill.mid { background:linear-gradient(90deg,#f59e0b,#fbbf24); }
.quota-fill.high { background:linear-gradient(90deg,#ef4444,#f97316); }
@keyframes fadeInUp { from { opacity:0; transform:translateY(10px); } to { opacity:1; transform:translateY(0); } }
.dashboard .dash-row > * { animation: fadeInUp 0.4s ease both; }
.dashboard .dash-row > *:nth-child(2) { animation-delay:0.05s; }
.dashboard .dash-row > *:nth-child(3) { animation-delay:0.1s; }
.dashboard .dash-row > *:nth-child(4) { animation-delay:0.15s; }
</style>
<div class="dashboard">
<!-- Date Picker -->
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:-8px">
<div style="font-size:15px;font-weight:600;color:#1a1a2e">数据概览</div>
<div style="display:flex;align-items:center;gap:8px">
<span style="font-size:13px;color:#666">日期</span>
<input type="date" class="g-input" style="width:140px;height:30px;font-size:12px" value="2025-01-15">
<span style="font-size:13px;color:#999"></span>
<input type="date" class="g-input" style="width:140px;height:30px;font-size:12px" value="2025-01-15">
</div>
</div>
<!-- KPI -->
<div class="dash-row kpi">
<div class="kpi-card c1">
<div class="kpi-top"><span class="kpi-label">今日订单</span><div class="kpi-icon"><i data-lucide="shopping-bag" style="width:20px;height:20px;"></i></div></div>
<div class="kpi-value">386</div>
<div class="kpi-sub"><span class="up">+12.5%</span> 较昨日</div>
</div>
<div class="kpi-card c2">
<div class="kpi-top"><span class="kpi-label">今日营收</span><div class="kpi-icon"><i data-lucide="trending-up" style="width:20px;height:20px;"></i></div></div>
<div class="kpi-value">&yen;12,680</div>
<div class="kpi-sub"><span class="up">+8.3%</span> 较昨日</div>
</div>
<div class="kpi-card c3">
<div class="kpi-top"><span class="kpi-label">客单价</span><div class="kpi-icon"><i data-lucide="receipt" style="width:20px;height:20px;"></i></div></div>
<div class="kpi-value">&yen;32.8</div>
<div class="kpi-sub"><span class="down">-2.1%</span> 较昨日</div>
</div>
<div class="kpi-card c4">
<div class="kpi-top"><span class="kpi-label">新增客户</span><div class="kpi-icon"><i data-lucide="user-plus" style="width:20px;height:20px;"></i></div></div>
<div class="kpi-value">57</div>
<div class="kpi-sub"><span class="up">+23.9%</span> 较昨日</div>
</div>
</div>
<!-- Revenue Chart + Order Status -->
<div class="dash-row mid">
<div class="g-card" style="padding:0;">
<div class="g-card-title" style="padding:14px 18px; margin-bottom:0; border-bottom:1px solid #f5f5f5; display:flex; align-items:center; justify-content:space-between;">近7日营收趋势<span class="dash-card-more">查看详情 &rarr;</span></div>
<div style="padding:16px 18px;">
<div class="bar-chart-wrap">
<div class="bar-y-axis"><span>¥15k</span><span>¥12k</span><span>¥9k</span><span>¥6k</span><span>¥3k</span><span>0</span></div>
<div class="bar-chart">
<div class="bar-col"><div class="bar-wrap"><div class="bar income" style="height:60%;"></div><div class="bar orders" style="height:45%;"></div></div><span class="bar-label">周一</span></div>
<div class="bar-col"><div class="bar-wrap"><div class="bar income" style="height:75%;"></div><div class="bar orders" style="height:55%;"></div></div><span class="bar-label">周二</span></div>
<div class="bar-col"><div class="bar-wrap"><div class="bar income" style="height:55%;"></div><div class="bar orders" style="height:40%;"></div></div><span class="bar-label">周三</span></div>
<div class="bar-col"><div class="bar-wrap"><div class="bar income" style="height:85%;"></div><div class="bar orders" style="height:65%;"></div></div><span class="bar-label">周四</span></div>
<div class="bar-col"><div class="bar-wrap"><div class="bar income" style="height:95%;"></div><div class="bar orders" style="height:72%;"></div></div><span class="bar-label">周五</span></div>
<div class="bar-col"><div class="bar-wrap"><div class="bar income" style="height:100%;"></div><div class="bar orders" style="height:80%;"></div></div><span class="bar-label">周六</span></div>
<div class="bar-col"><div class="bar-wrap"><div class="bar income" style="height:90%;"></div><div class="bar orders" style="height:70%;"></div></div><span class="bar-label">周日</span></div>
</div>
</div>
<div style="display:flex;gap:16px;justify-content:center;margin-top:10px;font-size:12px;color:#999;">
<span><span style="display:inline-block;width:10px;height:10px;border-radius:2px;background:#1890ff;margin-right:4px;"></span>营收</span>
<span><span style="display:inline-block;width:10px;height:10px;border-radius:2px;background:#52c41a;margin-right:4px;"></span>订单量</span>
</div>
</div>
</div>
<div class="g-card" style="padding:0;">
<div class="g-card-title" style="padding:14px 18px; margin-bottom:0; border-bottom:1px solid #f5f5f5; display:flex; align-items:center; justify-content:space-between;">今日订单状态</div>
<div style="padding:16px 18px;">
<div class="donut-wrap">
<div class="donut" style="background:conic-gradient(#1890ff 0% 15%,#fa8c16 15% 30%,#52c41a 30% 55%,#722ed1 55% 75%,#d9d9d9 75% 100%);">
<div class="donut-center"><span class="num">386</span><span class="txt">总订单</span></div>
</div>
<div class="donut-legend">
<div class="legend-item"><span class="legend-dot" style="background:#1890ff;"></span>待接单<span class="legend-val">58</span></div>
<div class="legend-item"><span class="legend-dot" style="background:#fa8c16;"></span>制作中<span class="legend-val">59</span></div>
<div class="legend-item"><span class="legend-dot" style="background:#52c41a;"></span>配送中<span class="legend-val">96</span></div>
<div class="legend-item"><span class="legend-dot" style="background:#722ed1;"></span>已完成<span class="legend-val">77</span></div>
<div class="legend-item"><span class="legend-dot" style="background:#d9d9d9;"></span>已取消<span class="legend-val">96</span></div>
</div>
</div>
</div>
</div>
</div>
<!-- Ranking + Feed + Alerts -->
<div class="dash-row bot">
<div class="g-card" style="padding:0;">
<div class="g-card-title" style="padding:14px 18px; margin-bottom:0; border-bottom:1px solid #f5f5f5; display:flex; align-items:center; justify-content:space-between;">热销菜品 TOP5<span class="dash-card-more">更多 &rarr;</span></div>
<div style="padding:16px 18px;">
<div class="rank-list">
<div class="rank-item"><span class="rank-num t1">1</span><span class="rank-name">招牌红烧肉饭</span><div class="rank-bar-bg"><div class="rank-bar-fill" style="width:100%;"></div></div><span class="rank-count">128</span></div>
<div class="rank-item"><span class="rank-num t2">2</span><span class="rank-name">香辣鸡腿堡套餐</span><div class="rank-bar-bg"><div class="rank-bar-fill" style="width:78%;"></div></div><span class="rank-count">99</span></div>
<div class="rank-item"><span class="rank-num t3">3</span><span class="rank-name">番茄牛腩面</span><div class="rank-bar-bg"><div class="rank-bar-fill" style="width:62%;"></div></div><span class="rank-count">79</span></div>
<div class="rank-item"><span class="rank-num tn">4</span><span class="rank-name">鱼香肉丝盖饭</span><div class="rank-bar-bg"><div class="rank-bar-fill" style="width:45%;"></div></div><span class="rank-count">58</span></div>
<div class="rank-item"><span class="rank-num tn">5</span><span class="rank-name">冰柠檬红茶</span><div class="rank-bar-bg"><div class="rank-bar-fill" style="width:35%;"></div></div><span class="rank-count">45</span></div>
</div>
</div>
</div>
<div class="g-card" style="padding:0;">
<div class="g-card-title" style="padding:14px 18px; margin-bottom:0; border-bottom:1px solid #f5f5f5; display:flex; align-items:center; justify-content:space-between;">实时订单<span class="dash-card-more">全部订单 &rarr;</span></div>
<div style="padding:8px 18px;">
<div class="feed-list">
<div class="feed-item"><div class="feed-avatar"></div><div class="feed-info"><div class="top"><span>#20260211001</span><span>&yen;45.5</span></div><div class="bot">张先生 &middot; 朝阳店 &middot; 2分钟前</div></div><span class="feed-badge new">待接单</span></div>
<div class="feed-item"><div class="feed-avatar"></div><div class="feed-info"><div class="top"><span>#20260211002</span><span>&yen;68.0</span></div><div class="bot">李女士 &middot; 海淀店 &middot; 5分钟前</div></div><span class="feed-badge cooking">制作中</span></div>
<div class="feed-item"><div class="feed-avatar"></div><div class="feed-info"><div class="top"><span>#20260211003</span><span>&yen;32.0</span></div><div class="bot">王先生 &middot; 望京店 &middot; 12分钟前</div></div><span class="feed-badge delivering">配送中</span></div>
<div class="feed-item"><div class="feed-avatar"></div><div class="feed-info"><div class="top"><span>#20260211004</span><span>&yen;55.5</span></div><div class="bot">赵女士 &middot; 朝阳店 &middot; 18分钟前</div></div><span class="feed-badge done">已完成</span></div>
<div class="feed-item"><div class="feed-avatar"></div><div class="feed-info"><div class="top"><span>#20260211005</span><span>&yen;28.0</span></div><div class="bot">刘先生 &middot; 通州店 &middot; 25分钟前</div></div><span class="feed-badge done">已完成</span></div>
</div>
</div>
</div>
<div class="g-card" style="padding:0;">
<div class="g-card-title" style="padding:14px 18px; margin-bottom:0; border-bottom:1px solid #f5f5f5; display:flex; align-items:center; justify-content:space-between;">运营提醒</div>
<div style="padding:16px 18px;">
<div class="alert-list">
<div class="alert-item danger"><i data-lucide="alert-triangle" style="width:16px;height:16px;" class="alert-icon"></i>3款商品库存不足请及时补货</div>
<div class="alert-item warn"><i data-lucide="clock" style="width:16px;height:16px;" class="alert-icon"></i>食品经营许可证将于30天后到期</div>
<div class="alert-item warn"><i data-lucide="message-square" style="width:16px;height:16px;" class="alert-icon"></i>5条新评价待回复</div>
<div class="alert-item info"><i data-lucide="gift" style="width:16px;height:16px;" class="alert-icon"></i>满减活动明日到期,建议续期</div>
</div>
<div style="margin-top:16px;">
<div style="font-size:13px;font-weight:600;color:#333;margin-bottom:10px;">套餐配额</div>
<div class="quota-item"><div class="quota-head"><span class="quota-name">门店数量</span><span class="quota-val">5 / 10</span></div><div class="quota-bar"><div class="quota-fill ok" style="width:50%;"></div></div></div>
<div class="quota-item"><div class="quota-head"><span class="quota-name">员工账号</span><span class="quota-val">18 / 20</span></div><div class="quota-bar"><div class="quota-fill high" style="width:90%;"></div></div></div>
<div class="quota-item"><div class="quota-head"><span class="quota-name">短信额度</span><span class="quota-val">320 / 1000</span></div><div class="quota-bar"><div class="quota-fill ok" style="width:32%;"></div></div></div>
<div class="quota-item"><div class="quota-head"><span class="quota-name">存储空间</span><span class="quota-val">1.8G / 5G</span></div><div class="quota-bar"><div class="quota-fill mid" style="width:36%;"></div></div></div>
</div>
</div>
</div>
</div>
</div>

306
pages/fin-cost.html Normal file
View File

@@ -0,0 +1,306 @@
<!-- 成本管理页 -->
<style>
.fc-page { max-width:1000px; }
.fc-seg { display:flex; background:#f8f9fb; border-radius:var(--g-radius); padding:3px; gap:2px; width:fit-content; margin-bottom:20px; }
.fc-seg-item { padding:7px 28px; border-radius:6px; font-size:13px; cursor:pointer; color:var(--g-text-secondary); transition:var(--g-transition); border:none; background:none; font-weight:500; }
.fc-seg-item.active { background:#fff; color:var(--primary); font-weight:600; box-shadow:var(--g-shadow-sm); }
/* 月份选择器 */
.fc-month-picker { display:flex; align-items:center; gap:14px; margin-bottom:20px; }
.fc-month-arrow { width:32px; height:32px; border-radius:var(--g-radius); border:1px solid #e5e7eb; background:#fff; display:flex; align-items:center; justify-content:center; cursor:pointer; transition:var(--g-transition); }
.fc-month-arrow:hover { border-color:var(--primary); color:var(--primary); }
.fc-month-title { font-size:16px; font-weight:700; color:var(--g-text); min-width:120px; text-align:center; }
/* 成本卡片 */
.fc-cost-card { margin-bottom:16px; }
.fc-cost-hd { display:flex; align-items:center; gap:12px; padding:16px 20px; cursor:pointer; user-select:none; }
.fc-cost-icon { width:40px; height:40px; border-radius:10px; display:flex; align-items:center; justify-content:center; flex-shrink:0; }
.fc-cost-icon.food { background:#eff6ff; color:#2563eb; }
.fc-cost-icon.labor { background:#f0fdf4; color:#16a34a; }
.fc-cost-icon.fixed { background:#fefce8; color:#ca8a04; }
.fc-cost-icon.pack { background:#fdf2f8; color:#db2777; }
.fc-cost-name { font-size:14px; font-weight:600; color:var(--g-text); }
.fc-cost-pct { font-size:12px; color:var(--g-text-muted); margin-left:4px; }
.fc-cost-total { margin-left:auto; display:flex; align-items:center; gap:8px; }
.fc-cost-total .fc-yen { font-size:13px; color:var(--g-text-secondary); }
.fc-cost-input { width:120px; height:34px; padding:0 10px; border:1px solid #e5e7eb; border-radius:6px; font-size:14px; font-weight:600; text-align:right; outline:none; transition:var(--g-transition); }
.fc-cost-input:focus { border-color:var(--primary); box-shadow:0 0 0 3px color-mix(in srgb, var(--primary) 12%, transparent); }
.fc-expand-btn { font-size:12px; color:var(--primary); cursor:pointer; display:flex; align-items:center; gap:4px; transition:var(--g-transition); white-space:nowrap; }
.fc-expand-btn:hover { opacity:.7; }
/* 明细区域 */
.fc-detail { display:none; padding:0 20px 16px; }
.fc-detail.open { display:block; }
.fc-detail-row { display:flex; align-items:center; gap:10px; padding:8px 0; border-bottom:1px solid #f3f4f6; font-size:13px; }
.fc-detail-row:last-child { border-bottom:none; }
.fc-detail-name { width:120px; color:var(--g-text); font-weight:500; }
.fc-detail-val { width:100px; }
.fc-detail-val input { width:100%; height:30px; padding:0 8px; border:1px solid #e5e7eb; border-radius:6px; font-size:13px; text-align:right; outline:none; transition:var(--g-transition); }
.fc-detail-val input:focus { border-color:var(--primary); }
.fc-detail-extra { font-size:12px; color:var(--g-text-muted); }
.fc-detail-sub { font-size:13px; font-weight:600; color:var(--g-text); width:80px; text-align:right; }
.fc-add-row { display:inline-flex; align-items:center; gap:4px; font-size:12px; color:var(--primary); cursor:pointer; margin-top:8px; transition:var(--g-transition); }
.fc-add-row:hover { opacity:.7; }
.fc-del-row { color:#ef4444; cursor:pointer; margin-left:6px; transition:var(--g-transition); }
.fc-del-row:hover { opacity:.7; }
/* 汇总行 */
.fc-summary { display:flex; align-items:center; justify-content:space-between; padding:16px 20px; background:#f8f9fb; border-radius:var(--g-radius); margin-top:8px; }
.fc-summary-label { font-size:15px; font-weight:700; color:var(--g-text); }
.fc-summary-val { font-size:22px; font-weight:800; color:var(--primary); }
/* 分析 - 统计卡片 */
.fc-stat-row { display:grid; grid-template-columns:repeat(4,1fr); gap:14px; margin-bottom:20px; }
.fc-stat-card { padding:18px; border-radius:10px; background:#fff; border:1px solid #f0f0f0; transition:var(--g-transition); }
.fc-stat-card:hover { box-shadow:var(--g-shadow-md); }
.fc-stat-label { font-size:12px; color:var(--g-text-muted); margin-bottom:8px; }
.fc-stat-val { font-size:22px; font-weight:800; color:var(--g-text); }
.fc-stat-val .fc-up { color:#ef4444; font-size:13px; font-weight:600; }
.fc-stat-val .fc-down { color:var(--g-success); font-size:13px; font-weight:600; }
/* 趋势图占位 */
.fc-chart-placeholder { height:220px; border:2px dashed #e5e7eb; border-radius:var(--g-radius); display:flex; flex-direction:column; align-items:center; justify-content:center; color:var(--g-text-muted); gap:8px; margin-bottom:20px; background:#fafafa; }
.fc-chart-placeholder .fc-chart-title { font-size:14px; font-weight:600; color:var(--g-text-secondary); }
.fc-chart-bars { display:flex; align-items:flex-end; gap:16px; height:100px; }
.fc-chart-bar { width:40px; border-radius:4px 4px 0 0; background:linear-gradient(180deg, color-mix(in srgb, var(--primary) 80%, #fff), color-mix(in srgb, var(--primary) 40%, #fff)); }
/* 饼图 */
.fc-pie-section { display:flex; gap:24px; align-items:center; margin-bottom:20px; }
.fc-donut { width:160px; height:160px; border-radius:50%; position:relative; flex-shrink:0; background:conic-gradient(#2563eb 0% 44%, #16a34a 44% 72%, #ca8a04 72% 92%, #db2777 92% 100%); }
.fc-donut-center { position:absolute; inset:32px; background:#fff; border-radius:50%; display:flex; flex-direction:column; align-items:center; justify-content:center; }
.fc-donut-center .num { font-size:18px; font-weight:800; color:var(--g-text); }
.fc-donut-center .txt { font-size:11px; color:var(--g-text-muted); }
.fc-legend { display:flex; flex-direction:column; gap:12px; }
.fc-legend-item { display:flex; align-items:center; gap:8px; font-size:13px; color:var(--g-text); }
.fc-legend-dot { width:10px; height:10px; border-radius:3px; flex-shrink:0; }
.fc-legend-pct { margin-left:auto; font-weight:600; min-width:40px; text-align:right; }
/* 明细表格 */
.fc-table { width:100%; border-collapse:collapse; font-size:13px; }
.fc-table th { background:#f8f9fb; padding:10px 12px; text-align:right; font-weight:600; color:var(--g-text-secondary); border-bottom:1px solid #e5e7eb; }
.fc-table th:first-child { text-align:left; }
.fc-table td { padding:10px 12px; border-bottom:1px solid #f3f4f6; color:var(--g-text); text-align:right; }
.fc-table td:first-child { text-align:left; font-weight:500; }
.fc-table tr:hover td { background:color-mix(in srgb, var(--primary) 3%, #fff); }
.fc-section-hd { font-size:15px; font-weight:600; color:var(--g-text); padding-left:10px; border-left:3px solid var(--primary); margin-bottom:16px; }
</style>
<div class="fc-page">
<!-- 分段Tab -->
<div class="fc-seg">
<button class="fc-seg-item active" onclick="fcSwitchTab(this,'fc-tab-entry')">成本录入</button>
<button class="fc-seg-item" onclick="fcSwitchTab(this,'fc-tab-analysis')">成本分析</button>
</div>
<!-- Tab1: 成本录入 -->
<div id="fc-tab-entry">
<!-- 月份选择器 -->
<div class="fc-month-picker">
<div class="fc-month-arrow" onclick="fcChangeMonth(-1)"><i data-lucide="chevron-left" style="width:16px;height:16px;"></i></div>
<div class="fc-month-title" id="fcMonthTitle">2026年2月</div>
<div class="fc-month-arrow" onclick="fcChangeMonth(1)"><i data-lucide="chevron-right" style="width:16px;height:16px;"></i></div>
</div>
<!-- 食材原料 -->
<div class="g-card fc-cost-card">
<div class="fc-cost-hd" onclick="fcToggleDetail('food')">
<div class="fc-cost-icon food"><i data-lucide="utensils-crossed" style="width:20px;height:20px;"></i></div>
<div>
<div class="fc-cost-name">食材原料 <span class="fc-cost-pct">43.8%</span></div>
</div>
<div class="fc-cost-total">
<span class="fc-yen">¥</span>
<input class="fc-cost-input" type="number" value="18500" placeholder="请输入金额">
</div>
<div class="fc-expand-btn"><i data-lucide="chevron-down" style="width:14px;height:14px;" id="fcArrowFood"></i>明细</div>
</div>
<div class="fc-detail" id="fcDetailFood">
<div class="fc-detail-row"><span class="fc-detail-name">肉类</span><div class="fc-detail-val"><input value="8200" placeholder="金额"></div><span class="fc-del-row"><i data-lucide="x" style="width:14px;height:14px;"></i></span></div>
<div class="fc-detail-row"><span class="fc-detail-name">蔬菜</span><div class="fc-detail-val"><input value="4800" placeholder="金额"></div><span class="fc-del-row"><i data-lucide="x" style="width:14px;height:14px;"></i></span></div>
<div class="fc-detail-row"><span class="fc-detail-name">调料</span><div class="fc-detail-val"><input value="3000" placeholder="金额"></div><span class="fc-del-row"><i data-lucide="x" style="width:14px;height:14px;"></i></span></div>
<div class="fc-detail-row"><span class="fc-detail-name">饮品原料</span><div class="fc-detail-val"><input value="2500" placeholder="金额"></div><span class="fc-del-row"><i data-lucide="x" style="width:14px;height:14px;"></i></span></div>
<div class="fc-add-row" onclick="void(0)"><i data-lucide="plus" style="width:14px;height:14px;"></i>添加品类</div>
</div>
</div>
<!-- 人工成本 -->
<div class="g-card fc-cost-card">
<div class="fc-cost-hd" onclick="fcToggleDetail('labor')">
<div class="fc-cost-icon labor"><i data-lucide="users" style="width:20px;height:20px;"></i></div>
<div>
<div class="fc-cost-name">人工成本 <span class="fc-cost-pct">28.4%</span></div>
</div>
<div class="fc-cost-total">
<span class="fc-yen">¥</span>
<input class="fc-cost-input" type="number" value="12000" placeholder="请输入金额">
</div>
<div class="fc-expand-btn"><i data-lucide="chevron-down" style="width:14px;height:14px;" id="fcArrowLabor"></i>明细</div>
</div>
<div class="fc-detail" id="fcDetailLabor">
<div class="fc-detail-row"><span class="fc-detail-name">厨师</span><div class="fc-detail-val"><input value="2" placeholder="人数" style="width:50px;"></div><div class="fc-detail-val"><input value="4000" placeholder="月薪"></div><div class="fc-detail-sub">¥8,000</div></div>
<div class="fc-detail-row"><span class="fc-detail-name">服务员</span><div class="fc-detail-val"><input value="1" placeholder="人数" style="width:50px;"></div><div class="fc-detail-val"><input value="2000" placeholder="月薪"></div><div class="fc-detail-sub">¥2,000</div></div>
<div class="fc-detail-row"><span class="fc-detail-name">配送员</span><div class="fc-detail-val"><input value="1" placeholder="人数" style="width:50px;"></div><div class="fc-detail-val"><input value="1000" placeholder="月薪"></div><div class="fc-detail-sub">¥1,000</div></div>
<div class="fc-detail-row"><span class="fc-detail-name">管理</span><div class="fc-detail-val"><input value="1" placeholder="人数" style="width:50px;"></div><div class="fc-detail-val"><input value="1000" placeholder="月薪"></div><div class="fc-detail-sub">¥1,000</div></div>
<div class="fc-add-row" onclick="void(0)"><i data-lucide="plus" style="width:14px;height:14px;"></i>添加岗位</div>
</div>
</div>
<!-- 固定费用 -->
<div class="g-card fc-cost-card">
<div class="fc-cost-hd" onclick="fcToggleDetail('fixed')">
<div class="fc-cost-icon fixed"><i data-lucide="building" style="width:20px;height:20px;"></i></div>
<div>
<div class="fc-cost-name">固定费用 <span class="fc-cost-pct">20.1%</span></div>
</div>
<div class="fc-cost-total">
<span class="fc-yen">¥</span>
<input class="fc-cost-input" type="number" value="8500" placeholder="请输入金额">
</div>
<div class="fc-expand-btn"><i data-lucide="chevron-down" style="width:14px;height:14px;" id="fcArrowFixed"></i>明细</div>
</div>
<div class="fc-detail" id="fcDetailFixed">
<div class="fc-detail-row"><span class="fc-detail-name">房租</span><div class="fc-detail-val"><input value="5000" placeholder="金额"></div><span class="fc-del-row"><i data-lucide="x" style="width:14px;height:14px;"></i></span></div>
<div class="fc-detail-row"><span class="fc-detail-name">水电</span><div class="fc-detail-val"><input value="1500" placeholder="金额"></div><span class="fc-del-row"><i data-lucide="x" style="width:14px;height:14px;"></i></span></div>
<div class="fc-detail-row"><span class="fc-detail-name">设备折旧</span><div class="fc-detail-val"><input value="2000" placeholder="金额"></div><span class="fc-del-row"><i data-lucide="x" style="width:14px;height:14px;"></i></span></div>
<div class="fc-add-row" onclick="void(0)"><i data-lucide="plus" style="width:14px;height:14px;"></i>添加项目</div>
</div>
</div>
<!-- 包装耗材 -->
<div class="g-card fc-cost-card">
<div class="fc-cost-hd" onclick="fcToggleDetail('pack')">
<div class="fc-cost-icon pack"><i data-lucide="package" style="width:20px;height:20px;"></i></div>
<div>
<div class="fc-cost-name">包装耗材 <span class="fc-cost-pct">7.6%</span></div>
</div>
<div class="fc-cost-total">
<span class="fc-yen">¥</span>
<input class="fc-cost-input" type="number" value="3200" placeholder="请输入金额">
</div>
<div class="fc-expand-btn"><i data-lucide="chevron-down" style="width:14px;height:14px;" id="fcArrowPack"></i>明细</div>
</div>
<div class="fc-detail" id="fcDetailPack">
<div class="fc-detail-row"><span class="fc-detail-name">外卖盒</span><div class="fc-detail-val"><input value="1800" placeholder="金额"></div><span class="fc-del-row"><i data-lucide="x" style="width:14px;height:14px;"></i></span></div>
<div class="fc-detail-row"><span class="fc-detail-name">袋子</span><div class="fc-detail-val"><input value="600" placeholder="金额"></div><span class="fc-del-row"><i data-lucide="x" style="width:14px;height:14px;"></i></span></div>
<div class="fc-detail-row"><span class="fc-detail-name">餐具</span><div class="fc-detail-val"><input value="500" placeholder="金额"></div><span class="fc-del-row"><i data-lucide="x" style="width:14px;height:14px;"></i></span></div>
<div class="fc-detail-row"><span class="fc-detail-name">其他</span><div class="fc-detail-val"><input value="300" placeholder="金额"></div><span class="fc-del-row"><i data-lucide="x" style="width:14px;height:14px;"></i></span></div>
<div class="fc-add-row" onclick="void(0)"><i data-lucide="plus" style="width:14px;height:14px;"></i>添加项目</div>
</div>
</div>
<!-- 汇总 -->
<div class="fc-summary">
<div class="fc-summary-label">本月总成本</div>
<div style="display:flex;align-items:center;gap:16px;">
<div class="fc-summary-val">¥42,200</div>
<button class="g-btn g-btn-primary">保存</button>
</div>
</div>
</div>
<!-- Tab2: 成本分析 -->
<div id="fc-tab-analysis" style="display:none;">
<!-- 统计卡片 -->
<div class="fc-stat-row">
<div class="fc-stat-card">
<div class="fc-stat-label">本月总成本</div>
<div class="fc-stat-val">¥42,200</div>
</div>
<div class="fc-stat-card">
<div class="fc-stat-label">食材成本率</div>
<div class="fc-stat-val">38.5%</div>
</div>
<div class="fc-stat-card">
<div class="fc-stat-label">单均成本</div>
<div class="fc-stat-val">¥12.4</div>
</div>
<div class="fc-stat-card">
<div class="fc-stat-label">环比变化</div>
<div class="fc-stat-val"><span class="fc-up">&#8593; 3.2%</span></div>
</div>
</div>
<!-- 月度趋势 -->
<div class="g-card" style="padding:20px; margin-bottom:20px;">
<div class="fc-section-hd">近6个月成本趋势</div>
<div class="fc-chart-placeholder">
<div class="fc-chart-bars">
<div style="text-align:center;"><div class="fc-chart-bar" style="height:65px;"></div><div style="font-size:11px;color:var(--g-text-muted);margin-top:6px;">9月</div></div>
<div style="text-align:center;"><div class="fc-chart-bar" style="height:72px;"></div><div style="font-size:11px;color:var(--g-text-muted);margin-top:6px;">10月</div></div>
<div style="text-align:center;"><div class="fc-chart-bar" style="height:80px;"></div><div style="font-size:11px;color:var(--g-text-muted);margin-top:6px;">11月</div></div>
<div style="text-align:center;"><div class="fc-chart-bar" style="height:68px;"></div><div style="font-size:11px;color:var(--g-text-muted);margin-top:6px;">12月</div></div>
<div style="text-align:center;"><div class="fc-chart-bar" style="height:75px;"></div><div style="font-size:11px;color:var(--g-text-muted);margin-top:6px;">1月</div></div>
<div style="text-align:center;"><div class="fc-chart-bar" style="height:88px;"></div><div style="font-size:11px;color:var(--g-text-muted);margin-top:6px;">2月</div></div>
</div>
<div style="font-size:12px;color:var(--g-text-muted);">成本趋势图表区域</div>
</div>
</div>
<!-- 成本构成饼图 -->
<div class="g-card" style="padding:20px; margin-bottom:20px;">
<div class="fc-section-hd">成本构成</div>
<div class="fc-pie-section">
<div class="fc-donut">
<div class="fc-donut-center">
<div class="num">¥42.2k</div>
<div class="txt">总成本</div>
</div>
</div>
<div class="fc-legend">
<div class="fc-legend-item"><span class="fc-legend-dot" style="background:#2563eb;"></span>食材原料<span class="fc-legend-pct">44%</span></div>
<div class="fc-legend-item"><span class="fc-legend-dot" style="background:#16a34a;"></span>人工成本<span class="fc-legend-pct">28%</span></div>
<div class="fc-legend-item"><span class="fc-legend-dot" style="background:#ca8a04;"></span>固定费用<span class="fc-legend-pct">20%</span></div>
<div class="fc-legend-item"><span class="fc-legend-dot" style="background:#db2777;"></span>包装耗材<span class="fc-legend-pct">8%</span></div>
</div>
</div>
</div>
<!-- 成本明细表格 -->
<div class="g-card" style="padding:20px;">
<div class="fc-section-hd">成本明细</div>
<table class="fc-table">
<thead>
<tr><th>月份</th><th>食材</th><th>人工</th><th>固定</th><th>包装</th><th>总计</th><th>成本率</th></tr>
</thead>
<tbody>
<tr><td>2026-02</td><td>¥18,500</td><td>¥12,000</td><td>¥8,500</td><td>¥3,200</td><td>¥42,200</td><td>38.5%</td></tr>
<tr><td>2026-01</td><td>¥17,800</td><td>¥12,000</td><td>¥8,500</td><td>¥3,100</td><td>¥41,400</td><td>37.8%</td></tr>
<tr><td>2025-12</td><td>¥19,200</td><td>¥11,500</td><td>¥8,500</td><td>¥3,400</td><td>¥42,600</td><td>39.2%</td></tr>
<tr><td>2025-11</td><td>¥16,500</td><td>¥11,500</td><td>¥8,500</td><td>¥2,900</td><td>¥39,400</td><td>36.5%</td></tr>
<tr><td>2025-10</td><td>¥15,800</td><td>¥11,000</td><td>¥8,500</td><td>¥2,800</td><td>¥38,100</td><td>35.9%</td></tr>
<tr><td>2025-09</td><td>¥14,600</td><td>¥11,000</td><td>¥8,500</td><td>¥2,600</td><td>¥36,700</td><td>34.8%</td></tr>
</tbody>
</table>
</div>
</div>
</div>
<script>
// Tab切换
function fcSwitchTab(el, tabId) {
document.querySelectorAll('.fc-seg-item').forEach(b => b.classList.remove('active'));
el.classList.add('active');
document.getElementById('fc-tab-entry').style.display = tabId === 'fc-tab-entry' ? '' : 'none';
document.getElementById('fc-tab-analysis').style.display = tabId === 'fc-tab-analysis' ? '' : 'none';
}
// 月份切换
var fcMonth = 2, fcYear = 2026;
function fcChangeMonth(dir) {
fcMonth += dir;
if (fcMonth > 12) { fcMonth = 1; fcYear++; }
if (fcMonth < 1) { fcMonth = 12; fcYear--; }
document.getElementById('fcMonthTitle').textContent = fcYear + '年' + fcMonth + '月';
}
// 展开/收起明细
function fcToggleDetail(type) {
var map = { food: 'fcDetailFood', labor: 'fcDetailLabor', fixed: 'fcDetailFixed', pack: 'fcDetailPack' };
var el = document.getElementById(map[type]);
if (el) el.classList.toggle('open');
}
// 初始化Lucide图标
if (typeof lucide !== 'undefined') lucide.createIcons();
</script>

268
pages/fin-invoice.html Normal file
View File

@@ -0,0 +1,268 @@
<!-- 发票管理页 -->
<style>
.fi-seg-wrap{margin-bottom:16px;}
.fi-stats{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;margin-bottom:16px;}
.fi-stat-card{background:#fff;border-radius:var(--g-radius);padding:18px 20px;box-shadow:var(--g-shadow-sm);transition:var(--g-transition);}
.fi-stat-card:hover{box-shadow:var(--g-shadow-md);}
.fi-stat-label{font-size:13px;color:var(--g-text-secondary);margin-bottom:8px;display:flex;align-items:center;gap:6px;}
.fi-stat-label i{width:16px;height:16px;}
.fi-stat-val{font-size:24px;font-weight:700;color:var(--g-text);}
.fi-stat-sub{font-size:12px;color:var(--g-text-muted);margin-top:4px;}
.fi-stat-card.warn .fi-stat-val{color:var(--g-warning);}
.fi-stat-card.danger .fi-stat-val{color:var(--g-danger);}
.fi-toolbar{display:flex;align-items:center;gap:10px;margin-bottom:16px;background:#fff;padding:12px 16px;border-radius:var(--g-radius);box-shadow:var(--g-shadow-sm);flex-wrap:wrap;}
.fi-toolbar input[type=date],.fi-toolbar select{height:34px;padding:0 10px;border:1px solid #e5e7eb;border-radius:6px;font-size:13px;outline:none;background:#fff;transition:var(--g-transition);}
.fi-toolbar input[type=date]:focus,.fi-toolbar select:focus{border-color:var(--primary);box-shadow:0 0 0 3px color-mix(in srgb,var(--primary) 12%,transparent);}
.fi-toolbar .fi-search{height:34px;padding:0 10px 0 32px;border:1px solid #e5e7eb;border-radius:6px;font-size:13px;outline:none;width:200px;background:#fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%239ca3af' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'/%3E%3Cpath d='m21 21-4.3-4.3'/%3E%3C/svg%3E") 10px center no-repeat;transition:var(--g-transition);}
.fi-toolbar .fi-search:focus{border-color:var(--primary);box-shadow:0 0 0 3px color-mix(in srgb,var(--primary) 12%,transparent);}
.fi-date-sep{color:var(--g-text-muted);font-size:13px;}
.fi-mono{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:13px;}
.fi-tag-purple{background:#f9f0ff;color:#722ed1;}
.fi-section-hd{font-size:15px;font-weight:600;color:var(--g-text);padding-left:10px;border-left:3px solid var(--primary);margin-bottom:16px;}
.fi-form-grid{display:grid;grid-template-columns:1fr 1fr;gap:14px 24px;}
.fi-toggle-row{display:flex;align-items:center;gap:10px;margin-bottom:14px;}
.fi-toggle-label{font-size:13px;font-weight:500;color:var(--g-text);}
.fi-toggle-hint{font-size:12px;color:var(--g-text-muted);margin-left:4px;}
.fi-save-bar{margin-top:20px;display:flex;justify-content:flex-end;gap:8px;}
@media(max-width:768px){.fi-stats{grid-template-columns:1fr;}.fi-form-grid{grid-template-columns:1fr;}}
</style>
<!-- 分段控制器 -->
<div class="fi-seg-wrap">
<div class="g-seg">
<div class="g-seg-item active" onclick="switchFiTab(this)" data-tab="records">开票记录</div>
<div class="g-seg-item" onclick="switchFiTab(this)" data-tab="settings">发票设置</div>
</div>
</div>
<!-- Tab1: 开票记录 -->
<div id="fiTabRecords">
<!-- 统计卡片 -->
<div class="fi-stats">
<div class="fi-stat-card">
<div class="fi-stat-label"><i data-lucide="receipt-text"></i>本月开票</div>
<div class="fi-stat-val">&yen;12,400</div>
<div class="fi-stat-sub">共 32 张</div>
</div>
<div class="fi-stat-card warn">
<div class="fi-stat-label"><i data-lucide="clock"></i>待开票</div>
<div class="fi-stat-val">5</div>
<div class="fi-stat-sub">张待处理</div>
</div>
<div class="fi-stat-card danger">
<div class="fi-stat-label"><i data-lucide="ban"></i>已作废</div>
<div class="fi-stat-val">2</div>
<div class="fi-stat-sub">张已作废</div>
</div>
</div>
<!-- 筛选工具栏 -->
<div class="fi-toolbar">
<input type="date" value="2026-01-01" />
<span class="fi-date-sep"></span>
<input type="date" value="2026-01-31" />
<select>
<option>全部状态</option>
<option>待开票</option>
<option>已开票</option>
<option>已作废</option>
</select>
<select>
<option>全部类型</option>
<option>普通发票</option>
<option>专用发票</option>
</select>
<div style="flex:1;"></div>
<input class="fi-search" type="text" placeholder="搜索发票号码/公司名" />
</div>
<!-- 发票记录表格 -->
<div class="g-card" style="padding:0;">
<table class="g-table">
<thead>
<tr>
<th>发票号码</th>
<th>申请人 / 公司名</th>
<th>发票类型</th>
<th>金额</th>
<th>关联订单</th>
<th>状态</th>
<th>申请时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr>
<td><span class="fi-mono">INV20260115001</span></td>
<td>北京鼎盛餐饮有限公司</td>
<td><span class="g-tag g-tag-blue">普票</span></td>
<td>&yen;860.00</td>
<td><span class="fi-mono">ORD20260115032</span></td>
<td><span class="g-tag g-tag-orange">待开票</span></td>
<td>2026-01-15 14:30</td>
<td><a class="g-action" href="javascript:;">开票</a></td>
</tr>
<tr>
<td><span class="fi-mono">INV20260114008</span></td>
<td>上海云帆科技有限公司</td>
<td><span class="g-tag fi-tag-purple">专票</span></td>
<td>&yen;2,400.00</td>
<td><span class="fi-mono">ORD20260114021</span></td>
<td><span class="g-tag g-tag-green">已开票</span></td>
<td>2026-01-14 10:15</td>
<td><a class="g-action" href="javascript:;">查看</a><a class="g-action g-action-danger" href="javascript:;">作废</a></td>
</tr>
<tr>
<td><span class="fi-mono">INV20260113005</span></td>
<td>张伟</td>
<td><span class="g-tag g-tag-blue">普票</span></td>
<td>&yen;156.00</td>
<td><span class="fi-mono">ORD20260113044</span></td>
<td><span class="g-tag g-tag-green">已开票</span></td>
<td>2026-01-13 18:42</td>
<td><a class="g-action" href="javascript:;">查看</a><a class="g-action g-action-danger" href="javascript:;">作废</a></td>
</tr>
<tr>
<td><span class="fi-mono">INV20260112003</span></td>
<td>深圳市万达商贸有限公司</td>
<td><span class="g-tag fi-tag-purple">专票</span></td>
<td>&yen;5,200.00</td>
<td><span class="fi-mono">ORD20260112018</span></td>
<td><span class="g-tag g-tag-orange">待开票</span></td>
<td>2026-01-12 09:20</td>
<td><a class="g-action" href="javascript:;">开票</a></td>
</tr>
<tr>
<td><span class="fi-mono">INV20260111007</span></td>
<td>李明</td>
<td><span class="g-tag g-tag-blue">普票</span></td>
<td>&yen;88.50</td>
<td><span class="fi-mono">ORD20260111055</span></td>
<td><span class="g-tag g-tag-gray">已作废</span></td>
<td>2026-01-11 20:10</td>
<td><a class="g-action" href="javascript:;">查看</a></td>
</tr>
<tr>
<td><span class="fi-mono">INV20260110002</span></td>
<td>广州天河餐饮管理有限公司</td>
<td><span class="g-tag g-tag-blue">普票</span></td>
<td>&yen;1,320.00</td>
<td><span class="fi-mono">ORD20260110009</span></td>
<td><span class="g-tag g-tag-green">已开票</span></td>
<td>2026-01-10 11:35</td>
<td><a class="g-action" href="javascript:;">查看</a><a class="g-action g-action-danger" href="javascript:;">作废</a></td>
</tr>
<tr>
<td><span class="fi-mono">INV20260109006</span></td>
<td>王芳</td>
<td><span class="g-tag g-tag-blue">普票</span></td>
<td>&yen;45.00</td>
<td><span class="fi-mono">ORD20260109071</span></td>
<td><span class="g-tag g-tag-gray">已作废</span></td>
<td>2026-01-09 16:50</td>
<td><a class="g-action" href="javascript:;">查看</a></td>
</tr>
</tbody>
</table>
</div>
<!-- 分页 -->
<div class="g-pagination" style="margin-top:16px;">
<button class="g-page-btn" disabled>&laquo;</button>
<button class="g-page-btn active">1</button>
<button class="g-page-btn">2</button>
<button class="g-page-btn">3</button>
<button class="g-page-btn">&raquo;</button>
</div>
</div>
<!-- Tab2: 发票设置 -->
<div id="fiTabSettings" style="display:none;">
<!-- 企业信息 -->
<div class="g-card" style="margin-bottom:16px;">
<div class="fi-section-hd">企业开票信息</div>
<div class="fi-form-grid">
<div class="g-form-group">
<label class="g-form-label required">企业名称</label>
<input class="g-input" value="北京老三家餐饮管理有限公司" placeholder="请输入企业全称" />
</div>
<div class="g-form-group">
<label class="g-form-label required">纳税人识别号</label>
<input class="g-input" value="91110105MA01ABCD5X" placeholder="请输入统一社会信用代码" />
</div>
<div class="g-form-group">
<label class="g-form-label">注册地址</label>
<input class="g-input" value="北京市朝阳区建国路88号" placeholder="请输入注册地址" />
</div>
<div class="g-form-group">
<label class="g-form-label">注册电话</label>
<input class="g-input" value="010-88886666" placeholder="请输入注册电话" />
</div>
<div class="g-form-group">
<label class="g-form-label">开户银行</label>
<input class="g-input" value="中国工商银行北京朝阳支行" placeholder="请输入开户银行名称" />
</div>
<div class="g-form-group">
<label class="g-form-label">银行账号</label>
<input class="g-input" value="0200 0461 1920 1234 567" placeholder="请输入银行账号" />
</div>
</div>
</div>
<!-- 发票类型设置 -->
<div class="g-card" style="margin-bottom:16px;">
<div class="fi-section-hd">发票类型设置</div>
<div class="fi-toggle-row">
<button class="g-toggle on" onclick="toggleSwitch(this)"></button>
<span class="fi-toggle-label">电子普通发票</span>
</div>
<div class="fi-toggle-row">
<button class="g-toggle" onclick="toggleSwitch(this)"></button>
<span class="fi-toggle-label">电子专用发票</span>
<span class="fi-toggle-hint">(需先完成税务资质认证)</span>
</div>
</div>
<!-- 自动开票设置 -->
<div class="g-card" style="margin-bottom:16px;">
<div class="fi-section-hd">自动开票设置</div>
<div class="fi-toggle-row">
<button class="g-toggle" onclick="toggleSwitch(this)"></button>
<span class="fi-toggle-label">自动开票</span>
<span class="fi-toggle-hint">开启后,顾客申请发票将自动开具</span>
</div>
<div class="g-divider" style="margin:14px 0;"></div>
<div class="g-form-group" style="max-width:320px;">
<label class="g-form-label">单张发票最大金额</label>
<div style="display:flex;align-items:center;gap:6px;">
<span style="font-size:13px;color:var(--g-text-secondary);">&yen;</span>
<input class="g-input" type="number" value="10000" placeholder="如10000" />
</div>
<span class="g-hint">超过此金额的发票需人工审核后开具</span>
</div>
</div>
<!-- 保存按钮 -->
<div class="fi-save-bar">
<button class="g-btn">重置</button>
<button class="g-btn g-btn-primary">保存设置</button>
</div>
</div>
<script>
function switchFiTab(el) {
var tab = el.getAttribute('data-tab');
el.parentElement.querySelectorAll('.g-seg-item').forEach(function(item) {
item.classList.remove('active');
});
el.classList.add('active');
document.getElementById('fiTabRecords').style.display = tab === 'records' ? '' : 'none';
document.getElementById('fiTabSettings').style.display = tab === 'settings' ? '' : 'none';
}
function toggleSwitch(el) {
el.classList.toggle('on');
}
if (typeof lucide !== 'undefined') { lucide.createIcons(); }
</script>

330
pages/fin-overview.html Normal file
View File

@@ -0,0 +1,330 @@
<!-- 财务概览驾驶舱 -->
<style>
.fo-page { display:flex; flex-direction:column; gap:16px; }
/* KPI 指标卡片行 */
.fo-kpi-row { display:grid; grid-template-columns:repeat(5,1fr); gap:14px; }
.fo-kpi { background:#fff; border-radius:var(--g-radius); border:1px solid var(--g-border); padding:18px 20px; display:flex; flex-direction:column; gap:10px; transition:var(--g-transition); }
.fo-kpi:hover { box-shadow:var(--g-shadow-md); transform:translateY(-1px); }
.fo-kpi-top { display:flex; align-items:center; justify-content:space-between; }
.fo-kpi-label { font-size:13px; color:var(--g-text-secondary); }
.fo-kpi-icon { width:36px; height:36px; border-radius:var(--g-radius); display:flex; align-items:center; justify-content:center; flex-shrink:0; }
.fo-kpi-icon.blue { background:hsl(212,100%,95%); color:hsl(212,100%,45%); }
.fo-kpi-icon.green { background:#f0fdf4; color:var(--g-success); }
.fo-kpi-icon.red { background:#fef2f2; color:var(--g-danger); }
.fo-kpi-icon.purple { background:#f5f3ff; color:#7c3aed; }
.fo-kpi-icon.orange { background:#fffbeb; color:var(--g-warning); }
.fo-kpi-value { font-size:26px; font-weight:800; color:var(--g-text); letter-spacing:-0.5px; }
.fo-kpi-change { font-size:12px; display:flex; align-items:center; gap:4px; }
.fo-kpi-change.up { color:var(--g-success); }
.fo-kpi-change.down { color:var(--g-danger); }
/* 区块标题 */
.fo-section-hd { font-size:15px; font-weight:600; color:var(--g-text); padding-left:10px; border-left:3px solid var(--primary); }
.fo-hd-row { display:flex; align-items:center; justify-content:space-between; margin-bottom:16px; }
/* Pill 切换 */
.fo-pills { display:flex; gap:6px; }
.fo-pill { padding:4px 14px; border-radius:20px; font-size:12px; color:var(--g-text-secondary); background:#f5f5f5; cursor:pointer; border:none; transition:var(--g-transition); }
.fo-pill.active { background:var(--primary); color:#fff; }
/* 折线图占位 */
.fo-chart-area { position:relative; height:200px; background:linear-gradient(180deg,hsl(212,100%,97%) 0%,#fff 100%); border-radius:var(--g-radius); border:1px dashed var(--g-border); overflow:hidden; }
.fo-chart-line { position:absolute; bottom:0; left:0; right:0; height:100%; }
.fo-chart-svg { width:100%; height:100%; }
.fo-chart-label { position:absolute; top:50%; left:50%; transform:translate(-50%,-50%); font-size:13px; color:var(--g-text-muted); pointer-events:none; }
.fo-chart-x { display:flex; justify-content:space-between; padding:8px 12px 0; font-size:11px; color:var(--g-text-muted); }
/* 两列布局 */
.fo-two-col { display:grid; grid-template-columns:1fr 1fr; gap:16px; }
/* 圆环图 */
.fo-donut-wrap { display:flex; align-items:center; gap:28px; padding:8px 0; }
.fo-donut { width:150px; height:150px; border-radius:50%; position:relative; flex-shrink:0; }
.fo-donut-center { position:absolute; inset:32px; background:#fff; border-radius:50%; display:flex; flex-direction:column; align-items:center; justify-content:center; }
.fo-donut-center .num { font-size:18px; font-weight:700; color:var(--g-text); }
.fo-donut-center .txt { font-size:11px; color:var(--g-text-muted); }
.fo-legend { display:flex; flex-direction:column; gap:10px; flex:1; }
.fo-legend-item { display:flex; align-items:center; gap:8px; font-size:13px; color:var(--g-text); }
.fo-legend-dot { width:8px; height:8px; border-radius:2px; flex-shrink:0; }
.fo-legend-val { margin-left:auto; font-weight:600; font-size:13px; }
.fo-legend-pct { color:var(--g-text-muted); font-size:12px; width:36px; text-align:right; }
/* 利润走势 */
.fo-profit-area { position:relative; height:180px; background:linear-gradient(180deg,hsl(212,100%,97%) 0%,#fff 100%); border-radius:var(--g-radius); border:1px dashed var(--g-border); overflow:hidden; }
.fo-profit-legend { display:flex; gap:20px; margin-top:10px; }
.fo-profit-legend-item { display:flex; align-items:center; gap:6px; font-size:12px; color:var(--g-text-secondary); }
.fo-profit-legend-line { width:20px; height:3px; border-radius:2px; }
/* TOP10 表格 */
.fo-rank-table { width:100%; border-collapse:collapse; }
.fo-rank-table th { text-align:left; font-size:12px; color:var(--g-text-muted); font-weight:500; padding:10px 12px; border-bottom:1px solid var(--g-border); background:#fafafa; }
.fo-rank-table th:first-child { border-radius:var(--g-radius) 0 0 0; }
.fo-rank-table th:last-child { border-radius:0 var(--g-radius) 0 0; }
.fo-rank-table td { padding:10px 12px; font-size:13px; color:var(--g-text); border-bottom:1px solid #f5f5f5; }
.fo-rank-table tr:hover td { background:color-mix(in srgb, var(--primary) 3%, #fff); }
.fo-rank-num { width:22px; height:22px; border-radius:4px; display:inline-flex; align-items:center; justify-content:center; font-size:11px; font-weight:700; color:#fff; }
.fo-rank-num.t1 { background:linear-gradient(135deg,#ef4444,#f97316); }
.fo-rank-num.t2 { background:linear-gradient(135deg,#f59e0b,#fbbf24); }
.fo-rank-num.t3 { background:linear-gradient(135deg,#1890ff,#69c0ff); }
.fo-rank-num.tn { background:#e5e7eb; color:#4b5563; }
.fo-progress-bg { width:100%; max-width:120px; height:6px; background:#e5e7eb; border-radius:3px; overflow:hidden; }
.fo-progress-fill { height:100%; border-radius:3px; background:linear-gradient(90deg,hsl(212,100%,45%),hsl(212,100%,65%)); }
@keyframes foFadeIn { from { opacity:0; transform:translateY(8px); } to { opacity:1; transform:translateY(0); } }
.fo-page > * { animation:foFadeIn 0.4s ease both; }
.fo-page > *:nth-child(2) { animation-delay:0.05s; }
.fo-page > *:nth-child(3) { animation-delay:0.1s; }
.fo-page > *:nth-child(4) { animation-delay:0.15s; }
.fo-page > *:nth-child(5) { animation-delay:0.2s; }
</style>
<div class="fo-page">
<!-- 1. 核心指标卡片 -->
<div class="fo-kpi-row">
<div class="fo-kpi">
<div class="fo-kpi-top">
<span class="fo-kpi-label">今日营业额</span>
<div class="fo-kpi-icon blue"><i data-lucide="coins" style="width:18px;height:18px"></i></div>
</div>
<div class="fo-kpi-value">&yen;8,620</div>
<div class="fo-kpi-change up"><i data-lucide="trending-up" style="width:14px;height:14px"></i> 12% 较昨日</div>
</div>
<div class="fo-kpi">
<div class="fo-kpi-top">
<span class="fo-kpi-label">实收</span>
<div class="fo-kpi-icon green"><i data-lucide="badge-check" style="width:18px;height:18px"></i></div>
</div>
<div class="fo-kpi-value">&yen;7,980</div>
<div class="fo-kpi-change up"><i data-lucide="trending-up" style="width:14px;height:14px"></i> 8% 较昨日</div>
</div>
<div class="fo-kpi">
<div class="fo-kpi-top">
<span class="fo-kpi-label">退款</span>
<div class="fo-kpi-icon red"><i data-lucide="undo-2" style="width:18px;height:18px"></i></div>
</div>
<div class="fo-kpi-value">&yen;640</div>
<div class="fo-kpi-change down"><i data-lucide="trending-down" style="width:14px;height:14px"></i> 3% 较昨日</div>
</div>
<div class="fo-kpi">
<div class="fo-kpi-top">
<span class="fo-kpi-label">净收入</span>
<div class="fo-kpi-icon purple"><i data-lucide="wallet" style="width:18px;height:18px"></i></div>
</div>
<div class="fo-kpi-value">&yen;6,850</div>
<div class="fo-kpi-change up"><i data-lucide="trending-up" style="width:14px;height:14px"></i> 5% 较昨日</div>
</div>
<div class="fo-kpi">
<div class="fo-kpi-top">
<span class="fo-kpi-label">可提现余额</span>
<div class="fo-kpi-icon orange"><i data-lucide="landmark" style="width:18px;height:18px"></i></div>
</div>
<div class="fo-kpi-value">&yen;32,400</div>
<div class="fo-kpi-change up"><i data-lucide="trending-up" style="width:14px;height:14px"></i> 15% 较上周</div>
</div>
</div>
<!-- 2. 收入趋势 -->
<div class="g-card" style="padding:20px">
<div class="fo-hd-row">
<div class="fo-section-hd">收入趋势</div>
<div class="fo-pills">
<button class="fo-pill active" onclick="foSwitchPill(this)">近7天</button>
<button class="fo-pill" onclick="foSwitchPill(this)">近30天</button>
</div>
</div>
<div class="fo-chart-area">
<svg class="fo-chart-svg" viewBox="0 0 700 200" preserveAspectRatio="none">
<defs>
<linearGradient id="foGrad" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="hsl(212,100%,45%)" stop-opacity="0.2"/>
<stop offset="100%" stop-color="hsl(212,100%,45%)" stop-opacity="0.02"/>
</linearGradient>
</defs>
<polyline fill="url(#foGrad)" stroke="none" points="0,200 0,140 100,100 200,120 300,60 400,80 500,40 600,70 700,30 700,200"/>
<polyline fill="none" stroke="hsl(212,100%,45%)" stroke-width="2.5" stroke-linejoin="round" stroke-linecap="round" points="0,140 100,100 200,120 300,60 400,80 500,40 600,70 700,30"/>
<circle cx="0" cy="140" r="4" fill="#fff" stroke="hsl(212,100%,45%)" stroke-width="2"/>
<circle cx="100" cy="100" r="4" fill="#fff" stroke="hsl(212,100%,45%)" stroke-width="2"/>
<circle cx="200" cy="120" r="4" fill="#fff" stroke="hsl(212,100%,45%)" stroke-width="2"/>
<circle cx="300" cy="60" r="4" fill="#fff" stroke="hsl(212,100%,45%)" stroke-width="2"/>
<circle cx="400" cy="80" r="4" fill="#fff" stroke="hsl(212,100%,45%)" stroke-width="2"/>
<circle cx="500" cy="40" r="4" fill="#fff" stroke="hsl(212,100%,45%)" stroke-width="2"/>
<circle cx="600" cy="70" r="4" fill="#fff" stroke="hsl(212,100%,45%)" stroke-width="2"/>
<circle cx="700" cy="30" r="4" fill="#fff" stroke="hsl(212,100%,45%)" stroke-width="2"/>
</svg>
</div>
<div class="fo-chart-x">
<span>02/06</span><span>02/07</span><span>02/08</span><span>02/09</span><span>02/10</span><span>02/11</span><span>02/12</span>
</div>
</div>
<!-- 3. 收入构成 + 成本占比 -->
<div class="fo-two-col">
<!-- 收入构成 -->
<div class="g-card" style="padding:20px">
<div class="fo-section-hd" style="margin-bottom:16px">收入构成</div>
<div class="fo-donut-wrap">
<div class="fo-donut" style="background:conic-gradient(hsl(212,100%,45%) 0% 58%,#22c55e 58% 83%,#f59e0b 83% 100%)">
<div class="fo-donut-center">
<span class="num">&yen;7,980</span>
<span class="txt">总实收</span>
</div>
</div>
<div class="fo-legend">
<div class="fo-legend-item">
<span class="fo-legend-dot" style="background:hsl(212,100%,45%)"></span>
外卖
<span class="fo-legend-pct">58%</span>
<span class="fo-legend-val">&yen;4,628</span>
</div>
<div class="fo-legend-item">
<span class="fo-legend-dot" style="background:#22c55e"></span>
自提
<span class="fo-legend-pct">25%</span>
<span class="fo-legend-val">&yen;1,995</span>
</div>
<div class="fo-legend-item">
<span class="fo-legend-dot" style="background:#f59e0b"></span>
堂食
<span class="fo-legend-pct">17%</span>
<span class="fo-legend-val">&yen;1,357</span>
</div>
</div>
</div>
</div>
<!-- 成本占比 -->
<div class="g-card" style="padding:20px">
<div class="fo-section-hd" style="margin-bottom:16px">成本占比</div>
<div class="fo-donut-wrap">
<div class="fo-donut" style="background:conic-gradient(#ef4444 0% 42%,#f59e0b 42% 70%,#8b5cf6 70% 88%,#06b6d4 88% 95%,#94a3b8 95% 100%)">
<div class="fo-donut-center">
<span class="num">&yen;1,130</span>
<span class="txt">总成本</span>
</div>
</div>
<div class="fo-legend">
<div class="fo-legend-item">
<span class="fo-legend-dot" style="background:#ef4444"></span>
食材
<span class="fo-legend-pct">42%</span>
<span class="fo-legend-val">&yen;475</span>
</div>
<div class="fo-legend-item">
<span class="fo-legend-dot" style="background:#f59e0b"></span>
人工
<span class="fo-legend-pct">28%</span>
<span class="fo-legend-val">&yen;316</span>
</div>
<div class="fo-legend-item">
<span class="fo-legend-dot" style="background:#8b5cf6"></span>
固定
<span class="fo-legend-pct">18%</span>
<span class="fo-legend-val">&yen;203</span>
</div>
<div class="fo-legend-item">
<span class="fo-legend-dot" style="background:#06b6d4"></span>
包装
<span class="fo-legend-pct">7%</span>
<span class="fo-legend-val">&yen;79</span>
</div>
<div class="fo-legend-item">
<span class="fo-legend-dot" style="background:#94a3b8"></span>
平台
<span class="fo-legend-pct">5%</span>
<span class="fo-legend-val">&yen;57</span>
</div>
</div>
</div>
</div>
</div>
<!-- 4. 利润走势 -->
<div class="g-card" style="padding:20px">
<div class="fo-hd-row">
<div class="fo-section-hd">利润走势</div>
<div class="fo-pills">
<button class="fo-pill active" onclick="foSwitchPill(this)">近7天</button>
<button class="fo-pill" onclick="foSwitchPill(this)">近30天</button>
</div>
</div>
<div class="fo-profit-area">
<svg class="fo-chart-svg" viewBox="0 0 700 180" preserveAspectRatio="none">
<!-- 营收线 -->
<polyline fill="none" stroke="hsl(212,100%,45%)" stroke-width="2.5" stroke-linejoin="round" stroke-linecap="round" points="0,120 100,90 200,100 300,50 400,65 500,35 600,55 700,25"/>
<!-- 成本线 -->
<polyline fill="none" stroke="#ef4444" stroke-width="2" stroke-dasharray="6,3" stroke-linejoin="round" stroke-linecap="round" points="0,150 100,140 200,145 300,125 400,130 500,120 600,128 700,115"/>
<!-- 净利润线 -->
<polyline fill="none" stroke="#22c55e" stroke-width="2.5" stroke-linejoin="round" stroke-linecap="round" points="0,160 100,145 200,150 300,120 400,128 500,108 600,118 700,95"/>
</svg>
</div>
<div class="fo-chart-x">
<span>02/06</span><span>02/07</span><span>02/08</span><span>02/09</span><span>02/10</span><span>02/11</span><span>02/12</span>
</div>
<div class="fo-profit-legend">
<div class="fo-profit-legend-item"><span class="fo-profit-legend-line" style="background:hsl(212,100%,45%)"></span>营收</div>
<div class="fo-profit-legend-item"><span class="fo-profit-legend-line" style="background:#ef4444"></span>成本</div>
<div class="fo-profit-legend-item"><span class="fo-profit-legend-line" style="background:#22c55e"></span>净利润</div>
</div>
</div>
<!-- 5. TOP10 商品营收排行 -->
<div class="g-card" style="padding:20px">
<div class="fo-section-hd" style="margin-bottom:16px">TOP10 商品营收排行</div>
<table class="fo-rank-table">
<thead>
<tr>
<th style="width:60px">排名</th>
<th>商品名称</th>
<th style="width:80px">销量</th>
<th style="width:100px">营收</th>
<th style="width:160px">占比</th>
</tr>
</thead>
<tbody id="foRankBody"></tbody>
</table>
</div>
</div>
<script>
(function(){
// TOP10 数据
var items = [
{ name:'招牌黄焖鸡米饭', sales:186, revenue:5580, pct:18.2 },
{ name:'香辣鸡腿堡套餐', sales:152, revenue:4560, pct:14.9 },
{ name:'麻辣小龙虾拌面', sales:134, revenue:4020, pct:13.1 },
{ name:'经典牛肉面', sales:128, revenue:3200, pct:10.4 },
{ name:'鱼香肉丝盖饭', sales:115, revenue:2875, pct:9.4 },
{ name:'宫保鸡丁套餐', sales:98, revenue:2450, pct:8.0 },
{ name:'番茄鸡蛋面', sales:92, revenue:1840, pct:6.0 },
{ name:'红烧排骨饭', sales:85, revenue:2550, pct:8.3 },
{ name:'皮蛋瘦肉粥', sales:76, revenue:1140, pct:3.7 },
{ name:'冰柠檬水', sales:210, revenue:1260, pct:4.1 }
];
var maxPct = items[0].pct;
var tbody = document.getElementById('foRankBody');
items.forEach(function(it, i){
var cls = i === 0 ? 't1' : i === 1 ? 't2' : i === 2 ? 't3' : 'tn';
var tr = document.createElement('tr');
tr.innerHTML =
'<td><span class="fo-rank-num ' + cls + '">' + (i+1) + '</span></td>' +
'<td>' + it.name + '</td>' +
'<td>' + it.sales + '</td>' +
'<td style="font-weight:600">&yen;' + it.revenue.toLocaleString() + '</td>' +
'<td><div style="display:flex;align-items:center;gap:8px"><div class="fo-progress-bg"><div class="fo-progress-fill" style="width:' + (it.pct / maxPct * 100) + '%"></div></div><span style="font-size:12px;color:var(--g-text-muted)">' + it.pct + '%</span></div></td>';
tbody.appendChild(tr);
});
})();
// Pill 切换
function foSwitchPill(el) {
var siblings = el.parentElement.querySelectorAll('.fo-pill');
siblings.forEach(function(s){ s.classList.remove('active'); });
el.classList.add('active');
}
// Lucide 图标初始化
if (typeof lucide !== 'undefined') { lucide.createIcons(); }
</script>

311
pages/fin-reports.html Normal file
View File

@@ -0,0 +1,311 @@
<!-- 经营报表 — fin-reports.html -->
<style>
/* ---- page-private: frp- prefix ---- */
.frp-toolbar {
background: #fff; border-radius: var(--g-radius); padding: 16px 20px;
display: flex; align-items: center; gap: 12px;
box-shadow: var(--g-shadow-sm); border: 1px solid var(--g-border);
}
.frp-toolbar-right { margin-left: auto; display: flex; gap: 8px; }
.frp-table-card {
background: #fff; border-radius: var(--g-radius); border: 1px solid var(--g-border);
margin-top: 12px; overflow: hidden;
}
.frp-table-card .g-table th { white-space: nowrap; }
.frp-table-card .g-table td { vertical-align: middle; white-space: nowrap; }
.frp-amount { font-weight: 600; }
/* 预览抽屉 */
.frp-drawer { width: 560px; }
/* 抽屉内区块 */
.frp-section { margin-bottom: 22px; }
.frp-section-hd {
font-size: 15px; font-weight: 600; color: var(--g-text);
padding-left: 10px; border-left: 3px solid var(--primary); margin-bottom: 14px;
}
/* 关键指标网格 */
.frp-kpi-grid {
display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 12px;
}
.frp-kpi-item {
background: #f8f9fb; border-radius: var(--g-radius-sm); padding: 14px 16px;
border: 1px solid var(--g-border);
}
.frp-kpi-label { font-size: 12px; color: var(--g-text-muted); margin-bottom: 6px; }
.frp-kpi-val { font-size: 20px; font-weight: 700; color: var(--g-text); }
.frp-kpi-change { font-size: 12px; margin-top: 4px; display: flex; gap: 8px; }
.frp-kpi-up { color: var(--g-success); }
.frp-kpi-down { color: var(--g-danger); }
/* 明细行 */
.frp-detail-row {
display: flex; justify-content: space-between; align-items: center;
padding: 10px 0; border-bottom: 1px solid #f5f5f5; font-size: 13px;
}
.frp-detail-row:last-child { border-bottom: none; }
.frp-detail-name { color: var(--g-text); }
.frp-detail-right { display: flex; gap: 16px; align-items: center; }
.frp-detail-amount { font-weight: 600; color: var(--g-text); min-width: 80px; text-align: right; }
.frp-detail-pct { color: var(--g-text-muted); font-size: 12px; min-width: 50px; text-align: right; }
.frp-divider { height: 1px; background: linear-gradient(90deg, var(--g-border), transparent); margin: 20px 0; }
</style>
<!-- 顶部工具栏 -->
<div class="frp-toolbar">
<!-- 分段控制器 -->
<div class="g-seg">
<div class="g-seg-item active" onclick="switchFrpTab(this,'daily')">日报</div>
<div class="g-seg-item" onclick="switchFrpTab(this,'weekly')">周报</div>
<div class="g-seg-item" onclick="switchFrpTab(this,'monthly')">月报</div>
</div>
<select class="g-select" style="width:200px;">
<option>全部门店</option>
<option>老三家外卖(朝阳店)</option>
<option>老三家外卖(海淀店)</option>
<option>老三家外卖(望京店)</option>
</select>
<div class="frp-toolbar-right">
<button class="g-btn g-btn-sm"><i data-lucide="download" style="width:14px;height:14px;"></i> 批量导出</button>
</div>
</div>
<!-- ========== 日报 Tab ========== -->
<div id="frpTabDaily" class="frp-tab-panel">
<div class="frp-table-card">
<table class="g-table">
<thead>
<tr>
<th>日期</th><th>营业额</th><th>订单数</th><th>客单价</th><th>退款率</th><th>成本总额</th><th>净利润</th><th>利润率</th><th>状态</th><th>操作</th>
</tr>
</thead>
<tbody>
<tr><td>2026-02-12</td><td class="frp-amount">&yen;8,620</td><td>186单</td><td>&yen;46.3</td><td>7.4%</td><td>&yen;5,280</td><td class="frp-amount" style="color:var(--g-success);">&yen;3,340</td><td>38.7%</td><td><span class="g-tag g-tag-green">已生成</span></td><td><a class="g-action" onclick="openFrpPreview('2026年2月12日 经营日报')">预览</a> <a class="g-action">下载PDF</a> <a class="g-action">下载Excel</a></td></tr>
<tr><td>2026-02-11</td><td class="frp-amount">&yen;9,150</td><td>198单</td><td>&yen;46.2</td><td>5.2%</td><td>&yen;5,610</td><td class="frp-amount" style="color:var(--g-success);">&yen;3,540</td><td>38.7%</td><td><span class="g-tag g-tag-green">已生成</span></td><td><a class="g-action" onclick="openFrpPreview('2026年2月11日 经营日报')">预览</a> <a class="g-action">下载PDF</a> <a class="g-action">下载Excel</a></td></tr>
<tr><td>2026-02-10</td><td class="frp-amount">&yen;7,830</td><td>172单</td><td>&yen;45.5</td><td>6.1%</td><td>&yen;4,920</td><td class="frp-amount" style="color:var(--g-success);">&yen;2,910</td><td>37.2%</td><td><span class="g-tag g-tag-green">已生成</span></td><td><a class="g-action" onclick="openFrpPreview('2026年2月10日 经营日报')">预览</a> <a class="g-action">下载PDF</a> <a class="g-action">下载Excel</a></td></tr>
<tr><td>2026-02-09</td><td class="frp-amount">&yen;10,240</td><td>221单</td><td>&yen;46.3</td><td>4.8%</td><td>&yen;6,150</td><td class="frp-amount" style="color:var(--g-success);">&yen;4,090</td><td>39.9%</td><td><span class="g-tag g-tag-green">已生成</span></td><td><a class="g-action" onclick="openFrpPreview('2026年2月9日 经营日报')">预览</a> <a class="g-action">下载PDF</a> <a class="g-action">下载Excel</a></td></tr>
<tr><td>2026-02-08</td><td class="frp-amount">&yen;11,380</td><td>245单</td><td>&yen;46.4</td><td>3.9%</td><td>&yen;6,830</td><td class="frp-amount" style="color:var(--g-success);">&yen;4,550</td><td>40.0%</td><td><span class="g-tag g-tag-green">已生成</span></td><td><a class="g-action" onclick="openFrpPreview('2026年2月8日 经营日报')">预览</a> <a class="g-action">下载PDF</a> <a class="g-action">下载Excel</a></td></tr>
<tr><td>2026-02-07</td><td class="frp-amount">&yen;10,560</td><td>228单</td><td>&yen;46.3</td><td>5.7%</td><td>&yen;6,440</td><td class="frp-amount" style="color:var(--g-success);">&yen;4,120</td><td>39.0%</td><td><span class="g-tag g-tag-green">已生成</span></td><td><a class="g-action" onclick="openFrpPreview('2026年2月7日 经营日报')">预览</a> <a class="g-action">下载PDF</a> <a class="g-action">下载Excel</a></td></tr>
<tr><td>2026-02-06</td><td class="frp-amount">&yen;8,970</td><td>194单</td><td>&yen;46.2</td><td>6.3%</td><td>&yen;5,490</td><td class="frp-amount" style="color:var(--g-success);">&yen;3,480</td><td>38.8%</td><td><span class="g-tag g-tag-blue">生成中</span></td><td><a class="g-action" onclick="openFrpPreview('2026年2月6日 经营日报')">预览</a> <a class="g-action">下载PDF</a> <a class="g-action">下载Excel</a></td></tr>
</tbody>
</table>
</div>
<div class="g-pagination">
<button class="g-page-btn">&lt;</button>
<button class="g-page-btn active">1</button>
<button class="g-page-btn">2</button>
<button class="g-page-btn">3</button>
<button class="g-page-btn">4</button>
<button class="g-page-btn">&gt;</button>
</div>
</div>
<!-- ========== 周报 Tab ========== -->
<div id="frpTabWeekly" class="frp-tab-panel" style="display:none;">
<div class="frp-table-card">
<table class="g-table">
<thead>
<tr>
<th>日期</th><th>营业额</th><th>订单数</th><th>客单价</th><th>退款率</th><th>成本总额</th><th>净利润</th><th>利润率</th><th>状态</th><th>操作</th>
</tr>
</thead>
<tbody>
<tr><td>02-10 ~ 02-16</td><td class="frp-amount">&yen;62,450</td><td>1,348单</td><td>&yen;46.3</td><td>5.8%</td><td>&yen;38,120</td><td class="frp-amount" style="color:var(--g-success);">&yen;24,330</td><td>39.0%</td><td><span class="g-tag g-tag-blue">生成中</span></td><td><a class="g-action" onclick="openFrpPreview('2026年第7周 经营周报')">预览</a> <a class="g-action">下载PDF</a> <a class="g-action">下载Excel</a></td></tr>
<tr><td>02-03 ~ 02-09</td><td class="frp-amount">&yen;68,750</td><td>1,486单</td><td>&yen;46.3</td><td>4.9%</td><td>&yen;41,250</td><td class="frp-amount" style="color:var(--g-success);">&yen;27,500</td><td>40.0%</td><td><span class="g-tag g-tag-green">已生成</span></td><td><a class="g-action" onclick="openFrpPreview('2026年第6周 经营周报')">预览</a> <a class="g-action">下载PDF</a> <a class="g-action">下载Excel</a></td></tr>
<tr><td>01-27 ~ 02-02</td><td class="frp-amount">&yen;59,320</td><td>1,282单</td><td>&yen;46.3</td><td>6.2%</td><td>&yen;36,780</td><td class="frp-amount" style="color:var(--g-success);">&yen;22,540</td><td>38.0%</td><td><span class="g-tag g-tag-green">已生成</span></td><td><a class="g-action" onclick="openFrpPreview('2026年第5周 经营周报')">预览</a> <a class="g-action">下载PDF</a> <a class="g-action">下载Excel</a></td></tr>
<tr><td>01-20 ~ 01-26</td><td class="frp-amount">&yen;55,180</td><td>1,192单</td><td>&yen;46.3</td><td>5.5%</td><td>&yen;34,410</td><td class="frp-amount" style="color:var(--g-success);">&yen;20,770</td><td>37.6%</td><td><span class="g-tag g-tag-green">已生成</span></td><td><a class="g-action" onclick="openFrpPreview('2026年第4周 经营周报')">预览</a> <a class="g-action">下载PDF</a> <a class="g-action">下载Excel</a></td></tr>
</tbody>
</table>
</div>
<div class="g-pagination">
<button class="g-page-btn">&lt;</button>
<button class="g-page-btn active">1</button>
<button class="g-page-btn">2</button>
<button class="g-page-btn">&gt;</button>
</div>
</div>
<!-- ========== 月报 Tab ========== -->
<div id="frpTabMonthly" class="frp-tab-panel" style="display:none;">
<div class="frp-table-card">
<table class="g-table">
<thead>
<tr>
<th>日期</th><th>营业额</th><th>订单数</th><th>客单价</th><th>退款率</th><th>成本总额</th><th>净利润</th><th>利润率</th><th>状态</th><th>操作</th>
</tr>
</thead>
<tbody>
<tr><td>2026年2月</td><td class="frp-amount">&yen;131,200</td><td>2,834单</td><td>&yen;46.3</td><td>5.4%</td><td>&yen;79,370</td><td class="frp-amount" style="color:var(--g-success);">&yen;51,830</td><td>39.5%</td><td><span class="g-tag g-tag-blue">生成中</span></td><td><a class="g-action" onclick="openFrpPreview('2026年2月 经营月报')">预览</a> <a class="g-action">下载PDF</a> <a class="g-action">下载Excel</a></td></tr>
<tr><td>2026年1月</td><td class="frp-amount">&yen;278,600</td><td>6,022单</td><td>&yen;46.3</td><td>5.1%</td><td>&yen;170,150</td><td class="frp-amount" style="color:var(--g-success);">&yen;108,450</td><td>38.9%</td><td><span class="g-tag g-tag-green">已生成</span></td><td><a class="g-action" onclick="openFrpPreview('2026年1月 经营月报')">预览</a> <a class="g-action">下载PDF</a> <a class="g-action">下载Excel</a></td></tr>
<tr><td>2025年12月</td><td class="frp-amount">&yen;265,400</td><td>5,736单</td><td>&yen;46.3</td><td>5.6%</td><td>&yen;163,920</td><td class="frp-amount" style="color:var(--g-success);">&yen;101,480</td><td>38.2%</td><td><span class="g-tag g-tag-green">已生成</span></td><td><a class="g-action" onclick="openFrpPreview('2025年12月 经营月报')">预览</a> <a class="g-action">下载PDF</a> <a class="g-action">下载Excel</a></td></tr>
</tbody>
</table>
</div>
<div class="g-pagination">
<button class="g-page-btn">&lt;</button>
<button class="g-page-btn active">1</button>
<button class="g-page-btn">2</button>
<button class="g-page-btn">&gt;</button>
</div>
</div>
<!-- ========== 预览抽屉 ========== -->
<div class="g-drawer-mask" id="frpDrawerMask" onclick="closeFrpPreview()"></div>
<div class="g-drawer frp-drawer" id="frpDrawer">
<div class="g-drawer-hd">
<span class="g-drawer-title" id="frpDrawerTitle">2026年2月12日 经营日报</span>
<button class="g-drawer-close" onclick="closeFrpPreview()"><i data-lucide="x" style="width:18px;height:18px;"></i></button>
</div>
<div class="g-drawer-bd">
<!-- 关键指标 -->
<div class="frp-section">
<div class="frp-section-hd">关键指标</div>
<div class="frp-kpi-grid">
<div class="frp-kpi-item">
<div class="frp-kpi-label">营业额</div>
<div class="frp-kpi-val">&yen;8,620</div>
<div class="frp-kpi-change">
<span class="frp-kpi-down">同比 ↓3.2%</span>
<span class="frp-kpi-up">环比 ↑5.8%</span>
</div>
</div>
<div class="frp-kpi-item">
<div class="frp-kpi-label">订单数</div>
<div class="frp-kpi-val">186</div>
<div class="frp-kpi-change">
<span class="frp-kpi-up">同比 ↑2.1%</span>
<span class="frp-kpi-down">环比 ↓1.4%</span>
</div>
</div>
<div class="frp-kpi-item">
<div class="frp-kpi-label">客单价</div>
<div class="frp-kpi-val">&yen;46.3</div>
<div class="frp-kpi-change">
<span class="frp-kpi-down">同比 ↓0.8%</span>
<span class="frp-kpi-up">环比 ↑1.2%</span>
</div>
</div>
<div class="frp-kpi-item">
<div class="frp-kpi-label">退款率</div>
<div class="frp-kpi-val">7.4%</div>
<div class="frp-kpi-change">
<span class="frp-kpi-down">同比 ↑2.1%</span>
<span class="frp-kpi-up">环比 ↓0.6%</span>
</div>
</div>
<div class="frp-kpi-item">
<div class="frp-kpi-label">净利润</div>
<div class="frp-kpi-val" style="color:var(--g-success);">&yen;3,340</div>
<div class="frp-kpi-change">
<span class="frp-kpi-up">同比 ↑4.5%</span>
<span class="frp-kpi-up">环比 ↑6.2%</span>
</div>
</div>
<div class="frp-kpi-item">
<div class="frp-kpi-label">利润率</div>
<div class="frp-kpi-val">38.7%</div>
<div class="frp-kpi-change">
<span class="frp-kpi-up">同比 ↑1.3%</span>
<span class="frp-kpi-up">环比 ↑0.9%</span>
</div>
</div>
</div>
</div>
<div class="frp-divider"></div>
<!-- 收入明细 -->
<div class="frp-section">
<div class="frp-section-hd">收入明细(按渠道)</div>
<div class="frp-detail-row">
<span class="frp-detail-name"><i data-lucide="bike" style="width:14px;height:14px;vertical-align:-2px;color:var(--primary);margin-right:6px;"></i>外卖</span>
<div class="frp-detail-right">
<span class="frp-detail-pct">58.3%</span>
<span class="frp-detail-amount">&yen;5,025</span>
</div>
</div>
<div class="frp-detail-row">
<span class="frp-detail-name"><i data-lucide="shopping-bag" style="width:14px;height:14px;vertical-align:-2px;color:var(--g-success);margin-right:6px;"></i>自提</span>
<div class="frp-detail-right">
<span class="frp-detail-pct">26.1%</span>
<span class="frp-detail-amount">&yen;2,250</span>
</div>
</div>
<div class="frp-detail-row">
<span class="frp-detail-name"><i data-lucide="utensils" style="width:14px;height:14px;vertical-align:-2px;color:var(--g-warning);margin-right:6px;"></i>堂食</span>
<div class="frp-detail-right">
<span class="frp-detail-pct">15.6%</span>
<span class="frp-detail-amount">&yen;1,345</span>
</div>
</div>
</div>
<div class="frp-divider"></div>
<!-- 成本明细 -->
<div class="frp-section">
<div class="frp-section-hd">成本明细(按类别)</div>
<div class="frp-detail-row">
<span class="frp-detail-name"><i data-lucide="carrot" style="width:14px;height:14px;vertical-align:-2px;color:var(--g-warning);margin-right:6px;"></i>食材成本</span>
<div class="frp-detail-right">
<span class="frp-detail-pct">56.8%</span>
<span class="frp-detail-amount">&yen;3,000</span>
</div>
</div>
<div class="frp-detail-row">
<span class="frp-detail-name"><i data-lucide="users" style="width:14px;height:14px;vertical-align:-2px;color:var(--primary);margin-right:6px;"></i>人工成本</span>
<div class="frp-detail-right">
<span class="frp-detail-pct">26.5%</span>
<span class="frp-detail-amount">&yen;1,400</span>
</div>
</div>
<div class="frp-detail-row">
<span class="frp-detail-name"><i data-lucide="building-2" style="width:14px;height:14px;vertical-align:-2px;color:var(--g-text-muted);margin-right:6px;"></i>固定成本</span>
<div class="frp-detail-right">
<span class="frp-detail-pct">11.4%</span>
<span class="frp-detail-amount">&yen;600</span>
</div>
</div>
<div class="frp-detail-row">
<span class="frp-detail-name"><i data-lucide="package" style="width:14px;height:14px;vertical-align:-2px;color:var(--g-success);margin-right:6px;"></i>包装成本</span>
<div class="frp-detail-right">
<span class="frp-detail-pct">5.3%</span>
<span class="frp-detail-amount">&yen;280</span>
</div>
</div>
</div>
</div>
<div class="g-drawer-ft">
<button class="g-btn" onclick="closeFrpPreview()">关闭</button>
<button class="g-btn g-btn-primary"><i data-lucide="file-text" style="width:14px;height:14px;"></i> 下载PDF</button>
<button class="g-btn"><i data-lucide="table" style="width:14px;height:14px;"></i> 下载Excel</button>
</div>
</div>
<script>
/* Tab 切换 */
function switchFrpTab(el, tab) {
document.querySelectorAll('.g-seg-item').forEach(function(s) { s.classList.remove('active'); });
el.classList.add('active');
document.querySelectorAll('.frp-tab-panel').forEach(function(p) { p.style.display = 'none'; });
var map = { daily: 'frpTabDaily', weekly: 'frpTabWeekly', monthly: 'frpTabMonthly' };
document.getElementById(map[tab]).style.display = '';
}
/* 预览抽屉 */
function openFrpPreview(title) {
document.getElementById('frpDrawerTitle').textContent = title || '报表预览';
document.getElementById('frpDrawerMask').classList.add('open');
document.getElementById('frpDrawer').classList.add('open');
if (typeof lucide !== 'undefined') lucide.createIcons();
}
function closeFrpPreview() {
document.getElementById('frpDrawerMask').classList.remove('open');
document.getElementById('frpDrawer').classList.remove('open');
}
</script>

297
pages/fin-settlement.html Normal file
View File

@@ -0,0 +1,297 @@
<!-- 到账查询 — fin-settlement.html -->
<style>
/* ---- page-private: fst- prefix ---- */
.fst-stats {
display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; margin-bottom: 16px;
}
.fst-stat-card {
background: #fff; border-radius: var(--g-radius); padding: 18px 20px;
border: 1px solid var(--g-border); box-shadow: var(--g-shadow-sm);
transition: box-shadow var(--g-transition);
}
.fst-stat-card:hover { box-shadow: var(--g-shadow-md); transform: translateY(-1px); }
.fst-stat-label { font-size: 13px; color: var(--g-text-muted); margin-bottom: 6px; display: flex; align-items: center; gap: 6px; }
.fst-stat-label i { width: 16px; height: 16px; }
.fst-stat-val { font-size: 24px; font-weight: 700; color: var(--g-text); }
/* 账户信息条 */
.fst-account-bar {
display: flex; align-items: center; gap: 16px; padding: 14px 20px; margin-bottom: 16px;
background: #fff; border-radius: var(--g-radius); border: 1px solid var(--g-border);
box-shadow: var(--g-shadow-sm); font-size: 13px; color: var(--g-text-secondary);
}
.fst-account-bar i { width: 18px; height: 18px; color: var(--primary); flex-shrink: 0; }
.fst-account-bar strong { color: var(--g-text); font-weight: 600; }
.fst-account-sep { width: 1px; height: 20px; background: var(--g-border); }
.fst-toolbar {
background: #fff; border-radius: var(--g-radius); padding: 14px 18px;
display: flex; align-items: center; gap: 10px; margin-bottom: 16px;
box-shadow: var(--g-shadow-sm); border: 1px solid var(--g-border);
}
.fst-toolbar .g-input,
.fst-toolbar .g-select { height: 32px; font-size: 13px; }
.fst-date-sep { color: var(--g-text-muted); font-size: 13px; line-height: 32px; }
.fst-table-card {
background: #fff; border-radius: var(--g-radius); border: 1px solid var(--g-border);
overflow: hidden; margin-bottom: 16px;
}
.fst-table-card .g-table th { white-space: nowrap; }
.fst-table-card .g-table td { vertical-align: middle; font-size: 13px; }
.fst-amount { font-weight: 600; white-space: nowrap; }
/* 展开明细行 */
.fst-detail-row { display: none; }
.fst-detail-row.open { display: table-row; }
.fst-detail-row td { padding: 0 !important; background: #f8f9fb; }
.fst-detail-wrap { padding: 14px 20px 14px 40px; }
.fst-detail-title { font-size: 13px; font-weight: 600; color: var(--g-text); margin-bottom: 10px; padding-left: 10px; border-left: 3px solid var(--primary); }
.fst-mini-table { width: 100%; border-collapse: collapse; font-size: 12px; }
.fst-mini-table th {
text-align: left; padding: 8px 10px; font-weight: 500;
color: var(--g-text-muted); background: #fff; border-bottom: 1px solid var(--g-border);
}
.fst-mini-table td { padding: 8px 10px; border-bottom: 1px solid #f3f4f6; color: var(--g-text); }
.fst-mini-table tr:last-child td { border-bottom: none; }
.fst-channel-icon { display: inline-flex; align-items: center; gap: 6px; }
.fst-channel-dot { width: 8px; height: 8px; border-radius: 50%; }
</style>
<!-- 统计卡片 -->
<div class="fst-stats">
<div class="fst-stat-card">
<div class="fst-stat-label"><i data-lucide="circle-check"></i>今日到账</div>
<div class="fst-stat-val" style="color:var(--g-success);">&yen;4,670</div>
</div>
<div class="fst-stat-card">
<div class="fst-stat-label"><i data-lucide="calendar-minus"></i>昨日到账</div>
<div class="fst-stat-val">&yen;4,940</div>
</div>
<div class="fst-stat-card">
<div class="fst-stat-label"><i data-lucide="calendar-check"></i>本月累计到账</div>
<div class="fst-stat-val">&yen;42,800</div>
</div>
<div class="fst-stat-card">
<div class="fst-stat-label"><i data-lucide="receipt"></i>本月交易笔数</div>
<div class="fst-stat-val">1,286</div>
</div>
</div>
<!-- 结算账户信息 -->
<div class="fst-account-bar">
<i data-lucide="landmark"></i>
<span>结算账户:<strong>招商银行 ****1234</strong></span>
<span class="fst-account-sep"></span>
<span>微信商户号:<strong>15xxxxxxxx</strong></span>
<span class="fst-account-sep"></span>
<span>支付宝PID<strong>2088xxxxxxxxxxxx</strong></span>
<span class="fst-account-sep"></span>
<span>结算周期:<strong>T+1 自动到账</strong></span>
</div>
<!-- 筛选工具栏 -->
<div class="fst-toolbar">
<input type="date" class="g-input" style="width:145px;" value="2026-02-01">
<span class="fst-date-sep">~</span>
<input type="date" class="g-input" style="width:145px;" value="2026-02-12">
<select class="g-select" style="width:130px;">
<option>全部渠道</option>
<option>微信支付</option>
<option>支付宝</option>
</select>
<div style="flex:1;"></div>
<button class="g-btn g-btn-sm"><i data-lucide="download" style="width:14px;height:14px;margin-right:4px;vertical-align:-2px;"></i>导出</button>
</div>
<!-- 到账记录表格 -->
<div class="fst-table-card">
<table class="g-table">
<thead>
<tr>
<th style="width:36px;"></th>
<th>到账日期</th>
<th>支付渠道</th>
<th>交易笔数</th>
<th style="text-align:right;">到账金额</th>
</tr>
</thead>
<tbody>
<!-- 1 -->
<tr>
<td><i data-lucide="chevron-right" style="width:14px;height:14px;cursor:pointer;color:var(--g-text-muted);transition:var(--g-transition);" onclick="toggleFstDetail(this,'fst-d1')"></i></td>
<td>2026-02-12</td>
<td><span class="fst-channel-icon"><span class="fst-channel-dot" style="background:#07c160;"></span>微信支付</span></td>
<td>86</td>
<td style="text-align:right;" class="fst-amount">&yen;3,420.00</td>
</tr>
<tr class="fst-detail-row" id="fst-d1">
<td colspan="5">
<div class="fst-detail-wrap">
<div class="fst-detail-title">交易明细(部分)</div>
<table class="fst-mini-table">
<thead><tr><th>订单号</th><th>金额</th><th>时间</th></tr></thead>
<tbody>
<tr><td>ORD20260212001</td><td>¥58.00</td><td>12:35</td></tr>
<tr><td>ORD20260212005</td><td>¥126.00</td><td>11:22</td></tr>
<tr><td>ORD20260212011</td><td>¥42.50</td><td>10:08</td></tr>
<tr><td>ORD20260212015</td><td>¥89.00</td><td>09:15</td></tr>
</tbody>
</table>
</div>
</td>
</tr>
<!-- 2 -->
<tr>
<td><i data-lucide="chevron-right" style="width:14px;height:14px;cursor:pointer;color:var(--g-text-muted);transition:var(--g-transition);" onclick="toggleFstDetail(this,'fst-d2')"></i></td>
<td>2026-02-12</td>
<td><span class="fst-channel-icon"><span class="fst-channel-dot" style="background:#1677ff;"></span>支付宝</span></td>
<td>32</td>
<td style="text-align:right;" class="fst-amount">&yen;1,250.00</td>
</tr>
<tr class="fst-detail-row" id="fst-d2">
<td colspan="5">
<div class="fst-detail-wrap">
<div class="fst-detail-title">交易明细(部分)</div>
<table class="fst-mini-table">
<thead><tr><th>订单号</th><th>金额</th><th>时间</th></tr></thead>
<tbody>
<tr><td>ORD20260212003</td><td>¥126.00</td><td>11:22</td></tr>
<tr><td>ORD20260212009</td><td>¥68.00</td><td>10:30</td></tr>
<tr><td>ORD20260212014</td><td>¥35.50</td><td>09:45</td></tr>
</tbody>
</table>
</div>
</td>
</tr>
<!-- 3 -->
<tr>
<td><i data-lucide="chevron-right" style="width:14px;height:14px;cursor:pointer;color:var(--g-text-muted);transition:var(--g-transition);" onclick="toggleFstDetail(this,'fst-d3')"></i></td>
<td>2026-02-11</td>
<td><span class="fst-channel-icon"><span class="fst-channel-dot" style="background:#07c160;"></span>微信支付</span></td>
<td>92</td>
<td style="text-align:right;" class="fst-amount">&yen;3,860.00</td>
</tr>
<tr class="fst-detail-row" id="fst-d3">
<td colspan="5">
<div class="fst-detail-wrap">
<div class="fst-detail-title">交易明细(部分)</div>
<table class="fst-mini-table">
<thead><tr><th>订单号</th><th>金额</th><th>时间</th></tr></thead>
<tbody>
<tr><td>ORD20260211042</td><td>¥89.00</td><td>20:15</td></tr>
<tr><td>ORD20260211038</td><td>¥56.00</td><td>19:30</td></tr>
<tr><td>ORD20260211035</td><td>¥72.00</td><td>18:45</td></tr>
<tr><td>ORD20260211031</td><td>¥38.50</td><td>17:20</td></tr>
</tbody>
</table>
</div>
</td>
</tr>
<!-- 4 -->
<tr>
<td><i data-lucide="chevron-right" style="width:14px;height:14px;cursor:pointer;color:var(--g-text-muted);transition:var(--g-transition);" onclick="toggleFstDetail(this,'fst-d4')"></i></td>
<td>2026-02-11</td>
<td><span class="fst-channel-icon"><span class="fst-channel-dot" style="background:#1677ff;"></span>支付宝</span></td>
<td>28</td>
<td style="text-align:right;" class="fst-amount">&yen;1,080.00</td>
</tr>
<tr class="fst-detail-row" id="fst-d4">
<td colspan="5">
<div class="fst-detail-wrap">
<div class="fst-detail-title">交易明细(部分)</div>
<table class="fst-mini-table">
<thead><tr><th>订单号</th><th>金额</th><th>时间</th></tr></thead>
<tbody>
<tr><td>ORD20260211040</td><td>¥45.00</td><td>19:50</td></tr>
<tr><td>ORD20260211036</td><td>¥62.00</td><td>18:10</td></tr>
<tr><td>ORD20260211033</td><td>¥33.00</td><td>16:40</td></tr>
</tbody>
</table>
</div>
</td>
</tr>
<!-- 5 -->
<tr>
<td><i data-lucide="chevron-right" style="width:14px;height:14px;cursor:pointer;color:var(--g-text-muted);transition:var(--g-transition);" onclick="toggleFstDetail(this,'fst-d5')"></i></td>
<td>2026-02-10</td>
<td><span class="fst-channel-icon"><span class="fst-channel-dot" style="background:#07c160;"></span>微信支付</span></td>
<td>78</td>
<td style="text-align:right;" class="fst-amount">&yen;3,150.00</td>
</tr>
<tr class="fst-detail-row" id="fst-d5">
<td colspan="5">
<div class="fst-detail-wrap">
<div class="fst-detail-title">交易明细(部分)</div>
<table class="fst-mini-table">
<thead><tr><th>订单号</th><th>金额</th><th>时间</th></tr></thead>
<tbody>
<tr><td>ORD20260210028</td><td>¥52.00</td><td>20:30</td></tr>
<tr><td>ORD20260210025</td><td>¥41.50</td><td>19:15</td></tr>
<tr><td>ORD20260210022</td><td>¥67.00</td><td>18:00</td></tr>
</tbody>
</table>
</div>
</td>
</tr>
<!-- 6 -->
<tr>
<td><i data-lucide="chevron-right" style="width:14px;height:14px;cursor:pointer;color:var(--g-text-muted);transition:var(--g-transition);" onclick="toggleFstDetail(this,'fst-d6')"></i></td>
<td>2026-02-10</td>
<td><span class="fst-channel-icon"><span class="fst-channel-dot" style="background:#1677ff;"></span>支付宝</span></td>
<td>25</td>
<td style="text-align:right;" class="fst-amount">&yen;960.00</td>
</tr>
<tr class="fst-detail-row" id="fst-d6">
<td colspan="5">
<div class="fst-detail-wrap">
<div class="fst-detail-title">交易明细(部分)</div>
<table class="fst-mini-table">
<thead><tr><th>订单号</th><th>金额</th><th>时间</th></tr></thead>
<tbody>
<tr><td>ORD20260210026</td><td>¥48.00</td><td>19:40</td></tr>
<tr><td>ORD20260210023</td><td>¥55.00</td><td>17:55</td></tr>
</tbody>
</table>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<!-- 分页 -->
<div class="g-pagination">
<span>共 68 条</span>
<select style="height:28px;padding:0 6px;border:1px solid #e5e7eb;border-radius:4px;font-size:12px;margin-right:4px;">
<option>20条/页</option>
<option>50条/页</option>
<option>100条/页</option>
</select>
<button class="g-page-btn" disabled>&lt;</button>
<button class="g-page-btn active">1</button>
<button class="g-page-btn">2</button>
<button class="g-page-btn">3</button>
<button class="g-page-btn">4</button>
<button class="g-page-btn">&gt;</button>
</div>
<script>
function toggleFstDetail(iconEl, rowId) {
var row = document.getElementById(rowId);
var isOpen = row.classList.contains('open');
row.classList.toggle('open');
var parentTr = row.previousElementSibling;
var icon = parentTr.querySelector('td:first-child i');
if (icon) {
icon.style.transform = isOpen ? 'rotate(0deg)' : 'rotate(90deg)';
}
}
if (typeof lucide !== 'undefined') { lucide.createIcons(); }
</script>

255
pages/fin-transactions.html Normal file
View File

@@ -0,0 +1,255 @@
<!-- 交易流水 — fin-transactions.html -->
<style>
/* ---- page-private: ft- prefix ---- */
.ft-stats {
display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; margin-bottom: 16px;
}
.ft-stat-card {
background: #fff; border-radius: var(--g-radius); padding: 18px 20px;
box-shadow: var(--g-shadow-sm); transition: var(--g-transition);
}
.ft-stat-card:hover { box-shadow: var(--g-shadow-md); transform: translateY(-1px); }
.ft-stat-label { font-size: 13px; color: var(--g-text-muted); margin-bottom: 6px; display: flex; align-items: center; gap: 6px; }
.ft-stat-label i { width: 16px; height: 16px; }
.ft-stat-val { font-size: 24px; font-weight: 700; color: var(--g-text); }
.ft-stat-val.green { color: var(--g-success); }
.ft-stat-val.red { color: var(--g-danger); }
.ft-stat-val.orange { color: var(--g-warning); }
.ft-toolbar {
background: #fff; border-radius: var(--g-radius); padding: 14px 18px;
display: flex; flex-wrap: wrap; gap: 10px; align-items: center;
box-shadow: var(--g-shadow-sm); border: 1px solid var(--g-border); margin-bottom: 16px;
}
.ft-toolbar select,
.ft-toolbar input {
height: 32px; padding: 0 10px; border: 1px solid #e5e7eb; border-radius: var(--g-radius-sm, 6px);
font-size: 13px; outline: none; transition: var(--g-transition); background: #fff; color: var(--g-text);
}
.ft-toolbar select:focus,
.ft-toolbar input:focus { border-color: var(--primary); box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 12%, transparent); }
.ft-toolbar select { min-width: 110px; }
.ft-date-sep { color: var(--g-text-muted); font-size: 13px; line-height: 32px; }
.ft-search-wrap { position: relative; width: 180px; }
.ft-search-wrap input { padding-left: 30px; width: 100%; }
.ft-search-wrap i { position: absolute; left: 8px; top: 50%; transform: translateY(-50%); color: var(--g-text-muted); pointer-events: none; }
.ft-toolbar-right { margin-left: auto; display: flex; gap: 8px; align-items: center; }
.ft-table-card {
background: #fff; border-radius: var(--g-radius); border: 1px solid var(--g-border);
overflow: hidden;
}
.ft-table-card .g-table th { white-space: nowrap; }
.ft-table-card .g-table td { vertical-align: middle; font-size: 13px; }
.ft-mono { font-family: 'SF Mono', 'Cascadia Code', 'Consolas', monospace; font-size: 12px; color: var(--g-text-secondary); }
.ft-amount { font-weight: 600; white-space: nowrap; }
.ft-amount.income { color: var(--g-success); }
.ft-amount.expense { color: var(--g-danger); }
.ft-amount.neutral { color: var(--g-text); }
.ft-time { font-size: 12px; color: var(--g-text-muted); white-space: nowrap; }
/* 快捷日期 */
.ft-quick-dates { display:flex; gap:4px; }
.ft-quick-dates button {
padding:4px 10px; font-size:12px; border:1px solid #e5e7eb; border-radius:4px;
background:#fff; color:var(--g-text-secondary); cursor:pointer; transition:all var(--g-transition);
}
.ft-quick-dates button:hover { border-color:var(--primary); color:var(--primary); }
.ft-quick-dates button.active { background:var(--primary); color:#fff; border-color:var(--primary); }
/* 汇总行 */
.ft-summary td { background:#f8f9fb; font-weight:600; font-size:13px; color:var(--g-text); border-top:2px solid #e5e7eb; }
.ft-remark { font-size:12px; color:var(--g-text-muted); max-width:120px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
</style>
<!-- 汇总指标 -->
<div class="ft-stats">
<div class="ft-stat-card">
<div class="ft-stat-label"><i data-lucide="trending-up"></i>总收入</div>
<div class="ft-stat-val green">&yen;45,680</div>
</div>
<div class="ft-stat-card">
<div class="ft-stat-label"><i data-lucide="undo-2"></i>总退款</div>
<div class="ft-stat-val red">&yen;3,200</div>
</div>
<div class="ft-stat-card">
<div class="ft-stat-label"><i data-lucide="receipt"></i>交易笔数</div>
<div class="ft-stat-val">256</div>
</div>
</div>
<!-- 筛选工具栏 -->
<div class="ft-toolbar">
<input type="date" value="2026-02-01">
<span class="ft-date-sep"></span>
<input type="date" value="2026-02-12">
<div class="ft-quick-dates">
<button onclick="ftQuickDate(this,'today')" class="active">今天</button>
<button onclick="ftQuickDate(this,'yesterday')">昨天</button>
<button onclick="ftQuickDate(this,'7d')">近7天</button>
<button onclick="ftQuickDate(this,'30d')">近30天</button>
<button onclick="ftQuickDate(this,'month')">本月</button>
</div>
<select>
<option>全部类型</option>
<option>收入</option>
<option>退款</option>
<option>储值充值</option>
<option>积分抵扣</option>
</select>
<select>
<option>全部渠道</option>
<option>外卖</option>
<option>自提</option>
<option>堂食</option>
</select>
<select>
<option>全部支付方式</option>
<option>微信</option>
<option>支付宝</option>
<option>现金</option>
<option>储值余额</option>
</select>
<div class="ft-search-wrap">
<i data-lucide="search" style="width:14px;height:14px;"></i>
<input type="text" placeholder="搜索流水号/订单号">
</div>
<div class="ft-toolbar-right">
<button class="g-btn g-btn-sm" onclick="this.textContent='导出中…';setTimeout(()=>{this.innerHTML='<i data-lucide=\'download\' style=\'width:14px;height:14px;margin-right:4px;vertical-align:-2px;\'></i>已导出';lucide.createIcons();setTimeout(()=>{this.innerHTML='<i data-lucide=\'download\' style=\'width:14px;height:14px;margin-right:4px;vertical-align:-2px;\'></i>导出';lucide.createIcons();},1500);},800)"><i data-lucide="download" style="width:14px;height:14px;margin-right:4px;vertical-align:-2px;"></i>导出</button>
</div>
</div>
<!-- 交易流水表格 -->
<div class="ft-table-card">
<table class="g-table">
<thead>
<tr>
<th>流水号</th>
<th>关联订单</th>
<th>类型</th>
<th>渠道</th>
<th>支付方式</th>
<th style="text-align:right;">金额</th>
<th>交易时间</th>
<th>备注</th>
</tr>
</thead>
<tbody>
<tr>
<td class="ft-mono">TXN20260212001</td>
<td><a class="g-action ft-mono">#ORD20260212001</a></td>
<td><span class="g-tag g-tag-green">收入</span></td>
<td>外卖</td>
<td>微信</td>
<td style="text-align:right;" class="ft-amount income">+&yen;58.00</td>
<td class="ft-time">2026-02-12 12:35:00</td>
<td class="ft-remark" title="外卖平台订单">外卖平台订单</td>
</tr>
<tr>
<td class="ft-mono">TXN20260212002</td>
<td><a class="g-action ft-mono">#ORD20260212003</a></td>
<td><span class="g-tag g-tag-red">退款</span></td>
<td>外卖</td>
<td>微信</td>
<td style="text-align:right;" class="ft-amount expense">-&yen;32.00</td>
<td class="ft-time">2026-02-12 11:50:22</td>
<td class="ft-remark" title="顾客申请退款-菜品不符">顾客申请退款-菜品不符</td>
</tr>
<tr>
<td class="ft-mono">TXN20260212003</td>
<td><a class="g-action ft-mono">#ORD20260212005</a></td>
<td><span class="g-tag g-tag-green">收入</span></td>
<td>堂食</td>
<td>支付宝</td>
<td style="text-align:right;" class="ft-amount income">+&yen;126.00</td>
<td class="ft-time">2026-02-12 11:22:10</td>
<td class="ft-remark"></td>
</tr>
<tr>
<td class="ft-mono">TXN20260212004</td>
<td class="ft-mono"></td>
<td><span class="g-tag g-tag-blue">储值充值</span></td>
<td></td>
<td>微信</td>
<td style="text-align:right;" class="ft-amount income">+&yen;500.00</td>
<td class="ft-time">2026-02-12 10:45:33</td>
<td class="ft-remark" title="会员张女士充值">会员张女士充值</td>
</tr>
<tr>
<td class="ft-mono">TXN20260212005</td>
<td><a class="g-action ft-mono">#ORD20260212008</a></td>
<td><span class="g-tag g-tag-orange">积分抵扣</span></td>
<td>自提</td>
<td>储值余额</td>
<td style="text-align:right;" class="ft-amount income">+&yen;45.00</td>
<td class="ft-time">2026-02-12 10:12:05</td>
<td class="ft-remark" title="抵扣200积分">抵扣200积分</td>
</tr>
<tr>
<td class="ft-mono">TXN20260212006</td>
<td><a class="g-action ft-mono">#ORD20260212010</a></td>
<td><span class="g-tag g-tag-green">收入</span></td>
<td>自提</td>
<td>现金</td>
<td style="text-align:right;" class="ft-amount income">+&yen;28.00</td>
<td class="ft-time">2026-02-12 09:38:17</td>
<td class="ft-remark"></td>
</tr>
<tr>
<td class="ft-mono">TXN20260212007</td>
<td><a class="g-action ft-mono">#ORD20260211042</a></td>
<td><span class="g-tag g-tag-green">收入</span></td>
<td>堂食</td>
<td>微信</td>
<td style="text-align:right;" class="ft-amount income">+&yen;89.00</td>
<td class="ft-time">2026-02-11 20:15:44</td>
<td class="ft-remark"></td>
</tr>
<!-- 汇总行 -->
<tr class="ft-summary">
<td colspan="5" style="text-align:right;">本页合计</td>
<td style="text-align:right;">收入 &yen;846.00 / 退款 &yen;32.00</td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
</div>
<!-- 分页 -->
<div class="g-pagination">
<span>共 256 条</span>
<select style="height:28px;padding:0 6px;border:1px solid #e5e7eb;border-radius:4px;font-size:12px;margin-right:4px;">
<option>20条/页</option>
<option>50条/页</option>
<option>100条/页</option>
</select>
<button class="g-page-btn" disabled>&lt;</button>
<button class="g-page-btn active">1</button>
<button class="g-page-btn">2</button>
<button class="g-page-btn">3</button>
<button class="g-page-btn">4</button>
<button class="g-page-btn">5</button>
<button class="g-page-btn">&gt;</button>
</div>
<script>
/* 快捷日期切换 */
function ftQuickDate(btn, range) {
btn.closest('.ft-quick-dates').querySelectorAll('button').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
var today = new Date('2026-02-12');
var inputs = document.querySelectorAll('.ft-toolbar input[type="date"]');
var fmt = d => d.toISOString().slice(0,10);
var start = today, end = today;
if (range === 'today') { start = end = today; }
else if (range === 'yesterday') { start = end = new Date(today - 86400000); }
else if (range === '7d') { start = new Date(today - 6 * 86400000); }
else if (range === '30d') { start = new Date(today - 29 * 86400000); }
else if (range === 'month') { start = new Date('2026-02-01'); }
inputs[0].value = fmt(start);
inputs[1].value = fmt(end);
}
if (typeof lucide !== 'undefined') { lucide.createIcons(); }
</script>

179
pages/help-center.html Normal file
View File

@@ -0,0 +1,179 @@
<!-- 帮助中心 -->
<style>
.hlp-page { display:flex; flex-direction:column; gap:24px; }
.hlp-search-area { display:flex; justify-content:center; padding:20px 0 8px; }
.hlp-search-box {
width:500px; position:relative;
}
.hlp-search-box input {
width:100%; height:44px; padding:0 16px 0 42px; border:1px solid var(--g-border);
border-radius:var(--g-radius-lg); font-size:14px; outline:none; transition:var(--g-transition);
background:#fff; box-shadow:var(--g-shadow-sm);
}
.hlp-search-box input:focus { border-color:var(--primary); box-shadow:0 0 0 3px color-mix(in srgb, var(--primary) 12%, transparent); }
.hlp-search-icon {
position:absolute; left:14px; top:50%; transform:translateY(-50%);
color:var(--g-text-muted); pointer-events:none;
}
.hlp-search-icon svg { width:18px; height:18px; }
.hlp-shortcuts { display:grid; grid-template-columns:repeat(4,1fr); gap:14px; }
.hlp-shortcut-card {
background:#fff; border-radius:var(--g-radius); padding:24px 20px;
display:flex; flex-direction:column; align-items:center; text-align:center;
gap:10px; box-shadow:var(--g-shadow-sm); transition:var(--g-transition); cursor:pointer;
}
.hlp-shortcut-card:hover { box-shadow:var(--g-shadow-md); transform:translateY(-1px); }
.hlp-shortcut-icon {
width:48px; height:48px; border-radius:12px; display:flex; align-items:center;
justify-content:center; color:#fff;
}
.hlp-shortcut-icon svg { width:22px; height:22px; }
.hlp-shortcut-icon.hlp-c1 { background:var(--primary); }
.hlp-shortcut-icon.hlp-c2 { background:var(--g-success); }
.hlp-shortcut-icon.hlp-c3 { background:var(--g-warning); }
.hlp-shortcut-icon.hlp-c4 { background:#722ed1; }
.hlp-shortcut-title { font-size:14px; font-weight:600; color:var(--g-text); }
.hlp-shortcut-desc { font-size:12px; color:var(--g-text-muted); line-height:1.5; }
.hlp-section-hd {
font-size:15px; font-weight:600; color:var(--g-text);
padding-left:10px; border-left:3px solid var(--primary); margin-bottom:16px;
}
.hlp-faq-list { display:flex; flex-direction:column; gap:6px; }
.hlp-faq-item {
display:flex; align-items:center; gap:10px; padding:12px 16px;
background:#fff; border-radius:var(--g-radius-sm); box-shadow:var(--g-shadow-sm);
transition:var(--g-transition); cursor:pointer;
}
.hlp-faq-item:hover { box-shadow:var(--g-shadow-md); background:color-mix(in srgb, var(--primary) 3%, #fff); }
.hlp-faq-item svg { width:16px; height:16px; color:var(--g-text-muted); flex-shrink:0; }
.hlp-faq-text { flex:1; font-size:13px; color:var(--g-text); }
.hlp-faq-views { font-size:12px; color:var(--g-text-muted); white-space:nowrap; }
.hlp-contact { display:grid; grid-template-columns:repeat(3,1fr); gap:14px; }
.hlp-contact-item {
display:flex; align-items:center; gap:14px; padding:20px;
}
.hlp-contact-icon {
width:44px; height:44px; border-radius:10px; display:flex; align-items:center;
justify-content:center; background:var(--primary-light); color:var(--primary); flex-shrink:0;
}
.hlp-contact-icon svg { width:20px; height:20px; }
.hlp-contact-info { display:flex; flex-direction:column; gap:2px; }
.hlp-contact-label { font-size:12px; color:var(--g-text-muted); }
.hlp-contact-value { font-size:14px; font-weight:600; color:var(--g-text); }
</style>
<div class="hlp-page">
<!-- 搜索区 -->
<div class="hlp-search-area">
<div class="hlp-search-box">
<span class="hlp-search-icon"><i data-lucide="search"></i></span>
<input class="g-input" placeholder="搜索常见问题..." style="padding-left:42px;height:44px;border-radius:var(--g-radius-lg);">
</div>
</div>
<!-- 快捷入口 -->
<div class="hlp-shortcuts">
<div class="hlp-shortcut-card">
<div class="hlp-shortcut-icon hlp-c1"><i data-lucide="book-open"></i></div>
<div class="hlp-shortcut-title">新手指南</div>
<div class="hlp-shortcut-desc">快速上手平台核心功能</div>
</div>
<div class="hlp-shortcut-card">
<div class="hlp-shortcut-icon hlp-c2"><i data-lucide="help-circle"></i></div>
<div class="hlp-shortcut-title">常见问题</div>
<div class="hlp-shortcut-desc">查看高频问题解答</div>
</div>
<div class="hlp-shortcut-card">
<div class="hlp-shortcut-icon hlp-c3"><i data-lucide="play-circle"></i></div>
<div class="hlp-shortcut-title">操作教程</div>
<div class="hlp-shortcut-desc">图文视频操作演示</div>
</div>
<div class="hlp-shortcut-card">
<div class="hlp-shortcut-icon hlp-c4"><i data-lucide="headphones"></i></div>
<div class="hlp-shortcut-title">联系客服</div>
<div class="hlp-shortcut-desc">在线客服实时响应</div>
</div>
</div>
<!-- 热门问题 -->
<div>
<div class="hlp-section-hd">热门问题</div>
<div class="hlp-faq-list">
<div class="hlp-faq-item">
<i data-lucide="chevron-right"></i>
<span class="hlp-faq-text">如何添加新商品?</span>
<span class="hlp-faq-views">3,842 次浏览</span>
</div>
<div class="hlp-faq-item">
<i data-lucide="chevron-right"></i>
<span class="hlp-faq-text">如何设置配送范围?</span>
<span class="hlp-faq-views">2,715 次浏览</span>
</div>
<div class="hlp-faq-item">
<i data-lucide="chevron-right"></i>
<span class="hlp-faq-text">如何开通微信支付?</span>
<span class="hlp-faq-views">2,308 次浏览</span>
</div>
<div class="hlp-faq-item">
<i data-lucide="chevron-right"></i>
<span class="hlp-faq-text">如何查看经营报表?</span>
<span class="hlp-faq-views">1,956 次浏览</span>
</div>
<div class="hlp-faq-item">
<i data-lucide="chevron-right"></i>
<span class="hlp-faq-text">如何管理员工权限?</span>
<span class="hlp-faq-views">1,623 次浏览</span>
</div>
<div class="hlp-faq-item">
<i data-lucide="chevron-right"></i>
<span class="hlp-faq-text">如何申请发票?</span>
<span class="hlp-faq-views">1,247 次浏览</span>
</div>
<div class="hlp-faq-item">
<i data-lucide="chevron-right"></i>
<span class="hlp-faq-text">如何对接美团/饿了么?</span>
<span class="hlp-faq-views">1,102 次浏览</span>
</div>
<div class="hlp-faq-item">
<i data-lucide="chevron-right"></i>
<span class="hlp-faq-text">如何使用库存管理?</span>
<span class="hlp-faq-views">986 次浏览</span>
</div>
</div>
</div>
<!-- 联系我们 -->
<div>
<div class="hlp-section-hd">联系我们</div>
<div class="g-card hlp-contact">
<div class="hlp-contact-item">
<div class="hlp-contact-icon"><i data-lucide="message-circle"></i></div>
<div class="hlp-contact-info">
<span class="hlp-contact-label">在线客服</span>
<span class="hlp-contact-value">工作时间 9:00-21:00</span>
</div>
</div>
<div class="hlp-contact-item">
<div class="hlp-contact-icon"><i data-lucide="phone"></i></div>
<div class="hlp-contact-info">
<span class="hlp-contact-label">客服电话</span>
<span class="hlp-contact-value">400-888-6666</span>
</div>
</div>
<div class="hlp-contact-item">
<div class="hlp-contact-icon"><i data-lucide="mail"></i></div>
<div class="hlp-contact-info">
<span class="hlp-contact-label">邮箱</span>
<span class="hlp-contact-value">support@takeoutsaas.com</span>
</div>
</div>
</div>
</div>
</div>
<script>
if(typeof lucide!=='undefined'){lucide.createIcons();}
</script>

249
pages/inv-expiry.html Normal file
View File

@@ -0,0 +1,249 @@
<!-- 效期管理页 -->
<style>
.ive-fifo{display:flex;align-items:center;gap:10px;padding:12px 18px;background:color-mix(in srgb, var(--primary) 8%, #fff);border:1px solid color-mix(in srgb, var(--primary) 20%, #fff);border-radius:var(--g-radius);margin-bottom:16px;font-size:13px;color:var(--primary);font-weight:500;}
.ive-fifo svg{width:18px;height:18px;flex-shrink:0;}
.ive-stats{display:grid;grid-template-columns:repeat(4,1fr);gap:14px;margin-bottom:16px;}
.ive-stat{display:flex;align-items:center;gap:14px;padding:16px 20px;background:#fff;border-radius:var(--g-radius);box-shadow:var(--g-shadow-sm);transition:var(--g-transition);border-left:3px solid transparent;}
.ive-stat:hover{box-shadow:var(--g-shadow-md);}
.ive-stat-icon{width:40px;height:40px;border-radius:10px;display:flex;align-items:center;justify-content:center;flex-shrink:0;}
.ive-stat-icon svg{width:20px;height:20px;}
.ive-stat-num{font-size:22px;font-weight:700;line-height:1.2;}
.ive-stat-label{font-size:12px;color:var(--g-text-muted);margin-top:2px;}
.ive-stat-red{border-left-color:var(--g-danger);}
.ive-stat-red .ive-stat-icon{background:#fff1f0;color:var(--g-danger);}
.ive-stat-red .ive-stat-num{color:var(--g-danger);}
.ive-stat-orange{border-left-color:#fa8c16;}
.ive-stat-orange .ive-stat-icon{background:#fff7e6;color:#fa8c16;}
.ive-stat-orange .ive-stat-num{color:#fa8c16;}
.ive-stat-yellow{border-left-color:#faad14;}
.ive-stat-yellow .ive-stat-icon{background:#fffbe6;color:#faad14;}
.ive-stat-yellow .ive-stat-num{color:#faad14;}
.ive-stat-green{border-left-color:var(--g-success);}
.ive-stat-green .ive-stat-icon{background:#f6ffed;color:var(--g-success);}
.ive-stat-green .ive-stat-num{color:var(--g-success);}
.ive-toolbar{display:flex;align-items:center;gap:10px;margin-bottom:16px;flex-wrap:wrap;padding:12px 16px;background:#fff;border-radius:var(--g-radius);box-shadow:var(--g-shadow-sm);}
.ive-toolbar select{height:34px;padding:0 10px;border:1px solid var(--g-border);border-radius:var(--g-radius-sm);font-size:13px;outline:none;background:#fff;min-width:140px;cursor:pointer;transition:var(--g-transition);}
.ive-toolbar select:focus{border-color:var(--primary);box-shadow:0 0 0 3px color-mix(in srgb, var(--primary) 12%, transparent);}
.ive-toolbar .ive-search{height:34px;padding:0 12px;border:1px solid var(--g-border);border-radius:var(--g-radius-sm);font-size:13px;outline:none;width:200px;transition:var(--g-transition);}
.ive-toolbar .ive-search:focus{border-color:var(--primary);box-shadow:0 0 0 3px color-mix(in srgb, var(--primary) 12%, transparent);}
.ive-toolbar .ive-spacer{flex:1;}
.ive-row-expired{background:#fff1f0 !important;}
.ive-row-3day{background:#fff7e6 !important;}
.ive-row-7day{background:#fffbe6 !important;}
.ive-days-expired{color:var(--g-danger);font-weight:600;font-size:12px;}
.ive-days-urgent{color:#fa8c16;font-weight:600;}
@media(max-width:800px){.ive-stats{grid-template-columns:repeat(2,1fr);}}
@media(max-width:500px){.ive-stats{grid-template-columns:1fr;}}
</style>
<!-- FIFO 提醒条 -->
<div class="ive-fifo">
<i data-lucide="info"></i>
<span>遵循先进先出原则,请优先使用临期批次的原料</span>
</div>
<!-- 统计卡片 -->
<div class="ive-stats">
<div class="ive-stat ive-stat-red">
<div class="ive-stat-icon"><i data-lucide="alert-circle"></i></div>
<div>
<div class="ive-stat-num">1 <span style="font-size:13px;font-weight:500;"></span></div>
<div class="ive-stat-label">已过期</div>
</div>
</div>
<div class="ive-stat ive-stat-orange">
<div class="ive-stat-icon"><i data-lucide="clock"></i></div>
<div>
<div class="ive-stat-num">3 <span style="font-size:13px;font-weight:500;"></span></div>
<div class="ive-stat-label">3天内到期</div>
</div>
</div>
<div class="ive-stat ive-stat-yellow">
<div class="ive-stat-icon"><i data-lucide="timer"></i></div>
<div>
<div class="ive-stat-num">5 <span style="font-size:13px;font-weight:500;"></span></div>
<div class="ive-stat-label">7天内到期</div>
</div>
</div>
<div class="ive-stat ive-stat-green">
<div class="ive-stat-icon"><i data-lucide="check-circle"></i></div>
<div>
<div class="ive-stat-num">38 <span style="font-size:13px;font-weight:500;"></span></div>
<div class="ive-stat-label">正常</div>
</div>
</div>
</div>
<!-- 筛选工具栏 -->
<div class="ive-toolbar">
<select>
<option>全部分类</option>
<option>肉类</option>
<option>蔬菜</option>
<option>调料</option>
<option>饮品原料</option>
<option>包装</option>
</select>
<select>
<option>全部状态</option>
<option>已过期</option>
<option>3天内到期</option>
<option>7天内到期</option>
<option>正常</option>
</select>
<div class="ive-spacer"></div>
<input class="ive-search" type="text" placeholder="搜索原料名称">
</div>
<!-- 批次列表 -->
<div class="g-card" style="padding:0;overflow:hidden;">
<table class="g-table" style="margin:0;">
<thead>
<tr>
<th>原料名称</th>
<th>批次号</th>
<th>分类</th>
<th>入库日期</th>
<th>保质期至</th>
<th>剩余天数</th>
<th>库存数量</th>
<th>状态</th>
<th style="width:130px;">操作</th>
</tr>
</thead>
<tbody>
<tr class="ive-row-expired">
<td style="font-weight:600;">鲜牛奶</td>
<td>B20260210</td>
<td>饮品原料</td>
<td>2026-02-10</td>
<td>2026-02-13</td>
<td><span class="ive-days-expired">已过期1天</span></td>
<td>12 瓶</td>
<td><span class="g-tag g-tag-red">已过期</span></td>
<td><a class="g-action g-action-danger" href="javascript:;">报损</a> <a class="g-action" href="javascript:;">优先使用</a></td>
</tr>
<tr class="ive-row-3day">
<td style="font-weight:600;">生菜</td>
<td>B20260211</td>
<td>蔬菜</td>
<td>2026-02-11</td>
<td>2026-02-14</td>
<td><span class="ive-days-urgent">2 天</span></td>
<td>8 kg</td>
<td><span class="g-tag g-tag-orange">3天内</span></td>
<td><a class="g-action g-action-danger" href="javascript:;">报损</a> <a class="g-action" href="javascript:;">优先使用</a></td>
</tr>
<tr class="ive-row-3day">
<td style="font-weight:600;">鸡胸肉</td>
<td>B20260209</td>
<td>肉类</td>
<td>2026-02-09</td>
<td>2026-02-15</td>
<td><span class="ive-days-urgent">3 天</span></td>
<td>15 kg</td>
<td><span class="g-tag g-tag-orange">3天内</span></td>
<td><a class="g-action g-action-danger" href="javascript:;">报损</a> <a class="g-action" href="javascript:;">优先使用</a></td>
</tr>
<tr class="ive-row-7day">
<td style="font-weight:600;">西红柿</td>
<td>B20260210</td>
<td>蔬菜</td>
<td>2026-02-10</td>
<td>2026-02-16</td>
<td><span class="ive-days-urgent">4 天</span></td>
<td>10 kg</td>
<td><span class="g-tag g-tag-orange">7天内</span></td>
<td><a class="g-action g-action-danger" href="javascript:;">报损</a> <a class="g-action" href="javascript:;">优先使用</a></td>
</tr>
<tr class="ive-row-7day">
<td style="font-weight:600;">豆腐</td>
<td>B20260211</td>
<td>蔬菜</td>
<td>2026-02-11</td>
<td>2026-02-17</td>
<td><span class="ive-days-urgent">5 天</span></td>
<td>20 盒</td>
<td><span class="g-tag g-tag-orange">7天内</span></td>
<td><a class="g-action g-action-danger" href="javascript:;">报损</a> <a class="g-action" href="javascript:;">优先使用</a></td>
</tr>
<tr class="ive-row-7day">
<td style="font-weight:600;">猪肉</td>
<td>B20260208</td>
<td>肉类</td>
<td>2026-02-08</td>
<td>2026-02-18</td>
<td><span class="ive-days-urgent">6 天</span></td>
<td>25 kg</td>
<td><span class="g-tag g-tag-orange">7天内</span></td>
<td><a class="g-action g-action-danger" href="javascript:;">报损</a> <a class="g-action" href="javascript:;">优先使用</a></td>
</tr>
<tr>
<td style="font-weight:600;">酱油</td>
<td>B20260101</td>
<td>调料</td>
<td>2026-01-01</td>
<td>2026-06-30</td>
<td>138 天</td>
<td>6 瓶</td>
<td><span class="g-tag g-tag-green">正常</span></td>
<td><a class="g-action g-action-danger" href="javascript:;">报损</a> <a class="g-action" href="javascript:;">优先使用</a></td>
</tr>
<tr>
<td style="font-weight:600;">食用油</td>
<td>B20260115</td>
<td>调料</td>
<td>2026-01-15</td>
<td>2026-07-15</td>
<td>153 天</td>
<td>4 桶</td>
<td><span class="g-tag g-tag-green">正常</span></td>
<td><a class="g-action g-action-danger" href="javascript:;">报损</a> <a class="g-action" href="javascript:;">优先使用</a></td>
</tr>
<tr>
<td style="font-weight:600;">咖啡豆</td>
<td>B20260201</td>
<td>饮品原料</td>
<td>2026-02-01</td>
<td>2026-08-01</td>
<td>170 天</td>
<td>10 kg</td>
<td><span class="g-tag g-tag-green">正常</span></td>
<td><a class="g-action g-action-danger" href="javascript:;">报损</a> <a class="g-action" href="javascript:;">优先使用</a></td>
</tr>
<tr>
<td style="font-weight:600;">外卖盒</td>
<td>B20260201</td>
<td>包装</td>
<td>2026-02-01</td>
<td></td>
<td></td>
<td>500 个</td>
<td><span class="g-tag g-tag-gray">无期限</span></td>
<td><a class="g-action g-action-danger" href="javascript:;">报损</a> <a class="g-action" href="javascript:;">优先使用</a></td>
</tr>
</tbody>
</table>
</div>
<!-- 分页 -->
<div class="g-pagination" style="margin-top:16px;">
<span style="font-size:13px;color:var(--g-text-muted);">共 47 条</span>
<div style="display:flex;gap:6px;margin-left:auto;">
<button class="g-page-btn" disabled>&lt;</button>
<button class="g-page-btn active">1</button>
<button class="g-page-btn">2</button>
<button class="g-page-btn">3</button>
<button class="g-page-btn">4</button>
<button class="g-page-btn">5</button>
<button class="g-page-btn">&gt;</button>
</div>
</div>
<script>
if (typeof lucide !== 'undefined') { lucide.createIcons(); }
</script>

445
pages/inv-inout.html Normal file
View File

@@ -0,0 +1,445 @@
<!-- 出入库管理 — inv-inout.html -->
<style>
/* ---- page-private: ivi- prefix ---- */
.ivi-toolbar {
background:#fff; border-radius:var(--g-radius); padding:14px 18px;
display:flex; flex-wrap:wrap; gap:10px; align-items:center;
box-shadow:var(--g-shadow-sm); border:1px solid var(--g-border); margin-bottom:16px;
}
.ivi-toolbar select,
.ivi-toolbar input[type="date"] {
height:32px; padding:0 10px; border:1px solid #e5e7eb; border-radius:var(--g-radius-sm,6px);
font-size:13px; outline:none; transition:var(--g-transition); background:#fff; color:var(--g-text);
}
.ivi-toolbar select:focus,
.ivi-toolbar input[type="date"]:focus { border-color:var(--primary); box-shadow:0 0 0 3px color-mix(in srgb, var(--primary) 12%, transparent); }
.ivi-toolbar select { min-width:120px; }
.ivi-date-sep { color:var(--g-text-muted); font-size:13px; line-height:32px; }
.ivi-toolbar-right { margin-left:auto; display:flex; gap:8px; align-items:center; }
.ivi-panel {
background:#fff; border-radius:var(--g-radius); box-shadow:var(--g-shadow-sm);
padding:18px 20px;
}
.ivi-seg-wrap { margin-bottom:16px; }
.ivi-table-card {
background:#fff; border-radius:var(--g-radius); border:1px solid var(--g-border);
overflow:hidden;
}
.ivi-table-card .g-table th { white-space:nowrap; }
.ivi-table-card .g-table td { vertical-align:middle; font-size:13px; }
.ivi-mono { font-family:'SF Mono','Cascadia Code','Consolas',monospace; font-size:12px; color:var(--g-text-secondary); }
.ivi-amount { font-weight:600; white-space:nowrap; }
.ivi-amount.positive { color:var(--g-success); }
.ivi-amount.negative { color:var(--g-danger); }
.ivi-detail-cell { max-width:200px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; font-size:12px; color:var(--g-text-secondary); }
/* 新增入库抽屉 */
.ivi-drawer { width:520px; }
.ivi-material-row {
display:flex; gap:8px; align-items:center; margin-bottom:10px;
}
.ivi-material-row select,
.ivi-material-row input {
height:34px; padding:0 10px; border:1px solid #e5e7eb; border-radius:var(--g-radius-sm,6px);
font-size:13px; outline:none; transition:var(--g-transition); background:#fff; color:var(--g-text);
}
.ivi-material-row select:focus,
.ivi-material-row input:focus { border-color:var(--primary); box-shadow:0 0 0 3px color-mix(in srgb, var(--primary) 12%, transparent); }
.ivi-material-row select { flex:2; min-width:0; }
.ivi-material-row input { flex:1; min-width:0; }
.ivi-subtotal {
flex:0 0 70px; text-align:right; font-size:13px; font-weight:600; color:var(--g-text);
}
.ivi-del-btn {
flex-shrink:0; width:30px; height:30px; border:none; background:none;
color:var(--g-text-muted); cursor:pointer; border-radius:6px; display:flex; align-items:center; justify-content:center;
transition:var(--g-transition);
}
.ivi-del-btn:hover { background:color-mix(in srgb, var(--g-danger) 8%, #fff); color:var(--g-danger); }
.ivi-add-row {
width:100%; height:36px; border:1px dashed #d9d9d9; border-radius:var(--g-radius-sm,6px);
background:#fafafa; color:var(--primary); font-size:13px; cursor:pointer;
display:flex; align-items:center; justify-content:center; gap:4px;
transition:var(--g-transition);
}
.ivi-add-row:hover { border-color:var(--primary); background:var(--primary-light,hsl(212,100%,95%)); }
.ivi-material-header {
display:flex; gap:8px; align-items:center; margin-bottom:8px; font-size:12px; color:var(--g-text-muted); font-weight:500;
}
.ivi-material-header span:nth-child(1) { flex:2; }
.ivi-material-header span:nth-child(2) { flex:1; }
.ivi-material-header span:nth-child(3) { flex:1; }
.ivi-material-header span:nth-child(4) { flex:0 0 70px; text-align:right; }
.ivi-material-header span:nth-child(5) { flex:0 0 30px; }
</style>
<!-- 面板 -->
<div class="ivi-panel">
<!-- 分段控制器 -->
<div class="ivi-seg-wrap">
<div class="g-seg">
<div class="g-seg-item active" onclick="switchIviTab(this)" data-tab="in">入库记录</div>
<div class="g-seg-item" onclick="switchIviTab(this)" data-tab="out">出库记录</div>
<div class="g-seg-item" onclick="switchIviTab(this)" data-tab="check">盘点记录</div>
</div>
</div>
<!-- ========== Tab1: 入库记录 ========== -->
<div id="iviTabIn">
<div class="ivi-toolbar" style="box-shadow:none;border:none;padding:0 0 14px;">
<input type="date" value="2026-01-01">
<span class="ivi-date-sep"></span>
<input type="date" value="2026-02-12">
<select>
<option>全部供应商</option>
<option>鑫源食材配送</option>
<option>绿野农产品</option>
<option>海鲜直供站</option>
</select>
<div class="ivi-toolbar-right">
<button class="g-btn g-btn-primary" onclick="openIviDrawer()"><i data-lucide="plus" style="width:14px;height:14px;"></i> 新增入库</button>
</div>
</div>
<div class="ivi-table-card">
<table class="g-table">
<thead>
<tr>
<th>入库单号</th>
<th>日期</th>
<th>供应商</th>
<th>原料明细</th>
<th>总金额</th>
<th>经办人</th>
<th style="width:60px;">操作</th>
</tr>
</thead>
<tbody>
<tr>
<td class="ivi-mono">RK20260212001</td>
<td>2026-02-12</td>
<td>鑫源食材配送</td>
<td class="ivi-detail-cell">五花肉 10kg、牛腩 8kg <span style="color:var(--primary);cursor:pointer;">等3项</span></td>
<td class="ivi-amount">&yen;2,360.00</td>
<td>张经理</td>
<td><a class="g-action">查看</a></td>
</tr>
<tr>
<td class="ivi-mono">RK20260211002</td>
<td>2026-02-11</td>
<td>绿野农产品</td>
<td class="ivi-detail-cell">生菜 5kg、西红柿 8kg <span style="color:var(--primary);cursor:pointer;">等5项</span></td>
<td class="ivi-amount">&yen;680.00</td>
<td>李主管</td>
<td><a class="g-action">查看</a></td>
</tr>
<tr>
<td class="ivi-mono">RK20260210003</td>
<td>2026-02-10</td>
<td>海鲜直供站</td>
<td class="ivi-detail-cell">鲈鱼 15条、基围虾 5kg</td>
<td class="ivi-amount">&yen;1,850.00</td>
<td>张经理</td>
<td><a class="g-action">查看</a></td>
</tr>
<tr>
<td class="ivi-mono">RK20260209004</td>
<td>2026-02-09</td>
<td>鑫源食材配送</td>
<td class="ivi-detail-cell">鸡胸肉 12kg、排骨 10kg <span style="color:var(--primary);cursor:pointer;">等4项</span></td>
<td class="ivi-amount">&yen;3,120.00</td>
<td>王厨师</td>
<td><a class="g-action">查看</a></td>
</tr>
<tr>
<td class="ivi-mono">RK20260208005</td>
<td>2026-02-08</td>
<td>绿野农产品</td>
<td class="ivi-detail-cell">土豆 20kg、洋葱 10kg</td>
<td class="ivi-amount">&yen;420.00</td>
<td>李主管</td>
<td><a class="g-action">查看</a></td>
</tr>
<tr>
<td class="ivi-mono">RK20260207006</td>
<td>2026-02-07</td>
<td>海鲜直供站</td>
<td class="ivi-detail-cell">三文鱼 8kg、扇贝 3kg <span style="color:var(--primary);cursor:pointer;">等3项</span></td>
<td class="ivi-amount">&yen;2,980.00</td>
<td>张经理</td>
<td><a class="g-action">查看</a></td>
</tr>
</tbody>
</table>
<div class="g-pagination">
<span style="font-size:13px;color:var(--g-text-secondary);margin-right:12px;">共 6 条</span>
<button class="g-page-btn" disabled><i data-lucide="chevron-left" style="width:14px;height:14px;"></i></button>
<button class="g-page-btn active">1</button>
<button class="g-page-btn" disabled><i data-lucide="chevron-right" style="width:14px;height:14px;"></i></button>
</div>
</div>
</div>
<!-- ========== Tab2: 出库记录 ========== -->
<div id="iviTabOut" style="display:none;">
<div class="ivi-toolbar" style="box-shadow:none;border:none;padding:0 0 14px;">
<input type="date" value="2026-01-01">
<span class="ivi-date-sep"></span>
<input type="date" value="2026-02-12">
<select>
<option>全部类型</option>
<option>订单消耗</option>
<option>报损</option>
<option>调拨</option>
<option>其他</option>
</select>
</div>
<div class="ivi-table-card">
<table class="g-table">
<thead>
<tr>
<th>出库单号</th>
<th>日期</th>
<th>类型</th>
<th>原料明细</th>
<th>数量</th>
<th>经办人</th>
</tr>
</thead>
<tbody>
<tr>
<td class="ivi-mono">CK20260212001</td>
<td>2026-02-12</td>
<td><span class="g-tag g-tag-blue">订单消耗</span></td>
<td class="ivi-detail-cell">五花肉、生菜、西红柿</td>
<td>3.5kg / 2kg / 1.5kg</td>
<td>系统自动</td>
</tr>
<tr>
<td class="ivi-mono">CK20260212002</td>
<td>2026-02-12</td>
<td><span class="g-tag g-tag-blue">订单消耗</span></td>
<td class="ivi-detail-cell">鸡胸肉、土豆</td>
<td>2kg / 3kg</td>
<td>系统自动</td>
</tr>
<tr>
<td class="ivi-mono">CK20260211003</td>
<td>2026-02-11</td>
<td><span class="g-tag g-tag-red">报损</span></td>
<td class="ivi-detail-cell">基围虾(变质)</td>
<td>1.2kg</td>
<td>王厨师</td>
</tr>
<tr>
<td class="ivi-mono">CK20260211004</td>
<td>2026-02-11</td>
<td><span class="g-tag g-tag-blue">订单消耗</span></td>
<td class="ivi-detail-cell">牛腩、洋葱、排骨</td>
<td>4kg / 2kg / 3kg</td>
<td>系统自动</td>
</tr>
<tr>
<td class="ivi-mono">CK20260210005</td>
<td>2026-02-10</td>
<td><span class="g-tag g-tag-gray">调拨</span></td>
<td class="ivi-detail-cell">三文鱼(调至分店)</td>
<td>2kg</td>
<td>张经理</td>
</tr>
<tr>
<td class="ivi-mono">CK20260210006</td>
<td>2026-02-10</td>
<td><span class="g-tag g-tag-blue">订单消耗</span></td>
<td class="ivi-detail-cell">鲈鱼、生菜、土豆</td>
<td>3条 / 1.5kg / 2kg</td>
<td>系统自动</td>
</tr>
</tbody>
</table>
<div class="g-pagination">
<span style="font-size:13px;color:var(--g-text-secondary);margin-right:12px;">共 6 条</span>
<button class="g-page-btn" disabled><i data-lucide="chevron-left" style="width:14px;height:14px;"></i></button>
<button class="g-page-btn active">1</button>
<button class="g-page-btn" disabled><i data-lucide="chevron-right" style="width:14px;height:14px;"></i></button>
</div>
</div>
</div>
<!-- ========== Tab3: 盘点记录 ========== -->
<div id="iviTabCheck" style="display:none;">
<div class="ivi-toolbar" style="box-shadow:none;border:none;padding:0 0 14px;">
<input type="date" value="2026-01-01">
<span class="ivi-date-sep"></span>
<input type="date" value="2026-02-12">
<div class="ivi-toolbar-right">
<button class="g-btn g-btn-primary"><i data-lucide="clipboard-list" style="width:14px;height:14px;"></i> 新建盘点</button>
</div>
</div>
<div class="ivi-table-card">
<table class="g-table">
<thead>
<tr>
<th>盘点单号</th>
<th>盘点日期</th>
<th>盘点范围</th>
<th>差异项数</th>
<th>盈亏金额</th>
<th>状态</th>
<th style="width:60px;">操作</th>
</tr>
</thead>
<tbody>
<tr>
<td class="ivi-mono">PD20260212001</td>
<td>2026-02-12</td>
<td>全部</td>
<td>3</td>
<td class="ivi-amount negative">-&yen;186.00</td>
<td><span class="g-tag g-tag-orange">进行中</span></td>
<td><a class="g-action">查看</a></td>
</tr>
<tr>
<td class="ivi-mono">PD20260205002</td>
<td>2026-02-05</td>
<td>肉类</td>
<td>1</td>
<td class="ivi-amount negative">-&yen;95.00</td>
<td><span class="g-tag g-tag-green">已完成</span></td>
<td><a class="g-action">查看</a></td>
</tr>
<tr>
<td class="ivi-mono">PD20260130003</td>
<td>2026-01-30</td>
<td>蔬菜</td>
<td>2</td>
<td class="ivi-amount positive">+&yen;32.00</td>
<td><span class="g-tag g-tag-green">已完成</span></td>
<td><a class="g-action">查看</a></td>
</tr>
<tr>
<td class="ivi-mono">PD20260120004</td>
<td>2026-01-20</td>
<td>全部</td>
<td>5</td>
<td class="ivi-amount negative">-&yen;420.00</td>
<td><span class="g-tag g-tag-green">已完成</span></td>
<td><a class="g-action">查看</a></td>
</tr>
</tbody>
</table>
<div class="g-pagination">
<span style="font-size:13px;color:var(--g-text-secondary);margin-right:12px;">共 4 条</span>
<button class="g-page-btn" disabled><i data-lucide="chevron-left" style="width:14px;height:14px;"></i></button>
<button class="g-page-btn active">1</button>
<button class="g-page-btn" disabled><i data-lucide="chevron-right" style="width:14px;height:14px;"></i></button>
</div>
</div>
</div>
</div>
<!-- ========== 新增入库抽屉 ========== -->
<div class="g-drawer-mask" id="iviDrawerMask" onclick="closeIviDrawer()"></div>
<div class="g-drawer ivi-drawer" id="iviDrawer">
<div class="g-drawer-hd">
<div class="g-drawer-title">新增入库单</div>
<button class="g-drawer-close" onclick="closeIviDrawer()"><i data-lucide="x" style="width:18px;height:18px;"></i></button>
</div>
<div class="g-drawer-bd">
<!-- 供应商 -->
<div class="g-form-group">
<label class="g-form-label required">供应商</label>
<select class="g-select" style="width:100%;">
<option value="">请选择供应商</option>
<option>鑫源食材配送</option>
<option>绿野农产品</option>
<option>海鲜直供站</option>
</select>
</div>
<!-- 入库日期 -->
<div class="g-form-group">
<label class="g-form-label">入库日期</label>
<input class="g-input" type="date" value="2026-02-12">
</div>
<!-- 原料明细 -->
<div class="g-form-group">
<label class="g-form-label required">原料明细</label>
<div class="ivi-material-header">
<span>原料名称</span>
<span>数量</span>
<span>单价(元)</span>
<span>小计</span>
<span></span>
</div>
<div id="iviMaterialList">
<div class="ivi-material-row">
<select><option>五花肉</option><option>牛腩</option><option>鸡胸肉</option><option>排骨</option><option>生菜</option><option>西红柿</option><option>土豆</option><option>鲈鱼</option><option>基围虾</option></select>
<input type="number" placeholder="数量" value="10">
<input type="number" placeholder="单价" value="32">
<div class="ivi-subtotal">&yen;320</div>
<button class="ivi-del-btn" onclick="this.parentElement.remove()"><i data-lucide="trash-2" style="width:14px;height:14px;"></i></button>
</div>
<div class="ivi-material-row">
<select><option>牛腩</option><option>五花肉</option><option>鸡胸肉</option><option>排骨</option><option>生菜</option><option>西红柿</option><option>土豆</option><option>鲈鱼</option><option>基围虾</option></select>
<input type="number" placeholder="数量" value="8">
<input type="number" placeholder="单价" value="58">
<div class="ivi-subtotal">&yen;464</div>
<button class="ivi-del-btn" onclick="this.parentElement.remove()"><i data-lucide="trash-2" style="width:14px;height:14px;"></i></button>
</div>
</div>
<button class="ivi-add-row" onclick="addIviMaterialRow()">
<i data-lucide="plus" style="width:14px;height:14px;"></i> 添加原料
</button>
</div>
<!-- 备注 -->
<div class="g-form-group">
<label class="g-form-label">备注</label>
<textarea class="g-textarea" rows="3" placeholder="请输入备注信息(选填)"></textarea>
</div>
</div>
<div class="g-drawer-ft">
<button class="g-btn" onclick="closeIviDrawer()">取消</button>
<button class="g-btn g-btn-primary">确认入库</button>
</div>
</div>
<script>
/* Tab 切换 */
function switchIviTab(el) {
document.querySelectorAll('.g-seg-item').forEach(function(s){ s.classList.remove('active'); });
el.classList.add('active');
var tab = el.getAttribute('data-tab');
document.getElementById('iviTabIn').style.display = tab === 'in' ? '' : 'none';
document.getElementById('iviTabOut').style.display = tab === 'out' ? '' : 'none';
document.getElementById('iviTabCheck').style.display = tab === 'check' ? '' : 'none';
}
/* 抽屉开关 */
function openIviDrawer() {
document.getElementById('iviDrawerMask').classList.add('open');
document.getElementById('iviDrawer').classList.add('open');
}
function closeIviDrawer() {
document.getElementById('iviDrawerMask').classList.remove('open');
document.getElementById('iviDrawer').classList.remove('open');
}
/* 添加原料行 */
function addIviMaterialRow() {
var html = '<div class="ivi-material-row">'
+ '<select><option value="">请选择原料</option><option>五花肉</option><option>牛腩</option><option>鸡胸肉</option><option>排骨</option><option>生菜</option><option>西红柿</option><option>土豆</option><option>鲈鱼</option><option>基围虾</option></select>'
+ '<input type="number" placeholder="数量">'
+ '<input type="number" placeholder="单价">'
+ '<div class="ivi-subtotal">&yen;0</div>'
+ '<button class="ivi-del-btn" onclick="this.parentElement.remove()"><i data-lucide="trash-2" style="width:14px;height:14px;"></i></button>'
+ '</div>';
document.getElementById('iviMaterialList').insertAdjacentHTML('beforeend', html);
if (typeof lucide !== 'undefined') { lucide.createIcons(); }
}
/* 初始化图标 */
if (typeof lucide !== 'undefined') { lucide.createIcons(); }
</script>

276
pages/inv-overview.html Normal file
View File

@@ -0,0 +1,276 @@
<!-- 库存总览 -->
<style>
.ivo-page { display:flex; flex-direction:column; gap:16px; }
/* 工具栏 */
.ivo-toolbar { display:flex; align-items:center; gap:10px; flex-wrap:wrap; }
.ivo-toolbar-right { margin-left:auto; display:flex; gap:8px; }
.ivo-search { width:220px; }
/* 统计卡片 */
.ivo-stats { display:grid; grid-template-columns:repeat(4,1fr); gap:14px; }
.ivo-stat { background:#fff; border-radius:var(--g-radius); border:1px solid var(--g-border); padding:18px 20px; display:flex; align-items:center; gap:14px; transition:var(--g-transition); cursor:default; }
.ivo-stat:hover { box-shadow:var(--g-shadow-md); transform:translateY(-1px); }
.ivo-stat-icon { width:42px; height:42px; border-radius:var(--g-radius); display:flex; align-items:center; justify-content:center; flex-shrink:0; }
.ivo-stat-icon.blue { background:hsl(212,100%,95%); color:hsl(212,100%,45%); }
.ivo-stat-icon.orange { background:#fff7e6; color:var(--g-warning); }
.ivo-stat-icon.red { background:#fff1f0; color:var(--g-danger); }
.ivo-stat-icon.green { background:#f0fdf4; color:var(--g-success); }
.ivo-stat-info { display:flex; flex-direction:column; gap:4px; }
.ivo-stat-label { font-size:13px; color:var(--g-text-secondary); }
.ivo-stat-value { font-size:24px; font-weight:700; color:var(--g-text); letter-spacing:-0.5px; }
.ivo-stat-value.orange { color:var(--g-warning); }
.ivo-stat-value.red { color:var(--g-danger); }
/* 表格容器 */
.ivo-table-wrap { background:#fff; border-radius:var(--g-radius); border:1px solid var(--g-border); overflow:hidden; }
.ivo-table-inner { overflow-x:auto; }
/* 行状态背景 */
.ivo-row-warn td { background:#fffbe6 !important; }
.ivo-row-danger td { background:#fff1f0 !important; }
/* 分页栏 */
.ivo-pager-bar { display:flex; align-items:center; justify-content:space-between; padding:12px 16px; border-top:1px solid var(--g-border); font-size:13px; color:var(--g-text-secondary); }
.ivo-pager-bar .g-select { width:auto; height:28px; font-size:12px; }
</style>
<div class="ivo-page">
<!-- 工具栏 -->
<div class="ivo-toolbar">
<input class="g-input ivo-search" placeholder="搜索原料名称…" />
<select class="g-select" style="width:140px;">
<option>全部分类</option>
<option>肉类</option>
<option>蔬菜</option>
<option>调料</option>
<option>饮品原料</option>
<option>包装耗材</option>
</select>
<select class="g-select" style="width:120px;">
<option>全部状态</option>
<option>正常</option>
<option>低库存</option>
<option>缺货</option>
</select>
<div class="ivo-toolbar-right">
<button class="g-btn" id="ivoBtnPurchase" onclick="ivoGenPurchase()">
<i data-lucide="clipboard-list" style="width:14px;height:14px;"></i> 一键生成采购单
</button>
<button class="g-btn g-btn-primary" onclick="openIvoDrawer()">
<i data-lucide="plus" style="width:14px;height:14px;"></i> 新增原料
</button>
</div>
</div>
<!-- 统计卡片 -->
<div class="ivo-stats">
<div class="ivo-stat">
<div class="ivo-stat-icon blue"><i data-lucide="package" style="width:20px;height:20px;"></i></div>
<div class="ivo-stat-info">
<span class="ivo-stat-label">原料 SKU 数</span>
<span class="ivo-stat-value">42</span>
</div>
</div>
<div class="ivo-stat">
<div class="ivo-stat-icon orange"><i data-lucide="alert-triangle" style="width:20px;height:20px;"></i></div>
<div class="ivo-stat-info">
<span class="ivo-stat-label">低库存预警</span>
<span class="ivo-stat-value orange">8</span>
</div>
</div>
<div class="ivo-stat">
<div class="ivo-stat-icon red"><i data-lucide="x-circle" style="width:20px;height:20px;"></i></div>
<div class="ivo-stat-info">
<span class="ivo-stat-label">缺货原料</span>
<span class="ivo-stat-value red">2</span>
</div>
</div>
<div class="ivo-stat">
<div class="ivo-stat-icon green"><i data-lucide="wallet" style="width:20px;height:20px;"></i></div>
<div class="ivo-stat-info">
<span class="ivo-stat-label">库存总值</span>
<span class="ivo-stat-value">¥28,600</span>
</div>
</div>
</div>
<!-- 原料列表 -->
<div class="ivo-table-wrap">
<div class="ivo-table-inner">
<table class="g-table">
<thead>
<tr>
<th>原料名称</th><th>分类</th><th>单位</th><th style="text-align:right;">当前库存</th><th style="text-align:right;">安全库存</th><th style="text-align:right;">单价(元)</th><th style="text-align:right;">库存价值</th><th>状态</th><th style="width:140px;">操作</th>
</tr>
</thead>
<tbody>
<tr>
<td style="font-weight:500;">猪肉</td><td>肉类</td><td>kg</td><td style="text-align:right;">45</td><td style="text-align:right;">20</td><td style="text-align:right;">32.00</td><td style="text-align:right;">¥1,440</td><td><span class="g-tag g-tag-green">正常</span></td>
<td><a class="g-action">编辑</a> <a class="g-action" style="margin-left:8px;">入库</a> <a class="g-action g-action-danger" style="margin-left:8px;">删除</a></td>
</tr>
<tr class="ivo-row-warn">
<td style="font-weight:500;">鸡胸肉</td><td>肉类</td><td>kg</td><td style="text-align:right;">8</td><td style="text-align:right;">15</td><td style="text-align:right;">28.00</td><td style="text-align:right;">¥224</td><td><span class="g-tag g-tag-orange">低库存</span></td>
<td><a class="g-action">编辑</a> <a class="g-action" style="margin-left:8px;">入库</a> <a class="g-action g-action-danger" style="margin-left:8px;">删除</a></td>
</tr>
<tr class="ivo-row-danger">
<td style="font-weight:500;">牛腩</td><td>肉类</td><td>kg</td><td style="text-align:right;">0</td><td style="text-align:right;">10</td><td style="text-align:right;">65.00</td><td style="text-align:right;">¥0</td><td><span class="g-tag g-tag-red">缺货</span></td>
<td><a class="g-action">编辑</a> <a class="g-action" style="margin-left:8px;">入库</a> <a class="g-action g-action-danger" style="margin-left:8px;">删除</a></td>
</tr>
<tr>
<td style="font-weight:500;">大白菜</td><td>蔬菜</td><td>kg</td><td style="text-align:right;">30</td><td style="text-align:right;">10</td><td style="text-align:right;">4.00</td><td style="text-align:right;">¥120</td><td><span class="g-tag g-tag-green">正常</span></td>
<td><a class="g-action">编辑</a> <a class="g-action" style="margin-left:8px;">入库</a> <a class="g-action g-action-danger" style="margin-left:8px;">删除</a></td>
</tr>
<tr>
<td style="font-weight:500;">西红柿</td><td>蔬菜</td><td>kg</td><td style="text-align:right;">12</td><td style="text-align:right;">8</td><td style="text-align:right;">6.00</td><td style="text-align:right;">¥72</td><td><span class="g-tag g-tag-green">正常</span></td>
<td><a class="g-action">编辑</a> <a class="g-action" style="margin-left:8px;">入库</a> <a class="g-action g-action-danger" style="margin-left:8px;">删除</a></td>
</tr>
<tr class="ivo-row-warn">
<td style="font-weight:500;">生菜</td><td>蔬菜</td><td>kg</td><td style="text-align:right;">3</td><td style="text-align:right;">5</td><td style="text-align:right;">8.00</td><td style="text-align:right;">¥24</td><td><span class="g-tag g-tag-orange">低库存</span></td>
<td><a class="g-action">编辑</a> <a class="g-action" style="margin-left:8px;">入库</a> <a class="g-action g-action-danger" style="margin-left:8px;">删除</a></td>
</tr>
<tr>
<td style="font-weight:500;">酱油</td><td>调料</td><td></td><td style="text-align:right;">25</td><td style="text-align:right;">10</td><td style="text-align:right;">12.00</td><td style="text-align:right;">¥300</td><td><span class="g-tag g-tag-green">正常</span></td>
<td><a class="g-action">编辑</a> <a class="g-action" style="margin-left:8px;">入库</a> <a class="g-action g-action-danger" style="margin-left:8px;">删除</a></td>
</tr>
<tr class="ivo-row-warn">
<td style="font-weight:500;">食用油</td><td>调料</td><td></td><td style="text-align:right;">4</td><td style="text-align:right;">5</td><td style="text-align:right;">58.00</td><td style="text-align:right;">¥232</td><td><span class="g-tag g-tag-orange">低库存</span></td>
<td><a class="g-action">编辑</a> <a class="g-action" style="margin-left:8px;">入库</a> <a class="g-action g-action-danger" style="margin-left:8px;">删除</a></td>
</tr>
<tr class="ivo-row-warn">
<td style="font-weight:500;">咖啡豆</td><td>饮品原料</td><td>kg</td><td style="text-align:right;">2</td><td style="text-align:right;">5</td><td style="text-align:right;">120.00</td><td style="text-align:right;">¥240</td><td><span class="g-tag g-tag-orange">低库存</span></td>
<td><a class="g-action">编辑</a> <a class="g-action" style="margin-left:8px;">入库</a> <a class="g-action g-action-danger" style="margin-left:8px;">删除</a></td>
</tr>
<tr class="ivo-row-warn">
<td style="font-weight:500;">牛奶</td><td>饮品原料</td><td>L</td><td style="text-align:right;">15</td><td style="text-align:right;">20</td><td style="text-align:right;">8.00</td><td style="text-align:right;">¥120</td><td><span class="g-tag g-tag-orange">低库存</span></td>
<td><a class="g-action">编辑</a> <a class="g-action" style="margin-left:8px;">入库</a> <a class="g-action g-action-danger" style="margin-left:8px;">删除</a></td>
</tr>
<tr>
<td style="font-weight:500;">外卖盒</td><td>包装耗材</td><td></td><td style="text-align:right;">500</td><td style="text-align:right;">200</td><td style="text-align:right;">0.80</td><td style="text-align:right;">¥400</td><td><span class="g-tag g-tag-green">正常</span></td>
<td><a class="g-action">编辑</a> <a class="g-action" style="margin-left:8px;">入库</a> <a class="g-action g-action-danger" style="margin-left:8px;">删除</a></td>
</tr>
<tr class="ivo-row-warn">
<td style="font-weight:500;">一次性筷子</td><td>包装耗材</td><td></td><td style="text-align:right;">80</td><td style="text-align:right;">100</td><td style="text-align:right;">0.30</td><td style="text-align:right;">¥24</td><td><span class="g-tag g-tag-orange">低库存</span></td>
<td><a class="g-action">编辑</a> <a class="g-action" style="margin-left:8px;">入库</a> <a class="g-action g-action-danger" style="margin-left:8px;">删除</a></td>
</tr>
</tbody>
</table>
</div>
<!-- 分页 -->
<div class="ivo-pager-bar">
<span>共 42 条</span>
<div style="display:flex;align-items:center;gap:8px;">
<select class="g-select" style="width:90px;height:28px;font-size:12px;">
<option>20 条/页</option>
<option>50 条/页</option>
<option>100 条/页</option>
</select>
<div class="g-pagination" style="margin-top:0;">
<button class="g-page-btn" disabled>&lt;</button>
<button class="g-page-btn active">1</button>
<button class="g-page-btn">2</button>
<button class="g-page-btn">3</button>
<button class="g-page-btn">&gt;</button>
</div>
</div>
</div>
</div>
</div>
<!-- 新增原料抽屉 -->
<div class="g-drawer-mask" id="ivoDrawerMask" onclick="closeIvoDrawer()"></div>
<div class="g-drawer" id="ivoDrawer" style="width:480px;">
<div class="g-drawer-hd">
<span class="g-drawer-title">新增原料</span>
<button class="g-drawer-close" onclick="closeIvoDrawer()"><i data-lucide="x" style="width:18px;height:18px;"></i></button>
</div>
<div class="g-drawer-bd">
<div class="g-form-group">
<label class="g-form-label required">原料名称</label>
<input class="g-input" placeholder="请输入原料名称" />
</div>
<div class="g-form-group">
<label class="g-form-label required">分类</label>
<select class="g-select">
<option value="">请选择分类</option>
<option>肉类</option>
<option>蔬菜</option>
<option>调料</option>
<option>饮品原料</option>
<option>包装耗材</option>
</select>
</div>
<div class="g-form-group">
<label class="g-form-label required">单位</label>
<select class="g-select">
<option value="">请选择单位</option>
<option>kg</option>
<option>g</option>
<option>L</option>
<option>ml</option>
<option></option>
<option></option>
<option></option>
<option></option>
<option></option>
</select>
</div>
<div class="g-form-group">
<label class="g-form-label required">安全库存</label>
<input class="g-input" type="number" placeholder="低于此数量将触发预警" />
</div>
<div class="g-form-group">
<label class="g-form-label required">单价</label>
<input class="g-input" type="number" placeholder="如29.90" />
</div>
<div class="g-form-group">
<label class="g-form-label">供应商</label>
<select class="g-select">
<option value="">请选择供应商</option>
<option>鲜达供应链</option>
<option>万家食材</option>
<option>优品冷链</option>
</select>
</div>
<div class="g-form-group">
<label class="g-form-label">备注</label>
<textarea class="g-textarea" rows="3" placeholder="补充说明,如存储要求、保质期等"></textarea>
</div>
</div>
<div class="g-drawer-ft">
<button class="g-btn" onclick="closeIvoDrawer()">取消</button>
<button class="g-btn g-btn-primary" onclick="closeIvoDrawer()">确认添加</button>
</div>
</div>
<script>
/* 抽屉开关 */
function openIvoDrawer() {
document.getElementById('ivoDrawerMask').classList.add('open');
document.getElementById('ivoDrawer').classList.add('open');
}
function closeIvoDrawer() {
document.getElementById('ivoDrawerMask').classList.remove('open');
document.getElementById('ivoDrawer').classList.remove('open');
}
/* 一键生成采购单 */
function ivoGenPurchase() {
var btn = document.getElementById('ivoBtnPurchase');
var orig = btn.innerHTML;
btn.innerHTML = '<i data-lucide="check" style="width:14px;height:14px;"></i> 已生成 3 条采购建议';
btn.style.borderColor = 'var(--g-success)';
btn.style.color = 'var(--g-success)';
if (typeof lucide !== 'undefined') { lucide.createIcons(); }
setTimeout(function() {
btn.innerHTML = orig;
btn.style.borderColor = '';
btn.style.color = '';
if (typeof lucide !== 'undefined') { lucide.createIcons(); }
}, 2000);
}
/* 初始化图标 */
if (typeof lucide !== 'undefined') { lucide.createIcons(); }
</script>

383
pages/inv-purchase.html Normal file
View File

@@ -0,0 +1,383 @@
<!-- 采购管理页 -->
<style>
.ivp-page { display:flex; flex-direction:column; gap:16px; font-size:13px; color:var(--g-text); }
/* 统计卡片 */
.ivp-stats { display:grid; grid-template-columns:repeat(3,1fr); gap:14px; }
.ivp-stat { background:#fff; border-radius:var(--g-radius); border:1px solid var(--g-border); padding:18px 20px; display:flex; align-items:center; gap:14px; transition:var(--g-transition); }
.ivp-stat:hover { box-shadow:var(--g-shadow-md); transform:translateY(-1px); }
.ivp-stat-icon { width:40px; height:40px; border-radius:var(--g-radius); display:flex; align-items:center; justify-content:center; flex-shrink:0; }
.ivp-stat-icon.blue { background:hsl(212,100%,95%); color:var(--primary); }
.ivp-stat-icon.orange { background:#fff7e6; color:var(--g-warning); }
.ivp-stat-icon.green { background:#f0fdf4; color:var(--g-success); }
.ivp-stat-label { font-size:12px; color:var(--g-text-muted); margin-bottom:4px; }
.ivp-stat-value { font-size:22px; font-weight:700; color:var(--g-text); }
/* 工具栏 */
.ivp-toolbar { display:flex; align-items:center; gap:12px; background:#fff; border-radius:10px; padding:12px 16px; box-shadow:var(--g-shadow-sm); flex-wrap:wrap; }
.ivp-toolbar .ivp-right { margin-left:auto; display:flex; gap:8px; }
/* 表格容器 */
.ivp-table-wrap { background:#fff; border-radius:10px; box-shadow:var(--g-shadow-sm); overflow:hidden; }
/* 供应商卡片网格 */
.ivp-vendor-grid { display:grid; grid-template-columns:repeat(2,1fr); gap:14px; }
.ivp-vendor-card { background:#fff; border-radius:var(--g-radius); border:1px solid var(--g-border); padding:20px; transition:var(--g-transition); }
.ivp-vendor-card:hover { box-shadow:var(--g-shadow-md); transform:translateY(-1px); }
.ivp-vendor-hd { display:flex; align-items:center; justify-content:space-between; margin-bottom:12px; }
.ivp-vendor-name { font-size:15px; font-weight:600; color:var(--g-text); }
.ivp-vendor-info { display:grid; grid-template-columns:1fr 1fr; gap:8px 20px; font-size:13px; color:var(--g-text-secondary); margin-bottom:12px; }
.ivp-vendor-info span { display:flex; align-items:center; gap:6px; }
.ivp-vendor-info i { width:14px; height:14px; color:var(--g-text-muted); }
.ivp-vendor-ft { display:flex; align-items:center; justify-content:space-between; padding-top:12px; border-top:1px solid var(--g-border); }
/* 星星评分 */
.ivp-stars { display:flex; gap:2px; }
.ivp-star { width:16px; height:16px; position:relative; }
.ivp-star-bg { color:#e5e7eb; }
.ivp-star-fg { color:#facc15; position:absolute; top:0; left:0; overflow:hidden; }
/* 采购明细行 */
.ivp-detail-row { display:flex; gap:8px; align-items:center; margin-bottom:10px; }
.ivp-detail-row .g-select, .ivp-detail-row .g-input { flex:1; }
.ivp-detail-row .ivp-qty { width:90px; flex:none; }
.ivp-detail-row .ivp-price { width:100px; flex:none; }
.ivp-del-btn { width:32px; height:32px; border:none; background:none; color:var(--g-text-muted); cursor:pointer; border-radius:var(--g-radius-sm); transition:var(--g-transition); display:flex; align-items:center; justify-content:center; flex-shrink:0; }
.ivp-del-btn:hover { color:var(--g-danger); background:#fef2f2; }
.ivp-add-row { display:flex; align-items:center; gap:6px; color:var(--primary); font-size:13px; cursor:pointer; padding:6px 0; border:none; background:none; transition:var(--g-transition); }
.ivp-add-row:hover { opacity:.8; }
</style>
<div class="ivp-page">
<!-- 分段切换 -->
<div class="g-seg" style="width:240px;">
<div class="g-seg-item active" onclick="switchIvpTab(this,'orders')">采购单</div>
<div class="g-seg-item" onclick="switchIvpTab(this,'vendors')">供应商</div>
</div>
<!-- ========== Tab1: 采购单 ========== -->
<div id="ivpTabOrders">
<!-- 统计卡片 -->
<div class="ivp-stats" style="margin-bottom:16px;">
<div class="ivp-stat">
<div class="ivp-stat-icon blue"><i data-lucide="wallet" style="width:20px;height:20px;"></i></div>
<div><div class="ivp-stat-label">本月采购额</div><div class="ivp-stat-value" style="color:var(--primary);">&#165;18,500</div></div>
</div>
<div class="ivp-stat">
<div class="ivp-stat-icon orange"><i data-lucide="clock" style="width:20px;height:20px;"></i></div>
<div><div class="ivp-stat-label">待审核</div><div class="ivp-stat-value" style="color:var(--g-warning);">3<span style="font-size:13px;font-weight:400;color:var(--g-text-muted);margin-left:4px;"></span></div></div>
</div>
<div class="ivp-stat">
<div class="ivp-stat-icon green"><i data-lucide="truck" style="width:20px;height:20px;"></i></div>
<div><div class="ivp-stat-label">待到货</div><div class="ivp-stat-value" style="color:var(--g-success);">2<span style="font-size:13px;font-weight:400;color:var(--g-text-muted);margin-left:4px;"></span></div></div>
</div>
</div>
<!-- 工具栏 -->
<div class="ivp-toolbar" style="margin-bottom:16px;">
<input class="g-input" type="date" value="2026-02-01" style="width:140px;">
<span style="color:var(--g-text-muted);"></span>
<input class="g-input" type="date" value="2026-02-12" style="width:140px;">
<select class="g-select" style="width:130px;">
<option>全部状态</option>
<option>待审核</option>
<option>已下单</option>
<option>已到货</option>
<option>已取消</option>
</select>
<div class="ivp-right">
<button class="g-btn g-btn-primary" onclick="openIvpDrawer()"><i data-lucide="plus" style="width:15px;height:15px;"></i> 新建采购单</button>
</div>
</div>
<!-- 表格 -->
<div class="ivp-table-wrap">
<table class="g-table">
<thead>
<tr>
<th>采购单号</th>
<th>创建日期</th>
<th>供应商</th>
<th>采购项数</th>
<th>总金额</th>
<th>状态</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr>
<td style="font-weight:500;">PO20260201001</td>
<td>2026-02-01</td>
<td>鑫源肉业</td>
<td>5</td>
<td style="font-weight:600;">&#165;4,200.00</td>
<td><span class="g-tag g-tag-orange">待审核</span></td>
<td><a class="g-action">查看</a><a class="g-action">确认到货</a><a class="g-action-danger">取消</a></td>
</tr>
<tr>
<td style="font-weight:500;">PO20260203002</td>
<td>2026-02-03</td>
<td>绿田蔬菜批发</td>
<td>8</td>
<td style="font-weight:600;">&#165;1,860.00</td>
<td><span class="g-tag g-tag-blue">已下单</span></td>
<td><a class="g-action">查看</a><a class="g-action">确认到货</a><a class="g-action-danger">取消</a></td>
</tr>
<tr>
<td style="font-weight:500;">PO20260205003</td>
<td>2026-02-05</td>
<td>味达调料商行</td>
<td>3</td>
<td style="font-weight:600;">&#165;920.00</td>
<td><span class="g-tag g-tag-green">已到货</span></td>
<td><a class="g-action">查看</a></td>
</tr>
<tr>
<td style="font-weight:500;">PO20260206004</td>
<td>2026-02-06</td>
<td>鑫源肉业</td>
<td>4</td>
<td style="font-weight:600;">&#165;5,300.00</td>
<td><span class="g-tag g-tag-orange">待审核</span></td>
<td><a class="g-action">查看</a><a class="g-action">确认到货</a><a class="g-action-danger">取消</a></td>
</tr>
<tr>
<td style="font-weight:500;">PO20260208005</td>
<td>2026-02-08</td>
<td>优品包装</td>
<td>6</td>
<td style="font-weight:600;">&#165;3,480.00</td>
<td><span class="g-tag g-tag-blue">已下单</span></td>
<td><a class="g-action">查看</a><a class="g-action">确认到货</a><a class="g-action-danger">取消</a></td>
</tr>
<tr>
<td style="font-weight:500;">PO20260210006</td>
<td>2026-02-10</td>
<td>绿田蔬菜批发</td>
<td>2</td>
<td style="font-weight:600;">&#165;640.00</td>
<td><span class="g-tag g-tag-gray">已取消</span></td>
<td><a class="g-action">查看</a></td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- ========== Tab2: 供应商 ========== -->
<div id="ivpTabVendors" style="display:none;">
<!-- 工具栏 -->
<div class="ivp-toolbar" style="margin-bottom:16px;">
<div style="position:relative;">
<i data-lucide="search" style="width:15px;height:15px;position:absolute;left:10px;top:50%;transform:translateY(-50%);color:var(--g-text-muted);"></i>
<input class="g-input" placeholder="搜索供应商名称" style="width:200px;padding-left:32px;">
</div>
<select class="g-select" style="width:130px;">
<option>全部分类</option>
<option>肉类</option>
<option>蔬菜</option>
<option>调料</option>
<option>饮品</option>
<option>包装</option>
</select>
<div class="ivp-right">
<button class="g-btn g-btn-primary"><i data-lucide="plus" style="width:15px;height:15px;"></i> 新增供应商</button>
</div>
</div>
<!-- 供应商卡片 -->
<div class="ivp-vendor-grid">
<!-- 鑫源肉业 -->
<div class="ivp-vendor-card">
<div class="ivp-vendor-hd">
<div class="ivp-vendor-name">鑫源肉业</div>
<span class="g-tag g-tag-red">肉类</span>
</div>
<div class="ivp-vendor-info">
<span><i data-lucide="user"></i> 张经理</span>
<span><i data-lucide="phone"></i> 138-0001-2345</span>
<span><i data-lucide="calendar"></i> 合作 2 年</span>
<span><i data-lucide="wallet"></i> 累计 &#165;86,000</span>
</div>
<div class="ivp-vendor-ft">
<div class="ivp-stars" data-score="4.5"></div>
<div><a class="g-action">编辑</a><a class="g-action">查看采购记录</a></div>
</div>
</div>
<!-- 绿田蔬菜批发 -->
<div class="ivp-vendor-card">
<div class="ivp-vendor-hd">
<div class="ivp-vendor-name">绿田蔬菜批发</div>
<span class="g-tag g-tag-green">蔬菜</span>
</div>
<div class="ivp-vendor-info">
<span><i data-lucide="user"></i> 李老板</span>
<span><i data-lucide="phone"></i> 139-8877-6543</span>
<span><i data-lucide="calendar"></i> 合作 3 年</span>
<span><i data-lucide="wallet"></i> 累计 &#165;124,500</span>
</div>
<div class="ivp-vendor-ft">
<div class="ivp-stars" data-score="4.8"></div>
<div><a class="g-action">编辑</a><a class="g-action">查看采购记录</a></div>
</div>
</div>
<!-- 味达调料商行 -->
<div class="ivp-vendor-card">
<div class="ivp-vendor-hd">
<div class="ivp-vendor-name">味达调料商行</div>
<span class="g-tag g-tag-orange">调料</span>
</div>
<div class="ivp-vendor-info">
<span><i data-lucide="user"></i> 王师傅</span>
<span><i data-lucide="phone"></i> 136-5544-3210</span>
<span><i data-lucide="calendar"></i> 合作 1 年</span>
<span><i data-lucide="wallet"></i> 累计 &#165;32,800</span>
</div>
<div class="ivp-vendor-ft">
<div class="ivp-stars" data-score="4.2"></div>
<div><a class="g-action">编辑</a><a class="g-action">查看采购记录</a></div>
</div>
</div>
<!-- 优品包装 -->
<div class="ivp-vendor-card">
<div class="ivp-vendor-hd">
<div class="ivp-vendor-name">优品包装</div>
<span class="g-tag g-tag-gray">包装耗材</span>
</div>
<div class="ivp-vendor-info">
<span><i data-lucide="user"></i> 陈总</span>
<span><i data-lucide="phone"></i> 137-2233-4455</span>
<span><i data-lucide="calendar"></i> 合作 1.5 年</span>
<span><i data-lucide="wallet"></i> 累计 &#165;18,200</span>
</div>
<div class="ivp-vendor-ft">
<div class="ivp-stars" data-score="4.0"></div>
<div><a class="g-action">编辑</a><a class="g-action">查看采购记录</a></div>
</div>
</div>
</div>
</div>
</div>
<!-- ========== 新建采购单抽屉 ========== -->
<div class="g-drawer-mask" id="ivpDrawerMask" onclick="closeIvpDrawer()"></div>
<div class="g-drawer" id="ivpDrawer" style="width:520px;">
<div class="g-drawer-hd">
<div class="g-drawer-title">新建采购单</div>
<button class="g-drawer-close" onclick="closeIvpDrawer()"><i data-lucide="x" style="width:18px;height:18px;"></i></button>
</div>
<div class="g-drawer-bd">
<div class="g-form-group">
<label class="g-form-label required">供应商</label>
<select class="g-select">
<option value="">请选择供应商</option>
<option>鑫源肉业</option>
<option>绿田蔬菜批发</option>
<option>味达调料商行</option>
<option>优品包装</option>
</select>
</div>
<div class="g-form-group">
<label class="g-form-label">预计到货日期</label>
<input class="g-input" type="date" value="2026-02-15">
</div>
<div class="g-form-group">
<label class="g-form-label required">采购明细</label>
<div id="ivpDetailList">
<div class="ivp-detail-row">
<select class="g-select"><option>鸡胸肉</option><option>牛腩</option><option>五花肉</option><option>牛奶</option><option>咖啡豆</option><option>生菜</option><option>番茄</option></select>
<input class="g-input ivp-qty" type="number" value="20" placeholder="数量">
<span style="color:var(--g-text-muted);flex-shrink:0;">kg</span>
<input class="g-input ivp-price" type="number" value="35.00" placeholder="单价">
<button class="ivp-del-btn" onclick="removeIvpRow(this)"><i data-lucide="trash-2" style="width:15px;height:15px;"></i></button>
</div>
<div class="ivp-detail-row">
<select class="g-select"><option>牛奶</option><option>鸡胸肉</option><option>牛腩</option><option>五花肉</option><option>咖啡豆</option><option>生菜</option><option>番茄</option></select>
<input class="g-input ivp-qty" type="number" value="30" placeholder="数量">
<span style="color:var(--g-text-muted);flex-shrink:0;">L</span>
<input class="g-input ivp-price" type="number" value="8.50" placeholder="单价">
<button class="ivp-del-btn" onclick="removeIvpRow(this)"><i data-lucide="trash-2" style="width:15px;height:15px;"></i></button>
</div>
<div class="ivp-detail-row">
<select class="g-select"><option>咖啡豆</option><option>鸡胸肉</option><option>牛腩</option><option>五花肉</option><option>牛奶</option><option>生菜</option><option>番茄</option></select>
<input class="g-input ivp-qty" type="number" value="5" placeholder="数量">
<span style="color:var(--g-text-muted);flex-shrink:0;">kg</span>
<input class="g-input ivp-price" type="number" value="120.00" placeholder="单价">
<button class="ivp-del-btn" onclick="removeIvpRow(this)"><i data-lucide="trash-2" style="width:15px;height:15px;"></i></button>
</div>
</div>
<button class="ivp-add-row" onclick="addIvpRow()"><i data-lucide="plus-circle" style="width:15px;height:15px;"></i> 添加原料</button>
</div>
<div class="g-form-group">
<label class="g-form-label">备注</label>
<textarea class="g-textarea" rows="3" placeholder="请输入备注信息,如特殊包装要求、配送时间等"></textarea>
</div>
</div>
<div class="g-drawer-ft">
<button class="g-btn" onclick="closeIvpDrawer()">取消</button>
<button class="g-btn g-btn-primary" onclick="closeIvpDrawer()">提交采购单</button>
</div>
</div>
<script>
/* Tab 切换 */
function switchIvpTab(el, tab) {
el.parentElement.querySelectorAll('.g-seg-item').forEach(function(s){ s.classList.remove('active'); });
el.classList.add('active');
document.getElementById('ivpTabOrders').style.display = tab === 'orders' ? '' : 'none';
document.getElementById('ivpTabVendors').style.display = tab === 'vendors' ? '' : 'none';
if (typeof lucide !== 'undefined') lucide.createIcons();
}
/* 抽屉 */
function openIvpDrawer() {
document.getElementById('ivpDrawerMask').classList.add('open');
document.getElementById('ivpDrawer').classList.add('open');
if (typeof lucide !== 'undefined') lucide.createIcons();
}
function closeIvpDrawer() {
document.getElementById('ivpDrawerMask').classList.remove('open');
document.getElementById('ivpDrawer').classList.remove('open');
}
/* 采购明细行 */
function addIvpRow() {
var list = document.getElementById('ivpDetailList');
var row = document.createElement('div');
row.className = 'ivp-detail-row';
row.innerHTML = '<select class="g-select"><option value="">请选择原料</option><option>鸡胸肉</option><option>牛腩</option><option>五花肉</option><option>牛奶</option><option>咖啡豆</option><option>生菜</option><option>番茄</option></select>'
+ '<input class="g-input ivp-qty" type="number" placeholder="数量">'
+ '<span style="color:var(--g-text-muted);flex-shrink:0;">kg</span>'
+ '<input class="g-input ivp-price" type="number" placeholder="单价">'
+ '<button class="ivp-del-btn" onclick="removeIvpRow(this)"><i data-lucide="trash-2" style="width:15px;height:15px;"></i></button>';
list.appendChild(row);
if (typeof lucide !== 'undefined') lucide.createIcons();
}
function removeIvpRow(btn) {
var row = btn.closest('.ivp-detail-row');
if (document.querySelectorAll('.ivp-detail-row').length > 1) {
row.remove();
}
}
/* 星星评分渲染 */
function renderIvpStars() {
document.querySelectorAll('.ivp-stars[data-score]').forEach(function(wrap) {
var score = parseFloat(wrap.getAttribute('data-score'));
var html = '';
for (var i = 1; i <= 5; i++) {
var pct = Math.min(100, Math.max(0, (score - i + 1) * 100));
html += '<div class="ivp-star">'
+ '<i data-lucide="star" class="ivp-star-bg" style="width:16px;height:16px;fill:#e5e7eb;stroke:none;"></i>'
+ '<div class="ivp-star-fg" style="width:' + pct + '%;">'
+ '<i data-lucide="star" style="width:16px;height:16px;fill:#facc15;stroke:none;"></i>'
+ '</div></div>';
}
html += '<span style="font-size:12px;color:var(--g-text-secondary);margin-left:6px;">' + score.toFixed(1) + '</span>';
wrap.innerHTML = html;
});
}
renderIvpStars();
if (typeof lucide !== 'undefined') { lucide.createIcons(); }
</script>

250
pages/inv-soldout.html Normal file
View File

@@ -0,0 +1,250 @@
<!-- 估清管理页 -->
<style>
.ivs-page { max-width:1000px; display:flex; flex-direction:column; gap:16px; font-size:13px; color:var(--g-text); }
/* 顶部自动估清卡片 */
.ivs-auto-card { display:flex; align-items:center; gap:16px; flex-wrap:wrap; }
.ivs-auto-left { flex:1; min-width:200px; }
.ivs-auto-title { font-size:15px; font-weight:600; color:var(--g-text); margin-bottom:4px; }
.ivs-auto-desc { font-size:12px; color:var(--g-text-secondary); }
.ivs-auto-hint { font-size:12px; color:var(--g-text-muted); margin-top:12px; width:100%; padding-top:10px; border-top:1px solid var(--g-border); display:flex; align-items:center; gap:4px; }
/* 统计卡片 */
.ivs-stats { display:grid; grid-template-columns:repeat(3,1fr); gap:14px; }
.ivs-stat-card { background:#fff; border-radius:var(--g-radius); padding:18px 20px; box-shadow:var(--g-shadow-sm); border:1px solid var(--g-border); transition:var(--g-transition); }
.ivs-stat-card:hover { box-shadow:var(--g-shadow-md); transform:translateY(-1px); }
.ivs-stat-label { font-size:12px; color:var(--g-text-muted); margin-bottom:8px; display:flex; align-items:center; gap:6px; }
.ivs-stat-label i { width:14px; height:14px; }
.ivs-stat-value { font-size:28px; font-weight:700; color:var(--g-text); }
.ivs-stat-value span { font-size:13px; font-weight:400; color:var(--g-text-secondary); margin-left:2px; }
.ivs-stat-sub { font-size:11px; color:var(--g-text-muted); margin-top:4px; }
.ivs-stat-card.danger .ivs-stat-value { color:var(--g-danger); }
.ivs-stat-card.warning .ivs-stat-value { color:var(--g-warning); }
/* Section 标题 */
.ivs-section-hd { font-size:15px; font-weight:600; color:var(--g-text); padding-left:10px; border-left:3px solid var(--primary); margin-bottom:16px; }
/* 表格微调 */
.ivs-page .g-table { font-size:13px; }
.ivs-page .g-table th { background:#f8f9fb; }
.ivs-page .g-table td { vertical-align:middle; }
.ivs-dishes { display:flex; flex-wrap:wrap; gap:4px; }
.ivs-dish-tag { background:color-mix(in srgb, var(--primary) 8%, #fff); color:var(--primary); font-size:11px; padding:2px 8px; border-radius:10px; white-space:nowrap; }
.ivs-more-tag { background:#f3f4f6; color:var(--g-text-muted); font-size:11px; padding:2px 8px; border-radius:10px; cursor:pointer; }
/* 手动估清操作区 */
.ivs-manual-bar { display:flex; align-items:center; gap:10px; flex-wrap:wrap; }
.ivs-manual-bar select { height:36px; padding:0 12px; border:1px solid #e5e7eb; border-radius:var(--g-radius-sm); font-size:13px; outline:none; min-width:240px; transition:var(--g-transition); }
.ivs-manual-bar select:focus { border-color:var(--primary); box-shadow:0 0 0 3px color-mix(in srgb, var(--primary) 12%, transparent); }
.ivs-section-desc { font-size:12px; color:var(--g-text-muted); margin-bottom:14px; display:flex; align-items:center; gap:4px; }
.ivs-section-desc i { width:14px; height:14px; }
</style>
<div class="ivs-page">
<!-- 顶部:自动估清开关 -->
<div class="g-card" style="padding:18px 20px;">
<div class="ivs-auto-card">
<div class="ivs-auto-left">
<div class="ivs-auto-title">自动估清</div>
<div class="ivs-auto-desc">当原料库存为 0 时,自动将关联菜品标记为售罄</div>
</div>
<div class="g-toggle on" onclick="toggleSwitch(this)"><div class="g-toggle-dot"></div></div>
</div>
<div class="ivs-auto-hint"><i data-lucide="link" style="width:13px;height:13px;"></i>已关联 28 个原料 &rarr; 56 道菜品</div>
</div>
<!-- 统计卡片 -->
<div class="ivs-stats">
<div class="ivs-stat-card danger">
<div class="ivs-stat-label"><i data-lucide="ban"></i>当前售罄菜品</div>
<div class="ivs-stat-value">3<span></span></div>
<div class="ivs-stat-sub">其中自动估清 2 道</div>
</div>
<div class="ivs-stat-card warning">
<div class="ivs-stat-label"><i data-lucide="alert-triangle"></i>低库存预警菜品</div>
<div class="ivs-stat-value">5<span></span></div>
<div class="ivs-stat-sub">原料即将耗尽</div>
</div>
<div class="ivs-stat-card">
<div class="ivs-stat-label"><i data-lucide="hand"></i>今日手动估清</div>
<div class="ivs-stat-value">1<span></span></div>
<div class="ivs-stat-sub">操作人:张经理</div>
</div>
</div>
<!-- Section 1: 当前已估清菜品 -->
<div class="g-card" style="padding:18px 20px;">
<div class="ivs-section-hd">当前已估清菜品</div>
<table class="g-table">
<thead><tr><th>菜品名称</th><th>估清原因</th><th>关联原料</th><th>原料库存</th><th>估清时间</th><th style="width:80px;">操作</th></tr></thead>
<tbody>
<tr>
<td style="font-weight:500;">红烧牛腩</td>
<td><span class="g-tag g-tag-orange">自动</span></td>
<td>牛腩</td>
<td><span style="color:var(--g-danger);font-weight:600;">0 kg</span></td>
<td>2026-02-12 09:15</td>
<td><span class="g-action" onclick="alert('已恢复上架')">恢复上架</span></td>
</tr>
<tr>
<td style="font-weight:500;">鸡胸肉沙拉</td>
<td><span class="g-tag g-tag-orange">自动</span></td>
<td>鸡胸肉</td>
<td><span style="color:var(--g-warning);font-weight:600;">0.3 kg</span></td>
<td>2026-02-12 10:30</td>
<td><span class="g-action" onclick="alert('已恢复上架')">恢复上架</span></td>
</tr>
<tr>
<td style="font-weight:500;">芒果冰沙</td>
<td><span class="g-tag g-tag-blue">手动</span></td>
<td></td>
<td></td>
<td>2026-02-12 11:00</td>
<td><span class="g-action" onclick="alert('已恢复上架')">恢复上架</span></td>
</tr>
<tr>
<td style="font-weight:500;">牛肉汉堡</td>
<td><span class="g-tag g-tag-orange">自动</span></td>
<td>牛腩</td>
<td><span style="color:var(--g-danger);font-weight:600;">0 kg</span></td>
<td>2026-02-12 09:15</td>
<td><span class="g-action" onclick="alert('已恢复上架')">恢复上架</span></td>
</tr>
</tbody>
</table>
</div>
<!-- Section 2: 原料→菜品关联映射 -->
<div class="g-card" style="padding:18px 20px;">
<div class="ivs-section-hd">原料 &rarr; 菜品关联映射</div>
<div class="ivs-section-desc"><i data-lucide="info"></i>设置原料与菜品的关联关系,当原料库存归零时自动估清关联菜品</div>
<table class="g-table">
<thead><tr><th>原料名称</th><th>当前库存</th><th>安全库存</th><th>关联菜品数</th><th>关联菜品</th><th style="width:80px;">自动估清</th></tr></thead>
<tbody>
<tr>
<td style="font-weight:500;">牛腩</td>
<td><span style="color:var(--g-danger);font-weight:600;">0 kg</span></td>
<td>5 kg</td>
<td>3</td>
<td><div class="ivs-dishes"><span class="ivs-dish-tag">红烧牛腩</span><span class="ivs-dish-tag">牛肉汉堡</span><span class="ivs-more-tag">+1</span></div></td>
<td><div class="g-toggle on" onclick="toggleSwitch(this)"><div class="g-toggle-dot"></div></div></td>
</tr>
<tr>
<td style="font-weight:500;">鸡胸肉</td>
<td><span style="color:var(--g-warning);font-weight:600;">0.3 kg</span></td>
<td>3 kg</td>
<td>2</td>
<td><div class="ivs-dishes"><span class="ivs-dish-tag">鸡胸肉沙拉</span><span class="ivs-dish-tag">照烧鸡排饭</span></div></td>
<td><div class="g-toggle on" onclick="toggleSwitch(this)"><div class="g-toggle-dot"></div></div></td>
</tr>
<tr>
<td style="font-weight:500;">三文鱼</td>
<td>2.5 kg</td>
<td>2 kg</td>
<td>2</td>
<td><div class="ivs-dishes"><span class="ivs-dish-tag">三文鱼刺身</span><span class="ivs-dish-tag">三文鱼寿司</span></div></td>
<td><div class="g-toggle on" onclick="toggleSwitch(this)"><div class="g-toggle-dot"></div></div></td>
</tr>
<tr>
<td style="font-weight:500;">芒果</td>
<td>8 个</td>
<td>5 个</td>
<td>3</td>
<td><div class="ivs-dishes"><span class="ivs-dish-tag">芒果冰沙</span><span class="ivs-dish-tag">杨枝甘露</span><span class="ivs-more-tag">+1</span></div></td>
<td><div class="g-toggle on" onclick="toggleSwitch(this)"><div class="g-toggle-dot"></div></div></td>
</tr>
<tr>
<td style="font-weight:500;">虾仁</td>
<td>1.2 kg</td>
<td>2 kg</td>
<td>4</td>
<td><div class="ivs-dishes"><span class="ivs-dish-tag">虾仁炒饭</span><span class="ivs-dish-tag">鲜虾云吞</span><span class="ivs-more-tag">+2</span></div></td>
<td><div class="g-toggle on" onclick="toggleSwitch(this)"><div class="g-toggle-dot"></div></div></td>
</tr>
<tr>
<td style="font-weight:500;">豆腐</td>
<td>15 块</td>
<td>10 块</td>
<td>2</td>
<td><div class="ivs-dishes"><span class="ivs-dish-tag">麻婆豆腐</span><span class="ivs-dish-tag">皮蛋豆腐</span></div></td>
<td><div class="g-toggle on" onclick="toggleSwitch(this)"><div class="g-toggle-dot"></div></div></td>
</tr>
<tr>
<td style="font-weight:500;">培根</td>
<td>3 kg</td>
<td>2 kg</td>
<td>3</td>
<td><div class="ivs-dishes"><span class="ivs-dish-tag">培根披萨</span><span class="ivs-dish-tag">培根意面</span><span class="ivs-more-tag">+1</span></div></td>
<td><div class="g-toggle" onclick="toggleSwitch(this)"><div class="g-toggle-dot"></div></div></td>
</tr>
<tr>
<td style="font-weight:500;">鳗鱼</td>
<td>1 kg</td>
<td>1.5 kg</td>
<td>1</td>
<td><div class="ivs-dishes"><span class="ivs-dish-tag">鳗鱼饭</span></div></td>
<td><div class="g-toggle on" onclick="toggleSwitch(this)"><div class="g-toggle-dot"></div></div></td>
</tr>
</tbody>
</table>
</div>
<!-- Section 3: 手动估清 -->
<div class="g-card" style="padding:18px 20px;">
<div class="ivs-section-hd">手动估清</div>
<div class="ivs-manual-bar" style="margin-bottom:16px;">
<select>
<option value="">请选择要估清的菜品...</option>
<option>宫保鸡丁</option>
<option>鱼香肉丝</option>
<option>水煮牛肉</option>
<option>糖醋排骨</option>
<option>蒜蓉西兰花</option>
<option>番茄鸡蛋汤</option>
</select>
<button class="g-btn g-btn-primary g-btn-sm" onclick="handleManualSoldout()"><i data-lucide="x-circle" style="width:14px;height:14px;margin-right:4px;vertical-align:-2px;"></i>标记售罄</button>
</div>
<div style="font-size:12px;color:var(--g-text-muted);margin-bottom:10px;">今日手动估清记录</div>
<table class="g-table">
<thead><tr><th>菜品</th><th>操作人</th><th>时间</th><th style="width:80px;">操作</th></tr></thead>
<tbody id="manualRecords">
<tr>
<td style="font-weight:500;">芒果冰沙</td>
<td>张经理</td>
<td>2026-02-12 11:00</td>
<td><span class="g-action" onclick="alert('已恢复上架')">恢复</span></td>
</tr>
</tbody>
</table>
</div>
</div>
<script>
function toggleSwitch(el) {
el.classList.toggle('on');
}
function handleManualSoldout() {
var sel = document.querySelector('.ivs-manual-bar select');
var name = sel.value;
if (!name) { alert('请先选择菜品'); return; }
var tbody = document.getElementById('manualRecords');
var now = new Date();
var ts = now.getFullYear() + '-' +
String(now.getMonth()+1).padStart(2,'0') + '-' +
String(now.getDate()).padStart(2,'0') + ' ' +
String(now.getHours()).padStart(2,'0') + ':' +
String(now.getMinutes()).padStart(2,'0');
var tr = document.createElement('tr');
tr.innerHTML = '<td style="font-weight:500;">' + name + '</td><td>当前用户</td><td>' + ts + '</td><td><span class="g-action" onclick="alert(\'已恢复上架\')">恢复</span></td>';
tbody.insertBefore(tr, tbody.firstChild);
sel.value = '';
alert('已将「' + name + '」标记为售罄');
}
if (typeof lucide !== 'undefined') { lucide.createIcons(); }
</script>

447
pages/mbr-customers.html Normal file
View File

@@ -0,0 +1,447 @@
<!-- 客户画像 — mbr-customers.html -->
<style>
/* ---- page-private: cu- prefix ---- */
.cu-toolbar {
background: #fff; border-radius: var(--g-radius); padding: 14px 20px;
display: flex; flex-wrap: wrap; gap: 10px; align-items: center;
box-shadow: var(--g-shadow-sm); border: 1px solid var(--g-border);
}
.cu-toolbar .g-select,
.cu-toolbar .g-input { height: 32px; font-size: 13px; }
.cu-search-wrap {
position: relative; width: 220px;
}
.cu-search-wrap .g-input { padding-left: 30px; width: 100%; }
.cu-search-wrap i {
position: absolute; left: 8px; top: 50%; transform: translateY(-50%);
color: var(--g-text-muted); pointer-events: none;
}
.cu-toolbar-right { margin-left: auto; display: flex; gap: 8px; }
/* stats */
.cu-stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; margin-top: 14px; }
.cu-stat-card {
background: #fff; border-radius: var(--g-radius); border: 1px solid var(--g-border);
padding: 18px 20px; box-shadow: var(--g-shadow-sm);
transition: box-shadow var(--g-transition);
}
.cu-stat-card:hover { box-shadow: var(--g-shadow-md); }
.cu-stat-title { font-size: 13px; color: var(--g-text-secondary); margin-bottom: 8px; font-weight: 500; display: flex; align-items: center; gap: 6px; }
.cu-stat-title i { width: 16px; height: 16px; }
.cu-stat-value { font-size: 28px; font-weight: 700; color: var(--g-text); line-height: 1.2; }
.cu-stat-value.green { color: var(--g-success); }
.cu-stat-value.orange { color: var(--g-warning); }
.cu-stat-value.red { color: var(--g-danger); }
.cu-stat-hint { font-size: 11px; color: var(--g-text-muted); margin-top: 6px; }
/* tag distribution */
.cu-tag-dist {
background: #fff; border-radius: var(--g-radius); border: 1px solid var(--g-border);
padding: 16px 20px; margin-top: 14px; box-shadow: var(--g-shadow-sm);
display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
}
.cu-tag-dist-label {
font-size: 13px; font-weight: 600; color: var(--g-text); margin-right: 4px; white-space: nowrap;
}
.cu-tag-pill {
display: inline-flex; align-items: center; gap: 5px; padding: 5px 14px;
border-radius: 16px; font-size: 12px; cursor: pointer; user-select: none;
transition: all var(--g-transition); border: 1px solid #e8e8e8; background: #fff;
}
.cu-tag-pill:hover { border-color: var(--primary); }
.cu-tag-pill.active { background: color-mix(in srgb, var(--primary) 10%, transparent); border-color: var(--primary); color: var(--primary); font-weight: 500; }
.cu-tag-pill .cu-tag-count {
background: #f0f0f0; color: var(--g-text-secondary); font-size: 11px;
padding: 1px 6px; border-radius: 8px; font-weight: 600;
}
.cu-tag-pill.active .cu-tag-count { background: color-mix(in srgb, var(--primary) 18%, transparent); color: var(--primary); }
/* table card */
.cu-table-card {
background: #fff; border-radius: var(--g-radius); border: 1px solid var(--g-border);
margin-top: 14px; box-shadow: var(--g-shadow-sm); overflow: hidden;
}
.cu-table-card .g-table td { white-space: nowrap; }
.cu-tags-cell { display: flex; gap: 4px; flex-wrap: wrap; }
.cu-mini-tag {
display: inline-block; padding: 1px 7px; border-radius: 3px; font-size: 11px; font-weight: 500;
}
.cu-mini-tag.green { background: #f6ffed; color: #52c41a; }
.cu-mini-tag.blue { background: #e6f7ff; color: #1890ff; }
.cu-mini-tag.orange { background: #fff7e6; color: #fa8c16; }
.cu-mini-tag.red { background: #fff1f0; color: #f5222d; }
.cu-mini-tag.purple { background: #f9f0ff; color: #722ed1; }
.cu-mini-tag.gold { background: #fffbe6; color: #d48806; }
.cu-row-dimmed td { opacity: 0.55; }
.cu-row-dimmed:hover td { opacity: 0.75; }
/* drawer profile */
.cu-profile-hd {
display: flex; align-items: center; gap: 16px; margin-bottom: 20px;
padding-bottom: 16px; border-bottom: 1px solid #f5f5f5;
}
.cu-avatar {
width: 56px; height: 56px; border-radius: 50%; background: color-mix(in srgb, var(--primary) 12%, #fff);
display: flex; align-items: center; justify-content: center; flex-shrink: 0;
color: var(--primary);
}
.cu-profile-name { font-size: 18px; font-weight: 700; color: var(--g-text); }
.cu-profile-phone { font-size: 13px; color: var(--g-text-muted); margin-top: 2px; }
.cu-profile-meta { display: flex; gap: 8px; margin-top: 6px; align-items: center; flex-wrap: wrap; }
.cu-section-hd {
font-size: 15px; font-weight: 600; color: var(--g-text);
padding-left: 10px; border-left: 3px solid var(--primary); margin-bottom: 14px;
}
.cu-overview-grid {
display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; margin-bottom: 20px;
}
.cu-ov-item {
background: var(--g-bg-subtle); border-radius: var(--g-radius-sm); padding: 12px 14px;
}
.cu-ov-label { font-size: 12px; color: var(--g-text-muted); margin-bottom: 4px; }
.cu-ov-val { font-size: 16px; font-weight: 700; color: var(--g-text); }
.cu-ov-val.primary { color: var(--primary); }
.cu-pref-section { margin-bottom: 20px; }
.cu-pref-row { margin-bottom: 10px; }
.cu-pref-label { font-size: 12px; color: var(--g-text-muted); margin-bottom: 6px; }
.cu-pref-pills { display: flex; gap: 6px; flex-wrap: wrap; }
.cu-pref-pill {
display: inline-block; padding: 4px 12px; border-radius: 14px;
font-size: 12px; background: #f5f5f5; color: var(--g-text-secondary);
}
.cu-mini-table { width: 100%; border-collapse: collapse; font-size: 12px; margin-bottom: 4px; }
.cu-mini-table th {
text-align: left; padding: 8px 10px; font-size: 11px; font-weight: 500;
color: var(--g-text-muted); background: var(--g-bg-subtle); border-bottom: 1px solid var(--g-border);
}
.cu-mini-table td {
padding: 8px 10px; border-bottom: 1px solid #f5f5f5; color: var(--g-text);
}
</style>
<div>
<!-- Toolbar -->
<div class="cu-toolbar">
<select class="g-select" style="width:180px;">
<option>全部门店</option>
<option>老三家外卖(朝阳店)</option>
<option>老三家外卖(海淀店)</option>
<option>老三家外卖(望京店)</option>
</select>
<select class="g-select" style="width:150px;" id="cuTagFilter" onchange="cuFilterTable()">
<option value="">全部标签</option>
<option value="高频客户">高频客户</option>
<option value="新客">新客</option>
<option value="沉睡客户">沉睡客户</option>
<option value="流失客户">流失客户</option>
<option value="午餐常客">午餐常客</option>
<option value="大额消费">大额消费</option>
</select>
<div class="cu-search-wrap">
<i data-lucide="search" style="width:14px;height:14px;"></i>
<input class="g-input" placeholder="搜索姓名/手机号" id="cuSearchInput" oninput="cuFilterTable()">
</div>
<div class="cu-toolbar-right">
<button class="g-btn"><i data-lucide="download" style="width:14px;height:14px;"></i> 导出</button>
</div>
</div>
<!-- Stats -->
<div class="cu-stats">
<div class="cu-stat-card">
<div class="cu-stat-title"><i data-lucide="users" style="color:var(--primary)"></i> 总客户</div>
<div class="cu-stat-value">2,156</div>
<div class="cu-stat-hint">累计注册客户数</div>
</div>
<div class="cu-stat-card">
<div class="cu-stat-title"><i data-lucide="flame" style="color:var(--g-success)"></i> 高频客户</div>
<div class="cu-stat-value green">328</div>
<div class="cu-stat-hint">月消费 ≥ 4次</div>
</div>
<div class="cu-stat-card">
<div class="cu-stat-title"><i data-lucide="moon" style="color:var(--g-warning)"></i> 沉睡客户</div>
<div class="cu-stat-value orange">456</div>
<div class="cu-stat-hint">30天未消费</div>
</div>
<div class="cu-stat-card">
<div class="cu-stat-title"><i data-lucide="user-x" style="color:var(--g-danger)"></i> 流失客户</div>
<div class="cu-stat-value red">189</div>
<div class="cu-stat-hint">60天未消费</div>
</div>
</div>
<!-- Tag Distribution -->
<div class="cu-tag-dist">
<span class="cu-tag-dist-label">标签分布</span>
<span class="cu-tag-pill" onclick="cuToggleTagPill(this, '高频客户')">高频客户 <span class="cu-tag-count">328</span></span>
<span class="cu-tag-pill" onclick="cuToggleTagPill(this, '新客')">新客 <span class="cu-tag-count">86</span></span>
<span class="cu-tag-pill" onclick="cuToggleTagPill(this, '午餐常客')">午餐常客 <span class="cu-tag-count">215</span></span>
<span class="cu-tag-pill" onclick="cuToggleTagPill(this, '大额消费')">大额消费 <span class="cu-tag-count">142</span></span>
<span class="cu-tag-pill" onclick="cuToggleTagPill(this, '沉睡客户')">沉睡客户 <span class="cu-tag-count">456</span></span>
<span class="cu-tag-pill" onclick="cuToggleTagPill(this, '流失客户')">流失客户 <span class="cu-tag-count">189</span></span>
</div>
<!-- Customer Table -->
<div class="cu-table-card">
<table class="g-table" id="cuTable">
<thead>
<tr>
<th>客户</th>
<th>手机号</th>
<th>标签</th>
<th>累计消费</th>
<th>消费次数</th>
<th>平均客单价</th>
<th>最近消费</th>
<th>注册时间</th>
<th>操作</th>
</tr>
</thead>
<tbody id="cuTableBody"></tbody>
</table>
<!-- Pagination -->
<div class="g-pagination" style="padding: 12px 16px;">
<span style="margin-right:auto;font-size:12px;color:var(--g-text-muted);">共 2,156 条</span>
<button class="g-page-btn" disabled>&lt;</button>
<button class="g-page-btn active">1</button>
<button class="g-page-btn">2</button>
<button class="g-page-btn">3</button>
<button class="g-page-btn">...</button>
<button class="g-page-btn">108</button>
<button class="g-page-btn">&gt;</button>
</div>
</div>
</div>
<!-- Customer Profile Drawer -->
<div class="g-drawer-mask" id="cuDrawerMask" onclick="closeCuDrawer()"></div>
<div class="g-drawer" id="cuDrawer" style="width:600px;">
<div class="g-drawer-hd">
<span class="g-drawer-title" id="cuDrawerTitle">客户画像 - 张**</span>
<button class="g-drawer-close" onclick="closeCuDrawer()"><i data-lucide="x" style="width:18px;height:18px;"></i></button>
</div>
<div class="g-drawer-bd">
<!-- 基本信息 -->
<div class="cu-profile-hd">
<div class="cu-avatar"><i data-lucide="user" style="width:28px;height:28px;"></i></div>
<div>
<div class="cu-profile-name" id="cuProfileName">张**</div>
<div class="cu-profile-phone" id="cuProfilePhone">138****6789</div>
<div class="cu-profile-meta" id="cuProfileMeta">
<span class="g-tag g-tag-blue">黄金会员</span>
<span style="font-size:12px;color:var(--g-text-muted);">注册于 2024-03-15</span>
</div>
<div class="cu-profile-meta" id="cuProfileTags" style="margin-top:4px;">
<span class="cu-mini-tag green">高频客户</span>
<span class="cu-mini-tag purple">午餐常客</span>
<span class="cu-mini-tag gold">大额消费</span>
</div>
</div>
</div>
<!-- 消费概览 -->
<div class="cu-section-hd">消费概览</div>
<div class="cu-overview-grid">
<div class="cu-ov-item">
<div class="cu-ov-label">累计消费</div>
<div class="cu-ov-val primary">&yen;3,680</div>
</div>
<div class="cu-ov-item">
<div class="cu-ov-label">消费次数</div>
<div class="cu-ov-val">42次</div>
</div>
<div class="cu-ov-item">
<div class="cu-ov-label">平均客单价</div>
<div class="cu-ov-val">&yen;87.6</div>
</div>
<div class="cu-ov-item">
<div class="cu-ov-label">最近消费</div>
<div class="cu-ov-val">2025-02-10</div>
</div>
<div class="cu-ov-item">
<div class="cu-ov-label">常用渠道</div>
<div class="cu-ov-val" style="font-size:13px;">外卖(68%) 自提(32%)</div>
</div>
<div class="cu-ov-item">
<div class="cu-ov-label">储值余额</div>
<div class="cu-ov-val primary">&yen;260</div>
</div>
</div>
<!-- 消费偏好 -->
<div class="cu-section-hd">消费偏好</div>
<div class="cu-pref-section">
<div class="cu-pref-row">
<div class="cu-pref-label">常点商品</div>
<div class="cu-pref-pills">
<span class="cu-pref-pill">宫保鸡丁 (12次)</span>
<span class="cu-pref-pill">鱼香肉丝 (8次)</span>
<span class="cu-pref-pill">米饭 (42次)</span>
</div>
</div>
<div class="cu-pref-row">
<div class="cu-pref-label">口味偏好</div>
<div class="cu-pref-pills">
<span class="cu-pref-pill">偏辣</span>
<span class="cu-pref-pill">少盐</span>
</div>
</div>
<div class="cu-pref-row">
<div class="cu-pref-label">下单时段</div>
<div class="cu-pref-pills">
<span class="cu-pref-pill">午餐 (78%)</span>
<span class="cu-pref-pill">晚餐 (22%)</span>
</div>
</div>
</div>
<!-- 最近订单 -->
<div class="cu-section-hd">最近订单</div>
<table class="cu-mini-table">
<thead>
<tr>
<th>日期</th>
<th>商品摘要</th>
<th>金额</th>
<th>渠道</th>
</tr>
</thead>
<tbody>
<tr>
<td>2025-02-10</td>
<td>宫保鸡丁+米饭+可乐</td>
<td>&yen;52.00</td>
<td><span class="g-tag g-tag-blue">外卖</span></td>
</tr>
<tr>
<td>2025-02-07</td>
<td>鱼香肉丝盖饭+蛋花汤</td>
<td>&yen;38.00</td>
<td><span class="g-tag g-tag-green">自提</span></td>
</tr>
<tr>
<td>2025-02-03</td>
<td>招牌红烧肉饭+凉菜拼盘</td>
<td>&yen;65.00</td>
<td><span class="g-tag g-tag-blue">外卖</span></td>
</tr>
<tr>
<td>2025-01-28</td>
<td>番茄牛腩面+珍珠奶茶</td>
<td>&yen;42.00</td>
<td><span class="g-tag g-tag-blue">外卖</span></td>
</tr>
<tr>
<td>2025-01-22</td>
<td>宫保鸡丁+麻婆豆腐+米饭x2</td>
<td>&yen;88.00</td>
<td><span class="g-tag g-tag-green">自提</span></td>
</tr>
</tbody>
</table>
</div>
<div class="g-drawer-ft">
<button class="g-btn" onclick="closeCuDrawer()">关闭</button>
<button class="g-btn g-btn-primary"><i data-lucide="send" style="width:14px;height:14px;"></i> 发送消息</button>
</div>
</div>
<script>
// Customer data
var cuCustomers = [
{ name:'张**', phone:'138****6789', tags:['高频客户','午餐常客','大额消费'], total:3680, count:42, avg:87.6, last:'2025-02-10', reg:'2024-03-15', churned:false },
{ name:'李**', phone:'139****2345', tags:['高频客户','大额消费'], total:5120, count:58, avg:88.3, last:'2025-02-11', reg:'2023-11-20', churned:false },
{ name:'王**', phone:'136****8901', tags:['新客'], total:156, count:2, avg:78.0, last:'2025-02-09', reg:'2025-02-01', churned:false },
{ name:'赵**', phone:'158****4567', tags:['午餐常客'], total:1890, count:28, avg:67.5, last:'2025-02-08', reg:'2024-06-10', churned:false },
{ name:'陈**', phone:'137****3210', tags:['沉睡客户'], total:960, count:12, avg:80.0, last:'2025-01-05', reg:'2024-04-22', churned:false },
{ name:'刘**', phone:'155****7654', tags:['流失客户'], total:420, count:5, avg:84.0, last:'2024-11-18', reg:'2024-08-03', churned:true },
{ name:'周**', phone:'186****9012', tags:['高频客户','午餐常客'], total:2760, count:35, avg:78.9, last:'2025-02-11', reg:'2024-01-08', churned:false },
{ name:'吴**', phone:'133****5678', tags:['流失客户','大额消费'], total:1540, count:11, avg:140.0, last:'2024-12-02', reg:'2024-05-15', churned:true },
];
var cuTagColorMap = {
'高频客户': 'green',
'新客': 'blue',
'沉睡客户': 'orange',
'流失客户': 'red',
'午餐常客': 'purple',
'大额消费': 'gold'
};
var cuActiveTagFilter = '';
function cuRenderTable() {
var kw = (document.getElementById('cuSearchInput').value || '').trim().toLowerCase();
var tagSel = document.getElementById('cuTagFilter').value;
var filterTag = cuActiveTagFilter || tagSel;
var tbody = document.getElementById('cuTableBody');
var html = '';
cuCustomers.forEach(function(c) {
if (kw && c.name.toLowerCase().indexOf(kw) === -1 && c.phone.indexOf(kw) === -1) return;
if (filterTag && c.tags.indexOf(filterTag) === -1) return;
var tagsHtml = c.tags.map(function(t) {
return '<span class="cu-mini-tag ' + (cuTagColorMap[t] || 'gray') + '">' + t + '</span>';
}).join('');
var rowClass = c.churned ? ' class="cu-row-dimmed"' : '';
html += '<tr' + rowClass + '>'
+ '<td style="font-weight:500;">' + c.name + '</td>'
+ '<td>' + c.phone + '</td>'
+ '<td><div class="cu-tags-cell">' + tagsHtml + '</div></td>'
+ '<td>&yen;' + c.total.toLocaleString() + '</td>'
+ '<td>' + c.count + '次</td>'
+ '<td>&yen;' + c.avg.toFixed(1) + '</td>'
+ '<td>' + c.last + '</td>'
+ '<td>' + c.reg + '</td>'
+ '<td><a class="g-action" onclick="openCuDrawer(\'' + c.name + '\')">画像</a></td>'
+ '</tr>';
});
if (!html) {
html = '<tr><td colspan="9" style="text-align:center;padding:40px;color:var(--g-text-muted);">暂无匹配客户</td></tr>';
}
tbody.innerHTML = html;
}
function cuFilterTable() {
cuActiveTagFilter = '';
document.querySelectorAll('.cu-tag-pill').forEach(function(p) { p.classList.remove('active'); });
cuRenderTable();
}
function cuToggleTagPill(el, tag) {
var wasActive = el.classList.contains('active');
document.querySelectorAll('.cu-tag-pill').forEach(function(p) { p.classList.remove('active'); });
if (wasActive) {
cuActiveTagFilter = '';
} else {
el.classList.add('active');
cuActiveTagFilter = tag;
}
document.getElementById('cuTagFilter').value = '';
cuRenderTable();
}
function openCuDrawer(name) {
var c = cuCustomers.find(function(x) { return x.name === name; });
if (c) {
document.getElementById('cuDrawerTitle').textContent = '客户画像 - ' + c.name;
document.getElementById('cuProfileName').textContent = c.name;
document.getElementById('cuProfilePhone').textContent = c.phone;
}
document.getElementById('cuDrawerMask').classList.add('open');
document.getElementById('cuDrawer').classList.add('open');
if (typeof lucide !== 'undefined') lucide.createIcons();
}
function closeCuDrawer() {
document.getElementById('cuDrawerMask').classList.remove('open');
document.getElementById('cuDrawer').classList.remove('open');
}
// Init
cuRenderTable();
if (typeof lucide !== 'undefined') lucide.createIcons();
</script>

698
pages/mbr-members.html Normal file
View File

@@ -0,0 +1,698 @@
<!-- 会员管理页 -->
<style>
.mm-toolbar { display:flex; align-items:center; gap:12px; margin-bottom:16px; box-shadow:var(--g-shadow-sm); border-radius:10px; padding:12px 16px; background:#fff; flex-wrap:wrap; }
.mm-toolbar .mm-right { margin-left:auto; display:flex; gap:8px; }
.mm-stats { display:grid; grid-template-columns:repeat(4,1fr); gap:12px; margin-bottom:16px; }
.mm-stat-card { background:#fff; border-radius:10px; padding:16px 20px; box-shadow:var(--g-shadow-sm); transition:all var(--g-transition); }
.mm-stat-card:hover { box-shadow:var(--g-shadow-md); transform:translateY(-1px); }
.mm-stat-label { font-size:13px; color:var(--g-text-muted); margin-bottom:6px; }
.mm-stat-value { font-size:24px; font-weight:700; color:var(--g-text); }
.mm-stat-value.blue { color:var(--primary); }
.mm-stat-value.green { color:var(--g-success); }
.mm-stat-value.orange { color:var(--g-warning); }
.mm-stat-sub { font-size:11px; color:var(--g-text-muted); margin-top:4px; }
.mm-table-wrap { background:#fff; border-radius:10px; box-shadow:var(--g-shadow-sm); overflow:hidden; }
.mm-member-cell { display:flex; align-items:center; gap:10px; }
.mm-avatar { width:34px; height:34px; border-radius:50%; display:flex; align-items:center; justify-content:center; color:#fff; font-size:13px; font-weight:600; flex-shrink:0; }
/* 等级体系 Tab */
.mm-level-grid { display:flex; flex-direction:column; gap:12px; margin-bottom:24px; }
.mm-level-card { background:#fff; border-radius:10px; padding:18px 20px; box-shadow:var(--g-shadow-sm); border:1px solid var(--g-border); transition:all var(--g-transition); display:flex; align-items:center; gap:16px; }
.mm-level-card:hover { box-shadow:var(--g-shadow-md); }
.mm-level-icon { width:48px; height:48px; border-radius:50%; display:flex; align-items:center; justify-content:center; flex-shrink:0; }
.mm-level-name { font-size:15px; font-weight:600; color:var(--g-text); }
.mm-level-cond { font-size:12px; color:var(--g-text-muted); margin-top:2px; }
.mm-level-perks { display:flex; gap:8px; flex-wrap:wrap; margin-top:6px; }
.mm-level-perk { display:inline-flex; align-items:center; gap:4px; font-size:12px; color:var(--g-text-secondary); padding:2px 8px; background:#f8f9fb; border-radius:4px; }
.mm-level-perk i { color:var(--g-success); flex-shrink:0; }
.mm-level-info { flex:1; min-width:0; }
.mm-level-actions { display:flex; gap:8px; flex-shrink:0; }
.mm-level-order { font-size:20px; font-weight:700; color:var(--g-text-muted); width:28px; text-align:center; flex-shrink:0; }
.mm-add-level-btn { width:100%; height:44px; border:1px dashed #d9d9d9; border-radius:10px; background:#fff; color:var(--g-text-muted); font-size:13px; cursor:pointer; display:flex; align-items:center; justify-content:center; gap:6px; transition:var(--g-transition); }
.mm-add-level-btn:hover { border-color:var(--primary); color:var(--primary); }
.mm-section-hd { font-size:15px; font-weight:600; color:var(--g-text); padding-left:10px; border-left:3px solid var(--primary); margin-bottom:16px; }
.mm-day-card { background:#fff; border-radius:10px; padding:20px 24px; box-shadow:var(--g-shadow-sm); border:1px solid var(--g-border); }
.mm-day-row { display:flex; align-items:center; gap:16px; margin-bottom:14px; }
.mm-day-row:last-child { margin-bottom:0; }
.mm-day-label { width:80px; font-size:13px; color:var(--g-text-secondary); flex-shrink:0; }
/* 抽屉内 */
.mm-drawer-header { display:flex; align-items:center; gap:14px; margin-bottom:20px; padding-bottom:16px; border-bottom:1px solid #f5f5f5; }
.mm-drawer-avatar { width:48px; height:48px; border-radius:50%; display:flex; align-items:center; justify-content:center; color:#fff; font-size:18px; font-weight:600; flex-shrink:0; background:var(--primary); }
.mm-drawer-name { font-size:16px; font-weight:600; color:var(--g-text); }
.mm-drawer-meta { font-size:12px; color:var(--g-text-muted); margin-top:3px; }
.mm-overview { display:grid; grid-template-columns:repeat(5,1fr); gap:12px; margin-bottom:20px; }
.mm-ov-item { text-align:center; padding:12px 8px; background:var(--g-bg-subtle); border-radius:8px; }
.mm-ov-val { font-size:18px; font-weight:700; color:var(--g-text); }
.mm-ov-label { font-size:11px; color:var(--g-text-muted); margin-top:4px; }
.mm-mini-table { width:100%; border-collapse:collapse; font-size:12px; margin-top:10px; }
.mm-mini-table th { text-align:left; padding:8px 10px; background:var(--g-bg-subtle); color:var(--g-text-muted); font-weight:500; font-size:11px; }
.mm-mini-table td { padding:8px 10px; border-bottom:1px solid #f5f5f5; color:var(--g-text); }
.mm-tag-list { display:flex; gap:6px; flex-wrap:wrap; margin-top:10px; }
.mm-tag-add { display:inline-flex; align-items:center; gap:4px; padding:5px 12px; border-radius:16px; border:1px dashed #d9d9d9; font-size:12px; cursor:pointer; color:var(--g-text-muted); transition:var(--g-transition); background:none; }
.mm-tag-add:hover { border-color:var(--primary); color:var(--primary); }
.mm-tag-input { width:80px; height:26px; padding:0 8px; border:1px solid var(--primary); border-radius:16px; font-size:12px; outline:none; }
.mm-tag-remove { margin-left:2px; cursor:pointer; color:var(--g-text-muted); display:inline-flex; align-items:center; transition:var(--g-transition); }
.mm-tag-remove:hover { color:var(--g-danger); }
/* 等级编辑抽屉 */
.mm-perk-row { display:flex; align-items:center; gap:8px; margin-bottom:8px; }
.mm-perk-row input { flex:1; }
.mm-perk-remove { background:none; border:none; color:var(--g-text-muted); cursor:pointer; display:flex; align-items:center; justify-content:center; width:24px; height:24px; border-radius:6px; transition:var(--g-transition); }
.mm-perk-remove:hover { color:var(--g-danger); background:#fef2f2; }
.mm-add-perk { color:var(--primary); font-size:12px; cursor:pointer; background:none; border:none; padding:0; display:flex; align-items:center; gap:4px; transition:var(--g-transition); }
.mm-add-perk:hover { text-decoration:underline; }
.mm-upgrade-section { background:#f8f9fb; border-radius:8px; padding:14px; margin-bottom:12px; }
.mm-upgrade-row { display:flex; align-items:center; gap:8px; font-size:13px; color:var(--g-text-secondary); }
.mm-perk-card { background:#f8f9fb; border-radius:8px; padding:12px 14px; margin-bottom:10px; border:1px solid #f0f0f0; }
.mm-perk-card-hd { display:flex; align-items:center; gap:10px; }
.mm-perk-card-hd .mm-perk-label { flex:1; font-size:13px; font-weight:500; color:var(--g-text); }
.mm-perk-card-bd { margin-top:10px; padding-top:10px; border-top:1px solid #eee; display:flex; align-items:center; gap:8px; flex-wrap:wrap; font-size:13px; color:var(--g-text-secondary); }
.mm-perk-card-bd.hidden { display:none; }
.mm-coupon-mini { display:flex; align-items:center; gap:6px; padding:4px 10px; background:#fff; border:1px solid #e5e7eb; border-radius:6px; font-size:12px; }
.mm-coupon-mini-remove { cursor:pointer; color:var(--g-text-muted); display:inline-flex; align-items:center; transition:var(--g-transition); }
.mm-coupon-mini-remove:hover { color:var(--g-danger); }
.mm-add-coupon-link { color:var(--primary); font-size:12px; cursor:pointer; background:none; border:none; padding:0; display:inline-flex; align-items:center; gap:3px; }
.mm-add-coupon-link:hover { text-decoration:underline; }
</style>
<div>
<!-- 分段切换 -->
<div class="g-seg" style="width:240px;margin-bottom:16px;">
<div class="g-seg-item active" onclick="switchMmTab(this,'list')">会员列表</div>
<div class="g-seg-item" onclick="switchMmTab(this,'levels')">等级体系</div>
</div>
<!-- ==================== TAB 1: 会员列表 ==================== -->
<div id="mmTabList">
<!-- 工具栏 -->
<div class="mm-toolbar">
<select class="g-select" style="width:180px;">
<option>全部门店</option>
<option>老三家外卖(朝阳店)</option>
<option>老三家外卖(海淀店)</option>
<option>老三家外卖(望京店)</option>
</select>
<select class="g-select" style="width:140px;" id="mmLevelFilter">
<option value="">全部等级</option>
<option>普通会员</option>
<option>银卡</option>
<option>金卡</option>
<option>钻石</option>
</select>
<div style="position:relative;">
<i data-lucide="search" style="position:absolute;left:10px;top:50%;transform:translateY(-50%);width:14px;height:14px;color:var(--g-text-muted);pointer-events:none;"></i>
<input class="g-input" style="width:200px;padding-left:32px;" placeholder="搜索姓名/手机号">
</div>
<div class="mm-right">
<button class="g-btn"><i data-lucide="download" style="width:14px;height:14px;"></i>导出</button>
</div>
</div>
<!-- 统计卡片 -->
<div class="mm-stats">
<div class="mm-stat-card">
<div class="mm-stat-label">会员总数</div>
<div class="mm-stat-value blue">1,286</div>
</div>
<div class="mm-stat-card">
<div class="mm-stat-label">本月新增</div>
<div class="mm-stat-value green">86</div>
</div>
<div class="mm-stat-card">
<div class="mm-stat-label">活跃会员</div>
<div class="mm-stat-value">658</div>
<div class="mm-stat-sub">30天内有消费</div>
</div>
<div class="mm-stat-card">
<div class="mm-stat-label">沉睡会员</div>
<div class="mm-stat-value orange">328</div>
</div>
</div>
<!-- 表格 -->
<div class="mm-table-wrap">
<table class="g-table">
<thead>
<tr>
<th>会员</th>
<th>手机号</th>
<th>等级</th>
<th>累计消费</th>
<th>消费次数</th>
<th>最近消费</th>
<th>储值余额</th>
<th>积分</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr>
<td><div class="mm-member-cell"><div class="mm-avatar" style="background:#1890ff;"></div><span>张**</span></div></td>
<td>138****6721</td>
<td><span class="g-tag" style="color:#722ed1;background:#f9f0ff;border:1px solid #d3adf7;">钻石</span></td>
<td>&yen;15,280</td>
<td>186</td>
<td>2026-02-11</td>
<td>&yen;2,350.00</td>
<td>8,620</td>
<td><a class="g-action" onclick="openMmDrawer(0)">详情</a></td>
</tr>
<tr>
<td><div class="mm-member-cell"><div class="mm-avatar" style="background:#fa8c16;"></div><span>李**</span></div></td>
<td>139****3345</td>
<td><span class="g-tag g-tag-orange">金卡</span></td>
<td>&yen;4,560</td>
<td>72</td>
<td>2026-02-10</td>
<td>&yen;800.00</td>
<td>3,240</td>
<td><a class="g-action" onclick="openMmDrawer(1)">详情</a></td>
</tr>
<tr>
<td><div class="mm-member-cell"><div class="mm-avatar" style="background:#52c41a;"></div><span>王**</span></div></td>
<td>136****8890</td>
<td><span class="g-tag g-tag-blue">银卡</span></td>
<td>&yen;1,230</td>
<td>28</td>
<td>2026-02-08</td>
<td>&yen;200.00</td>
<td>1,560</td>
<td><a class="g-action" onclick="openMmDrawer(2)">详情</a></td>
</tr>
<tr>
<td><div class="mm-member-cell"><div class="mm-avatar" style="background:#722ed1;"></div><span>赵**</span></div></td>
<td>158****2210</td>
<td><span class="g-tag g-tag-gray">普通</span></td>
<td>&yen;320</td>
<td>8</td>
<td>2026-01-25</td>
<td>&yen;0.00</td>
<td>420</td>
<td><a class="g-action" onclick="openMmDrawer(3)">详情</a></td>
</tr>
<tr>
<td><div class="mm-member-cell"><div class="mm-avatar" style="background:#eb2f96;"></div><span>陈**</span></div></td>
<td>137****5567</td>
<td><span class="g-tag g-tag-orange">金卡</span></td>
<td>&yen;3,890</td>
<td>56</td>
<td>2026-02-09</td>
<td>&yen;1,200.00</td>
<td>2,780</td>
<td><a class="g-action" onclick="openMmDrawer(4)">详情</a></td>
</tr>
<tr>
<td><div class="mm-member-cell"><div class="mm-avatar" style="background:#13c2c2;"></div><span>刘**</span></div></td>
<td>155****9901</td>
<td><span class="g-tag g-tag-blue">银卡</span></td>
<td>&yen;980</td>
<td>18</td>
<td>2026-02-05</td>
<td>&yen;150.00</td>
<td>890</td>
<td><a class="g-action" onclick="openMmDrawer(5)">详情</a></td>
</tr>
<tr>
<td><div class="mm-member-cell"><div class="mm-avatar" style="background:#f5222d;"></div><span>孙**</span></div></td>
<td>186****4432</td>
<td><span class="g-tag" style="color:#722ed1;background:#f9f0ff;border:1px solid #d3adf7;">钻石</span></td>
<td>&yen;12,600</td>
<td>142</td>
<td>2026-02-12</td>
<td>&yen;3,800.00</td>
<td>7,150</td>
<td><a class="g-action" onclick="openMmDrawer(6)">详情</a></td>
</tr>
<tr>
<td><div class="mm-member-cell"><div class="mm-avatar" style="background:#2f54eb;"></div><span>周**</span></div></td>
<td>133****7788</td>
<td><span class="g-tag g-tag-gray">普通</span></td>
<td>&yen;160</td>
<td>4</td>
<td>2026-01-18</td>
<td>&yen;0.00</td>
<td>210</td>
<td><a class="g-action" onclick="openMmDrawer(7)">详情</a></td>
</tr>
</tbody>
</table>
</div>
<!-- 分页 -->
<div class="g-pagination">
<span style="margin-right:8px;">共 1,286 条</span>
<button class="g-page-btn" disabled>&lt;</button>
<button class="g-page-btn active">1</button>
<button class="g-page-btn">2</button>
<button class="g-page-btn">3</button>
<button class="g-page-btn">...</button>
<button class="g-page-btn">129</button>
<button class="g-page-btn">&gt;</button>
</div>
</div>
<!-- ==================== TAB 2: 等级体系 ==================== -->
<div id="mmTabLevels" style="display:none;">
<div class="mm-section-hd">会员等级</div>
<div class="mm-level-grid" id="mmLevelList">
<!-- Lv1 普通 -->
<div class="mm-level-card">
<div class="mm-level-order">1</div>
<div class="mm-level-icon" style="background:#f5f5f5;color:#999;">
<i data-lucide="user" style="width:24px;height:24px;"></i>
</div>
<div class="mm-level-info">
<div class="mm-level-name">普通会员</div>
<div class="mm-level-cond">注册即享</div>
<div class="mm-level-perks">
<span class="mm-level-perk"><i data-lucide="check" style="width:12px;height:12px;"></i>积分累计</span>
<span class="mm-level-perk"><i data-lucide="check" style="width:12px;height:12px;"></i>会员价商品</span>
</div>
</div>
<div class="mm-level-actions">
<button class="g-btn g-btn-sm" onclick="openMmLevelDrawer('普通会员')">编辑</button>
</div>
</div>
<!-- Lv2 银卡 -->
<div class="mm-level-card">
<div class="mm-level-order">2</div>
<div class="mm-level-icon" style="background:#e6f7ff;color:#1890ff;">
<i data-lucide="award" style="width:24px;height:24px;"></i>
</div>
<div class="mm-level-info">
<div class="mm-level-name" style="color:#1890ff;">银卡会员</div>
<div class="mm-level-cond">累计消费满 &yen;500</div>
<div class="mm-level-perks">
<span class="mm-level-perk"><i data-lucide="check" style="width:12px;height:12px;"></i>全场9.8折</span>
<span class="mm-level-perk"><i data-lucide="check" style="width:12px;height:12px;"></i>生日赠券</span>
<span class="mm-level-perk"><i data-lucide="check" style="width:12px;height:12px;"></i>积分1.2倍</span>
</div>
</div>
<div class="mm-level-actions">
<button class="g-btn g-btn-sm" onclick="openMmLevelDrawer('银卡会员')">编辑</button>
<button class="g-btn g-btn-sm" style="color:var(--g-danger);border-color:var(--g-danger);" onclick="this.closest('.mm-level-card').remove()"><i data-lucide="trash-2" style="width:13px;height:13px;"></i></button>
</div>
</div>
<!-- Lv3 金卡 -->
<div class="mm-level-card">
<div class="mm-level-order">3</div>
<div class="mm-level-icon" style="background:#fff7e6;color:#fa8c16;">
<i data-lucide="trophy" style="width:24px;height:24px;"></i>
</div>
<div class="mm-level-info">
<div class="mm-level-name" style="color:#fa8c16;">金卡会员</div>
<div class="mm-level-cond">累计消费满 &yen;2,000</div>
<div class="mm-level-perks">
<span class="mm-level-perk"><i data-lucide="check" style="width:12px;height:12px;"></i>全场9.5折</span>
<span class="mm-level-perk"><i data-lucide="check" style="width:12px;height:12px;"></i>生日双倍积分</span>
<span class="mm-level-perk"><i data-lucide="check" style="width:12px;height:12px;"></i>每月赠券</span>
<span class="mm-level-perk"><i data-lucide="check" style="width:12px;height:12px;"></i>积分1.5倍</span>
</div>
</div>
<div class="mm-level-actions">
<button class="g-btn g-btn-sm" onclick="openMmLevelDrawer('金卡会员')">编辑</button>
<button class="g-btn g-btn-sm" style="color:var(--g-danger);border-color:var(--g-danger);" onclick="this.closest('.mm-level-card').remove()"><i data-lucide="trash-2" style="width:13px;height:13px;"></i></button>
</div>
</div>
<!-- Lv4 钻石 -->
<div class="mm-level-card">
<div class="mm-level-order">4</div>
<div class="mm-level-icon" style="background:#f9f0ff;color:#722ed1;">
<i data-lucide="gem" style="width:24px;height:24px;"></i>
</div>
<div class="mm-level-info">
<div class="mm-level-name" style="color:#722ed1;">钻石会员</div>
<div class="mm-level-cond">累计消费满 &yen;5,000</div>
<div class="mm-level-perks">
<span class="mm-level-perk"><i data-lucide="check" style="width:12px;height:12px;"></i>全场9折</span>
<span class="mm-level-perk"><i data-lucide="check" style="width:12px;height:12px;"></i>生日赠券+双倍积分</span>
<span class="mm-level-perk"><i data-lucide="check" style="width:12px;height:12px;"></i>每月赠券</span>
<span class="mm-level-perk"><i data-lucide="check" style="width:12px;height:12px;"></i>免配送费</span>
<span class="mm-level-perk"><i data-lucide="check" style="width:12px;height:12px;"></i>优先配送</span>
<span class="mm-level-perk"><i data-lucide="check" style="width:12px;height:12px;"></i>积分2倍</span>
</div>
</div>
<div class="mm-level-actions">
<button class="g-btn g-btn-sm" onclick="openMmLevelDrawer('钻石会员')">编辑</button>
<button class="g-btn g-btn-sm" style="color:var(--g-danger);border-color:var(--g-danger);" onclick="this.closest('.mm-level-card').remove()"><i data-lucide="trash-2" style="width:13px;height:13px;"></i></button>
</div>
</div>
</div>
<button class="mm-add-level-btn" onclick="openMmLevelDrawer()"><i data-lucide="plus" style="width:14px;height:14px;"></i>添加等级</button>
<!-- 会员日设置 -->
<div class="mm-section-hd">会员日设置</div>
<div class="mm-day-card">
<div class="mm-day-row">
<div class="mm-day-label">启用会员日</div>
<div class="g-toggle on" onclick="this.classList.toggle('on')"></div>
</div>
<div class="mm-day-row">
<div class="mm-day-label">会员日</div>
<select class="g-select" style="width:160px;">
<option>每周二</option>
<option>每周一</option>
<option>每周三</option>
<option>每周四</option>
<option>每周五</option>
<option>每周六</option>
<option>每周日</option>
</select>
</div>
<div class="mm-day-row">
<div class="mm-day-label">会员日权益</div>
<div style="display:flex;align-items:center;gap:8px;">
<span style="font-size:13px;color:var(--g-text-secondary);">额外</span>
<input class="g-input" style="width:80px;text-align:center;" placeholder="如9" value="9">
<span style="font-size:13px;color:var(--g-text-secondary);">折优惠</span>
</div>
</div>
</div>
</div>
</div>
<!-- 会员详情抽屉 -->
<div class="g-drawer-mask" id="mmDrawerMask" onclick="closeMmDrawer()"></div>
<div class="g-drawer" id="mmDrawer" style="width:560px;">
<div class="g-drawer-hd">
<div class="g-drawer-title">会员详情</div>
<button class="g-drawer-close" onclick="closeMmDrawer()"><i data-lucide="x" style="width:18px;height:18px;"></i></button>
</div>
<div class="g-drawer-bd">
<!-- 头部信息 -->
<div class="mm-drawer-header">
<div class="mm-drawer-avatar" id="mmDrAvatar"></div>
<div>
<div style="display:flex;align-items:center;gap:8px;">
<span class="mm-drawer-name" id="mmDrName">张**</span>
<span class="g-tag" id="mmDrLevel" style="color:#722ed1;background:#f9f0ff;border:1px solid #d3adf7;">钻石</span>
</div>
<div class="mm-drawer-meta" id="mmDrMeta">138****6721 | 注册于 2024-03-15</div>
</div>
</div>
<!-- 消费概览 -->
<div class="mm-section-hd">消费概览</div>
<div class="mm-overview" id="mmDrOverview">
<div class="mm-ov-item"><div class="mm-ov-val">&yen;15,280</div><div class="mm-ov-label">累计消费</div></div>
<div class="mm-ov-item"><div class="mm-ov-val">186</div><div class="mm-ov-label">消费次数</div></div>
<div class="mm-ov-item"><div class="mm-ov-val">&yen;82.2</div><div class="mm-ov-label">平均客单价</div></div>
<div class="mm-ov-item"><div class="mm-ov-val">&yen;2,350</div><div class="mm-ov-label">储值余额</div><div style="font-size:10px;color:var(--g-text-muted);margin-top:2px;">实充 &yen;2,000 | 赠金 &yen;350</div></div>
<div class="mm-ov-item"><div class="mm-ov-val">8,620</div><div class="mm-ov-label">积分</div></div>
</div>
<!-- 最近订单 -->
<div class="mm-section-hd">最近订单</div>
<table class="mm-mini-table">
<thead><tr><th>日期</th><th>订单号</th><th>金额</th><th>状态</th><th style="width:50px;">操作</th></tr></thead>
<tbody id="mmDrOrders">
<tr><td>2026-02-11</td><td>OD20260211086</td><td>&yen;68.00</td><td><span class="g-tag g-tag-green">已完成</span></td><td><a class="g-action" style="font-size:12px;">详情</a></td></tr>
<tr><td>2026-02-08</td><td>OD20260208052</td><td>&yen;95.50</td><td><span class="g-tag g-tag-green">已完成</span></td><td><a class="g-action" style="font-size:12px;">详情</a></td></tr>
<tr><td>2026-02-05</td><td>OD20260205031</td><td>&yen;42.00</td><td><span class="g-tag g-tag-green">已完成</span></td><td><a class="g-action" style="font-size:12px;">详情</a></td></tr>
</tbody>
</table>
<!-- 会员标签 -->
<div style="margin-top:20px;">
<div class="mm-section-hd">会员标签</div>
<div class="mm-tag-list" id="mmTagList">
<span class="g-pill checked" onclick="this.classList.toggle('checked')">高频<span class="mm-tag-remove" onclick="event.stopPropagation();this.parentElement.remove()"><i data-lucide="x" style="width:10px;height:10px;"></i></span></span>
<span class="g-pill checked" onclick="this.classList.toggle('checked')">午餐常客<span class="mm-tag-remove" onclick="event.stopPropagation();this.parentElement.remove()"><i data-lucide="x" style="width:10px;height:10px;"></i></span></span>
<span class="g-pill checked" onclick="this.classList.toggle('checked')">偏好辣味<span class="mm-tag-remove" onclick="event.stopPropagation();this.parentElement.remove()"><i data-lucide="x" style="width:10px;height:10px;"></i></span></span>
<span class="g-pill" onclick="this.classList.toggle('checked')">下午茶<span class="mm-tag-remove" onclick="event.stopPropagation();this.parentElement.remove()"><i data-lucide="x" style="width:10px;height:10px;"></i></span></span>
<span class="g-pill" onclick="this.classList.toggle('checked')">家庭聚餐<span class="mm-tag-remove" onclick="event.stopPropagation();this.parentElement.remove()"><i data-lucide="x" style="width:10px;height:10px;"></i></span></span>
<span class="g-pill" onclick="this.classList.toggle('checked')">外卖为主<span class="mm-tag-remove" onclick="event.stopPropagation();this.parentElement.remove()"><i data-lucide="x" style="width:10px;height:10px;"></i></span></span>
<button class="mm-tag-add" id="mmTagAddBtn" onclick="showMmTagInput()"><i data-lucide="plus" style="width:12px;height:12px;"></i>自定义标签</button>
</div>
</div>
</div>
<div class="g-drawer-ft">
<button class="g-btn" onclick="closeMmDrawer()">关闭</button>
</div>
</div>
<!-- 等级编辑抽屉 -->
<div class="g-drawer-mask" id="mmLevelMask" onclick="closeMmLevelDrawer()"></div>
<div class="g-drawer" id="mmLevelDrawer" style="width:520px;">
<div class="g-drawer-hd">
<div class="g-drawer-title" id="mmLevelTitle">编辑等级</div>
<button class="g-drawer-close" onclick="closeMmLevelDrawer()"><i data-lucide="x" style="width:18px;height:18px;"></i></button>
</div>
<div class="g-drawer-bd">
<div class="g-form-group">
<label class="g-form-label required">等级名称</label>
<input class="g-input" id="mmLevelName" placeholder="如:金卡会员">
</div>
<div class="g-form-group">
<label class="g-form-label">等级图标</label>
<div style="display:flex;gap:8px;">
<span class="g-pill checked" onclick="selectMmIcon(this)"><i data-lucide="user" style="width:14px;height:14px;"></i>默认</span>
<span class="g-pill" onclick="selectMmIcon(this)"><i data-lucide="award" style="width:14px;height:14px;"></i>奖章</span>
<span class="g-pill" onclick="selectMmIcon(this)"><i data-lucide="trophy" style="width:14px;height:14px;"></i>奖杯</span>
<span class="g-pill" onclick="selectMmIcon(this)"><i data-lucide="gem" style="width:14px;height:14px;"></i>钻石</span>
<span class="g-pill" onclick="selectMmIcon(this)"><i data-lucide="crown" style="width:14px;height:14px;"></i>皇冠</span>
<span class="g-pill" onclick="selectMmIcon(this)"><i data-lucide="star" style="width:14px;height:14px;"></i>星星</span>
</div>
</div>
<div class="g-form-group">
<label class="g-form-label">等级颜色</label>
<div style="display:flex;gap:8px;">
<span style="width:28px;height:28px;border-radius:50%;background:#999;cursor:pointer;border:2px solid transparent;" onclick="selectMmColor(this)"></span>
<span style="width:28px;height:28px;border-radius:50%;background:#1890ff;cursor:pointer;border:2px solid var(--primary);" onclick="selectMmColor(this)"></span>
<span style="width:28px;height:28px;border-radius:50%;background:#fa8c16;cursor:pointer;border:2px solid transparent;" onclick="selectMmColor(this)"></span>
<span style="width:28px;height:28px;border-radius:50%;background:#722ed1;cursor:pointer;border:2px solid transparent;" onclick="selectMmColor(this)"></span>
<span style="width:28px;height:28px;border-radius:50%;background:#eb2f96;cursor:pointer;border:2px solid transparent;" onclick="selectMmColor(this)"></span>
<span style="width:28px;height:28px;border-radius:50%;background:#f5222d;cursor:pointer;border:2px solid transparent;" onclick="selectMmColor(this)"></span>
</div>
</div>
<div class="g-form-group">
<label class="g-form-label required">升级规则</label>
<div class="mm-upgrade-section">
<div style="display:flex;gap:8px;margin-bottom:12px;">
<span class="g-pill checked" onclick="selectMmUpgradeType(this,'amount')">按累计消费金额</span>
<span class="g-pill" onclick="selectMmUpgradeType(this,'count')">按消费次数</span>
<span class="g-pill" onclick="selectMmUpgradeType(this,'both')">同时满足</span>
</div>
<div id="mmUpgradeAmount">
<div class="mm-upgrade-row">
<span>累计消费满</span>
<input class="g-input" type="number" style="width:120px;" placeholder="如2000" value="2000">
<span></span>
</div>
</div>
<div id="mmUpgradeCount" style="display:none;">
<div class="mm-upgrade-row">
<span>消费满</span>
<input class="g-input" type="number" style="width:120px;" placeholder="如50" value="50">
<span></span>
</div>
</div>
</div>
<div class="g-hint">达到条件自动升级连续90天未达标自动降一级</div>
</div>
<div class="g-form-group">
<label class="g-form-label">等级权益</label>
<!-- 折扣权益 -->
<div class="mm-perk-card">
<div class="mm-perk-card-hd">
<span class="mm-perk-label">折扣权益</span>
<div class="g-toggle on" onclick="toggleMmPerk(this)"></div>
</div>
<div class="mm-perk-card-bd">
<span>全场</span>
<input class="g-input" type="number" style="width:70px;text-align:center;" placeholder="9.5" value="9.5" step="0.1">
<span></span>
</div>
</div>
<!-- 积分倍率 -->
<div class="mm-perk-card">
<div class="mm-perk-card-hd">
<span class="mm-perk-label">积分倍率</span>
<div class="g-toggle on" onclick="toggleMmPerk(this)"></div>
</div>
<div class="mm-perk-card-bd">
<span>消费积分</span>
<input class="g-input" type="number" style="width:70px;text-align:center;" placeholder="1.5" value="1.5" step="0.1">
<span></span>
</div>
</div>
<!-- 生日特权 -->
<div class="mm-perk-card">
<div class="mm-perk-card-hd">
<span class="mm-perk-label">生日特权</span>
<div class="g-toggle on" onclick="toggleMmPerk(this)"></div>
</div>
<div class="mm-perk-card-bd">
<div style="display:flex;flex-direction:column;gap:8px;width:100%;">
<label style="display:flex;align-items:center;gap:6px;font-size:12px;cursor:pointer;">
<input type="checkbox" checked> 生日双倍积分
</label>
<label style="display:flex;align-items:center;gap:6px;font-size:12px;cursor:pointer;">
<input type="checkbox" checked> 生日赠券
</label>
<div style="display:flex;align-items:center;gap:6px;margin-left:20px;">
<div class="mm-coupon-mini">满30减5 <span class="mm-coupon-mini-remove" onclick="this.parentElement.remove()"><i data-lucide="x" style="width:10px;height:10px;"></i></span></div>
<button class="mm-add-coupon-link" onclick="openProductPicker({title:'选择优惠券',onConfirm:function(){}})"><i data-lucide="plus" style="width:11px;height:11px;"></i>选券</button>
</div>
</div>
</div>
</div>
<!-- 每月赠券 -->
<div class="mm-perk-card">
<div class="mm-perk-card-hd">
<span class="mm-perk-label">每月赠券</span>
<div class="g-toggle" onclick="toggleMmPerk(this)"></div>
</div>
<div class="mm-perk-card-bd hidden">
<div style="display:flex;flex-direction:column;gap:8px;width:100%;">
<div style="display:flex;align-items:center;gap:6px;font-size:12px;">
<span>每月</span>
<input class="g-input" type="number" style="width:50px;text-align:center;" placeholder="1" value="1">
<span>日自动发放以下优惠券:</span>
</div>
<div style="display:flex;align-items:center;gap:6px;">
<div class="mm-coupon-mini">满50减8 <span class="mm-coupon-mini-remove" onclick="this.parentElement.remove()"><i data-lucide="x" style="width:10px;height:10px;"></i></span></div>
<button class="mm-add-coupon-link" onclick="openProductPicker({title:'选择优惠券',onConfirm:function(){}})"><i data-lucide="plus" style="width:11px;height:11px;"></i>选券</button>
</div>
</div>
</div>
</div>
<!-- 免配送费 -->
<div class="mm-perk-card">
<div class="mm-perk-card-hd">
<span class="mm-perk-label">免配送费</span>
<div class="g-toggle" onclick="toggleMmPerk(this)"></div>
</div>
<div class="mm-perk-card-bd hidden">
<span>每月免配送费</span>
<input class="g-input" type="number" style="width:60px;text-align:center;" placeholder="5" value="5">
<span></span>
</div>
</div>
<!-- 优先配送 -->
<div class="mm-perk-card">
<div class="mm-perk-card-hd">
<span class="mm-perk-label">优先配送</span>
<div class="g-toggle" onclick="toggleMmPerk(this)"></div>
</div>
</div>
<!-- 专属客服 -->
<div class="mm-perk-card">
<div class="mm-perk-card-hd">
<span class="mm-perk-label">专属客服</span>
<div class="g-toggle" onclick="toggleMmPerk(this)"></div>
</div>
</div>
</div>
</div>
<div class="g-drawer-ft">
<button class="g-btn" onclick="closeMmLevelDrawer()">取消</button>
<button class="g-btn g-btn-primary" onclick="closeMmLevelDrawer()">保存</button>
</div>
</div>
<script>
/* Tab 切换 */
function switchMmTab(el, tab) {
el.parentElement.querySelectorAll('.g-seg-item').forEach(function(s){ s.classList.remove('active'); });
el.classList.add('active');
document.getElementById('mmTabList').style.display = tab === 'list' ? '' : 'none';
document.getElementById('mmTabLevels').style.display = tab === 'levels' ? '' : 'none';
if (typeof lucide !== 'undefined') lucide.createIcons();
}
/* 抽屉 */
function openMmDrawer(idx) {
document.getElementById('mmDrawerMask').classList.add('open');
document.getElementById('mmDrawer').classList.add('open');
if (typeof lucide !== 'undefined') lucide.createIcons();
}
function closeMmDrawer() {
document.getElementById('mmDrawerMask').classList.remove('open');
document.getElementById('mmDrawer').classList.remove('open');
}
/* 等级编辑抽屉 */
function openMmLevelDrawer(name) {
document.getElementById('mmLevelMask').classList.add('open');
document.getElementById('mmLevelDrawer').classList.add('open');
document.getElementById('mmLevelTitle').textContent = '编辑等级 - ' + name;
document.getElementById('mmLevelName').value = name;
if (typeof lucide !== 'undefined') lucide.createIcons();
}
function closeMmLevelDrawer() {
document.getElementById('mmLevelMask').classList.remove('open');
document.getElementById('mmLevelDrawer').classList.remove('open');
}
function selectMmUpgradeType(el, type) {
el.parentElement.querySelectorAll('.g-pill').forEach(function(p){ p.classList.remove('checked'); });
el.classList.add('checked');
document.getElementById('mmUpgradeAmount').style.display = (type === 'amount' || type === 'both') ? '' : 'none';
document.getElementById('mmUpgradeCount').style.display = (type === 'count' || type === 'both') ? '' : 'none';
}
function toggleMmPerk(toggleEl) {
toggleEl.classList.toggle('on');
var card = toggleEl.closest('.mm-perk-card');
var bd = card.querySelector('.mm-perk-card-bd');
if (bd) {
bd.classList.toggle('hidden', !toggleEl.classList.contains('on'));
}
}
function selectMmIcon(el) {
el.parentElement.querySelectorAll('.g-pill').forEach(function(p){ p.classList.remove('checked'); });
el.classList.add('checked');
}
function selectMmColor(el) {
el.parentElement.querySelectorAll('span').forEach(function(s){ s.style.borderColor = 'transparent'; });
el.style.borderColor = el.style.background;
}
function addMmPerk() {
var row = document.createElement('div');
row.className = 'mm-perk-row';
row.innerHTML = '<input class="g-input" placeholder="请输入权益描述"><button class="mm-perk-remove" onclick="this.parentElement.remove()"><i data-lucide="trash-2" style="width:14px;height:14px;"></i></button>';
document.getElementById('mmPerkList').appendChild(row);
if (typeof lucide !== 'undefined') lucide.createIcons();
}
/* 自定义标签 */
function showMmTagInput() {
var btn = document.getElementById('mmTagAddBtn');
var input = document.createElement('input');
input.className = 'mm-tag-input';
input.placeholder = '输入标签名';
input.onkeydown = function(e) {
if (e.key === 'Enter' && this.value.trim()) {
var tag = document.createElement('span');
tag.className = 'g-pill checked';
tag.onclick = function(){ this.classList.toggle('checked'); };
tag.textContent = this.value.trim();
var rm = document.createElement('span');
rm.className = 'mm-tag-remove';
rm.innerHTML = '<i data-lucide="x" style="width:10px;height:10px;"></i>';
rm.onclick = function(e){ e.stopPropagation(); this.parentElement.remove(); };
tag.appendChild(rm);
btn.parentElement.insertBefore(tag, btn);
this.remove();
btn.style.display = '';
if (typeof lucide !== 'undefined') lucide.createIcons();
}
if (e.key === 'Escape') { this.remove(); btn.style.display = ''; }
};
input.onblur = function() { this.remove(); btn.style.display = ''; };
btn.style.display = 'none';
btn.parentElement.insertBefore(input, btn);
input.focus();
}
if (typeof lucide !== 'undefined') lucide.createIcons();
</script>

436
pages/mbr-messaging.html Normal file
View File

@@ -0,0 +1,436 @@
<!-- 消息触达页 -->
<style>
.msg-toolbar { display:flex; align-items:center; gap:10px; margin-bottom:16px; background:#fff; border-radius:10px; box-shadow:var(--g-shadow-sm); padding:12px 16px; flex-wrap:wrap; }
.msg-spacer { flex:1; }
.msg-stats { display:grid; grid-template-columns:repeat(4,1fr); gap:14px; margin-bottom:16px; }
.msg-stat-card { background:#fff; border-radius:10px; box-shadow:var(--g-shadow-sm); padding:16px 20px; transition:box-shadow var(--g-transition), transform var(--g-transition); }
.msg-stat-card:hover { box-shadow:var(--g-shadow-md); transform:translateY(-1px); }
.msg-stat-label { font-size:12px; color:var(--g-text-muted); margin-bottom:6px; }
.msg-stat-value { font-size:22px; font-weight:700; color:var(--g-text); }
.msg-stat-value .msg-stat-unit { font-size:13px; font-weight:400; color:var(--g-text-secondary); margin-left:2px; }
.msg-channel-dot { display:inline-block; width:6px; height:6px; border-radius:50%; margin-right:4px; vertical-align:middle; }
.msg-open-rate { font-size:11px; color:var(--g-text-muted); margin-top:2px; }
/* Template cards */
.msg-tpl-grid { display:grid; grid-template-columns:1fr 1fr; gap:14px; }
.msg-tpl-card { background:#fff; border-radius:10px; box-shadow:var(--g-shadow-sm); padding:18px 20px; transition:box-shadow var(--g-transition), transform var(--g-transition); display:flex; flex-direction:column; }
.msg-tpl-card:hover { box-shadow:var(--g-shadow-md); transform:translateY(-1px); }
.msg-tpl-hd { display:flex; align-items:center; gap:8px; margin-bottom:10px; }
.msg-tpl-name { font-size:14px; font-weight:600; color:var(--g-text); flex:1; }
.msg-tpl-preview { font-size:12px; color:var(--g-text-secondary); line-height:1.6; display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden; margin-bottom:12px; flex:1; }
.msg-tpl-ft { display:flex; align-items:center; justify-content:space-between; margin-top:auto; }
.msg-tpl-usage { font-size:12px; color:var(--g-text-muted); }
.msg-tpl-actions { display:flex; align-items:center; gap:10px; }
.msg-tpl-actions a { font-size:12px; color:var(--primary); cursor:pointer; text-decoration:none; transition:var(--g-transition); }
.msg-tpl-actions a:hover { text-decoration:underline; }
.msg-tpl-actions a.msg-tpl-danger { color:var(--g-danger); }
/* Drawer: tag pills for target audience */
.msg-tag-pills { display:flex; flex-wrap:wrap; gap:8px; margin-top:10px; }
.msg-reach { font-size:12px; color:var(--primary); margin-top:10px; display:flex; align-items:center; gap:4px; }
.msg-tpl-link { font-size:13px; color:var(--primary); cursor:pointer; text-decoration:none; display:inline-flex; align-items:center; gap:4px; margin-top:8px; transition:var(--g-transition); }
.msg-tpl-link:hover { text-decoration:underline; }
</style>
<div>
<!-- Tabs -->
<div class="g-seg" style="margin-bottom:16px;">
<div class="g-seg-item active" onclick="switchMsgTab(this,'list')">消息列表</div>
<div class="g-seg-item" onclick="switchMsgTab(this,'tpl')">消息模板</div>
</div>
<!-- TAB 1: 消息列表 -->
<div id="msgTabList">
<div class="msg-toolbar">
<select class="g-select" style="min-width:100px">
<option>全部状态</option>
<option>已发送</option>
<option>待发送</option>
<option>草稿</option>
</select>
<select class="g-select" style="min-width:100px">
<option>全部渠道</option>
<option>站内信</option>
<option>短信</option>
<option>微信</option>
</select>
<input class="g-input" placeholder="搜索消息标题" style="width:180px" />
<span class="msg-spacer"></span>
<button class="g-btn g-btn-primary" onclick="openMsgDrawer()"><i data-lucide="plus" style="width:14px;height:14px;vertical-align:-2px;margin-right:2px;"></i> 创建消息</button>
</div>
<!-- Stats -->
<div class="msg-stats">
<div class="msg-stat-card">
<div class="msg-stat-label">本月发送</div>
<div class="msg-stat-value">12<span class="msg-stat-unit"></span></div>
</div>
<div class="msg-stat-card">
<div class="msg-stat-label">触达人数</div>
<div class="msg-stat-value">3,680<span class="msg-stat-unit"></span></div>
</div>
<div class="msg-stat-card">
<div class="msg-stat-label">打开率</div>
<div class="msg-stat-value">45.2<span class="msg-stat-unit">%</span></div>
</div>
<div class="msg-stat-card">
<div class="msg-stat-label">转化率</div>
<div class="msg-stat-value">12.8<span class="msg-stat-unit">%</span></div>
</div>
</div>
<!-- Table -->
<div class="g-card">
<table class="g-table">
<thead>
<tr>
<th>消息标题</th>
<th>推送渠道</th>
<th>目标人群</th>
<th>触达人数</th>
<th>发送时间</th>
<th>状态</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<div style="font-weight:500;color:var(--g-text)">新品上线通知</div>
<div class="msg-open-rate">打开率 52.3% | 转化率 15.6%</div>
</td>
<td><span class="g-tag g-tag-blue">站内信</span></td>
<td>全部会员</td>
<td>1,280</td>
<td>2026-02-10 10:00</td>
<td><span class="g-tag g-tag-green">已发送</span></td>
<td><a class="g-action">详情</a></td>
</tr>
<tr>
<td>
<div style="font-weight:500;color:var(--g-text)">春节优惠活动</div>
<div class="msg-open-rate">打开率 48.1% | 转化率 18.2%</div>
</td>
<td><span class="g-tag g-tag-green">短信</span></td>
<td>全部会员</td>
<td>2,400</td>
<td>2026-02-08 09:30</td>
<td><span class="g-tag g-tag-green">已发送</span></td>
<td><a class="g-action">详情</a></td>
</tr>
<tr>
<td>
<div style="font-weight:500;color:var(--g-text)">沉睡客户召回</div>
<div class="msg-open-rate">打开率 35.7% | 转化率 8.4%</div>
</td>
<td><span class="g-tag g-tag-orange">微信模板</span></td>
<td>沉睡客户(456人)</td>
<td>456</td>
<td>2026-02-05 14:00</td>
<td><span class="g-tag g-tag-green">已发送</span></td>
<td><a class="g-action">详情</a></td>
</tr>
<tr>
<td>
<div style="font-weight:500;color:var(--g-text)">会员日提醒</div>
</td>
<td><span class="g-tag g-tag-blue">站内信</span></td>
<td>高频客户(328人)</td>
<td>328</td>
<td>2026-02-15 08:00</td>
<td><span class="g-tag g-tag-blue">待发送</span></td>
<td><a class="g-action">详情</a><a class="g-action-danger">删除</a></td>
</tr>
<tr>
<td>
<div style="font-weight:500;color:var(--g-text)">储值优惠推送</div>
</td>
<td><span class="g-tag g-tag-green">短信</span></td>
<td>全部会员</td>
<td></td>
<td></td>
<td><span class="g-tag g-tag-gray">草稿</span></td>
<td><a class="g-action">详情</a><a class="g-action">编辑</a><a class="g-action-danger">删除</a></td>
</tr>
<tr>
<td>
<div style="font-weight:500;color:var(--g-text)">新客欢迎礼</div>
</td>
<td><span class="g-tag g-tag-orange">微信模板</span></td>
<td>新客(86人)</td>
<td></td>
<td></td>
<td><span class="g-tag g-tag-gray">草稿</span></td>
<td><a class="g-action">详情</a><a class="g-action">编辑</a><a class="g-action-danger">删除</a></td>
</tr>
</tbody>
</table>
<div class="g-pagination" style="margin-top:16px;">
<span style="font-size:13px;color:var(--g-text-secondary);">共 6 条</span>
<button class="g-page-btn" disabled>&lt;</button>
<button class="g-page-btn active">1</button>
<button class="g-page-btn">&gt;</button>
</div>
</div>
</div>
<!-- TAB 2: 消息模板 -->
<div id="msgTabTpl" style="display:none">
<div class="msg-toolbar">
<select class="g-select" style="min-width:100px">
<option>全部分类</option>
<option>营销</option>
<option>通知</option>
<option>召回</option>
</select>
<input class="g-input" placeholder="搜索模板名称" style="width:180px" />
<span class="msg-spacer"></span>
<button class="g-btn g-btn-primary"><i data-lucide="plus" style="width:14px;height:14px;vertical-align:-2px;margin-right:2px;"></i> 新建模板</button>
</div>
<div class="msg-tpl-grid">
<!-- Template 1 -->
<div class="msg-tpl-card">
<div class="msg-tpl-hd">
<span class="msg-tpl-name">新品上线通知</span>
<span class="g-tag g-tag-blue">通知</span>
</div>
<div class="msg-tpl-preview">亲爱的{name},我们上新啦!{product}现已上线,口感升级,诚意满满,快来尝鲜吧!</div>
<div class="msg-tpl-ft">
<span class="msg-tpl-usage"><i data-lucide="bar-chart-2" style="width:12px;height:12px;vertical-align:-1px;margin-right:2px;"></i>已使用 28次</span>
<div class="msg-tpl-actions">
<button class="g-btn g-btn-sm g-btn-primary" onclick="useMsgTemplate('新品上线通知','亲爱的{name},我们上新啦!{product}现已上线,口感升级,诚意满满,快来尝鲜吧!')">使用</button>
<a>编辑</a>
<a class="msg-tpl-danger">删除</a>
</div>
</div>
</div>
<!-- Template 2 -->
<div class="msg-tpl-card">
<div class="msg-tpl-hd">
<span class="msg-tpl-name">节日问候</span>
<span class="g-tag g-tag-orange">营销</span>
</div>
<div class="msg-tpl-preview">亲爱的{name}{holiday}快乐!为您准备了专属优惠,下单立享折扣,温暖过节从一顿好饭开始!</div>
<div class="msg-tpl-ft">
<span class="msg-tpl-usage"><i data-lucide="bar-chart-2" style="width:12px;height:12px;vertical-align:-1px;margin-right:2px;"></i>已使用 15次</span>
<div class="msg-tpl-actions">
<button class="g-btn g-btn-sm g-btn-primary" onclick="useMsgTemplate('节日问候','亲爱的{name}{holiday}快乐!为您准备了专属优惠,下单立享折扣,温暖过节从一顿好饭开始!')">使用</button>
<a>编辑</a>
<a class="msg-tpl-danger">删除</a>
</div>
</div>
</div>
<!-- Template 3 -->
<div class="msg-tpl-card">
<div class="msg-tpl-hd">
<span class="msg-tpl-name">沉睡客户召回</span>
<span class="g-tag g-tag-red">召回</span>
</div>
<div class="msg-tpl-preview">{name},好久不见!我们很想念您,特送上一份专属回归礼券,期待与您再次相遇!</div>
<div class="msg-tpl-ft">
<span class="msg-tpl-usage"><i data-lucide="bar-chart-2" style="width:12px;height:12px;vertical-align:-1px;margin-right:2px;"></i>已使用 42次</span>
<div class="msg-tpl-actions">
<button class="g-btn g-btn-sm g-btn-primary" onclick="useMsgTemplate('沉睡客户召回','{name},好久不见!我们很想念您,特送上一份专属回归礼券,期待与您再次相遇!')">使用</button>
<a>编辑</a>
<a class="msg-tpl-danger">删除</a>
</div>
</div>
</div>
<!-- Template 4 -->
<div class="msg-tpl-card">
<div class="msg-tpl-hd">
<span class="msg-tpl-name">储值优惠提醒</span>
<span class="g-tag g-tag-orange">营销</span>
</div>
<div class="msg-tpl-preview">限时充值优惠!充{amount}送{gift},多充多送,优惠截止本周日,别错过!</div>
<div class="msg-tpl-ft">
<span class="msg-tpl-usage"><i data-lucide="bar-chart-2" style="width:12px;height:12px;vertical-align:-1px;margin-right:2px;"></i>已使用 19次</span>
<div class="msg-tpl-actions">
<button class="g-btn g-btn-sm g-btn-primary" onclick="useMsgTemplate('储值优惠提醒','限时充值优惠!充{amount}送{gift},多充多送,优惠截止本周日,别错过!')">使用</button>
<a>编辑</a>
<a class="msg-tpl-danger">删除</a>
</div>
</div>
</div>
<!-- Template 5 -->
<div class="msg-tpl-card">
<div class="msg-tpl-hd">
<span class="msg-tpl-name">订单完成感谢</span>
<span class="g-tag g-tag-blue">通知</span>
</div>
<div class="msg-tpl-preview">感谢您的订单!期待下次光临,如有任何问题请随时联系我们,祝您用餐愉快!</div>
<div class="msg-tpl-ft">
<span class="msg-tpl-usage"><i data-lucide="bar-chart-2" style="width:12px;height:12px;vertical-align:-1px;margin-right:2px;"></i>已使用 56次</span>
<div class="msg-tpl-actions">
<button class="g-btn g-btn-sm g-btn-primary" onclick="useMsgTemplate('订单完成感谢','感谢您的订单!期待下次光临,如有任何问题请随时联系我们,祝您用餐愉快!')">使用</button>
<a>编辑</a>
<a class="msg-tpl-danger">删除</a>
</div>
</div>
</div>
<!-- Template 6 -->
<div class="msg-tpl-card">
<div class="msg-tpl-hd">
<span class="msg-tpl-name">会员升级通知</span>
<span class="g-tag g-tag-blue">通知</span>
</div>
<div class="msg-tpl-preview">恭喜{name}升级为{level}会员!解锁更多专属权益,感谢您一路以来的支持与信赖!</div>
<div class="msg-tpl-ft">
<span class="msg-tpl-usage"><i data-lucide="bar-chart-2" style="width:12px;height:12px;vertical-align:-1px;margin-right:2px;"></i>已使用 33次</span>
<div class="msg-tpl-actions">
<button class="g-btn g-btn-sm g-btn-primary" onclick="useMsgTemplate('会员升级通知','恭喜{name}升级为{level}会员!解锁更多专属权益,感谢您一路以来的支持与信赖!')">使用</button>
<a>编辑</a>
<a class="msg-tpl-danger">删除</a>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Drawer mask -->
<div class="g-drawer-mask" id="msgDrawerMask" onclick="closeMsgDrawer()"></div>
<!-- Create message drawer -->
<div class="g-drawer" id="msgDrawer" style="width:560px;">
<div class="g-drawer-hd">
<span class="g-drawer-title">创建消息</span>
<button class="g-drawer-close" onclick="closeMsgDrawer()"><i data-lucide="x" style="width:18px;height:18px;"></i></button>
</div>
<div class="g-drawer-bd">
<!-- 消息标题 -->
<div class="g-form-group">
<label class="g-form-label required">消息标题</label>
<input class="g-input" id="msgTitle" placeholder="如:新品上线通知" />
</div>
<!-- 推送渠道 -->
<div class="g-form-group">
<label class="g-form-label required">推送渠道</label>
<div style="display:flex;gap:8px;">
<span class="g-pill checked" onclick="selectMsgChannel(this)">站内信</span>
<span class="g-pill" onclick="selectMsgChannel(this)">短信</span>
<span class="g-pill" onclick="selectMsgChannel(this)">微信模板消息</span>
</div>
</div>
<!-- 目标人群 -->
<div class="g-form-group">
<label class="g-form-label required">目标人群</label>
<select class="g-select" id="msgTargetSelect" onchange="selectMsgTarget(this)">
<option value="all">全部会员</option>
<option value="tag">按标签筛选</option>
</select>
<div id="msgTagSection" style="display:none;">
<div class="msg-tag-pills">
<span class="g-pill" onclick="toggleMsgTagPill(this)">高频客户</span>
<span class="g-pill" onclick="toggleMsgTagPill(this)">新客</span>
<span class="g-pill" onclick="toggleMsgTagPill(this)">沉睡客户</span>
<span class="g-pill" onclick="toggleMsgTagPill(this)">流失客户</span>
<span class="g-pill" onclick="toggleMsgTagPill(this)">午餐常客</span>
<span class="g-pill" onclick="toggleMsgTagPill(this)">大额消费</span>
</div>
<div class="msg-reach" id="msgReach">
<i data-lucide="users" style="width:14px;height:14px;"></i>
<span>预计触达 456人</span>
</div>
</div>
</div>
<!-- 消息内容 -->
<div class="g-form-group">
<label class="g-form-label required">消息内容</label>
<textarea class="g-textarea" id="msgContent" rows="5" placeholder="支持变量:{name}姓名 {level}等级"></textarea>
<div class="g-hint">可使用变量实现个性化推送</div>
</div>
<!-- 发送时间 -->
<div class="g-form-group">
<label class="g-form-label required">发送时间</label>
<div style="display:flex;gap:8px;">
<span class="g-pill checked" onclick="selectMsgSendTime(this,'now')">立即发送</span>
<span class="g-pill" onclick="selectMsgSendTime(this,'scheduled')">定时发送</span>
</div>
<div id="msgScheduleTime" style="display:none;margin-top:10px;">
<input class="g-input" type="datetime-local" style="width:240px;" />
</div>
</div>
<!-- 选择模板 -->
<div class="g-form-group">
<a class="msg-tpl-link" onclick="switchMsgTab(document.querySelectorAll('.g-seg-item')[1],'tpl');closeMsgDrawer();">
<i data-lucide="file-text" style="width:14px;height:14px;"></i>
从模板选择
</a>
</div>
</div>
<div class="g-drawer-ft">
<button class="g-btn" onclick="closeMsgDrawer()">存为草稿</button>
<button class="g-btn g-btn-primary" onclick="closeMsgDrawer()">发送</button>
</div>
</div>
<script>
/* Tab switching */
function switchMsgTab(el, tab) {
document.querySelectorAll('.g-seg-item').forEach(function(s){ s.classList.remove('active'); });
el.classList.add('active');
document.getElementById('msgTabList').style.display = tab === 'list' ? '' : 'none';
document.getElementById('msgTabTpl').style.display = tab === 'tpl' ? '' : 'none';
}
/* Drawer open/close */
function openMsgDrawer() {
document.getElementById('msgDrawerMask').classList.add('open');
document.getElementById('msgDrawer').classList.add('open');
}
function closeMsgDrawer() {
document.getElementById('msgDrawerMask').classList.remove('open');
document.getElementById('msgDrawer').classList.remove('open');
}
/* Channel pill: single select */
function selectMsgChannel(el) {
var pills = el.parentElement.querySelectorAll('.g-pill');
pills.forEach(function(p){ p.classList.remove('checked'); });
el.classList.add('checked');
}
/* Target audience */
function selectMsgTarget(el) {
var section = document.getElementById('msgTagSection');
section.style.display = el.value === 'tag' ? '' : 'none';
}
/* Tag pill toggle (multi-select) */
function toggleMsgTagPill(el) {
el.classList.toggle('checked');
var count = el.parentElement.querySelectorAll('.g-pill.checked').length;
var reachMap = [0, 456, 712, 980, 1240, 1580, 1860];
var reach = reachMap[count] || 0;
document.getElementById('msgReach').querySelector('span').textContent = '预计触达 ' + reach + '人';
}
/* Send time pill: single select */
function selectMsgSendTime(el, mode) {
var pills = el.parentElement.querySelectorAll('.g-pill');
pills.forEach(function(p){ p.classList.remove('checked'); });
el.classList.add('checked');
document.getElementById('msgScheduleTime').style.display = mode === 'scheduled' ? '' : 'none';
}
/* Use template: pre-fill drawer */
function useMsgTemplate(title, content) {
openMsgDrawer();
document.getElementById('msgTitle').value = title;
document.getElementById('msgContent').value = content;
}
if (typeof lucide !== 'undefined') lucide.createIcons();
</script>

771
pages/mbr-points.html Normal file
View File

@@ -0,0 +1,771 @@
<!-- 积分商城页 -->
<style>
.page-pt { max-width:1100px; }
/* 分段 Tab */
.pt-seg { display:flex; background:#f5f5f5; border-radius:var(--g-radius); overflow:hidden; border:1px solid #e8e8e8; width:fit-content; margin-bottom:16px; }
.pt-seg-item {
padding:8px 28px; font-size:13px; cursor:pointer; color:var(--g-text-muted);
transition:all var(--g-transition); user-select:none; border-right:1px solid #e8e8e8;
background:transparent; border-top:none; border-bottom:none; border-left:none;
}
.pt-seg-item:last-child { border-right:none; }
.pt-seg-item:hover { color:var(--g-text-secondary); background:#f0f0f0; }
.pt-seg-item.active { background:#fff; color:var(--primary); font-weight:600; box-shadow:0 1px 4px rgba(0,0,0,.06); }
/* 统计卡片 */
.pt-stats { display:grid; grid-template-columns:repeat(4,1fr); gap:12px; margin-bottom:16px; }
.pt-stat-card {
background:#fff; border-radius:10px; padding:16px 20px;
box-shadow:var(--g-shadow-sm); transition:all var(--g-transition);
}
.pt-stat-card:hover { box-shadow:var(--g-shadow-md); transform:translateY(-1px); }
.pt-stat-label { font-size:13px; color:var(--g-text-muted); margin-bottom:6px; }
.pt-stat-value { font-size:24px; font-weight:700; color:var(--g-text); }
.pt-stat-value.primary { color:var(--primary); }
.pt-stat-value.green { color:var(--g-success); }
.pt-stat-value.orange { color:var(--g-warning); }
/* 规则卡片 section 标题 */
.pt-section-hd {
font-size:15px; font-weight:600; color:var(--g-text);
padding-left:10px; border-left:3px solid var(--primary); margin-bottom:16px;
}
/* 规则行 */
.pt-rule-row {
display:flex; align-items:center; gap:12px; padding:14px 0;
border-bottom:1px solid #f5f5f5; font-size:13px; color:var(--g-text);
}
.pt-rule-row:last-child { border-bottom:none; }
.pt-rule-row .g-toggle { flex-shrink:0; }
.pt-rule-label { min-width:110px; font-weight:500; flex-shrink:0; }
.pt-rule-row input[type="number"] {
width:70px; height:32px; padding:0 8px; border:1px solid #d9d9d9; border-radius:var(--g-radius-sm);
font-size:13px; outline:none; text-align:center; transition:all var(--g-transition);
}
.pt-rule-row input[type="number"]:focus { border-color:var(--primary); box-shadow:0 0 0 3px color-mix(in srgb, var(--primary) 10%, transparent); }
.pt-rule-text { color:var(--g-text-secondary); }
/* 有效期 pill */
.pt-expiry-pills { display:flex; gap:10px; margin-bottom:12px; }
/* 工具栏 */
.pt-toolbar {
display:flex; align-items:center; gap:12px; flex-wrap:wrap; margin-bottom:16px;
box-shadow:var(--g-shadow-sm); border-radius:10px; padding:10px 14px; background:#fff;
}
.pt-toolbar select,
.pt-toolbar input[type="text"] {
height:34px; padding:0 10px; border:1px solid #e5e7eb; border-radius:8px;
font-size:13px; outline:none; transition:all var(--g-transition); background:#fff;
}
.pt-toolbar select:focus,
.pt-toolbar input[type="text"]:focus { border-color:var(--primary); box-shadow:0 0 0 3px color-mix(in srgb, var(--primary) 12%, transparent); }
/* 商品网格 */
.pt-grid { display:grid; grid-template-columns:repeat(3,1fr); gap:16px; margin-bottom:16px; }
.pt-prod-card {
background:#fff; border-radius:10px; overflow:hidden;
box-shadow:var(--g-shadow-sm); transition:all var(--g-transition);
}
.pt-prod-card:hover { box-shadow:var(--g-shadow-md); transform:translateY(-1px); }
.pt-prod-card.pt-off { opacity:.55; }
.pt-prod-img {
width:100%; height:120px; background:#f0f0f0; display:flex;
align-items:center; justify-content:center; color:#ccc;
}
.pt-prod-bd { padding:14px 16px; }
.pt-prod-name { font-size:14px; font-weight:600; color:var(--g-text); margin-bottom:8px; display:flex; align-items:center; gap:6px; }
.pt-type-tag { font-size:11px; font-weight:500; padding:1px 6px; border-radius:4px; flex-shrink:0; }
.pt-type-tag.blue { background:color-mix(in srgb, var(--primary) 12%, #fff); color:var(--primary); }
.pt-type-tag.green { background:color-mix(in srgb, var(--g-success) 12%, #fff); color:var(--g-success); }
.pt-type-tag.orange { background:color-mix(in srgb, var(--g-warning) 12%, #fff); color:var(--g-warning); }
.pt-prod-price { font-size:18px; font-weight:700; color:var(--primary); margin-bottom:6px; }
.pt-prod-price .pt-cash { font-size:13px; font-weight:500; color:var(--g-text-secondary); margin-left:4px; }
.pt-prod-meta { display:flex; gap:16px; font-size:12px; color:var(--g-text-muted); margin-bottom:10px; }
.pt-prod-ft {
display:flex; align-items:center; gap:6px; padding-top:10px;
border-top:1px solid #f5f5f5;
}
.pt-prod-ft .g-tag { margin-left:auto; }
/* 抽屉内上传占位 */
.pt-upload-placeholder {
width:100%; height:120px; border:2px dashed #d9d9d9; border-radius:var(--g-radius);
display:flex; flex-direction:column; align-items:center; justify-content:center;
color:var(--g-text-muted); font-size:13px; gap:6px; cursor:pointer;
transition:all var(--g-transition); background:var(--g-bg-subtle);
}
.pt-upload-placeholder:hover { border-color:var(--primary); color:var(--primary); background:color-mix(in srgb, var(--primary) 4%, #fff); }
/* 兑换方式 pill 组 */
.pt-exchange-pills { display:flex; gap:10px; }
/* 通知 pill 多选 */
.pt-notify-pills { display:flex; gap:10px; }
.pt-notify-pills .g-pill { position:relative; padding-left:24px; }
.pt-notify-pills .g-pill::before {
content:''; position:absolute; left:8px; top:50%; transform:translateY(-50%);
width:12px; height:12px; border:1.5px solid #ccc; border-radius:3px; transition:all var(--g-transition);
}
.pt-notify-pills .g-pill.checked::before {
background:var(--primary); border-color:var(--primary);
background-image:url("data:image/svg+xml,%3Csvg viewBox='0 0 12 12' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M2.5 6L5 8.5L9.5 4' stroke='white' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}
/* 兑换记录表格 */
.pt-rec-status { display:inline-flex; align-items:center; gap:4px; font-size:12px; }
.pt-rec-status::before { content:''; width:6px; height:6px; border-radius:50%; }
.pt-rec-status.green::before { background:var(--g-success); }
.pt-rec-status.orange::before { background:var(--g-warning); }
.pt-rec-status.gray::before { background:#bbb; }
.pt-rec-status.red::before { background:var(--g-danger); }
.pt-rec-member { display:flex; flex-direction:column; gap:2px; }
.pt-rec-member-name { font-size:13px; font-weight:500; color:var(--g-text); }
.pt-rec-member-phone { font-size:12px; color:var(--g-text-muted); }
/* 保存栏 */
.pt-save-bar { margin-top:20px; display:flex; justify-content:flex-end; }
</style>
<div class="page-pt">
<!-- 分段 Tab -->
<div class="pt-seg">
<button class="pt-seg-item active" onclick="switchPtTab(this, 'rules')">积分规则</button>
<button class="pt-seg-item" onclick="switchPtTab(this, 'products')">兑换商品</button>
<button class="pt-seg-item" onclick="switchPtTab(this, 'records')">兑换记录</button>
</div>
<!-- ==================== TAB 1: 积分规则 ==================== -->
<div id="ptTabRules">
<!-- 统计卡片 -->
<div class="pt-stats">
<div class="pt-stat-card">
<div class="pt-stat-label">累计发放积分</div>
<div class="pt-stat-value primary">128,600</div>
</div>
<div class="pt-stat-card">
<div class="pt-stat-label">已兑换积分</div>
<div class="pt-stat-value orange">45,200</div>
</div>
<div class="pt-stat-card">
<div class="pt-stat-label">积分用户</div>
<div class="pt-stat-value">856人</div>
</div>
<div class="pt-stat-card">
<div class="pt-stat-label">兑换率</div>
<div class="pt-stat-value green">35.1%</div>
</div>
</div>
<!-- 积分获取规则 -->
<div class="g-card" style="margin-bottom:16px;">
<div class="pt-section-hd">积分获取规则</div>
<div class="pt-rule-row">
<div class="g-toggle on" onclick="toggleSwitch(this)"></div>
<span class="pt-rule-label">消费获取</span>
<span class="pt-rule-text">每消费</span>
<input type="number" value="1" min="0" placeholder="如1">
<span class="pt-rule-text">元获得</span>
<input type="number" value="1" min="0" placeholder="如1">
<span class="pt-rule-text">积分</span>
</div>
<div class="pt-rule-row">
<div class="g-toggle on" onclick="toggleSwitch(this)"></div>
<span class="pt-rule-label">评价奖励</span>
<span class="pt-rule-text">发表评价奖励</span>
<input type="number" value="10" min="0" placeholder="如10">
<span class="pt-rule-text">积分</span>
</div>
<div class="pt-rule-row">
<div class="g-toggle on" onclick="toggleSwitch(this)"></div>
<span class="pt-rule-label">注册奖励</span>
<span class="pt-rule-text">新用户注册赠送</span>
<input type="number" value="100" min="0" placeholder="如100">
<span class="pt-rule-text">积分</span>
</div>
<div class="pt-rule-row">
<div class="g-toggle" onclick="toggleSwitch(this)"></div>
<span class="pt-rule-label">签到奖励</span>
<span class="pt-rule-text">每日签到奖励</span>
<input type="number" value="5" min="0" placeholder="如5">
<span class="pt-rule-text">积分</span>
</div>
</div>
<!-- 积分有效期 -->
<div class="g-card" style="margin-bottom:16px;">
<div class="pt-section-hd">积分有效期</div>
<div class="pt-expiry-pills">
<span class="g-pill" onclick="selectPtExpiry(this)">永久有效</span>
<span class="g-pill checked" onclick="selectPtExpiry(this)">按年清零</span>
</div>
<div class="g-hint" id="ptExpiryHint">每年12月31日清零当年获得的积分请提前通知会员</div>
</div>
<!-- 保存按钮 -->
<div class="pt-save-bar">
<button class="g-btn g-btn-primary g-btn-lg">
<i data-lucide="save" style="width:15px;height:15px;"></i>保存规则
</button>
</div>
</div>
<!-- ==================== TAB 2: 兑换商品 ==================== -->
<div id="ptTabProducts" style="display:none;">
<!-- 工具栏 -->
<div class="pt-toolbar">
<select style="width:130px;">
<option value="">全部状态</option>
<option>上架</option>
<option>下架</option>
</select>
<input type="text" placeholder="搜索兑换商品" style="width:200px;">
<div style="flex:1;"></div>
<button class="g-btn g-btn-primary" onclick="openPtDrawer()">
<i data-lucide="plus" style="width:14px;height:14px;"></i>添加兑换商品
</button>
</div>
<!-- 商品网格 -->
<div class="pt-grid">
<!-- 商品1 -->
<div class="pt-prod-card">
<div class="pt-prod-img"><i data-lucide="image" style="width:32px;height:32px;"></i></div>
<div class="pt-prod-bd">
<div class="pt-prod-name">可乐兑换券<span class="pt-type-tag blue">商品</span></div>
<div class="pt-prod-price">100积分</div>
<div class="pt-prod-meta">
<span>库存 50</span>
<span>已兑换 28</span>
</div>
<div class="pt-prod-ft">
<a class="g-action" onclick="openPtDrawer()">编辑</a>
<a class="g-action">下架</a>
<a class="g-action g-action-danger">删除</a>
<span class="g-tag g-tag-green">上架</span>
</div>
</div>
</div>
<!-- 商品2 -->
<div class="pt-prod-card">
<div class="pt-prod-img"><i data-lucide="image" style="width:32px;height:32px;"></i></div>
<div class="pt-prod-bd">
<div class="pt-prod-name">凉拌黄瓜<span class="pt-type-tag blue">商品</span></div>
<div class="pt-prod-price">200积分</div>
<div class="pt-prod-meta">
<span>库存 30</span>
<span>已兑换 45</span>
</div>
<div class="pt-prod-ft">
<a class="g-action" onclick="openPtDrawer()">编辑</a>
<a class="g-action">下架</a>
<a class="g-action g-action-danger">删除</a>
<span class="g-tag g-tag-green">上架</span>
</div>
</div>
</div>
<!-- 商品3 -->
<div class="pt-prod-card">
<div class="pt-prod-img"><i data-lucide="image" style="width:32px;height:32px;"></i></div>
<div class="pt-prod-bd">
<div class="pt-prod-name">满20减5券<span class="pt-type-tag green">优惠券</span></div>
<div class="pt-prod-price">300积分</div>
<div class="pt-prod-meta">
<span>库存 100</span>
<span>已兑换 67</span>
</div>
<div class="pt-prod-ft">
<a class="g-action" onclick="openPtDrawer()">编辑</a>
<a class="g-action">下架</a>
<a class="g-action g-action-danger">删除</a>
<span class="g-tag g-tag-green">上架</span>
</div>
</div>
</div>
<!-- 商品4 -->
<div class="pt-prod-card">
<div class="pt-prod-img"><i data-lucide="image" style="width:32px;height:32px;"></i></div>
<div class="pt-prod-bd">
<div class="pt-prod-name">招牌奶茶<span class="pt-type-tag blue">商品</span></div>
<div class="pt-prod-price">500积分<span class="pt-cash">+ ¥3.00</span></div>
<div class="pt-prod-meta">
<span>库存 20</span>
<span>已兑换 12</span>
</div>
<div class="pt-prod-ft">
<a class="g-action" onclick="openPtDrawer()">编辑</a>
<a class="g-action">下架</a>
<a class="g-action g-action-danger">删除</a>
<span class="g-tag g-tag-green">上架</span>
</div>
</div>
</div>
<!-- 商品5 -->
<div class="pt-prod-card">
<div class="pt-prod-img"><i data-lucide="image" style="width:32px;height:32px;"></i></div>
<div class="pt-prod-bd">
<div class="pt-prod-name">双人套餐券<span class="pt-type-tag green">优惠券</span></div>
<div class="pt-prod-price">1000积分</div>
<div class="pt-prod-meta">
<span>库存 10</span>
<span>已兑换 8</span>
</div>
<div class="pt-prod-ft">
<a class="g-action" onclick="openPtDrawer()">编辑</a>
<a class="g-action">下架</a>
<a class="g-action g-action-danger">删除</a>
<span class="g-tag g-tag-green">上架</span>
</div>
</div>
</div>
<!-- 商品6已下架 -->
<div class="pt-prod-card pt-off">
<div class="pt-prod-img"><i data-lucide="image" style="width:32px;height:32px;"></i></div>
<div class="pt-prod-bd">
<div class="pt-prod-name">定制保温杯<span class="pt-type-tag orange">实物</span></div>
<div class="pt-prod-price">2000积分</div>
<div class="pt-prod-meta">
<span>库存 0</span>
<span>已兑换 15</span>
</div>
<div class="pt-prod-ft">
<a class="g-action" onclick="openPtDrawer()">编辑</a>
<a class="g-action">上架</a>
<a class="g-action g-action-danger">删除</a>
<span class="g-tag g-tag-gray">下架</span>
</div>
</div>
</div>
</div>
</div>
<!-- ==================== TAB 3: 兑换记录 ==================== -->
<div id="ptTabRecords" style="display:none;">
<!-- 筛选工具栏 -->
<div class="pt-toolbar">
<select style="width:120px;">
<option value="">全部类型</option>
<option>兑换商品</option>
<option>兑换优惠券</option>
<option>兑换实物</option>
</select>
<select style="width:120px;">
<option value="">全部状态</option>
<option>待领取</option>
<option>已发放</option>
<option>已完成</option>
<option>已取消</option>
</select>
<input type="text" placeholder="搜索会员/商品名称" style="width:200px;">
<div style="flex:1;"></div>
<button class="g-btn g-btn-sm"><i data-lucide="download" style="width:13px;height:13px;"></i>导出</button>
</div>
<!-- 统计 -->
<div class="pt-stats" style="grid-template-columns:repeat(3,1fr);">
<div class="pt-stat-card">
<div class="pt-stat-label">今日兑换</div>
<div class="pt-stat-value primary">12</div>
</div>
<div class="pt-stat-card">
<div class="pt-stat-label">待领取实物</div>
<div class="pt-stat-value orange">3</div>
</div>
<div class="pt-stat-card">
<div class="pt-stat-label">本月消耗积分</div>
<div class="pt-stat-value">8,400</div>
</div>
</div>
<!-- 兑换记录表格 -->
<div class="g-card" style="padding:0;">
<table class="g-table">
<thead>
<tr>
<th>兑换单号</th>
<th>会员</th>
<th>兑换商品</th>
<th>类型</th>
<th>消耗积分</th>
<th>兑换时间</th>
<th>状态</th>
<th style="width:100px;">操作</th>
</tr>
</thead>
<tbody>
<tr>
<td style="font-family:monospace;font-size:12px;">PT20260212001</td>
<td><div class="pt-rec-member"><span class="pt-rec-member-name">张三</span><span class="pt-rec-member-phone">138****6789</span></div></td>
<td>定制保温杯</td>
<td><span class="pt-type-tag orange">实物</span></td>
<td style="font-weight:600;">2,000</td>
<td style="font-size:12px;color:var(--g-text-secondary);">2026-02-12 10:30</td>
<td><span class="pt-rec-status orange">待领取</span></td>
<td><a class="g-action" onclick="openPtVerifyDrawer()">核销</a><a class="g-action">详情</a></td>
</tr>
<tr>
<td style="font-family:monospace;font-size:12px;">PT20260212002</td>
<td><div class="pt-rec-member"><span class="pt-rec-member-name">李四</span><span class="pt-rec-member-phone">139****1234</span></div></td>
<td>可乐兑换券</td>
<td><span class="pt-type-tag blue">商品</span></td>
<td style="font-weight:600;">100</td>
<td style="font-size:12px;color:var(--g-text-secondary);">2026-02-12 09:45</td>
<td><span class="pt-rec-status green">已发放</span></td>
<td><a class="g-action">详情</a></td>
</tr>
<tr>
<td style="font-family:monospace;font-size:12px;">PT20260211003</td>
<td><div class="pt-rec-member"><span class="pt-rec-member-name">王五</span><span class="pt-rec-member-phone">137****5678</span></div></td>
<td>满20减5券</td>
<td><span class="pt-type-tag green">优惠券</span></td>
<td style="font-weight:600;">300</td>
<td style="font-size:12px;color:var(--g-text-secondary);">2026-02-11 18:20</td>
<td><span class="pt-rec-status green">已发放</span></td>
<td><a class="g-action">详情</a></td>
</tr>
<tr>
<td style="font-family:monospace;font-size:12px;">PT20260211004</td>
<td><div class="pt-rec-member"><span class="pt-rec-member-name">赵六</span><span class="pt-rec-member-phone">136****4321</span></div></td>
<td>定制保温杯</td>
<td><span class="pt-type-tag orange">实物</span></td>
<td style="font-weight:600;">2,000</td>
<td style="font-size:12px;color:var(--g-text-secondary);">2026-02-11 14:10</td>
<td><span class="pt-rec-status orange">待领取</span></td>
<td><a class="g-action" onclick="openPtVerifyDrawer()">核销</a><a class="g-action">详情</a></td>
</tr>
<tr>
<td style="font-family:monospace;font-size:12px;">PT20260210005</td>
<td><div class="pt-rec-member"><span class="pt-rec-member-name">孙七</span><span class="pt-rec-member-phone">135****8765</span></div></td>
<td>招牌奶茶</td>
<td><span class="pt-type-tag blue">商品</span></td>
<td style="font-weight:600;">500</td>
<td style="font-size:12px;color:var(--g-text-secondary);">2026-02-10 16:55</td>
<td><span class="pt-rec-status green">已完成</span></td>
<td><a class="g-action">详情</a></td>
</tr>
<tr>
<td style="font-family:monospace;font-size:12px;">PT20260210006</td>
<td><div class="pt-rec-member"><span class="pt-rec-member-name">周八</span><span class="pt-rec-member-phone">133****2222</span></div></td>
<td>双人套餐券</td>
<td><span class="pt-type-tag green">优惠券</span></td>
<td style="font-weight:600;">1,000</td>
<td style="font-size:12px;color:var(--g-text-secondary);">2026-02-10 11:30</td>
<td><span class="pt-rec-status gray">已取消</span></td>
<td><a class="g-action">详情</a></td>
</tr>
<tr>
<td style="font-family:monospace;font-size:12px;">PT20260209007</td>
<td><div class="pt-rec-member"><span class="pt-rec-member-name">吴九</span><span class="pt-rec-member-phone">131****9999</span></div></td>
<td>定制保温杯</td>
<td><span class="pt-type-tag orange">实物</span></td>
<td style="font-weight:600;">2,000</td>
<td style="font-size:12px;color:var(--g-text-secondary);">2026-02-09 09:00</td>
<td><span class="pt-rec-status green">已完成</span></td>
<td><a class="g-action">详情</a></td>
</tr>
</tbody>
</table>
</div>
<!-- 分页 -->
<div class="g-pagination" style="margin-top:16px;">
<button class="g-page-btn" disabled><i data-lucide="chevron-left" style="width:14px;height:14px;"></i></button>
<button class="g-page-btn active">1</button>
<button class="g-page-btn">2</button>
<button class="g-page-btn">3</button>
<button class="g-page-btn"><i data-lucide="chevron-right" style="width:14px;height:14px;"></i></button>
</div>
</div>
</div>
<!-- ==================== 添加/编辑兑换商品抽屉 ==================== -->
<div class="g-drawer-mask" id="ptDrawerMask" onclick="closePtDrawer()"></div>
<div class="g-drawer" id="ptDrawer" style="width:520px;">
<div class="g-drawer-hd">
<span class="g-drawer-title">添加兑换商品</span>
<button class="g-drawer-close" onclick="closePtDrawer()">&times;</button>
</div>
<div class="g-drawer-bd">
<!-- 兑换类型 -->
<div class="g-form-group">
<label class="g-form-label required">兑换类型</label>
<div class="pt-exchange-pills">
<span class="g-pill checked" onclick="selectPtRedeemType(this, 'product')">兑换商品</span>
<span class="g-pill" onclick="selectPtRedeemType(this, 'coupon')">兑换优惠券</span>
<span class="g-pill" onclick="selectPtRedeemType(this, 'physical')">兑换实物</span>
</div>
<div class="g-hint" id="ptRedeemHintProduct">顾客兑换后系统自动发放商品兑换券,下单时该商品免费</div>
<div class="g-hint" id="ptRedeemHintCoupon" style="display:none;">顾客兑换后优惠券直接发到券包</div>
<div class="g-hint" id="ptRedeemHintPhysical" style="display:none;">实物商品需到店自提,不走线上履约</div>
</div>
<!-- 兑换商品:选择商品 -->
<div id="ptFieldProduct">
<div class="g-form-group">
<label class="g-form-label required">关联商品</label>
<div id="ptSelectedProduct" style="display:none;padding:10px 12px;background:#f8f9fb;border-radius:8px;border:1px solid #e5e7eb;margin-bottom:8px;display:flex;align-items:center;gap:10px;">
<div style="width:40px;height:40px;background:#eee;border-radius:6px;display:flex;align-items:center;justify-content:center;color:#bbb;flex-shrink:0;"><i data-lucide="image" style="width:18px;height:18px;"></i></div>
<div style="flex:1;">
<div style="font-size:13px;font-weight:500;">招牌奶茶</div>
<div style="font-size:12px;color:var(--g-text-muted);">原价 ¥15.00</div>
</div>
<button style="background:none;border:none;color:var(--g-text-muted);cursor:pointer;" onclick="this.parentElement.style.display='none'"><i data-lucide="x" style="width:14px;height:14px;"></i></button>
</div>
<button class="g-btn g-btn-sm" onclick="openProductPicker({title:'选择兑换商品',subtitle:'积分商城',onConfirm:function(){}})"><i data-lucide="plus" style="width:13px;height:13px;"></i>选择商品/套餐</button>
<div class="g-hint">兑换后系统自动生成该商品的兑换券</div>
</div>
</div>
<!-- 兑换优惠券:选择券 -->
<div id="ptFieldCoupon" style="display:none;">
<div class="g-form-group">
<label class="g-form-label required">关联优惠券</label>
<select class="g-select" style="width:100%;">
<option value="">请选择优惠券</option>
<option>新用户满减券满50减15</option>
<option>全场8折券</option>
<option>免配送费券</option>
<option>满30减5券</option>
<option>满80减20券</option>
</select>
<div class="g-hint">选择已在优惠券管理中创建的券,兑换后直接发到顾客券包</div>
</div>
</div>
<!-- 兑换实物:名称+描述 -->
<div id="ptFieldPhysical" style="display:none;">
<div class="g-form-group">
<label class="g-form-label required">实物名称</label>
<input class="g-input" placeholder="如:定制保温杯、品牌帆布袋">
</div>
<div class="g-form-group">
<label class="g-form-label">领取方式</label>
<div style="display:flex;gap:8px;">
<span class="g-pill checked" onclick="this.parentElement.querySelectorAll('.g-pill').forEach(function(p){p.classList.remove('checked')});this.classList.add('checked')">到店自提</span>
<span class="g-pill" onclick="this.parentElement.querySelectorAll('.g-pill').forEach(function(p){p.classList.remove('checked')});this.classList.add('checked')">快递配送</span>
</div>
</div>
</div>
<!-- 商品名称(展示名) -->
<div class="g-form-group">
<label class="g-form-label required">展示名称</label>
<input class="g-input" placeholder="如招牌奶茶兑换券、满50减15券">
<div class="g-hint">顾客在积分商城看到的名称</div>
</div>
<!-- 商品图片 -->
<div class="g-form-group">
<label class="g-form-label">展示图片</label>
<div class="pt-upload-placeholder">
<i data-lucide="upload-cloud" style="width:28px;height:28px;"></i>
<span>点击上传商品图片</span>
</div>
</div>
<!-- 兑换方式 -->
<div class="g-form-group">
<label class="g-form-label required">兑换积分</label>
<div class="pt-exchange-pills" style="margin-bottom:8px;">
<span class="g-pill checked" onclick="selectPtExchangeType(this, 'points')">纯积分</span>
<span class="g-pill" onclick="selectPtExchangeType(this, 'mixed')">积分+现金</span>
</div>
</div>
<!-- 所需积分 -->
<div class="g-form-group">
<label class="g-form-label required">所需积分</label>
<input class="g-input" type="number" placeholder="请输入所需积分数量" min="0">
</div>
<!-- 现金部分(默认隐藏) -->
<div class="g-form-group" id="ptCashField" style="display:none;">
<label class="g-form-label required">现金部分</label>
<input class="g-input" type="number" placeholder="请输入需额外支付的金额" min="0" step="0.01">
<div class="g-hint">用户兑换时需额外支付的现金金额</div>
</div>
<!-- 库存数量 -->
<div class="g-form-group">
<label class="g-form-label required">库存数量</label>
<input class="g-input" type="number" placeholder="请输入库存数量" min="0">
</div>
<!-- 兑换限制 -->
<div class="g-form-group">
<label class="g-form-label">兑换限制</label>
<div style="display:flex; align-items:center; gap:8px;">
<span style="font-size:13px; color:var(--g-text-secondary);">每人限兑</span>
<input class="g-input" type="number" placeholder="不限" min="0" style="width:100px;">
<span style="font-size:13px; color:var(--g-text-secondary);"></span>
</div>
<div class="g-hint">留空或填0表示不限制兑换次数</div>
</div>
<!-- 商品描述 -->
<div class="g-form-group">
<label class="g-form-label">商品描述</label>
<textarea class="g-textarea" rows="4" placeholder="请输入商品描述信息,如使用规则、有效期等"></textarea>
</div>
<!-- 到账通知 -->
<div class="g-form-group">
<label class="g-form-label">到账通知</label>
<div class="pt-notify-pills" id="ptNotifyPills">
<span class="g-pill checked" onclick="togglePill(this)">站内消息</span>
<span class="g-pill" onclick="togglePill(this)">短信通知</span>
</div>
<div class="g-hint">选择兑换成功后通知顾客的方式,实物商品建议开启短信通知</div>
</div>
</div>
<div class="g-drawer-ft">
<button class="g-btn" onclick="closePtDrawer()">取消</button>
<button class="g-btn g-btn-primary">保存</button>
</div>
</div>
<!-- ==================== 核销抽屉 ==================== -->
<div class="g-drawer-mask" id="ptVerifyMask" onclick="closePtVerifyDrawer()"></div>
<div class="g-drawer" id="ptVerifyDrawer" style="width:440px;">
<div class="g-drawer-hd">
<span class="g-drawer-title">兑换核销</span>
<button class="g-drawer-close" onclick="closePtVerifyDrawer()">&times;</button>
</div>
<div class="g-drawer-bd">
<!-- 兑换信息 -->
<div style="background:#f8f9fb;border-radius:10px;padding:16px;margin-bottom:20px;">
<div style="display:flex;align-items:center;gap:12px;margin-bottom:12px;">
<div style="width:48px;height:48px;background:#eee;border-radius:8px;display:flex;align-items:center;justify-content:center;color:#bbb;flex-shrink:0;"><i data-lucide="image" style="width:22px;height:22px;"></i></div>
<div>
<div style="font-size:14px;font-weight:600;color:var(--g-text);margin-bottom:2px;">定制保温杯</div>
<span class="pt-type-tag orange">实物</span>
</div>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:8px;font-size:13px;">
<div><span style="color:var(--g-text-muted);">兑换单号</span><div style="font-family:monospace;margin-top:2px;">PT20260212001</div></div>
<div><span style="color:var(--g-text-muted);">消耗积分</span><div style="font-weight:600;color:var(--primary);margin-top:2px;">2,000</div></div>
<div><span style="color:var(--g-text-muted);">兑换会员</span><div style="margin-top:2px;">张三 (138****6789)</div></div>
<div><span style="color:var(--g-text-muted);">兑换时间</span><div style="margin-top:2px;">2026-02-12 10:30</div></div>
</div>
</div>
<!-- 核销方式 -->
<div class="g-form-group">
<label class="g-form-label">核销方式</label>
<div style="display:flex;gap:10px;">
<span class="g-pill checked" onclick="this.parentElement.querySelectorAll('.g-pill').forEach(function(p){p.classList.remove('checked')});this.classList.add('checked')">扫码核销</span>
<span class="g-pill" onclick="this.parentElement.querySelectorAll('.g-pill').forEach(function(p){p.classList.remove('checked')});this.classList.add('checked')">手动核销</span>
</div>
</div>
<!-- 备注 -->
<div class="g-form-group">
<label class="g-form-label">核销备注</label>
<textarea class="g-textarea" rows="2" placeholder="可选,如:顾客已到店领取"></textarea>
</div>
</div>
<div class="g-drawer-ft">
<button class="g-btn" onclick="closePtVerifyDrawer()">取消</button>
<button class="g-btn g-btn-primary">确认核销</button>
</div>
</div>
<script>
/* Tab 切换 */
function switchPtTab(el, tab) {
document.querySelectorAll('.pt-seg-item').forEach(function(item) { item.classList.remove('active'); });
el.classList.add('active');
document.getElementById('ptTabRules').style.display = tab === 'rules' ? '' : 'none';
document.getElementById('ptTabProducts').style.display = tab === 'products' ? '' : 'none';
document.getElementById('ptTabRecords').style.display = tab === 'records' ? '' : 'none';
}
/* Toggle 开关 */
function toggleSwitch(el) {
el.classList.toggle('on');
}
/* 有效期 pill 单选 */
function selectPtExpiry(el) {
document.querySelectorAll('.pt-expiry-pills .g-pill').forEach(function(p) { p.classList.remove('checked'); });
el.classList.add('checked');
var hint = document.getElementById('ptExpiryHint');
hint.style.display = el.textContent.trim() === '按年清零' ? '' : 'none';
}
/* 兑换类型切换(商品/优惠券/实物) */
function selectPtRedeemType(el, type) {
el.parentElement.querySelectorAll('.g-pill').forEach(function(p) { p.classList.remove('checked'); });
el.classList.add('checked');
document.getElementById('ptFieldProduct').style.display = type === 'product' ? '' : 'none';
document.getElementById('ptFieldCoupon').style.display = type === 'coupon' ? '' : 'none';
document.getElementById('ptFieldPhysical').style.display = type === 'physical' ? '' : 'none';
document.getElementById('ptRedeemHintProduct').style.display = type === 'product' ? '' : 'none';
document.getElementById('ptRedeemHintCoupon').style.display = type === 'coupon' ? '' : 'none';
document.getElementById('ptRedeemHintPhysical').style.display = type === 'physical' ? '' : 'none';
/* 自动设置通知默认值:实物默认勾选短信 */
var pills = document.querySelectorAll('#ptNotifyPills .g-pill');
if (type === 'physical') {
pills.forEach(function(p) { p.classList.add('checked'); });
} else {
pills[0].classList.add('checked');
pills[1].classList.remove('checked');
}
}
/* 兑换方式 pill 单选(纯积分/积分+现金) */
function selectPtExchangeType(el, type) {
el.parentElement.querySelectorAll('.g-pill').forEach(function(p) { p.classList.remove('checked'); });
el.classList.add('checked');
document.getElementById('ptCashField').style.display = type === 'mixed' ? '' : 'none';
}
/* 抽屉开关 */
function openPtDrawer() {
document.getElementById('ptDrawerMask').classList.add('open');
document.getElementById('ptDrawer').classList.add('open');
}
function closePtDrawer() {
document.getElementById('ptDrawerMask').classList.remove('open');
document.getElementById('ptDrawer').classList.remove('open');
}
/* 通知 pill 多选 */
function togglePill(el) {
el.classList.toggle('checked');
}
/* 核销抽屉 */
function openPtVerifyDrawer() {
document.getElementById('ptVerifyMask').classList.add('open');
document.getElementById('ptVerifyDrawer').classList.add('open');
}
function closePtVerifyDrawer() {
document.getElementById('ptVerifyMask').classList.remove('open');
document.getElementById('ptVerifyDrawer').classList.remove('open');
}
/* 初始化 Lucide 图标 */
if (typeof lucide !== 'undefined') { lucide.createIcons(); }
</script>

463
pages/mbr-prepaid.html Normal file
View File

@@ -0,0 +1,463 @@
<!-- 储值卡页 -->
<style>
/* 分段 Tab */
.pv-tabs { display:flex; background:#f8f9fb; border-radius:8px; padding:3px; gap:2px; width:fit-content; margin-bottom:16px; }
.pv-tab { padding:7px 22px; border-radius:6px; font-size:13px; cursor:pointer; color:#4b5563; transition:var(--g-transition); border:none; background:none; font-weight:500; }
.pv-tab.active { background:#fff; color:var(--primary); font-weight:600; box-shadow:var(--g-shadow-sm); }
/* 统计卡片 */
.pv-stats { display:grid; grid-template-columns:repeat(4,1fr); gap:12px; margin-bottom:16px; }
.pv-stat-card { background:#fff; border-radius:10px; padding:16px 20px; box-shadow:var(--g-shadow-sm); transition:var(--g-transition); }
.pv-stat-card:hover { box-shadow:var(--g-shadow-md); transform:translateY(-1px); }
.pv-stat-label { font-size:13px; color:#9ca3af; margin-bottom:6px; }
.pv-stat-value { font-size:24px; font-weight:700; color:#1a1a2e; }
.pv-stat-value.green { color:var(--g-success); }
.pv-stat-value.orange { color:var(--g-warning); }
/* 方案卡片网格 */
.pv-plan-grid { display:grid; grid-template-columns:repeat(3,1fr); gap:16px; margin-bottom:16px; }
.pv-plan-card {
background:#fff; border-radius:12px; padding:0; overflow:hidden;
box-shadow:var(--g-shadow-sm); transition:var(--g-transition); position:relative;
}
.pv-plan-card:hover { box-shadow:var(--g-shadow-md); transform:translateY(-1px); }
.pv-plan-card.disabled { opacity:0.5; }
.pv-plan-hd {
background:linear-gradient(135deg, hsl(212,100%,45%) 0%, hsl(212,80%,55%) 100%);
padding:24px 20px 18px; color:#fff; position:relative; overflow:hidden;
}
.pv-plan-hd::after {
content:''; position:absolute; right:-20px; top:-20px;
width:80px; height:80px; border-radius:50%;
background:rgba(255,255,255,0.08);
}
.pv-plan-amount { font-size:28px; font-weight:700; letter-spacing:-0.5px; }
.pv-plan-amount span { font-size:15px; font-weight:400; opacity:0.8; }
.pv-plan-gift {
display:inline-block; margin-top:8px; padding:3px 10px; border-radius:999px;
background:rgba(255,255,255,0.2); font-size:13px; font-weight:600; color:#fff;
}
.pv-plan-gift .pv-gift-num { color:#ffd666; }
.pv-plan-effective {
margin-top:8px; font-size:13px; opacity:0.85;
}
.pv-plan-bd { padding:14px 20px; }
.pv-plan-meta { display:flex; justify-content:space-between; align-items:center; margin-bottom:12px; }
.pv-plan-meta-item { font-size:12px; color:var(--g-text-muted); }
.pv-plan-meta-item span { font-weight:600; color:var(--g-text-secondary); }
.pv-plan-ft { display:flex; align-items:center; justify-content:space-between; padding:0 20px 14px; }
.pv-plan-actions { display:flex; gap:6px; }
/* 工具栏 */
.pv-toolbar {
display:flex; align-items:center; gap:12px; flex-wrap:wrap;
margin-bottom:16px; box-shadow:var(--g-shadow-sm); border-radius:10px;
padding:10px 14px; background:#fff;
}
.pv-toolbar input[type="text"],
.pv-toolbar input[type="date"] {
height:34px; padding:0 10px; border:1px solid #e5e7eb; border-radius:8px;
font-size:13px; outline:none; transition:var(--g-transition); background:#fff;
}
.pv-toolbar input:focus { border-color:var(--primary); box-shadow:0 0 0 3px color-mix(in srgb, var(--primary) 12%, transparent); }
/* 分页 */
.pv-pagination {
display:flex; align-items:center; justify-content:flex-end;
padding:12px 16px; gap:6px; font-size:13px; color:#4b5563;
background:#fff; border-radius:10px; box-shadow:var(--g-shadow-sm);
}
/* 抽屉内赠送比例提示 */
.pv-ratio-hint {
display:inline-flex; align-items:center; gap:4px;
padding:4px 10px; border-radius:6px; font-size:12px; font-weight:500;
background:color-mix(in srgb, var(--primary) 8%, #fff);
color:var(--primary); margin-top:6px;
}
.pv-section-hd {
font-size:15px; font-weight:600; color:var(--g-text);
padding-left:10px; border-left:3px solid var(--primary); margin-bottom:16px;
}
</style>
<div class="page-pv">
<!-- 分段 Tab -->
<div class="pv-tabs">
<button class="pv-tab active" onclick="switchPvTab(this, 'plans')">充值方案</button>
<button class="pv-tab" onclick="switchPvTab(this, 'records')">充值记录</button>
</div>
<!-- ========== TAB 1: 充值方案 ========== -->
<div id="pvTabPlans">
<!-- 统计卡片 -->
<div class="pv-stats">
<div class="pv-stat-card">
<div class="pv-stat-label">储值总额</div>
<div class="pv-stat-value">¥186,500</div>
</div>
<div class="pv-stat-card">
<div class="pv-stat-label">赠金总额</div>
<div class="pv-stat-value orange">¥18,650</div>
</div>
<div class="pv-stat-card">
<div class="pv-stat-label">本月充值</div>
<div class="pv-stat-value green">¥12,800</div>
</div>
<div class="pv-stat-card">
<div class="pv-stat-label">储值用户</div>
<div class="pv-stat-value">326人</div>
</div>
</div>
<!-- 方案卡片 -->
<div class="pv-section-hd">充值方案</div>
<div class="pv-plan-grid">
<!-- 方案1: 100送10 -->
<div class="pv-plan-card">
<div class="pv-plan-hd">
<div class="pv-plan-amount"><span></span> ¥100</div>
<div class="pv-plan-gift"><span class="pv-gift-num">¥10</span></div>
<div class="pv-plan-effective">到账 ¥110</div>
</div>
<div class="pv-plan-bd">
<div class="pv-plan-meta">
<span class="pv-plan-meta-item">累计充值 <span>286次</span></span>
<span class="pv-plan-meta-item">合计 <span>¥28,600</span></span>
</div>
</div>
<div class="pv-plan-ft">
<div class="g-toggle on" onclick="toggleSwitch(this)"><div class="g-toggle-dot"></div></div>
<div class="pv-plan-actions">
<a class="g-action" onclick="openPvDrawer('edit')">编辑</a>
<a class="g-action g-action-danger">删除</a>
</div>
</div>
</div>
<!-- 方案2: 200送30 -->
<div class="pv-plan-card">
<div class="pv-plan-hd">
<div class="pv-plan-amount"><span></span> ¥200</div>
<div class="pv-plan-gift"><span class="pv-gift-num">¥30</span></div>
<div class="pv-plan-effective">到账 ¥230</div>
</div>
<div class="pv-plan-bd">
<div class="pv-plan-meta">
<span class="pv-plan-meta-item">累计充值 <span>198次</span></span>
<span class="pv-plan-meta-item">合计 <span>¥39,600</span></span>
</div>
</div>
<div class="pv-plan-ft">
<div class="g-toggle on" onclick="toggleSwitch(this)"><div class="g-toggle-dot"></div></div>
<div class="pv-plan-actions">
<a class="g-action" onclick="openPvDrawer('edit')">编辑</a>
<a class="g-action g-action-danger">删除</a>
</div>
</div>
</div>
<!-- 方案3: 300送50 -->
<div class="pv-plan-card">
<div class="pv-plan-hd">
<div class="pv-plan-amount"><span></span> ¥300</div>
<div class="pv-plan-gift"><span class="pv-gift-num">¥50</span></div>
<div class="pv-plan-effective">到账 ¥350</div>
</div>
<div class="pv-plan-bd">
<div class="pv-plan-meta">
<span class="pv-plan-meta-item">累计充值 <span>145次</span></span>
<span class="pv-plan-meta-item">合计 <span>¥43,500</span></span>
</div>
</div>
<div class="pv-plan-ft">
<div class="g-toggle on" onclick="toggleSwitch(this)"><div class="g-toggle-dot"></div></div>
<div class="pv-plan-actions">
<a class="g-action" onclick="openPvDrawer('edit')">编辑</a>
<a class="g-action g-action-danger">删除</a>
</div>
</div>
</div>
<!-- 方案4: 500送100 -->
<div class="pv-plan-card">
<div class="pv-plan-hd">
<div class="pv-plan-amount"><span></span> ¥500</div>
<div class="pv-plan-gift"><span class="pv-gift-num">¥100</span></div>
<div class="pv-plan-effective">到账 ¥600</div>
</div>
<div class="pv-plan-bd">
<div class="pv-plan-meta">
<span class="pv-plan-meta-item">累计充值 <span>92次</span></span>
<span class="pv-plan-meta-item">合计 <span>¥46,000</span></span>
</div>
</div>
<div class="pv-plan-ft">
<div class="g-toggle on" onclick="toggleSwitch(this)"><div class="g-toggle-dot"></div></div>
<div class="pv-plan-actions">
<a class="g-action" onclick="openPvDrawer('edit')">编辑</a>
<a class="g-action g-action-danger">删除</a>
</div>
</div>
</div>
<!-- 方案5: 1000送250 -->
<div class="pv-plan-card">
<div class="pv-plan-hd">
<div class="pv-plan-amount"><span></span> ¥1000</div>
<div class="pv-plan-gift"><span class="pv-gift-num">¥250</span></div>
<div class="pv-plan-effective">到账 ¥1250</div>
</div>
<div class="pv-plan-bd">
<div class="pv-plan-meta">
<span class="pv-plan-meta-item">累计充值 <span>38次</span></span>
<span class="pv-plan-meta-item">合计 <span>¥38,000</span></span>
</div>
</div>
<div class="pv-plan-ft">
<div class="g-toggle on" onclick="toggleSwitch(this)"><div class="g-toggle-dot"></div></div>
<div class="pv-plan-actions">
<a class="g-action" onclick="openPvDrawer('edit')">编辑</a>
<a class="g-action g-action-danger">删除</a>
</div>
</div>
</div>
<!-- 方案6: 2000送600 (已停用) -->
<div class="pv-plan-card disabled">
<div class="pv-plan-hd" style="background:linear-gradient(135deg, #9ca3af 0%, #b0b8c4 100%);">
<div class="pv-plan-amount"><span></span> ¥2000</div>
<div class="pv-plan-gift"><span class="pv-gift-num" style="color:#ddd;">¥600</span></div>
<div class="pv-plan-effective">到账 ¥2600</div>
</div>
<div class="pv-plan-bd">
<div class="pv-plan-meta">
<span class="pv-plan-meta-item">累计充值 <span>5次</span></span>
<span class="pv-plan-meta-item">合计 <span>¥10,000</span></span>
</div>
</div>
<div class="pv-plan-ft">
<div class="g-toggle" onclick="toggleSwitch(this)"><div class="g-toggle-dot"></div></div>
<div class="pv-plan-actions">
<a class="g-action" onclick="openPvDrawer('edit')">编辑</a>
<a class="g-action g-action-danger">删除</a>
</div>
</div>
</div>
</div>
<button class="g-btn g-btn-primary" onclick="openPvDrawer('create')">
<i data-lucide="plus" style="width:14px;height:14px;"></i>添加充值方案
</button>
</div>
<!-- ========== TAB 2: 充值记录 ========== -->
<div id="pvTabRecords" style="display:none;">
<!-- 工具栏 -->
<div class="pv-toolbar">
<input type="date" value="2026-01-01" />
<span style="color:var(--g-text-muted); font-size:13px;"></span>
<input type="date" value="2026-02-12" />
<input type="text" placeholder="搜索会员姓名/手机号" style="width:200px;" />
<div style="flex:1;"></div>
<button class="g-btn"><i data-lucide="download" style="width:14px;height:14px;"></i>导出</button>
</div>
<!-- 表格 -->
<div class="g-card" style="padding:0;">
<table class="g-table">
<thead>
<tr>
<th>充值单号</th>
<th>会员</th>
<th>手机号</th>
<th>充值金额</th>
<th>赠送金额</th>
<th>到账金额</th>
<th>支付方式</th>
<th>充值时间</th>
</tr>
</thead>
<tbody>
<tr>
<td style="font-family:monospace; font-size:12px;">CZ20260212001</td>
<td>张三</td>
<td>138****6789</td>
<td style="font-weight:600;">¥500.00</td>
<td style="color:var(--g-warning); font-weight:500;">¥100.00</td>
<td style="font-weight:600;">¥600.00</td>
<td><span class="g-tag g-tag-green">微信支付</span></td>
<td>2026-02-12 14:32</td>
</tr>
<tr>
<td style="font-family:monospace; font-size:12px;">CZ20260212002</td>
<td>李四</td>
<td>139****2345</td>
<td style="font-weight:600;">¥300.00</td>
<td style="color:var(--g-warning); font-weight:500;">¥50.00</td>
<td style="font-weight:600;">¥350.00</td>
<td><span class="g-tag g-tag-green">微信支付</span></td>
<td>2026-02-12 11:18</td>
</tr>
<tr>
<td style="font-family:monospace; font-size:12px;">CZ20260211003</td>
<td>王五</td>
<td>136****8901</td>
<td style="font-weight:600;">¥1000.00</td>
<td style="color:var(--g-warning); font-weight:500;">¥250.00</td>
<td style="font-weight:600;">¥1250.00</td>
<td><span class="g-tag g-tag-blue">支付宝</span></td>
<td>2026-02-11 19:45</td>
</tr>
<tr>
<td style="font-family:monospace; font-size:12px;">CZ20260211004</td>
<td>赵六</td>
<td>158****4567</td>
<td style="font-weight:600;">¥200.00</td>
<td style="color:var(--g-warning); font-weight:500;">¥30.00</td>
<td style="font-weight:600;">¥230.00</td>
<td><span class="g-tag g-tag-green">微信支付</span></td>
<td>2026-02-11 16:22</td>
</tr>
<tr>
<td style="font-family:monospace; font-size:12px;">CZ20260210005</td>
<td>孙七</td>
<td>137****3456</td>
<td style="font-weight:600;">¥100.00</td>
<td style="color:var(--g-warning); font-weight:500;">¥10.00</td>
<td style="font-weight:600;">¥110.00</td>
<td><span class="g-tag g-tag-orange">现金</span></td>
<td>2026-02-10 12:08</td>
</tr>
<tr>
<td style="font-family:monospace; font-size:12px;">CZ20260210006</td>
<td>周八</td>
<td>155****7890</td>
<td style="font-weight:600;">¥500.00</td>
<td style="color:var(--g-warning); font-weight:500;">¥100.00</td>
<td style="font-weight:600;">¥600.00</td>
<td><span class="g-tag g-tag-blue">支付宝</span></td>
<td>2026-02-10 10:35</td>
</tr>
<tr>
<td style="font-family:monospace; font-size:12px;">CZ20260209007</td>
<td>吴九</td>
<td>132****1234</td>
<td style="font-weight:600;">¥300.00</td>
<td style="color:var(--g-warning); font-weight:500;">¥50.00</td>
<td style="font-weight:600;">¥350.00</td>
<td><span class="g-tag g-tag-green">微信支付</span></td>
<td>2026-02-09 20:15</td>
</tr>
<tr>
<td style="font-family:monospace; font-size:12px;">CZ20260209008</td>
<td>郑十</td>
<td>186****5678</td>
<td style="font-weight:600;">¥200.00</td>
<td style="color:var(--g-warning); font-weight:500;">¥30.00</td>
<td style="font-weight:600;">¥230.00</td>
<td><span class="g-tag g-tag-green">微信支付</span></td>
<td>2026-02-09 09:50</td>
</tr>
</tbody>
</table>
</div>
<!-- 分页 -->
<div class="pv-pagination" style="margin-top:16px;">
<span>共 86 条</span>
<button class="g-page-btn">&lt;</button>
<button class="g-page-btn active">1</button>
<button class="g-page-btn">2</button>
<button class="g-page-btn">3</button>
<button class="g-page-btn">...</button>
<button class="g-page-btn">11</button>
<button class="g-page-btn">&gt;</button>
<span style="margin-left:8px;">8 条/页</span>
</div>
</div>
</div>
<!-- 添加/编辑充值方案抽屉 -->
<div class="g-drawer-mask" id="pvDrawerMask" onclick="closePvDrawer()"></div>
<div class="g-drawer" id="pvDrawer" style="width:480px">
<div class="g-drawer-hd">
<div class="g-drawer-title" id="pvDrawerTitle">添加充值方案</div>
<button class="g-drawer-close" onclick="closePvDrawer()"><i data-lucide="x" style="width:18px;height:18px;"></i></button>
</div>
<div class="g-drawer-bd">
<div class="g-form-group">
<label class="g-form-label required">充值金额</label>
<input class="g-input" id="pvAmountInput" type="number" placeholder="如100" oninput="calcPvRatio()" />
</div>
<div class="g-form-group">
<label class="g-form-label required">赠送金额</label>
<input class="g-input" id="pvGiftInput" type="number" placeholder="如10" oninput="calcPvRatio()" />
<div class="pv-ratio-hint" id="pvRatioHint">
<i data-lucide="info" style="width:13px;height:13px;"></i>
<span id="pvRatioText">赠送比例 --</span>
</div>
</div>
<div class="g-form-group">
<label class="g-form-label">排序</label>
<input class="g-input" type="number" placeholder="数字越小越靠前1" style="width:160px;" />
<div class="g-hint">数字越小排序越靠前</div>
</div>
<div class="g-form-group">
<label class="g-form-label">启用状态</label>
<div style="display:flex; align-items:center; gap:10px;">
<div class="g-toggle on" onclick="toggleSwitch(this)"><div class="g-toggle-dot"></div></div>
<span style="font-size:13px; color:var(--g-text-secondary);">启用后会员可选择此方案充值</span>
</div>
</div>
</div>
<div class="g-drawer-ft">
<button class="g-btn" onclick="closePvDrawer()">取消</button>
<button class="g-btn g-btn-primary" onclick="closePvDrawer()">保存</button>
</div>
</div>
<script>
/* Tab 切换 */
function switchPvTab(el, tab) {
document.querySelectorAll('.pv-tab').forEach(function(t) { t.classList.remove('active'); });
el.classList.add('active');
document.getElementById('pvTabPlans').style.display = tab === 'plans' ? '' : 'none';
document.getElementById('pvTabRecords').style.display = tab === 'records' ? '' : 'none';
}
/* 抽屉开关 */
function openPvDrawer(mode) {
document.getElementById('pvDrawerMask').classList.add('open');
document.getElementById('pvDrawer').classList.add('open');
document.getElementById('pvDrawerTitle').textContent = mode === 'edit' ? '编辑充值方案' : '添加充值方案';
if (window.lucide) lucide.createIcons();
}
function closePvDrawer() {
document.getElementById('pvDrawerMask').classList.remove('open');
document.getElementById('pvDrawer').classList.remove('open');
}
/* Toggle 开关 */
function toggleSwitch(el) {
el.classList.toggle('on');
}
/* 赠送比例自动计算 */
function calcPvRatio() {
var amount = parseFloat(document.getElementById('pvAmountInput').value) || 0;
var gift = parseFloat(document.getElementById('pvGiftInput').value) || 0;
var text = '赠送比例 --';
if (amount > 0 && gift > 0) {
var ratio = ((gift / amount) * 100).toFixed(1);
text = '赠送比例 ' + ratio + '%';
}
document.getElementById('pvRatioText').textContent = text;
}
</script>

284
pages/mkt-calendar.html Normal file
View File

@@ -0,0 +1,284 @@
<!-- 营销日历页 -->
<style>
.page-mc { max-width:1100px; }
/* 顶部控制栏 */
.mc-header {
display:flex; align-items:center; justify-content:space-between; margin-bottom:16px;
background:#fff; border-radius:10px; padding:14px 20px; box-shadow:var(--g-shadow-sm);
}
.mc-month-nav { display:flex; align-items:center; gap:12px; }
.mc-month-nav button {
width:32px; height:32px; border:1px solid #e5e7eb; border-radius:8px; background:#fff;
cursor:pointer; display:flex; align-items:center; justify-content:center;
transition:all var(--g-transition); color:var(--g-text-secondary);
}
.mc-month-nav button:hover { border-color:var(--primary); color:var(--primary); }
.mc-month-title { font-size:18px; font-weight:700; color:var(--g-text); min-width:140px; text-align:center; }
.mc-legend { display:flex; gap:14px; font-size:12px; color:var(--g-text-secondary); }
.mc-legend-item { display:flex; align-items:center; gap:4px; }
.mc-legend-dot { width:10px; height:10px; border-radius:3px; }
/* 统计 */
.mc-stats { display:grid; grid-template-columns:repeat(4,1fr); gap:12px; margin-bottom:16px; }
.mc-stat-card {
background:#fff; border-radius:10px; padding:14px 18px;
box-shadow:var(--g-shadow-sm); transition:all var(--g-transition);
}
.mc-stat-card:hover { box-shadow:var(--g-shadow-md); transform:translateY(-1px); }
.mc-stat-label { font-size:12px; color:var(--g-text-muted); margin-bottom:4px; }
.mc-stat-value { font-size:20px; font-weight:700; color:var(--g-text); }
.mc-stat-value.primary { color:var(--primary); }
.mc-stat-value.orange { color:var(--g-warning); }
.mc-stat-value.green { color:var(--g-success); }
/* 甘特图容器 */
.mc-gantt {
background:#fff; border-radius:10px; box-shadow:var(--g-shadow-sm); overflow:hidden;
}
/* 日期头 */
.mc-dates {
display:grid; grid-template-columns:180px repeat(28, 1fr);
border-bottom:1px solid #e5e7eb; background:#f8f9fb;
}
.mc-dates-label {
padding:10px 16px; font-size:12px; font-weight:600; color:var(--g-text-secondary);
border-right:1px solid #f0f0f0; display:flex; align-items:center;
}
.mc-date-cell {
padding:8px 0; font-size:11px; color:var(--g-text-muted); text-align:center;
border-right:1px solid #f5f5f5;
}
.mc-date-cell.today { background:color-mix(in srgb, var(--primary) 8%, #fff); color:var(--primary); font-weight:600; }
.mc-date-cell.weekend { color:#ccc; }
/* 活动行 */
.mc-row {
display:grid; grid-template-columns:180px 1fr;
border-bottom:1px solid #f5f5f5; min-height:44px; transition:background var(--g-transition);
}
.mc-row:hover { background:color-mix(in srgb, var(--primary) 2%, #fff); }
.mc-row:last-child { border-bottom:none; }
.mc-row-label {
padding:10px 16px; font-size:13px; color:var(--g-text); display:flex;
align-items:center; gap:8px; border-right:1px solid #f0f0f0;
}
.mc-row-label-name { font-weight:500; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.mc-type-dot { width:8px; height:8px; border-radius:2px; flex-shrink:0; }
/* 条形区域 */
.mc-bar-area {
position:relative; display:grid; grid-template-columns:repeat(28, 1fr); align-items:center;
}
.mc-bar {
position:absolute; height:24px; border-radius:4px; top:50%; transform:translateY(-50%);
font-size:11px; color:#fff; font-weight:500; display:flex; align-items:center;
padding:0 8px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
cursor:pointer; transition:filter var(--g-transition);
}
.mc-bar:hover { filter:brightness(1.1); }
/* 今日线 */
.mc-today-line {
position:absolute; top:0; bottom:0; width:2px; background:var(--g-danger);
z-index:2; opacity:.6;
}
.mc-today-line::before {
content:'今'; position:absolute; top:-18px; left:50%; transform:translateX(-50%);
font-size:10px; color:var(--g-danger); font-weight:600;
}
/* 冲突提示 */
.mc-conflict-banner {
display:flex; align-items:center; gap:8px; padding:10px 16px; margin-bottom:16px;
background:#fff7ed; border:1px solid #fed7aa; border-radius:8px; font-size:13px; color:#9a3412;
}
/* 空行提示 */
.mc-empty-row {
padding:40px; text-align:center; color:var(--g-text-muted); font-size:13px;
}
</style>
<div class="page-mc">
<!-- 冲突提示 -->
<div class="mc-conflict-banner">
<i data-lucide="alert-triangle" style="width:16px;height:16px;color:#f59e0b;flex-shrink:0;"></i>
<span>2月10日~2月28日 有 <b>4个活动</b> 同时进行,请注意优惠叠加可能影响利润</span>
</div>
<!-- 顶部控制栏 -->
<div class="mc-header">
<div class="mc-month-nav">
<button><i data-lucide="chevron-left" style="width:16px;height:16px;"></i></button>
<div class="mc-month-title">2026年 2月</div>
<button><i data-lucide="chevron-right" style="width:16px;height:16px;"></i></button>
</div>
<div class="mc-legend">
<span class="mc-legend-item"><span class="mc-legend-dot" style="background:#3b82f6;"></span>满减</span>
<span class="mc-legend-item"><span class="mc-legend-dot" style="background:#22c55e;"></span>满赠</span>
<span class="mc-legend-item"><span class="mc-legend-dot" style="background:#f59e0b;"></span>第二份半价</span>
<span class="mc-legend-item"><span class="mc-legend-dot" style="background:#8b5cf6;"></span>限时折扣</span>
<span class="mc-legend-item"><span class="mc-legend-dot" style="background:#ef4444;"></span>秒杀</span>
<span class="mc-legend-item"><span class="mc-legend-dot" style="background:#ec4899;"></span>优惠券</span>
<span class="mc-legend-item"><span class="mc-legend-dot" style="background:#06b6d4;"></span>次卡</span>
</div>
</div>
<!-- 统计 -->
<div class="mc-stats">
<div class="mc-stat-card">
<div class="mc-stat-label">本月活动</div>
<div class="mc-stat-value primary">8</div>
</div>
<div class="mc-stat-card">
<div class="mc-stat-label">进行中</div>
<div class="mc-stat-value green">6</div>
</div>
<div class="mc-stat-card">
<div class="mc-stat-label">最大并行数</div>
<div class="mc-stat-value orange">4</div>
</div>
<div class="mc-stat-card">
<div class="mc-stat-label">本月预计优惠</div>
<div class="mc-stat-value">¥6,200</div>
</div>
</div>
<!-- 甘特图 -->
<div class="mc-gantt">
<!-- 日期头 -->
<div class="mc-dates">
<div class="mc-dates-label">活动名称</div>
<div class="mc-date-cell">1</div>
<div class="mc-date-cell">2</div>
<div class="mc-date-cell">3</div>
<div class="mc-date-cell">4</div>
<div class="mc-date-cell">5</div>
<div class="mc-date-cell">6</div>
<div class="mc-date-cell weekend">7</div>
<div class="mc-date-cell weekend">8</div>
<div class="mc-date-cell">9</div>
<div class="mc-date-cell">10</div>
<div class="mc-date-cell">11</div>
<div class="mc-date-cell today">12</div>
<div class="mc-date-cell">13</div>
<div class="mc-date-cell weekend">14</div>
<div class="mc-date-cell weekend">15</div>
<div class="mc-date-cell">16</div>
<div class="mc-date-cell">17</div>
<div class="mc-date-cell">18</div>
<div class="mc-date-cell">19</div>
<div class="mc-date-cell">20</div>
<div class="mc-date-cell weekend">21</div>
<div class="mc-date-cell weekend">22</div>
<div class="mc-date-cell">23</div>
<div class="mc-date-cell">24</div>
<div class="mc-date-cell">25</div>
<div class="mc-date-cell">26</div>
<div class="mc-date-cell">27</div>
<div class="mc-date-cell weekend">28</div>
</div>
<!-- 活动行 -->
<!-- 1. 午市满减 1/1~3/31 全月覆盖 -->
<div class="mc-row">
<div class="mc-row-label">
<span class="mc-type-dot" style="background:#3b82f6;"></span>
<span class="mc-row-label-name">午市满减优惠</span>
</div>
<div class="mc-bar-area">
<div class="mc-bar" style="left:0;right:0;background:#3b82f6;">满30减5 / 满50减10 / 满80减20</div>
<div class="mc-today-line" style="left:calc((12 - 0.5) / 28 * 100%);"></div>
</div>
</div>
<!-- 2. 晚市大额满减 1/15~4/15 全月覆盖 -->
<div class="mc-row">
<div class="mc-row-label">
<span class="mc-type-dot" style="background:#3b82f6;"></span>
<span class="mc-row-label-name">晚市大额满减</span>
</div>
<div class="mc-bar-area">
<div class="mc-bar" style="left:0;right:0;background:#3b82f6;opacity:.75;">满50减8 / 满80减15 / 满120减30</div>
</div>
</div>
<!-- 3. 饮品买二赠一 2/1~3/31 全月覆盖 -->
<div class="mc-row">
<div class="mc-row-label">
<span class="mc-type-dot" style="background:#22c55e;"></span>
<span class="mc-row-label-name">饮品买二赠一</span>
</div>
<div class="mc-bar-area">
<div class="mc-bar" style="left:0;right:0;background:#22c55e;">买2杯赠1杯</div>
</div>
</div>
<!-- 4. 甜品第二份半价 2/10~2/28 -->
<div class="mc-row">
<div class="mc-row-label">
<span class="mc-type-dot" style="background:#f59e0b;"></span>
<span class="mc-row-label-name">甜品第二份半价</span>
</div>
<div class="mc-bar-area">
<div class="mc-bar" style="left:calc((10 - 1) / 28 * 100%);right:0;background:#f59e0b;">第2份5折</div>
</div>
</div>
<!-- 5. 疯狂星期四 限时折扣 每周四 -->
<div class="mc-row">
<div class="mc-row-label">
<span class="mc-type-dot" style="background:#8b5cf6;"></span>
<span class="mc-row-label-name">疯狂星期四</span>
</div>
<div class="mc-bar-area">
<!-- 2/5, 2/12, 2/19, 2/26 are Thursdays -->
<div class="mc-bar" style="left:calc((5 - 1) / 28 * 100%);width:calc(1 / 28 * 100%);background:#8b5cf6;border-radius:4px;font-size:0;"></div>
<div class="mc-bar" style="left:calc((12 - 1) / 28 * 100%);width:calc(1 / 28 * 100%);background:#8b5cf6;border-radius:4px;font-size:0;"></div>
<div class="mc-bar" style="left:calc((19 - 1) / 28 * 100%);width:calc(1 / 28 * 100%);background:#8b5cf6;border-radius:4px;font-size:0;"></div>
<div class="mc-bar" style="left:calc((26 - 1) / 28 * 100%);width:calc(1 / 28 * 100%);background:#8b5cf6;border-radius:4px;font-size:0;"></div>
</div>
</div>
<!-- 6. 情人节秒杀 2/13~2/14 -->
<div class="mc-row">
<div class="mc-row-label">
<span class="mc-type-dot" style="background:#ef4444;"></span>
<span class="mc-row-label-name">情人节秒杀</span>
</div>
<div class="mc-bar-area">
<div class="mc-bar" style="left:calc((13 - 1) / 28 * 100%);width:calc(2 / 28 * 100%);background:#ef4444;">¥9.9 双人套餐</div>
</div>
</div>
<!-- 7. 新用户满减券 长期 -->
<div class="mc-row">
<div class="mc-row-label">
<span class="mc-type-dot" style="background:#ec4899;"></span>
<span class="mc-row-label-name">新用户满减券</span>
</div>
<div class="mc-bar-area">
<div class="mc-bar" style="left:0;right:0;background:#ec4899;opacity:.65;">满50减15长期</div>
</div>
</div>
<!-- 8. 咖啡月卡 次卡 长期 -->
<div class="mc-row">
<div class="mc-row-label">
<span class="mc-type-dot" style="background:#06b6d4;"></span>
<span class="mc-row-label-name">咖啡月卡</span>
</div>
<div class="mc-bar-area">
<div class="mc-bar" style="left:0;right:0;background:#06b6d4;opacity:.65;">¥99 / 30次长期在售</div>
</div>
</div>
</div>
</div>
<script>
if (typeof lucide !== 'undefined') { lucide.createIcons(); }
</script>

489
pages/mkt-coupon.html Normal file
View File

@@ -0,0 +1,489 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<style>
/* ===== 优惠券页面私有样式 (cp- prefix) ===== */
.cp-toolbar {
display: flex; align-items: center; gap: 10px; margin-bottom: 16px; flex-wrap: wrap;
}
.cp-toolbar .g-select, .cp-toolbar .g-input { width: auto; min-width: 140px; }
.cp-toolbar .g-input { min-width: 180px; }
.cp-toolbar-right { margin-left: auto; flex-shrink: 0; }
/* 统计行 */
.cp-stats {
display: flex; gap: 16px; margin-bottom: 20px;
}
.cp-stat-item {
display: flex; align-items: center; gap: 8px; padding: 12px 20px;
background: #fff; border: 1px solid var(--g-border); border-radius: var(--g-radius);
font-size: 13px; color: var(--g-text-secondary); transition: box-shadow var(--g-transition);
flex: 1;
}
.cp-stat-item:hover { box-shadow: var(--g-shadow-md); }
.cp-stat-icon {
width: 36px; height: 36px; border-radius: 8px; display: flex; align-items: center; justify-content: center;
flex-shrink: 0;
}
.cp-stat-icon.blue { background: #e6f7ff; color: #1890ff; }
.cp-stat-icon.green { background: #f6ffed; color: #52c41a; }
.cp-stat-icon.orange { background: #fff7e6; color: #fa8c16; }
.cp-stat-icon.purple { background: #f9f0ff; color: #722ed1; }
.cp-stat-val { font-size: 22px; font-weight: 700; color: var(--g-text); line-height: 1; }
.cp-stat-label { font-size: 12px; color: var(--g-text-muted); margin-top: 2px; }
/* 优惠券卡片列表 */
.cp-list { display: flex; flex-direction: column; gap: 12px; }
.cp-coupon {
display: flex; border-radius: var(--g-radius); overflow: hidden;
border: 1px solid var(--g-border); background: #fff;
transition: box-shadow var(--g-transition);
}
.cp-coupon:hover { box-shadow: var(--g-shadow-md); }
.cp-coupon.cp-dimmed { opacity: 0.55; }
/* 左侧券面 */
.cp-left {
width: 160px; min-height: 130px; flex-shrink: 0;
display: flex; flex-direction: column; align-items: center; justify-content: center;
color: #fff; position: relative; padding: 16px 12px;
}
.cp-left::after {
content: '';
position: absolute; right: -6px; top: 50%; transform: translateY(-50%);
width: 12px; height: 12px; background: #fff; border-radius: 50%;
box-shadow: inset 2px 0 4px rgba(0,0,0,.06);
}
.cp-left.cp-red { background: linear-gradient(135deg, #ff6b6b, #ee5a24); }
.cp-left.cp-blue { background: linear-gradient(135deg, #4facfe, #0078d4); }
.cp-left.cp-green { background: linear-gradient(135deg, #43e97b, #38f9d7); color: #1a5c3a; }
.cp-left-amount { font-size: 32px; font-weight: 800; line-height: 1.1; }
.cp-left-amount .cp-unit { font-size: 16px; font-weight: 600; }
.cp-left-cond { font-size: 12px; margin-top: 6px; opacity: 0.85; }
/* 右侧详情 */
.cp-right {
flex: 1; padding: 14px 18px; display: flex; flex-direction: column; justify-content: center; gap: 6px;
min-width: 0;
}
.cp-name { font-size: 15px; font-weight: 600; color: var(--g-text); }
.cp-validity { font-size: 12px; color: var(--g-text-muted); display: flex; align-items: center; gap: 4px; }
.cp-rules { font-size: 12px; color: var(--g-text-secondary); }
.cp-stats-row { display: flex; align-items: center; gap: 12px; font-size: 12px; color: var(--g-text-secondary); }
.cp-progress-wrap { flex: 1; max-width: 200px; }
.cp-progress-bar {
height: 6px; background: #f0f0f0; border-radius: 3px; overflow: hidden;
}
.cp-progress-fill { height: 100%; border-radius: 3px; transition: width var(--g-transition); }
.cp-progress-fill.red { background: linear-gradient(90deg, #ff6b6b, #ee5a24); }
.cp-progress-fill.blue { background: linear-gradient(90deg, #4facfe, #0078d4); }
.cp-progress-fill.green { background: linear-gradient(90deg, #43e97b, #38f9d7); }
.cp-bottom-row {
display: flex; align-items: center; justify-content: space-between; margin-top: 2px;
}
.cp-actions { display: flex; align-items: center; gap: 12px; }
/* 抽屉 */
.cp-drawer { width: 560px; }
.cp-type-pills { display: flex; gap: 8px; }
.cp-amount-row { display: flex; align-items: center; gap: 6px; font-size: 13px; color: var(--g-text-secondary); }
.cp-amount-row .g-input { width: 100px; }
.cp-valid-section { margin-top: 8px; }
.cp-channel-pills { display: flex; gap: 8px; flex-wrap: wrap; }
</style>
</head>
<body>
<!-- 工具栏 -->
<div class="cp-toolbar">
<select class="g-select" style="min-width:170px;">
<option>全部门店</option>
<option>老三家外卖(朝阳店)</option>
<option>老三家外卖(海淀店)</option>
<option>老三家外卖(望京店)</option>
<option>老三家外卖(通州店)</option>
<option>老三家外卖(丰台店)</option>
</select>
<select class="g-select" id="cpStatusFilter">
<option value="">全部状态</option>
<option value="进行中">进行中</option>
<option value="未开始">未开始</option>
<option value="已结束">已结束</option>
<option value="已停用">已停用</option>
</select>
<select class="g-select" id="cpTypeFilter">
<option value="">全部类型</option>
<option value="满减券">满减券</option>
<option value="折扣券">折扣券</option>
<option value="免配送费券">免配送费券</option>
</select>
<input class="g-input" placeholder="搜索券名称" id="cpSearch" style="min-width:160px;">
<div class="cp-toolbar-right">
<button class="g-btn g-btn-primary" onclick="openCpDrawer('create')">
<i data-lucide="plus" style="width:15px;height:15px;"></i> 创建优惠券
</button>
</div>
</div>
<!-- 统计行 -->
<div class="cp-stats">
<div class="cp-stat-item">
<div class="cp-stat-icon blue"><i data-lucide="ticket" style="width:18px;height:18px;"></i></div>
<div><div class="cp-stat-val">28</div><div class="cp-stat-label">优惠券总数</div></div>
</div>
<div class="cp-stat-item">
<div class="cp-stat-icon green"><i data-lucide="play-circle" style="width:18px;height:18px;"></i></div>
<div><div class="cp-stat-val">12</div><div class="cp-stat-label">进行中</div></div>
</div>
<div class="cp-stat-item">
<div class="cp-stat-icon orange"><i data-lucide="download" style="width:18px;height:18px;"></i></div>
<div><div class="cp-stat-val">3,680</div><div class="cp-stat-label">已领取(张)</div></div>
</div>
<div class="cp-stat-item">
<div class="cp-stat-icon purple"><i data-lucide="check-circle" style="width:18px;height:18px;"></i></div>
<div><div class="cp-stat-val">2,156</div><div class="cp-stat-label">已核销(核销率 58.6%</div></div>
</div>
</div>
<!-- 优惠券卡片列表 -->
<div class="cp-list" id="cpList">
<!-- 1. 满减券 - 进行中 -->
<div class="cp-coupon">
<div class="cp-left cp-red">
<div class="cp-left-amount"><span class="cp-unit">&yen;</span>15</div>
<div class="cp-left-cond">满50可用</div>
</div>
<div class="cp-right">
<div class="cp-name">新用户满减券</div>
<div class="cp-validity"><i data-lucide="calendar" style="width:12px;height:12px;"></i> 2025.02.01 - 2025.03.01</div>
<div class="cp-rules">每人限领1张 | 仅外卖可用</div>
<div class="cp-stats-row">
<span>已领 580/1000</span>
<div class="cp-progress-wrap">
<div class="cp-progress-bar"><div class="cp-progress-fill red" style="width:58%;"></div></div>
</div>
<span>已用 320</span>
</div>
<div class="cp-bottom-row">
<span class="g-tag g-tag-green">进行中</span>
<div class="cp-actions">
<a class="g-action" onclick="openCpDrawer('edit')">编辑</a>
<a class="g-action" onclick="void(0)">停用</a>
<a class="g-action g-action-danger" onclick="void(0)">删除</a>
</div>
</div>
</div>
</div>
<!-- 2. 折扣券 - 进行中 -->
<div class="cp-coupon">
<div class="cp-left cp-blue">
<div class="cp-left-amount">8<span class="cp-unit"></span></div>
<div class="cp-left-cond">全场通用</div>
</div>
<div class="cp-right">
<div class="cp-name">周末全场8折券</div>
<div class="cp-validity"><i data-lucide="calendar" style="width:12px;height:12px;"></i> 2025.01.15 - 2025.04.15</div>
<div class="cp-rules">每人限领2张 | 外卖/自提可用</div>
<div class="cp-stats-row">
<span>已领 1,200/2000</span>
<div class="cp-progress-wrap">
<div class="cp-progress-bar"><div class="cp-progress-fill blue" style="width:60%;"></div></div>
</div>
<span>已用 860</span>
</div>
<div class="cp-bottom-row">
<span class="g-tag g-tag-green">进行中</span>
<div class="cp-actions">
<a class="g-action" onclick="openCpDrawer('edit')">编辑</a>
<a class="g-action" onclick="void(0)">停用</a>
<a class="g-action g-action-danger" onclick="void(0)">删除</a>
</div>
</div>
</div>
</div>
<!-- 3. 免配送费券 - 未开始 -->
<div class="cp-coupon">
<div class="cp-left cp-green">
<div class="cp-left-amount" style="font-size:24px;">免配送</div>
<div class="cp-left-cond">无门槛</div>
</div>
<div class="cp-right">
<div class="cp-name">春季免配送费券</div>
<div class="cp-validity"><i data-lucide="calendar" style="width:12px;height:12px;"></i> 2025.03.01 - 2025.03.31</div>
<div class="cp-rules">每人限领3张 | 仅外卖可用</div>
<div class="cp-stats-row">
<span>已领 0/5000</span>
<div class="cp-progress-wrap">
<div class="cp-progress-bar"><div class="cp-progress-fill green" style="width:0%;"></div></div>
</div>
<span>已用 0</span>
</div>
<div class="cp-bottom-row">
<span class="g-tag g-tag-blue">未开始</span>
<div class="cp-actions">
<a class="g-action" onclick="openCpDrawer('edit')">编辑</a>
<a class="g-action" onclick="void(0)">停用</a>
<a class="g-action g-action-danger" onclick="void(0)">删除</a>
</div>
</div>
</div>
</div>
<!-- 4. 满减券 - 已结束 (dimmed) -->
<div class="cp-coupon cp-dimmed">
<div class="cp-left cp-red">
<div class="cp-left-amount"><span class="cp-unit">&yen;</span>10</div>
<div class="cp-left-cond">满30可用</div>
</div>
<div class="cp-right">
<div class="cp-name">元旦满减券</div>
<div class="cp-validity"><i data-lucide="calendar" style="width:12px;height:12px;"></i> 2024.12.28 - 2025.01.03</div>
<div class="cp-rules">每人限领1张 | 全渠道可用</div>
<div class="cp-stats-row">
<span>已领 900/1000</span>
<div class="cp-progress-wrap">
<div class="cp-progress-bar"><div class="cp-progress-fill red" style="width:90%;"></div></div>
</div>
<span>已用 756</span>
</div>
<div class="cp-bottom-row">
<span class="g-tag g-tag-gray">已结束</span>
<div class="cp-actions">
<a class="g-action" onclick="void(0)">查看</a>
<a class="g-action g-action-danger" onclick="void(0)">删除</a>
</div>
</div>
</div>
</div>
<!-- 5. 折扣券 - 已停用 (dimmed) -->
<div class="cp-coupon cp-dimmed">
<div class="cp-left cp-blue">
<div class="cp-left-amount">7<span class="cp-unit"></span></div>
<div class="cp-left-cond">满80可用</div>
</div>
<div class="cp-right">
<div class="cp-name">会员专享7折券</div>
<div class="cp-validity"><i data-lucide="calendar" style="width:12px;height:12px;"></i> 2025.01.01 - 2025.06.30</div>
<div class="cp-rules">每人限领1张 | 仅堂食可用</div>
<div class="cp-stats-row">
<span>已领 200/500</span>
<div class="cp-progress-wrap">
<div class="cp-progress-bar"><div class="cp-progress-fill blue" style="width:40%;"></div></div>
</div>
<span>已用 120</span>
</div>
<div class="cp-bottom-row">
<span class="g-tag g-tag-red">已停用</span>
<div class="cp-actions">
<a class="g-action" onclick="void(0)">启用</a>
<a class="g-action g-action-danger" onclick="void(0)">删除</a>
</div>
</div>
</div>
</div>
</div>
<!-- 分页 -->
<div class="g-pagination">
<span style="margin-right:8px;">共 28 条</span>
<button class="g-page-btn" disabled>&lt;</button>
<button class="g-page-btn active">1</button>
<button class="g-page-btn">2</button>
<button class="g-page-btn">3</button>
<button class="g-page-btn">&gt;</button>
</div>
<!-- 抽屉遮罩 -->
<div class="g-drawer-mask" id="cpDrawerMask" onclick="closeCpDrawer()"></div>
<!-- 创建/编辑抽屉 -->
<div class="g-drawer cp-drawer" id="cpDrawer">
<div class="g-drawer-hd">
<div class="g-drawer-title" id="cpDrawerTitle">创建优惠券</div>
<button class="g-drawer-close" onclick="closeCpDrawer()"><i data-lucide="x" style="width:18px;height:18px;"></i></button>
</div>
<div class="g-drawer-bd">
<!-- 券名称 -->
<div class="g-form-group">
<label class="g-form-label required">券名称</label>
<input class="g-input" placeholder="如:新用户满减券" id="cpName">
</div>
<!-- 券类型 -->
<div class="g-form-group">
<label class="g-form-label required">券类型</label>
<div class="cp-type-pills">
<span class="g-pill checked" onclick="selectCpType(this, 'reduce')">满减券</span>
<span class="g-pill" onclick="selectCpType(this, 'discount')">折扣券</span>
<span class="g-pill" onclick="selectCpType(this, 'free_delivery')">免配送费券</span>
</div>
</div>
<!-- 面额设置 - 满减 -->
<div class="g-form-group" id="cpAmountReduce">
<label class="g-form-label required">面额设置</label>
<div class="cp-amount-row">
<span></span>
<input class="g-input" type="number" placeholder="如50" id="cpThresholdInput">
<span>元减</span>
<input class="g-input" type="number" placeholder="如15">
<span></span>
</div>
</div>
<!-- 面额设置 - 折扣 -->
<div class="g-form-group" id="cpAmountDiscount" style="display:none;">
<label class="g-form-label required">折扣力度</label>
<div class="cp-amount-row">
<input class="g-input" type="number" placeholder="如8 表示8折" style="width:160px;">
<span></span>
</div>
<div class="g-hint">输入 8 表示打8折即优惠20%</div>
</div>
<!-- 面额设置 - 免配送费 -->
<div class="g-form-group" id="cpAmountFreeDelivery" style="display:none;">
<label class="g-form-label">面额设置</label>
<div style="font-size:13px;color:var(--g-text-muted);padding:8px 0;">免配送费券无需设置面额</div>
</div>
<div class="g-divider"></div>
<!-- 发放总量 -->
<div class="g-form-group">
<label class="g-form-label required">发放总量</label>
<input class="g-input" type="number" placeholder="如1000" style="width:200px;">
<div class="g-hint">设置后不可减少,可增加</div>
</div>
<!-- 每人限领 -->
<div class="g-form-group">
<label class="g-form-label">每人限领</label>
<input class="g-input" type="number" placeholder="如1" style="width:200px;">
<div class="g-hint">不填则不限制</div>
</div>
<div class="g-divider"></div>
<!-- 有效期类型 -->
<div class="g-form-group">
<label class="g-form-label required">有效期类型</label>
<div class="cp-type-pills">
<span class="g-pill checked" onclick="selectCpValidType(this, 'fixed')">固定时间</span>
<span class="g-pill" onclick="selectCpValidType(this, 'days')">领取后N天</span>
</div>
</div>
<!-- 固定时间 -->
<div class="g-form-group cp-valid-section" id="cpValidFixed">
<label class="g-form-label required">有效时间范围</label>
<div style="display:flex;align-items:center;gap:8px;">
<input class="g-input" type="date" style="width:180px;">
<span style="color:var(--g-text-muted);"></span>
<input class="g-input" type="date" style="width:180px;">
</div>
</div>
<!-- 领取后N天 -->
<div class="g-form-group cp-valid-section" id="cpValidDays" style="display:none;">
<label class="g-form-label required">领取后有效天数</label>
<div class="cp-amount-row">
<span>领取后</span>
<input class="g-input" type="number" placeholder="如7" style="width:120px;">
<span>天内有效</span>
</div>
</div>
<div class="g-divider"></div>
<!-- 使用门槛 -->
<div class="g-form-group" id="cpThresholdGroup">
<label class="g-form-label">使用门槛</label>
<div class="cp-amount-row">
<span>订单满</span>
<input class="g-input" type="number" placeholder="如50" style="width:140px;" id="cpThresholdVal">
<span>元可用</span>
</div>
<div class="g-hint">不填则无门槛</div>
</div>
<!-- 适用渠道 -->
<div class="g-form-group">
<label class="g-form-label">适用渠道</label>
<div class="cp-channel-pills">
<span class="g-pill checked" onclick="toggleCpPill(this)">外卖</span>
<span class="g-pill checked" onclick="toggleCpPill(this)">自提</span>
<span class="g-pill checked" onclick="toggleCpPill(this)">堂食</span>
</div>
</div>
<!-- 适用门店 -->
<div class="g-form-group">
<label class="g-form-label">适用门店</label>
<select class="g-select">
<option>全部门店</option>
<option>老三家外卖(朝阳店)</option>
<option>老三家外卖(海淀店)</option>
<option>老三家外卖(望京店)</option>
<option>老三家外卖(通州店)</option>
<option>老三家外卖(丰台店)</option>
</select>
</div>
</div>
<div class="g-drawer-ft">
<button class="g-btn" onclick="closeCpDrawer()">取消</button>
<button class="g-btn g-btn-primary">保存</button>
</div>
</div>
<script>
/* ===== 抽屉开关 ===== */
function openCpDrawer(mode) {
document.getElementById('cpDrawerMask').classList.add('open');
document.getElementById('cpDrawer').classList.add('open');
document.getElementById('cpDrawerTitle').textContent = mode === 'edit' ? '编辑优惠券' : '创建优惠券';
if (typeof lucide !== 'undefined') lucide.createIcons();
}
function closeCpDrawer() {
document.getElementById('cpDrawerMask').classList.remove('open');
document.getElementById('cpDrawer').classList.remove('open');
}
/* ===== 券类型切换 ===== */
function selectCpType(el, type) {
el.parentElement.querySelectorAll('.g-pill').forEach(function(p) { p.classList.remove('checked'); });
el.classList.add('checked');
document.getElementById('cpAmountReduce').style.display = type === 'reduce' ? '' : 'none';
document.getElementById('cpAmountDiscount').style.display = type === 'discount' ? '' : 'none';
document.getElementById('cpAmountFreeDelivery').style.display = type === 'free_delivery' ? '' : 'none';
// 免配送费时隐藏使用门槛
document.getElementById('cpThresholdGroup').style.display = type === 'free_delivery' ? 'none' : '';
}
/* ===== 有效期类型切换 ===== */
function selectCpValidType(el, type) {
el.parentElement.querySelectorAll('.g-pill').forEach(function(p) { p.classList.remove('checked'); });
el.classList.add('checked');
document.getElementById('cpValidFixed').style.display = type === 'fixed' ? '' : 'none';
document.getElementById('cpValidDays').style.display = type === 'days' ? '' : 'none';
}
/* ===== Pill 多选切换 ===== */
function toggleCpPill(el) {
el.classList.toggle('checked');
}
/* ===== 初始化图标 ===== */
if (typeof lucide !== 'undefined') lucide.createIcons();
</script>
</body>
</html>

472
pages/mkt-flash-sale.html Normal file
View File

@@ -0,0 +1,472 @@
<!-- 限时折扣页 -->
<style>
.fs-toolbar { display:flex; align-items:center; gap:12px; margin-bottom:16px; box-shadow:var(--g-shadow-sm); border-radius:10px; padding:12px 16px; background:#fff; flex-wrap:wrap; }
.fs-search { position:relative; }
.fs-search input { height:34px; padding:0 10px 0 32px; border:1px solid #e5e7eb; border-radius:8px; font-size:13px; outline:none; width:200px; transition:var(--g-transition); }
.fs-search input:focus { border-color:var(--primary); box-shadow:0 0 0 3px color-mix(in srgb, var(--primary) 12%, transparent); }
.fs-search i { position:absolute; left:9px; top:50%; transform:translateY(-50%); color:#bbb; pointer-events:none; }
.fs-stats { display:flex; gap:24px; margin-bottom:16px; padding:10px 16px; background:#fff; border-radius:10px; box-shadow:var(--g-shadow-sm); font-size:13px; color:#4b5563; }
.fs-stats span { display:flex; align-items:center; gap:6px; }
.fs-stats strong { color:#1a1a2e; font-weight:600; }
.fs-card { background:#fff; border-radius:10px; box-shadow:var(--g-shadow-sm); padding:20px; margin-bottom:16px; transition:box-shadow var(--g-transition); }
.fs-card:hover { box-shadow:var(--g-shadow-md); }
.fs-card.ended { opacity:.5; }
.fs-card-hd { display:flex; align-items:center; gap:10px; margin-bottom:14px; flex-wrap:wrap; }
.fs-card-name { font-size:15px; font-weight:600; color:#1a1a2e; }
.fs-card-time { font-size:12px; color:var(--g-text-muted); display:flex; align-items:center; gap:4px; }
.fs-tag-running { background:#dcfce7; color:#22c55e; border:1px solid #bbf7d0; border-radius:6px; font-weight:600; }
.fs-tag-ended { background:#f8f9fb; color:#9ca3af; border:1px solid #e5e7eb; border-radius:6px; font-weight:600; }
.fs-prod-table { width:100%; border-collapse:collapse; font-size:13px; margin-bottom:14px; }
.fs-prod-table th { background:#f8f9fb; padding:8px 12px; text-align:left; font-weight:600; color:#6b7280; border-bottom:1px solid #e5e7eb; font-size:12px; }
.fs-prod-table td { padding:8px 12px; border-bottom:1px solid #f3f4f6; color:#1a1a2e; }
.fs-prod-table tr:last-child td { border-bottom:none; }
.fs-prod-table tr:hover td { background:color-mix(in srgb, var(--primary) 3%, #fff); }
.fs-orig-price { color:var(--g-text-muted); text-decoration:line-through; font-size:12px; }
.fs-disc-price { color:#ef4444; font-weight:600; }
.fs-disc-rate { display:inline-block; padding:1px 6px; border-radius:4px; background:#fff1f0; color:#ef4444; font-size:11px; font-weight:600; }
.fs-card-summary { display:flex; gap:24px; font-size:12px; color:#4b5563; margin-bottom:12px; padding:10px 12px; background:#f8f9fb; border-radius:8px; }
.fs-card-summary strong { color:#1a1a2e; font-weight:600; }
.fs-card-ft { display:flex; gap:16px; border-top:1px solid #f3f4f6; padding-top:12px; }
/* 抽屉内折扣商品列表 */
.fs-drawer-prod { border:1px solid #e5e7eb; border-radius:8px; overflow:hidden; margin-bottom:12px; }
.fs-drawer-prod-hd { display:flex; align-items:center; justify-content:space-between; padding:8px 12px; background:#f8f9fb; border-bottom:1px solid #e5e7eb; }
.fs-drawer-prod-hd span { font-size:13px; font-weight:500; color:#1a1a2e; }
.fs-drawer-prod-remove { background:none; border:none; color:#9ca3af; cursor:pointer; display:flex; align-items:center; justify-content:center; width:24px; height:24px; border-radius:6px; transition:var(--g-transition); }
.fs-drawer-prod-remove:hover { color:#ef4444; background:#fef2f2; }
.fs-drawer-prod-bd { padding:10px 12px; display:flex; gap:12px; align-items:center; flex-wrap:wrap; }
.fs-drawer-prod-bd .fs-field { display:flex; flex-direction:column; gap:4px; }
.fs-drawer-prod-bd .fs-field label { font-size:11px; color:#6b7280; }
.fs-drawer-prod-bd .fs-field input { width:100px; height:30px; padding:0 8px; border:1px solid #e5e7eb; border-radius:6px; font-size:13px; outline:none; transition:var(--g-transition); }
.fs-drawer-prod-bd .fs-field input:focus { border-color:var(--primary); box-shadow:0 0 0 3px color-mix(in srgb, var(--primary) 12%, transparent); }
.fs-drawer-prod-bd .fs-field input[readonly] { background:#f8f9fb; color:var(--g-text-muted); cursor:default; }
.fs-drawer-prod-bd .fs-field .fs-auto-rate { font-size:12px; color:#ef4444; font-weight:600; min-width:36px; line-height:30px; }
.fs-pill-group { display:flex; gap:8px; flex-wrap:wrap; }
.fs-pill { padding:6px 16px; border-radius:8px; border:1px solid #e5e7eb; background:#fff; font-size:13px; cursor:pointer; color:#4b5563; transition:var(--g-transition); }
.fs-pill:hover { border-color:var(--primary); color:var(--primary); }
.fs-pill.checked { background:var(--primary); color:#fff; border-color:var(--primary); }
.fs-limit-row { display:flex; align-items:center; gap:8px; }
.fs-limit-row input { width:80px; }
.fs-limit-row .fs-unit { font-size:13px; color:var(--g-text-secondary); }
.fs-tag-recurring { background:#f0f5ff; color:var(--primary); border:1px solid #adc6ff; border-radius:6px; font-weight:600; }
.fs-tag-notstarted { background:#f0f5ff; color:var(--primary); border:1px solid #adc6ff; border-radius:6px; font-weight:600; }
.fs-recur-badge { display:inline-flex; align-items:center; gap:3px; padding:2px 8px; border-radius:4px; font-size:11px; font-weight:600; background:#f0f5ff; color:var(--primary); border:1px solid #adc6ff; }
.fs-day-sel { display:flex; gap:6px; flex-wrap:wrap; margin-bottom:8px; }
.fs-day { width:40px; height:28px; border-radius:14px; font-size:12px; display:inline-flex; align-items:center; justify-content:center; border:1px solid #e5e7eb; color:#9ca3af; background:#f8f9fb; cursor:pointer; transition:var(--g-transition); }
.fs-day.active { background:var(--primary); color:#fff; border-color:var(--primary); }
.fs-day-quick { display:flex; gap:6px; }
.fs-cycle-section { display:none; }
.fs-cycle-section.show { display:block; }
.fs-date-section { display:block; }
.fs-date-section.hide { display:none; }
</style>
<div class="fs-toolbar">
<select class="g-select" style="width:200px;">
<option>老三家外卖(朝阳店)</option>
<option>老三家外卖(海淀店)</option>
<option>老三家外卖(望京店)</option>
<option>老三家外卖(通州店)</option>
<option>老三家外卖(丰台店)</option>
</select>
<select class="g-select" style="width:120px;">
<option value="">全部状态</option>
<option>进行中</option>
<option>已结束</option>
<option>未开始</option>
</select>
<div class="fs-search">
<i data-lucide="search" style="width:14px;height:14px;"></i>
<input type="text" placeholder="搜索活动名称…" />
</div>
<div style="flex:1;"></div>
<button class="g-btn g-btn-primary" onclick="openFsDrawer('add')"><i data-lucide="plus" style="width:14px;height:14px;"></i>创建限时折扣</button>
</div>
<div class="fs-stats">
<span>活动总数 <strong>6</strong></span>
<span>进行中 <strong>3</strong></span>
<span>参与商品 <strong>18</strong></span>
<span>本月折扣销量 <strong>560</strong></span>
</div>
<!-- 活动卡片列表 -->
<div id="fsActivityList">
<!-- 午市特惠 - 进行中 -->
<div class="fs-card">
<div class="fs-card-hd">
<span class="fs-card-name">午市特惠</span>
<span class="g-tag fs-tag-running">进行中</span>
<span class="fs-card-time"><i data-lucide="clock" style="width:12px;height:12px;"></i>每天 11:00 - 14:00</span>
<span class="fs-card-time"><i data-lucide="calendar" style="width:12px;height:12px;"></i>2026-01-01 ~ 2026-03-31</span>
</div>
<table class="fs-prod-table">
<thead><tr><th>商品名</th><th>原价</th><th>折扣价</th><th>折扣力度</th><th>限购</th><th>已售</th></tr></thead>
<tbody>
<tr><td>香辣鸡腿堡套餐</td><td class="fs-orig-price">¥32.00</td><td class="fs-disc-price">¥25.60</td><td><span class="fs-disc-rate">8折</span></td><td>2件/人</td><td>86</td></tr>
<tr><td>牛肉拌面</td><td class="fs-orig-price">¥28.00</td><td class="fs-disc-price">¥19.60</td><td><span class="fs-disc-rate">7折</span></td><td>1件/人</td><td>52</td></tr>
<tr><td>招牌卤肉饭</td><td class="fs-orig-price">¥22.00</td><td class="fs-disc-price">¥17.60</td><td><span class="fs-disc-rate">8折</span></td><td>2件/人</td><td>42</td></tr>
</tbody>
</table>
<div class="fs-card-summary">
<span>活动销量 <strong>180</strong></span>
<span>折扣总额 <strong>¥1,260</strong></span>
</div>
<div class="fs-card-ft">
<span class="g-action" onclick="openFsDrawer('edit','午市特惠')">编辑</span>
<span class="g-action">停用</span>
<span class="g-action g-action-danger">删除</span>
</div>
</div>
<!-- 新品尝鲜 - 进行中 -->
<div class="fs-card">
<div class="fs-card-hd">
<span class="fs-card-name">新品尝鲜</span>
<span class="g-tag fs-tag-running">进行中</span>
<span class="fs-card-time"><i data-lucide="clock" style="width:12px;height:12px;"></i>每天 10:00 - 22:00</span>
<span class="fs-card-time"><i data-lucide="calendar" style="width:12px;height:12px;"></i>2026-02-01 ~ 2026-02-28</span>
</div>
<table class="fs-prod-table">
<thead><tr><th>商品名</th><th>原价</th><th>折扣价</th><th>折扣力度</th><th>限购</th><th>已售</th></tr></thead>
<tbody>
<tr><td>黑松露鸡肉卷</td><td class="fs-orig-price">¥38.00</td><td class="fs-disc-price">¥26.60</td><td><span class="fs-disc-rate">7折</span></td><td>1件/人</td><td>67</td></tr>
<tr><td>芝士焗虾饭</td><td class="fs-orig-price">¥35.00</td><td class="fs-disc-price">¥28.00</td><td><span class="fs-disc-rate">8折</span></td><td>1件/人</td><td>45</td></tr>
<tr><td>抹茶冰淇淋</td><td class="fs-orig-price">¥18.00</td><td class="fs-disc-price">¥9.00</td><td><span class="fs-disc-rate">5折</span></td><td>2件/人</td><td>120</td></tr>
<tr><td>鲜榨牛油果汁</td><td class="fs-orig-price">¥22.00</td><td class="fs-disc-price">¥15.40</td><td><span class="fs-disc-rate">7折</span></td><td>1件/人</td><td>38</td></tr>
</tbody>
</table>
<div class="fs-card-summary">
<span>活动销量 <strong>270</strong></span>
<span>折扣总额 <strong>¥2,480</strong></span>
</div>
<div class="fs-card-ft">
<span class="g-action" onclick="openFsDrawer('edit','新品尝鲜')">编辑</span>
<span class="g-action">停用</span>
<span class="g-action g-action-danger">删除</span>
</div>
</div>
<!-- 周末狂欢 - 已结束 -->
<div class="fs-card ended">
<div class="fs-card-hd">
<span class="fs-card-name">周末狂欢</span>
<span class="g-tag fs-tag-ended">已结束</span>
<span class="fs-recur-badge"><i data-lucide="repeat" style="width:11px;height:11px;"></i>每周六、日</span>
<span class="fs-card-time"><i data-lucide="clock" style="width:12px;height:12px;"></i>17:00 - 21:00</span>
<span class="fs-card-time"><i data-lucide="calendar" style="width:12px;height:12px;"></i>2025-12-01 ~ 2026-01-31</span>
</div>
<table class="fs-prod-table">
<thead><tr><th>商品名</th><th>原价</th><th>折扣价</th><th>折扣力度</th><th>限购</th><th>已售</th></tr></thead>
<tbody>
<tr><td>烤鸡翅拼盘</td><td class="fs-orig-price">¥48.00</td><td class="fs-disc-price">¥33.60</td><td><span class="fs-disc-rate">7折</span></td><td>1件/人</td><td>95</td></tr>
<tr><td>小龙虾套餐</td><td class="fs-orig-price">¥68.00</td><td class="fs-disc-price">¥47.60</td><td><span class="fs-disc-rate">7折</span></td><td>1件/人</td><td>62</td></tr>
<tr><td>啤酒6连包</td><td class="fs-orig-price">¥36.00</td><td class="fs-disc-price">¥21.60</td><td><span class="fs-disc-rate">6折</span></td><td>2件/人</td><td>78</td></tr>
</tbody>
</table>
<div class="fs-card-summary">
<span>活动销量 <strong>235</strong></span>
<span>折扣总额 <strong>¥3,920</strong></span>
</div>
<div class="fs-card-ft">
<span class="g-action" onclick="openFsDrawer('edit','周末狂欢')">编辑</span>
<span class="g-action">启用</span>
<span class="g-action g-action-danger">删除</span>
</div>
</div>
<!-- 疯狂星期四 - 进行中(周期循环) -->
<div class="fs-card">
<div class="fs-card-hd">
<span class="fs-card-name">疯狂星期四</span>
<span class="g-tag fs-tag-running">进行中</span>
<span class="fs-recur-badge"><i data-lucide="repeat" style="width:11px;height:11px;"></i>每周四</span>
<span class="fs-card-time"><i data-lucide="clock" style="width:12px;height:12px;"></i>11:00 - 22:00</span>
<span class="fs-card-time"><i data-lucide="calendar" style="width:12px;height:12px;"></i>长期有效</span>
</div>
<table class="fs-prod-table">
<thead><tr><th>商品名</th><th>原价</th><th>折扣价</th><th>折扣力度</th><th>限购</th><th>已售</th></tr></thead>
<tbody>
<tr><td>香辣鸡腿堡</td><td class="fs-orig-price">¥22.00</td><td class="fs-disc-price">¥9.90</td><td><span class="fs-disc-rate">4.5折</span></td><td>2件/人</td><td>326</td></tr>
<tr><td>黄金脆皮鸡</td><td class="fs-orig-price">¥39.90</td><td class="fs-disc-price">¥19.90</td><td><span class="fs-disc-rate">5折</span></td><td>1件/人</td><td>218</td></tr>
<tr><td>薯条(大)</td><td class="fs-orig-price">¥16.00</td><td class="fs-disc-price">¥6.00</td><td><span class="fs-disc-rate">3.8折</span></td><td>3件/人</td><td>485</td></tr>
<tr><td>可乐(大杯)</td><td class="fs-orig-price">¥12.00</td><td class="fs-disc-price">¥1.00</td><td><span class="fs-disc-rate">0.8折</span></td><td>1件/人</td><td>512</td></tr>
</tbody>
</table>
<div class="fs-card-summary">
<span>活动销量 <strong>1,541</strong></span>
<span>折扣总额 <strong>¥18,260</strong></span>
<span>已循环 <strong>12</strong></span>
</div>
<div class="fs-card-ft">
<span class="g-action" onclick="openFsDrawer('edit','疯狂星期四')">编辑</span>
<span class="g-action">停用</span>
<span class="g-action g-action-danger">删除</span>
</div>
</div>
</div>
<div class="g-pagination" style="margin-top:8px;">
<button class="g-page-btn" disabled><i data-lucide="chevron-left" style="width:14px;height:14px;"></i></button>
<button class="g-page-btn active">1</button>
<button class="g-page-btn">2</button>
<button class="g-page-btn"><i data-lucide="chevron-right" style="width:14px;height:14px;"></i></button>
<span style="font-size:12px;color:var(--g-text-muted);margin-left:8px;">共 6 条</span>
</div>
<!-- 创建/编辑抽屉 -->
<div class="g-drawer-mask" id="fsDrawerMask" onclick="closeFsDrawer()"></div>
<div class="g-drawer" id="fsDrawer" style="width:560px">
<div class="g-drawer-hd">
<span class="g-drawer-title" id="fsDrawerTitle">创建限时折扣</span>
<button class="g-drawer-close" onclick="closeFsDrawer()"><i data-lucide="x" style="width:18px;height:18px;"></i></button>
</div>
<div class="g-drawer-bd">
<div class="g-form-group">
<label class="g-form-label required">活动名称</label>
<input class="g-input" id="fsName" placeholder="请输入活动名称,如:午市特惠、新品尝鲜">
</div>
<div class="g-form-group">
<label class="g-form-label required">活动周期</label>
<div class="fs-pill-group" style="margin-bottom:10px;">
<span class="fs-pill checked" onclick="selectFsCycle(this,'once')">一次性活动</span>
<span class="fs-pill" onclick="selectFsCycle(this,'recurring')">周期循环</span>
</div>
<div class="g-hint">周期循环适合固定日期的促销,如"疯狂星期四"、"周末特惠"</div>
</div>
<div class="g-form-group fs-date-section" id="fsDateSection">
<label class="g-form-label required">活动时间</label>
<div style="display:flex;gap:8px;align-items:center;">
<input class="g-input" type="date" id="fsDateStart" value="2026-02-01" style="flex:1;">
<span style="color:var(--g-text-muted);">~</span>
<input class="g-input" type="date" id="fsDateEnd" value="2026-03-31" style="flex:1;">
</div>
</div>
<div class="g-form-group">
<label class="g-form-label">每日时段</label>
<div style="display:flex;gap:8px;align-items:center;">
<input class="g-input" type="time" id="fsTimeStart" value="11:00" style="flex:1;">
<span style="color:var(--g-text-muted);">~</span>
<input class="g-input" type="time" id="fsTimeEnd" value="14:00" style="flex:1;">
</div>
<div class="g-hint">留空则全天有效</div>
</div>
<div class="g-form-group fs-cycle-section" id="fsCycleSection">
<label class="g-form-label required">循环日期</label>
<div class="fs-day-sel" id="fsDaySel">
<span class="fs-day" onclick="toggleFsDay(this)">周一</span>
<span class="fs-day" onclick="toggleFsDay(this)">周二</span>
<span class="fs-day" onclick="toggleFsDay(this)">周三</span>
<span class="fs-day active" onclick="toggleFsDay(this)">周四</span>
<span class="fs-day" onclick="toggleFsDay(this)">周五</span>
<span class="fs-day" onclick="toggleFsDay(this)">周六</span>
<span class="fs-day" onclick="toggleFsDay(this)">周日</span>
</div>
<div class="fs-day-quick">
<button class="g-btn g-btn-sm" onclick="fsDayQuick('weekday')">工作日</button>
<button class="g-btn g-btn-sm" onclick="fsDayQuick('weekend')">周末</button>
<button class="g-btn g-btn-sm" onclick="fsDayQuick('all')">全选</button>
</div>
<div class="g-hint" style="margin-top:8px;">活动将在选中的每个星期自动生效,无需手动开启</div>
</div>
<!-- 折扣商品 -->
<div class="g-form-group">
<label class="g-form-label required" style="margin-bottom:10px;">折扣商品</label>
<div id="fsProductList">
<!-- 预填商品 1 -->
<div class="fs-drawer-prod">
<div class="fs-drawer-prod-hd">
<span>香辣鸡腿堡套餐</span>
<button class="fs-drawer-prod-remove" onclick="removeFsProduct(this)"><i data-lucide="x" style="width:14px;height:14px;"></i></button>
</div>
<div class="fs-drawer-prod-bd">
<div class="fs-field">
<label>原价</label>
<input type="text" value="¥32.00" readonly>
</div>
<div class="fs-field">
<label>折扣价</label>
<input type="number" value="25.60" step="0.01" placeholder="请输入折扣价" oninput="calcFsRate(this)">
</div>
<div class="fs-field">
<label>折扣</label>
<span class="fs-auto-rate">8折</span>
</div>
<div class="fs-field">
<label>限购/人</label>
<input type="number" value="2" min="0" placeholder="不限则留空">
</div>
</div>
</div>
<!-- 预填商品 2 -->
<div class="fs-drawer-prod">
<div class="fs-drawer-prod-hd">
<span>牛肉拌面</span>
<button class="fs-drawer-prod-remove" onclick="removeFsProduct(this)"><i data-lucide="x" style="width:14px;height:14px;"></i></button>
</div>
<div class="fs-drawer-prod-bd">
<div class="fs-field">
<label>原价</label>
<input type="text" value="¥28.00" readonly>
</div>
<div class="fs-field">
<label>折扣价</label>
<input type="number" value="19.60" step="0.01" placeholder="请输入折扣价" oninput="calcFsRate(this)">
</div>
<div class="fs-field">
<label>折扣</label>
<span class="fs-auto-rate">7折</span>
</div>
<div class="fs-field">
<label>限购/人</label>
<input type="number" value="1" min="0" placeholder="不限则留空">
</div>
</div>
</div>
<!-- 预填商品 3 -->
<div class="fs-drawer-prod">
<div class="fs-drawer-prod-hd">
<span>招牌卤肉饭</span>
<button class="fs-drawer-prod-remove" onclick="removeFsProduct(this)"><i data-lucide="x" style="width:14px;height:14px;"></i></button>
</div>
<div class="fs-drawer-prod-bd">
<div class="fs-field">
<label>原价</label>
<input type="text" value="¥22.00" readonly>
</div>
<div class="fs-field">
<label>折扣价</label>
<input type="number" value="17.60" step="0.01" placeholder="请输入折扣价" oninput="calcFsRate(this)">
</div>
<div class="fs-field">
<label>折扣</label>
<span class="fs-auto-rate">8折</span>
</div>
<div class="fs-field">
<label>限购/人</label>
<input type="number" value="2" min="0" placeholder="不限则留空">
</div>
</div>
</div>
</div>
<button class="g-btn g-btn-sm" style="margin-top:4px;" onclick="openProductPicker({title:'添加折扣商品',subtitle:'限时折扣',onConfirm:addFsProducts})"><i data-lucide="plus" style="width:13px;height:13px;"></i>添加商品</button>
</div>
<!-- 适用渠道 -->
<div class="g-form-group">
<label class="g-form-label">适用渠道</label>
<div class="fs-pill-group">
<span class="fs-pill checked" onclick="toggleFsPill(this)">外卖配送</span>
<span class="fs-pill checked" onclick="toggleFsPill(this)">到店自取</span>
<span class="fs-pill" onclick="toggleFsPill(this)">堂食点餐</span>
</div>
</div>
<!-- 限购设置 -->
<div class="g-form-group">
<label class="g-form-label">每人限购</label>
<div class="fs-limit-row">
<input class="g-input" type="number" value="5" min="0" placeholder="不限则留空" style="width:100px;">
<span class="fs-unit"></span>
</div>
<div class="g-hint">活动期间每人累计可购买的折扣商品总数,留空不限</div>
</div>
</div>
<div class="g-drawer-ft">
<button class="g-btn" onclick="closeFsDrawer()">取消</button>
<button class="g-btn g-btn-primary" onclick="closeFsDrawer()">保存</button>
</div>
</div>
<script>
function openFsDrawer(mode, name) {
document.getElementById('fsDrawerMask').classList.add('open');
document.getElementById('fsDrawer').classList.add('open');
if (mode === 'edit') {
document.getElementById('fsDrawerTitle').textContent = '编辑限时折扣';
document.getElementById('fsName').value = name || '';
} else {
document.getElementById('fsDrawerTitle').textContent = '创建限时折扣';
document.getElementById('fsName').value = '';
}
if (typeof lucide !== 'undefined') lucide.createIcons();
}
function closeFsDrawer() {
document.getElementById('fsDrawerMask').classList.remove('open');
document.getElementById('fsDrawer').classList.remove('open');
}
function removeFsProduct(btn) {
btn.closest('.fs-drawer-prod').remove();
}
function toggleFsPill(el) {
el.classList.toggle('checked');
}
/* 自动计算折扣率 */
function calcFsRate(input) {
var row = input.closest('.fs-drawer-prod-bd');
var origText = row.querySelector('input[readonly]').value.replace(/[¥,]/g, '');
var orig = parseFloat(origText);
var disc = parseFloat(input.value);
var rateEl = row.querySelector('.fs-auto-rate');
if (orig > 0 && disc > 0 && disc <= orig) {
var r = Math.round((disc / orig) * 10);
rateEl.textContent = r + '折';
} else {
rateEl.textContent = '-';
}
}
/* 活动周期切换 */
function selectFsCycle(el, type) {
el.parentElement.querySelectorAll('.fs-pill').forEach(function(p) { p.classList.remove('checked'); });
el.classList.add('checked');
var dateSection = document.getElementById('fsDateSection');
var cycleSection = document.getElementById('fsCycleSection');
if (type === 'recurring') {
dateSection.classList.add('hide');
cycleSection.classList.add('show');
} else {
dateSection.classList.remove('hide');
cycleSection.classList.remove('show');
}
}
function toggleFsDay(el) {
el.classList.toggle('active');
}
function fsDayQuick(mode) {
var days = document.querySelectorAll('#fsDaySel .fs-day');
days.forEach(function(d, i) {
if (mode === 'all') d.classList.add('active');
else if (mode === 'weekday') d.classList.toggle('active', i < 5);
else if (mode === 'weekend') d.classList.toggle('active', i >= 5);
});
}
/* 商品选择器回调 */
function addFsProducts(items) {
var list = document.getElementById('fsProductList');
items.forEach(function(p) {
var price = p.price || (Math.floor(Math.random() * 30 + 15) + '.00');
var div = document.createElement('div');
div.className = 'fs-drawer-prod';
div.innerHTML =
'<div class="fs-drawer-prod-hd"><span>' + p.name + '</span>' +
'<button class="fs-drawer-prod-remove" onclick="removeFsProduct(this)"><i data-lucide="x" style="width:14px;height:14px;"></i></button></div>' +
'<div class="fs-drawer-prod-bd">' +
'<div class="fs-field"><label>原价</label><input type="text" value="¥' + price + '" readonly></div>' +
'<div class="fs-field"><label>折扣价</label><input type="number" step="0.01" placeholder="请输入折扣价" oninput="calcFsRate(this)"></div>' +
'<div class="fs-field"><label>折扣</label><span class="fs-auto-rate">-</span></div>' +
'<div class="fs-field"><label>限购/人</label><input type="number" min="0" placeholder="不限则留空"></div>' +
'</div>';
list.appendChild(div);
});
if (typeof lucide !== 'undefined') lucide.createIcons();
}
</script>

396
pages/mkt-new-customer.html Normal file
View File

@@ -0,0 +1,396 @@
<!-- 新客有礼页 -->
<style>
.page-nc { max-width:960px; }
.nc-toolbar { display:flex; align-items:center; gap:12px; margin-bottom:16px; box-shadow:var(--g-shadow-sm); border-radius:10px; padding:10px 14px; background:#fff; }
.nc-toolbar select { height:34px; padding:0 10px; border:1px solid #e5e7eb; border-radius:8px; font-size:13px; outline:none; background:#fff; transition:var(--g-transition); }
.nc-toolbar select:focus { border-color:var(--primary); box-shadow:0 0 0 3px color-mix(in srgb, var(--primary) 12%, transparent); }
.nc-banner { display:flex; align-items:center; gap:12px; padding:14px 18px; background:linear-gradient(135deg, color-mix(in srgb, var(--primary) 8%, #fff), color-mix(in srgb, var(--primary) 3%, #fff)); border:1px solid color-mix(in srgb, var(--primary) 15%, #fff); border-radius:10px; margin-bottom:16px; }
.nc-banner-icon { width:40px; height:40px; border-radius:10px; background:color-mix(in srgb, var(--primary) 12%, #fff); display:flex; align-items:center; justify-content:center; color:var(--primary); flex-shrink:0; }
.nc-banner-text { font-size:13px; color:var(--g-text-secondary); line-height:1.6; }
.nc-banner-text strong { color:var(--g-text); font-weight:600; }
.nc-stats { display:grid; grid-template-columns:repeat(3,1fr); gap:16px; margin-bottom:16px; }
.nc-stat-card { background:#fff; border:1px solid var(--g-border); border-radius:10px; padding:18px 20px; transition:box-shadow var(--g-transition); }
.nc-stat-card:hover { box-shadow:var(--g-shadow-md); }
.nc-stat-label { font-size:12px; color:var(--g-text-muted); margin-bottom:8px; display:flex; align-items:center; gap:6px; }
.nc-stat-val { font-size:28px; font-weight:700; color:var(--g-text); display:flex; align-items:baseline; gap:8px; }
.nc-stat-unit { font-size:13px; font-weight:400; color:var(--g-text-muted); }
.nc-stat-trend { font-size:12px; font-weight:500; display:inline-flex; align-items:center; gap:2px; }
.nc-stat-trend.up { color:var(--g-success); }
.nc-stat-sub { font-size:11px; color:var(--g-text-muted); margin-top:4px; }
.nc-section-hd { font-size:15px; font-weight:600; color:var(--g-text); padding-left:10px; border-left:3px solid var(--primary); margin-bottom:16px; }
.nc-row { display:flex; align-items:flex-start; gap:16px; padding:14px 0; border-bottom:1px solid #f3f4f6; }
.nc-row:last-child { border-bottom:none; }
.nc-row-label { width:120px; flex-shrink:0; font-size:13px; font-weight:500; color:var(--g-text); line-height:22px; padding-top:5px; }
.nc-row-ctrl { flex:1; display:flex; flex-direction:column; gap:6px; }
.nc-row-ctrl-inline { display:flex; align-items:center; gap:8px; flex-wrap:wrap; }
.nc-row-hint { font-size:11px; color:var(--g-text-muted); }
.nc-coupon-list { display:flex; flex-direction:column; gap:10px; }
.nc-coupon-item { display:flex; align-items:center; gap:12px; padding:12px 14px; background:#fff; border:1px solid var(--g-border); border-radius:8px; border-left:3px solid var(--primary); transition:box-shadow var(--g-transition); }
.nc-coupon-item:nth-child(2) { border-left-color:var(--g-success); }
.nc-coupon-item:nth-child(3) { border-left-color:var(--g-warning); }
.nc-coupon-item:hover { box-shadow:var(--g-shadow); }
.nc-coupon-info { flex:1; }
.nc-coupon-name { font-size:14px; font-weight:600; color:var(--g-text); }
.nc-coupon-desc { font-size:11px; color:var(--g-text-muted); margin-top:2px; }
.nc-coupon-validity { font-size:12px; color:var(--g-text-secondary); display:flex; align-items:center; gap:4px; }
.nc-add-coupon-btn { display:flex; align-items:center; justify-content:center; gap:6px; padding:12px; border:2px dashed #d9d9d9; border-radius:8px; color:var(--g-text-muted); font-size:13px; cursor:pointer; transition:all var(--g-transition); background:none; width:100%; }
.nc-add-coupon-btn:hover { border-color:var(--primary); color:var(--primary); background:color-mix(in srgb, var(--primary) 3%, #fff); }
.nc-direct-panel { display:flex; flex-direction:column; gap:12px; }
.nc-share-url { display:flex; align-items:center; gap:8px; }
.nc-share-url input { flex:1; }
.nc-invite-table { margin-top:8px; }
.nc-save-bar { margin-top:20px; display:flex; justify-content:flex-end; gap:8px; }
.nc-num-input { width:100px; height:34px; padding:0 10px; border:1px solid #e5e7eb; border-radius:8px; font-size:13px; outline:none; transition:var(--g-transition); }
.nc-num-input:focus { border-color:var(--primary); box-shadow:0 0 0 3px color-mix(in srgb, var(--primary) 12%, transparent); }
.nc-suffix { font-size:13px; color:var(--g-text-secondary); white-space:nowrap; }
</style>
<div class="page-nc">
<!-- 顶部工具栏 -->
<div class="nc-toolbar">
<select style="width:200px;">
<option>老三家外卖(朝阳店)</option>
<option>老三家外卖(海淀店)</option>
<option>老三家外卖(望京店)</option>
<option>老三家外卖(通州店)</option>
<option>老三家外卖(丰台店)</option>
</select>
<div style="flex:1;"></div>
</div>
<!-- 说明横幅 -->
<div class="nc-banner">
<div class="nc-banner-icon"><i data-lucide="gift" style="width:20px;height:20px;"></i></div>
<div class="nc-banner-text"><strong>新客有礼</strong> — 新用户首次下单自动发放优惠礼包,提升新客转化率。开启后,每位新注册用户将自动收到您配置的欢迎礼包。</div>
</div>
<!-- 统计卡片 -->
<div class="nc-stats">
<div class="nc-stat-card">
<div class="nc-stat-label"><i data-lucide="user-plus" style="width:14px;height:14px;color:var(--primary);"></i>本月新客</div>
<div class="nc-stat-val">86<span class="nc-stat-unit"></span><span class="nc-stat-trend up"><i data-lucide="trending-up" style="width:14px;height:14px;"></i>+23%</span></div>
<div class="nc-stat-sub">较上月增长 16 人</div>
</div>
<div class="nc-stat-card">
<div class="nc-stat-label"><i data-lucide="ticket" style="width:14px;height:14px;color:var(--g-warning);"></i>礼包领取率</div>
<div class="nc-stat-val">92.4<span class="nc-stat-unit">%</span></div>
<div class="nc-stat-sub">86 人中 80 人已领取</div>
</div>
<div class="nc-stat-card">
<div class="nc-stat-label"><i data-lucide="shopping-bag" style="width:14px;height:14px;color:var(--g-success);"></i>新客转化率</div>
<div class="nc-stat-val">68.5<span class="nc-stat-unit">%</span></div>
<div class="nc-stat-sub">首单完成率59 人已下单</div>
</div>
</div>
<!-- 新客礼包配置 -->
<div class="g-card" style="padding:0; margin-bottom:16px;">
<div style="padding:18px 18px 0;">
<div class="nc-section-hd">新客礼包配置</div>
</div>
<div style="padding:0 18px 18px;">
<!-- 启用状态 -->
<div class="nc-row">
<div class="nc-row-label">启用状态</div>
<div class="nc-row-ctrl">
<div class="nc-row-ctrl-inline">
<div class="g-toggle on" onclick="toggleSwitch(this)"></div>
<span class="g-toggle-label">开启后新用户注册即发放礼包</span>
</div>
</div>
</div>
<!-- 礼包类型 -->
<div class="nc-row">
<div class="nc-row-label">礼包类型</div>
<div class="nc-row-ctrl">
<div class="nc-row-ctrl-inline">
<span class="g-pill checked" onclick="selectNcType('coupon',this)">优惠券包</span>
<span class="g-pill" onclick="selectNcType('direct',this)">首单直减</span>
</div>
</div>
</div>
<!-- 优惠券包内容 -->
<div id="ncTypeCoupon">
<div class="nc-row" style="flex-direction:column; gap:12px;">
<div class="nc-row-label" style="width:auto;">券包内容</div>
<div class="nc-coupon-list" id="ncCouponList">
<div class="nc-coupon-item" data-idx="0">
<div class="nc-coupon-info">
<div class="nc-coupon-name">满30减5</div>
<div class="nc-coupon-desc">订单满 30 元可用</div>
</div>
<div class="nc-coupon-validity"><i data-lucide="clock" style="width:12px;height:12px;"></i>领取后7天</div>
<button class="g-btn g-btn-sm g-btn-danger" onclick="removeNcCoupon(this)"><i data-lucide="trash-2" style="width:12px;height:12px;"></i></button>
</div>
<div class="nc-coupon-item" data-idx="1">
<div class="nc-coupon-info">
<div class="nc-coupon-name">满50享8折</div>
<div class="nc-coupon-desc">订单满 50 元可用</div>
</div>
<div class="nc-coupon-validity"><i data-lucide="clock" style="width:12px;height:12px;"></i>领取后7天</div>
<button class="g-btn g-btn-sm g-btn-danger" onclick="removeNcCoupon(this)"><i data-lucide="trash-2" style="width:12px;height:12px;"></i></button>
</div>
<div class="nc-coupon-item" data-idx="2">
<div class="nc-coupon-info">
<div class="nc-coupon-name">免配送费</div>
<div class="nc-coupon-desc">无门槛,减免配送费</div>
</div>
<div class="nc-coupon-validity"><i data-lucide="clock" style="width:12px;height:12px;"></i>领取后7天</div>
<button class="g-btn g-btn-sm g-btn-danger" onclick="removeNcCoupon(this)"><i data-lucide="trash-2" style="width:12px;height:12px;"></i></button>
</div>
</div>
<button class="nc-add-coupon-btn" onclick="openNcDrawer()"><i data-lucide="plus" style="width:14px;height:14px;"></i>添加优惠券</button>
</div>
</div>
<!-- 首单直减 -->
<div id="ncTypeDirect" style="display:none;">
<div class="nc-row">
<div class="nc-row-label">直减金额</div>
<div class="nc-row-ctrl">
<div class="nc-row-ctrl-inline">
<span class="nc-suffix">¥</span>
<input type="number" class="nc-num-input" placeholder="如10" value="10" />
</div>
<div class="nc-row-hint">新用户首单自动减免的金额</div>
</div>
</div>
<div class="nc-row">
<div class="nc-row-label">使用门槛</div>
<div class="nc-row-ctrl">
<div class="nc-row-ctrl-inline">
<span class="nc-suffix">订单满</span>
<input type="number" class="nc-num-input" placeholder="如30" value="30" />
<span class="nc-suffix">元可用</span>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 老带新分享 -->
<div class="g-card" style="padding:0; margin-bottom:16px;">
<div style="padding:18px 18px 0;">
<div class="nc-section-hd">老带新分享</div>
</div>
<div style="padding:0 18px 18px;">
<div class="nc-row">
<div class="nc-row-label">启用状态</div>
<div class="nc-row-ctrl">
<div class="nc-row-ctrl-inline">
<div class="g-toggle on" onclick="toggleSwitch(this)"></div>
<span class="g-toggle-label">开启后顾客可在小程序内分享邀请好友</span>
</div>
</div>
</div>
<div class="nc-row" style="flex-direction:column; gap:12px;">
<div class="nc-row-label" style="width:auto;">邀请人奖励 <span style="font-weight:400;color:var(--g-text-muted);font-size:12px;">成功邀请新用户下单后发放</span></div>
<div class="nc-coupon-list" id="ncInviterList">
<div class="nc-coupon-item">
<div class="nc-coupon-info">
<div class="nc-coupon-name">满30减5</div>
<div class="nc-coupon-desc">订单满 30 元可用</div>
</div>
<div class="nc-coupon-validity"><i data-lucide="clock" style="width:12px;height:12px;"></i>领取后7天</div>
<button class="g-btn g-btn-sm g-btn-danger" onclick="removeNcCoupon(this)"><i data-lucide="trash-2" style="width:12px;height:12px;"></i></button>
</div>
</div>
<button class="nc-add-coupon-btn" onclick="openNcDrawer('inviter')"><i data-lucide="plus" style="width:14px;height:14px;"></i>添加奖励券</button>
</div>
<div class="nc-row" style="flex-direction:column; gap:12px;">
<div class="nc-row-label" style="width:auto;">被邀请人奖励 <span style="font-weight:400;color:var(--g-text-muted);font-size:12px;">新用户通过邀请注册后额外获得,叠加新客礼包</span></div>
<div class="nc-coupon-list" id="ncInviteeList">
<div class="nc-coupon-item">
<div class="nc-coupon-info">
<div class="nc-coupon-name">满20减3</div>
<div class="nc-coupon-desc">订单满 20 元可用</div>
</div>
<div class="nc-coupon-validity"><i data-lucide="clock" style="width:12px;height:12px;"></i>领取后7天</div>
<button class="g-btn g-btn-sm g-btn-danger" onclick="removeNcCoupon(this)"><i data-lucide="trash-2" style="width:12px;height:12px;"></i></button>
</div>
<div class="nc-coupon-item">
<div class="nc-coupon-info">
<div class="nc-coupon-name">免配送费</div>
<div class="nc-coupon-desc">无门槛,减免配送费</div>
</div>
<div class="nc-coupon-validity"><i data-lucide="clock" style="width:12px;height:12px;"></i>领取后7天</div>
<button class="g-btn g-btn-sm g-btn-danger" onclick="removeNcCoupon(this)"><i data-lucide="trash-2" style="width:12px;height:12px;"></i></button>
</div>
</div>
<button class="nc-add-coupon-btn" onclick="openNcDrawer('invitee')"><i data-lucide="plus" style="width:14px;height:14px;"></i>添加奖励券</button>
</div>
<div class="nc-row">
<div class="nc-row-label">分享渠道</div>
<div class="nc-row-ctrl">
<div style="display:flex;gap:8px;">
<span class="g-pill checked" onclick="this.classList.toggle('checked')">微信好友</span>
<span class="g-pill checked" onclick="this.classList.toggle('checked')">朋友圈</span>
<span class="g-pill" onclick="this.classList.toggle('checked')">短信</span>
</div>
<div class="nc-row-hint">顾客在小程序内可通过以上渠道分享邀请</div>
</div>
</div>
<div class="nc-row" style="flex-direction:column; gap:12px;">
<div class="nc-row-label" style="width:auto;">邀请记录(近期)</div>
<div class="nc-invite-table" style="width:100%;">
<table class="g-table">
<thead>
<tr>
<th>邀请人</th>
<th>被邀请人</th>
<th>邀请时间</th>
<th>状态</th>
<th>奖励发放</th>
</tr>
</thead>
<tbody>
<tr>
<td>张**</td>
<td>李**</td>
<td>2025-02-10 14:32</td>
<td><span class="g-tag g-tag-green">已下单</span></td>
<td><span class="g-tag g-tag-green">已发放</span></td>
</tr>
<tr>
<td>王**</td>
<td>赵**</td>
<td>2025-02-09 09:15</td>
<td><span class="g-tag g-tag-orange">待下单</span></td>
<td><span class="g-tag g-tag-gray">待触发</span></td>
</tr>
<tr>
<td>张**</td>
<td>孙**</td>
<td>2025-02-08 18:47</td>
<td><span class="g-tag g-tag-green">已下单</span></td>
<td><span class="g-tag g-tag-green">已发放</span></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<!-- 底部保存 -->
<div class="g-card" style="padding:14px 18px;">
<div style="display:flex; justify-content:flex-end; gap:8px;">
<button class="g-btn"><i data-lucide="rotate-ccw" style="width:14px;height:14px;"></i>重置</button>
<button class="g-btn g-btn-primary"><i data-lucide="save" style="width:14px;height:14px;"></i>保存设置</button>
</div>
</div>
</div>
<!-- 添加优惠券抽屉 -->
<div class="g-drawer-mask" id="ncDrawerMask" onclick="closeNcDrawer()"></div>
<div class="g-drawer" id="ncDrawer" style="width:480px;">
<div class="g-drawer-hd">
<span class="g-drawer-title">添加优惠券</span>
<button class="g-drawer-close" onclick="closeNcDrawer()"><i data-lucide="x" style="width:18px;height:18px;"></i></button>
</div>
<div class="g-drawer-bd">
<div class="g-form-group">
<label class="g-form-label required">券类型</label>
<div style="display:flex; gap:8px;">
<span class="g-pill checked" onclick="selectNcCouponType('reduce',this)">满减券</span>
<span class="g-pill" onclick="selectNcCouponType('discount',this)">折扣券</span>
<span class="g-pill" onclick="selectNcCouponType('shipping',this)">免配送费</span>
</div>
</div>
<!-- 满减券面额 -->
<div id="ncCouponReduce">
<div class="g-form-group">
<label class="g-form-label required">减免金额</label>
<div style="display:flex; align-items:center; gap:8px;">
<span style="font-size:13px; color:var(--g-text-secondary);">¥</span>
<input class="g-input" type="number" placeholder="如5" style="width:120px;" />
</div>
</div>
</div>
<!-- 折扣券面额 -->
<div id="ncCouponDiscount" style="display:none;">
<div class="g-form-group">
<label class="g-form-label required">折扣</label>
<div style="display:flex; align-items:center; gap:8px;">
<input class="g-input" type="number" placeholder="如8即8折" style="width:160px;" />
<span style="font-size:13px; color:var(--g-text-secondary);"></span>
</div>
</div>
</div>
<!-- 免配送费无额外字段 -->
<div id="ncCouponShipping" style="display:none;">
<div class="g-form-group">
<div style="padding:16px; background:#f8f9fb; border-radius:8px; font-size:13px; color:var(--g-text-secondary); text-align:center;">
<i data-lucide="truck" style="width:20px;height:20px;color:var(--g-text-muted);display:block;margin:0 auto 6px;"></i>
免配送费券无需设置面额
</div>
</div>
</div>
<div class="g-form-group">
<label class="g-form-label">使用门槛</label>
<div style="display:flex; align-items:center; gap:8px;">
<span style="font-size:13px; color:var(--g-text-secondary);">订单满</span>
<input class="g-input" type="number" placeholder="如30" style="width:120px;" />
<span style="font-size:13px; color:var(--g-text-secondary);">元可用</span>
</div>
<div class="g-hint">留空或填 0 表示无门槛</div>
</div>
<div class="g-form-group">
<label class="g-form-label required">有效期</label>
<div style="display:flex; align-items:center; gap:8px;">
<span style="font-size:13px; color:var(--g-text-secondary);">领取后</span>
<input class="g-input" type="number" placeholder="如7" style="width:100px;" value="7" />
<span style="font-size:13px; color:var(--g-text-secondary);"></span>
</div>
</div>
</div>
<div class="g-drawer-ft">
<button class="g-btn" onclick="closeNcDrawer()">取消</button>
<button class="g-btn g-btn-primary" onclick="closeNcDrawer()">添加</button>
</div>
</div>
<script>
function toggleSwitch(el) {
el.classList.toggle('on');
}
function selectNcType(type, el) {
el.parentElement.querySelectorAll('.g-pill').forEach(function(p) { p.classList.remove('checked'); });
el.classList.add('checked');
document.getElementById('ncTypeCoupon').style.display = type === 'coupon' ? '' : 'none';
document.getElementById('ncTypeDirect').style.display = type === 'direct' ? '' : 'none';
}
function openNcDrawer() {
document.getElementById('ncDrawerMask').classList.add('open');
document.getElementById('ncDrawer').classList.add('open');
if (typeof lucide !== 'undefined') lucide.createIcons();
}
function closeNcDrawer() {
document.getElementById('ncDrawerMask').classList.remove('open');
document.getElementById('ncDrawer').classList.remove('open');
}
function removeNcCoupon(btn) {
var item = btn.closest('.nc-coupon-item');
if (item) { item.style.transition = 'opacity 200ms, transform 200ms'; item.style.opacity = '0'; item.style.transform = 'translateX(20px)'; setTimeout(function() { item.remove(); }, 200); }
}
function selectNcCouponType(type, el) {
el.parentElement.querySelectorAll('.g-pill').forEach(function(p) { p.classList.remove('checked'); });
el.classList.add('checked');
document.getElementById('ncCouponReduce').style.display = type === 'reduce' ? '' : 'none';
document.getElementById('ncCouponDiscount').style.display = type === 'discount' ? '' : 'none';
document.getElementById('ncCouponShipping').style.display = type === 'shipping' ? '' : 'none';
}
function copyNcLink() {
var input = document.querySelector('.nc-share-url input');
input.select();
document.execCommand('copy');
}
if (typeof lucide !== 'undefined') { lucide.createIcons(); }
</script>

678
pages/mkt-pass-card.html Normal file
View File

@@ -0,0 +1,678 @@
<!-- 次卡管理页 -->
<style>
.page-pk { max-width:1100px; }
/* 分段 Tab */
.pk-seg { display:flex; background:#f5f5f5; border-radius:var(--g-radius); overflow:hidden; border:1px solid #e8e8e8; width:fit-content; margin-bottom:16px; }
.pk-seg-item {
padding:8px 28px; font-size:13px; cursor:pointer; color:var(--g-text-muted);
transition:all var(--g-transition); user-select:none; border-right:1px solid #e8e8e8;
background:transparent; border-top:none; border-bottom:none; border-left:none;
}
.pk-seg-item:last-child { border-right:none; }
.pk-seg-item:hover { color:var(--g-text-secondary); background:#f0f0f0; }
.pk-seg-item.active { background:#fff; color:var(--primary); font-weight:600; box-shadow:0 1px 4px rgba(0,0,0,.06); }
/* 统计卡片 */
.pk-stats { display:grid; grid-template-columns:repeat(4,1fr); gap:12px; margin-bottom:16px; }
.pk-stat-card {
background:#fff; border-radius:10px; padding:16px 20px;
box-shadow:var(--g-shadow-sm); transition:all var(--g-transition);
}
.pk-stat-card:hover { box-shadow:var(--g-shadow-md); transform:translateY(-1px); }
.pk-stat-label { font-size:13px; color:var(--g-text-muted); margin-bottom:6px; }
.pk-stat-value { font-size:24px; font-weight:700; color:var(--g-text); }
.pk-stat-value.primary { color:var(--primary); }
.pk-stat-value.green { color:var(--g-success); }
.pk-stat-value.orange { color:var(--g-warning); }
/* 工具栏 */
.pk-toolbar {
display:flex; align-items:center; gap:12px; flex-wrap:wrap; margin-bottom:16px;
box-shadow:var(--g-shadow-sm); border-radius:10px; padding:10px 14px; background:#fff;
}
.pk-toolbar select, .pk-toolbar input[type="text"] {
height:34px; padding:0 10px; border:1px solid #e5e7eb; border-radius:8px;
font-size:13px; outline:none; transition:all var(--g-transition); background:#fff;
}
.pk-toolbar select:focus, .pk-toolbar input[type="text"]:focus {
border-color:var(--primary); box-shadow:0 0 0 3px color-mix(in srgb, var(--primary) 12%, transparent);
}
/* 次卡网格 */
.pk-grid { display:grid; grid-template-columns:repeat(2,1fr); gap:16px; margin-bottom:16px; }
.pk-card {
background:#fff; border-radius:12px; overflow:hidden;
box-shadow:var(--g-shadow-sm); transition:all var(--g-transition); display:flex;
}
.pk-card:hover { box-shadow:var(--g-shadow-md); transform:translateY(-1px); }
.pk-card.pk-off { opacity:.55; }
.pk-card-cover {
width:140px; min-height:160px; flex-shrink:0; display:flex; align-items:center; justify-content:center;
color:#fff; font-size:28px; font-weight:700; position:relative;
}
.pk-card-cover .pk-cover-icon { opacity:.3; position:absolute; }
.pk-card-cover-label { position:relative; z-index:1; text-align:center; line-height:1.3; }
.pk-card-cover-label small { display:block; font-size:12px; font-weight:400; opacity:.8; }
.pk-card-bd { flex:1; padding:16px; display:flex; flex-direction:column; }
.pk-card-name { font-size:15px; font-weight:600; color:var(--g-text); margin-bottom:6px; display:flex; align-items:center; gap:6px; }
.pk-card-price { margin-bottom:8px; display:flex; align-items:baseline; gap:6px; }
.pk-card-price-now { font-size:22px; font-weight:700; color:var(--g-danger); }
.pk-card-price-orig { font-size:13px; color:var(--g-text-muted); text-decoration:line-through; }
.pk-card-info { display:flex; flex-wrap:wrap; gap:6px; margin-bottom:10px; }
.pk-info-tag {
font-size:11px; padding:2px 8px; border-radius:4px; background:#f5f5f5; color:var(--g-text-secondary);
}
.pk-card-meta { font-size:12px; color:var(--g-text-muted); margin-bottom:10px; display:flex; gap:14px; }
.pk-card-ft { display:flex; align-items:center; gap:6px; margin-top:auto; padding-top:10px; border-top:1px solid #f5f5f5; }
.pk-card-ft .g-tag { margin-left:auto; }
/* scope tag 颜色 */
.pk-scope-tag { font-size:11px; font-weight:500; padding:1px 6px; border-radius:4px; }
.pk-scope-tag.blue { background:color-mix(in srgb, var(--primary) 12%, #fff); color:var(--primary); }
.pk-scope-tag.green { background:color-mix(in srgb, var(--g-success) 12%, #fff); color:var(--g-success); }
.pk-scope-tag.orange { background:color-mix(in srgb, var(--g-warning) 12%, #fff); color:var(--g-warning); }
.pk-scope-tag.purple { background:color-mix(in srgb, #7c3aed 12%, #fff); color:#7c3aed; }
/* 使用记录 */
.pk-rec-member { display:flex; flex-direction:column; gap:2px; }
.pk-rec-member-name { font-size:13px; font-weight:500; color:var(--g-text); }
.pk-rec-member-phone { font-size:12px; color:var(--g-text-muted); }
.pk-rec-status { display:inline-flex; align-items:center; gap:4px; font-size:12px; }
.pk-rec-status::before { content:''; width:6px; height:6px; border-radius:50%; }
.pk-rec-status.green::before { background:var(--g-success); }
.pk-rec-status.orange::before { background:var(--g-warning); }
.pk-rec-status.gray::before { background:#bbb; }
/* 抽屉内上传 */
.pk-upload-placeholder {
width:100%; height:100px; border:2px dashed #d9d9d9; border-radius:var(--g-radius);
display:flex; flex-direction:column; align-items:center; justify-content:center;
color:var(--g-text-muted); font-size:13px; gap:6px; cursor:pointer;
transition:all var(--g-transition); background:var(--g-bg-subtle);
}
.pk-upload-placeholder:hover { border-color:var(--primary); color:var(--primary); background:color-mix(in srgb, var(--primary) 4%, #fff); }
/* 通知 pill 多选 */
.pk-notify-pills { display:flex; gap:10px; }
.pk-notify-pills .g-pill { position:relative; padding-left:24px; }
.pk-notify-pills .g-pill::before {
content:''; position:absolute; left:8px; top:50%; transform:translateY(-50%);
width:12px; height:12px; border:1.5px solid #ccc; border-radius:3px; transition:all var(--g-transition);
}
.pk-notify-pills .g-pill.checked::before {
background:var(--primary); border-color:var(--primary);
background-image:url("data:image/svg+xml,%3Csvg viewBox='0 0 12 12' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M2.5 6L5 8.5L9.5 4' stroke='white' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}
/* section 标题 */
.pk-section-hd {
font-size:15px; font-weight:600; color:var(--g-text);
padding-left:10px; border-left:3px solid var(--primary); margin-bottom:16px;
}
/* 适用范围选中项 */
.pk-scope-items { display:flex; flex-wrap:wrap; gap:6px; margin-top:8px; }
.pk-scope-item {
display:inline-flex; align-items:center; gap:4px; padding:4px 10px; background:#f5f5f5;
border-radius:6px; font-size:12px; color:var(--g-text-secondary);
}
.pk-scope-item button {
background:none; border:none; color:var(--g-text-muted); cursor:pointer; padding:0; line-height:1;
}
.pk-scope-item button:hover { color:var(--g-danger); }
</style>
<div class="page-pk">
<!-- 分段 Tab -->
<div class="pk-seg">
<button class="pk-seg-item active" onclick="switchPkTab(this, 'list')">次卡列表</button>
<button class="pk-seg-item" onclick="switchPkTab(this, 'records')">使用记录</button>
</div>
<!-- ==================== TAB 1: 次卡列表 ==================== -->
<div id="pkTabList">
<!-- 统计 -->
<div class="pk-stats">
<div class="pk-stat-card">
<div class="pk-stat-label">在售次卡</div>
<div class="pk-stat-value primary">3</div>
</div>
<div class="pk-stat-card">
<div class="pk-stat-label">累计售出</div>
<div class="pk-stat-value">287张</div>
</div>
<div class="pk-stat-card">
<div class="pk-stat-label">累计收入</div>
<div class="pk-stat-value green">¥18,640</div>
</div>
<div class="pk-stat-card">
<div class="pk-stat-label">使用中</div>
<div class="pk-stat-value orange">124张</div>
</div>
</div>
<!-- 工具栏 -->
<div class="pk-toolbar">
<select style="width:120px;">
<option value="">全部状态</option>
<option>上架</option>
<option>下架</option>
</select>
<input type="text" placeholder="搜索次卡名称" style="width:200px;">
<div style="flex:1;"></div>
<button class="g-btn g-btn-primary" onclick="openPkDrawer()">
<i data-lucide="plus" style="width:14px;height:14px;"></i>创建次卡
</button>
</div>
<!-- 次卡网格 -->
<div class="pk-grid">
<!-- 卡1: 咖啡月卡 -->
<div class="pk-card">
<div class="pk-card-cover" style="background:linear-gradient(135deg,#6366f1,#8b5cf6);">
<i data-lucide="coffee" class="pk-cover-icon" style="width:60px;height:60px;"></i>
<div class="pk-card-cover-label">30<small></small></div>
</div>
<div class="pk-card-bd">
<div class="pk-card-name">咖啡月卡<span class="pk-scope-tag blue">指定分类</span></div>
<div class="pk-card-price">
<span class="pk-card-price-now">¥99</span>
<span class="pk-card-price-orig">¥150</span>
</div>
<div class="pk-card-info">
<span class="pk-info-tag">30天有效</span>
<span class="pk-info-tag">完全免费</span>
<span class="pk-info-tag">每日限1次</span>
</div>
<div class="pk-card-meta">
<span>已售 156</span>
<span>使用中 68</span>
</div>
<div class="pk-card-ft">
<a class="g-action" onclick="openPkDrawer()">编辑</a>
<a class="g-action">下架</a>
<a class="g-action g-action-danger">删除</a>
<span class="g-tag g-tag-green">上架</span>
</div>
</div>
</div>
<!-- 卡2: 早餐周卡 -->
<div class="pk-card">
<div class="pk-card-cover" style="background:linear-gradient(135deg,#f59e0b,#f97316);">
<i data-lucide="sunrise" class="pk-cover-icon" style="width:60px;height:60px;"></i>
<div class="pk-card-cover-label">7<small></small></div>
</div>
<div class="pk-card-bd">
<div class="pk-card-name">早餐周卡<span class="pk-scope-tag green">指定标签</span></div>
<div class="pk-card-price">
<span class="pk-card-price-now">¥29</span>
<span class="pk-card-price-orig">¥56</span>
</div>
<div class="pk-card-info">
<span class="pk-info-tag">7天有效</span>
<span class="pk-info-tag">完全免费</span>
<span class="pk-info-tag">每日限1次</span>
</div>
<div class="pk-card-meta">
<span>已售 89</span>
<span>使用中 34</span>
</div>
<div class="pk-card-ft">
<a class="g-action" onclick="openPkDrawer()">编辑</a>
<a class="g-action">下架</a>
<a class="g-action g-action-danger">删除</a>
<span class="g-tag g-tag-green">上架</span>
</div>
</div>
</div>
<!-- 卡3: 奶茶畅饮卡 -->
<div class="pk-card">
<div class="pk-card-cover" style="background:linear-gradient(135deg,#ec4899,#f43f5e);">
<i data-lucide="cup-soda" class="pk-cover-icon" style="width:60px;height:60px;"></i>
<div class="pk-card-cover-label">15<small></small></div>
</div>
<div class="pk-card-bd">
<div class="pk-card-name">奶茶畅饮卡<span class="pk-scope-tag purple">指定商品</span></div>
<div class="pk-card-price">
<span class="pk-card-price-now">¥59</span>
<span class="pk-card-price-orig">¥120</span>
</div>
<div class="pk-card-info">
<span class="pk-info-tag">15天有效</span>
<span class="pk-info-tag">上限¥20/次</span>
<span class="pk-info-tag">不限次/日</span>
</div>
<div class="pk-card-meta">
<span>已售 42</span>
<span>使用中 22</span>
</div>
<div class="pk-card-ft">
<a class="g-action" onclick="openPkDrawer()">编辑</a>
<a class="g-action">下架</a>
<a class="g-action g-action-danger">删除</a>
<span class="g-tag g-tag-green">上架</span>
</div>
</div>
</div>
<!-- 卡4: 午餐次卡(已下架) -->
<div class="pk-card pk-off">
<div class="pk-card-cover" style="background:linear-gradient(135deg,#64748b,#94a3af);">
<i data-lucide="utensils" class="pk-cover-icon" style="width:60px;height:60px;"></i>
<div class="pk-card-cover-label">20<small></small></div>
</div>
<div class="pk-card-bd">
<div class="pk-card-name">午餐次卡<span class="pk-scope-tag blue">指定分类</span></div>
<div class="pk-card-price">
<span class="pk-card-price-now">¥199</span>
<span class="pk-card-price-orig">¥300</span>
</div>
<div class="pk-card-info">
<span class="pk-info-tag">30天有效</span>
<span class="pk-info-tag">完全免费</span>
<span class="pk-info-tag">每日限1次</span>
</div>
<div class="pk-card-meta">
<span>已售 0</span>
<span>使用中 0</span>
</div>
<div class="pk-card-ft">
<a class="g-action" onclick="openPkDrawer()">编辑</a>
<a class="g-action">上架</a>
<a class="g-action g-action-danger">删除</a>
<span class="g-tag g-tag-gray">下架</span>
</div>
</div>
</div>
</div>
</div>
<!-- ==================== TAB 2: 使用记录 ==================== -->
<div id="pkTabRecords" style="display:none;">
<!-- 筛选工具栏 -->
<div class="pk-toolbar">
<select style="width:130px;">
<option value="">全部次卡</option>
<option>咖啡月卡</option>
<option>早餐周卡</option>
<option>奶茶畅饮卡</option>
</select>
<select style="width:120px;">
<option value="">全部状态</option>
<option>正常使用</option>
<option>已用完</option>
<option>已过期</option>
</select>
<input type="text" placeholder="搜索会员/商品" style="width:200px;">
<div style="flex:1;"></div>
<button class="g-btn g-btn-sm"><i data-lucide="download" style="width:13px;height:13px;"></i>导出</button>
</div>
<!-- 统计 -->
<div class="pk-stats" style="grid-template-columns:repeat(3,1fr);">
<div class="pk-stat-card">
<div class="pk-stat-label">今日使用</div>
<div class="pk-stat-value primary">18</div>
</div>
<div class="pk-stat-card">
<div class="pk-stat-label">本月使用</div>
<div class="pk-stat-value">342</div>
</div>
<div class="pk-stat-card">
<div class="pk-stat-label">即将过期7天内</div>
<div class="pk-stat-value orange">15张</div>
</div>
</div>
<!-- 使用记录表格 -->
<div class="g-card" style="padding:0;">
<table class="g-table">
<thead>
<tr>
<th>使用单号</th>
<th>会员</th>
<th>次卡</th>
<th>兑换商品</th>
<th>使用时间</th>
<th>剩余次数</th>
<th>状态</th>
</tr>
</thead>
<tbody>
<tr>
<td style="font-family:monospace;font-size:12px;">PK20260212001</td>
<td><div class="pk-rec-member"><span class="pk-rec-member-name">张三</span><span class="pk-rec-member-phone">138****6789</span></div></td>
<td>咖啡月卡</td>
<td>美式咖啡</td>
<td style="font-size:12px;color:var(--g-text-secondary);">2026-02-12 08:30</td>
<td><span style="font-weight:600;color:var(--primary);">22/30</span></td>
<td><span class="pk-rec-status green">正常使用</span></td>
</tr>
<tr>
<td style="font-family:monospace;font-size:12px;">PK20260212002</td>
<td><div class="pk-rec-member"><span class="pk-rec-member-name">李四</span><span class="pk-rec-member-phone">139****1234</span></div></td>
<td>早餐周卡</td>
<td>鸡蛋灌饼</td>
<td style="font-size:12px;color:var(--g-text-secondary);">2026-02-12 07:45</td>
<td><span style="font-weight:600;color:var(--primary);">3/7</span></td>
<td><span class="pk-rec-status green">正常使用</span></td>
</tr>
<tr>
<td style="font-family:monospace;font-size:12px;">PK20260211003</td>
<td><div class="pk-rec-member"><span class="pk-rec-member-name">王五</span><span class="pk-rec-member-phone">137****5678</span></div></td>
<td>奶茶畅饮卡</td>
<td>珍珠奶茶补差价¥5</td>
<td style="font-size:12px;color:var(--g-text-secondary);">2026-02-11 15:20</td>
<td><span style="font-weight:600;color:var(--g-warning);">2/15</span></td>
<td><span class="pk-rec-status orange">即将用完</span></td>
</tr>
<tr>
<td style="font-family:monospace;font-size:12px;">PK20260211004</td>
<td><div class="pk-rec-member"><span class="pk-rec-member-name">赵六</span><span class="pk-rec-member-phone">136****4321</span></div></td>
<td>咖啡月卡</td>
<td>拿铁</td>
<td style="font-size:12px;color:var(--g-text-secondary);">2026-02-11 09:10</td>
<td><span style="font-weight:600;color:var(--g-text-muted);">0/30</span></td>
<td><span class="pk-rec-status gray">已用完</span></td>
</tr>
<tr>
<td style="font-family:monospace;font-size:12px;">PK20260210005</td>
<td><div class="pk-rec-member"><span class="pk-rec-member-name">孙七</span><span class="pk-rec-member-phone">135****8765</span></div></td>
<td>早餐周卡</td>
<td>豆浆油条套餐</td>
<td style="font-size:12px;color:var(--g-text-secondary);">2026-02-10 07:30</td>
<td><span style="font-weight:600;color:var(--g-text-muted);">0/7</span></td>
<td><span class="pk-rec-status gray">已过期</span></td>
</tr>
<tr>
<td style="font-family:monospace;font-size:12px;">PK20260210006</td>
<td><div class="pk-rec-member"><span class="pk-rec-member-name">周八</span><span class="pk-rec-member-phone">133****2222</span></div></td>
<td>咖啡月卡</td>
<td>卡布奇诺</td>
<td style="font-size:12px;color:var(--g-text-secondary);">2026-02-10 10:45</td>
<td><span style="font-weight:600;color:var(--primary);">15/30</span></td>
<td><span class="pk-rec-status green">正常使用</span></td>
</tr>
</tbody>
</table>
</div>
<!-- 分页 -->
<div class="g-pagination" style="margin-top:16px;">
<button class="g-page-btn" disabled><i data-lucide="chevron-left" style="width:14px;height:14px;"></i></button>
<button class="g-page-btn active">1</button>
<button class="g-page-btn">2</button>
<button class="g-page-btn">3</button>
<button class="g-page-btn"><i data-lucide="chevron-right" style="width:14px;height:14px;"></i></button>
</div>
</div>
</div>
<!-- ==================== 创建/编辑次卡抽屉 ==================== -->
<div class="g-drawer-mask" id="pkDrawerMask" onclick="closePkDrawer()"></div>
<div class="g-drawer" id="pkDrawer" style="width:520px;">
<div class="g-drawer-hd">
<span class="g-drawer-title">创建次卡</span>
<button class="g-drawer-close" onclick="closePkDrawer()">&times;</button>
</div>
<div class="g-drawer-bd">
<!-- 次卡名称 -->
<div class="g-form-group">
<label class="g-form-label required">次卡名称</label>
<input class="g-input" placeholder="如:咖啡月卡、早餐周卡">
</div>
<!-- 封面图片 -->
<div class="g-form-group">
<label class="g-form-label">封面图片</label>
<div class="pk-upload-placeholder">
<i data-lucide="upload-cloud" style="width:24px;height:24px;"></i>
<span>点击上传封面</span>
</div>
</div>
<!-- 价格 -->
<div style="display:flex;gap:12px;">
<div class="g-form-group" style="flex:1;">
<label class="g-form-label required">售价</label>
<input class="g-input" type="number" placeholder="如99" min="0" step="0.01">
</div>
<div class="g-form-group" style="flex:1;">
<label class="g-form-label">原价</label>
<input class="g-input" type="number" placeholder="如150划线价" min="0" step="0.01">
</div>
</div>
<!-- 总次数 -->
<div class="g-form-group">
<label class="g-form-label required">总次数</label>
<input class="g-input" type="number" placeholder="如30" min="1">
<div class="g-hint">购买后可使用的总次数</div>
</div>
<!-- 有效期 -->
<div class="g-form-group">
<label class="g-form-label required">有效期</label>
<div style="display:flex;gap:10px;margin-bottom:8px;">
<span class="g-pill checked" onclick="selectPkValidity(this,'days')">固定天数</span>
<span class="g-pill" onclick="selectPkValidity(this,'range')">日期范围</span>
</div>
<div id="pkValidityDays">
<div style="display:flex;align-items:center;gap:8px;">
<span style="font-size:13px;color:var(--g-text-secondary);">购买后</span>
<input class="g-input" type="number" placeholder="如30" min="1" style="width:100px;">
<span style="font-size:13px;color:var(--g-text-secondary);">天内有效</span>
</div>
</div>
<div id="pkValidityRange" style="display:none;">
<div style="display:flex;align-items:center;gap:8px;">
<input class="g-input" type="date" style="width:160px;">
<span style="font-size:13px;color:var(--g-text-secondary);"></span>
<input class="g-input" type="date" style="width:160px;">
</div>
</div>
</div>
<div class="g-divider"></div>
<!-- 适用范围 -->
<div class="g-form-group">
<label class="g-form-label required">适用范围</label>
<div style="display:flex;gap:10px;margin-bottom:8px;">
<span class="g-pill checked" onclick="selectPkScope(this,'all')">全部商品</span>
<span class="g-pill" onclick="selectPkScope(this,'category')">指定分类</span>
<span class="g-pill" onclick="selectPkScope(this,'tag')">指定标签</span>
<span class="g-pill" onclick="selectPkScope(this,'product')">指定商品</span>
</div>
<div id="pkScopeAll">
<div class="g-hint">持卡可兑换店内任意商品</div>
</div>
<div id="pkScopeCategory" style="display:none;">
<select class="g-select" style="width:100%;">
<option value="">请选择商品分类</option>
<option>热饮</option>
<option>冷饮</option>
<option>早餐</option>
<option>午餐套餐</option>
<option>小食甜点</option>
</select>
<div class="pk-scope-items">
<span class="pk-scope-item">热饮 <button onclick="this.parentElement.remove()"><i data-lucide="x" style="width:12px;height:12px;"></i></button></span>
<span class="pk-scope-item">冷饮 <button onclick="this.parentElement.remove()"><i data-lucide="x" style="width:12px;height:12px;"></i></button></span>
</div>
<div class="g-hint">可多选,持卡可兑换所选分类下的任意商品</div>
</div>
<div id="pkScopeTag" style="display:none;">
<select class="g-select" style="width:100%;">
<option value="">请选择商品标签</option>
<option>早餐系列</option>
<option>人气爆款</option>
<option>新品上市</option>
<option>轻食健康</option>
</select>
<div class="pk-scope-items">
<span class="pk-scope-item">早餐系列 <button onclick="this.parentElement.remove()"><i data-lucide="x" style="width:12px;height:12px;"></i></button></span>
</div>
<div class="g-hint">可多选,持卡可兑换带有所选标签的商品</div>
</div>
<div id="pkScopeProduct" style="display:none;">
<button class="g-btn g-btn-sm" onclick="openProductPicker({title:'选择适用商品',subtitle:'次卡管理',onConfirm:function(){}})"><i data-lucide="plus" style="width:13px;height:13px;"></i>选择商品</button>
<div class="pk-scope-items">
<span class="pk-scope-item">美式咖啡 <button onclick="this.parentElement.remove()"><i data-lucide="x" style="width:12px;height:12px;"></i></button></span>
<span class="pk-scope-item">拿铁 <button onclick="this.parentElement.remove()"><i data-lucide="x" style="width:12px;height:12px;"></i></button></span>
<span class="pk-scope-item">卡布奇诺 <button onclick="this.parentElement.remove()"><i data-lucide="x" style="width:12px;height:12px;"></i></button></span>
</div>
<div class="g-hint">可多选,持卡仅可兑换指定的商品</div>
</div>
</div>
<!-- 使用模式 -->
<div class="g-form-group">
<label class="g-form-label required">使用模式</label>
<div style="display:flex;gap:10px;margin-bottom:8px;">
<span class="g-pill checked" onclick="selectPkUsageMode(this,'free')">完全免费</span>
<span class="g-pill" onclick="selectPkUsageMode(this,'cap')">金额上限</span>
</div>
<div id="pkModeFree">
<div class="g-hint">每次使用免费兑换一件适用范围内的商品,不限原价</div>
</div>
<div id="pkModeCap" style="display:none;">
<div style="display:flex;align-items:center;gap:8px;">
<span style="font-size:13px;color:var(--g-text-secondary);">每次使用上限</span>
<input class="g-input" type="number" placeholder="如20" min="0" step="0.01" style="width:100px;">
<span style="font-size:13px;color:var(--g-text-secondary);"></span>
</div>
<div class="g-hint">超出上限部分由顾客补差价</div>
</div>
</div>
<div class="g-divider"></div>
<!-- 使用规则 -->
<div class="g-form-group">
<label class="g-form-label">每日限用</label>
<div style="display:flex;align-items:center;gap:8px;">
<input class="g-input" type="number" placeholder="不限" min="0" style="width:100px;">
<span style="font-size:13px;color:var(--g-text-secondary);">次/天</span>
</div>
<div class="g-hint">留空或填0表示不限制每日使用次数</div>
</div>
<div class="g-form-group">
<label class="g-form-label">每单限用</label>
<div style="display:flex;align-items:center;gap:8px;">
<input class="g-input" type="number" placeholder="不限" min="0" style="width:100px;">
<span style="font-size:13px;color:var(--g-text-secondary);">次/单</span>
</div>
<div class="g-hint">同一订单内可使用的次数如一单点5杯可用5次</div>
</div>
<div class="g-form-group">
<label class="g-form-label">每人限购</label>
<div style="display:flex;align-items:center;gap:8px;">
<input class="g-input" type="number" placeholder="不限" min="0" style="width:100px;">
<span style="font-size:13px;color:var(--g-text-secondary);"></span>
</div>
<div class="g-hint">留空或填0表示不限制购买数量</div>
</div>
<div style="display:flex;gap:24px;margin-bottom:16px;">
<div style="display:flex;align-items:center;gap:10px;">
<span style="font-size:13px;color:var(--g-text);">允许转赠</span>
<div class="g-toggle" onclick="toggleSwitch(this)"></div>
</div>
</div>
<!-- 过期策略 -->
<div class="g-form-group">
<label class="g-form-label">过期策略</label>
<div style="display:flex;gap:10px;">
<span class="g-pill checked" onclick="this.parentElement.querySelectorAll('.g-pill').forEach(function(p){p.classList.remove('checked')});this.classList.add('checked')">剩余次数作废</span>
<span class="g-pill" onclick="this.parentElement.querySelectorAll('.g-pill').forEach(function(p){p.classList.remove('checked')});this.classList.add('checked')">可申请退款</span>
</div>
<div class="g-hint">到期后未使用完的次数处理方式</div>
</div>
<!-- 描述 -->
<div class="g-form-group">
<label class="g-form-label">次卡描述</label>
<textarea class="g-textarea" rows="3" placeholder="请输入次卡说明,如使用规则、适用门店等"></textarea>
</div>
<!-- 到账通知 -->
<div class="g-form-group">
<label class="g-form-label">购买通知</label>
<div class="pk-notify-pills">
<span class="g-pill checked" onclick="togglePill(this)">站内消息</span>
<span class="g-pill" onclick="togglePill(this)">短信通知</span>
</div>
<div class="g-hint">顾客购买次卡后的通知方式</div>
</div>
</div>
<div class="g-drawer-ft">
<button class="g-btn" onclick="closePkDrawer()">取消</button>
<button class="g-btn g-btn-primary">保存</button>
</div>
</div>
<script>
/* Tab 切换 */
function switchPkTab(el, tab) {
document.querySelectorAll('.pk-seg-item').forEach(function(item) { item.classList.remove('active'); });
el.classList.add('active');
document.getElementById('pkTabList').style.display = tab === 'list' ? '' : 'none';
document.getElementById('pkTabRecords').style.display = tab === 'records' ? '' : 'none';
}
/* Toggle 开关 */
function toggleSwitch(el) { el.classList.toggle('on'); }
function togglePill(el) { el.classList.toggle('checked'); }
/* 有效期类型切换 */
function selectPkValidity(el, type) {
el.parentElement.querySelectorAll('.g-pill').forEach(function(p) { p.classList.remove('checked'); });
el.classList.add('checked');
document.getElementById('pkValidityDays').style.display = type === 'days' ? '' : 'none';
document.getElementById('pkValidityRange').style.display = type === 'range' ? '' : 'none';
}
/* 适用范围切换 */
function selectPkScope(el, scope) {
el.parentElement.querySelectorAll('.g-pill').forEach(function(p) { p.classList.remove('checked'); });
el.classList.add('checked');
document.getElementById('pkScopeAll').style.display = scope === 'all' ? '' : 'none';
document.getElementById('pkScopeCategory').style.display = scope === 'category' ? '' : 'none';
document.getElementById('pkScopeTag').style.display = scope === 'tag' ? '' : 'none';
document.getElementById('pkScopeProduct').style.display = scope === 'product' ? '' : 'none';
}
/* 使用模式切换 */
function selectPkUsageMode(el, mode) {
el.parentElement.querySelectorAll('.g-pill').forEach(function(p) { p.classList.remove('checked'); });
el.classList.add('checked');
document.getElementById('pkModeFree').style.display = mode === 'free' ? '' : 'none';
document.getElementById('pkModeCap').style.display = mode === 'cap' ? '' : 'none';
}
/* 抽屉开关 */
function openPkDrawer() {
document.getElementById('pkDrawerMask').classList.add('open');
document.getElementById('pkDrawer').classList.add('open');
}
function closePkDrawer() {
document.getElementById('pkDrawerMask').classList.remove('open');
document.getElementById('pkDrawer').classList.remove('open');
}
/* 初始化 Lucide 图标 */
if (typeof lucide !== 'undefined') { lucide.createIcons(); }
</script>

583
pages/mkt-reduction.html Normal file
View File

@@ -0,0 +1,583 @@
<!-- 满减活动页 -->
<style>
.fr-toolbar { display:flex; align-items:center; gap:12px; flex-wrap:wrap; margin-bottom:16px; box-shadow:var(--g-shadow-sm); border-radius:10px; padding:10px 14px; background:#fff; }
.fr-toolbar select,
.fr-toolbar input[type="text"] {
height:34px; padding:0 10px; border:1px solid #e5e7eb; border-radius:8px;
font-size:13px; outline:none; transition:var(--g-transition); background:#fff;
}
.fr-toolbar select:focus,
.fr-toolbar input[type="text"]:focus { border-color:var(--primary); box-shadow:0 0 0 3px color-mix(in srgb, var(--primary) 12%, transparent); }
/* 统计卡片 */
.fr-stats { display:grid; grid-template-columns:repeat(4,1fr); gap:12px; margin-bottom:16px; }
.fr-stat-card {
background:#fff; border-radius:10px; padding:16px 20px;
box-shadow:var(--g-shadow-sm); transition:var(--g-transition);
}
.fr-stat-card:hover { box-shadow:var(--g-shadow-md); transform:translateY(-1px); }
.fr-stat-card .fr-stat-label { font-size:13px; color:#9ca3af; margin-bottom:6px; }
.fr-stat-card .fr-stat-value { font-size:24px; font-weight:700; color:#1a1a2e; }
.fr-stat-card .fr-stat-value.green { color:var(--g-success); }
.fr-stat-card .fr-stat-value.orange { color:var(--g-warning); }
/* 活动卡片列表 */
.fr-list { display:flex; flex-direction:column; gap:12px; margin-bottom:16px; }
.fr-card {
background:#fff; border-radius:10px; padding:20px 24px;
box-shadow:var(--g-shadow-sm); transition:var(--g-transition);
}
.fr-card:hover { box-shadow:var(--g-shadow-md); }
.fr-card.fr-ended { opacity:0.55; }
.fr-card-hd { display:flex; align-items:center; gap:10px; margin-bottom:14px; }
.fr-card-name { font-size:15px; font-weight:600; color:var(--g-text); }
.fr-card-hd .g-tag { flex-shrink:0; }
/* 阶梯规则可视化 */
.fr-tiers { display:flex; align-items:center; gap:0; margin-bottom:14px; flex-wrap:wrap; }
.fr-tier-pill {
display:inline-flex; align-items:center; padding:6px 14px; border-radius:999px;
background:color-mix(in srgb, var(--primary) 8%, #fff);
border:1px solid color-mix(in srgb, var(--primary) 20%, transparent);
font-size:13px; font-weight:500; color:var(--primary); white-space:nowrap;
}
.fr-tier-arrow {
display:inline-flex; align-items:center; padding:0 6px; color:#c0c6cf;
}
/* 活动信息行 */
.fr-meta { display:flex; flex-wrap:wrap; gap:8px 24px; font-size:13px; color:var(--g-text-secondary); margin-bottom:12px; }
.fr-meta-item { display:flex; align-items:center; gap:5px; }
.fr-meta-item i { width:14px; height:14px; color:#9ca3af; }
/* 活动数据行 */
.fr-data { display:flex; gap:24px; font-size:13px; color:var(--g-text-secondary); margin-bottom:14px; padding:10px 14px; background:#f8f9fb; border-radius:8px; }
.fr-data-item span { font-weight:600; color:var(--g-text); }
/* 操作行 */
.fr-card-ft { display:flex; align-items:center; gap:6px; border-top:1px solid #f3f4f6; padding-top:12px; }
/* 分页 */
.fr-pagination {
display:flex; align-items:center; justify-content:flex-end;
padding:12px 16px; gap:6px; font-size:13px; color:#4b5563;
background:#fff; border-radius:10px; box-shadow:var(--g-shadow-sm);
}
.fr-page-btn {
min-width:34px; height:34px; border:1px solid #e5e7eb; border-radius:8px;
background:#fff; cursor:pointer; display:flex; align-items:center;
justify-content:center; font-size:13px; transition:var(--g-transition);
}
.fr-page-btn:hover { border-color:var(--primary); color:var(--primary); }
.fr-page-btn.active { background:var(--primary); color:#fff; border-color:var(--primary); box-shadow:0 0 0 3px color-mix(in srgb, var(--primary) 12%, transparent); }
/* 抽屉内:阶梯规则行 */
.fr-tier-row { display:flex; align-items:center; gap:8px; margin-bottom:10px; font-size:13px; color:var(--g-text); }
.fr-tier-row input[type="number"] {
width:80px; height:34px; padding:0 10px; border:1px solid #e5e7eb; border-radius:8px;
font-size:13px; outline:none; transition:var(--g-transition); text-align:center;
}
.fr-tier-row input[type="number"]:focus { border-color:var(--primary); box-shadow:0 0 0 3px color-mix(in srgb, var(--primary) 12%, transparent); }
.fr-tier-del {
width:30px; height:30px; border:none; background:none; cursor:pointer;
display:flex; align-items:center; justify-content:center; border-radius:6px;
color:#999; transition:var(--g-transition);
}
.fr-tier-del:hover { background:#fef2f2; color:var(--g-danger); }
.fr-add-tier {
display:inline-flex; align-items:center; gap:4px; padding:6px 14px;
border:1px dashed #d0d5dd; border-radius:8px; background:none;
font-size:13px; color:var(--g-text-secondary); cursor:pointer;
transition:var(--g-transition);
}
.fr-add-tier:hover { border-color:var(--primary); color:var(--primary); background:color-mix(in srgb, var(--primary) 4%, #fff); }
.fr-section-hd {
font-size:15px; font-weight:600; color:var(--g-text);
padding-left:10px; border-left:3px solid var(--primary); margin-bottom:16px;
}
/* 活动类型标签 */
.fr-type-tag { font-size:11px; font-weight:500; padding:1px 6px; border-radius:4px; }
.fr-type-tag.blue { background:color-mix(in srgb, var(--primary) 12%, #fff); color:var(--primary); }
.fr-type-tag.green { background:color-mix(in srgb, var(--g-success) 12%, #fff); color:var(--g-success); }
.fr-type-tag.orange { background:color-mix(in srgb, var(--g-warning) 12%, #fff); color:var(--g-warning); }
/* 满赠商品选择 */
.fr-gift-item {
display:inline-flex; align-items:center; gap:6px; padding:6px 10px;
background:#f8f9fb; border-radius:6px; font-size:12px; color:var(--g-text-secondary); border:1px solid #e5e7eb;
}
.fr-gift-item button { background:none; border:none; color:var(--g-text-muted); cursor:pointer; padding:0; }
.fr-gift-item button:hover { color:var(--g-danger); }
</style>
<div class="page-fr">
<!-- 工具栏 -->
<div class="fr-toolbar">
<select style="width:200px;">
<option value="">全部门店</option>
<option>老三家外卖(朝阳店)</option>
<option>老三家外卖(海淀店)</option>
<option>老三家外卖(望京店)</option>
<option>老三家外卖(通州店)</option>
<option>老三家外卖(丰台店)</option>
</select>
<select style="width:130px;">
<option value="">全部类型</option>
<option>满减</option>
<option>满赠</option>
<option>第二份半价</option>
</select>
<select style="width:130px;">
<option value="">全部状态</option>
<option>进行中</option>
<option>未开始</option>
<option>已结束</option>
</select>
<input type="text" placeholder="搜索活动名称" style="width:200px;" />
<div style="flex:1;"></div>
<button class="g-btn g-btn-primary" onclick="openFrDrawer('create')">
<i data-lucide="plus" style="width:14px;height:14px;"></i>创建活动
</button>
</div>
<!-- 统计卡片 -->
<div class="fr-stats">
<div class="fr-stat-card">
<div class="fr-stat-label">活动总数</div>
<div class="fr-stat-value">8</div>
</div>
<div class="fr-stat-card">
<div class="fr-stat-label">进行中</div>
<div class="fr-stat-value green">3</div>
</div>
<div class="fr-stat-card">
<div class="fr-stat-label">本月带动销售额</div>
<div class="fr-stat-value orange">¥12,680</div>
</div>
<div class="fr-stat-card">
<div class="fr-stat-label">平均客单价提升</div>
<div class="fr-stat-value">¥8.5</div>
</div>
</div>
<!-- 活动卡片列表 -->
<div class="fr-list">
<!-- 活动1进行中 -->
<div class="fr-card">
<div class="fr-card-hd">
<span class="fr-card-name">午市满减优惠</span>
<span class="fr-type-tag blue">满减</span>
<span class="g-tag g-tag-green">进行中</span>
</div>
<div class="fr-tiers">
<span class="fr-tier-pill">满30减5</span>
<span class="fr-tier-arrow"><i data-lucide="chevron-right" style="width:16px;height:16px;"></i></span>
<span class="fr-tier-pill">满50减10</span>
<span class="fr-tier-arrow"><i data-lucide="chevron-right" style="width:16px;height:16px;"></i></span>
<span class="fr-tier-pill">满80减20</span>
</div>
<div class="fr-meta">
<span class="fr-meta-item"><i data-lucide="calendar" style="width:14px;height:14px;"></i>2026-01-01 ~ 2026-03-31</span>
<span class="fr-meta-item"><i data-lucide="truck" style="width:14px;height:14px;"></i>外卖 / 自提</span>
<span class="fr-meta-item"><i data-lucide="store" style="width:14px;height:14px;"></i>全部门店</span>
</div>
<div class="fr-data">
<span class="fr-data-item">参与订单 <span>286单</span></span>
<span class="fr-data-item">优惠总额 <span>¥2,860</span></span>
<span class="fr-data-item">客单价提升 <span>¥8.2</span></span>
</div>
<div class="fr-card-ft">
<a class="g-action" onclick="openFrDrawer('edit')">编辑</a>
<a class="g-action">停用</a>
<a class="g-action g-action-danger">删除</a>
</div>
</div>
<!-- 活动2进行中 -->
<div class="fr-card">
<div class="fr-card-hd">
<span class="fr-card-name">晚市大额满减</span>
<span class="fr-type-tag blue">满减</span>
<span class="g-tag g-tag-green">进行中</span>
</div>
<div class="fr-tiers">
<span class="fr-tier-pill">满50减8</span>
<span class="fr-tier-arrow"><i data-lucide="chevron-right" style="width:16px;height:16px;"></i></span>
<span class="fr-tier-pill">满80减15</span>
<span class="fr-tier-arrow"><i data-lucide="chevron-right" style="width:16px;height:16px;"></i></span>
<span class="fr-tier-pill">满120减30</span>
</div>
<div class="fr-meta">
<span class="fr-meta-item"><i data-lucide="calendar" style="width:14px;height:14px;"></i>2026-01-15 ~ 2026-04-15</span>
<span class="fr-meta-item"><i data-lucide="truck" style="width:14px;height:14px;"></i>外卖</span>
<span class="fr-meta-item"><i data-lucide="store" style="width:14px;height:14px;"></i>朝阳店 / 海淀店</span>
</div>
<div class="fr-data">
<span class="fr-data-item">参与订单 <span>152单</span></span>
<span class="fr-data-item">优惠总额 <span>¥1,920</span></span>
<span class="fr-data-item">客单价提升 <span>¥10.5</span></span>
</div>
<div class="fr-card-ft">
<a class="g-action" onclick="openFrDrawer('edit')">编辑</a>
<a class="g-action">停用</a>
<a class="g-action g-action-danger">删除</a>
</div>
</div>
<!-- 活动3未开始 -->
<div class="fr-card">
<div class="fr-card-hd">
<span class="fr-card-name">春季新客满减</span>
<span class="fr-type-tag blue">满减</span>
<span class="g-tag g-tag-blue">未开始</span>
</div>
<div class="fr-tiers">
<span class="fr-tier-pill">满25减3</span>
<span class="fr-tier-arrow"><i data-lucide="chevron-right" style="width:16px;height:16px;"></i></span>
<span class="fr-tier-pill">满45减8</span>
<span class="fr-tier-arrow"><i data-lucide="chevron-right" style="width:16px;height:16px;"></i></span>
<span class="fr-tier-pill">满70减15</span>
</div>
<div class="fr-meta">
<span class="fr-meta-item"><i data-lucide="calendar" style="width:14px;height:14px;"></i>2026-03-01 ~ 2026-05-31</span>
<span class="fr-meta-item"><i data-lucide="truck" style="width:14px;height:14px;"></i>外卖 / 自提 / 堂食</span>
<span class="fr-meta-item"><i data-lucide="store" style="width:14px;height:14px;"></i>全部门店</span>
</div>
<div class="fr-data">
<span class="fr-data-item">参与订单 <span>0单</span></span>
<span class="fr-data-item">优惠总额 <span>¥0</span></span>
<span class="fr-data-item">客单价提升 <span>--</span></span>
</div>
<div class="fr-card-ft">
<a class="g-action" onclick="openFrDrawer('edit')">编辑</a>
<a class="g-action">停用</a>
<a class="g-action g-action-danger">删除</a>
</div>
</div>
<!-- 活动4已结束弱化 -->
<div class="fr-card fr-ended">
<div class="fr-card-hd">
<span class="fr-card-name">元旦满减狂欢</span>
<span class="fr-type-tag blue">满减</span>
<span class="g-tag g-tag-gray">已结束</span>
</div>
<div class="fr-tiers">
<span class="fr-tier-pill">满20减3</span>
<span class="fr-tier-arrow"><i data-lucide="chevron-right" style="width:16px;height:16px;"></i></span>
<span class="fr-tier-pill">满40减8</span>
<span class="fr-tier-arrow"><i data-lucide="chevron-right" style="width:16px;height:16px;"></i></span>
<span class="fr-tier-pill">满60减15</span>
</div>
<div class="fr-meta">
<span class="fr-meta-item"><i data-lucide="calendar" style="width:14px;height:14px;"></i>2025-12-25 ~ 2026-01-05</span>
<span class="fr-meta-item"><i data-lucide="truck" style="width:14px;height:14px;"></i>外卖 / 自提</span>
<span class="fr-meta-item"><i data-lucide="store" style="width:14px;height:14px;"></i>望京店 / 通州店</span>
</div>
<div class="fr-data">
<span class="fr-data-item">参与订单 <span>410单</span></span>
<span class="fr-data-item">优惠总额 <span>¥3,680</span></span>
<span class="fr-data-item">客单价提升 <span>¥6.8</span></span>
</div>
<div class="fr-card-ft">
<a class="g-action" onclick="openFrDrawer('edit')">编辑</a>
<a class="g-action g-action-danger">删除</a>
</div>
</div>
<!-- 活动5满赠 进行中 -->
<div class="fr-card">
<div class="fr-card-hd">
<span class="fr-card-name">饮品买二赠一</span>
<span class="fr-type-tag green">满赠</span>
<span class="g-tag g-tag-green">进行中</span>
</div>
<div class="fr-tiers">
<span class="fr-tier-pill">买2杯赠1杯</span>
</div>
<div class="fr-meta">
<span class="fr-meta-item"><i data-lucide="calendar" style="width:14px;height:14px;"></i>2026-02-01 ~ 2026-03-31</span>
<span class="fr-meta-item"><i data-lucide="truck" style="width:14px;height:14px;"></i>外卖 / 自提 / 堂食</span>
<span class="fr-meta-item"><i data-lucide="store" style="width:14px;height:14px;"></i>全部门店</span>
<span class="fr-meta-item"><i data-lucide="gift" style="width:14px;height:14px;"></i>赠品:指定饮品(价低者)</span>
</div>
<div class="fr-data">
<span class="fr-data-item">参与订单 <span>98单</span></span>
<span class="fr-data-item">赠出商品 <span>98件</span></span>
<span class="fr-data-item">带动销售 <span>¥4,120</span></span>
</div>
<div class="fr-card-ft">
<a class="g-action" onclick="openFrDrawer('edit')">编辑</a>
<a class="g-action">停用</a>
<a class="g-action g-action-danger">删除</a>
</div>
</div>
<!-- 活动6第二份半价 进行中 -->
<div class="fr-card">
<div class="fr-card-hd">
<span class="fr-card-name">甜品第二份半价</span>
<span class="fr-type-tag orange">第二份半价</span>
<span class="g-tag g-tag-green">进行中</span>
</div>
<div class="fr-tiers">
<span class="fr-tier-pill">第2份5折</span>
</div>
<div class="fr-meta">
<span class="fr-meta-item"><i data-lucide="calendar" style="width:14px;height:14px;"></i>2026-02-10 ~ 2026-02-28</span>
<span class="fr-meta-item"><i data-lucide="truck" style="width:14px;height:14px;"></i>外卖 / 堂食</span>
<span class="fr-meta-item"><i data-lucide="store" style="width:14px;height:14px;"></i>全部门店</span>
<span class="fr-meta-item"><i data-lucide="tag" style="width:14px;height:14px;"></i>适用:甜品分类</span>
</div>
<div class="fr-data">
<span class="fr-data-item">参与订单 <span>45单</span></span>
<span class="fr-data-item">优惠总额 <span>¥380</span></span>
<span class="fr-data-item">连带率提升 <span>32%</span></span>
</div>
<div class="fr-card-ft">
<a class="g-action" onclick="openFrDrawer('edit')">编辑</a>
<a class="g-action">停用</a>
<a class="g-action g-action-danger">删除</a>
</div>
</div>
</div>
<!-- 分页 -->
<div class="fr-pagination">
<span>共 8 条</span>
<button class="fr-page-btn">&lt;</button>
<button class="fr-page-btn active">1</button>
<button class="fr-page-btn">2</button>
<button class="fr-page-btn">&gt;</button>
<span style="margin-left:8px;">4 条/页</span>
</div>
</div>
<!-- 创建/编辑抽屉 -->
<div class="g-drawer-mask" id="frDrawerMask" onclick="closeFrDrawer()"></div>
<div class="g-drawer" id="frDrawer" style="width:560px">
<div class="g-drawer-hd">
<div class="g-drawer-title" id="frDrawerTitle">创建满减活动</div>
<button class="g-drawer-close" onclick="closeFrDrawer()"><i data-lucide="x" style="width:18px;height:18px;"></i></button>
</div>
<div class="g-drawer-bd">
<!-- 活动类型 -->
<div class="g-form-group">
<label class="g-form-label required">活动类型</label>
<div style="display:flex;gap:10px;">
<span class="g-pill checked" onclick="selectFrType(this,'reduce')">满减</span>
<span class="g-pill" onclick="selectFrType(this,'gift')">满赠</span>
<span class="g-pill" onclick="selectFrType(this,'half')">第二份半价</span>
</div>
</div>
<!-- 活动名称 -->
<div class="g-form-group">
<label class="g-form-label required">活动名称</label>
<input class="g-input" placeholder="如:午市满减优惠" />
</div>
<!-- ===== 满减规则 ===== -->
<div id="frRuleReduce">
<div class="fr-section-hd" style="margin-top:8px;">满减规则</div>
<div id="frTierList">
<div class="fr-tier-row">
<span></span>
<input type="number" value="30" placeholder="金额" />
<span>元减</span>
<input type="number" value="5" placeholder="金额" />
<span></span>
<button class="fr-tier-del" onclick="removeFrTier(this)" title="删除"><i data-lucide="trash-2" style="width:14px;height:14px;"></i></button>
</div>
<div class="fr-tier-row">
<span></span>
<input type="number" value="50" placeholder="金额" />
<span>元减</span>
<input type="number" value="10" placeholder="金额" />
<span></span>
<button class="fr-tier-del" onclick="removeFrTier(this)" title="删除"><i data-lucide="trash-2" style="width:14px;height:14px;"></i></button>
</div>
<div class="fr-tier-row">
<span></span>
<input type="number" value="80" placeholder="金额" />
<span>元减</span>
<input type="number" value="20" placeholder="金额" />
<span></span>
<button class="fr-tier-del" onclick="removeFrTier(this)" title="删除"><i data-lucide="trash-2" style="width:14px;height:14px;"></i></button>
</div>
</div>
<button class="fr-add-tier" onclick="addFrTier()">
<i data-lucide="plus" style="width:14px;height:14px;"></i>添加阶梯
</button>
</div>
<!-- ===== 满赠规则 ===== -->
<div id="frRuleGift" style="display:none;">
<div class="fr-section-hd" style="margin-top:8px;">满赠规则</div>
<div class="g-form-group">
<label class="g-form-label required">赠送条件</label>
<div style="display:flex;align-items:center;gap:8px;font-size:13px;color:var(--g-text);">
<span>购买满</span>
<input class="g-input" type="number" value="2" min="1" style="width:70px;text-align:center;" placeholder="如2">
<span>件赠</span>
<input class="g-input" type="number" value="1" min="1" style="width:70px;text-align:center;" placeholder="如1">
<span></span>
</div>
</div>
<div class="g-form-group">
<label class="g-form-label required">赠品范围</label>
<div style="display:flex;gap:10px;margin-bottom:8px;">
<span class="g-pill checked" onclick="this.parentElement.querySelectorAll('.g-pill').forEach(function(p){p.classList.remove('checked')});this.classList.add('checked')">同商品(价低者)</span>
<span class="g-pill" onclick="this.parentElement.querySelectorAll('.g-pill').forEach(function(p){p.classList.remove('checked')});this.classList.add('checked')">指定赠品</span>
</div>
<div class="g-hint">选"同商品"时,赠品为购买商品中价格最低的一件</div>
</div>
<div class="g-form-group">
<label class="g-form-label">适用商品</label>
<button class="g-btn g-btn-sm" onclick="openProductPicker({title:'选择适用商品',subtitle:'满赠活动',onConfirm:function(){}})"><i data-lucide="plus" style="width:13px;height:13px;"></i>选择商品/分类</button>
<div style="display:flex;flex-wrap:wrap;gap:6px;margin-top:8px;">
<span class="fr-gift-item">热饮分类 <button onclick="this.parentElement.remove()"><i data-lucide="x" style="width:12px;height:12px;"></i></button></span>
<span class="fr-gift-item">冷饮分类 <button onclick="this.parentElement.remove()"><i data-lucide="x" style="width:12px;height:12px;"></i></button></span>
</div>
<div class="g-hint">留空表示全部商品参与</div>
</div>
</div>
<!-- ===== 第二份半价规则 ===== -->
<div id="frRuleHalf" style="display:none;">
<div class="fr-section-hd" style="margin-top:8px;">第二份优惠</div>
<div class="g-form-group">
<label class="g-form-label required">第二份折扣</label>
<div style="display:flex;gap:10px;">
<span class="g-pill checked" onclick="this.parentElement.querySelectorAll('.g-pill').forEach(function(p){p.classList.remove('checked')});this.classList.add('checked')">5折</span>
<span class="g-pill" onclick="this.parentElement.querySelectorAll('.g-pill').forEach(function(p){p.classList.remove('checked')});this.classList.add('checked')">6折</span>
<span class="g-pill" onclick="this.parentElement.querySelectorAll('.g-pill').forEach(function(p){p.classList.remove('checked')});this.classList.add('checked')">7折</span>
<span class="g-pill" onclick="this.parentElement.querySelectorAll('.g-pill').forEach(function(p){p.classList.remove('checked')});this.classList.add('checked')">免费</span>
</div>
</div>
<div class="g-form-group">
<label class="g-form-label required">适用商品</label>
<div style="display:flex;gap:10px;margin-bottom:8px;">
<span class="g-pill checked" onclick="this.parentElement.querySelectorAll('.g-pill').forEach(function(p){p.classList.remove('checked')});this.classList.add('checked')">指定分类</span>
<span class="g-pill" onclick="this.parentElement.querySelectorAll('.g-pill').forEach(function(p){p.classList.remove('checked')});this.classList.add('checked')">指定商品</span>
</div>
<select class="g-select" style="width:100%;">
<option value="">请选择分类</option>
<option>甜品</option>
<option>饮品</option>
<option>小食</option>
</select>
<div class="g-hint">第二份优惠仅适用于所选范围内的商品</div>
</div>
</div>
<!-- 活动时间 -->
<div class="g-form-group" style="margin-top:20px;">
<label class="g-form-label required">活动时间</label>
<div style="display:flex; align-items:center; gap:8px;">
<input class="g-input" type="date" style="flex:1;" />
<span style="color:var(--g-text-muted);"></span>
<input class="g-input" type="date" style="flex:1;" />
</div>
</div>
<!-- 适用渠道 -->
<div class="g-form-group">
<label class="g-form-label">适用渠道</label>
<div style="display:flex; gap:8px; flex-wrap:wrap;">
<span class="g-pill checked" onclick="this.classList.toggle('checked')">外卖</span>
<span class="g-pill checked" onclick="this.classList.toggle('checked')">自提</span>
<span class="g-pill" onclick="this.classList.toggle('checked')">堂食</span>
</div>
</div>
<!-- 适用门店 -->
<div class="g-form-group">
<label class="g-form-label">适用门店</label>
<select class="g-select">
<option>全部门店</option>
<option>老三家外卖(朝阳店)</option>
<option>老三家外卖(海淀店)</option>
<option>老三家外卖(望京店)</option>
<option>老三家外卖(通州店)</option>
<option>老三家外卖(丰台店)</option>
</select>
</div>
<!-- 叠加规则 -->
<div class="g-form-group">
<label class="g-form-label">叠加规则</label>
<div style="display:flex; gap:8px; flex-wrap:wrap;">
<span class="g-pill checked" onclick="frToggleStack(this, 'no')">不可叠加优惠券</span>
<span class="g-pill" onclick="frToggleStack(this, 'yes')">可叠加优惠券</span>
</div>
</div>
<!-- 活动说明 -->
<div class="g-form-group">
<label class="g-form-label">活动说明</label>
<textarea class="g-textarea" rows="3" placeholder="如:每单限享一次满减优惠"></textarea>
</div>
</div>
<div class="g-drawer-ft">
<button class="g-btn" onclick="closeFrDrawer()">取消</button>
<button class="g-btn g-btn-primary" onclick="closeFrDrawer()">保存</button>
</div>
</div>
<script>
/* 抽屉开关 */
function openFrDrawer(mode) {
document.getElementById('frDrawerMask').classList.add('open');
document.getElementById('frDrawer').classList.add('open');
document.getElementById('frDrawerTitle').textContent = mode === 'edit' ? '编辑活动' : '创建活动';
}
function closeFrDrawer() {
document.getElementById('frDrawerMask').classList.remove('open');
document.getElementById('frDrawer').classList.remove('open');
}
/* 活动类型切换 */
function selectFrType(el, type) {
el.parentElement.querySelectorAll('.g-pill').forEach(function(p) { p.classList.remove('checked'); });
el.classList.add('checked');
document.getElementById('frRuleReduce').style.display = type === 'reduce' ? '' : 'none';
document.getElementById('frRuleGift').style.display = type === 'gift' ? '' : 'none';
document.getElementById('frRuleHalf').style.display = type === 'half' ? '' : 'none';
}
/* 添加阶梯行 */
function addFrTier() {
var list = document.getElementById('frTierList');
var row = document.createElement('div');
row.className = 'fr-tier-row';
row.innerHTML = '<span>满</span>' +
'<input type="number" placeholder="金额" />' +
'<span>元减</span>' +
'<input type="number" placeholder="金额" />' +
'<span>元</span>' +
'<button class="fr-tier-del" onclick="removeFrTier(this)" title="删除"><i data-lucide="trash-2" style="width:14px;height:14px;"></i></button>';
list.appendChild(row);
if (window.lucide) lucide.createIcons();
}
/* 删除阶梯行 */
function removeFrTier(btn) {
var row = btn.closest('.fr-tier-row');
if (document.querySelectorAll('.fr-tier-row').length > 1) {
row.remove();
}
}
/* 叠加规则互斥切换 */
function frToggleStack(el, type) {
var pills = el.parentElement.querySelectorAll('.g-pill');
pills.forEach(function(p) { p.classList.remove('checked'); });
el.classList.add('checked');
}
</script>

567
pages/mkt-seckill.html Normal file
View File

@@ -0,0 +1,567 @@
<!-- 秒杀活动页 -->
<style>
.sk-toolbar { display:flex; align-items:center; gap:12px; margin-bottom:16px; box-shadow:var(--g-shadow-sm); border-radius:10px; padding:12px 16px; background:#fff; flex-wrap:wrap; }
.sk-search { position:relative; }
.sk-search input { height:34px; padding:0 10px 0 32px; border:1px solid #e5e7eb; border-radius:8px; font-size:13px; outline:none; width:200px; transition:var(--g-transition); }
.sk-search input:focus { border-color:var(--primary); box-shadow:0 0 0 3px color-mix(in srgb, var(--primary) 12%, transparent); }
.sk-search i { position:absolute; left:9px; top:50%; transform:translateY(-50%); color:#bbb; pointer-events:none; }
.sk-stats { display:flex; gap:24px; margin-bottom:16px; padding:10px 16px; background:#fff; border-radius:10px; box-shadow:var(--g-shadow-sm); font-size:13px; color:#4b5563; }
.sk-stats span { display:flex; align-items:center; gap:6px; }
.sk-stats strong { color:#1a1a2e; font-weight:600; }
.sk-card { background:#fff; border-radius:10px; box-shadow:var(--g-shadow-sm); padding:20px; margin-bottom:16px; transition:box-shadow var(--g-transition); }
.sk-card:hover { box-shadow:var(--g-shadow-md); }
.sk-card.ended { opacity:.5; }
.sk-card-hd { display:flex; align-items:center; gap:10px; margin-bottom:14px; flex-wrap:wrap; }
.sk-card-name { font-size:15px; font-weight:600; color:#1a1a2e; }
.sk-card-time { font-size:12px; color:var(--g-text-muted); display:flex; align-items:center; gap:4px; }
.sk-tag-running { background:#dcfce7; color:#22c55e; border:1px solid #bbf7d0; border-radius:6px; font-weight:600; }
.sk-tag-notstarted { background:#f0f5ff; color:var(--primary); border:1px solid #adc6ff; border-radius:6px; font-weight:600; }
.sk-tag-ended { background:#f8f9fb; color:#9ca3af; border:1px solid #e5e7eb; border-radius:6px; font-weight:600; }
/* 秒杀商品行 */
.sk-prod-list { margin-bottom:14px; }
.sk-prod-row { display:flex; align-items:center; gap:16px; padding:10px 12px; border-bottom:1px solid #f3f4f6; font-size:13px; }
.sk-prod-row:last-child { border-bottom:none; }
.sk-prod-row:hover { background:color-mix(in srgb, var(--primary) 3%, #fff); border-radius:6px; }
.sk-prod-name { width:140px; font-weight:500; color:#1a1a2e; flex-shrink:0; }
.sk-prod-prices { display:flex; align-items:baseline; gap:8px; width:140px; flex-shrink:0; }
.sk-orig-price { color:var(--g-text-muted); text-decoration:line-through; font-size:12px; }
.sk-seckill-price { color:#ef4444; font-weight:700; font-size:16px; }
/* 进度条 */
.sk-progress-wrap { flex:1; min-width:160px; display:flex; align-items:center; gap:8px; }
.sk-progress { position:relative; height:16px; background:#f3f4f6; border-radius:8px; overflow:hidden; flex:1; }
.sk-progress-fill { height:100%; border-radius:8px; transition:width 0.3s ease; }
.sk-progress-fill.green { background:linear-gradient(90deg, #52c41a, #73d13d); }
.sk-progress-fill.orange { background:linear-gradient(90deg, #fa8c16, #ffa940); }
.sk-progress-fill.red { background:linear-gradient(90deg, #f5222d, #ff4d4f); }
.sk-progress-text { position:absolute; inset:0; display:flex; align-items:center; justify-content:center; font-size:11px; font-weight:600; color:#fff; text-shadow:0 1px 2px rgba(0,0,0,.2); white-space:nowrap; }
.sk-sold-out { display:inline-flex; align-items:center; gap:3px; padding:2px 8px; border-radius:4px; font-size:11px; font-weight:600; background:#fff1f0; color:#f5222d; border:1px solid #ffa39e; white-space:nowrap; }
.sk-card-summary { display:flex; gap:24px; font-size:12px; color:#4b5563; margin-bottom:12px; padding:10px 12px; background:#f8f9fb; border-radius:8px; }
.sk-card-summary strong { color:#1a1a2e; font-weight:600; }
.sk-card-ft { display:flex; gap:16px; border-top:1px solid #f3f4f6; padding-top:12px; }
/* 抽屉内秒杀商品 */
.sk-drawer-prod { border:1px solid #e5e7eb; border-radius:8px; overflow:hidden; margin-bottom:12px; }
.sk-drawer-prod-hd { display:flex; align-items:center; justify-content:space-between; padding:8px 12px; background:#f8f9fb; border-bottom:1px solid #e5e7eb; }
.sk-drawer-prod-hd span { font-size:13px; font-weight:500; color:#1a1a2e; }
.sk-drawer-prod-remove { background:none; border:none; color:#9ca3af; cursor:pointer; display:flex; align-items:center; justify-content:center; width:24px; height:24px; border-radius:6px; transition:var(--g-transition); }
.sk-drawer-prod-remove:hover { color:#ef4444; background:#fef2f2; }
.sk-drawer-prod-bd { padding:10px 12px; display:flex; gap:12px; align-items:center; flex-wrap:wrap; }
.sk-drawer-prod-bd .sk-field { display:flex; flex-direction:column; gap:4px; }
.sk-drawer-prod-bd .sk-field label { font-size:11px; color:#6b7280; }
.sk-drawer-prod-bd .sk-field input { width:100px; height:30px; padding:0 8px; border:1px solid #e5e7eb; border-radius:6px; font-size:13px; outline:none; transition:var(--g-transition); }
.sk-drawer-prod-bd .sk-field input:focus { border-color:var(--primary); box-shadow:0 0 0 3px color-mix(in srgb, var(--primary) 12%, transparent); }
.sk-drawer-prod-bd .sk-field input[readonly] { background:#f8f9fb; color:var(--g-text-muted); cursor:default; }
.sk-pill-group { display:flex; gap:8px; flex-wrap:wrap; }
.sk-pill { padding:6px 16px; border-radius:8px; border:1px solid #e5e7eb; background:#fff; font-size:13px; cursor:pointer; color:#4b5563; transition:var(--g-transition); }
.sk-pill:hover { border-color:var(--primary); color:var(--primary); }
.sk-pill.checked { background:var(--primary); color:#fff; border-color:var(--primary); }
/* 场次设置 */
.sk-session-section { display:none; margin-top:12px; }
.sk-session-section.show { display:block; }
.sk-date-section { display:block; margin-top:12px; }
.sk-date-section.hide { display:none; }
.sk-session-row { display:flex; align-items:center; gap:8px; margin-bottom:8px; padding:8px 12px; background:#f8f9fb; border-radius:6px; border:1px solid #f0f0f0; }
.sk-session-row input { width:90px; }
.sk-session-label { font-size:12px; color:var(--g-text-secondary); }
.sk-session-remove { background:none; border:none; color:#9ca3af; cursor:pointer; display:flex; align-items:center; justify-content:center; width:24px; height:24px; border-radius:6px; transition:var(--g-transition); }
.sk-session-remove:hover { color:#ef4444; background:#fef2f2; }
.sk-limit-row { display:flex; align-items:center; gap:8px; }
.sk-limit-row input { width:80px; }
.sk-unit { font-size:13px; color:var(--g-text-secondary); }
.sk-preheat-row { display:flex; align-items:center; gap:10px; margin-top:8px; }
.sk-preheat-row input { width:60px; }
</style>
<!-- 工具栏 -->
<div class="sk-toolbar">
<select class="g-select" style="width:200px;">
<option>老三家外卖(朝阳店)</option>
<option>老三家外卖(海淀店)</option>
<option>老三家外卖(望京店)</option>
<option>老三家外卖(通州店)</option>
<option>老三家外卖(丰台店)</option>
</select>
<select class="g-select" style="width:120px;">
<option value="">全部状态</option>
<option>进行中</option>
<option>未开始</option>
<option>已结束</option>
</select>
<div class="sk-search">
<i data-lucide="search" style="width:14px;height:14px;"></i>
<input type="text" placeholder="搜索秒杀活动名称…" />
</div>
<div style="flex:1;"></div>
<button class="g-btn g-btn-primary" onclick="openSkDrawer('add')"><i data-lucide="plus" style="width:14px;height:14px;"></i>创建秒杀</button>
</div>
<!-- 统计栏 -->
<div class="sk-stats">
<span>秒杀活动 <strong>6</strong></span>
<span>进行中 <strong>2</strong></span>
<span>本月秒杀销量 <strong>860</strong></span>
<span>秒杀转化率 <strong>78.5%</strong></span>
</div>
<!-- 活动卡片列表 -->
<div id="skActivityList">
<!-- 午间秒杀 - 进行中 -->
<div class="sk-card">
<div class="sk-card-hd">
<span class="sk-card-name">午间秒杀</span>
<span class="g-tag sk-tag-running">进行中</span>
<span class="sk-card-time"><i data-lucide="clock" style="width:12px;height:12px;"></i>每天 11:00 - 13:00</span>
</div>
<div class="sk-prod-list">
<div class="sk-prod-row">
<span class="sk-prod-name">招牌卤肉饭</span>
<span class="sk-prod-prices">
<span class="sk-orig-price">¥22</span>
<span class="sk-seckill-price">¥9.9</span>
</span>
<div class="sk-progress-wrap">
<div class="sk-progress">
<div class="sk-progress-fill orange" style="width:84%;"></div>
<span class="sk-progress-text">已抢 42/50</span>
</div>
</div>
</div>
<div class="sk-prod-row">
<span class="sk-prod-name">冰粉</span>
<span class="sk-prod-prices">
<span class="sk-orig-price">¥8</span>
<span class="sk-seckill-price">¥1</span>
</span>
<div class="sk-progress-wrap">
<div class="sk-progress">
<div class="sk-progress-fill red" style="width:100%;"></div>
<span class="sk-progress-text">已抢 50/50</span>
</div>
<span class="sk-sold-out"><i data-lucide="flame" style="width:11px;height:11px;"></i>已抢光</span>
</div>
</div>
</div>
<div class="sk-card-summary">
<span>参与人数 <strong>286</strong></span>
<span>成交 <strong>218</strong></span>
<span>转化率 <strong>76.2%</strong></span>
</div>
<div class="sk-card-ft">
<span class="g-action" onclick="openSkDrawer('edit','午间秒杀')">编辑</span>
<span class="g-action">停用</span>
<span class="g-action g-action-danger">删除</span>
</div>
</div>
<!-- 整点特惠 - 进行中 -->
<div class="sk-card">
<div class="sk-card-hd">
<span class="sk-card-name">整点特惠</span>
<span class="g-tag sk-tag-running">进行中</span>
<span class="sk-card-time"><i data-lucide="clock" style="width:12px;height:12px;"></i>每天 10:00 场 / 14:00 场 / 18:00 场</span>
</div>
<div class="sk-prod-list">
<div class="sk-prod-row">
<span class="sk-prod-name">黄焖鸡米饭</span>
<span class="sk-prod-prices">
<span class="sk-orig-price">¥28</span>
<span class="sk-seckill-price">¥14.9</span>
</span>
<div class="sk-progress-wrap">
<div class="sk-progress">
<div class="sk-progress-fill orange" style="width:70%;"></div>
<span class="sk-progress-text">已抢 28/40</span>
</div>
</div>
</div>
<div class="sk-prod-row">
<span class="sk-prod-name">奶茶(大杯)</span>
<span class="sk-prod-prices">
<span class="sk-orig-price">¥15</span>
<span class="sk-seckill-price">¥5</span>
</span>
<div class="sk-progress-wrap">
<div class="sk-progress">
<div class="sk-progress-fill orange" style="width:70%;"></div>
<span class="sk-progress-text">已抢 35/50</span>
</div>
</div>
</div>
<div class="sk-prod-row">
<span class="sk-prod-name">炸鸡翅(6只)</span>
<span class="sk-prod-prices">
<span class="sk-orig-price">¥25</span>
<span class="sk-seckill-price">¥12.9</span>
</span>
<div class="sk-progress-wrap">
<div class="sk-progress">
<div class="sk-progress-fill green" style="width:60%;"></div>
<span class="sk-progress-text">已抢 18/30</span>
</div>
</div>
</div>
</div>
<div class="sk-card-summary">
<span>参与人数 <strong>198</strong></span>
<span>成交 <strong>156</strong></span>
<span>转化率 <strong>78.8%</strong></span>
</div>
<div class="sk-card-ft">
<span class="g-action" onclick="openSkDrawer('edit','整点特惠')">编辑</span>
<span class="g-action">停用</span>
<span class="g-action g-action-danger">删除</span>
</div>
</div>
<!-- 新品首发秒杀 - 未开始 -->
<div class="sk-card">
<div class="sk-card-hd">
<span class="sk-card-name">新品首发秒杀</span>
<span class="g-tag sk-tag-notstarted">未开始</span>
<span class="sk-card-time"><i data-lucide="calendar" style="width:12px;height:12px;"></i>2026-02-20 10:00 开始</span>
</div>
<div class="sk-prod-list">
<div class="sk-prod-row">
<span class="sk-prod-name">黑松露鸡肉卷</span>
<span class="sk-prod-prices">
<span class="sk-orig-price">¥38</span>
<span class="sk-seckill-price">¥19.9</span>
</span>
<div class="sk-progress-wrap">
<div class="sk-progress">
<div class="sk-progress-fill green" style="width:0%;"></div>
<span class="sk-progress-text" style="color:#999;">0/100</span>
</div>
</div>
</div>
<div class="sk-prod-row">
<span class="sk-prod-name">芝士焗虾饭</span>
<span class="sk-prod-prices">
<span class="sk-orig-price">¥35</span>
<span class="sk-seckill-price">¥17.5</span>
</span>
<div class="sk-progress-wrap">
<div class="sk-progress">
<div class="sk-progress-fill green" style="width:0%;"></div>
<span class="sk-progress-text" style="color:#999;">0/80</span>
</div>
</div>
</div>
</div>
<div class="sk-card-ft">
<span class="g-action" onclick="openSkDrawer('edit','新品首发秒杀')">编辑</span>
<span class="g-action">停用</span>
<span class="g-action g-action-danger">删除</span>
</div>
</div>
<!-- 周年庆秒杀 - 已结束 -->
<div class="sk-card ended">
<div class="sk-card-hd">
<span class="sk-card-name">周年庆秒杀</span>
<span class="g-tag sk-tag-ended">已结束</span>
<span class="sk-card-time"><i data-lucide="calendar" style="width:12px;height:12px;"></i>2026-01-15 ~ 2026-01-17</span>
</div>
<div class="sk-prod-list">
<div class="sk-prod-row">
<span class="sk-prod-name">全场5折套餐</span>
<span class="sk-prod-prices">
<span class="sk-orig-price">¥49</span>
<span class="sk-seckill-price">¥24.5</span>
</span>
<div class="sk-progress-wrap">
<div class="sk-progress">
<div class="sk-progress-fill red" style="width:100%;"></div>
<span class="sk-progress-text">已抢 200/200</span>
</div>
<span class="sk-sold-out"><i data-lucide="flame" style="width:11px;height:11px;"></i>已抢光</span>
</div>
</div>
</div>
<div class="sk-card-summary">
<span>参与人数 <strong>520</strong></span>
<span>成交 <strong>412</strong></span>
</div>
<div class="sk-card-ft">
<span class="g-action" onclick="openSkDrawer('edit','周年庆秒杀')">编辑</span>
<span class="g-action">启用</span>
<span class="g-action g-action-danger">删除</span>
</div>
</div>
</div>
<!-- 分页 -->
<div class="g-pagination" style="margin-top:8px;">
<button class="g-page-btn" disabled><i data-lucide="chevron-left" style="width:14px;height:14px;"></i></button>
<button class="g-page-btn active">1</button>
<button class="g-page-btn">2</button>
<button class="g-page-btn"><i data-lucide="chevron-right" style="width:14px;height:14px;"></i></button>
<span style="font-size:12px;color:var(--g-text-muted);margin-left:8px;">共 6 条</span>
</div>
<!-- 创建/编辑抽屉 -->
<div class="g-drawer-mask" id="skDrawerMask" onclick="closeSkDrawer()"></div>
<div class="g-drawer" id="skDrawer" style="width:560px">
<div class="g-drawer-hd">
<span class="g-drawer-title" id="skDrawerTitle">创建秒杀活动</span>
<button class="g-drawer-close" onclick="closeSkDrawer()"><i data-lucide="x" style="width:18px;height:18px;"></i></button>
</div>
<div class="g-drawer-bd">
<!-- 活动名称 -->
<div class="g-form-group">
<label class="g-form-label required">活动名称</label>
<input class="g-input" id="skName" placeholder="如:午间秒杀、整点特惠">
</div>
<!-- 活动类型 -->
<div class="g-form-group">
<label class="g-form-label required">活动类型</label>
<div class="sk-pill-group">
<span class="sk-pill checked" onclick="selectSkType(this,'timed')">限时秒杀</span>
<span class="sk-pill" onclick="selectSkType(this,'hourly')">整点秒杀</span>
</div>
</div>
<!-- 限时秒杀:日期+时间范围 -->
<div class="g-form-group sk-date-section" id="skDateSection">
<label class="g-form-label required">活动时间</label>
<div style="display:flex;gap:8px;align-items:center;">
<input class="g-input" type="date" value="2026-02-15" style="flex:1;" placeholder="开始日期">
<span style="color:var(--g-text-muted);">~</span>
<input class="g-input" type="date" value="2026-03-15" style="flex:1;" placeholder="结束日期">
</div>
<div style="display:flex;gap:8px;align-items:center;margin-top:8px;">
<input class="g-input" type="time" value="11:00" style="flex:1;" placeholder="开始时间">
<span style="color:var(--g-text-muted);">~</span>
<input class="g-input" type="time" value="13:00" style="flex:1;" placeholder="结束时间">
</div>
</div>
<!-- 整点秒杀:场次设置 -->
<div class="g-form-group sk-session-section" id="skSessionSection">
<label class="g-form-label required">场次设置</label>
<div id="skSessionList">
<div class="sk-session-row">
<input class="g-input" type="time" value="10:00" placeholder="场次时间">
<span class="sk-session-label">持续</span>
<input class="g-input" type="number" value="60" min="1" style="width:70px;" placeholder="分钟">
<span class="sk-session-label">分钟</span>
<button class="sk-session-remove" onclick="removeSkSession(this)"><i data-lucide="x" style="width:14px;height:14px;"></i></button>
</div>
<div class="sk-session-row">
<input class="g-input" type="time" value="14:00" placeholder="场次时间">
<span class="sk-session-label">持续</span>
<input class="g-input" type="number" value="60" min="1" style="width:70px;" placeholder="分钟">
<span class="sk-session-label">分钟</span>
<button class="sk-session-remove" onclick="removeSkSession(this)"><i data-lucide="x" style="width:14px;height:14px;"></i></button>
</div>
<div class="sk-session-row">
<input class="g-input" type="time" value="18:00" placeholder="场次时间">
<span class="sk-session-label">持续</span>
<input class="g-input" type="number" value="60" min="1" style="width:70px;" placeholder="分钟">
<span class="sk-session-label">分钟</span>
<button class="sk-session-remove" onclick="removeSkSession(this)"><i data-lucide="x" style="width:14px;height:14px;"></i></button>
</div>
</div>
<button class="g-btn g-btn-sm" style="margin-top:4px;" onclick="addSkSession()"><i data-lucide="plus" style="width:13px;height:13px;"></i>添加场次</button>
</div>
<!-- 秒杀商品 -->
<div class="g-form-group">
<label class="g-form-label required" style="margin-bottom:10px;">秒杀商品</label>
<div id="skProductList">
<!-- 预填商品 1 -->
<div class="sk-drawer-prod">
<div class="sk-drawer-prod-hd">
<span>招牌卤肉饭</span>
<button class="sk-drawer-prod-remove" onclick="removeSkProduct(this)"><i data-lucide="x" style="width:14px;height:14px;"></i></button>
</div>
<div class="sk-drawer-prod-bd">
<div class="sk-field">
<label>原价</label>
<input type="text" value="¥22.00" readonly>
</div>
<div class="sk-field">
<label>秒杀价</label>
<input type="number" value="9.90" step="0.01" placeholder="请输入秒杀价">
</div>
<div class="sk-field">
<label>限量(份)</label>
<input type="number" value="50" min="1" placeholder="如50">
</div>
<div class="sk-field">
<label>每人限购</label>
<input type="number" value="1" min="0" placeholder="不限则留空">
</div>
</div>
</div>
<!-- 预填商品 2 -->
<div class="sk-drawer-prod">
<div class="sk-drawer-prod-hd">
<span>冰粉</span>
<button class="sk-drawer-prod-remove" onclick="removeSkProduct(this)"><i data-lucide="x" style="width:14px;height:14px;"></i></button>
</div>
<div class="sk-drawer-prod-bd">
<div class="sk-field">
<label>原价</label>
<input type="text" value="¥8.00" readonly>
</div>
<div class="sk-field">
<label>秒杀价</label>
<input type="number" value="1.00" step="0.01" placeholder="请输入秒杀价">
</div>
<div class="sk-field">
<label>限量(份)</label>
<input type="number" value="50" min="1" placeholder="如50">
</div>
<div class="sk-field">
<label>每人限购</label>
<input type="number" value="1" min="0" placeholder="不限则留空">
</div>
</div>
</div>
</div>
<button class="g-btn g-btn-sm" style="margin-top:4px;" onclick="addSkProducts()"><i data-lucide="plus" style="width:13px;height:13px;"></i>添加商品</button>
</div>
<!-- 适用渠道 -->
<div class="g-form-group">
<label class="g-form-label">适用渠道</label>
<div class="sk-pill-group">
<span class="sk-pill checked" onclick="toggleSkPill(this)">外卖</span>
<span class="sk-pill checked" onclick="toggleSkPill(this)">自提</span>
<span class="sk-pill checked" onclick="toggleSkPill(this)">堂食</span>
</div>
</div>
<!-- 每人限购 -->
<div class="g-form-group">
<label class="g-form-label">每人限购</label>
<div class="sk-limit-row">
<input class="g-input" type="number" value="2" min="0" placeholder="不限则留空" style="width:100px;">
<span class="sk-unit"></span>
</div>
<div class="g-hint">活动期间每人累计可秒杀的商品总数,留空不限</div>
</div>
<!-- 预热设置 -->
<div class="g-form-group">
<label class="g-form-label">预热设置</label>
<div class="g-toggle-wrap">
<div class="g-toggle on" id="skPreheatToggle" onclick="this.classList.toggle('on')"></div>
<span class="g-toggle-label">开启预热</span>
</div>
<div class="sk-preheat-row">
<span class="sk-session-label">提前</span>
<input class="g-input" type="number" value="2" min="1" placeholder="小时" style="width:60px;">
<span class="sk-session-label">小时</span>
</div>
<div class="g-hint">在商品页展示秒杀预告,吸引用户提前关注</div>
</div>
</div>
<div class="g-drawer-ft">
<button class="g-btn" onclick="closeSkDrawer()">取消</button>
<button class="g-btn g-btn-primary" onclick="closeSkDrawer()">保存</button>
</div>
</div>
<script>
/* 抽屉开关 */
function openSkDrawer(mode, name) {
document.getElementById('skDrawerMask').classList.add('open');
document.getElementById('skDrawer').classList.add('open');
if (mode === 'edit') {
document.getElementById('skDrawerTitle').textContent = '编辑秒杀活动';
document.getElementById('skName').value = name || '';
} else {
document.getElementById('skDrawerTitle').textContent = '创建秒杀活动';
document.getElementById('skName').value = '';
}
if (typeof lucide !== 'undefined') lucide.createIcons();
}
function closeSkDrawer() {
document.getElementById('skDrawerMask').classList.remove('open');
document.getElementById('skDrawer').classList.remove('open');
}
/* 活动类型切换 */
function selectSkType(el, type) {
el.parentElement.querySelectorAll('.sk-pill').forEach(function(p) { p.classList.remove('checked'); });
el.classList.add('checked');
var dateSection = document.getElementById('skDateSection');
var sessionSection = document.getElementById('skSessionSection');
if (type === 'hourly') {
dateSection.classList.add('hide');
sessionSection.classList.add('show');
} else {
dateSection.classList.remove('hide');
sessionSection.classList.remove('show');
}
}
/* 场次管理 */
function addSkSession() {
var list = document.getElementById('skSessionList');
var row = document.createElement('div');
row.className = 'sk-session-row';
row.innerHTML =
'<input class="g-input" type="time" placeholder="场次时间">' +
'<span class="sk-session-label">持续</span>' +
'<input class="g-input" type="number" value="60" min="1" style="width:70px;" placeholder="分钟">' +
'<span class="sk-session-label">分钟</span>' +
'<button class="sk-session-remove" onclick="removeSkSession(this)"><i data-lucide="x" style="width:14px;height:14px;"></i></button>';
list.appendChild(row);
if (typeof lucide !== 'undefined') lucide.createIcons();
}
function removeSkSession(btn) {
btn.closest('.sk-session-row').remove();
}
/* 商品管理 */
function addSkProducts() {
var names = ['黄焖鸡米饭', '奶茶(大杯)', '炸鸡翅(6只)'];
var prices = ['28.00', '15.00', '25.00'];
var idx = Math.floor(Math.random() * names.length);
var list = document.getElementById('skProductList');
var div = document.createElement('div');
div.className = 'sk-drawer-prod';
div.innerHTML =
'<div class="sk-drawer-prod-hd"><span>' + names[idx] + '</span>' +
'<button class="sk-drawer-prod-remove" onclick="removeSkProduct(this)"><i data-lucide="x" style="width:14px;height:14px;"></i></button></div>' +
'<div class="sk-drawer-prod-bd">' +
'<div class="sk-field"><label>原价</label><input type="text" value="¥' + prices[idx] + '" readonly></div>' +
'<div class="sk-field"><label>秒杀价</label><input type="number" step="0.01" placeholder="请输入秒杀价"></div>' +
'<div class="sk-field"><label>限量(份)</label><input type="number" min="1" placeholder="如50"></div>' +
'<div class="sk-field"><label>每人限购</label><input type="number" min="0" placeholder="不限则留空"></div>' +
'</div>';
list.appendChild(div);
if (typeof lucide !== 'undefined') lucide.createIcons();
}
function removeSkProduct(btn) {
btn.closest('.sk-drawer-prod').remove();
}
/* Pill 切换 */
function toggleSkPill(el) {
el.classList.toggle('checked');
}
/* 初始化图标 */
if (typeof lucide !== 'undefined') lucide.createIcons();
</script>

142
pages/msg-center.html Normal file
View File

@@ -0,0 +1,142 @@
<!-- 消息中心 -->
<style>
.msg-page { display:flex; flex-direction:column; gap:16px; }
.msg-topbar { display:flex; align-items:center; justify-content:space-between; }
.msg-list { display:flex; flex-direction:column; gap:10px; }
.msg-item {
display:flex; align-items:flex-start; gap:14px; padding:16px 18px;
background:#fff; border-radius:var(--g-radius); box-shadow:var(--g-shadow-sm);
transition:var(--g-transition); cursor:pointer;
}
.msg-item:hover { box-shadow:var(--g-shadow-md); transform:translateY(-1px); }
.msg-icon {
width:40px; height:40px; border-radius:50%; display:flex; align-items:center;
justify-content:center; flex-shrink:0; color:#fff;
}
.msg-icon svg { width:18px; height:18px; }
.msg-icon.msg-sys { background:var(--primary); }
.msg-icon.msg-order { background:var(--g-success); }
.msg-icon.msg-promo { background:var(--g-warning); }
.msg-icon.msg-warn { background:var(--g-danger); }
.msg-body { flex:1; min-width:0; }
.msg-title-row { display:flex; align-items:center; gap:8px; margin-bottom:4px; }
.msg-dot { width:8px; height:8px; border-radius:50%; background:var(--primary); flex-shrink:0; }
.msg-title { font-size:14px; font-weight:600; color:var(--g-text); }
.msg-summary {
font-size:13px; color:var(--g-text-muted); white-space:nowrap;
overflow:hidden; text-overflow:ellipsis; line-height:1.6;
}
.msg-time { font-size:12px; color:var(--g-text-muted); white-space:nowrap; flex-shrink:0; padding-top:2px; }
.msg-load-more {
display:flex; justify-content:center; padding:14px 0;
}
.msg-load-btn {
background:none; border:1px dashed var(--g-border); border-radius:var(--g-radius-sm);
padding:8px 32px; font-size:13px; color:var(--g-text-secondary); cursor:pointer;
transition:var(--g-transition);
}
.msg-load-btn:hover { border-color:var(--primary); color:var(--primary); }
</style>
<div class="msg-page">
<!-- 顶部栏 -->
<div class="msg-topbar">
<div class="g-seg">
<div class="g-seg-item active" onclick="msgSwitchTab(event)">全部消息</div>
<div class="g-seg-item" onclick="msgSwitchTab(event)">系统通知</div>
<div class="g-seg-item" onclick="msgSwitchTab(event)">订单消息</div>
<div class="g-seg-item" onclick="msgSwitchTab(event)">营销提醒</div>
</div>
<button class="g-btn g-btn-sm"><i data-lucide="check-check" style="width:14px;height:14px;margin-right:4px;"></i>全部已读</button>
</div>
<!-- 消息列表 -->
<div class="msg-list">
<!-- 未读 - 系统通知 -->
<div class="msg-item">
<div class="msg-icon msg-sys"><i data-lucide="monitor"></i></div>
<div class="msg-body">
<div class="msg-title-row"><span class="msg-dot"></span><span class="msg-title">系统将于今晚 23:00 进行维护升级</span></div>
<div class="msg-summary">预计维护时间 2 小时,届时部分功能可能无法正常使用,请提前做好准备。</div>
</div>
<div class="msg-time">5分钟前</div>
</div>
<!-- 未读 - 订单消息 -->
<div class="msg-item">
<div class="msg-icon msg-order"><i data-lucide="shopping-bag"></i></div>
<div class="msg-body">
<div class="msg-title-row"><span class="msg-dot"></span><span class="msg-title">新订单 #ORD20260213089 待接单</span></div>
<div class="msg-summary">用户下单 3 件商品,订单金额 ¥86.50,请尽快处理。</div>
</div>
<div class="msg-time">12分钟前</div>
</div>
<!-- 未读 - 预警 -->
<div class="msg-item">
<div class="msg-icon msg-warn"><i data-lucide="alert-triangle"></i></div>
<div class="msg-body">
<div class="msg-title-row"><span class="msg-dot"></span><span class="msg-title">食品经营许可证将于30天后到期</span></div>
<div class="msg-summary">请及时续办相关资质证件,避免影响正常经营。</div>
</div>
<div class="msg-time">1小时前</div>
</div>
<!-- 已读 - 系统通知 -->
<div class="msg-item">
<div class="msg-icon msg-sys"><i data-lucide="monitor"></i></div>
<div class="msg-body">
<div class="msg-title-row"><span class="msg-title">v2.3.1 版本更新说明</span></div>
<div class="msg-summary">新增库存预警功能、优化订单打印模板、修复已知问题若干。</div>
</div>
<div class="msg-time">3小时前</div>
</div>
<!-- 已读 - 订单消息 -->
<div class="msg-item">
<div class="msg-icon msg-order"><i data-lucide="shopping-bag"></i></div>
<div class="msg-body">
<div class="msg-title-row"><span class="msg-title">退款申请 #RF20260213002 待处理</span></div>
<div class="msg-summary">用户申请退款 ¥32.00,原因:商品与描述不符,请及时审核。</div>
</div>
<div class="msg-time">5小时前</div>
</div>
<!-- 已读 - 营销提醒 -->
<div class="msg-item">
<div class="msg-icon msg-promo"><i data-lucide="megaphone"></i></div>
<div class="msg-body">
<div class="msg-title-row"><span class="msg-title">优惠券「满50减10」今日到期</span></div>
<div class="msg-summary">该优惠券已发放 326 张,核销率 41%,到期后将自动失效。</div>
</div>
<div class="msg-time">昨天</div>
</div>
<!-- 已读 - 系统通知 -->
<div class="msg-item">
<div class="msg-icon msg-sys"><i data-lucide="monitor"></i></div>
<div class="msg-body">
<div class="msg-title-row"><span class="msg-title">2月账单已生成请查看</span></div>
<div class="msg-summary">2026年2月平台服务费账单已出合计 ¥1,280.00,请前往财务中心查看。</div>
</div>
<div class="msg-time">2天前</div>
</div>
<!-- 已读 - 订单消息 -->
<div class="msg-item">
<div class="msg-icon msg-order"><i data-lucide="shopping-bag"></i></div>
<div class="msg-body">
<div class="msg-title-row"><span class="msg-title">订单 #ORD20260211045 客户已评价</span></div>
<div class="msg-summary">客户给出 5 星好评:"味道很好,配送也快,下次还会再点!"</div>
</div>
<div class="msg-time">3天前</div>
</div>
</div>
<!-- 加载更多 -->
<div class="msg-load-more">
<button class="msg-load-btn">加载更多</button>
</div>
</div>
<script>
function msgSwitchTab(e) {
var items = e.currentTarget.parentElement.querySelectorAll('.g-seg-item');
items.forEach(function(i){ i.classList.remove('active'); });
e.currentTarget.classList.add('active');
}
if(typeof lucide!=='undefined'){lucide.createIcons();}
</script>

116
pages/my-profile.html Normal file
View File

@@ -0,0 +1,116 @@
<style>
.myp-wrap{max-width:720px;margin:0 auto;padding:24px 0;}
.myp-avatar-area{display:flex;flex-direction:column;align-items:center;margin-bottom:28px;}
.myp-avatar{width:80px;height:80px;border-radius:50%;background:var(--primary);display:flex;align-items:center;justify-content:center;color:#fff;font-size:32px;font-weight:700;user-select:none;}
.myp-change-avatar{margin-top:10px;font-size:13px;color:var(--primary);cursor:pointer;background:none;border:none;padding:0;}
.myp-change-avatar:hover{text-decoration:underline;}
.myp-name{font-size:18px;font-weight:600;color:var(--g-text);margin-top:10px;}
.myp-role{margin-top:6px;}
.myp-section-hd{font-size:15px;font-weight:600;color:var(--g-text);padding-left:10px;border-left:3px solid var(--primary);margin-bottom:16px;}
.myp-form-grid{display:grid;grid-template-columns:1fr 1fr;gap:16px 20px;}
.myp-card-ft{padding-top:18px;display:flex;justify-content:flex-end;}
.myp-security-row{display:flex;align-items:center;justify-content:space-between;padding:14px 0;}
.myp-security-left{display:flex;flex-direction:column;gap:3px;}
.myp-security-title{font-size:14px;font-weight:500;color:var(--g-text);}
.myp-security-desc{font-size:12px;color:var(--g-text-muted);}
.myp-log-item{display:flex;align-items:center;justify-content:space-between;padding:10px 0;border-bottom:1px solid var(--g-border);}
.myp-log-item:last-child{border-bottom:none;}
.myp-log-time{font-size:13px;color:var(--g-text);display:flex;align-items:center;gap:6px;}
.myp-log-meta{font-size:12px;color:var(--g-text-muted);display:flex;align-items:center;gap:12px;}
</style>
<div class="myp-wrap">
<!-- 头像区 -->
<div class="myp-avatar-area">
<div class="myp-avatar"></div>
<button class="myp-change-avatar">更换头像</button>
<div class="myp-name">张伟</div>
<span class="g-tag g-tag-green myp-role">管理员</span>
</div>
<!-- 基本信息 -->
<div class="g-card" style="margin-bottom:20px;">
<div class="myp-section-hd">基本信息</div>
<div class="myp-form-grid">
<div class="g-form-group">
<label class="g-form-label required">姓名</label>
<input class="g-input" value="张伟" placeholder="请输入姓名">
</div>
<div class="g-form-group">
<label class="g-form-label required">手机号</label>
<input class="g-input" value="138****8001" placeholder="请输入手机号">
</div>
<div class="g-form-group">
<label class="g-form-label">邮箱</label>
<input class="g-input" value="zhangwei@example.com" placeholder="请输入邮箱地址">
</div>
<div class="g-form-group">
<label class="g-form-label">所属门店</label>
<input class="g-input" value="全部门店" disabled placeholder="所属门店">
</div>
</div>
<div class="myp-card-ft">
<button class="g-btn g-btn-primary">保存修改</button>
</div>
</div>
<!-- 安全设置 -->
<div class="g-card" style="margin-bottom:20px;">
<div class="myp-section-hd">安全设置</div>
<div class="myp-security-row">
<div class="myp-security-left">
<span class="myp-security-title"><i data-lucide="lock" style="width:14px;height:14px;display:inline-block;vertical-align:-2px;margin-right:4px;"></i>登录密码</span>
<span class="myp-security-desc">已设置,可修改</span>
</div>
<button class="g-btn g-btn-sm">修改</button>
</div>
<div class="g-divider"></div>
<div class="myp-security-row">
<div class="myp-security-left">
<span class="myp-security-title"><i data-lucide="smartphone" style="width:14px;height:14px;display:inline-block;vertical-align:-2px;margin-right:4px;"></i>绑定手机</span>
<span class="myp-security-desc">138****8001</span>
</div>
<button class="g-btn g-btn-sm">更换</button>
</div>
<div class="g-divider"></div>
<div class="myp-security-row">
<div class="myp-security-left">
<span class="myp-security-title"><i data-lucide="shield-check" style="width:14px;height:14px;display:inline-block;vertical-align:-2px;margin-right:4px;"></i>两步验证</span>
<span class="myp-security-desc">未开启</span>
</div>
<div class="g-toggle" onclick="toggleSwitch(this)"></div>
</div>
</div>
<!-- 最近登录 -->
<div class="g-card">
<div class="myp-section-hd">最近登录</div>
<div class="myp-log-item">
<span class="myp-log-time"><i data-lucide="monitor" style="width:14px;height:14px;"></i>2026-02-13 09:32</span>
<span class="myp-log-meta"><span>192.168.1.105</span><span>Chrome / Windows</span></span>
</div>
<div class="myp-log-item">
<span class="myp-log-time"><i data-lucide="smartphone" style="width:14px;height:14px;"></i>2026-02-12 18:15</span>
<span class="myp-log-meta"><span>10.0.0.88</span><span>微信 / iPhone</span></span>
</div>
<div class="myp-log-item">
<span class="myp-log-time"><i data-lucide="monitor" style="width:14px;height:14px;"></i>2026-02-11 14:07</span>
<span class="myp-log-meta"><span>172.16.0.23</span><span>Chrome / MacOS</span></span>
</div>
<div class="myp-log-item">
<span class="myp-log-time"><i data-lucide="tablet" style="width:14px;height:14px;"></i>2026-02-10 20:44</span>
<span class="myp-log-meta"><span>192.168.0.51</span><span>Safari / iPad</span></span>
</div>
</div>
</div>
<script>
function toggleSwitch(el){el.classList.toggle('on');}
if(typeof lucide!=='undefined'){lucide.createIcons();}
</script>

662
pages/order-board.html Normal file
View File

@@ -0,0 +1,662 @@
<!-- 订单大厅 - 实时看板 -->
<style>
/* ===== Page Layout ===== */
.ob-page {
display: flex;
flex-direction: column;
gap: 12px;
font-size: 13px;
color: var(--g-text);
height: calc(100vh - var(--header-height) - var(--tabbar-height) - 32px);
}
/* ===== Toolbar ===== */
.ob-toolbar {
background: #fff;
border-radius: 10px;
box-shadow: var(--g-shadow-sm);
padding: 14px 20px;
display: flex;
align-items: center;
gap: 12px;
flex-shrink: 0;
}
.ob-toolbar-right {
margin-left: auto;
display: flex;
align-items: center;
gap: 8px;
}
.ob-icon-btn {
width: 32px;
height: 32px;
border-radius: var(--g-radius-sm);
border: 1px solid var(--g-border-hover);
background: #fff;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
color: var(--g-text-secondary);
transition: all var(--g-transition);
}
.ob-icon-btn:hover {
border-color: var(--primary);
color: var(--primary);
}
.ob-icon-btn.active {
background: var(--primary);
border-color: var(--primary);
color: #fff;
}
/* ===== Channel Filter Pills ===== */
.ob-channels {
display: flex;
gap: 6px;
}
.ob-channel {
height: 30px;
padding: 0 14px;
border-radius: 15px;
border: 1px solid var(--g-border-hover);
background: #fff;
font-size: 12px;
color: var(--g-text-secondary);
cursor: pointer;
transition: all var(--g-transition);
display: inline-flex;
align-items: center;
white-space: nowrap;
}
.ob-channel:hover {
border-color: var(--primary);
color: var(--primary);
}
.ob-channel.active {
background: color-mix(in srgb, var(--primary) 10%, transparent);
border-color: var(--primary);
color: var(--primary);
font-weight: 500;
}
/* ===== Stats Row ===== */
.ob-stats {
background: #fff;
border-radius: 10px;
box-shadow: var(--g-shadow-sm);
padding: 12px 20px;
display: flex;
align-items: center;
gap: 0;
flex-shrink: 0;
}
.ob-stat-item {
display: flex;
align-items: center;
gap: 6px;
padding: 0 20px;
border-right: 1px solid var(--g-border);
}
.ob-stat-item:first-child { padding-left: 0; }
.ob-stat-item:last-child { border-right: none; }
.ob-stat-label {
font-size: 12px;
color: var(--g-text-muted);
}
.ob-stat-value {
font-size: 20px;
font-weight: 700;
color: var(--g-text);
}
.ob-stat-item.highlight .ob-stat-value {
color: var(--primary);
}
/* ===== Kanban Board ===== */
.ob-board {
display: flex;
gap: 16px;
flex: 1;
overflow-x: auto;
min-height: 0;
padding-bottom: 4px;
}
.ob-board::-webkit-scrollbar { height: 6px; }
.ob-board::-webkit-scrollbar-thumb { background: #d9d9d9; border-radius: 3px; }
/* ===== Column ===== */
.ob-column {
min-width: 280px;
flex: 1;
display: flex;
flex-direction: column;
background: #f8f9fb;
border-radius: 10px;
overflow: hidden;
}
.ob-col-hd {
display: flex;
align-items: center;
gap: 8px;
padding: 14px 16px;
font-size: 14px;
font-weight: 600;
color: var(--g-text);
flex-shrink: 0;
border-left: 3px solid transparent;
}
.ob-col-hd.pending { border-left-color: #fa8c16; }
.ob-col-hd.making { border-left-color: var(--primary); }
.ob-col-hd.delivering { border-left-color: #52c41a; }
.ob-col-hd.done { border-left-color: #d9d9d9; }
.ob-col-count {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 20px;
height: 20px;
padding: 0 6px;
border-radius: 10px;
font-size: 11px;
font-weight: 600;
color: #fff;
}
.ob-col-hd.pending .ob-col-count { background: #fa8c16; }
.ob-col-hd.making .ob-col-count { background: var(--primary); }
.ob-col-hd.delivering .ob-col-count { background: #52c41a; }
.ob-col-hd.done .ob-col-count { background: #d9d9d9; color: #666; }
.ob-col-body {
flex: 1;
overflow-y: auto;
padding: 0 10px 10px;
display: flex;
flex-direction: column;
gap: 10px;
}
.ob-col-body::-webkit-scrollbar { width: 4px; }
.ob-col-body::-webkit-scrollbar-thumb { background: #d9d9d9; border-radius: 2px; }
/* ===== Order Card ===== */
.ob-order {
background: #fff;
border-radius: 10px;
box-shadow: var(--g-shadow-sm);
padding: 14px;
border-left: 3px solid transparent;
transition: box-shadow var(--g-transition);
cursor: default;
}
.ob-order:hover {
box-shadow: var(--g-shadow-md);
}
.ob-order.border-orange { border-left-color: #fa8c16; }
.ob-order.border-blue { border-left-color: var(--primary); }
.ob-order.border-green { border-left-color: #52c41a; }
.ob-order.border-gray { border-left-color: #d9d9d9; }
.ob-order.dimmed { opacity: 0.6; }
/* Pulse animation for new orders */
@keyframes ob-pulse-border {
0%, 100% { border-left-color: #fa8c16; }
50% { border-left-color: #ffd591; }
}
.ob-order.pulse {
animation: ob-pulse-border 2s ease-in-out infinite;
}
/* ===== Order Header ===== */
.ob-order-hd {
display: flex;
align-items: center;
gap: 6px;
margin-bottom: 10px;
}
.ob-order-no {
font-size: 13px;
font-weight: 600;
color: var(--g-text);
}
.ob-order-time {
margin-left: auto;
font-size: 11px;
color: var(--g-text-muted);
}
/* ===== Order Info ===== */
.ob-order-info {
display: flex;
flex-direction: column;
gap: 6px;
margin-bottom: 12px;
font-size: 12px;
color: var(--g-text-secondary);
}
.ob-order-info-row {
display: flex;
align-items: center;
gap: 4px;
}
.ob-order-info-row i {
width: 14px;
height: 14px;
color: var(--g-text-muted);
flex-shrink: 0;
}
.ob-order-items {
color: var(--g-text);
line-height: 1.5;
}
.ob-order-total {
font-size: 14px;
font-weight: 600;
display: flex;
align-items: baseline;
gap: 4px;
}
.ob-order-total-label {
font-size: 12px;
font-weight: 400;
color: var(--g-text-muted);
}
.ob-tag-reserve {
display: inline-flex;
align-items: center;
gap: 3px;
padding: 2px 8px;
border-radius: 4px;
font-size: 11px;
font-weight: 600;
background: #fff7e6;
color: #d46b08;
border: 1px solid #ffe58f;
}
.ob-tag-urge {
display: inline-flex;
align-items: center;
gap: 3px;
padding: 2px 8px;
border-radius: 4px;
font-size: 11px;
font-weight: 600;
background: #fff1f0;
color: var(--g-danger);
border: 1px solid #ffa39e;
animation: ob-urge-blink 1.2s ease-in-out infinite;
}
@keyframes ob-urge-blink {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
.ob-order-status-line {
font-size: 12px;
color: var(--primary);
font-weight: 500;
}
.ob-order-status-line.green {
color: #52c41a;
}
.ob-order-complete-time {
font-size: 12px;
color: var(--g-text-muted);
}
.ob-order-rider, .ob-order-pickup, .ob-order-table {
font-size: 12px;
color: var(--g-text-secondary);
display: flex;
align-items: center;
gap: 4px;
}
/* ===== Order Footer ===== */
.ob-order-ft {
display: flex;
align-items: center;
gap: 8px;
padding-top: 10px;
border-top: 1px solid #f5f5f5;
}
.ob-order-ft .g-btn { flex: 1; }
</style>
<div class="ob-page">
<!-- Toolbar -->
<div class="ob-toolbar">
<select class="g-select" style="width:200px;">
<option>万达广场店</option>
<option>银泰城店</option>
<option>大学城店</option>
<option>火车站店</option>
<option>科技园店</option>
</select>
<div class="ob-channels">
<span class="ob-channel active" onclick="filterObChannel(this)">全部</span>
<span class="ob-channel" onclick="filterObChannel(this)">外卖</span>
<span class="ob-channel" onclick="filterObChannel(this)">自提</span>
<span class="ob-channel" onclick="filterObChannel(this)">堂食</span>
</div>
<div class="ob-toolbar-right">
<button class="ob-icon-btn active" title="提示音" onclick="this.classList.toggle('active')">
<i data-lucide="volume-2" style="width:16px;height:16px;"></i>
</button>
<button class="ob-icon-btn" title="刷新" onclick="refreshBoard()">
<i data-lucide="refresh-cw" style="width:16px;height:16px;"></i>
</button>
</div>
</div>
<!-- Stats -->
<div class="ob-stats">
<div class="ob-stat-item highlight">
<span class="ob-stat-label">今日订单</span>
<span class="ob-stat-value">128</span>
</div>
<div class="ob-stat-item">
<span class="ob-stat-label">待接单</span>
<span class="ob-stat-value" style="color:#fa8c16;">5</span>
</div>
<div class="ob-stat-item">
<span class="ob-stat-label">制作中</span>
<span class="ob-stat-value" style="color:var(--primary);">8</span>
</div>
<div class="ob-stat-item">
<span class="ob-stat-label">配送中</span>
<span class="ob-stat-value" style="color:#52c41a;">12</span>
</div>
<div class="ob-stat-item">
<span class="ob-stat-label">已完成</span>
<span class="ob-stat-value">103</span>
</div>
</div>
<!-- Kanban Board -->
<div class="ob-board">
<!-- Column: 待接单 -->
<div class="ob-column">
<div class="ob-col-hd pending">
<span>待接单</span>
<span class="ob-col-count">3</span>
</div>
<div class="ob-col-body">
<!-- Order 1 -->
<div class="ob-order border-orange pulse">
<div class="ob-order-hd">
<span class="ob-order-no">#20250212001</span>
<span class="g-tag g-tag-blue">外卖</span>
<span class="ob-order-time">3分钟前</span>
</div>
<div class="ob-order-info">
<div class="ob-order-info-row">
<i data-lucide="user"></i>
<span>张** 138****6789</span>
</div>
<div class="ob-order-items">宫保鸡丁x1, 米饭x1 等3件</div>
<div class="ob-order-total"><span class="ob-order-total-label">实付</span>&yen;68.00</div>
</div>
<div class="ob-order-ft">
<button class="g-btn g-btn-primary g-btn-sm">接单</button>
<button class="g-btn g-btn-sm">拒单</button>
</div>
</div>
<!-- Order 2 -->
<div class="ob-order border-orange pulse">
<div class="ob-order-hd">
<span class="ob-order-no">#20250212002</span>
<span class="g-tag g-tag-green">自提</span>
<span class="ob-tag-reserve"><i data-lucide="clock" style="width:11px;height:11px;"></i>预约 18:00</span>
<span class="ob-order-time">5分钟前</span>
</div>
<div class="ob-order-info">
<div class="ob-order-info-row">
<i data-lucide="user"></i>
<span>李** 159****3210</span>
</div>
<div class="ob-order-items">麻辣香锅(大份)x1, 可乐x2</div>
<div class="ob-order-total"><span class="ob-order-total-label">实付</span>&yen;95.50</div>
</div>
<div class="ob-order-ft">
<button class="g-btn g-btn-primary g-btn-sm">接单</button>
<button class="g-btn g-btn-sm">拒单</button>
</div>
</div>
<!-- Order 3 -->
<div class="ob-order border-orange pulse">
<div class="ob-order-hd">
<span class="ob-order-no">#20250212003</span>
<span class="g-tag g-tag-orange">堂食</span>
<span class="ob-order-time">1分钟前</span>
</div>
<div class="ob-order-info">
<div class="ob-order-info-row">
<i data-lucide="user"></i>
<span>王** (桌号 B5)</span>
</div>
<div class="ob-order-items">鱼香肉丝x1, 蛋炒饭x2, 紫菜蛋花汤x1</div>
<div class="ob-order-total"><span class="ob-order-total-label">实付</span>&yen;52.00</div>
</div>
<div class="ob-order-ft">
<button class="g-btn g-btn-primary g-btn-sm">接单</button>
<button class="g-btn g-btn-sm">拒单</button>
</div>
</div>
</div>
</div>
<!-- Column: 制作中 -->
<div class="ob-column">
<div class="ob-col-hd making">
<span>制作中</span>
<span class="ob-col-count">3</span>
</div>
<div class="ob-col-body">
<!-- Order 4 -->
<div class="ob-order border-blue">
<div class="ob-order-hd">
<span class="ob-order-no">#20250212004</span>
<span class="g-tag g-tag-blue">外卖</span>
<span class="ob-order-time">12:08 接单</span>
</div>
<div class="ob-order-info">
<div class="ob-order-status-line">已接单 5分钟</div>
<div class="ob-order-info-row">
<i data-lucide="user"></i>
<span>赵** 136****8877</span>
</div>
<div class="ob-order-items">红烧牛肉面x2, 凉拌黄瓜x1</div>
<div class="ob-order-total"><span class="ob-order-total-label">实付</span>&yen;76.00</div>
</div>
<div class="ob-order-ft">
<button class="g-btn g-btn-primary g-btn-sm">出餐完成</button>
</div>
</div>
<!-- Order 5 -->
<div class="ob-order border-blue">
<div class="ob-order-hd">
<span class="ob-order-no">#20250212005</span>
<span class="g-tag g-tag-green">自提</span>
<span class="ob-order-time">12:02 接单</span>
</div>
<div class="ob-order-info">
<div class="ob-order-status-line">已接单 11分钟</div>
<span class="ob-tag-urge"><i data-lucide="alert-circle" style="width:11px;height:11px;"></i>顾客催单</span>
<div class="ob-order-info-row">
<i data-lucide="user"></i>
<span>孙** 177****4455</span>
</div>
<div class="ob-order-items">烤鸭套餐x1, 酸梅汤x2 等5件</div>
<div class="ob-order-total"><span class="ob-order-total-label">实付</span>&yen;138.00</div>
</div>
<div class="ob-order-ft">
<button class="g-btn g-btn-primary g-btn-sm">出餐完成</button>
</div>
</div>
<!-- Order 6 -->
<div class="ob-order border-blue">
<div class="ob-order-hd">
<span class="ob-order-no">#20250212006</span>
<span class="g-tag g-tag-orange">堂食</span>
<span class="ob-order-time">12:10 接单</span>
</div>
<div class="ob-order-info">
<div class="ob-order-status-line">已接单 3分钟</div>
<div class="ob-order-info-row">
<i data-lucide="user"></i>
<span>周** (桌号 A1)</span>
</div>
<div class="ob-order-items">水煮鱼x1, 米饭x3</div>
<div class="ob-order-total"><span class="ob-order-total-label">实付</span>&yen;89.00</div>
</div>
<div class="ob-order-ft">
<button class="g-btn g-btn-primary g-btn-sm">出餐完成</button>
</div>
</div>
</div>
</div>
<!-- Column: 配送/取餐中 -->
<div class="ob-column">
<div class="ob-col-hd delivering">
<span>配送/取餐中</span>
<span class="ob-col-count">2</span>
</div>
<div class="ob-col-body">
<!-- Order 7 - 外卖配送 -->
<div class="ob-order border-green">
<div class="ob-order-hd">
<span class="ob-order-no">#20250212007</span>
<span class="g-tag g-tag-blue">外卖</span>
<span class="ob-order-time">11:50 出餐</span>
</div>
<div class="ob-order-info">
<div class="ob-order-status-line green">配送中</div>
<div class="ob-order-rider">
<i data-lucide="bike" style="width:14px;height:14px;"></i>
<span>骑手: 李师傅 138****5678</span>
</div>
<div class="ob-order-info-row">
<i data-lucide="user"></i>
<span>陈** 150****9900</span>
</div>
<div class="ob-order-items">黄焖鸡米饭x1, 加辣 等2件</div>
<div class="ob-order-total"><span class="ob-order-total-label">实付</span>&yen;35.00</div>
</div>
<div class="ob-order-ft">
<button class="g-btn g-btn-primary g-btn-sm">确认送达</button>
</div>
</div>
<!-- Order 8 - 自提 -->
<div class="ob-order border-green">
<div class="ob-order-hd">
<span class="ob-order-no">#20250212008</span>
<span class="g-tag g-tag-green">自提</span>
<span class="ob-order-time">11:55 出餐</span>
</div>
<div class="ob-order-info">
<div class="ob-order-status-line green">等待取餐</div>
<div class="ob-order-pickup">
<i data-lucide="hash" style="width:14px;height:14px;"></i>
<span>取餐码: <strong style="font-size:16px;color:var(--g-text);">A088</strong></span>
</div>
<div class="ob-order-info-row">
<i data-lucide="user"></i>
<span>吴** 133****2211</span>
</div>
<div class="ob-order-items">炸鸡桶x1, 薯条x2, 可乐x2</div>
<div class="ob-order-total"><span class="ob-order-total-label">实付</span>&yen;79.00</div>
</div>
<div class="ob-order-ft">
<button class="g-btn g-btn-primary g-btn-sm">确认取餐</button>
</div>
</div>
</div>
</div>
<!-- Column: 已完成 -->
<div class="ob-column">
<div class="ob-col-hd done">
<span>已完成</span>
<span class="ob-col-count">103</span>
</div>
<div class="ob-col-body">
<!-- Order 9 -->
<div class="ob-order border-gray dimmed">
<div class="ob-order-hd">
<span class="ob-order-no">#20250212009</span>
<span class="g-tag g-tag-blue">外卖</span>
<span class="ob-order-time">12:35 完成</span>
</div>
<div class="ob-order-info">
<div class="ob-order-info-row">
<i data-lucide="user"></i>
<span>刘** 186****7766</span>
</div>
<div class="ob-order-items">麻婆豆腐x1, 米饭x1</div>
<div class="ob-order-total"><span class="ob-order-total-label">实付</span>&yen;32.00</div>
</div>
<div class="ob-order-ft">
<a class="g-action">查看详情</a>
</div>
</div>
<!-- Order 10 -->
<div class="ob-order border-gray dimmed">
<div class="ob-order-hd">
<span class="ob-order-no">#20250212010</span>
<span class="g-tag g-tag-orange">堂食</span>
<span class="ob-order-time">12:20 完成</span>
</div>
<div class="ob-order-info">
<div class="ob-order-info-row">
<i data-lucide="user"></i>
<span>杨** (桌号 A3)</span>
</div>
<div class="ob-order-items">剁椒鱼头x1, 蒜蓉西兰花x1, 米饭x2</div>
<div class="ob-order-total"><span class="ob-order-total-label">实付</span>&yen;118.00</div>
</div>
<div class="ob-order-ft">
<a class="g-action">查看详情</a>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
/* 渠道筛选切换 */
function filterObChannel(el) {
el.parentElement.querySelectorAll('.ob-channel').forEach(function(c) {
c.classList.remove('active');
});
el.classList.add('active');
}
/* 刷新看板(原型占位) */
function refreshBoard() {
// 原型演示,无真实刷新逻辑
}
/* 初始化 Lucide 图标 */
if (typeof lucide !== 'undefined') {
lucide.createIcons();
}
</script>

442
pages/order-list.html Normal file
View File

@@ -0,0 +1,442 @@
<!-- 全部订单 — order-list.html -->
<style>
/* ---- page-private: ol- prefix ---- */
.ol-toolbar {
background: #fff; border-radius: var(--g-radius); padding: 16px 20px;
display: flex; flex-wrap: wrap; gap: 10px; align-items: center;
box-shadow: var(--g-shadow-sm); border: 1px solid var(--g-border);
}
.ol-toolbar .g-select,
.ol-toolbar .g-input { height: 32px; font-size: 13px; }
.ol-search-wrap {
position: relative; width: 200px;
}
.ol-search-wrap .g-input { padding-left: 30px; width: 100%; }
.ol-search-wrap i {
position: absolute; left: 8px; top: 50%; transform: translateY(-50%);
color: var(--g-text-muted); pointer-events: none;
}
.ol-toolbar-right { margin-left: auto; }
.ol-date-sep { color: var(--g-text-muted); font-size: 13px; line-height: 32px; }
/* stats row */
.ol-stats {
display: flex; gap: 24px; padding: 12px 0 4px; font-size: 13px; color: var(--g-text-secondary);
}
.ol-stats span { white-space: nowrap; }
.ol-stats strong { color: var(--g-text); font-weight: 600; margin-left: 4px; }
/* table card */
.ol-table-card {
background: #fff; border-radius: var(--g-radius); border: 1px solid var(--g-border);
margin-top: 12px; overflow: hidden;
}
.ol-table-card .g-table th { white-space: nowrap; }
.ol-table-card .g-table td { vertical-align: middle; }
/* dimmed row for cancelled / refunded */
.ol-row-dim td { opacity: .55; }
.ol-row-dim:hover td { opacity: .75; }
/* items cell */
.ol-items { max-width: 180px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* amount */
.ol-amount { font-weight: 600; white-space: nowrap; }
/* drawer */
.ol-drawer { width: 560px; }
/* drawer sections */
.ol-section { margin-bottom: 22px; }
.ol-section-hd {
font-size: 14px; font-weight: 600; color: var(--g-text);
padding-left: 10px; border-left: 3px solid var(--primary); margin-bottom: 14px;
}
.ol-info-grid {
display: grid; grid-template-columns: 1fr 1fr; gap: 10px 20px; font-size: 13px;
}
.ol-info-label { color: var(--g-text-muted); }
.ol-info-val { color: var(--g-text); }
.ol-info-full { grid-column: 1 / -1; }
/* mini product table */
.ol-prod-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.ol-prod-table th {
text-align: left; padding: 8px 10px; font-size: 12px; font-weight: 500;
color: var(--g-text-muted); background: var(--g-bg-subtle); border-bottom: 1px solid var(--g-border);
}
.ol-prod-table td { padding: 8px 10px; border-bottom: 1px solid #f5f5f5; color: var(--g-text); }
.ol-prod-table .ol-sum-row td {
font-weight: 600; border-bottom: none; padding-top: 10px;
}
/* timeline */
.ol-timeline { position: relative; padding-left: 22px; }
.ol-tl-item {
position: relative; padding-bottom: 18px; font-size: 13px;
}
.ol-tl-item:last-child { padding-bottom: 0; }
.ol-tl-item::before {
content: ''; position: absolute; left: -22px; top: 5px;
width: 10px; height: 10px; border-radius: 50%;
background: var(--primary); border: 2px solid var(--primary-light);
}
.ol-tl-item:not(:last-child)::after {
content: ''; position: absolute; left: -18px; top: 17px;
width: 2px; height: calc(100% - 12px); background: #e8e8e8;
}
.ol-tl-label { color: var(--g-text); font-weight: 500; }
.ol-tl-time { color: var(--g-text-muted); margin-left: 8px; }
/* remark */
.ol-remark {
background: var(--g-bg-subtle); border-radius: var(--g-radius-sm);
padding: 10px 14px; font-size: 13px; color: var(--g-text-secondary);
border: 1px solid var(--g-border);
}
</style>
<!-- 顶部筛选栏 -->
<div class="ol-toolbar">
<select class="g-select" style="width:200px;">
<option>全部门店</option>
<option>老三家外卖(朝阳店)</option>
<option>老三家外卖(海淀店)</option>
<option>老三家外卖(望京店)</option>
<option>老三家外卖(通州店)</option>
<option>老三家外卖(丰台店)</option>
</select>
<input type="date" class="g-input" style="width:145px;" value="2025-02-12">
<span class="ol-date-sep">~</span>
<input type="date" class="g-input" style="width:145px;" value="2025-02-12">
<select class="g-select" style="width:120px;" onchange="filterOlStatus()">
<option>全部状态</option>
<option>待接单</option>
<option>制作中</option>
<option>配送中</option>
<option>待取餐</option>
<option>已完成</option>
<option>已取消</option>
<option>已退款</option>
</select>
<select class="g-select" style="width:110px;">
<option>全部渠道</option>
<option>外卖</option>
<option>自提</option>
<option>堂食</option>
</select>
<select class="g-select" style="width:120px;">
<option>全部支付</option>
<option>微信支付</option>
<option>支付宝</option>
<option>余额支付</option>
</select>
<div class="ol-search-wrap">
<i data-lucide="search" style="width:14px;height:14px;"></i>
<input class="g-input" placeholder="订单号/手机号" oninput="searchOlOrders(this.value)">
</div>
<div class="ol-toolbar-right">
<button class="g-btn" onclick="exportOrders()">
<i data-lucide="download" style="width:14px;height:14px;"></i> 导出
</button>
</div>
</div>
<!-- 统计行 -->
<div class="ol-stats">
<span>筛选结果<strong>128 单</strong></span>
<span>总金额<strong>&yen;12,680.00</strong></span>
<span>平均客单价<strong>&yen;99.06</strong></span>
<span>退款<strong style="color:var(--g-danger);">3 单</strong></span>
</div>
<!-- 表格 -->
<div class="ol-table-card">
<table class="g-table">
<thead>
<tr>
<th>订单号</th>
<th>下单时间</th>
<th>渠道</th>
<th>顾客</th>
<th>商品</th>
<th>金额</th>
<th>状态</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr>
<td style="font-family:monospace;">#20250212001</td>
<td>2025-02-12 12:00:15</td>
<td><span class="g-tag g-tag-blue">外卖</span></td>
<td>张**</td>
<td class="ol-items">宫保鸡丁等3件</td>
<td class="ol-amount">&yen;89.00</td>
<td><span class="g-tag g-tag-green">已完成</span></td>
<td><a class="g-action" onclick="openOlDrawer('#20250212001')">详情</a></td>
</tr>
<tr>
<td style="font-family:monospace;">#20250212002</td>
<td>2025-02-12 12:05:30</td>
<td><span class="g-tag g-tag-green">自提</span></td>
<td>李**</td>
<td class="ol-items">双人超值套餐等2件</td>
<td class="ol-amount">&yen;128.00</td>
<td><span class="g-tag g-tag-green">已完成</span></td>
<td><a class="g-action" onclick="openOlDrawer('#20250212002')">详情</a></td>
</tr>
<tr>
<td style="font-family:monospace;">#20250212003</td>
<td>2025-02-12 12:12:08</td>
<td><span class="g-tag g-tag-blue">外卖</span></td>
<td>王**</td>
<td class="ol-items">鱼香肉丝盖饭等1件</td>
<td class="ol-amount">&yen;35.50</td>
<td><span class="g-tag g-tag-blue">制作中</span></td>
<td><a class="g-action" onclick="openOlDrawer('#20250212003')">详情</a></td>
</tr>
<tr>
<td style="font-family:monospace;">#20250212004</td>
<td>2025-02-12 12:18:45</td>
<td><span class="g-tag g-tag-orange">堂食</span></td>
<td>赵**</td>
<td class="ol-items">招牌红烧肉饭等4件</td>
<td class="ol-amount">&yen;168.00</td>
<td><span class="g-tag g-tag-green">已完成</span></td>
<td><a class="g-action" onclick="openOlDrawer('#20250212004')">详情</a></td>
</tr>
<tr>
<td style="font-family:monospace;">#20250212005</td>
<td>2025-02-12 12:25:10</td>
<td><span class="g-tag g-tag-blue">外卖</span></td>
<td>刘**</td>
<td class="ol-items">番茄牛腩面等2件</td>
<td class="ol-amount">&yen;56.00</td>
<td><span class="g-tag g-tag-blue">配送中</span></td>
<td><a class="g-action" onclick="openOlDrawer('#20250212005')">详情</a></td>
</tr>
<tr class="ol-row-dim">
<td style="font-family:monospace;">#20250212006</td>
<td>2025-02-12 12:30:22</td>
<td><span class="g-tag g-tag-blue">外卖</span></td>
<td>陈**</td>
<td class="ol-items">香辣鸡腿堡套餐等1件</td>
<td class="ol-amount">&yen;45.00</td>
<td><span class="g-tag g-tag-gray">已取消</span></td>
<td><a class="g-action" onclick="openOlDrawer('#20250212006')">详情</a></td>
</tr>
<tr>
<td style="font-family:monospace;">#20250212007</td>
<td>2025-02-12 12:35:50</td>
<td><span class="g-tag g-tag-green">自提</span></td>
<td>孙**</td>
<td class="ol-items">麻婆豆腐等3件</td>
<td class="ol-amount">&yen;72.00</td>
<td><span class="g-tag g-tag-green">已完成</span></td>
<td><a class="g-action" onclick="openOlDrawer('#20250212007')">详情</a></td>
</tr>
<tr class="ol-row-dim">
<td style="font-family:monospace;">#20250212008</td>
<td>2025-02-12 12:40:18</td>
<td><span class="g-tag g-tag-blue">外卖</span></td>
<td>周**</td>
<td class="ol-items">酸辣土豆丝等2件</td>
<td class="ol-amount">&yen;42.00</td>
<td><span class="g-tag g-tag-red">已退款</span></td>
<td><a class="g-action" onclick="openOlDrawer('#20250212008')">详情</a></td>
</tr>
<tr>
<td style="font-family:monospace;">#20250212009</td>
<td>2025-02-12 12:48:33</td>
<td><span class="g-tag g-tag-orange">堂食</span></td>
<td>吴**</td>
<td class="ol-items">皮蛋豆腐等5件</td>
<td class="ol-amount">&yen;156.00</td>
<td><span class="g-tag g-tag-green">已完成</span></td>
<td><a class="g-action" onclick="openOlDrawer('#20250212009')">详情</a></td>
</tr>
<tr>
<td style="font-family:monospace;">#20250212010</td>
<td>2025-02-12 12:55:02</td>
<td><span class="g-tag g-tag-blue">外卖</span></td>
<td>郑**</td>
<td class="ol-items">蛋炒饭等2件</td>
<td class="ol-amount">&yen;38.00</td>
<td><span class="g-tag g-tag-blue">制作中</span></td>
<td><a class="g-action" onclick="openOlDrawer('#20250212010')">详情</a></td>
</tr>
</tbody>
</table>
</div>
<!-- 分页 -->
<div class="g-pagination">
<span>共 128 条</span>
<button class="g-page-btn" disabled>&lt;</button>
<button class="g-page-btn active">1</button>
<button class="g-page-btn">2</button>
<button class="g-page-btn">3</button>
<button class="g-page-btn">4</button>
<button class="g-page-btn">5</button>
<button class="g-page-btn">&gt;</button>
</div>
<!-- 订单详情抽屉 -->
<div class="g-drawer-mask" id="olDrawerMask" onclick="closeOlDrawer()"></div>
<div class="g-drawer ol-drawer" id="olDrawer">
<div class="g-drawer-hd">
<span class="g-drawer-title" id="olDrawerTitle">订单详情 #20250212001</span>
<button class="g-drawer-close" onclick="closeOlDrawer()"><i data-lucide="x" style="width:18px;height:18px;"></i></button>
</div>
<div class="g-drawer-bd">
<!-- 基本信息 -->
<div class="ol-section">
<div class="ol-section-hd">基本信息</div>
<div class="ol-info-grid">
<div><span class="ol-info-label">订单号:</span><span class="ol-info-val" style="font-family:monospace;">#20250212001</span></div>
<div><span class="ol-info-label">渠道:</span><span class="g-tag g-tag-blue">外卖</span></div>
<div><span class="ol-info-label">状态:</span><span class="g-tag g-tag-green">已完成</span></div>
<div><span class="ol-info-label">支付方式:</span><span class="ol-info-val">微信支付</span></div>
<div><span class="ol-info-label">下单时间:</span><span class="ol-info-val">2025-02-12 12:00:00</span></div>
<div><span class="ol-info-label">支付时间:</span><span class="ol-info-val">2025-02-12 12:00:15</span></div>
<div><span class="ol-info-label">完成时间:</span><span class="ol-info-val">2025-02-12 12:35:00</span></div>
</div>
</div>
<!-- 顾客信息 -->
<div class="ol-section">
<div class="ol-section-hd">顾客信息</div>
<div class="ol-info-grid">
<div><span class="ol-info-label">姓名:</span><span class="ol-info-val">张**</span></div>
<div><span class="ol-info-label">手机号:</span><span class="ol-info-val">138****6789</span></div>
<div class="ol-info-full"><span class="ol-info-label">收货地址:</span><span class="ol-info-val">北京市朝阳区建国路88号院3号楼1单元502</span></div>
</div>
</div>
<!-- 商品明细 -->
<div class="ol-section">
<div class="ol-section-hd">商品明细</div>
<table class="ol-prod-table">
<thead>
<tr>
<th>商品</th>
<th>规格</th>
<th>数量</th>
<th>单价</th>
<th style="text-align:right;">小计</th>
</tr>
</thead>
<tbody>
<tr>
<td>宫保鸡丁</td>
<td>大份</td>
<td>1</td>
<td>&yen;38.00</td>
<td style="text-align:right;">&yen;38.00</td>
</tr>
<tr>
<td>酸辣土豆丝</td>
<td>标准</td>
<td>1</td>
<td>&yen;14.00</td>
<td style="text-align:right;">&yen;14.00</td>
</tr>
<tr>
<td>米饭</td>
<td>标准</td>
<td>2</td>
<td>&yen;3.00</td>
<td style="text-align:right;">&yen;6.00</td>
</tr>
</tbody>
</table>
<div style="font-size:13px; padding:12px 10px 0; display:flex; flex-direction:column; gap:6px;">
<div style="display:flex; justify-content:space-between; color:var(--g-text-secondary);">
<span>商品合计</span><span>&yen;58.00</span>
</div>
<div style="display:flex; justify-content:space-between; color:var(--g-text-secondary);">
<span>配送费</span><span>&yen;5.00</span>
</div>
<div style="display:flex; justify-content:space-between; color:var(--g-text-secondary);">
<span>优惠减免</span><span style="color:var(--g-danger);">-&yen;8.00</span>
</div>
<div style="display:flex; justify-content:space-between; font-weight:600; color:var(--g-text); border-top:1px solid var(--g-border); padding-top:8px; margin-top:4px;">
<span>实付金额</span><span style="color:var(--primary); font-size:15px;">&yen;55.00</span>
</div>
</div>
</div>
<!-- 状态时间线 -->
<div class="ol-section">
<div class="ol-section-hd">状态时间线</div>
<div class="ol-timeline">
<div class="ol-tl-item">
<span class="ol-tl-label">下单</span>
<span class="ol-tl-time">12:00:00</span>
</div>
<div class="ol-tl-item">
<span class="ol-tl-label">支付成功</span>
<span class="ol-tl-time">12:00:15</span>
</div>
<div class="ol-tl-item">
<span class="ol-tl-label">商家接单</span>
<span class="ol-tl-time">12:01:30</span>
</div>
<div class="ol-tl-item">
<span class="ol-tl-label">开始制作</span>
<span class="ol-tl-time">12:01:30</span>
</div>
<div class="ol-tl-item">
<span class="ol-tl-label">出餐完成</span>
<span class="ol-tl-time">12:15:00</span>
</div>
<div class="ol-tl-item">
<span class="ol-tl-label">骑手取餐</span>
<span class="ol-tl-time">12:16:20</span>
</div>
<div class="ol-tl-item">
<span class="ol-tl-label">已送达</span>
<span class="ol-tl-time">12:35:00</span>
</div>
</div>
</div>
<!-- 备注 -->
<div class="ol-section">
<div class="ol-section-hd">备注</div>
<div class="ol-remark">少放辣,多加饭</div>
</div>
</div>
<div class="g-drawer-ft">
<button class="g-btn" onclick="closeOlDrawer()">关闭</button>
</div>
</div>
<script>
/* 抽屉开关 */
function openOlDrawer(orderNo) {
document.getElementById('olDrawerTitle').textContent = '订单详情 ' + orderNo;
document.getElementById('olDrawerMask').classList.add('open');
document.getElementById('olDrawer').classList.add('open');
if (typeof lucide !== 'undefined') lucide.createIcons();
}
function closeOlDrawer() {
document.getElementById('olDrawerMask').classList.remove('open');
document.getElementById('olDrawer').classList.remove('open');
}
/* 筛选占位 */
function filterOlStatus() {
/* 原型占位,无真实逻辑 */
}
function searchOlOrders(keyword) {
/* 原型占位,无真实逻辑 */
}
function exportOrders() {
alert('导出功能仅为原型演示');
}
</script>

402
pages/order-refund.html Normal file
View File

@@ -0,0 +1,402 @@
<!-- 退款售后页 -->
<style>
.orf-toolbar { display:flex; align-items:center; gap:12px; margin-bottom:16px; box-shadow:var(--g-shadow-sm); border-radius:10px; padding:12px 16px; background:#fff; flex-wrap:wrap; }
.orf-toolbar .g-select { width:200px; }
.orf-toolbar input[type="date"] { height:34px; padding:0 10px; border:1px solid #e5e7eb; border-radius:8px; font-size:13px; outline:none; background:#fff; transition:var(--g-transition); color:var(--g-text); }
.orf-toolbar input[type="date"]:focus { border-color:var(--primary); box-shadow:0 0 0 3px color-mix(in srgb, var(--primary) 12%, transparent); }
.orf-date-sep { color:var(--g-text-muted); font-size:13px; }
.orf-search { width:220px; }
/* 统计卡片 */
.orf-stats { display:grid; grid-template-columns:repeat(3,1fr); gap:16px; margin-bottom:16px; }
.orf-stat-card { background:#fff; border-radius:10px; padding:18px 20px; box-shadow:var(--g-shadow-sm); display:flex; align-items:flex-start; gap:14px; transition:box-shadow var(--g-transition); }
.orf-stat-card:hover { box-shadow:var(--g-shadow-md); }
.orf-stat-icon { width:42px; height:42px; border-radius:10px; display:flex; align-items:center; justify-content:center; flex-shrink:0; }
.orf-stat-icon.blue { background:color-mix(in srgb, var(--primary) 10%, #fff); color:var(--primary); }
.orf-stat-icon.orange { background:color-mix(in srgb, var(--g-warning) 10%, #fff); color:var(--g-warning); }
.orf-stat-icon.green { background:color-mix(in srgb, var(--g-success) 10%, #fff); color:var(--g-success); }
.orf-stat-body { flex:1; }
.orf-stat-label { font-size:12px; color:var(--g-text-muted); margin-bottom:6px; }
.orf-stat-value { font-size:22px; font-weight:700; color:var(--g-text); }
.orf-stat-trend { font-size:11px; margin-top:4px; display:flex; align-items:center; gap:3px; }
.orf-stat-trend.good { color:var(--g-success); }
.orf-stat-trend.bad { color:var(--g-danger); }
/* 表格区域 */
.orf-panel { background:#fff; border-radius:10px; box-shadow:var(--g-shadow-sm); padding:16px 18px; }
.orf-seg-wrap { margin-bottom:16px; }
.orf-table-wrap { overflow-x:auto; }
.orf-dimmed { opacity:0.55; }
/* 抽屉内信息块 */
.orf-info-section { margin-bottom:20px; }
.orf-info-title { font-size:14px; font-weight:600; color:var(--g-text); padding-left:10px; border-left:3px solid var(--primary); margin-bottom:12px; }
.orf-info-grid { display:grid; grid-template-columns:1fr 1fr; gap:8px 24px; }
.orf-info-item { display:flex; flex-direction:column; gap:2px; }
.orf-info-label { font-size:12px; color:var(--g-text-muted); }
.orf-info-value { font-size:13px; color:var(--g-text); font-weight:500; }
.orf-reason-text { font-size:13px; color:var(--g-text); line-height:1.6; margin-bottom:10px; }
.orf-photo-placeholder { width:80px; height:80px; background:#f8f9fb; border:1px dashed #e5e7eb; border-radius:8px; display:flex; align-items:center; justify-content:center; color:var(--g-text-muted); font-size:11px; text-align:center; line-height:1.3; }
.orf-product-row { display:flex; align-items:center; gap:12px; padding:10px 0; border-bottom:1px solid #f3f4f6; }
.orf-product-row:last-child { border-bottom:none; }
.orf-product-img { width:48px; height:48px; background:#f8f9fb; border-radius:8px; display:flex; align-items:center; justify-content:center; color:var(--g-text-muted); flex-shrink:0; }
.orf-product-info { flex:1; }
.orf-product-name { font-size:13px; font-weight:500; color:var(--g-text); }
.orf-product-spec { font-size:12px; color:var(--g-text-muted); margin-top:2px; }
.orf-product-price { font-size:13px; font-weight:600; color:var(--g-text); white-space:nowrap; }
/* 处理操作 */
.orf-action-pills { display:flex; gap:10px; margin-bottom:14px; }
.orf-action-pill { padding:8px 20px; border-radius:8px; font-size:13px; cursor:pointer; border:1px solid #e5e7eb; background:#fff; color:var(--g-text-secondary); transition:var(--g-transition); font-weight:500; }
.orf-action-pill:hover { border-color:var(--primary); color:var(--primary); }
.orf-action-pill.selected-approve { border-color:var(--g-success); background:color-mix(in srgb, var(--g-success) 8%, #fff); color:var(--g-success); }
.orf-action-pill.selected-reject { border-color:var(--g-danger); background:color-mix(in srgb, var(--g-danger) 8%, #fff); color:var(--g-danger); }
</style>
<!-- 顶部工具栏 -->
<div class="orf-toolbar">
<select class="g-select" style="width:200px;">
<option>老三家外卖(总店)</option>
<option>老三家外卖(朝阳店)</option>
<option>老三家外卖(海淀店)</option>
<option>老三家外卖(丰台店)</option>
<option>老三家外卖(通州店)</option>
</select>
<input type="date" value="2025-02-01" />
<span class="orf-date-sep"></span>
<input type="date" value="2025-02-12" />
<input class="g-input orf-search" placeholder="订单号/退款单号" />
<div style="flex:1;"></div>
<button class="g-btn"><i data-lucide="download" style="width:14px;height:14px;"></i> 导出</button>
</div>
<!-- 统计卡片 -->
<div class="orf-stats">
<div class="orf-stat-card">
<div class="orf-stat-icon blue"><i data-lucide="trending-down" style="width:20px;height:20px;"></i></div>
<div class="orf-stat-body">
<div class="orf-stat-label">本月退款笔数</div>
<div class="orf-stat-value">23</div>
<div class="orf-stat-trend good"><i data-lucide="arrow-down-right" style="width:12px;height:12px;"></i> 较上月减少5笔</div>
</div>
</div>
<div class="orf-stat-card">
<div class="orf-stat-icon orange"><i data-lucide="dollar-sign" style="width:20px;height:20px;"></i></div>
<div class="orf-stat-body">
<div class="orf-stat-label">本月退款金额</div>
<div class="orf-stat-value">¥1,856.00</div>
<div class="orf-stat-trend good"><i data-lucide="arrow-down-right" style="width:12px;height:12px;"></i> 较上月↓¥320</div>
</div>
</div>
<div class="orf-stat-card">
<div class="orf-stat-icon green"><i data-lucide="percent" style="width:20px;height:20px;"></i></div>
<div class="orf-stat-body">
<div class="orf-stat-label">退款率</div>
<div class="orf-stat-value">2.8%</div>
<div class="orf-stat-trend good"><i data-lucide="arrow-down-right" style="width:12px;height:12px;"></i> 较上月↓0.3%</div>
</div>
</div>
</div>
<!-- 表格面板 -->
<div class="orf-panel">
<!-- 分段控制器 -->
<div class="orf-seg-wrap">
<div class="g-seg">
<div class="g-seg-item active" onclick="switchOrfTab(this)" data-tab="pending">待处理(3)</div>
<div class="g-seg-item" onclick="switchOrfTab(this)" data-tab="processed">已处理(20)</div>
</div>
</div>
<!-- 待处理表格 -->
<div id="orfTabPending" class="orf-table-wrap">
<table class="g-table">
<thead>
<tr>
<th>退款单号</th>
<th>关联订单</th>
<th>申请时间</th>
<th>顾客</th>
<th>退款原因</th>
<th>退款金额</th>
<th style="width:80px;">操作</th>
</tr>
</thead>
<tbody>
<tr>
<td style="font-weight:500;">RF20250212001</td>
<td style="color:var(--primary);cursor:pointer;">#20250212003</td>
<td>今天 12:30</td>
<td>王**</td>
<td>商品与描述不符</td>
<td style="font-weight:600;">¥68.00</td>
<td>
<a class="g-action" onclick="openOrfDrawer('RF20250212001')">处理</a>
</td>
</tr>
<tr>
<td style="font-weight:500;">RF20250212002</td>
<td style="color:var(--primary);cursor:pointer;">#20250212005</td>
<td>今天 11:45</td>
<td>赵**</td>
<td>配送超时</td>
<td style="font-weight:600;">¥35.00</td>
<td>
<a class="g-action" onclick="openOrfDrawer('RF20250212002')">处理</a>
</td>
</tr>
<tr>
<td style="font-weight:500;">RF20250212003</td>
<td style="color:var(--primary);cursor:pointer;">#20250212008</td>
<td>今天 10:20</td>
<td>孙**</td>
<td>少送商品</td>
<td style="font-weight:600;">¥15.00</td>
<td>
<a class="g-action" onclick="openOrfDrawer('RF20250212003')">处理</a>
</td>
</tr>
</tbody>
</table>
<div class="g-pagination">
<span style="font-size:13px;color:var(--g-text-secondary);margin-right:12px;">共 3 条</span>
<button class="g-page-btn" disabled><i data-lucide="chevron-left" style="width:14px;height:14px;"></i></button>
<button class="g-page-btn active">1</button>
<button class="g-page-btn" disabled><i data-lucide="chevron-right" style="width:14px;height:14px;"></i></button>
</div>
</div>
<!-- 已处理表格 -->
<div id="orfTabProcessed" class="orf-table-wrap" style="display:none;">
<table class="g-table">
<thead>
<tr>
<th>退款单号</th>
<th>关联订单</th>
<th>申请时间</th>
<th>顾客</th>
<th>退款原因</th>
<th>退款金额</th>
<th>处理结果</th>
<th>处理时间</th>
<th style="width:60px;">操作</th>
</tr>
</thead>
<tbody>
<tr>
<td style="font-weight:500;">RF20250211012</td>
<td style="color:var(--primary);cursor:pointer;">#20250211018</td>
<td>昨天 14:20</td>
<td>李**</td>
<td>商品质量问题</td>
<td style="font-weight:600;">¥42.00</td>
<td><span class="g-tag g-tag-green">已退款</span></td>
<td>昨天 15:05</td>
<td><a class="g-action" onclick="openOrfDrawer('RF20250211012')">详情</a></td>
<tr>
<td style="font-weight:500;">RF20250211008</td>
<td style="color:var(--primary);cursor:pointer;">#20250211010</td>
<td>昨天 11:30</td>
<td>张**</td>
<td>配送超时</td>
<td style="font-weight:600;">¥28.50</td>
<td><span class="g-tag g-tag-green">已退款</span></td>
<td>昨天 12:10</td>
<td><a class="g-action" onclick="openOrfDrawer('RF20250211008')">详情</a></td>
<tr class="orf-dimmed">
<td style="font-weight:500;">RF20250210015</td>
<td style="color:var(--primary);cursor:pointer;">#20250210022</td>
<td>02-10 16:45</td>
<td>周**</td>
<td>不想要了</td>
<td style="font-weight:600;">¥56.00</td>
<td><span class="g-tag g-tag-red">已拒绝</span></td>
<td>02-10 17:20</td>
<td><a class="g-action" onclick="openOrfDrawer('RF20250210015')">详情</a></td>
<tr>
<td style="font-weight:500;">RF20250210009</td>
<td style="color:var(--primary);cursor:pointer;">#20250210014</td>
<td>02-10 12:00</td>
<td>吴**</td>
<td>少送商品</td>
<td style="font-weight:600;">¥18.00</td>
<td><span class="g-tag g-tag-green">已退款</span></td>
<td>02-10 13:30</td>
<td><a class="g-action" onclick="openOrfDrawer('RF20250210009')">详情</a></td>
<tr class="orf-dimmed">
<td style="font-weight:500;">RF20250209021</td>
<td style="color:var(--primary);cursor:pointer;">#20250209030</td>
<td>02-09 19:10</td>
<td>陈**</td>
<td>商品与描述不符</td>
<td style="font-weight:600;">¥75.00</td>
<td><span class="g-tag g-tag-red">已拒绝</span></td>
<td>02-09 20:00</td>
<td><a class="g-action" onclick="openOrfDrawer('RF20250209021')">详情</a></td>
</tbody>
</table>
<div class="g-pagination">
<span style="font-size:13px;color:var(--g-text-secondary);margin-right:12px;">共 20 条</span>
<button class="g-page-btn" disabled><i data-lucide="chevron-left" style="width:14px;height:14px;"></i></button>
<button class="g-page-btn active">1</button>
<button class="g-page-btn">2</button>
<button class="g-page-btn">3</button>
<button class="g-page-btn">4</button>
<button class="g-page-btn"><i data-lucide="chevron-right" style="width:14px;height:14px;"></i></button>
</div>
</div>
</div>
<!-- 退款处理抽屉 -->
<div class="g-drawer-mask" id="orfDrawerMask" onclick="closeOrfDrawer()"></div>
<div class="g-drawer" id="orfDrawer" style="width:480px;">
<div class="g-drawer-hd">
<span class="g-drawer-title">退款处理</span>
<span class="g-drawer-close" onclick="closeOrfDrawer()"><i data-lucide="x" style="width:18px;height:18px;"></i></span>
</div>
<div class="g-drawer-bd">
<!-- 退款信息 -->
<div class="orf-info-section">
<div class="orf-info-title">退款信息</div>
<div class="orf-info-grid">
<div class="orf-info-item">
<span class="orf-info-label">退款单号</span>
<span class="orf-info-value" id="orfDetailNo">RF20250212001</span>
</div>
<div class="orf-info-item">
<span class="orf-info-label">关联订单</span>
<span class="orf-info-value" style="color:var(--primary);cursor:pointer;" id="orfDetailOrder">#20250212003</span>
</div>
<div class="orf-info-item">
<span class="orf-info-label">申请时间</span>
<span class="orf-info-value" id="orfDetailTime">今天 12:30</span>
</div>
<div class="orf-info-item">
<span class="orf-info-label">顾客</span>
<span class="orf-info-value" id="orfDetailCustomer">王**</span>
</div>
<div class="orf-info-item">
<span class="orf-info-label">退款金额</span>
<span class="orf-info-value" style="color:var(--g-danger);font-size:16px;" id="orfDetailAmount">¥68.00</span>
</div>
</div>
</div>
<!-- 退款原因 -->
<div class="orf-info-section">
<div class="orf-info-title">退款原因</div>
<div class="orf-reason-text" id="orfDetailReason">商品与描述不符,收到的菜品和图片差距较大,口味也不对。</div>
<div class="orf-photo-placeholder"><i data-lucide="image" style="width:20px;height:20px;margin-bottom:2px;"></i><br/>顾客上传图片</div>
</div>
<!-- 商品信息 -->
<div class="orf-info-section">
<div class="orf-info-title">商品信息</div>
<div class="orf-product-row">
<div class="orf-product-img"><i data-lucide="utensils" style="width:20px;height:20px;"></i></div>
<div class="orf-product-info">
<div class="orf-product-name">香辣鸡腿堡套餐</div>
<div class="orf-product-spec">大份 / 微辣 x1</div>
</div>
<div class="orf-product-price">¥38.00</div>
</div>
<div class="orf-product-row">
<div class="orf-product-img"><i data-lucide="cup-soda" style="width:20px;height:20px;"></i></div>
<div class="orf-product-info">
<div class="orf-product-name">冰红茶(大杯)</div>
<div class="orf-product-spec">加冰 x1</div>
</div>
<div class="orf-product-price">¥12.00</div>
</div>
<div class="orf-product-row">
<div class="orf-product-img"><i data-lucide="cookie" style="width:20px;height:20px;"></i></div>
<div class="orf-product-info">
<div class="orf-product-name">蛋挞2只装</div>
<div class="orf-product-spec">x1</div>
</div>
<div class="orf-product-price">¥18.00</div>
</div>
</div>
<!-- 处理操作 -->
<div class="orf-info-section">
<div class="orf-info-title">处理操作</div>
<div class="orf-action-pills">
<div class="orf-action-pill" onclick="selectOrfAction(this, 'approve')"><i data-lucide="check" style="width:14px;height:14px;vertical-align:-2px;margin-right:4px;"></i>同意退款</div>
<div class="orf-action-pill" onclick="selectOrfAction(this, 'reject')"><i data-lucide="x" style="width:14px;height:14px;vertical-align:-2px;margin-right:4px;"></i>拒绝退款</div>
</div>
<div id="orfRejectArea" style="display:none;">
<div class="g-form-group">
<label class="g-form-label required">拒绝原因</label>
<textarea class="g-textarea" rows="3" placeholder="请输入拒绝原因,如:商品已确认送达"></textarea>
<div class="g-hint">拒绝原因将通知顾客,请认真填写</div>
</div>
</div>
</div>
</div>
<div class="g-drawer-ft">
<button class="g-btn" onclick="closeOrfDrawer()">取消</button>
<button class="g-btn g-btn-primary">提交</button>
</div>
</div>
<script>
/* 切换待处理/已处理 tab */
function switchOrfTab(el) {
var tab = el.getAttribute('data-tab');
el.parentElement.querySelectorAll('.g-seg-item').forEach(function(item) {
item.classList.remove('active');
});
el.classList.add('active');
document.getElementById('orfTabPending').style.display = tab === 'pending' ? '' : 'none';
document.getElementById('orfTabProcessed').style.display = tab === 'processed' ? '' : 'none';
}
/* 退款详情数据映射(原型模拟) */
var orfMockData = {
'RF20250212001': { no:'RF20250212001', order:'#20250212003', time:'今天 12:30', customer:'王**', amount:'¥68.00', reason:'商品与描述不符,收到的菜品和图片差距较大,口味也不对。' },
'RF20250212002': { no:'RF20250212002', order:'#20250212005', time:'今天 11:45', customer:'赵**', amount:'¥35.00', reason:'配送超时,等了将近一个小时才送到,食物已经凉了。' },
'RF20250212003': { no:'RF20250212003', order:'#20250212008', time:'今天 10:20', customer:'孙**', amount:'¥15.00', reason:'少送了一份蛋挞,订单里有但是没收到。' }
};
/* 打开抽屉 */
function openOrfDrawer(refundNo) {
var data = orfMockData[refundNo];
if (data) {
document.getElementById('orfDetailNo').textContent = data.no;
document.getElementById('orfDetailOrder').textContent = data.order;
document.getElementById('orfDetailTime').textContent = data.time;
document.getElementById('orfDetailCustomer').textContent = data.customer;
document.getElementById('orfDetailAmount').textContent = data.amount;
document.getElementById('orfDetailReason').textContent = data.reason;
}
/* 重置操作状态 */
document.querySelectorAll('.orf-action-pill').forEach(function(p) {
p.classList.remove('selected-approve', 'selected-reject');
});
document.getElementById('orfRejectArea').style.display = 'none';
document.getElementById('orfDrawerMask').classList.add('open');
document.getElementById('orfDrawer').classList.add('open');
}
/* 关闭抽屉 */
function closeOrfDrawer() {
document.getElementById('orfDrawerMask').classList.remove('open');
document.getElementById('orfDrawer').classList.remove('open');
}
/* 选择同意/拒绝 */
function selectOrfAction(el, action) {
document.querySelectorAll('.orf-action-pill').forEach(function(p) {
p.classList.remove('selected-approve', 'selected-reject');
});
if (action === 'approve') {
el.classList.add('selected-approve');
document.getElementById('orfRejectArea').style.display = 'none';
} else {
el.classList.add('selected-reject');
document.getElementById('orfRejectArea').style.display = '';
}
}
</script>

318
pages/order-settings.html Normal file
View File

@@ -0,0 +1,318 @@
<!-- 订单设置页 -->
<style>
.page-order-settings { max-width:960px; }
.os-toolbar { display:flex; align-items:center; gap:12px; margin-bottom:16px; box-shadow:var(--g-shadow-sm); border-radius:10px; padding:10px 14px; background:#fff; }
.os-toolbar select { height:34px; padding:0 10px; border:1px solid #e5e7eb; border-radius:8px; font-size:13px; outline:none; background:#fff; transition:var(--g-transition); }
.os-toolbar select:focus { border-color:var(--primary); box-shadow:0 0 0 3px color-mix(in srgb, var(--primary) 12%, transparent); }
.os-toolbar .os-page-title { font-size:15px; font-weight:600; color:var(--g-text); }
.os-section-hd { font-size:15px; font-weight:600; color:var(--g-text); padding-left:10px; border-left:3px solid var(--primary); margin-bottom:16px; }
.os-row { display:flex; align-items:flex-start; gap:16px; padding:14px 0; border-bottom:1px solid #f3f4f6; }
.os-row:last-child { border-bottom:none; }
.os-row-label { width:200px; flex-shrink:0; }
.os-row-label .os-label-main { font-size:13px; font-weight:500; color:var(--g-text); }
.os-row-label .os-label-sub { font-size:11px; color:var(--g-text-muted); margin-top:2px; }
.os-row-ctrl { flex:1; display:flex; flex-direction:column; gap:6px; }
.os-row-ctrl-inline { display:flex; align-items:center; gap:8px; }
.os-row-hint { font-size:11px; color:var(--g-text-muted); margin-top:2px; }
.os-num-input { width:120px; height:34px; padding:0 10px; border:1px solid #e5e7eb; border-radius:8px; font-size:13px; outline:none; transition:var(--g-transition); }
.os-num-input:focus { border-color:var(--primary); box-shadow:0 0 0 3px color-mix(in srgb, var(--primary) 12%, transparent); }
.os-text-input { width:160px; height:34px; padding:0 10px; border:1px solid #e5e7eb; border-radius:8px; font-size:13px; outline:none; transition:var(--g-transition); }
.os-text-input:focus { border-color:var(--primary); box-shadow:0 0 0 3px color-mix(in srgb, var(--primary) 12%, transparent); }
.os-suffix { font-size:13px; color:var(--g-text-secondary); white-space:nowrap; }
.os-select { height:34px; padding:0 10px; border:1px solid #e5e7eb; border-radius:8px; font-size:13px; outline:none; background:#fff; transition:var(--g-transition); min-width:140px; }
.os-select:focus { border-color:var(--primary); box-shadow:0 0 0 3px color-mix(in srgb, var(--primary) 12%, transparent); }
.os-checkbox-group { display:flex; flex-wrap:wrap; gap:10px 20px; }
.os-checkbox-item { display:flex; align-items:center; gap:6px; cursor:pointer; font-size:13px; color:var(--g-text); }
.os-checkbox-item input[type=checkbox] { width:16px; height:16px; accent-color:var(--primary); cursor:pointer; }
.os-preview { display:inline-flex; align-items:center; gap:6px; padding:8px 14px; background:#f8f9fb; border:1px dashed #e5e7eb; border-radius:8px; font-size:13px; color:var(--g-text-secondary); font-family:monospace; letter-spacing:0.5px; margin-top:4px; }
.os-save-bar { position:sticky; bottom:0; background:#fff; border-top:1px solid #f0f0f0; padding:14px 18px; display:flex; justify-content:flex-end; gap:8px; margin:0 -18px -20px; border-radius:0 0 10px 10px; }
</style>
<div class="page-order-settings">
<!-- 顶部工具栏 -->
<div class="os-toolbar">
<select style="width:200px;">
<option>老三家外卖(朝阳店)</option>
<option>老三家外卖(海淀店)</option>
<option>老三家外卖(望京店)</option>
<option>老三家外卖(通州店)</option>
<option>老三家外卖(丰台店)</option>
</select>
<div style="flex:1;"></div>
<span class="os-page-title">订单设置</span>
</div>
<!-- 接单设置 -->
<div class="g-card" style="padding:0; margin-bottom:16px;">
<div style="padding:18px 18px 0;">
<div class="os-section-hd">接单设置</div>
</div>
<div style="padding:0 18px 18px;">
<div class="os-row">
<div class="os-row-label"><div class="os-label-main">自动接单</div></div>
<div class="os-row-ctrl">
<div class="os-row-ctrl-inline">
<div class="g-toggle on" onclick="toggleSwitch(this)"></div>
<span class="g-toggle-label">开启后新订单将自动接单</span>
</div>
</div>
</div>
<div class="os-row">
<div class="os-row-label"><div class="os-label-main">自动接单延迟</div></div>
<div class="os-row-ctrl">
<div class="os-row-ctrl-inline">
<input type="number" class="os-num-input" placeholder="如30" value="30" />
<span class="os-suffix"></span>
</div>
<div class="os-row-hint">设置自动接单前的等待时间,方便商家提前查看订单</div>
</div>
</div>
<div class="os-row">
<div class="os-row-label"><div class="os-label-main">默认出餐时间</div></div>
<div class="os-row-ctrl">
<div class="os-row-ctrl-inline">
<input type="number" class="os-num-input" placeholder="如20" value="20" />
<span class="os-suffix">分钟</span>
</div>
<div class="os-row-hint">预计出餐时间,将展示给顾客</div>
</div>
</div>
</div>
</div>
<!-- 超时设置 -->
<div class="g-card" style="padding:0; margin-bottom:16px;">
<div style="padding:18px 18px 0;">
<div class="os-section-hd">超时设置</div>
</div>
<div style="padding:0 18px 18px;">
<div class="os-row">
<div class="os-row-label"><div class="os-label-main">未接单自动取消</div></div>
<div class="os-row-ctrl">
<div class="os-row-ctrl-inline">
<input type="number" class="os-num-input" placeholder="如5" value="5" />
<span class="os-suffix">分钟</span>
</div>
<div class="os-row-hint">超过该时间未接单,订单将自动取消并退款</div>
</div>
</div>
<div class="os-row">
<div class="os-row-label"><div class="os-label-main">催单提醒阈值</div></div>
<div class="os-row-ctrl">
<div class="os-row-ctrl-inline">
<input type="number" class="os-num-input" placeholder="如10" value="10" />
<span class="os-suffix">分钟</span>
</div>
<div class="os-row-hint">订单超过该时间未出餐,系统将发送催单提醒</div>
</div>
</div>
</div>
</div>
<!-- 提醒设置 -->
<div class="g-card" style="padding:0; margin-bottom:16px;">
<div style="padding:18px 18px 0;">
<div class="os-section-hd">提醒设置</div>
</div>
<div style="padding:0 18px 18px;">
<div class="os-row">
<div class="os-row-label"><div class="os-label-main">新订单提示音</div></div>
<div class="os-row-ctrl">
<div class="os-row-ctrl-inline">
<div class="g-toggle on" onclick="toggleSwitch(this)"></div>
<span class="g-toggle-label">新订单到达时播放提示音</span>
<select class="os-select" style="margin-left:12px;">
<option>叮咚提示音</option>
<option>语音播报</option>
<option>铃声提示</option>
</select>
</div>
</div>
</div>
<div class="os-row">
<div class="os-row-label"><div class="os-label-main">催单提示音</div></div>
<div class="os-row-ctrl">
<div class="os-row-ctrl-inline">
<div class="g-toggle on" onclick="toggleSwitch(this)"></div>
<span class="g-toggle-label">收到催单时播放提示音</span>
</div>
</div>
</div>
<div class="os-row">
<div class="os-row-label"><div class="os-label-main">退款提醒</div></div>
<div class="os-row-ctrl">
<div class="os-row-ctrl-inline">
<div class="g-toggle on" onclick="toggleSwitch(this)"></div>
<span class="g-toggle-label">收到退款申请时提醒</span>
</div>
</div>
</div>
<div class="os-row">
<div class="os-row-label"><div class="os-label-main">爆单预警</div></div>
<div class="os-row-ctrl">
<div class="os-row-ctrl-inline">
<div class="g-toggle" onclick="toggleSwitch(this)"></div>
<span class="g-toggle-label">当待处理订单超过阈值时预警</span>
</div>
<div class="os-row-hint">开启后,待处理订单数量超过设定值时将发出预警通知</div>
</div>
</div>
<div class="os-row">
<div class="os-row-label"><div class="os-label-main">预警阈值</div></div>
<div class="os-row-ctrl">
<div class="os-row-ctrl-inline">
<input type="number" class="os-num-input" placeholder="如20" value="20" />
<span class="os-suffix"></span>
</div>
</div>
</div>
</div>
</div>
<!-- 打印设置 -->
<div class="g-card" style="padding:0; margin-bottom:16px;">
<div style="padding:18px 18px 0;">
<div class="os-section-hd">打印设置</div>
</div>
<div style="padding:0 18px 18px;">
<div class="os-row">
<div class="os-row-label"><div class="os-label-main">自动打印</div></div>
<div class="os-row-ctrl">
<div class="os-row-ctrl-inline">
<div class="g-toggle on" onclick="toggleSwitch(this)"></div>
<span class="g-toggle-label">接单后自动打印小票</span>
</div>
</div>
</div>
<div class="os-row">
<div class="os-row-label"><div class="os-label-main">打印份数</div></div>
<div class="os-row-ctrl">
<div class="os-row-ctrl-inline">
<input type="number" class="os-num-input" placeholder="如2" value="2" min="1" />
<span class="os-suffix"></span>
</div>
</div>
</div>
<div class="os-row">
<div class="os-row-label"><div class="os-label-main">小票模板</div></div>
<div class="os-row-ctrl">
<div class="os-row-ctrl-inline">
<span class="g-pill checked" onclick="selectOsTemplate(this)">标准模板</span>
<span class="g-pill" onclick="selectOsTemplate(this)">简洁模板</span>
<span class="g-pill" onclick="selectOsTemplate(this)">详细模板</span>
</div>
</div>
</div>
<div class="os-row">
<div class="os-row-label"><div class="os-label-main">打印内容</div></div>
<div class="os-row-ctrl">
<div class="os-checkbox-group">
<label class="os-checkbox-item"><input type="checkbox" checked />订单号</label>
<label class="os-checkbox-item"><input type="checkbox" checked />下单时间</label>
<label class="os-checkbox-item"><input type="checkbox" checked />顾客信息</label>
<label class="os-checkbox-item"><input type="checkbox" checked />商品明细</label>
<label class="os-checkbox-item"><input type="checkbox" checked />备注</label>
<label class="os-checkbox-item"><input type="checkbox" checked />配送信息</label>
</div>
</div>
</div>
<div class="os-row">
<div class="os-row-label"><div class="os-label-main">后厨联</div></div>
<div class="os-row-ctrl">
<div class="os-row-ctrl-inline">
<div class="g-toggle on" onclick="toggleSwitch(this)"></div>
<span class="g-toggle-label">同时打印后厨联(仅含商品和备注)</span>
</div>
</div>
</div>
</div>
</div>
<!-- 订单编号 -->
<div class="g-card" style="padding:0; margin-bottom:16px;">
<div style="padding:18px 18px 0;">
<div class="os-section-hd">订单编号</div>
</div>
<div style="padding:0 18px 18px;">
<div class="os-row">
<div class="os-row-label"><div class="os-label-main">编号前缀</div></div>
<div class="os-row-ctrl">
<div class="os-row-ctrl-inline">
<input type="text" class="os-text-input" placeholder="如DD" value="DD" id="osPrefix" oninput="updateOsPreview()" />
</div>
<div class="os-row-hint">订单号将以此前缀开头,如 DD20250212001</div>
</div>
</div>
<div class="os-row">
<div class="os-row-label"><div class="os-label-main">流水号位数</div></div>
<div class="os-row-ctrl">
<div class="os-row-ctrl-inline">
<select class="os-select" id="osDigits" onchange="updateOsPreview()">
<option value="4">4位</option>
<option value="6" selected>6位</option>
<option value="8">8位</option>
</select>
</div>
</div>
</div>
<div class="os-row">
<div class="os-row-label"><div class="os-label-main">每日重置</div></div>
<div class="os-row-ctrl">
<div class="os-row-ctrl-inline">
<div class="g-toggle on" onclick="toggleSwitch(this)"></div>
<span class="g-toggle-label">每天从001开始重新编号</span>
</div>
</div>
</div>
<div class="os-row">
<div class="os-row-label"><div class="os-label-main">编号预览</div></div>
<div class="os-row-ctrl">
<div class="os-preview" id="osPreviewBox">
<i data-lucide="hash" style="width:14px;height:14px;opacity:0.4;"></i>
<span id="osPreviewText">DD250212000001</span>
</div>
</div>
</div>
</div>
</div>
<!-- 底部保存栏 -->
<div class="g-card" style="padding:14px 18px; margin-bottom:16px;">
<div style="display:flex; justify-content:flex-end; gap:8px;">
<button class="g-btn"><i data-lucide="rotate-ccw" style="width:14px;height:14px;"></i>重置</button>
<button class="g-btn g-btn-primary"><i data-lucide="save" style="width:14px;height:14px;"></i>保存设置</button>
</div>
</div>
</div>
<script>
function toggleSwitch(el) {
el.classList.toggle('on');
}
function selectOsTemplate(el) {
el.parentElement.querySelectorAll('.g-pill').forEach(function(p) { p.classList.remove('checked'); });
el.classList.add('checked');
}
function updateOsPreview() {
var prefix = document.getElementById('osPrefix').value || 'DD';
var digits = parseInt(document.getElementById('osDigits').value) || 6;
var now = new Date();
var y = String(now.getFullYear()).slice(2);
var m = String(now.getMonth() + 1).padStart(2, '0');
var d = String(now.getDate()).padStart(2, '0');
var seq = '1'.padStart(digits, '0');
document.getElementById('osPreviewText').textContent = prefix + y + m + d + seq;
}
/* 初始化 Lucide 图标 */
if (typeof lucide !== 'undefined') { lucide.createIcons(); }
</script>

303
pages/product-addons.html Normal file
View File

@@ -0,0 +1,303 @@
<!-- 加料管理页 -->
<style>
.page-addons { max-width:960px; }
.pad-toolbar { display:flex; align-items:center; gap:12px; margin-bottom:16px; box-shadow:var(--g-shadow-sm); border-radius:10px; padding:12px 16px; background:#fff; flex-wrap:wrap; }
.pad-search { position:relative; }
.pad-search input { height:34px; padding:0 10px 0 32px; border:1px solid #e5e7eb; border-radius:8px; font-size:13px; outline:none; width:200px; transition:var(--g-transition); }
.pad-search input:focus { border-color:var(--primary); box-shadow:0 0 0 3px color-mix(in srgb, var(--primary) 12%, transparent); }
.pad-search i { position:absolute; left:9px; top:50%; transform:translateY(-50%); color:#bbb; pointer-events:none; }
.pad-stats { display:flex; gap:24px; margin-bottom:16px; padding:10px 16px; background:#fff; border-radius:10px; box-shadow:var(--g-shadow-sm); font-size:13px; color:#4b5563; }
.pad-stats span { display:flex; align-items:center; gap:6px; }
.pad-stats strong { color:#1a1a2e; font-weight:600; }
.pad-card { background:#fff; border-radius:10px; border:none; box-shadow:var(--g-shadow-sm); padding:20px; margin-bottom:16px; transition:var(--g-transition); }
.pad-card:hover { box-shadow:var(--g-shadow-md); }
.pad-card.disabled { opacity:0.5; }
.pad-card-header { display:flex; align-items:center; gap:10px; margin-bottom:14px; }
.pad-card-name { font-size:14px; font-weight:600; color:#1a1a2e; }
.pad-tag-required { background:#fef2f2; color:#ef4444; }
.pad-tag-optional { background:#eff6ff; color:#3b82f6; }
.pad-tag-disabled { background:#f3f4f6; color:#9ca3af; }
.pad-rule { font-size:12px; color:#9ca3af; margin-left:4px; }
.pad-table { width:100%; border-collapse:collapse; font-size:13px; margin-bottom:12px; }
.pad-table th { background:#f8f9fb; padding:8px 12px; text-align:left; font-weight:600; color:#6b7280; border-bottom:1px solid #f3f4f6; }
.pad-table td { padding:8px 12px; border-bottom:1px solid #f3f4f6; color:#1a1a2e; }
.pad-table tr:last-child td { border-bottom:none; }
.pad-table tr:hover td { background:color-mix(in srgb, var(--primary) 3%, #fff); }
.pad-stock-ok { color:#22c55e; font-size:12px; font-weight:600; }
.pad-stock-low { color:#f59e0b; font-size:12px; font-weight:600; }
.pad-assoc { font-size:12px; color:#9ca3af; margin-bottom:12px; }
.pad-card-footer { display:flex; align-items:center; gap:8px; border-top:1px solid #f3f4f6; padding-top:12px; }
.pad-sel-row { display:flex; align-items:center; gap:8px; margin-bottom:16px; }
.pad-sel-row span { font-size:13px; color:#4b5563; font-weight:500; }
.pad-opt-row { display:flex; align-items:center; gap:8px; margin-bottom:10px; }
.pad-opt-row input { height:34px; padding:0 10px; border:1px solid #e5e7eb; border-radius:8px; font-size:13px; outline:none; transition:var(--g-transition); }
.pad-opt-row input:focus { border-color:var(--primary); box-shadow:0 0 0 3px color-mix(in srgb, var(--primary) 12%, transparent); }
.pad-opt-name { flex:1; }
.pad-opt-price { width:80px; }
.pad-opt-stock { width:80px; }
.pad-opt-del { width:30px; height:30px; border:none; background:none; cursor:pointer; color:#9ca3af; display:flex; align-items:center; justify-content:center; border-radius:8px; transition:var(--g-transition); }
.pad-opt-del:hover { background:#fef2f2; color:#ef4444; }
.pad-opt-list-header { display:flex; align-items:center; gap:8px; margin-bottom:8px; font-size:12px; color:#6b7280; font-weight:600; }
.pad-opt-list-header .h-name { flex:1; }
.pad-opt-list-header .h-price { width:80px; }
.pad-opt-list-header .h-stock { width:80px; }
.pad-opt-list-header .h-act { width:30px; }
.pad-input-num { width:80px; }
</style>
<div class="page-addons">
<div class="pad-toolbar">
<select class="g-select" style="width:200px;">
<option>老三家外卖(朝阳店)</option>
<option>老三家外卖(海淀店)</option>
<option>老三家外卖(望京店)</option>
<option>老三家外卖(通州店)</option>
<option>老三家外卖(丰台店)</option>
</select>
<div class="pad-search">
<i data-lucide="search" style="width:14px;height:14px;"></i>
<input type="text" placeholder="搜索加料组名称…" oninput="searchAddons(this.value)" />
</div>
<div style="flex:1;"></div>
<button class="g-btn g-btn-primary" onclick="openAddonDrawer('add')"><i data-lucide="plus" style="width:14px;height:14px;"></i>添加加料组</button>
</div>
<div class="pad-stats" id="padStats">
<span>加料组 <strong>5</strong></span>
<span>选项总数 <strong>20</strong></span>
<span>关联商品 <strong>41</strong></span>
</div>
<!-- 加料 -->
<div class="pad-card" data-name="加料">
<div class="pad-card-header">
<span class="pad-card-name">加料</span>
<span class="g-tag pad-tag-optional">可选</span>
<span class="pad-rule">最少选0最多选3</span>
</div>
<table class="pad-table">
<thead><tr><th>选项名称</th><th>加价</th><th>库存</th><th>操作</th></tr></thead>
<tbody>
<tr><td>珍珠</td><td>+¥3.00</td><td><span class="pad-stock-ok">充足</span></td><td><span class="g-action">改名</span><span class="g-action g-action-danger">移除</span></td></tr>
<tr><td>椰果</td><td>+¥3.00</td><td><span class="pad-stock-ok">充足</span></td><td><span class="g-action">改名</span><span class="g-action g-action-danger">移除</span></td></tr>
<tr><td>芋圆</td><td>+¥4.00</td><td><span class="pad-stock-low">偏少</span></td><td><span class="g-action">改名</span><span class="g-action g-action-danger">移除</span></td></tr>
<tr><td>芝士奶盖</td><td>+¥5.00</td><td><span class="pad-stock-ok">充足</span></td><td><span class="g-action">改名</span><span class="g-action g-action-danger">移除</span></td></tr>
<tr><td>红豆</td><td>+¥3.00</td><td><span class="pad-stock-low">偏少</span></td><td><span class="g-action">改名</span><span class="g-action g-action-danger">移除</span></td></tr>
</tbody>
</table>
<div class="pad-assoc">已关联 8 个商品</div>
<div class="pad-card-footer">
<span class="g-action" onclick="openAddonDrawer('edit','加料')">编辑</span>
<span class="g-action" onclick="openProductPicker({title:'关联商品',onConfirm:function(){}})">关联商品</span>
<span class="g-action g-action-danger">删除</span>
</div>
</div>
<!-- 配菜 -->
<div class="pad-card" data-name="配菜">
<div class="pad-card-header">
<span class="pad-card-name">配菜</span>
<span class="g-tag pad-tag-optional">可选</span>
<span class="pad-rule">最少选0最多选2</span>
</div>
<table class="pad-table">
<thead><tr><th>选项名称</th><th>加价</th><th>库存</th><th>操作</th></tr></thead>
<tbody>
<tr><td>卤蛋</td><td>+¥2.00</td><td><span class="pad-stock-ok">充足</span></td><td><span class="g-action">改名</span><span class="g-action g-action-danger">移除</span></td></tr>
<tr><td>海带</td><td>+¥1.00</td><td><span class="pad-stock-ok">充足</span></td><td><span class="g-action">改名</span><span class="g-action g-action-danger">移除</span></td></tr>
<tr><td>豆腐</td><td>+¥1.00</td><td><span class="pad-stock-low">偏少</span></td><td><span class="g-action">改名</span><span class="g-action g-action-danger">移除</span></td></tr>
<tr><td>午餐肉</td><td>+¥4.00</td><td><span class="pad-stock-ok">充足</span></td><td><span class="g-action">改名</span><span class="g-action g-action-danger">移除</span></td></tr>
</tbody>
</table>
<div class="pad-assoc">已关联 12 个商品</div>
<div class="pad-card-footer">
<span class="g-action" onclick="openAddonDrawer('edit','配菜')">编辑</span>
<span class="g-action" onclick="openProductPicker({title:'关联商品',onConfirm:function(){}})">关联商品</span>
<span class="g-action g-action-danger">删除</span>
</div>
</div>
<!-- 主食升级 -->
<div class="pad-card" data-name="主食升级">
<div class="pad-card-header">
<span class="pad-card-name">主食升级</span>
<span class="g-tag pad-tag-required">必选</span>
<span class="pad-rule">必须选1</span>
</div>
<table class="pad-table">
<thead><tr><th>选项名称</th><th>加价</th><th>库存</th><th>操作</th></tr></thead>
<tbody>
<tr><td>米饭(默认)</td><td>+¥0.00</td><td><span class="pad-stock-ok">充足</span></td><td><span class="g-action">改名</span><span class="g-action g-action-danger">移除</span></td></tr>
<tr><td>炒饭</td><td>+¥3.00</td><td><span class="pad-stock-ok">充足</span></td><td><span class="g-action">改名</span><span class="g-action g-action-danger">移除</span></td></tr>
<tr><td>拌面</td><td>+¥3.00</td><td><span class="pad-stock-ok">充足</span></td><td><span class="g-action">改名</span><span class="g-action g-action-danger">移除</span></td></tr>
</tbody>
</table>
<div class="pad-assoc">已关联 15 个商品</div>
<div class="pad-card-footer">
<span class="g-action" onclick="openAddonDrawer('edit','主食升级')">编辑</span>
<span class="g-action" onclick="openProductPicker({title:'关联商品',onConfirm:function(){}})">关联商品</span>
<span class="g-action g-action-danger">删除</span>
</div>
</div>
<!-- 锅底选择 -->
<div class="pad-card" data-name="锅底选择">
<div class="pad-card-header">
<span class="pad-card-name">锅底选择</span>
<span class="g-tag pad-tag-required">必选</span>
<span class="pad-rule">必须选1</span>
</div>
<table class="pad-table">
<thead><tr><th>选项名称</th><th>加价</th><th>库存</th><th>操作</th></tr></thead>
<tbody>
<tr><td>番茄锅</td><td>+¥0.00</td><td><span class="pad-stock-ok">充足</span></td><td><span class="g-action">改名</span><span class="g-action g-action-danger">移除</span></td></tr>
<tr><td>麻辣锅</td><td>+¥8.00</td><td><span class="pad-stock-ok">充足</span></td><td><span class="g-action">改名</span><span class="g-action g-action-danger">移除</span></td></tr>
<tr><td>菌汤锅</td><td>+¥5.00</td><td><span class="pad-stock-low">偏少</span></td><td><span class="g-action">改名</span><span class="g-action g-action-danger">移除</span></td></tr>
<tr><td>鸳鸯锅</td><td>+¥12.00</td><td><span class="pad-stock-ok">充足</span></td><td><span class="g-action">改名</span><span class="g-action g-action-danger">移除</span></td></tr>
</tbody>
</table>
<div class="pad-assoc">已关联 6 个商品</div>
<div class="pad-card-footer">
<span class="g-action" onclick="openAddonDrawer('edit','锅底选择')">编辑</span>
<span class="g-action" onclick="openProductPicker({title:'关联商品',onConfirm:function(){}})">关联商品</span>
<span class="g-action g-action-danger">删除</span>
</div>
</div>
<!-- 饮品加料(已停用) -->
<div class="pad-card disabled" data-name="饮品加料">
<div class="pad-card-header">
<span class="pad-card-name">饮品加料</span>
<span class="g-tag pad-tag-disabled">已停用</span>
<span class="pad-rule">最少选0最多选2</span>
</div>
<table class="pad-table">
<thead><tr><th>选项名称</th><th>加价</th><th>库存</th><th>操作</th></tr></thead>
<tbody>
<tr><td>冰淇淋球</td><td>+¥6.00</td><td><span class="pad-stock-ok">充足</span></td><td><span class="g-action">改名</span><span class="g-action g-action-danger">移除</span></td></tr>
<tr><td>奶油顶</td><td>+¥4.00</td><td><span class="pad-stock-ok">充足</span></td><td><span class="g-action">改名</span><span class="g-action g-action-danger">移除</span></td></tr>
<tr><td>巧克力碎</td><td>+¥3.00</td><td><span class="pad-stock-ok">充足</span></td><td><span class="g-action">改名</span><span class="g-action g-action-danger">移除</span></td></tr>
<tr><td>抹茶粉</td><td>+¥2.00</td><td><span class="pad-stock-low">偏少</span></td><td><span class="g-action">改名</span><span class="g-action g-action-danger">移除</span></td></tr>
</tbody>
</table>
<div class="pad-assoc">已关联 0 个商品</div>
<div class="pad-card-footer">
<span class="g-action" onclick="openAddonDrawer('edit','饮品加料')">编辑</span>
<span class="g-action">启用</span>
<span class="g-action g-action-danger">删除</span>
</div>
</div>
</div>
<!-- 添加/编辑加料组 Drawer -->
<div class="g-drawer-mask" id="padDrawerMask" onclick="closeAddonDrawer()"></div>
<div class="g-drawer" id="padDrawer" style="width:520px;">
<div class="g-drawer-hd">
<span class="g-drawer-title" id="padDrawerTitle">添加加料组</span>
<button class="g-drawer-close" onclick="closeAddonDrawer()"><i data-lucide="x" style="width:16px;height:16px;"></i></button>
</div>
<div class="g-drawer-bd">
<div class="g-form-group">
<label class="g-form-label required">组名称</label>
<input class="g-input" type="text" id="padGroupName" placeholder="请输入加料组名称,如:加料、配菜" />
</div>
<div style="display:flex; align-items:center; gap:10px; margin-bottom:16px;">
<button class="g-toggle on" id="padRequiredToggle" onclick="toggleSwitch(this)"></button>
<span class="g-toggle-label">是否必选</span>
</div>
<div class="pad-sel-row">
<span>选择数量:最少</span>
<input class="g-input pad-input-num" type="number" id="padMin" value="0" min="0" placeholder="如0" />
<span>最多</span>
<input class="g-input pad-input-num" type="number" id="padMax" value="3" min="1" placeholder="如3" />
</div>
<div class="g-form-group">
<label class="g-form-label required">选项列表</label>
<div class="g-hint" style="margin-bottom:8px;">每个选项可设置加价和库存,加价为 0 表示免费</div>
<div class="pad-opt-list-header">
<span class="h-name">名称</span>
<span class="h-price">加价(¥)</span>
<span class="h-stock">库存</span>
<span class="h-act"></span>
</div>
<div id="padOptList">
<div class="pad-opt-row">
<input class="pad-opt-name" type="text" placeholder="如:珍珠" value="珍珠" />
<input class="pad-opt-price" type="number" placeholder="如3.00" value="3" min="0" step="0.01" />
<input class="pad-opt-stock" type="number" placeholder="库存数" value="999" min="0" />
<button class="pad-opt-del" onclick="removeAddonOption(this)"><i data-lucide="trash-2" style="width:14px;height:14px;"></i></button>
</div>
<div class="pad-opt-row">
<input class="pad-opt-name" type="text" placeholder="如:椰果" value="椰果" />
<input class="pad-opt-price" type="number" placeholder="如3.00" value="3" min="0" step="0.01" />
<input class="pad-opt-stock" type="number" placeholder="库存数" value="999" min="0" />
<button class="pad-opt-del" onclick="removeAddonOption(this)"><i data-lucide="trash-2" style="width:14px;height:14px;"></i></button>
</div>
<div class="pad-opt-row">
<input class="pad-opt-name" type="text" placeholder="如:芋圆" value="芋圆" />
<input class="pad-opt-price" type="number" placeholder="如4.00" value="5" min="0" step="0.01" />
<input class="pad-opt-stock" type="number" placeholder="库存数" value="500" min="0" />
<button class="pad-opt-del" onclick="removeAddonOption(this)"><i data-lucide="trash-2" style="width:14px;height:14px;"></i></button>
</div>
</div>
<button class="g-btn" style="width:100%;margin-top:8px;justify-content:center;border-style:dashed;" onclick="addAddonOption()"><i data-lucide="plus" style="width:14px;height:14px;"></i>添加选项</button>
</div>
<div class="g-form-group">
<label class="g-form-label">备注</label>
<textarea class="g-textarea" rows="2" placeholder="可选,如:仅限堂食商品使用"></textarea>
</div>
</div>
<div class="g-drawer-ft">
<button class="g-btn" onclick="closeAddonDrawer()">取消</button>
<button class="g-btn g-btn-primary" onclick="closeAddonDrawer()">确认保存</button>
</div>
</div>
<script>
function openAddonDrawer(mode, name) {
document.getElementById('padDrawerMask').classList.add('open');
document.getElementById('padDrawer').classList.add('open');
if (mode === 'edit' && name) {
document.getElementById('padDrawerTitle').textContent = '编辑加料组 - ' + name;
document.getElementById('padGroupName').value = name;
} else {
document.getElementById('padDrawerTitle').textContent = '添加加料组';
document.getElementById('padGroupName').value = '';
}
if (typeof lucide !== 'undefined') lucide.createIcons();
}
function closeAddonDrawer() {
document.getElementById('padDrawerMask').classList.remove('open');
document.getElementById('padDrawer').classList.remove('open');
}
function addAddonOption() {
var list = document.getElementById('padOptList');
var row = document.createElement('div');
row.className = 'pad-opt-row';
row.innerHTML = '<input class="pad-opt-name" type="text" placeholder="请输入选项名称" />' +
'<input class="pad-opt-price" type="number" placeholder="如3.00" value="0" min="0" step="0.01" />' +
'<input class="pad-opt-stock" type="number" placeholder="库存数" value="999" min="0" />' +
'<button class="pad-opt-del" onclick="removeAddonOption(this)"><i data-lucide="trash-2" style="width:14px;height:14px;"></i></button>';
list.appendChild(row);
if (typeof lucide !== 'undefined') lucide.createIcons();
}
function removeAddonOption(el) {
var row = el.closest('.pad-opt-row');
if (row) row.remove();
}
function searchAddons(keyword) {
var kw = keyword.trim().toLowerCase();
document.querySelectorAll('.pad-card').forEach(function(card) {
var name = (card.getAttribute('data-name') || '').toLowerCase();
card.style.display = (!kw || name.includes(kw)) ? '' : 'none';
});
}
function toggleSwitch(el) {
el.classList.toggle('on');
}
</script>

282
pages/product-batch.html Normal file
View File

@@ -0,0 +1,282 @@
<!-- 批量工具页 -->
<style>
.pbt-toolbar { display:flex; align-items:center; gap:12px; margin-bottom:16px; box-shadow:var(--g-shadow-sm); border-radius:10px; padding:12px 16px; background:#fff; flex-wrap:wrap; }
.pbt-grid { display:grid; grid-template-columns:repeat(2,1fr); gap:16px; margin-bottom:16px; }
.pbt-card-icon { width:40px; height:40px; border-radius:10px; display:flex; align-items:center; justify-content:center; margin-bottom:12px; }
.pbt-card-name { font-size:15px; font-weight:600; color:#1a1a2e; margin-bottom:4px; }
.pbt-card-desc { font-size:13px; color:#9ca3af; margin-bottom:14px; line-height:1.5; }
.pbt-expand { background:#fff; border-radius:10px; border:none; box-shadow:var(--g-shadow-sm); padding:20px; margin-bottom:16px; display:none; transition:var(--g-transition); }
.pbt-expand.open { display:block; }
.pbt-expand-title { font-size:14px; font-weight:600; color:#1a1a2e; margin-bottom:16px; display:flex; align-items:center; justify-content:space-between; padding-left:10px; border-left:3px solid var(--primary); }
.pbt-expand-close { background:none; border:none; color:#9ca3af; cursor:pointer; display:flex; align-items:center; justify-content:center; width:28px; height:28px; border-radius:8px; transition:var(--g-transition); }
.pbt-expand-close:hover { color:#1a1a2e; background:#f8f9fb; }
.pbt-step { margin-bottom:18px; }
.pbt-step-label { font-size:13px; font-weight:500; color:#4b5563; margin-bottom:8px; display:flex; align-items:center; gap:6px; }
.pbt-step-num { width:20px; height:20px; border-radius:50%; background:var(--primary); color:#fff; font-size:11px; display:inline-flex; align-items:center; justify-content:center; font-weight:600; }
.pbt-pills { display:flex; gap:8px; flex-wrap:wrap; margin-bottom:8px; }
.pbt-pill { height:34px; padding:0 14px; border-radius:8px; font-size:13px; cursor:pointer; border:1px solid #e5e7eb; background:#fff; color:#4b5563; display:inline-flex; align-items:center; transition:var(--g-transition); }
.pbt-pill:hover { border-color:var(--primary); color:var(--primary); }
.pbt-pill.active { background:var(--primary); color:#fff; border-color:var(--primary); }
.pbt-table { width:100%; border-collapse:collapse; font-size:13px; margin-top:8px; }
.pbt-table th { background:#f8f9fb; padding:8px 12px; text-align:left; font-weight:600; color:#6b7280; border-bottom:1px solid #f3f4f6; }
.pbt-table td { padding:8px 12px; border-bottom:1px solid #f3f4f6; color:#1a1a2e; }
.pbt-table tr:last-child td { border-bottom:none; }
.pbt-table tr:hover td { background:color-mix(in srgb, var(--primary) 3%, #fff); }
.pbt-price-up { color:#ef4444; font-weight:600; }
.pbt-price-down { color:#22c55e; font-weight:600; }
.pbt-actions { display:flex; gap:8px; margin-top:16px; padding-top:16px; border-top:1px solid #f3f4f6; }
.pbt-chk-group { display:flex; flex-wrap:wrap; gap:10px; margin-top:6px; }
.pbt-chk-group label { font-size:13px; color:#4b5563; display:flex; align-items:center; gap:4px; cursor:pointer; }
.pbt-chk-group label input[type=checkbox] { accent-color:var(--primary); cursor:pointer; }
.pbt-info { font-size:13px; color:#4b5563; background:#f0fdf4; border:1px solid #bbf7d0; border-radius:8px; padding:8px 12px; margin-top:10px; display:flex; align-items:center; gap:6px; }
.pbt-upload-area { border:2px dashed #e5e7eb; border-radius:10px; padding:30px; text-align:center; color:#9ca3af; font-size:13px; cursor:pointer; transition:var(--g-transition); display:flex; flex-direction:column; align-items:center; gap:8px; }
.pbt-upload-area:hover { border-color:var(--primary); color:var(--primary); }
.pbt-sub-cards { display:grid; grid-template-columns:1fr 1fr; gap:16px; }
.pbt-sub-card { border:none; border-radius:10px; padding:16px; box-shadow:var(--g-shadow-sm); transition:var(--g-transition); }
.pbt-sub-card:hover { box-shadow:var(--g-shadow-md); }
.pbt-sub-card h4 { font-size:14px; font-weight:600; color:#1a1a2e; margin:0 0 12px 0; }
.pbt-link { color:var(--primary); font-size:13px; text-decoration:none; cursor:pointer; display:inline-flex; align-items:center; gap:4px; transition:var(--g-transition); }
.pbt-link:hover { text-decoration:underline; }
</style>
<div class="pbt-toolbar">
<select class="g-select" style="width:200px;">
<option>老三家外卖(朝阳店)</option>
<option>老三家外卖(海淀店)</option>
<option>老三家外卖(望京店)</option>
<option>老三家外卖(通州店)</option>
<option>老三家外卖(丰台店)</option>
</select>
<div style="flex:1;"></div>
<span style="font-size:13px;color:#9ca3af;">选择门店后操作将应用于该门店的商品</span>
</div>
<div class="pbt-grid" id="pbtGrid">
<div class="g-card">
<div class="pbt-card-icon" style="background:#fff7e6;color:#fa8c16;"><i data-lucide="dollar-sign" style="width:20px;height:20px;"></i></div>
<div class="pbt-card-name">批量调价</div>
<div class="pbt-card-desc">按分类或手动选择商品,统一涨价或降价</div>
<button class="g-btn" onclick="expandBatchTool(0)">开始操作</button>
</div>
<div class="g-card">
<div class="pbt-card-icon" style="background:#f6ffed;color:#52c41a;"><i data-lucide="arrow-up-down" style="width:20px;height:20px;"></i></div>
<div class="pbt-card-name">批量上下架</div>
<div class="pbt-card-desc">快速批量上架或下架商品</div>
<button class="g-btn" onclick="expandBatchTool(1)">开始操作</button>
</div>
<div class="g-card">
<div class="pbt-card-icon" style="background:#e6f7ff;color:#1890ff;"><i data-lucide="folder-input" style="width:20px;height:20px;"></i></div>
<div class="pbt-card-name">批量移动分类</div>
<div class="pbt-card-desc">将商品批量移动到其他分类</div>
<button class="g-btn" onclick="expandBatchTool(2)">开始操作</button>
</div>
<div class="g-card">
<div class="pbt-card-icon" style="background:#f9f0ff;color:#722ed1;"><i data-lucide="refresh-cw" style="width:20px;height:20px;"></i></div>
<div class="pbt-card-name">批量同步门店</div>
<div class="pbt-card-desc">将商品同步到其他门店</div>
<button class="g-btn" onclick="expandBatchTool(3)">开始操作</button>
</div>
<div class="g-card">
<div class="pbt-card-icon" style="background:#e6fffb;color:#13c2c2;"><i data-lucide="file-spreadsheet" style="width:20px;height:20px;"></i></div>
<div class="pbt-card-name">导入导出</div>
<div class="pbt-card-desc">通过Excel批量导入或导出商品数据</div>
<button class="g-btn" onclick="expandBatchTool(4)">开始操作</button>
</div>
</div>
<!-- Expand 0: 批量调价 -->
<div class="pbt-expand" id="pbtExpand0">
<div class="pbt-expand-title">批量调价 <button class="pbt-expand-close" onclick="expandBatchTool(-1)"><i data-lucide="x" style="width:16px;height:16px;"></i></button></div>
<div class="pbt-step">
<div class="pbt-step-label"><span class="pbt-step-num">1</span> 选择范围</div>
<div class="pbt-pills" id="pbtRange">
<span class="pbt-pill active" onclick="selectBatchRange('all')">全部商品</span>
<span class="pbt-pill" onclick="selectBatchRange('category')">按分类</span>
<span class="pbt-pill" onclick="selectBatchRange('manual')">手动选择</span>
</div>
<div id="pbtRangeCat" style="display:none;" class="pbt-chk-group">
<label><input type="checkbox"> 热销套餐</label>
<label><input type="checkbox"> 招牌菜品</label>
<label><input type="checkbox"> 饮品小食</label>
<label><input type="checkbox"> 时令特供</label>
</div>
</div>
<div class="pbt-step">
<div class="pbt-step-label"><span class="pbt-step-num">2</span> 调价方式</div>
<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap;">
<div class="pbt-pills" id="pbtPriceDir">
<span class="pbt-pill active" onclick="selectPriceMode('up')">涨价</span>
<span class="pbt-pill" onclick="selectPriceMode('down')">降价</span>
</div>
<div class="pbt-pills" id="pbtPriceType">
<span class="pbt-pill active" onclick="selectPriceType('fixed')">固定金额</span>
<span class="pbt-pill" onclick="selectPriceType('percent')">百分比</span>
</div>
<input class="g-input" style="width:120px" type="number" placeholder="如2.00" min="0" value="2">
</div>
</div>
<div class="pbt-step">
<div class="pbt-step-label"><span class="pbt-step-num">3</span> 预览</div>
<table class="pbt-table">
<thead><tr><th>商品名</th><th>原价</th><th>新价</th><th>变动</th></tr></thead>
<tbody>
<tr><td>宫保鸡丁</td><td>¥28.00</td><td>¥30.00</td><td class="pbt-price-up">+¥2.00</td></tr>
<tr><td>鱼香肉丝</td><td>¥26.00</td><td>¥28.00</td><td class="pbt-price-up">+¥2.00</td></tr>
<tr><td>可乐鸡翅</td><td>¥32.00</td><td>¥34.00</td><td class="pbt-price-up">+¥2.00</td></tr>
</tbody>
</table>
</div>
<div class="pbt-actions">
<button class="g-btn g-btn-primary">确认调价</button>
<button class="g-btn" onclick="expandBatchTool(-1)">取消</button>
</div>
</div>
<!-- Expand 1: 批量上下架 -->
<div class="pbt-expand" id="pbtExpand1">
<div class="pbt-expand-title">批量上下架 <button class="pbt-expand-close" onclick="expandBatchTool(-1)"><i data-lucide="x" style="width:16px;height:16px;"></i></button></div>
<div class="pbt-step">
<div class="pbt-step-label"><span class="pbt-step-num">1</span> 选择分类</div>
<div class="pbt-chk-group">
<label><input type="checkbox"> 热销套餐</label>
<label><input type="checkbox"> 招牌菜品</label>
<label><input type="checkbox"> 饮品小食</label>
<label><input type="checkbox"> 时令特供</label>
</div>
</div>
<div class="pbt-step">
<div class="pbt-step-label"><span class="pbt-step-num">2</span> 操作类型</div>
<div class="pbt-pills" id="pbtShelfMode">
<span class="pbt-pill active">上架</span>
<span class="pbt-pill">下架</span>
</div>
</div>
<div class="pbt-info"><i data-lucide="info" style="width:14px;height:14px;flex-shrink:0;"></i> 预计影响 <strong>24</strong> 个商品</div>
<div class="pbt-actions">
<button class="g-btn g-btn-primary">确认执行</button>
<button class="g-btn" onclick="expandBatchTool(-1)">取消</button>
</div>
</div>
<!-- Expand 2: 批量移动分类 -->
<div class="pbt-expand" id="pbtExpand2">
<div class="pbt-expand-title">批量移动分类 <button class="pbt-expand-close" onclick="expandBatchTool(-1)"><i data-lucide="x" style="width:16px;height:16px;"></i></button></div>
<div class="pbt-step">
<div class="pbt-step-label"><span class="pbt-step-num">1</span> 源分类</div>
<select class="g-select" style="min-width:200px"><option value="">请选择源分类</option><option>热销套餐</option><option>招牌菜品</option><option>饮品小食</option><option>时令特供</option></select>
</div>
<div class="pbt-step">
<div class="pbt-step-label"><span class="pbt-step-num">2</span> 目标分类</div>
<select class="g-select" style="min-width:200px"><option value="">请选择目标分类</option><option>招牌菜品</option><option>热销套餐</option><option>饮品小食</option><option>时令特供</option></select>
</div>
<div class="pbt-info"><i data-lucide="info" style="width:14px;height:14px;flex-shrink:0;"></i> 该分类下共 <strong>12</strong> 个商品将被移动</div>
<div class="pbt-actions">
<button class="g-btn g-btn-primary">确认移动</button>
<button class="g-btn" onclick="expandBatchTool(-1)">取消</button>
</div>
</div>
<!-- Expand 3: 批量同步门店 -->
<div class="pbt-expand" id="pbtExpand3">
<div class="pbt-expand-title">批量同步门店 <button class="pbt-expand-close" onclick="expandBatchTool(-1)"><i data-lucide="x" style="width:16px;height:16px;"></i></button></div>
<div class="pbt-step">
<div class="pbt-step-label"><span class="pbt-step-num">1</span> 源门店</div>
<select class="g-select" style="min-width:200px"><option value="">请选择源门店</option><option>中关村店</option><option>望京店</option><option>国贸店</option><option>西单店</option></select>
</div>
<div class="pbt-step">
<div class="pbt-step-label"><span class="pbt-step-num">2</span> 目标门店</div>
<div class="pbt-chk-group">
<label><input type="checkbox"> 望京店</label>
<label><input type="checkbox"> 国贸店</label>
<label><input type="checkbox"> 西单店</label>
<label><input type="checkbox"> 三里屯店</label>
</div>
</div>
<div class="pbt-step">
<div class="pbt-step-label"><span class="pbt-step-num">3</span> 同步选项</div>
<div class="pbt-chk-group">
<label><input type="checkbox" checked> 价格</label>
<label><input type="checkbox" checked> 库存</label>
<label><input type="checkbox"> 状态</label>
</div>
</div>
<div class="pbt-actions">
<button class="g-btn g-btn-primary">确认同步</button>
<button class="g-btn" onclick="expandBatchTool(-1)">取消</button>
</div>
</div>
<!-- Expand 4: 导入导出 -->
<div class="pbt-expand" id="pbtExpand4">
<div class="pbt-expand-title">导入导出 <button class="pbt-expand-close" onclick="expandBatchTool(-1)"><i data-lucide="x" style="width:16px;height:16px;"></i></button></div>
<div class="pbt-sub-cards">
<div class="pbt-sub-card">
<h4>导入商品</h4>
<div class="pbt-upload-area">
<i data-lucide="upload" style="width:24px;height:24px;"></i>
<span>点击或拖拽 Excel 文件到此处上传</span>
<span style="font-size:11px;color:#bbb;">支持 .xlsx、.xls 格式,不超过 10MB</span>
</div>
<div style="margin-top:10px;"><a class="pbt-link"><i data-lucide="download" style="width:13px;height:13px;"></i> 下载导入模板</a></div>
</div>
<div class="pbt-sub-card">
<h4>导出商品</h4>
<div class="pbt-step" style="margin-bottom:12px;">
<div class="pbt-pills" id="pbtExportRange">
<span class="pbt-pill active">全部</span>
<span class="pbt-pill">按分类</span>
</div>
</div>
<button class="g-btn g-btn-primary"><i data-lucide="download" style="width:14px;height:14px;"></i> 导出 Excel</button>
</div>
</div>
<div class="pbt-actions">
<button class="g-btn" onclick="expandBatchTool(-1)">关闭</button>
</div>
</div>
<script>
var _pbtCurrent = -1;
function expandBatchTool(idx) {
for (var i = 0; i < 5; i++) {
var el = document.getElementById('pbtExpand' + i);
if (el) el.classList.remove('open');
}
if (idx === _pbtCurrent || idx < 0) { _pbtCurrent = -1; return; }
var target = document.getElementById('pbtExpand' + idx);
if (target) target.classList.add('open');
_pbtCurrent = idx;
if (typeof lucide !== 'undefined') lucide.createIcons();
}
function selectBatchRange(mode) {
var pills = document.querySelectorAll('#pbtRange .pbt-pill');
pills.forEach(function(p) { p.classList.remove('active'); });
var map = { all: 0, category: 1, manual: 2 };
if (pills[map[mode]]) pills[map[mode]].classList.add('active');
var catEl = document.getElementById('pbtRangeCat');
if (catEl) catEl.style.display = mode === 'category' ? 'flex' : 'none';
}
function selectPriceMode(mode) {
var pills = document.querySelectorAll('#pbtPriceDir .pbt-pill');
pills.forEach(function(p) { p.classList.remove('active'); });
if (mode === 'up') pills[0].classList.add('active');
else pills[1].classList.add('active');
}
function selectPriceType(mode) {
var pills = document.querySelectorAll('#pbtPriceType .pbt-pill');
pills.forEach(function(p) { p.classList.remove('active'); });
if (mode === 'fixed') pills[0].classList.add('active');
else pills[1].classList.add('active');
}
document.querySelectorAll('#pbtShelfMode .pbt-pill, #pbtExportRange .pbt-pill').forEach(function(pill) {
pill.addEventListener('click', function() {
this.parentElement.querySelectorAll('.pbt-pill').forEach(function(p) { p.classList.remove('active'); });
this.classList.add('active');
});
});
</script>

474
pages/product-category.html Normal file
View File

@@ -0,0 +1,474 @@
<!-- 分类管理页 -->
<style>
.pcat-toolbar{display:flex;align-items:center;gap:12px;margin-bottom:16px;box-shadow:var(--g-shadow-sm);border-radius:10px;padding:12px 16px;background:#fff;flex-wrap:wrap}
.pcat-store-select{height:34px;border-radius:8px;border:1px solid #e5e7eb;padding:0 12px;font-size:13px;min-width:180px;outline:none;color:#1a1a2e;transition:var(--g-transition)}
.pcat-store-select:focus{border-color:var(--primary);box-shadow:0 0 0 3px color-mix(in srgb,var(--primary) 12%,transparent)}
.pcat-toolbar .pcat-spacer{flex:1}
.pcat-stats{display:flex;gap:24px;margin-bottom:16px;padding:10px 16px;background:#fff;border-radius:10px;box-shadow:var(--g-shadow-sm);font-size:13px;color:#4b5563;flex-wrap:wrap}
.pcat-stats span{display:flex;align-items:center;gap:6px}
.pcat-stats strong{color:#1a1a2e;font-weight:600}
.pcat-stats .pcat-ch-stat{display:inline-flex;align-items:center;gap:4px;margin-left:2px}
.pcat-stats .pcat-ch-dot{width:6px;height:6px;border-radius:50%;display:inline-block}
.pcat-main{display:flex;gap:18px}
/* 左侧分类列表 */
.pcat-left{width:280px;flex-shrink:0;background:#fff;border-radius:10px;box-shadow:var(--g-shadow-sm);display:flex;flex-direction:column;max-height:600px}
.pcat-left-hd{padding:14px 16px 12px;border-bottom:1px solid #f3f4f6;flex-shrink:0}
.pcat-left-title{font-size:14px;font-weight:600;color:#1a1a2e;margin-bottom:10px;display:flex;align-items:center;justify-content:space-between}
.pcat-left-title span{font-size:12px;color:#9ca3af;font-weight:400}
.pcat-ch-filter{display:flex;gap:6px;margin-bottom:10px}
.pcat-ch-filter .pcat-ch-pill{padding:3px 10px;border-radius:6px;font-size:11px;font-weight:500;cursor:pointer;border:1px solid #e5e7eb;background:#fff;color:#6b7280;transition:var(--g-transition);user-select:none}
.pcat-ch-filter .pcat-ch-pill:hover{border-color:var(--primary);color:var(--primary)}
.pcat-ch-filter .pcat-ch-pill.active{background:var(--primary);color:#fff;border-color:var(--primary)}
.pcat-search{position:relative}
.pcat-search input{width:100%;height:32px;border-radius:8px;border:1px solid #e5e7eb;padding:0 10px 0 32px;font-size:12px;outline:none;color:#1a1a2e;transition:var(--g-transition)}
.pcat-search input:focus{border-color:var(--primary);box-shadow:0 0 0 3px color-mix(in srgb,var(--primary) 12%,transparent)}
.pcat-search i{position:absolute;left:9px;top:50%;transform:translateY(-50%);color:#bbb;pointer-events:none}
.pcat-left-list{flex:1;overflow-y:auto;padding:6px 0}
.pcat-left-list::-webkit-scrollbar{width:3px}
.pcat-left-list::-webkit-scrollbar-thumb{background:#e5e7eb;border-radius:2px}
.pcat-cat-item{display:flex;align-items:center;gap:8px;padding:9px 16px;cursor:pointer;font-size:13px;transition:var(--g-transition);user-select:none;color:#4b5563}
.pcat-cat-item:hover{background:color-mix(in srgb,var(--primary) 3%,#fff)}
.pcat-cat-item.active{background:color-mix(in srgb,var(--primary) 10%,transparent);color:var(--primary);font-weight:600}
.pcat-cat-item.disabled{opacity:0.5}
.pcat-drag-handle{color:#bbb;cursor:grab;flex-shrink:0;display:flex;align-items:center}
.pcat-cat-name{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pcat-cat-tags{display:flex;gap:3px;flex-shrink:0}
.pcat-ch-mini{font-size:10px;font-weight:600;padding:1px 4px;border-radius:3px;line-height:14px;color:#fff}
.pcat-ch-mini.wm{background:#1890ff}
.pcat-ch-mini.zt{background:#52c41a}
.pcat-ch-mini.ts{background:#fa8c16}
.pcat-cat-badge{background:#f8f9fb;border-radius:10px;padding:1px 8px;font-size:11px;color:#6b7280;flex-shrink:0}
.pcat-cat-item.active .pcat-cat-badge{background:color-mix(in srgb,var(--primary) 18%,transparent);color:var(--primary)}
/* 右侧详情面板 */
.pcat-right{flex:1;display:flex;flex-direction:column;gap:16px}
.pcat-info-card{background:#fff;border-radius:10px;box-shadow:var(--g-shadow-sm);padding:20px}
.pcat-info-hd{display:flex;align-items:flex-start;justify-content:space-between;margin-bottom:16px}
.pcat-info-title{font-size:18px;font-weight:600;color:#1a1a2e}
.pcat-info-desc{font-size:13px;color:#9ca3af;margin-top:4px}
.pcat-info-actions{display:flex;gap:8px;flex-shrink:0}
.pcat-attr-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:12px;padding-top:16px;border-top:1px solid #f3f4f6}
.pcat-attr-item{display:flex;flex-direction:column;gap:4px;padding:10px 12px;background:#f8f9fb;border-radius:8px}
.pcat-attr-label{font-size:11px;color:#9ca3af;font-weight:500}
.pcat-attr-value{font-size:14px;color:#1a1a2e;font-weight:600}
.pcat-attr-value .g-tag{font-size:11px}
/* 渠道可见性卡片 */
.pcat-channel-card{background:#fff;border-radius:10px;box-shadow:var(--g-shadow-sm);padding:16px 20px}
.pcat-channel-hd{font-size:14px;font-weight:600;color:#1a1a2e;padding-left:10px;border-left:3px solid var(--primary);margin-bottom:14px}
.pcat-channel-list{display:flex;gap:12px;flex-wrap:wrap}
.pcat-channel-item{display:flex;align-items:center;gap:10px;padding:10px 16px;background:#f8f9fb;border-radius:8px;border:1px solid #f3f4f6;min-width:160px;flex:1}
.pcat-channel-item .pcat-ch-icon{width:32px;height:32px;border-radius:8px;display:flex;align-items:center;justify-content:center;flex-shrink:0}
.pcat-channel-item .pcat-ch-icon.wm{background:color-mix(in srgb,#1890ff 12%,#fff);color:#1890ff}
.pcat-channel-item .pcat-ch-icon.zt{background:color-mix(in srgb,#52c41a 12%,#fff);color:#52c41a}
.pcat-channel-item .pcat-ch-icon.ts{background:color-mix(in srgb,#fa8c16 12%,#fff);color:#fa8c16}
.pcat-channel-item .pcat-ch-info{flex:1}
.pcat-channel-item .pcat-ch-name{font-size:13px;font-weight:600;color:#1a1a2e}
.pcat-channel-item .pcat-ch-sub{font-size:11px;color:#9ca3af;margin-top:2px}
.pcat-channel-item .pcat-ch-status{font-size:11px;font-weight:600;display:flex;align-items:center;gap:4px}
.pcat-channel-item .pcat-ch-status.on{color:#52c41a}
.pcat-channel-item .pcat-ch-status.off{color:#9ca3af}
.pcat-channel-item .pcat-ch-status-dot{width:6px;height:6px;border-radius:50%;background:currentColor}
/* 商品预览表 */
.pcat-prod-card{background:#fff;border-radius:10px;box-shadow:var(--g-shadow-sm);padding:0;flex:1}
.pcat-prod-hd{display:flex;align-items:center;justify-content:space-between;padding:14px 18px;border-bottom:1px solid #f3f4f6}
.pcat-prod-title{font-size:14px;font-weight:600;color:#1a1a2e;display:flex;align-items:center;gap:8px}
.pcat-prod-title span{font-size:12px;color:#9ca3af;font-weight:400}
.pcat-prod-table{width:100%;border-collapse:collapse;font-size:13px}
.pcat-prod-table th{text-align:left;padding:10px 18px;font-size:12px;font-weight:500;color:#6b7280;background:#f8f9fb}
.pcat-prod-table td{padding:10px 18px;border-bottom:1px solid #f3f4f6;color:#1a1a2e}
.pcat-prod-table tr:last-child td{border-bottom:none}
.pcat-prod-table tr:hover td{background:color-mix(in srgb,var(--primary) 3%,#fff)}
.pcat-prod-name{display:flex;align-items:center;gap:10px}
.pcat-prod-thumb{width:36px;height:36px;border-radius:6px;background:#f3f4f6;display:flex;align-items:center;justify-content:center;flex-shrink:0;color:#bbb}
.pcat-prod-info .name{font-weight:500}
.pcat-prod-info .spu{font-size:11px;color:#9ca3af;margin-top:1px}
.pcat-prod-price{font-weight:600;color:#1a1a2e}
/* 抽屉内渠道选择 */
.pcat-drawer-channels{display:flex;gap:8px;flex-wrap:wrap}
.pcat-drawer-ch{display:flex;align-items:center;gap:6px;padding:8px 14px;border-radius:8px;border:1.5px solid #e5e7eb;cursor:pointer;transition:var(--g-transition);user-select:none;font-size:13px;color:#4b5563;background:#fff}
.pcat-drawer-ch:hover{border-color:var(--primary)}
.pcat-drawer-ch.checked{border-color:var(--primary);background:color-mix(in srgb,var(--primary) 6%,#fff);color:var(--primary);font-weight:500}
.pcat-drawer-ch .pcat-ch-check{width:16px;height:16px;border-radius:4px;border:1.5px solid #d1d5db;display:flex;align-items:center;justify-content:center;transition:var(--g-transition);flex-shrink:0}
.pcat-drawer-ch.checked .pcat-ch-check{background:var(--primary);border-color:var(--primary)}
.pcat-drawer-ch .pcat-ch-check svg{display:none}
.pcat-drawer-ch.checked .pcat-ch-check svg{display:block}
/* 图标上传 */
.pcat-icon-upload{width:80px;height:80px;border:1px dashed #e5e7eb;border-radius:10px;display:flex;flex-direction:column;align-items:center;justify-content:center;color:#bbb;cursor:pointer;transition:var(--g-transition);gap:4px}
.pcat-icon-upload:hover{border-color:var(--primary);color:var(--primary)}
</style>
<div class="pcat-toolbar">
<select class="pcat-store-select">
<option>老三家外卖 总店</option>
<option>老三家外卖 朝阳店</option>
<option>老三家外卖 海淀店</option>
<option>老三家外卖 丰台店</option>
<option>老三家外卖 通州店</option>
</select>
<button class="g-btn" onclick="openCopyStoreModal()">复制到其他门店</button>
<span class="pcat-spacer"></span>
<button class="g-btn g-btn-primary" onclick="openCatDrawer()"><i data-lucide="plus" style="width:14px;height:14px;"></i> 添加分类</button>
</div>
<div class="pcat-stats">
<span>分类总数 <strong>7</strong></span>
<span>已启用 <strong>6</strong></span>
<span>商品总数 <strong>58</strong></span>
<span>渠道覆盖
<span class="pcat-ch-stat"><span class="pcat-ch-dot" style="background:#1890ff"></span>外卖 <strong>7</strong></span>
<span class="pcat-ch-stat"><span class="pcat-ch-dot" style="background:#52c41a"></span>自提 <strong>6</strong></span>
<span class="pcat-ch-stat"><span class="pcat-ch-dot" style="background:#fa8c16"></span>堂食 <strong>5</strong></span>
</span>
</div>
<div class="pcat-main">
<!-- 左侧分类列表 -->
<div class="pcat-left">
<div class="pcat-left-hd">
<div class="pcat-left-title">全部分类 <span>共 7 个</span></div>
<div class="pcat-ch-filter">
<span class="pcat-ch-pill active" onclick="filterByChannel(this,'all')">全部</span>
<span class="pcat-ch-pill" onclick="filterByChannel(this,'wm')">外卖</span>
<span class="pcat-ch-pill" onclick="filterByChannel(this,'zt')">自提</span>
<span class="pcat-ch-pill" onclick="filterByChannel(this,'ts')">堂食</span>
</div>
<div class="pcat-search">
<i data-lucide="search" style="width:14px;height:14px;"></i>
<input type="text" placeholder="搜索分类名称…" oninput="searchCategories(this.value)">
</div>
</div>
<div class="pcat-left-list" id="pcatList">
<div class="pcat-cat-item active" onclick="selectCategory(this)" data-name="热销推荐" data-desc="店铺热销菜品推荐" data-count="12" data-sort="1" data-channels="wm,zt,ts" data-status="on">
<span class="pcat-drag-handle"><i data-lucide="grip-vertical" style="width:14px;height:14px;"></i></span>
<span class="pcat-cat-name">热销推荐</span>
<span class="pcat-cat-tags"><span class="pcat-ch-mini wm"></span><span class="pcat-ch-mini zt"></span><span class="pcat-ch-mini ts"></span></span>
<span class="pcat-cat-badge">12</span>
</div>
<div class="pcat-cat-item" onclick="selectCategory(this)" data-name="主食" data-desc="米饭、面条等主食" data-count="8" data-sort="2" data-channels="wm,zt,ts" data-status="on">
<span class="pcat-drag-handle"><i data-lucide="grip-vertical" style="width:14px;height:14px;"></i></span>
<span class="pcat-cat-name">主食</span>
<span class="pcat-cat-tags"><span class="pcat-ch-mini wm"></span><span class="pcat-ch-mini zt"></span><span class="pcat-ch-mini ts"></span></span>
<span class="pcat-cat-badge">8</span>
</div>
<div class="pcat-cat-item" onclick="selectCategory(this)" data-name="小吃凉菜" data-desc="各类小吃与凉拌菜" data-count="15" data-sort="3" data-channels="wm,zt" data-status="on">
<span class="pcat-drag-handle"><i data-lucide="grip-vertical" style="width:14px;height:14px;"></i></span>
<span class="pcat-cat-name">小吃凉菜</span>
<span class="pcat-cat-tags"><span class="pcat-ch-mini wm"></span><span class="pcat-ch-mini zt"></span></span>
<span class="pcat-cat-badge">15</span>
</div>
<div class="pcat-cat-item" onclick="selectCategory(this)" data-name="汤羹粥品" data-desc="汤类、羹类、粥品" data-count="6" data-sort="4" data-channels="wm,ts" data-status="on">
<span class="pcat-drag-handle"><i data-lucide="grip-vertical" style="width:14px;height:14px;"></i></span>
<span class="pcat-cat-name">汤羹粥品</span>
<span class="pcat-cat-tags"><span class="pcat-ch-mini wm"></span><span class="pcat-ch-mini ts"></span></span>
<span class="pcat-cat-badge">6</span>
</div>
<div class="pcat-cat-item" onclick="selectCategory(this)" data-name="饮品" data-desc="各类冷热饮品" data-count="10" data-sort="5" data-channels="wm,zt,ts" data-status="on">
<span class="pcat-drag-handle"><i data-lucide="grip-vertical" style="width:14px;height:14px;"></i></span>
<span class="pcat-cat-name">饮品</span>
<span class="pcat-cat-tags"><span class="pcat-ch-mini wm"></span><span class="pcat-ch-mini zt"></span><span class="pcat-ch-mini ts"></span></span>
<span class="pcat-cat-badge">10</span>
</div>
<div class="pcat-cat-item" onclick="selectCategory(this)" data-name="酒水" data-desc="啤酒、白酒、红酒等" data-count="4" data-sort="6" data-channels="ts" data-status="on">
<span class="pcat-drag-handle"><i data-lucide="grip-vertical" style="width:14px;height:14px;"></i></span>
<span class="pcat-cat-name">酒水</span>
<span class="pcat-cat-tags"><span class="pcat-ch-mini ts"></span></span>
<span class="pcat-cat-badge">4</span>
</div>
<div class="pcat-cat-item disabled" onclick="selectCategory(this)" data-name="季节限定" data-desc="季节性限定菜品" data-count="3" data-sort="7" data-channels="wm" data-status="off">
<span class="pcat-drag-handle"><i data-lucide="grip-vertical" style="width:14px;height:14px;"></i></span>
<span class="pcat-cat-name">季节限定</span>
<span class="pcat-cat-tags"><span class="pcat-ch-mini wm"></span></span>
<span class="pcat-cat-badge">3</span>
</div>
</div>
</div>
<!-- 右侧详情 -->
<div class="pcat-right" id="pcatDetail">
<!-- 分类信息卡片 -->
<div class="pcat-info-card">
<div class="pcat-info-hd">
<div>
<div class="pcat-info-title" id="pcatDetailName">热销推荐</div>
<div class="pcat-info-desc" id="pcatDetailDesc">店铺热销菜品推荐</div>
</div>
<div class="pcat-info-actions">
<button class="g-btn" onclick="openCatDrawer('edit',document.getElementById('pcatDetailName').textContent)"><i data-lucide="pencil" style="width:13px;height:13px;"></i> 编辑</button>
<button class="g-btn g-btn-danger"><i data-lucide="trash-2" style="width:13px;height:13px;"></i> 删除</button>
</div>
</div>
<div class="pcat-attr-grid">
<div class="pcat-attr-item">
<span class="pcat-attr-label">商品数量</span>
<span class="pcat-attr-value" id="pcatDetailCount">12</span>
</div>
<div class="pcat-attr-item">
<span class="pcat-attr-label">排序权重</span>
<span class="pcat-attr-value" id="pcatDetailSort">1</span>
</div>
<div class="pcat-attr-item">
<span class="pcat-attr-label">启用状态</span>
<span class="pcat-attr-value" id="pcatDetailStatus"><span class="g-tag g-tag-green">已启用</span></span>
</div>
<div class="pcat-attr-item">
<span class="pcat-attr-label">分类图标</span>
<span class="pcat-attr-value" style="color:#9ca3af;font-weight:400;font-size:12px">未设置</span>
</div>
</div>
</div>
<!-- 渠道可见性 -->
<div class="pcat-channel-card">
<div class="pcat-channel-hd">渠道可见性</div>
<div class="pcat-channel-list" id="pcatChannelList">
<div class="pcat-channel-item">
<div class="pcat-ch-icon wm"><i data-lucide="bike" style="width:18px;height:18px;"></i></div>
<div class="pcat-ch-info">
<div class="pcat-ch-name">外卖</div>
<div class="pcat-ch-sub">外卖平台展示此分类</div>
</div>
<div class="pcat-ch-status on"><span class="pcat-ch-status-dot"></span>已开启</div>
</div>
<div class="pcat-channel-item">
<div class="pcat-ch-icon zt"><i data-lucide="shopping-bag" style="width:18px;height:18px;"></i></div>
<div class="pcat-ch-info">
<div class="pcat-ch-name">自提</div>
<div class="pcat-ch-sub">到店自提展示此分类</div>
</div>
<div class="pcat-ch-status on"><span class="pcat-ch-status-dot"></span>已开启</div>
</div>
<div class="pcat-channel-item">
<div class="pcat-ch-icon ts"><i data-lucide="utensils" style="width:18px;height:18px;"></i></div>
<div class="pcat-ch-info">
<div class="pcat-ch-name">堂食</div>
<div class="pcat-ch-sub">堂食扫码点餐展示此分类</div>
</div>
<div class="pcat-ch-status on"><span class="pcat-ch-status-dot"></span>已开启</div>
</div>
</div>
</div>
<!-- 分类下商品列表 -->
<div class="pcat-prod-card">
<div class="pcat-prod-hd">
<div class="pcat-prod-title">分类商品 <span id="pcatProdCount">12 个商品</span></div>
<button class="g-btn g-btn-sm" onclick="openProductPicker({title:'添加商品到分类',subtitle:document.getElementById('pcatDetailName').textContent,onConfirm:function(items){document.getElementById('pcatProdCount').textContent=items.length+' 个商品'}})"><i data-lucide="plus" style="width:12px;height:12px;"></i> 添加商品到此分类</button>
</div>
<table class="pcat-prod-table">
<thead>
<tr><th>商品</th><th>价格</th><th>月销</th><th>状态</th><th>操作</th></tr>
</thead>
<tbody id="pcatProdList">
<tr>
<td><div class="pcat-prod-name"><div class="pcat-prod-thumb"><i data-lucide="image" style="width:16px;height:16px;"></i></div><div class="pcat-prod-info"><div class="name">招牌红烧肉饭</div><div class="spu">SPU20240001</div></div></div></td>
<td class="pcat-prod-price">&yen;28.00</td>
<td>128</td>
<td><span class="g-tag g-tag-green">在售</span></td>
<td><span class="g-action">移出</span></td>
</tr>
<tr>
<td><div class="pcat-prod-name"><div class="pcat-prod-thumb"><i data-lucide="image" style="width:16px;height:16px;"></i></div><div class="pcat-prod-info"><div class="name">香辣鸡腿堡套餐</div><div class="spu">SPU20240005</div></div></div></td>
<td class="pcat-prod-price">&yen;35.00</td>
<td>99</td>
<td><span class="g-tag g-tag-green">在售</span></td>
<td><span class="g-action">移出</span></td>
</tr>
<tr>
<td><div class="pcat-prod-name"><div class="pcat-prod-thumb"><i data-lucide="image" style="width:16px;height:16px;"></i></div><div class="pcat-prod-info"><div class="name">番茄牛腩面</div><div class="spu">SPU20240008</div></div></div></td>
<td class="pcat-prod-price">&yen;26.00</td>
<td>79</td>
<td><span class="g-tag g-tag-green">在售</span></td>
<td><span class="g-action">移出</span></td>
</tr>
<tr>
<td><div class="pcat-prod-name"><div class="pcat-prod-thumb"><i data-lucide="image" style="width:16px;height:16px;"></i></div><div class="pcat-prod-info"><div class="name">鱼香肉丝盖饭</div><div class="spu">SPU20240002</div></div></div></td>
<td class="pcat-prod-price">&yen;22.00</td>
<td>58</td>
<td><span class="g-tag g-tag-green">在售</span></td>
<td><span class="g-action">移出</span></td>
</tr>
<tr>
<td><div class="pcat-prod-name"><div class="pcat-prod-thumb"><i data-lucide="image" style="width:16px;height:16px;"></i></div><div class="pcat-prod-info"><div class="name">冰柠檬红茶</div><div class="spu">SPU20240012</div></div></div></td>
<td class="pcat-prod-price">&yen;8.00</td>
<td>45</td>
<td><span class="g-tag g-tag-gray" style="opacity:0.7">下架</span></td>
<td><span class="g-action">移出</span></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<!-- 添加/编辑分类抽屉 -->
<div class="g-drawer-mask" id="pcatMask" onclick="closeCatDrawer()"></div>
<div class="g-drawer" id="pcatDrawer" style="width:480px">
<div class="g-drawer-hd">
<span class="g-drawer-title" id="pcatDrawerTitle">添加分类</span>
<button class="g-drawer-close" onclick="closeCatDrawer()"><i data-lucide="x" style="width:18px;height:18px;"></i></button>
</div>
<div class="g-drawer-bd">
<div class="g-form-group">
<label class="g-form-label required">分类名称</label>
<input type="text" class="g-input" id="pcatFormName" placeholder="请输入分类名称,如:热销推荐">
</div>
<div class="g-form-group">
<label class="g-form-label">分类描述</label>
<textarea class="g-textarea" id="pcatFormDesc" rows="2" placeholder="请输入分类描述,帮助顾客理解分类内容"></textarea>
</div>
<div class="g-form-group">
<label class="g-form-label">分类图标</label>
<div class="pcat-icon-upload" title="上传图标">
<i data-lucide="image-plus" style="width:22px;height:22px;"></i>
<span style="font-size:11px;">上传图标</span>
</div>
<div class="g-hint">建议尺寸 120x120px支持 PNG/SVG</div>
</div>
<div class="g-form-group">
<label class="g-form-label required">销售渠道</label>
<div class="g-hint" style="margin-bottom:8px;">选择该分类在哪些渠道展示,至少选择一个</div>
<div class="pcat-drawer-channels" id="pcatFormChannels">
<div class="pcat-drawer-ch checked" onclick="toggleChannel(this)" data-ch="wm">
<span class="pcat-ch-check"><svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg></span>
<i data-lucide="bike" style="width:14px;height:14px;"></i>
外卖
</div>
<div class="pcat-drawer-ch checked" onclick="toggleChannel(this)" data-ch="zt">
<span class="pcat-ch-check"><svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg></span>
<i data-lucide="shopping-bag" style="width:14px;height:14px;"></i>
自提
</div>
<div class="pcat-drawer-ch checked" onclick="toggleChannel(this)" data-ch="ts">
<span class="pcat-ch-check"><svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg></span>
<i data-lucide="utensils" style="width:14px;height:14px;"></i>
堂食
</div>
</div>
</div>
<div class="g-form-group">
<label class="g-form-label">排序</label>
<input type="number" class="g-input" id="pcatFormSort" placeholder="数字越小越靠前1" style="width:180px">
</div>
<div class="g-form-group">
<label class="g-form-label">启用状态</label>
<div style="display:flex;align-items:center;gap:10px;">
<button class="g-toggle on" id="pcatFormToggle" onclick="toggleSwitch(this)"></button>
<span class="g-toggle-label">启用</span>
</div>
</div>
</div>
<div class="g-drawer-ft">
<button class="g-btn" onclick="closeCatDrawer()">取消</button>
<button class="g-btn g-btn-primary" id="pcatDrawerSubmit" onclick="closeCatDrawer()">确认添加</button>
</div>
</div>
<script>
var _channelMap = {wm:{name:'外卖',icon:'bike',sub:'外卖平台展示此分类',cls:'wm'},zt:{name:'自提',icon:'shopping-bag',sub:'到店自提展示此分类',cls:'zt'},ts:{name:'堂食',icon:'utensils',sub:'堂食扫码点餐展示此分类',cls:'ts'}};
function openCatDrawer(mode, name) {
var title = document.getElementById('pcatDrawerTitle');
var submit = document.getElementById('pcatDrawerSubmit');
var nameInput = document.getElementById('pcatFormName');
var descInput = document.getElementById('pcatFormDesc');
var sortInput = document.getElementById('pcatFormSort');
var tog = document.getElementById('pcatFormToggle');
if (mode === 'edit' && name) {
title.textContent = '编辑分类';
submit.textContent = '保存修改';
nameInput.value = name;
var active = document.querySelector('#pcatList .pcat-cat-item.active');
if (active) {
descInput.value = active.getAttribute('data-desc') || '';
sortInput.value = active.getAttribute('data-sort') || '';
var channels = (active.getAttribute('data-channels') || '').split(',');
document.querySelectorAll('#pcatFormChannels .pcat-drawer-ch').forEach(function(ch) {
var key = ch.getAttribute('data-ch');
ch.classList.toggle('checked', channels.indexOf(key) > -1);
});
var isOn = active.getAttribute('data-status') !== 'off';
tog.classList.toggle('on', isOn);
}
} else {
title.textContent = '添加分类';
submit.textContent = '确认添加';
nameInput.value = '';
descInput.value = '';
sortInput.value = '';
document.querySelectorAll('#pcatFormChannels .pcat-drawer-ch').forEach(function(ch) { ch.classList.add('checked'); });
if (!tog.classList.contains('on')) tog.classList.add('on');
}
document.getElementById('pcatMask').classList.add('open');
document.getElementById('pcatDrawer').classList.add('open');
if (typeof lucide !== 'undefined') lucide.createIcons();
}
function closeCatDrawer() {
document.getElementById('pcatMask').classList.remove('open');
document.getElementById('pcatDrawer').classList.remove('open');
}
function selectCategory(el) {
document.querySelectorAll('#pcatList .pcat-cat-item').forEach(function(item) { item.classList.remove('active'); });
el.classList.add('active');
document.getElementById('pcatDetailName').textContent = el.getAttribute('data-name');
document.getElementById('pcatDetailDesc').textContent = el.getAttribute('data-desc');
document.getElementById('pcatDetailCount').textContent = el.getAttribute('data-count');
document.getElementById('pcatDetailSort').textContent = el.getAttribute('data-sort');
document.getElementById('pcatProdCount').textContent = el.getAttribute('data-count') + ' 个商品';
var isOn = el.getAttribute('data-status') !== 'off';
document.getElementById('pcatDetailStatus').innerHTML = isOn
? '<span class="g-tag g-tag-green">已启用</span>'
: '<span class="g-tag g-tag-gray">已停用</span>';
updateChannelDisplay(el.getAttribute('data-channels') || '');
}
function updateChannelDisplay(channelsStr) {
var channels = channelsStr ? channelsStr.split(',') : [];
var html = '';
['wm','zt','ts'].forEach(function(key) {
var ch = _channelMap[key];
var isOn = channels.indexOf(key) > -1;
html += '<div class="pcat-channel-item">'
+ '<div class="pcat-ch-icon ' + ch.cls + '"><i data-lucide="' + ch.icon + '" style="width:18px;height:18px;"></i></div>'
+ '<div class="pcat-ch-info"><div class="pcat-ch-name">' + ch.name + '</div><div class="pcat-ch-sub">' + ch.sub + '</div></div>'
+ '<div class="pcat-ch-status ' + (isOn ? 'on' : 'off') + '"><span class="pcat-ch-status-dot"></span>' + (isOn ? '已开启' : '未开启') + '</div>'
+ '</div>';
});
document.getElementById('pcatChannelList').innerHTML = html;
if (typeof lucide !== 'undefined') lucide.createIcons();
}
function searchCategories(keyword) {
var kw = keyword.trim().toLowerCase();
document.querySelectorAll('#pcatList .pcat-cat-item').forEach(function(item) {
var name = (item.getAttribute('data-name') || '').toLowerCase();
item.style.display = (!kw || name.indexOf(kw) > -1) ? '' : 'none';
});
}
function filterByChannel(el, channel) {
document.querySelectorAll('.pcat-ch-filter .pcat-ch-pill').forEach(function(p) { p.classList.remove('active'); });
el.classList.add('active');
document.querySelectorAll('#pcatList .pcat-cat-item').forEach(function(item) {
if (channel === 'all') { item.style.display = ''; return; }
var channels = (item.getAttribute('data-channels') || '').split(',');
item.style.display = channels.indexOf(channel) > -1 ? '' : 'none';
});
}
function toggleChannel(el) {
el.classList.toggle('checked');
}
function toggleSwitch(el) {
el.classList.toggle('on');
}
function openCopyStoreModal() {}
</script>

344
pages/product-combos.html Normal file
View File

@@ -0,0 +1,344 @@
<!-- 套餐管理页 -->
<style>
.pcmb-toolbar{display:flex;align-items:center;gap:12px;margin-bottom:16px;box-shadow:var(--g-shadow-sm);border-radius:10px;padding:12px 16px;background:#fff;flex-wrap:wrap}
.pcmb-search{position:relative}
.pcmb-search input{height:34px;padding:0 10px 0 32px;border:1px solid #e5e7eb;border-radius:8px;font-size:13px;outline:none;width:200px;transition:var(--g-transition)}
.pcmb-search input:focus{border-color:var(--primary);box-shadow:0 0 0 3px color-mix(in srgb,var(--primary) 12%,transparent)}
.pcmb-search i{position:absolute;left:9px;top:50%;transform:translateY(-50%);color:#bbb;pointer-events:none}
.pcmb-filters{display:flex;gap:8px}
.pcmb-filter-pill{padding:6px 16px;border-radius:8px;border:1px solid #e5e7eb;background:#fff;font-size:13px;cursor:pointer;color:#4b5563;transition:var(--g-transition)}
.pcmb-filter-pill:hover{border-color:var(--primary);color:var(--primary)}
.pcmb-filter-pill.active{background:var(--primary);color:#fff;border-color:var(--primary)}
.pcmb-stats{display:flex;gap:24px;margin-bottom:16px;padding:10px 16px;background:#fff;border-radius:10px;box-shadow:var(--g-shadow-sm);font-size:13px;color:#4b5563}
.pcmb-stats span{display:flex;align-items:center;gap:6px}
.pcmb-stats strong{color:#1a1a2e;font-weight:600}
.pcmb-list{display:flex;flex-direction:column;gap:12px}
.pcmb-card{display:flex;align-items:flex-start;gap:16px;background:#fff;border:none;border-radius:10px;padding:20px;font-size:13px;box-shadow:var(--g-shadow-sm);transition:var(--g-transition)}
.pcmb-card:hover{box-shadow:var(--g-shadow-md)}
.pcmb-card.disabled{opacity:0.5}
.pcmb-cover{width:100px;height:100px;border-radius:8px;background:#f8f9fb;display:flex;align-items:center;justify-content:center;color:#bbb;font-size:12px;flex-shrink:0;border:1px solid #e5e7eb}
.pcmb-info{flex:1;min-width:0}
.pcmb-name{font-weight:600;font-size:15px;margin-bottom:6px;display:flex;align-items:center;gap:8px;color:#1a1a2e}
.pcmb-tag-fixed{background:#e6f0ff;color:#1a6dff;border-radius:6px;font-weight:600}
.pcmb-tag-custom{background:#e6f7e9;color:#18a058;border-radius:6px;font-weight:600}
.pcmb-desc{color:#9ca3af;margin-bottom:6px;font-size:12px}
.pcmb-breakdown{color:#4b5563;margin-bottom:8px;line-height:1.6}
.pcmb-breakdown .group-label{color:#6b7280;font-size:12px}
.pcmb-price-row{display:flex;align-items:baseline;gap:10px;margin-bottom:8px}
.pcmb-price{color:#ef4444;font-size:18px;font-weight:700}
.pcmb-price-orig{color:#9ca3af;text-decoration:line-through;font-size:12px}
.pcmb-price-save{color:#22c55e;font-size:12px;font-weight:600}
.pcmb-stats-text{color:#9ca3af;font-size:12px}
.pcmb-right{display:flex;flex-direction:column;align-items:flex-end;gap:8px;flex-shrink:0}
.pcmb-status{padding:2px 10px;border-radius:6px;font-size:11px;font-weight:600}
.pcmb-status-on{background:#dcfce7;color:#22c55e}
.pcmb-status-off{background:#f8f9fb;color:#9ca3af}
.pcmb-actions{display:flex;gap:6px}
.pcmb-act{height:34px;padding:0 10px;border:1px solid #e5e7eb;border-radius:8px;background:#fff;font-size:12px;cursor:pointer;color:#4b5563;transition:var(--g-transition)}
.pcmb-act:hover{border-color:var(--primary);color:var(--primary)}
.pcmb-act-del:hover{border-color:#ef4444;color:#ef4444}
.pcmb-type-pills{display:flex;gap:8px}
.pcmb-type-pill{padding:6px 20px;border-radius:8px;border:1px solid #e5e7eb;background:#fff;font-size:13px;cursor:pointer;color:#4b5563;transition:var(--g-transition)}
.pcmb-type-pill:hover{border-color:var(--primary);color:var(--primary)}
.pcmb-type-pill.active{background:var(--primary);color:#fff;border-color:var(--primary)}
.pcmb-upload-box{width:100px;height:100px;border:1px dashed #e5e7eb;border-radius:8px;display:flex;flex-direction:column;align-items:center;justify-content:center;color:#bbb;font-size:12px;cursor:pointer;gap:4px;transition:var(--g-transition)}
.pcmb-upload-box:hover{border-color:var(--primary);color:var(--primary)}
.pcmb-section-title{font-size:14px;font-weight:600;margin-bottom:10px;color:#1a1a2e;padding-left:10px;border-left:3px solid var(--primary)}
.pcmb-group-card{background:#f8f9fb;border-radius:8px;padding:14px;margin-bottom:10px;border:1px solid #e5e7eb}
.pcmb-group-header{display:flex;align-items:center;gap:8px;margin-bottom:10px}
.pcmb-group-header .pcmb-input{flex:1;height:34px}
.pcmb-group-header span{font-size:12px;color:#6b7280;white-space:nowrap}
.pcmb-group-header .pcmb-input-sm{width:50px;height:34px;text-align:center}
.pcmb-group-remove{background:none;border:none;color:#9ca3af;cursor:pointer;display:flex;align-items:center;justify-content:center;width:28px;height:28px;border-radius:8px;transition:var(--g-transition)}
.pcmb-group-remove:hover{color:#ef4444;background:#fef2f2}
.pcmb-group-items{margin-bottom:8px}
.pcmb-group-item{display:flex;align-items:center;gap:8px;margin-bottom:6px;font-size:12px;color:#4b5563}
.pcmb-group-item span{flex:1}
.pcmb-group-item .qty{color:#9ca3af;width:40px;text-align:center}
.pcmb-item-remove{background:none;border:none;color:#9ca3af;cursor:pointer;display:flex;align-items:center;justify-content:center;width:24px;height:24px;border-radius:6px;transition:var(--g-transition)}
.pcmb-item-remove:hover{color:#ef4444;background:#fef2f2}
.pcmb-add-link{color:var(--primary);font-size:12px;cursor:pointer;background:none;border:none;padding:0;display:flex;align-items:center;gap:4px;transition:var(--g-transition)}
.pcmb-add-link:hover{text-decoration:underline}
.pcmb-add-group-btn{width:100%;height:36px;border:1px dashed #e5e7eb;border-radius:8px;background:#fff;color:#9ca3af;font-size:13px;cursor:pointer;margin-top:4px;display:flex;align-items:center;justify-content:center;gap:4px;transition:var(--g-transition)}
.pcmb-add-group-btn:hover{border-color:var(--primary);color:var(--primary)}
.pcmb-price-inputs{display:flex;gap:12px;align-items:center}
.pcmb-price-inputs label{font-size:12px;color:#6b7280;font-weight:500;white-space:nowrap}
.pcmb-price-inputs .pcmb-input{width:120px}
.pcmb-input{width:100%;height:34px;padding:0 10px;border:1px solid #e5e7eb;border-radius:8px;font-size:13px;box-sizing:border-box;transition:var(--g-transition);color:#1a1a2e}
.pcmb-input::placeholder{color:#bbb}
.pcmb-input:focus{outline:none;border-color:var(--primary);box-shadow:0 0 0 3px color-mix(in srgb,var(--primary) 12%,transparent)}
</style>
<div class="pcmb-toolbar">
<select class="g-select" style="width:200px;">
<option>老三家外卖(朝阳店)</option>
<option>老三家外卖(海淀店)</option>
<option>老三家外卖(望京店)</option>
<option>老三家外卖(通州店)</option>
<option>老三家外卖(丰台店)</option>
</select>
<div class="pcmb-search">
<i data-lucide="search" style="width:14px;height:14px;"></i>
<input type="text" placeholder="搜索套餐名称…" oninput="searchCombos(this.value)" />
</div>
<div class="pcmb-filters">
<span class="pcmb-filter-pill active" onclick="filterComboType(this)" data-type="all">全部</span>
<span class="pcmb-filter-pill" onclick="filterComboType(this)" data-type="fixed">固定套餐</span>
<span class="pcmb-filter-pill" onclick="filterComboType(this)" data-type="custom">自选套餐</span>
</div>
<div style="flex:1;"></div>
<button class="g-btn g-btn-primary" onclick="openComboDrawer('add')"><i data-lucide="plus" style="width:14px;height:14px;"></i>添加套餐</button>
</div>
<div class="pcmb-stats">
<span>套餐 <strong>4</strong></span>
<span>在售 <strong>3</strong></span>
<span>月总销量 <strong>265</strong></span>
</div>
<div class="pcmb-list">
<div class="pcmb-card" data-combo-type="fixed" data-name="双人超值套餐">
<div class="pcmb-cover">暂无图片</div>
<div class="pcmb-info">
<div class="pcmb-name">双人超值套餐 <span class="g-tag pcmb-tag-fixed">固定套餐</span></div>
<div class="pcmb-desc">超值双人组合,荤素搭配</div>
<div class="pcmb-breakdown">宫保鸡丁 + 鱼香肉丝 + 米饭×2 + 饮品×2</div>
<div class="pcmb-price-row"><span class="pcmb-price">¥68</span><span class="pcmb-price-orig">¥96</span><span class="pcmb-price-save">已省 ¥28</span></div>
<div class="pcmb-stats-text">月销 45</div>
</div>
<div class="pcmb-right">
<span class="pcmb-status pcmb-status-on">在售</span>
<div class="pcmb-actions">
<button class="pcmb-act" onclick="openComboDrawer('edit','双人超值套餐')">编辑</button>
<button class="pcmb-act">下架</button>
<button class="pcmb-act pcmb-act-del">删除</button>
</div>
</div>
</div>
<div class="pcmb-card" data-combo-type="fixed" data-name="单人工作餐">
<div class="pcmb-cover">暂无图片</div>
<div class="pcmb-info">
<div class="pcmb-name">单人工作餐 <span class="g-tag pcmb-tag-fixed">固定套餐</span></div>
<div class="pcmb-desc">简单快捷,工作日首选</div>
<div class="pcmb-breakdown">蛋炒饭 + 酸辣汤 + 凉拌黄瓜</div>
<div class="pcmb-price-row"><span class="pcmb-price">¥35</span><span class="pcmb-price-orig">¥45</span><span class="pcmb-price-save">已省 ¥10</span></div>
<div class="pcmb-stats-text">月销 120</div>
</div>
<div class="pcmb-right">
<span class="pcmb-status pcmb-status-on">在售</span>
<div class="pcmb-actions">
<button class="pcmb-act" onclick="openComboDrawer('edit','单人工作餐')">编辑</button>
<button class="pcmb-act">下架</button>
<button class="pcmb-act pcmb-act-del">删除</button>
</div>
</div>
</div>
<div class="pcmb-card" data-combo-type="custom" data-name="任选午餐">
<div class="pcmb-cover">暂无图片</div>
<div class="pcmb-info">
<div class="pcmb-name">任选午餐 <span class="g-tag pcmb-tag-custom">自选套餐</span></div>
<div class="pcmb-desc">自由搭配,满足不同口味</div>
<div class="pcmb-breakdown">
<span class="group-label">A 主食(选1):</span> 宫保鸡丁 / 鱼香肉丝 / 麻婆豆腐<br>
<span class="group-label">B 小食(选1):</span> 凉拌黄瓜 / 酸辣汤<br>
<span class="group-label">C 饮品(选1):</span> 冰粉 / 可乐
</div>
<div class="pcmb-price-row"><span class="pcmb-price">¥38</span></div>
<div class="pcmb-stats-text">月销 88</div>
</div>
<div class="pcmb-right">
<span class="pcmb-status pcmb-status-on">在售</span>
<div class="pcmb-actions">
<button class="pcmb-act" onclick="openComboDrawer('edit','任选午餐')">编辑</button>
<button class="pcmb-act">下架</button>
<button class="pcmb-act pcmb-act-del">删除</button>
</div>
</div>
</div>
<div class="pcmb-card disabled" data-combo-type="fixed" data-name="家庭聚餐套餐">
<div class="pcmb-cover">暂无图片</div>
<div class="pcmb-info">
<div class="pcmb-name">家庭聚餐套餐 <span class="g-tag pcmb-tag-fixed">固定套餐</span></div>
<div class="pcmb-desc">家庭聚餐,丰盛实惠</div>
<div class="pcmb-breakdown">红烧排骨 + 宫保鸡丁 + 麻婆豆腐 + 蛋炒饭×2 + 酸辣汤×2</div>
<div class="pcmb-price-row"><span class="pcmb-price">¥128</span><span class="pcmb-price-orig">¥168</span><span class="pcmb-price-save">已省 ¥40</span></div>
<div class="pcmb-stats-text">月销 12</div>
</div>
<div class="pcmb-right">
<span class="pcmb-status pcmb-status-off">已下架</span>
<div class="pcmb-actions">
<button class="pcmb-act" onclick="openComboDrawer('edit','家庭聚餐套餐')">编辑</button>
<button class="pcmb-act">上架</button>
<button class="pcmb-act pcmb-act-del">删除</button>
</div>
</div>
</div>
</div>
<!-- 添加/编辑套餐抽屉 -->
<div class="g-drawer-mask" id="pcmbDrawerMask" onclick="closeComboDrawer()"></div>
<div class="g-drawer" id="pcmbDrawer" style="width:560px;">
<div class="g-drawer-hd">
<span class="g-drawer-title" id="pcmbDrawerTitle">添加套餐</span>
<button class="g-drawer-close" onclick="closeComboDrawer()"><i data-lucide="x" style="width:18px;height:18px;"></i></button>
</div>
<div class="g-drawer-bd">
<div class="g-form-group">
<label class="g-form-label required">套餐名称</label>
<input class="g-input" id="pcmbName" placeholder="请输入套餐名称,如:双人超值套餐">
</div>
<div class="g-form-group">
<label class="g-form-label required">套餐类型</label>
<div class="pcmb-type-pills">
<span class="pcmb-type-pill active" onclick="selectComboType(this)">固定套餐</span>
<span class="pcmb-type-pill" onclick="selectComboType(this)">自选套餐</span>
</div>
<div class="g-hint">固定套餐:商品固定搭配;自选套餐:顾客从分组中自由选择</div>
</div>
<div class="g-form-group">
<label class="g-form-label">封面图</label>
<div class="pcmb-upload-box">
<i data-lucide="image-plus" style="width:24px;height:24px;"></i>
<span>上传图片</span>
</div>
</div>
<div class="g-form-group">
<label class="g-form-label">套餐描述</label>
<textarea class="g-textarea" rows="2" placeholder="请输入套餐描述,如:超值双人组合,荤素搭配"></textarea>
</div>
<div class="g-form-group">
<div class="pcmb-section-title">套餐分组</div>
<div class="g-hint" style="margin-bottom:10px;">每个分组可包含多个商品,顾客按分组选择</div>
<div id="pcmbGroups">
<div class="pcmb-group-card">
<div class="pcmb-group-header">
<input class="pcmb-input" value="主菜" placeholder="请输入分组名称">
<span></span><input class="pcmb-input pcmb-input-sm" type="number" value="1" min="1"><span></span>
<button class="pcmb-group-remove" onclick="removeComboGroup(this)"><i data-lucide="trash-2" style="width:14px;height:14px;"></i></button>
</div>
<div class="pcmb-group-items">
<div class="pcmb-group-item"><span>宫保鸡丁</span><span class="qty">×1</span><button class="pcmb-item-remove" onclick="this.closest('.pcmb-group-item').remove()"><i data-lucide="x" style="width:12px;height:12px;"></i></button></div>
<div class="pcmb-group-item"><span>鱼香肉丝</span><span class="qty">×1</span><button class="pcmb-item-remove" onclick="this.closest('.pcmb-group-item').remove()"><i data-lucide="x" style="width:12px;height:12px;"></i></button></div>
</div>
<button class="pcmb-add-link" onclick="openProductPicker({title:'添加商品到分组',onConfirm:addComboProducts})"><i data-lucide="plus" style="width:12px;height:12px;"></i> 添加商品</button>
</div>
<div class="pcmb-group-card">
<div class="pcmb-group-header">
<input class="pcmb-input" value="主食" placeholder="请输入分组名称">
<span></span><input class="pcmb-input pcmb-input-sm" type="number" value="1" min="1"><span></span>
<button class="pcmb-group-remove" onclick="removeComboGroup(this)"><i data-lucide="trash-2" style="width:14px;height:14px;"></i></button>
</div>
<div class="pcmb-group-items">
<div class="pcmb-group-item"><span>米饭</span><span class="qty">×2</span><button class="pcmb-item-remove" onclick="this.closest('.pcmb-group-item').remove()"><i data-lucide="x" style="width:12px;height:12px;"></i></button></div>
</div>
<button class="pcmb-add-link" onclick="openProductPicker({title:'添加商品到分组',onConfirm:addComboProducts})"><i data-lucide="plus" style="width:12px;height:12px;"></i> 添加商品</button>
</div>
</div>
<button class="pcmb-add-group-btn" onclick="addComboGroup()"><i data-lucide="plus" style="width:14px;height:14px;"></i> 添加分组</button>
</div>
<div class="g-form-group">
<label class="g-form-label required">价格设置</label>
<div class="pcmb-price-inputs">
<label>套餐价</label><input class="pcmb-input" type="number" placeholder="如68.00">
<label>原价</label><input class="pcmb-input" type="number" placeholder="自动计算" readonly style="background:#f9f9f9">
</div>
<div class="g-hint">原价根据分组内商品单价自动计算</div>
</div>
</div>
<div class="g-drawer-ft">
<button class="g-btn" onclick="closeComboDrawer()">取消</button>
<button class="g-btn g-btn-primary" onclick="closeComboDrawer()">保存</button>
</div>
</div>
<script>
function openComboDrawer(mode, name) {
document.getElementById('pcmbDrawer').classList.add('open');
document.getElementById('pcmbDrawerMask').classList.add('open');
document.getElementById('pcmbDrawerTitle').textContent = mode === 'edit' ? '编辑套餐' : '添加套餐';
if (mode === 'edit' && name) {
document.getElementById('pcmbName').value = name;
} else {
document.getElementById('pcmbName').value = '';
}
if (typeof lucide !== 'undefined') lucide.createIcons();
}
function closeComboDrawer() {
document.getElementById('pcmbDrawer').classList.remove('open');
document.getElementById('pcmbDrawerMask').classList.remove('open');
}
function selectComboType(el) {
el.parentElement.querySelectorAll('.pcmb-type-pill').forEach(function(p) { p.classList.remove('active'); });
el.classList.add('active');
}
function addComboGroup() {
var g = document.createElement('div');
g.className = 'pcmb-group-card';
var h = document.createElement('div');
h.className = 'pcmb-group-header';
h.innerHTML = '<input class="pcmb-input" placeholder="请输入分组名称"><span>选</span><input class="pcmb-input pcmb-input-sm" type="number" value="1" min="1"><span>份</span>';
var rb = document.createElement('button');
rb.className = 'pcmb-group-remove';
rb.onclick = function(){ removeComboGroup(this); };
rb.innerHTML = '<i data-lucide="trash-2" style="width:14px;height:14px;"></i>';
h.appendChild(rb);
var items = document.createElement('div');
items.className = 'pcmb-group-items';
var addBtn = document.createElement('button');
addBtn.className = 'pcmb-add-link';
addBtn.innerHTML = '<i data-lucide="plus" style="width:12px;height:12px;"></i> 添加商品';
addBtn.onclick = function(){ openProductPicker({title:'添加商品到分组',onConfirm:addComboProducts}); };
g.appendChild(h);
g.appendChild(items);
g.appendChild(addBtn);
document.getElementById('pcmbGroups').appendChild(g);
if (typeof lucide !== 'undefined') lucide.createIcons();
}
function removeComboGroup(el) {
var card = el.closest('.pcmb-group-card');
if (card) card.remove();
}
function filterComboType(el) {
el.parentElement.querySelectorAll('.pcmb-filter-pill').forEach(function(p) { p.classList.remove('active'); });
el.classList.add('active');
var type = el.getAttribute('data-type');
document.querySelectorAll('.pcmb-card').forEach(function(card) {
if (type === 'all') { card.style.display = ''; }
else { card.style.display = card.getAttribute('data-combo-type') === type ? '' : 'none'; }
});
}
function searchCombos(keyword) {
var kw = keyword.trim().toLowerCase();
document.querySelectorAll('.pcmb-card').forEach(function(card) {
var name = (card.getAttribute('data-name') || '').toLowerCase();
card.style.display = (!kw || name.includes(kw)) ? '' : 'none';
});
}
function addComboProducts(items) {
var groups = document.querySelectorAll('#pcmbGroups .pcmb-group-card');
var lastGroup = groups.length ? groups[groups.length - 1] : null;
if (!lastGroup) return;
var list = lastGroup.querySelector('.pcmb-group-items');
items.forEach(function(p) {
var d = document.createElement('div');
d.className = 'pcmb-group-item';
var nameSpan = document.createElement('span');
nameSpan.textContent = p.name;
var qtySpan = document.createElement('span');
qtySpan.className = 'qty';
qtySpan.textContent = '×1';
var rmBtn = document.createElement('button');
rmBtn.className = 'pcmb-item-remove';
rmBtn.innerHTML = '<i data-lucide="x" style="width:12px;height:12px;"></i>';
rmBtn.onclick = function(){ this.closest('.pcmb-group-item').remove(); };
d.appendChild(nameSpan);
d.appendChild(qtySpan);
d.appendChild(rmBtn);
list.appendChild(d);
});
if (typeof lucide !== 'undefined') lucide.createIcons();
}
</script>

473
pages/product-detail.html Normal file
View File

@@ -0,0 +1,473 @@
<!-- 商品详情页 -->
<style>
.pd-page{font-size:13px}
.pd-header{display:flex;align-items:center;gap:12px;margin-bottom:18px;flex-wrap:wrap}
.pd-back{width:34px;height:34px;border-radius:8px;border:1px solid #e5e7eb;background:#fff;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;color:#4b5563;transition:var(--g-transition);flex-shrink:0}
.pd-back:hover{border-color:var(--primary);color:var(--primary);box-shadow:var(--g-shadow-sm)}
.pd-back:focus{box-shadow:0 0 0 3px color-mix(in srgb, var(--primary) 12%, transparent)}
.pd-hd-name{font-size:18px;font-weight:700;color:#1a1a2e}
.pd-hd-spu{font-size:12px;color:#9ca3af;font-family:monospace}
.pd-hd-status{display:inline-block;padding:2px 12px;border-radius:6px;font-size:11px;font-weight:600;color:#fff;margin-left:4px}
.pd-hd-spacer{flex:1}
.pd-body{display:flex;gap:18px;align-items:flex-start}
.pd-nav{width:160px;flex-shrink:0;background:#fff;border-radius:10px;border:none;box-shadow:var(--g-shadow-md);padding:8px 0;position:sticky;top:0}
.pd-nav-item{display:block;padding:9px 20px;font-size:13px;color:#4b5563;cursor:pointer;border-left:3px solid transparent;transition:var(--g-transition);text-decoration:none}
.pd-nav-item:hover{color:var(--primary);background:#f8f9fb}
.pd-nav-item.active{color:var(--primary);font-weight:600;border-left-color:var(--primary);background:color-mix(in srgb,var(--primary) 6%,transparent)}
.pd-content{flex:1;min-width:0;display:flex;flex-direction:column;gap:16px}
.pd-row{display:flex;align-items:flex-start;margin-bottom:14px;gap:12px}
.pd-label{width:80px;flex-shrink:0;font-size:13px;color:#4b5563;font-weight:500;line-height:34px;text-align:right}
.pd-label.required::before{content:'*';color:#ef4444;margin-right:2px}
.pd-ctrl{flex:1;min-width:0}
.pd-inline{display:flex;gap:10px;align-items:center}
.pd-inline .g-input{width:auto;flex:1}
.pd-unit{font-size:12px;color:#9ca3af;white-space:nowrap}
/* Upload */
.pd-thumbs{display:flex;gap:10px;margin-top:12px;flex-wrap:wrap}
.pd-thumb{width:80px;height:80px;border-radius:8px;background:#f8f9fb;display:flex;align-items:center;justify-content:center;position:relative;border:1px solid #e5e7eb;transition:var(--g-transition)}
.pd-thumb:hover{box-shadow:var(--g-shadow-sm)}
.pd-thumb-main{border-color:var(--primary);box-shadow:0 0 0 1px var(--primary)}
.pd-thumb-badge{position:absolute;bottom:-1px;left:0;right:0;text-align:center;font-size:10px;color:#fff;background:var(--primary);border-radius:0 0 7px 7px;padding:1px 0}
.pd-thumb-del{position:absolute;top:-6px;right:-6px;width:18px;height:18px;border-radius:50%;background:#ef4444;color:#fff;font-size:12px;border:none;cursor:pointer;display:flex;align-items:center;justify-content:center;line-height:1;transition:var(--g-transition)}
.pd-thumb-del:hover{background:#dc2626}
/* Pills */
.pd-pills{display:flex;flex-wrap:wrap;gap:8px}
/* Toggle */
.pd-toggle-row{display:flex;align-items:center;gap:12px;margin-bottom:10px}
/* Tags */
.pd-tag-sel{display:inline-flex;align-items:center;padding:4px 12px;border-radius:6px;font-size:12px;cursor:pointer;border:1px solid #e5e7eb;transition:var(--g-transition);user-select:none;margin:0 8px 8px 0}
.pd-tag-sel:hover{border-color:var(--primary);box-shadow:var(--g-shadow-sm)}
.pd-tag-sel.selected{border-color:var(--primary);background:color-mix(in srgb,var(--primary) 8%,transparent);color:var(--primary);font-weight:600}
/* Shelf radio */
.pd-shelf-group{display:flex;flex-direction:column;gap:10px}
.pd-shelf-opt{display:flex;align-items:flex-start;gap:8px;padding:12px;border:1px solid #e5e7eb;border-radius:10px;cursor:pointer;transition:var(--g-transition)}
.pd-shelf-opt:hover{border-color:var(--primary);box-shadow:var(--g-shadow-sm)}
.pd-shelf-opt.active{border-color:var(--primary);background:color-mix(in srgb,var(--primary) 5%,transparent)}
.pd-shelf-opt input[type=radio]{margin-top:2px;accent-color:var(--primary);cursor:pointer}
.pd-shelf-opt-body{flex:1}
.pd-shelf-opt-title{font-size:13px;font-weight:600;color:#1a1a2e}
.pd-shelf-opt-desc{font-size:12px;color:#9ca3af;margin-top:2px}
.pd-shelf-time{margin-top:8px;display:none;gap:8px;align-items:center}
.pd-shelf-opt.active .pd-shelf-time.show-active{display:flex}
/* Save bar */
.pd-savebar{background:#fafbfc;border-radius:10px;border:none;box-shadow:var(--g-shadow-sm);padding:14px 24px;display:flex;justify-content:flex-end;gap:10px;position:sticky;bottom:0;z-index:10}
/* SKU Matrix */
.pd-sku-hd{display:flex;align-items:center;justify-content:space-between;margin-bottom:14px}
.pd-sku-hd-left{display:flex;align-items:center;gap:10px}
.pd-sku-hd-left .g-card-title{margin:0}
.pd-sku-count{font-size:12px;color:#9ca3af;font-weight:400}
.pd-sku-hint{font-size:12px;color:#9ca3af}
.pd-sku-table{width:100%;border-collapse:collapse;font-size:13px}
.pd-sku-table th{text-align:left;padding:9px 12px;font-size:12px;font-weight:500;color:#6b7280;background:#f8f9fb;border-bottom:1px solid #f0f0f0;white-space:nowrap}
.pd-sku-table td{padding:8px 12px;border-bottom:1px solid #f3f4f6;color:#1a1a2e;vertical-align:middle}
.pd-sku-table tr:last-child td{border-bottom:none}
.pd-sku-table tr:hover td{background:color-mix(in srgb,var(--primary) 3%,#fff)}
.pd-sku-table input[type=number]{width:90px;height:30px;padding:0 8px;border:1px solid #e5e7eb;border-radius:6px;font-size:13px;color:#1a1a2e;transition:var(--g-transition);text-align:right}
.pd-sku-table input[type=number]:focus{border-color:var(--primary);box-shadow:0 0 0 3px color-mix(in srgb,var(--primary) 12%,transparent);outline:none}
.pd-sku-spec{display:inline-block;padding:2px 8px;border-radius:4px;font-size:11px;font-weight:500;background:#f0f0f0;color:#4b5563}
.pd-sku-spec.s1{background:color-mix(in srgb,var(--primary) 10%,#fff);color:var(--primary)}
.pd-sku-spec.s2{background:color-mix(in srgb,#fa8c16 10%,#fff);color:#fa8c16}
.pd-sku-code{font-size:11px;color:#9ca3af;font-family:monospace}
.pd-sku-stock-warn{color:#fa8c16;font-size:11px;display:flex;align-items:center;gap:3px;margin-top:2px}
.pd-sku-batch{display:flex;gap:8px;align-items:center;margin-bottom:12px}
.pd-sku-batch label{font-size:12px;color:#6b7280;white-space:nowrap}
.pd-sku-batch input[type=number]{width:100px;height:30px;padding:0 8px;border:1px solid #e5e7eb;border-radius:6px;font-size:13px;color:#1a1a2e}
.pd-sku-toggle{display:flex;align-items:center;justify-content:center}
</style>
<div class="pd-page">
<!-- Header -->
<div class="pd-header">
<button class="pd-back" onclick="navigateTo('商品列表')" title="返回列表">&#x2190;</button>
<span class="pd-hd-name" id="pdName">宫保鸡丁</span>
<span class="pd-hd-spu">SPU20240001</span>
<span class="pd-hd-status" style="background:#52c41a">在售</span>
<span class="pd-hd-spacer"></span>
<button class="g-btn g-btn-danger">删除商品</button>
<button class="g-btn">下架</button>
</div>
<div class="pd-body">
<!-- Left anchor nav -->
<div class="pd-nav" id="pdNav">
<a class="pd-nav-item active" data-target="pd-sec-basic" onclick="scrollToPdSec(this)">基本信息</a>
<a class="pd-nav-item" data-target="pd-sec-images" onclick="scrollToPdSec(this)">商品图片</a>
<a class="pd-nav-item" data-target="pd-sec-pricing" onclick="scrollToPdSec(this)">价格库存</a>
<a class="pd-nav-item" data-target="pd-sec-specs" onclick="scrollToPdSec(this)">规格做法</a>
<a class="pd-nav-item" data-target="pd-sec-sku" onclick="scrollToPdSec(this)">SKU管理</a>
<a class="pd-nav-item" data-target="pd-sec-addons" onclick="scrollToPdSec(this)">加料管理</a>
<a class="pd-nav-item" data-target="pd-sec-tags" onclick="scrollToPdSec(this)">商品标签</a>
<a class="pd-nav-item" data-target="pd-sec-shelf" onclick="scrollToPdSec(this)">上架设置</a>
</div>
<!-- Right content -->
<div class="pd-content" id="pdContent">
<!-- 基本信息 -->
<div class="g-card" id="pd-sec-basic">
<div class="g-card-title">基本信息</div>
<div class="pd-row">
<label class="pd-label required">商品名称</label>
<div class="pd-ctrl"><input class="g-input" value="宫保鸡丁" maxlength="30" /><div class="g-hint">最多30个字符</div></div>
</div>
<div class="pd-row">
<label class="pd-label">副标题</label>
<div class="pd-ctrl"><input class="g-input" value="经典川菜,香辣可口" maxlength="50" /></div>
</div>
<div class="pd-row">
<label class="pd-label">SPU编码</label>
<div class="pd-ctrl"><input class="g-input" value="SPU20240001" readonly style="background:#fafafa;color:#999" /></div>
</div>
<div class="pd-row">
<label class="pd-label required">所属分类</label>
<div class="pd-ctrl">
<select class="g-select" style="cursor:pointer">
<option>热销推荐</option><option selected>主食</option><option>小吃凉菜</option>
<option>汤羹粥品</option><option>饮品</option><option>酒水</option><option>套餐</option>
</select>
</div>
</div>
<div class="pd-row">
<label class="pd-label">商品描述</label>
<div class="pd-ctrl"><textarea class="g-textarea" rows="3" placeholder="详细描述,展示在商品详情页">花生米、鸡丁、干辣椒爆炒,麻辣鲜香,佐饭佳品。</textarea></div>
</div>
<div class="pd-row">
<label class="pd-label">排序权重</label>
<div class="pd-ctrl"><div class="pd-inline"><input class="g-input" type="number" min="0" value="10" style="width:100px" /><span class="pd-unit">数值越大越靠前</span></div></div>
</div>
</div>
<!-- 商品图片 -->
<div class="g-card" id="pd-sec-images">
<div class="g-card-title">商品图片</div>
<div class="g-upload-zone" onclick="this.querySelector('input').click()">
<input type="file" accept="image/*" multiple style="display:none" />
<svg viewBox="0 0 24 24" width="32" height="32" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M12 5v14M5 12h14"/></svg>
点击或拖拽上传图片<br><span style="font-size:11px;color:#bbb">建议尺寸 750×750最多5张首张为主图</span>
</div>
<div class="pd-thumbs">
<div class="pd-thumb pd-thumb-main"><svg viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="#ccc" stroke-width="1.5"><path d="M3 6h18v12H3z"/><circle cx="12" cy="12" r="3"/></svg><span class="pd-thumb-badge">主图</span><button class="pd-thumb-del">&times;</button></div>
<div class="pd-thumb"><svg viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="#ccc" stroke-width="1.5"><path d="M3 6h18v12H3z"/><circle cx="12" cy="12" r="3"/></svg><button class="pd-thumb-del">&times;</button></div>
<div class="pd-thumb"><svg viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="#ccc" stroke-width="1.5"><path d="M3 6h18v12H3z"/><circle cx="12" cy="12" r="3"/></svg><button class="pd-thumb-del">&times;</button></div>
</div>
</div>
<!-- 价格库存 -->
<div class="g-card" id="pd-sec-pricing">
<div class="g-card-title">价格库存</div>
<div style="background:color-mix(in srgb,var(--primary) 6%,#fff);border:1px solid color-mix(in srgb,var(--primary) 15%,#fff);border-radius:8px;padding:8px 12px;margin-bottom:14px;font-size:12px;color:var(--primary);display:flex;align-items:center;gap:6px">
<i data-lucide="info" style="width:14px;height:14px;flex-shrink:0"></i>
已启用多规格售价和库存以下方「SKU管理」中的各规格组合为准此处为默认基准价
</div>
<div class="pd-row">
<label class="pd-label required">售价</label>
<div class="pd-ctrl"><div class="pd-inline"><span class="pd-unit">&yen;</span><input class="g-input" type="number" min="0" step="0.01" value="32" /></div></div>
</div>
<div class="pd-row">
<label class="pd-label">划线价</label>
<div class="pd-ctrl"><div class="pd-inline"><span class="pd-unit">&yen;</span><input class="g-input" type="number" min="0" step="0.01" value="38" /></div><div class="g-hint">展示在售价旁,划线显示原价</div></div>
</div>
<div class="pd-row">
<label class="pd-label">库存数量</label>
<div class="pd-ctrl"><div class="pd-inline"><input class="g-input" type="number" min="0" value="86" /><span class="pd-unit"></span></div></div>
</div>
<div class="pd-row">
<label class="pd-label">库存预警</label>
<div class="pd-ctrl"><div class="pd-inline"><input class="g-input" type="number" min="0" value="10" /><span class="pd-unit"></span></div><div class="g-hint">库存低于此值时提醒补货</div></div>
</div>
<div class="pd-row">
<label class="pd-label">打包费</label>
<div class="pd-ctrl"><div class="pd-inline"><span class="pd-unit">&yen;</span><input class="g-input" type="number" min="0" step="0.1" value="1.0" /><span class="pd-unit">/份</span></div><div class="g-hint">商品打包费优先;未设置时使用门店统一打包费</div></div>
</div>
</div>
<!-- 规格做法 -->
<div class="g-card" id="pd-sec-specs">
<div class="g-card-title">规格做法</div>
<div class="pd-row">
<label class="pd-label">关联规格</label>
<div class="pd-ctrl">
<div class="pd-pills">
<span class="g-pill" onclick="this.classList.toggle('checked')">杯型</span>
<span class="g-pill" onclick="this.classList.toggle('checked')">温度</span>
<span class="g-pill checked" onclick="this.classList.toggle('checked')">份量</span>
<span class="g-pill checked" onclick="this.classList.toggle('checked')">辣度</span>
<span class="g-pill" onclick="this.classList.toggle('checked')">甜度</span>
<span class="g-pill" onclick="this.classList.toggle('checked')">烹饪方式</span>
</div>
<div class="g-hint">选中后顾客下单时可选择对应规格,前往「规格做法」页面管理具体选项</div>
</div>
</div>
</div>
<!-- 加料管理 -->
<!-- SKU管理 -->
<div class="g-card" id="pd-sec-sku">
<div class="pd-sku-hd">
<div class="pd-sku-hd-left">
<div class="g-card-title">SKU管理</div>
<span class="pd-sku-count">共 8 个SKU</span>
</div>
<div class="pd-sku-hint">基于已选规格自动生成组合,可独立设置价格和库存</div>
</div>
<div class="pd-sku-batch">
<label>批量设价</label>
<span style="color:#9ca3af;font-size:12px">&yen;</span>
<input type="number" placeholder="统一售价" />
<button class="g-btn g-btn-sm" onclick="batchSkuPrice()">应用</button>
<span style="width:16px"></span>
<label>批量设库存</label>
<input type="number" placeholder="统一库存" />
<button class="g-btn g-btn-sm" onclick="batchSkuStock()">应用</button>
</div>
<table class="pd-sku-table">
<thead>
<tr>
<th>份量</th>
<th>辣度</th>
<th>SKU编码</th>
<th>售价 (&yen;)</th>
<th>划线价 (&yen;)</th>
<th>库存</th>
<th>状态</th>
</tr>
</thead>
<tbody>
<tr>
<td><span class="pd-sku-spec s1">整份</span></td>
<td><span class="pd-sku-spec s2">不辣</span></td>
<td><span class="pd-sku-code">SKU20240001-01</span></td>
<td><input type="number" value="32" min="0" step="0.01" /></td>
<td><input type="number" value="38" min="0" step="0.01" /></td>
<td>
<input type="number" value="50" min="0" />
</td>
<td class="pd-sku-toggle"><button class="g-toggle on" onclick="toggleSwitch(this)"></button></td>
</tr>
<tr>
<td><span class="pd-sku-spec s1">整份</span></td>
<td><span class="pd-sku-spec s2">微辣</span></td>
<td><span class="pd-sku-code">SKU20240001-02</span></td>
<td><input type="number" value="32" min="0" step="0.01" /></td>
<td><input type="number" value="38" min="0" step="0.01" /></td>
<td>
<input type="number" value="30" min="0" />
</td>
<td class="pd-sku-toggle"><button class="g-toggle on" onclick="toggleSwitch(this)"></button></td>
</tr>
<tr>
<td><span class="pd-sku-spec s1">整份</span></td>
<td><span class="pd-sku-spec s2">中辣</span></td>
<td><span class="pd-sku-code">SKU20240001-03</span></td>
<td><input type="number" value="32" min="0" step="0.01" /></td>
<td><input type="number" value="38" min="0" step="0.01" /></td>
<td>
<input type="number" value="20" min="0" />
</td>
<td class="pd-sku-toggle"><button class="g-toggle on" onclick="toggleSwitch(this)"></button></td>
</tr>
<tr>
<td><span class="pd-sku-spec s1">整份</span></td>
<td><span class="pd-sku-spec s2">特辣</span></td>
<td><span class="pd-sku-code">SKU20240001-04</span></td>
<td><input type="number" value="32" min="0" step="0.01" /></td>
<td><input type="number" value="38" min="0" step="0.01" /></td>
<td>
<input type="number" value="8" min="0" />
<div class="pd-sku-stock-warn"><i data-lucide="alert-triangle" style="width:11px;height:11px"></i>库存偏低</div>
</td>
<td class="pd-sku-toggle"><button class="g-toggle on" onclick="toggleSwitch(this)"></button></td>
</tr>
<tr>
<td><span class="pd-sku-spec s1">半份</span></td>
<td><span class="pd-sku-spec s2">不辣</span></td>
<td><span class="pd-sku-code">SKU20240001-05</span></td>
<td><input type="number" value="18" min="0" step="0.01" /></td>
<td><input type="number" value="22" min="0" step="0.01" /></td>
<td>
<input type="number" value="40" min="0" />
</td>
<td class="pd-sku-toggle"><button class="g-toggle on" onclick="toggleSwitch(this)"></button></td>
</tr>
<tr>
<td><span class="pd-sku-spec s1">半份</span></td>
<td><span class="pd-sku-spec s2">微辣</span></td>
<td><span class="pd-sku-code">SKU20240001-06</span></td>
<td><input type="number" value="18" min="0" step="0.01" /></td>
<td><input type="number" value="22" min="0" step="0.01" /></td>
<td>
<input type="number" value="25" min="0" />
</td>
<td class="pd-sku-toggle"><button class="g-toggle on" onclick="toggleSwitch(this)"></button></td>
</tr>
<tr>
<td><span class="pd-sku-spec s1">半份</span></td>
<td><span class="pd-sku-spec s2">中辣</span></td>
<td><span class="pd-sku-code">SKU20240001-07</span></td>
<td><input type="number" value="18" min="0" step="0.01" /></td>
<td><input type="number" value="22" min="0" step="0.01" /></td>
<td>
<input type="number" value="15" min="0" />
</td>
<td class="pd-sku-toggle"><button class="g-toggle on" onclick="toggleSwitch(this)"></button></td>
</tr>
<tr>
<td><span class="pd-sku-spec s1">半份</span></td>
<td><span class="pd-sku-spec s2">特辣</span></td>
<td><span class="pd-sku-code">SKU20240001-08</span></td>
<td><input type="number" value="18" min="0" step="0.01" /></td>
<td><input type="number" value="22" min="0" step="0.01" /></td>
<td>
<input type="number" value="0" min="0" />
<div class="pd-sku-stock-warn"><i data-lucide="alert-triangle" style="width:11px;height:11px"></i>已售罄</div>
</td>
<td class="pd-sku-toggle"><button class="g-toggle" onclick="toggleSwitch(this)"></button></td>
</tr>
</tbody>
</table>
</div>
<!-- 加料管理 -->
<div class="g-card" id="pd-sec-addons">
<div class="g-card-title">加料管理</div>
<div class="pd-row">
<label class="pd-label">关联加料</label>
<div class="pd-ctrl">
<div class="pd-pills">
<span class="g-pill checked" onclick="this.classList.toggle('checked')">加料</span>
<span class="g-pill" onclick="this.classList.toggle('checked')">配菜</span>
<span class="g-pill" onclick="this.classList.toggle('checked')">主食升级</span>
<span class="g-pill" onclick="this.classList.toggle('checked')">锅底</span>
</div>
<div class="g-hint">前往「加料管理」页面管理具体加料项和价格</div>
</div>
</div>
</div>
<!-- 商品标签 -->
<div class="g-card" id="pd-sec-tags">
<div class="g-card-title">商品标签</div>
<div style="display:flex;flex-wrap:wrap">
<span class="pd-tag-sel selected" onclick="this.classList.toggle('selected')">招牌</span>
<span class="pd-tag-sel" onclick="this.classList.toggle('selected')">必点</span>
<span class="pd-tag-sel" onclick="this.classList.toggle('selected')">新品</span>
<span class="pd-tag-sel" onclick="this.classList.toggle('selected')">限时</span>
<span class="pd-tag-sel" onclick="this.classList.toggle('selected')"></span>
<span class="pd-tag-sel" onclick="this.classList.toggle('selected')">素食</span>
<span class="pd-tag-sel" onclick="this.classList.toggle('selected')">低卡</span>
<span class="pd-tag-sel" onclick="this.classList.toggle('selected')">人气</span>
</div>
</div>
<!-- 上架设置 -->
<div class="g-card" id="pd-sec-shelf">
<div class="g-card-title">上架设置</div>
<div class="pd-shelf-group">
<div class="pd-shelf-opt" onclick="selectPdShelf(this)">
<input type="radio" name="pdShelf" value="draft" />
<div class="pd-shelf-opt-body">
<div class="pd-shelf-opt-title">暂不上架(草稿)</div>
<div class="pd-shelf-opt-desc">保存修改但不同步到顾客端</div>
</div>
</div>
<div class="pd-shelf-opt active" onclick="selectPdShelf(this)">
<input type="radio" name="pdShelf" value="now" checked />
<div class="pd-shelf-opt-body">
<div class="pd-shelf-opt-title">立即上架</div>
<div class="pd-shelf-opt-desc">保存后顾客可见</div>
</div>
</div>
<div class="pd-shelf-opt" onclick="selectPdShelf(this)">
<input type="radio" name="pdShelf" value="scheduled" />
<div class="pd-shelf-opt-body">
<div class="pd-shelf-opt-title">定时上架</div>
<div class="pd-shelf-opt-desc">到达指定时间后自动上架</div>
<div class="pd-shelf-time show-active" style="gap:8px;align-items:center">
<span style="font-size:12px;color:#666">上架时间</span>
<input class="g-input" type="datetime-local" style="width:auto;flex:1" />
</div>
</div>
</div>
</div>
</div>
<!-- Sticky save bar -->
<div class="pd-savebar">
<button class="g-btn" onclick="navigateTo('商品列表')">取消</button>
<button class="g-btn g-btn-primary">保存</button>
</div>
</div>
</div>
</div>
<script>
/* Anchor nav scroll */
function scrollToPdSec(el){
var id=el.dataset.target;
var sec=document.getElementById(id);
if(sec) sec.scrollIntoView({behavior:'smooth',block:'start'});
document.querySelectorAll('.pd-nav-item').forEach(function(n){n.classList.remove('active')});
el.classList.add('active');
}
/* Scroll spy */
(function(){
var content=document.getElementById('pdContent');
var nav=document.getElementById('pdNav');
if(!content||!nav) return;
var secs=content.querySelectorAll('.g-card[id]');
var items=nav.querySelectorAll('.pd-nav-item');
var area=document.getElementById('contentArea');
if(!area) return;
area.addEventListener('scroll',function(){
var top=area.scrollTop+60;
var current='';
secs.forEach(function(s){
if(s.offsetTop<=top) current=s.id;
});
items.forEach(function(it){
it.classList.toggle('active',it.dataset.target===current);
});
});
})();
/* Shelf mode */
function selectPdShelf(el){
var group=el.parentElement;
group.querySelectorAll('.pd-shelf-opt').forEach(function(r){r.classList.remove('active');r.querySelector('input').checked=false});
el.classList.add('active');
el.querySelector('input').checked=true;
}
/* Prefill from navigateTo options */
(function(){
var opts=window._pageOptions||{};
if(opts.name){
var el=document.getElementById('pdName');
if(el) el.textContent=opts.name;
}
})();
function toggleSwitch(el){el.classList.toggle('on')}
function batchSkuPrice(){
var v=document.querySelector('.pd-sku-batch input[placeholder="统一售价"]').value;
if(!v)return;
document.querySelectorAll('.pd-sku-table tbody td:nth-child(4) input').forEach(function(inp){inp.value=v});
}
function batchSkuStock(){
var v=document.querySelector('.pd-sku-batch input[placeholder="统一库存"]').value;
if(!v)return;
document.querySelectorAll('.pd-sku-table tbody td:nth-child(6) input').forEach(function(inp){inp.value=v});
}
</script>

247
pages/product-labels.html Normal file
View File

@@ -0,0 +1,247 @@
<!-- 商品标签页 -->
<style>
.plbl-toolbar { display:flex; align-items:center; gap:12px; margin-bottom:16px; box-shadow:var(--g-shadow-sm); border-radius:10px; padding:12px 16px; background:#fff; flex-wrap:wrap; }
.plbl-search { position:relative; }
.plbl-search input { height:34px; padding:0 10px 0 32px; border:1px solid #e5e7eb; border-radius:8px; font-size:13px; outline:none; width:200px; transition:var(--g-transition); }
.plbl-search input:focus { border-color:var(--primary); box-shadow:0 0 0 3px color-mix(in srgb, var(--primary) 12%, transparent); }
.plbl-search i { position:absolute; left:9px; top:50%; transform:translateY(-50%); color:#bbb; pointer-events:none; }
.plbl-stats { display:flex; gap:24px; margin-bottom:16px; padding:10px 16px; background:#fff; border-radius:10px; box-shadow:var(--g-shadow-sm); font-size:13px; color:#4b5563; }
.plbl-stats span { display:flex; align-items:center; gap:6px; }
.plbl-stats strong { color:#1a1a2e; font-weight:600; }
.plbl-card { background:#fff; border-radius:10px; border:none; box-shadow:var(--g-shadow-sm); padding:20px; }
.plbl-table { width:100%; border-collapse:collapse; font-size:13px; }
.plbl-table th { background:#f8f9fb; padding:10px 12px; text-align:left; font-weight:600; color:#6b7280; border-bottom:1px solid #f3f4f6; white-space:nowrap; }
.plbl-table td { padding:12px; border-bottom:1px solid #f3f4f6; color:#1a1a2e; vertical-align:middle; }
.plbl-table tr:hover td { background:color-mix(in srgb, var(--primary) 3%, #fff); }
.plbl-table tr:last-child td { border-bottom:none; }
.plbl-table tr.disabled td { opacity:0.5; }
.plbl-pill { display:inline-block; padding:2px 10px; border-radius:6px; font-size:12px; font-weight:600; color:#fff; line-height:20px; }
.plbl-color-dot { width:14px; height:14px; border-radius:50%; display:inline-block; vertical-align:middle; }
.plbl-status { display:inline-flex; align-items:center; gap:5px; font-size:12px; font-weight:600; }
.plbl-status-dot { width:6px; height:6px; border-radius:50%; }
.plbl-status-on .plbl-status-dot { background:#22c55e; }
.plbl-status-on { color:#22c55e; }
.plbl-status-off .plbl-status-dot { background:#9ca3af; }
.plbl-status-off { color:#9ca3af; }
.plbl-color-grid { display:grid; grid-template-columns:repeat(4,1fr); gap:10px; }
.plbl-color-swatch { width:100%; aspect-ratio:1; border-radius:10px; cursor:pointer; border:2px solid transparent; display:flex; align-items:center; justify-content:center; transition:var(--g-transition); position:relative; }
.plbl-color-swatch:hover { box-shadow:0 0 0 3px rgba(0,0,0,0.1); }
.plbl-color-swatch.selected { border-color:#1a1a2e; box-shadow:0 0 0 2px rgba(0,0,0,0.1); }
.plbl-color-swatch .plbl-check { display:none; color:#fff; }
.plbl-color-swatch.selected .plbl-check { display:block; }
.plbl-preview-area { padding:16px; background:#f8f9fb; border-radius:10px; border:1px solid #e5e7eb; display:flex; align-items:center; justify-content:center; min-height:40px; }
</style>
<div class="plbl-toolbar">
<select class="g-select" style="width:200px;">
<option>老三家外卖(朝阳店)</option>
<option>老三家外卖(海淀店)</option>
<option>老三家外卖(望京店)</option>
<option>老三家外卖(通州店)</option>
<option>老三家外卖(丰台店)</option>
</select>
<div class="plbl-search">
<i data-lucide="search" style="width:14px;height:14px;"></i>
<input type="text" placeholder="搜索标签名称…" oninput="searchLabels(this.value)" />
</div>
<div style="flex:1;"></div>
<button class="g-btn g-btn-primary" onclick="openLabelDrawer('add')"><i data-lucide="plus" style="width:14px;height:14px;"></i>添加标签</button>
</div>
<div class="plbl-stats">
<span>标签总数 <strong>8</strong></span>
<span>启用 <strong>7</strong></span>
<span>关联商品 <strong>51</strong></span>
</div>
<div class="plbl-card">
<table class="plbl-table">
<thead>
<tr>
<th>标签预览</th>
<th>标签名称</th>
<th>颜色</th>
<th>已关联商品</th>
<th>状态</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr data-name="新品">
<td><span class="plbl-pill" style="background:#1890ff">新品</span></td>
<td>新品</td>
<td><span class="plbl-color-dot" style="background:#1890ff"></span> #1890ff</td>
<td>5个商品</td>
<td><span class="plbl-status plbl-status-on"><span class="plbl-status-dot"></span>启用</span></td>
<td><span class="g-action" onclick="openLabelDrawer('edit','新品','#1890ff')">编辑</span><span class="g-action g-action-danger">删除</span></td>
</tr>
<tr data-name="招牌">
<td><span class="plbl-pill" style="background:#ff4d4f">招牌</span></td>
<td>招牌</td>
<td><span class="plbl-color-dot" style="background:#ff4d4f"></span> #ff4d4f</td>
<td>12个商品</td>
<td><span class="plbl-status plbl-status-on"><span class="plbl-status-dot"></span>启用</span></td>
<td><span class="g-action" onclick="openLabelDrawer('edit','招牌','#ff4d4f')">编辑</span><span class="g-action g-action-danger">删除</span></td>
</tr>
<tr data-name="必点">
<td><span class="plbl-pill" style="background:#fa8c16">必点</span></td>
<td>必点</td>
<td><span class="plbl-color-dot" style="background:#fa8c16"></span> #fa8c16</td>
<td>8个商品</td>
<td><span class="plbl-status plbl-status-on"><span class="plbl-status-dot"></span>启用</span></td>
<td><span class="g-action" onclick="openLabelDrawer('edit','必点','#fa8c16')">编辑</span><span class="g-action g-action-danger">删除</span></td>
</tr>
<tr data-name="辣">
<td><span class="plbl-pill" style="background:#f5222d"></span></td>
<td></td>
<td><span class="plbl-color-dot" style="background:#f5222d"></span> #f5222d</td>
<td>15个商品</td>
<td><span class="plbl-status plbl-status-on"><span class="plbl-status-dot"></span>启用</span></td>
<td><span class="g-action" onclick="openLabelDrawer('edit','辣','#f5222d')">编辑</span><span class="g-action g-action-danger">删除</span></td>
</tr>
<tr data-name="素食">
<td><span class="plbl-pill" style="background:#52c41a">素食</span></td>
<td>素食</td>
<td><span class="plbl-color-dot" style="background:#52c41a"></span> #52c41a</td>
<td>6个商品</td>
<td><span class="plbl-status plbl-status-on"><span class="plbl-status-dot"></span>启用</span></td>
<td><span class="g-action" onclick="openLabelDrawer('edit','素食','#52c41a')">编辑</span><span class="g-action g-action-danger">删除</span></td>
</tr>
<tr data-name="限时">
<td><span class="plbl-pill" style="background:#722ed1">限时</span></td>
<td>限时</td>
<td><span class="plbl-color-dot" style="background:#722ed1"></span> #722ed1</td>
<td>3个商品</td>
<td><span class="plbl-status plbl-status-on"><span class="plbl-status-dot"></span>启用</span></td>
<td><span class="g-action" onclick="openLabelDrawer('edit','限时','#722ed1')">编辑</span><span class="g-action g-action-danger">删除</span></td>
</tr>
<tr data-name="买一送一">
<td><span class="plbl-pill" style="background:#eb2f96">买一送一</span></td>
<td>买一送一</td>
<td><span class="plbl-color-dot" style="background:#eb2f96"></span> #eb2f96</td>
<td>2个商品</td>
<td><span class="plbl-status plbl-status-on"><span class="plbl-status-dot"></span>启用</span></td>
<td><span class="g-action" onclick="openLabelDrawer('edit','买一送一','#eb2f96')">编辑</span><span class="g-action g-action-danger">删除</span></td>
</tr>
<tr data-name="已下架" class="disabled">
<td><span class="plbl-pill" style="background:#999">已下架</span></td>
<td>已下架</td>
<td><span class="plbl-color-dot" style="background:#999"></span> #999999</td>
<td>0个商品</td>
<td><span class="plbl-status plbl-status-off"><span class="plbl-status-dot"></span>停用</span></td>
<td><span class="g-action" onclick="openLabelDrawer('edit','已下架','#999')">编辑</span><span class="g-action g-action-danger">删除</span></td>
</tr>
</tbody>
</table>
</div>
<!-- 添加/编辑标签抽屉 -->
<div class="g-drawer-mask" id="plblDrawerMask" onclick="closeLabelDrawer()"></div>
<div class="g-drawer" id="plblDrawer" style="width:460px;">
<div class="g-drawer-hd">
<span class="g-drawer-title" id="plblDrawerTitle">添加标签</span>
<button class="g-drawer-close" onclick="closeLabelDrawer()"><i data-lucide="x" style="width:18px;height:18px;"></i></button>
</div>
<div class="g-drawer-bd">
<div class="g-form-group">
<label class="g-form-label required">标签名称</label>
<input class="g-input" id="plblName" placeholder="请输入标签名称,如:新品、招牌" oninput="updateLabelPreview()">
</div>
<div class="g-form-group">
<label class="g-form-label required">标签颜色</label>
<div class="plbl-color-grid" id="plblColorGrid">
<div class="plbl-color-swatch selected" style="background:#1890ff" onclick="pickLabelColor(this)" data-color="#1890ff"><span class="plbl-check"><i data-lucide="check" style="width:18px;height:18px;"></i></span></div>
<div class="plbl-color-swatch" style="background:#ff4d4f" onclick="pickLabelColor(this)" data-color="#ff4d4f"><span class="plbl-check"><i data-lucide="check" style="width:18px;height:18px;"></i></span></div>
<div class="plbl-color-swatch" style="background:#fa8c16" onclick="pickLabelColor(this)" data-color="#fa8c16"><span class="plbl-check"><i data-lucide="check" style="width:18px;height:18px;"></i></span></div>
<div class="plbl-color-swatch" style="background:#52c41a" onclick="pickLabelColor(this)" data-color="#52c41a"><span class="plbl-check"><i data-lucide="check" style="width:18px;height:18px;"></i></span></div>
<div class="plbl-color-swatch" style="background:#722ed1" onclick="pickLabelColor(this)" data-color="#722ed1"><span class="plbl-check"><i data-lucide="check" style="width:18px;height:18px;"></i></span></div>
<div class="plbl-color-swatch" style="background:#eb2f96" onclick="pickLabelColor(this)" data-color="#eb2f96"><span class="plbl-check"><i data-lucide="check" style="width:18px;height:18px;"></i></span></div>
<div class="plbl-color-swatch" style="background:#13c2c2" onclick="pickLabelColor(this)" data-color="#13c2c2"><span class="plbl-check"><i data-lucide="check" style="width:18px;height:18px;"></i></span></div>
<div class="plbl-color-swatch" style="background:#999" onclick="pickLabelColor(this)" data-color="#999"><span class="plbl-check"><i data-lucide="check" style="width:18px;height:18px;"></i></span></div>
</div>
</div>
<div class="g-form-group">
<label class="g-form-label">预览</label>
<div class="plbl-preview-area">
<span class="plbl-pill" id="plblPreviewPill" style="background:#1890ff">标签名称</span>
</div>
</div>
<div class="g-form-group">
<label class="g-form-label">排序</label>
<input class="g-input" id="plblSort" type="number" placeholder="数字越小越靠前0" value="0" min="0">
</div>
<div class="g-form-group">
<label class="g-form-label">启用状态</label>
<div style="display:flex;align-items:center;gap:10px;">
<button class="g-toggle on" id="plblToggle" onclick="toggleSwitch(this)"></button>
<span class="g-toggle-label">启用</span>
</div>
</div>
</div>
<div class="g-drawer-ft">
<button class="g-btn" onclick="closeLabelDrawer()">取消</button>
<button class="g-btn g-btn-primary" onclick="closeLabelDrawer()">保存</button>
</div>
</div>
<script>
var _plblColor = '#1890ff';
function openLabelDrawer(mode, name, color) {
document.getElementById('plblDrawerMask').classList.add('open');
document.getElementById('plblDrawer').classList.add('open');
if (mode === 'edit') {
document.getElementById('plblDrawerTitle').textContent = '编辑标签';
document.getElementById('plblName').value = name || '';
if (color) {
_plblColor = color;
document.querySelectorAll('.plbl-color-swatch').forEach(function(s) {
s.classList.toggle('selected', s.getAttribute('data-color') === color);
});
}
} else {
document.getElementById('plblDrawerTitle').textContent = '添加标签';
document.getElementById('plblName').value = '';
_plblColor = '#1890ff';
document.querySelectorAll('.plbl-color-swatch').forEach(function(s, i) {
s.classList.toggle('selected', i === 0);
});
document.getElementById('plblSort').value = '0';
var tog = document.getElementById('plblToggle');
if (!tog.classList.contains('on')) tog.classList.add('on');
}
updateLabelPreview();
if (typeof lucide !== 'undefined') lucide.createIcons();
}
function closeLabelDrawer() {
document.getElementById('plblDrawerMask').classList.remove('open');
document.getElementById('plblDrawer').classList.remove('open');
}
function pickLabelColor(el) {
document.querySelectorAll('.plbl-color-swatch').forEach(function(s) { s.classList.remove('selected'); });
el.classList.add('selected');
_plblColor = el.getAttribute('data-color');
updateLabelPreview();
}
function updateLabelPreview() {
var name = document.getElementById('plblName').value || '标签名称';
var pill = document.getElementById('plblPreviewPill');
pill.textContent = name;
pill.style.background = _plblColor;
}
function searchLabels(keyword) {
var kw = keyword.trim().toLowerCase();
document.querySelectorAll('.plbl-table tbody tr').forEach(function(row) {
var name = (row.getAttribute('data-name') || '').toLowerCase();
row.style.display = (!kw || name.includes(kw)) ? '' : 'none';
});
}
function toggleSwitch(el) {
el.classList.toggle('on');
}
</script>

1407
pages/product-list.html Normal file

File diff suppressed because it is too large Load Diff

323
pages/product-schedule.html Normal file
View File

@@ -0,0 +1,323 @@
<!-- 时段供应页 -->
<style>
.ptm-toolbar { display:flex; align-items:center; gap:12px; margin-bottom:16px; box-shadow:var(--g-shadow-sm); border-radius:10px; padding:12px 16px; background:#fff; flex-wrap:wrap; }
.ptm-search { position:relative; }
.ptm-search input { height:34px; padding:0 10px 0 32px; border:1px solid #e5e7eb; border-radius:8px; font-size:13px; outline:none; width:200px; transition:var(--g-transition); }
.ptm-search input:focus { border-color:var(--primary); box-shadow:0 0 0 3px color-mix(in srgb, var(--primary) 12%, transparent); }
.ptm-search i { position:absolute; left:9px; top:50%; transform:translateY(-50%); color:#bbb; pointer-events:none; }
.ptm-stats { display:flex; gap:24px; margin-bottom:16px; padding:10px 16px; background:#fff; border-radius:10px; box-shadow:var(--g-shadow-sm); font-size:13px; color:#4b5563; }
.ptm-stats span { display:flex; align-items:center; gap:6px; }
.ptm-stats strong { color:#1a1a2e; font-weight:600; }
.ptm-banner { background:#e6f7ff; border:none; border-radius:10px; padding:12px 16px; font-size:13px; color:#0050b3; margin-bottom:16px; display:flex; align-items:center; gap:8px; box-shadow:var(--g-shadow-sm); }
.ptm-card-hd { display:flex; align-items:center; gap:10px; margin-bottom:14px; }
.ptm-card-name { font-size:15px; font-weight:600; color:#1a1a2e; }
.ptm-tag-on { background:#dcfce7; color:#22c55e; border:1px solid #bbf7d0; border-radius:6px; font-weight:600; }
.ptm-tag-off { background:#f8f9fb; color:#9ca3af; border:1px solid #e5e7eb; border-radius:6px; font-weight:600; }
.ptm-time-row { display:flex; align-items:center; gap:16px; margin-bottom:12px; }
.ptm-time-text { font-size:22px; font-weight:700; color:#1a1a2e; letter-spacing:1px; }
.ptm-timebar-wrap { flex:1; height:18px; background:#f8f9fb; border-radius:9px; position:relative; overflow:hidden; }
.ptm-timebar-fill { position:absolute; top:0; bottom:0; border-radius:9px; opacity:0.7; }
.ptm-days { display:flex; gap:6px; margin-bottom:12px; flex-wrap:wrap; }
.ptm-day { width:36px; height:26px; border-radius:13px; font-size:12px; display:inline-flex; align-items:center; justify-content:center; border:1px solid #e5e7eb; color:#9ca3af; background:#f8f9fb; transition:var(--g-transition); }
.ptm-day.active { background:var(--primary); color:#fff; border-color:var(--primary); }
.ptm-products { display:flex; gap:6px; flex-wrap:wrap; margin-bottom:14px; }
.ptm-prod-pill { display:inline-block; padding:2px 10px; border-radius:12px; font-size:12px; background:#f8f9fb; color:#4b5563; border:1px solid #e5e7eb; transition:var(--g-transition); }
.ptm-prod-more { color:var(--primary); cursor:pointer; }
.ptm-card-ft { display:flex; gap:16px; border-top:1px solid #f3f4f6; padding-top:12px; }
.ptm-tl-title { font-size:15px; font-weight:600; color:#1a1a2e; margin-bottom:16px; padding-left:10px; border-left:3px solid var(--primary); }
.ptm-tl-axis { position:relative; height:20px; margin-bottom:4px; }
.ptm-tl-axis span { position:absolute; transform:translateX(-50%); font-size:10px; color:#9ca3af; }
.ptm-tl-rows { display:flex; flex-direction:column; gap:8px; }
.ptm-tl-row { display:flex; align-items:center; gap:10px; }
.ptm-tl-label { width:80px; font-size:12px; color:#4b5563; text-align:right; flex-shrink:0; }
.ptm-tl-track { flex:1; height:22px; background:#f8f9fb; border-radius:11px; position:relative; overflow:hidden; }
.ptm-tl-bar { position:absolute; top:0; bottom:0; border-radius:11px; display:flex; align-items:center; justify-content:center; font-size:10px; color:#fff; font-weight:500; white-space:nowrap; overflow:hidden; }
.ptm-fg-row { display:flex; gap:10px; align-items:center; }
.ptm-fg-row .g-input { flex:1; }
.ptm-fg-sep { font-size:14px; color:#9ca3af; }
.ptm-day-sel { display:flex; gap:6px; flex-wrap:wrap; margin-bottom:8px; }
.ptm-day-sel .ptm-day { cursor:pointer; transition:var(--g-transition); }
.ptm-day-quick { display:flex; gap:6px; }
.ptm-prod-search { position:relative; margin-bottom:10px; }
.ptm-prod-selected { display:flex; gap:6px; flex-wrap:wrap; }
.ptm-prod-chip { display:inline-flex; align-items:center; gap:4px; padding:4px 10px; border-radius:14px; font-size:12px; background:#f0f5ff; color:var(--primary); border:1px solid #adc6ff; transition:var(--g-transition); }
.ptm-prod-chip-x { cursor:pointer; color:#9ca3af; display:flex; align-items:center; transition:var(--g-transition); }
.ptm-prod-chip-x:hover { color:#ef4444; }
</style>
<div class="ptm-toolbar">
<select class="g-select" style="width:200px;">
<option>老三家外卖(朝阳店)</option>
<option>老三家外卖(海淀店)</option>
<option>老三家外卖(望京店)</option>
<option>老三家外卖(通州店)</option>
<option>老三家外卖(丰台店)</option>
</select>
<div class="ptm-search">
<i data-lucide="search" style="width:14px;height:14px;"></i>
<input type="text" placeholder="搜索规则名称…" oninput="searchSchedules(this.value)" />
</div>
<div style="flex:1;"></div>
<button class="g-btn g-btn-primary" onclick="openScheduleDrawer('add')"><i data-lucide="plus" style="width:14px;height:14px;"></i>添加时段规则</button>
</div>
<div class="ptm-stats">
<span>时段规则 <strong>4</strong></span>
<span>启用 <strong>3</strong></span>
<span>覆盖商品 <strong>14</strong></span>
</div>
<div class="ptm-banner">
<i data-lucide="info" style="width:16px;height:16px;flex-shrink:0;"></i>
设置商品在特定时段内供应,未被任何规则覆盖的商品默认全天供应。
</div>
<div id="ptmRuleList">
<div class="g-card" style="margin-bottom:16px" data-name="早餐供应">
<div class="ptm-card-hd">
<span class="ptm-card-name">早餐供应</span>
<span class="g-tag ptm-tag-on">启用</span>
</div>
<div class="ptm-time-row">
<span class="ptm-time-text">06:00 ~ 10:00</span>
<div class="ptm-timebar-wrap">
<div class="ptm-timebar-fill" style="left:25%;width:16.67%;background:#1890ff"></div>
</div>
</div>
<div class="ptm-days">
<span class="ptm-day active">周一</span><span class="ptm-day active">周二</span><span class="ptm-day active">周三</span><span class="ptm-day active">周四</span><span class="ptm-day active">周五</span><span class="ptm-day active">周六</span><span class="ptm-day active">周日</span>
</div>
<div class="ptm-products">
<span class="ptm-prod-pill">皮蛋瘦肉粥</span><span class="ptm-prod-pill">小笼包</span><span class="ptm-prod-pill">油条</span><span class="ptm-prod-pill ptm-prod-more">+2更多</span>
</div>
<div class="ptm-card-ft">
<span class="g-action" onclick="openScheduleDrawer('edit','早餐供应')">编辑</span>
<span class="g-action">停用</span>
<span class="g-action g-action-danger">删除</span>
</div>
</div>
<div class="g-card" style="margin-bottom:16px" data-name="午市套餐">
<div class="ptm-card-hd">
<span class="ptm-card-name">午市套餐</span>
<span class="g-tag ptm-tag-on">启用</span>
</div>
<div class="ptm-time-row">
<span class="ptm-time-text">11:00 ~ 14:00</span>
<div class="ptm-timebar-wrap">
<div class="ptm-timebar-fill" style="left:45.83%;width:12.5%;background:#52c41a"></div>
</div>
</div>
<div class="ptm-days">
<span class="ptm-day active">周一</span><span class="ptm-day active">周二</span><span class="ptm-day active">周三</span><span class="ptm-day active">周四</span><span class="ptm-day active">周五</span><span class="ptm-day">周六</span><span class="ptm-day">周日</span>
</div>
<div class="ptm-products">
<span class="ptm-prod-pill">单人工作餐</span><span class="ptm-prod-pill">任选午餐</span>
</div>
<div class="ptm-card-ft">
<span class="g-action" onclick="openScheduleDrawer('edit','午市套餐')">编辑</span>
<span class="g-action">停用</span>
<span class="g-action g-action-danger">删除</span>
</div>
</div>
<div class="g-card" style="margin-bottom:16px" data-name="下午茶">
<div class="ptm-card-hd">
<span class="ptm-card-name">下午茶</span>
<span class="g-tag ptm-tag-on">启用</span>
</div>
<div class="ptm-time-row">
<span class="ptm-time-text">14:00 ~ 17:00</span>
<div class="ptm-timebar-wrap">
<div class="ptm-timebar-fill" style="left:58.33%;width:12.5%;background:#722ed1"></div>
</div>
</div>
<div class="ptm-days">
<span class="ptm-day active">周一</span><span class="ptm-day active">周二</span><span class="ptm-day active">周三</span><span class="ptm-day active">周四</span><span class="ptm-day active">周五</span><span class="ptm-day active">周六</span><span class="ptm-day active">周日</span>
</div>
<div class="ptm-products">
<span class="ptm-prod-pill">冰粉</span><span class="ptm-prod-pill">奶茶</span><span class="ptm-prod-pill">蛋糕</span><span class="ptm-prod-pill ptm-prod-more">+1更多</span>
</div>
<div class="ptm-card-ft">
<span class="g-action" onclick="openScheduleDrawer('edit','下午茶')">编辑</span>
<span class="g-action">停用</span>
<span class="g-action g-action-danger">删除</span>
</div>
</div>
<div class="g-card" style="margin-bottom:16px;opacity:0.5" data-name="夜宵时段">
<div class="ptm-card-hd">
<span class="ptm-card-name">夜宵时段</span>
<span class="g-tag ptm-tag-off">停用</span>
</div>
<div class="ptm-time-row">
<span class="ptm-time-text">21:00 ~ 02:00 (次日)</span>
<div class="ptm-timebar-wrap">
<div class="ptm-timebar-fill" style="left:87.5%;width:12.5%;background:#fa8c16"></div>
<div class="ptm-timebar-fill" style="left:0%;width:8.33%;background:#fa8c16"></div>
</div>
</div>
<div class="ptm-days">
<span class="ptm-day">周一</span><span class="ptm-day">周二</span><span class="ptm-day">周三</span><span class="ptm-day">周四</span><span class="ptm-day active">周五</span><span class="ptm-day active">周六</span><span class="ptm-day active">周日</span>
</div>
<div class="ptm-products">
<span class="ptm-prod-pill">烧烤拼盘</span><span class="ptm-prod-pill">啤酒</span><span class="ptm-prod-pill">毛豆</span><span class="ptm-prod-pill ptm-prod-more">+1更多</span>
</div>
<div class="ptm-card-ft">
<span class="g-action" onclick="openScheduleDrawer('edit','夜宵时段')">编辑</span>
<span class="g-action">启用</span>
<span class="g-action g-action-danger">删除</span>
</div>
</div>
</div>
<div class="g-card">
<div class="ptm-tl-title">今日供应时间轴</div>
<div class="ptm-tl-axis" style="margin-left:90px;margin-right:10px;">
<span style="left:0%">0</span><span style="left:12.5%">3</span><span style="left:25%">6</span><span style="left:37.5%">9</span><span style="left:50%">12</span><span style="left:62.5%">15</span><span style="left:75%">18</span><span style="left:87.5%">21</span><span style="left:100%">24</span>
</div>
<div class="ptm-tl-rows">
<div class="ptm-tl-row">
<span class="ptm-tl-label">早餐供应</span>
<div class="ptm-tl-track">
<div class="ptm-tl-bar" style="left:25%;width:16.67%;background:#1890ff">06~10</div>
</div>
</div>
<div class="ptm-tl-row">
<span class="ptm-tl-label">午市套餐</span>
<div class="ptm-tl-track">
<div class="ptm-tl-bar" style="left:45.83%;width:12.5%;background:#52c41a">11~14</div>
</div>
</div>
<div class="ptm-tl-row">
<span class="ptm-tl-label">下午茶</span>
<div class="ptm-tl-track">
<div class="ptm-tl-bar" style="left:58.33%;width:12.5%;background:#722ed1">14~17</div>
</div>
</div>
<div class="ptm-tl-row">
<span class="ptm-tl-label">夜宵时段</span>
<div class="ptm-tl-track">
<div class="ptm-tl-bar" style="left:87.5%;width:12.5%;background:#fa8c16;border-radius:11px 0 0 11px">21~24</div>
<div class="ptm-tl-bar" style="left:0%;width:8.33%;background:#fa8c16;border-radius:0 11px 11px 0">0~2</div>
</div>
</div>
</div>
</div>
<!-- 添加/编辑时段规则抽屉 -->
<div class="g-drawer-mask" id="ptmDrawerMask" onclick="closeScheduleDrawer()"></div>
<div class="g-drawer" id="ptmDrawer" style="width:520px">
<div class="g-drawer-hd">
<span class="g-drawer-title" id="ptmDrawerTitle">添加时段规则</span>
<button class="g-drawer-close" onclick="closeScheduleDrawer()"><i data-lucide="x" style="width:18px;height:18px;"></i></button>
</div>
<div class="g-drawer-bd">
<div class="g-form-group">
<label class="g-form-label required">规则名称</label>
<input class="g-input" id="ptmRuleName" placeholder="请输入规则名称,如:早餐供应、午市套餐">
</div>
<div class="g-form-group">
<label class="g-form-label required">时间范围</label>
<div class="ptm-fg-row">
<input class="g-input" type="time" id="ptmTimeStart" value="06:00">
<span class="ptm-fg-sep">~</span>
<input class="g-input" type="time" id="ptmTimeEnd" value="10:00">
</div>
<div class="g-hint">结束时间小于开始时间时视为跨天(如 21:00~02:00</div>
</div>
<div class="g-form-group">
<label class="g-form-label required">适用星期</label>
<div class="ptm-day-sel" id="ptmDaySel">
<span class="ptm-day active" onclick="toggleSchedDay(this)">周一</span>
<span class="ptm-day active" onclick="toggleSchedDay(this)">周二</span>
<span class="ptm-day active" onclick="toggleSchedDay(this)">周三</span>
<span class="ptm-day active" onclick="toggleSchedDay(this)">周四</span>
<span class="ptm-day active" onclick="toggleSchedDay(this)">周五</span>
<span class="ptm-day active" onclick="toggleSchedDay(this)">周六</span>
<span class="ptm-day active" onclick="toggleSchedDay(this)">周日</span>
</div>
<div class="ptm-day-quick">
<button class="g-btn g-btn-sm" onclick="schedQuickDays('all')">全选</button>
<button class="g-btn g-btn-sm" onclick="schedQuickDays('weekday')">工作日</button>
<button class="g-btn g-btn-sm" onclick="schedQuickDays('weekend')">周末</button>
</div>
</div>
<div class="g-form-group">
<label class="g-form-label required">关联商品</label>
<div class="ptm-prod-search">
<input class="g-input" placeholder="搜索并选择要关联的商品…" onclick="openProductPicker({title:'关联商品',subtitle:'时段规则',onConfirm:function(items){var c=document.getElementById('ptmProdSelected');items.forEach(function(p){var s=document.createElement('span');s.className='ptm-prod-chip';s.innerHTML=p.name+' <span class=ptm-prod-chip-x onclick=removeSchedProduct(this)><i data-lucide=x style=width:12px;height:12px></i></span>';c.appendChild(s)});if(typeof lucide!=='undefined')lucide.createIcons()}})" readonly style="cursor:pointer">
</div>
<div class="ptm-prod-selected" id="ptmProdSelected">
<span class="ptm-prod-chip">皮蛋瘦肉粥 <span class="ptm-prod-chip-x" onclick="removeSchedProduct(this)"><i data-lucide="x" style="width:12px;height:12px;"></i></span></span>
<span class="ptm-prod-chip">小笼包 <span class="ptm-prod-chip-x" onclick="removeSchedProduct(this)"><i data-lucide="x" style="width:12px;height:12px;"></i></span></span>
<span class="ptm-prod-chip">油条 <span class="ptm-prod-chip-x" onclick="removeSchedProduct(this)"><i data-lucide="x" style="width:12px;height:12px;"></i></span></span>
</div>
</div>
<div class="g-form-group">
<label class="g-form-label">启用状态</label>
<div style="display:flex;align-items:center;gap:10px;">
<button class="g-toggle on" id="ptmToggle" onclick="toggleSwitch(this)"></button>
<span class="g-toggle-label">启用</span>
</div>
</div>
</div>
<div class="g-drawer-ft">
<button class="g-btn" onclick="closeScheduleDrawer()">取消</button>
<button class="g-btn g-btn-primary" onclick="closeScheduleDrawer()">保存</button>
</div>
</div>
<script>
function openScheduleDrawer(mode, name) {
document.getElementById('ptmDrawerMask').classList.add('open');
document.getElementById('ptmDrawer').classList.add('open');
if (mode === 'edit') {
document.getElementById('ptmDrawerTitle').textContent = '编辑时段规则';
document.getElementById('ptmRuleName').value = name || '';
} else {
document.getElementById('ptmDrawerTitle').textContent = '添加时段规则';
document.getElementById('ptmRuleName').value = '';
document.getElementById('ptmTimeStart').value = '06:00';
document.getElementById('ptmTimeEnd').value = '10:00';
schedQuickDays('all');
var tog = document.getElementById('ptmToggle');
if (!tog.classList.contains('on')) tog.classList.add('on');
}
if (typeof lucide !== 'undefined') lucide.createIcons();
}
function closeScheduleDrawer() {
document.getElementById('ptmDrawerMask').classList.remove('open');
document.getElementById('ptmDrawer').classList.remove('open');
}
function toggleSchedDay(el) {
el.classList.toggle('active');
}
function schedQuickDays(mode) {
var days = document.querySelectorAll('#ptmDaySel .ptm-day');
days.forEach(function(d, i) {
if (mode === 'all') d.classList.add('active');
else if (mode === 'weekday') d.classList.toggle('active', i < 5);
else if (mode === 'weekend') d.classList.toggle('active', i >= 5);
});
}
function removeSchedProduct(el) {
el.closest('.ptm-prod-chip').remove();
}
function searchSchedules(keyword) {
var kw = keyword.trim().toLowerCase();
document.querySelectorAll('#ptmRuleList > .g-card').forEach(function(card) {
var name = (card.getAttribute('data-name') || '').toLowerCase();
card.style.display = (!kw || name.includes(kw)) ? '' : 'none';
});
}
function toggleSwitch(el) {
el.classList.toggle('on');
}
</script>

328
pages/product-specs.html Normal file
View File

@@ -0,0 +1,328 @@
<!-- 规格做法页 -->
<style>
.psp-toolbar { display:flex; align-items:center; gap:12px; margin-bottom:16px; box-shadow:var(--g-shadow-sm); border-radius:10px; padding:12px 16px; background:#fff; }
.psp-store-select { height:34px; border-radius:8px; border:1px solid #e5e7eb; padding:0 12px; font-size:13px; min-width:180px; outline:none; color:#1a1a2e; transition:var(--g-transition); }
.psp-store-select:focus { border-color:var(--primary); box-shadow:0 0 0 3px color-mix(in srgb,var(--primary) 12%,transparent); }
.psp-spacer { flex:1; }
.psp-search { width:200px; height:34px; border-radius:8px; border:1px solid #e5e7eb; padding:0 10px 0 32px; font-size:13px; outline:none; color:#1a1a2e; transition:var(--g-transition); background:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%239ca3af' stroke-width='2'%3E%3Ccircle cx='11' cy='11' r='8'/%3E%3Cpath d='m21 21-4.3-4.3'/%3E%3C/svg%3E") 10px center no-repeat; }
.psp-search:focus { border-color:var(--primary); box-shadow:0 0 0 3px color-mix(in srgb,var(--primary) 12%,transparent); }
/* 筛选标签 */
.psp-filter-tabs { display:flex; gap:4px; }
.psp-filter-tab { height:30px; padding:0 14px; border-radius:6px; font-size:12px; cursor:pointer; border:1px solid transparent; background:transparent; color:#4b5563; transition:var(--g-transition); font-weight:500; }
.psp-filter-tab:hover { background:#f3f4f6; }
.psp-filter-tab.active { background:color-mix(in srgb,var(--primary) 10%,transparent); color:var(--primary); border-color:color-mix(in srgb,var(--primary) 20%,transparent); font-weight:600; }
.psp-filter-tab .count { font-size:11px; margin-left:3px; opacity:0.7; }
/* 统计条 */
.psp-stats { display:flex; gap:12px; margin-bottom:16px; }
.psp-stat { display:flex; align-items:center; gap:8px; padding:10px 16px; background:#fff; border-radius:8px; box-shadow:var(--g-shadow-sm); font-size:13px; color:#4b5563; }
.psp-stat-num { font-size:18px; font-weight:700; color:#1a1a2e; }
.psp-stat-dot { width:8px; height:8px; border-radius:50%; flex-shrink:0; }
.psp-btn-dashed { border-style:dashed; width:100%; justify-content:center; margin-top:8px; color:#9ca3af; }
.psp-btn-dashed:hover { border-color:var(--primary); color:var(--primary); }
.psp-grid { display:grid; grid-template-columns:repeat(2,1fr); gap:16px; }
.psp-card {
background:#fff; border-radius:10px; border:none; box-shadow:var(--g-shadow-sm); padding:20px;
font-size:13px; display:flex; flex-direction:column; gap:10px; transition:var(--g-transition);
}
.psp-card:hover { box-shadow:var(--g-shadow-md); }
.psp-card.disabled { opacity:0.5; }
.psp-card-hd { display:flex; align-items:center; gap:8px; }
.psp-card-hd .name { font-weight:600; font-size:14px; color:#1a1a2e; }
.psp-card-meta { color:#9ca3af; font-size:12px; display:flex; align-items:center; gap:8px; }
.psp-pills { display:flex; flex-wrap:wrap; gap:6px; }
.psp-pill {
display:inline-block; padding:2px 10px; border-radius:12px;
background:#f8f9fb; font-size:12px; color:#1a1a2e;
}
.psp-pill .price { color:var(--primary); margin-left:2px; }
.psp-card-assoc { font-size:12px; color:#9ca3af; }
.psp-card-ft { display:flex; gap:16px; border-top:1px solid #f3f4f6; padding-top:10px; }
.psp-pill-group { display:flex; gap:8px; }
.psp-pill-btn {
height:34px; padding:0 16px; border-radius:8px; font-size:13px; cursor:pointer;
border:1px solid #e5e7eb; background:#fff; color:#1a1a2e; transition:var(--g-transition);
}
.psp-pill-btn:hover { border-color:var(--primary); color:var(--primary); }
.psp-pill-btn:focus { box-shadow:0 0 0 3px color-mix(in srgb, var(--primary) 12%, transparent); }
.psp-pill-btn.active { background:var(--primary); color:#fff; border-color:var(--primary); }
.psp-opt-list { display:flex; flex-direction:column; gap:8px; }
.psp-opt-row { display:flex; align-items:center; gap:8px; }
.psp-opt-row input[type="text"] {
flex:1; height:34px; padding:0 10px; border:1px solid #e5e7eb; border-radius:8px;
font-size:13px; outline:none; transition:var(--g-transition);
}
.psp-opt-row input[type="text"]:focus { border-color:var(--primary); box-shadow:0 0 0 3px color-mix(in srgb, var(--primary) 12%, transparent); }
.psp-opt-row .psp-price-wrap {
width:120px; display:flex; align-items:center; height:34px; border:1px solid #e5e7eb;
border-radius:8px; overflow:hidden; font-size:13px; flex-shrink:0; transition:var(--g-transition);
}
.psp-opt-row .psp-price-wrap span { padding:0 8px; color:#9ca3af; background:#f8f9fb; height:100%; display:flex; align-items:center; border-right:1px solid #e5e7eb; }
.psp-opt-row .psp-price-wrap input {
border:none; outline:none; width:100%; height:100%; padding:0 8px; font-size:13px;
}
.psp-opt-row .psp-price-wrap input:focus { box-shadow:none; }
.psp-opt-del {
width:34px; height:34px; border:1px solid #e5e7eb; border-radius:8px; background:#fff;
cursor:pointer; color:#9ca3af; display:flex; align-items:center; justify-content:center; font-size:14px; flex-shrink:0; transition:var(--g-transition);
}
.psp-opt-del:hover { border-color:#ef4444; color:#ef4444; background:#fef2f2; }
</style>
<!-- 工具栏 -->
<div class="psp-toolbar">
<select class="psp-store-select">
<option>老三家外卖 总店</option>
<option>老三家外卖 朝阳店</option>
<option>老三家外卖 海淀店</option>
<option>老三家外卖 丰台店</option>
<option>老三家外卖 通州店</option>
</select>
<div class="psp-filter-tabs">
<button class="psp-filter-tab active" onclick="filterSpecs('all',this)">全部<span class="count">6</span></button>
<button class="psp-filter-tab" onclick="filterSpecs('spec',this)">规格<span class="count">3</span></button>
<button class="psp-filter-tab" onclick="filterSpecs('method',this)">做法<span class="count">3</span></button>
</div>
<span class="psp-spacer"></span>
<input class="psp-search" placeholder="搜索模板名称…" oninput="searchSpecs(this.value)">
<button class="g-btn g-btn-primary" onclick="openSpecDrawer('add')">+ 添加模板</button>
</div>
<!-- 统计条 -->
<div class="psp-stats">
<div class="psp-stat"><span class="psp-stat-dot" style="background:#1890ff"></span>规格模板 <span class="psp-stat-num">3</span></div>
<div class="psp-stat"><span class="psp-stat-dot" style="background:#fa8c16"></span>做法模板 <span class="psp-stat-num">3</span></div>
<div class="psp-stat"><span class="psp-stat-dot" style="background:#52c41a"></span>总关联商品 <span class="psp-stat-num">79</span></div>
</div>
<!-- 卡片网格 -->
<div class="psp-grid" id="pspGrid">
<div class="psp-card" data-type="spec" data-name="杯型">
<div class="psp-card-hd"><span class="name">杯型</span><span class="g-tag g-tag-blue">规格</span></div>
<div class="psp-card-meta">单选 · 必选</div>
<div class="psp-pills">
<span class="psp-pill">大杯 <span class="price">+3元</span></span>
<span class="psp-pill">中杯</span>
<span class="psp-pill">小杯 <span class="price">-2元</span></span>
</div>
<div class="psp-card-assoc">已关联 12 个商品</div>
<div class="psp-card-ft">
<button class="g-action" onclick="openSpecDrawer('edit','杯型')">编辑</button>
<button class="g-action">复制</button>
<button class="g-action g-action-danger">删除</button>
</div>
</div>
<div class="psp-card" data-type="spec" data-name="温度">
<div class="psp-card-hd"><span class="name">温度</span><span class="g-tag g-tag-blue">规格</span></div>
<div class="psp-card-meta">单选 · 必选</div>
<div class="psp-pills">
<span class="psp-pill"></span>
<span class="psp-pill"></span>
<span class="psp-pill"></span>
<span class="psp-pill">去冰</span>
</div>
<div class="psp-card-assoc">已关联 20 个商品</div>
<div class="psp-card-ft">
<button class="g-action" onclick="openSpecDrawer('edit','温度')">编辑</button>
<button class="g-action">复制</button>
<button class="g-action g-action-danger">删除</button>
</div>
</div>
<div class="psp-card" data-type="spec" data-name="份量">
<div class="psp-card-hd"><span class="name">份量</span><span class="g-tag g-tag-blue">规格</span></div>
<div class="psp-card-meta">单选 · 可选</div>
<div class="psp-pills">
<span class="psp-pill">整份</span>
<span class="psp-pill">半份 <span class="price">-5元</span></span>
</div>
<div class="psp-card-assoc">已关联 8 个商品</div>
<div class="psp-card-ft">
<button class="g-action" onclick="openSpecDrawer('edit','份量')">编辑</button>
<button class="g-action">复制</button>
<button class="g-action g-action-danger">删除</button>
</div>
</div>
<div class="psp-card" data-type="method" data-name="辣度">
<div class="psp-card-hd"><span class="name">辣度</span><span class="g-tag g-tag-orange">做法</span></div>
<div class="psp-card-meta">单选 · 必选</div>
<div class="psp-pills">
<span class="psp-pill">不辣</span>
<span class="psp-pill">微辣</span>
<span class="psp-pill">中辣</span>
<span class="psp-pill">特辣</span>
</div>
<div class="psp-card-assoc">已关联 15 个商品</div>
<div class="psp-card-ft">
<button class="g-action" onclick="openSpecDrawer('edit','辣度')">编辑</button>
<button class="g-action">复制</button>
<button class="g-action g-action-danger">删除</button>
</div>
</div>
<div class="psp-card" data-type="method" data-name="甜度">
<div class="psp-card-hd"><span class="name">甜度</span><span class="g-tag g-tag-orange">做法</span></div>
<div class="psp-card-meta">单选 · 必选</div>
<div class="psp-pills">
<span class="psp-pill">全糖</span>
<span class="psp-pill">七分糖</span>
<span class="psp-pill">半糖</span>
<span class="psp-pill">三分糖</span>
<span class="psp-pill">无糖</span>
</div>
<div class="psp-card-assoc">已关联 18 个商品</div>
<div class="psp-card-ft">
<button class="g-action" onclick="openSpecDrawer('edit','甜度')">编辑</button>
<button class="g-action">复制</button>
<button class="g-action g-action-danger">删除</button>
</div>
</div>
<div class="psp-card disabled" data-type="method" data-name="烹饪方式">
<div class="psp-card-hd"><span class="name">烹饪方式</span><span class="g-tag g-tag-orange">做法</span><span class="g-tag g-tag-gray">已停用</span></div>
<div class="psp-card-meta">单选 · 可选</div>
<div class="psp-pills">
<span class="psp-pill">清蒸</span>
<span class="psp-pill">红烧</span>
<span class="psp-pill">干煸</span>
<span class="psp-pill">水煮</span>
</div>
<div class="psp-card-assoc">已关联 6 个商品</div>
<div class="psp-card-ft">
<button class="g-action" onclick="openSpecDrawer('edit','烹饪方式')">编辑</button>
<button class="g-action">复制</button>
<button class="g-action g-action-danger">删除</button>
</div>
</div>
</div>
<!-- 抽屉 -->
<div class="g-drawer-mask" id="pspDrawerMask" onclick="closeSpecDrawer()"></div>
<div class="g-drawer" id="pspDrawer" style="width:520px;">
<div class="g-drawer-hd">
<span class="g-drawer-title" id="pspDrawerTitle">添加模板</span>
<button class="g-drawer-close" onclick="closeSpecDrawer()">&times;</button>
</div>
<div class="g-drawer-bd">
<div class="g-form-group">
<label class="g-form-label required">模板名称</label>
<input type="text" class="g-input" id="pspName" placeholder="如:杯型、辣度、甜度">
</div>
<div class="g-form-group">
<label class="g-form-label required">模板类型</label>
<div class="psp-pill-group" id="pspTypeGroup">
<button class="psp-pill-btn active" onclick="selectSpecType(this)" data-val="spec">规格</button>
<button class="psp-pill-btn" onclick="selectSpecType(this)" data-val="method">做法</button>
</div>
<div class="g-hint">规格影响价格和库存,做法仅影响制作方式</div>
</div>
<div class="g-form-group">
<label class="g-form-label required">选择方式</label>
<div class="psp-pill-group" id="pspSelectGroup">
<button class="psp-pill-btn active" onclick="selectSpecSelect(this)" data-val="single">单选</button>
<button class="psp-pill-btn" onclick="selectSpecSelect(this)" data-val="multi">多选</button>
</div>
</div>
<div class="g-form-group">
<label class="g-form-label">是否必选</label>
<div class="g-toggle-wrap">
<label class="g-toggle-input"><input type="checkbox" id="pspRequired" checked><span class="g-toggle-sl"></span></label>
<span class="g-toggle-label" id="pspRequiredLabel"></span>
</div>
</div>
<div class="g-form-group">
<label class="g-form-label required">选项列表</label>
<div class="psp-opt-list" id="pspOptList">
<div class="psp-opt-row">
<input type="text" placeholder="选项名称,如:大杯" value="大杯">
<div class="psp-price-wrap"><span>&yen;</span><input type="number" placeholder="加价金额" value="3"></div>
<button class="psp-opt-del" onclick="removeSpecOption(this)">&times;</button>
</div>
<div class="psp-opt-row">
<input type="text" placeholder="选项名称,如:中杯" value="中杯">
<div class="psp-price-wrap"><span>&yen;</span><input type="number" placeholder="加价金额"></div>
<button class="psp-opt-del" onclick="removeSpecOption(this)">&times;</button>
</div>
<div class="psp-opt-row">
<input type="text" placeholder="选项名称,如:小杯" value="小杯">
<div class="psp-price-wrap"><span>&yen;</span><input type="number" placeholder="加价金额" value="-2"></div>
<button class="psp-opt-del" onclick="removeSpecOption(this)">&times;</button>
</div>
</div>
<button class="g-btn psp-btn-dashed" onclick="addSpecOption()">+ 添加选项</button>
</div>
</div>
<div class="g-drawer-ft">
<button class="g-btn" onclick="closeSpecDrawer()">取消</button>
<button class="g-btn g-btn-primary" id="pspSubmitBtn" onclick="closeSpecDrawer()">确认添加</button>
</div>
</div>
<script>
function openSpecDrawer(mode, name) {
document.getElementById('pspDrawerMask').classList.add('open');
document.getElementById('pspDrawer').classList.add('open');
if (mode === 'edit') {
document.getElementById('pspDrawerTitle').textContent = '编辑模板';
document.getElementById('pspSubmitBtn').textContent = '保存修改';
if (name) document.getElementById('pspName').value = name;
} else {
document.getElementById('pspDrawerTitle').textContent = '添加模板';
document.getElementById('pspSubmitBtn').textContent = '确认添加';
document.getElementById('pspName').value = '';
}
}
function closeSpecDrawer() {
document.getElementById('pspDrawerMask').classList.remove('open');
document.getElementById('pspDrawer').classList.remove('open');
}
function selectSpecType(el) {
el.parentElement.querySelectorAll('.psp-pill-btn').forEach(function(b){ b.classList.remove('active'); });
el.classList.add('active');
}
function selectSpecSelect(el) {
el.parentElement.querySelectorAll('.psp-pill-btn').forEach(function(b){ b.classList.remove('active'); });
el.classList.add('active');
}
function addSpecOption() {
var list = document.getElementById('pspOptList');
var row = document.createElement('div');
row.className = 'psp-opt-row';
row.innerHTML = '<input type="text" placeholder="选项名称">' +
'<div class="psp-price-wrap"><span>&yen;</span><input type="number" placeholder="加价金额"></div>' +
'<button class="psp-opt-del" onclick="removeSpecOption(this)">&times;</button>';
list.appendChild(row);
}
function removeSpecOption(el) {
var row = el.closest('.psp-opt-row');
if (row) row.remove();
}
/* 筛选 */
function filterSpecs(type, el) {
document.querySelectorAll('.psp-filter-tab').forEach(function(t){ t.classList.remove('active'); });
el.classList.add('active');
document.querySelectorAll('#pspGrid .psp-card').forEach(function(card) {
if (type === 'all') { card.style.display = ''; return; }
card.style.display = card.getAttribute('data-type') === type ? '' : 'none';
});
}
/* 搜索 */
function searchSpecs(keyword) {
keyword = keyword.trim().toLowerCase();
document.querySelectorAll('#pspGrid .psp-card').forEach(function(card) {
var name = card.getAttribute('data-name').toLowerCase();
card.style.display = (!keyword || name.indexOf(keyword) > -1) ? '' : 'none';
});
}
</script>

549
pages/reviews.html Normal file
View File

@@ -0,0 +1,549 @@
<!-- 评价管理 — reviews.html -->
<style>
/* ---- page-private: rv- prefix ---- */
.rv-toolbar {
background: #fff; border-radius: var(--g-radius); padding: 16px 20px;
display: flex; flex-wrap: wrap; gap: 10px; align-items: center;
box-shadow: var(--g-shadow-sm); border: 1px solid var(--g-border);
}
.rv-toolbar .g-select,
.rv-toolbar .g-input { height: 32px; font-size: 13px; }
.rv-search-wrap {
position: relative; width: 200px;
}
.rv-search-wrap .g-input { padding-left: 30px; width: 100%; }
.rv-search-wrap i {
position: absolute; left: 8px; top: 50%; transform: translateY(-50%);
color: var(--g-text-muted); pointer-events: none;
}
.rv-toolbar-right { margin-left: auto; }
.rv-date-sep { color: var(--g-text-muted); font-size: 13px; line-height: 32px; }
/* stats row */
.rv-stats { display: flex; gap: 16px; margin-top: 12px; }
.rv-stat-card {
flex: 1; background: #fff; border-radius: var(--g-radius); border: 1px solid var(--g-border);
padding: 18px 20px; box-shadow: var(--g-shadow-sm);
transition: box-shadow var(--g-transition);
}
.rv-stat-card:hover { box-shadow: var(--g-shadow-md); }
.rv-stat-value { font-size: 28px; font-weight: 700; color: var(--g-text); line-height: 1.2; }
.rv-stat-value.green { color: var(--g-success); }
.rv-stat-value.orange { color: var(--g-warning); }
.rv-stat-value.primary { color: var(--primary); }
.rv-stat-label { font-size: 12px; color: var(--g-text-muted); margin-top: 6px; }
.rv-stat-title { font-size: 13px; color: var(--g-text-secondary); margin-bottom: 8px; font-weight: 500; }
.rv-stars-inline { display: inline-flex; gap: 2px; vertical-align: middle; margin-left: 6px; }
.rv-stars-inline i { width: 16px; height: 16px; }
/* distribution card */
.rv-dist {
background: #fff; border-radius: var(--g-radius); border: 1px solid var(--g-border);
padding: 20px 24px; margin-top: 12px; box-shadow: var(--g-shadow-sm);
}
.rv-dist-title {
font-size: 15px; font-weight: 600; color: var(--g-text);
padding-left: 10px; border-left: 3px solid var(--primary); margin-bottom: 16px;
}
.rv-dist-row {
display: flex; align-items: center; gap: 12px; margin-bottom: 10px; font-size: 13px;
}
.rv-dist-row:last-child { margin-bottom: 0; }
.rv-dist-label { width: 32px; color: var(--g-text-secondary); flex-shrink: 0; text-align: right; }
.rv-dist-bar {
flex: 1; height: 18px; background: #f5f5f5; border-radius: 9px; overflow: hidden;
}
.rv-dist-fill {
height: 100%; border-radius: 9px; transition: width var(--g-transition);
}
.rv-dist-fill.green { background: var(--g-success); }
.rv-dist-fill.yellow { background: var(--g-warning); }
.rv-dist-fill.red { background: var(--g-danger); }
.rv-dist-count { width: 36px; color: var(--g-text); font-weight: 500; text-align: right; flex-shrink: 0; }
.rv-dist-pct { width: 48px; color: var(--g-text-muted); text-align: right; flex-shrink: 0; }
/* review list */
.rv-list {
margin-top: 12px;
background: #fff; border-radius: var(--g-radius); border: 1px solid var(--g-border);
box-shadow: var(--g-shadow-sm); overflow: hidden;
}
.rv-list-hd {
font-size: 15px; font-weight: 600; color: var(--g-text);
padding: 18px 24px 0; margin-bottom: 4px;
display: flex; align-items: center;
padding-left: 34px;
}
.rv-list-hd::before {
content: ''; display: inline-block; width: 3px; height: 16px;
background: var(--primary); border-radius: 2px; margin-right: 10px; margin-left: -10px;
}
.rv-card {
padding: 18px 24px; border-bottom: 1px solid #f5f5f5;
transition: background var(--g-transition);
}
.rv-card:last-child { border-bottom: none; }
.rv-card:hover { background: color-mix(in srgb, var(--primary) 2%, #fff); }
.rv-card-hd {
display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 8px;
}
.rv-stars { display: inline-flex; gap: 2px; }
.rv-star { width: 14px; height: 14px; }
.rv-star.filled { color: #faad14; }
.rv-star.empty { color: #e5e7eb; }
.rv-star.half { color: #faad14; }
.rv-customer { font-size: 13px; font-weight: 500; color: var(--g-text); }
.rv-time { font-size: 12px; color: var(--g-text-muted); }
.rv-order-link {
font-size: 12px; color: var(--primary); cursor: pointer; font-family: monospace;
text-decoration: none;
}
.rv-order-link:hover { text-decoration: underline; }
.rv-content { font-size: 13px; color: var(--g-text); line-height: 1.6; margin-bottom: 10px; }
.rv-images { display: flex; gap: 8px; margin-bottom: 10px; }
.rv-img-placeholder {
width: 60px; height: 60px; background: #f5f5f5; border-radius: var(--g-radius-sm);
border: 1px solid var(--g-border); display: flex; align-items: center; justify-content: center;
font-size: 11px; color: var(--g-text-muted);
}
.rv-products { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 10px; }
.rv-product-tag {
display: inline-block; padding: 2px 10px; border-radius: 12px;
background: #f5f5f5; font-size: 12px; color: var(--g-text-secondary); border: 1px solid var(--g-border);
}
.rv-reply-box {
background: var(--g-bg-subtle); border-radius: var(--g-radius-sm);
padding: 10px 14px; font-size: 13px; color: var(--g-text-secondary);
border: 1px solid var(--g-border); margin-top: 4px;
}
.rv-reply-prefix { font-weight: 600; color: var(--g-text); margin-right: 4px; }
.rv-reply-time { font-size: 11px; color: var(--g-text-muted); margin-top: 4px; }
/* drawer */
.rv-drawer { width: 480px; }
.rv-section { margin-bottom: 20px; }
.rv-section-hd {
font-size: 14px; font-weight: 600; color: var(--g-text);
padding-left: 10px; border-left: 3px solid var(--primary); margin-bottom: 14px;
}
.rv-info-row {
display: flex; gap: 10px; font-size: 13px; margin-bottom: 8px; align-items: center;
}
.rv-info-label { color: var(--g-text-muted); width: 70px; flex-shrink: 0; }
.rv-info-val { color: var(--g-text); }
.rv-quick-replies { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.rv-quick-reply {
display: inline-block; padding: 5px 12px; border-radius: 14px;
border: 1px solid #d9d9d9; font-size: 12px; cursor: pointer;
background: #fff; color: var(--g-text-secondary);
transition: all var(--g-transition); user-select: none;
}
.rv-quick-reply:hover {
border-color: var(--primary); color: var(--primary);
background: color-mix(in srgb, var(--primary) 6%, #fff);
}
</style>
<!-- 顶部筛选栏 -->
<div class="rv-toolbar">
<select class="g-select" style="width:200px;">
<option>全部门店</option>
<option>老三家外卖(朝阳店)</option>
<option>老三家外卖(海淀店)</option>
<option>老三家外卖(望京店)</option>
<option>老三家外卖(通州店)</option>
<option>老三家外卖(丰台店)</option>
</select>
<select class="g-select" style="width:120px;" onchange="filterRvStars()">
<option>全部评分</option>
<option>5星</option>
<option>4星</option>
<option>3星</option>
<option>2星</option>
<option>1星</option>
</select>
<select class="g-select" style="width:120px;">
<option>全部状态</option>
<option>待回复</option>
<option>已回复</option>
</select>
<input type="date" class="g-input" style="width:145px;" value="2025-01-01">
<span class="rv-date-sep">~</span>
<input type="date" class="g-input" style="width:145px;" value="2025-02-12">
<div class="rv-search-wrap">
<i data-lucide="search" style="width:14px;height:14px;"></i>
<input class="g-input" placeholder="搜索评价内容/订单号">
</div>
<div class="rv-toolbar-right">
<button class="g-btn">
<i data-lucide="download" style="width:14px;height:14px;"></i> 导出
</button>
</div>
</div>
<!-- 统计卡片 -->
<div class="rv-stats">
<div class="rv-stat-card">
<div class="rv-stat-title">综合评分</div>
<div class="rv-stat-value">
4.8
<span class="rv-stars-inline">
<i data-lucide="star" style="fill:#faad14;color:#faad14;"></i>
<i data-lucide="star" style="fill:#faad14;color:#faad14;"></i>
<i data-lucide="star" style="fill:#faad14;color:#faad14;"></i>
<i data-lucide="star" style="fill:#faad14;color:#faad14;"></i>
<i data-lucide="star-half" style="fill:#faad14;color:#faad14;"></i>
</span>
</div>
<div class="rv-stat-label">共 856 条评价</div>
</div>
<div class="rv-stat-card">
<div class="rv-stat-title">好评率</div>
<div class="rv-stat-value green">96.2%</div>
<div class="rv-stat-label">5星+4星占比</div>
</div>
<div class="rv-stat-card">
<div class="rv-stat-title">待回复</div>
<div class="rv-stat-value orange">12</div>
<div class="rv-stat-label">建议24小时内回复</div>
</div>
<div class="rv-stat-card">
<div class="rv-stat-title">本月评价</div>
<div class="rv-stat-value primary">128</div>
<div class="rv-stat-label">较上月 ↑15%</div>
</div>
</div>
<!-- 评分分布 -->
<div class="rv-dist">
<div class="rv-dist-title">评分分布</div>
<div class="rv-dist-row">
<span class="rv-dist-label">5星</span>
<div class="rv-dist-bar"><div class="rv-dist-fill green" style="width:79.4%;"></div></div>
<span class="rv-dist-count">680</span>
<span class="rv-dist-pct">79.4%</span>
</div>
<div class="rv-dist-row">
<span class="rv-dist-label">4星</span>
<div class="rv-dist-bar"><div class="rv-dist-fill green" style="width:16.8%;"></div></div>
<span class="rv-dist-count">144</span>
<span class="rv-dist-pct">16.8%</span>
</div>
<div class="rv-dist-row">
<span class="rv-dist-label">3星</span>
<div class="rv-dist-bar"><div class="rv-dist-fill yellow" style="width:2.1%;"></div></div>
<span class="rv-dist-count">18</span>
<span class="rv-dist-pct">2.1%</span>
</div>
<div class="rv-dist-row">
<span class="rv-dist-label">2星</span>
<div class="rv-dist-bar"><div class="rv-dist-fill red" style="width:0.9%;"></div></div>
<span class="rv-dist-count">8</span>
<span class="rv-dist-pct">0.9%</span>
</div>
<div class="rv-dist-row">
<span class="rv-dist-label">1星</span>
<div class="rv-dist-bar"><div class="rv-dist-fill red" style="width:0.7%;"></div></div>
<span class="rv-dist-count">6</span>
<span class="rv-dist-pct">0.7%</span>
</div>
</div>
<!-- 评价列表 -->
<div class="rv-list">
<div class="rv-list-hd">评价列表</div>
<!-- 评价 1: 5星, 已回复 -->
<div class="rv-card">
<div class="rv-card-hd">
<div class="rv-stars">
<i data-lucide="star" class="rv-star filled" style="fill:#faad14;color:#faad14;"></i>
<i data-lucide="star" class="rv-star filled" style="fill:#faad14;color:#faad14;"></i>
<i data-lucide="star" class="rv-star filled" style="fill:#faad14;color:#faad14;"></i>
<i data-lucide="star" class="rv-star filled" style="fill:#faad14;color:#faad14;"></i>
<i data-lucide="star" class="rv-star filled" style="fill:#faad14;color:#faad14;"></i>
</div>
<span class="rv-customer">张**</span>
<span class="rv-time">2025-02-12 12:30</span>
<span class="g-tag g-tag-blue">外卖</span>
<a class="rv-order-link">#20250212001</a>
</div>
<div class="rv-content">味道很好,配送也快,下次还会点!</div>
<div class="rv-products">
<span class="rv-product-tag">宫保鸡丁</span>
<span class="rv-product-tag">米饭</span>
<span class="rv-product-tag">可乐</span>
</div>
<div class="rv-reply-box">
<span class="rv-reply-prefix">商家回复:</span>感谢您的好评,您的满意是我们最大的动力!期待下次为您服务~
<div class="rv-reply-time">2025-02-12 14:00</div>
</div>
</div>
<!-- 评价 2: 5星, 已回复 -->
<div class="rv-card">
<div class="rv-card-hd">
<div class="rv-stars">
<i data-lucide="star" class="rv-star filled" style="fill:#faad14;color:#faad14;"></i>
<i data-lucide="star" class="rv-star filled" style="fill:#faad14;color:#faad14;"></i>
<i data-lucide="star" class="rv-star filled" style="fill:#faad14;color:#faad14;"></i>
<i data-lucide="star" class="rv-star filled" style="fill:#faad14;color:#faad14;"></i>
<i data-lucide="star" class="rv-star filled" style="fill:#faad14;color:#faad14;"></i>
</div>
<span class="rv-customer">李**</span>
<span class="rv-time">2025-02-12 11:45</span>
<span class="g-tag g-tag-green">自提</span>
<a class="rv-order-link">#20250212002</a>
</div>
<div class="rv-content">套餐很划算,分量足,味道也不错,性价比很高。</div>
<div class="rv-products">
<span class="rv-product-tag">双人超值套餐</span>
<span class="rv-product-tag">酸梅汤</span>
</div>
<div class="rv-reply-box">
<span class="rv-reply-prefix">商家回复:</span>感谢您的支持!我们会继续保持品质,欢迎再次光临!
<div class="rv-reply-time">2025-02-12 13:20</div>
</div>
</div>
<!-- 评价 3: 4星, 待回复 -->
<div class="rv-card">
<div class="rv-card-hd">
<div class="rv-stars">
<i data-lucide="star" class="rv-star filled" style="fill:#faad14;color:#faad14;"></i>
<i data-lucide="star" class="rv-star filled" style="fill:#faad14;color:#faad14;"></i>
<i data-lucide="star" class="rv-star filled" style="fill:#faad14;color:#faad14;"></i>
<i data-lucide="star" class="rv-star filled" style="fill:#faad14;color:#faad14;"></i>
<i data-lucide="star" class="rv-star empty" style="color:#e5e7eb;"></i>
</div>
<span class="rv-customer">王**</span>
<span class="rv-time">2025-02-11 18:20</span>
<span class="g-tag g-tag-orange">堂食</span>
<a class="rv-order-link">#20250211008</a>
<span class="g-tag g-tag-orange">待回复</span>
</div>
<div class="rv-content">菜品不错,就是等了比较久,高峰期可以理解,希望能优化一下出餐速度。</div>
<div class="rv-products">
<span class="rv-product-tag">红烧肉套餐</span>
<span class="rv-product-tag">紫菜蛋花汤</span>
</div>
<div style="margin-top:8px;">
<button class="g-btn g-btn-sm" onclick="openRvDrawer('rv3')">
<i data-lucide="message-square" style="width:12px;height:12px;"></i> 回复
</button>
</div>
</div>
<!-- 评价 4: 3星, 待回复, 有图片 -->
<div class="rv-card">
<div class="rv-card-hd">
<div class="rv-stars">
<i data-lucide="star" class="rv-star filled" style="fill:#faad14;color:#faad14;"></i>
<i data-lucide="star" class="rv-star filled" style="fill:#faad14;color:#faad14;"></i>
<i data-lucide="star" class="rv-star filled" style="fill:#faad14;color:#faad14;"></i>
<i data-lucide="star" class="rv-star empty" style="color:#e5e7eb;"></i>
<i data-lucide="star" class="rv-star empty" style="color:#e5e7eb;"></i>
</div>
<span class="rv-customer">赵**</span>
<span class="rv-time">2025-02-11 12:50</span>
<span class="g-tag g-tag-blue">外卖</span>
<a class="rv-order-link">#20250211005</a>
<span class="g-tag g-tag-orange">待回复</span>
</div>
<div class="rv-content">包装有点漏了,汤洒了一些出来,味道还行但体验打了折扣。</div>
<div class="rv-images">
<div class="rv-img-placeholder"><i data-lucide="image" style="width:16px;height:16px;"></i></div>
<div class="rv-img-placeholder"><i data-lucide="image" style="width:16px;height:16px;"></i></div>
<div class="rv-img-placeholder"><i data-lucide="image" style="width:16px;height:16px;"></i></div>
</div>
<div class="rv-products">
<span class="rv-product-tag">番茄牛腩面</span>
<span class="rv-product-tag">卤蛋</span>
</div>
<div style="margin-top:8px;">
<button class="g-btn g-btn-sm" onclick="openRvDrawer('rv4')">
<i data-lucide="message-square" style="width:12px;height:12px;"></i> 回复
</button>
</div>
</div>
<!-- 评价 5: 2星, 待回复 -->
<div class="rv-card">
<div class="rv-card-hd">
<div class="rv-stars">
<i data-lucide="star" class="rv-star filled" style="fill:#faad14;color:#faad14;"></i>
<i data-lucide="star" class="rv-star filled" style="fill:#faad14;color:#faad14;"></i>
<i data-lucide="star" class="rv-star empty" style="color:#e5e7eb;"></i>
<i data-lucide="star" class="rv-star empty" style="color:#e5e7eb;"></i>
<i data-lucide="star" class="rv-star empty" style="color:#e5e7eb;"></i>
</div>
<span class="rv-customer">孙**</span>
<span class="rv-time">2025-02-10 19:30</span>
<span class="g-tag g-tag-blue">外卖</span>
<a class="rv-order-link">#20250210012</a>
<span class="g-tag g-tag-orange">待回复</span>
</div>
<div class="rv-content">配送太慢了,等了将近一个小时,饭都凉了,体验很差。</div>
<div class="rv-products">
<span class="rv-product-tag">鱼香肉丝盖饭</span>
<span class="rv-product-tag">米饭</span>
</div>
<div style="margin-top:8px;">
<button class="g-btn g-btn-sm" onclick="openRvDrawer('rv5')">
<i data-lucide="message-square" style="width:12px;height:12px;"></i> 回复
</button>
</div>
</div>
<!-- 评价 6: 5星, 已回复 -->
<div class="rv-card">
<div class="rv-card-hd">
<div class="rv-stars">
<i data-lucide="star" class="rv-star filled" style="fill:#faad14;color:#faad14;"></i>
<i data-lucide="star" class="rv-star filled" style="fill:#faad14;color:#faad14;"></i>
<i data-lucide="star" class="rv-star filled" style="fill:#faad14;color:#faad14;"></i>
<i data-lucide="star" class="rv-star filled" style="fill:#faad14;color:#faad14;"></i>
<i data-lucide="star" class="rv-star filled" style="fill:#faad14;color:#faad14;"></i>
</div>
<span class="rv-customer">周**</span>
<span class="rv-time">2025-02-10 12:15</span>
<span class="g-tag g-tag-green">自提</span>
<a class="rv-order-link">#20250210003</a>
</div>
<div class="rv-content">每次来都很满意,老顾客了,菜品质量一直很稳定,值得推荐!</div>
<div class="rv-products">
<span class="rv-product-tag">招牌红烧肉</span>
<span class="rv-product-tag">清炒时蔬</span>
<span class="rv-product-tag">米饭</span>
</div>
<div class="rv-reply-box">
<span class="rv-reply-prefix">商家回复:</span>感谢老顾客的一直支持!我们会继续努力,期待每次为您服务!
<div class="rv-reply-time">2025-02-10 15:30</div>
</div>
</div>
</div>
<!-- 分页 -->
<div class="g-pagination">
<span>共 856 条</span>
<button class="g-page-btn" disabled>&lt;</button>
<button class="g-page-btn active">1</button>
<button class="g-page-btn">2</button>
<button class="g-page-btn">3</button>
<button class="g-page-btn">...</button>
<button class="g-page-btn">86</button>
<button class="g-page-btn">&gt;</button>
</div>
<!-- 回复抽屉 -->
<div class="g-drawer-mask" id="rvDrawerMask" onclick="closeRvDrawer()"></div>
<div class="g-drawer rv-drawer" id="rvDrawer">
<div class="g-drawer-hd">
<span class="g-drawer-title">回复评价</span>
<button class="g-drawer-close" onclick="closeRvDrawer()">&times;</button>
</div>
<div class="g-drawer-bd">
<!-- 评价信息 -->
<div class="rv-section">
<div class="rv-section-hd">评价信息</div>
<div class="rv-info-row">
<span class="rv-info-label">评分</span>
<span class="rv-info-val">
<span class="rv-stars">
<i data-lucide="star" class="rv-star filled" style="fill:#faad14;color:#faad14;width:14px;height:14px;"></i>
<i data-lucide="star" class="rv-star filled" style="fill:#faad14;color:#faad14;width:14px;height:14px;"></i>
<i data-lucide="star" class="rv-star filled" style="fill:#faad14;color:#faad14;width:14px;height:14px;"></i>
<i data-lucide="star" class="rv-star filled" style="fill:#faad14;color:#faad14;width:14px;height:14px;"></i>
<i data-lucide="star" class="rv-star empty" style="color:#e5e7eb;width:14px;height:14px;"></i>
</span>
</span>
</div>
<div class="rv-info-row">
<span class="rv-info-label">顾客</span>
<span class="rv-info-val">王**</span>
</div>
<div class="rv-info-row">
<span class="rv-info-label">时间</span>
<span class="rv-info-val">2025-02-11 18:20</span>
</div>
<div class="rv-info-row" style="align-items:flex-start;">
<span class="rv-info-label">内容</span>
<span class="rv-info-val">菜品不错,就是等了比较久,高峰期可以理解,希望能优化一下出餐速度。</span>
</div>
</div>
<!-- 关联订单 -->
<div class="rv-section">
<div class="rv-section-hd">关联订单</div>
<div class="rv-info-row">
<span class="rv-info-label">订单号</span>
<span class="rv-info-val"><a class="rv-order-link">#20250211008</a></span>
</div>
<div class="rv-info-row">
<span class="rv-info-label">渠道</span>
<span class="rv-info-val"><span class="g-tag g-tag-orange">堂食</span></span>
</div>
<div class="rv-info-row">
<span class="rv-info-label">金额</span>
<span class="rv-info-val" style="font-weight:600;">&yen;68.00</span>
</div>
</div>
<!-- 商品 -->
<div class="rv-section">
<div class="rv-section-hd">商品</div>
<div class="rv-products">
<span class="rv-product-tag">红烧肉套餐</span>
<span class="rv-product-tag">紫菜蛋花汤</span>
</div>
</div>
<!-- 回复内容 -->
<div class="rv-section">
<div class="rv-section-hd">回复内容</div>
<div class="g-form-group">
<textarea class="g-textarea" id="rvReplyText" rows="4" placeholder="请输入回复内容,建议真诚友好地回应顾客反馈"></textarea>
</div>
<div class="g-hint" style="margin-bottom:10px;">快捷回复</div>
<div class="rv-quick-replies">
<span class="rv-quick-reply" onclick="fillQuickReply('感谢您的好评,期待下次光临!')">感谢您的好评,期待下次光临!</span>
<span class="rv-quick-reply" onclick="fillQuickReply('非常抱歉给您带来不好的体验,我们会改进!')">非常抱歉给您带来不好的体验,我们会改进!</span>
<span class="rv-quick-reply" onclick="fillQuickReply('感谢反馈,已记录并会尽快优化!')">感谢反馈,已记录并会尽快优化!</span>
<span class="rv-quick-reply" onclick="fillQuickReply('谢谢支持,欢迎再次光临!')">谢谢支持,欢迎再次光临!</span>
</div>
</div>
</div>
<div class="g-drawer-ft">
<button class="g-btn" onclick="closeRvDrawer()">取消</button>
<button class="g-btn g-btn-primary" onclick="closeRvDrawer()">发送回复</button>
</div>
</div>
<script>
/* 抽屉开关 */
function openRvDrawer(reviewId) {
document.getElementById('rvDrawerMask').classList.add('open');
document.getElementById('rvDrawer').classList.add('open');
}
function closeRvDrawer() {
document.getElementById('rvDrawerMask').classList.remove('open');
document.getElementById('rvDrawer').classList.remove('open');
document.getElementById('rvReplyText').value = '';
}
/* 快捷回复 */
function fillQuickReply(text) {
document.getElementById('rvReplyText').value = text;
document.getElementById('rvReplyText').focus();
}
/* 评分筛选(占位) */
function filterRvStars() {
// 原型占位,无真实逻辑
}
/* 初始化 Lucide 图标 */
document.addEventListener('DOMContentLoaded', function() {
if (typeof lucide !== 'undefined') { lucide.createIcons(); }
});
</script>

281
pages/stat-customer.html Normal file
View File

@@ -0,0 +1,281 @@
<!-- 客户分析 — stat-customer.html -->
<style>
/* ---- page-private: sc- prefix ---- */
.sc-page { display:flex; flex-direction:column; gap:14px; }
/* 顶部统计卡片 */
.sc-kpi-row { display:grid; grid-template-columns:repeat(4,1fr); gap:14px; }
.sc-kpi {
background:#fff; border-radius:var(--g-radius); border:1px solid var(--g-border);
padding:18px 20px; box-shadow:var(--g-shadow-sm); transition:box-shadow var(--g-transition);
}
.sc-kpi:hover { box-shadow:var(--g-shadow-md); }
.sc-kpi-label { font-size:13px; color:var(--g-text-secondary); margin-bottom:8px; display:flex; align-items:center; gap:6px; }
.sc-kpi-label i { width:16px; height:16px; }
.sc-kpi-val { font-size:28px; font-weight:700; color:var(--g-text); line-height:1.2; }
.sc-kpi-hint { font-size:11px; color:var(--g-text-muted); margin-top:6px; display:flex; align-items:center; gap:4px; }
.sc-kpi-hint .up { color:var(--g-success); font-weight:600; }
.sc-kpi-hint .down { color:var(--g-danger); font-weight:600; }
/* 分段Tab */
.sc-seg-wrap { background:#fff; border-radius:var(--g-radius); border:1px solid var(--g-border); padding:14px 20px 0; box-shadow:var(--g-shadow-sm); }
.sc-tab-panel { display:none; padding:16px 0 20px; }
.sc-tab-panel.active { display:block; }
/* Section 标题 */
.sc-section-hd { font-size:15px; font-weight:600; color:var(--g-text); padding-left:10px; border-left:3px solid var(--primary); margin-bottom:16px; }
/* Tab1: 柱状图 */
.sc-chart-wrap { background:#fafafa; border-radius:var(--g-radius); padding:20px; margin-bottom:20px; }
.sc-bar-chart { display:flex; align-items:flex-end; gap:16px; height:220px; padding:10px 0; }
.sc-bar-group { flex:1; display:flex; flex-direction:column; align-items:center; gap:6px; }
.sc-bar-pair { display:flex; gap:4px; align-items:flex-end; height:180px; }
.sc-bar { width:24px; border-radius:4px 4px 0 0; transition:height 0.5s cubic-bezier(.2,0,0,1); position:relative; cursor:pointer; }
.sc-bar:hover { opacity:0.85; }
.sc-bar.new-c { background:linear-gradient(180deg,#1890ff,#69c0ff); }
.sc-bar.ret-c { background:linear-gradient(180deg,#52c41a,#95de64); }
.sc-bar-num { position:absolute; top:-18px; left:50%; transform:translateX(-50%); font-size:10px; color:var(--g-text-secondary); white-space:nowrap; font-weight:600; }
.sc-bar-label { font-size:12px; color:var(--g-text-muted); }
.sc-legend { display:flex; gap:16px; justify-content:center; margin-top:10px; font-size:12px; color:var(--g-text-muted); }
.sc-legend-dot { display:inline-block; width:10px; height:10px; border-radius:2px; margin-right:4px; vertical-align:middle; }
/* Tab2: RFM */
.sc-rfm-info {
background:color-mix(in srgb, var(--primary) 6%, #fff); border:1px solid color-mix(in srgb, var(--primary) 15%, #fff);
border-radius:var(--g-radius-sm); padding:10px 14px; font-size:12px; color:var(--g-text-secondary); margin-bottom:16px;
display:flex; align-items:center; gap:6px;
}
.sc-rfm-info i { width:16px; height:16px; color:var(--primary); flex-shrink:0; }
.sc-rfm-grid { display:grid; grid-template-columns:1fr 1fr 1fr; gap:14px; }
.sc-rfm-card {
border-radius:var(--g-radius); padding:18px; border:1px solid var(--g-border);
transition:box-shadow var(--g-transition); position:relative; overflow:hidden;
}
.sc-rfm-card:hover { box-shadow:var(--g-shadow-md); }
.sc-rfm-card::before { content:''; position:absolute; top:0; left:0; right:0; height:3px; }
.sc-rfm-card.green::before { background:var(--g-success); }
.sc-rfm-card.blue::before { background:var(--primary); }
.sc-rfm-card.orange::before { background:var(--g-warning); }
.sc-rfm-card.red::before { background:var(--g-danger); }
.sc-rfm-card.cyan::before { background:#13c2c2; }
.sc-rfm-card.gray::before { background:#bfbfbf; }
.sc-rfm-name { font-size:14px; font-weight:600; color:var(--g-text); margin-bottom:4px; }
.sc-rfm-desc { font-size:11px; color:var(--g-text-muted); margin-bottom:10px; }
.sc-rfm-num { font-size:22px; font-weight:700; color:var(--g-text); }
.sc-rfm-pct { font-size:12px; color:var(--g-text-secondary); margin-left:6px; }
.sc-rfm-link { font-size:12px; color:var(--primary); cursor:pointer; margin-top:10px; display:inline-block; }
.sc-rfm-link:hover { text-decoration:underline; }
/* Tab3: 流失预警 */
.sc-warn-row { display:grid; grid-template-columns:repeat(3,1fr); gap:14px; margin-bottom:16px; }
.sc-warn-card {
background:#fff; border-radius:var(--g-radius); border:1px solid var(--g-border);
padding:16px 18px; text-align:center; box-shadow:var(--g-shadow-sm);
}
.sc-warn-card-val { font-size:24px; font-weight:700; margin:6px 0 4px; }
.sc-warn-card-val.low { color:var(--g-warning); }
.sc-warn-card-val.mid { color:#fa541c; }
.sc-warn-card-val.high { color:var(--g-danger); }
.sc-warn-card-label { font-size:13px; color:var(--g-text-secondary); }
/* Tab4: 复购周期 - 横向柱状图 */
.sc-hbar-wrap { margin-bottom:20px; }
.sc-hbar-item { display:flex; align-items:center; gap:10px; margin-bottom:10px; }
.sc-hbar-label { width:70px; font-size:13px; color:var(--g-text-secondary); text-align:right; flex-shrink:0; }
.sc-hbar-track { flex:1; height:24px; background:#f5f5f5; border-radius:4px; overflow:hidden; position:relative; }
.sc-hbar-fill { height:100%; border-radius:4px; background:linear-gradient(90deg,#1890ff,#69c0ff); transition:width 0.6s cubic-bezier(.2,0,0,1); display:flex; align-items:center; padding-left:8px; }
.sc-hbar-fill span { font-size:11px; color:#fff; font-weight:600; white-space:nowrap; }
.sc-hbar-val { width:90px; font-size:12px; color:var(--g-text-muted); flex-shrink:0; }
.sc-hbar-desc { font-size:12px; color:var(--g-text-muted); margin-bottom:16px; padding:8px 12px; background:#fafafa; border-radius:var(--g-radius-sm); }
</style>
<div class="sc-page">
<!-- 顶部统计卡片 -->
<div class="sc-kpi-row">
<div class="sc-kpi">
<div class="sc-kpi-label"><i data-lucide="users"></i>累计客户</div>
<div class="sc-kpi-val">2,860</div>
<div class="sc-kpi-hint">较上月 +210</div>
</div>
<div class="sc-kpi">
<div class="sc-kpi-label"><i data-lucide="user-plus"></i>本月新客</div>
<div class="sc-kpi-val">186</div>
<div class="sc-kpi-hint">环比 <span class="up">+12%</span></div>
</div>
<div class="sc-kpi">
<div class="sc-kpi-label"><i data-lucide="repeat"></i>回头客占比</div>
<div class="sc-kpi-val">42.5%</div>
<div class="sc-kpi-hint">环比 <span class="up">+3.2%</span></div>
</div>
<div class="sc-kpi">
<div class="sc-kpi-label"><i data-lucide="calendar-clock"></i>平均消费频次</div>
<div class="sc-kpi-val">2.8<span style="font-size:14px;font-weight:400;color:var(--g-text-muted);margin-left:2px">次/月</span></div>
<div class="sc-kpi-hint">环比 <span class="up">+0.3</span></div>
</div>
</div>
<!-- 分段Tab -->
<div class="sc-seg-wrap">
<div class="g-seg" style="max-width:420px;">
<div class="g-seg-item active" onclick="switchScTab(this,'overview')">客户概览</div>
<div class="g-seg-item" onclick="switchScTab(this,'rfm')">RFM分层</div>
<div class="g-seg-item" onclick="switchScTab(this,'churn')">流失预警</div>
<div class="g-seg-item" onclick="switchScTab(this,'repurchase')">复购周期</div>
</div>
<!-- Tab1: 客户概览 -->
<div id="scTabOverview" class="sc-tab-panel active">
<div class="sc-section-hd">新客 vs 回头客趋势近6个月</div>
<div class="sc-chart-wrap">
<div class="sc-bar-chart">
<div class="sc-bar-group"><div class="sc-bar-pair"><div class="sc-bar new-c" style="height:40%;"><span class="sc-bar-num">120</span></div><div class="sc-bar ret-c" style="height:55%;"><span class="sc-bar-num">165</span></div></div><span class="sc-bar-label">8月</span></div>
<div class="sc-bar-group"><div class="sc-bar-pair"><div class="sc-bar new-c" style="height:48%;"><span class="sc-bar-num">145</span></div><div class="sc-bar ret-c" style="height:52%;"><span class="sc-bar-num">156</span></div></div><span class="sc-bar-label">9月</span></div>
<div class="sc-bar-group"><div class="sc-bar-pair"><div class="sc-bar new-c" style="height:55%;"><span class="sc-bar-num">168</span></div><div class="sc-bar ret-c" style="height:60%;"><span class="sc-bar-num">180</span></div></div><span class="sc-bar-label">10月</span></div>
<div class="sc-bar-group"><div class="sc-bar-pair"><div class="sc-bar new-c" style="height:45%;"><span class="sc-bar-num">138</span></div><div class="sc-bar ret-c" style="height:65%;"><span class="sc-bar-num">195</span></div></div><span class="sc-bar-label">11月</span></div>
<div class="sc-bar-group"><div class="sc-bar-pair"><div class="sc-bar new-c" style="height:52%;"><span class="sc-bar-num">158</span></div><div class="sc-bar ret-c" style="height:70%;"><span class="sc-bar-num">210</span></div></div><span class="sc-bar-label">12月</span></div>
<div class="sc-bar-group"><div class="sc-bar-pair"><div class="sc-bar new-c" style="height:60%;"><span class="sc-bar-num">186</span></div><div class="sc-bar ret-c" style="height:72%;"><span class="sc-bar-num">218</span></div></div><span class="sc-bar-label">1月</span></div>
</div>
<div class="sc-legend">
<span><span class="sc-legend-dot" style="background:#1890ff;"></span>新客</span>
<span><span class="sc-legend-dot" style="background:#52c41a;"></span>回头客</span>
</div>
</div>
<div class="sc-section-hd">客单价分布</div>
<table class="g-table">
<thead><tr><th>价格区间</th><th>客户数</th><th>占比</th><th>累计占比</th></tr></thead>
<tbody>
<tr><td>0 - 20 元</td><td>286</td><td>10.0%</td><td>10.0%</td></tr>
<tr><td>20 - 40 元</td><td>915</td><td>32.0%</td><td>42.0%</td></tr>
<tr><td>40 - 60 元</td><td>743</td><td>26.0%</td><td>68.0%</td></tr>
<tr><td>60 - 80 元</td><td>486</td><td>17.0%</td><td>85.0%</td></tr>
<tr><td>80 - 100 元</td><td>258</td><td>9.0%</td><td>94.0%</td></tr>
<tr><td>100 元以上</td><td>172</td><td>6.0%</td><td>100.0%</td></tr>
</tbody>
</table>
</div>
<!-- Tab2: RFM分层 -->
<div id="scTabRfm" class="sc-tab-panel">
<div class="sc-section-hd">RFM 客户分层</div>
<div class="sc-rfm-info">
<i data-lucide="info"></i>
RFM 模型基于三个维度对客户进行分层最近消费时间Recency、消费频率Frequency、消费金额Monetary帮助识别高价值客户并制定差异化运营策略。
</div>
<div class="sc-rfm-grid">
<div class="sc-rfm-card green">
<div class="sc-rfm-name">重要价值客户</div>
<div class="sc-rfm-desc">R高 F高 M高</div>
<div><span class="sc-rfm-num">320</span><span class="sc-rfm-pct">人,占 11.2%</span></div>
<a class="sc-rfm-link">查看详情 &rarr;</a>
</div>
<div class="sc-rfm-card blue">
<div class="sc-rfm-name">重要发展客户</div>
<div class="sc-rfm-desc">R高 F低 M高</div>
<div><span class="sc-rfm-num">186</span><span class="sc-rfm-pct">人,占 6.5%</span></div>
<a class="sc-rfm-link">查看详情 &rarr;</a>
</div>
<div class="sc-rfm-card orange">
<div class="sc-rfm-name">重要保持客户</div>
<div class="sc-rfm-desc">R低 F高 M高</div>
<div><span class="sc-rfm-num">245</span><span class="sc-rfm-pct">人,占 8.6%</span></div>
<a class="sc-rfm-link">查看详情 &rarr;</a>
</div>
<div class="sc-rfm-card red">
<div class="sc-rfm-name">重要挽留客户</div>
<div class="sc-rfm-desc">R低 F低 M高</div>
<div><span class="sc-rfm-num">128</span><span class="sc-rfm-pct">人,占 4.5%</span></div>
<a class="sc-rfm-link">查看详情 &rarr;</a>
</div>
<div class="sc-rfm-card cyan">
<div class="sc-rfm-name">一般价值客户</div>
<div class="sc-rfm-desc">R高 F高 M低</div>
<div><span class="sc-rfm-num">580</span><span class="sc-rfm-pct">人,占 20.3%</span></div>
<a class="sc-rfm-link">查看详情 &rarr;</a>
</div>
<div class="sc-rfm-card gray">
<div class="sc-rfm-name">一般客户</div>
<div class="sc-rfm-desc">其他组合</div>
<div><span class="sc-rfm-num">1,401</span><span class="sc-rfm-pct">人,占 49.0%</span></div>
<a class="sc-rfm-link">查看详情 &rarr;</a>
</div>
</div>
</div>
<!-- Tab3: 流失预警 -->
<div id="scTabChurn" class="sc-tab-panel">
<div class="sc-section-hd">流失预警</div>
<div class="sc-warn-row">
<div class="sc-warn-card">
<div class="sc-warn-card-label">30天未消费</div>
<div class="sc-warn-card-val low">186</div>
<div style="font-size:11px;color:var(--g-text-muted);"></div>
</div>
<div class="sc-warn-card">
<div class="sc-warn-card-label">60天未消费</div>
<div class="sc-warn-card-val mid">95</div>
<div style="font-size:11px;color:var(--g-text-muted);"></div>
</div>
<div class="sc-warn-card">
<div class="sc-warn-card-label">90天+未消费</div>
<div class="sc-warn-card-val high">42</div>
<div style="font-size:11px;color:var(--g-text-muted);"></div>
</div>
</div>
<table class="g-table">
<thead>
<tr><th>客户名</th><th>手机号</th><th>最后消费时间</th><th>累计消费</th><th>未消费天数</th><th>流失风险</th><th>操作</th></tr>
</thead>
<tbody>
<tr><td>张*明</td><td>138****6721</td><td>2025-11-02</td><td>&yen;1,280</td><td>92</td><td><span class="g-tag g-tag-red"></span></td><td><a class="g-action">发券挽回</a></td></tr>
<tr><td>李*华</td><td>159****3348</td><td>2025-11-18</td><td>&yen;860</td><td>76</td><td><span class="g-tag g-tag-red"></span></td><td><a class="g-action">发券挽回</a></td></tr>
<tr><td>王*</td><td>136****9012</td><td>2025-12-05</td><td>&yen;2,150</td><td>59</td><td><span class="g-tag g-tag-orange"></span></td><td><a class="g-action">发券挽回</a></td></tr>
<tr><td>赵*丽</td><td>188****4567</td><td>2025-12-12</td><td>&yen;560</td><td>52</td><td><span class="g-tag g-tag-orange"></span></td><td><a class="g-action">发券挽回</a></td></tr>
<tr><td>陈*</td><td>177****8901</td><td>2025-12-28</td><td>&yen;3,420</td><td>36</td><td><span class="g-tag g-tag-blue"></span></td><td><a class="g-action">发券挽回</a></td></tr>
<tr><td>刘*强</td><td>155****2345</td><td>2026-01-03</td><td>&yen;720</td><td>30</td><td><span class="g-tag g-tag-blue"></span></td><td><a class="g-action">发券挽回</a></td></tr>
</tbody>
</table>
</div>
<!-- Tab4: 复购周期 -->
<div id="scTabRepurchase" class="sc-tab-panel">
<div class="sc-section-hd">复购周期分布</div>
<div class="sc-hbar-desc">分析客户两次消费之间的间隔天数分布,帮助制定精准的复购提醒策略。</div>
<div class="sc-hbar-wrap">
<div class="sc-hbar-item"><span class="sc-hbar-label">1-3 天</span><div class="sc-hbar-track"><div class="sc-hbar-fill" style="width:65%;"><span>486人</span></div></div><span class="sc-hbar-val">占 32.4%</span></div>
<div class="sc-hbar-item"><span class="sc-hbar-label">4-7 天</span><div class="sc-hbar-track"><div class="sc-hbar-fill" style="width:50%;"><span>375人</span></div></div><span class="sc-hbar-val">占 25.0%</span></div>
<div class="sc-hbar-item"><span class="sc-hbar-label">8-14 天</span><div class="sc-hbar-track"><div class="sc-hbar-fill" style="width:30%;"><span>225人</span></div></div><span class="sc-hbar-val">占 15.0%</span></div>
<div class="sc-hbar-item"><span class="sc-hbar-label">15-30 天</span><div class="sc-hbar-track"><div class="sc-hbar-fill" style="width:25%;"><span>210人</span></div></div><span class="sc-hbar-val">占 14.0%</span></div>
<div class="sc-hbar-item"><span class="sc-hbar-label">30 天+</span><div class="sc-hbar-track"><div class="sc-hbar-fill" style="width:17%;background:linear-gradient(90deg,#fa8c16,#ffc069);"><span>204人</span></div></div><span class="sc-hbar-val">占 13.6%</span></div>
</div>
<div class="sc-section-hd">复购率趋势近6个月</div>
<table class="g-table">
<thead><tr><th>月份</th><th>总客户</th><th>复购客户</th><th>复购率</th><th>环比</th></tr></thead>
<tbody>
<tr><td>2025-08</td><td>680</td><td>245</td><td>36.0%</td><td></td></tr>
<tr><td>2025-09</td><td>720</td><td>268</td><td>37.2%</td><td><span style="color:var(--g-success)">+1.2%</span></td></tr>
<tr><td>2025-10</td><td>785</td><td>306</td><td>39.0%</td><td><span style="color:var(--g-success)">+1.8%</span></td></tr>
<tr><td>2025-11</td><td>810</td><td>324</td><td>40.0%</td><td><span style="color:var(--g-success)">+1.0%</span></td></tr>
<tr><td>2025-12</td><td>856</td><td>350</td><td>40.9%</td><td><span style="color:var(--g-success)">+0.9%</span></td></tr>
<tr><td>2026-01</td><td>892</td><td>379</td><td>42.5%</td><td><span style="color:var(--g-success)">+1.6%</span></td></tr>
</tbody>
</table>
</div>
</div>
</div>
<script>
/* Tab 切换 */
function switchScTab(el, tab) {
el.closest('.g-seg').querySelectorAll('.g-seg-item').forEach(function(s) { s.classList.remove('active'); });
el.classList.add('active');
document.querySelectorAll('.sc-tab-panel').forEach(function(p) { p.classList.remove('active'); });
var map = { overview:'scTabOverview', rfm:'scTabRfm', churn:'scTabChurn', repurchase:'scTabRepurchase' };
document.getElementById(map[tab]).classList.add('active');
}
/* Lucide Icons */
if (typeof lucide !== 'undefined') { lucide.createIcons(); }
</script>

475
pages/stat-marketing.html Normal file
View File

@@ -0,0 +1,475 @@
<!-- 营销分析 -->
<style>
.sma-page { display:flex; flex-direction:column; gap:16px; }
/* 顶部统计卡片 */
.sma-kpi-row { display:grid; grid-template-columns:repeat(4,1fr); gap:14px; }
.sma-kpi { background:#fff; border-radius:var(--g-radius); border:1px solid var(--g-border); padding:18px 20px; display:flex; flex-direction:column; gap:10px; transition:var(--g-transition); }
.sma-kpi:hover { box-shadow:var(--g-shadow-md); transform:translateY(-1px); }
.sma-kpi-top { display:flex; align-items:center; justify-content:space-between; }
.sma-kpi-label { font-size:13px; color:var(--g-text-secondary); }
.sma-kpi-icon { width:36px; height:36px; border-radius:var(--g-radius); display:flex; align-items:center; justify-content:center; flex-shrink:0; }
.sma-kpi-icon.blue { background:hsl(212,100%,95%); color:hsl(212,100%,45%); }
.sma-kpi-icon.orange { background:#fffbeb; color:var(--g-warning); }
.sma-kpi-icon.green { background:#f0fdf4; color:var(--g-success); }
.sma-kpi-icon.purple { background:#f5f3ff; color:#7c3aed; }
.sma-kpi-value { font-size:26px; font-weight:800; color:var(--g-text); letter-spacing:-0.5px; }
.sma-kpi-sub { font-size:12px; color:var(--g-text-muted); }
/* Section 标题 */
.sma-section-hd { font-size:15px; font-weight:600; color:var(--g-text); padding-left:10px; border-left:3px solid var(--primary); margin-bottom:16px; }
/* Tab 内容区 */
.sma-tab-panel { display:none; }
.sma-tab-panel.active { display:block; }
/* 小统计卡片行 */
.sma-mini-row { display:grid; grid-template-columns:repeat(3,1fr); gap:14px; margin-bottom:16px; }
.sma-mini { background:#f8f9fb; border-radius:var(--g-radius); padding:16px 20px; text-align:center; border:1px solid var(--g-border); }
.sma-mini-label { font-size:12px; color:var(--g-text-muted); margin-bottom:6px; }
.sma-mini-val { font-size:22px; font-weight:700; color:var(--g-text); }
/* ROI 颜色 */
.sma-roi-good { color:var(--g-success); font-weight:600; }
.sma-roi-bad { color:var(--g-danger); font-weight:600; }
.sma-roi-mid { color:var(--g-warning); font-weight:600; }
/* 核销率进度条 */
.sma-progress { display:flex; align-items:center; gap:8px; }
.sma-progress-bg { width:80px; height:6px; background:#e5e7eb; border-radius:3px; overflow:hidden; }
.sma-progress-fill { height:100%; border-radius:3px; background:linear-gradient(90deg,hsl(212,100%,45%),hsl(212,100%,65%)); }
/* 漏斗图 */
.sma-funnel-wrap { display:grid; grid-template-columns:1fr 1fr; gap:24px; }
.sma-funnel { display:flex; flex-direction:column; align-items:center; gap:0; padding:20px 0; }
.sma-funnel-step { display:flex; align-items:center; justify-content:center; color:#fff; font-size:14px; font-weight:600; transition:var(--g-transition); position:relative; height:52px; border-radius:4px; }
.sma-funnel-step:hover { filter:brightness(1.08); }
.sma-funnel-step .sma-funnel-num { font-size:12px; font-weight:400; opacity:0.85; margin-left:6px; }
.sma-funnel-arrow { font-size:11px; color:var(--g-text-muted); padding:4px 0; display:flex; align-items:center; gap:4px; }
.sma-funnel-arrow i { color:var(--g-text-muted); }
/* 转化对比表 */
.sma-compare-title { font-size:14px; font-weight:600; color:var(--g-text); margin-bottom:12px; }
.sma-change-up { color:var(--g-success); font-size:12px; }
.sma-change-down { color:var(--g-danger); font-size:12px; }
/* 动画 */
@keyframes smaFadeIn { from { opacity:0; transform:translateY(8px); } to { opacity:1; transform:translateY(0); } }
.sma-page > * { animation:smaFadeIn 0.4s ease both; }
.sma-page > *:nth-child(2) { animation-delay:0.05s; }
.sma-page > *:nth-child(3) { animation-delay:0.1s; }
</style>
<div class="sma-page">
<!-- 1. 顶部 KPI 卡片 -->
<div class="sma-kpi-row">
<div class="sma-kpi">
<div class="sma-kpi-top">
<span class="sma-kpi-label">进行中活动</span>
<div class="sma-kpi-icon blue"><i data-lucide="megaphone" style="width:18px;height:18px"></i></div>
</div>
<div class="sma-kpi-value">6</div>
<div class="sma-kpi-sub">较上月 +2 个</div>
</div>
<div class="sma-kpi">
<div class="sma-kpi-top">
<span class="sma-kpi-label">本月优惠总额</span>
<div class="sma-kpi-icon orange"><i data-lucide="ticket" style="width:18px;height:18px"></i></div>
</div>
<div class="sma-kpi-value">&yen;6,200</div>
<div class="sma-kpi-sub">覆盖 1,840 笔订单</div>
</div>
<div class="sma-kpi">
<div class="sma-kpi-top">
<span class="sma-kpi-label">平均核销率</span>
<div class="sma-kpi-icon green"><i data-lucide="check-circle" style="width:18px;height:18px"></i></div>
</div>
<div class="sma-kpi-value">68.5%</div>
<div class="sma-kpi-sub">行业均值 55%</div>
</div>
<div class="sma-kpi">
<div class="sma-kpi-top">
<span class="sma-kpi-label">营销 ROI</span>
<div class="sma-kpi-icon purple"><i data-lucide="trending-up" style="width:18px;height:18px"></i></div>
</div>
<div class="sma-kpi-value">3.2x</div>
<div class="sma-kpi-sub">每投入 1 元带来 3.2 元收入</div>
</div>
</div>
<!-- 2. 分段 Tab -->
<div class="g-card" style="padding:20px;">
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:20px;">
<div class="sma-section-hd" style="margin-bottom:0;">营销数据分析</div>
<div class="g-seg">
<div class="g-seg-item active" onclick="smaTab(this,0)">活动效果</div>
<div class="g-seg-item" onclick="smaTab(this,1)">优惠券统计</div>
<div class="g-seg-item" onclick="smaTab(this,2)">次卡分析</div>
<div class="g-seg-item" onclick="smaTab(this,3)">转化漏斗</div>
</div>
</div>
<!-- Tab1: 活动效果 -->
<div class="sma-tab-panel active" id="smaPanel0">
<table class="g-table">
<thead>
<tr>
<th>活动名称</th>
<th>类型</th>
<th>时间范围</th>
<th>参与订单</th>
<th>优惠金额</th>
<th>带动销售额</th>
<th>ROI</th>
<th>状态</th>
</tr>
</thead>
<tbody>
<tr>
<td>午市满减狂欢</td>
<td><span class="g-tag g-tag-blue">满减</span></td>
<td>02/01 - 02/28</td>
<td>520</td>
<td>&yen;1,560</td>
<td>&yen;6,240</td>
<td><span class="sma-roi-good">4.0x</span></td>
<td><span class="g-tag g-tag-green">进行中</span></td>
</tr>
<tr>
<td>新品尝鲜第二份半价</td>
<td><span class="g-tag g-tag-purple">第二份半价</span></td>
<td>02/05 - 02/20</td>
<td>310</td>
<td>&yen;930</td>
<td>&yen;3,720</td>
<td><span class="sma-roi-good">4.0x</span></td>
<td><span class="g-tag g-tag-green">进行中</span></td>
</tr>
<tr>
<td>情人节限时折扣</td>
<td><span class="g-tag g-tag-orange">限时折扣</span></td>
<td>02/13 - 02/15</td>
<td>280</td>
<td>&yen;840</td>
<td>&yen;2,800</td>
<td><span class="sma-roi-good">3.3x</span></td>
<td><span class="g-tag g-tag-green">进行中</span></td>
</tr>
<tr>
<td>周末满赠甜品</td>
<td><span class="g-tag g-tag-green">满赠</span></td>
<td>02/01 - 02/28</td>
<td>190</td>
<td>&yen;570</td>
<td>&yen;1,900</td>
<td><span class="sma-roi-good">3.3x</span></td>
<td><span class="g-tag g-tag-green">进行中</span></td>
</tr>
<tr>
<td>春节秒杀专场</td>
<td><span class="g-tag g-tag-red">秒杀</span></td>
<td>01/28 - 02/02</td>
<td>450</td>
<td>&yen;1,350</td>
<td>&yen;3,600</td>
<td><span class="sma-roi-mid">2.7x</span></td>
<td><span class="g-tag g-tag-gray">已结束</span></td>
</tr>
<tr>
<td>元旦满减回馈</td>
<td><span class="g-tag g-tag-blue">满减</span></td>
<td>12/30 - 01/03</td>
<td>380</td>
<td>&yen;1,140</td>
<td>&yen;3,420</td>
<td><span class="sma-roi-good">3.0x</span></td>
<td><span class="g-tag g-tag-gray">已结束</span></td>
</tr>
<tr>
<td>冬季暖饮折扣</td>
<td><span class="g-tag g-tag-orange">限时折扣</span></td>
<td>12/15 - 01/15</td>
<td>620</td>
<td>&yen;1,860</td>
<td>&yen;4,960</td>
<td><span class="sma-roi-mid">2.7x</span></td>
<td><span class="g-tag g-tag-gray">已结束</span></td>
</tr>
<tr>
<td>双12秒杀狂欢</td>
<td><span class="g-tag g-tag-red">秒杀</span></td>
<td>12/10 - 12/13</td>
<td>260</td>
<td>&yen;1,040</td>
<td>&yen;1,820</td>
<td><span class="sma-roi-bad">1.8x</span></td>
<td><span class="g-tag g-tag-gray">已结束</span></td>
</tr>
</tbody>
</table>
</div>
<!-- Tab2: 优惠券统计 -->
<div class="sma-tab-panel" id="smaPanel1">
<div class="sma-mini-row">
<div class="sma-mini">
<div class="sma-mini-label">已发放</div>
<div class="sma-mini-val">1,200 张</div>
</div>
<div class="sma-mini">
<div class="sma-mini-label">已使用</div>
<div class="sma-mini-val">820 张</div>
</div>
<div class="sma-mini">
<div class="sma-mini-label">核销率</div>
<div class="sma-mini-val">68.3%</div>
</div>
</div>
<table class="g-table">
<thead>
<tr>
<th>券名称</th>
<th>类型</th>
<th>面值</th>
<th>发放数</th>
<th>使用数</th>
<th>核销率</th>
<th>带动消费</th>
</tr>
</thead>
<tbody>
<tr>
<td>满50减10元券</td>
<td><span class="g-tag g-tag-blue">满减券</span></td>
<td>&yen;10</td>
<td>400</td>
<td>312</td>
<td>
<div class="sma-progress">
<div class="sma-progress-bg"><div class="sma-progress-fill" style="width:78%"></div></div>
<span style="font-size:12px;color:var(--g-text-secondary)">78.0%</span>
</div>
</td>
<td>&yen;18,720</td>
</tr>
<tr>
<td>8折优享券</td>
<td><span class="g-tag g-tag-purple">折扣券</span></td>
<td>8折</td>
<td>300</td>
<td>198</td>
<td>
<div class="sma-progress">
<div class="sma-progress-bg"><div class="sma-progress-fill" style="width:66%"></div></div>
<span style="font-size:12px;color:var(--g-text-secondary)">66.0%</span>
</div>
</td>
<td>&yen;11,880</td>
</tr>
<tr>
<td>新人免费甜品券</td>
<td><span class="g-tag g-tag-green">免费券</span></td>
<td>&yen;18</td>
<td>200</td>
<td>156</td>
<td>
<div class="sma-progress">
<div class="sma-progress-bg"><div class="sma-progress-fill" style="width:78%"></div></div>
<span style="font-size:12px;color:var(--g-text-secondary)">78.0%</span>
</div>
</td>
<td>&yen;6,240</td>
</tr>
<tr>
<td>满80减15元券</td>
<td><span class="g-tag g-tag-blue">满减券</span></td>
<td>&yen;15</td>
<td>200</td>
<td>108</td>
<td>
<div class="sma-progress">
<div class="sma-progress-bg"><div class="sma-progress-fill" style="width:54%"></div></div>
<span style="font-size:12px;color:var(--g-text-secondary)">54.0%</span>
</div>
</td>
<td>&yen;8,640</td>
</tr>
<tr>
<td>午市专享7折券</td>
<td><span class="g-tag g-tag-purple">折扣券</span></td>
<td>7折</td>
<td>100</td>
<td>46</td>
<td>
<div class="sma-progress">
<div class="sma-progress-bg"><div class="sma-progress-fill" style="width:46%"></div></div>
<span style="font-size:12px;color:var(--g-text-secondary)">46.0%</span>
</div>
</td>
<td>&yen;3,220</td>
</tr>
</tbody>
</table>
</div>
<!-- Tab3: 次卡分析 -->
<div class="sma-tab-panel" id="smaPanel2">
<div class="sma-mini-row">
<div class="sma-mini">
<div class="sma-mini-label">在售次卡</div>
<div class="sma-mini-val">3 种</div>
</div>
<div class="sma-mini">
<div class="sma-mini-label">已售出</div>
<div class="sma-mini-val">156 张</div>
</div>
<div class="sma-mini">
<div class="sma-mini-label">总使用次数</div>
<div class="sma-mini-val">1,280 次</div>
</div>
</div>
<table class="g-table">
<thead>
<tr>
<th>次卡名称</th>
<th>售价</th>
<th>已售</th>
<th>总使用次数</th>
<th>平均使用率</th>
<th>剩余未用次数</th>
<th>收入</th>
</tr>
</thead>
<tbody>
<tr>
<td>咖啡月卡</td>
<td>&yen;128</td>
<td>86 张</td>
<td>720 次</td>
<td>
<div class="sma-progress">
<div class="sma-progress-bg"><div class="sma-progress-fill" style="width:83.7%"></div></div>
<span style="font-size:12px;color:var(--g-text-secondary)">83.7%</span>
</div>
</td>
<td>140 次</td>
<td>&yen;11,008</td>
</tr>
<tr>
<td>甜品5次卡</td>
<td>&yen;68</td>
<td>42 张</td>
<td>340 次</td>
<td>
<div class="sma-progress">
<div class="sma-progress-bg"><div class="sma-progress-fill" style="width:72%"></div></div>
<span style="font-size:12px;color:var(--g-text-secondary)">72.0%</span>
</div>
</td>
<td>60 次</td>
<td>&yen;2,856</td>
</tr>
<tr>
<td>午餐10次卡</td>
<td>&yen;188</td>
<td>28 张</td>
<td>220 次</td>
<td>
<div class="sma-progress">
<div class="sma-progress-bg"><div class="sma-progress-fill" style="width:78.6%"></div></div>
<span style="font-size:12px;color:var(--g-text-secondary)">78.6%</span>
</div>
</td>
<td>60 次</td>
<td>&yen;5,264</td>
</tr>
</tbody>
</table>
</div>
<!-- Tab4: 转化漏斗 -->
<div class="sma-tab-panel" id="smaPanel3">
<div class="sma-funnel-wrap">
<!-- 左侧漏斗图 -->
<div>
<div class="sma-funnel">
<div class="sma-funnel-step" style="width:100%;background:linear-gradient(135deg,#1890ff,#096dd9);">浏览店铺 <span class="sma-funnel-num">12,000 (100%)</span></div>
<div class="sma-funnel-arrow"><i data-lucide="chevron-down" style="width:14px;height:14px"></i> 转化率 40.0%</div>
<div class="sma-funnel-step" style="width:82%;background:linear-gradient(135deg,#36a3f7,#1890ff);">加入购物车 <span class="sma-funnel-num">4,800 (40%)</span></div>
<div class="sma-funnel-arrow"><i data-lucide="chevron-down" style="width:14px;height:14px"></i> 转化率 75.0%</div>
<div class="sma-funnel-step" style="width:64%;background:linear-gradient(135deg,#52c41a,#389e0d);">提交订单 <span class="sma-funnel-num">3,600 (30%)</span></div>
<div class="sma-funnel-arrow"><i data-lucide="chevron-down" style="width:14px;height:14px"></i> 转化率 95.0%</div>
<div class="sma-funnel-step" style="width:52%;background:linear-gradient(135deg,#73d13d,#52c41a);">完成支付 <span class="sma-funnel-num">3,420 (28.5%)</span></div>
<div class="sma-funnel-arrow"><i data-lucide="chevron-down" style="width:14px;height:14px"></i> 转化率 42.4%</div>
<div class="sma-funnel-step" style="width:38%;background:linear-gradient(135deg,#faad14,#d48806);">复购 <span class="sma-funnel-num">1,450 (12.1%)</span></div>
</div>
</div>
<!-- 右侧转化率对比表 -->
<div>
<div class="sma-compare-title">转化率环比对比</div>
<table class="g-table">
<thead>
<tr>
<th>阶段</th>
<th>本月</th>
<th>上月</th>
<th>变化</th>
</tr>
</thead>
<tbody>
<tr>
<td>浏览 → 加购</td>
<td>40.0%</td>
<td>36.5%</td>
<td><span class="sma-change-up"><i data-lucide="trending-up" style="width:12px;height:12px;vertical-align:middle"></i> +3.5%</span></td>
</tr>
<tr>
<td>加购 → 下单</td>
<td>75.0%</td>
<td>72.0%</td>
<td><span class="sma-change-up"><i data-lucide="trending-up" style="width:12px;height:12px;vertical-align:middle"></i> +3.0%</span></td>
</tr>
<tr>
<td>下单 → 支付</td>
<td>95.0%</td>
<td>93.8%</td>
<td><span class="sma-change-up"><i data-lucide="trending-up" style="width:12px;height:12px;vertical-align:middle"></i> +1.2%</span></td>
</tr>
<tr>
<td>支付 → 复购</td>
<td>42.4%</td>
<td>44.1%</td>
<td><span class="sma-change-down"><i data-lucide="trending-down" style="width:12px;height:12px;vertical-align:middle"></i> -1.7%</span></td>
</tr>
<tr>
<td>整体转化</td>
<td>12.1%</td>
<td>10.8%</td>
<td><span class="sma-change-up"><i data-lucide="trending-up" style="width:12px;height:12px;vertical-align:middle"></i> +1.3%</span></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<script>
/* Tab 切换 */
function smaTab(el, idx) {
var items = el.parentElement.querySelectorAll('.g-seg-item');
items.forEach(function(item) { item.classList.remove('active'); });
el.classList.add('active');
for (var i = 0; i < 4; i++) {
var panel = document.getElementById('smaPanel' + i);
if (panel) panel.classList.toggle('active', i === idx);
}
}
/* 初始化 Lucide 图标 */
if (typeof lucide !== 'undefined') { lucide.createIcons(); }
</script>

299
pages/stat-order.html Normal file
View File

@@ -0,0 +1,299 @@
<!-- 订单分析 stat-order -->
<style>
.so-page { display:flex; flex-direction:column; gap:20px; }
/* 筛选栏 */
.so-filter { display:flex; align-items:center; gap:16px; flex-wrap:wrap; }
.so-date-input { height:34px; padding:0 10px; border:1px solid var(--g-border); border-radius:var(--g-radius-sm); font-size:13px; color:var(--g-text); outline:none; transition:var(--g-transition); }
.so-date-input:focus { border-color:var(--primary); }
.so-date-sep { color:var(--g-text-muted); font-size:13px; }
.so-quick-btns { display:flex; gap:6px; }
.so-quick-btn { height:30px; padding:0 12px; border:1px solid var(--g-border); border-radius:var(--g-radius-sm); background:#fff; font-size:12px; color:var(--g-text-secondary); cursor:pointer; transition:var(--g-transition); }
.so-quick-btn:hover { border-color:var(--primary); color:var(--primary); }
.so-quick-btn.active { background:var(--primary-light); border-color:var(--primary); color:var(--primary); font-weight:500; }
.so-channel-btns { display:flex; gap:0; margin-left:auto; }
.so-channel-btn { height:30px; padding:0 14px; border:1px solid var(--g-border); background:#fff; font-size:12px; color:var(--g-text-secondary); cursor:pointer; transition:var(--g-transition); }
.so-channel-btn:first-child { border-radius:var(--g-radius-sm) 0 0 var(--g-radius-sm); }
.so-channel-btn:last-child { border-radius:0 var(--g-radius-sm) var(--g-radius-sm) 0; }
.so-channel-btn + .so-channel-btn { border-left:none; }
.so-channel-btn:hover { color:var(--primary); }
.so-channel-btn.active { background:var(--primary); border-color:var(--primary); color:#fff; }
/* 统计卡片 */
.so-kpi-row { display:grid; grid-template-columns:repeat(4,1fr); gap:16px; }
.so-kpi { border-radius:var(--g-radius); padding:20px 24px; background:#fff; border:1px solid var(--g-border); transition:var(--g-transition); display:flex; flex-direction:column; gap:8px; }
.so-kpi:hover { box-shadow:var(--g-shadow-md); }
.so-kpi-label { font-size:13px; color:var(--g-text-muted); display:flex; align-items:center; gap:6px; }
.so-kpi-label i { width:16px; height:16px; }
.so-kpi-value { font-size:28px; font-weight:700; color:var(--g-text); letter-spacing:-0.5px; }
.so-kpi-sub { font-size:12px; color:var(--g-text-muted); display:flex; align-items:center; gap:4px; }
.so-kpi-sub .up { color:var(--g-success); font-weight:500; }
.so-kpi-sub .down { color:var(--g-danger); font-weight:500; }
/* Section 标题 */
.so-section-hd { font-size:15px; font-weight:600; color:var(--g-text); padding-left:10px; border-left:3px solid var(--primary); margin-bottom:16px; }
/* 趋势图 */
.so-chart-wrap { position:relative; }
.so-chart-svg { width:100%; height:220px; }
.so-chart-summary { display:flex; gap:32px; margin-top:12px; }
.so-chart-stat { font-size:13px; color:var(--g-text-secondary); }
.so-chart-stat strong { color:var(--g-text); font-weight:600; }
/* 热力图 */
.so-heat-wrap { display:flex; gap:20px; align-items:flex-start; }
.so-heat-grid { display:grid; grid-template-columns:48px repeat(14, 36px); gap:2px; }
.so-heat-header { font-size:11px; color:var(--g-text-muted); display:flex; align-items:center; justify-content:center; height:20px; }
.so-heat-row-label { font-size:12px; color:var(--g-text-secondary); display:flex; align-items:center; height:28px; }
.so-heat-cell { width:36px; height:28px; border-radius:3px; transition:var(--g-transition); cursor:default; }
.so-heat-cell:hover { outline:2px solid var(--primary); outline-offset:-1px; }
.so-heat-l0 { background:hsl(212,60%,95%); }
.so-heat-l1 { background:hsl(212,70%,82%); }
.so-heat-l2 { background:hsl(212,80%,65%); }
.so-heat-l3 { background:hsl(212,90%,45%); }
.so-heat-legend { display:flex; flex-direction:column; gap:6px; padding-top:24px; }
.so-heat-legend-item { display:flex; align-items:center; gap:6px; font-size:12px; color:var(--g-text-secondary); }
.so-heat-legend-dot { width:16px; height:12px; border-radius:2px; }
/* 渠道对比 */
.so-channel-row { display:grid; grid-template-columns:repeat(3,1fr); gap:16px; }
.so-ch-card { border-radius:var(--g-radius); padding:24px; background:#fff; border:1px solid var(--g-border); transition:var(--g-transition); }
.so-ch-card:hover { box-shadow:var(--g-shadow-md); }
.so-ch-hd { display:flex; align-items:center; gap:10px; margin-bottom:16px; }
.so-ch-icon { width:36px; height:36px; border-radius:var(--g-radius-sm); display:flex; align-items:center; justify-content:center; }
.so-ch-icon.waimai { background:hsl(212,100%,95%); color:var(--primary); }
.so-ch-icon.ziti { background:hsl(150,60%,94%); color:var(--g-success); }
.so-ch-icon.tangshi { background:hsl(30,90%,94%); color:var(--g-warning); }
.so-ch-name { font-size:15px; font-weight:600; color:var(--g-text); }
.so-ch-metrics { display:flex; flex-direction:column; gap:10px; margin-bottom:16px; }
.so-ch-metric { display:flex; justify-content:space-between; font-size:13px; }
.so-ch-metric-label { color:var(--g-text-muted); }
.so-ch-metric-val { color:var(--g-text); font-weight:500; }
.so-ch-bar-bg { height:8px; background:#f0f0f0; border-radius:4px; overflow:hidden; }
.so-ch-bar-fill { height:100%; border-radius:4px; transition:width 0.6s cubic-bezier(.2,0,0,1); }
.so-ch-bar-fill.waimai { background:linear-gradient(90deg,#1890ff,#69c0ff); }
.so-ch-bar-fill.ziti { background:linear-gradient(90deg,#52c41a,#95de64); }
.so-ch-bar-fill.tangshi { background:linear-gradient(90deg,#fa8c16,#ffc069); }
</style>
<div class="so-page" style="padding:4px 0;">
<!-- 顶部筛选 -->
<div class="g-card" style="padding:16px 20px;">
<div class="so-filter">
<input type="date" class="so-date-input" value="2026-01-30">
<span class="so-date-sep"></span>
<input type="date" class="so-date-input" value="2026-02-12">
<div class="so-quick-btns">
<button class="so-quick-btn" onclick="pickQuick(this)">今天</button>
<button class="so-quick-btn" onclick="pickQuick(this)">近7天</button>
<button class="so-quick-btn active" onclick="pickQuick(this)">近14天</button>
<button class="so-quick-btn" onclick="pickQuick(this)">本月</button>
</div>
<div class="so-channel-btns">
<button class="so-channel-btn active" onclick="pickChannel(this)">全部</button>
<button class="so-channel-btn" onclick="pickChannel(this)">外卖</button>
<button class="so-channel-btn" onclick="pickChannel(this)">自提</button>
<button class="so-channel-btn" onclick="pickChannel(this)">堂食</button>
</div>
</div>
</div>
<!-- 统计卡片 -->
<div class="so-kpi-row">
<div class="so-kpi">
<div class="so-kpi-label"><i data-lucide="file-text" style="width:16px;height:16px;"></i> 总订单数</div>
<div class="so-kpi-value">3,420</div>
<div class="so-kpi-sub">较上期 <span class="up">+5.2%</span></div>
</div>
<div class="so-kpi">
<div class="so-kpi-label"><i data-lucide="circle-check" style="width:16px;height:16px;"></i> 有效订单</div>
<div class="so-kpi-value">3,286</div>
<div class="so-kpi-sub">完成率 <strong style="color:var(--g-success);">96.1%</strong></div>
</div>
<div class="so-kpi">
<div class="so-kpi-label"><i data-lucide="banknote" style="width:16px;height:16px;"></i> 总销售额</div>
<div class="so-kpi-value">¥128,600</div>
<div class="so-kpi-sub">较上期 <span class="up">+8.7%</span></div>
</div>
<div class="so-kpi">
<div class="so-kpi-label"><i data-lucide="receipt" style="width:16px;height:16px;"></i> 平均客单价</div>
<div class="so-kpi-value">¥39.1</div>
<div class="so-kpi-sub">环比 <span class="up">+2.3%</span></div>
</div>
</div>
<!-- 区块1: 订单趋势 -->
<div class="g-card" style="padding:20px 24px;">
<div class="so-section-hd">订单趋势</div>
<div class="so-chart-wrap">
<svg class="so-chart-svg" viewBox="0 0 700 200" preserveAspectRatio="none">
<!-- 网格线 -->
<line x1="40" y1="10" x2="40" y2="170" stroke="#f0f0f0" stroke-width="1"/>
<line x1="40" y1="170" x2="690" y2="170" stroke="#f0f0f0" stroke-width="1"/>
<line x1="40" y1="130" x2="690" y2="130" stroke="#f5f5f5" stroke-width="1" stroke-dasharray="4"/>
<line x1="40" y1="90" x2="690" y2="90" stroke="#f5f5f5" stroke-width="1" stroke-dasharray="4"/>
<line x1="40" y1="50" x2="690" y2="50" stroke="#f5f5f5" stroke-width="1" stroke-dasharray="4"/>
<!-- Y轴标签 -->
<text x="34" y="174" text-anchor="end" font-size="10" fill="#999">100</text>
<text x="34" y="134" text-anchor="end" font-size="10" fill="#999">200</text>
<text x="34" y="94" text-anchor="end" font-size="10" fill="#999">300</text>
<text x="34" y="54" text-anchor="end" font-size="10" fill="#999">400</text>
<!-- 面积 -->
<polygon points="87,98 133,106 180,112 226,118 273,130 319,148 366,138 412,78 459,92 505,102 552,96 598,108 645,104 690,100 690,170 87,170"
fill="url(#soGrad)" opacity="0.15"/>
<defs>
<linearGradient id="soGrad" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="hsl(212,100%,45%)"/>
<stop offset="100%" stop-color="hsl(212,100%,45%)" stop-opacity="0"/>
</linearGradient>
</defs>
<!-- 折线 -->
<polyline points="87,98 133,106 180,112 226,118 273,130 319,148 366,138 412,78 459,92 505,102 552,96 598,108 645,104 690,100"
fill="none" stroke="var(--primary)" stroke-width="2.5" stroke-linejoin="round" stroke-linecap="round"/>
<!-- 关键数据点 -->
<circle cx="319" cy="148" r="4" fill="#fff" stroke="var(--g-danger)" stroke-width="2"/>
<text x="319" y="164" text-anchor="middle" font-size="10" fill="var(--g-danger)">186</text>
<circle cx="412" cy="78" r="4" fill="#fff" stroke="var(--g-success)" stroke-width="2"/>
<text x="412" y="72" text-anchor="middle" font-size="10" fill="var(--g-success)">312</text>
<circle cx="690" cy="100" r="4" fill="#fff" stroke="var(--primary)" stroke-width="2"/>
<text x="690" y="94" text-anchor="middle" font-size="10" fill="var(--primary)">248</text>
<!-- X轴日期 -->
<text x="87" y="186" text-anchor="middle" font-size="10" fill="#999">1/30</text>
<text x="180" y="186" text-anchor="middle" font-size="10" fill="#999">2/1</text>
<text x="273" y="186" text-anchor="middle" font-size="10" fill="#999">2/3</text>
<text x="319" y="186" text-anchor="middle" font-size="10" fill="#999">2/5</text>
<text x="412" y="186" text-anchor="middle" font-size="10" fill="#999">2/8</text>
<text x="505" y="186" text-anchor="middle" font-size="10" fill="#999">2/10</text>
<text x="645" y="186" text-anchor="middle" font-size="10" fill="#999">2/11</text>
<text x="690" y="186" text-anchor="middle" font-size="10" fill="#999">2/12</text>
</svg>
</div>
<div class="so-chart-summary">
<div class="so-chart-stat">日均订单 <strong>244</strong></div>
<div class="so-chart-stat">峰值 <strong>312</strong>2/8</div>
<div class="so-chart-stat">谷值 <strong>186</strong>2/5</div>
</div>
</div>
<!-- 区块2: 时段分布热力图 -->
<div class="g-card" style="padding:20px 24px;">
<div class="so-section-hd">时段分布</div>
<div class="so-heat-wrap">
<div class="so-heat-grid" id="heatGrid"></div>
<div class="so-heat-legend">
<div class="so-heat-legend-item"><div class="so-heat-legend-dot so-heat-l0"></div></div>
<div class="so-heat-legend-item"><div class="so-heat-legend-dot so-heat-l1"></div>较少</div>
<div class="so-heat-legend-item"><div class="so-heat-legend-dot so-heat-l2"></div>较多</div>
<div class="so-heat-legend-item"><div class="so-heat-legend-dot so-heat-l3"></div></div>
</div>
</div>
</div>
<!-- 区块3: 渠道对比 -->
<div class="g-card" style="padding:20px 24px;">
<div class="so-section-hd">渠道对比</div>
<div class="so-channel-row">
<!-- 外卖 -->
<div class="so-ch-card">
<div class="so-ch-hd">
<div class="so-ch-icon waimai"><i data-lucide="bike" style="width:20px;height:20px;"></i></div>
<div class="so-ch-name">外卖</div>
<span class="g-tag g-tag-blue" style="margin-left:auto;">主力渠道</span>
</div>
<div class="so-ch-metrics">
<div class="so-ch-metric"><span class="so-ch-metric-label">订单数</span><span class="so-ch-metric-val">1,881</span></div>
<div class="so-ch-metric"><span class="so-ch-metric-label">销售额</span><span class="so-ch-metric-val">¥70,730</span></div>
<div class="so-ch-metric"><span class="so-ch-metric-label">客单价</span><span class="so-ch-metric-val">¥37.6</span></div>
<div class="so-ch-metric"><span class="so-ch-metric-label">占比</span><span class="so-ch-metric-val">55%</span></div>
</div>
<div class="so-ch-bar-bg"><div class="so-ch-bar-fill waimai" style="width:55%;"></div></div>
</div>
<!-- 自提 -->
<div class="so-ch-card">
<div class="so-ch-hd">
<div class="so-ch-icon ziti"><i data-lucide="package" style="width:20px;height:20px;"></i></div>
<div class="so-ch-name">自提</div>
</div>
<div class="so-ch-metrics">
<div class="so-ch-metric"><span class="so-ch-metric-label">订单数</span><span class="so-ch-metric-val">855</span></div>
<div class="so-ch-metric"><span class="so-ch-metric-label">销售额</span><span class="so-ch-metric-val">¥32,150</span></div>
<div class="so-ch-metric"><span class="so-ch-metric-label">客单价</span><span class="so-ch-metric-val">¥37.6</span></div>
<div class="so-ch-metric"><span class="so-ch-metric-label">占比</span><span class="so-ch-metric-val">25%</span></div>
</div>
<div class="so-ch-bar-bg"><div class="so-ch-bar-fill ziti" style="width:25%;"></div></div>
</div>
<!-- 堂食 -->
<div class="so-ch-card">
<div class="so-ch-hd">
<div class="so-ch-icon tangshi"><i data-lucide="utensils" style="width:20px;height:20px;"></i></div>
<div class="so-ch-name">堂食</div>
</div>
<div class="so-ch-metrics">
<div class="so-ch-metric"><span class="so-ch-metric-label">订单数</span><span class="so-ch-metric-val">684</span></div>
<div class="so-ch-metric"><span class="so-ch-metric-label">销售额</span><span class="so-ch-metric-val">¥25,720</span></div>
<div class="so-ch-metric"><span class="so-ch-metric-label">客单价</span><span class="so-ch-metric-val">¥37.6</span></div>
<div class="so-ch-metric"><span class="so-ch-metric-label">占比</span><span class="so-ch-metric-val">20%</span></div>
</div>
<div class="so-ch-bar-bg"><div class="so-ch-bar-fill tangshi" style="width:20%;"></div></div>
</div>
</div>
</div>
</div>
<script>
/* 快捷日期按钮 */
function pickQuick(el) {
el.parentElement.querySelectorAll('.so-quick-btn').forEach(function(b){ b.classList.remove('active'); });
el.classList.add('active');
}
/* 渠道按钮 */
function pickChannel(el) {
el.parentElement.querySelectorAll('.so-channel-btn').forEach(function(b){ b.classList.remove('active'); });
el.classList.add('active');
}
/* 生成热力图 */
(function(){
var days = ['周一','周二','周三','周四','周五','周六','周日'];
var hours = [];
for(var h=9; h<=22; h++) hours.push(h+':00');
var grid = document.getElementById('heatGrid');
// 模拟数据:周末午晚高峰密度高
var data = [
[0,0,1,1,2,1,1,0,0,1,2,2,1,0],
[0,0,1,1,2,1,0,0,0,1,2,1,1,0],
[0,1,1,2,2,1,1,0,1,2,2,2,1,0],
[0,0,1,2,2,1,1,0,1,2,3,2,1,0],
[0,1,2,2,3,2,1,1,2,3,3,2,1,0],
[1,2,2,3,3,2,2,1,2,3,3,3,2,1],
[1,2,2,3,3,2,1,1,2,3,3,2,2,1]
];
// 表头行
var blank = document.createElement('div');
blank.className = 'so-heat-header';
grid.appendChild(blank);
hours.forEach(function(h){
var hd = document.createElement('div');
hd.className = 'so-heat-header';
hd.textContent = h;
grid.appendChild(hd);
});
// 数据行
days.forEach(function(day, di){
var label = document.createElement('div');
label.className = 'so-heat-row-label';
label.textContent = day;
grid.appendChild(label);
data[di].forEach(function(v){
var cell = document.createElement('div');
cell.className = 'so-heat-cell so-heat-l' + v;
grid.appendChild(cell);
});
});
})();
/* Lucide 图标初始化 */
if (typeof lucide !== 'undefined') { lucide.createIcons(); }
</script>

248
pages/stat-product.html Normal file
View File

@@ -0,0 +1,248 @@
<!-- 商品分析 — stat-product.html -->
<style>
/* ---- page-private: sp- prefix ---- */
.sp-page { display: flex; flex-direction: column; gap: 16px; font-size: 13px; color: var(--g-text); }
/* 顶部筛选栏 */
.sp-toolbar {
background: #fff; border-radius: var(--g-radius); padding: 14px 20px;
display: flex; align-items: center; justify-content: space-between;
box-shadow: var(--g-shadow-sm); border: 1px solid var(--g-border);
}
.sp-month-picker {
display: flex; align-items: center; gap: 12px; user-select: none;
}
.sp-month-arrow {
width: 28px; height: 28px; border-radius: var(--g-radius-sm);
border: 1px solid var(--g-border); background: #fff;
display: flex; align-items: center; justify-content: center;
cursor: pointer; transition: var(--g-transition); color: var(--g-text-secondary);
}
.sp-month-arrow:hover { border-color: var(--primary); color: var(--primary); }
.sp-month-text { font-size: 15px; font-weight: 600; min-width: 100px; text-align: center; }
/* 统计卡片行 */
.sp-kpi-row { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; }
.sp-kpi-card {
background: #fff; border-radius: var(--g-radius); padding: 20px 22px;
border: 1px solid var(--g-border); box-shadow: var(--g-shadow-sm);
transition: var(--g-transition); display: flex; flex-direction: column; gap: 8px;
}
.sp-kpi-card:hover { box-shadow: var(--g-shadow-md); transform: translateY(-1px); }
.sp-kpi-label { font-size: 12px; color: var(--g-text-muted); display: flex; align-items: center; gap: 6px; }
.sp-kpi-label i { width: 16px; height: 16px; }
.sp-kpi-val { font-size: 26px; font-weight: 700; color: var(--g-text); letter-spacing: -0.5px; }
/* 分段 + 内容区 */
.sp-content-card {
background: #fff; border-radius: var(--g-radius); border: 1px solid var(--g-border);
box-shadow: var(--g-shadow-sm); overflow: hidden;
}
.sp-content-hd { padding: 16px 20px 0; }
.sp-tab-panel { display: none; }
.sp-tab-panel.active { display: block; }
/* 排名高亮 */
.sp-rank-top { color: var(--primary); font-weight: 700; }
.sp-trend-up { color: var(--g-success); font-weight: 500; }
.sp-trend-down { color: var(--g-danger); font-weight: 500; }
/* 品类分析布局 */
.sp-category-wrap { display: flex; gap: 32px; padding: 20px; align-items: flex-start; }
.sp-donut-wrap { flex-shrink: 0; display: flex; flex-direction: column; align-items: center; gap: 16px; }
.sp-donut {
width: 180px; height: 180px; border-radius: 50%; position: relative;
background: conic-gradient(
var(--primary) 0deg 162deg,
#22c55e 162deg 252deg,
#f59e0b 252deg 316.8deg,
#a855f7 316.8deg 360deg
);
}
.sp-donut-center {
position: absolute; inset: 36px; background: #fff; border-radius: 50%;
display: flex; flex-direction: column; align-items: center; justify-content: center;
}
.sp-donut-center .num { font-size: 22px; font-weight: 700; color: var(--g-text); }
.sp-donut-center .txt { font-size: 11px; color: var(--g-text-muted); }
.sp-legend { display: flex; flex-wrap: wrap; gap: 10px 20px; }
.sp-legend-item { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--g-text-secondary); }
.sp-legend-dot { width: 8px; height: 8px; border-radius: 2px; flex-shrink: 0; }
.sp-category-table { flex: 1; min-width: 0; }
/* 滞销预警提示条 */
.sp-warn-bar {
display: flex; align-items: center; gap: 8px; padding: 10px 16px;
background: #fffbe6; border: 1px solid #ffe58f; border-radius: var(--g-radius-sm);
margin: 16px 20px 0; font-size: 13px; color: #ad6800;
}
.sp-warn-bar i { width: 16px; height: 16px; flex-shrink: 0; }
.sp-low-sales { color: var(--g-danger); font-weight: 600; }
</style>
<div class="sp-page">
<!-- 顶部筛选栏 -->
<div class="sp-toolbar">
<div class="sp-month-picker">
<div class="sp-month-arrow" onclick="spChangeMonth(-1)"><i data-lucide="chevron-left" style="width:16px;height:16px;"></i></div>
<span class="sp-month-text" id="spMonthText">2026年2月</span>
<div class="sp-month-arrow" onclick="spChangeMonth(1)"><i data-lucide="chevron-right" style="width:16px;height:16px;"></i></div>
</div>
<select class="g-select" style="width:160px;">
<option>全部渠道</option>
<option>外卖</option>
<option>自提</option>
<option>堂食</option>
</select>
</div>
<!-- 统计卡片行 -->
<div class="sp-kpi-row">
<div class="sp-kpi-card">
<div class="sp-kpi-label"><i data-lucide="package"></i> 在售商品数</div>
<div class="sp-kpi-val">86</div>
</div>
<div class="sp-kpi-card">
<div class="sp-kpi-label"><i data-lucide="shopping-bag"></i> 本月销量</div>
<div class="sp-kpi-val">3,420</div>
</div>
<div class="sp-kpi-card">
<div class="sp-kpi-label"><i data-lucide="banknote"></i> 销售额</div>
<div class="sp-kpi-val">¥45,680</div>
</div>
<div class="sp-kpi-card">
<div class="sp-kpi-label"><i data-lucide="receipt"></i> 平均客单价</div>
<div class="sp-kpi-val">¥38.5</div>
</div>
</div>
<!-- 分段Tab + 内容 -->
<div class="sp-content-card">
<div class="sp-content-hd">
<div class="g-seg">
<div class="g-seg-item active" onclick="spSwitchTab(this,'rank')">销量排行</div>
<div class="g-seg-item" onclick="spSwitchTab(this,'category')">品类分析</div>
<div class="g-seg-item" onclick="spSwitchTab(this,'slow')">滞销预警</div>
</div>
</div>
<!-- Tab1: 销量排行 -->
<div id="spTabRank" class="sp-tab-panel active">
<table class="g-table">
<thead>
<tr>
<th style="width:60px;">排名</th>
<th>商品名称</th>
<th>品类</th>
<th style="text-align:right;">销量</th>
<th style="text-align:right;">销售额</th>
<th style="text-align:right;">占比</th>
<th style="text-align:right;">趋势</th>
</tr>
</thead>
<tbody>
<tr><td class="sp-rank-top">1</td><td>招牌牛肉饭</td><td><span class="g-tag g-tag-blue">主食</span></td><td style="text-align:right;">520</td><td style="text-align:right;">¥7,800</td><td style="text-align:right;">15.2%</td><td style="text-align:right;" class="sp-trend-up">↑ 12.3%</td></tr>
<tr><td class="sp-rank-top">2</td><td>香辣鸡腿堡</td><td><span class="g-tag g-tag-blue">主食</span></td><td style="text-align:right;">468</td><td style="text-align:right;">¥6,552</td><td style="text-align:right;">13.7%</td><td style="text-align:right;" class="sp-trend-up">↑ 8.1%</td></tr>
<tr><td class="sp-rank-top">3</td><td>珍珠奶茶</td><td><span class="g-tag g-tag-green">饮品</span></td><td style="text-align:right;">412</td><td style="text-align:right;">¥4,944</td><td style="text-align:right;">12.0%</td><td style="text-align:right;" class="sp-trend-down">↓ 2.5%</td></tr>
<tr><td>4</td><td>黄焖鸡米饭</td><td><span class="g-tag g-tag-blue">主食</span></td><td style="text-align:right;">356</td><td style="text-align:right;">¥5,340</td><td style="text-align:right;">10.4%</td><td style="text-align:right;" class="sp-trend-up">↑ 5.6%</td></tr>
<tr><td>5</td><td>薯条(大份)</td><td><span class="g-tag g-tag-orange">小吃</span></td><td style="text-align:right;">298</td><td style="text-align:right;">¥2,980</td><td style="text-align:right;">8.7%</td><td style="text-align:right;" class="sp-trend-down">↓ 1.2%</td></tr>
<tr><td>6</td><td>冰美式咖啡</td><td><span class="g-tag g-tag-green">饮品</span></td><td style="text-align:right;">265</td><td style="text-align:right;">¥3,180</td><td style="text-align:right;">7.7%</td><td style="text-align:right;" class="sp-trend-up">↑ 15.8%</td></tr>
<tr><td>7</td><td>芒果千层蛋糕</td><td><span class="g-tag g-tag-purple">甜品</span></td><td style="text-align:right;">224</td><td style="text-align:right;">¥4,480</td><td style="text-align:right;">6.5%</td><td style="text-align:right;" class="sp-trend-up">↑ 3.2%</td></tr>
<tr><td>8</td><td>鸡米花</td><td><span class="g-tag g-tag-orange">小吃</span></td><td style="text-align:right;">198</td><td style="text-align:right;">¥1,584</td><td style="text-align:right;">5.8%</td><td style="text-align:right;" class="sp-trend-down">↓ 4.7%</td></tr>
<tr><td>9</td><td>麻辣烫套餐</td><td><span class="g-tag g-tag-blue">主食</span></td><td style="text-align:right;">176</td><td style="text-align:right;">¥3,520</td><td style="text-align:right;">5.1%</td><td style="text-align:right;" class="sp-trend-up">↑ 6.9%</td></tr>
<tr><td>10</td><td>柠檬茶</td><td><span class="g-tag g-tag-green">饮品</span></td><td style="text-align:right;">153</td><td style="text-align:right;">¥1,530</td><td style="text-align:right;">4.5%</td><td style="text-align:right;" class="sp-trend-down">↓ 0.8%</td></tr>
</tbody>
</table>
</div>
<!-- Tab2: 品类分析 -->
<div id="spTabCategory" class="sp-tab-panel">
<div class="sp-category-wrap">
<div class="sp-donut-wrap">
<div class="sp-donut">
<div class="sp-donut-center">
<span class="num">3,420</span>
<span class="txt">总销量</span>
</div>
</div>
<div class="sp-legend">
<div class="sp-legend-item"><span class="sp-legend-dot" style="background:var(--primary);"></span> 主食 45%</div>
<div class="sp-legend-item"><span class="sp-legend-dot" style="background:#22c55e;"></span> 饮品 25%</div>
<div class="sp-legend-item"><span class="sp-legend-dot" style="background:#f59e0b;"></span> 小吃 18%</div>
<div class="sp-legend-item"><span class="sp-legend-dot" style="background:#a855f7;"></span> 甜品 12%</div>
</div>
</div>
<div class="sp-category-table">
<table class="g-table">
<thead>
<tr>
<th>品类</th>
<th style="text-align:right;">商品数</th>
<th style="text-align:right;">销量</th>
<th style="text-align:right;">销售额</th>
<th style="text-align:right;">占比</th>
<th style="text-align:right;">环比变化</th>
</tr>
</thead>
<tbody>
<tr><td><span class="g-tag g-tag-blue">主食</span></td><td style="text-align:right;">32</td><td style="text-align:right;">1,539</td><td style="text-align:right;">¥23,085</td><td style="text-align:right;">45%</td><td style="text-align:right;" class="sp-trend-up">↑ 6.2%</td></tr>
<tr><td><span class="g-tag g-tag-green">饮品</span></td><td style="text-align:right;">22</td><td style="text-align:right;">855</td><td style="text-align:right;">¥10,260</td><td style="text-align:right;">25%</td><td style="text-align:right;" class="sp-trend-down">↓ 1.8%</td></tr>
<tr><td><span class="g-tag g-tag-orange">小吃</span></td><td style="text-align:right;">18</td><td style="text-align:right;">616</td><td style="text-align:right;">¥6,160</td><td style="text-align:right;">18%</td><td style="text-align:right;" class="sp-trend-up">↑ 3.5%</td></tr>
<tr><td><span class="g-tag g-tag-purple">甜品</span></td><td style="text-align:right;">14</td><td style="text-align:right;">410</td><td style="text-align:right;">¥6,175</td><td style="text-align:right;">12%</td><td style="text-align:right;" class="sp-trend-up">↑ 9.1%</td></tr>
</tbody>
</table>
</div>
</div>
</div>
<!-- Tab3: 滞销预警 -->
<div id="spTabSlow" class="sp-tab-panel">
<div class="sp-warn-bar">
<i data-lucide="alert-triangle"></i>
以下商品近30天销量低于5单建议优化或下架
</div>
<table class="g-table" style="margin-top:0;">
<thead>
<tr>
<th>商品名称</th>
<th>品类</th>
<th style="text-align:right;">近30天销量</th>
<th style="text-align:right;">库存</th>
<th style="text-align:right;">上架天数</th>
<th style="text-align:center;">操作</th>
</tr>
</thead>
<tbody>
<tr><td>蓝莓慕斯</td><td><span class="g-tag g-tag-purple">甜品</span></td><td style="text-align:right;" class="sp-low-sales">2</td><td style="text-align:right;">15</td><td style="text-align:right;">120</td><td style="text-align:center;"><a class="g-action">查看</a> <a class="g-action g-action-danger">下架</a></td></tr>
<tr><td>抹茶拿铁</td><td><span class="g-tag g-tag-green">饮品</span></td><td style="text-align:right;" class="sp-low-sales">3</td><td style="text-align:right;">28</td><td style="text-align:right;">90</td><td style="text-align:center;"><a class="g-action">查看</a> <a class="g-action g-action-danger">下架</a></td></tr>
<tr><td>芝士焗饭</td><td><span class="g-tag g-tag-blue">主食</span></td><td style="text-align:right;" class="sp-low-sales">1</td><td style="text-align:right;">8</td><td style="text-align:right;">180</td><td style="text-align:center;"><a class="g-action">查看</a> <a class="g-action g-action-danger">下架</a></td></tr>
<tr><td>杨枝甘露</td><td><span class="g-tag g-tag-green">饮品</span></td><td style="text-align:right;" class="sp-low-sales">4</td><td style="text-align:right;">20</td><td style="text-align:right;">60</td><td style="text-align:center;"><a class="g-action">查看</a> <a class="g-action g-action-danger">下架</a></td></tr>
<tr><td>香草冰淇淋</td><td><span class="g-tag g-tag-purple">甜品</span></td><td style="text-align:right;" class="sp-low-sales">0</td><td style="text-align:right;">35</td><td style="text-align:right;">210</td><td style="text-align:center;"><a class="g-action">查看</a> <a class="g-action g-action-danger">下架</a></td></tr>
</tbody>
</table>
</div>
</div>
</div>
<script>
/* 分段Tab切换 */
function spSwitchTab(el, tab) {
el.parentElement.querySelectorAll('.g-seg-item').forEach(function(s) { s.classList.remove('active'); });
el.classList.add('active');
document.querySelectorAll('.sp-tab-panel').forEach(function(p) { p.classList.remove('active'); });
var map = { rank: 'spTabRank', category: 'spTabCategory', slow: 'spTabSlow' };
document.getElementById(map[tab]).classList.add('active');
}
/* 月份切换(纯展示) */
var spMonth = 2, spYear = 2026;
function spChangeMonth(dir) {
spMonth += dir;
if (spMonth > 12) { spMonth = 1; spYear++; }
if (spMonth < 1) { spMonth = 12; spYear--; }
document.getElementById('spMonthText').textContent = spYear + '年' + spMonth + '月';
}
/* 初始化图标 */
if (typeof lucide !== 'undefined') { lucide.createIcons(); }
</script>

250
pages/store-delivery.html Normal file
View File

@@ -0,0 +1,250 @@
<!-- 配送设置页 -->
<style>
.page-delivery { max-width:960px; }
.pd-toolbar { display:flex; align-items:center; gap:12px; margin-bottom:16px; box-shadow:var(--g-shadow-sm); border-radius:10px; padding:10px 14px; background:#fff; }
.pd-toolbar select { height:34px; padding:0 10px; border:1px solid #e5e7eb; border-radius:8px; font-size:13px; outline:none; background:#fff; transition:var(--g-transition); }
.pd-toolbar select:focus { border-color:var(--primary); box-shadow:0 0 0 3px color-mix(in srgb, var(--primary) 12%, transparent); }
/* Mode Switch */
.mode-switch { display:flex; background:#f8f9fb; border-radius:8px; padding:3px; gap:2px; width:fit-content; margin-bottom:16px; }
.mode-switch-item { padding:6px 18px; border-radius:6px; font-size:13px; cursor:pointer; color:#4b5563; transition:var(--g-transition); border:none; background:none; }
.mode-switch-item.active { background:#fff; color:var(--primary); font-weight:600; box-shadow:var(--g-shadow-sm); }
/* Map Placeholder */
.map-area { width:100%; height:320px; background:#f0f5ff; border-radius:10px; border:1px dashed #adc6ff; display:flex; flex-direction:column; align-items:center; justify-content:center; color:#597ef7; margin-bottom:16px; position:relative; overflow:hidden; }
.map-area .map-icon { font-size:48px; margin-bottom:8px; opacity:0.5; }
.map-area .map-label { font-size:13px; opacity:0.7; }
.map-area .map-overlay { position:absolute; top:0; left:0; right:0; bottom:0; }
/* Fake map grid */
.map-area .grid-line { position:absolute; background:#d6e4ff; }
.map-area .grid-h { height:1px; left:0; right:0; }
.map-area .grid-v { width:1px; top:0; bottom:0; }
.map-area .map-pin { position:absolute; top:50%; left:50%; transform:translate(-50%,-100%); font-size:24px; z-index:2; }
/* Radius circles */
.map-area .radius-circle { position:absolute; top:50%; left:50%; border-radius:50%; border:2px dashed; transform:translate(-50%,-50%); }
.map-area .rc1 { width:100px; height:100px; border-color:#22c55e; background:rgba(34,197,94,0.06); }
.map-area .rc2 { width:180px; height:180px; border-color:#f59e0b; background:rgba(245,158,11,0.04); }
.map-area .rc3 { width:260px; height:260px; border-color:#ef4444; background:rgba(239,68,68,0.03); }
.map-area .rc-label { position:absolute; font-size:10px; font-weight:600; white-space:nowrap; }
.map-area .rc1 .rc-label { bottom:-16px; left:50%; transform:translateX(-50%); color:#22c55e; }
.map-area .rc2 .rc-label { bottom:-16px; left:50%; transform:translateX(-50%); color:#f59e0b; }
.map-area .rc3 .rc-label { bottom:-16px; left:50%; transform:translateX(-50%); color:#ef4444; }
/* Zone Table */
.zone-table { width:100%; border-collapse:collapse; font-size:13px; }
.zone-table th { background:#f8f9fb; padding:10px 12px; text-align:left; font-weight:600; color:#6b7280; border-bottom:1px solid #e5e7eb; }
.zone-table td { padding:10px 12px; border-bottom:1px solid #f3f4f6; color:#1a1a2e; }
.zone-table tr:last-child td { border-bottom:none; }
.zone-table tr:hover td { background:color-mix(in srgb, var(--primary) 3%, #fff); }
.zone-color { width:12px; height:12px; border-radius:3px; display:inline-block; vertical-align:middle; margin-right:6px; }
.z-link { color:var(--primary); cursor:pointer; font-size:12px; margin-right:8px; transition:var(--g-transition); }
.z-link:hover { text-decoration:underline; }
.z-link.danger { color:#ef4444; }
/* Radius Tier Cards */
.tier-list { display:flex; flex-direction:column; gap:10px; }
.tier-card { display:grid; grid-template-columns:40px 1fr 1fr 1fr 1fr auto; gap:12px; align-items:center; padding:12px 14px; background:#f8f9fb; border-radius:10px; border:none; box-shadow:var(--g-shadow-sm); transition:var(--g-transition); }
.tier-card:hover { box-shadow:var(--g-shadow-md); }
.tier-num { width:28px; height:28px; border-radius:50%; display:flex; align-items:center; justify-content:center; font-size:12px; font-weight:600; color:#fff; }
.tier-field label { font-size:11px; color:#9ca3af; display:block; margin-bottom:3px; }
.tier-field .val { font-size:13px; font-weight:500; color:#1a1a2e; }
.tier-actions { display:flex; gap:4px; }
.tier-edit { width:28px; height:28px; display:flex; align-items:center; justify-content:center; border-radius:8px; cursor:pointer; color:#9ca3af; border:none; background:none; transition:var(--g-transition); }
.tier-edit:hover { background:#fff; color:var(--primary); }
.pd-tip { font-size:12px; color:#9ca3af; margin-top:8px; display:flex; align-items:center; gap:4px; }
</style>
<div class="page-delivery">
<div class="pd-toolbar">
<select style="width:200px;">
<option>老三家外卖(朝阳店)</option>
<option>老三家外卖(海淀店)</option>
<option>老三家外卖(望京店)</option>
<option>老三家外卖(通州店)</option>
<option>老三家外卖(丰台店)</option>
</select>
<div style="flex:1;"></div>
<button class="g-btn" onclick="openCopyStoreModal('复制配送设置到其他门店')">
<i data-lucide="copy" style="width:14px;height:14px;"></i>复制到其他门店
</button>
</div>
<!-- 配送模式 -->
<div class="g-card" style="padding:0; margin-bottom:16px;">
<div class="g-card-title" style="padding:14px 18px; margin-bottom:0; border-bottom:1px solid #f5f5f5;">配送模式</div>
<div style="padding:16px 18px;">
<div class="mode-switch">
<button class="mode-switch-item active" onclick="switchDeliveryTab('radius')">按半径配送</button>
<button class="mode-switch-item" onclick="switchDeliveryTab('polygon')">按区域配送(多边形)</button>
</div>
<!-- 地图区域 -->
<div class="map-area">
<div class="grid-line grid-h" style="top:25%;"></div>
<div class="grid-line grid-h" style="top:50%;"></div>
<div class="grid-line grid-h" style="top:75%;"></div>
<div class="grid-line grid-v" style="left:25%;"></div>
<div class="grid-line grid-v" style="left:50%;"></div>
<div class="grid-line grid-v" style="left:75%;"></div>
<div class="map-pin"><i data-lucide="map-pin" style="width:16px;height:16px"></i></div>
<!-- Radius mode circles -->
<div id="radiusOverlay">
<div class="radius-circle rc3"><span class="rc-label">5km</span></div>
<div class="radius-circle rc2"><span class="rc-label">3km</span></div>
<div class="radius-circle rc1"><span class="rc-label">1km</span></div>
</div>
<!-- Polygon mode hint (hidden by default) -->
<div id="polygonOverlay" style="display:none; text-align:center;">
<div class="map-icon"><i data-lucide="map" style="width:16px;height:16px"></i></div>
<div class="map-label">点击地图绘制配送区域多边形</div>
</div>
</div>
</div>
</div>
<!-- 半径配送 - 距离梯度 -->
<div class="g-card" style="padding:0; margin-bottom:16px;" id="radiusSection">
<div class="g-card-title" style="padding:14px 18px; margin-bottom:0; border-bottom:1px solid #f5f5f5;">
距离梯度
<button class="g-btn g-btn-primary g-btn-sm">
<i data-lucide="plus" style="width:13px;height:13px;"></i>添加梯度
</button>
</div>
<div style="padding:16px 18px;">
<div class="tier-list">
<div class="tier-card">
<div class="tier-num" style="background:#52c41a;">1</div>
<div class="tier-field"><label>距离范围</label><div class="val">0 ~ 1 km</div></div>
<div class="tier-field"><label>配送费</label><div class="val">¥3.00</div></div>
<div class="tier-field"><label>预计送达</label><div class="val">20 分钟</div></div>
<div class="tier-field"><label>起送金额</label><div class="val">¥15.00</div></div>
<div class="tier-actions">
<button class="tier-edit"><i data-lucide="pencil" style="width:14px;height:14px;"></i></button>
<button class="tier-edit"><i data-lucide="trash-2" style="width:14px;height:14px;color:#ff4d4f;"></i></button>
</div>
</div>
<div class="tier-card">
<div class="tier-num" style="background:#faad14;">2</div>
<div class="tier-field"><label>距离范围</label><div class="val">1 ~ 3 km</div></div>
<div class="tier-field"><label>配送费</label><div class="val">¥5.00</div></div>
<div class="tier-field"><label>预计送达</label><div class="val">35 分钟</div></div>
<div class="tier-field"><label>起送金额</label><div class="val">¥20.00</div></div>
<div class="tier-actions">
<button class="tier-edit"><i data-lucide="pencil" style="width:14px;height:14px;"></i></button>
<button class="tier-edit"><i data-lucide="trash-2" style="width:14px;height:14px;color:#ff4d4f;"></i></button>
</div>
</div>
<div class="tier-card">
<div class="tier-num" style="background:#ff4d4f;">3</div>
<div class="tier-field"><label>距离范围</label><div class="val">3 ~ 5 km</div></div>
<div class="tier-field"><label>配送费</label><div class="val">¥8.00</div></div>
<div class="tier-field"><label>预计送达</label><div class="val">50 分钟</div></div>
<div class="tier-field"><label>起送金额</label><div class="val">¥25.00</div></div>
<div class="tier-actions">
<button class="tier-edit"><i data-lucide="pencil" style="width:14px;height:14px;"></i></button>
<button class="tier-edit"><i data-lucide="trash-2" style="width:14px;height:14px;color:#ff4d4f;"></i></button>
</div>
</div>
</div>
<div class="pd-tip"><i data-lucide="info" style="width:13px;height:13px;"></i>超出最大配送半径的地址将无法下单</div>
</div>
</div>
<!-- 多边形配送 - 区域列表 (hidden by default) -->
<div class="g-card" style="padding:0; margin-bottom:16px; display:none;" id="polygonSection">
<div class="g-card-title" style="padding:14px 18px; margin-bottom:0; border-bottom:1px solid #f5f5f5;">
配送区域
<button class="g-btn g-btn-primary g-btn-sm">
<i data-lucide="plus" style="width:13px;height:13px;"></i>绘制新区域
</button>
</div>
<div style="padding:0;">
<table class="zone-table">
<thead><tr><th>区域名称</th><th>配送费</th><th>起送金额</th><th>预计送达</th><th>优先级</th><th style="width:100px;">操作</th></tr></thead>
<tbody>
<tr>
<td><span class="zone-color" style="background:#52c41a;"></span>核心区域</td>
<td>¥3.00</td>
<td>¥15.00</td>
<td>20 分钟</td>
<td>1</td>
<td><a class="z-link">编辑</a><a class="z-link danger">删除</a></td>
</tr>
<tr>
<td><span class="zone-color" style="background:#1890ff;"></span>朝阳CBD</td>
<td>¥5.00</td>
<td>¥20.00</td>
<td>35 分钟</td>
<td>2</td>
<td><a class="z-link">编辑</a><a class="z-link danger">删除</a></td>
</tr>
<tr>
<td><span class="zone-color" style="background:#faad14;"></span>三里屯片区</td>
<td>¥6.00</td>
<td>¥25.00</td>
<td>40 分钟</td>
<td>3</td>
<td><a class="z-link">编辑</a><a class="z-link danger">删除</a></td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- 通用配送设置 -->
<div class="g-card" style="padding:0; margin-bottom:16px;">
<div class="g-card-title" style="padding:14px 18px; margin-bottom:0; border-bottom:1px solid #f5f5f5;">通用设置</div>
<div style="padding:16px 18px;">
<div style="display:grid; grid-template-columns:1fr 1fr; gap:16px 24px;">
<div>
<label style="font-size:12px; color:#666; display:block; margin-bottom:6px;">免配送费门槛</label>
<div style="display:flex; align-items:center; gap:6px;">
<input type="number" value="30" placeholder="如50" style="width:120px; height:32px; padding:0 10px; border:1px solid #d9d9d9; border-radius:6px; font-size:13px; outline:none;" />
<span style="font-size:13px; color:#666;"></span>
</div>
<div style="font-size:11px; color:#999; margin-top:4px;">订单满此金额免配送费,留空则不启用</div>
</div>
<div>
<label style="font-size:12px; color:#666; display:block; margin-bottom:6px;">最大配送距离</label>
<div style="display:flex; align-items:center; gap:6px;">
<input type="number" value="5" placeholder="如5" style="width:120px; height:32px; padding:0 10px; border:1px solid #d9d9d9; border-radius:6px; font-size:13px; outline:none;" />
<span style="font-size:13px; color:#666;">公里</span>
</div>
<div style="font-size:11px; color:#999; margin-top:4px;">仅半径模式生效</div>
</div>
<div>
<label style="font-size:12px; color:#666; display:block; margin-bottom:6px;">每小时最大配送单量</label>
<div style="display:flex; align-items:center; gap:6px;">
<input type="number" value="50" placeholder="如50" style="width:120px; height:32px; padding:0 10px; border:1px solid #d9d9d9; border-radius:6px; font-size:13px; outline:none;" />
<span style="font-size:13px; color:#666;"></span>
</div>
<div style="font-size:11px; color:#999; margin-top:4px;">达到上限后暂停接单</div>
</div>
<div>
<label style="font-size:12px; color:#666; display:block; margin-bottom:6px;">配送时间预估加成</label>
<div style="display:flex; align-items:center; gap:6px;">
<input type="number" value="10" placeholder="如30" style="width:120px; height:32px; padding:0 10px; border:1px solid #d9d9d9; border-radius:6px; font-size:13px; outline:none;" />
<span style="font-size:13px; color:#666;">分钟</span>
</div>
<div style="font-size:11px; color:#999; margin-top:4px;">高峰期自动增加预估时间</div>
</div>
</div>
<div style="margin-top:20px; display:flex; justify-content:flex-end; gap:8px;">
<button class="g-btn">重置</button>
<button class="g-btn g-btn-primary">保存设置</button>
</div>
</div>
</div>
</div>
<script>
function switchDeliveryTab(mode) {
document.querySelectorAll('.mode-switch-item').forEach(b => b.classList.remove('active'));
event.currentTarget.classList.add('active');
document.getElementById('radiusOverlay').style.display = mode === 'radius' ? '' : 'none';
document.getElementById('polygonOverlay').style.display = mode === 'polygon' ? '' : 'none';
document.getElementById('radiusSection').style.display = mode === 'radius' ? '' : 'none';
document.getElementById('polygonSection').style.display = mode === 'polygon' ? '' : 'none';
}
</script>

288
pages/store-dinein.html Normal file
View File

@@ -0,0 +1,288 @@
<!-- 堂食管理页 -->
<style>
.pdi-toolbar { display:flex; align-items:center; gap:12px; margin-bottom:16px; box-shadow:var(--g-shadow-sm); border-radius:10px; padding:12px 16px; background:#fff; }
.pdi-toolbar select { height:34px; padding:0 10px; border:1px solid #e5e7eb; border-radius:8px; font-size:13px; outline:none; background:#fff; cursor:pointer; transition:var(--g-transition); }
.pdi-toolbar select:focus { border-color:var(--primary); box-shadow:0 0 0 3px color-mix(in srgb, var(--primary) 12%, transparent); }
.pdi-card-hd { display:flex; align-items:center; justify-content:space-between; margin-bottom:16px; font-size:14px; font-weight:600; color:#1a1a2e; }
.pdi-card-hd .right { display:flex; gap:8px; }
.pdi-area-pills { display:flex; gap:8px; flex-wrap:wrap; margin-bottom:16px; }
.pdi-area-pill { padding:6px 16px; border-radius:20px; font-size:13px; cursor:pointer; border:1px solid #e5e7eb; background:#fff; color:#4b5563; transition:var(--g-transition); }
.pdi-area-pill:hover { border-color:var(--primary); color:var(--primary); }
.pdi-area-pill.active { background:var(--primary); color:#fff; border-color:var(--primary); }
.pdi-area-info { background:#f8f9fb; border-radius:8px; padding:14px 18px; display:flex; align-items:center; justify-content:space-between; font-size:13px; color:#4b5563; }
.pdi-area-info .actions { display:flex; gap:8px; }
.pdi-action-link { color:var(--primary); cursor:pointer; font-size:12px; text-decoration:none; transition:var(--g-transition); }
.pdi-action-link:hover { text-decoration:underline; }
.pdi-action-link.danger { color:#ef4444; }
.pdi-table-grid { display:grid; grid-template-columns:repeat(4,1fr); gap:14px; }
.pdi-table-card { background:#fff; border:none; border-radius:10px; padding:16px; box-shadow:var(--g-shadow-sm); transition:var(--g-transition); }
.pdi-table-card:hover { box-shadow:0 4px 16px rgba(0,0,0,0.10); }
.pdi-table-code { font-size:22px; font-weight:700; color:#1a1a2e; margin-bottom:8px; }
.pdi-table-cap { font-size:13px; color:#4b5563; display:flex; align-items:center; gap:5px; margin-bottom:8px; }
.pdi-table-status { display:inline-flex; align-items:center; gap:5px; font-size:12px; font-weight:600; margin-bottom:8px; }
.pdi-status-dot { width:7px; height:7px; border-radius:50%; display:inline-block; }
.pdi-s-free .pdi-status-dot { background:#22c55e; } .pdi-s-free { color:#22c55e; }
.pdi-s-dining .pdi-status-dot { background:#f59e0b; } .pdi-s-dining { color:#f59e0b; }
.pdi-s-reserved .pdi-status-dot { background:#1890ff; } .pdi-s-reserved { color:#1890ff; }
.pdi-s-disabled .pdi-status-dot { background:#9ca3af; } .pdi-s-disabled { color:#9ca3af; }
.pdi-table-tags { display:flex; gap:4px; flex-wrap:wrap; margin-bottom:10px; min-height:22px; }
.pdi-table-foot { display:flex; justify-content:flex-end; gap:8px; border-top:1px solid #f3f4f6; padding-top:10px; }
.pdi-icon-btn { width:28px; height:28px; border-radius:8px; border:1px solid #e5e7eb; background:#fff; cursor:pointer; display:inline-flex; align-items:center; justify-content:center; font-size:14px; color:#9ca3af; transition:var(--g-transition); }
.pdi-icon-btn:hover { border-color:var(--primary); color:var(--primary); }
.pdi-form-row { display:flex; align-items:center; gap:12px; margin-bottom:16px; font-size:13px; color:#1a1a2e; }
.pdi-form-row label { width:120px; text-align:right; flex-shrink:0; font-weight:500; color:#4b5563; }
.pdi-form-row input[type=number] { width:120px; height:34px; padding:0 10px; border:1px solid #e5e7eb; border-radius:8px; font-size:13px; outline:none; transition:var(--g-transition); }
.pdi-form-row input[type=number]:focus { border-color:var(--primary); box-shadow:0 0 0 3px color-mix(in srgb, var(--primary) 12%, transparent); }
.pdi-form-row .hint { font-size:12px; color:#9ca3af; }
.pdi-form-row .unit { color:#4b5563; }
.pdi-form-actions { display:flex; gap:8px; margin-left:132px; margin-top:8px; }
.pdi-tag-input-wrap { display:flex; flex-wrap:wrap; gap:6px; padding:6px 8px; border:1px solid #e5e7eb; border-radius:8px; min-height:36px; align-items:center; transition:var(--g-transition); }
.pdi-tag-item { display:inline-flex; align-items:center; gap:4px; padding:2px 8px; background:#f0f5ff; color:#597ef7; border-radius:6px; font-size:12px; }
.pdi-tag-item .remove { cursor:pointer; color:#aaa; font-size:14px; transition:var(--g-transition); }
.pdi-tag-item .remove:hover { color:#ef4444; }
.pdi-modal-mask { position:fixed; inset:0; background:rgba(0,0,0,0.4); z-index:2000; opacity:0; pointer-events:none; transition:var(--g-transition); display:flex; align-items:center; justify-content:center; }
.pdi-modal-mask.open { opacity:1; pointer-events:auto; }
.pdi-modal { background:#fff; border-radius:10px; width:480px; display:flex; flex-direction:column; box-shadow:0 8px 30px rgba(0,0,0,0.12); transform:translateY(12px);opacity:0; transition:var(--g-transition); }
.pdi-modal-mask.open .pdi-modal { transform:translateY(0);opacity:1; }
.pdi-modal-hd { padding:16px 20px; border-bottom:1px solid #e5e7eb; display:flex; align-items:center; justify-content:space-between; }
.pdi-modal-hd h3 { font-size:15px; font-weight:600; color:#1a1a2e; margin:0; }
.pdi-modal-bd { padding:20px; }
.pdi-modal-ft { padding:12px 20px; border-top:1px solid #e5e7eb; display:flex; justify-content:flex-end; gap:8px; background:#fafbfc; border-radius:0 0 10px 10px; }
.pdi-preview-box { background:#f8f9fb; border:1px solid #e5e7eb; border-radius:8px; padding:12px; margin-top:12px; }
.pdi-preview-box .pv-title { font-size:12px; color:#9ca3af; margin-bottom:8px; }
.pdi-preview-tags { display:flex; flex-wrap:wrap; gap:6px; }
.pdi-preview-tag { padding:4px 10px; background:#f0f5ff; color:#597ef7; border-radius:6px; font-size:12px; }
</style>
<div class="pdi-page">
<div class="pdi-toolbar">
<select>
<option>老三家外卖(中关村店)</option>
<option>老三家外卖(望京店)</option>
<option>老三家外卖(国贸店)</option>
<option>老三家外卖(西单店)</option>
<option>老三家外卖(五道口店)</option>
</select>
<button class="g-btn" onclick="openCopyStoreModal('复制堂食设置到其他门店')">复制到其他门店</button>
</div>
<div class="g-card" style="padding:20px;margin-bottom:16px;">
<div class="pdi-card-hd"><span>区域管理</span><div class="right"><button class="g-btn g-btn-primary g-btn-sm" onclick="openDiArea()">+ 添加区域</button></div></div>
<div class="pdi-area-pills">
<span class="pdi-area-pill active">大厅 (12桌)</span>
<span class="pdi-area-pill">包间 (4桌)</span>
<span class="pdi-area-pill">露台 (6桌)</span>
</div>
<div class="pdi-area-info">
<span>大厅 &mdash; 主要用餐区域共12张桌位可容纳约48人同时用餐</span>
<div class="actions"><a class="pdi-action-link" onclick="openDiArea('edit','大厅')">编辑</a><a class="pdi-action-link danger">删除</a></div>
</div>
</div>
<div class="g-card" style="padding:20px;margin-bottom:16px;">
<div class="pdi-card-hd"><span>桌位列表</span><div class="right"><button class="g-btn g-btn-sm" onclick="openDiBatch()">批量生成</button><button class="g-btn g-btn-primary g-btn-sm" onclick="openDiTable()">+ 添加桌位</button></div></div>
<div class="pdi-table-grid">
<div class="pdi-table-card">
<div class="pdi-table-code">A01</div>
<div class="pdi-table-cap"><i data-lucide="armchair" style="width:14px;height:14px"></i> 4人桌</div>
<div class="pdi-table-status pdi-s-free"><span class="pdi-status-dot"></span>空闲</div>
<div class="pdi-table-tags"><span class="g-tag g-tag-gray">靠窗</span></div>
<div class="pdi-table-foot"><button class="pdi-icon-btn" title="二维码"><i data-lucide="qr-code" style="width:12px;height:12px"></i></button><button class="pdi-icon-btn" title="编辑" onclick="openDiTable('edit',this.closest('.pdi-table-card').querySelector('.pdi-table-code').textContent)"><i data-lucide="pencil" style="width:12px;height:12px"></i></button><a class="g-action g-action-danger" style="font-size:12px;cursor:pointer">删除</a></div>
</div>
<div class="pdi-table-card">
<div class="pdi-table-code">A02</div>
<div class="pdi-table-cap"><i data-lucide="armchair" style="width:14px;height:14px"></i> 2人桌</div>
<div class="pdi-table-status pdi-s-dining"><span class="pdi-status-dot"></span>就餐中</div>
<div class="pdi-table-tags"></div>
<div class="pdi-table-foot"><button class="pdi-icon-btn" title="二维码"><i data-lucide="qr-code" style="width:12px;height:12px"></i></button><button class="pdi-icon-btn" title="编辑" onclick="openDiTable('edit',this.closest('.pdi-table-card').querySelector('.pdi-table-code').textContent)"><i data-lucide="pencil" style="width:12px;height:12px"></i></button><a class="g-action g-action-danger" style="font-size:12px;cursor:pointer">删除</a></div>
</div>
<div class="pdi-table-card">
<div class="pdi-table-code">A03</div>
<div class="pdi-table-cap"><i data-lucide="armchair" style="width:14px;height:14px"></i> 6人桌</div>
<div class="pdi-table-status pdi-s-free"><span class="pdi-status-dot"></span>空闲</div>
<div class="pdi-table-tags"><span class="g-tag g-tag-gray">VIP</span><span class="g-tag g-tag-gray">靠窗</span></div>
<div class="pdi-table-foot"><button class="pdi-icon-btn" title="二维码"><i data-lucide="qr-code" style="width:12px;height:12px"></i></button><button class="pdi-icon-btn" title="编辑" onclick="openDiTable('edit',this.closest('.pdi-table-card').querySelector('.pdi-table-code').textContent)"><i data-lucide="pencil" style="width:12px;height:12px"></i></button><a class="g-action g-action-danger" style="font-size:12px;cursor:pointer">删除</a></div>
</div>
<div class="pdi-table-card">
<div class="pdi-table-code">A04</div>
<div class="pdi-table-cap"><i data-lucide="armchair" style="width:14px;height:14px"></i> 4人桌</div>
<div class="pdi-table-status pdi-s-reserved"><span class="pdi-status-dot"></span>已预约</div>
<div class="pdi-table-tags"></div>
<div class="pdi-table-foot"><button class="pdi-icon-btn" title="二维码"><i data-lucide="qr-code" style="width:12px;height:12px"></i></button><button class="pdi-icon-btn" title="编辑" onclick="openDiTable('edit',this.closest('.pdi-table-card').querySelector('.pdi-table-code').textContent)"><i data-lucide="pencil" style="width:12px;height:12px"></i></button><a class="g-action g-action-danger" style="font-size:12px;cursor:pointer">删除</a></div>
</div>
<div class="pdi-table-card">
<div class="pdi-table-code">A05</div>
<div class="pdi-table-cap"><i data-lucide="armchair" style="width:14px;height:14px"></i> 8人桌</div>
<div class="pdi-table-status pdi-s-dining"><span class="pdi-status-dot"></span>就餐中</div>
<div class="pdi-table-tags"><span class="g-tag g-tag-gray">包厢</span></div>
<div class="pdi-table-foot"><button class="pdi-icon-btn" title="二维码"><i data-lucide="qr-code" style="width:12px;height:12px"></i></button><button class="pdi-icon-btn" title="编辑" onclick="openDiTable('edit',this.closest('.pdi-table-card').querySelector('.pdi-table-code').textContent)"><i data-lucide="pencil" style="width:12px;height:12px"></i></button><a class="g-action g-action-danger" style="font-size:12px;cursor:pointer">删除</a></div>
</div>
<div class="pdi-table-card">
<div class="pdi-table-code">A06</div>
<div class="pdi-table-cap"><i data-lucide="armchair" style="width:14px;height:14px"></i> 2人桌</div>
<div class="pdi-table-status pdi-s-free"><span class="pdi-status-dot"></span>空闲</div>
<div class="pdi-table-tags"><span class="g-tag g-tag-gray">靠窗</span></div>
<div class="pdi-table-foot"><button class="pdi-icon-btn" title="二维码"><i data-lucide="qr-code" style="width:12px;height:12px"></i></button><button class="pdi-icon-btn" title="编辑" onclick="openDiTable('edit',this.closest('.pdi-table-card').querySelector('.pdi-table-code').textContent)"><i data-lucide="pencil" style="width:12px;height:12px"></i></button><a class="g-action g-action-danger" style="font-size:12px;cursor:pointer">删除</a></div>
</div>
<div class="pdi-table-card" style="opacity:0.5;">
<div class="pdi-table-code">A07</div>
<div class="pdi-table-cap"><i data-lucide="armchair" style="width:14px;height:14px"></i> 4人桌</div>
<div class="pdi-table-status pdi-s-disabled"><span class="pdi-status-dot"></span>停用</div>
<div class="pdi-table-tags"></div>
<div class="pdi-table-foot"><button class="pdi-icon-btn" title="二维码"><i data-lucide="qr-code" style="width:12px;height:12px"></i></button><button class="pdi-icon-btn" title="编辑" onclick="openDiTable('edit',this.closest('.pdi-table-card').querySelector('.pdi-table-code').textContent)"><i data-lucide="pencil" style="width:12px;height:12px"></i></button><a class="g-action g-action-danger" style="font-size:12px;cursor:pointer">删除</a></div>
</div>
<div class="pdi-table-card">
<div class="pdi-table-code">A08</div>
<div class="pdi-table-cap"><i data-lucide="armchair" style="width:14px;height:14px"></i> 4人桌</div>
<div class="pdi-table-status pdi-s-free"><span class="pdi-status-dot"></span>空闲</div>
<div class="pdi-table-tags"><span class="g-tag g-tag-gray">VIP</span></div>
<div class="pdi-table-foot"><button class="pdi-icon-btn" title="二维码"><i data-lucide="qr-code" style="width:12px;height:12px"></i></button><button class="pdi-icon-btn" title="编辑" onclick="openDiTable('edit',this.closest('.pdi-table-card').querySelector('.pdi-table-code').textContent)"><i data-lucide="pencil" style="width:12px;height:12px"></i></button><a class="g-action g-action-danger" style="font-size:12px;cursor:pointer">删除</a></div>
</div>
</div>
</div>
<div class="g-card" style="padding:20px;margin-bottom:16px;">
<div class="pdi-card-hd"><span>堂食设置</span></div>
<div class="pdi-form-row"><label>是否开启堂食</label><button class="g-toggle on" onclick="this.classList.toggle('on')"></button></div>
<div class="pdi-form-row"><label>默认用餐时长</label><input type="number" value="90"><span class="unit">分钟</span></div>
<div class="pdi-form-row"><label>超时提醒</label><input type="number" value="10"><span class="unit">分钟</span><span class="hint">超过用餐时长后提醒</span></div>
<div class="pdi-form-actions"><button class="g-btn g-btn-primary">保存设置</button><button class="g-btn">重置</button></div>
</div>
</div>
<div class="g-drawer-mask" id="diDrawerMask" onclick="closeDiDrawer()"></div>
<!-- 添加/编辑区域 -->
<div class="g-drawer" id="diAreaDrawer" style="width:460px;">
<div class="g-drawer-hd"><span class="g-drawer-title" id="diAreaTitle">添加区域</span><button class="g-drawer-close" onclick="closeDiDrawer()"><i data-lucide="x" style="width:18px;height:18px;"></i></button></div>
<div class="g-drawer-bd">
<div class="g-form-group"><label class="g-form-label required">区域名称</label><input type="text" class="g-input" id="diAreaName" placeholder="如:大厅、包间、露台" /></div>
<div class="g-form-group"><label class="g-form-label">区域描述</label><textarea class="g-textarea" id="diAreaDesc" rows="3" placeholder="可选主要用餐区域可容纳约48人"></textarea></div>
<div class="g-form-group"><label class="g-form-label">排序</label><div style="display:flex;align-items:center;gap:6px;"><input type="number" class="g-input" id="diAreaSort" value="1" style="width:80px;" /><span style="font-size:12px;color:#999;">数字越小越靠前</span></div></div>
</div>
<div class="g-drawer-ft"><button class="g-btn" onclick="closeDiDrawer()">取消</button><button class="g-btn g-btn-primary" id="diAreaSubmit" onclick="closeDiDrawer()">确认添加</button></div>
</div>
<!-- 添加/编辑桌位 -->
<div class="g-drawer" id="diTableDrawer" style="width:460px;">
<div class="g-drawer-hd"><span class="g-drawer-title" id="diTableTitle">添加桌位</span><button class="g-drawer-close" onclick="closeDiDrawer()"><i data-lucide="x" style="width:18px;height:18px;"></i></button></div>
<div class="g-drawer-bd">
<div class="g-form-group"><label class="g-form-label required">桌位编号</label><input type="text" class="g-input" id="diTableCode" placeholder="如A09" /></div>
<div class="g-form-group"><label class="g-form-label">所属区域</label><select class="g-select" id="diTableArea"><option>大厅</option><option>包间</option><option>露台</option></select></div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:12px;">
<div class="g-form-group"><label class="g-form-label required">座位数</label><select class="g-select" id="diTableCap"><option>2人桌</option><option selected>4人桌</option><option>6人桌</option><option>8人桌</option><option>10人桌</option><option>12人桌</option></select></div>
<div class="g-form-group"><label class="g-form-label">状态</label><select class="g-select" id="diTableStatus"><option selected>空闲</option><option>停用</option></select></div>
</div>
<div class="g-form-group"><label class="g-form-label">标签</label>
<div class="pdi-tag-input-wrap" id="diTagWrap">
<span class="pdi-tag-item">靠窗 <span class="remove" onclick="this.parentElement.remove()"><i data-lucide="x" style="width:10px;height:10px"></i></span></span>
<input type="text" id="diTagInput" placeholder="输入后回车添加" style="border:none;outline:none;font-size:12px;flex:1;min-width:80px;" onkeydown="if(event.key==='Enter'){addDiTag();event.preventDefault();}" />
</div>
<div class="g-hint">输入标签名后按回车添加靠窗、VIP、包厢</div>
</div>
</div>
<div class="g-drawer-ft"><button class="g-btn" onclick="closeDiDrawer()">取消</button><button class="g-btn g-btn-primary" id="diTableSubmit" onclick="closeDiDrawer()">确认添加</button></div>
</div>
<!-- 批量生成弹窗 -->
<div class="pdi-modal-mask" id="diBatchMask" onclick="if(event.target===this)closeDiBatch()">
<div class="pdi-modal">
<div class="pdi-modal-hd"><h3>批量生成桌位</h3><button class="g-drawer-close" onclick="closeDiBatch()"><i data-lucide="x" style="width:16px;height:16px"></i></button></div>
<div class="pdi-modal-bd">
<div class="g-form-group"><label class="g-form-label">所属区域</label><select class="g-select" id="diBatchArea" onchange="updateBatchPreview()"><option>大厅</option><option>包间</option><option>露台</option></select></div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:12px;">
<div class="g-form-group"><label class="g-form-label">编号前缀</label><input type="text" class="g-input" id="diBatchPrefix" value="A" oninput="updateBatchPreview()" /></div>
<div class="g-form-group"><label class="g-form-label">起始编号</label><input type="number" class="g-input" id="diBatchStart" value="9" oninput="updateBatchPreview()" /></div>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:12px;">
<div class="g-form-group"><label class="g-form-label">生成数量</label><input type="number" class="g-input" id="diBatchCount" value="4" min="1" max="50" oninput="updateBatchPreview()" /></div>
<div class="g-form-group"><label class="g-form-label">座位数</label><select class="g-select" id="diBatchCap"><option>2人桌</option><option selected>4人桌</option><option>6人桌</option><option>8人桌</option></select></div>
</div>
<div class="pdi-preview-box">
<div class="pv-title">预览:将生成以下桌位</div>
<div class="pdi-preview-tags" id="diBatchPreview"></div>
</div>
</div>
<div class="pdi-modal-ft"><button class="g-btn" onclick="closeDiBatch()">取消</button><button class="g-btn g-btn-primary" onclick="closeDiBatch()">确认生成</button></div>
</div>
</div>
<script>
function openDiArea(mode, name) {
var t = document.getElementById('diAreaTitle');
var s = document.getElementById('diAreaSubmit');
if (mode === 'edit') {
t.textContent = '编辑区域';
s.textContent = '保存修改';
document.getElementById('diAreaName').value = name || '';
document.getElementById('diAreaDesc').value = '主要用餐区域可容纳约48人同时用餐';
document.getElementById('diAreaSort').value = '1';
} else {
t.textContent = '添加区域';
s.textContent = '确认添加';
document.getElementById('diAreaName').value = '';
document.getElementById('diAreaDesc').value = '';
document.getElementById('diAreaSort').value = '1';
}
document.getElementById('diDrawerMask').classList.add('open');
document.getElementById('diAreaDrawer').classList.add('open');
}
function openDiTable(mode, code) {
var t = document.getElementById('diTableTitle');
var s = document.getElementById('diTableSubmit');
if (mode === 'edit') {
t.textContent = '编辑桌位 - ' + code;
s.textContent = '保存修改';
document.getElementById('diTableCode').value = code || '';
} else {
t.textContent = '添加桌位';
s.textContent = '确认添加';
document.getElementById('diTableCode').value = '';
}
document.getElementById('diDrawerMask').classList.add('open');
document.getElementById('diTableDrawer').classList.add('open');
}
function closeDiDrawer() {
document.getElementById('diDrawerMask').classList.remove('open');
document.getElementById('diAreaDrawer').classList.remove('open');
document.getElementById('diTableDrawer').classList.remove('open');
}
function addDiTag() {
var inp = document.getElementById('diTagInput');
var v = inp.value.trim();
if (!v) return;
var span = document.createElement('span');
span.className = 'pdi-tag-item';
span.innerHTML = v + ' <span class="remove" onclick="this.parentElement.remove()"><i data-lucide="x" style="width:10px;height:10px"></i></span>';
document.getElementById('diTagWrap').insertBefore(span, inp);
inp.value = '';
}
function openDiBatch() {
document.getElementById('diBatchMask').classList.add('open');
updateBatchPreview();
}
function closeDiBatch() {
document.getElementById('diBatchMask').classList.remove('open');
}
function updateBatchPreview() {
var prefix = document.getElementById('diBatchPrefix').value || 'A';
var start = parseInt(document.getElementById('diBatchStart').value) || 1;
var count = Math.min(Math.max(parseInt(document.getElementById('diBatchCount').value) || 1, 1), 50);
var html = '';
for (var i = 0; i < count; i++) {
var num = (start + i).toString().padStart(2, '0');
html += '<span class="pdi-preview-tag">' + prefix + num + '</span>';
}
document.getElementById('diBatchPreview').innerHTML = html;
}
// init icons
if (typeof lucide !== 'undefined') lucide.createIcons();
</script>

131
pages/store-fees.html Normal file
View File

@@ -0,0 +1,131 @@
<!-- 费用设置页 -->
<style>
.page-fees { max-width:960px; }
.pf-toolbar { display:flex; align-items:center; gap:12px; margin-bottom:16px; box-shadow:var(--g-shadow-sm); border-radius:10px; padding:10px 14px; background:#fff; }
.pf-toolbar select { height:34px; padding:0 10px; border:1px solid #e5e7eb; border-radius:8px; font-size:13px; outline:none; background:#fff; transition:var(--g-transition); }
.pf-toolbar select:focus { border-color:var(--primary); box-shadow:0 0 0 3px color-mix(in srgb, var(--primary) 12%, transparent); }
.pf-grid { display:grid; grid-template-columns:1fr 1fr; gap:16px 24px; }
.pf-field label { font-size:12px; color:#6b7280; display:block; margin-bottom:6px; font-weight:500; }
.pf-field .pf-input-wrap { display:flex; align-items:center; gap:6px; }
.pf-field input[type=number] { width:140px; height:34px; padding:0 10px; border:1px solid #e5e7eb; border-radius:8px; font-size:13px; outline:none; transition:var(--g-transition); }
.pf-field input[type=number]:focus { border-color:var(--primary); box-shadow:0 0 0 3px color-mix(in srgb, var(--primary) 12%, transparent); }
.pf-field .pf-unit { font-size:13px; color:#4b5563; }
.pf-mode-switch { display:flex; background:#f8f9fb; border-radius:8px; padding:3px; gap:2px; width:fit-content; margin-bottom:16px; }
.pf-mode-switch-item { padding:6px 18px; border-radius:6px; font-size:13px; cursor:pointer; color:#4b5563; transition:var(--g-transition); border:none; background:none; }
.pf-mode-switch-item.active { background:#fff; color:var(--primary); font-weight:600; box-shadow:var(--g-shadow-sm); }
.pf-toggle-row { display:flex; align-items:center; gap:10px; margin-bottom:12px; }
.pf-toggle-label { font-size:13px; color:#1a1a2e; font-weight:500; }
.pf-tier-table { width:100%; border-collapse:collapse; font-size:13px; margin-top:10px; }
.pf-tier-table th { background:#f8f9fb; padding:10px 12px; text-align:left; font-weight:600; color:#6b7280; border-bottom:1px solid #e5e7eb; }
.pf-tier-table td { padding:10px 12px; border-bottom:1px solid #f3f4f6; color:#1a1a2e; }
.pf-tier-table tr:last-child td { border-bottom:none; }
.pf-tier-table tr:hover td { background:color-mix(in srgb, var(--primary) 3%, #fff); }
.pf-t-link { color:var(--primary); cursor:pointer; font-size:12px; margin-right:6px; transition:var(--g-transition); }
.pf-t-link:hover { text-decoration:underline; }
.pf-t-link.danger { color:#ef4444; }
.pf-other-row { display:flex; align-items:flex-start; gap:14px; padding:14px 0; border-bottom:1px solid #f3f4f6; }
.pf-other-row:last-child { border-bottom:none; }
.pf-other-info { flex:1; }
.pf-other-info .pf-other-name { font-size:13px; font-weight:500; color:#1a1a2e; margin-bottom:2px; }
.pf-other-info .pf-other-hint { font-size:11px; color:#9ca3af; }
.pf-other-input { display:flex; align-items:center; gap:6px; }
.pf-other-input input { width:100px; height:34px; padding:0 10px; border:1px solid #e5e7eb; border-radius:8px; font-size:13px; outline:none; transition:var(--g-transition); }
.pf-other-input input:disabled { background:#f8f9fb; color:#bbb; }
.pf-save-bar { margin-top:20px; display:flex; justify-content:flex-end; gap:8px; }
.pf-note { font-size:12px; color:#9ca3af; display:flex; align-items:center; gap:4px; }
</style>
<div class="page-fees">
<div class="pf-toolbar">
<select style="width:200px;">
<option>老三家外卖(朝阳店)</option>
<option>老三家外卖(海淀店)</option>
<option>老三家外卖(望京店)</option>
<option>老三家外卖(通州店)</option>
<option>老三家外卖(丰台店)</option>
</select>
<div style="flex:1;"></div>
<button class="g-btn" onclick="openCopyStoreModal('复制费用设置到其他门店')"><i data-lucide="copy" style="width:14px;height:14px;"></i>复制到其他门店</button>
</div>
<div class="g-card" style="padding:0; margin-bottom:16px;">
<div class="g-card-title" style="padding:14px 18px; margin-bottom:0; border-bottom:1px solid #f5f5f5;">起送与配送费</div>
<div style="padding:16px 18px;">
<div class="pf-grid">
<div class="pf-field"><label>起送金额</label><div class="pf-input-wrap"><span class="pf-unit">¥</span><input type="number" value="15.00" step="0.01" min="0" placeholder="如15.00" /></div><div class="g-hint">低于此金额的订单无法下单</div></div>
<div class="pf-field"><label>基础配送费</label><div class="pf-input-wrap"><span class="pf-unit">¥</span><input type="number" value="3.00" step="0.01" min="0" placeholder="如3.00" /></div><div class="g-hint">每笔订单默认收取的配送费</div></div>
<div class="pf-field"><label>免配送费门槛</label><div class="pf-input-wrap"><span class="pf-unit">¥</span><input type="number" value="30.00" step="0.01" min="0" placeholder="如30.00" /></div><div class="g-hint">订单满此金额免配送费,留空不启用</div></div>
</div>
<div class="pf-save-bar"><button class="g-btn">重置</button><button class="g-btn g-btn-primary">保存设置</button></div>
</div>
</div>
<div class="g-card" style="padding:0; margin-bottom:16px;">
<div class="g-card-title" style="padding:14px 18px; margin-bottom:0; border-bottom:1px solid #f5f5f5;">包装费设置</div>
<div style="padding:16px 18px;">
<div class="pf-mode-switch">
<button class="pf-mode-switch-item active" onclick="switchPkgMode('order')">按订单收取</button>
<button class="pf-mode-switch-item" onclick="switchPkgMode('item')">按商品收取</button>
</div>
<div id="pkgModeOrder">
<div class="pf-grid">
<div class="pf-field"><label>固定包装费</label><div class="pf-input-wrap"><span class="pf-unit">¥</span><input type="number" value="2.00" step="0.01" min="0" placeholder="如2.00" /></div><div class="g-hint">每笔订单统一收取的包装费</div></div>
</div>
<div style="margin-top:18px; padding-top:16px; border-top:1px solid #f5f5f5;">
<div class="pf-toggle-row"><label class="g-toggle-input"><input type="checkbox" id="tierToggle" onchange="toggleTiers()" checked /><span class="g-toggle-sl"></span></label><span class="pf-toggle-label">启用阶梯包装费</span></div>
<div id="tierSection">
<div class="pf-note" style="margin-bottom:10px;">启用后将按订单金额区间收取不同包装费,覆盖上方固定包装费</div>
<table class="pf-tier-table"><thead><tr><th>订单金额区间</th><th>包装费</th><th style="width:100px;">操作</th></tr></thead><tbody>
<tr><td>0 ~ 30 元</td><td>¥ 2.00</td><td><span class="pf-t-link">编辑</span><span class="pf-t-link danger">删除</span></td></tr>
<tr><td>30 ~ 60 元</td><td>¥ 3.00</td><td><span class="pf-t-link">编辑</span><span class="pf-t-link danger">删除</span></td></tr>
<tr><td>60 元以上</td><td>¥ 5.00</td><td><span class="pf-t-link">编辑</span><span class="pf-t-link danger">删除</span></td></tr>
</tbody></table>
<div style="margin-top:10px;"><button class="g-btn g-btn-sm"><i data-lucide="plus" style="width:13px;height:13px;"></i>添加阶梯</button></div>
</div>
</div>
</div>
<div id="pkgModeItem" style="display:none;">
<div style="padding:20px; background:#fafafa; border-radius:8px; text-align:center;">
<div style="font-size:28px; margin-bottom:8px; opacity:0.4;"><i data-lucide="package" style="width:16px;height:16px"></i></div>
<div style="font-size:13px; color:#666;">每个商品单独设置包装费,请在 <span style="color:var(--primary); cursor:pointer; font-weight:500;">商品管理</span> 中配置</div>
<div style="font-size:11px; color:#999; margin-top:6px;">系统将自动汇总订单内所有商品的包装费</div>
</div>
</div>
<div class="pf-save-bar"><button class="g-btn">重置</button><button class="g-btn g-btn-primary">保存设置</button></div>
</div>
</div>
<div class="g-card" style="padding:0; margin-bottom:16px;">
<div class="g-card-title" style="padding:14px 18px; margin-bottom:0; border-bottom:1px solid #f5f5f5;">其他费用<span style="font-size:11px; color:#999; font-weight:400; margin-left:8px;">可选,按需开启</span></div>
<div style="padding:16px 18px;">
<div class="pf-other-row">
<label class="g-toggle-input"><input type="checkbox" id="cutleryToggle" onchange="toggleOtherFee('cutlery')" /><span class="g-toggle-sl"></span></label>
<div class="pf-other-info"><div class="pf-other-name">餐具费</div><div class="pf-other-hint">向顾客收取一次性餐具费用,顾客可选择不需要餐具</div></div>
<div class="pf-other-input"><span class="pf-unit">¥</span><input type="number" id="cutleryInput" value="1.00" step="0.01" min="0" placeholder="如1.00" disabled /></div>
</div>
<div class="pf-other-row">
<label class="g-toggle-input"><input type="checkbox" id="rushToggle" onchange="toggleOtherFee('rush')" /><span class="g-toggle-sl"></span></label>
<div class="pf-other-info"><div class="pf-other-name">加急费</div><div class="pf-other-hint">顾客选择加急配送时额外收取的费用</div></div>
<div class="pf-other-input"><span class="pf-unit">¥</span><input type="number" id="rushInput" value="3.00" step="0.01" min="0" placeholder="如3.00" disabled /></div>
</div>
<div class="pf-save-bar"><button class="g-btn">重置</button><button class="g-btn g-btn-primary">保存设置</button></div>
</div>
</div>
</div>
<script>
function switchPkgMode(mode) {
document.querySelectorAll('.pf-mode-switch-item').forEach(function(b){b.classList.remove('active')});
event.currentTarget.classList.add('active');
document.getElementById('pkgModeOrder').style.display = mode === 'order' ? '' : 'none';
document.getElementById('pkgModeItem').style.display = mode === 'item' ? '' : 'none';
}
function toggleTiers() {
var on = document.getElementById('tierToggle').checked;
document.getElementById('tierSection').style.display = on ? '' : 'none';
}
function toggleOtherFee(type) {
if (type === 'cutlery') { document.getElementById('cutleryInput').disabled = !document.getElementById('cutleryToggle').checked; }
else if (type === 'rush') { document.getElementById('rushInput').disabled = !document.getElementById('rushToggle').checked; }
}
</script>

472
pages/store-hours.html Normal file
View File

@@ -0,0 +1,472 @@
<!-- 营业时间页 -->
<style>
.page-hours { max-width:960px; }
.ph-toolbar { display:flex; align-items:center; gap:12px; margin-bottom:16px; box-shadow:var(--g-shadow-sm); border-radius:10px; padding:12px 16px; background:#fff; }
.ph-card-header { padding:14px 18px; font-size:14px; font-weight:600; color:#1a1a2e; background:#f8f9fb; border-bottom:1px solid #e5e7eb; display:flex; align-items:center; justify-content:space-between; border-radius:10px 10px 0 0; }
.ph-card-body { padding:16px 18px; }
.week-grid { display:flex; flex-direction:column; gap:0; }
.week-row { display:grid; grid-template-columns:80px 1fr auto; align-items:center; padding:12px 0; border-bottom:1px solid #f3f4f6; gap:12px; transition:var(--g-transition); }
.week-row:last-child { border-bottom:none; }
.week-row:hover { background:color-mix(in srgb, var(--primary) 3%, #fff); }
.week-day { font-size:13px; font-weight:500; color:#1a1a2e; }
.week-day .day-en { font-size:11px; color:#9ca3af; font-weight:400; }
.week-slots { display:flex; flex-wrap:wrap; gap:8px; }
.time-slot { display:inline-flex; align-items:center; gap:6px; padding:4px 10px; border-radius:6px; font-size:12px; background:#f6ffed; border:1px solid #b7eb8f; color:#389e0d; }
.time-slot.delivery { background:#e6f7ff; border-color:#91d5ff; color:#1890ff; }
.time-slot.pickup { background:#fff7e6; border-color:#ffd591; color:#d46b08; }
.time-slot .slot-type { font-weight:600; }
.time-slot .slot-time { color:inherit; opacity:0.85; }
.time-slot .slot-cap { font-size:10px; opacity:0.7; }
.week-actions { display:flex; gap:4px; }
.week-edit { width:28px; height:28px; display:flex; align-items:center; justify-content:center; border-radius:8px; cursor:pointer; color:#9ca3af; border:none; background:none; transition:var(--g-transition); }
.week-edit:hover { background:#f8f9fb; color:var(--primary); }
.week-closed { font-size:12px; color:#9ca3af; font-style:italic; }
.holiday-table { width:100%; border-collapse:collapse; font-size:13px; }
.holiday-table th { background:#f8f9fb; padding:8px 12px; text-align:left; font-weight:600; color:#6b7280; border-bottom:1px solid #e5e7eb; }
.holiday-table td { padding:10px 12px; border-bottom:1px solid #f3f4f6; color:#1a1a2e; }
.holiday-table tr:last-child td { border-bottom:none; }
.holiday-table tr:hover td { background:color-mix(in srgb, var(--primary) 3%, #fff); }
.h-tag { display:inline-block; padding:1px 8px; border-radius:6px; font-size:11px; font-weight:600; }
.h-tag.closed { background:#fff2f0; color:#ef4444; }
.h-tag.special { background:#fff7e6; color:#f59e0b; }
.h-link { color:var(--primary); cursor:pointer; font-size:12px; margin-right:8px; transition:var(--g-transition); }
.h-link:hover { text-decoration:underline; }
.h-link.danger { color:#ef4444; }
.ph-legend { display:flex; gap:16px; font-size:12px; color:#9ca3af; margin-bottom:12px; }
.ph-legend span { display:flex; align-items:center; gap:4px; }
.ph-legend-dot { width:10px; height:10px; border-radius:2px; }
/* Type pills */
.ph-type-pills { display:flex; gap:8px; }
.ph-type-pill { padding:6px 16px; border-radius:8px; font-size:13px; cursor:pointer; border:1px solid #e5e7eb; background:#fff; color:#4b5563; transition:var(--g-transition); }
.ph-type-pill.tp-biz.active { background:#f6ffed; border-color:#b7eb8f; color:#389e0d; font-weight:600; }
.ph-type-pill.tp-del.active { background:#e6f7ff; border-color:#91d5ff; color:#1890ff; font-weight:600; }
.ph-type-pill.tp-pick.active { background:#fff7e6; border-color:#ffd591; color:#d46b08; font-weight:600; }
/* Day pills */
.ph-day-pills { display:flex; gap:6px; flex-wrap:wrap; }
.ph-day-pill { width:42px; height:34px; border-radius:8px; font-size:12px; cursor:pointer; border:1px solid #e5e7eb; background:#fff; color:#4b5563; display:flex; align-items:center; justify-content:center; transition:var(--g-transition); }
.ph-day-pill.selected { background:var(--primary); border-color:var(--primary); color:#fff; }
.ph-quick-btns { display:flex; gap:8px; margin-top:8px; }
.ph-quick-btn { font-size:11px; color:var(--primary); cursor:pointer; background:none; border:none; padding:0; transition:var(--g-transition); }
.ph-quick-btn:hover { text-decoration:underline; }
/* Edit drawer slot cards */
.ph-slot-card { background:#f8f9fb; border:1px solid #e5e7eb; border-radius:10px; padding:12px 14px; margin-bottom:10px; display:grid; grid-template-columns:auto 1fr auto; gap:12px; align-items:center; }
.ph-slot-tag { padding:3px 10px; border-radius:6px; font-size:12px; font-weight:600; white-space:nowrap; }
.ph-slot-tag.st-biz { background:#f6ffed; border:1px solid #b7eb8f; color:#389e0d; }
.ph-slot-tag.st-del { background:#e6f7ff; border:1px solid #91d5ff; color:#1890ff; }
.ph-slot-tag.st-pick { background:#fff7e6; border:1px solid #ffd591; color:#d46b08; }
.ph-slot-fields { display:flex; gap:10px; align-items:center; flex-wrap:wrap; }
.ph-slot-fields label { font-size:11px; color:#9ca3af; font-weight:500; }
.ph-slot-fields input { width:100px; }
.ph-slot-del { width:28px; height:28px; border:none; background:none; cursor:pointer; color:#bbb; display:flex; align-items:center; justify-content:center; border-radius:8px; transition:var(--g-transition); }
.ph-slot-del:hover { background:#fff2f0; color:#ef4444; }
.ph-add-dashed { width:100%; height:40px; border:1px dashed #e5e7eb; border-radius:10px; background:none; cursor:pointer; color:#9ca3af; font-size:13px; display:flex; align-items:center; justify-content:center; gap:6px; transition:var(--g-transition); }
.ph-add-dashed:hover { border-color:var(--primary); color:var(--primary); }
/* Toggle row */
.ph-toggle-row { display:flex; align-items:center; gap:10px; margin-bottom:16px; }
.ph-toggle-hint { font-size:12px; color:#ef4444; margin-left:8px; display:none; }
/* Holiday drawer */
.ph-hol-type-pills { display:flex; gap:8px; }
.ph-hol-type-pill { padding:6px 16px; border-radius:8px; font-size:13px; cursor:pointer; border:1px solid #e5e7eb; background:#fff; color:#4b5563; transition:var(--g-transition); }
.ph-hol-type-pill.ht-closed.active { background:#fff2f0; border-color:#ffa39e; color:#ef4444; font-weight:600; }
.ph-hol-type-pill.ht-special.active { background:#fff7e6; border-color:#ffd591; color:#f59e0b; font-weight:600; }
.ph-date-mode { display:flex; gap:8px; margin-bottom:10px; }
.ph-date-mode-pill { padding:4px 12px; border-radius:8px; font-size:12px; cursor:pointer; border:1px solid #e5e7eb; background:#fff; color:#4b5563; transition:var(--g-transition); }
.ph-date-mode-pill.active { background:var(--primary); border-color:var(--primary); color:#fff; }
</style>
<div class="page-hours">
<div class="ph-toolbar">
<select class="g-select" style="width:200px;">
<option>老三家外卖(朝阳店)</option>
<option>老三家外卖(海淀店)</option>
<option>老三家外卖(望京店)</option>
<option>老三家外卖(通州店)</option>
<option>老三家外卖(丰台店)</option>
</select>
<div style="flex:1;"></div>
<button class="g-btn" onclick="openCopyStoreModal('复制营业时间到其他门店')">
<i data-lucide="copy" style="width:14px;height:14px;"></i>复制到其他门店
</button>
</div>
<div class="g-card">
<div class="ph-card-header">
每周营业时间
<button class="g-btn g-btn-primary" style="height:28px;font-size:12px;" onclick="openHoursDrawer('add')">
<i data-lucide="plus" style="width:13px;height:13px;"></i>添加时段
</button>
</div>
<div class="ph-card-body">
<div class="ph-legend">
<span><span class="ph-legend-dot" style="background:#b7eb8f;"></span>营业</span>
<span><span class="ph-legend-dot" style="background:#91d5ff;"></span>配送</span>
<span><span class="ph-legend-dot" style="background:#ffd591;"></span>自提</span>
</div>
<div class="week-grid">
<div class="week-row">
<div class="week-day">周一<div class="day-en">Monday</div></div>
<div class="week-slots">
<div class="time-slot"><span class="slot-type">营业</span><span class="slot-time">09:00-22:00</span></div>
<div class="time-slot delivery"><span class="slot-type">配送</span><span class="slot-time">10:00-21:30</span><span class="slot-cap">50单/h</span></div>
<div class="time-slot pickup"><span class="slot-type">自提</span><span class="slot-time">09:00-21:00</span></div>
</div>
<div class="week-actions"><button class="week-edit" onclick="openHoursDrawer('editDay','周一')"><i data-lucide="pencil" style="width:14px;height:14px;"></i></button></div>
</div>
<div class="week-row">
<div class="week-day">周二<div class="day-en">Tuesday</div></div>
<div class="week-slots">
<div class="time-slot"><span class="slot-type">营业</span><span class="slot-time">09:00-22:00</span></div>
<div class="time-slot delivery"><span class="slot-type">配送</span><span class="slot-time">10:00-21:30</span><span class="slot-cap">50单/h</span></div>
<div class="time-slot pickup"><span class="slot-type">自提</span><span class="slot-time">09:00-21:00</span></div>
</div>
<div class="week-actions"><button class="week-edit" onclick="openHoursDrawer('editDay','周二')"><i data-lucide="pencil" style="width:14px;height:14px;"></i></button></div>
</div>
<div class="week-row">
<div class="week-day">周三<div class="day-en">Wednesday</div></div>
<div class="week-slots">
<div class="time-slot"><span class="slot-type">营业</span><span class="slot-time">09:00-22:00</span></div>
<div class="time-slot delivery"><span class="slot-type">配送</span><span class="slot-time">10:00-21:30</span><span class="slot-cap">50单/h</span></div>
<div class="time-slot pickup"><span class="slot-type">自提</span><span class="slot-time">09:00-21:00</span></div>
</div>
<div class="week-actions"><button class="week-edit" onclick="openHoursDrawer('editDay','周三')"><i data-lucide="pencil" style="width:14px;height:14px;"></i></button></div>
</div>
<div class="week-row">
<div class="week-day">周四<div class="day-en">Thursday</div></div>
<div class="week-slots">
<div class="time-slot"><span class="slot-type">营业</span><span class="slot-time">09:00-22:00</span></div>
<div class="time-slot delivery"><span class="slot-type">配送</span><span class="slot-time">10:00-21:30</span><span class="slot-cap">50单/h</span></div>
<div class="time-slot pickup"><span class="slot-type">自提</span><span class="slot-time">09:00-21:00</span></div>
</div>
<div class="week-actions"><button class="week-edit" onclick="openHoursDrawer('editDay','周四')"><i data-lucide="pencil" style="width:14px;height:14px;"></i></button></div>
</div>
<div class="week-row">
<div class="week-day">周五<div class="day-en">Friday</div></div>
<div class="week-slots">
<div class="time-slot"><span class="slot-type">营业</span><span class="slot-time">09:00-23:00</span></div>
<div class="time-slot delivery"><span class="slot-type">配送</span><span class="slot-time">10:00-22:30</span><span class="slot-cap">80单/h</span></div>
<div class="time-slot pickup"><span class="slot-type">自提</span><span class="slot-time">09:00-22:00</span></div>
</div>
<div class="week-actions"><button class="week-edit" onclick="openHoursDrawer('editDay','周五')"><i data-lucide="pencil" style="width:14px;height:14px;"></i></button></div>
</div>
<div class="week-row">
<div class="week-day">周六<div class="day-en">Saturday</div></div>
<div class="week-slots">
<div class="time-slot"><span class="slot-type">营业</span><span class="slot-time">09:00-23:00</span></div>
<div class="time-slot delivery"><span class="slot-type">配送</span><span class="slot-time">10:00-22:30</span><span class="slot-cap">80单/h</span></div>
<div class="time-slot pickup"><span class="slot-type">自提</span><span class="slot-time">09:00-22:00</span></div>
</div>
<div class="week-actions"><button class="week-edit" onclick="openHoursDrawer('editDay','周六')"><i data-lucide="pencil" style="width:14px;height:14px;"></i></button></div>
</div>
<div class="week-row">
<div class="week-day">周日<div class="day-en">Sunday</div></div>
<div class="week-slots">
<div class="time-slot"><span class="slot-type">营业</span><span class="slot-time">10:00-22:00</span></div>
<div class="time-slot delivery"><span class="slot-type">配送</span><span class="slot-time">10:30-21:30</span><span class="slot-cap">60单/h</span></div>
</div>
<div class="week-actions"><button class="week-edit" onclick="openHoursDrawer('editDay','周日')"><i data-lucide="pencil" style="width:14px;height:14px;"></i></button></div>
</div>
</div>
</div>
</div>
<div class="g-card">
<div class="ph-card-header">
节假日 / 特殊日期
<button class="g-btn g-btn-primary" style="height:28px;font-size:12px;" onclick="openHolidayDrawer('add')">
<i data-lucide="plus" style="width:13px;height:13px;"></i>添加日期
</button>
</div>
<div class="ph-card-body" style="padding:0;">
<table class="holiday-table">
<thead><tr><th>日期</th><th>类型</th><th>时间</th><th>原因</th><th style="width:100px;">操作</th></tr></thead>
<tbody>
<tr><td>2026-02-17 ~ 2026-02-19</td><td><span class="h-tag closed">休息</span></td><td>全天</td><td>春节假期</td><td><a class="h-link" onclick="openHolidayDrawer('edit',{date:'2026-02-17 ~ 2026-02-19',type:'closed',time:'',reason:'春节假期'})">编辑</a><a class="h-link danger">删除</a></td></tr>
<tr><td>2026-04-05</td><td><span class="h-tag closed">休息</span></td><td>全天</td><td>清明节</td><td><a class="h-link" onclick="openHolidayDrawer('edit',{date:'2026-04-05',type:'closed',time:'',reason:'清明节'})">编辑</a><a class="h-link danger">删除</a></td></tr>
<tr><td>2026-02-14</td><td><span class="h-tag special">特殊营业</span></td><td>09:00 - 23:30</td><td>情人节延长营业</td><td><a class="h-link" onclick="openHolidayDrawer('edit',{date:'2026-02-14',type:'special',time:'09:00-23:30',reason:'情人节延长营业'})">编辑</a><a class="h-link danger">删除</a></td></tr>
<tr><td>2026-05-01</td><td><span class="h-tag special">特殊营业</span></td><td>10:00 - 20:00</td><td>劳动节缩短营业</td><td><a class="h-link" onclick="openHolidayDrawer('edit',{date:'2026-05-01',type:'special',time:'10:00-20:00',reason:'劳动节缩短营业'})">编辑</a><a class="h-link danger">删除</a></td></tr>
</tbody>
</table>
</div>
</div>
</div>
<!-- 添加时段抽屉 -->
<div class="g-drawer-mask" id="hoursDrawerMask" onclick="closeHoursDrawer()"></div>
<div class="g-drawer" id="addSlotDrawer" style="width:480px;">
<div class="g-drawer-hd">
<span class="g-drawer-title">添加时段</span>
<button class="g-drawer-close" onclick="closeHoursDrawer()"><i data-lucide="x" style="width:18px;height:18px;"></i></button>
</div>
<div class="g-drawer-bd">
<div class="g-form-group">
<label class="g-form-label required">时段类型</label>
<div class="ph-type-pills">
<button class="ph-type-pill tp-biz active" onclick="selectSlotType('biz',this)">营业</button>
<button class="ph-type-pill tp-del" onclick="selectSlotType('del',this)">配送</button>
<button class="ph-type-pill tp-pick" onclick="selectSlotType('pick',this)">自提</button>
</div>
</div>
<div class="g-form-group">
<label class="g-form-label required">适用星期</label>
<div class="ph-day-pills" id="addDayPills">
<button class="ph-day-pill selected" onclick="toggleDayPill(this)">周一</button>
<button class="ph-day-pill selected" onclick="toggleDayPill(this)">周二</button>
<button class="ph-day-pill selected" onclick="toggleDayPill(this)">周三</button>
<button class="ph-day-pill selected" onclick="toggleDayPill(this)">周四</button>
<button class="ph-day-pill selected" onclick="toggleDayPill(this)">周五</button>
<button class="ph-day-pill" onclick="toggleDayPill(this)">周六</button>
<button class="ph-day-pill" onclick="toggleDayPill(this)">周日</button>
</div>
<div class="ph-quick-btns">
<button class="ph-quick-btn" onclick="quickSelectDays('all')">全选</button>
<button class="ph-quick-btn" onclick="quickSelectDays('weekday')">工作日</button>
<button class="ph-quick-btn" onclick="quickSelectDays('weekend')">周末</button>
</div>
</div>
<div style="display:grid; grid-template-columns:1fr 1fr; gap:12px;">
<div class="g-form-group">
<label class="g-form-label required">开始时间</label>
<input type="time" class="g-input" value="09:00" style="width:100%;" />
</div>
<div class="g-form-group">
<label class="g-form-label required">结束时间</label>
<input type="time" class="g-input" value="22:00" style="width:100%;" />
</div>
</div>
<div class="g-form-group" id="addCapField" style="display:none;">
<label class="g-form-label">容量上限</label>
<div style="display:flex; align-items:center; gap:6px;">
<input type="number" class="g-input" value="50" style="width:120px;" />
<span style="font-size:13px; color:#666;">单/小时</span>
</div>
<div class="g-hint">该时段内每小时最大接单量</div>
</div>
<div class="g-form-group">
<label class="g-form-label">备注</label>
<textarea class="g-textarea" rows="2" placeholder="可选,如:午市高峰时段"></textarea>
</div>
</div>
<div class="g-drawer-ft">
<button class="g-btn" onclick="closeHoursDrawer()">取消</button>
<button class="g-btn g-btn-primary" onclick="closeHoursDrawer()">确认添加</button>
</div>
</div>
<!-- 编辑时段抽屉 -->
<div class="g-drawer" id="editDayDrawer" style="width:520px;">
<div class="g-drawer-hd">
<span class="g-drawer-title" id="editDayTitle">编辑时段 - 周一</span>
<button class="g-drawer-close" onclick="closeHoursDrawer()"><i data-lucide="x" style="width:18px;height:18px;"></i></button>
</div>
<div class="g-drawer-bd">
<div class="ph-toggle-row">
<button class="g-toggle on" id="dayOpenToggle" onclick="toggleDayOpen(this)"></button>
<span class="g-toggle-label">今日营业</span>
<span class="ph-toggle-hint" id="dayClosedHint">该日休息,不接单</span>
</div>
<div id="editSlotList">
<div class="ph-slot-card">
<span class="ph-slot-tag st-biz">营业</span>
<div class="ph-slot-fields">
<div><label>开始</label><input type="time" class="g-input" value="09:00" /></div>
<div><label>结束</label><input type="time" class="g-input" value="22:00" /></div>
</div>
<button class="ph-slot-del"><i data-lucide="trash-2" style="width:14px;height:14px;"></i></button>
</div>
<div class="ph-slot-card">
<span class="ph-slot-tag st-del">配送</span>
<div class="ph-slot-fields">
<div><label>开始</label><input type="time" class="g-input" value="10:00" /></div>
<div><label>结束</label><input type="time" class="g-input" value="21:30" /></div>
<div><label>容量</label><input type="number" class="g-input" value="50" style="width:70px;" /><span style="font-size:11px;color:#999;margin-left:3px;">单/h</span></div>
</div>
<button class="ph-slot-del"><i data-lucide="trash-2" style="width:14px;height:14px;"></i></button>
</div>
<div class="ph-slot-card">
<span class="ph-slot-tag st-pick">自提</span>
<div class="ph-slot-fields">
<div><label>开始</label><input type="time" class="g-input" value="09:00" /></div>
<div><label>结束</label><input type="time" class="g-input" value="21:00" /></div>
</div>
<button class="ph-slot-del"><i data-lucide="trash-2" style="width:14px;height:14px;"></i></button>
</div>
</div>
<button class="ph-add-dashed" style="margin-top:4px;" onclick="addSlotRow()">
<i data-lucide="plus" style="width:14px;height:14px;"></i>添加时段
</button>
</div>
<div class="g-drawer-ft">
<button class="g-btn" onclick="closeHoursDrawer()">取消</button>
<button class="g-btn g-btn-primary" onclick="closeHoursDrawer()">保存修改</button>
</div>
</div>
<!-- 节假日抽屉 -->
<div class="g-drawer" id="holidayDrawer" style="width:480px;">
<div class="g-drawer-hd">
<span class="g-drawer-title" id="holidayDrawerTitle">添加日期</span>
<button class="g-drawer-close" onclick="closeHoursDrawer()"><i data-lucide="x" style="width:18px;height:18px;"></i></button>
</div>
<div class="g-drawer-bd">
<div class="g-form-group">
<label class="g-form-label required">类型</label>
<div class="ph-hol-type-pills">
<button class="ph-hol-type-pill ht-closed active" onclick="selectHolType('closed',this)">休息</button>
<button class="ph-hol-type-pill ht-special" onclick="selectHolType('special',this)">特殊营业</button>
</div>
</div>
<div class="g-form-group">
<label class="g-form-label required">日期</label>
<div class="ph-date-mode">
<button class="ph-date-mode-pill active" onclick="switchDateMode('single',this)">单日</button>
<button class="ph-date-mode-pill" onclick="switchDateMode('range',this)">日期范围</button>
</div>
<div id="holDateSingle">
<input type="date" class="g-input" id="holDate1" value="2026-05-01" style="width:100%;" />
</div>
<div id="holDateRange" style="display:none;">
<div style="display:flex; align-items:center; gap:8px;">
<input type="date" class="g-input" id="holDateFrom" value="2026-05-01" style="flex:1;" />
<span style="color:#999;">~</span>
<input type="date" class="g-input" id="holDateTo" value="2026-05-03" style="flex:1;" />
</div>
</div>
</div>
<div class="g-form-group" id="holTimeGroup" style="display:none;">
<label class="g-form-label">营业时间</label>
<div style="display:grid; grid-template-columns:1fr 1fr; gap:12px;">
<div>
<label style="font-size:11px;color:#999;">开始</label>
<input type="time" class="g-input" id="holTimeStart" value="09:00" style="width:100%;" />
</div>
<div>
<label style="font-size:11px;color:#999;">结束</label>
<input type="time" class="g-input" id="holTimeEnd" value="22:00" style="width:100%;" />
</div>
</div>
<div class="g-hint">特殊营业日的营业时间段</div>
</div>
<div class="g-form-group">
<label class="g-form-label required">原因</label>
<input type="text" class="g-input" id="holReason" placeholder="如:春节假期、情人节延长营业" style="width:100%;" />
</div>
<div class="g-form-group">
<label class="g-form-label">备注</label>
<textarea class="g-textarea" rows="2" placeholder="可选"></textarea>
</div>
</div>
<div class="g-drawer-ft">
<button class="g-btn" onclick="closeHoursDrawer()">取消</button>
<button class="g-btn g-btn-primary" id="holSubmitBtn" onclick="closeHoursDrawer()">确认添加</button>
</div>
</div>
<script>
function openHoursDrawer(mode, dayName) {
document.getElementById('hoursDrawerMask').classList.add('open');
if (mode === 'add') {
document.getElementById('addSlotDrawer').classList.add('open');
} else if (mode === 'editDay') {
document.getElementById('editDayTitle').textContent = '编辑时段 - ' + dayName;
document.getElementById('editDayDrawer').classList.add('open');
}
if (typeof lucide !== 'undefined') lucide.createIcons();
}
function closeHoursDrawer() {
document.getElementById('hoursDrawerMask').classList.remove('open');
document.getElementById('addSlotDrawer').classList.remove('open');
document.getElementById('editDayDrawer').classList.remove('open');
document.getElementById('holidayDrawer').classList.remove('open');
}
function selectSlotType(type, el) {
el.parentElement.querySelectorAll('.ph-type-pill').forEach(function(b){ b.classList.remove('active'); });
el.classList.add('active');
document.getElementById('addCapField').style.display = type === 'del' ? '' : 'none';
}
function toggleDayPill(el) {
el.classList.toggle('selected');
}
function quickSelectDays(mode) {
var pills = document.querySelectorAll('#addDayPills .ph-day-pill');
var weekdays = ['周一','周二','周三','周四','周五'];
var weekends = ['周六','周日'];
pills.forEach(function(p) {
var day = p.textContent;
if (mode === 'all') p.classList.add('selected');
else if (mode === 'weekday') { weekdays.includes(day) ? p.classList.add('selected') : p.classList.remove('selected'); }
else if (mode === 'weekend') { weekends.includes(day) ? p.classList.add('selected') : p.classList.remove('selected'); }
});
}
function toggleDayOpen(el) {
el.classList.toggle('on');
var isOn = el.classList.contains('on');
document.getElementById('dayClosedHint').style.display = isOn ? 'none' : 'inline';
document.getElementById('editSlotList').style.opacity = isOn ? '1' : '0.4';
document.getElementById('editSlotList').style.pointerEvents = isOn ? '' : 'none';
}
function openHolidayDrawer(mode, data) {
document.getElementById('hoursDrawerMask').classList.add('open');
document.getElementById('holidayDrawer').classList.add('open');
if (mode === 'edit' && data) {
document.getElementById('holidayDrawerTitle').textContent = '编辑日期';
document.getElementById('holSubmitBtn').textContent = '保存修改';
document.getElementById('holReason').value = data.reason || '';
// set type pill
document.querySelectorAll('.ph-hol-type-pill').forEach(function(b){ b.classList.remove('active'); });
if (data.type === 'closed') document.querySelector('.ht-closed').classList.add('active');
else document.querySelector('.ht-special').classList.add('active');
document.getElementById('holTimeGroup').style.display = data.type === 'special' ? '' : 'none';
if (data.time) {
var parts = data.time.replace(/\s/g,'').split('-');
if (parts.length === 2) {
document.getElementById('holTimeStart').value = parts[0];
document.getElementById('holTimeEnd').value = parts[1];
}
}
// set date
if (data.date && data.date.includes('~')) {
switchDateMode('range', document.querySelectorAll('.ph-date-mode-pill')[1]);
var dp = data.date.split('~').map(function(s){ return s.trim(); });
document.getElementById('holDateFrom').value = dp[0];
document.getElementById('holDateTo').value = dp[1];
} else {
switchDateMode('single', document.querySelectorAll('.ph-date-mode-pill')[0]);
document.getElementById('holDate1').value = data.date || '';
}
} else {
document.getElementById('holidayDrawerTitle').textContent = '添加日期';
document.getElementById('holSubmitBtn').textContent = '确认添加';
document.getElementById('holReason').value = '';
document.querySelectorAll('.ph-hol-type-pill').forEach(function(b){ b.classList.remove('active'); });
document.querySelector('.ht-closed').classList.add('active');
document.getElementById('holTimeGroup').style.display = 'none';
switchDateMode('single', document.querySelectorAll('.ph-date-mode-pill')[0]);
}
if (typeof lucide !== 'undefined') lucide.createIcons();
}
function selectHolType(type, el) {
el.parentElement.querySelectorAll('.ph-hol-type-pill').forEach(function(b){ b.classList.remove('active'); });
el.classList.add('active');
document.getElementById('holTimeGroup').style.display = type === 'special' ? '' : 'none';
}
function switchDateMode(mode, el) {
document.querySelectorAll('.ph-date-mode-pill').forEach(function(b){ b.classList.remove('active'); });
if (el) el.classList.add('active');
document.getElementById('holDateSingle').style.display = mode === 'single' ? '' : 'none';
document.getElementById('holDateRange').style.display = mode === 'range' ? '' : 'none';
}
function addSlotRow() {
var list = document.getElementById('editSlotList');
if (!list) return;
var cards = list.querySelectorAll('.ph-slot-card');
if (cards.length > 0) {
var clone = cards[cards.length - 1].cloneNode(true);
clone.querySelectorAll('input').forEach(function(inp){ inp.value = ''; });
list.appendChild(clone);
if (typeof lucide !== 'undefined') lucide.createIcons();
}
}
</script>

366
pages/store-list.html Normal file
View File

@@ -0,0 +1,366 @@
<!-- 门店列表页 -->
<style>
.page-store-list .filter-bar { display:flex; gap:12px; flex-wrap:wrap; align-items:center; margin-bottom:16px; box-shadow:var(--g-shadow-sm); border-radius:10px; padding:10px 14px; background:#fff; }
.page-store-list .filter-bar input,
.page-store-list .filter-bar select {
height:34px; padding:0 10px; border:1px solid #e5e7eb; border-radius:8px;
font-size:13px; outline:none; transition:var(--g-transition); background:#fff;
}
.page-store-list .filter-bar input:focus,
.page-store-list .filter-bar select:focus { border-color:var(--primary); box-shadow:0 0 0 3px color-mix(in srgb, var(--primary) 12%, transparent); }
.page-store-list .filter-bar input { width:220px; }
.page-store-list .stat-cards { display:grid; grid-template-columns:repeat(4,1fr); gap:12px; margin-bottom:16px; }
.page-store-list .stat-card {
background:#fff; border-radius:10px; padding:16px 20px;
border:none; box-shadow:var(--g-shadow-sm); transition:var(--g-transition);
}
.page-store-list .stat-card:hover { box-shadow:var(--g-shadow-md); transform:translateY(-2px); }
.page-store-list .stat-card .label { font-size:13px; color:#9ca3af; margin-bottom:6px; }
.page-store-list .stat-card .value { font-size:24px; font-weight:700; color:#1a1a2e; }
.page-store-list .stat-card .value.green { color:#22c55e; }
.page-store-list .stat-card .value.orange { color:#f59e0b; }
.page-store-list .stat-card .value.red { color:#ef4444; }
.page-store-list .table-wrap {
background:#fff; border-radius:10px; border:none; box-shadow:var(--g-shadow-sm); overflow:hidden;
}
.page-store-list table { width:100%; border-collapse:collapse; font-size:13px; }
.page-store-list thead th {
background:#f8f9fb; padding:10px 12px; text-align:left;
font-weight:600; color:#6b7280; border-bottom:1px solid #e5e7eb;
white-space:nowrap;
}
.page-store-list tbody td {
padding:12px; border-bottom:1px solid #f3f4f6; color:#1a1a2e;
}
.page-store-list tbody tr:hover { background:color-mix(in srgb, var(--primary) 3%, #fff); }
.page-store-list tbody tr:last-child td { border-bottom:none; }
.page-store-list .status {
display:inline-flex; align-items:center; gap:5px; font-size:12px; font-weight:600;
}
.page-store-list .status-dot {
width:6px; height:6px; border-radius:50%;
}
.page-store-list .status-open .status-dot { background:#22c55e; }
.page-store-list .status-open { color:#22c55e; }
.page-store-list .status-closed .status-dot { background:#9ca3af; }
.page-store-list .status-closed { color:#9ca3af; }
.page-store-list .status-forced .status-dot { background:#ef4444; }
.page-store-list .status-forced { color:#ef4444; }
.page-store-list .audit-pass { color:#22c55e; font-weight:600; }
.page-store-list .audit-pending { color:#f59e0b; font-weight:600; }
.page-store-list .audit-reject { color:#ef4444; font-weight:600; }
.page-store-list .pagination {
display:flex; align-items:center; justify-content:flex-end;
padding:12px 16px; gap:6px; font-size:13px; color:#4b5563;
background:#fafbfc;
}
.page-store-list .page-btn {
min-width:34px; height:34px; border:1px solid #e5e7eb; border-radius:8px;
background:#fff; cursor:pointer; display:flex; align-items:center;
justify-content:center; font-size:13px; transition:var(--g-transition);
}
.page-store-list .page-btn:hover { border-color:var(--primary); color:var(--primary); }
.page-store-list .page-btn.active { background:var(--primary); color:#fff; border-color:var(--primary); box-shadow:0 0 0 3px color-mix(in srgb, var(--primary) 12%, transparent); }
.page-store-list .store-name-cell { display:flex; align-items:center; gap:10px; }
.page-store-list .store-avatar {
width:36px; height:36px; border-radius:8px; background:#f8f9fb;
display:flex; align-items:center; justify-content:center; flex-shrink:0;
overflow:hidden; font-size:14px; color:#9ca3af;
}
.page-store-list .store-info .name { font-weight:500; color:#1a1a2e; }
.page-store-list .store-info .code { font-size:11px; color:#9ca3af; margin-top:2px; }
</style>
<div class="page-store-list">
<!-- 筛选栏 -->
<div class="filter-bar">
<input type="text" placeholder="搜索门店名称 / 编号 / 电话" />
<select>
<option value="">营业状态</option>
<option>营业中</option>
<option>休息中</option>
<option>强制关闭</option>
</select>
<select>
<option value="">审核状态</option>
<option>待审核</option>
<option>已通过</option>
<option>已拒绝</option>
</select>
<select>
<option value="">服务方式</option>
<option>外卖</option>
<option>自提</option>
<option>堂食</option>
</select>
<button class="g-btn">查询</button>
<button class="g-btn">重置</button>
<div style="flex:1;"></div>
<button class="g-btn g-btn-primary" onclick="openSlDrawer('create')">
<i data-lucide="plus" style="width:14px;height:14px;"></i>新增门店
</button>
<button class="g-btn">
<i data-lucide="download" style="width:14px;height:14px;"></i>导出
</button>
</div>
<!-- 统计卡片 -->
<div class="stat-cards">
<div class="stat-card">
<div class="label">门店总数</div>
<div class="value">5</div>
</div>
<div class="stat-card">
<div class="label">营业中</div>
<div class="value green">3</div>
</div>
<div class="stat-card">
<div class="label">休息中</div>
<div class="value orange">1</div>
</div>
<div class="stat-card">
<div class="label">待审核</div>
<div class="value red">1</div>
</div>
</div>
<!-- 表格 -->
<div class="table-wrap">
<table>
<thead>
<tr>
<th style="width:220px;">门店信息</th>
<th>联系电话</th>
<th>店长</th>
<th>地址</th>
<th>服务方式</th>
<th>营业状态</th>
<th>审核状态</th>
<th>创建时间</th>
<th style="width:160px;">操作</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<div class="store-name-cell">
<div class="store-avatar" style="background:#3b82f6"><i data-lucide="store" style="width:20px;height:20px;color:#fff"></i></div>
<div class="store-info">
<div class="name">老三家外卖(朝阳店)</div>
<div class="code">ST20250001</div>
</div>
</div>
</td>
<td>138****8001</td>
<td>张伟</td>
<td>北京市朝阳区建国路88号</td>
<td>
<span class="g-tag g-tag-blue">外卖</span>
<span class="g-tag g-tag-green">自提</span>
<span class="g-tag g-tag-orange">堂食</span>
</td>
<td><span class="status status-open"><span class="status-dot"></span>营业中</span></td>
<td><span class="audit-pass">已通过</span></td>
<td>2025-06-15</td>
<td>
<a class="g-action" onclick="openSlDrawer('edit',{name:'老三家外卖(朝阳店)',code:'ST20250001',phone:'13800008001',manager:'张伟'})">编辑</a>
<a class="g-action">歇业</a>
<a class="g-action g-action-danger">删除</a>
</td>
</tr>
<tr>
<td>
<div class="store-name-cell">
<div class="store-avatar" style="background:#f59e0b"><i data-lucide="store" style="width:20px;height:20px;color:#fff"></i></div>
<div class="store-info">
<div class="name">老三家外卖(海淀店)</div>
<div class="code">ST20250002</div>
</div>
</div>
</td>
<td>138****8002</td>
<td>李娜</td>
<td>北京市海淀区中关村大街66号</td>
<td>
<span class="g-tag g-tag-blue">外卖</span>
<span class="g-tag g-tag-green">自提</span>
</td>
<td><span class="status status-open"><span class="status-dot"></span>营业中</span></td>
<td><span class="audit-pass">已通过</span></td>
<td>2025-07-20</td>
<td>
<a class="g-action" onclick="openSlDrawer('edit',{name:'老三家外卖(海淀店)',code:'ST20250002',phone:'13800008002',manager:'李娜'})">编辑</a>
<a class="g-action">歇业</a>
<a class="g-action g-action-danger">删除</a>
</td>
</tr>
<tr>
<td>
<div class="store-name-cell">
<div class="store-avatar" style="background:#8b5cf6"><i data-lucide="store" style="width:20px;height:20px;color:#fff"></i></div>
<div class="store-info">
<div class="name">老三家外卖(望京店)</div>
<div class="code">ST20250003</div>
</div>
</div>
</td>
<td>138****8003</td>
<td>王磊</td>
<td>北京市朝阳区望京西路50号</td>
<td>
<span class="g-tag g-tag-blue">外卖</span>
</td>
<td><span class="status status-open"><span class="status-dot"></span>营业中</span></td>
<td><span class="audit-pass">已通过</span></td>
<td>2025-09-01</td>
<td>
<a class="g-action" onclick="openSlDrawer('edit',{name:'老三家外卖(望京店)',code:'ST20250003',phone:'13800008003',manager:'王磊'})">编辑</a>
<a class="g-action">歇业</a>
<a class="g-action g-action-danger">删除</a>
</td>
</tr>
<tr>
<td>
<div class="store-name-cell">
<div class="store-avatar" style="background:#ef4444"><i data-lucide="store" style="width:20px;height:20px;color:#fff"></i></div>
<div class="store-info">
<div class="name">老三家外卖(通州店)</div>
<div class="code">ST20250004</div>
</div>
</div>
</td>
<td>138****8004</td>
<td>赵敏</td>
<td>北京市通州区新华大街120号</td>
<td>
<span class="g-tag g-tag-blue">外卖</span>
<span class="g-tag g-tag-green">自提</span>
<span class="g-tag g-tag-orange">堂食</span>
</td>
<td><span class="status status-closed"><span class="status-dot"></span>休息中</span></td>
<td><span class="audit-pass">已通过</span></td>
<td>2025-10-12</td>
<td>
<a class="g-action" onclick="openSlDrawer('edit',{name:'老三家外卖(通州店)',code:'ST20250004',phone:'13800008004',manager:'赵敏'})">编辑</a>
<a class="g-action">开业</a>
<a class="g-action g-action-danger">删除</a>
</td>
</tr>
<tr>
<td>
<div class="store-name-cell">
<div class="store-avatar" style="background:#22c55e"><i data-lucide="store" style="width:20px;height:20px;color:#fff"></i></div>
<div class="store-info">
<div class="name">老三家外卖(丰台店)</div>
<div class="code">ST20250005</div>
</div>
</div>
</td>
<td>138****8005</td>
<td>刘洋</td>
<td>北京市丰台区丰台路18号</td>
<td>
<span class="g-tag g-tag-blue">外卖</span>
<span class="g-tag g-tag-green">自提</span>
</td>
<td><span class="status status-forced"><span class="status-dot"></span>待审核</span></td>
<td><span class="audit-pending">待审核</span></td>
<td>2026-01-28</td>
<td>
<a class="g-action" onclick="openSlDrawer('edit',{name:'老三家外卖(丰台店)',code:'ST20250005',phone:'13800008005',manager:'刘洋'})">编辑</a>
<a class="g-action g-action-danger">删除</a>
</td>
</tr>
</tbody>
</table>
<!-- 分页 -->
<div class="pagination">
<span>共 5 条</span>
<button class="page-btn">&lt;</button>
<button class="page-btn active">1</button>
<button class="page-btn">&gt;</button>
<span style="margin-left:8px;">10 条/页</span>
</div>
</div>
</div>
<!-- 门店添加/编辑抽屉 -->
<div class="g-drawer-mask" id="slDrawerMask" onclick="closeSlDrawer()"></div>
<div class="g-drawer" id="slDrawer" style="width:560px">
<div class="g-drawer-hd">
<div class="g-drawer-title" id="slDrawerTitle">添加门店</div>
<button class="g-drawer-close" onclick="closeSlDrawer()"><i data-lucide="x" style="width:18px;height:18px;"></i></button>
</div>
<div class="g-drawer-bd">
<div style="font-size:15px;font-weight:600;color:var(--g-text);padding-left:10px;border-left:3px solid var(--primary);margin-bottom:16px">基本信息</div>
<div class="g-form-group">
<label class="g-form-label required">门店名称</label>
<input class="g-input" placeholder="请输入门店名称">
</div>
<div class="g-form-group">
<label class="g-form-label required">门店编码</label>
<input class="g-input" placeholder="如ST20250006">
</div>
<div class="g-form-group">
<label class="g-form-label required">联系电话</label>
<input class="g-input" placeholder="请输入联系电话">
</div>
<div class="g-form-group">
<label class="g-form-label required">负责人</label>
<input class="g-input" placeholder="请输入负责人姓名">
</div>
<div class="g-form-group">
<label class="g-form-label required">门店地址</label>
<input class="g-input" placeholder="请输入详细地址">
</div>
<div class="g-form-group">
<label class="g-form-label">门店图片</label>
<div class="g-upload-zone" style="padding:16px">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>
<span>点击上传门店图片</span>
</div>
</div>
<div style="font-size:15px;font-weight:600;color:var(--g-text);padding-left:10px;border-left:3px solid var(--primary);margin-bottom:16px;margin-top:24px">营业设置</div>
<div class="g-form-group">
<label class="g-form-label">营业状态</label>
<select class="g-select">
<option>营业中</option>
<option>歇业中</option>
<option>装修中</option>
</select>
</div>
<div class="g-form-group">
<label class="g-form-label">配送方式</label>
<div style="display:flex;gap:8px;flex-wrap:wrap">
<span class="g-pill checked" onclick="this.classList.toggle('checked')">外卖配送</span>
<span class="g-pill" onclick="this.classList.toggle('checked')">到店自提</span>
<span class="g-pill" onclick="this.classList.toggle('checked')">堂食</span>
</div>
</div>
</div>
<div class="g-drawer-ft">
<button class="g-btn" onclick="closeSlDrawer()">取消</button>
<button class="g-btn g-btn-primary" onclick="closeSlDrawer()">确认</button>
</div>
</div>
<script>
function openSlDrawer(mode) {
document.getElementById('slDrawerMask').classList.add('open');
document.getElementById('slDrawer').classList.add('open');
document.getElementById('slDrawerTitle').textContent = mode === 'edit' ? '编辑门店' : '添加门店';
}
function closeSlDrawer() {
document.getElementById('slDrawerMask').classList.remove('open');
document.getElementById('slDrawer').classList.remove('open');
}
</script>

245
pages/store-pickup.html Normal file
View File

@@ -0,0 +1,245 @@
<!-- 自提设置页 -->
<style>
.page-pickup { max-width:960px; }
.pp-toolbar { display:flex; align-items:center; gap:12px; margin-bottom:16px; box-shadow:var(--g-shadow-sm); border-radius:10px; padding:10px 14px; background:#fff; }
.pp-toolbar select { height:34px; padding:0 10px; border:1px solid #e5e7eb; border-radius:8px; font-size:13px; outline:none; background:#fff; transition:var(--g-transition); }
.pp-card { background:#fff; border-radius:10px; border:none; box-shadow:var(--g-shadow-sm); margin-bottom:16px; overflow:hidden; transition:var(--g-transition); }
.pp-card:hover { box-shadow:var(--g-shadow-md); }
.pp-card-header { padding:14px 18px; font-size:14px; font-weight:600; color:#1a1a2e; border-bottom:1px solid #f3f4f6; display:flex; align-items:center; justify-content:space-between; background:#f8f9fb; }
.pp-card-body { padding:16px 18px; }
.pp-form-row { display:flex; align-items:center; padding:12px 0; border-bottom:1px solid #f3f4f6; gap:12px; }
.pp-form-row:last-child { border-bottom:none; }
.pp-label { width:130px; font-size:13px; font-weight:500; color:#4b5563; flex-shrink:0; }
.pp-control { display:flex; align-items:center; gap:8px; flex:1; }
.pp-input { height:34px; padding:0 10px; border:1px solid #e5e7eb; border-radius:8px; font-size:13px; outline:none; background:#fff; width:120px; transition:var(--g-transition); }
.pp-input:focus { border-color:var(--primary); box-shadow:0 0 0 3px color-mix(in srgb, var(--primary) 12%, transparent); }
.pp-unit { font-size:12px; color:#9ca3af; }
.pp-hint { font-size:12px; color:#9ca3af; margin-left:8px; }
.pp-form-actions { display:flex; justify-content:flex-end; gap:8px; padding-top:14px; border-top:1px solid #f3f4f6; margin-top:4px; }
.pp-table { width:100%; border-collapse:collapse; font-size:13px; }
.pp-table th { background:#f8f9fb; padding:10px 12px; text-align:left; font-weight:600; color:#6b7280; border-bottom:1px solid #e5e7eb; white-space:nowrap; }
.pp-table td { padding:10px 12px; border-bottom:1px solid #f3f4f6; color:#1a1a2e; }
.pp-table tr:last-child td { border-bottom:none; }
.pp-table tr:hover td { background:color-mix(in srgb, var(--primary) 3%, #fff); }
.pp-progress { display:flex; align-items:center; gap:6px; }
.pp-progress-bar { width:60px; height:6px; background:#e5e7eb; border-radius:3px; overflow:hidden; }
.pp-progress-fill { height:100%; border-radius:3px; background:var(--primary); }
.pp-progress-text { font-size:11px; color:#9ca3af; white-space:nowrap; }
.pp-weekday { display:inline-block; padding:1px 8px; border-radius:6px; font-size:11px; font-weight:600; background:#f0f5ff; color:#597ef7; }
.pp-mode-switch { display:flex; background:#f8f9fb; border-radius:8px; padding:3px; gap:2px; width:fit-content; margin-bottom:16px; }
.pp-mode-item { padding:6px 18px; border-radius:6px; font-size:13px; cursor:pointer; color:#4b5563; border:none; background:none; transition:var(--g-transition); }
.pp-mode-item.active { background:#fff; color:var(--primary); font-weight:600; box-shadow:var(--g-shadow-sm); }
.pp-day-tabs { display:flex; gap:8px; margin-bottom:14px; }
.pp-day-tab { padding:8px 16px; border-radius:8px; font-size:13px; cursor:pointer; border:1px solid #e5e7eb; background:#fff; color:#4b5563; transition:var(--g-transition); }
.pp-day-tab.active { background:var(--primary); border-color:var(--primary); color:#fff; }
.pp-day-tab .dt-date { font-weight:500; }
.pp-day-tab .dt-sub { font-size:11px; opacity:0.8; }
.pp-slot-grid { display:flex; flex-wrap:wrap; gap:8px; }
.pp-slot-cell { width:82px; padding:8px 6px; border-radius:8px; text-align:center; border:1px solid #e5e7eb; transition:var(--g-transition); }
.pp-slot-cell .sc-time { font-size:13px; font-weight:500; }
.pp-slot-cell .sc-status { font-size:11px; margin-top:2px; }
.pp-slot-cell.expired { background:#f8f9fb; color:#bbb; border-color:#e5e7eb; }
.pp-slot-cell.available { background:#f0fdf4; color:#16a34a; border-color:#bbf7d0; }
.pp-slot-cell.almost { background:#fffbeb; color:#d97706; border-color:#fde68a; }
.pp-slot-cell.full { background:#fef2f2; color:#ef4444; border-color:#fecaca; }
.pp-legend { display:flex; gap:16px; font-size:12px; color:#9ca3af; margin-top:14px; }
.pp-legend span { display:flex; align-items:center; gap:4px; }
.pp-legend-dot { width:10px; height:10px; border-radius:2px; }
.pp-fg { margin-bottom:18px; }
.pp-fg-label { font-size:13px; font-weight:500; color:#4b5563; margin-bottom:6px; display:block; }
.pp-fg-hint { font-size:11px; color:#9ca3af; margin-top:4px; }
.pp-fg-input { height:34px; border:1px solid #e5e7eb; border-radius:8px; font-size:13px; padding:0 10px; outline:none; background:#fff; transition:var(--g-transition); }
.pp-fg-input:focus { border-color:var(--primary); box-shadow:0 0 0 3px color-mix(in srgb, var(--primary) 12%, transparent); }
.pp-day-pills { display:flex; gap:6px; }
.pp-day-pill { width:42px; height:34px; border-radius:8px; font-size:12px; cursor:pointer; border:1px solid #e5e7eb; background:#fff; color:#4b5563; display:flex; align-items:center; justify-content:center; transition:var(--g-transition); }
.pp-day-pill.selected { background:var(--primary); border-color:var(--primary); color:#fff; }
.pp-qk-btns { display:flex; gap:8px; margin-top:8px; }
.pp-qk-btn { font-size:11px; color:var(--primary); cursor:pointer; background:none; border:none; padding:0; transition:var(--g-transition); }
.pp-qk-btn:hover { text-decoration:underline; }
</style>
<div class="page-pickup">
<div class="pp-toolbar">
<select style="width:200px;">
<option>老三家外卖(朝阳店)</option>
<option>老三家外卖(海淀店)</option>
<option>老三家外卖(望京店)</option>
<option>老三家外卖(通州店)</option>
<option>老三家外卖(丰台店)</option>
</select>
<div style="flex:1;"></div>
<button class="g-btn" onclick="openCopyStoreModal('复制自提设置到其他门店')">
<i data-lucide="copy" style="width:14px;height:14px;"></i>复制到其他门店
</button>
</div>
<div class="pp-card">
<div class="pp-card-header">基本设置</div>
<div class="pp-card-body">
<div class="pp-form-row">
<div class="pp-label">允许当天自提</div>
<div class="pp-control"><label class="g-toggle-input"><input type="checkbox" checked><span class="g-toggle-sl"></span></label><span class="pp-hint">开启后顾客可选择当天自提</span></div>
</div>
<div class="pp-form-row">
<div class="pp-label">可预约天数</div>
<div class="pp-control"><input type="number" class="pp-input" value="3" style="width:80px;"><span class="pp-unit"></span><span class="pp-hint">顾客可提前预约的天数</span></div>
</div>
<div class="pp-form-row">
<div class="pp-label">单笔最大数量</div>
<div class="pp-control"><input type="number" class="pp-input" value="20" style="width:80px;"><span class="pp-unit"></span><span class="pp-hint">留空则不限制</span></div>
</div>
<div class="pp-form-actions"><button class="g-btn">重置</button><button class="g-btn g-btn-primary"><i data-lucide="save" style="width:14px;height:14px;"></i>保存设置</button></div>
</div>
</div>
<div class="pp-mode-switch">
<button class="pp-mode-item active" onclick="switchPickupMode('big')">大时段模式</button>
<button class="pp-mode-item" onclick="switchPickupMode('fine')">精细时段模式</button>
</div>
<div id="bigSlotSection">
<div class="pp-card">
<div class="pp-card-header">自提时段<button class="g-btn g-btn-primary g-btn-sm" onclick="openPickupDrawer('add')"><i data-lucide="plus" style="width:13px;height:13px;"></i>添加时段</button></div>
<div class="pp-card-body" style="padding:0;">
<table class="pp-table"><thead><tr><th>时段名称</th><th>时间范围</th><th>截止(分钟)</th><th>容量</th><th>已预约</th><th>适用星期</th><th>状态</th><th>操作</th></tr></thead>
<tbody>
<tr><td style="font-weight:500;">上午时段</td><td>09:00-11:30</td><td>30</td><td>20</td><td><div class="pp-progress"><div class="pp-progress-bar"><div class="pp-progress-fill" style="width:25%;"></div></div><span class="pp-progress-text">5/20</span></div></td><td><span class="pp-weekday">周一至周五</span></td><td><label class="g-toggle-input"><input type="checkbox" checked><span class="g-toggle-sl"></span></label></td><td><a class="g-action" onclick="openPickupDrawer('edit',{name:'上午时段',start:'09:00',end:'11:30',cutoff:30,cap:20,days:'weekday'})">编辑</a><a class="g-action g-action-danger">删除</a></td></tr>
<tr><td style="font-weight:500;">午间时段</td><td>11:30-14:00</td><td>20</td><td>30</td><td><div class="pp-progress"><div class="pp-progress-bar"><div class="pp-progress-fill" style="width:40%;"></div></div><span class="pp-progress-text">12/30</span></div></td><td><span class="pp-weekday">每天</span></td><td><label class="g-toggle-input"><input type="checkbox" checked><span class="g-toggle-sl"></span></label></td><td><a class="g-action" onclick="openPickupDrawer('edit',{name:'午间时段',start:'11:30',end:'14:00',cutoff:20,cap:30,days:'all'})">编辑</a><a class="g-action g-action-danger">删除</a></td></tr>
<tr><td style="font-weight:500;">下午时段</td><td>14:00-17:00</td><td>30</td><td>15</td><td><div class="pp-progress"><div class="pp-progress-bar"><div class="pp-progress-fill" style="width:20%;"></div></div><span class="pp-progress-text">3/15</span></div></td><td><span class="pp-weekday">周一至周五</span></td><td><label class="g-toggle-input"><input type="checkbox" checked><span class="g-toggle-sl"></span></label></td><td><a class="g-action" onclick="openPickupDrawer('edit',{name:'下午时段',start:'14:00',end:'17:00',cutoff:30,cap:15,days:'weekday'})">编辑</a><a class="g-action g-action-danger">删除</a></td></tr>
<tr><td style="font-weight:500;">晚间时段</td><td>17:00-20:30</td><td>30</td><td>25</td><td><div class="pp-progress"><div class="pp-progress-bar"><div class="pp-progress-fill" style="width:32%;"></div></div><span class="pp-progress-text">8/25</span></div></td><td><span class="pp-weekday">每天</span></td><td><label class="g-toggle-input"><input type="checkbox" checked><span class="g-toggle-sl"></span></label></td><td><a class="g-action" onclick="openPickupDrawer('edit',{name:'晚间时段',start:'17:00',end:'20:30',cutoff:30,cap:25,days:'all'})">编辑</a><a class="g-action g-action-danger">删除</a></td></tr>
<tr><td style="font-weight:500;">周末特惠</td><td>10:00-15:00</td><td>45</td><td>40</td><td><div class="pp-progress"><div class="pp-progress-bar"><div class="pp-progress-fill" style="width:45%;background:#faad14;"></div></div><span class="pp-progress-text">18/40</span></div></td><td><span class="pp-weekday">周六周日</span></td><td><label class="g-toggle-input"><input type="checkbox"><span class="g-toggle-sl"></span></label></td><td><a class="g-action" onclick="openPickupDrawer('edit',{name:'周末特惠',start:'10:00',end:'15:00',cutoff:45,cap:40,days:'weekend'})">编辑</a><a class="g-action g-action-danger">删除</a></td></tr>
</tbody></table>
</div>
</div>
</div>
<div id="fineSlotSection" style="display:none;">
<div class="pp-card">
<div class="pp-card-header">生成规则</div>
<div class="pp-card-body">
<div style="display:grid;grid-template-columns:1fr 1fr;gap:14px 24px;">
<div class="pp-fg"><label class="pp-fg-label">时间间隔</label><select class="pp-input" style="width:100%;"><option>15 分钟</option><option>20 分钟</option><option>25 分钟</option><option selected>30 分钟</option><option>45 分钟</option><option>60 分钟</option></select></div>
<div class="pp-fg"><label class="pp-fg-label">每个时段容量</label><div style="display:flex;align-items:center;gap:6px;"><input type="number" class="pp-input" value="5" style="width:80px;"><span class="pp-unit"></span></div><div class="pp-fg-hint">每个时间窗口最大接单量</div></div>
<div class="pp-fg"><label class="pp-fg-label">每日开始时间</label><input type="time" class="pp-input" value="09:00" style="width:100%;"></div>
<div class="pp-fg"><label class="pp-fg-label">每日结束时间</label><input type="time" class="pp-input" value="20:30" style="width:100%;"></div>
<div class="pp-fg"><label class="pp-fg-label">最少提前预约</label><div style="display:flex;align-items:center;gap:6px;"><input type="number" class="pp-input" value="2" style="width:80px;"><span class="pp-unit">小时</span></div><div class="pp-fg-hint">下单时间距取餐时间的最小间隔</div></div>
</div>
<div class="pp-fg" style="margin-top:4px;"><label class="pp-fg-label">适用星期</label>
<div class="pp-day-pills" id="fineDayPills">
<button class="pp-day-pill selected" onclick="this.classList.toggle('selected')">周一</button>
<button class="pp-day-pill selected" onclick="this.classList.toggle('selected')">周二</button>
<button class="pp-day-pill selected" onclick="this.classList.toggle('selected')">周三</button>
<button class="pp-day-pill selected" onclick="this.classList.toggle('selected')">周四</button>
<button class="pp-day-pill selected" onclick="this.classList.toggle('selected')">周五</button>
<button class="pp-day-pill selected" onclick="this.classList.toggle('selected')">周六</button>
<button class="pp-day-pill selected" onclick="this.classList.toggle('selected')">周日</button>
</div>
<div class="pp-qk-btns"><button class="pp-qk-btn" onclick="ppQuickDays('all','fineDayPills')">全选</button><button class="pp-qk-btn" onclick="ppQuickDays('weekday','fineDayPills')">工作日</button><button class="pp-qk-btn" onclick="ppQuickDays('weekend','fineDayPills')">周末</button></div>
</div>
<div class="pp-form-actions"><button class="g-btn">重置</button><button class="g-btn g-btn-primary"><i data-lucide="save" style="width:14px;height:14px;"></i>保存规则</button></div>
</div>
</div>
<div class="pp-card">
<div class="pp-card-header">时段预览<span style="font-size:12px;font-weight:400;color:#999;">根据规则自动生成,以下为预览效果</span></div>
<div class="pp-card-body">
<div class="pp-day-tabs">
<button class="pp-day-tab active" onclick="switchPreviewDay(this)"><span class="dt-date">2/11</span> <span class="dt-sub">周三 今天</span></button>
<button class="pp-day-tab" onclick="switchPreviewDay(this)"><span class="dt-date">2/12</span> <span class="dt-sub">周四 明天</span></button>
<button class="pp-day-tab" onclick="switchPreviewDay(this)"><span class="dt-date">2/13</span> <span class="dt-sub">周五 后天</span></button>
</div>
<div class="pp-slot-grid"><div class="pp-slot-cell expired"><div class="sc-time">09:00</div><div class="sc-status">已过期</div></div><div class="pp-slot-cell expired"><div class="sc-time">09:30</div><div class="sc-status">已过期</div></div><div class="pp-slot-cell expired"><div class="sc-time">10:00</div><div class="sc-status">已过期</div></div><div class="pp-slot-cell expired"><div class="sc-time">10:30</div><div class="sc-status">已过期</div></div><div class="pp-slot-cell available"><div class="sc-time">11:00</div><div class="sc-status">剩余 3</div></div><div class="pp-slot-cell available"><div class="sc-time">11:30</div><div class="sc-status">剩余 5</div></div><div class="pp-slot-cell almost"><div class="sc-time">12:00</div><div class="sc-status">剩余 1</div></div><div class="pp-slot-cell full"><div class="sc-time">12:30</div><div class="sc-status">已满</div></div><div class="pp-slot-cell full"><div class="sc-time">13:00</div><div class="sc-status">已满</div></div><div class="pp-slot-cell available"><div class="sc-time">13:30</div><div class="sc-status">剩余 2</div></div><div class="pp-slot-cell available"><div class="sc-time">14:00</div><div class="sc-status">剩余 5</div></div><div class="pp-slot-cell available"><div class="sc-time">14:30</div><div class="sc-status">剩余 5</div></div><div class="pp-slot-cell available"><div class="sc-time">15:00</div><div class="sc-status">剩余 4</div></div><div class="pp-slot-cell available"><div class="sc-time">15:30</div><div class="sc-status">剩余 5</div></div><div class="pp-slot-cell available"><div class="sc-time">16:00</div><div class="sc-status">剩余 5</div></div><div class="pp-slot-cell available"><div class="sc-time">16:30</div><div class="sc-status">剩余 5</div></div><div class="pp-slot-cell available"><div class="sc-time">17:00</div><div class="sc-status">剩余 3</div></div><div class="pp-slot-cell available"><div class="sc-time">17:30</div><div class="sc-status">剩余 5</div></div><div class="pp-slot-cell almost"><div class="sc-time">18:00</div><div class="sc-status">剩余 1</div></div><div class="pp-slot-cell available"><div class="sc-time">18:30</div><div class="sc-status">剩余 5</div></div><div class="pp-slot-cell available"><div class="sc-time">19:00</div><div class="sc-status">剩余 5</div></div><div class="pp-slot-cell available"><div class="sc-time">19:30</div><div class="sc-status">剩余 4</div></div><div class="pp-slot-cell available"><div class="sc-time">20:00</div><div class="sc-status">剩余 5</div></div></div>
<div class="pp-legend">
<span><span class="pp-legend-dot" style="background:#d9d9d9;"></span>已过期</span>
<span><span class="pp-legend-dot" style="background:#b7eb8f;"></span>可预约</span>
<span><span class="pp-legend-dot" style="background:#ffd591;"></span>即将满</span>
<span><span class="pp-legend-dot" style="background:#ffa39e;"></span>已满</span>
</div>
</div>
</div>
</div>
</div>
<div class="g-drawer-mask" id="ppDrawerMask" onclick="closePickupDrawer()"></div>
<div class="g-drawer" id="ppSlotDrawer" style="width:480px;">
<div class="g-drawer-hd"><span class="g-drawer-title" id="ppDrawerTitle">添加时段</span><button class="g-drawer-close" onclick="closePickupDrawer()"><i data-lucide="x" style="width:18px;height:18px;"></i></button></div>
<div class="g-drawer-bd">
<div class="pp-fg"><label class="pp-fg-label">时段名称</label><input type="text" class="pp-fg-input" id="ppName" placeholder="如:上午时段" style="width:100%;" /></div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:12px;">
<div class="pp-fg"><label class="pp-fg-label">开始时间</label><input type="time" class="pp-fg-input" id="ppStart" value="09:00" style="width:100%;" /></div>
<div class="pp-fg"><label class="pp-fg-label">结束时间</label><input type="time" class="pp-fg-input" id="ppEnd" value="17:00" style="width:100%;" /></div>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:12px;">
<div class="pp-fg"><label class="pp-fg-label">截止时间</label><div style="display:flex;align-items:center;gap:6px;"><input type="number" class="pp-fg-input" id="ppCutoff" value="30" style="width:80px;"><span class="pp-unit">分钟</span></div><div class="pp-fg-hint">时段开始前停止接单</div></div>
<div class="pp-fg"><label class="pp-fg-label">容量上限</label><div style="display:flex;align-items:center;gap:6px;"><input type="number" class="pp-fg-input" id="ppCap" value="20" style="width:80px;"><span class="pp-unit"></span></div><div class="pp-fg-hint">该时段最大接单数</div></div>
</div>
<div class="pp-fg"><label class="pp-fg-label">适用星期</label>
<div class="pp-day-pills" id="ppDayPills">
<button class="pp-day-pill selected" onclick="this.classList.toggle('selected')">周一</button>
<button class="pp-day-pill selected" onclick="this.classList.toggle('selected')">周二</button>
<button class="pp-day-pill selected" onclick="this.classList.toggle('selected')">周三</button>
<button class="pp-day-pill selected" onclick="this.classList.toggle('selected')">周四</button>
<button class="pp-day-pill selected" onclick="this.classList.toggle('selected')">周五</button>
<button class="pp-day-pill" onclick="this.classList.toggle('selected')">周六</button>
<button class="pp-day-pill" onclick="this.classList.toggle('selected')">周日</button>
</div>
<div class="pp-qk-btns"><button class="pp-qk-btn" onclick="ppQuickDays('all','ppDayPills')">全选</button><button class="pp-qk-btn" onclick="ppQuickDays('weekday','ppDayPills')">工作日</button><button class="pp-qk-btn" onclick="ppQuickDays('weekend','ppDayPills')">周末</button></div>
</div>
<div class="pp-fg"><label class="pp-fg-label">启用状态</label><label class="g-toggle-input"><input type="checkbox" id="ppEnabled" checked><span class="g-toggle-sl"></span></label></div>
</div>
<div class="g-drawer-ft"><button class="g-btn" onclick="closePickupDrawer()">取消</button><button class="g-btn g-btn-primary" id="ppSubmitBtn" onclick="closePickupDrawer()">确认添加</button></div>
</div>
<script>
function switchPickupMode(mode) {
document.querySelectorAll('.pp-mode-item').forEach(function(b){ b.classList.remove('active'); });
event.currentTarget.classList.add('active');
document.getElementById('bigSlotSection').style.display = mode==='big' ? '' : 'none';
document.getElementById('fineSlotSection').style.display = mode==='fine' ? '' : 'none';
}
function openPickupDrawer(mode, data) {
document.getElementById('ppDrawerMask').classList.add('open');
document.getElementById('ppSlotDrawer').classList.add('open');
var weekdays=['周一','周二','周三','周四','周五'], weekends=['周六','周日'];
if (mode==='edit' && data) {
document.getElementById('ppDrawerTitle').textContent='编辑时段 - '+data.name;
document.getElementById('ppSubmitBtn').textContent='保存修改';
document.getElementById('ppName').value=data.name||'';
document.getElementById('ppStart').value=data.start||'';
document.getElementById('ppEnd').value=data.end||'';
document.getElementById('ppCutoff').value=data.cutoff||30;
document.getElementById('ppCap').value=data.cap||20;
document.querySelectorAll('#ppDayPills .pp-day-pill').forEach(function(p){
var d=p.textContent;
if(data.days==='all') p.classList.add('selected');
else if(data.days==='weekday') p.classList.toggle('selected',weekdays.includes(d));
else if(data.days==='weekend') p.classList.toggle('selected',weekends.includes(d));
});
} else {
document.getElementById('ppDrawerTitle').textContent='添加时段';
document.getElementById('ppSubmitBtn').textContent='确认添加';
document.getElementById('ppName').value='';
document.getElementById('ppStart').value='09:00';
document.getElementById('ppEnd').value='17:00';
document.getElementById('ppCutoff').value=30;
document.getElementById('ppCap').value=20;
ppQuickDays('weekday','ppDayPills');
}
if(typeof lucide!=='undefined') lucide.createIcons();
}
function closePickupDrawer() {
document.getElementById('ppDrawerMask').classList.remove('open');
document.getElementById('ppSlotDrawer').classList.remove('open');
}
function ppQuickDays(mode, containerId) {
var pills=document.querySelectorAll('#'+containerId+' .pp-day-pill');
var weekdays=['周一','周二','周三','周四','周五'], weekends=['周六','周日'];
pills.forEach(function(p){
var d=p.textContent;
if(mode==='all') p.classList.add('selected');
else if(mode==='weekday') p.classList.toggle('selected',weekdays.includes(d));
else if(mode==='weekend') p.classList.toggle('selected',weekends.includes(d));
});
}
function switchPreviewDay(el) {
document.querySelectorAll('.pp-day-tab').forEach(function(b){ b.classList.remove('active'); });
el.classList.add('active');
}
</script>

View File

@@ -0,0 +1,187 @@
<!-- 资质证照页 -->
<style>
.pq-top-bar{display:flex;align-items:center;gap:12px;margin-bottom:16px;flex-wrap:wrap;box-shadow:var(--g-shadow-sm);border-radius:10px;padding:12px 16px;background:#fff;}
.pq-top-bar select{height:34px;padding:0 10px;border:1px solid #e5e7eb;border-radius:8px;font-size:13px;outline:none;background:#fff;min-width:180px;cursor:pointer;transition:var(--g-transition);}
.pq-top-bar select:focus{border-color:var(--primary);box-shadow:0 0 0 3px color-mix(in srgb, var(--primary) 12%, transparent);}
.pq-card-hd{display:flex;align-items:center;justify-content:space-between;margin-bottom:16px;}
.pq-card-title{font-size:15px;font-weight:600;color:#1a1a2e;}
.pq-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;}
.pq-item{border:none;border-radius:10px;overflow:hidden;box-shadow:var(--g-shadow-sm);transition:var(--g-transition);}
.pq-item:hover{box-shadow:0 4px 16px rgba(0,0,0,0.10);}
.pq-thumb{height:160px;background:#f8f9fb;display:flex;align-items:center;justify-content:center;border-bottom:1px dashed #e5e7eb;}
.pq-thumb svg{width:48px;height:48px;color:#bbb;}
.pq-body{padding:12px 14px 14px;}
.pq-type-tag{display:inline-block;padding:2px 10px;border-radius:6px;font-size:12px;margin-bottom:8px;font-weight:600;}
.pq-tag-blue{background:#e6f7ff;color:#1890ff;}
.pq-tag-green{background:#f6ffed;color:#22c55e;}
.pq-tag-purple{background:#f9f0ff;color:#722ed1;}
.pq-tag-orange{background:#fff7e6;color:#f59e0b;}
.pq-tag-teal{background:#e6fffb;color:#13c2c2;}
.pq-tag-grey{background:#f8f9fb;color:#9ca3af;}
.pq-doc-no{font-size:13px;color:#1a1a2e;margin-bottom:6px;word-break:break-all;}
.pq-dates{font-size:12px;color:#9ca3af;margin-bottom:8px;}
.pq-status{display:inline-flex;align-items:center;gap:5px;font-size:12px;margin-bottom:10px;font-weight:600;}
.pq-status-dot{width:6px;height:6px;border-radius:50%;flex-shrink:0;}
.pq-status-green .pq-status-dot{background:#22c55e;} .pq-status-green{color:#22c55e;}
.pq-status-orange .pq-status-dot{background:#f59e0b;} .pq-status-orange{color:#f59e0b;}
.pq-status-red .pq-status-dot{background:#ef4444;} .pq-status-red{color:#ef4444;}
.pq-actions{display:flex;gap:12px;border-top:1px solid #f3f4f6;padding-top:10px;}
.pq-remind-row{display:flex;align-items:center;gap:12px;margin-bottom:14px;font-size:13px;color:#1a1a2e;}
.pq-remind-row label{min-width:100px;text-align:right;font-weight:500;color:#4b5563;}
.pq-remind-row input[type=number]{width:80px;height:34px;padding:0 10px;border:1px solid #e5e7eb;border-radius:8px;font-size:13px;outline:none;text-align:center;transition:var(--g-transition);}
.pq-remind-row input[type=number]:focus{border-color:var(--primary);box-shadow:0 0 0 3px color-mix(in srgb, var(--primary) 12%, transparent);}
.pq-remind-row .unit{color:#9ca3af;font-size:12px;}
.pq-checks{display:flex;gap:18px;}
.pq-checks label{display:flex;align-items:center;gap:5px;font-size:13px;color:#1a1a2e;cursor:pointer;}
.pq-checks input[type=checkbox]{accent-color:var(--primary);cursor:pointer;}
.pq-item.pq-expired { border-left:3px solid #ef4444; background:#fff1f0; }
.pq-item.pq-expiring { border-left:3px solid #f59e0b; background:#fffbeb; }
@media(max-width:900px){.pq-grid{grid-template-columns:repeat(2,1fr);}}
@media(max-width:600px){.pq-grid{grid-template-columns:1fr;}}
</style>
<div class="pq-top-bar">
<select>
<option>老三家外卖(朝阳店)</option>
<option>老三家外卖(海淀店)</option>
<option>老三家外卖(丰台店)</option>
<option>老三家外卖(西城店)</option>
<option>老三家外卖(东城店)</option>
</select>
</div>
<div class="g-card" style="margin-bottom:16px;">
<div class="pq-card-hd">
<span class="pq-card-title">资质证照</span>
<button class="g-btn g-btn-primary" onclick="openSqDrawer()">+ 上传证照</button>
</div>
<div class="pq-grid">
<div class="pq-item">
<div class="pq-thumb"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/></svg></div>
<div class="pq-body">
<span class="pq-type-tag pq-tag-blue">营业执照</span>
<div class="pq-doc-no">No.91110105MA01XXXX</div>
<div class="pq-dates">发证日期 2024-01-15 有效期至 2027-01-14</div>
<div class="pq-status pq-status-green"><span class="pq-status-dot"></span>有效</div>
<div class="pq-actions"><a class="g-action" href="javascript:;">编辑</a><a class="g-action g-action-danger" href="javascript:;">删除</a></div>
</div>
</div>
<div class="pq-item">
<div class="pq-thumb"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/></svg></div>
<div class="pq-body">
<span class="pq-type-tag pq-tag-green">食品经营许可证</span>
<div class="pq-doc-no">No.JY11105XXXXXXX</div>
<div class="pq-dates">发证日期 2024-03-20 有效期至 2027-03-19</div>
<div class="pq-status pq-status-green"><span class="pq-status-dot"></span>有效</div>
<div class="pq-actions"><a class="g-action" href="javascript:;">编辑</a><a class="g-action g-action-danger" href="javascript:;">删除</a></div>
</div>
</div>
<div class="pq-item pq-expiring">
<div class="pq-thumb"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/></svg></div>
<div class="pq-body">
<span class="pq-type-tag pq-tag-purple">食品安全管理员证</span>
<div class="pq-doc-no">No.AQGL2024XXXX</div>
<div class="pq-dates">发证日期 2024-06-01 有效期至 2025-05-31</div>
<div class="pq-status pq-status-orange"><span class="pq-status-dot"></span>即将过期</div>
<div class="pq-actions"><a class="g-action" href="javascript:;">编辑</a><a class="g-action g-action-danger" href="javascript:;">删除</a></div>
</div>
</div>
<div class="pq-item pq-expired">
<div class="pq-thumb"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/></svg></div>
<div class="pq-body">
<span class="pq-type-tag pq-tag-orange">消防安全检查合格证</span>
<div class="pq-doc-no">No.XF2024XXXXX</div>
<div class="pq-dates">发证日期 2024-02-10 有效期至 2026-02-09</div>
<div class="pq-status pq-status-red"><span class="pq-status-dot"></span>已过期</div>
<div class="pq-actions"><a class="g-action" href="javascript:;">编辑</a><a class="g-action g-action-danger" href="javascript:;">删除</a></div>
</div>
</div>
<div class="pq-item">
<div class="pq-thumb"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/></svg></div>
<div class="pq-body">
<span class="pq-type-tag pq-tag-teal">健康证(张伟)</span>
<div class="pq-doc-no">No.JK2024XXXXX</div>
<div class="pq-dates">发证日期 2024-08-15 有效期至 2025-08-14</div>
<div class="pq-status pq-status-green"><span class="pq-status-dot"></span>有效</div>
<div class="pq-actions"><a class="g-action" href="javascript:;">编辑</a><a class="g-action g-action-danger" href="javascript:;">删除</a></div>
</div>
</div>
<div class="pq-item">
<div class="pq-thumb"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/></svg></div>
<div class="pq-body">
<span class="pq-type-tag pq-tag-grey">排污许可证</span>
<div class="pq-doc-no">No.PW2024XXXXX</div>
<div class="pq-dates">发证日期 2025-01-01 有效期至 2028-12-31</div>
<div class="pq-status pq-status-green"><span class="pq-status-dot"></span>有效</div>
<div class="pq-actions"><a class="g-action" href="javascript:;">编辑</a><a class="g-action g-action-danger" href="javascript:;">删除</a></div>
</div>
</div>
</div>
</div>
<div class="g-card">
<div class="pq-card-hd"><span class="pq-card-title">到期提醒设置</span></div>
<div class="pq-remind-row"><label>提前提醒天数</label><input type="number" value="30"><span class="unit"></span></div>
<div class="pq-remind-row"><label>提醒方式</label><div class="pq-checks"><label><input type="checkbox" checked>站内通知</label><label><input type="checkbox">短信通知</label><label><input type="checkbox">邮件通知</label></div></div>
<div class="pq-remind-row"><label></label><button class="g-btn g-btn-primary">保存设置</button></div>
</div>
<!-- 上传证照抽屉 -->
<div class="g-drawer-mask" id="sqDrawerMask" onclick="closeSqDrawer()"></div>
<div class="g-drawer" id="sqDrawer" style="width:480px">
<div class="g-drawer-hd">
<div class="g-drawer-title">上传证照</div>
<button class="g-drawer-close" onclick="closeSqDrawer()"><i data-lucide="x" style="width:18px;height:18px;"></i></button>
</div>
<div class="g-drawer-bd">
<div class="g-form-group">
<label class="g-form-label required">证照类型</label>
<select class="g-select">
<option value="">请选择证照类型</option>
<option>营业执照</option>
<option>食品经营许可证</option>
<option>卫生许可证</option>
<option>消防安全检查合格证</option>
<option>食品安全管理员证</option>
<option>健康证</option>
<option>排污许可证</option>
<option>其他</option>
</select>
</div>
<div class="g-form-group">
<label class="g-form-label required">证照编号</label>
<input class="g-input" placeholder="请输入证照编号">
</div>
<div class="g-form-group">
<label class="g-form-label required">有效期至</label>
<input type="date" class="g-input">
</div>
<div class="g-form-group">
<label class="g-form-label required">上传证照图片</label>
<div class="g-upload-zone">
<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>
<p>点击或拖拽上传证照图片</p>
<p style="font-size:11px;color:#999;margin-top:4px">支持 JPG、PNG 格式,不超过 5MB</p>
</div>
</div>
<div class="g-form-group">
<label class="g-form-label">备注</label>
<textarea class="g-textarea" rows="3" placeholder="如有特殊说明请填写"></textarea>
</div>
</div>
<div class="g-drawer-ft">
<button class="g-btn" onclick="closeSqDrawer()">取消</button>
<button class="g-btn g-btn-primary" onclick="closeSqDrawer()">确认上传</button>
</div>
</div>
<script>
function openSqDrawer() {
document.getElementById('sqDrawerMask').classList.add('open');
document.getElementById('sqDrawer').classList.add('open');
}
function closeSqDrawer() {
document.getElementById('sqDrawerMask').classList.remove('open');
document.getElementById('sqDrawer').classList.remove('open');
}
</script>

406
pages/store-staff.html Normal file
View File

@@ -0,0 +1,406 @@
<!-- 员工排班页 -->
<style>
.ps-toolbar { display:flex; align-items:center; gap:12px; margin-bottom:16px; box-shadow:var(--g-shadow-sm); border-radius:10px; padding:12px 16px; background:#fff; }
.ps-filters { display:flex; gap:12px; margin-bottom:16px; flex-wrap:wrap; }
.ps-staff-info { display:flex; align-items:center; gap:10px; }
.ps-avatar { width:36px; height:36px; border-radius:50%; display:flex; align-items:center; justify-content:center; color:#fff; font-size:14px; font-weight:500; flex-shrink:0; }
.ps-staff-name { font-weight:500; color:#1a1a2e; }
.ps-staff-phone { color:#4b5563; font-size:12px; margin-top:2px; }
.ps-status { display:flex; align-items:center; gap:6px; }
.ps-dot { width:6px; height:6px; border-radius:50%; }
.ps-dot-green { background:#22c55e; }
.ps-dot-orange { background:#f59e0b; }
.ps-dot-grey { background:#9ca3af; }
.ps-perm-pill { display:inline-block; padding:1px 6px; background:#f0f5ff; color:#2f54eb; border-radius:6px; font-size:11px; margin:1px 2px; font-weight:600; }
.ps-actions a { color:var(--primary); text-decoration:none; margin-right:12px; font-size:13px; cursor:pointer; transition:var(--g-transition); }
.ps-actions a:hover { text-decoration:underline; }
.ps-actions a.ps-danger { color:#ef4444; }
.ps-pagination { display:flex; justify-content:flex-end; align-items:center; margin-top:16px; gap:4px; }
.ps-page-btn { min-width:32px; height:34px; border:1px solid #e5e7eb; border-radius:8px; background:#fff; cursor:pointer; display:flex; align-items:center; justify-content:center; font-size:13px; color:#1a1a2e; transition:var(--g-transition); }
.ps-page-btn.active { border-color:var(--primary); color:var(--primary); }
.ps-page-btn:hover { border-color:var(--primary); color:var(--primary); }
.ps-page-info { color:#4b5563; font-size:13px; margin-right:12px; }
.ps-schedule { width:100%; border-collapse:collapse; }
.ps-schedule th,.ps-schedule td { padding:10px 8px; text-align:center; border-bottom:1px solid #f3f4f6; font-size:13px; }
.ps-schedule th { background:#f8f9fb; font-weight:600; color:#6b7280; }
.ps-schedule td.ps-shift-morning { background:#e6f7ff; color:#096dd9; }
.ps-schedule td.ps-shift-evening { background:#fff7e6; color:#d46b08; }
.ps-schedule td.ps-shift-full { background:#f6ffed; color:#389e0d; }
.ps-schedule td.ps-shift-off { background:#f8f9fb; color:#9ca3af; }
.ps-schedule .ps-sched-name { text-align:left; font-weight:500; padding-left:12px; white-space:nowrap; color:#1a1a2e; }
.ps-schedule .ps-sched-role { font-size:11px; color:#4b5563; font-weight:normal; }
.ps-shift-label { font-size:11px; display:block; color:inherit; opacity:0.7; }
.ps-perm-grid { display:grid; grid-template-columns:1fr 1fr; gap:8px; }
.ps-perm-check { display:flex; align-items:center; gap:6px; font-size:13px; color:#1a1a2e; cursor:pointer; padding:6px 10px; border:1px solid #e5e7eb; border-radius:8px; transition:var(--g-transition); }
.ps-perm-check:hover { border-color:var(--primary); }
.ps-perm-check input { accent-color:var(--primary); cursor:pointer; }
.ps-perm-check.all { grid-column:1/-1; background:#f0f5ff; border-color:#d6e4ff; }
/* Schedule drawer */
.ps-sched-row { display:flex; align-items:center; gap:10px; padding:12px 0; border-bottom:1px solid #f3f4f6; }
.ps-sched-row:last-child { border-bottom:none; }
.ps-sched-day { width:40px; font-weight:600; color:#1a1a2e; flex-shrink:0; }
.ps-shift-pills { display:flex; gap:6px; flex-shrink:0; }
.ps-shift-pill { padding:4px 10px; border-radius:6px; font-size:12px; cursor:pointer; border:1px solid #e5e7eb; background:#fff; color:#4b5563; transition:var(--g-transition); }
.ps-shift-pill:hover { border-color:var(--primary); color:var(--primary); }
.ps-shift-pill.active { color:#fff; border-color:transparent; }
.ps-shift-pill.s-morning.active { background:#1890ff; }
.ps-shift-pill.s-evening.active { background:#fa8c16; }
.ps-shift-pill.s-full.active { background:#22c55e; }
.ps-shift-pill.s-off.active { background:#9ca3af; }
.ps-sched-times { display:flex; align-items:center; gap:4px; font-size:12px; color:#4b5563; }
.ps-sched-times input[type=time] { height:34px; border:1px solid #e5e7eb; border-radius:8px; font-size:12px; padding:0 4px; outline:none; transition:var(--g-transition); }
.ps-sched-times input[type=time]:focus { border-color:var(--primary); box-shadow:0 0 0 3px color-mix(in srgb, var(--primary) 12%, transparent); }
/* Week editor */
.ps-week-table { width:100%; border-collapse:collapse; }
.ps-week-table th { background:#f8f9fb; padding:8px 6px; text-align:center; font-weight:600; color:#6b7280; border:1px solid #f3f4f6; font-size:12px; }
.ps-week-table td { border:1px solid #f3f4f6; padding:0; text-align:center; height:52px; }
.ps-week-table .ps-wk-name { text-align:left; padding:8px 10px; font-weight:500; font-size:13px; white-space:nowrap; background:#fff; color:#1a1a2e; }
.ps-wk-cell { width:100%; height:100%; display:flex; flex-direction:column; align-items:center; justify-content:center; cursor:pointer; transition:var(--g-transition); user-select:none; font-size:12px; gap:2px; }
.ps-wk-cell:hover { filter:brightness(0.95); }
.ps-wk-cell .wk-time { font-size:11px; opacity:0.8; }
.ps-wk-cell[data-shift=morning] { background:#e6f7ff; color:#096dd9; }
.ps-wk-cell[data-shift=evening] { background:#fff7e6; color:#d46b08; }
.ps-wk-cell[data-shift=full] { background:#f6ffed; color:#389e0d; }
.ps-wk-cell[data-shift=off] { background:#f8f9fb; color:#9ca3af; }
.ps-week-legend { display:flex; gap:16px; margin-bottom:14px; font-size:12px; color:#4b5563; }
.ps-week-legend span { display:flex; align-items:center; gap:4px; }
.ps-week-legend i { width:12px; height:12px; border-radius:2px; display:inline-block; }
.ps-week-legend .lg-m { background:#e6f7ff; border:1px solid #91d5ff; }
.ps-week-legend .lg-e { background:#fff7e6; border:1px solid #ffd591; }
.ps-week-legend .lg-f { background:#f6ffed; border:1px solid #b7eb8f; }
.ps-week-legend .lg-o { background:#f8f9fb; border:1px solid #e5e7eb; }
/* Shift template */
.ps-tpl-row { display:flex; align-items:center; gap:14px; padding:10px 0; border-bottom:1px solid #f3f4f6; }
.ps-tpl-row:last-child { border-bottom:none; }
.ps-tpl-dot { width:10px; height:10px; border-radius:2px; flex-shrink:0; }
.ps-tpl-dot.m { background:#1890ff; }
.ps-tpl-dot.e { background:#fa8c16; }
.ps-tpl-dot.f { background:#22c55e; }
.ps-tpl-name { width:50px; font-weight:500; font-size:13px; color:#1a1a2e; }
.ps-tpl-times { display:flex; align-items:center; gap:6px; }
.ps-tpl-times input[type=time] { height:34px; border:1px solid #e5e7eb; border-radius:8px; font-size:13px; padding:0 8px; outline:none; width:110px; transition:var(--g-transition); }
.ps-tpl-times input[type=time]:focus { border-color:var(--primary); box-shadow:0 0 0 3px color-mix(in srgb, var(--primary) 12%, transparent); }
.ps-tpl-times .sep { color:#9ca3af; }
</style>
<div class="ps-page">
<div class="ps-toolbar">
<select class="g-select">
<option>老三家外卖(总店)</option>
<option>老三家外卖(朝阳店)</option>
<option>老三家外卖(海淀店)</option>
<option>老三家外卖(丰台店)</option>
<option>老三家外卖(通州店)</option>
</select>
<button class="g-btn" onclick="openCopyStoreModal('复制员工排班到其他门店')">复制到其他门店</button>
</div>
<div class="g-card">
<div class="g-card-hd"><span class="g-card-title">员工列表</span><button class="g-btn g-btn-primary" onclick="openStaffDrawer()">+ 添加员工</button></div>
<div class="ps-filters">
<input class="g-input" placeholder="搜索姓名/手机号" />
<select class="g-select"><option>全部角色</option><option>店长</option><option>收银员</option><option>配送员</option><option>厨师</option></select>
<select class="g-select"><option>全部状态</option><option>在职</option><option>休假</option><option>离职</option></select>
</div>
<table class="g-table"><thead><tr><th>员工信息</th><th>角色</th><th>邮箱</th><th>状态</th><th>权限</th><th>入职时间</th><th>操作</th></tr></thead>
<tbody><tr>
<td><div class="ps-staff-info"><div class="ps-avatar" style="background:#f56a00"></div><div><div class="ps-staff-name">张伟</div><div class="ps-staff-phone">138****8001</div></div></div></td>
<td><span class="g-tag g-tag-red">店长</span></td>
<td>zhangwei@example.com</td>
<td><span class="ps-status"><span class="ps-dot ps-dot-green"></span>在职</span></td>
<td><span class='ps-perm-pill'>全部权限</span></td>
<td>2024-01-15</td>
<td class="ps-actions"><a onclick="openStaffDrawer('edit','张伟')">编辑</a><a onclick="openSchedDrawer('张伟','店长')">排班</a><a class="ps-danger">删除</a></td>
</tr>
<tr>
<td><div class="ps-staff-info"><div class="ps-avatar" style="background:#7265e6"></div><div><div class="ps-staff-name">李娜</div><div class="ps-staff-phone">138****8002</div></div></div></td>
<td><span class="g-tag g-tag-blue">收银员</span></td>
<td>lina@example.com</td>
<td><span class="ps-status"><span class="ps-dot ps-dot-green"></span>在职</span></td>
<td><span class='ps-perm-pill'>收银</span><span class='ps-perm-pill'>退款</span></td>
<td>2024-03-20</td>
<td class="ps-actions"><a onclick="openStaffDrawer('edit','李娜')">编辑</a><a onclick="openSchedDrawer('李娜','收银员')">排班</a><a class="ps-danger">删除</a></td>
</tr>
<tr>
<td><div class="ps-staff-info"><div class="ps-avatar" style="background:#52c41a"></div><div><div class="ps-staff-name">王磊</div><div class="ps-staff-phone">138****8003</div></div></div></td>
<td><span class="g-tag g-tag-green">配送员</span></td>
<td><span style='color:#999'>-</span></td>
<td><span class="ps-status"><span class="ps-dot ps-dot-green"></span>在职</span></td>
<td><span class='ps-perm-pill'>配送管理</span></td>
<td>2024-06-01</td>
<td class="ps-actions"><a onclick="openStaffDrawer('edit','王磊')">编辑</a><a onclick="openSchedDrawer('王磊','配送员')">排班</a><a class="ps-danger">删除</a></td>
</tr>
<tr>
<td><div class="ps-staff-info"><div class="ps-avatar" style="background:#fa8c16"></div><div><div class="ps-staff-name">赵敏</div><div class="ps-staff-phone">138****8004</div></div></div></td>
<td><span class="g-tag g-tag-orange">厨师</span></td>
<td><span style='color:#999'>-</span></td>
<td><span class="ps-status"><span class="ps-dot ps-dot-green"></span>在职</span></td>
<td><span class='ps-perm-pill'>订单查看</span></td>
<td>2024-08-10</td>
<td class="ps-actions"><a onclick="openStaffDrawer('edit','赵敏')">编辑</a><a onclick="openSchedDrawer('赵敏','厨师')">排班</a><a class="ps-danger">删除</a></td>
</tr>
<tr>
<td><div class="ps-staff-info"><div class="ps-avatar" style="background:#1890ff"></div><div><div class="ps-staff-name">刘洋</div><div class="ps-staff-phone">138****8005</div></div></div></td>
<td><span class="g-tag g-tag-green">配送员</span></td>
<td>liuyang@example.com</td>
<td><span class="ps-status"><span class="ps-dot ps-dot-orange"></span>休假</span></td>
<td><span class='ps-perm-pill'>配送管理</span></td>
<td>2025-01-05</td>
<td class="ps-actions"><a onclick="openStaffDrawer('edit','刘洋')">编辑</a><a onclick="openSchedDrawer('刘洋','配送员')">排班</a><a class="ps-danger">删除</a></td>
</tr>
<tr style="opacity:0.5">
<td><div class="ps-staff-info"><div class="ps-avatar" style="background:#bbb"></div><div><div class="ps-staff-name">陈静</div><div class="ps-staff-phone">138****8006</div></div></div></td>
<td><span class="g-tag g-tag-blue">收银员</span></td>
<td><span style='color:#999'>-</span></td>
<td><span class="ps-status"><span class="ps-dot ps-dot-grey"></span>离职</span></td>
<td><span style='color:#999'>-</span></td>
<td>2024-11-20</td>
<td class="ps-actions"><a onclick="openStaffDrawer('edit','陈静')">编辑</a><a style="opacity:0.4;pointer-events:none;cursor:not-allowed">排班</a><a class="ps-danger">删除</a></td>
</tr>
</tbody></table>
<div class="ps-pagination"><span class="ps-page-info">共 6 条</span><button class="ps-page-btn">&lt;</button><button class="ps-page-btn active">1</button><button class="ps-page-btn">&gt;</button></div>
</div>
<div class="g-card">
<div class="g-card-hd"><span class="g-card-title">班次模板</span></div>
<div class="ps-tpl-row">
<span class="ps-tpl-dot m"></span><span class="ps-tpl-name">早班</span>
<div class="ps-tpl-times"><input type="time" id="psTplMs" value="09:00" onchange="syncShiftTpl()"><span class="sep">~</span><input type="time" id="psTplMe" value="14:00" onchange="syncShiftTpl()"></div>
</div>
<div class="ps-tpl-row">
<span class="ps-tpl-dot e"></span><span class="ps-tpl-name">晚班</span>
<div class="ps-tpl-times"><input type="time" id="psTplEs" value="14:00" onchange="syncShiftTpl()"><span class="sep">~</span><input type="time" id="psTplEe" value="21:00" onchange="syncShiftTpl()"></div>
</div>
<div class="ps-tpl-row">
<span class="ps-tpl-dot f"></span><span class="ps-tpl-name">全天</span>
<div class="ps-tpl-times"><input type="time" id="psTplFs" value="09:00" onchange="syncShiftTpl()"><span class="sep">~</span><input type="time" id="psTplFe" value="21:00" onchange="syncShiftTpl()"></div>
</div>
</div>
<div class="g-card"><button class="g-btn" onclick="openWeekEditor()">编辑排班</button></div>
<table class="ps-schedule"><thead><tr><th style="text-align:left;padding-left:12px">员工</th><th>周一</th><th>周二</th><th>周三</th><th>周四</th><th>周五</th><th>周六</th><th>周日</th></tr></thead>
<tbody><tr><td class="ps-sched-name">张伟 <span class="ps-sched-role">店长</span></td><td class="ps-shift-full">09:00-21:00<span class="ps-shift-label">全天</span></td><td class="ps-shift-full">09:00-21:00<span class="ps-shift-label">全天</span></td><td class="ps-shift-full">09:00-21:00<span class="ps-shift-label">全天</span></td><td class="ps-shift-full">09:00-21:00<span class="ps-shift-label">全天</span></td><td class="ps-shift-full">09:00-21:00<span class="ps-shift-label">全天</span></td><td class="ps-shift-morning">09:00-14:00<span class="ps-shift-label">早班</span></td><td class="ps-shift-off"></td></tr>
<tr><td class="ps-sched-name">李娜 <span class="ps-sched-role">收银员</span></td><td class="ps-shift-morning">09:00-14:00<span class="ps-shift-label">早班</span></td><td class="ps-shift-morning">09:00-14:00<span class="ps-shift-label">早班</span></td><td class="ps-shift-off"></td><td class="ps-shift-morning">09:00-14:00<span class="ps-shift-label">早班</span></td><td class="ps-shift-evening">14:00-21:00<span class="ps-shift-label">晚班</span></td><td class="ps-shift-full">09:00-21:00<span class="ps-shift-label">全天</span></td><td class="ps-shift-full">09:00-21:00<span class="ps-shift-label">全天</span></td></tr>
<tr><td class="ps-sched-name">王磊 <span class="ps-sched-role">配送员</span></td><td class="ps-shift-morning">09:00-14:00<span class="ps-shift-label">早班</span></td><td class="ps-shift-evening">14:00-21:00<span class="ps-shift-label">晚班</span></td><td class="ps-shift-morning">09:00-14:00<span class="ps-shift-label">早班</span></td><td class="ps-shift-evening">14:00-21:00<span class="ps-shift-label">晚班</span></td><td class="ps-shift-morning">09:00-14:00<span class="ps-shift-label">早班</span></td><td class="ps-shift-evening">14:00-21:00<span class="ps-shift-label">晚班</span></td><td class="ps-shift-off"></td></tr>
<tr><td class="ps-sched-name">赵敏 <span class="ps-sched-role">厨师</span></td><td class="ps-shift-full">09:00-21:00<span class="ps-shift-label">全天</span></td><td class="ps-shift-full">09:00-21:00<span class="ps-shift-label">全天</span></td><td class="ps-shift-evening">14:00-21:00<span class="ps-shift-label">晚班</span></td><td class="ps-shift-off"></td><td class="ps-shift-full">09:00-21:00<span class="ps-shift-label">全天</span></td><td class="ps-shift-full">09:00-21:00<span class="ps-shift-label">全天</span></td><td class="ps-shift-morning">09:00-14:00<span class="ps-shift-label">早班</span></td></tr>
<tr><td class="ps-sched-name">刘洋 <span class="ps-sched-role">配送员</span></td><td class="ps-shift-off"></td><td class="ps-shift-off"></td><td class="ps-shift-off"></td><td class="ps-shift-off"></td><td class="ps-shift-off"></td><td class="ps-shift-off"></td><td class="ps-shift-off"></td></tr>
</tbody></table>
</div>
</div>
<div class="g-drawer-mask" id="psDrawerMask" onclick="closeStaffDrawer()"></div>
<!-- 添加/编辑员工 -->
<div class="g-drawer" id="psStaffDrawer">
<div class="g-drawer-hd"><span class="g-drawer-title" id="psStaffTitle">添加员工</span><button class="g-drawer-close" onclick="closeStaffDrawer()"><i data-lucide="x" style="width:18px;height:18px;"></i></button></div>
<div class="g-drawer-bd">
<div class="g-form-group"><label class="g-form-label required">姓名</label><input type="text" class="g-input" id="psStaffName" placeholder="请输入员工姓名" /></div>
<div class="g-form-group"><label class="g-form-label required">手机号</label><input type="text" class="g-input" id="psStaffPhone" placeholder="请输入手机号" /></div>
<div class="g-form-group"><label class="g-form-label">邮箱</label><input type="text" class="g-input" id="psStaffEmail" placeholder="可选" /><div class="g-hint">用于接收系统通知</div></div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:12px;">
<div class="g-form-group"><label class="g-form-label required">角色</label><select class="g-select" id="psStaffRole"><option>店长</option><option>收银员</option><option>配送员</option><option>厨师</option></select></div>
<div class="g-form-group"><label class="g-form-label">状态</label><select class="g-select" id="psStaffStatus"><option>在职</option><option>休假</option><option>离职</option></select></div>
</div>
<div class="g-form-group"><label class="g-form-label">权限</label>
<div class="ps-perm-grid">
<label class="ps-perm-check all"><input type="checkbox" id="psPermAll" onchange="toggleAllPerms(this)"> 全部权限</label>
<label class="ps-perm-check"><input type="checkbox" class="ps-perm-cb"> 收银</label>
<label class="ps-perm-check"><input type="checkbox" class="ps-perm-cb"> 退款</label>
<label class="ps-perm-check"><input type="checkbox" class="ps-perm-cb"> 配送管理</label>
<label class="ps-perm-check"><input type="checkbox" class="ps-perm-cb"> 订单查看</label>
<label class="ps-perm-check"><input type="checkbox" class="ps-perm-cb"> 库存管理</label>
<label class="ps-perm-check"><input type="checkbox" class="ps-perm-cb"> 数据统计</label>
</div>
</div>
</div>
<div class="g-drawer-ft"><button class="g-btn" onclick="closeStaffDrawer()">取消</button><button class="g-btn g-btn-primary" id="psStaffSubmit" onclick="closeStaffDrawer()">确认添加</button></div>
</div>
<!-- 编辑排班 (个人) -->
<div class="g-drawer" id="psSchedDrawer" style="width:560px;">
<div class="g-drawer-hd"><span class="g-drawer-title" id="psSchedTitle">编辑排班 - 张伟</span><button class="g-drawer-close" onclick="closeStaffDrawer()"><i data-lucide="x" style="width:18px;height:18px;"></i></button></div>
<div class="g-drawer-bd">
<div style="background:#f0f5ff;border-radius:6px;padding:10px 14px;margin-bottom:16px;font-size:12px;color:#597ef7;">点击班次类型自动填充时间,也可手动修改。选择"休息"则该日不排班。</div>
<div id="psSchedRows"></div>
</div>
<div class="g-drawer-ft"><button class="g-btn" onclick="closeStaffDrawer()">取消</button><button class="g-btn g-btn-primary" onclick="closeStaffDrawer()">保存排班</button></div>
</div>
<!-- 编辑排班表 (全员周视图) -->
<div class="g-drawer" id="psWeekDrawer" style="width:640px;">
<div class="g-drawer-hd"><span class="g-drawer-title">编辑本周排班表</span><button class="g-drawer-close" onclick="closeStaffDrawer()"><i data-lucide="x" style="width:18px;height:18px;"></i></button></div>
<div class="g-drawer-bd">
<div class="ps-week-legend" id="psWeekLegend">
<span><i class="lg-m"></i><span class="lg-m-t">早班 09:00-14:00</span></span>
<span><i class="lg-e"></i><span class="lg-e-t">晚班 14:00-21:00</span></span>
<span><i class="lg-f"></i><span class="lg-f-t">全天 09:00-21:00</span></span>
<span><i class="lg-o"></i>休息</span>
<span style="margin-left:auto;color:#999;font-size:11px;">点击单元格切换班次</span>
</div>
<table class="ps-week-table">
<thead><tr><th style="text-align:left;padding-left:10px;width:90px;">员工</th><th>周一</th><th>周二</th><th>周三</th><th>周四</th><th>周五</th><th>周六</th><th>周日</th></tr></thead>
<tbody id="psWeekBody"></tbody>
</table>
</div>
<div class="g-drawer-ft"><button class="g-btn" onclick="closeStaffDrawer()">取消</button><button class="g-btn g-btn-primary" onclick="closeStaffDrawer()">保存排班</button></div>
</div>
<script>
var _dayNames = ['周一','周二','周三','周四','周五','周六','周日'];
var _defaultShifts = {
morning: ['09:00','14:00'],
evening: ['14:00','21:00'],
full: ['09:00','21:00'],
off: ['','']
};
var _shiftCycle = ['morning','evening','full','off'];
var _shiftLabels = {morning:'早班',evening:'晚班',full:'全天',off:'休息'};
function getShiftTimes() {
return {
morning: (document.getElementById('psTplMs').value||'09:00').slice(0,5) + '-' + (document.getElementById('psTplMe').value||'14:00').slice(0,5),
evening: (document.getElementById('psTplEs').value||'14:00').slice(0,5) + '-' + (document.getElementById('psTplEe').value||'21:00').slice(0,5),
full: (document.getElementById('psTplFs').value||'09:00').slice(0,5) + '-' + (document.getElementById('psTplFe').value||'21:00').slice(0,5),
off: ''
};
}
function syncShiftTpl() {
_defaultShifts.morning = [document.getElementById('psTplMs').value, document.getElementById('psTplMe').value];
_defaultShifts.evening = [document.getElementById('psTplEs').value, document.getElementById('psTplEe').value];
_defaultShifts.full = [document.getElementById('psTplFs').value, document.getElementById('psTplFe').value];
var t = getShiftTimes();
var lg = document.getElementById('psWeekLegend');
if (lg) {
lg.querySelector('.lg-m-t').textContent = '早班 ' + t.morning;
lg.querySelector('.lg-e-t').textContent = '晚班 ' + t.evening;
lg.querySelector('.lg-f-t').textContent = '全天 ' + t.full;
}
}
function openStaffDrawer(mode, name) {
var t = document.getElementById('psStaffTitle');
var s = document.getElementById('psStaffSubmit');
if (mode === 'edit') {
t.textContent = '编辑员工 - ' + name;
s.textContent = '保存修改';
document.getElementById('psStaffName').value = name || '';
document.getElementById('psStaffPhone').value = '138****8001';
document.getElementById('psStaffEmail').value = '';
} else {
t.textContent = '添加员工';
s.textContent = '确认添加';
document.getElementById('psStaffName').value = '';
document.getElementById('psStaffPhone').value = '';
document.getElementById('psStaffEmail').value = '';
document.querySelectorAll('.ps-perm-cb').forEach(function(c){c.checked=false;});
document.getElementById('psPermAll').checked = false;
}
document.getElementById('psDrawerMask').classList.add('open');
document.getElementById('psStaffDrawer').classList.add('open');
}
function openSchedDrawer(name, role) {
document.getElementById('psSchedTitle').textContent = '编辑排班 - ' + name + ' (' + role + ')';
var container = document.getElementById('psSchedRows');
var html = '';
for (var i = 0; i < 7; i++) {
var d = _dayNames[i];
var def = (i < 5) ? 'full' : (i === 5 ? 'morning' : 'off');
var st = _defaultShifts[def][0];
var et = _defaultShifts[def][1];
var hide = def === 'off' ? 'style="visibility:hidden"' : '';
html += '<div class="ps-sched-row">';
html += '<span class="ps-sched-day">' + d + '</span>';
html += '<div class="ps-shift-pills">';
['morning','evening','full','off'].forEach(function(typ){
var lb = {morning:'早班',evening:'晚班',full:'全天',off:'休息'}[typ];
var ac = (typ === def) ? ' active' : '';
html += '<span class="ps-shift-pill s-' + typ + ac + '" onclick="pickShift(this,\'' + typ + '\',' + i + ')">' + lb + '</span>';
});
html += '</div>';
html += '<div class="ps-sched-times" id="psTime' + i + '" ' + hide + '>';
html += '<input type="time" value="' + st + '" style="width:100px" /> <span>~</span> <input type="time" value="' + et + '" style="width:100px" />';
html += '</div></div>';
}
container.innerHTML = html;
document.getElementById('psDrawerMask').classList.add('open');
document.getElementById('psSchedDrawer').classList.add('open');
}
function closeStaffDrawer() {
document.getElementById('psDrawerMask').classList.remove('open');
document.getElementById('psStaffDrawer').classList.remove('open');
document.getElementById('psSchedDrawer').classList.remove('open');
document.getElementById('psWeekDrawer').classList.remove('open');
}
function pickShift(el, typ, dayIdx) {
var pills = el.parentElement.querySelectorAll('.ps-shift-pill');
pills.forEach(function(p){ p.classList.remove('active'); });
el.classList.add('active');
var timeDiv = document.getElementById('psTime' + dayIdx);
if (typ === 'off') {
timeDiv.style.visibility = 'hidden';
} else {
timeDiv.style.visibility = 'visible';
var inputs = timeDiv.querySelectorAll('input[type=time]');
inputs[0].value = _defaultShifts[typ][0];
inputs[1].value = _defaultShifts[typ][1];
}
}
function toggleAllPerms(el) {
var checked = el.checked;
document.querySelectorAll('.ps-perm-cb').forEach(function(c){ c.checked = checked; });
}
var _weekData = [
{name:'张伟',role:'店长',shifts:['full','full','full','full','full','morning','off']},
{name:'李娜',role:'收银员',shifts:['morning','morning','off','morning','evening','full','full']},
{name:'王磊',role:'配送员',shifts:['morning','evening','morning','evening','morning','evening','off']},
{name:'赵敏',role:'厨师',shifts:['full','full','evening','off','full','full','morning']},
{name:'刘洋',role:'配送员',shifts:['off','off','off','off','off','off','off']}
];
function openWeekEditor() {
syncShiftTpl();
var t = getShiftTimes();
var body = document.getElementById('psWeekBody');
var html = '';
for (var r = 0; r < _weekData.length; r++) {
var s = _weekData[r];
html += '<tr><td class="ps-wk-name">' + s.name + '<br><span style="font-size:11px;color:#999;font-weight:normal">' + s.role + '</span></td>';
for (var d = 0; d < 7; d++) {
var sh = s.shifts[d];
var tm = t[sh];
html += '<td><div class="ps-wk-cell" data-shift="' + sh + '" data-r="' + r + '" data-d="' + d + '" onclick="cycleShift(this)">';
html += '<span>' + _shiftLabels[sh] + '</span>';
if (tm) html += '<span class="wk-time">' + tm + '</span>';
html += '</div></td>';
}
html += '</tr>';
}
body.innerHTML = html;
document.getElementById('psDrawerMask').classList.add('open');
document.getElementById('psWeekDrawer').classList.add('open');
}
function cycleShift(el) {
var cur = el.getAttribute('data-shift');
var r = parseInt(el.getAttribute('data-r'));
var d = parseInt(el.getAttribute('data-d'));
var idx = (_shiftCycle.indexOf(cur) + 1) % _shiftCycle.length;
var next = _shiftCycle[idx];
el.setAttribute('data-shift', next);
var t = getShiftTimes();
var tm = t[next];
el.innerHTML = '<span>' + _shiftLabels[next] + '</span>' + (tm ? '<span class="wk-time">' + tm + '</span>' : '');
_weekData[r].shifts[d] = next;
}
if (typeof lucide !== 'undefined') lucide.createIcons();
</script>

75
pages/sys-basic.html Normal file
View File

@@ -0,0 +1,75 @@
<style>
.sba-wrap{padding:24px;max-width:900px;}
.sba-section-hd{font-size:15px;font-weight:600;color:var(--g-text);padding-left:10px;border-left:3px solid var(--primary);margin-bottom:16px;}
.sba-grid{display:grid;grid-template-columns:1fr 1fr;gap:16px 28px;}
.sba-full{grid-column:1/-1;}
.sba-logo-zone{width:120px;height:120px;display:flex;align-items:center;justify-content:center;flex-direction:column;gap:6px;}
.sba-logo-zone i{width:28px;height:28px;color:var(--g-text-muted);}
.sba-logo-zone span{font-size:12px;color:var(--g-text-muted);}
.sba-footer{display:flex;justify-content:flex-end;gap:10px;padding:20px 24px 0;margin-top:8px;}
</style>
<div class="sba-wrap">
<div class="g-card" style="padding:24px;">
<!-- 品牌信息 -->
<div class="sba-section-hd">品牌信息</div>
<div class="sba-grid">
<div class="g-form-group">
<label class="g-form-label required">品牌名称</label>
<input class="g-input" value="老三家外卖" placeholder="请输入品牌名称">
</div>
<div class="g-form-group">
<label class="g-form-label">客服电话</label>
<input class="g-input" value="400-888-6666" placeholder="请输入客服电话">
</div>
<div class="sba-full g-form-group">
<label class="g-form-label">品牌Logo</label>
<div class="g-upload-zone sba-logo-zone">
<i data-lucide="upload-cloud"></i>
<span>点击上传</span>
</div>
</div>
<div class="sba-full g-form-group">
<label class="g-form-label">品牌简介</label>
<textarea class="g-textarea" rows="2" placeholder="请输入品牌简介,将展示在店铺首页"></textarea>
</div>
</div>
<div class="g-divider" style="margin:24px 0;"></div>
<!-- 系统设置 -->
<div class="sba-section-hd">系统设置</div>
<div class="sba-grid">
<div class="g-form-group">
<label class="g-form-label required">默认语言</label>
<select class="g-select">
<option selected>简体中文</option>
<option>English</option>
</select>
</div>
<div class="g-form-group">
<label class="g-form-label required">时区</label>
<select class="g-select">
<option selected>Asia/Shanghai (UTC+8)</option>
<option>America/New_York (UTC-5)</option>
</select>
</div>
<div class="g-form-group">
<label class="g-form-label required">货币</label>
<select class="g-select">
<option selected>CNY ¥</option>
<option>USD $</option>
</select>
</div>
</div>
</div>
<div class="sba-footer">
<button class="g-btn">重置</button>
<button class="g-btn g-btn-primary">保存</button>
</div>
</div>
<script>
if(typeof lucide!=='undefined'){lucide.createIcons();}
</script>

125
pages/sys-billing.html Normal file
View File

@@ -0,0 +1,125 @@
<!-- 账单记录 -->
<style>
.sbi-page { display:flex; flex-direction:column; gap:16px; }
/* 筛选栏 */
.sbi-filter { display:flex; align-items:center; gap:12px; background:#fff; border-radius:var(--g-radius); border:1px solid var(--g-border); padding:14px 20px; }
.sbi-filter-right { margin-left:auto; }
/* 表格容器 */
.sbi-table-wrap { background:#fff; border-radius:var(--g-radius); border:1px solid var(--g-border); padding:0; overflow:hidden; }
.sbi-table-wrap .g-table { margin:0; }
.sbi-table-wrap .g-table th:first-child,
.sbi-table-wrap .g-table td:first-child { padding-left:20px; }
.sbi-table-wrap .g-table th:last-child,
.sbi-table-wrap .g-table td:last-child { padding-right:20px; }
.sbi-mono { font-family:'SF Mono','Cascadia Code','Consolas',monospace; font-size:12px; letter-spacing:0.3px; }
.sbi-amount { font-weight:600; color:var(--g-text); }
/* 分页容器 */
.sbi-pagi { display:flex; justify-content:flex-end; padding:0 4px; }
</style>
<div class="sbi-page">
<!-- 筛选栏 -->
<div class="sbi-filter">
<select class="g-select" style="width:120px;">
<option>2026年</option>
<option>2025年</option>
<option>2024年</option>
</select>
<select class="g-select" style="width:140px;">
<option>全部类型</option>
<option>新购</option>
<option>续费</option>
<option>升级</option>
<option>增值包</option>
</select>
<div class="sbi-filter-right">
<button class="g-btn"><i data-lucide="download" style="width:14px;height:14px;margin-right:4px;vertical-align:-2px;"></i>导出</button>
</div>
</div>
<!-- 表格 -->
<div class="sbi-table-wrap">
<table class="g-table">
<thead>
<tr>
<th>账单编号</th>
<th>日期</th>
<th>类型</th>
<th>套餐 / 项目</th>
<th>金额</th>
<th>支付方式</th>
<th>状态</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr>
<td><span class="sbi-mono">BIL20260213001</span></td>
<td>2026-02-13</td>
<td><span class="g-tag g-tag-green">续费</span></td>
<td>专业版1年</td>
<td><span class="sbi-amount">¥4,980.00</span></td>
<td>微信支付</td>
<td><span class="g-tag g-tag-green">已支付</span></td>
<td><a class="g-action">查看</a><a class="g-action" style="margin-left:8px;">下载发票</a></td>
</tr>
<tr>
<td><span class="sbi-mono">BIL20250801001</span></td>
<td>2025-08-01</td>
<td><span class="g-tag g-tag-orange">升级</span></td>
<td>基础版→专业版(补差价)</td>
<td><span class="sbi-amount">¥2,400.00</span></td>
<td>支付宝</td>
<td><span class="g-tag g-tag-green">已支付</span></td>
<td><a class="g-action">查看</a><a class="g-action" style="margin-left:8px;">下载发票</a></td>
</tr>
<tr>
<td><span class="sbi-mono">BIL20250501001</span></td>
<td>2025-05-01</td>
<td><span class="g-tag g-tag-gray">增值包</span></td>
<td>短信包 5000条</td>
<td><span class="sbi-amount">¥200.00</span></td>
<td>微信支付</td>
<td><span class="g-tag g-tag-green">已支付</span></td>
<td><a class="g-action">查看</a><a class="g-action" style="margin-left:8px;">下载发票</a></td>
</tr>
<tr>
<td><span class="sbi-mono">BIL20250213001</span></td>
<td>2025-02-13</td>
<td><span class="g-tag g-tag-blue">新购</span></td>
<td>基础版1年</td>
<td><span class="sbi-amount">¥1,980.00</span></td>
<td>微信支付</td>
<td><span class="g-tag g-tag-green">已支付</span></td>
<td><a class="g-action">查看</a><a class="g-action" style="margin-left:8px;">下载发票</a></td>
</tr>
<tr>
<td><span class="sbi-mono">BIL20250213002</span></td>
<td>2025-02-13</td>
<td><span class="g-tag g-tag-gray">增值包</span></td>
<td>额外存储 5GB</td>
<td><span class="sbi-amount">¥300.00</span></td>
<td>支付宝</td>
<td><span class="g-tag g-tag-green">已支付</span></td>
<td><a class="g-action">查看</a><a class="g-action" style="margin-left:8px;">下载发票</a></td>
</tr>
</tbody>
</table>
</div>
<!-- 分页 -->
<div class="sbi-pagi">
<div class="g-pagination">
<button class="g-page-btn" disabled><i data-lucide="chevron-left" style="width:14px;height:14px;"></i></button>
<button class="g-page-btn active">1</button>
<button class="g-page-btn"><i data-lucide="chevron-right" style="width:14px;height:14px;"></i></button>
</div>
</div>
</div>
<script>
if(typeof lucide!=='undefined'){lucide.createIcons();}
</script>

52
pages/sys-logs.html Normal file
View File

@@ -0,0 +1,52 @@
<!-- 操作日志 -->
<style>
.slg-filter{display:flex;align-items:center;gap:12px;flex-wrap:wrap;}
.slg-filter .g-input,.slg-filter .g-select{height:36px;font-size:13px;}
.slg-filter .g-input[type=date]{width:150px;}
.slg-filter .g-select{width:140px;}
.slg-sep{color:var(--g-text-muted);font-size:13px;}
.slg-content-cell{max-width:320px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
</style>
<!-- 筛选栏 -->
<div class="g-card">
<div class="slg-filter">
<input class="g-input" type="date" value="2026-02-01">
<span class="slg-sep"></span>
<input class="g-input" type="date" value="2026-02-13">
<select class="g-select">
<option>全部操作人</option><option>张伟</option><option>李娜</option><option>王磊</option><option>赵敏</option>
</select>
<select class="g-select">
<option>全部类型</option><option>登录</option><option>数据修改</option><option>权限变更</option><option>删除操作</option>
</select>
<button class="g-btn g-btn-primary"><i data-lucide="search" style="width:15px;height:15px;margin-right:4px;vertical-align:-2px;"></i>查询</button>
</div>
</div>
<!-- 表格 -->
<div class="g-card" style="padding:0;overflow:hidden;margin-top:16px;">
<table class="g-table">
<thead><tr><th>时间</th><th>操作人</th><th>操作类型</th><th>操作内容</th><th>IP地址</th></tr></thead>
<tbody>
<tr><td>2026-02-13 09:30</td><td>张伟</td><td><span class="g-tag g-tag-blue">登录</span></td><td class="slg-content-cell">管理员登录系统</td><td>192.168.1.100</td></tr>
<tr><td>2026-02-13 09:45</td><td>张伟</td><td><span class="g-tag g-tag-orange">数据修改</span></td><td class="slg-content-cell">修改商品「招牌小龙虾」价格 68→58</td><td>192.168.1.100</td></tr>
<tr><td>2026-02-13 10:20</td><td>李娜</td><td><span class="g-tag g-tag-orange">数据修改</span></td><td class="slg-content-cell">新增商品「冰粉」</td><td>192.168.1.101</td></tr>
<tr><td>2026-02-13 11:00</td><td>张伟</td><td><span class="g-tag g-tag-green">权限变更</span></td><td class="slg-content-cell">将「王磊」角色从配送员改为收银员</td><td>192.168.1.100</td></tr>
<tr><td>2026-02-12 18:40</td><td>王磊</td><td><span class="g-tag g-tag-blue">登录</span></td><td class="slg-content-cell">收银员登录系统</td><td>192.168.1.102</td></tr>
<tr><td>2026-02-12 15:30</td><td>李娜</td><td><span class="g-tag g-tag-red">删除</span></td><td class="slg-content-cell">删除过期商品「圣诞套餐」</td><td>192.168.1.101</td></tr>
<tr><td>2026-02-12 14:20</td><td>赵敏</td><td><span class="g-tag g-tag-blue">登录</span></td><td class="slg-content-cell">配送员登录系统</td><td>10.0.0.55</td></tr>
<tr><td>2026-02-11 09:00</td><td>张伟</td><td><span class="g-tag g-tag-orange">数据修改</span></td><td class="slg-content-cell">修改门店「朝阳店」营业时间</td><td>192.168.1.100</td></tr>
</tbody>
</table>
</div>
<!-- 分页 -->
<div class="g-pagination" style="margin-top:16px;">
<span style="color:var(--g-text-secondary);font-size:13px;margin-right:12px;">共 8 条</span>
<button class="g-page-btn active">1</button>
</div>
<script>
if(typeof lucide!=='undefined'){lucide.createIcons();}
</script>

97
pages/sys-notify.html Normal file
View File

@@ -0,0 +1,97 @@
<style>
.snf-wrap{padding:24px;max-width:960px;display:flex;flex-direction:column;gap:20px;}
.snf-section-hd{font-size:15px;font-weight:600;color:var(--g-text);padding-left:10px;border-left:3px solid var(--primary);margin-bottom:16px;}
.snf-toggle-row{display:flex;align-items:center;justify-content:space-between;padding:12px 0;border-bottom:1px solid var(--g-border);}
.snf-toggle-row:last-child{border-bottom:none;}
.snf-toggle-left{display:flex;flex-direction:column;gap:4px;}
.snf-toggle-label{font-size:14px;color:var(--g-text);font-weight:500;}
.snf-toggle-hint{font-size:12px;color:var(--g-text-muted);}
.snf-event-table th,.snf-event-table td{text-align:center;}
.snf-event-table td:first-child,.snf-event-table th:first-child{text-align:left;}
.snf-event-table input[type="checkbox"]{width:16px;height:16px;accent-color:var(--primary);cursor:pointer;}
.snf-footer{display:flex;justify-content:flex-end;gap:10px;padding:4px 0 0;}
</style>
<div class="snf-wrap">
<!-- 通知渠道 -->
<div class="g-card" style="padding:24px;">
<div class="snf-section-hd">通知渠道</div>
<div class="snf-toggle-row">
<div class="snf-toggle-left">
<span class="snf-toggle-label">站内通知</span>
</div>
<div class="g-toggle on" onclick="toggleSwitch(this)"></div>
</div>
<div class="snf-toggle-row">
<div class="snf-toggle-left">
<span class="snf-toggle-label">微信推送</span>
</div>
<div class="g-toggle on" onclick="toggleSwitch(this)"></div>
</div>
<div class="snf-toggle-row">
<div class="snf-toggle-left">
<span class="snf-toggle-label">短信通知</span>
<span class="snf-toggle-hint">需充值短信包,当前余额 0 条</span>
</div>
<div class="g-toggle" onclick="toggleSwitch(this)"></div>
</div>
</div>
<!-- 通知事件 -->
<div class="g-card" style="padding:24px;">
<div class="snf-section-hd">通知事件</div>
<table class="g-table snf-event-table">
<thead>
<tr><th>事件名称</th><th>站内</th><th>微信</th><th>短信</th></tr>
</thead>
<tbody>
<tr>
<td>新订单提醒</td>
<td><input type="checkbox" checked></td>
<td><input type="checkbox" checked></td>
<td><input type="checkbox" checked></td>
</tr>
<tr>
<td>退款申请</td>
<td><input type="checkbox" checked></td>
<td><input type="checkbox" checked></td>
<td><input type="checkbox"></td>
</tr>
<tr>
<td>库存预警</td>
<td><input type="checkbox" checked></td>
<td><input type="checkbox"></td>
<td><input type="checkbox"></td>
</tr>
<tr>
<td>会员注册</td>
<td><input type="checkbox" checked></td>
<td><input type="checkbox"></td>
<td><input type="checkbox"></td>
</tr>
<tr>
<td>差评提醒</td>
<td><input type="checkbox" checked></td>
<td><input type="checkbox" checked></td>
<td><input type="checkbox"></td>
</tr>
<tr>
<td>资质到期</td>
<td><input type="checkbox" checked></td>
<td><input type="checkbox" checked></td>
<td><input type="checkbox" checked></td>
</tr>
</tbody>
</table>
</div>
<div class="snf-footer">
<button class="g-btn">重置</button>
<button class="g-btn g-btn-primary">保存</button>
</div>
</div>
<script>
function toggleSwitch(el){el.classList.toggle('on');}
if(typeof lucide!=='undefined'){lucide.createIcons();}
</script>

81
pages/sys-payment.html Normal file
View File

@@ -0,0 +1,81 @@
<style>
.spy-wrap{padding:24px;max-width:900px;display:flex;flex-direction:column;gap:20px;}
.spy-card-hd{display:flex;align-items:center;gap:12px;margin-bottom:20px;}
.spy-card-hd h3{font-size:16px;font-weight:600;color:var(--g-text);margin:0;}
.spy-grid{display:grid;grid-template-columns:1fr 1fr;gap:16px 28px;}
.spy-upload-sm{width:100%;height:64px;display:flex;align-items:center;justify-content:center;gap:6px;}
.spy-upload-sm i{width:18px;height:18px;color:var(--g-text-muted);}
.spy-upload-sm span{font-size:12px;color:var(--g-text-muted);}
.spy-test-row{margin-top:16px;padding-top:16px;border-top:1px solid var(--g-border);}
.spy-footer{display:flex;justify-content:flex-end;gap:10px;padding:4px 0 0;}
</style>
<div class="spy-wrap">
<!-- 微信支付 -->
<div class="g-card" style="padding:24px;">
<div class="spy-card-hd">
<div class="g-toggle on" onclick="toggleSwitch(this)"></div>
<h3>微信支付</h3>
<span class="g-tag g-tag-green">已接入</span>
</div>
<div class="spy-grid">
<div class="g-form-group">
<label class="g-form-label required">商户号</label>
<input class="g-input" value="15xxxxx" placeholder="请输入微信支付商户号">
</div>
<div class="g-form-group">
<label class="g-form-label required">API密钥</label>
<input class="g-input" type="password" value="********" placeholder="请输入API密钥">
</div>
<div class="g-form-group">
<label class="g-form-label">证书上传</label>
<div class="g-upload-zone spy-upload-sm">
<i data-lucide="upload-cloud"></i>
<span>点击上传证书文件</span>
</div>
</div>
</div>
<div class="spy-test-row">
<button class="g-btn"><i data-lucide="wifi" style="width:14px;height:14px;margin-right:4px;vertical-align:-2px;"></i>测试连接</button>
</div>
</div>
<!-- 支付宝 -->
<div class="g-card" style="padding:24px;">
<div class="spy-card-hd">
<div class="g-toggle" onclick="toggleSwitch(this)"></div>
<h3>支付宝</h3>
<span class="g-tag g-tag-gray">未接入</span>
</div>
<div class="spy-grid">
<div class="g-form-group">
<label class="g-form-label required">APPID</label>
<input class="g-input" placeholder="请输入支付宝APPID">
</div>
<div class="g-form-group">
<label class="g-form-label required">私钥</label>
<input class="g-input" type="password" placeholder="请输入应用私钥">
</div>
<div class="g-form-group">
<label class="g-form-label">证书上传</label>
<div class="g-upload-zone spy-upload-sm">
<i data-lucide="upload-cloud"></i>
<span>点击上传证书文件</span>
</div>
</div>
</div>
<div class="spy-test-row">
<button class="g-btn"><i data-lucide="wifi" style="width:14px;height:14px;margin-right:4px;vertical-align:-2px;"></i>测试连接</button>
</div>
</div>
<div class="spy-footer">
<button class="g-btn">重置</button>
<button class="g-btn g-btn-primary">保存</button>
</div>
</div>
<script>
function toggleSwitch(el){el.classList.toggle('on');}
if(typeof lucide!=='undefined'){lucide.createIcons();}
</script>

111
pages/sys-plan.html Normal file
View File

@@ -0,0 +1,111 @@
<!-- 当前套餐 -->
<style>
.spl-page { display:flex; flex-direction:column; gap:20px; }
/* 顶部大卡片 */
.spl-hero { background:#fff; border-radius:var(--g-radius-lg); border:1px solid var(--g-border); padding:24px; }
.spl-hero-top { display:flex; justify-content:space-between; align-items:flex-start; }
.spl-hero-left { display:flex; flex-direction:column; gap:10px; }
.spl-plan-row { display:flex; align-items:center; gap:10px; }
.spl-plan-name { font-size:22px; font-weight:700; color:var(--g-text); }
.spl-meta { display:flex; align-items:center; gap:16px; font-size:13px; color:var(--g-text-secondary); }
.spl-meta-label { color:var(--g-text-muted); }
.spl-days { color:var(--g-success); font-weight:700; }
.spl-hero-right { display:flex; gap:10px; align-items:flex-start; }
/* 使用情况 */
.spl-usage-grid { display:grid; grid-template-columns:repeat(3,1fr); gap:16px; margin-top:20px; }
.spl-usage-card { background:#fff; border-radius:var(--g-radius); border:1px solid var(--g-border); padding:18px 20px; transition:var(--g-transition); }
.spl-usage-card:hover { box-shadow:var(--g-shadow-md); transform:translateY(-1px); }
.spl-usage-hd { display:flex; align-items:center; justify-content:space-between; margin-bottom:12px; }
.spl-usage-label { font-size:13px; color:var(--g-text-secondary); display:flex; align-items:center; gap:6px; }
.spl-usage-label i { width:16px; height:16px; color:var(--primary); }
.spl-usage-val { font-size:14px; font-weight:600; color:var(--g-text); }
.spl-bar { height:6px; background:#f0f0f0; border-radius:3px; overflow:hidden; }
.spl-bar-inner { height:100%; background:var(--primary); border-radius:3px; transition:var(--g-transition); }
/* Section 标题 */
.spl-section-hd { font-size:15px; font-weight:600; color:var(--g-text); padding-left:10px; border-left:3px solid var(--primary); margin-bottom:16px; }
/* 功能区 */
.spl-features { display:flex; flex-wrap:wrap; gap:8px; }
.spl-lock-tag { display:inline-flex; align-items:center; gap:4px; }
.spl-lock-tag i { width:12px; height:12px; }
.spl-hint { font-size:12px; color:var(--g-text-muted); margin-top:10px; }
</style>
<div class="spl-page">
<!-- 顶部大卡片 -->
<div class="spl-hero">
<div class="spl-hero-top">
<div class="spl-hero-left">
<div class="spl-plan-row">
<span class="spl-plan-name">专业版</span>
<span class="g-tag g-tag-blue">当前套餐</span>
</div>
<div class="spl-meta">
<span><span class="spl-meta-label">到期时间:</span>2027-02-13</span>
<span><span class="spl-meta-label">剩余:</span><span class="spl-days">365天</span></span>
</div>
</div>
<div class="spl-hero-right">
<button class="g-btn">续费</button>
<button class="g-btn g-btn-primary">升级套餐</button>
</div>
</div>
<!-- 使用情况 -->
<div class="spl-usage-grid">
<div class="spl-usage-card">
<div class="spl-usage-hd">
<span class="spl-usage-label"><i data-lucide="store"></i>门店数量</span>
<span class="spl-usage-val">3 / 5</span>
</div>
<div class="spl-bar"><div class="spl-bar-inner" style="width:60%"></div></div>
</div>
<div class="spl-usage-card">
<div class="spl-usage-hd">
<span class="spl-usage-label"><i data-lucide="users"></i>子账号数</span>
<span class="spl-usage-val">4 / 10</span>
</div>
<div class="spl-bar"><div class="spl-bar-inner" style="width:40%"></div></div>
</div>
<div class="spl-usage-card">
<div class="spl-usage-hd">
<span class="spl-usage-label"><i data-lucide="hard-drive"></i>存储空间</span>
<span class="spl-usage-val">2.1GB / 10GB</span>
</div>
<div class="spl-bar"><div class="spl-bar-inner" style="width:21%"></div></div>
</div>
</div>
</div>
<!-- 已开通功能 -->
<div class="g-card" style="padding:20px 24px;">
<div class="spl-section-hd">已开通功能</div>
<div class="spl-features">
<span class="g-tag g-tag-blue">商品管理</span>
<span class="g-tag g-tag-blue">订单管理</span>
<span class="g-tag g-tag-blue">门店管理</span>
<span class="g-tag g-tag-blue">财务中心</span>
<span class="g-tag g-tag-blue">数据统计</span>
<span class="g-tag g-tag-blue">会员营销</span>
<span class="g-tag g-tag-blue">库存管理</span>
<span class="g-tag g-tag-blue">多渠道整合</span>
</div>
</div>
<!-- 未开通功能 -->
<div class="g-card" style="padding:20px 24px;">
<div class="spl-section-hd">未开通功能</div>
<div class="spl-features">
<span class="g-tag g-tag-gray spl-lock-tag"><i data-lucide="lock"></i>AI智能</span>
<span class="g-tag g-tag-gray spl-lock-tag"><i data-lucide="lock"></i>连锁管理</span>
</div>
<div class="spl-hint">升级旗舰版解锁以上功能</div>
</div>
</div>
<script>
if(typeof lucide!=='undefined'){lucide.createIcons();}
</script>

100
pages/sys-printer.html Normal file
View File

@@ -0,0 +1,100 @@
<style>
.spr-wrap{padding:24px;max-width:960px;display:flex;flex-direction:column;gap:20px;}
.spr-section-hd{font-size:15px;font-weight:600;color:var(--g-text);padding-left:10px;border-left:3px solid var(--primary);margin-bottom:16px;}
.spr-card-hd{display:flex;align-items:center;justify-content:space-between;margin-bottom:16px;}
.spr-toggle-row{display:flex;align-items:center;justify-content:space-between;padding:12px 0;border-bottom:1px solid var(--g-border);}
.spr-toggle-row:last-child{border-bottom:none;}
.spr-toggle-left{display:flex;flex-direction:column;gap:2px;}
.spr-toggle-label{font-size:14px;color:var(--g-text);font-weight:500;}
.spr-grid{display:grid;grid-template-columns:1fr 1fr;gap:16px 28px;}
.spr-offline{opacity:0.5;}
.spr-footer{display:flex;justify-content:flex-end;gap:10px;padding:4px 0 0;}
</style>
<div class="spr-wrap">
<!-- 打印机列表 -->
<div class="g-card" style="padding:24px;">
<div class="spr-card-hd">
<div class="spr-section-hd" style="margin-bottom:0;">打印机管理</div>
<button class="g-btn g-btn-primary g-btn-sm"><i data-lucide="plus" style="width:14px;height:14px;margin-right:4px;vertical-align:-2px;"></i>添加打印机</button>
</div>
<table class="g-table">
<thead>
<tr><th>名称</th><th>型号</th><th>连接方式</th><th>IP/端口</th><th>状态</th><th>操作</th></tr>
</thead>
<tbody>
<tr>
<td>前台打印机</td><td>飞鹅 FP-58</td><td>WiFi</td><td>192.168.1.101</td>
<td><span class="g-tag g-tag-green">在线</span></td>
<td><a class="g-action">编辑</a><a class="g-action g-action-danger">删除</a></td>
</tr>
<tr>
<td>后厨打印机</td><td>商米 T2</td><td>USB</td><td></td>
<td><span class="g-tag g-tag-green">在线</span></td>
<td><a class="g-action">编辑</a><a class="g-action g-action-danger">删除</a></td>
</tr>
<tr class="spr-offline">
<td>标签打印机</td><td>佳博 GP-3120</td><td>蓝牙</td><td></td>
<td><span class="g-tag g-tag-gray">离线</span></td>
<td><a class="g-action">编辑</a><a class="g-action g-action-danger">删除</a></td>
</tr>
</tbody>
</table>
</div>
<!-- 小票设置 -->
<div class="g-card" style="padding:24px;">
<div class="spr-section-hd">小票设置</div>
<div class="spr-toggle-row">
<span class="spr-toggle-label">自动打印</span>
<div class="g-toggle on" onclick="toggleSwitch(this)"></div>
</div>
<div style="margin-top:16px;">
<div class="spr-grid">
<div class="g-form-group">
<label class="g-form-label">打印份数</label>
<select class="g-select">
<option>1</option>
<option selected>2</option>
<option>3</option>
</select>
</div>
<div class="g-form-group">
<label class="g-form-label">小票宽度</label>
<select class="g-select">
<option>58mm</option>
<option selected>80mm</option>
</select>
</div>
</div>
</div>
<div class="g-divider" style="margin:20px 0;"></div>
<div class="spr-section-hd">打印内容</div>
<div class="spr-toggle-row">
<span class="spr-toggle-label">订单详情</span>
<div class="g-toggle on" onclick="toggleSwitch(this)"></div>
</div>
<div class="spr-toggle-row">
<span class="spr-toggle-label">商品明细</span>
<div class="g-toggle on" onclick="toggleSwitch(this)"></div>
</div>
<div class="spr-toggle-row">
<span class="spr-toggle-label">配送信息</span>
<div class="g-toggle on" onclick="toggleSwitch(this)"></div>
</div>
<div class="spr-toggle-row">
<span class="spr-toggle-label">二维码</span>
<div class="g-toggle" onclick="toggleSwitch(this)"></div>
</div>
</div>
<div class="spr-footer">
<button class="g-btn">重置</button>
<button class="g-btn g-btn-primary">保存</button>
</div>
</div>
<script>
function toggleSwitch(el){el.classList.toggle('on');}
if(typeof lucide!=='undefined'){lucide.createIcons();}
</script>

83
pages/sys-roles.html Normal file
View File

@@ -0,0 +1,83 @@
<!-- 角色权限 -->
<style>
.srl-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:16px;}
.srl-header-title{font-size:15px;font-weight:600;color:var(--g-text);padding-left:10px;border-left:3px solid var(--primary);}
.srl-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:16px;margin-bottom:24px;}
.srl-role-card{background:#fff;border-radius:10px;border:1px solid var(--g-border);padding:20px;transition:var(--g-transition);position:relative;}
.srl-role-card:hover{box-shadow:var(--g-shadow-md);transform:translateY(-1px);}
.srl-role-name{font-size:18px;font-weight:700;color:var(--g-text);margin-bottom:4px;}
.srl-role-count{font-size:13px;color:var(--g-text-secondary);margin-bottom:8px;}
.srl-role-desc{font-size:13px;color:var(--g-text-muted);margin-bottom:14px;line-height:1.5;}
.srl-role-foot{display:flex;align-items:center;justify-content:space-between;}
.srl-role-foot a{color:var(--primary);font-size:13px;cursor:pointer;text-decoration:none;transition:var(--g-transition);}
.srl-role-foot a:hover{text-decoration:underline;}
.srl-perm-section{margin-top:8px;}
.srl-section-hd{font-size:15px;font-weight:600;color:var(--g-text);padding-left:10px;border-left:3px solid var(--primary);margin-bottom:16px;}
.srl-save-bar{display:flex;justify-content:flex-end;margin-top:16px;gap:10px;}
.srl-cb{accent-color:var(--primary);width:16px;height:16px;cursor:pointer;}
</style>
<!-- 角色卡片 -->
<div class="srl-header">
<span class="srl-header-title">角色管理</span>
<button class="g-btn g-btn-primary"><i data-lucide="plus" style="width:15px;height:15px;margin-right:4px;vertical-align:-2px;"></i>添加角色</button>
</div>
<div class="srl-grid">
<div class="srl-role-card">
<div class="srl-role-name">管理员</div>
<div class="srl-role-count">1 人</div>
<div class="srl-role-desc">拥有系统全部权限,可管理所有门店和员工</div>
<div class="srl-role-foot"><span class="g-tag g-tag-orange">系统角色,不可删除</span><a>编辑权限</a></div>
</div>
<div class="srl-role-card">
<div class="srl-role-name">店长</div>
<div class="srl-role-count">2 人</div>
<div class="srl-role-desc">管理所属门店的商品、订单、库存和会员</div>
<div class="srl-role-foot"><a class="g-action g-action-danger" style="font-size:13px;">删除</a><a>编辑权限</a></div>
</div>
<div class="srl-role-card">
<div class="srl-role-name">收银员</div>
<div class="srl-role-count">1 人</div>
<div class="srl-role-desc">处理门店订单,接单、出餐和退款操作</div>
<div class="srl-role-foot"><a class="g-action g-action-danger" style="font-size:13px;">删除</a><a>编辑权限</a></div>
</div>
<div class="srl-role-card">
<div class="srl-role-name">配送员</div>
<div class="srl-role-count">1 人</div>
<div class="srl-role-desc">查看待配送订单,更新配送状态</div>
<div class="srl-role-foot"><a class="g-action g-action-danger" style="font-size:13px;">删除</a><a>编辑权限</a></div>
</div>
</div>
<div class="g-divider"></div>
<!-- 权限矩阵 -->
<div class="srl-perm-section">
<div class="srl-section-hd">权限配置</div>
<div class="g-card" style="padding:0;overflow:hidden;">
<table class="g-table">
<thead><tr><th style="text-align:left;">功能模块</th><th>管理员</th><th>店长</th><th>收银员</th><th>配送员</th></tr></thead>
<tbody>
<tr><td style="font-weight:500;">商品管理</td><td style="text-align:center;"><input type="checkbox" class="srl-cb" checked disabled></td><td style="text-align:center;"><input type="checkbox" class="srl-cb" checked></td><td style="text-align:center;"><input type="checkbox" class="srl-cb"></td><td style="text-align:center;"><input type="checkbox" class="srl-cb"></td></tr>
<tr><td style="font-weight:500;">订单管理</td><td style="text-align:center;"><input type="checkbox" class="srl-cb" checked disabled></td><td style="text-align:center;"><input type="checkbox" class="srl-cb" checked></td><td style="text-align:center;"><input type="checkbox" class="srl-cb" checked></td><td style="text-align:center;"><input type="checkbox" class="srl-cb" checked></td></tr>
<tr><td style="font-weight:500;">门店管理</td><td style="text-align:center;"><input type="checkbox" class="srl-cb" checked disabled></td><td style="text-align:center;"><input type="checkbox" class="srl-cb" checked></td><td style="text-align:center;"><input type="checkbox" class="srl-cb"></td><td style="text-align:center;"><input type="checkbox" class="srl-cb"></td></tr>
<tr><td style="font-weight:500;">财务中心</td><td style="text-align:center;"><input type="checkbox" class="srl-cb" checked disabled></td><td style="text-align:center;"><input type="checkbox" class="srl-cb"></td><td style="text-align:center;"><input type="checkbox" class="srl-cb"></td><td style="text-align:center;"><input type="checkbox" class="srl-cb"></td></tr>
<tr><td style="font-weight:500;">数据统计</td><td style="text-align:center;"><input type="checkbox" class="srl-cb" checked disabled></td><td style="text-align:center;"><input type="checkbox" class="srl-cb"></td><td style="text-align:center;"><input type="checkbox" class="srl-cb"></td><td style="text-align:center;"><input type="checkbox" class="srl-cb"></td></tr>
<tr><td style="font-weight:500;">会员营销</td><td style="text-align:center;"><input type="checkbox" class="srl-cb" checked disabled></td><td style="text-align:center;"><input type="checkbox" class="srl-cb" checked></td><td style="text-align:center;"><input type="checkbox" class="srl-cb"></td><td style="text-align:center;"><input type="checkbox" class="srl-cb"></td></tr>
<tr><td style="font-weight:500;">库存管理</td><td style="text-align:center;"><input type="checkbox" class="srl-cb" checked disabled></td><td style="text-align:center;"><input type="checkbox" class="srl-cb" checked></td><td style="text-align:center;"><input type="checkbox" class="srl-cb"></td><td style="text-align:center;"><input type="checkbox" class="srl-cb"></td></tr>
<tr><td style="font-weight:500;">系统设置</td><td style="text-align:center;"><input type="checkbox" class="srl-cb" checked disabled></td><td style="text-align:center;"><input type="checkbox" class="srl-cb"></td><td style="text-align:center;"><input type="checkbox" class="srl-cb"></td><td style="text-align:center;"><input type="checkbox" class="srl-cb"></td></tr>
</tbody>
</table>
</div>
</div>
<!-- 保存栏 -->
<div class="srl-save-bar">
<button class="g-btn">重置</button>
<button class="g-btn g-btn-primary"><i data-lucide="save" style="width:15px;height:15px;margin-right:4px;vertical-align:-2px;"></i>保存权限</button>
</div>
<script>
if(typeof lucide!=='undefined'){lucide.createIcons();}
</script>

110
pages/sys-subaccount.html Normal file
View File

@@ -0,0 +1,110 @@
<!-- 子账号管理 -->
<style>
.ssa-toolbar{display:flex;align-items:center;gap:12px;margin-bottom:16px;padding:12px 16px;background:#fff;border-radius:10px;box-shadow:var(--g-shadow-sm);flex-wrap:wrap;}
.ssa-toolbar .g-input,.ssa-toolbar .g-select{height:36px;font-size:13px;}
.ssa-toolbar .g-input{width:220px;}
.ssa-toolbar .g-select{width:140px;}
.ssa-spacer{flex:1;}
.ssa-name-cell{display:flex;align-items:center;gap:8px;}
.ssa-avatar{width:32px;height:32px;border-radius:50%;display:flex;align-items:center;justify-content:center;color:#fff;font-size:13px;font-weight:600;flex-shrink:0;}
.ssa-row-disabled{opacity:0.5;}
.ssa-store-pills{display:flex;flex-wrap:wrap;gap:6px;margin-top:4px;}
</style>
<!-- 工具栏 -->
<div class="ssa-toolbar">
<input class="g-input" placeholder="搜索姓名/手机号">
<select class="g-select">
<option>全部角色</option><option>管理员</option><option>店长</option><option>收银员</option><option>配送员</option>
</select>
<select class="g-select">
<option>全部状态</option><option>启用</option><option>禁用</option>
</select>
<div class="ssa-spacer"></div>
<button class="g-btn g-btn-primary" onclick="ssaOpenDrawer()"><i data-lucide="plus" style="width:15px;height:15px;margin-right:4px;vertical-align:-2px;"></i>添加账号</button>
</div>
<!-- 表格 -->
<div class="g-card" style="padding:0;overflow:hidden;">
<table class="g-table">
<thead><tr><th>姓名</th><th>手机号</th><th>角色</th><th>所属门店</th><th>状态</th><th>最后登录</th><th>操作</th></tr></thead>
<tbody>
<tr>
<td><div class="ssa-name-cell"><div class="ssa-avatar" style="background:#3b82f6;"></div>张伟</div></td>
<td>138****1001</td><td><span class="g-tag g-tag-blue">管理员</span></td><td>全部门店</td><td><span class="g-tag g-tag-green">启用</span></td><td>2026-02-13 09:30</td>
<td><a class="g-action">编辑</a><a class="g-action">禁用</a><a class="g-action g-action-danger">删除</a></td>
</tr>
<tr>
<td><div class="ssa-name-cell"><div class="ssa-avatar" style="background:#22c55e;"></div>李娜</div></td>
<td>139****2002</td><td><span class="g-tag g-tag-green">店长</span></td><td>朝阳店</td><td><span class="g-tag g-tag-green">启用</span></td><td>2026-02-13 08:15</td>
<td><a class="g-action">编辑</a><a class="g-action">禁用</a><a class="g-action g-action-danger">删除</a></td>
</tr>
<tr>
<td><div class="ssa-name-cell"><div class="ssa-avatar" style="background:#f59e0b;"></div>王磊</div></td>
<td>137****3003</td><td><span class="g-tag g-tag-orange">收银员</span></td><td>海淀店</td><td><span class="g-tag g-tag-green">启用</span></td><td>2026-02-12 18:40</td>
<td><a class="g-action">编辑</a><a class="g-action">禁用</a><a class="g-action g-action-danger">删除</a></td>
</tr>
<tr>
<td><div class="ssa-name-cell"><div class="ssa-avatar" style="background:#9ca3af;"></div>赵敏</div></td>
<td>136****4004</td><td><span class="g-tag g-tag-gray">配送员</span></td><td>望京店</td><td><span class="g-tag g-tag-green">启用</span></td><td>2026-02-12 14:20</td>
<td><a class="g-action">编辑</a><a class="g-action">禁用</a><a class="g-action g-action-danger">删除</a></td>
</tr>
<tr class="ssa-row-disabled">
<td><div class="ssa-name-cell"><div class="ssa-avatar" style="background:#6b7280;"></div>刘洋</div></td>
<td>135****5005</td><td><span class="g-tag g-tag-green">店长</span></td><td>通州店</td><td><span class="g-tag g-tag-gray">禁用</span></td><td>2026-01-15 10:00</td>
<td><a class="g-action">编辑</a><a class="g-action">启用</a><a class="g-action g-action-danger">删除</a></td>
</tr>
</tbody>
</table>
</div>
<!-- 分页 -->
<div class="g-pagination" style="margin-top:16px;">
<span style="color:var(--g-text-secondary);font-size:13px;margin-right:12px;">共 5 条</span>
<button class="g-page-btn active">1</button>
</div>
<!-- 抽屉 -->
<div class="g-drawer-mask" id="ssaDrawerMask" onclick="ssaCloseDrawer()"></div>
<div class="g-drawer" id="ssaDrawer" style="width:520px;">
<div class="g-drawer-hd">
<span class="g-drawer-title">添加账号</span>
<button class="g-drawer-close" onclick="ssaCloseDrawer()"><i data-lucide="x"></i></button>
</div>
<div class="g-drawer-bd">
<div class="g-form-group"><label class="g-form-label required">姓名</label><input class="g-input" placeholder="请输入员工姓名"></div>
<div class="g-form-group"><label class="g-form-label required">手机号</label><input class="g-input" placeholder="请输入手机号"></div>
<div class="g-form-group"><label class="g-form-label required">角色</label>
<select class="g-select"><option value="">请选择角色</option><option>管理员</option><option>店长</option><option>收银员</option><option>配送员</option></select>
</div>
<div class="g-form-group"><label class="g-form-label required">所属门店</label>
<div class="ssa-store-pills">
<span class="g-pill checked" onclick="togglePill(event.currentTarget)">朝阳店</span>
<span class="g-pill" onclick="togglePill(event.currentTarget)">海淀店</span>
<span class="g-pill" onclick="togglePill(event.currentTarget)">望京店</span>
<span class="g-pill" onclick="togglePill(event.currentTarget)">通州店</span>
<span class="g-pill" onclick="togglePill(event.currentTarget)">丰台店</span>
</div>
<p class="g-hint">可多选,点击切换选中状态</p>
</div>
<div class="g-form-group"><label class="g-form-label required">初始密码</label><input class="g-input" type="password" placeholder="请设置初始密码"></div>
<div class="g-form-group"><label class="g-form-label">备注</label><textarea class="g-textarea" placeholder="选填,如入职日期、特殊说明等"></textarea></div>
</div>
<div class="g-drawer-ft">
<button class="g-btn" onclick="ssaCloseDrawer()">取消</button>
<button class="g-btn g-btn-primary" onclick="ssaCloseDrawer()">确认添加</button>
</div>
</div>
<script>
function ssaOpenDrawer(){
document.getElementById('ssaDrawerMask').classList.add('open');
document.getElementById('ssaDrawer').classList.add('open');
}
function ssaCloseDrawer(){
document.getElementById('ssaDrawerMask').classList.remove('open');
document.getElementById('ssaDrawer').classList.remove('open');
}
function togglePill(el){el.classList.toggle('checked');}
if(typeof lucide!=='undefined'){lucide.createIcons();}
</script>

98
pages/sys-upgrade.html Normal file
View File

@@ -0,0 +1,98 @@
<!-- 续费升级 -->
<style>
.sup-page { display:flex; flex-direction:column; gap:20px; }
/* 套餐卡片 */
.sup-plans { display:grid; grid-template-columns:repeat(3,1fr); gap:20px; }
.sup-card { background:#fff; border-radius:var(--g-radius-lg); border:1px solid var(--g-border); padding:28px 24px; display:flex; flex-direction:column; align-items:center; text-align:center; position:relative; transition:var(--g-transition); }
.sup-card:hover { box-shadow:var(--g-shadow-md); transform:translateY(-1px); }
.sup-card.current { border:2px solid var(--primary); }
.sup-ribbon { position:absolute; top:12px; right:12px; }
.sup-plan-name { font-size:20px; font-weight:700; color:var(--g-text); margin-bottom:8px; }
.sup-price { font-size:28px; font-weight:800; color:var(--primary); margin-bottom:4px; letter-spacing:-0.5px; }
.sup-price-unit { font-size:14px; font-weight:400; color:var(--g-text-muted); }
.sup-features { list-style:none; padding:0; margin:20px 0 24px; width:100%; text-align:left; display:flex; flex-direction:column; gap:10px; }
.sup-features li { display:flex; align-items:center; gap:8px; font-size:13px; color:var(--g-text-secondary); }
.sup-features li i { width:16px; height:16px; color:var(--g-success); flex-shrink:0; }
.sup-card .g-btn { width:100%; }
/* Section 标题 */
.sup-section-hd { font-size:15px; font-weight:600; color:var(--g-text); padding-left:10px; border-left:3px solid var(--primary); margin-bottom:16px; }
/* 对比表 */
.sup-compare { background:#fff; border-radius:var(--g-radius); border:1px solid var(--g-border); padding:20px 24px; }
.sup-compare .g-table th:not(:first-child),
.sup-compare .g-table td:not(:first-child) { text-align:center; }
.sup-compare .g-table th:first-child { min-width:140px; }
</style>
<div class="sup-page">
<!-- 套餐卡片 -->
<div class="sup-plans">
<!-- 基础版 -->
<div class="sup-card">
<div class="sup-plan-name">基础版</div>
<div class="sup-price">¥1,980<span class="sup-price-unit">/年</span></div>
<ul class="sup-features">
<li><i data-lucide="check"></i>1 个门店</li>
<li><i data-lucide="check"></i>3 个子账号</li>
<li><i data-lucide="check"></i>5GB 存储空间</li>
<li><i data-lucide="check"></i>基础功能</li>
</ul>
<button class="g-btn">降级</button>
</div>
<!-- 专业版 -->
<div class="sup-card current">
<div class="sup-ribbon"><span class="g-tag g-tag-blue">当前套餐</span></div>
<div class="sup-plan-name">专业版</div>
<div class="sup-price">¥4,980<span class="sup-price-unit">/年</span></div>
<ul class="sup-features">
<li><i data-lucide="check"></i>5 个门店</li>
<li><i data-lucide="check"></i>10 个子账号</li>
<li><i data-lucide="check"></i>10GB 存储空间</li>
<li><i data-lucide="check"></i>全部功能不含AI和连锁</li>
</ul>
<button class="g-btn" disabled>当前套餐</button>
</div>
<!-- 旗舰版 -->
<div class="sup-card">
<div class="sup-plan-name">旗舰版</div>
<div class="sup-price">¥9,980<span class="sup-price-unit">/年</span></div>
<ul class="sup-features">
<li><i data-lucide="check"></i>不限门店</li>
<li><i data-lucide="check"></i>不限子账号</li>
<li><i data-lucide="check"></i>50GB 存储空间</li>
<li><i data-lucide="check"></i>全部功能</li>
</ul>
<button class="g-btn g-btn-primary">立即升级</button>
</div>
</div>
<div class="g-divider"></div>
<!-- 功能对比 -->
<div class="sup-compare">
<div class="sup-section-hd">功能对比</div>
<table class="g-table">
<thead>
<tr><th>功能</th><th>基础版</th><th>专业版</th><th>旗舰版</th></tr>
</thead>
<tbody>
<tr><td>门店数量</td><td>1</td><td>5</td><td>不限</td></tr>
<tr><td>子账号</td><td>3</td><td>10</td><td>不限</td></tr>
<tr><td>存储空间</td><td>5GB</td><td>10GB</td><td>50GB</td></tr>
<tr><td>商品管理</td><td><i data-lucide="check" style="color:var(--g-success);width:16px;height:16px;"></i></td><td><i data-lucide="check" style="color:var(--g-success);width:16px;height:16px;"></i></td><td><i data-lucide="check" style="color:var(--g-success);width:16px;height:16px;"></i></td></tr>
<tr><td>订单管理</td><td><i data-lucide="check" style="color:var(--g-success);width:16px;height:16px;"></i></td><td><i data-lucide="check" style="color:var(--g-success);width:16px;height:16px;"></i></td><td><i data-lucide="check" style="color:var(--g-success);width:16px;height:16px;"></i></td></tr>
<tr><td>数据统计</td><td>基础</td><td>高级</td><td>高级</td></tr>
<tr><td>多渠道整合</td><td><i data-lucide="x" style="color:var(--g-text-muted);width:16px;height:16px;"></i></td><td><i data-lucide="check" style="color:var(--g-success);width:16px;height:16px;"></i></td><td><i data-lucide="check" style="color:var(--g-success);width:16px;height:16px;"></i></td></tr>
<tr><td>AI智能</td><td><i data-lucide="x" style="color:var(--g-text-muted);width:16px;height:16px;"></i></td><td><i data-lucide="x" style="color:var(--g-text-muted);width:16px;height:16px;"></i></td><td><i data-lucide="check" style="color:var(--g-success);width:16px;height:16px;"></i></td></tr>
<tr><td>连锁管理</td><td><i data-lucide="x" style="color:var(--g-text-muted);width:16px;height:16px;"></i></td><td><i data-lucide="x" style="color:var(--g-text-muted);width:16px;height:16px;"></i></td><td><i data-lucide="check" style="color:var(--g-success);width:16px;height:16px;"></i></td></tr>
<tr><td>专属客服</td><td><i data-lucide="x" style="color:var(--g-text-muted);width:16px;height:16px;"></i></td><td><i data-lucide="x" style="color:var(--g-text-muted);width:16px;height:16px;"></i></td><td><i data-lucide="check" style="color:var(--g-success);width:16px;height:16px;"></i></td></tr>
</tbody>
</table>
</div>
</div>
<script>
if(typeof lucide!=='undefined'){lucide.createIcons();}
</script>