首页/ phph5/ 甜趣蛋糕 · 烘焙蛋糕店 PHP+H5 商城 + 商家后台/ 代码预览

💻 代码预览 · 甜趣蛋糕 · 烘焙蛋糕店 PHP+H5 商城 + 商家后台

ph_mall · 上传者 驰旭教育
← 返回详情
r117/public/admin.js · 约 23508 字符
1/* 甜趣蛋糕 · 商家后台逻辑(PHP 后端 admin API;只读开关在 config.php 中控制) */2const AD = (function () {3  'use strict';4  let RO = false; // 只读5  let navs = [6    ['dashboard', '📊 看板'], ['products', '🍰 商品'], ['orders', '📦 订单'],7    ['members', '👥 会员'], ['coupons', '🎟️ 优惠券'], ['reviews', '⭐ 评价'],8    ['banners', '🖼️ 轮播'], ['custom', '🎨 定制'], ['settings', '⚙️ 设置']9  ];10  let cur = 'dashboard';1112  function $(s) { return document.querySelector(s); }13  function toast(m) { const t = $('#toast'); t.textContent = m; t.classList.add('show'); clearTimeout(t._t); t._t = setTimeout(() => t.classList.remove('show'), 1800); }14  function openSheet(h) { $('#sheet').innerHTML = h; $('#mask').classList.add('show'); }15  function closeSheet() { $('#mask').classList.remove('show'); }16  $('#mask').addEventListener('click', e => { if (e.target.id === 'mask') closeSheet(); });1718  async function adm(action, payload, method) {19    method = method || 'GET';20    try {21      const opt = { method, headers: { 'Content-Type': 'application/json' } };22      let url = 'index.php?c=admin&a=' + action;23      if (method === 'GET' && payload) url += '&' + new URLSearchParams(payload).toString();24      else if (payload) opt.body = JSON.stringify(payload);25      const r = await fetch(url, opt);26      if (!r.ok) throw 0;27      const j = await r.json();28      return j || { ok: 0 };29    } catch (e) { return { ok: 0, msg: '需运行在 PHP 后端环境' }; }30  }31  const esc = s => (s == null ? '' : String(s)).replace(/[&<>"]/g, c => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;' }[c]));3233  async function login() {34    const r = await adm('login', { user: $('#user').value, pass: $('#pass').value }, 'POST');35    if (r.ok) {36      $('#login').style.display = 'none'; $('#app').style.display = 'block';37      const cfg = await adm('config');38      RO = !!(cfg.read_only);39      $('#robar').style.display = RO ? 'block' : 'none';40      renderNav(); render();41    } else toast(r.msg || '登录失败');42  }43  function logout() { $('#app').style.display = 'none'; $('#login').style.display = 'flex'; }4445  function renderNav() {46    $('#nav').innerHTML = navs.map(n => '<div class="n ' + (n[0] === cur ? 'on' : '') + '" onclick="AD.nav(\'' + n[0] + '\')">' + n[1] + '</div>').join('');47  }48  function render() {49    renderNav();50    if (cur === 'dashboard') return R.dashboard();51    if (cur === 'products') return R.products();52    if (cur === 'orders') return R.orders();53    if (cur === 'members') return R.members();54    if (cur === 'coupons') return R.coupons();55    if (cur === 'reviews') return R.reviews();56    if (cur === 'banners') return R.banners();57    if (cur === 'custom') return R.custom();58    if (cur === 'settings') return R.settings();59  }6061  // 写操作前置检查62  function guard() {63    if (RO) { toast('🔒 后台只读模式已开启,写操作被拦截'); return false; }64    return true;65  }66  function W(r) { if (r.readonly) { RO = true; $('#robar').style.display = 'block'; toast('🔒 ' + r.msg); return false; } if (r.ok) { toast('已保存'); closeSheet(); render(); } else toast(r.msg || '操作失败'); return r.ok; }6768  const STATUS = { pending: ['待支付', 'st-pending'], paid: ['已支付·待制作', 'st-paid'], making: ['制作中', 'st-making'], delivering: ['配送中', 'st-delivering'], done: ['已完成', 'st-done'], canceled: ['已取消', 'st-canceled'], refund: ['退款', 'st-refund'] };6970  const R = {71    async dashboard() {72      const r = await adm('dashboard'); const d = r.ok ? r.data : {};73      const cards = [['今日销售额', '¥' + (d.sales_today || 0)], ['今日订单', d.orders_today || 0], ['订单总数', d.order_total || 0], ['会员总数', d.member_total || 0], ['商品总数', d.product_total || 0], ['待处理', d.pending_orders || 0], ['评价数', d.review_total || 0]];74      const trend = (d.trend || []);75      const max = Math.max(1, ...trend.map(t => +t.s));76      const chart = trend.length ? '<div class="chart">' + trend.map(t => '<div class="c" style="height:' + (Math.max(8, (+t.s / max) * 100)) + '%"><span>' + t.d.slice(5) + '</span></div>').join('') + '</div>' : '<div class="empty">暂无数据</div>';77      const top = (d.top_products || []).map(p => '<div>' + esc(p.name) + ' · 售出 ' + p.qty + '</div>').join('') || '<div class="empty">暂无</div>';78      const lv = (d.level_dist || []).map(l => ({ normal: '普通', silver: '银卡', gold: '金卡', diamond: '钻石' }[l.level] || l.level) + ' ' + l.c).join(' / ');79      $('#content').innerHTML =80        '<div class="cards">' + cards.map(c => '<div class="card"><div class="n">' + c[1] + '</div><div class="l">' + c[0] + '</div></div>').join('') + '</div>' +81        '<div class="panel"><h3>近 7 日销售额趋势</h3>' + chart + '</div>' +82        '<div class="panel mt12"><h3>热销 TOP5</h3>' + top + '</div>' +83        '<div class="panel mt12"><h3>会员等级分布</h3><div class="mini">' + (lv || '—') + '</div></div>';84    },8586    async products() {87      const [list, cats] = await Promise.all([adm('products_list'), adm('categories')]);88      const kinds = (cats.ok ? cats : { kinds: [] }).kinds || [];89      const rows = (list.ok ? list.data : []);90      $('#content').innerHTML = '<div class="panel"><h3>商品管理 <button class="btn pri sm" onclick="AD.addProduct()"' + (RO ? ' disabled' : '') + '>+ 新增</button></h3>' +91        '<table><tr><th>ID</th><th>名称</th><th>品类</th><th>价格</th><th>库存</th><th>状态</th><th>操作</th></tr>' +92        rows.map(p => '<tr><td>' + p.id + '</td><td>' + esc(p.name) + '</td><td>' + esc(p.kind_name || '') + '</td><td>¥' + p.price + '</td><td>' + p.stock + '</td><td><span class="tag ' + (p.status ? 'on' : 'off') + '">' + (p.status ? '在售' : '下架') + '</span></td><td><button class="btn ghost sm" onclick="AD.editProduct(' + p.id + ')">编辑</button> <button class="btn ghost sm" onclick="AD.toggleProduct(' + p.id + ')">上下架</button> <button class="btn danger sm" onclick="AD.delProduct(' + p.id + ')">删</button></td></tr>').join('') + '</table></div>';93      AD._kinds = kinds;94    },95    addProduct() { if (!guard()) return; openSheet('<span class="close" onclick="closeSheet()">×</span><h3>新增商品</h3>' + productForm({}) + '<button class="btn pri block" onclick="AD.saveProduct(0)">保存</button>'); },96    editProduct(id) { if (!guard()) return; adm('products_list').then(r => { const p = r.data.find(x => x.id == id); openSheet('<span class="close" onclick="closeSheet()">×</span><h3>编辑商品 #' + id + '</h3>' + productForm(p) + '<button class="btn pri block" onclick="AD.saveProduct(' + id + ')">保存</button>'); }); },97    saveProduct(id) {98      if (!guard()) return;99      const payload = { id, name: $('#f_name').value, kind_id: $('#f_kind').value, scenes: $('#f_scenes').value, price: $('#f_price').value, original_price: $('#f_oprice').value, stock: $('#f_stock').value, is_new: $('#f_new').checked ? 1 : 0, is_hot: $('#f_hot').checked ? 1 : 0, is_recommend: $('#f_rec').checked ? 1 : 0, description: $('#f_desc').value, ingredients: $('#f_ing').value, allergen: $('#f_all').value, shelf_life: $('#f_life').value, advice: $('#f_advice').value, images: $('#f_images').value, tags: $('#f_tags').value };100      adm('product_save', payload, 'POST').then(W);101    },102    toggleProduct(id) { if (!guard()) return; adm('product_toggle', { id }, 'POST').then(W); },103    delProduct(id) { if (!guard()) return; if (!confirm('确认删除该商品?')) return; adm('product_delete', { id }, 'POST').then(W); },104105    async orders() {106      const r = await adm('orders_list');107      const rows = (r.ok ? r.data : []);108      const opts = Object.keys(STATUS).map(k => '<option value="' + k + '">' + STATUS[k][0] + '</option>').join('');109      $('#content').innerHTML = '<div class="panel"><h3>订单管理 <span class="mini">(只读模式下状态变更被拦截)</span></h3>' +110        '<table><tr><th>单号</th><th>收货人</th><th>金额</th><th>状态</th><th>操作</th></tr>' +111        rows.map(o => '<tr><td>' + o.order_no + '</td><td>' + esc(o.contact_name) + '</td><td>¥' + o.pay_amount + '</td><td><span class="st ' + (STATUS[o.status] || ['', ''])[1] + '">' + (STATUS[o.status] || ['', ''])[0] + '</span></td><td><select id="os_' + o.id + '">' + opts + '</select> <button class="btn ghost sm" onclick="AD.setOrder(' + o.id + ')">更新</button></td></tr>').join('') + '</table></div>';112      rows.forEach(o => { const s = $('#os_' + o.id); if (s) s.value = o.status; });113    },114    setOrder(id) { if (!guard()) return; const st = $('#os_' + id).value; adm('order_update_status', { id, status: st }, 'POST').then(W); },115116    async members() {117      const r = await adm('members_list');118      const rows = (r.ok ? r.data : []);119      const lvName = { normal: '普通', silver: '银卡', gold: '金卡', diamond: '钻石' };120      $('#content').innerHTML = '<div class="panel"><h3>会员管理</h3><table><tr><th>ID</th><th>昵称</th><th>手机</th><th>等级</th><th>积分</th><th>余额</th><th>操作</th></tr>' +121        rows.map(m => '<tr><td>' + m.id + '</td><td>' + esc(m.username) + '</td><td>' + esc(m.phone) + '</td><td>' + (lvName[m.level] || m.level) + '</td><td>' + m.points + '</td><td>¥' + m.balance + '</td><td><button class="btn ghost sm" onclick="AD.editMember(' + m.id + ')">编辑</button> <button class="btn danger sm" onclick="AD.delMember(' + m.id + ')">删</button></td></tr>').join('') + '</table></div>';122    },123    editMember(id) { if (!guard()) return; adm('members_list').then(r => { const m = r.data.find(x => x.id == id); openSheet('<span class="close" onclick="closeSheet()">×</span><h3>编辑会员 #' + id + '</h3>' + memberForm(m) + '<button class="btn pri block" onclick="AD.saveMember(' + id + ')">保存</button>'); }); },124    saveMember(id) { if (!guard()) return; const p = { id, username: $('#m_user').value, phone: $('#m_phone').value, level: $('#m_level').value, points: $('#m_points').value, balance: $('#m_balance').value, growth: $('#m_growth').value, birthday: $('#m_birth').value }; adm('member_save', p, 'POST').then(W); },125    delMember(id) { if (!guard()) return; if (!confirm('确认删除?')) return; adm('member_delete', { id }, 'POST').then(W); },126127    async coupons() {128      const r = await adm('coupons_list');129      const rows = (r.ok ? r.data : []);130      const typeName = { full_reduce: '满减', discount: '折扣', no_threshold: '无门槛', newcomer: '新人', birthday: '生日' };131      $('#content').innerHTML = '<div class="panel"><h3>优惠券 <button class="btn pri sm" onclick="AD.addCoupon()"' + (RO ? ' disabled' : '') + '>+ 新增</button></h3><table><tr><th>ID</th><th>名称</th><th>类型</th><th>规则</th><th>已领/总量</th><th>操作</th></tr>' +132        rows.map(c => '<tr><td>' + c.id + '</td><td>' + esc(c.name) + '</td><td>' + (typeName[c.type] || c.type) + '</td><td>' + esc(c.desc || '') + '</td><td>' + c.received + '/' + c.total + '</td><td><button class="btn ghost sm" onclick="AD.editCoupon(' + c.id + ')">编辑</button> <button class="btn danger sm" onclick="AD.delCoupon(' + c.id + ')">删</button></td></tr>').join('') + '</table></div>';133    },134    addCoupon() { if (!guard()) return; openSheet('<span class="close" onclick="closeSheet()">×</span><h3>新增优惠券</h3>' + couponForm({}) + '<button class="btn pri block" onclick="AD.saveCoupon(0)">保存</button>'); },135    editCoupon(id) { if (!guard()) return; adm('coupons_list').then(r => { const c = r.data.find(x => x.id == id); openSheet('<span class="close" onclick="closeSheet()">×</span><h3>编辑券 #' + id + '</h3>' + couponForm(c) + '<button class="btn pri block" onclick="AD.saveCoupon(' + id + ')">保存</button>'); }); },136    saveCoupon(id) { if (!guard()) return; const p = { id, name: $('#c_name').value, type: $('#c_type').value, threshold: $('#c_th').value, value: $('#c_val').value, discount: $('#c_dis').value, valid_days: $('#c_days').value, total: $('#c_total').value }; adm('coupon_save', p, 'POST').then(W); },137    delCoupon(id) { if (!guard()) return; if (!confirm('确认删除?')) return; adm('coupon_delete', { id }, 'POST').then(W); },138139    async reviews() {140      const r = await adm('reviews_list');141      const rows = (r.ok ? r.data : []);142      $('#content').innerHTML = '<div class="panel"><h3>评价管理</h3><table><tr><th>商品</th><th>用户</th><th>评分</th><th>内容</th><th>操作</th></tr>' +143        rows.map(rv => '<tr><td>' + esc(rv.pname || '') + '</td><td>' + esc(rv.username || '匿名') + '</td><td>' + '★'.repeat(rv.rating) + '</td><td>' + esc(rv.content).slice(0, 20) + (rv.reply ? ' <span class="mini" style="color:#2bb673">已回复</span>' : '') + '</td><td><button class="btn ghost sm" onclick="AD.replyReview(' + rv.id + ')">回复</button> <button class="btn danger sm" onclick="AD.delReview(' + rv.id + ')">删</button></td></tr>').join('') + '</table></div>';144    },145    replyReview(id) { if (!guard()) return; openSheet('<span class="close" onclick="closeSheet()">×</span><h3>回复评价 #' + id + '</h3><textarea id="r_reply" placeholder="商家回复内容"></textarea><button class="btn pri block mt12" onclick="AD.doReply(' + id + ')">提交回复</button>'); },146    doReply(id) { if (!guard()) return; adm('review_reply', { id, reply: $('#r_reply').value }, 'POST').then(W); },147    delReview(id) { if (!guard()) return; if (!confirm('确认删除?')) return; adm('review_delete', { id }, 'POST').then(W); },148149    async banners() {150      const r = await adm('banners_list');151      const rows = (r.ok ? r.data : []);152      $('#content').innerHTML = '<div class="panel"><h3>首页轮播 <button class="btn pri sm" onclick="AD.addBanner()"' + (RO ? ' disabled' : '') + '>+ 新增</button></h3><table><tr><th>ID</th><th>图标</th><th>标题</th><th>状态</th><th>操作</th></tr>' +153        rows.map(b => '<tr><td>' + b.id + '</td><td style="font-size:22px">' + esc(b.image) + '</td><td>' + esc(b.title) + '</td><td><span class="tag ' + (b.status ? 'on' : 'off') + '">' + (b.status ? '显示' : '隐藏') + '</span></td><td><button class="btn ghost sm" onclick="AD.editBanner(' + b.id + ')">编辑</button> <button class="btn danger sm" onclick="AD.delBanner(' + b.id + ')">删</button></td></tr>').join('') + '</table></div>';154    },155    addBanner() { if (!guard()) return; openSheet('<span class="close" onclick="closeSheet()">×</span><h3>新增轮播</h3>' + bannerForm({}) + '<button class="btn pri block" onclick="AD.saveBanner(0)">保存</button>'); },156    editBanner(id) { if (!guard()) return; adm('banners_list').then(r => { const b = r.data.find(x => x.id == id); openSheet('<span class="close" onclick="closeSheet()">×</span><h3>编辑轮播 #' + id + '</h3>' + bannerForm(b) + '<button class="btn pri block" onclick="AD.saveBanner(' + id + ')">保存</button>'); }); },157    saveBanner(id) { if (!guard()) return; const p = { id, title: $('#b_title').value, image: $('#b_image').value, link: $('#b_link').value, sort: $('#b_sort').value, status: $('#b_status').checked ? 1 : 0 }; adm('banner_save', p, 'POST').then(W); },158    delBanner(id) { if (!guard()) return; if (!confirm('确认删除?')) return; adm('banner_delete', { id }, 'POST').then(W); },159160    async custom() {161      const r = await adm('custom_list');162      const rows = (r.ok ? r.data : []);163      $('#content').innerHTML = '<div class="panel"><h3>定制需求 <span class="mini">(只读模式下审核被拦截)</span></h3><table><tr><th>ID</th><th>用户</th><th>文字</th><th>尺寸</th><th>状态</th><th>操作</th></tr>' +164        rows.map(c => '<tr><td>' + c.id + '</td><td>' + esc(c.username || '') + '</td><td>' + esc(c.text).slice(0, 16) + '</td><td>' + esc(c.size) + '</td><td><span class="tag ' + (c.status === 'approved' ? 'on' : c.status === 'rejected' ? 'off' : '') + '">' + ({ pending: '待审核', approved: '已通过', rejected: '已拒绝' }[c.status] || c.status) + '</span></td><td><button class="btn ghost sm" onclick="AD.setCustom(' + c.id + ',\'approved\')">通过</button> <button class="btn ghost sm" onclick="AD.setCustom(' + c.id + ',\'rejected\')">拒绝</button></td></tr>').join('') + '</table></div>';165    },166    setCustom(id, st) { if (!guard()) return; adm('custom_update', { id, status: st }, 'POST').then(W); },167168    async settings() {169      const r = await adm('settings_get');170      const d = (r.ok ? r.data : {});171      $('#content').innerHTML = '<div class="panel"><h3>店铺设置 ' + (RO ? '<span class="tag off">只读</span>' : '') + '</h3>' +172        '<label>店铺名称</label><input id="s_name" value="' + esc(d.shop_name || '') + '">' +173        '<label>客服电话</label><input id="s_phone" value="' + esc(d.phone || '') + '">' +174        '<label>门店地址</label><input id="s_addr" value="' + esc(d.address || '') + '">' +175        '<label>配送费(元)</label><input id="s_fee" value="' + esc(d.delivery_fee || '') + '">' +176        '<label>免配送费门槛(元)</label><input id="s_free" value="' + esc(d.free_delivery_amount || '') + '">' +177        '<label>配送范围(公里)</label><input id="s_range" value="' + esc(d.delivery_range_km || '') + '">' +178        '<label>提前预约(小时)</label><input id="s_adv" value="' + esc(d.delivery_advance_hours || '') + '">' +179        '<label>营业时间</label><input id="s_open" value="' + esc(d.open_time || '') + '">' +180        '<label>公告说明</label><textarea id="s_notice">' + esc(d.notice || '') + '</textarea>' +181        '<button class="btn pri block" onclick="AD.saveSettings()" ' + (RO ? 'disabled' : '') + '>保存设置</button>' +182        (RO ? '<div class="mini" style="color:#b26a00;margin-top:8px">🔒 当前为只读模式,保存按钮已禁用。</div>' : '') + '</div>';183    },184    saveSettings() { if (!guard()) return; const p = { shop_name: $('#s_name').value, phone: $('#s_phone').value, address: $('#s_addr').value, delivery_fee: $('#s_fee').value, free_delivery_amount: $('#s_free').value, delivery_range_km: $('#s_range').value, delivery_advance_hours: $('#s_adv').value, open_time: $('#s_open').value, notice: $('#s_notice').value }; adm('settings_save', p, 'POST').then(W); }185  };186187  function productForm(p) {188    p = p || {};189    const kindOpts = (AD._kinds || []).map(k => '<option value="' + k.id + '" ' + (k.id == p.kind_id ? 'selected' : '') + '>' + esc(k.name) + '</option>').join('');190    return '<label>名称</label><input id="f_name" value="' + esc(p.name || '') + '">' +191      '<label>品类</label><select id="f_kind">' + kindOpts + '</select>' +192      '<label>适用场景(| 分隔,如 生日蛋糕|儿童节)</label><input id="f_scenes" value="' + esc(p.scenes || '') + '">' +193      '<label>售价 / 原价</label><div class="bar"><input id="f_price" placeholder="售价" value="' + (p.price || '') + '"><input id="f_oprice" placeholder="原价" value="' + (p.original_price || '') + '"></div>' +194      '<label>库存</label><input id="f_stock" value="' + (p.stock != null ? p.stock : 99) + '">' +195      '<label><input type="checkbox" id="f_new" ' + (p.is_new ? 'checked' : '') + '> 新品 <input type="checkbox" id="f_hot" ' + (p.is_hot ? 'checked' : '') + '> 热销 <input type="checkbox" id="f_rec" ' + (p.is_recommend ? 'checked' : '') + '> 推荐</label>' +196      '<label>描述</label><textarea id="f_desc">' + esc(p.description || '') + '</textarea>' +197      '<label>配料 / 过敏原</label><div class="bar"><input id="f_ing" value="' + esc(p.ingredients || '') + '"><input id="f_all" value="' + esc(p.allergen || '') + '"></div>' +198      '<label>保质期 / 食用建议</label><div class="bar"><input id="f_life" value="' + esc(p.shelf_life || '') + '"><input id="f_advice" value="' + esc(p.advice || '') + '"></div>' +199      '<label>图片 emoji(| 分隔,如 🍓|🍰)</label><input id="f_images" value="' + esc(p.images || '🎂') + '">' +200      '<label>标签</label><input id="f_tags" value="' + esc(p.tags || '') + '">';201  }202  function memberForm(m) {203    m = m || {};204    return '<label>昵称</label><input id="m_user" value="' + esc(m.username || '') + '">' +205      '<label>手机</label><input id="m_phone" value="' + esc(m.phone || '') + '">' +206      '<label>等级</label><select id="m_level"><option value="normal" ' + (m.level == 'normal' ? 'selected' : '') + '>普通</option><option value="silver" ' + (m.level == 'silver' ? 'selected' : '') + '>银卡</option><option value="gold" ' + (m.level == 'gold' ? 'selected' : '') + '>金卡</option><option value="diamond" ' + (m.level == 'diamond' ? 'selected' : '') + '>钻石</option></select>' +207      '<label>积分 / 成长值</label><div class="bar"><input id="m_points" value="' + (m.points || 0) + '"><input id="m_growth" value="' + (m.growth || 0) + '"></div>' +208      '<label>储值余额</label><input id="m_balance" value="' + (m.balance || 0) + '">' +209      '<label>生日</label><input id="m_birth" value="' + esc(m.birthday || '') + '">';210  }211  function couponForm(c) {212    c = c || {};213    const t = c.type || 'full_reduce';214    return '<label>名称</label><input id="c_name" value="' + esc(c.name || '') + '">' +215      '<label>类型</label><select id="c_type"><option value="full_reduce" ' + (t == 'full_reduce' ? 'selected' : '') + '>满减</option><option value="discount" ' + (t == 'discount' ? 'selected' : '') + '>折扣</option><option value="no_threshold" ' + (t == 'no_threshold' ? 'selected' : '') + '>无门槛</option><option value="newcomer" ' + (t == 'newcomer' ? 'selected' : '') + '>新人</option><option value="birthday" ' + (t == 'birthday' ? 'selected' : '') + '>生日</option></select>' +216      '<label>满减门槛 / 抵扣金额</label><div class="bar"><input id="c_th" placeholder="门槛(满减)" value="' + (c.threshold || 0) + '"><input id="c_val" placeholder="金额/无门槛值" value="' + (c.value || 0) + '"></div>' +217      '<label>折扣(0.8=8折)</label><input id="c_dis" value="' + (c.discount || 1) + '">' +218      '<label>有效期(天) / 总量</label><div class="bar"><input id="c_days" value="' + (c.valid_days || 30) + '"><input id="c_total" value="' + (c.total || 999) + '"></div>';219  }220  function bannerForm(b) {221    b = b || {};222    return '<label>图标 emoji</label><input id="b_image" value="' + esc(b.image || '🎂') + '">' +223      '<label>标题</label><input id="b_title" value="' + esc(b.title || '') + '">' +224      '<label>跳转(#home/#category/#member)</label><input id="b_link" value="' + esc(b.link || '#home') + '">' +225      '<label>排序</label><input id="b_sort" value="' + (b.sort || 0) + '">' +226      '<label><input type="checkbox" id="b_status" ' + (b.status ? 'checked' : '') + '> 显示</label>';227  }228229  return { login, logout, nav: v => { cur = v; render(); }, render,230    addProduct: R.addProduct, editProduct: R.editProduct, saveProduct: R.saveProduct, toggleProduct: R.toggleProduct, delProduct: R.delProduct,231    setOrder: R.setOrder, editMember: R.editMember, saveMember: R.saveMember, delMember: R.delMember,232    addCoupon: R.addCoupon, editCoupon: R.editCoupon, saveCoupon: R.saveCoupon, delCoupon: R.delCoupon,233    replyReview: R.replyReview, doReply: R.doReply, delReview: R.delReview,234    addBanner: R.addBanner, editBanner: R.editBanner, saveBanner: R.saveBanner, delBanner: R.delBanner,235    setCustom: R.setCustom, saveSettings: R.saveSettings };236})();237