首页/ mobileweb/ 同城跑腿服务H5移动端功能演示/ 代码预览

💻 代码预览 · 同城跑腿服务H5移动端功能演示

mw_service · 上传者 驰旭教育
← 返回详情
r101/app.js · 约 19639 字符
1/* ===== 同城跑腿 H5 · 应用逻辑 ===== */
2(function ($) {
3  'use strict';
4
5  var ROUTES = ['home', 'send', 'buy', 'orders', 'detail', 'runner', 'me', 'wallet', 'message', 'address'];
6  var CUR = 'home';
7  var ORD_TAB = '进行中';
8
9  // ---- 数据 ----
10  var ORDERS = {
11    '进行中': [
12      ['📦', '帮我送 · 文件', '取件:科技路 18 号 → 高新万达', '10:24', '配送中', '¥8.0', 'o1'],
13      ['🛵', '万能帮忙 · 排队', '医院挂号排队 3 号窗口', '10:02', '取件中', '¥15.0', 'o6']
14    ],
15    '待支付': [
16      ['🛒', '帮我买 · 咖啡', '星巴克(赛格店) → 家中', '09:50', '待支付', '¥36.0', 'o2']
17    ],
18    '已完成': [
19      ['📮', '代取快递 · 顺丰', '菜鸟驿站 → 公司前台', '昨天', '已完成', '¥5.0', 'o3'],
20      ['🔧', '万能帮忙 · 搬东西', '师大路 → 6 号楼', '07-25', '已完成', '¥30.0', 'o4']
21    ],
22    '已取消': [
23      ['📄', '文件速递', '已取消', '07-20', '已取消', '¥0.0', 'o5']
24    ]
25  };
26
27  var MSG = {
28    '系统通知': [
29      ['📢', '系统升级维护通知', '为提升体验,7月30日 02:00-04:00 系统维护', '07-28'],
30      ['🎉', '新人券包已到账', '您有 3 张优惠券等待使用', '07-27']
31    ],
32    '订单消息': [
33      ['📦', '订单已接单', '王师傅已接单,正在赶往取件点', '10:25'],
34      ['🛵', '订单配送中', '您的文件正在配送中,预计 12 分钟送达', '10:31']
35    ],
36    '活动推送': [
37      ['🎁', '邀请有奖', '邀请好友得 ¥5 现金红包', '07-26'],
38      ['🔥', '深夜急送 8 折', '22:00-06:00 全场 8 折', '07-25']
39    ]
40  };
41
42  // ---- 小工具 ----
43  function go(h) { location.hash = h; }
44  window.go = go;
45  function goAttr(h) { return 'onclick="go(\'' + h + '\')"'; }
46  function alertAttr(t) { return 'onclick="alert(\'' + t + '\')"'; }
47  window.chipTap = function (el) { $(el).siblings().removeClass('on'); $(el).addClass('on'); };
48
49  // ---- 顶部栏 ----
50  function homeTop() {
51    return '<div class="topbar home">'
52      + '<div class="loc" ' + goAttr('me') + '>西安<span class="arr">▾</span></div>'
53      + '<div class="search" ' + goAttr('send') + '>🔍 搜索:文件、美食、代购、取件…</div>'
54      + '<div class="bell" ' + goAttr('message') + '>🔔</div>'
55      + '</div>';
56  }
57  function innTop(title, back) {
58    back = back || 'home';
59    return '<div class="topbar">'
60      + '<div class="back" ' + goAttr(back) + '>‹</div>'
61      + '<div class="ttl">' + title + '</div>'
62      + '<div class="right"></div>'
63      + '</div>';
64  }
65  function runnerTop() {
66    return '<div class="topbar home"><div class="loc">跑腿员中心</div>'
67      + '<div class="online" style="margin-left:auto">在线<div class="toggle on" id="ron" onclick="$(\'#ron\').toggleClass(\'on\')"></div></div></div>';
68  }
69  function tabbar(active) {
70    var items = [
71      ['home', '🏠', '首页'],
72      ['orders', '📋', '订单'],
73      ['message', '🔔', '消息'],
74      ['me', '👤', '我的']
75    ];
76    var h = '<div class="tabbar">';
77    items.forEach(function (it) {
78      var on = (it[0] === active) ? ' on' : '';
79      h += '<div class="tb' + on + '" ' + goAttr(it[0]) + '><div class="ic">' + it[1] + '</div><div class="lb">' + it[2] + '</div></div>';
80    });
81    h += '</div>';
82    return h;
83  }
84
85  // ---- 首页 ----
86  function banner() {
87    return '<div class="banner">'
88      + '<h3>新用户首单立减 ¥10</h3>'
89      + '<p>同城跑腿 · 30 分钟极速送达</p>'
90      + '<span class="tag">点击领取新人券包</span>'
91      + '</div>'
92      + '<div class="dots"><i class="on"></i><i></i><i></i></div>';
93  }
94  function grid() {
95    var items = [
96      ['📦', '帮我送', 'send'],
97      ['🛒', '帮我买', 'buy'],
98      ['📮', '代取快递', 'send'],
99      ['⏳', '排队代办', 'send'],
100      ['🔧', '万能帮忙', 'send'],
101      ['🍔', '美食代取', 'buy'],
102      ['📄', '文件速递', 'send'],
103      ['➕', '更多服务', 'send']
104    ];
105    var h = '<div class="grid">';
106    items.forEach(function (it) {
107      h += '<div class="it" ' + goAttr(it[2]) + '><div class="ic">' + it[0] + '</div><div class="lb">' + it[1] + '</div></div>';
108    });
109    h += '</div>';
110    return h;
111  }
112  function runners() {
113    var rs = [
114      ['🧑‍🦱', '王师傅', '4.9分 · 距您 0.8km · 今日已接 32 单'],
115      ['👨‍🦰', '李跑跑', '4.8分 · 距您 1.2km · 今日已接 21 单'],
116      ['🧔', '赵哥', '5.0分 · 距您 0.5km · 今日已接 40 单']
117    ];
118    var h = '<div class="sec"><div class="sec-h"><div class="t">附近跑腿员</div><div class="more">查看全部 ›</div></div>';
119    rs.forEach(function (r) {
120      h += '<div class="card runner"><div class="av">' + r[0] + '</div>'
121        + '<div><div class="nm">' + r[1] + '</div><div class="meta">' + r[2] + '</div></div>'
122        + '<div class="go" ' + goAttr('runner') + '>接单中</div></div>';
123    });
124    h += '</div>';
125    return h;
126  }
127  function promos() {
128    return '<div class="sec"><div class="sec-h"><div class="t">限时特惠</div><div class="more">更多 ›</div></div>'
129      + '<div class="promo">'
130      + '<div class="p a"><h4>会员免配送费</h4><span>开通即享全城免运</span></div>'
131      + '<div class="p b"><h4>深夜急送 8 折</h4><span>22:00-06:00 专属</span></div>'
132      + '</div></div>';
133  }
134  function homeBody() { return banner() + grid() + runners() + promos(); }
135
136  // ---- 通用表单 ----
137  function fld(label, type, ph) {
138    return '<div class="fld"><label>' + label + '</label><input type="' + type + '" placeholder="' + ph + '"></div>';
139  }
140  function chip(t) { return '<div class="chip" onclick="chipTap(this)">' + t + '</div>'; }
141
142  function sendBody() {
143    return '<div class="frm">'
144      + fld('取件地址', 'text', '请输入取件位置')
145      + fld('收件地址', 'text', '请输入收件位置')
146      + '<div class="fld"><label>物品类型</label><div class="chips" id="itype">'
147      + chip('文件') + chip('美食') + chip('数码') + chip('服饰') + chip('其他')
148      + '</div></div>'
149      + '<div class="fld"><label>期望时间</label><select><option>尽快送达(约 30 分钟)</option><option>1 小时内</option><option>指定时间</option></select></div>'
150      + '<div class="fld"><label>加急配送</label><div class="toggle" data-k="urgent"></div></div>'
151      + '<div class="fld"><label>物品保价</label><div class="toggle" data-k="insure"></div></div>'
152      + '<div class="fld"><label>备注</label><input placeholder="如:放门口、轻拿轻放"></div>'
153      + '</div>'
154      + '<div class="fee"><span>预估费用</span><b>¥8.0</b></div>'
155      + '<div class="btn" ' + goAttr('detail') + '>立即下单</div>';
156  }
157  function buyBody() {
158    return '<div class="frm">'
159      + fld('商品/店铺', 'text', '如:星巴克(赛格店) 拿铁')
160      + fld('购买地点', 'text', '请输入商家位置')
161      + fld('数量规格', 'text', '如:中杯 2 杯')
162      + fld('预算上限', 'text', '如:¥50')
163      + fld('送达地址', 'text', '请输入送达位置')
164      + fld('备注', 'text', '如:少糖、去冰')
165      + '</div>'
166      + '<div class="card" style="margin-top:12px;font-size:13px;color:#8a8f9c;line-height:1.7">💡 <b style="color:#FF6A00">实报实销</b>:跑腿员按实际小票金额垫付,您确认收货后按小票 + 服务费结算,全程透明可追溯。</div>'
167      + '<div class="fee"><span>预估服务费</span><b>¥6.0</b></div>'
168      + '<div class="btn" ' + goAttr('detail') + '>立即下单</div>';
169  }
170  function bindForm() {
171    $('.toggle').off('click').on('click', function () { $(this).toggleClass('on'); });
172    $('#itype .chip').off('click').on('click', function () { $('#itype .chip').removeClass('on'); $(this).addClass('on'); });
173  }
174
175  // ---- 订单列表 ----
176  function ordList(tab) {
177    var arr = ORDERS[tab] || [];
178    if (!arr.length) return '<div class="empty">暂无' + tab + '订单</div>';
179    var h = '';
180    arr.forEach(function (o) {
181      h += '<div class="ord" ' + goAttr('detail?id=' + o[6]) + '>'
182        + '<div class="oic">' + o[0] + '</div>'
183        + '<div class="mid"><div class="ti">' + o[1] + '</div><div class="ln">' + o[2] + '</div><div class="tm">' + o[3] + '</div></div>'
184        + '<div class="rt"><div class="st">' + o[4] + '</div><div class="amt">' + o[5] + '</div></div>'
185        + '</div>';
186    });
187    return h;
188  }
189  function ordersBody() {
190    var keys = ['进行中', '待支付', '已完成', '已取消'];
191    var t = '<div class="tabs" id="otabs">';
192    keys.forEach(function (k) { t += '<div class="tb' + (k === ORD_TAB ? ' on' : '') + '" data-k="' + k + '">' + k + '</div>'; });
193    t += '</div><div id="olist">' + ordList(ORD_TAB) + '</div>';
194    return t;
195  }
196  function bindOrders() {
197    $('#otabs .tb').off('click').on('click', function () {
198      ORD_TAB = $(this).data('k');
199      $('#otabs .tb').removeClass('on'); $(this).addClass('on');
200      $('#olist').html(ordList(ORD_TAB));
201    });
202  }
203
204  // ---- 订单详情 ----
205  function detailBody(q) {
206    var id = (q && q.id) || 'o1';
207    var all = [];
208    $.each(ORDERS, function (k, a) { all = all.concat(a); });
209    var o = all.filter(function (x) { return x[6] === id; })[0] || all[0];
210    var steps = ['已接单', '取件中', '配送中', '已完成'];
211    var h = '<div class="card"><div style="font-size:13px;color:#9aa0ac">订单号 ' + id.toUpperCase() + '</div>'
212      + '<div style="font-size:18px;font-weight:800;margin:6px 0">' + o[1] + '</div>'
213      + '<div class="steps">';
214    steps.forEach(function (s, idx) {
215      h += '<div class="s' + (idx < 3 ? ' on' : '') + '"><div class="dot">' + (idx < 3 ? '✓' : (idx + 1)) + '</div><div class="lb">' + s + '</div></div>';
216    });
217    h += '</div></div>';
218    h += '<div class="card runner"><div class="av">🧑‍🦱</div><div><div class="nm">王师傅 · 4.9分</div><div class="meta">陕A·8888 · 已为您服务 12 次</div></div><div class="go" ' + alertAttr('已联系跑腿员') + '>联系</div></div>';
219    h += '<div class="map"><span class="pin a">📍</span><span class="pin b">🏁</span><span class="route">实时轨迹 · 预计 12 分钟送达</span></div>';
220    h += '<div class="card"><div class="fees">'
221      + '<div class="fr"><span>基础配送费</span><span>¥6.0</span></div>'
222      + '<div class="fr"><span>加急费</span><span>¥2.0</span></div>'
223      + '<div class="fr"><span>夜间服务费</span><span>¥0.0</span></div>'
224      + '<div class="fr tot"><span>合计</span><span>' + o[5] + '</span></div>'
225      + '</div></div>';
226    h += '<div class="btn" ' + alertAttr('已发起取消申请') + '>取消订单</div>';
227    return h;
228  }
229
230  // ---- 跑腿员中心 ----
231  function runnerBody() {
232    var b = '<div class="stats">'
233      + '<div class="c"><b>¥128</b><span>今日收入</span></div>'
234      + '<div class="c"><b>12</b><span>接单量</span></div>'
235      + '<div class="c"><b>4.9</b><span>综合评分</span></div></div>';
236    var grabs = [
237      ['📦', '帮我送 · 文件', '距您 0.6km · 预计 18 分钟', '¥8.0'],
238      ['📮', '代取快递 · 京东', '距您 1.1km · 预计 10 分钟', '¥5.0'],
239      ['🍔', '美食代取 · 奶茶', '距您 0.9km · 预计 15 分钟', '¥7.0']
240    ];
241    b += '<div class="sec"><div class="sec-h"><div class="t">附近可抢订单</div><div class="more">刷新 ›</div></div>';
242    grabs.forEach(function (g) {
243      b += '<div class="card runner"><div class="av">' + g[0] + '</div><div><div class="nm">' + g[1] + '</div><div class="meta">' + g[2] + '</div></div><div class="go" ' + alertAttr('抢单成功') + '>抢单</div></div>';
244    });
245    b += '</div>';
246    b += '<div class="card" ' + goAttr('wallet') + ' style="display:flex;align-items:center;justify-content:space-between;margin-top:12px"><div>💰 我的钱包</div><div style="color:#FF6A00;font-weight:700">查看 ›</div></div>';
247    return b;
248  }
249
250  // ---- 我的 ----
251  function mg(ic, lb, to) { return '<div class="it" ' + goAttr(to) + '><div class="ic">' + ic + '</div><div class="lb">' + lb + '</div></div>'; }
252  function meBody() {
253    var h = '<div class="me-h"><div class="av">🛵</div><div><div class="nm">跑腿小哥</div><div class="vip">👑 VIP 会员</div></div></div>';
254    h += '<div class="me-grid">'
255      + mg('💰', '我的钱包', 'wallet') + mg('🎟️', '优惠券', 'wallet') + mg('📍', '我的地址', 'address') + mg('👥', '常用收件人', 'address')
256      + mg('📋', '我的订单', 'orders') + mg('🏃', '跑腿接单', 'runner') + mg('💬', '在线客服', 'message') + mg('⚙️', '设置', 'me')
257      + '</div>';
258    h += '<div class="invite"><div class="em">🎁</div><div class="tx"><b>邀请好友得现金</b><span>每邀请 1 人得 ¥5,上不封顶</span></div><div class="btn2" ' + alertAttr('已复制邀请链接') + '>立即邀请</div></div>';
259    return h;
260  }
261
262  // ---- 钱包 / 优惠券 ----
263  function coupons() {
264    var cs = [
265      ['减5', '满20', '全场跑腿通用', '2026.08.31 到期', '#FF7A00'],
266      ['免', '配送费券', '下单立免基础运费', '2026.08.15 到期', '#6A5CFF'],
267      ['减10', '新人', '首单专享', '2026.09.01 到期', '#FF5252']
268    ];
269    var h = '';
270    cs.forEach(function (c) {
271      h += '<div class="card" style="display:flex;align-items:center;gap:14px">'
272        + '<div style="width:64px;height:64px;border-radius:12px;background:linear-gradient(135deg,' + c[4] + ',#ff9a6b);color:#fff;display:flex;flex-direction:column;align-items:center;justify-content:center;flex:0 0 auto"><b style="font-size:18px">' + c[0] + '</b><span style="font-size:10px">' + c[1] + '</span></div>'
273        + '<div style="flex:1"><div style="font-weight:700">' + c[2] + '</div><div style="font-size:12px;color:#9aa0ac;margin-top:3px">' + c[3] + '</div></div>'
274        + '<div style="background:#FFF1E8;color:#FF6A00;font-size:12px;font-weight:700;padding:6px 12px;border-radius:16px">去使用</div></div>';
275    });
276    return h;
277  }
278  function bills() {
279    var bs = [['微信支付充值', '+¥50.00', '07-28'], ['帮送订单支付', '-¥8.00', '07-27'], ['新人立减', '-¥10.00', '07-26']];
280    var h = '';
281    bs.forEach(function (b) {
282      h += '<div class="card" style="display:flex;justify-content:space-between;align-items:center"><div><div style="font-weight:600">' + b[0] + '</div><div style="font-size:11px;color:#bcc0ca">' + b[2] + '</div></div><div style="font-weight:700;color:' + (b[1][0] === '+' ? '#22c55e' : '#1f2330') + '">' + b[1] + '</div></div>';
283    });
284    return h;
285  }
286  function walletBody() {
287    var b = '<div class="bal"><div class="l">账户余额(元)</div><div class="v">¥0.00</div><div class="rech" ' + alertAttr('前往充值') + '>充值</div></div>';
288    b += '<div class="tabs" id="wtabs"><div class="tb on" data-k="优惠券">优惠券</div><div class="tb" data-k="账单">账单</div></div>';
289    b += '<div id="wbody">' + coupons() + '</div>';
290    return b;
291  }
292  function bindWallet() {
293    $('#wtabs .tb').off('click').on('click', function () {
294      $('#wtabs .tb').removeClass('on'); $(this).addClass('on');
295      var k = $(this).data('k');
296      $('#wbody').html(k === '优惠券' ? coupons() : bills());
297    });
298  }
299
300  // ---- 消息中心 ----
301  function msgList(arr) {
302    var h = '';
303    arr.forEach(function (m) {
304      var cls = m[0] === '📢' ? 'a' : (m[0] === '📦' ? 'b' : 'c');
305      h += '<div class="msg"><div class="mic ' + cls + '">' + m[0] + '</div><div class="mid"><div class="ti">' + m[1] + '<span class="tm">' + m[3] + '</span></div><div class="sn">' + m[2] + '</div></div></div>';
306    });
307    return h;
308  }
309  function messageBody() {
310    var keys = ['系统通知', '订单消息', '活动推送'];
311    var t = '<div class="tabs" id="mtabs">';
312    keys.forEach(function (k, i) { t += '<div class="tb' + (i === 0 ? ' on' : '') + '" data-k="' + k + '">' + k + '</div>'; });
313    t += '</div><div id="mlist">' + msgList(MSG[keys[0]]) + '</div>';
314    return t;
315  }
316  function bindMessage() {
317    $('#mtabs .tb').off('click').on('click', function () {
318      $('#mtabs .tb').removeClass('on'); $(this).addClass('on');
319      var k = $(this).data('k');
320      $('#mlist').html(msgList(MSG[k]));
321    });
322  }
323
324  // ---- 地址管理 ----
325  function addressBody() {
326    var addrs = [
327      ['默认', '🏠', '张三', '189****8888', '西安市雁塔区科技路18号 高新万达2号楼1503', true],
328      ['公司', '🏢', '张三', '189****8888', '西安市高新区唐延路25号 软件园F座6层', false],
329      ['学校', '🏫', '张三', '189****8888', '西安市碑林区师大路1号 6号楼302', false]
330    ];
331    var h = '';
332    addrs.forEach(function (a) {
333      h += '<div class="addr">'
334        + '<div class="' + (a[5] ? 'def' : '') + '">' + (a[5] ? '默认' : a[0]) + '</div>'
335        + '<div class="info"><div class="nm">' + a[1] + ' ' + a[2] + ' ' + a[3] + '</div><div class="ln">' + a[4] + '</div></div>'
336        + '<div class="op" ' + alertAttr('编辑地址') + '>编辑</div>'
337        + '<div class="op" ' + alertAttr('删除地址') + '>删除</div>'
338        + '</div>';
339    });
340    h += '<div class="btn" ' + alertAttr('新增地址') + '>+ 新增地址</div>';
341    return h;
342  }
343
344  // ---- 页面表 ----
345  var PAGES = {
346    home:   function () { return { top: homeTop(), body: homeBody(), tab: true }; },
347    send:   function () { return { top: innTop('帮我送'), body: sendBody(), bind: bindForm }; },
348    buy:    function () { return { top: innTop('帮我买'), body: buyBody(), bind: bindForm }; },
349    orders: function () { return { top: innTop('我的订单'), body: ordersBody(), bind: bindOrders, tab: true }; },
350    detail: function (q) { return { top: innTop('订单详情'), body: detailBody(q) }; },
351    runner: function () { return { top: runnerTop(), body: runnerBody() }; },
352    me:     function () { return { top: innTop('我的'), body: meBody(), tab: true }; },
353    wallet: function () { return { top: innTop('钱包'), body: walletBody(), bind: bindWallet }; },
354    message:function () { return { top: innTop('消息中心'), body: messageBody(), bind: bindMessage, tab: true }; },
355    address:function () { return { top: innTop('地址管理'), body: addressBody() }; }
356  };
357
358  // ---- 路由 ----
359  function parseHash() {
360    var h = location.hash.replace(/^#/, '');
361    if (!h) h = 'home';
362    var q = {};
363    var qi = h.indexOf('?');
364    if (qi >= 0) {
365      var qs = h.substr(qi + 1); h = h.substr(0, qi);
366      qs.split('&').forEach(function (p) {
367        var kv = p.split('=');
368        q[decodeURIComponent(kv[0])] = decodeURIComponent(kv[1] || '');
369      });
370    }
371    return { name: h, q: q };
372  }
373  function render() {
374    var p = parseHash();
375    var name = ROUTES.indexOf(p.name) >= 0 ? p.name : 'home';
376    CUR = name;
377    var v = PAGES[name](p.q);
378    var html = (v.top || '') + '<div class="page">' + (v.body || '') + '</div>' + (v.tab ? tabbar(name) : '');
379    $('#app').html(html);
380    if (v.bind) v.bind();
381    $('.page').scrollTop(0);
382  }
383
384  $(window).on('hashchange', render);
385  $(function () { render(); });
386})(jQuery);
387