Files
TakeoutSaaS.Prototypes/pages/mbr-points.html
MSuMshk 7463c595a2 feat: 新增订单管理、评价管理、营销中心、会员中心模块
- 订单管理(4页): 订单大厅(看板)、全部订单、退款售后、订单设置
- 评价管理(1页): 评价列表+统计+回复抽屉
- 营销中心(5页): 优惠券、满减活动、限时折扣(含周期循环)、秒杀活动、新客有礼
- 会员中心(5页): 会员管理、储值卡、积分商城、客户画像、消息触达
- 侧边栏菜单重构: 营销中心拆分为营销中心+会员中心两个一级菜单
2026-02-12 12:08:28 +08:00

449 lines
18 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!-- 积分商城页 -->
<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; }
.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; }
/* 保存栏 */
.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>
</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">可乐兑换券</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">凉拌黄瓜</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券</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">招牌奶茶</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">双人套餐券</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">定制保温杯</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>
</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>
<input class="g-input" placeholder="请输入兑换商品名称">
</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">
<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>
<div class="g-drawer-ft">
<button class="g-btn" onclick="closePtDrawer()">取消</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';
}
/* 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';
}
/* 兑换方式 pill 单选 */
function selectPtExchangeType(el, type) {
document.querySelectorAll('.pt-exchange-pills .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');
}
/* 初始化 Lucide 图标 */
if (typeof lucide !== 'undefined') { lucide.createIcons(); }
</script>