编辑页面

修改后将从当前时间重新计算过期时间
报名人数

${a.type==='offline'?'最多 3 人,一人一票':'每个账号 1 个名额'}

${btn('−','quantity','-1','','aria-label="减少报名人数" '+(d.count<=1?'disabled':''))}${d.count}${btn('+','quantity','1','','aria-label="增加报名人数" '+(d.count>=Math.min(a.type==='offline'?3:1,available(a))?'disabled':''))}

费用明细

${a.price?'单人活动票':'免费报名'}¥${money(a.price)} × ${d.count}
合计¥${money(a.price*d.count)}
`;actionbar(`
合计
¥${money(a.price*d.count)}
`)} function field(name,label,ph,val='',type='text',required=false){return ``} function captureDraft(){let f=$('#signup-form');if(f){let fd=new FormData(f);['name','phone','pet','breed','note'].forEach(k=>state.draft[k]=String(fd.get(k)||''));state.draft.agree=fd.has('agree')}} function submitSignup(e){e.preventDefault();captureDraft();let d=state.draft,a=act(),errors={};if(!d.name.trim())errors.name='请填写联系人姓名';if(!/^1\d{10}$/.test(d.phone))errors.phone='请填写正确的11位手机号';if(a.type==='offline'&&!d.pet.trim())errors.pet='请填写宠物昵称';if(!d.agree)errors.agree='请先阅读并同意活动报名须知';$('#errors').innerHTML='';for(let k of ['name','phone','pet']){let inp=$(`[name="${k}"]`);inp?.removeAttribute('aria-invalid');if($('#error-'+k))$('#error-'+k).textContent=''}if(Object.keys(errors).length){for(let [k,v]of Object.entries(errors)){$(`[name="${k}"]`)?.setAttribute('aria-invalid','true')}const fk=Object.keys(errors)[0],fe=$(`[name="${fk}"]`);toast(errors[fk]);fe?.scrollIntoView({block:'center'});fe?.focus({preventScroll:true});return}let exist=liveOrder(a);if(exist){state.order=exist.id;navigate('order');return}if(currentStatus(a)!=='open'||d.count>available(a)){toast('活动不可报名或剩余名额不足');return}let o={id:'HD'+Date.now().toString().slice(-11),aid:a.id,count:d.count,total:a.price*d.count,status:a.price?'pending':'registered',info:{...d},created:Date.now(),tickets:[],refundReason:''};orders.unshift(o);persist();state.order=o.id;state.draft={name:'',phone:'',pet:'',breed:'',note:'',count:1,agree:false};if(a.price){navigate('order');pay(o.id)}else{state.result='success';navigate('result')}} function showDialog(title,content,actions='',wide=false){modalReturn=document.activeElement;$('#dialog-title').textContent=title;$('#dialog-content').innerHTML=content;$('#dialog-actions').innerHTML=actions;modal.classList.toggle('wide',wide);if(!modal.open)modal.showModal()} function closeDialog(){modal.close();if(modalReturn?.isConnected)modalReturn.focus()} function pay(id){let o=orders.find(x=>x.id===id);if(!o||o.status!=='pending')return toast('当前订单无需支付');state.order=o.id;showDialog('微信支付 · 演示',`

本页面只模拟支付结果,不会发起真实扣款。

${orderActivity(o).title}

需支付

¥${money(o.total)}
`,btn('确认支付成功','payment','success','primary full')+btn('模拟支付失败','payment','failed','secondary full')+btn('暂不支付','payment','cancel','small-btn full'))} function payment(result){let o=currentOrder();closeDialog();if(!o||o.status!=='pending'){toast('订单状态已更新,请重新查看');render();return}if(result==='success'){o.status='paid';o.paidAt=Date.now();o.tickets=Array.from({length:o.count},(_,i)=>({code:String(10000000+((Date.now()+i)%89999999)),used:false}));persist();state.result='success';navigate('result')}else if(result==='failed'){state.result='failed';navigate('result')}else{navigate('order');toast('已取消支付,订单保留 10 分钟')}} function result(){let o=currentOrder();if(!o)return ordersPage();let a=orderActivity(o),ok=state.result==='success';screen.innerHTML=`
${icon(ok?'check':'alert')}

${ok?(a.price?'支付成功':'报名成功'):'支付未完成'}

${ok?(a.verify?'已为您生成独立票码,请到场出示核销。':'您的报名已确认,可在订单中查看参与方式。'):'订单仍为待支付状态,可以重新尝试支付。'}

${a.title}

${a.time}

报名人数${o.count} 人
${ok?'实付金额':'待付金额'}¥${money(o.total)}
${ok?(a.verify?btn('查看核销码','ticket',o.id,'primary full'):btn('查看参与方式','openOrder',o.id,'primary full')):btn('重新支付','pay',o.id,'primary full')}${btn('查看订单','openOrder',o.id,'secondary full')}${ok?btn('分享给朋友','share',a.id,'small-btn full'):''}
`;actionbar(btn('返回活动首页','tab','activities','secondary full'))} function orderActions(o){if(o.status==='pending')return btn('取消订单','cancelOrder',o.id,'small-btn')+btn('去支付','pay',o.id,'small-btn blue');if(['paid','completed'].includes(o.status))return btn('订单详情','openOrder',o.id,'small-btn')+btn('查看核销码','ticket',o.id,'small-btn blue');return btn(o.status==='refunding'?'退款进度':'查看详情','openOrder',o.id,'small-btn blue')} function ordersPage(){screen.innerHTML=`
${btn('服务订单','orderType','service',state.orderType==='service'?'active':'')}${btn('活动订单','orderType','activity',state.orderType==='activity'?'active':'')}
${(state.orderType==='service'?['全部','待接单','待选择服务人员','服务中','待评价']:['全部','待支付','待核销','已报名','已完成','退款/取消']).map(t=>btn(t,'orderFilter',t,state.orderFilter===t?'active':'')).join('')}
`;let list=orders.filter(o=>state.orderFilter==='全部'||(state.orderFilter==='退款/取消'?['refunding','refunded','cancelled'].includes(o.status):statusLabel(o)===state.orderFilter));$('#order-list').innerHTML=state.orderType==='service'?`
${icon('order')}

暂无${state.orderFilter==='全部'?'服务':state.orderFilter}订单

${btn('去预约服务','service','feed','secondary')}
`:list.length?list.map(o=>`
小布在家 · 活动报名${statusLabel(o)}
共 ${o.count} 个名额 ${o.status==='pending'?'待付款':'订单金额'} ¥${money(o.total)}
${orderActions(o)}
`).join(''):`
${icon('ticket')}

暂无${state.orderFilter==='全部'?'活动':state.orderFilter}订单

和毛孩子的下一段美好,等你开启

${btn('去看看活动','tab','activities','primary')}

`;tabbar()} function orderPage(){let o=currentOrder();if(!o)return navigate('orders',{},true);let a=orderActivity(o),msg={pending:'请在 内完成支付,超时将自动取消。',paid:'报名已确认,请在活动现场出示核销码。',registered:'已报名成功,活动开始前可查看参与方式。',completed:'本订单全部票码已核销,感谢您的参与。',refunding:'退款申请已提交,审核期间票码暂停使用。',refunded:'退款已完成,票码已失效。',cancelled:'订单已取消,您可以重新报名其他活动。'};screen.innerHTML=`

${statusLabel(o)}

${msg[o.status]}

${a.verify&&o.tickets.length?`
${o.tickets.filter(t=>t.used).length} / ${o.count} 张票已核销${btn('查看票码 →','ticket',o.id,'small-btn blue','style="float:right"')}
`:''}
${o.status==='registered'?`

线上参与方式

请于 ${a.time} 返回此处进入课堂。

${btn('进入线上课堂','classroom','','secondary full')}

无需核销,报名信息已保留。

`:''}

报名信息

${info('联系人',o.info.name)}${info('手机号',o.info.phone.slice(0,3)+'****'+o.info.phone.slice(-4))}${info('宠物昵称',o.info.pet||'未填写')}${info('报名人数',o.count+' 人')}${o.info.note?info('备注',o.info.note):''}

订单信息

${info('订单编号',o.id)}${info('创建时间',new Date(o.created).toLocaleString('zh-CN',{hour12:false}))}${info('订单金额','¥'+money(o.total))}${info('支付方式',a.price?'微信支付':'免费报名')}${o.refundReason?info('退款原因',o.refundReason):''}
${btn('联系客服','support','','small-btn')}${btn('分享活动','share',a.id,'small-btn blue')}
`;if(o.status==='pending')actionbar(btn('取消订单','cancelOrder',o.id,'secondary')+btn('去支付','pay',o.id));else if(o.status==='paid')actionbar(btn('申请退款','refund',o.id,'secondary')+btn('查看核销码','ticket',o.id));else if(o.status==='registered')actionbar(btn('取消报名','cancelOrder',o.id,'secondary')+btn('查看活动','detail',a.id));else if(o.status==='refunding'||o.status==='refunded')actionbar(btn('查看退款进度','refundProgress',o.id,'secondary full'));else actionbar(btn('返回活动订单','activityOrders','','secondary full'));updateCountdown()} function info(k,v){return `
${esc(k)}${esc(v)}
`} function qr(seed){let n=25,cells='';const finder=(x,y)=>``;for(let y=0;y16&&y<8)||(x<8&&y>16))continue;if(((x*13+y*17+Number(seed.slice(-3))+x*y)%7)<3)cells+=``}return `${cells}${finder(0,0)}${finder(18,0)}${finder(0,18)}`} function ticket(){let o=currentOrder();if(!o||!o.tickets.length)return navigate('order',{},true);let t=o.tickets[state.ticket]||o.tickets[0],a=orderActivity(o),phase=currentStatus(a),disabled=t.used||['refunding','refunded','cancelled'].includes(o.status)||['ended','offline'].includes(phase),label=t.used?'已核销':o.status==='refunding'?'退款中 · 暂停使用':o.status==='refunded'?'已退款 · 票码失效':o.status==='cancelled'?'已作废':phase==='ended'?'已过期':phase==='offline'?'活动已下架 · 暂停使用':'待核销';screen.innerHTML=`
到场后向工作人员出示,一人一票。请勿向陌生人转发票码。
${o.tickets.map((v,i)=>btn(`票 ${i+1}${v.used?' · 已使用':''}`,'selectTicket',String(i),i===state.ticket?'active':'')).join('')}
${label}

${a.title}

${o.info.name.slice(0,1)}** ${o.info.phone.slice(0,3)}****${o.info.phone.slice(-4)}

${qr(String(Number(t.code)+qrVersion))}

演示码图案 · 可用下方数字码演示核销

${t.code}
${disabled?label:'二维码 '+qrTick+' 秒后刷新'}
${!disabled?btn(icon('refresh')+'刷新核销码','refreshQR','','small-btn','style="margin-top:13px"'):''}

使用说明

${a.time}
${a.place}
每张票仅限使用一次,核销前需由工作人员确认。退款完成后票码失效。

${btn('查看订单','openOrder',o.id,'small-btn blue')}
`;actionbar(btn('返回活动订单','activityOrders','','secondary full'))} function refund(){let o=currentOrder();screen.innerHTML=`

申请整单退款

${orderActivity(o).title}

${info('可退金额','¥'+money(o.total))}${info('退款去向','原支付渠道')}

提交申请后票码暂停使用;审核通过、退款完成后票码失效。

`;actionbar('')} function refundResult(){let o=currentOrder();screen.innerHTML=`
${icon(o.status==='refunded'?'check':'clock')}

${o.status==='refunded'?'退款成功':'退款申请已提交'}

退款金额 ¥${money(o.total)} · 原路退回

${icon('check')}退款申请已提交
${esc(o.refundReason)}
${icon(o.status==='refunded'?'check':'clock')}${o.status==='refunded'?'审核通过,退款已完成':'等待后台审核'}
${o.status==='refunded'?'全部票码已作废':'退款完成后将通知您'}
${o.status==='refunded'?'原型已模拟退款成功,不涉及真实资金。':'演示时可在右侧控制区或顶部“···”中模拟退款完成。'}
`;actionbar(btn('返回订单','openOrder',o.id,'secondary full'))} function mine(){screen.innerHTML=`
${icon('paw')}

小布用户

小布在家,为您服务

安心豆 0.00
${btn(icon('paw')+'申请宠托师','info','join','')}${btn(icon('wash')+'申请洗护师','info','join','')}
${menu('order','我的订单','activityOrders')}${menu('ticket','我的活动','activityOrders',orders.length+' 个订单')}${menu('paw','宠物档案','info','', 'pet')}${menu('wash','洗护叮嘱','info','','care')}${menu('service','喂养叮嘱','info','','feedcare')}${menu('paw','遛狗叮嘱','info','','walkcare')}${menu('chat','我的评价','info','','reviews')}
${state.role==='staff'?`
${menu('scan','核销员入口','verify')}${menu('order','核销记录','verifyRecords')}
`:''}
${menu('chat','联系客服','support')}${menu('shield','关于我们','info','','about')}${menu('image','查看原始页面','references')}${menu('refresh','演示设置','demo')}
`;tabbar()} function menu(i,text,action,badge='',arg=''){return btn(icon(i)+`${text}${badge?`${badge}`:''}›`,action,arg,'menu-item')} function share(){let a=act();screen.innerHTML=`

好活动,和朋友一起

分享活动邀请,不会包含您的个人票码。

${btn(icon('chat')+'分享给朋友','shareSim','','primary full')}${btn(icon('image')+'生成活动海报','poster','','secondary full')}

微信分享由小程序内调起,此处演示分享卡片和落地效果。

`;actionbar(btn('返回活动','detail',a.id,'secondary full'))} function poster(){let a=act();screen.innerHTML=`
小布在家

和毛孩子一起,发现生活的小美好

${esc(a.short).replace('\n','
')}

${a.art==='wash'?crop('wash',200,730,920,830):crop('feed',220,820,920,830)}

${a.date} · ${a.type==='offline'?'重庆':'线上参与'}

${a.price?'¥'+a.price+' / 人':'免费报名'}${qr('26092601')}

演示海报 · 小程序码占位

正式活动将生成可识别的小程序码

`;actionbar(btn('返回分享','share',a.id,'secondary')+btn('保存海报示例','savePoster','','primary'))} function service(){let wash=state.service==='wash';screen.innerHTML=`
${wash?crop('wash',0,270,1260,1410,'低压低敏洗护,在家轻松搞定'):crop('feed',0,270,1260,1410,'不搬家不关笼,有人陪')}

${wash?'在家洗澡,避免应激与交叉感染':'留在熟悉的地盘,生活节奏不乱'}

安心退意外险人人有面试 · 周周有培训

主服务项

${wash?'上门洗猫:低压洗护、烘干护理。上门洗狗:全身洗护、清洁与吹干。':'添粮换水、观察健康状态、清理宠物卫生、确认环境安全、户外遛狗。'}

可选服务项

${wash?'毛发开结、去底绒、清理耳螨、刷牙等。':'陪玩、毛发梳理、用药、饮食管理、剪指甲等。'}

想和毛孩子一起出门?

发现本地宠物友好活动

${btn('去看看','tab','activities','small-btn blue')}
`;actionbar(btn('立即预约','serviceBook','','primary full'))} function serviceBook(){screen.innerHTML=`
服务预约为原有业务衔接演示;本次新增重点为活动报名与核销。

${state.service==='wash'?'上门洗护':'上门喂遛'}预约

${field('pet','宠物昵称','请输入宠物昵称','','text',true)}

演示提交仅展示预约确认,不生成实际服务订单。

`;actionbar(btn('返回服务介绍','service',state.service,'secondary full'))} function verify(){if(state.role!=='staff'){screen.innerHTML=`
${icon('shield')}

暂无活动核销权限

仅授权核销员可使用此入口。
`;actionbar(btn('返回我的','tab','mine','secondary full'));return}screen.innerHTML=`
核销员:小布工作人员 · 授权范围:重庆活动

现场核销

${menu('order','查看核销记录','verifyRecords',logs.filter(l=>l.ok).length+' 条成功')}
`;actionbar(btn('返回我的','tab','mine','secondary full'))} function lookup(code){for(let o of orders){let t=o.tickets.find(t=>t.code===code);if(t)return {o,t,a:orderActivity(o)}}return null} function checkCode(code){if(state.role!=='staff')return '当前账号无核销权限';let found=lookup(code);if(!found)return '核销码不存在,请检查后重试';let {o,t,a}=found;if(o.status==='refunded')return '订单已退款,票码已失效';if(o.status==='refunding')return '订单正在退款中,暂不可核销';if(o.status==='cancelled')return '订单已取消,票码已作废';if(t.used)return '此票码已核销,请勿重复操作';if(o.status!=='paid')return '订单未支付,不可核销';let s=currentStatus(a);if(s==='offline')return '活动已下架,暂不可核销';if(s==='ended')return '已超过核销有效期';if(state.phase!=='live')return '未到核销时间,请在活动开放核销后操作';return null} function queryCode(code){state.verifyCode=code.trim();let reason=checkCode(state.verifyCode);if(reason){recordVerify(false,reason);return}let {o,t,a}=lookup(state.verifyCode);showDialog('确认核销',`

票码有效 · 待使用

${a.title}

${info('报名人',o.info.name.slice(0,1)+'**')}${info('核销码',t.code)}${info('本次核销','1 个名额')}

请核对现场参与人。确认后本票码不可重复使用。

`,btn('确认核销 1 张票','confirmVerify','','primary full')+btn('暂不核销','close','','secondary full'))} function recordVerify(ok,msg){let found=lookup(state.verifyCode);if(ok){found.t.used=true;found.t.usedAt=Date.now();if(found.o.tickets.every(t=>t.used))found.o.status='completed'}let record={code:state.verifyCode,ok,msg,at:Date.now(),title:found?.a.title||'未知活动'};logs.unshift(record);persist();state.verifyResult=record;navigate('verifyResult')} function confirmVerify(){closeDialog();let reason=checkCode(state.verifyCode);recordVerify(!reason,reason||'本票码核销成功')} function verifyResult(){let r=state.verifyResult||{ok:false,msg:'请先查询票码',code:''};screen.innerHTML=`
${icon(r.ok?'check':'alert')}

${r.ok?'核销成功':'核销未通过'}

${esc(r.msg)}

${info('核销码',r.code)}${r.title?info('活动',r.title):''}${r.ok?info('核销数量','1 张票'):''}${info('操作时间',new Date(r.at||Date.now()).toLocaleTimeString('zh-CN'))}
${btn('继续核销','verify','','primary full')}${btn('查看核销记录','verifyRecords','','secondary full')}
`;actionbar(btn('返回我的','tab','mine','secondary full'))} function verifyRecords(){if(state.role!=='staff')return verify();screen.innerHTML=`

核销记录

成功 ${logs.filter(l=>l.ok).length} 次 · 未通过 ${logs.filter(l=>!l.ok).length} 次

${logs.length?logs.map(r=>`
${r.code}${r.ok?'成功':'未通过'}
${esc(r.title)}
${esc(r.msg)} · ${new Date(r.at).toLocaleTimeString('zh-CN')}
`).join(''):'
暂无核销记录
'}
`;actionbar(btn('去核销','verify','','primary full'))} function render(){header();const views={home,activities:activitiesPage,detail,signup,orders:ordersPage,order:orderPage,result,ticket,refund,refundResult,mine,share,poster,service,serviceBook,verify,verifyResult,verifyRecords};(views[state.page]||home)();$('#customer').hidden=!['home','activities','orders','mine','detail','service'].includes(state.page);document.querySelectorAll('.side-nav button').forEach(b=>b.classList.toggle('active',b.dataset.arg===state.tab));$('#demo-state').textContent=({home:'原有首页 × 新增活动入口',activities:'活动中心 · 分类与筛选',detail:'活动详情 · 报名与分享',signup:'报名表单 · 信息校验',orders:'订单中心 · 服务与活动分区',mine:'我的 · 活动与角色入口',ticket:'独立票码 · 状态同步',verify:'工作人员 · 查询与核销'})[state.page]||'活动业务流程演示';$('#stats').innerHTML=`
${orders.filter(o=>!['pending','cancelled','refunded'].includes(o.status)).length}有效报名单
${orders.reduce((n,o)=>n+o.tickets.filter(t=>t.used).length,0)}已核销票
${shareId}分享演示
`;$('#role-select').value=state.role;$('#phase-select').value=state.phase} function updateCountdown(){let o=currentOrder(),el=$('#pay-countdown');if(o&&el){let left=Math.max(0,Math.ceil((o.created+600000-Date.now())/1000));el.textContent=String(Math.floor(left/60)).padStart(2,'0')+':'+String(left%60).padStart(2,'0')}} function demoPanel(){if(state.page==='signup')captureDraft();showDialog('演示设置',`

交互原型 · 支付、退款、扫码和分享均为本地模拟。示例活动、价格及退款规则用于演示,最终范围以评审为准。原始截图和 v1.1 方案保持不变。

切换身份仅为原型演示,不代表正式账号授权方式。数据保存在当前浏览器。

`,btn('填入示例报名信息','fill','','secondary full')+btn('模拟待支付订单超时','expire','','secondary full')+btn('模拟退款完成','approveRefund','','secondary full')+btn('查看原始截图','references','','secondary full')+btn('重置演示数据','reset','','danger full'));$('#dialog-role').value=state.role;$('#dialog-phase').value=state.phase} function references(name='home'){state.reference=name;const map={home:'首页',activity:'活动',orders:'订单',mine:'我的',feed:'上门喂遛',wash:'上门洗护'};showDialog('原始页面截图',`
${Object.entries(map).map(([k,v])=>btn(v,'reference',k,k===name?'active':'')).join('')}
`,'',true)} function refundApprove(){let o=currentOrder()?.status==='refunding'?currentOrder():orders.find(o=>o.status==='refunding');if(!o){toast('请先提交退款申请');return}o.status='refunded';o.refundedAt=Date.now();state.order=o.id;persist();if(modal.open)closeDialog();navigate('refundResult');toast('已模拟原路退款成功')} function savePoster(){const a=act();const asset=document.getElementById('asset-'+(a.art==='wash'?'wash':'feed')).getAttribute('href');const svg=`小布在家和毛孩子一起,发现生活的小美好${esc(a.short.split('\n')[0])}${esc(a.short.split('\n')[1])}${a.date} · ${a.type==='offline'?'重庆':'线上参与'}${a.price?'¥'+a.price+' / 人':'免费报名'}小程序码演示占位活动海报示例 · 不用于真实报名`;let blob=new Blob([svg],{type:'image/svg+xml'}),url=URL.createObjectURL(blob),link=document.createElement('a');link.href=url;link.download='小布在家-'+a.id+'-活动海报示例.svg';link.click();setTimeout(()=>URL.revokeObjectURL(url),5000);toast('已导出海报示例 SVG')} const actions={tab:p=>{state.history=[];if(p==='orders')state.orderFilter='全部';navigate(p,{},true)},back,detail:id=>navigate('detail',{activity:id,tab:'activities'}),category:t=>{state.category=t;render()},clearFilters:()=>{state.query='';state.filter='all';state.category='热门';render()},signup:id=>{if(currentStatus(act())!=='open')return;state.draft.count=1;navigate('signup',{activity:id})},quantity:n=>{captureDraft();state.draft.count=Math.max(1,Math.min(act().type==='offline'?3:1,available(act()),state.draft.count+Number(n)));let pos=screen.scrollTop;render();screen.scrollTop=pos},existing:id=>{let o=orders.find(o=>o.id===id);state.order=id;if(o.status==='pending'){navigate('order');pay(id)}else if(o.status==='refunding')navigate('refundResult');else if(o.tickets.length)navigate('ticket',{ticket:0});else navigate('order')},openOrder:id=>navigate('order',{order:id,activity:orders.find(o=>o.id===id)?.aid||state.activity,tab:'orders'}),pay,payment,orderType:t=>{state.orderType=t;state.orderFilter='全部';render()},orderFilter:t=>{state.orderFilter=t;render()},activityOrders:()=>navigate('orders',{orderType:'activity',orderFilter:'全部',tab:'orders'}),ticket:id=>{qrTick=60;navigate('ticket',{order:id,ticket:0})},selectTicket:i=>{state.ticket=Number(i);qrTick=60;render()},refreshQR:()=>{qrTick=60;qrVersion++;render();toast('核销码已刷新')},cancelOrder:id=>{let o=orders.find(o=>o.id===id);if(!['pending','registered'].includes(o.status))return;showDialog(o.status==='registered'?'取消免费报名':'取消待支付订单','

取消后将释放名额,您可以重新报名。

',btn('确认取消','confirmCancel',id,'danger full')+btn('保留订单','close','','secondary full'))},confirmCancel:id=>{let o=orders.find(o=>o.id===id);if(['pending','registered'].includes(o.status)){o.status='cancelled';persist()}closeDialog();state.order=id;navigate('order')},refund:id=>{let o=orders.find(o=>o.id===id);if(o.status!=='paid'||o.tickets.some(t=>t.used))return showDialog('暂不支持自助退款','

本订单已有票码核销,请联系活动客服处理。

',btn('联系客服','support','','primary full'));navigate('refund',{order:id})},refundProgress:id=>navigate('refundResult',{order:id}),share:id=>{if(modal.open)closeDialog();navigate('share',{activity:id||state.activity})},poster:()=>navigate('poster'),savePoster,shareSim:()=>{shareId++;render();showDialog('分享卡片已生成','

已模拟一次分享,可预览朋友打开卡片后的活动详情。

正式小程序会记录活动、分享人及分享记录的关联。

',btn('预览分享落地页','sharedLanding','','primary full'))},sharedLanding:()=>{closeDialog();navigate('detail');toast('已进入分享活动详情')},service:t=>navigate('service',{service:t}),serviceBook:()=>navigate('serviceBook'),verify:()=>navigate('verify',{tab:'mine'}),verifyRecords:()=>navigate('verifyRecords',{tab:'mine'}),scanDemo:()=>{if(state.role!=='staff')return toast('无核销权限');let all=orders.flatMap(o=>o.tickets.map((t,i)=>({o,t,i})));showDialog('模拟扫码结果',all.length?`

选择一张已有票码,模拟扫码读取。包括已使用及已退款票码,可验证异常反馈。

${all.map(({o,t,i})=>btn(`${orderActivity(o).title}
票 ${i+1} · ${t.code} · ${t.used?'已核销':statusLabel(o)}`,'chooseCode',t.code,'control full')).join('')}`:'

暂无票码,请先完成一个付费活动报名。

',btn('返回','close','','secondary full'))},chooseCode:code=>{closeDialog();queryCode(code)},confirmVerify,close:closeDialog,demo:demoPanel,reference:references,references:()=>references('home'),approveRefund:refundApprove,fill:()=>{if(modal.open)closeDialog();if(state.page!=='signup')navigate('signup',{activity:'outing',tab:'activities'});state.draft={name:'演示用户',phone:'13800000000',pet:'小布',breed:'比熊',note:'',count:1,agree:true};render();toast('已填入虚构示例信息')},expire:()=>{let o=currentOrder()?.status==='pending'?currentOrder():orders.find(o=>o.status==='pending');if(!o)return toast('暂无待支付订单');o.status='cancelled';persist();if(modal.open)closeDialog();navigate('order',{order:o.id});toast('已模拟超时取消并释放名额')},reset:()=>showDialog('重置演示数据','

将清除本原型的报名、订单和核销记录。源文件与截图不会改变。

',btn('确认重置','confirmReset','','danger full')+btn('保留数据','close','','secondary full')),confirmReset:()=>{orders=[];logs=[];shareId=0;state.role='user';state.phase='normal';persist();closeDialog();state.history=[];Object.assign(state,{query:'',filter:'all',category:'热门',orderType:'activity',orderFilter:'全部',order:null,ticket:0,draft:{name:'',phone:'',pet:'',breed:'',note:'',count:1,agree:false}});navigate('home',{},true);toast('演示数据已重置')},support:()=>showDialog('小布在家客服','

您好,请问需要哪方面的帮助?

'+btn('活动报名与费用','supportTopic','报名后可在活动订单查看费用和报名信息。付费订单需支付成功后确认名额。','secondary full')+btn('核销码与现场签到','supportTopic','在“订单 → 活动订单 → 查看核销码”出示票码,由工作人员核销。每张票只能使用一次。','secondary full')+btn('退款与取消','supportTopic','待支付订单可取消;未核销的付费订单可申请退款,审核期间票码暂停使用。','secondary full')+'

这是客服入口演示,未接入真实客服。

'),supportTopic:t=>showDialog('客服帮助',`

${esc(t)}

`,btn('知道了','close','','primary full')),city:()=>showDialog('当前服务城市','

本次演示使用重庆。线上活动可跨城市参与。

',btn('继续浏览重庆活动','close','','primary full')),map:()=>showDialog('活动地点',`

${act().place}

请提前 15 分钟到集合点签到。

${icon('pin')}活动集合点

正式小程序调用地图导航;当前为地点展示演示。

`,btn('知道了','close','','primary full')),terms:()=>showDialog('活动报名须知','

请填写真实有效的联系信息,按活动要求携带宠物与物品。线下活动一人一票,每张票仅限使用一次。

示例付费活动支持未核销订单申请整单退款;实际退款时间与范围由活动发布规则确定。

',btn('我已阅读','agreeTerms','','primary full')),agreeTerms:()=>{closeDialog();if(state.page==='signup'){state.draft.agree=true;$('[name="agree"]').checked=true}},classroom:()=>showDialog('线上活动参与说明','

活动时间:09月27日 19:30–20:30

正式活动开课后显示直播入口;当前原型展示报名与参与方式,不提供真实直播。

',btn('知道了','close','','primary full')),promotion:t=>showDialog(t==='holiday'?'中秋国庆 · 提前锁定服务':'新版本限时价','

沿用现有小程序服务促销入口。可以继续了解上门喂遛服务,也可以探索新增的宠物友好活动。

',btn('了解上门喂遛','promoService','','primary full')+btn('浏览活动','promoActivity','','secondary full')),promoService:()=>{closeDialog();navigate('service',{service:'feed'})},promoActivity:()=>{closeDialog();navigate('activities')},info:k=>{let m={join:['加入小布在家','欢迎申请宠托师或洗护师。正式小程序提交资料并审核,本次原型保留原有入驻入口。'],pet:['宠物档案','小布 · 比熊 · 2岁。活动报名可填写宠物昵称及品种,方便工作人员提前了解。'],care:['洗护叮嘱','请提前说明宠物皮肤状况、洗护习惯及注意事项。'],feedcare:['喂养叮嘱','请说明喂食量、饮水位置和宠物日常习惯。'],walkcare:['遛狗叮嘱','请准备牵引绳,并说明遛狗路线、时长和特殊注意事项。'],reviews:['用户评价','原有服务评价入口保留。活动页面以报名、到场与核销流程为本次演示重点。'],about:['关于小布在家','提供上门洗护、上门喂遛等宠物服务,并通过活动连接更多宠物家庭。']};let [title,t]=m[k]||m.about;showDialog(title,`

${t}

`,btn('知道了','close','','primary full'))}}; document.addEventListener('click',e=>{let b=e.target.closest('[data-action]');if(!b||b.disabled)return;actions[b.dataset.action]?.(b.dataset.arg)}); document.addEventListener('input',e=>{if(e.target.id==='search'){state.query=e.target.value;listActivities()}}); document.addEventListener('change',e=>{let id=e.target.id;if(id==='activity-filter'){state.filter=e.target.value;listActivities()}if(['role-select','dialog-role'].includes(id)){if(state.page==='signup')captureDraft();state.role=e.target.value;render();toast(state.role==='staff'?'已切换为演示核销员':'已切换为普通用户')}if(['phase-select','dialog-phase'].includes(id)){if(state.page==='signup')captureDraft();state.phase=e.target.value;render();toast('活动演示状态已更新')}}); document.addEventListener('submit',e=>{if(e.target.id==='signup-form')return submitSignup(e);if(e.target.id==='verify-form'){e.preventDefault();queryCode(new FormData(e.target).get('code'));return}if(e.target.id==='refund-form'){e.preventDefault();let o=currentOrder();if(o.status!=='paid'||o.tickets.some(t=>t.used))return toast('订单当前不可退款');let f=new FormData(e.target);o.status='refunding';o.refundReason=String(f.get('reason'))+(f.get('note')?':'+String(f.get('note')):'');persist();navigate('refundResult')}if(e.target.id==='service-form'){e.preventDefault();showDialog('预约信息已确认','

已完成服务预约信息演示。正式小程序将进入服务下单流程。

',btn('返回首页','closeService','','primary full'))}});actions.closeService=()=>{closeDialog();navigate('home')}; modal.addEventListener('click',e=>{if(e.target===modal)closeDialog()}); setInterval(()=>{let expired=false;for(let o of orders)if(o.status==='pending'&&Date.now()-o.created>=600000){o.status='cancelled';expired=true}if(expired){persist();if(modal.open)closeDialog();render();toast('待支付订单已超时取消')}updateCountdown();if(state.page==='ticket'){qrTick--;if(qrTick<=0){qrTick=60;qrVersion++;ticket()}if($('#qr-countdown'))$('#qr-countdown').textContent=qrTick}},1000); render();
取消
访问码: r73uzic9