瀏覽代碼

商家订单列表添加mdId过滤

qmj 3 周之前
父節點
當前提交
0f466c53d2
共有 3 個文件被更改,包括 2120 次插入0 次删除
  1. 546 0
      meituan-checkout-demo.html
  2. 679 0
      meituan-promotion-types-demo.html
  3. 895 0
      merchant-promotion-ui-demo.html

+ 546 - 0
meituan-checkout-demo.html

@@ -0,0 +1,546 @@
+<!DOCTYPE html>
+<html lang="zh-TW">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
+<title>美團外賣 - 結算頁優惠券選擇 Demo</title>
+<style>
+* { margin: 0; padding: 0; box-sizing: border-box; }
+body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang TC', 'Microsoft JhengHei', sans-serif; background: #f5f5f5; color: #333; }
+
+.phone-frame {
+  width: 375px; height: 812px; margin: 20px auto;
+  border: 3px solid #333; border-radius: 40px; overflow: hidden;
+  background: #f5f5f5; position: relative;
+  box-shadow: 0 10px 40px rgba(0,0,0,0.2);
+}
+
+/* Header */
+.header {
+  background: linear-gradient(135deg, #FFD100, #FFB800);
+  padding: 44px 16px 12px; position: sticky; top: 0; z-index: 10;
+}
+.header h1 { font-size: 18px; font-weight: 600; text-align: center; }
+
+/* Scrollable content */
+.content { height: calc(812px - 56px - 60px); overflow-y: auto; padding-bottom: 20px; }
+
+/* Section card */
+.card {
+  background: #fff; margin: 10px 12px; border-radius: 12px;
+  padding: 14px 16px; box-shadow: 0 1px 4px rgba(0,0,0,0.06);
+}
+
+/* Address */
+.address-section { display: flex; align-items: center; gap: 10px; }
+.address-icon { font-size: 22px; }
+.address-info { flex: 1; }
+.address-name { font-size: 15px; font-weight: 600; }
+.address-name span { font-weight: 400; color: #999; margin-left: 8px; font-size: 13px; }
+.address-detail { font-size: 13px; color: #666; margin-top: 2px; }
+.address-arrow { color: #ccc; font-size: 16px; }
+
+/* Items */
+.section-title { font-size: 15px; font-weight: 600; margin-bottom: 10px; color: #333; }
+.merchant-tag {
+  display: inline-block; background: #FFF3E0; color: #FF8C00;
+  font-size: 11px; padding: 2px 6px; border-radius: 4px; margin-left: 8px; font-weight: 400;
+}
+
+.order-item {
+  display: flex; justify-content: space-between; align-items: center;
+  padding: 8px 0; border-bottom: 1px solid #f5f5f5;
+}
+.order-item:last-child { border-bottom: none; }
+.item-name { font-size: 14px; color: #333; }
+.item-name .qty { color: #999; margin-left: 4px; font-size: 12px; }
+.item-price { font-size: 14px; color: #333; }
+.subtotal-row { display: flex; justify-content: space-between; padding-top: 10px; border-top: 1px dashed #eee; margin-top: 4px; }
+.subtotal-label { font-size: 14px; color: #666; }
+.subtotal-amount { font-size: 15px; font-weight: 600; }
+
+/* Discount section */
+.discount-item {
+  display: flex; justify-content: space-between; align-items: center;
+  padding: 10px 0; border-bottom: 1px solid #f8f8f8;
+}
+.discount-item:last-child { border-bottom: none; }
+
+.discount-left { display: flex; align-items: center; gap: 8px; flex: 1; }
+.discount-label { font-size: 14px; color: #333; }
+.discount-tag {
+  font-size: 10px; padding: 1px 5px; border-radius: 3px; font-weight: 500;
+}
+.tag-auto { background: #E8F5E9; color: #4CAF50; }
+.tag-merchant { background: #FFF3E0; color: #FF8C00; }
+.tag-platform { background: #E3F2FD; color: #1E88E5; }
+
+.discount-amount { font-size: 14px; color: #FF5722; font-weight: 500; }
+
+.discount-select {
+  display: flex; align-items: center; gap: 4px;
+  font-size: 13px; color: #999; cursor: pointer;
+}
+.discount-select .arrow { font-size: 14px; transition: transform 0.2s; }
+
+/* Fee rows */
+.fee-row {
+  display: flex; justify-content: space-between; padding: 6px 0;
+  font-size: 13px; color: #999;
+}
+
+/* Total bar */
+.total-bar {
+  display: flex; justify-content: space-between; align-items: center;
+  padding-top: 12px; border-top: 1px solid #eee; margin-top: 4px;
+}
+.saved-amount { font-size: 13px; color: #FF5722; }
+.total-amount { font-size: 20px; font-weight: 700; color: #333; }
+.total-amount span { font-size: 13px; color: #999; margin-right: 4px; }
+
+/* Misc options */
+.misc-item {
+  display: flex; justify-content: space-between; align-items: center;
+  padding: 10px 0; border-bottom: 1px solid #f5f5f5;
+}
+.misc-item:last-child { border-bottom: none; }
+.misc-label { font-size: 14px; color: #333; }
+.misc-value { font-size: 13px; color: #999; display: flex; align-items: center; gap: 4px; }
+
+/* Submit button */
+.submit-bar {
+  position: absolute; bottom: 0; left: 0; right: 0;
+  background: #fff; padding: 10px 16px 28px;
+  border-top: 1px solid #eee; display: flex; align-items: center; justify-content: space-between;
+}
+.submit-info { font-size: 13px; color: #999; }
+.submit-info .price { font-size: 22px; color: #FF5722; font-weight: 700; }
+.submit-btn {
+  background: linear-gradient(135deg, #FFD100, #FFB800); border: none;
+  padding: 12px 32px; border-radius: 24px; font-size: 16px;
+  font-weight: 600; color: #333; cursor: pointer;
+}
+.submit-btn:hover { filter: brightness(1.05); }
+
+/* Bottom Sheet Overlay */
+.sheet-overlay {
+  position: absolute; top: 0; left: 0; right: 0; bottom: 0;
+  background: rgba(0,0,0,0.5); z-index: 100;
+  opacity: 0; pointer-events: none; transition: opacity 0.3s;
+}
+.sheet-overlay.active { opacity: 1; pointer-events: auto; }
+
+/* Bottom Sheet */
+.bottom-sheet {
+  position: absolute; bottom: 0; left: 0; right: 0;
+  background: #fff; border-radius: 16px 16px 0 0;
+  z-index: 101; transform: translateY(100%);
+  transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+  max-height: 70%;
+}
+.bottom-sheet.active { transform: translateY(0); }
+
+.sheet-header {
+  display: flex; justify-content: space-between; align-items: center;
+  padding: 16px 20px; border-bottom: 1px solid #f0f0f0;
+  position: sticky; top: 0; background: #fff; z-index: 1;
+}
+.sheet-title { font-size: 16px; font-weight: 600; }
+.sheet-close {
+  width: 28px; height: 28px; border-radius: 50%; background: #f0f0f0;
+  display: flex; align-items: center; justify-content: center;
+  font-size: 16px; color: #999; cursor: pointer; border: none;
+}
+
+.sheet-body { overflow-y: auto; max-height: calc(70vh - 56px); padding: 12px 16px 28px; }
+
+/* Coupon card */
+.coupon-card {
+  display: flex; border-radius: 10px; margin-bottom: 10px;
+  overflow: hidden; cursor: pointer; border: 2px solid transparent;
+  transition: border-color 0.2s, opacity 0.2s;
+  position: relative;
+}
+.coupon-card.selected { border-color: #FF5722; }
+.coupon-card.disabled { opacity: 0.5; cursor: not-allowed; }
+
+.coupon-amount-side {
+  width: 90px; display: flex; flex-direction: column;
+  align-items: center; justify-content: center;
+  padding: 12px 8px; color: #fff;
+}
+.coupon-amount-side.merchant { background: linear-gradient(135deg, #FF8C00, #FF6D00); }
+.coupon-amount-side.platform { background: linear-gradient(135deg, #1E88E5, #1565C0); }
+.coupon-amount-side.disabled-bg { background: #bbb !important; }
+
+.coupon-symbol { font-size: 12px; }
+.coupon-value { font-size: 28px; font-weight: 700; line-height: 1.1; }
+.coupon-condition { font-size: 10px; margin-top: 2px; opacity: 0.9; }
+
+.coupon-info-side {
+  flex: 1; padding: 10px 14px; background: #fff;
+  display: flex; flex-direction: column; justify-content: center;
+}
+.coupon-name { font-size: 14px; font-weight: 600; color: #333; }
+.coupon-desc { font-size: 12px; color: #999; margin-top: 3px; }
+.coupon-expire { font-size: 11px; color: #bbb; margin-top: 3px; }
+.coupon-reason { font-size: 11px; color: #FF5722; margin-top: 3px; }
+
+.coupon-check {
+  position: absolute; top: 10px; right: 10px;
+  width: 22px; height: 22px; border-radius: 50%;
+  display: flex; align-items: center; justify-content: center;
+}
+.coupon-check.checked { background: #FF5722; color: #fff; }
+.coupon-check.unchecked { border: 1.5px solid #ddd; }
+
+.sheet-divider {
+  text-align: center; color: #ccc; font-size: 12px;
+  padding: 10px 0; position: relative;
+}
+.sheet-divider::before, .sheet-divider::after {
+  content: ''; position: absolute; top: 50%; width: 30%;
+  border-top: 1px solid #eee;
+}
+.sheet-divider::before { left: 0; }
+.sheet-divider::after { right: 0; }
+
+/* Tabs in sheet */
+.sheet-tabs {
+  display: flex; border-bottom: 1px solid #f0f0f0; margin-bottom: 4px;
+}
+.sheet-tab {
+  flex: 1; text-align: center; padding: 10px; font-size: 14px;
+  color: #999; cursor: pointer; position: relative;
+}
+.sheet-tab.active { color: #333; font-weight: 600; }
+.sheet-tab.active::after {
+  content: ''; position: absolute; bottom: 0; left: 30%; right: 30%;
+  height: 3px; background: #FF5722; border-radius: 2px;
+}
+.sheet-tab .badge {
+  display: inline-block; background: #FF5722; color: #fff;
+  font-size: 10px; padding: 0 5px; border-radius: 8px;
+  margin-left: 2px; vertical-align: top;
+}
+
+/* Realtime update flash */
+.flash { animation: flashHighlight 0.6s ease; }
+@keyframes flashHighlight {
+  0% { background: #FFF8E1; }
+  100% { background: transparent; }
+}
+
+/* Responsive */
+@media (max-width: 400px) {
+  .phone-frame { width: 100%; height: 100vh; border: none; border-radius: 0; margin: 0; }
+  .content { height: calc(100vh - 56px - 60px); }
+}
+</style>
+</head>
+<body>
+
+<div class="phone-frame">
+  <div class="header">
+    <h1>提交訂單</h1>
+  </div>
+
+  <div class="content">
+    <!-- Address -->
+    <div class="card">
+      <div class="address-section">
+        <div class="address-icon">📍</div>
+        <div class="address-info">
+          <div class="address-name">張先生<span>138****8888</span></div>
+          <div class="address-detail">台北市中山區南京東路一段120號3樓</div>
+        </div>
+        <div class="address-arrow">›</div>
+      </div>
+    </div>
+
+    <!-- Delivery time -->
+    <div class="card" style="padding: 12px 16px;">
+      <div style="display:flex; justify-content:space-between; align-items:center;">
+        <span style="font-size:14px;">🕐 盡快送達(約25-35分鐘)</span>
+        <span style="font-size:13px; color:#999;">›</span>
+      </div>
+    </div>
+
+    <!-- Order items -->
+    <div class="card">
+      <div class="section-title">
+        好吃便當店
+        <span class="merchant-tag">品牌</span>
+      </div>
+      <div class="order-item">
+        <span class="item-name">招牌排骨便當<span class="qty">×1</span></span>
+        <span class="item-price">¥85</span>
+      </div>
+      <div class="order-item">
+        <span class="item-name">珍珠奶茶<span class="qty">×2</span></span>
+        <span class="item-price">¥60</span>
+      </div>
+      <div class="subtotal-row">
+        <span class="subtotal-label">小計</span>
+        <span class="subtotal-amount">¥145</span>
+      </div>
+    </div>
+
+    <!-- Discounts -->
+    <div class="card" id="discountCard">
+      <div class="section-title">優惠明細</div>
+
+      <!-- 商家滿減 (auto) -->
+      <div class="discount-item">
+        <div class="discount-left">
+          <span class="discount-label">商家滿減</span>
+          <span class="discount-tag tag-auto">自動</span>
+        </div>
+        <span class="discount-amount" id="merchantPromo">-¥20</span>
+      </div>
+
+      <!-- 商家優惠券 (selectable) -->
+      <div class="discount-item" id="merchantCouponRow" onclick="openSheet('merchant')">
+        <div class="discount-left">
+          <span class="discount-label">商家優惠券</span>
+          <span class="discount-tag tag-merchant">選1張</span>
+        </div>
+        <div class="discount-select">
+          <span class="discount-amount" id="merchantCouponAmount">-¥15</span>
+          <span class="arrow">›</span>
+        </div>
+      </div>
+
+      <!-- 平台紅包 (selectable) -->
+      <div class="discount-item" id="platformCouponRow" onclick="openSheet('platform')">
+        <div class="discount-left">
+          <span class="discount-label">平台紅包</span>
+          <span class="discount-tag tag-platform">選1張</span>
+        </div>
+        <div class="discount-select">
+          <span class="discount-amount" id="platformCouponAmount">-¥18</span>
+          <span class="arrow">›</span>
+        </div>
+      </div>
+
+      <div style="height: 4px;"></div>
+
+      <div class="fee-row">
+        <span>配送費</span><span>¥15</span>
+      </div>
+      <div class="fee-row">
+        <span>包裝費</span><span>¥5</span>
+      </div>
+    </div>
+
+    <!-- Misc -->
+    <div class="card">
+      <div class="misc-item">
+        <span class="misc-label">📝 備註</span>
+        <span class="misc-value">口味偏好等<span>›</span></span>
+      </div>
+      <div class="misc-item">
+        <span class="misc-label">🥢 餐具</span>
+        <span class="misc-value">1份<span>›</span></span>
+      </div>
+      <div class="misc-item">
+        <span class="misc-label">🧾 發票</span>
+        <span class="misc-value">不需要<span>›</span></span>
+      </div>
+    </div>
+
+    <!-- Total -->
+    <div class="card" id="totalCard">
+      <div class="total-bar">
+        <span class="saved-amount">已優惠 ¥53</span>
+        <div>
+          <span class="total-amount"><span>實付</span>¥112</span>
+        </div>
+      </div>
+    </div>
+  </div>
+
+  <!-- Submit bar -->
+  <div class="submit-bar">
+    <div class="submit-info">
+      <span>實付 </span><span class="price" id="submitPrice">¥112</span>
+    </div>
+    <button class="submit-btn">提交訂單</button>
+  </div>
+
+  <!-- Overlay -->
+  <div class="sheet-overlay" id="sheetOverlay" onclick="closeSheet()"></div>
+
+  <!-- Merchant coupon sheet -->
+  <div class="bottom-sheet" id="merchantSheet">
+    <div class="sheet-header">
+      <span class="sheet-title">商家優惠券</span>
+      <button class="sheet-close" onclick="closeSheet()">✕</button>
+    </div>
+    <div class="sheet-tabs">
+      <div class="sheet-tab active">可用<span class="badge">3</span></div>
+      <div class="sheet-tab">不可用<span class="badge">1</span></div>
+    </div>
+    <div class="sheet-body" id="merchantSheetBody"></div>
+  </div>
+
+  <!-- Platform coupon sheet -->
+  <div class="bottom-sheet" id="platformSheet">
+    <div class="sheet-header">
+      <span class="sheet-title">平台紅包</span>
+      <button class="sheet-close" onclick="closeSheet()">✕</button>
+    </div>
+    <div class="sheet-tabs">
+      <div class="sheet-tab active">可用<span class="badge">3</span></div>
+      <div class="sheet-tab">不可用<span class="badge">1</span></div>
+    </div>
+    <div class="sheet-body" id="platformSheetBody"></div>
+  </div>
+</div>
+
+<script>
+const subtotal = 145;
+const deliveryFee = 15;
+const packageFee = 5;
+const merchantPromo = 20; // fixed auto
+
+// Available merchant coupons
+const merchantCoupons = [
+  { id: 'm1', name: '滿減券', desc: '全店通用', condition: '滿100元可用', threshold: 100, amount: 15, expire: '2026-06-30', usable: true },
+  { id: 'm2', name: '滿減券', desc: '全店通用', condition: '滿80元可用', threshold: 80, amount: 10, expire: '2026-06-15', usable: true },
+  { id: 'm3', name: '折扣券', desc: '限奶茶類', condition: '滿50元可用', threshold: 50, amount: 5, expire: '2026-06-20', usable: true },
+  { id: 'm4', name: '滿減券', desc: '全店通用', condition: '滿200元可用', threshold: 200, amount: 40, expire: '2026-06-25', usable: false, reason: '訂單不滿200元,無法使用' },
+];
+
+// Available platform coupons
+const platformCoupons = [
+  { id: 'p1', name: '天天神券', desc: '全場通用', condition: '滿50元可用', threshold: 50, amount: 18, expire: '2026-06-28', usable: true },
+  { id: 'p2', name: '會員專享券', desc: '全場通用', condition: '滿100元可用', threshold: 100, amount: 25, expire: '2026-06-30', usable: true },
+  { id: 'p3', name: '新人紅包', desc: '全場通用', condition: '滿30元可用', threshold: 30, amount: 5, expire: '2026-06-22', usable: true },
+  { id: 'p4', name: '神會員券', desc: '全場通用', condition: '滿50元可用', threshold: 50, amount: 15, expire: '2026-07-01', usable: false, reason: '需開通神會員' },
+];
+
+let selectedMerchant = 'm1';
+let selectedPlatform = 'p1';
+
+function getDiscountedSubtotal() {
+  return subtotal - merchantPromo;
+}
+
+function calcTotal() {
+  const mc = merchantCoupons.find(c => c.id === selectedMerchant);
+  const pc = platformCoupons.find(c => c.id === selectedPlatform);
+  const mcAmt = mc ? mc.amount : 0;
+  const pcAmt = pc ? pc.amount : 0;
+  const totalSaved = merchantPromo + mcAmt + pcAmt;
+  const total = subtotal - totalSaved + deliveryFee + packageFee;
+  return { mcAmt, pcAmt, totalSaved, total };
+}
+
+function updateUI() {
+  const { mcAmt, pcAmt, totalSaved, total } = calcTotal();
+  document.getElementById('merchantCouponAmount').textContent = mcAmt ? `-¥${mcAmt}` : '未選擇';
+  document.getElementById('platformCouponAmount').textContent = pcAmt ? `-¥${pcAmt}` : '未選擇';
+
+  const totalCard = document.getElementById('totalCard');
+  totalCard.innerHTML = `<div class="total-bar">
+    <span class="saved-amount">已優惠 ¥${totalSaved}</span>
+    <div><span class="total-amount"><span>實付</span>¥${total}</span></div>
+  </div>`;
+
+  document.getElementById('submitPrice').textContent = `¥${total}`;
+
+  // Flash effect
+  totalCard.classList.remove('flash');
+  void totalCard.offsetWidth;
+  totalCard.classList.add('flash');
+}
+
+function renderCoupons(coupons, selectedId, type) {
+  const bodyId = type === 'merchant' ? 'merchantSheetBody' : 'platformSheetBody';
+  const body = document.getElementById(bodyId);
+
+  const usable = coupons.filter(c => c.usable);
+  const unusable = coupons.filter(c => !c.usable);
+
+  let html = '';
+
+  usable.forEach(c => {
+    const sel = c.id === selectedId;
+    const sideClass = type === 'merchant' ? 'merchant' : 'platform';
+    html += `
+      <div class="coupon-card ${sel ? 'selected' : ''}" onclick="selectCoupon('${type}', '${c.id}')">
+        <div class="coupon-amount-side ${sideClass}">
+          <span class="coupon-symbol">¥</span>
+          <span class="coupon-value">${c.amount}</span>
+          <span class="coupon-condition">${c.condition}</span>
+        </div>
+        <div class="coupon-info-side">
+          <div class="coupon-name">${c.name}</div>
+          <div class="coupon-desc">${c.desc}</div>
+          <div class="coupon-expire">有效期至 ${c.expire}</div>
+        </div>
+        <div class="coupon-check ${sel ? 'checked' : 'unchecked'}">${sel ? '✓' : ''}</div>
+      </div>`;
+  });
+
+  if (unusable.length > 0) {
+    html += `<div class="sheet-divider">不可用</div>`;
+    unusable.forEach(c => {
+      html += `
+        <div class="coupon-card disabled">
+          <div class="coupon-amount-side disabled-bg">
+            <span class="coupon-symbol">¥</span>
+            <span class="coupon-value">${c.amount}</span>
+            <span class="coupon-condition">${c.condition}</span>
+          </div>
+          <div class="coupon-info-side">
+            <div class="coupon-name">${c.name}</div>
+            <div class="coupon-desc">${c.desc}</div>
+            <div class="coupon-reason">${c.reason}</div>
+          </div>
+        </div>`;
+    });
+  }
+
+  body.innerHTML = html;
+}
+
+function selectCoupon(type, id) {
+  if (type === 'merchant') {
+    selectedMerchant = selectedMerchant === id ? null : id;
+    renderCoupons(merchantCoupons, selectedMerchant, 'merchant');
+  } else {
+    selectedPlatform = selectedPlatform === id ? null : id;
+    renderCoupons(platformCoupons, selectedPlatform, 'platform');
+  }
+  updateUI();
+}
+
+function openSheet(type) {
+  const overlay = document.getElementById('sheetOverlay');
+  overlay.classList.add('active');
+
+  if (type === 'merchant') {
+    const sheet = document.getElementById('merchantSheet');
+    sheet.classList.add('active');
+    renderCoupons(merchantCoupons, selectedMerchant, 'merchant');
+  } else {
+    const sheet = document.getElementById('platformSheet');
+    sheet.classList.add('active');
+    renderCoupons(platformCoupons, selectedPlatform, 'platform');
+  }
+}
+
+function closeSheet() {
+  document.getElementById('sheetOverlay').classList.remove('active');
+  document.getElementById('merchantSheet').classList.remove('active');
+  document.getElementById('platformSheet').classList.remove('active');
+}
+
+// Init
+updateUI();
+</script>
+
+</body>
+</html>

+ 679 - 0
meituan-promotion-types-demo.html

@@ -0,0 +1,679 @@
+<!DOCTYPE html>
+<html lang="zh-CN">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>美团商家促销类型解析</title>
+<style>
+  * { margin: 0; padding: 0; box-sizing: border-box; }
+  body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif; background: #f5f5f5; color: #333; padding: 16px; max-width: 480px; margin: 0 auto; }
+
+  .page-title { text-align: center; font-size: 20px; font-weight: 700; margin: 12px 0 20px; color: #333; }
+
+  /* Category Section */
+  .category { background: #fff; border-radius: 12px; margin-bottom: 16px; overflow: hidden; box-shadow: 0 1px 4px rgba(0,0,0,0.08); }
+  .category-header { padding: 14px 16px; display: flex; align-items: center; gap: 10px; }
+  .category-header .icon { width: 36px; height: 36px; border-radius: 8px; display: flex; align-items: center; justify-content: center; font-size: 18px; color: #fff; flex-shrink: 0; }
+  .category-header .info h3 { font-size: 16px; font-weight: 600; }
+  .category-header .info p { font-size: 12px; color: #999; margin-top: 2px; }
+  .cat-order .icon { background: linear-gradient(135deg, #ff6b35, #ff4500); }
+  .cat-item .icon { background: linear-gradient(135deg, #4facfe, #2196f3); }
+  .cat-delivery .icon { background: linear-gradient(135deg, #43e97b, #38b249); }
+  .cat-coupon .icon { background: linear-gradient(135deg, #fa709a, #e91e63); }
+
+  /* Promo Card */
+  .promo-card { padding: 12px 16px; border-top: 1px solid #f0f0f0; }
+  .promo-card .promo-title { font-size: 14px; font-weight: 600; margin-bottom: 6px; display: flex; align-items: center; gap: 6px; }
+  .promo-card .promo-title .badge { font-size: 10px; padding: 1px 6px; border-radius: 3px; color: #fff; font-weight: 500; }
+  .badge-auto { background: #ff6b35; }
+  .badge-select { background: #2196f3; }
+  .badge-manual { background: #e91e63; }
+  .promo-card .promo-desc { font-size: 12px; color: #666; margin-bottom: 8px; line-height: 1.6; }
+  .promo-card .example { background: #fff8f0; border-radius: 8px; padding: 10px 12px; font-size: 12px; }
+  .cat-item .promo-card .example { background: #f0f7ff; }
+  .cat-delivery .promo-card .example { background: #f0fff4; }
+  .cat-coupon .promo-card .example { background: #fff0f5; }
+  .example .label { color: #999; margin-bottom: 4px; }
+  .example .calc { color: #333; line-height: 1.8; }
+  .example .calc .price { color: #ff4500; font-weight: 600; }
+  .example .calc .line-through { text-decoration: line-through; color: #999; }
+  .example .calc .highlight { background: #fff3cd; padding: 0 3px; border-radius: 2px; }
+
+  /* Interactive Demo */
+  .demo-section { background: #fff; border-radius: 12px; margin-bottom: 16px; overflow: hidden; box-shadow: 0 1px 4px rgba(0,0,0,0.08); }
+  .demo-header { padding: 14px 16px; background: linear-gradient(135deg, #667eea, #764ba2); color: #fff; }
+  .demo-header h3 { font-size: 16px; font-weight: 600; }
+  .demo-header p { font-size: 12px; opacity: 0.8; margin-top: 2px; }
+
+  /* Order Items */
+  .order-items { padding: 12px 16px; }
+  .order-item { display: flex; justify-content: space-between; align-items: center; padding: 8px 0; border-bottom: 1px solid #f5f5f5; font-size: 13px; }
+  .order-item:last-child { border-bottom: none; }
+  .order-item .name { flex: 1; }
+  .order-item .item-price { color: #333; font-weight: 500; min-width: 60px; text-align: right; }
+  .order-item .item-final { color: #ff4500; font-weight: 600; min-width: 60px; text-align: right; }
+  .order-item .item-tag { font-size: 10px; padding: 1px 5px; border-radius: 3px; color: #fff; margin-left: 6px; }
+  .item-tag-discount { background: #2196f3; }
+  .item-tag-normal { background: #ccc; }
+
+  /* Calculation Steps */
+  .calc-steps { padding: 0 16px 16px; }
+  .calc-step { display: flex; justify-content: space-between; align-items: center; padding: 6px 0; font-size: 13px; }
+  .calc-step .step-label { color: #666; display: flex; align-items: center; gap: 6px; }
+  .calc-step .step-label .step-icon { width: 20px; height: 20px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 10px; color: #fff; flex-shrink: 0; }
+  .step-order { background: #ff6b35; }
+  .step-item { background: #2196f3; }
+  .step-delivery { background: #43e97b; }
+  .step-coupon { background: #e91e63; }
+  .calc-step .step-value { font-weight: 500; }
+  .step-minus { color: #ff4500; }
+  .step-plus { color: #333; }
+  .calc-divider { border-top: 1px dashed #ddd; margin: 6px 0; }
+  .calc-total { display: flex; justify-content: space-between; padding: 8px 0; font-size: 16px; font-weight: 700; }
+  .calc-total .total-price { color: #ff4500; }
+
+  /* Toggle switches */
+  .toggle-section { padding: 12px 16px; background: #fafafa; border-top: 1px solid #f0f0f0; }
+  .toggle-row { display: flex; align-items: center; justify-content: space-between; padding: 6px 0; font-size: 13px; }
+  .toggle-row .toggle-info { display: flex; align-items: center; gap: 6px; }
+  .toggle-row .toggle-info .dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
+  .toggle { width: 44px; height: 24px; border-radius: 12px; background: #ddd; position: relative; cursor: pointer; transition: background 0.2s; flex-shrink: 0; }
+  .toggle.active { background: #4facfe; }
+  .toggle .knob { width: 20px; height: 20px; border-radius: 50%; background: #fff; position: absolute; top: 2px; left: 2px; transition: left 0.2s; box-shadow: 0 1px 3px rgba(0,0,0,0.2); }
+  .toggle.active .knob { left: 22px; }
+
+  /* Tabs */
+  .tabs { display: flex; border-bottom: 1px solid #f0f0f0; }
+  .tab { flex: 1; text-align: center; padding: 12px 0; font-size: 14px; color: #999; cursor: pointer; position: relative; transition: color 0.2s; }
+  .tab.active { color: #ff6b35; font-weight: 600; }
+  .tab.active::after { content: ''; position: absolute; bottom: 0; left: 30%; right: 30%; height: 3px; background: #ff6b35; border-radius: 2px; }
+  .tab-content { display: none; }
+  .tab-content.active { display: block; }
+
+  /* Stack diagram */
+  .stack-diagram { padding: 16px; }
+  .stack-bar { display: flex; align-items: center; margin-bottom: 8px; }
+  .stack-bar .bar-label { width: 80px; font-size: 11px; color: #666; text-align: right; padding-right: 10px; flex-shrink: 0; }
+  .stack-bar .bar-track { flex: 1; height: 28px; background: #f5f5f5; border-radius: 6px; position: relative; overflow: hidden; }
+  .stack-bar .bar-fill { height: 100%; border-radius: 6px; display: flex; align-items: center; justify-content: center; font-size: 11px; color: #fff; font-weight: 600; transition: width 0.5s ease; }
+  .bar-original { background: linear-gradient(90deg, #b0b0b0, #999); }
+  .bar-promo { background: linear-gradient(90deg, #ff6b35, #ff4500); }
+  .bar-discount { background: linear-gradient(90deg, #4facfe, #2196f3); }
+  .bar-delivery { background: linear-gradient(90deg, #43e97b, #38b249); }
+  .bar-coupon { background: linear-gradient(90deg, #fa709a, #e91e63); }
+  .bar-final { background: linear-gradient(90deg, #667eea, #764ba2); }
+
+  .note { font-size: 11px; color: #999; padding: 0 16px 12px; line-height: 1.6; }
+
+  /* Exclusive table */
+  .mutex-table { padding: 12px 16px; }
+  .mutex-table table { width: 100%; border-collapse: collapse; font-size: 11px; }
+  .mutex-table th { background: #f8f8f8; padding: 6px 4px; text-align: center; border: 1px solid #eee; font-weight: 600; color: #333; }
+  .mutex-table td { padding: 6px 4px; text-align: center; border: 1px solid #eee; }
+  .mutex-yes { color: #43e97b; font-weight: 700; }
+  .mutex-no { color: #ff4500; font-weight: 700; }
+  .mutex-self { color: #ccc; }
+</style>
+</head>
+<body>
+
+<div class="page-title">美团商家促销类型解析</div>
+
+<!-- Tabs -->
+<div class="category">
+  <div class="tabs">
+    <div class="tab active" onclick="switchTab('overview')">类型总览</div>
+    <div class="tab" onclick="switchTab('demo')">算价演示</div>
+    <div class="tab" onclick="switchTab('mutex')">互斥规则</div>
+  </div>
+
+  <!-- Tab 1: Overview -->
+  <div id="tab-overview" class="tab-content active">
+    <!-- Order Level -->
+    <div class="category cat-order">
+      <div class="category-header">
+        <div class="icon">📦</div>
+        <div class="info">
+          <h3>订单级促销</h3>
+          <p>作用在"订单总价"上</p>
+        </div>
+      </div>
+      <div class="promo-card">
+        <div class="promo-title">满减活动 <span class="badge badge-auto">自动生效</span></div>
+        <div class="promo-desc">订单金额达到X元自动减免Y元,可设多档位。全场商品参与,不需要选商品。</div>
+        <div class="example">
+          <div class="label">📋 示例</div>
+          <div class="calc">
+            满减档位:满20减5 / 满40减12 / 满60减20<br>
+            订单商品合计 <span class="price">¥45</span> → 命中"满40减12"<br>
+            实付商品金额 = 45 - 12 = <span class="price">¥33</span>
+          </div>
+        </div>
+      </div>
+      <div class="promo-card">
+        <div class="promo-title">门店新客立减 <span class="badge badge-auto">自动生效</span></div>
+        <div class="promo-desc">首次在本店下单的用户,订单总价直接减X元。仅限新客。</div>
+        <div class="example">
+          <div class="label">📋 示例</div>
+          <div class="calc">
+            新客立减 <span class="price">¥3</span><br>
+            新客下单 ¥45 → 实付 = 45 - 3 = <span class="price">¥42</span>
+          </div>
+        </div>
+      </div>
+    </div>
+
+    <!-- Item Level -->
+    <div class="category cat-item">
+      <div class="category-header">
+        <div class="icon">🏷️</div>
+        <div class="info">
+          <h3>商品级促销</h3>
+          <p>作用在"指定商品单价"上</p>
+        </div>
+      </div>
+      <div class="promo-card">
+        <div class="promo-title">折扣商品 <span class="badge badge-select">选商品</span></div>
+        <div class="promo-desc">商家选择特定商品设折扣价。只有被选中的商品打折,其他商品原价。自动创建"折扣"专区。</div>
+        <div class="example">
+          <div class="label">📋 示例</div>
+          <div class="calc">
+            宫保鸡丁 原价 <span class="line-through">¥20</span> → 7折 = <span class="price">¥14</span><br>
+            麻婆豆腐 原价 ¥18(未设折扣,原价)<br>
+            订单合计 = 14 + 18 = <span class="price">¥32</span>
+          </div>
+        </div>
+      </div>
+      <div class="promo-card">
+        <div class="promo-title">第二份半价 <span class="badge badge-select">选商品</span></div>
+        <div class="promo-desc">选择特定商品,买2件时第2件半价。按商品单独计算。</div>
+        <div class="example">
+          <div class="label">📋 示例</div>
+          <div class="calc">
+            可乐 原价 ¥6,买2件<br>
+            = 6 + 6×0.5 = <span class="price">¥9</span>(省¥3)<br>
+            不是对订单总价打折,是这1件商品的价格变了
+          </div>
+        </div>
+      </div>
+      <div class="promo-card">
+        <div class="promo-title">爆品特价 <span class="badge badge-select">选商品</span></div>
+        <div class="promo-desc">选择特定商品设超低特价引流,可与满减/折扣同享(独特优势)。</div>
+        <div class="example">
+          <div class="label">📋 示例</div>
+          <div class="calc">
+            招牌鸡腿饭 原价 <span class="line-through">¥25</span> → 爆品价 <span class="price">¥9.9</span><br>
+            爆品价 ¥9.9 仍然计入满减的订单金额
+          </div>
+        </div>
+      </div>
+    </div>
+
+    <!-- Delivery Level -->
+    <div class="category cat-delivery">
+      <div class="category-header">
+        <div class="icon">🛵</div>
+        <div class="info">
+          <h3>配送费级促销</h3>
+          <p>作用在"配送费"上</p>
+        </div>
+      </div>
+      <div class="promo-card">
+        <div class="promo-title">减配送费 <span class="badge badge-auto">自动生效</span></div>
+        <div class="promo-desc">商家固定补贴X元配送费,所有用户自动享受。</div>
+        <div class="example">
+          <div class="label">📋 示例</div>
+          <div class="calc">
+            配送费 ¥5 → 商家补贴 ¥2 → 用户付 <span class="price">¥3</span><br>
+            与商品价格无关,只扣配送费
+          </div>
+        </div>
+      </div>
+      <div class="promo-card">
+        <div class="promo-title">满减运费 <span class="badge badge-auto">自动生效</span></div>
+        <div class="promo-desc">订单金额满X元免配送费。与"减配送费"互斥二选一。</div>
+        <div class="example">
+          <div class="label">📋 示例</div>
+          <div class="calc">
+            满30元免配送费<br>
+            订单 ¥35 → 配送费 <span class="price">¥0</span><br>
+            订单 ¥25 → 配送费 ¥5(未达标)
+          </div>
+        </div>
+      </div>
+    </div>
+
+    <!-- Coupon Level -->
+    <div class="category cat-coupon">
+      <div class="category-header">
+        <div class="icon">🎫</div>
+        <div class="info">
+          <h3>优惠券类(商家券)</h3>
+          <p>用户需先领券,下单时手动选择使用</p>
+        </div>
+      </div>
+      <div class="promo-card">
+        <div class="promo-title">满减券 <span class="badge badge-manual">用户选券</span></div>
+        <div class="promo-desc">分"同享券"(可与满减叠加)和"互斥券"(不可与满减/折扣叠加)。作用在订单总价上。</div>
+        <div class="example">
+          <div class="label">📋 示例</div>
+          <div class="calc">
+            满30减5元券(同享券)<br>
+            满减后 ¥33 → 再扣券 → <span class="price">¥28</span>
+          </div>
+        </div>
+      </div>
+      <div class="promo-card">
+        <div class="promo-title">商品券 <span class="badge badge-manual">用户选券</span></div>
+        <div class="promo-desc">只能用于指定商品。分"折扣券"(指定商品X折)和"抵用券"(指定商品免费兑换)。</div>
+        <div class="example">
+          <div class="label">📋 示例</div>
+          <div class="calc">
+            抵用券:免费兑换1杯可乐(原价¥6)<br>
+            折扣券:宫保鸡丁5折券(¥20 → <span class="price">¥10</span>)
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+
+  <!-- Tab 2: Demo -->
+  <div id="tab-demo" class="tab-content">
+    <div class="demo-section">
+      <div class="demo-header">
+        <h3>💰 算价演示</h3>
+        <p>开启/关闭不同促销,看价格怎么变</p>
+      </div>
+
+      <div class="toggle-section">
+        <div class="toggle-row">
+          <div class="toggle-info">
+            <div class="dot" style="background:#ff6b35"></div>
+            <span>满减(满40减12)</span>
+          </div>
+          <div class="toggle active" onclick="toggle(this, 'manjian')"><div class="knob"></div></div>
+        </div>
+        <div class="toggle-row">
+          <div class="toggle-info">
+            <div class="dot" style="background:#2196f3"></div>
+            <span>折扣(宫保鸡丁7折)</span>
+          </div>
+          <div class="toggle" onclick="toggle(this, 'zhekou')"><div class="knob"></div></div>
+        </div>
+        <div class="toggle-row">
+          <div class="toggle-info">
+            <div class="dot" style="background:#43e97b"></div>
+            <span>减配送费(减¥2)</span>
+          </div>
+          <div class="toggle active" onclick="toggle(this, 'delivery')"><div class="knob"></div></div>
+        </div>
+        <div class="toggle-row">
+          <div class="toggle-info">
+            <div class="dot" style="background:#e91e63"></div>
+            <span>商家满减券(满30减5)</span>
+          </div>
+          <div class="toggle active" onclick="toggle(this, 'coupon')"><div class="knob"></div></div>
+        </div>
+      </div>
+
+      <div class="order-items">
+        <div class="order-item">
+          <span class="name">🍗 宫保鸡丁</span>
+          <span class="item-price" id="item1-price">¥20</span>
+          <span class="item-final" id="item1-final">¥20</span>
+          <span class="item-tag item-tag-normal" id="item1-tag">原价</span>
+        </div>
+        <div class="order-item">
+          <span class="name">🥘 麻婆豆腐</span>
+          <span class="item-price">¥18</span>
+          <span class="item-final">¥18</span>
+        </div>
+        <div class="order-item">
+          <span class="name">🥤 可乐×2</span>
+          <span class="item-price">¥12</span>
+          <span class="item-final">¥12</span>
+        </div>
+        <div class="order-item">
+          <span class="name">📦 餐盒费</span>
+          <span class="item-price">¥3</span>
+          <span class="item-final">¥3</span>
+        </div>
+      </div>
+
+      <div class="calc-steps">
+        <div class="calc-step">
+          <span class="step-label"><span class="step-icon step-order">📦</span> 商品原价合计</span>
+          <span class="step-value" id="step-original">¥53</span>
+        </div>
+        <div class="calc-step" id="row-zhekou">
+          <span class="step-label"><span class="step-icon step-item">🏷️</span> 折扣(宫保鸡丁7折)</span>
+          <span class="step-value step-minus" id="step-zhekou">—</span>
+        </div>
+        <div class="calc-step" id="row-discount-subtotal">
+          <span class="step-label" style="color:#999; font-size:12px;">&nbsp;&nbsp;&nbsp;折扣后小计</span>
+          <span class="step-value" id="step-discount-subtotal" style="color:#999; font-size:12px;">—</span>
+        </div>
+        <div class="calc-step" id="row-manjian">
+          <span class="step-label"><span class="step-icon step-order">📦</span> 满减(满40减12)</span>
+          <span class="step-value step-minus" id="step-manjian">-¥12</span>
+        </div>
+        <div class="calc-step" id="row-coupon">
+          <span class="step-label"><span class="step-icon step-coupon">🎫</span> 商家满减券(满30减5)</span>
+          <span class="step-value step-minus" id="step-coupon">-¥5</span>
+        </div>
+        <div class="calc-divider"></div>
+        <div class="calc-step">
+          <span class="step-label">商品实付</span>
+          <span class="step-value" id="step-item-total">¥36</span>
+        </div>
+        <div class="calc-step" id="row-delivery">
+          <span class="step-label"><span class="step-icon step-delivery">🛵</span> 配送费(原¥5,减¥2)</span>
+          <span class="step-value step-plus" id="step-delivery">+¥3</span>
+        </div>
+        <div class="calc-divider"></div>
+        <div class="calc-total">
+          <span>实付金额</span>
+          <span class="total-price" id="step-total">¥39</span>
+        </div>
+      </div>
+    </div>
+
+    <!-- Stack Diagram -->
+    <div class="category">
+      <div class="category-header">
+        <div class="icon">📊</div>
+        <div class="info">
+          <h3>价格叠加图</h3>
+          <p>各层级优惠怎么从原价扣到实付</p>
+        </div>
+      </div>
+      <div class="stack-diagram">
+        <div class="stack-bar">
+          <span class="bar-label">商品原价</span>
+          <div class="bar-track"><div class="bar-fill bar-original" id="bar-original" style="width:100%">¥53</div></div>
+        </div>
+        <div class="stack-bar" id="bar-zhekou-row">
+          <span class="bar-label">商品折扣</span>
+          <div class="bar-track"><div class="bar-fill bar-discount" id="bar-zhekou" style="width:0%"></div></div>
+        </div>
+        <div class="stack-bar" id="bar-manjian-row">
+          <span class="bar-label">订单满减</span>
+          <div class="bar-track"><div class="bar-fill bar-promo" id="bar-manjian" style="width:22%">-¥12</div></div>
+        </div>
+        <div class="stack-bar" id="bar-coupon-row">
+          <span class="bar-label">商家券</span>
+          <div class="bar-track"><div class="bar-fill bar-coupon" id="bar-coupon" style="width:9%">-¥5</div></div>
+        </div>
+        <div class="stack-bar" id="bar-delivery-row">
+          <span class="bar-label">配送费</span>
+          <div class="bar-track"><div class="bar-fill bar-delivery" id="bar-delivery" style="width:5%">+¥3</div></div>
+        </div>
+        <div class="stack-bar">
+          <span class="bar-label" style="font-weight:700;">实付</span>
+          <div class="bar-track"><div class="bar-fill bar-final" id="bar-final" style="width:73%">¥39</div></div>
+        </div>
+      </div>
+      <div class="note">
+        💡 每一层作用在不同的价格基础上:商品折扣改单价 → 满减看折后订单总额 → 券再扣 → 配送费单独加
+      </div>
+    </div>
+  </div>
+
+  <!-- Tab 3: Mutex -->
+  <div id="tab-mutex" class="tab-content">
+    <div class="category">
+      <div class="category-header">
+        <div class="icon">⚠️</div>
+        <div class="info">
+          <h3>互斥规则</h3>
+          <p>哪些促销不能同时使用</p>
+        </div>
+      </div>
+      <div class="mutex-table">
+        <table>
+          <tr>
+            <th></th>
+            <th>满减</th>
+            <th>折扣</th>
+            <th>第二份<br>半价</th>
+            <th>新客<br>立减</th>
+            <th>减配<br>送费</th>
+            <th>满减<br>运费</th>
+          </tr>
+          <tr>
+            <th>满减</th>
+            <td class="mutex-self">—</td>
+            <td class="mutex-no">✗</td>
+            <td class="mutex-no">✗</td>
+            <td class="mutex-yes">✓</td>
+            <td class="mutex-yes">✓</td>
+            <td class="mutex-yes">✓</td>
+          </tr>
+          <tr>
+            <th>折扣</th>
+            <td class="mutex-no">✗</td>
+            <td class="mutex-self">—</td>
+            <td class="mutex-no">✗</td>
+            <td class="mutex-yes">✓</td>
+            <td class="mutex-yes">✓</td>
+            <td class="mutex-yes">✓</td>
+          </tr>
+          <tr>
+            <th>第二份半价</th>
+            <td class="mutex-no">✗</td>
+            <td class="mutex-no">✗</td>
+            <td class="mutex-self">—</td>
+            <td class="mutex-no">✗</td>
+            <td class="mutex-yes">✓</td>
+            <td class="mutex-yes">✓</td>
+          </tr>
+          <tr>
+            <th>新客立减</th>
+            <td class="mutex-yes">✓</td>
+            <td class="mutex-yes">✓</td>
+            <td class="mutex-no">✗</td>
+            <td class="mutex-self">—</td>
+            <td class="mutex-yes">✓</td>
+            <td class="mutex-yes">✓</td>
+          </tr>
+          <tr>
+            <th>减配送费</th>
+            <td class="mutex-yes">✓</td>
+            <td class="mutex-yes">✓</td>
+            <td class="mutex-yes">✓</td>
+            <td class="mutex-yes">✓</td>
+            <td class="mutex-self">—</td>
+            <td class="mutex-no">✗</td>
+          </tr>
+          <tr>
+            <th>满减运费</th>
+            <td class="mutex-yes">✓</td>
+            <td class="mutex-yes">✓</td>
+            <td class="mutex-yes">✓</td>
+            <td class="mutex-yes">✓</td>
+            <td class="mutex-no">✗</td>
+            <td class="mutex-self">—</td>
+          </tr>
+        </table>
+      </div>
+      <div class="note">
+        ✓ = 可同时使用 &nbsp;&nbsp; ✗ = 互斥不可同时使用<br><br>
+        💡 核心互斥链:满减 ↔ 折扣 ↔ 第二份半价(三选一)<br>
+        💡 配送费互斥:减配送费 ↔ 满减运费(二选一)<br>
+        💡 满赠、售卖代金券、下单返券与所有活动同享
+      </div>
+    </div>
+  </div>
+
+</div>
+
+<script>
+// State
+const state = {
+  manjian: true,
+  zhekou: false,
+  delivery: true,
+  coupon: true
+};
+
+// Prices
+const ORIGINAL_PRICE = { chicken: 20, tofu: 18, cola: 12, box: 3 }; // cola = 6*2
+const DELIVERY_FEE = 5;
+const MANJIAN_THRESHOLD = 40;
+const MANJIAN_AMOUNT = 12;
+const ZHEKOU_RATE = 0.7;
+const DELIVERY_DISCOUNT = 2;
+const COUPON_THRESHOLD = 30;
+const COUPON_AMOUNT = 5;
+
+function toggle(el, key) {
+  // 满减和折扣互斥
+  if (key === 'manjian' && !state.manjian) {
+    state.zhekou = false;
+    document.querySelectorAll('.toggle')[1].classList.remove('active');
+  }
+  if (key === 'zhekou' && !state.zhekou) {
+    state.manjian = false;
+    document.querySelectorAll('.toggle')[0].classList.remove('active');
+  }
+
+  state[key] = !state[key];
+  el.classList.toggle('active');
+  recalculate();
+}
+
+function recalculate() {
+  let chicken = ORIGINAL_PRICE.chicken;
+  let itemDiscount = 0;
+
+  // 折扣
+  if (state.zhekou) {
+    chicken = Math.round(ORIGINAL_PRICE.chicken * ZHEKOU_RATE * 10) / 10;
+    itemDiscount = ORIGINAL_PRICE.chicken - chicken;
+    document.getElementById('item1-final').textContent = '¥' + chicken;
+    document.getElementById('item1-tag').textContent = '7折';
+    document.getElementById('item1-tag').className = 'item-tag item-tag-discount';
+  } else {
+    document.getElementById('item1-final').textContent = '¥' + ORIGINAL_PRICE.chicken;
+    document.getElementById('item1-tag').textContent = '原价';
+    document.getElementById('item1-tag').className = 'item-tag item-tag-normal';
+  }
+
+  let subtotal = chicken + ORIGINAL_PRICE.tofu + ORIGINAL_PRICE.cola + ORIGINAL_PRICE.box;
+
+  // 满减
+  let manjianAmount = 0;
+  if (state.manjian && subtotal >= MANJIAN_THRESHOLD) {
+    manjianAmount = MANJIAN_AMOUNT;
+  }
+
+  // 券
+  let couponAmount = 0;
+  let afterManjian = subtotal - manjianAmount;
+  if (state.coupon && afterManjian >= COUPON_THRESHOLD) {
+    couponAmount = COUPON_AMOUNT;
+  }
+
+  let itemTotal = subtotal - manjianAmount - couponAmount;
+
+  // 配送费
+  let deliveryPay = DELIVERY_FEE;
+  if (state.delivery) {
+    deliveryPay = DELIVERY_FEE - DELIVERY_DISCOUNT;
+  }
+
+  let total = itemTotal + deliveryPay;
+
+  // Update UI
+  document.getElementById('step-original').textContent = '¥' + ORIGINAL_PRICE.chicken;
+
+  if (state.zhekou) {
+    document.getElementById('row-zhekou').style.display = 'flex';
+    document.getElementById('row-discount-subtotal').style.display = 'flex';
+    document.getElementById('step-zhekou').textContent = '-¥' + itemDiscount.toFixed(1);
+    document.getElementById('step-discount-subtotal').textContent = '¥' + subtotal;
+    document.getElementById('step-original').textContent = '¥' + (ORIGINAL_PRICE.chicken + ORIGINAL_PRICE.tofu + ORIGINAL_PRICE.cola + ORIGINAL_PRICE.box);
+  } else {
+    document.getElementById('row-zhekou').style.display = 'none';
+    document.getElementById('row-discount-subtotal').style.display = 'none';
+  }
+
+  if (state.manjian && manjianAmount > 0) {
+    document.getElementById('row-manjian').style.display = 'flex';
+    document.getElementById('step-manjian').textContent = '-¥' + manjianAmount;
+  } else {
+    document.getElementById('row-manjian').style.display = state.manjian ? 'flex' : 'none';
+    document.getElementById('step-manjian').textContent = state.manjian ? '未达到¥' + MANJIAN_THRESHOLD : '';
+    document.getElementById('step-manjian').style.color = state.manjian && manjianAmount === 0 ? '#999' : '#ff4500';
+  }
+
+  if (state.coupon) {
+    document.getElementById('row-coupon').style.display = 'flex';
+    document.getElementById('step-coupon').textContent = couponAmount > 0 ? '-¥' + couponAmount : '未达到¥' + COUPON_THRESHOLD;
+    document.getElementById('step-coupon').style.color = couponAmount > 0 ? '#ff4500' : '#999';
+  } else {
+    document.getElementById('row-coupon').style.display = 'none';
+  }
+
+  document.getElementById('step-item-total').textContent = '¥' + itemTotal;
+
+  if (state.delivery) {
+    document.getElementById('row-delivery').style.display = 'flex';
+    document.getElementById('step-delivery').textContent = '+¥' + deliveryPay + '(原¥' + DELIVERY_FEE + ')';
+  } else {
+    document.getElementById('row-delivery').style.display = 'flex';
+    document.getElementById('step-delivery').textContent = '+¥' + DELIVERY_FEE;
+  }
+
+  document.getElementById('step-total').textContent = '¥' + total;
+
+  // Update bars
+  let maxVal = ORIGINAL_PRICE.chicken + ORIGINAL_PRICE.tofu + ORIGINAL_PRICE.cola + ORIGINAL_PRICE.box + DELIVERY_FEE;
+  document.getElementById('bar-original').style.width = ((subtotal + itemDiscount) / maxVal * 100) + '%';
+  document.getElementById('bar-original').textContent = '¥' + (subtotal + itemDiscount);
+
+  if (state.zhekou) {
+    document.getElementById('bar-zhekou-row').style.display = 'flex';
+    document.getElementById('bar-zhekou').style.width = (itemDiscount / maxVal * 100) + '%';
+    document.getElementById('bar-zhekou').textContent = '-¥' + itemDiscount.toFixed(1);
+  } else {
+    document.getElementById('bar-zhekou-row').style.display = 'none';
+  }
+
+  if (state.manjian && manjianAmount > 0) {
+    document.getElementById('bar-manjian-row').style.display = 'flex';
+    document.getElementById('bar-manjian').style.width = (manjianAmount / maxVal * 100) + '%';
+    document.getElementById('bar-manjian').textContent = '-¥' + manjianAmount;
+  } else {
+    document.getElementById('bar-manjian-row').style.display = 'none';
+  }
+
+  if (state.coupon && couponAmount > 0) {
+    document.getElementById('bar-coupon-row').style.display = 'flex';
+    document.getElementById('bar-coupon').style.width = (couponAmount / maxVal * 100) + '%';
+    document.getElementById('bar-coupon').textContent = '-¥' + couponAmount;
+  } else {
+    document.getElementById('bar-coupon-row').style.display = 'none';
+  }
+
+  document.getElementById('bar-delivery').style.width = (deliveryPay / maxVal * 100) + '%';
+  document.getElementById('bar-delivery').textContent = '+¥' + deliveryPay;
+
+  document.getElementById('bar-final').style.width = (total / maxVal * 100) + '%';
+  document.getElementById('bar-final').textContent = '¥' + total;
+}
+
+function switchTab(name) {
+  document.querySelectorAll('.tab').forEach((t, i) => {
+    t.classList.toggle('active', ['overview', 'demo', 'mutex'][i] === name);
+  });
+  document.querySelectorAll('.tab-content').forEach(tc => tc.classList.remove('active'));
+  document.getElementById('tab-' + name).classList.add('active');
+}
+
+// Init
+recalculate();
+</script>
+
+</body>
+</html>

+ 895 - 0
merchant-promotion-ui-demo.html

@@ -0,0 +1,895 @@
+<!DOCTYPE html>
+<html lang="zh-CN">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>商家促销管理 - 操作演示</title>
+<style>
+* { margin: 0; padding: 0; box-sizing: border-box; }
+body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif; background: #f0f2f5; color: #333; }
+
+/* Layout */
+.app { max-width: 900px; margin: 0 auto; min-height: 100vh; }
+.header { background: #fff; padding: 16px 20px; border-bottom: 1px solid #e8e8e8; display: flex; justify-content: space-between; align-items: center; position: sticky; top: 0; z-index: 100; }
+.header h1 { font-size: 18px; font-weight: 600; }
+.header .store-name { font-size: 13px; color: #999; }
+
+/* Content */
+.content { padding: 16px; }
+
+/* Button */
+.btn { padding: 8px 20px; border-radius: 6px; border: none; cursor: pointer; font-size: 14px; font-weight: 500; transition: all 0.2s; }
+.btn-primary { background: #ff6b35; color: #fff; }
+.btn-primary:hover { background: #e55a2b; }
+.btn-default { background: #fff; color: #666; border: 1px solid #d9d9d9; }
+.btn-default:hover { border-color: #ff6b35; color: #ff6b35; }
+.btn-danger { background: #fff; color: #ff4d4f; border: 1px solid #ff4d4f; }
+.btn-danger:hover { background: #fff1f0; }
+.btn-sm { padding: 4px 12px; font-size: 12px; }
+.btn:disabled { opacity: 0.5; cursor: not-allowed; }
+
+/* Card */
+.card { background: #fff; border-radius: 8px; margin-bottom: 12px; box-shadow: 0 1px 3px rgba(0,0,0,0.06); }
+.card-header { padding: 14px 16px; border-bottom: 1px solid #f0f0f0; display: flex; justify-content: space-between; align-items: center; }
+.card-header h3 { font-size: 15px; font-weight: 600; }
+.card-body { padding: 16px; }
+
+/* Status Tags */
+.tag { display: inline-block; padding: 2px 8px; border-radius: 4px; font-size: 12px; font-weight: 500; }
+.tag-active { background: #f6ffed; color: #52c41a; border: 1px solid #b7eb8f; }
+.tag-pending { background: #fff7e6; color: #fa8c16; border: 1px solid #ffd591; }
+.tag-expired { background: #f5f5f5; color: #999; border: 1px solid #d9d9d9; }
+
+/* Promotion List Item */
+.promo-item { padding: 14px 16px; border-bottom: 1px solid #f5f5f5; display: flex; justify-content: space-between; align-items: center; }
+.promo-item:last-child { border-bottom: none; }
+.promo-info { flex: 1; }
+.promo-info .name { font-size: 14px; font-weight: 500; margin-bottom: 4px; }
+.promo-info .detail { font-size: 12px; color: #999; }
+.promo-info .detail span { margin-right: 12px; }
+.promo-type-icon { display: inline-block; width: 22px; height: 22px; border-radius: 4px; text-align: center; line-height: 22px; font-size: 12px; color: #fff; margin-right: 6px; vertical-align: middle; }
+.type-manjian { background: #ff6b35; }
+.type-zhekou { background: #2196f3; }
+.type-ban { background: #9c27b0; }
+.type-xinke { background: #43e97b; }
+
+/* Modal / Drawer */
+.drawer-overlay { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.4); z-index: 200; display: none; }
+.drawer-overlay.show { display: block; }
+.drawer { position: fixed; right: -600px; top: 0; bottom: 0; width: 580px; max-width: 100%; background: #fff; z-index: 201; transition: right 0.3s ease; display: flex; flex-direction: column; }
+.drawer.show { right: 0; }
+.drawer-header { padding: 16px 20px; border-bottom: 1px solid #e8e8e8; display: flex; justify-content: space-between; align-items: center; }
+.drawer-header h2 { font-size: 17px; font-weight: 600; }
+.drawer-close { font-size: 20px; cursor: pointer; color: #999; background: none; border: none; padding: 4px; }
+.drawer-body { flex: 1; overflow-y: auto; padding: 20px; }
+.drawer-footer { padding: 14px 20px; border-top: 1px solid #e8e8e8; display: flex; justify-content: flex-end; gap: 10px; }
+
+/* Form */
+.form-group { margin-bottom: 20px; }
+.form-label { display: block; font-size: 14px; font-weight: 500; margin-bottom: 8px; color: #333; }
+.form-label .required { color: #ff4d4f; margin-right: 2px; }
+.form-hint { font-size: 12px; color: #999; margin-top: 4px; }
+.form-input { width: 100%; padding: 8px 12px; border: 1px solid #d9d9d9; border-radius: 6px; font-size: 14px; outline: none; transition: border-color 0.2s; }
+.form-input:focus { border-color: #ff6b35; box-shadow: 0 0 0 2px rgba(255,107,53,0.1); }
+
+/* Type Selector */
+.type-selector { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; }
+.type-card { border: 2px solid #e8e8e8; border-radius: 8px; padding: 14px 10px; text-align: center; cursor: pointer; transition: all 0.2s; }
+.type-card:hover { border-color: #ff6b35; }
+.type-card.selected { border-color: #ff6b35; background: #fff8f0; }
+.type-card .icon { font-size: 24px; margin-bottom: 6px; }
+.type-card .label { font-size: 13px; font-weight: 500; }
+.type-card .desc { font-size: 11px; color: #999; margin-top: 2px; }
+.type-card.disabled { opacity: 0.4; cursor: not-allowed; position: relative; }
+.type-card.disabled::after { content: '已互斥'; position: absolute; top: 4px; right: 4px; font-size: 10px; background: #ff4d4f; color: #fff; padding: 1px 5px; border-radius: 3px; }
+
+/* Tiers (满减) */
+.tier-row { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; }
+.tier-row .tier-text { font-size: 14px; color: #666; white-space: nowrap; }
+.tier-input { width: 80px; padding: 6px 10px; border: 1px solid #d9d9d9; border-radius: 4px; font-size: 14px; text-align: center; outline: none; }
+.tier-input:focus { border-color: #ff6b35; }
+.tier-remove { cursor: pointer; color: #ff4d4f; font-size: 18px; background: none; border: none; padding: 2px 6px; }
+.add-tier { color: #ff6b35; cursor: pointer; font-size: 13px; background: none; border: none; padding: 4px 0; }
+.add-tier:hover { text-decoration: underline; }
+
+/* Product Selector */
+.product-select-area { border: 1px solid #e8e8e8; border-radius: 8px; overflow: hidden; }
+.product-search { padding: 10px 12px; border-bottom: 1px solid #f0f0f0; }
+.product-search input { width: 100%; border: none; outline: none; font-size: 13px; padding: 4px 0; }
+.product-list { max-height: 320px; overflow-y: auto; }
+.product-item { display: flex; align-items: center; padding: 10px 12px; border-bottom: 1px solid #f5f5f5; transition: background 0.15s; }
+.product-item:hover { background: #fafafa; }
+.product-item.selected { background: #fff8f0; }
+.product-check { width: 18px; height: 18px; border-radius: 3px; border: 1px solid #d9d9d9; margin-right: 10px; cursor: pointer; display: flex; align-items: center; justify-content: center; flex-shrink: 0; transition: all 0.15s; }
+.product-item.selected .product-check { background: #ff6b35; border-color: #ff6b35; }
+.product-check::after { content: '✓'; color: #fff; font-size: 12px; font-weight: 700; display: none; }
+.product-item.selected .product-check::after { display: block; }
+.product-img { width: 40px; height: 40px; border-radius: 4px; background: #f5f5f5; margin-right: 10px; display: flex; align-items: center; justify-content: center; font-size: 20px; flex-shrink: 0; }
+.product-meta { flex: 1; min-width: 0; }
+.product-meta .pname { font-size: 13px; font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
+.product-meta .pprice { font-size: 12px; color: #999; }
+.product-rate { width: 72px; text-align: center; }
+.product-rate input { width: 52px; padding: 4px 6px; border: 1px solid #d9d9d9; border-radius: 4px; font-size: 13px; text-align: center; outline: none; }
+.product-rate input:focus { border-color: #ff6b35; }
+.product-rate span { font-size: 11px; color: #999; }
+.product-result { display: inline-block; padding: 2px 8px; background: #fff2e8; border-radius: 4px; font-size: 12px; color: #ff6b35; font-weight: 500; min-width: 50px; text-align: center; }
+
+/* Selected summary */
+.selected-summary { margin-top: 12px; padding: 12px; background: #fafafa; border-radius: 6px; }
+.selected-summary .title { font-size: 13px; font-weight: 500; margin-bottom: 8px; color: #666; }
+.selected-item { display: flex; align-items: center; padding: 6px 0; font-size: 13px; }
+.selected-item .sname { flex: 1; }
+.selected-item .srate { color: #ff6b35; font-weight: 500; margin-right: 8px; }
+.selected-item .sresult { color: #666; }
+.selected-item .sremove { color: #ff4d4f; cursor: pointer; margin-left: 8px; }
+
+/* Mutex Warning */
+.mutex-warning { background: #fff7e6; border: 1px solid #ffd591; border-radius: 6px; padding: 10px 14px; margin-bottom: 16px; display: flex; align-items: flex-start; gap: 8px; }
+.mutex-warning .icon { font-size: 16px; flex-shrink: 0; }
+.mutex-warning .text { font-size: 13px; color: #ad6800; line-height: 1.6; }
+
+/* Step indicator */
+.steps { display: flex; margin-bottom: 20px; }
+.step { flex: 1; text-align: center; position: relative; }
+.step .step-dot { width: 28px; height: 28px; border-radius: 50%; background: #e8e8e8; color: #999; display: inline-flex; align-items: center; justify-content: center; font-size: 13px; font-weight: 600; margin-bottom: 6px; }
+.step.active .step-dot { background: #ff6b35; color: #fff; }
+.step.done .step-dot { background: #52c41a; color: #fff; }
+.step .step-label { font-size: 12px; color: #999; display: block; }
+.step.active .step-label { color: #ff6b35; font-weight: 500; }
+.step::after { content: ''; position: absolute; top: 14px; left: 55%; right: -45%; height: 2px; background: #e8e8e8; }
+.step:last-child::after { display: none; }
+.step.done::after { background: #52c41a; }
+
+/* Empty state */
+.empty { text-align: center; padding: 40px 20px; color: #999; }
+.empty .icon { font-size: 48px; margin-bottom: 12px; }
+.empty p { font-size: 14px; margin-bottom: 16px; }
+
+/* Toast */
+.toast { position: fixed; top: 20px; left: 50%; transform: translateX(-50%); background: #52c41a; color: #fff; padding: 10px 24px; border-radius: 6px; font-size: 14px; z-index: 300; display: none; box-shadow: 0 4px 12px rgba(0,0,0,0.15); }
+.toast.show { display: block; animation: fadeInOut 2s ease; }
+@keyframes fadeInOut { 0% { opacity: 0; transform: translateX(-50%) translateY(-10px); } 15% { opacity: 1; transform: translateX(-50%) translateY(0); } 80% { opacity: 1; } 100% { opacity: 0; } }
+
+/* Tabs */
+.tabs { display: flex; border-bottom: 2px solid #f0f0f0; margin-bottom: 16px; }
+.tab-btn { padding: 10px 20px; font-size: 14px; color: #666; cursor: pointer; border: none; background: none; position: relative; font-weight: 500; }
+.tab-btn.active { color: #ff6b35; }
+.tab-btn.active::after { content: ''; position: absolute; bottom: -2px; left: 0; right: 0; height: 2px; background: #ff6b35; }
+.tab-panel { display: none; }
+.tab-panel.active { display: block; }
+
+/* Coupon batch card */
+.coupon-card { border: 1px solid #e8e8e8; border-radius: 8px; padding: 14px 16px; margin-bottom: 10px; position: relative; }
+.coupon-card::before { content: ''; position: absolute; left: 0; top: 12px; bottom: 12px; width: 3px; background: #ff6b35; border-radius: 0 2px 2px 0; }
+.coupon-card .coupon-name { font-size: 14px; font-weight: 600; margin-bottom: 6px; }
+.coupon-card .coupon-info { font-size: 12px; color: #999; display: flex; gap: 16px; }
+.coupon-card .coupon-stock { margin-top: 8px; }
+.stock-bar { height: 4px; background: #f0f0f0; border-radius: 2px; margin-top: 4px; }
+.stock-fill { height: 100%; background: #ff6b35; border-radius: 2px; transition: width 0.3s; }
+
+/* Divider */
+.divider { border: none; border-top: 1px dashed #e8e8e8; margin: 16px 0; }
+</style>
+</head>
+<body>
+
+<div class="app">
+  <div class="header">
+    <div>
+      <h1>营销管理</h1>
+      <span class="store-name">🍔 好味道快餐店</span>
+    </div>
+    <button class="btn btn-primary" onclick="openDrawer('promotion')">+ 创建促销活动</button>
+  </div>
+
+  <div class="content">
+    <!-- Tabs -->
+    <div class="tabs">
+      <button class="tab-btn active" onclick="switchMainTab('promotions')">促销活动</button>
+      <button class="tab-btn" onclick="switchMainTab('coupons')">优惠券管理</button>
+    </div>
+
+    <!-- Promotions Tab -->
+    <div id="panel-promotions" class="tab-panel active">
+      <div class="mutex-warning">
+        <span class="icon">⚠️</span>
+        <div class="text">
+          <strong>互斥规则:</strong>满减、折扣商品、第二份半价三种活动同一时间只能开启一种。新客立减可与以上任意一种叠加使用。
+        </div>
+      </div>
+
+      <div class="card">
+        <div class="card-header">
+          <h3>活动列表</h3>
+        </div>
+        <div id="promo-list">
+          <div class="promo-item">
+            <div class="promo-info">
+              <div class="name"><span class="promo-type-icon type-manjian">减</span> 全场满减</div>
+              <div class="detail"><span>满20减5 / 满40减12 / 满60减20</span><span>2026-06-01 ~ 2026-06-30</span></div>
+            </div>
+            <div style="display:flex;align-items:center;gap:8px;">
+              <span class="tag tag-active">进行中</span>
+              <button class="btn btn-sm btn-danger" onclick="endPromo(this)">结束</button>
+            </div>
+          </div>
+          <div class="promo-item">
+            <div class="promo-info">
+              <div class="name"><span class="promo-type-icon type-xinke">新</span> 新客立减</div>
+              <div class="detail"><span>新客立减3元</span><span>2026-06-01 ~ 2026-06-30</span></div>
+            </div>
+            <div style="display:flex;align-items:center;gap:8px;">
+              <span class="tag tag-active">进行中</span>
+              <button class="btn btn-sm btn-danger" onclick="endPromo(this)">结束</button>
+            </div>
+          </div>
+        </div>
+      </div>
+
+      <div class="card">
+        <div class="card-header"><h3>已结束活动</h3></div>
+        <div>
+          <div class="promo-item" style="opacity:0.5">
+            <div class="promo-info">
+              <div class="name"><span class="promo-type-icon type-zhekou">折</span> 夏日折扣</div>
+              <div class="detail"><span>3个商品参加折扣</span><span>2026-05-01 ~ 2026-05-15</span></div>
+            </div>
+            <span class="tag tag-expired">已结束</span>
+          </div>
+        </div>
+      </div>
+    </div>
+
+    <!-- Coupons Tab -->
+    <div id="panel-coupons" class="tab-panel">
+      <div class="card">
+        <div class="card-header">
+          <h3>优惠券列表</h3>
+          <button class="btn btn-primary btn-sm" onclick="openDrawer('coupon')">+ 创建优惠券</button>
+        </div>
+        <div id="coupon-list">
+          <div class="coupon-card">
+            <div class="coupon-name">满30减5优惠券(同享券)</div>
+            <div class="coupon-info">
+              <span>满减券 / 同享</span>
+              <span>有效期 7天</span>
+              <span>已领 45/100</span>
+            </div>
+            <div class="coupon-stock">
+              <div class="stock-bar"><div class="stock-fill" style="width:45%"></div></div>
+            </div>
+          </div>
+          <div class="coupon-card">
+            <div class="coupon-name">宫保鸡丁5折券</div>
+            <div class="coupon-info">
+              <span>商品券 / 折扣券</span>
+              <span>有效期 30天</span>
+              <span>已领 23/50</span>
+            </div>
+            <div class="coupon-stock">
+              <div class="stock-bar"><div class="stock-fill" style="width:46%"></div></div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</div>
+
+<!-- Drawer Overlay -->
+<div class="drawer-overlay" id="drawer-overlay" onclick="closeDrawer()"></div>
+
+<!-- Drawer -->
+<div class="drawer" id="drawer">
+  <div class="drawer-header">
+    <h2 id="drawer-title">创建促销活动</h2>
+    <button class="drawer-close" onclick="closeDrawer()">✕</button>
+  </div>
+  <div class="drawer-body" id="drawer-body">
+    <!-- Dynamic content -->
+  </div>
+  <div class="drawer-footer" id="drawer-footer">
+    <button class="btn btn-default" onclick="closeDrawer()">取消</button>
+    <button class="btn btn-primary" id="drawer-confirm" onclick="confirmCreate()">确认创建</button>
+  </div>
+</div>
+
+<!-- Toast -->
+<div class="toast" id="toast"></div>
+
+<script>
+// Products data
+const products = [
+  { id: 1, name: '宫保鸡丁', price: 20, emoji: '🍗' },
+  { id: 2, name: '麻婆豆腐', price: 18, emoji: '🥘' },
+  { id: 3, name: '可乐', price: 6, emoji: '🥤' },
+  { id: 4, name: '雪碧', price: 6, emoji: '🧊' },
+  { id: 5, name: '薯条', price: 8, emoji: '🍟' },
+  { id: 6, name: '鸡腿饭', price: 25, emoji: '🍚' },
+  { id: 7, name: '酸辣汤', price: 12, emoji: '🍲' },
+  { id: 8, name: '鸡蛋炒饭', price: 15, emoji: '🍳' },
+  { id: 9, name: '红烧肉', price: 28, emoji: '🥩' },
+  { id: 10, name: '青菜', price: 10, emoji: '🥬' },
+];
+
+let currentType = null;
+let selectedProducts = {};
+let drawerMode = '';
+
+function switchMainTab(name) {
+  document.querySelectorAll('.tab-btn').forEach((b, i) => b.classList.toggle('active', ['promotions','coupons'][i] === name));
+  document.querySelectorAll('.tab-panel').forEach(p => p.classList.remove('active'));
+  document.getElementById('panel-' + name).classList.add('active');
+}
+
+function openDrawer(mode) {
+  drawerMode = mode;
+  currentType = null;
+  selectedProducts = {};
+  document.getElementById('drawer-overlay').classList.add('show');
+  document.getElementById('drawer').classList.add('show');
+  document.getElementById('drawer-title').textContent = mode === 'promotion' ? '创建促销活动' : '创建优惠券';
+  renderDrawerContent();
+}
+
+function closeDrawer() {
+  document.getElementById('drawer-overlay').classList.remove('show');
+  document.getElementById('drawer').classList.remove('show');
+}
+
+function renderDrawerContent() {
+  const body = document.getElementById('drawer-body');
+  if (drawerMode === 'promotion') {
+    renderPromotionForm(body);
+  } else {
+    renderCouponForm(body);
+  }
+}
+
+function renderPromotionForm(body) {
+  body.innerHTML = `
+    <div class="steps" id="form-steps">
+      <div class="step active" id="step1"><span class="step-dot">1</span><span class="step-label">选择类型</span></div>
+      <div class="step" id="step2"><span class="step-dot">2</span><span class="step-label">设置规则</span></div>
+      <div class="step" id="step3"><span class="step-dot">3</span><span class="step-label">确认提交</span></div>
+    </div>
+    <div id="step-content">
+      ${renderStep1()}
+    </div>
+  `;
+}
+
+function renderStep1() {
+  const hasActive = { manjian: true, zhekou: false, ban: false }; // simulate: 满减 is active
+  return `
+    <div class="form-group">
+      <label class="form-label"><span class="required">*</span> 选择促销类型</label>
+      <div class="type-selector">
+        <div class="type-card ${currentType==='manjian'?'selected':''} ${hasActive.manjian?'disabled':''}"
+             onclick="${hasActive.manjian?'':'selectType(\"manjian\")'}">
+          <div class="icon">📦</div>
+          <div class="label">满减</div>
+          <div class="desc">订单满X减Y</div>
+          ${hasActive.manjian?'<div style="font-size:10px;color:#999;margin-top:4px">已有进行中活动</div>':''}
+        </div>
+        <div class="type-card ${currentType==='zhekou'?'selected':''} ${hasActive.zhekou?'disabled':''}"
+             onclick="${hasActive.zhekou?'':'selectType(\"zhekou\")'}">
+          <div class="icon">🏷️</div>
+          <div class="label">折扣商品</div>
+          <div class="desc">指定商品打折</div>
+          ${hasActive.zhekou?'<div style="font-size:10px;color:#999;margin-top:4px">与满减互斥</div>':''}
+        </div>
+        <div class="type-card ${currentType==='ban'?'selected':''} ${hasActive.ban?'disabled':''}"
+             onclick="${hasActive.ban?'':'selectType(\"ban\")'}">
+          <div class="icon">🔄</div>
+          <div class="label">第二份半价</div>
+          <div class="desc">买2件第2件半价</div>
+          ${hasActive.ban?'<div style="font-size:10px;color:#999;margin-top:4px">与满减互斥</div>':''}
+        </div>
+        <div class="type-card ${currentType==='xinke'?'selected':''}" onclick="selectType('xinke')">
+          <div class="icon">🆕</div>
+          <div class="label">新客立减</div>
+          <div class="desc">首次下单减X元</div>
+        </div>
+      </div>
+    </div>
+    <div style="text-align:right;margin-top:20px;">
+      <button class="btn btn-primary" onclick="goStep2()" ${currentType?'':'disabled'} id="next-step1">下一步 →</button>
+    </div>
+  `;
+}
+
+function selectType(type) {
+  currentType = type;
+  renderDrawerContent();
+}
+
+function goStep2() {
+  if (!currentType) return;
+  document.getElementById('step1').className = 'step done';
+  document.getElementById('step2').className = 'step active';
+  const content = document.getElementById('step-content');
+  content.innerHTML = renderStep2Content();
+}
+
+function renderStep2Content() {
+  let html = '';
+  if (currentType === 'manjian') {
+    html = renderManjianForm();
+  } else if (currentType === 'zhekou') {
+    html = renderZhekouForm();
+  } else if (currentType === 'ban') {
+    html = renderBanForm();
+  } else if (currentType === 'xinke') {
+    html = renderXinkeForm();
+  }
+  html += `
+    <hr class="divider">
+    <div class="form-group">
+      <label class="form-label">活动时间</label>
+      <div style="display:flex;gap:10px;align-items:center;">
+        <input type="date" class="form-input" style="width:auto" value="2026-06-01">
+        <span>~</span>
+        <input type="date" class="form-input" style="width:auto" value="2026-06-30">
+      </div>
+    </div>
+    <div style="display:flex;justify-content:space-between;margin-top:20px;">
+      <button class="btn btn-default" onclick="goBackStep1()">← 上一步</button>
+      <button class="btn btn-primary" onclick="goStep3()">下一步 →</button>
+    </div>
+  `;
+  return html;
+}
+
+function renderManjianForm() {
+  return `
+    <div class="form-group">
+      <label class="form-label"><span class="required">*</span> 设置满减档位</label>
+      <div class="form-hint" style="margin-bottom:12px">建议根据客单价分布设置2-3个档位,每个档位自动匹配最优</div>
+      <div id="tier-list">
+        <div class="tier-row" data-tier="0">
+          <span class="tier-text">满</span>
+          <input class="tier-input" type="number" placeholder="20" value="20">
+          <span class="tier-text">减</span>
+          <input class="tier-input" type="number" placeholder="5" value="5">
+          <span class="tier-text">元</span>
+        </div>
+        <div class="tier-row" data-tier="1">
+          <span class="tier-text">满</span>
+          <input class="tier-input" type="number" placeholder="40" value="40">
+          <span class="tier-text">减</span>
+          <input class="tier-input" type="number" placeholder="12" value="12">
+          <span class="tier-text">元</span>
+          <button class="tier-remove" onclick="removeTier(this)">✕</button>
+        </div>
+      </div>
+      <button class="add-tier" onclick="addTier()">+ 添加档位</button>
+    </div>
+  `;
+}
+
+function renderZhekouForm() {
+  return `
+    <div class="mutex-warning" style="margin-bottom:16px;">
+      <span class="icon">💡</span>
+      <div class="text">
+        <strong>操作说明:</strong>从下方商品列表中勾选要参加折扣的商品,<strong>每个商品可以设置不同的折扣率</strong>。
+        用户下单时,参加折扣的商品按折扣价计算,未参加的商品仍按原价。折扣商品和满减活动互斥,用户系统自动选最优。
+      </div>
+    </div>
+    <div class="form-group">
+      <label class="form-label"><span class="required">*</span> 选择折扣商品并设置折扣率</label>
+      <div class="product-select-area">
+        <div class="product-search">
+          <input type="text" placeholder="🔍 搜索商品名称..." oninput="filterProducts(this.value)">
+        </div>
+        <div class="product-list" id="product-list">
+          ${products.map(p => renderProductItem(p)).join('')}
+        </div>
+      </div>
+      <div class="selected-summary" id="selected-summary" style="display:none;">
+        <div class="title">已选商品(点击折扣率可修改)</div>
+        <div id="selected-items"></div>
+      </div>
+    </div>
+  `;
+}
+
+function renderProductItem(p) {
+  const sel = selectedProducts[p.id];
+  return `
+    <div class="product-item ${sel?'selected':''}" id="pitem-${p.id}">
+      <div class="product-check" onclick="toggleProduct(${p.id})">${sel?'✓':''}</div>
+      <div class="product-img">${p.emoji}</div>
+      <div class="product-meta">
+        <div class="pname">${p.name}</div>
+        <div class="pprice">原价 ¥${p.price}</div>
+      </div>
+      <div class="product-rate" id="prate-${p.id}" style="${sel?'':'opacity:0.3;pointer-events:none'}">
+        <input type="number" value="${sel?sel.rate:7}" min="1" max="9" step="0.1" onchange="updateRate(${p.id}, this.value)" style="width:48px"> <span>折</span>
+      </div>
+      <div class="product-result" id="presult-${p.id}">${sel?'¥'+(p.price*sel.rate/10).toFixed(1):'-'}</div>
+    </div>
+  `;
+}
+
+function toggleProduct(id) {
+  if (selectedProducts[id]) {
+    delete selectedProducts[id];
+  } else {
+    const p = products.find(x => x.id === id);
+    selectedProducts[id] = { rate: 7 }; // default 7折
+  }
+  refreshProductUI();
+}
+
+function updateRate(id, val) {
+  const rate = parseFloat(val) || 7;
+  selectedProducts[id].rate = rate;
+  refreshProductUI();
+}
+
+function refreshProductUI() {
+  const list = document.getElementById('product-list');
+  list.innerHTML = products.map(p => renderProductItem(p)).join('');
+  updateSelectedSummary();
+}
+
+function updateSelectedSummary() {
+  const summary = document.getElementById('selected-summary');
+  const items = document.getElementById('selected-items');
+  const ids = Object.keys(selectedProducts);
+  if (ids.length === 0) {
+    summary.style.display = 'none';
+    return;
+  }
+  summary.style.display = 'block';
+  items.innerHTML = ids.map(id => {
+    const p = products.find(x => x.id == id);
+    const rate = selectedProducts[id].rate;
+    const result = (p.price * rate / 10).toFixed(1);
+    return `
+      <div class="selected-item">
+        <span class="sname">${p.emoji} ${p.name}(原价¥${p.price})</span>
+        <span class="srate">${rate}折</span>
+        <span class="sresult">→ ¥${result}</span>
+        <span class="sremove" onclick="toggleProduct(${p.id})">移除</span>
+      </div>
+    `;
+  }).join('');
+}
+
+function filterProducts(keyword) {
+  const filtered = products.filter(p => p.name.includes(keyword));
+  document.getElementById('product-list').innerHTML = filtered.map(p => renderProductItem(p)).join('');
+  updateSelectedSummary();
+}
+
+function renderBanForm() {
+  return `
+    <div class="mutex-warning" style="margin-bottom:16px;">
+      <span class="icon">💡</span>
+      <div class="text">
+        <strong>操作说明:</strong>从商品列表中勾选参加"第二份半价"的商品。用户买2件同一商品时,第2件自动半价。
+      </div>
+    </div>
+    <div class="form-group">
+      <label class="form-label"><span class="required">*</span> 选择参加第二份半价的商品</label>
+      <div class="product-select-area">
+        <div class="product-list" id="product-list">
+          ${products.map(p => {
+            const sel = selectedProducts[p.id];
+            return `
+              <div class="product-item ${sel?'selected':''}" id="pitem-${p.id}">
+                <div class="product-check" onclick="toggleBanProduct(${p.id})">${sel?'✓':''}</div>
+                <div class="product-img">${p.emoji}</div>
+                <div class="product-meta">
+                  <div class="pname">${p.name}</div>
+                  <div class="pprice">原价 ¥${p.price} → 第2件 ¥${(p.price*0.5).toFixed(1)}</div>
+                </div>
+              </div>
+            `;
+          }).join('')}
+        </div>
+      </div>
+    </div>
+  `;
+}
+
+function toggleBanProduct(id) {
+  if (selectedProducts[id]) {
+    delete selectedProducts[id];
+  } else {
+    selectedProducts[id] = true;
+  }
+  document.getElementById('product-list').innerHTML = products.map(p => {
+    const sel = selectedProducts[p.id];
+    return `
+      <div class="product-item ${sel?'selected':''}" id="pitem-${p.id}">
+        <div class="product-check" onclick="toggleBanProduct(${p.id})">${sel?'✓':''}</div>
+        <div class="product-img">${p.emoji}</div>
+        <div class="product-meta">
+          <div class="pname">${p.name}</div>
+          <div class="pprice">原价 ¥${p.price} → 第2件 ¥${(p.price*0.5).toFixed(1)}</div>
+        </div>
+      </div>
+    `;
+  }).join('');
+}
+
+function renderXinkeForm() {
+  return `
+    <div class="form-group">
+      <label class="form-label"><span class="required">*</span> 新客立减金额</label>
+      <div style="display:flex;align-items:center;gap:8px;">
+        <span style="font-size:14px;color:#666;">首次在本店下单减</span>
+        <input class="tier-input" type="number" value="3" style="width:80px">
+        <span style="font-size:14px;color:#666;">元</span>
+      </div>
+      <div class="form-hint">建议设置2-5元,可与满减/折扣/第二份半价叠加使用</div>
+    </div>
+  `;
+}
+
+function addTier() {
+  const list = document.getElementById('tier-list');
+  const count = list.children.length;
+  const row = document.createElement('div');
+  row.className = 'tier-row';
+  row.dataset.tier = count;
+  row.innerHTML = `
+    <span class="tier-text">满</span>
+    <input class="tier-input" type="number" placeholder="60">
+    <span class="tier-text">减</span>
+    <input class="tier-input" type="number" placeholder="20">
+    <span class="tier-text">元</span>
+    <button class="tier-remove" onclick="removeTier(this)">✕</button>
+  `;
+  list.appendChild(row);
+}
+
+function removeTier(btn) {
+  btn.parentElement.remove();
+}
+
+function goBackStep1() {
+  document.getElementById('step1').className = 'step active';
+  document.getElementById('step2').className = 'step';
+  document.getElementById('step-content').innerHTML = renderStep1();
+}
+
+function goStep3() {
+  document.getElementById('step2').className = 'step done';
+  document.getElementById('step3').className = 'step active';
+  document.getElementById('step-content').innerHTML = renderStep3();
+  document.getElementById('drawer-footer').innerHTML = `
+    <button class="btn btn-default" onclick="goBackStep2()">上一步</button>
+    <button class="btn btn-primary" onclick="confirmCreate()">✓ 确认创建</button>
+  `;
+}
+
+function renderStep3() {
+  let summary = '';
+  const typeNames = { manjian: '满减活动', zhekou: '折扣商品', ban: '第二份半价', xinke: '新客立减' };
+  summary += `<div style="margin-bottom:12px;font-size:15px;font-weight:600;">📋 创建确认</div>`;
+  summary += `<div style="font-size:14px;margin-bottom:4px;"><strong>类型:</strong>${typeNames[currentType]}</div>`;
+
+  if (currentType === 'manjian') {
+    summary += `<div style="font-size:14px;margin-bottom:4px;"><strong>档位:</strong></div>`;
+    summary += `<div style="background:#fafafa;padding:10px;border-radius:6px;margin-bottom:12px;">`;
+    document.querySelectorAll('.tier-row').forEach(row => {
+      const inputs = row.querySelectorAll('.tier-input');
+      summary += `<div style="font-size:13px;padding:2px 0;">满 <strong>${inputs[0].value}</strong> 减 <strong style="color:#ff6b35">${inputs[1].value}</strong> 元</div>`;
+    });
+    summary += `</div>`;
+  } else if (currentType === 'zhekou') {
+    const ids = Object.keys(selectedProducts);
+    summary += `<div style="font-size:14px;margin-bottom:4px;"><strong>折扣商品(${ids.length}个):</strong></div>`;
+    summary += `<div style="background:#fafafa;padding:10px;border-radius:6px;margin-bottom:12px;">`;
+    ids.forEach(id => {
+      const p = products.find(x => x.id == id);
+      const rate = selectedProducts[id].rate;
+      summary += `<div style="font-size:13px;padding:2px 0;">${p.emoji} ${p.name}:原价¥${p.price} → <strong style="color:#ff6b35">${rate}折 ¥${(p.price*rate/10).toFixed(1)}</strong></div>`;
+    });
+    summary += `</div>`;
+  } else if (currentType === 'ban') {
+    const ids = Object.keys(selectedProducts);
+    summary += `<div style="font-size:14px;margin-bottom:4px;"><strong>参加商品(${ids.length}个):</strong></div>`;
+    summary += `<div style="background:#fafafa;padding:10px;border-radius:6px;margin-bottom:12px;">`;
+    ids.forEach(id => {
+      const p = products.find(x => x.id == id);
+      summary += `<div style="font-size:13px;padding:2px 0;">${p.emoji} ${p.name}:买2件 = ¥${p.price} + ¥${(p.price*0.5).toFixed(1)} = <strong style="color:#ff6b35">¥${(p.price*1.5).toFixed(1)}</strong></div>`;
+    });
+    summary += `</div>`;
+  } else if (currentType === 'xinke') {
+    summary += `<div style="font-size:14px;margin-bottom:4px;">新客首次下单减 <strong style="color:#ff6b35">3元</strong></div>`;
+  }
+
+  summary += `<div style="font-size:14px;"><strong>活动时间:</strong>2026-06-01 ~ 2026-06-30</div>`;
+  summary += `<div class="mutex-warning" style="margin-top:16px;">
+    <span class="icon">⚠️</span>
+    <div class="text">
+      ${currentType === 'xinke' ? '新客立减可与满减/折扣/第二份半价叠加使用' : '创建此活动后,由于互斥规则,与当前进行中的<strong>满减活动</strong>将需要二选一。请确认是否继续。'}
+    </div>
+  </div>`;
+
+  return summary;
+}
+
+function goBackStep2() {
+  document.getElementById('step2').className = 'step active';
+  document.getElementById('step3').className = 'step';
+  document.getElementById('step-content').innerHTML = renderStep2Content();
+  document.getElementById('drawer-footer').innerHTML = `
+    <button class="btn btn-default" onclick="closeDrawer()">取消</button>
+    <button class="btn btn-primary" onclick="goStep3()">下一步 →</button>
+  `;
+}
+
+function confirmCreate() {
+  closeDrawer();
+  showToast('✓ 促销活动创建成功!');
+}
+
+function endPromo(btn) {
+  if (confirm('确定要结束此活动吗?结束后不可恢复。')) {
+    const item = btn.closest('.promo-item');
+    item.querySelector('.tag').className = 'tag tag-expired';
+    item.querySelector('.tag').textContent = '已结束';
+    item.style.opacity = '0.5';
+    btn.remove();
+  }
+}
+
+function showToast(msg) {
+  const toast = document.getElementById('toast');
+  toast.textContent = msg;
+  toast.classList.add('show');
+  setTimeout(() => toast.classList.remove('show'), 2000);
+}
+
+// Coupon form
+function renderCouponForm(body) {
+  body.innerHTML = `
+    <div class="form-group">
+      <label class="form-label"><span class="required">*</span> 优惠券名称</label>
+      <input class="form-input" placeholder="如:满30减5优惠券" value="满30减5优惠券">
+    </div>
+    <div class="form-group">
+      <label class="form-label"><span class="required">*</span> 券类型</label>
+      <div style="display:flex;gap:10px;">
+        <div class="type-card selected" style="flex:1" onclick="selectCouponType(this, 'manjian')">
+          <div class="label">满减券</div>
+          <div class="desc">满X减Y元</div>
+        </div>
+        <div class="type-card" style="flex:1" onclick="selectCouponType(this, 'shangpin')">
+          <div class="label">商品券</div>
+          <div class="desc">指定商品折扣/兑换</div>
+        </div>
+      </div>
+    </div>
+    <div id="coupon-rule-area">
+      <div class="form-group">
+        <label class="form-label">同享/互斥</label>
+        <div style="display:flex;gap:10px;">
+          <label style="display:flex;align-items:center;gap:6px;cursor:pointer;font-size:14px;">
+            <input type="radio" name="mutex" value="0" checked> 同享券(可与满减叠加)
+          </label>
+          <label style="display:flex;align-items:center;gap:6px;cursor:pointer;font-size:14px;">
+            <input type="radio" name="mutex" value="1"> 互斥券(不可与满减叠加)
+          </label>
+        </div>
+      </div>
+      <div class="form-group">
+        <label class="form-label"><span class="required">*</span> 使用门槛</label>
+        <div style="display:flex;align-items:center;gap:8px;">
+          <span style="font-size:14px;color:#666;">满</span>
+          <input class="tier-input" type="number" value="30" style="width:80px">
+          <span style="font-size:14px;color:#666;">元可用,减</span>
+          <input class="tier-input" type="number" value="5" style="width:80px">
+          <span style="font-size:14px;color:#666;">元</span>
+        </div>
+      </div>
+    </div>
+    <hr class="divider">
+    <div class="form-group">
+      <label class="form-label"><span class="required">*</span> 发放总量</label>
+      <input class="form-input" type="number" value="100" style="width:200px">
+      <div class="form-hint">每个用户限领1张</div>
+    </div>
+    <div class="form-group">
+      <label class="form-label"><span class="required">*</span> 领取时间</label>
+      <div style="display:flex;gap:10px;align-items:center;">
+        <input type="date" class="form-input" style="width:auto" value="2026-06-01">
+        <span>~</span>
+        <input type="date" class="form-input" style="width:auto" value="2026-06-30">
+      </div>
+    </div>
+    <div class="form-group">
+      <label class="form-label"><span class="required">*</span> 领取后有效天数</label>
+      <div style="display:flex;gap:10px;align-items:center;">
+        <input class="form-input" type="number" value="7" style="width:100px">
+        <span style="font-size:14px;color:#666;">天</span>
+      </div>
+    </div>
+    <div class="mutex-warning">
+      <span class="icon">💡</span>
+      <div class="text">
+        发放方式:<strong>店内领券</strong> — 用户进入店铺时可在领券区看到并领取,下单时手动选择使用。
+      </div>
+    </div>
+  `;
+}
+
+function selectCouponType(el, type) {
+  el.parentElement.querySelectorAll('.type-card').forEach(c => c.classList.remove('selected'));
+  el.classList.add('selected');
+  const area = document.getElementById('coupon-rule-area');
+  if (type === 'shangpin') {
+    area.innerHTML = `
+      <div class="form-group">
+        <label class="form-label">商品券类型</label>
+        <div style="display:flex;gap:10px;">
+          <label style="display:flex;align-items:center;gap:6px;cursor:pointer;font-size:14px;">
+            <input type="radio" name="sp_type" value="discount" checked> 折扣券(指定商品X折)
+          </label>
+          <label style="display:flex;align-items:center;gap:6px;cursor:pointer;font-size:14px;">
+            <input type="radio" name="sp_type" value="exchange"> 抵用券(指定商品免费兑换)
+          </label>
+        </div>
+      </div>
+      <div class="form-group">
+        <label class="form-label"><span class="required">*</span> 选择适用商品</label>
+        <div class="product-select-area">
+          <div class="product-list" style="max-height:200px;">
+            ${products.slice(0,6).map(p => `
+              <div class="product-item" onclick="this.classList.toggle('selected');this.querySelector('.product-check').innerHTML=this.classList.contains('selected')?'✓':'';">
+                <div class="product-check"></div>
+                <div class="product-img">${p.emoji}</div>
+                <div class="product-meta">
+                  <div class="pname">${p.name}</div>
+                  <div class="pprice">¥${p.price}</div>
+                </div>
+              </div>
+            `).join('')}
+          </div>
+        </div>
+      </div>
+      <div class="form-group">
+        <label class="form-label"><span class="required">*</span> 折扣率</label>
+        <div style="display:flex;align-items:center;gap:8px;">
+          <input class="tier-input" type="number" value="5" step="0.1" style="width:80px">
+          <span style="font-size:14px;color:#666;">折</span>
+        </div>
+      </div>
+    `;
+  } else {
+    area.innerHTML = `
+      <div class="form-group">
+        <label class="form-label">同享/互斥</label>
+        <div style="display:flex;gap:10px;">
+          <label style="display:flex;align-items:center;gap:6px;cursor:pointer;font-size:14px;">
+            <input type="radio" name="mutex" value="0" checked> 同享券(可与满减叠加)
+          </label>
+          <label style="display:flex;align-items:center;gap:6px;cursor:pointer;font-size:14px;">
+            <input type="radio" name="mutex" value="1"> 互斥券(不可与满减叠加)
+          </label>
+        </div>
+      </div>
+      <div class="form-group">
+        <label class="form-label"><span class="required">*</span> 使用门槛</label>
+        <div style="display:flex;align-items:center;gap:8px;">
+          <span style="font-size:14px;color:#666;">满</span>
+          <input class="tier-input" type="number" value="30" style="width:80px">
+          <span style="font-size:14px;color:#666;">元可用,减</span>
+          <input class="tier-input" type="number" value="5" style="width:80px">
+          <span style="font-size:14px;color:#666;">元</span>
+        </div>
+      </div>
+    `;
+  }
+}
+</script>
+
+</body>
+</html>