[
  {
    "path": "README.md",
    "content": "# 微信小程序－点餐\n\n### 说明：\n\n实现了点餐功能，包括菜品展示，分类筛选，加入购物车，提交订单等功能。\n\n### 数据接口:\n\n使用本地数据\n\n### 目录结构：\n\n- res — 存放项目图片\n- pages — 存放项目页面相关文件，包括home,order等页面\n- utils — 存放时间处理文件，可require引入\n\n### 开发环境：\n\n微信web开发者工具 v0.10.102800\n\n### 项目截图：\n\nhttps://www.getweapp.com/project?projectId=583291e9bb2538f8186c706d\n"
  },
  {
    "path": "app.js",
    "content": "//app.js\nApp({\n  // onLaunch: function () {\n  //   //调用API从本地缓存中获取数据\n  //   var logs = wx.getStorageSync('logs') || []\n  //   logs.unshift(Date.now())\n  //   wx.setStorageSync('logs', logs)\n  // },\n  getUserInfo:function(cb){\n    var that = this\n    if(this.globalData.userInfo){\n      typeof cb == \"function\" && cb(this.globalData.userInfo)\n    }else{\n      //调用登录接口\n      wx.login({\n        success: function () {\n          wx.getUserInfo({\n            success: function (res) {\n              that.globalData.userInfo = res.userInfo\n              typeof cb == \"function\" && cb(that.globalData.userInfo)\n            }\n          })\n        }\n      })\n    }\n  },\n  globalData:{\n    userInfo: null,\n    foodList: [\n      {\n        id: 1,\n        title: \"黑胡椒意酱面\",\n        cost: 45,\n        desc: \"进口意大利通心粉制作，搭配有机番茄秘制酱汁。\",\n        icon: \"https://fuss10.elemecdn.com/8/05/0b0f3719bf1c9c1673ed69e262888jpeg.jpeg\",\n        num: 0\n      },\n      {\n        id: 2,\n        title: \"吉士意大利虾仁面\",\n        cost: 40,\n        desc: \"进口意大利通心粉制作，搭配进口地中海大虾仁。\",\n        icon: \"https://fuss10.elemecdn.com/4/df/ff64bc5b06893a12aafb1e94f8b31jpeg.jpeg\",\n        num: 0\n      },\n      {\n        id: 3,\n        title: \"牛排意大利面\",\n        cost: 38,\n        desc: \"进口意大利通心粉制作，搭配新鲜酱汁牛排和甜糯玉米。\",\n        icon: \"https://fuss10.elemecdn.com/3/42/70aae8406958d22657c5772e2412ejpeg.jpeg\",\n        num: 0\n      },\n      {\n        id: 4,\n        title: \"香炸鸡翅\",\n        cost: 12,\n        desc: \"有机食品，绿色农场放心肉源\",\n        icon: \"https://fuss10.elemecdn.com/e/0e/df4f9d07b191d34ceddc3fedd88dcjpeg.jpeg\",\n        num: 0\n      },\n      {\n        id: 5,\n        title: \"一品寿司\",\n        cost: 22,\n        desc: \"百分百手工，百分百口味，来自东海岸的问候。\",\n        icon: \"https://fuss10.elemecdn.com/8/53/353cf146fc9cab79479efcfb6e88ajpeg.jpeg\",\n        num: 0\n      },\n      {\n        id: 6,\n        title: \"水果拼盘\",\n        cost: 16,\n        desc: \"新鲜时蔬，源自生活的百味奇珍。\",\n        icon: \"https://fuss10.elemecdn.com/0/b9/42b68495a09ec2e501ec3eaa36c6ejpeg.jpeg\",\n        num: 0\n      },\n      {\n        id: 7,\n        title: \"巧手三明治\",\n        cost: 22,\n        desc: \"好吃不贵，明治选择。\",\n        icon: \"https://fuss10.elemecdn.com/a/fe/0d647946855f76e9dcdfbedfcad61jpeg.jpeg\",\n        num: 0\n      },\n      {\n        id: 8,\n        title: \"培根焗饭\",\n        cost: 26,\n        desc: \"好吃不解释，真的不解释。\",\n        icon: \"https://fuss10.elemecdn.com/9/de/30676686cf98d88961eb69f1f3083jpeg.jpeg\",\n        num: 0\n      }\n\n    ]\n    \n  }\n})"
  },
  {
    "path": "app.json",
    "content": "{\n  \"pages\":[\n    \"pages/home/home\",\n    \"pages/order/order\"\n  ],\n  \"window\":{\n    \"backgroundTextStyle\":\"light\",\n    \"backgroundColor\": \"#fbf9fe\",\n    \"navigationBarBackgroundColor\": \"#000\",\n    \"navigationBarTitleText\": \"餐厅\",\n    \"navigationBarTextStyle\":\"white\"\n  }\n}\n"
  },
  {
    "path": "app.wxss",
    "content": "/**app.wxss**/\n.container {\n  height: 100%;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: space-between;\n  padding: 200rpx 0;\n  box-sizing: border-box;\n} \n"
  },
  {
    "path": "pages/home/home.js",
    "content": "Page({\n  data:{\n    \n    foodList:[],\n    detailFood:{},\n    modalHidden: true,\n\n    //订单数据\n    orderList: {},\n    orderNum: 0,\n    orderCost: 0,\n    toastHidden: true,\n\n    //轮播图\n    imgUrls: [\n      'https://fuss10.elemecdn.com/d/c0/56cfcdabba9fec97a3307b571ca8cjpeg.jpeg',\n      'https://fuss10.elemecdn.com/6/f2/5cd85b966281a8d545c34019d0fd1jpeg.jpeg',\n      'https://fuss10.elemecdn.com/e/55/b00aef689cb424aaaeae9d50d3e76jpeg.jpeg',\n      'https://fuss10.elemecdn.com/a/88/98d10d5580ce28db07673e28726ccjpeg.jpeg'\n\n    ],\n    indicatorDots: true,\n    autoplay: true,\n    interval: 4000,\n    duration: 1000,\n\n    //picker\n    foodTypes: ['全部菜品', '披萨', '面条', '水果', '寿司', '三明治'],\n    foodTypesIndex: 0,\n    rankTypes: ['综合排序', '热度', '价格', '好评', '时间'],\n    rankTypesIndex: 0\n  },\n\n  // 页面初始化 options为页面跳转所带来的参数\n  onLoad:function(options){\n    \n    //全局数据中获得食品列表\n    var appInstance = getApp();\n    var t_foodList = appInstance.globalData.foodList;\n\n    var t_arr;\n    for(var i=0; i<t_foodList.length; i++){\n      if(i%2 == 0){\n        t_arr = [];\n        this.data.foodList.push(t_arr); //整理成二维数据，方便显示\n      }\n      t_arr.push(t_foodList[i]);\n    }\n  },\n\n  //关闭食品详情弹窗\n  closeModal: function(e) {\n    this.setData({\n      modalHidden: true\n    })\n  },\n\n  //展示食品详情弹窗\n  imageClick:function(e){\n\n    var dataset = e.currentTarget.dataset;\n    \n    var appInstance = getApp();\n    var t_foodList = appInstance.globalData.foodList;\n\n    //设置数据，自动刷新界面\n    this.setData({\n      modalHidden: false,\n      detailFood: t_foodList[dataset.index],\n      detailIndex: dataset.index\n    })\n  },\n\n  //提交订单\n  sublimitClick:function(e){\n\n    //订单列表 传参\n    var agrs = JSON.stringify(this.data.orderList);\n    wx.navigateTo({\n        url: '../order/order?order=' + agrs\n    })\n  },\n\n  //加入到购物车\n  addCartClick:function(e){\n    var dataset = e.currentTarget.dataset;\n    this.changeNum(dataset.index, true);\n\n    this.setData({\n      toastHidden: false\n    });\n\n    //1s后关闭\n    var _this = this;\n    setTimeout(function(){\n      _this.setData({\n        toastHidden: true\n      });\n    }, 1000);\n\n    //关闭商品详情面板\n    this.closeModal();\n  },\n\n  //增加数量\n  addClick:function(e){\n    var dataset = e.currentTarget.dataset;\n    this.changeNum(dataset.index, true);\n  },\n\n  //减少数量\n  reduceClick:function(e){\n    var dataset = e.currentTarget.dataset;\n    this.changeNum(dataset.index, false);\n  },\n\n  changeNum:function(index, bool){\n    var appInstance = getApp();\n    var t_food = appInstance.globalData.foodList[index];\n\n    var orderList = this.data.orderList;\n\n    var obj = orderList[t_food.id];\n\n    //如果存在，则数量变化\n    if(obj){\n        if(bool){\n          obj.num = obj.num + 1;\n        }else{\n          if(obj.num > 0){\n            obj.num = obj.num - 1;\n          }else{\n            return;//已经减少为0\n          }\n        }\n    }else{\n        if(bool){\n          //不存在，点击增加，则写入一条订单数据，数量默认1\n          obj = {\n            id: t_food.id,\n            num: 1,\n            cost: t_food.cost,\n            title: t_food.title\n          };\n          this.data.orderList[t_food.id] = obj;\n        }else{\n          return;//不存在，并且点击的是减少\n        }\n    }\n    \n    var order_num = 0;\n    var order_cost = 0;\n    for(var k in orderList){\n      order_num = orderList[k].num + order_num; //计算总数量\n      order_cost = order_cost + orderList[k].cost * orderList[k].num; //计算总价格\n    }\n\n    this.setData({\n      orderList: orderList,\n      orderNum: order_num,\n      orderCost: order_cost\n    });\n  },\n\n  //食品类型\n  foodTypeChange: function(e) {\n    console.log('picker发送选择改变，携带值为', e.detail.value)\n    this.setData({\n      foodTypesIndex: e.detail.value\n    })\n  },\n\n  //排序类型\n  rankTypeChange: function(e) {\n    console.log('picker发送选择改变，携带值为', e.detail.value)\n    this.setData({\n      rankTypesIndex: e.detail.value\n    })\n  },\n\n})"
  },
  {
    "path": "pages/home/home.wxml",
    "content": "<view class=\"section\">\n\n  <!-- banner -->\n  <view class=\"banner\" >\n    <swiper indicator-dots=\"{{indicatorDots}}\"\n      autoplay=\"{{autoplay}}\" interval=\"{{interval}}\" duration=\"{{duration}}\">\n      <block wx:for=\"{{imgUrls}}\" wx:key=\"item.id\">\n        <swiper-item>\n          <image src=\"{{item}}\" mode=\"scaleToFill\" class=\"slide-image\" />\n        </swiper-item>\n      </block>\n    </swiper>\n  </view>\n\n  <!-- picker -->\n  <view class=\"memu\">\n    <picker style=\"flex: 1;\" bindchange=\"foodTypeChange\" value=\"0\" range=\"{{foodTypes}}\">\n        <view class=\"memu-bt\">{{foodTypes[foodTypesIndex]}}<span style=\"font-size:22px;\">﹀</span></view>\n    </picker>\n\n    <picker style=\"flex: 1;\" bindchange=\"rankTypeChange\" value=\"0\" range=\"{{rankTypes}}\">\n        <view class=\"memu-bt\">{{rankTypes[rankTypesIndex]}}<span style=\"font-size:22px;\">﹀</span></view>\n    </picker>\n  </view>\n\n  <!-- 食品列表 -->\n  <view>\n    <scroll-view style=\"height: 100%;\" scroll-y=\"true\">\n      <view class=\"fooditem\" wx:for=\"{{foodList}}\" wx:key=\"item.id\">\n\n        <view class=\"fooditem-l\" >\n          <image mode=\"scaleToFill\" src=\"{{item[0].icon}}\" data-index=\"{{index*2}}\" bindtap=\"imageClick\"></image>\n          <view class=\"foodtitle\">{{item[0].title}}</view>\n          <view class=\"fooditem-handle\">\n            <span class=\"fooditem-jia\" data-index=\"{{index*2}}\" bindtap=\"addClick\" >+</span>\n            <span class=\"fooditem-num\">{{orderList[item[0].id].num ? orderList[item[0].id].num : 0}}</span>\n            <span class=\"fooditem-jia\" data-index=\"{{index*2}}\" bindtap=\"reduceClick\">-</span>\n            <span class=\"fooditem-cost\">￥{{item[0].cost}}</span>\n          </view>\n        </view>\n        <view class=\"fooditem-r\" >\n          <image mode=\"scaleToFill\" src=\"{{item[1].icon}}\" data-index=\"{{index*2 + 1}}\" bindtap=\"imageClick\"></image>\n          <view class=\"foodtitle\">{{item[1].title}}</view>\n          <view class=\"fooditem-handle\">\n            <span class=\"fooditem-jia\" data-index=\"{{index*2 + 1}}\" bindtap=\"addClick\">+</span>\n            <span class=\"fooditem-num\">{{orderList[item[1].id].num ? orderList[item[1].id].num : 0}}</span>\n            <span class=\"fooditem-jia\" data-index=\"{{index*2 + 1}}\" bindtap=\"reduceClick\">-</span>\n            <span class=\"fooditem-cost\">￥{{item[1].cost}}</span>\n          </view>\n        </view>\n        \n      </view>\n    </scroll-view>\n  </view>\n\n  <!-- toast -->\n  <toast hidden=\"{{toastHidden}}\">已加入购物车</toast>\n\n  <!-- 弹出面板 -->\n  <view class=\"detail\" style=\"display:{{modalHidden ? 'none' : 'block'}};\">\n    <view class=\"detail-top\" bindtap=\"closeModal\"></view>\n    <view class=\"detail-bottom\">\n      <image src=\"{{detailFood.icon}}\"></image>\n      <view class=\"detail-title\">{{detailFood.title}}</view>\n      <view class=\"detail-cost\">￥{{detailFood.cost}}</view>\n      <view class=\"detail-desc\">{{detailFood.desc}}</view>\n    </view>\n    <view class=\"detail-buybt\" data-index=\"{{detailIndex}}\" bindtap=\"addCartClick\">加入购物车</view>\n  </view>\n\n  <view style=\"height:45px;\" ><view>\n\n  <!-- 提交订单 -->\n  <view class=\"order-view\" style=\"display:{{modalHidden ? 'flex' : 'none'}};\">\n      <view class=\"order-number\">\n        <image mode=\"scaleToFill\" src=\"/res/img/cart.png\"></image>\n        <span class=\"order-num-txt\">{{orderNum}}</span>\n        <span>￥{{orderCost}}</span>\n      </view>\n      <view class=\"order-bt\" bindtap=\"sublimitClick\">提交订单</view>\n  <view>\n\n</view>\n\n\n"
  },
  {
    "path": "pages/home/home.wxss",
    "content": "\n/* banner image */\n.slide-image{\n    width: 96%;\n    height: 250px;\n    margin: 0 auto;\n    margin-left: 2%;\n}\n\n/* 下拉菜单 */\n.memu{\n    display: flex;\n    flex-direction: row;\n    text-align: center;\n    border-bottom: 1px solid #ccc;\n}\n.memu-bt{\n    display: block;\n    flex: 1;\n    padding: 10px 0px;\n}\n.memu-bt span{\n    position: relative;\n    top: 7px;\n    left: 5px;\n}\n\n/* 食品列表 */\n.fooditem{\n    flex-direction: row;\n    display: flex;\n    margin: 15px 0px;\n}\n.fooditem-l,.fooditem-r{\n    flex: 1;\n}\n.fooditem-l{\n    margin-left: 5%;\n}\n.fooditem-r{\n    margin-left: 0%;\n}\n.fooditem image{\n    width: 90%; \n    height: 100px;\n}\n.foodtitle{\n    font-size: 14px;\n    margin-top: 10px;\n}\n.fooditem-handle{\n    margin-top: 12px;\n}\n.fooditem-jia{\n    font-size: 20px;\n    width: 18px;\n    height: 18px;\n    display: inline-block;\n    border: 1px solid #ff2d2d;\n    color: #ff2d2d;\n    border-radius: 10px;\n    text-align: center;\n    line-height: 20px;\n}\n.fooditem-num{\n    font-size: 14px;\n    display: inline-block;\n    color: #ff2d2d;\n    width: 28px;\n    text-align: center;\n    margin-top: -3px;\n}\n.fooditem-cost{\n    font-size: 14px;\n    color: #ff2d2d;\n    width: 40px;\n    float: right;\n    margin-top: 5px;\n}\n\n/* 提交订单 */\n.order-view{\n    position: fixed;\n    display: flex;\n    bottom: 0px;\n    left: 0px;\n    right: 0px;\n    height: 45px;\n}\n.order-number{\n    width: 50%;\n    background-color: #eee;\n    color: #ff2d2d;\n    text-align: center;\n    line-height: 45px;\n}\n.order-number image{\n    position: absolute;\n    width: 20px; \n    height: 20px;\n    left: 19px;\n    top: 13px;\n}\n.order-num-txt{\n    position: absolute;\n    width: 20px; \n    height: 20px;\n    display: block;\n    font-size: 14px;\n    border-radius: 10px;\n    background-color: #ff2d2d;\n    color: #fff;\n    line-height: 22px;\n    left: 35px;\n    top: 5px;\n}\n.order-bt{\n    width: 50%;\n    background-color: #09bb07;\n    color: #fff;\n    text-align: center;\n    line-height: 45px;\n}\n\n/* 食品详情-弹出面板 */\n.detail{\n    width: 100%;\n    position: fixed;\n    bottom: 0px;\n    top: 0px;\n    left: 0px;\n    right: 0px;\n}\n.detail-top{\n    height: 16%;\n    background-color: #000;\n    opacity: 0.75;\n}\n.detail-bottom{\n    height: 84%;\n    background-color: #fff;\n}\n.detail-bottom image{\n    width: 100%;\n}\n.detail-title{\n    font-size: 14px;\n    margin: 20px 0px 0px 20px;\n}\n.detail-cost{\n    font-size: 14px;\n    color: #ff2d2d;\n    margin: 10px 0px 20px 20px;\n}\n.detail-desc{\n    font-size: 14px;\n    color: #aaa;\n    padding: 20px 0px 0px 20px;\n    border-top: 1px solid #999;\n}\n.detail-buybt{\n    position: absolute;\n    bottom: 0px;\n    left: 0px;\n    right: 0px;\n    height: 50px;\n    background-color: #09bb07;\n    line-height: 50px;\n    text-align: center;\n    color: #fff;\n    font-size: 20px;\n}"
  },
  {
    "path": "pages/order/order.js",
    "content": "Page({\n  data:{\n    orderList: [],\n    total: 0, //总价格\n    toastHidden: true,\n    toastTxt: \"\",\n    tables: [\"1号\", \"2号\", \"3号\", \"4号\", \"5号\", \"6号\", \"7号\", \"8号\", \"9号\"], //桌号\n    tableIndex: 0\n  },\n\n  // 页面初始化 options为页面跳转所带来的参数\n  onLoad:function(options){\n    \n    //object 转 array\n    var order = JSON.parse(options.order);\n    var t_order = [];\n    var t_total = 0;\n    for(var k in order){\n      if(order[k].num > 0){\n          t_order.push(order[k]);\n          t_total = t_total + order[k].cost*order[k].num; //计算总价格\n      }\n    }\n  \n    this.setData({\n      orderList: t_order,\n      total: t_total\n    });\n    \n  },\n\n  //修改标题\n  onReady:function(){\n    wx.setNavigationBarTitle({\n      title: '确认菜品'\n    })\n  },\n\n  //返回修改\n  returnClick:function(){\n    wx.navigateBack();\n  },\n\n  //确认提交\n  okClick:function(){\n\n    //1s后关闭\n    var _this = this;\n    setTimeout(function(){\n      _this.setData({\n        toastHidden: true\n      });\n    }, 1000);\n\n    if(this.data.orderList.length == 0){\n        this.setData({\n          toastHidden: false,\n          toastTxt: \"没有选择商品\"\n        });\n    }else{\n        this.setData({\n          toastHidden: false,\n          toastTxt: \"提交成功\"\n        });\n    }\n  },\n\n  bindPickerChange: function(e) {\n    console.log('picker发送选择改变，携带值为', e.detail.value)\n    this.setData({\n      tableIndex: e.detail.value\n    })\n  },\n})"
  },
  {
    "path": "pages/order/order.wxml",
    "content": "<view>\n  <view class=\"table\">\n    <view class=\"table-num\">桌号</view>\n    \n    <picker bindchange=\"bindPickerChange\" value=\"{{tableIndex}}\" range=\"{{tables}}\">\n      <view class=\"table-input\">{{tables[tableIndex]}}</view>\n      <span class=\"table-arrow\">﹀</span>\n    </picker>\n\n  </view>\n\n  <view class=\"beizhu\">\n    <view class=\"beizhu-txt\">备注</view>\n    <input class=\"beizhu-input\"  placeholder=\"\" />\n  </view>\n\n  <view class=\"order\">\n    <view class=\"cost\">总价：<span>￥{{total}}</span></view>\n\n    <view class=\"orderlist\" wx:for=\"{{orderList}}\" wx:key=\"item.id\">\n      <span>{{item.title}}</span><span style=\"float:right;\">￥{{item.cost}} * {{item.num}}</span>\n    </view>\n  </view>\n\n  <view class=\"bt-area\">\n    <view class=\"bt-return\" bindtap=\"returnClick\">返回修改</view>\n    <view class=\"bt-ok\" bindtap=\"okClick\">确定提交</view>\n  </view>\n\n  <toast hidden=\"{{toastHidden}}\">{{toastTxt}}</toast>\n\n</view>"
  },
  {
    "path": "pages/order/order.wxss",
    "content": "\n.table{\n    padding: 40px 0px 0px 30px;\n}\n.table-num{\n    margin-right: 20px;\n    font-size: 16px;\n    margin-top: 4px;\n    float: left;\n}\n.table-input{\n    width: 50px;\n    height: 25px;\n    display: inline-block;\n    border: 1px solid #ccc;\n    vertical-align: middle;\n    text-align: center;\n    line-height: 25px;\n    font-size: 14px;\n}\n.table-arrow{\n    width: 30px;\n    height: 25px;\n    margin-left: -1px;\n    display: inline-block;\n    border: 1px solid #ccc;\n    text-align: center;\n    line-height: 35px;\n    vertical-align: middle;\n}\n\n.beizhu{\n    padding: 30px 40px 40px 30px;\n    border-bottom: 1px solid #aaa;\n}\n.beizhu-txt{\n    margin-top: 4px;\n    margin-right: 20px;\n    font-size: 16px;\n    float: left;\n}\n.beizhu-input{\n    padding-left: 10px;\n    border: 1px solid #ccc;\n    font-size: 14px;\n}\n\n.order{\n    padding: 20px 20px 48px 30px;\n}\n.cost{\n    font-size: 16px;\n    margin-bottom: 20px;\n}\n.cost span{\n    color: #ff2d2d;\n    margin-left: 12px; \n}\n.orderlist{\n    margin-top: 20px;\n}\n.orderlist span{\n    font-size: 15px;\n}\n\n.bt-area{\n    position: fixed;\n    bottom: 0px;\n    left: 0px;\n    right: 0px;\n\n}\n.bt-return{\n    width: 50%;\n    height: 48px;\n    display: inline-block;\n    background-color: #eee;\n    text-align: center;\n    line-height: 48px;\n    font-size: 20px;\n}\n.bt-ok{\n    width: 50%;\n    height: 48px;\n    display: inline-block;\n    background-color: #09bb07;\n    text-align: center;\n    line-height: 48px;\n    font-size: 20px;\n    color: #fff;\n}"
  },
  {
    "path": "utils/util.js",
    "content": "function formatTime(date) {\n  var year = date.getFullYear()\n  var month = date.getMonth() + 1\n  var day = date.getDate()\n\n  var hour = date.getHours()\n  var minute = date.getMinutes()\n  var second = date.getSeconds()\n\n\n  return [year, month, day].map(formatNumber).join('/') + ' ' + [hour, minute, second].map(formatNumber).join(':')\n}\n\nfunction formatNumber(n) {\n  n = n.toString()\n  return n[1] ? n : '0' + n\n}\n\nmodule.exports = {\n  formatTime: formatTime\n}\n"
  }
]