├── pages
├── index
│ ├── index.json
│ ├── index.wxss
│ ├── index.wxml
│ └── index.js
├── pre-order
│ ├── pre-order.json
│ ├── pre-order.wxml
│ ├── pre-order.wxss
│ └── pre-order.js
├── order-detail
│ ├── order-detail.json
│ ├── order-detail.wxss
│ ├── order-detail.wxml
│ └── order-detail.js
├── order-success
│ ├── order-success.json
│ ├── order-success.wxml
│ ├── order-success.js
│ └── order-success.wxss
├── people-number
│ ├── people-number.json
│ ├── people-number.js
│ ├── people-number.wxml
│ └── people-number.wxss
├── history-orders
│ ├── history-orders.json
│ ├── history-orders.wxml
│ ├── history-orders.wxss
│ └── history-orders.js
├── dishes
│ ├── dishes.json
│ ├── dishes.wxss
│ ├── dishes.wxml
│ ├── dishes.js
│ └── data.js
└── dish-detail
│ ├── dish-detail.json
│ ├── dish-detail.wxml
│ ├── dish-detail.js
│ └── dish-detail.wxss
├── images
├── bg.jpg
├── add2@3x.png
├── add@3x.png
├── img2@3x.png
├── logo@3x.png
├── map@3x.png
├── minus@3x.png
├── order@3x.png
├── shop@3x.png
├── tel@3x.png
├── trash@3x.png
├── user@3x.png
├── minus2@3x.png
├── btn-bill@3x.png
├── down-arrow@3x.png
├── line-left@3x.png
├── line-right@3x.png
├── vegetable@3x.png
├── scan-qr-code@3x.png
├── user-single@3x.png
├── order-success@3x.png
└── user-multiple@3x.png
├── components
└── title-bar
│ ├── title-bar.json
│ ├── title-bar.wxss
│ ├── title-bar.wxml
│ └── title-bar.js
├── sitemap.json
├── README.md
├── .gitignore
├── app.wxss
├── app.json
├── utils
├── util.js
├── cart.js
└── request.js
├── project.config.json
└── app.js
/pages/index/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "disableScroll": true
3 | }
--------------------------------------------------------------------------------
/images/bg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zkboys/weixin-order-food/HEAD/images/bg.jpg
--------------------------------------------------------------------------------
/components/title-bar/title-bar.json:
--------------------------------------------------------------------------------
1 | {
2 | "component": true,
3 | "usingComponents": {}
4 | }
--------------------------------------------------------------------------------
/images/add2@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zkboys/weixin-order-food/HEAD/images/add2@3x.png
--------------------------------------------------------------------------------
/images/add@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zkboys/weixin-order-food/HEAD/images/add@3x.png
--------------------------------------------------------------------------------
/images/img2@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zkboys/weixin-order-food/HEAD/images/img2@3x.png
--------------------------------------------------------------------------------
/images/logo@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zkboys/weixin-order-food/HEAD/images/logo@3x.png
--------------------------------------------------------------------------------
/images/map@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zkboys/weixin-order-food/HEAD/images/map@3x.png
--------------------------------------------------------------------------------
/images/minus@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zkboys/weixin-order-food/HEAD/images/minus@3x.png
--------------------------------------------------------------------------------
/images/order@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zkboys/weixin-order-food/HEAD/images/order@3x.png
--------------------------------------------------------------------------------
/images/shop@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zkboys/weixin-order-food/HEAD/images/shop@3x.png
--------------------------------------------------------------------------------
/images/tel@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zkboys/weixin-order-food/HEAD/images/tel@3x.png
--------------------------------------------------------------------------------
/images/trash@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zkboys/weixin-order-food/HEAD/images/trash@3x.png
--------------------------------------------------------------------------------
/images/user@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zkboys/weixin-order-food/HEAD/images/user@3x.png
--------------------------------------------------------------------------------
/images/minus2@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zkboys/weixin-order-food/HEAD/images/minus2@3x.png
--------------------------------------------------------------------------------
/images/btn-bill@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zkboys/weixin-order-food/HEAD/images/btn-bill@3x.png
--------------------------------------------------------------------------------
/images/down-arrow@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zkboys/weixin-order-food/HEAD/images/down-arrow@3x.png
--------------------------------------------------------------------------------
/images/line-left@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zkboys/weixin-order-food/HEAD/images/line-left@3x.png
--------------------------------------------------------------------------------
/images/line-right@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zkboys/weixin-order-food/HEAD/images/line-right@3x.png
--------------------------------------------------------------------------------
/images/vegetable@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zkboys/weixin-order-food/HEAD/images/vegetable@3x.png
--------------------------------------------------------------------------------
/pages/pre-order/pre-order.json:
--------------------------------------------------------------------------------
1 | {
2 | "disableScroll": true,
3 | "navigationBarTitleText": "订单确认"
4 | }
--------------------------------------------------------------------------------
/images/scan-qr-code@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zkboys/weixin-order-food/HEAD/images/scan-qr-code@3x.png
--------------------------------------------------------------------------------
/images/user-single@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zkboys/weixin-order-food/HEAD/images/user-single@3x.png
--------------------------------------------------------------------------------
/pages/order-detail/order-detail.json:
--------------------------------------------------------------------------------
1 | {
2 | "disableScroll": true,
3 | "navigationBarTitleText": "订单详情"
4 | }
--------------------------------------------------------------------------------
/pages/order-success/order-success.json:
--------------------------------------------------------------------------------
1 | {
2 | "disableScroll": true,
3 | "navigationBarTitleText": "下单成功"
4 | }
--------------------------------------------------------------------------------
/pages/people-number/people-number.json:
--------------------------------------------------------------------------------
1 | {
2 | "disableScroll": true,
3 | "navigationBarTitleText": "选择人数"
4 | }
--------------------------------------------------------------------------------
/images/order-success@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zkboys/weixin-order-food/HEAD/images/order-success@3x.png
--------------------------------------------------------------------------------
/images/user-multiple@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zkboys/weixin-order-food/HEAD/images/user-multiple@3x.png
--------------------------------------------------------------------------------
/pages/history-orders/history-orders.json:
--------------------------------------------------------------------------------
1 | {
2 | "disableScroll": true,
3 | "navigationBarTitleText": "我的订单"
4 | }
--------------------------------------------------------------------------------
/pages/dishes/dishes.json:
--------------------------------------------------------------------------------
1 | {
2 | "disableScroll": true,
3 | "navigationBarTitleText": "点餐",
4 | "usingComponents": {
5 | "title-bar": "../../components/title-bar/title-bar"
6 | }
7 | }
--------------------------------------------------------------------------------
/pages/dish-detail/dish-detail.json:
--------------------------------------------------------------------------------
1 | {
2 | "disableScroll": true,
3 | "navigationBarTitleText": "菜品详情",
4 | "usingComponents": {
5 | "title-bar": "../../components/title-bar/title-bar"
6 | }
7 | }
--------------------------------------------------------------------------------
/sitemap.json:
--------------------------------------------------------------------------------
1 | {
2 | "desc": "关于本文件的更多信息,请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html",
3 | "rules": [{
4 | "action": "allow",
5 | "page": "*"
6 | }]
7 | }
--------------------------------------------------------------------------------
/components/title-bar/title-bar.wxss:
--------------------------------------------------------------------------------
1 | .title-bar {
2 | text-align: center;
3 | padding: 10rpx;
4 | background: #fff;
5 | }
6 |
7 | .bar-icon {
8 | width: 74rpx;
9 | height: 17rpx;
10 | }
11 |
12 | .bar-text {
13 | margin: 0 34rpx;
14 | font-size: 28rpx;
15 | color: #D1A646;
16 | }
--------------------------------------------------------------------------------
/components/title-bar/title-bar.wxml:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{title}}
4 |
5 |
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # weixin-order-food
2 | 微信小程序-扫码点餐
3 |
4 | ## TODO
5 | - [ ] 多人同步点餐,购物车、确认订单中,是否显示点餐人头像?
6 | - [ ] 系统稍作改造可以支持服务员点餐
7 | - 后端指定微信用户为服务员
8 | - 小程序中,检测为服务员之后,提交订单 不发起微信支付,直接下单,后端直接打印小票
9 |
10 |
11 | - [ ] 微信小程序获取到的数据问题:
12 | - type === '02',里面的data 属性没有必要吧?一层就可以了
13 | - type === '03',指的是推荐菜品,系统上只能设置一个推荐菜品: 推荐名称 + 推荐内包含的菜品列表
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | Thumbs.db
2 | .DS_Store
3 | .gradle
4 | build/
5 | classes/
6 | .idea
7 | *.iml
8 | *.ipr
9 | *.iws
10 | .project
11 | .settings
12 | .classpath
13 | bin/
14 | .git/
15 | catalina*
16 | logs/
17 | *.log
18 | *.cache
19 | gradle
20 | /gradlew
21 | gradlew*
22 | .apt_generated
23 | .factorypath
24 | .springBeans
25 | .lock
26 | out/
27 | *.bak
28 |
--------------------------------------------------------------------------------
/components/title-bar/title-bar.js:
--------------------------------------------------------------------------------
1 | // components/title-bar/title-bar.js
2 | Component({
3 | /**
4 | * 组件的属性列表
5 | */
6 | properties: {
7 | title: {
8 | type: String,
9 | value: '',
10 | },
11 | titleStyle: {
12 | type: String,
13 | value: '',
14 | }
15 | },
16 |
17 | /**
18 | * 组件的初始数据
19 | */
20 | data: {
21 |
22 | },
23 |
24 | /**
25 | * 组件的方法列表
26 | */
27 | methods: {
28 |
29 | }
30 | })
31 |
--------------------------------------------------------------------------------
/app.wxss:
--------------------------------------------------------------------------------
1 | /**app.wxss**/
2 | .container {
3 | height: 100vh;
4 | display: flex;
5 | flex-direction: column;
6 | align-items: center;
7 | justify-content: space-between;
8 | box-sizing: border-box;
9 | font-family: Helvetica,"Microsoft YaHei", Arial, Helvetica, sans-serif;
10 | color: #D1A646;
11 | }
12 | .container-bg{
13 | position: absolute;
14 | top: 0;
15 | bottom: 0;
16 | height: 100%;
17 | width: 100%;
18 | z-index: -1;
19 | background-color: #000;
20 | }
21 |
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "pages": [
3 | "pages/index/index",
4 | "pages/people-number/people-number",
5 | "pages/dishes/dishes",
6 | "pages/dish-detail/dish-detail",
7 | "pages/pre-order/pre-order",
8 | "pages/order-success/order-success",
9 | "pages/history-orders/history-orders",
10 | "pages/order-detail/order-detail"
11 | ],
12 | "window": {
13 | "backgroundTextStyle": "light",
14 | "backgroundColor": "#333",
15 | "navigationBarBackgroundColor": "#000",
16 | "navigationBarTitleText": "小行点餐",
17 | "navigationBarTextStyle": "white"
18 | },
19 | "sitemapLocation": "sitemap.json"
20 | }
--------------------------------------------------------------------------------
/pages/people-number/people-number.js:
--------------------------------------------------------------------------------
1 | const app = getApp();
2 |
3 | Page({
4 |
5 | /**
6 | * 页面的初始数据
7 | */
8 | data: {
9 | userInfo: {},
10 | hasUserInfo: false,
11 | peopleNumber: '',
12 | },
13 |
14 | /**
15 | * 生命周期函数--监听页面加载
16 | */
17 | onLoad: function (options) {
18 | if (app.globalData.userInfo) {
19 | this.setData({
20 | userInfo: app.globalData.userInfo,
21 | hasUserInfo: true
22 | })
23 | }
24 | },
25 |
26 | handleNumberClick: function (e) {
27 | const {number} = e.currentTarget.dataset;
28 | this.setData({peopleNumber: number});
29 |
30 | wx.setStorageSync('peopleNumber', number);
31 | wx.navigateTo({
32 | url: '/pages/dishes/dishes',
33 | })
34 | },
35 | });
--------------------------------------------------------------------------------
/pages/people-number/people-number.wxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 请选择用餐人数
7 |
8 |
9 |
10 |
17 | {{row * 4 + col}}人
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/pages/people-number/people-number.wxss:
--------------------------------------------------------------------------------
1 | .container{
2 | display: flex;
3 | flex-direction: column;
4 | justify-content: center;
5 | }
6 | .title{
7 | text-align: center;
8 | }
9 |
10 | .avatar {
11 | flex-basis: 95rpx;
12 | flex-shrink: 0;
13 | width: 95rpx;
14 | height: 95rpx;
15 | border-radius: 50%;
16 | }
17 |
18 | .tip {
19 | margin-top: 25rpx;
20 | font-size: 24rpx;
21 | color: #D1A646;
22 | }
23 |
24 | .number-box {
25 | margin-top: 50rpx;
26 | }
27 |
28 | .number-item {
29 | display: inline-block;
30 | width: 85rpx;
31 | height: 70rpx;
32 | line-height: 70rpx;
33 | margin: 24rpx 26rpx;
34 | color: #D1A646;
35 | border: 1rpx solid #D1A646;
36 | border-radius: 6rpx;
37 | font-size: 24rpx;
38 | text-align: center;
39 | }
40 |
41 | .number-item.active{
42 | background: #D1A646;
43 | color: #fff;
44 | }
45 |
46 | .btn-ok{
47 | padding: 22rpx 0;
48 | margin: 70rpx 20rpx 0 20rpx;
49 | background: #D1A646;
50 | border-radius: 6rpx;
51 | font-size: 32rpx;
52 | color: #fff;
53 | text-align: center;
54 | }
--------------------------------------------------------------------------------
/pages/history-orders/history-orders.wxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 | {{item.name}}
11 |
12 |
13 |
14 |
15 | {{item.storeName}}
16 |
17 |
18 | {{item.orderTime}}
19 | {{item.priceStr}}
20 |
21 |
22 | 店内自助
23 | {{item.status}}
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/pages/order-success/order-success.wxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 下单成功,感谢您的光临!
5 |
6 |
7 | 已下单({{dishTotalCount}})
8 |
9 |
10 | 下单时间:{{orderTime}}
11 | 桌号:{{deskNo}}
12 |
13 |
14 |
15 |
16 | {{dish.name}}
17 | /{{dish.unit}}
18 |
19 |
20 | ¥
21 | {{dish.priceStr}}
22 | x
23 | {{dish.count}}
24 |
25 |
26 |
27 |
28 | 小计:{{dishTotalPriceStr}}
29 |
30 |
31 | 继续点餐
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/pages/history-orders/history-orders.wxss:
--------------------------------------------------------------------------------
1 | .history-order-container{
2 | height: 100vh;
3 | display: flex;
4 | flex-direction: column;
5 | font-weight: normal;
6 | background-color: rgba(246,246,246,1);
7 | }
8 |
9 | .tab-container {
10 | display: flex;
11 | flex-shrink: 0;
12 | background: #fff;
13 | margin-bottom: 10rpx;
14 | }
15 |
16 | .tab-container .tab {
17 | flex-grow: 1;
18 | padding: 25rpx 0;
19 | text-align: center;
20 | color: #666;
21 | font-size: 32rpx;
22 | }
23 |
24 | .tab-container .tab.active {
25 | color: #D1A646;
26 | border-bottom: 2rpx solid #D1A646;
27 | }
28 |
29 | .order-list {
30 | flex-grow: 1;
31 | overflow: auto;
32 | -webkit-overflow-scrolling: touch;
33 | }
34 |
35 | .order-item {
36 | background: #fff;
37 | margin-bottom: 10rpx;
38 | }
39 |
40 | .store-name{
41 | color: #333;
42 | font-size: 28rpx;
43 | padding: 20rpx 30rpx;
44 | border-bottom: 1rpx solid #DFE2E5;
45 | }
46 |
47 | .time-money, .type-status {
48 | display: flex;
49 | justify-content: space-between;
50 | font-size: 24rpx;
51 | padding: 30rpx;
52 | }
53 |
54 | .time-money {
55 | padding-bottom: 15rpx;
56 | color: #A1A1A1;
57 | }
58 |
59 | .time-money .money{
60 | color: #535353;
61 | }
62 |
63 | .type-status {
64 | padding-top: 0;
65 | color: #333333;
66 | }
67 |
68 | .type-status .status {
69 | color: #535353;
70 | }
--------------------------------------------------------------------------------
/pages/index/index.wxss:
--------------------------------------------------------------------------------
1 | /**index.wxss**/
2 | .user-info {
3 | position:relative;
4 | display: flex;
5 | width: 100%;
6 | padding-top: 50rpx;
7 | padding-left: 100rpx;
8 | align-items: center;
9 | }
10 | .user-info button {
11 | position: absolute;
12 | top: 0;
13 | right: 50%;
14 | bottom: 0;
15 | left: 0;
16 | opacity: 0;
17 | }
18 | .avatar {
19 | flex-basis: 95rpx;
20 | flex-shrink: 0;
21 | width: 95rpx;
22 | height: 95rpx;
23 | border-radius: 50%;
24 | }
25 | .welcome {
26 | padding-left: 30rpx;
27 | color: #D1A646;
28 | font-size: 28rpx;
29 | }
30 |
31 | .store-info {
32 | display: flex;
33 | flex-direction: column;
34 | align-items: center;
35 | }
36 |
37 | .store-logo {
38 | width: 241rpx;
39 | height: 194rpx;
40 | }
41 |
42 | .store-name {
43 | margin-top: 26rpx;
44 | font-size: 31rpx;
45 | }
46 |
47 | .store-desk {
48 | margin-top: 74rpx;
49 | font-size: 28rpx;
50 | }
51 |
52 | .btn-operator-group {
53 | display: flex;
54 | align-items: center;
55 | justify-content: space-between;
56 | padding-bottom: 200rpx;
57 | }
58 | .btn-operator {
59 | display: flex;
60 | flex-direction: column;
61 | align-items: center;
62 | margin: 0 80rpx;
63 | }
64 |
65 | .order {
66 | width: 120rpx;
67 | height: 120rpx;
68 | }
69 |
70 | .order-text{
71 | margin-top: 18rpx;
72 | font-size: 30rpx;
73 | }
74 |
75 | .scan-qr-code{
76 | width: 250rpx;
77 | height: 250rpx;
78 | }
--------------------------------------------------------------------------------
/pages/order-success/order-success.js:
--------------------------------------------------------------------------------
1 | const cart = require('../../utils/cart');
2 | const {formatCurrency, formatTime} = require('../../utils/util');
3 |
4 | Page({
5 |
6 | /**
7 | * 页面的初始数据
8 | */
9 | data: {
10 | orderTime: '',
11 | deskNo: '',
12 | dishes: [],
13 | dishTotalCount: 0,
14 | dishTotalPrice: 0,
15 | dishTotalPriceStr: '¥0.00',
16 | },
17 |
18 | /**
19 | * 生命周期函数--监听页面加载
20 | */
21 | onLoad: function (options) {
22 |
23 | const dishTotalPrice = cart.getTotalPrice();
24 | const dishTotalCount = cart.getTotalCount();
25 | const dishes = cart.getDataSource();
26 | const deskNo = wx.getStorageSync('deskNo');
27 | const dishTotalPriceStr = formatCurrency(dishTotalPrice);
28 | const orderTime = formatTime(new Date());
29 |
30 | dishes.forEach(dish => {
31 | dish.priceStr = formatCurrency(dish.price, {prefix: ''});
32 | });
33 |
34 | this.setData({
35 | deskNo,
36 | dishes,
37 | dishTotalCount,
38 | dishTotalPrice,
39 | dishTotalPriceStr,
40 | orderTime,
41 | }, () => {
42 | // 清除本次流程存储的相关数据, 其他数据应该不用清除
43 | cart.clear(); // cart clear 用到 storeId 了
44 | // 清除storeId deskNo之后,需要重新扫码
45 | wx.removeStorageSync('storeId');
46 | wx.removeStorageSync('deskNo');
47 | });
48 | },
49 |
50 | handleReOrder: function () {
51 | wx.navigateTo({
52 | url: '/pages/index/index',
53 | })
54 | },
55 | })
56 |
--------------------------------------------------------------------------------
/pages/pre-order/pre-order.wxml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 待下单({{dishTotalCount}})
4 |
5 | 桌号:{{deskNo}}
6 |
7 |
8 |
9 |
10 |
11 | {{dish.name}}
12 | /{{dish.unit}}
13 |
14 |
15 | ¥
16 | {{dish.priceStr}}
17 | x
18 | {{dish.count}}
19 |
20 |
21 |
22 |
23 | 小计:{{dishTotalPriceStr}}
24 |
25 |
26 |
27 | 人数:
28 | {{peopleNumber}}
29 |
30 |
31 |
32 |
36 |
37 |
38 | {{dishTotalPriceStr}}
39 |
40 |
41 | 提交订单
42 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/pages/dish-detail/dish-detail.wxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | {{dish.name}}
7 |
8 |
9 |
10 | {{item.dishName}}
11 |
12 |
13 |
14 | {{item.label}}
22 |
23 |
24 |
25 | 暂无菜品介绍
26 |
27 |
28 | 菜品介绍
29 |
30 | {{dish.presentation}}
31 |
32 |
33 |
34 |
35 |
36 | 单价:
37 | {{dish.priceStr}}
38 | 元
39 |
40 |
41 |
42 |
43 | {{dish.count}}
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/pages/order-detail/order-detail.wxss:
--------------------------------------------------------------------------------
1 | .order-detail-container{
2 | height: 100vh;
3 | display: flex;
4 | flex-direction: column;
5 | font-weight: normal;
6 | background-color: rgba(246,246,246,1);
7 | }
8 | .store-msg {
9 | text-align: center;
10 | padding-top: 50rpx;
11 | background: #fff;
12 | flex-shrink: 0;
13 | border-bottom: 1rpx solid #DFE2E5;
14 | }
15 | .mobile-position{
16 | display: flex;
17 | padding-bottom: 30rpx;
18 | color: #A1A1A1;
19 | font-size: 24rpx;
20 | }
21 |
22 | .mobile-position view.mp-item{
23 | display:flex;
24 | align-items: center;
25 | justify-content: center;
26 | flex-grow: 1;
27 | }
28 |
29 | .mobile-position view.mp-item.first{
30 | border-right: 1px solid #A1A1A1;
31 | }
32 |
33 | .store-name {
34 | color: #333;
35 | font-size: 28rpx;
36 | margin-bottom: 30rpx;
37 | }
38 | .mobile-position image {
39 | width: 20rpx;
40 | height: 25rpx;
41 | margin-right: 10rpx;
42 | }
43 |
44 | .block{
45 | flex-grow: 1;
46 | overflow: auto;
47 | -webkit-overflow-scrolling: touch;
48 | }
49 |
50 | .order-msg,
51 | .dish-msg
52 | {
53 | margin-top: 10rpx;
54 | background: #ffff;
55 | }
56 | .order-msg .title,
57 | .dish-msg .title
58 | {
59 | display: flex;
60 | justify-content: space-between;
61 | padding: 20rpx 30rpx;
62 | color: #333;
63 | font-size: 28rpx;
64 | border-bottom: 1rpx solid #DFE2E5;
65 | }
66 |
67 | .dish-msg .money {
68 | color: #ED3E39;
69 | }
70 |
71 | .order-msg{
72 | margin-bottom: 50rpx;
73 | }
74 | .item {
75 | display: flex;
76 | justify-content: space-between;
77 | align-items: center;
78 | padding: 20rpx 30rpx;
79 | background: #fff;
80 | }
81 |
82 | .item .label{
83 | color: #A1A1A1;
84 | font-size: 24rpx;
85 | }
86 |
87 | .item .value{
88 | color: #333;
89 | font-size: 24rpx;
90 | }
91 |
92 | .dish-msg .item .value {
93 | flex-basis: 150rpx;
94 | flex-shrink: 0;
95 | text-align: right;
96 | }
97 | .dish-name {
98 | color: #333;
99 | }
100 |
101 | .money-prefix{
102 | color: #A1A1A1;
103 | }
--------------------------------------------------------------------------------
/pages/order-success/order-success.wxss:
--------------------------------------------------------------------------------
1 | .order-success-container{
2 | height: 100vh;
3 | display: flex;
4 | flex-direction: column;
5 | font-weight: normal;
6 | background-color: rgba(246,246,246,1);
7 | }
8 |
9 | .success-tip{
10 | padding: 50rpx 0;
11 | background: #fff;
12 | text-align: center;
13 | color: #333;
14 | font-size: 32rpx;
15 | border-bottom: 1px solid rgba(246,246,246,1);
16 | }
17 |
18 | .success-tip image {
19 | width: 85rpx;
20 | height: 85rpx;
21 | margin-bottom: 30rpx;
22 | }
23 |
24 | .title {
25 | padding: 30rpx 0;
26 | margin-top: 15rpx;
27 | color: #333333;
28 | font-size: 28rpx;
29 | text-align: center;
30 | background: #fff;
31 | }
32 |
33 | .desk-number {
34 | display: flex;
35 | justify-content: space-between;
36 | align-items: center;
37 | padding-right: 30rpx;
38 | padding-left: 30rpx;
39 | padding-bottom: 20rpx;
40 | color: #A1A1A1;
41 | font-size: 24rpx;
42 | background: #fff;
43 | text-align: right;
44 | border-bottom: 1rpx solid #DFE2E5;
45 | }
46 | .dish-list{
47 | flex-grow: 1;
48 | overflow: auto;
49 | -webkit-overflow-scrolling: touch;
50 | background: #fff;
51 | }
52 |
53 | .dish-item{
54 | display: flex;
55 | justify-content: space-between;
56 | align-items: center;
57 | padding: 25rpx 30rpx;
58 | }
59 |
60 | .dish-msg {
61 | flex-grow: 1;
62 | font-size: 28rpx;
63 | }
64 |
65 | .dish-name{
66 | color: #333;
67 | }
68 |
69 | .dish-unit{
70 | color: #A1A1A1;
71 | }
72 |
73 | .dish-money {
74 | flex-basis: 150rpx;
75 | flex-shrink: 0;
76 | font-size: 26rpx;
77 | color: #333;
78 | text-align: right;
79 | }
80 |
81 | .money-prefix {
82 | color: #A1A1A1;
83 | }
84 |
85 | .dish-total{
86 | margin-top: 30rpx;
87 | margin-bottom: 30rpx;
88 | padding-right: 30rpx;
89 | font-size: 28rpx;
90 | color: #ED3E39;
91 | text-align: right;
92 | }
93 |
94 |
95 | .reorder {
96 | flex-basis: 104rpx;
97 | flex-shrink: 0;
98 | line-height: 104rpx;
99 | z-index: 999;
100 | background: #D1A646;
101 | text-align: center;
102 | color: #fff;
103 | font-size: 32rpx;
104 | }
105 |
--------------------------------------------------------------------------------
/pages/index/index.wxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | 顾客
9 | 您好,欢迎光临!
10 |
11 |
12 |
13 |
14 |
15 |
16 | {{userInfo.nickName}}
17 | 您好,欢迎光临!
18 |
19 |
20 |
21 |
22 | {{store.name}}
23 | 桌位:{{deskNo}}
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | 开始点餐
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 | 加菜
45 |
46 |
47 |
48 | 结账
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/pages/order-detail/order-detail.wxml:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{order.storeName}}
4 |
5 |
6 |
7 | 联系商家
8 |
9 |
10 |
11 | 地图导航
12 |
13 |
14 |
15 |
16 |
17 |
18 | 订单总价
19 | ¥{{order.totalPriceStr}}
20 |
21 |
22 |
23 | {{dish.name}}
24 | /{{dish.unit}}
25 |
26 |
27 | ¥
28 | {{dish.priceStr}} x {{dish.count}}
29 |
30 |
31 |
32 |
33 | 订单信息
34 |
35 | 订单状态
36 | {{order.status}}
37 |
38 |
39 | 订单号
40 | {{order.orderNo}}
41 |
42 |
43 | 桌号
44 | {{order.deskNo}}
45 |
46 |
47 | 消费时间
48 | {{order.orderTime}}
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/pages/dish-detail/dish-detail.js:
--------------------------------------------------------------------------------
1 | const {formatCurrency} = require('../../utils/util');
2 | const cart = require('../../utils/cart');
3 | Page({
4 |
5 | /**
6 | * 页面的初始数据
7 | */
8 | data: {
9 | isSuit: false, // 是否是套餐
10 | dish: {},
11 | },
12 |
13 | syncFromCart: function (dish) {
14 | const cartDish = cart.getById(dish.id);
15 |
16 | let d = dish;
17 | if (cartDish) {
18 | d = {
19 | ...dish,
20 | count: cartDish.count,
21 | specification: cartDish.specification || dish.specification,
22 | };
23 | }
24 | this.setData({dish: d});
25 | return d;
26 | },
27 |
28 | syncToCart: function (data) {
29 | cart.update(data);
30 | },
31 | /**
32 | * 生命周期函数--监听页面加载
33 | */
34 | onLoad: function (options) {
35 | // let {id, isSuit} = options;
36 |
37 | const dish = wx.getStorageSync('dish-for-detail');
38 | if (dish) {
39 | let specification = dish.dishSpecification ? dish.dishSpecification.split(',') : [];
40 | specification = specification.map(item => ({selected: false, label: item}));
41 | let d = {
42 | id: dish.id,
43 | picture: dish.picture,
44 | name: dish.name,
45 | type: dish.type,
46 | isSuit: dish.isSuit,
47 | dishes: dish.list,
48 | specification,
49 | presentation: dish.isSuit ? dish.dishsuitPresentation : dish.dishPresentation,
50 | price: dish.price,
51 | priceStr: formatCurrency(dish.price, {prefix: ''}),
52 | count: dish.count,
53 | };
54 |
55 | d = this.syncFromCart(d);
56 |
57 | this.setData({
58 | dish: d,
59 | });
60 | }
61 | },
62 |
63 | handleSpecificationClick: function (e) {
64 | let {dish} = this.data;
65 | const {specification} = dish;
66 | const {label, selected} = e.currentTarget.dataset;
67 | const s = specification.find(item => item.label === label);
68 | s.selected = !selected;
69 |
70 | this.setData({dish});
71 |
72 | this.syncToCart({id: dish.id, specification});
73 | },
74 |
75 | addCart: function () {
76 | const {dish} = this.data;
77 | cart.add(dish);
78 | this.syncFromCart(dish);
79 | },
80 |
81 | minusCart: function (e) {
82 | const {dish} = this.data;
83 | cart.remove(dish);
84 | this.syncFromCart(dish);
85 | },
86 |
87 | });
88 |
--------------------------------------------------------------------------------
/pages/history-orders/history-orders.js:
--------------------------------------------------------------------------------
1 | const request = require('../../utils/request');
2 | const {formatCurrency} = require('../../utils/util');
3 | Page({
4 |
5 | /**
6 | * 页面的初始数据
7 | */
8 | data: {
9 | storeName: '世纪金源店',
10 | activeTabStatus: 'all',
11 | tabs: [
12 | {status: 'all', name: '全部'},
13 | {status: '03', name: '待支付'},
14 | {status: '00', name: '已完成'},
15 | ],
16 | },
17 |
18 | /**
19 | * 生命周期函数--监听页面加载
20 | */
21 | onLoad: function (options) {
22 | const {activeTabStatus} = this.data;
23 | this.getOrdersByStatus(activeTabStatus);
24 | },
25 |
26 | getOrdersByStatus: function (status) {
27 | status = status === 'all' ? void 0 : status;
28 | const statusList = {
29 | '00': '已完成',
30 | '01': '支付失败',
31 | '02': '订单初始化',
32 | '03': '待支付',
33 | };
34 | request.getHistoryOrders({status})
35 | .then(res => {
36 | let dataSource = [];
37 | if (res.data.code === '0000') {
38 | dataSource = res.data.data;
39 | }
40 | const ds = dataSource.map(item => {
41 | const {date, time} = item;
42 | const status = statusList[item.status] || '';
43 | let orderTime = '';
44 | if (date && time) {
45 | const year = date.substr(0, 4);
46 | const month = date.substr(4, 2);
47 | const day = date.substr(6, 2);
48 | const hour = time.substr(0, 2);
49 | const minute = time.substr(2, 2);
50 | const second = time.substr(4, 2);
51 | orderTime = `${year}-${month}-${day} ${hour}:${minute}:${second}`;
52 | }
53 | return {
54 | ...item,
55 | id: item.id,
56 | orderNo: item.id,
57 | price: item.price,
58 | priceStr: formatCurrency(item.price),
59 | orderTime,
60 | storeName: item.store.name,
61 | status,
62 | };
63 | });
64 | this.setData({dataSource: ds});
65 | });
66 | },
67 |
68 | handleTabClick: function (e) {
69 | const {status} = e.currentTarget.dataset;
70 | this.setData({activeTabStatus: status});
71 | this.getOrdersByStatus(status);
72 | },
73 |
74 | handleOrderClick: function (e) {
75 | const {id} = e.currentTarget.dataset;
76 | wx.navigateTo({
77 | url: '/pages/order-detail/order-detail?id=' + id,
78 | })
79 | },
80 | });
81 |
--------------------------------------------------------------------------------
/utils/util.js:
--------------------------------------------------------------------------------
1 | const formatTime = date => {
2 | const year = date.getFullYear()
3 | const month = date.getMonth() + 1
4 | const day = date.getDate()
5 | const hour = date.getHours()
6 | const minute = date.getMinutes()
7 | const second = date.getSeconds()
8 |
9 | return [year, month, day].map(formatNumber).join('-') + ' ' + [hour, minute, second].map(formatNumber).join(':')
10 | };
11 |
12 | const formatNumber = n => {
13 | n = n.toString()
14 | return n[1] ? n : '0' + n
15 | };
16 |
17 |
18 | /**
19 | * 将数值四舍五入后格式化成金额形式
20 | *
21 | * @param num 数值(Number或者String)
22 | * @param options 可选参数
23 | * @param options.prefix 金钱前缀,默认为空,一般为 ¥ 或 $
24 | * @param options.decimalNum 保留小数点个数,默认为2 一般为 0 1 2
25 | * @param options.splitSymbol 格式化分割符,默认为英文逗号,分隔符必须是单字符
26 | * @return 金额格式的字符串,如'¥1,234,567.45'
27 | * @type String
28 | */
29 | function formatCurrency(num, options = {}) {
30 | let {decimalNum, splitSymbol} = options;
31 | const {prefix = '¥'} = options;
32 | let centsPercent = 100;
33 | if (splitSymbol === undefined) splitSymbol = ',';
34 | if (decimalNum !== 0 && decimalNum !== 1 && decimalNum !== 2) decimalNum = 2;
35 | if (decimalNum === 0) centsPercent = 1;
36 | if (decimalNum === 1) centsPercent = 10;
37 | num = num.toString().replace(/\$|,/g, '');
38 | if (isNaN(num)) num = '0';
39 | const sign = (num === Math.abs(num).toString()) ? '' : '-';
40 | num = Math.abs(num);
41 | num = Math.floor((num * centsPercent) + 0.50000000001);
42 | let cents = num % centsPercent;
43 | num = Math.floor(num / centsPercent).toString();
44 | if (cents < 10 && decimalNum === 2) {
45 | cents = `0${cents}`;
46 | }
47 | for (let i = 0; i < Math.floor((num.length - (1 + i)) / 3); i++) {
48 | const endPosition = (4 * i) + 3;
49 | num = num.substring(0, num.length - endPosition)
50 | + splitSymbol + num.substring(num.length - endPosition);
51 | }
52 | if (decimalNum === 0) {
53 | return prefix + sign + num;
54 | }
55 | return `${prefix}${sign}${num}.${cents}`;
56 | }
57 |
58 | function parseQueryString(str) {
59 | const kvs = str.split('&');
60 | const obj = {};
61 | if (kvs && kvs.length) {
62 | kvs.forEach(kv => {
63 | const keyValue = kv.split('=');
64 | let key;
65 | let value;
66 | if (keyValue && keyValue.length === 1) {
67 | key = keyValue[0];
68 | }
69 |
70 | if (keyValue && keyValue.length === 2) {
71 | key = keyValue[0];
72 | value = keyValue[1];
73 | }
74 |
75 | if (key) {
76 | obj[key] = value;
77 | }
78 | });
79 | }
80 | return obj;
81 | }
82 |
83 | module.exports = {
84 | formatTime: formatTime,
85 | formatCurrency: formatCurrency,
86 | parseQueryString: parseQueryString,
87 | };
88 |
--------------------------------------------------------------------------------
/project.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "项目配置文件。",
3 | "packOptions": {
4 | "ignore": []
5 | },
6 | "setting": {
7 | "urlCheck": true,
8 | "es6": true,
9 | "postcss": true,
10 | "minified": true,
11 | "newFeature": true,
12 | "checkInvalidKey": true
13 | },
14 | "compileType": "miniprogram",
15 | "libVersion": "2.0.4",
16 | "appid": "wx22c007b857d08d07",
17 | "projectname": "%E5%BE%AE%E4%BF%A1%E5%B0%8F%E7%A8%8B%E5%BA%8F-%E6%89%AB%E7%A0%81%E7%82%B9%E9%A4%90",
18 | "isGameTourist": false,
19 | "simulatorType": "wechat",
20 | "simulatorPluginLibVersion": {},
21 | "condition": {
22 | "search": {
23 | "current": -1,
24 | "list": []
25 | },
26 | "conversation": {
27 | "current": -1,
28 | "list": []
29 | },
30 | "plugin": {
31 | "current": -1,
32 | "list": []
33 | },
34 | "game": {
35 | "currentL": -1,
36 | "list": []
37 | },
38 | "miniprogram": {
39 | "current": 9,
40 | "list": [
41 | {
42 | "id": 0,
43 | "name": "1011: 扫描二维码 scene=storeId=1231231&deskNo=大厅011",
44 | "pathName": "pages/index/index",
45 | "query": "scene=storeId%3d1231231%26deskNo%3d大厅011",
46 | "scene": 1011
47 | },
48 | {
49 | "id": 1,
50 | "name": "1001: 发现栏小程序主入口",
51 | "pathName": "pages/dishes/dishes",
52 | "query": "",
53 | "scene": "1001"
54 | },
55 | {
56 | "id": -1,
57 | "name": "people-number",
58 | "pathName": "pages/people-number/people-number",
59 | "query": "",
60 | "scene": "1001"
61 | },
62 | {
63 | "id": -1,
64 | "name": "pre-order",
65 | "pathName": "pages/pre-order/pre-order",
66 | "query": "",
67 | "scene": "1001"
68 | },
69 | {
70 | "id": -1,
71 | "name": "order-success",
72 | "pathName": "pages/order-success/order-success",
73 | "query": "",
74 | "scene": "1001"
75 | },
76 | {
77 | "id": -1,
78 | "name": "history-orders",
79 | "pathName": "pages/history-orders/history-orders",
80 | "query": "scene=storeId%3d1%26deskNo%3d01",
81 | "scene": "1047"
82 | },
83 | {
84 | "id": -1,
85 | "name": "order-detail",
86 | "pathName": "pages/order-detail/order-detail",
87 | "query": "scene=storeId%3d1%26deskNo%3d01",
88 | "scene": "1047"
89 | },
90 | {
91 | "id": -1,
92 | "name": "dishes",
93 | "pathName": "pages/dishes/dishes",
94 | "query": "scene=storeId%3d1%26deskNo%3d01",
95 | "scene": "1047"
96 | },
97 | {
98 | "id": -1,
99 | "name": "dish-detail",
100 | "pathName": "pages/dish-detail/dish-detail",
101 | "query": "scene=storeId%3d1%26deskNo%3d01",
102 | "scene": "1011"
103 | },
104 | {
105 | "id": -1,
106 | "name": "index",
107 | "pathName": "pages/index/index",
108 | "query": "",
109 | "scene": null
110 | }
111 | ]
112 | }
113 | }
114 | }
--------------------------------------------------------------------------------
/pages/pre-order/pre-order.wxss:
--------------------------------------------------------------------------------
1 | .pre-order-container{
2 | height: 100vh;
3 | display: flex;
4 | flex-direction: column;
5 | font-weight: normal;
6 | background-color: rgba(246,246,246,1);
7 | }
8 |
9 | .title {
10 | position: relative;
11 | padding: 30rpx 0;
12 | color: #333333;
13 | font-size: 28rpx;
14 | text-align: center;
15 | background: #fff;
16 | border-bottom: 1rpx solid #DFE2E5;
17 | }
18 |
19 | .desk-number {
20 | position: absolute;
21 | top: 37rpx;
22 | right: 30rpx;
23 | color: #A1A1A1;
24 | font-size: 24rpx;
25 | background: #fff;
26 | text-align: right;
27 | }
28 |
29 | .dish-list{
30 | overflow: auto;
31 | -webkit-overflow-scrolling: touch;
32 | flex-grow: 1;
33 | background: #fff;
34 | }
35 |
36 | .dish-item{
37 | display: flex;
38 | justify-content: space-between;
39 | align-items: center;
40 | padding: 25rpx 30rpx;
41 | }
42 |
43 | .dish-msg {
44 | flex-grow: 1;
45 | font-size: 28rpx;
46 | }
47 |
48 | .dish-name{
49 | color: #333;
50 | }
51 |
52 | .dish-unit{
53 | color: #A1A1A1;
54 | }
55 |
56 | .dish-money {
57 | flex-basis: 150rpx;
58 | flex-shrink: 0;
59 | font-size: 26rpx;
60 | color: #333;
61 | text-align: right;
62 | }
63 |
64 | .money-prefix {
65 | color: #A1A1A1;
66 | }
67 |
68 | .dish-total{
69 | margin-top: 30rpx;
70 | margin-bottom: 30rpx;
71 | padding-right: 30rpx;
72 | font-size: 28rpx;
73 | color: #ED3E39;
74 | text-align: right;
75 | }
76 | .people-number{
77 | padding: 30rpx;
78 | background: #fff;
79 | border-bottom: 1rpx solid #DFE2E5;
80 | font-size: 28rpx;
81 | color: #333;
82 | }
83 |
84 | .people-number .label{
85 | display: inline-block;
86 | width: 100rpx;
87 | color: #A1A1A1;
88 | }
89 |
90 | .people-number .down-arrow{
91 | width: 16rpx;
92 | height: 10rpx;
93 | float: right;
94 | margin-top: 20rpx;
95 | }
96 |
97 | .remark{
98 | display: flex;
99 | align-items: center;
100 | padding: 30rpx;
101 | margin-bottom: 50rpx;
102 | background: #fff;
103 | font-size: 28rpx;
104 | color: #A1A1A1;
105 | }
106 |
107 | .remark .label {
108 | flex-basis: 100rpx;
109 | flex-shrink: 0;
110 | }
111 |
112 | .remark input{
113 | flex-grow: 1;
114 | color: #333;
115 | }
116 |
117 | .nav-bar {
118 | display: flex;
119 | flex-basis: 104rpx;
120 | flex-shrink: 0;
121 | line-height: 104rpx;
122 | z-index: 999;
123 | }
124 |
125 | .nav-bar .msg{
126 | position: relative;
127 | flex-grow: 1;
128 | padding-left: 180rpx;
129 | font-size: 32rpx;
130 | color: #fff;
131 | background: #343C3F;
132 | }
133 |
134 | .nav-bar .btn{
135 | flex-basis: 250rpx;
136 | flex-shrink: 0;
137 | background: #ED3E39;
138 | font-size: 32rpx;
139 | color: #fff;
140 | text-align: center;
141 | }
142 |
143 | .money {
144 | color: #ED3E39;
145 | margin-right: 10rpx;
146 | }
147 |
148 |
149 |
--------------------------------------------------------------------------------
/utils/cart.js:
--------------------------------------------------------------------------------
1 | // 同步本地存储
2 | function syncStorage(dataSource, storeId) {
3 | wx.setStorage({
4 | key: storeId + '-cart',
5 | data: dataSource,
6 | })
7 | }
8 |
9 | module.exports = {
10 | getDataSource: function () {
11 | const storeId = wx.getStorageSync('storeId') || '';
12 | return wx.getStorageSync(storeId + '-cart') || [];
13 | },
14 |
15 | getById: function (id) {
16 | const storeId = wx.getStorageSync('storeId') || '';
17 | const dataSource = wx.getStorageSync(storeId + '-cart') || [];
18 | return dataSource.find(item => item.id === id);
19 | },
20 |
21 | update: function (data) {
22 | const storeId = wx.getStorageSync('storeId') || '';
23 | const dataSource = wx.getStorageSync(storeId + '-cart') || [];
24 | const cartDish = dataSource.find(item => item.id === data.id);
25 | if(cartDish) {
26 | Object.keys(data).forEach(key => {
27 | cartDish[key] = data[key];
28 | });
29 | syncStorage(dataSource, storeId);
30 | }
31 | },
32 |
33 | add: function (data) {
34 | const storeId = wx.getStorageSync('storeId') || '';
35 | const dataSource = wx.getStorageSync(storeId + '-cart') || [];
36 | const existData = dataSource.find(item => item.id === data.id);
37 |
38 | if (existData) {
39 | existData.count = existData.count + 1;
40 | } else {
41 | data.count = 1;
42 | dataSource.push(data);
43 | }
44 |
45 | syncStorage(dataSource, storeId);
46 | },
47 |
48 | remove: function (data) {
49 | const storeId = wx.getStorageSync('storeId') || '';
50 | let dataSource = wx.getStorageSync(storeId + '-cart') || [];
51 | const existData = dataSource.find(item => item.id === data.id);
52 |
53 | if (existData && existData.count > 1) {
54 | existData.count = existData.count - 1;
55 | } else {
56 | dataSource = dataSource.filter(item => item.id !== data.id);
57 | }
58 |
59 | syncStorage(dataSource, storeId);
60 | },
61 |
62 | clear: function () {
63 | console.log('clear cart');
64 | const storeId = wx.getStorageSync('storeId') || '';
65 | let dataSource = [];
66 |
67 | syncStorage(dataSource, storeId);
68 | },
69 |
70 | getTotalCount: function () {
71 | const storeId = wx.getStorageSync('storeId') || '';
72 | const dataSource = wx.getStorageSync(storeId + '-cart') || [];
73 | let count = 0;
74 |
75 | dataSource.forEach(item => {
76 | count = count + item.count;
77 | });
78 |
79 | return count;
80 | },
81 |
82 | getTotalPrice: function () {
83 | const storeId = wx.getStorageSync('storeId') || '';
84 | const dataSource = wx.getStorageSync(storeId + '-cart') || [];
85 | let price = 0;
86 |
87 | dataSource.forEach(item => {
88 | price = price + (item.price * 100 * item.count) / 100;
89 | });
90 |
91 | return price;
92 | },
93 | };
94 |
--------------------------------------------------------------------------------
/pages/order-detail/order-detail.js:
--------------------------------------------------------------------------------
1 | const request = require('../../utils/request');
2 | const {formatCurrency} = require('../../utils/util');
3 |
4 | Page({
5 |
6 | /**
7 | * 页面的初始数据
8 | */
9 | data: {
10 | order: {},
11 | },
12 |
13 | /**
14 | * 生命周期函数--监听页面加载
15 | */
16 | onLoad: function (options) {
17 | const {id} = options;
18 | const statusList = {
19 | '00': '已完成',
20 | '01': '支付失败',
21 | '02': '订单初始化',
22 | '03': '待支付',
23 | };
24 | request.getHistoryOrderDetail({id})
25 | .then(res => {
26 | let order = {};
27 | if (res.data.code === '0000') {
28 | const d = res.data.data;
29 | const {date, time} = d;
30 | let orderTime = '';
31 | if (date && time) {
32 | const year = date.substr(0, 4);
33 | const month = date.substr(4, 2);
34 | const day = date.substr(6, 2);
35 | const hour = time.substr(0, 2);
36 | const minute = time.substr(2, 2);
37 | const second = time.substr(4, 2);
38 | orderTime = `${year}-${month}-${day} ${hour}:${minute}:${second}`;
39 | }
40 | order = {
41 | ...d,
42 | storeName: d.store.name,
43 | storeMobile: d.store.phone,
44 | storePosition: {
45 | latitude: d.store.latitude,
46 | longitude: d.store.longitude,
47 | },
48 | storeAddress: d.store.address,
49 | totalPrice: d.price,
50 | totalPriceStr: formatCurrency(d.price, {prefix: ''}),
51 | dishes: d.list.map(item => ({id: item.id, name: item.dishName, unit: item.unit, count: item.count, price: item.price, priceStr: formatCurrency(item.price, {prefix: ''})})),
52 | status: statusList[d.status] || '未知',
53 | orderNo: d.id,
54 | deskNo: d.deskNo,
55 | orderTime,
56 | };
57 | }
58 | this.setData({order});
59 | });
60 | },
61 |
62 | handleContactClick: function () {
63 | const {storeMobile} = this.data.order;
64 | wx.makePhoneCall({
65 | phoneNumber: storeMobile,
66 | success: () => {
67 |
68 | },
69 | fail: () => {
70 | },
71 |
72 | });
73 | },
74 |
75 | handleMapClick: function () {
76 | const {latitude, longitude} = this.data.order.storePosition;
77 | const {storeName, storeAddress} = this.data.order;
78 | // 好像不用户授权
79 | wx.openLocation({
80 | latitude: parseFloat(latitude),
81 | longitude: parseFloat(longitude),
82 | name: storeName,
83 | address: storeAddress,
84 | success: () => {
85 |
86 | },
87 | fail: () => {
88 | },
89 | });
90 | },
91 | });
--------------------------------------------------------------------------------
/pages/dish-detail/dish-detail.wxss:
--------------------------------------------------------------------------------
1 | .detail-container {
2 | height: 100vh;
3 | display: flex;
4 | flex-direction: column;
5 | font-weight: normal;
6 | background-color: rgba(246,246,246,1);
7 | }
8 |
9 | .detail-block{
10 | flex-grow: 1;
11 | overflow: auto;
12 | -webkit-overflow-scrolling: touch;
13 | background-color: rgba(246,246,246,1);
14 | }
15 |
16 | .detail-block .picture {
17 | font-size: 0;
18 | }
19 | .detail-block .picture image {
20 | width: 100%;
21 | }
22 |
23 | .detail-block .title{
24 | height: 108rpx;
25 | line-height: 108rpx;
26 | padding: 0 25rpx;
27 | border-bottom: 1rpx solid #D3D3D3;
28 | color: #333;
29 | font-size: 32rpx;
30 | background: #fff;
31 | }
32 |
33 | .detail-block .tag.suit {
34 | padding-top: 10rpx;
35 | }
36 | .detail-block .tag {
37 | padding: 40rpx 25rpx;
38 | background: #fff;
39 | }
40 |
41 | .detail-block .tag view{
42 | display: inline-block;
43 | padding: 10rpx 40rpx;
44 | margin-right: 20rpx;
45 | margin-bottom: 25rpx;
46 | border: 1rpx solid #D3D3D3;
47 | border-radius: 10rpx;
48 | font-size: 24rpx;
49 | color: #333;
50 | }
51 |
52 | .detail-block .tag view.active {
53 | background: #F3E0B6;
54 | border-color: #D1A646;
55 | color: #B88D2D;
56 | }
57 |
58 | .description {
59 | margin-top: 10rpx;
60 | margin-bottom: 50rpx;
61 | padding: 26rpx 33rpx;
62 | background: #fff;
63 | }
64 |
65 | .description-title {
66 | font-size: 28rpx;
67 | color: #333;
68 | }
69 |
70 | .description-title.no-des {
71 | color: #A1A1A1;
72 | }
73 |
74 | .description .content {
75 | margin-top: 20rpx;
76 | font-size: 24rpx;
77 | color: #666;
78 | }
79 |
80 | .nav-bar {
81 | display: flex;
82 | flex-basis: 104rpx;
83 | flex-shrink: 0;
84 | background: yellow;
85 | line-height: 104rpx;
86 | }
87 |
88 | .nav-bar .msg{
89 | flex-grow: 1;
90 | font-size: 32rpx;
91 | color: #fff;
92 | background: #343C3F;
93 | padding-left: 50rpx;
94 | }
95 |
96 | .nav-bar .msg .button {
97 | position: relative;
98 | display: inline-block;
99 | width: 200rpx;
100 | height: 65rpx;
101 | line-height: 65rpx;
102 | margin: 0 30rpx;
103 | text-align: center;
104 | float: right;
105 | }
106 | .nav-bar .msg .button .minus,
107 | .nav-bar .msg .button .plus {
108 | width: 60rpx;
109 | height: 100%;
110 | }
111 |
112 | .nav-bar .msg .button .minus {
113 | position: absolute;
114 | top: 20rpx;
115 | left: 0;
116 | line-height: 50rpx;
117 | border: 1rpx solid #B0AFAF;
118 | border-right: 0;
119 | border-top-left-radius: 50rpx;
120 | border-bottom-left-radius: 50rpx;
121 | }
122 |
123 | .nav-bar .msg .button .plus {
124 | position: absolute;
125 | top: 20rpx;
126 | left: 150rpx;
127 | border: 1rpx solid #B0AFAF;
128 | border-left: 0;
129 | border-top-right-radius: 50rpx;
130 | border-bottom-right-radius: 50rpx;
131 | }
132 |
133 | .nav-bar .msg .button .minus image {
134 | width: 26rpx;
135 | height: 6rpx;
136 | }
137 |
138 | .nav-bar .msg .button .plus image {
139 | width: 26rpx;
140 | height: 26rpx;
141 | }
142 |
143 | .nav-bar .msg .count {
144 | position:absolute;
145 | top: 20rpx;
146 | left: 60rpx;
147 | display: inline-block;
148 | width: 90rpx;
149 | height: 100%;
150 | border: 1rpx solid #B0AFAF;
151 | text-align: center;
152 | font-size: 28rpx;
153 | color: #B0AFAF;
154 | }
155 |
156 | .nav-bar .msg .price {
157 | font-size: 32rpx;
158 | color: #666;
159 | }
160 |
161 | .nav-bar .msg .money {
162 | margin-right: 10rpx;
163 | color: #ED3E39;
164 | }
165 |
166 | .nav-bar .btn{
167 | flex-basis: 250rpx;
168 | flex-shrink: 0;
169 | background: #ED3E39;
170 | font-size: 32rpx;
171 | color: #fff;
172 | text-align: center;
173 | }
174 |
--------------------------------------------------------------------------------
/utils/request.js:
--------------------------------------------------------------------------------
1 | const base_url = 'https://ordering.httpshop.com/wxapi';
2 |
3 | // const base_url = 'http://172.16.42.96:8080';
4 |
5 | function request(options) {
6 | const app = getApp();
7 | const {
8 | url,
9 | header = {},
10 | data,
11 | loading = {title: '加载中', mask: true},
12 | ...others
13 | } = options;
14 | if (!header['SXF-TOKEN']) header['SXF-TOKEN'] = app.globalData.token || '';
15 |
16 | let params = {};
17 | if (data && !Array.isArray(data)) {
18 | Object.keys(data).forEach(key => {
19 | const value = data[key];
20 | if (value !== void 0) {
21 | params[key] = value;
22 | }
23 | });
24 | } else {
25 | params = data;
26 | }
27 |
28 | const opts = {
29 | url: base_url + url,
30 | header,
31 | data: params,
32 | ...others
33 | };
34 |
35 | return new Promise((resolve, reject) => {
36 | wx.showLoading(loading);
37 |
38 | wx.request({
39 | ...opts,
40 | success: (res) => {
41 | if (res.statusCode !== 200) {
42 | reject(res);
43 | } else {
44 | resolve(res);
45 | }
46 | },
47 | fail: (error) => {
48 | reject(error);
49 | },
50 | complete: () => {
51 | wx.hideLoading();
52 | },
53 | });
54 | });
55 |
56 | }
57 |
58 | module.exports = {
59 | login: (options) => {
60 | const url = '/weChat/login';
61 | const {code, deskNo, storeId} = options;
62 | const data = {code, deskNo, storeId};
63 |
64 | return request({
65 | url,
66 | method: 'POST',
67 | data,
68 | });
69 | },
70 | getDishes: () => {
71 | const url = '/weChat/homePage/query';
72 | return request({
73 | url,
74 | }).catch(() => {
75 | wx.showToast({
76 | title: '获取菜品失败',
77 | icon: 'none',
78 | });
79 | });
80 | },
81 | getHistoryOrders: (params) => {
82 | const url = '/weChat/queryOrderByCustomer';
83 | const data = {
84 | beginDate: params.beginDate,
85 | endDate: params.endDate,
86 | status: params.status,
87 | };
88 |
89 | return request({
90 | method: 'POST',
91 | data,
92 | url,
93 | }).catch(() => {
94 | wx.showToast({
95 | title: '获取订单失败',
96 | icon: 'none',
97 | });
98 | });
99 | },
100 | getHistoryOrderDetail: (params) => {
101 | const url = '/weChat/queryOrderDetails';
102 | const data = {orderId: params.id};
103 |
104 | return request({
105 | data,
106 | url,
107 | }).catch(() => {
108 | wx.showToast({
109 | title: '获取订单详情失败',
110 | icon: 'none',
111 | });
112 | });
113 | },
114 | submitOrder: (params) => {
115 | const url = '/weChat/pay';
116 | const data = params;
117 | return request({
118 | method: 'POST',
119 | data,
120 | url,
121 | }).catch(() => {
122 | wx.showToast({
123 | title: '下单失败',
124 | icon: 'none',
125 | });
126 | });
127 | },
128 | getStore: () => {
129 | const url = '/weChat/queryStoreById';
130 | const data = {};
131 | return request({
132 | method: 'GET',
133 | data,
134 | url,
135 | }).catch(() => {
136 | wx.showToast({
137 | title: '获取店铺信息失败',
138 | icon: 'none',
139 | });
140 | });
141 | },
142 | getOrderStatus: (options) => request(options),
143 | };
144 |
--------------------------------------------------------------------------------
/pages/pre-order/pre-order.js:
--------------------------------------------------------------------------------
1 | const cart = require('../../utils/cart');
2 | const request = require('../../utils/request');
3 | const {formatCurrency} = require('../../utils/util');
4 |
5 | Page({
6 |
7 | /**
8 | * 页面的初始数据
9 | */
10 | data: {
11 | deskNo: '',
12 | dishes: [],
13 | dishTotalCount: 0,
14 | dishTotalPrice: 0,
15 | dishTotalPriceStr: '¥0.00',
16 | peopleNumber: 0,
17 | peopleNumbers: [],
18 | remark: '',
19 | },
20 |
21 | /**
22 | * 生命周期函数--监听页面加载
23 | */
24 | onLoad: function (options) {
25 |
26 | const dishTotalPrice = cart.getTotalPrice();
27 | const dishes = cart.getDataSource();
28 | dishes.forEach(dish => {
29 | dish.priceStr = formatCurrency(dish.price, {prefix: ''});
30 | });
31 |
32 | const peopleNumbers = [];
33 | for (let i = 1; i <= 50; i++) {
34 | peopleNumbers.push(i + ' 人');
35 | }
36 |
37 |
38 | this.setData({
39 | deskNo: wx.getStorageSync('deskNo'),
40 | dishes: cart.getDataSource(),
41 | dishTotalCount: cart.getTotalCount(),
42 | dishTotalPrice,
43 | dishTotalPriceStr: formatCurrency(dishTotalPrice),
44 | peopleNumber: wx.getStorageSync('peopleNumber'),
45 | peopleNumbers,
46 | });
47 | },
48 |
49 | handleInputRemark: function (e) {
50 | const {value} = e.detail;
51 | this.setData({remark: value});
52 | },
53 | handlePeopleNumberChange: function (e) {
54 | const peopleNumber = parseInt(e.detail.value) + 1;
55 | this.setData({peopleNumber});
56 | },
57 | handleSubmit: function () {
58 | const {remark} = this.data;
59 | const dataSource = cart.getDataSource();
60 | const params = dataSource.map(item => {
61 | const {specification} = item;
62 | const remark = [];
63 | if (specification && specification.length) {
64 | specification.forEach(it => {
65 | if (it.selected) {
66 | remark.push(it.label);
67 | }
68 | })
69 | }
70 | let type = item.type;
71 | if (!type || type === '03') type = '01';
72 | return {
73 | type,
74 | dishId: item.id,
75 | count: item.count,
76 | remark: remark.join(',')
77 | };
78 |
79 | });
80 |
81 | request.submitOrder(params)
82 | .then(res => {
83 | console.log('submitOrder', res);
84 | /*
85 | appid: "wx7c89ca859d0345fd"
86 | nonceStr: "d039684ee129447dbfb37e620ae1fbdc"
87 | package: "prepay_id=test2018091501111"
88 | paySign: "0316B4C25449ADEBEACA612EE4CDCD9C"
89 | timeStamp: "1554186699"
90 |
91 | timeStamp string 是 时间戳,从 1970 年 1 月 1 日 00:00:00 至今的秒数,即当前的时间
92 | nonceStr string 是 随机字符串,长度为32个字符以下
93 | package string 是 统一下单接口返回的 prepay_id 参数值,提交格式如:prepay_id=***
94 | signType string MD5 否 签名算法
95 | paySign string 是 签名,具体签名方案参见 小程序支付接口文档
96 | success function 否 接口调用成功的回调函数
97 | fail function 否 接口调用失败的回调函数
98 | complete function 否 接口调用结束的回调函数(调用成功、失败都会执行)
99 | */
100 | const {appid, nonceStr, paySign, timeStamp} = res.data.data;
101 | wx.requestPayment({
102 | timeStamp,
103 | nonceStr,
104 | package: res.data.data.package, // package是关键字
105 | paySign,
106 | signType: 'MD5',
107 | success: () => {
108 | console.log('success');
109 | // 支付成功之后跳转
110 | wx.navigateTo({
111 | url: '/pages/order-success/order-success',
112 | })
113 | },
114 | fail: () => {
115 | console.log('fail');
116 | },
117 | complete: () => {
118 | console.log('complete');
119 | },
120 | });
121 | });
122 | },
123 | })
124 |
--------------------------------------------------------------------------------
/pages/index/index.js:
--------------------------------------------------------------------------------
1 | //index.js
2 | //获取应用实例
3 | const app = getApp();
4 | const request = require('../../utils/request');
5 | const {parseQueryString} = require('../../utils/util');
6 |
7 | Page({
8 | data: {
9 | hasUncompletedOrder: false,
10 | store: {},
11 | storeId: '',
12 | deskNo: '', // 当前点餐桌号
13 | showScanTip: false, // 显示提示扫码
14 | showOrderButton: false, // 显示点餐按钮
15 | showAddPayButton: false, // 显示加菜 结账按钮
16 | userInfo: {},
17 | hasUserInfo: false,
18 | canIUse: wx.canIUse('button.open-type.getUserInfo'),
19 | },
20 |
21 | onLoad: function () {
22 | let isInit = false;
23 | app.initReadyCallBack = () => { // 初始化成功回调
24 | this.init();
25 | isInit = true;
26 | };
27 | if (!isInit) this.init();
28 | },
29 |
30 | init: function () {
31 | const hasUncompletedOrder = wx.getStorageSync('hasUncompletedOrder');
32 | const store = wx.getStorageSync('store');
33 | const storeId = wx.getStorageSync('storeId');
34 | const deskNo = wx.getStorageSync('deskNo'); // 当前点餐桌号
35 | const showScanTip = !storeId && !deskNo;
36 | const showAddPayButton = hasUncompletedOrder;
37 | const showOrderButton = storeId && deskNo && !showAddPayButton;
38 |
39 | this.setData({
40 | hasUncompletedOrder,
41 | store,
42 | storeId,
43 | deskNo,
44 | showScanTip,
45 | showAddPayButton,
46 | showOrderButton,
47 | });
48 |
49 | this.initUserInfo();
50 | },
51 |
52 | // 用户已经授权,获取用户信息
53 | initUserInfo: function () {
54 | if (app.globalData.userInfo) {
55 | this.setData({
56 | userInfo: app.globalData.userInfo,
57 | hasUserInfo: true
58 | })
59 | } else if (this.data.canIUse) {
60 | // 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回
61 | // 所以此处加入 callback 以防止这种情况
62 | app.userInfoReadyCallback = res => {
63 | this.setData({
64 | userInfo: res.userInfo,
65 | hasUserInfo: true
66 | })
67 | }
68 | } else {
69 | // 在没有 open-type=getUserInfo 版本的兼容处理
70 | wx.getUserInfo({
71 | success: res => {
72 | app.globalData.userInfo = res.userInfo
73 | this.setData({
74 | userInfo: res.userInfo,
75 | hasUserInfo: true
76 | })
77 | }
78 | })
79 | }
80 | },
81 |
82 | // 点击头像,如果未授权,弹出授权询问框
83 | getUserInfo: function (e) {
84 | if (e.detail.userInfo) {
85 | app.globalData.userInfo = e.detail.userInfo;
86 | this.setData({
87 | userInfo: e.detail.userInfo,
88 | hasUserInfo: true
89 | })
90 | } else {
91 | // 用户点击了拒绝
92 | }
93 | this.toHistoryOrders();
94 | },
95 |
96 | // 两个点餐按钮点击事件
97 | handleOrderClick: function (e) {
98 | // 多人、单人 multiple single
99 | const {type} = e.currentTarget.dataset;
100 |
101 | wx.setStorageSync('orderType', type);
102 |
103 | wx.navigateTo({
104 | url: '/pages/people-number/people-number'
105 | });
106 | },
107 |
108 | // 扫描二维码点餐
109 | handleScanQRCodeClick: function (e) {
110 | wx.scanCode({
111 | scanType: ['qrCode'],
112 | success: (data) => {
113 | console.log('success');
114 | console.log(data);
115 |
116 | // TODO 获取扫描结果中的storeId deskNo
117 | const {path} = data;
118 | const params = parseQueryString(decodeURIComponent(path.split('?')[1]));
119 | const {storeId, deskNo} = params;
120 | console.log(params);
121 |
122 | wx.setStorageSync('storeId', storeId);
123 | wx.setStorageSync('deskNo', deskNo);
124 | wx.setStorageSync('innerScan', true);
125 |
126 | app.init();
127 |
128 | this.setData({
129 | showScanTip: false,
130 | showAddPayButton: false,
131 | showOrderButton: true,
132 | });
133 | }
134 | });
135 | },
136 | // 加菜按钮点击事件
137 | handleAddDishClick: function (e) {
138 | // TODO
139 | },
140 |
141 | // 结账按钮点击事件
142 | handlePayClick: function (e) {
143 | // TODO
144 | },
145 |
146 | // 跳转到用户历史订单
147 | toHistoryOrders: function () {
148 | wx.navigateTo({
149 | url: '/pages/history-orders/history-orders'
150 | })
151 | },
152 | });
153 |
--------------------------------------------------------------------------------
/app.js:
--------------------------------------------------------------------------------
1 | //app.js
2 | const {parseQueryString} = require('./utils/util');
3 | const request = require('./utils/request');
4 |
5 | // console.log(App);
6 | App({
7 | // onLaunch 小程序启动的时候触发,再次打开并不触发
8 | // fixme onShow 页面再次显示触发,调用扫码、地图等,再次切换回来也触发
9 | // onLaunch: function () {
10 | // },
11 | onShow: function (options) {
12 | this.getSettings();
13 | this.init(options);
14 | },
15 |
16 | onHide: function () {
17 | // wx.showToast({title: 'app.js onHide'});
18 |
19 | wx.setStorageSync('innerScan', false);
20 | },
21 |
22 | init: function (options = {}) {
23 | console.log(options);
24 | // 要启动的页面path
25 | const {scene: sceneType, path} = options;
26 |
27 | const innerScan = wx.getStorageSync('innerScan');
28 |
29 | // TODO 进入方式判断
30 | // const scanIn = '' + sceneType === '1011' || innerScan;
31 | //
32 | // // 非扫码方式进入, 统一跳到首页,提示扫码
33 | // if (!scanIn) {
34 | // wx.removeStorageSync('storeId');
35 | // wx.removeStorageSync('deskNo');
36 | // this.toIndex(path);
37 | // if (this.initReadyCallBack) {
38 | // this.initReadyCallBack();
39 | // }
40 | // return;
41 | // }
42 |
43 | // 二维码中携带的参数
44 | const query = options && options.query || {};
45 |
46 |
47 | // 参数存在 进入初始化流程
48 | let storeId = query.storeId;
49 | let deskNo = query.deskNo;
50 |
51 | // 存在参数,优先从参数中获取storeId deskNo
52 | if (storeId && deskNo) {
53 | // 缓存storeId deskNo
54 | wx.setStorageSync('storeId', storeId);
55 | wx.setStorageSync('deskNo', deskNo);
56 |
57 | // 记录获取到storeId deskNo时间
58 | wx.setStorageSync('scanTime-' + storeId + deskNo, Date.now());
59 | } else {
60 | // 参数不存在(非扫码进入),从存储中获取storeId deskNo
61 | storeId = wx.getStorageSync('storeId');
62 | deskNo = wx.getStorageSync('deskNo');
63 |
64 | // 记录获取到storeId deskNo时间
65 | wx.setStorageSync('scanTime-' + storeId + deskNo, Date.now());
66 |
67 | if (storeId && deskNo && this.isExpired()) {
68 | storeId = null;
69 | deskNo = null;
70 | wx.setStorageSync('storeId', storeId);
71 | wx.setStorageSync('deskNo', deskNo);
72 | }
73 | }
74 |
75 | // 如果storeId deskNo不存在,部分页面要跳转到提示扫码页面
76 | if (!storeId && !deskNo) {
77 | this.toIndex(path);
78 | } else {
79 | // 登录
80 | wx.login({
81 | success: res => {
82 |
83 | request.login({code: res.code, storeId, deskNo})
84 | .then(res => {
85 | if (res && res.data && res.data.data && res.data.data.token) {
86 | const token = res.data.data.token;
87 | // 登录成功之后,设置一些登录信息,比如token等
88 | this.globalData.token = token;
89 |
90 | // 登录成功之后,进行初始化
91 | this.initStoreMessage();
92 | }
93 | })
94 | .catch(err => {
95 | console.log(err);
96 | wx.showToast({
97 | title: '登录失败',
98 | icon: 'none',
99 | });
100 | });
101 | }
102 | });
103 |
104 | }
105 | },
106 |
107 | // 初始化点餐相关数据
108 | initStoreMessage: function (callBack) {
109 | const storeId = wx.getStorageSync('storeId');
110 | const deskNo = wx.getStorageSync('deskNo');
111 |
112 | request.getStore().then(res => {
113 | const {name} = res.data.data;
114 | const store = {
115 | logo: '/images/logo.png',
116 | name,
117 | };
118 | wx.setStorageSync('store', store);
119 |
120 | const checkOutType = 'before'; // before 先结账 after 后结账
121 | wx.setStorageSync('checkOutType', checkOutType);
122 |
123 | // 后付款用户,会存在未完成订单,需要跳转到首页,提示加菜、结账等
124 | let hasUncompletedOrder = false;
125 | wx.setStorageSync('hasUncompletedOrder', hasUncompletedOrder);
126 | if (hasUncompletedOrder) {
127 | // TODO 根据未完成订单恢复购物车数据、点餐人数,然后跳转到首页,提示加菜、结账
128 | }
129 |
130 | if (this.initReadyCallBack) {
131 | this.initReadyCallBack();
132 | }
133 |
134 | });
135 | },
136 |
137 | // 是否过期,提示重新扫码
138 | isExpired: function () {
139 | const storeId = wx.getStorageSync('storeId');
140 | const deskNo = wx.getStorageSync('deskNo');
141 | const lastScanTime = wx.getStorageSync('scanTime-' + storeId + deskNo);
142 | const expireTime = 1000 * 60 * 60 * 5; // fixme 5 小时后过期
143 | return Date.now() - lastScanTime > expireTime;
144 | },
145 |
146 | toIndex: function (currentPath) {
147 | // 不需要跳转的页面
148 | const ignorePaths = [
149 | 'pages/index/index',
150 | 'pages/history-orders/history-orders',
151 | 'pages/order-detail/order-detail',
152 | ];
153 |
154 | if (ignorePaths.indexOf(currentPath) === -1) {
155 | wx.reLaunch({
156 | url: '/pages/index/index'
157 | })
158 | }
159 | },
160 |
161 | // 启动时,获取一些设置信息
162 | getSettings: function () {
163 | // 获取用户信息
164 | wx.getSetting({
165 | success: res => {
166 | if (res.authSetting['scope.userInfo']) {
167 | // 已经授权,可以直接调用 getUserInfo 获取头像昵称,不会弹框
168 | wx.getUserInfo({
169 | success: res => {
170 | // 可以将 res 发送给后台解码出 unionId
171 | this.globalData.userInfo = res.userInfo
172 |
173 | // 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回
174 | // 所以此处加入 callback 以防止这种情况
175 | if (this.userInfoReadyCallback) {
176 | this.userInfoReadyCallback(res)
177 | }
178 | }
179 | })
180 | }
181 | }
182 | })
183 | },
184 | globalData: {
185 | userInfo: null,
186 | }
187 | });
188 |
--------------------------------------------------------------------------------
/pages/dishes/dishes.wxss:
--------------------------------------------------------------------------------
1 | /* pages/dishes.wxss */
2 | .dish-container {
3 | height: 100vh;
4 | display: flex;
5 | flex-direction: column;
6 | font-weight: normal;
7 | background-color: rgba(246,246,246,1);
8 | }
9 |
10 | .top {
11 | background-color: #fff;
12 | flex-basis: 253rpx;
13 | flex-shrink: 0;
14 | }
15 |
16 | .banner {
17 | display: flex;
18 | height: 200rpx;
19 | padding-left: 10rpx;
20 | overflow: auto;
21 | -webkit-overflow-scrolling: touch;
22 | }
23 |
24 | .banner-item {
25 | position: relative;
26 | height: 178rpx;
27 | padding-right: 10rpx;
28 | }
29 |
30 | .banner-item image {
31 | width: 320rpx;
32 | height: 178rpx;
33 | }
34 |
35 | .banner-item-tip {
36 | position: absolute;
37 | bottom: 0;
38 | left: 0;
39 | right: 10rpx;
40 | height: 46rpx;
41 | line-height: 46rpx;
42 | background-color: rgba(0, 0, 0, 0.7);
43 | text-align: center;
44 | color: #fff;
45 | font-size: 24rpx;
46 | }
47 |
48 | .dish-block{
49 | display: flex;
50 | flex-grow: 1;
51 | margin-top: 13rpx;
52 | background-color: #fff;
53 | }
54 |
55 | .dish-nav {
56 | flex-basis: 170rpx;
57 | flex-shrink: 0;
58 | height: 100%;
59 | background-color: rgba(246,246,246,1);
60 | }
61 |
62 | .dish-nav view {
63 | display: flex;
64 | justify-content: center;
65 | align-items: center;
66 | height: 100rpx;
67 | padding: 0 10rpx;
68 | border-bottom: 1rpx solid #D3D3D3;
69 | font-size: 28rpx;
70 | color: #939597;
71 | }
72 |
73 | .dish-nav view.active{
74 | background-color: #fff;
75 | color: #333;
76 | }
77 |
78 | .dish-list {
79 | overflow: auto;
80 | /*-webkit-overflow-scrolling: touch;*/
81 | flex-grow: 1;
82 | height: 500rpx;
83 | }
84 |
85 | .dish-list .nav-title-bar{
86 | margin-top: 10rpx;
87 | padding: 0;
88 | }
89 |
90 | .cart-icon image{
91 | width: 46rpx;
92 | height: 42rpx;
93 | }
94 |
95 | .badge {
96 | width: 38rpx;
97 | height: 38rpx;
98 | line-height: 38rpx;
99 | border-radius: 50%;
100 | background: red;
101 | text-align: center;
102 | color: #fff;
103 | font-size: 28rpx;
104 | }
105 |
106 | .dish-list-item {
107 | display: flex;
108 | padding: 22rpx;
109 | padding-bottom: 0;
110 | }
111 |
112 | .dish-image {
113 | flex-basis: 200rpx;
114 | flex-shrink: 0;
115 | margin-top: 8rpx;
116 | height: 133rpx;
117 | }
118 |
119 | .dish-msg-block {
120 | flex-grow: 1;
121 | margin-left: 15rpx;
122 | padding-right: 15rpx;
123 | border-bottom: 1rpx solid #D3D3D3;
124 | }
125 |
126 | .dish-msg-block .title {
127 | color: #000;
128 | font-size: 28rpx;
129 | }
130 |
131 | .dish-msg-block .price{
132 | margin-top: 10rpx;
133 | font-size: 24rpx;
134 | }
135 |
136 | .money {
137 | color: #ED3E39;
138 | }
139 |
140 | .dish-msg-block .price .unit {
141 | color: #A1A1A1;
142 | }
143 |
144 | .dish-operator-button {
145 | display: flex;
146 | justify-content: flex-end;
147 | align-items: center;
148 | }
149 |
150 | .dish-operator-button .minus,
151 | .dish-operator-button .plus {
152 | padding: 20rpx; /*扩大点击范围*/
153 | width: 38rpx;
154 | height: 38rpx;
155 | }
156 |
157 | .dish-operator-button .count {
158 | display: inline-block;
159 | width: 50rpx;
160 | text-align: center;
161 | height: 38rpx;
162 | line-height: 38rpx;
163 | font-size: 24rpx;
164 | color: #666;
165 | }
166 |
167 | .nav-bar {
168 | display: flex;
169 | flex-basis: 104rpx;
170 | flex-shrink: 0;
171 | line-height: 104rpx;
172 | z-index: 999;
173 | }
174 |
175 | .nav-bar .msg{
176 | position: relative;
177 | flex-grow: 1;
178 | padding-left: 180rpx;
179 | font-size: 32rpx;
180 | color: #fff;
181 | background: #343C3F;
182 | }
183 |
184 | .nav-bar .btn{
185 | flex-basis: 250rpx;
186 | flex-shrink: 0;
187 | background: #ED3E39;
188 | font-size: 32rpx;
189 | color: #fff;
190 | text-align: center;
191 | }
192 |
193 | .nav-bar .btn.disabled {
194 | background: #999;
195 | }
196 |
197 | .nav-bar .cart-icon {
198 | position: absolute;
199 | left: 50rpx;
200 | top: -30rpx;
201 | width: 105rpx;
202 | height: 105rpx;
203 | text-align: center;
204 | border-radius: 50%;
205 | background: #2C3437;
206 | }
207 | .nav-bar .cart-icon .badge {
208 | position: absolute;
209 | right: 0;
210 | top: -10rpx;
211 | font-size: 22rpx;
212 | }
213 | .nav-bar .cart-icon image {
214 | margin-top: 35rpx;
215 | width: 46rpx;
216 | height: 42rpx;
217 | }
218 |
219 | .cart{
220 | position: absolute;
221 | top: 0;
222 | right: 0;
223 | bottom: 0;
224 | left: 0;
225 | z-index: 888;
226 | background: rgba(0, 0, 0, 0.6);
227 | }
228 |
229 | .cart-bg {
230 | position: absolute;
231 | top: 0;
232 | right: 0;
233 | bottom: 50%;
234 | left: 0;
235 | }
236 |
237 | .cart-block {
238 | position: absolute;
239 | bottom: 0;
240 | height: 50%;
241 | width: 100%;
242 | }
243 | .cart .top-bar {
244 | height: 70rpx;
245 | line-height: 70rpx;
246 | padding: 0 25rpx;
247 | background: #343C3F;
248 | font-size: 24rpx;
249 | color: #fff;
250 | border-top-left-radius: 15rpx;
251 | border-top-right-radius: 15rpx;
252 | }
253 | .cart .clear {
254 | width: 30rpx;
255 | height: 30rpx;
256 | margin-top: 20rpx;
257 | margin-right: 20rpx;
258 | float: right;
259 | }
260 |
261 | .cart .cart-content {
262 | background: #efefef;
263 | height: 100%;
264 | padding: 0 20rpx;
265 | overflow: auto;
266 | -webkit-overflow-scrolling: touch;
267 | }
268 |
269 | .cart-item {
270 | display: flex;
271 | align-items: center;
272 | padding: 10rpx 20rpx;
273 | border-bottom: 1rpx solid #D3D3D3;
274 | text-align: center;
275 | font-size: 26rpx;
276 | color: #333;
277 | overflow: auto;
278 | }
279 |
280 | .cart-item .dish-title {
281 | display:inline-block;
282 | flex-grow: 1;
283 | text-align: left;
284 | }
285 |
286 | .cart-item .dish-price {
287 | display: inline-block;
288 | flex-basis: 100rpx;
289 | flex-shrink: 0;
290 | padding-right: 30rpx;
291 | text-align: right;
292 | color: #ED3E39;
293 | }
294 | .cart-item .dish-operator-button {
295 | flex-basis: 160rpx;
296 | flex-shrink: 0;
297 | display: inline-flex;
298 | }
299 |
300 | .animation-point-container {
301 | opacity: 0;
302 | position: absolute;
303 | top: -50rpx;
304 | width: 38rpx;
305 | height: 38rpx;
306 | z-index: 999;
307 | }
308 | .animation-point{
309 | position: absolute;
310 | left: 0;
311 | top: 0;
312 | width: 38rpx;
313 | height: 38rpx;
314 | border-radius: 50%;
315 | background: #ED3E39;
316 | z-index: 999;
317 | }
318 |
--------------------------------------------------------------------------------
/pages/dishes/dishes.wxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | {{item.name}}
8 |
9 |
10 |
11 |
12 |
18 |
26 | {{item.name}}
27 |
28 |
29 |
30 |
31 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | {{dish.name}}
48 |
49 |
50 | {{dish.priceStr}}
51 | /{{dish.unit}}
52 |
53 |
54 |
62 | {{dish.count}}
63 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 | {{cartTotalCount}}
81 |
82 |
83 |
84 | 共
85 | {{cartTotalPriceStr}}
86 |
87 | 购物车空空如也~
88 |
89 |
94 | 点好了
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 | 您已点好的菜品
103 |
109 |
110 |
111 |
112 | {{item.name}}
113 | {{item.totalPriceStr}}
114 |
115 |
122 | {{item.count}}
123 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
144 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
--------------------------------------------------------------------------------
/pages/dishes/dishes.js:
--------------------------------------------------------------------------------
1 | // pages/dishes.js
2 | const cart = require('../../utils/cart');
3 | const request = require('../../utils/request');
4 | const {formatCurrency} = require('../../utils/util');
5 |
6 | Page({
7 |
8 | /**
9 | * 页面的初始数据
10 | */
11 | data: {
12 | showCart: false,
13 | category: [],
14 | dishes: [],
15 | recommendDishes: [], // 推荐菜品列表
16 | activeCategoryId: '', // 菜品分类中,当前选中的分类
17 | scrollCategoryId: '', // 菜品列表中,当前滚动到的分类
18 | blockHeight: 500, // 菜品列表的高度,计算左侧选中分类时用到
19 | blockTop: 100, // 菜品列表距离头部距离,计算左侧选中分类时用到
20 | listScrollTop: 0, // 菜品列表滚动距离
21 |
22 | cartDataSource: [],
23 | cartTotalCount: 0,
24 | cartTotalPrice: 0,
25 | cartTotalPriceStr: '¥0',
26 |
27 | disableOkButton: true,
28 |
29 | // 动画相关
30 | dishListLeft: 0,
31 | dishListTop: 0,
32 | cartAnimation: {},
33 | pointAnimations: [],
34 | pointContainerAnimations: [],
35 | pointCount: 5, // 动画所需红点个数
36 | pointDuration: 500, // 红点动画持续时间
37 | cartBadgePosition: {},
38 | },
39 |
40 | // 同步购物车数据到当前的data中
41 | syncCart: function () {
42 | const cartDataSource = cart.getDataSource();
43 | const cartTotalCount = cart.getTotalCount();
44 |
45 | const dishes = this.data.dishes.map(item => {
46 | const cartDish = cartDataSource.find(it => it.id === item.id);
47 | if (cartDish) {
48 | return {
49 | ...item,
50 | count: cartDish.count,
51 | }
52 | }
53 | return {
54 | ...item,
55 | count: 0,
56 | };
57 | });
58 |
59 | // 计算单个菜品的总价
60 | cartDataSource.forEach(item => {
61 | item.totalPrice = item.price * item.count;
62 | item.totalPriceStr = formatCurrency(item.totalPrice);
63 | });
64 |
65 | const disableOkButton = !cartDataSource || !cartDataSource.length;
66 |
67 | //
68 | if (cartTotalCount === 0 && this.data.showCart) {
69 | this.setData({showCart: false});
70 | }
71 |
72 | this.setData({
73 | dishes,
74 | disableOkButton,
75 | cartDataSource: cartDataSource,
76 | cartTotalCount: cartTotalCount,
77 | cartTotalPrice: cart.getTotalPrice(),
78 | cartTotalPriceStr: formatCurrency(cart.getTotalPrice()),
79 | });
80 | },
81 |
82 | getDishes: function () {
83 | let activeCategoryId;
84 | let scrollCategoryId;
85 | const category = [];
86 | const dishes = [];
87 | const recommendDishes = [];
88 | let recommendName = '';
89 | let hasDishSuit = false;
90 | const dishSuitCategoryId = 'dishSuit';
91 |
92 | const imageUrl = 'https://ordering.httpshop.com';
93 |
94 | request.getDishes()
95 | .then(res => {
96 | if (res && res.data && res.data.data && res.data.data.length) {
97 | const data = res.data.data;
98 | data.forEach(item => {
99 | // 普通菜品
100 | if (item.type === '01') {
101 | const categoryId = item.id;
102 | (item.data || []).forEach(dish => {
103 | dishes.push({
104 | ...dish,
105 | type: item.type,
106 | id: dish.id,
107 | isSuit: false,
108 | name: dish.name,
109 | picture: imageUrl + dish.dishPictureUrl,
110 | categoryId,
111 | price: dish.dishPrice,
112 | priceStr: formatCurrency(dish.dishPrice),
113 | unit: dish.unit || '份',
114 | });
115 | });
116 |
117 | category.push({
118 | ...item,
119 | id: item.id,
120 | name: item.name,
121 | });
122 | }
123 |
124 | // 套餐
125 | if (item.type === '02') {
126 | hasDishSuit = true;
127 | dishes.push({
128 | ...item.data,
129 | ...item,
130 | type: item.type,
131 | isSuit: true,
132 | id: item.id,
133 | name: item.name,
134 | picture: imageUrl + item.data.dishsuitPictureUril, // Uril ?
135 | categoryId: dishSuitCategoryId,
136 | price: item.data.dishsuitPrice,
137 | priceStr: formatCurrency(item.data.dishsuitPrice),
138 | unit: item.data.unit || '份',
139 | });
140 | }
141 |
142 | // 推荐
143 | if (item.type === '03' && item.data && item.data.length) {
144 | recommendName = item.data[0].name;
145 | item.data[0].dishList.forEach(dish => {
146 | recommendDishes.push({
147 | ...dish,
148 | type: item.type,
149 | id: dish.id,
150 | isSuit: false,
151 | name: dish.name,
152 | picture: imageUrl + dish.dishPictureUrl,
153 | });
154 | })
155 | }
156 | });
157 |
158 | if (hasDishSuit) {
159 | // 套餐放入第一个
160 | category.unshift({
161 | id: dishSuitCategoryId,
162 | name: '套餐',
163 | });
164 | }
165 |
166 |
167 | if (category && category.length) {
168 | activeCategoryId = category[0].id;
169 | scrollCategoryId = category[0].id;
170 | }
171 | this.setData({
172 | activeCategoryId,
173 | scrollCategoryId,
174 | category,
175 | dishes,
176 | recommendName,
177 | recommendDishes,
178 | });
179 | this.syncCart();
180 | }
181 | console.log(res);
182 | })
183 | .catch(err => {
184 | console.log(err);
185 | });
186 | },
187 |
188 | onLoad: function (options) {
189 | this.getDishes();
190 | },
191 |
192 | onShow: function () {
193 | this.syncCart();
194 | },
195 |
196 | /**
197 | * 生命周期函数--监听页面初次渲染完成
198 | */
199 | onReady: function () {
200 | // scroll-view 需要设置高度
201 | const query = wx.createSelectorQuery();
202 | query.select('.dish-block').boundingClientRect();
203 | query.exec((res) => {
204 | this.setData({
205 | blockHeight: res[0].height,
206 | blockTop: res[0].top,
207 | });
208 | });
209 |
210 | // 获取 dish-list 的 let top 计算加号精确位置时用到
211 | const queryDishList = wx.createSelectorQuery();
212 | queryDishList.select('.dish-list').boundingClientRect();
213 | queryDishList.exec((res) => {
214 | this.setData({
215 | dishListLeft: res[0].left,
216 | dishListTop: res[0].top,
217 | });
218 | });
219 |
220 | // 获取 购物车 badge 位置
221 | const queryBadge = wx.createSelectorQuery();
222 | queryBadge.select('.badge').boundingClientRect();
223 | queryBadge.exec((res) => {
224 | this.setData({
225 | cartBadgePosition: {
226 | left: res[0].left,
227 | top: res[0].top,
228 | },
229 | });
230 | })
231 | },
232 |
233 | // 点击菜品分类
234 | handleCategoryClick: function (e) {
235 | const {id} = e.currentTarget.dataset;
236 | this.setData({activeCategoryId: id, scrollCategoryId: id});
237 | },
238 |
239 |
240 | // 点击菜品图片,跳转菜品详情
241 | toDishDetail: function (e) {
242 | const {id, suit = false} = e.currentTarget.dataset;
243 | const {
244 | dishes,
245 | recommendDishes,
246 | } = this.data;
247 | let dish = dishes.find(item => item.id === id);
248 | if (!dish) {
249 | dish = recommendDishes.find(item => item.id === id);
250 | }
251 | wx.setStorageSync('dish-for-detail', dish);
252 |
253 | wx.navigateTo({
254 | url: `/pages/dish-detail/dish-detail?id=${id}&isSuit=${suit}`
255 | })
256 | },
257 |
258 | // 菜品列表滚动事件,通过截流,提高性能
259 | scrollST: 0,
260 | handleDishScroll: function (e) {
261 | if (this.scrollST) {
262 | clearTimeout(this.scrollST);
263 | }
264 |
265 | this.scrollST = setTimeout(() => {
266 | const {scrollTop} = e.detail;
267 | this.setData({listScrollTop: scrollTop});
268 |
269 | const {blockHeight, blockTop} = this.data;
270 |
271 | const query = wx.createSelectorQuery();
272 | query.selectAll('.dish-category-title').boundingClientRect()
273 | query.exec((res) => {
274 | let hasTitleInShow = false;
275 | let activeCategoryId;
276 |
277 | // 分类title在可见范围之内
278 | for (let i = 0; i < res[0].length; i++) {
279 | const title = res[0][i];
280 | const id = title.id.replace('dc', '');
281 | const titleToBlockTop = title.top - blockTop;
282 | // title在60%上部分
283 | if (titleToBlockTop >= 0 && titleToBlockTop < (blockHeight * 0.6)) {
284 | activeCategoryId = id;
285 | hasTitleInShow = true;
286 | break;
287 | }
288 | }
289 |
290 | // // 分类title不在可见范围之内,判断当前内容是那个分类的
291 | if (!hasTitleInShow) {
292 | let nearestTitleTop;
293 |
294 | for (let i = 0; i < res[0].length; i++) {
295 | const title = res[0][i];
296 | const id = title.id.replace('dc', '');
297 | const titleToBlockTop = title.top - blockTop;
298 |
299 | if (nearestTitleTop === undefined) {
300 | nearestTitleTop = titleToBlockTop;
301 | activeCategoryId = id;
302 | }
303 |
304 | if (titleToBlockTop < 0 && titleToBlockTop > nearestTitleTop) {
305 | nearestTitleTop = titleToBlockTop;
306 | activeCategoryId = id;
307 | }
308 | }
309 | }
310 |
311 | this.setData({activeCategoryId});
312 | })
313 | }, 100);
314 | },
315 |
316 | // 切换购物车显示隐藏
317 | toggleCart: function () {
318 | if (this.data.showCart) {
319 | this.hideCart();
320 | } else {
321 | this.showCart();
322 | }
323 | },
324 |
325 | // 购物车图标点击 弹出购物车
326 | showCart: function () {
327 | if (this.data.cartDataSource && this.data.cartDataSource.length) {
328 | this.setData({showCart: true});
329 | }
330 | },
331 |
332 | // 隐藏购物车
333 | hideCart: function () {
334 | this.setData({showCart: false});
335 | },
336 |
337 | addCart: function (e) {
338 | const {id: dishId} = e.currentTarget.dataset;
339 | const {dishes, pointDuration} = this.data;
340 | const dish = dishes.find(item => item.id === dishId);
341 |
342 | cart.add(dish);
343 |
344 | this.syncCart();
345 |
346 | // 购物车动画 红点落入时启动
347 | setTimeout(() => {
348 | this.playCartAnimation();
349 | }, pointDuration);
350 |
351 | // 红点进入购物车动画
352 | this.playAddPointAnimation(e);
353 | },
354 |
355 | minusCart: function (e) {
356 | const dishId = e.currentTarget.dataset.id;
357 | const {dishes} = this.data;
358 | const dish = dishes.find(item => item.id === dishId);
359 |
360 | cart.remove(dish);
361 | this.syncCart();
362 | },
363 |
364 | clearCart: function () {
365 | cart.clear();
366 | this.syncCart();
367 | this.hideCart();
368 | },
369 |
370 | // 点好了按钮点击事件
371 | handleOk: function (e) {
372 | if (e.currentTarget.dataset.disabled) return;
373 |
374 | wx.navigateTo({
375 | url: '/pages/pre-order/pre-order',
376 | })
377 | },
378 |
379 | playCartAnimation: function () {
380 | const duration = 200;
381 | const animation = wx.createAnimation({
382 | duration,
383 | timingFunction: 'ease',
384 | });
385 |
386 | animation.scale(1.1, 1.1).step();
387 |
388 | this.setData({
389 | cartAnimation: animation.export()
390 | });
391 |
392 | // 官方:iOS/Android 6.3.30 通过 step() 分隔动画,只有第一步动画能生效
393 | setTimeout(() => {
394 | animation.scale(1, 1).step();
395 |
396 | this.setData({
397 | cartAnimation: animation.export()
398 | });
399 | }, duration);
400 | },
401 |
402 | playAddPointAnimation: function (e) {
403 | const {type} = e.currentTarget.dataset;
404 | const {offsetLeft, offsetTop} = e.currentTarget;
405 | const {
406 | listScrollTop,
407 | dishListLeft,
408 | dishListTop,
409 | pointAnimations,
410 | pointContainerAnimations,
411 | pointCount,
412 | pointDuration,
413 | cartBadgePosition,
414 | } = this.data;
415 |
416 | let initLeft = offsetLeft + dishListLeft;
417 | let initTop = offsetTop + dishListTop - listScrollTop;
418 |
419 | if (type === 'inCart') {
420 | return;
421 | }
422 |
423 | // 查找一个没有进行动画的红点节点
424 | let id = 0;
425 | for (let i = 0; i < pointCount; i++) {
426 | if (!pointAnimations[i] || !pointAnimations[i].isAnimating) {
427 | id = i;
428 | break;
429 | }
430 | }
431 |
432 | // 内部函数,执行动画
433 | const doAnimations = (left, top, duration) => {
434 | if (!pointAnimations[id]) pointAnimations[id] = {};
435 | if (!pointContainerAnimations[id]) pointContainerAnimations[id] = {};
436 |
437 | // 红点自由落体运动,相对于 point-container
438 | const pointAnimation = wx.createAnimation({
439 | duration,
440 | timingFunction: 'ease-in',
441 | });
442 |
443 | pointAnimation.top(top - initTop).step();
444 |
445 | pointAnimations[id].animation = pointAnimation.export();
446 |
447 | // 红点容器水平匀速运动
448 | const containerAnimation = wx.createAnimation({
449 | duration,
450 | timingFunction: 'ease-out', // 使用 linear 类抛物线, ease-out弧度大,效果好一些
451 | });
452 |
453 |
454 | // 加入 opacity 视觉上弱化动画开始时,卡顿现象
455 | if (duration === 0) {
456 | containerAnimation.opacity(0).top(top).left(left).step();
457 | } else {
458 | containerAnimation.opacity(1).left(left).step();
459 | }
460 |
461 | pointContainerAnimations[id].animation = containerAnimation.export();
462 |
463 | this.setData({
464 | pointAnimations,
465 | pointContainerAnimations,
466 | });
467 | };
468 |
469 | // 红点初始化为点击加号位置
470 | doAnimations(initLeft, initTop, 0);
471 |
472 | // 开始动画
473 | const waitTime = 20; // 等待初始化位置设置成功时间
474 | pointAnimations[id].isAnimating = true;
475 | setTimeout(() => {
476 | doAnimations(cartBadgePosition.left, cartBadgePosition.top, pointDuration);
477 | }, waitTime);
478 |
479 | // 动画结束
480 | setTimeout(() => {
481 | pointAnimations[id].isAnimating = false;
482 | doAnimations(-50, -50, 0);
483 | }, waitTime + pointDuration);
484 | },
485 | });
486 |
--------------------------------------------------------------------------------
/pages/dishes/data.js:
--------------------------------------------------------------------------------
1 | module.exports = [
2 | {
3 | "data": {
4 | "activeFlag": "1",
5 | "createCustomerId": "12312312",
6 | "createTime": 1536321668000,
7 | "updateCustomerId": null,
8 | "updateTime": null,
9 | "id": "151ccdc033ae49359c5b7eb31d210e64",
10 | "storeId": "93eb675d2bbc4f11",
11 | "name": "套餐大全",
12 | "dishsuitPictureUril": "D:\\test\\dishsuit\\山西小面馆\\153632166799320309.png",
13 | "dishsuitPresentation": "套餐汤类大全",
14 | "dishsuitPrice": 20.25,
15 | "dishPictureBase64": null,
16 | "list": [{
17 | "activeFlag": "1",
18 | "createCustomerId": "12312312",
19 | "createTime": 1536321668000,
20 | "updateCustomerId": null,
21 | "updateTime": null,
22 | "id": "450a7cd86f8d4a158049c0397331d3c4",
23 | "dishId": "a53fe76db5f6493e921903f293ceecfc",
24 | "dishsuitId": "151ccdc033ae49359c5b7eb31d210e64",
25 | "count": 1,
26 | "dishName": "西红柿鸡蛋汤",
27 | "dishPictureUrl": "D:\\test\\dish\\山西小面馆\\153630764082582346.png"
28 | }],
29 | "orderNo": 4
30 | },
31 | "name": "套餐大全",
32 | "id": "151ccdc033ae49359c5b7eb31d210e64",
33 | "type": "02",
34 | "order": 4
35 | },
36 | {
37 | "data": {
38 | "activeFlag": "1",
39 | "createCustomerId": "12312312",
40 | "createTime": 1536321030000,
41 | "updateCustomerId": null,
42 | "updateTime": null,
43 | "id": "fae97c5c619e4f8ba12b64134d9722e8",
44 | "storeId": "93eb675d2bbc4f11",
45 | "name": "中秋套餐大酬宾",
46 | "dishsuitPictureUril": "D:\\test\\dishsuit\\山西小面馆\\153630833251574945.png",
47 | "dishsuitPresentation": "中秋套餐大全",
48 | "dishsuitPrice": 20.50,
49 | "dishPictureBase64": null,
50 | "list": [{
51 | "activeFlag": "1",
52 | "createCustomerId": "12312312",
53 | "createTime": 1536308332000,
54 | "updateCustomerId": null,
55 | "updateTime": null,
56 | "id": "79f34d6dca864d8eac2ad4c9f347b48f",
57 | "dishId": "0e9db17758164cebab0574e29e93650f",
58 | "dishsuitId": "fae97c5c619e4f8ba12b64134d9722e8",
59 | "count": 1,
60 | "dishName": "小炒肉",
61 | "dishPictureUrl": "D:\\test\\dish\\山西小面馆\\153630784455499022.png"
62 | }, {
63 | "activeFlag": "1",
64 | "createCustomerId": "12312312",
65 | "createTime": 1536308332000,
66 | "updateCustomerId": null,
67 | "updateTime": null,
68 | "id": "d9c061183b82449b86c69691a7b72221",
69 | "dishId": "0ad03d8f01be40beb78e9a87d46465dc",
70 | "dishsuitId": "fae97c5c619e4f8ba12b64134d9722e8",
71 | "count": 1,
72 | "dishName": "拍黄瓜",
73 | "dishPictureUrl": "D:\\test\\dish\\山西小面馆\\153630780013403552.png"
74 | }],
75 | "orderNo": 2
76 | }, "name": "中秋套餐大酬宾", "id": "fae97c5c619e4f8ba12b64134d9722e8", "type": "02", "order": 2
77 | }, {
78 | "data": {
79 | "activeFlag": "1",
80 | "createCustomerId": "12312312",
81 | "createTime": 1536321027000,
82 | "updateCustomerId": null,
83 | "updateTime": null,
84 | "id": "9340d70657f8481ca45021dccea0dcfc",
85 | "storeId": "93eb675d2bbc4f11",
86 | "name": "国庆套餐大酬宾",
87 | "dishsuitPictureUril": "D:\\test\\dishsuit\\山西小面馆\\153630840921404495.png",
88 | "dishsuitPresentation": "国庆套餐大全",
89 | "dishsuitPrice": 50.00,
90 | "dishPictureBase64": null,
91 | "list": [{
92 | "activeFlag": "1",
93 | "createCustomerId": "12312312",
94 | "createTime": 1536308409000,
95 | "updateCustomerId": null,
96 | "updateTime": null,
97 | "id": "1fc7f45920264e7380599ffc24db38a3",
98 | "dishId": "a2774e0966a5484198d1ea9dd78a3463",
99 | "dishsuitId": "9340d70657f8481ca45021dccea0dcfc",
100 | "count": 1,
101 | "dishName": "米饭",
102 | "dishPictureUrl": "D:\\test\\dish\\山西小面馆\\153630775999396496.png"
103 | }, {
104 | "activeFlag": "1",
105 | "createCustomerId": "12312312",
106 | "createTime": 1536308409000,
107 | "updateCustomerId": null,
108 | "updateTime": null,
109 | "id": "d345fa93ed9a4b56b356733e7cee9642",
110 | "dishId": "cdec9741b572464fab825c038f7b65f8",
111 | "dishsuitId": "9340d70657f8481ca45021dccea0dcfc",
112 | "count": 1,
113 | "dishName": "鱼香肉丝",
114 | "dishPictureUrl": "D:\\test\\dish\\山西小面馆\\153630787785486609.png"
115 | }, {
116 | "activeFlag": "1",
117 | "createCustomerId": "12312312",
118 | "createTime": 1536308409000,
119 | "updateCustomerId": null,
120 | "updateTime": null,
121 | "id": "f3b6ce34d73f404aa68f3aa356e33ba2",
122 | "dishId": "596c1c001a084a16aed9b7c79d9299d6",
123 | "dishsuitId": "9340d70657f8481ca45021dccea0dcfc",
124 | "count": 1,
125 | "dishName": "紫菜鸡蛋汤",
126 | "dishPictureUrl": "D:\\test\\dish\\山西小面馆\\153630769722277213.png"
127 | }],
128 | "orderNo": 1
129 | }, "name": "国庆套餐大酬宾", "id": "9340d70657f8481ca45021dccea0dcfc", "type": "02", "order": 1
130 | }, {
131 | "data": [{
132 | "activeFlag": "1",
133 | "createCustomerId": "12312312",
134 | "createTime": 1536307760000,
135 | "updateCustomerId": null,
136 | "updateTime": null,
137 | "id": "a2774e0966a5484198d1ea9dd78a3463",
138 | "storeId": "93eb675d2bbc4f11",
139 | "name": "米饭",
140 | "categoryId": "45e5657d247649ea905de3c238af5e68",
141 | "dishPictureUrl": "D:\\test\\dish\\山西小面馆\\153630775999396496.png",
142 | "dishPresentation": "米饭",
143 | "dishPrice": 2.00,
144 | "dishSpecification": "蒸米饭",
145 | "dishBase64": null,
146 | "categoryName": "饭类",
147 | "dishType": null,
148 | "categoryOrder": 4,
149 | "orderNo": 3
150 | }], "name": "饭类", "id": "45e5657d247649ea905de3c238af5e68", "type": "01", "order": 4
151 | }, {
152 | "data": [{
153 | "activeFlag": "1",
154 | "createCustomerId": "12312312",
155 | "createTime": 1536307642000,
156 | "updateCustomerId": null,
157 | "updateTime": 1536307712000,
158 | "id": "a53fe76db5f6493e921903f293ceecfc",
159 | "storeId": "93eb675d2bbc4f11",
160 | "name": "西红柿鸡蛋汤",
161 | "categoryId": "00cd5b4877814ff1bcca62ecddea33f6",
162 | "dishPictureUrl": "D:\\test\\dish\\山西小面馆\\153630764082582346.png",
163 | "dishPresentation": "西红柿鸡蛋汤好喝",
164 | "dishPrice": 12.50,
165 | "dishSpecification": "清汤",
166 | "dishBase64": null,
167 | "categoryName": "汤类",
168 | "dishType": null,
169 | "categoryOrder": 3,
170 | "orderNo": 1
171 | }, {
172 | "activeFlag": "1",
173 | "createCustomerId": "12312312",
174 | "createTime": 1536307697000,
175 | "updateCustomerId": null,
176 | "updateTime": 1536307716000,
177 | "id": "596c1c001a084a16aed9b7c79d9299d6",
178 | "storeId": "93eb675d2bbc4f11",
179 | "name": "紫菜鸡蛋汤",
180 | "categoryId": "00cd5b4877814ff1bcca62ecddea33f6",
181 | "dishPictureUrl": "D:\\test\\dish\\山西小面馆\\153630769722277213.png",
182 | "dishPresentation": "紫菜鸡蛋汤好喝",
183 | "dishPrice": 15.50,
184 | "dishSpecification": "清汤",
185 | "dishBase64": null,
186 | "categoryName": "汤类",
187 | "dishType": null,
188 | "categoryOrder": 3,
189 | "orderNo": 2
190 | }], "name": "汤类", "id": "00cd5b4877814ff1bcca62ecddea33f6", "type": "01", "order": 3
191 | }, {
192 | "data": [{
193 | "activeFlag": "1",
194 | "createCustomerId": "12312312",
195 | "createTime": 1536307844000,
196 | "updateCustomerId": null,
197 | "updateTime": null,
198 | "id": "0e9db17758164cebab0574e29e93650f",
199 | "storeId": "93eb675d2bbc4f11",
200 | "name": "小炒肉",
201 | "categoryId": "c7637a966b404e518ef2599d735f3b65",
202 | "dishPictureUrl": "D:\\test\\dish\\山西小面馆\\153630784455499022.png",
203 | "dishPresentation": "小炒肉",
204 | "dishPrice": 20.00,
205 | "dishSpecification": "小炒",
206 | "dishBase64": null,
207 | "categoryName": "热菜",
208 | "dishType": null,
209 | "categoryOrder": 2,
210 | "orderNo": 5
211 | }, {
212 | "activeFlag": "1",
213 | "createCustomerId": "12312312",
214 | "createTime": 1536307878000,
215 | "updateCustomerId": null,
216 | "updateTime": null,
217 | "id": "cdec9741b572464fab825c038f7b65f8",
218 | "storeId": "93eb675d2bbc4f11",
219 | "name": "鱼香肉丝",
220 | "categoryId": "c7637a966b404e518ef2599d735f3b65",
221 | "dishPictureUrl": "D:\\test\\dish\\山西小面馆\\153630787785486609.png",
222 | "dishPresentation": "鱼香肉丝",
223 | "dishPrice": 20.00,
224 | "dishSpecification": "小炒",
225 | "dishBase64": null,
226 | "categoryName": "热菜",
227 | "dishType": null,
228 | "categoryOrder": 2,
229 | "orderNo": 6
230 | }], "name": "热菜", "id": "c7637a966b404e518ef2599d735f3b65", "type": "01", "order": 2
231 | }, {
232 | "data": [{
233 | "activeFlag": "1",
234 | "createCustomerId": "12312312",
235 | "createTime": 1536307800000,
236 | "updateCustomerId": null,
237 | "updateTime": null,
238 | "id": "0ad03d8f01be40beb78e9a87d46465dc",
239 | "storeId": "93eb675d2bbc4f11",
240 | "name": "拍黄瓜",
241 | "categoryId": "aab272abdb5946138a3a73c732561758",
242 | "dishPictureUrl": "D:\\test\\dish\\山西小面馆\\153630780013403552.png",
243 | "dishPresentation": "拍黄瓜",
244 | "dishPrice": 2.00,
245 | "dishSpecification": "凉菜",
246 | "dishBase64": null,
247 | "categoryName": "凉菜",
248 | "dishType": null,
249 | "categoryOrder": 1,
250 | "orderNo": 4
251 | }], "name": "凉菜", "id": "aab272abdb5946138a3a73c732561758", "type": "01", "order": 1
252 | },
253 | {
254 | "data": [
255 | {
256 | "activeFlag": "1",
257 | "createCustomerId": "12312312",
258 | "createTime": 1536308641000,
259 | "updateCustomerId": null,
260 | "updateTime": null,
261 | "id": "4245838b82c6427f8d429144a76e42e2",
262 | "name": "国庆节推荐",
263 | "storeId": "93eb675d2bbc4f11",
264 | "dishList": [
265 | {
266 | "activeFlag": null,
267 | "createCustomerId": null,
268 | "createTime": null,
269 | "updateCustomerId": null,
270 | "updateTime": null,
271 | "id": "9340d70657f8481ca45021dccea0dcfc",
272 | "storeId": null,
273 | "name": "国庆套餐大酬宾",
274 | "categoryId": null,
275 | "dishPictureUrl": "D:\\test\\dishsuit\\山西小面馆\\153630840921404495.png",
276 | "dishPresentation": null,
277 | "dishPrice": null,
278 | "dishSpecification": null,
279 | "dishBase64": null,
280 | "categoryName": null,
281 | "dishType": "02",
282 | "categoryOrder": 0,
283 | "orderNo": null
284 | },
285 | {
286 | "activeFlag": "1",
287 | "createCustomerId": "12312312",
288 | "createTime": 1536307697000,
289 | "updateCustomerId": null,
290 | "updateTime": 1536307716000,
291 | "id": "596c1c001a084a16aed9b7c79d9299d6",
292 | "storeId": "93eb675d2bbc4f11",
293 | "name": "紫菜鸡蛋汤",
294 | "categoryId": "00cd5b4877814ff1bcca62ecddea33f6",
295 | "dishPictureUrl": "D:\\test\\dish\\山西小面馆\\153630769722277213.png",
296 | "dishPresentation": "紫菜鸡蛋汤好喝",
297 | "dishPrice": 15.50,
298 | "dishSpecification": "清汤",
299 | "dishBase64": null,
300 | "categoryName": "汤类",
301 | "dishType": "01",
302 | "categoryOrder": 0,
303 | "orderNo": 2
304 | },
305 | {
306 | "activeFlag": null,
307 | "createCustomerId": null,
308 | "createTime": null,
309 | "updateCustomerId": null,
310 | "updateTime": null,
311 | "id": "9340d70657f8481ca45021dccea0dcfc1",
312 | "storeId": null,
313 | "name": "国庆套餐大酬宾",
314 | "categoryId": null,
315 | "dishPictureUrl": "D:\\test\\dishsuit\\山西小面馆\\153630840921404495.png",
316 | "dishPresentation": null,
317 | "dishPrice": null,
318 | "dishSpecification": null,
319 | "dishBase64": null,
320 | "categoryName": null,
321 | "dishType": "02",
322 | "categoryOrder": 0,
323 | "orderNo": null
324 | },
325 | {
326 | "activeFlag": "1",
327 | "createCustomerId": "12312312",
328 | "createTime": 1536307697000,
329 | "updateCustomerId": null,
330 | "updateTime": 1536307716000,
331 | "id": "596c1c001a084a16aed9b7c79d9299d6",
332 | "storeId": "93eb675d2bbc4f11",
333 | "name": "紫菜鸡蛋汤",
334 | "categoryId": "00cd5b4877814ff1bcca62ecddea33f61",
335 | "dishPictureUrl": "D:\\test\\dish\\山西小面馆\\153630769722277213.png",
336 | "dishPresentation": "紫菜鸡蛋汤好喝",
337 | "dishPrice": 15.50,
338 | "dishSpecification": "清汤",
339 | "dishBase64": null,
340 | "categoryName": "汤类",
341 | "dishType": "01",
342 | "categoryOrder": 0,
343 | "orderNo": 2
344 | }
345 | ]
346 | },
347 | {
348 | "activeFlag": "1",
349 | "createCustomerId": "12312312",
350 | "createTime": 1536308612000,
351 | "updateCustomerId": null,
352 | "updateTime": null,
353 | "id": "6967ef547ec644ffbc50b47866d51623",
354 | "name": "中秋节推荐",
355 | "storeId": "93eb675d2bbc4f11",
356 | "dishList": [
357 | {
358 | "activeFlag": "1",
359 | "createCustomerId": "12312312",
360 | "createTime": 1536307642000,
361 | "updateCustomerId": null,
362 | "updateTime": 1536307712000,
363 | "id": "a53fe76db5f6493e921903f293ceecfc",
364 | "storeId": "93eb675d2bbc4f11",
365 | "name": "西红柿鸡蛋汤",
366 | "categoryId": "00cd5b4877814ff1bcca62ecddea33f6",
367 | "dishPictureUrl": "D:\\test\\dish\\山西小面馆\\153630764082582346.png",
368 | "dishPresentation": "西红柿鸡蛋汤好喝",
369 | "dishPrice": 12.50,
370 | "dishSpecification": "清汤",
371 | "dishBase64": null,
372 | "categoryName": "汤类",
373 | "dishType": "01",
374 | "categoryOrder": 0,
375 | "orderNo": 1
376 | },
377 | {
378 | "activeFlag": null,
379 | "createCustomerId": null,
380 | "createTime": null,
381 | "updateCustomerId": null,
382 | "updateTime": null,
383 | "id": "fae97c5c619e4f8ba12b64134d9722e8",
384 | "storeId": null,
385 | "name": "中秋套餐大酬宾",
386 | "categoryId": null,
387 | "dishPictureUrl": "D:\\test\\dishsuit\\山西小面馆\\153630833251574945.png",
388 | "dishPresentation": null,
389 | "dishPrice": null,
390 | "dishSpecification": null,
391 | "dishBase64": null,
392 | "categoryName": null,
393 | "dishType": "02",
394 | "categoryOrder": 0,
395 | "orderNo": null
396 | }
397 | ]
398 | },
399 | {
400 | "activeFlag": "1",
401 | "createCustomerId": "12312312",
402 | "createTime": 1536308566000,
403 | "updateCustomerId": null,
404 | "updateTime": null,
405 | "id": "000bb32f7a924609a5746059b9fb6e55",
406 | "name": "今日特价菜推荐",
407 | "storeId": "93eb675d2bbc4f11",
408 | "dishList": [
409 | {
410 | "activeFlag": "1",
411 | "createCustomerId": "12312312",
412 | "createTime": 1536307697000,
413 | "updateCustomerId": null,
414 | "updateTime": 1536307716000,
415 | "id": "596c1c001a084a16aed9b7c79d9299d6",
416 | "storeId": "93eb675d2bbc4f11",
417 | "name": "紫菜鸡蛋汤",
418 | "categoryId": "00cd5b4877814ff1bcca62ecddea33f6",
419 | "dishPictureUrl": "D:\\test\\dish\\山西小面馆\\153630769722277213.png",
420 | "dishPresentation": "紫菜鸡蛋汤好喝",
421 | "dishPrice": 15.50,
422 | "dishSpecification": "清汤",
423 | "dishBase64": null,
424 | "categoryName": "汤类",
425 | "dishType": "01",
426 | "categoryOrder": 0,
427 | "orderNo": 2
428 | },
429 | {
430 | "activeFlag": "1",
431 | "createCustomerId": "12312312",
432 | "createTime": 1536307800000,
433 | "updateCustomerId": null,
434 | "updateTime": null,
435 | "id": "0ad03d8f01be40beb78e9a87d46465dc",
436 | "storeId": "93eb675d2bbc4f11",
437 | "name": "拍黄瓜",
438 | "categoryId": "aab272abdb5946138a3a73c732561758",
439 | "dishPictureUrl": "D:\\test\\dish\\山西小面馆\\153630780013403552.png",
440 | "dishPresentation": "拍黄瓜",
441 | "dishPrice": 2.00,
442 | "dishSpecification": "凉菜",
443 | "dishBase64": null,
444 | "categoryName": "凉菜",
445 | "dishType": "01",
446 | "categoryOrder": 0,
447 | "orderNo": 4
448 | }
449 | ]
450 | }
451 | ],
452 | "type": "03"
453 | }
454 | ]
455 |
--------------------------------------------------------------------------------