瀏覽代碼

首次提交

1004749546@qq.com 4 年之前
當前提交
763ddb9000
共有 100 個文件被更改,包括 6491 次插入0 次删除
  1. 4 0
      .gitignore
  2. 11 0
      .hbuilderx/launch.json
  3. 53 0
      App.vue
  4. 10 0
      api/banner.js
  5. 46 0
      api/coupon.js
  6. 37 0
      api/data.js
  7. 10 0
      api/deliveryRecord.js
  8. 27 0
      api/goods.js
  9. 42 0
      api/login.js
  10. 70 0
      api/luckyDraw.js
  11. 116 0
      api/order.js
  12. 35 0
      api/receiveAddress.js
  13. 36 0
      api/shoppingCart.js
  14. 24 0
      api/station.js
  15. 42 0
      api/store.js
  16. 62 0
      api/user.js
  17. 443 0
      components/address.vue
  18. 721 0
      components/almost-lottery/almost-lottery.vue
  19. 96 0
      components/uni-icons/icons.js
  20. 10 0
      components/uni-icons/uni-icons.vue
  21. 178 0
      components/uni-numbers-box/uni-numbers-box.vue
  22. 122 0
      components/uni-popup/image-txt-popup.vue
  23. 22 0
      components/uni-popup/message.js
  24. 96 0
      components/uni-popup/popup-con.vue
  25. 25 0
      components/uni-popup/popup.js
  26. 243 0
      components/uni-popup/uni-popup-dialog.vue
  27. 116 0
      components/uni-popup/uni-popup-message.vue
  28. 165 0
      components/uni-popup/uni-popup-share.vue
  29. 295 0
      components/uni-popup/uni-popup.vue
  30. 279 0
      components/uni-transition/uni-transition.vue
  31. 82 0
      libs/axios.js
  32. 85 0
      libs/getDate.js
  33. 29 0
      libs/tools.js
  34. 5 0
      libs/validate.js
  35. 17 0
      main.js
  36. 85 0
      manifest.json
  37. 56 0
      package-lock.json
  38. 23 0
      package.json
  39. 127 0
      pages.json
  40. 223 0
      pages/agreement/agreement.vue
  41. 174 0
      pages/index/index.vue
  42. 85 0
      pages/login/login.scss
  43. 199 0
      pages/login/login.vue
  44. 212 0
      pages/login/phoneLogin.vue
  45. 71 0
      pages/userCenter/aboutUs.vue
  46. 166 0
      pages/userCenter/deliveryRecord.vue
  47. 40 0
      pages/userCenter/fenlei.vue
  48. 194 0
      pages/userCenter/index.vue
  49. 254 0
      pages/userCenter/ldDetailed.vue
  50. 63 0
      pages/userCenter/myCode/myCode.vue
  51. 70 0
      pages/userCenter/question.vue
  52. 102 0
      pages/userCenter/userInfo/changePayPwd.vue
  53. 175 0
      pages/userCenter/userInfo/paymentPwd.vue
  54. 210 0
      pages/userCenter/userInfo/smsVerification.vue
  55. 139 0
      pages/userCenter/userInfo/userInfo.vue
  56. 169 0
      pages/userCenter/zhinan.vue
  57. 二進制
      static/CLOTHES.png
  58. 二進制
      static/FIRST_DELIVERY.png
  59. 二進制
      static/FIRST_ONLINE_EXCHANGE.png
  60. 二進制
      static/GLASS.png
  61. 二進制
      static/HARM.png
  62. 二進制
      static/METAL.png
  63. 二進制
      static/MET_PLA.png
  64. 二進制
      static/NEW_CUSTOMER.png
  65. 二進制
      static/NEW_FIRST_DELIVER.png
  66. 二進制
      static/OLD_FIRST_DELIVER.png
  67. 二進制
      static/PAPER.png
  68. 二進制
      static/PLASTIC.png
  69. 二進制
      static/RECYCLING.png
  70. 二進制
      static/SHARE_FRIEND.png
  71. 二進制
      static/add-cart.png
  72. 二進制
      static/addCart.png
  73. 二進制
      static/almost-lottery/almost-lottery__action.png
  74. 二進制
      static/almost-lottery/almost-lottery__action2x.png
  75. 二進制
      static/almost-lottery/almost-lottery__action3x.png
  76. 二進制
      static/almost-lottery/almost-lottery__bg.png
  77. 二進制
      static/almost-lottery/almost-lottery__bg2x.png
  78. 二進制
      static/almost-lottery/almost-lottery__bg3x.png
  79. 二進制
      static/bued.png
  80. 二進制
      static/cart.png
  81. 二進制
      static/checked.png
  82. 二進制
      static/closeLottery.png
  83. 二進制
      static/dz-icon.png
  84. 二進制
      static/edit.png
  85. 二進制
      static/full-bg.png
  86. 二進制
      static/giftcard/bg01.jpg
  87. 二進制
      static/giftcard/bg02.jpg
  88. 二進制
      static/giftcard/input-bg.png
  89. 二進制
      static/giftcard/leaf-left-pic.png
  90. 二進制
      static/giftcard/leaf-right-pic.png
  91. 二進制
      static/giftcard/module-footer-bg.png
  92. 二進制
      static/giftcard/module-header-bg.png
  93. 二進制
      static/giftcard/pop-bg.png
  94. 二進制
      static/giftcard/red-envelopes.png
  95. 二進制
      static/giftcard/tit-left.png
  96. 二進制
      static/giftcard/tit-right.png
  97. 二進制
      static/hexiao.png
  98. 二進制
      static/imgError.png
  99. 二進制
      static/ledou.png
  100. 二進制
      static/liucheng.png

+ 4 - 0
.gitignore

@@ -0,0 +1,4 @@
+node_modules/
+.project
+unpackage/
+.DS_Store

+ 11 - 0
.hbuilderx/launch.json

@@ -0,0 +1,11 @@
+{ // launch.json 配置了启动调试时相关设置,configurations下节点名称可为 app-plus/h5/mp-weixin/mp-baidu/mp-alipay/mp-qq/mp-toutiao/mp-360/
+  // launchtype项可配置值为local或remote, local代表前端连本地云函数,remote代表前端连云端云函数
+    "version": "0.0",
+    "configurations": [{
+            "type": "uniCloud",
+            "default": {
+                "launchtype": "remote"
+            }
+        }
+    ]
+}

+ 53 - 0
App.vue

@@ -0,0 +1,53 @@
+<script>
+	const uat_domain = 'https://lese.test.sxzxyj.net' // 预发布
+	// const uat_domain = 'http://192.168.16.220:8302' // 本地
+	const pro_domain = 'https://lese.sxzxyj.net' // 生产
+	const uat_URL = uat_domain+'/gc-shop/' // 预发布
+	const pro_URL = pro_domain+'/gc-shop/'  // 生产
+	const buildType = 0 // 打包环境对应类型,1 生产 0 预发布
+	const buildURL = buildType ? pro_URL : uat_URL // 打包后实际对应的url
+	const envText = ['预发布环境','生产环境']
+	const envTips = process.env.NODE_ENV == 'development' ? envText[0] : envText[buildType]
+	console.log(process.env.NODE_ENV)
+	export default {
+		globalData: {  
+			baseDomain: process.env.NODE_ENV == 'development' ? uat_domain : pro_domain,
+			baseUrl: process.env.NODE_ENV == 'development' ? uat_URL : buildURL,
+			token: '',
+			version: '', // 当前版本号
+			buildType: buildType,
+			envTips: envTips
+		},
+		mounted() {
+			// 非V3模式
+			if(this.$scope){
+				this.$scope.globalData.token = this.$store.state.vuex_token
+			}else{
+				// V3模式
+				getApp({allowDefault: true}).globalData.token = this.$store.state.vuex_token
+			}
+		},
+		onLaunch: function() {
+			 console.log('App onLaunch')
+		},
+		onShow: function() {
+			console.log('App Show')
+		},
+		onHide: function() {
+			console.log('App Hide')
+		},
+	}
+</script>
+
+<style lang="scss">
+	@import "uview-ui/index.scss";
+	/*每个页面公共css */
+	page {
+		min-height: 100%;
+		display: flex;
+		background-color: #F8F8F8;
+	}
+	.content {
+		padding: 20upx;
+	}
+</style>

+ 10 - 0
api/banner.js

@@ -0,0 +1,10 @@
+import axios from '@/libs/axios.js';
+
+// 轮播图
+export const bannerList = params => {
+  return axios.request({
+    url: `advert/findByType/${params.type}/${params.location}`,
+    method: 'get',
+	data: {}
+  })
+};

+ 46 - 0
api/coupon.js

@@ -0,0 +1,46 @@
+import axios from '@/libs/axios.js';
+// 确认领取优惠券
+export const receivesCoupon = (params) => {
+	return axios.request({
+		url: `couponReceives/receivesCoupon`,
+		method: 'post',
+		data: params
+	})
+}
+
+// 根据券码查询优惠券信息
+export const couponData = (params) => {
+	return axios.request({
+		url: `coupon/findByCode/${params.couponCode}`,
+		method: 'get'
+	})
+}
+
+// 优惠券领取明细查询
+export const couponListFind = (params) => {
+	let url = `couponReceives/findPage/${params.pageNo}/${params.pageSize}`
+	delete params.pageNo
+	delete params.pageSize
+	return axios.request({
+		url: url,
+		method: 'post',
+		data: params
+	})
+}
+// 订单可用优惠券查询
+export const couponEnable = (params) => {
+	return axios.request({
+		url: 'couponReceives/findUsableCoupon',
+		method: 'post',
+		data: params
+	})
+}
+
+//  礼品卡   立即充值
+export const giftCardUseCard = (params) => {
+	return axios.request({
+		url: `giftCard/useCard/${params.sequence}`,
+		method: 'post',
+		data: {}
+	})
+}

+ 37 - 0
api/data.js

@@ -0,0 +1,37 @@
+import axios from '@/libs/axios.js';
+// 根据字典code查询选项
+export const getLookUpDatas = (params) => {
+  return axios.request({
+    url: `lookup/findByLookup/${params.type}`,
+    method: 'get'
+  })
+}
+// 开箱操作
+export const openDevice = (params) => {
+  return axios.request({
+    url: `device/ctl/open`,
+    method: 'post',
+	data: params
+  })
+}
+//查询省
+export const  getProvince = () => {
+  return axios.request({
+    url: `area/PROVINCE`,
+    method: 'get'
+  })
+}
+//查询市
+export const  getCityByPro = params => {
+  return axios.request({
+    url: `area/CITY/${params.id}`,
+    method: 'get'
+  })
+}
+//查询区
+export const  getDistrictByCity = params => {
+  return axios.request({
+    url: `area/DISTRICT/${params.id}`,
+    method: 'get'
+  })
+}

+ 10 - 0
api/deliveryRecord.js

@@ -0,0 +1,10 @@
+import axios from '@/libs/axios.js';
+
+// 列表
+export const getDeliveryLogList = params => {
+  return axios.request({
+    url: `deliveryLog/query/${params.pageNo}/${params.pageSize}`,
+    method: 'post',
+	data: params
+  })
+};

+ 27 - 0
api/goods.js

@@ -0,0 +1,27 @@
+import axios from '@/libs/axios.js';
+
+// 获取商品分类
+export const getGoodsClass = params => {
+  return axios.request({
+    url: `goodsType/queryList`,
+    method: 'post',
+	data: params
+  })
+};
+
+// 获取商品列表
+export const getGoodsList = params => {
+  return axios.request({
+    url: `goods/query/${params.pageNo}/${params.pageSize}`,
+    method: 'post',
+	data: params
+  })
+};
+
+// 获取商品详情
+export const getGoodsDetail = params => {
+  return axios.request({
+    url: `goods/findById/${params.id}`,
+    method: 'get',
+  })
+};

+ 42 - 0
api/login.js

@@ -0,0 +1,42 @@
+import axios from '@/libs/axios.js';
+
+export const login = params => {
+  return axios.request({
+    url: `login`,
+    method: 'post',
+	data: params
+  },true)
+};
+export const logout = params => {
+  return axios.request({
+    url: `logout`,
+    data: params,
+    method: 'get'
+  })
+};
+
+// 验证是否登录过
+export const checkLogin = () => {
+  let token = getApp().globalData.token
+  if (token&&token!='') {
+    return axios.request({
+      url: `checkToken`,
+      method: 'get'
+    })
+  } else {
+    return new Promise(resolve => {
+      resolve({
+        status: '9001',
+        message: '系统未登录'
+      });
+    });
+  }
+};
+
+// 获取登录用户信息
+export const getUserInfo = params => {
+  return axios.request({
+    url: `customer/detail`,
+    method: 'get'
+  })
+};

+ 70 - 0
api/luckyDraw.js

@@ -0,0 +1,70 @@
+import axios from '@/libs/axios.js';
+// 获取是否有转盘
+export const findEnable = params => {
+  return axios.request({
+    url: `luckyDraw/findEnable`,
+    method: 'get',
+  })
+};
+// 抽奖takeOut
+export const takeOut = params => {
+  return axios.request({
+    url: `luckyDraw/takeOut/${params.id}`,
+    method: 'get',
+  })
+};
+// 获取免费抽奖次数
+export const findCustomerTimes = params => {
+  return axios.request({
+    url: `luckyDrawCust/findCustomerTimes/${params.id}`,
+    method: 'get',
+  })
+};
+// 获取任务列表
+export const findCustomerTask = params => {
+  return axios.request({
+    url: `luckyDrawCust/findCustomerTask/${params.id}`,
+    method: 'get',
+  })
+};
+// 购买抽奖次数
+export const buyDrawTimes = params => {
+  return axios.request({
+    url: `luckyDrawCust/buyDrawTimes`,
+    method: 'post',
+	data: params
+  })
+};
+// 获取已中奖列表
+export const findWinPrize = params => {
+  return axios.request({
+    url: `luckyDrawCust/findWinPrize`,
+    method: 'get',
+  })
+};
+
+// 领奖
+export const receive = params => {
+  return axios.request({
+    url: `luckyDrawCust/receive`,
+    method: 'post',
+	data: params
+  })
+};
+
+// 获奖记录
+export const queryWinRecord = params => {
+  return axios.request({
+    url: `luckyDraw/queryWinRecord/${params.pageNo}/${params.pageSize}`,
+    method: 'post',
+	data: params
+  })
+};
+
+// 抽奖任务
+export const doTask = params => {
+  return axios.request({
+    url: `luckyDrawCust/doTask/${params.luckyDrawNo}/${params.taskCode}`,
+    method: 'get',
+  })
+};

+ 116 - 0
api/order.js

@@ -0,0 +1,116 @@
+import axios from '@/libs/axios.js';
+
+// 获取网点下的服务项目
+export const getStoreItems = params => {
+  return axios.request({
+    url: `item/storeItems/?storeId=${params.storeId}`,
+    method: 'get',
+  })
+};
+// 查看用户是否设置过支付密码
+export const existPayPwd = params => {
+  return axios.request({
+    url: `customer/existPayPwd`,
+    method: 'get',
+  })
+};
+// 校验短信验证码是否正确
+export const checkCode = params => {
+  return axios.request({
+    url: `customer/checkVerificationCode`,
+	data: params,
+    method: 'post',
+  })
+};
+// 设置支付密码
+export const setPayPwd = params => {
+  return axios.request({
+    url: `customer/setPayPassword`,
+	data: params,
+    method: 'post',
+  })
+};
+// 校验原密码是否正确
+export const checkPayPwd = params => {
+  return axios.request({
+    url: `customer/checkPayPwd`,
+	data: params,
+    method: 'post',
+  })
+};
+
+// 修改支付密码
+export const changePayPwd = params => {
+  return axios.request({
+    url: `customer/updatePayPwd`,
+	data: params,
+    method: 'post',
+  })
+};
+
+// 下单 保存订单
+export const saveOrder = params => {
+  return axios.request({
+    url: `order/addOrder`,
+    method: 'post',
+	data: params
+  })
+};
+
+// 下单 预支付
+export const signPay  = params => {
+  return axios.request({
+    url: `order/payOrder`,
+    method: 'post',
+    data: params
+  })
+};
+
+// 我的订单
+export const getOrderList = params => {
+	let url = `order/queryPage/${params.pageNo}/${params.pageSize}`
+	delete params.pageNo
+	delete params.pageSize
+	return axios.request({
+		url: url,
+		method: 'post',
+		data: params
+	})
+};
+// 订单详情
+export const orderDetail = params => {
+  return axios.request({
+    url: `order/findById/${params.id}`,
+    method: 'get',
+  })
+};
+
+// 查询物流
+export const queryOrderExpress = params => {
+	return axios.request({
+	  url: `orderExpress/findByOrderNo/${params.orderSn}`,
+	  method: 'get',
+	})
+}
+// 确认收货
+export const finishOrder = params => {
+	return axios.request({
+	  url: `orderExpress/finishOrderExpress`,
+	  method: 'post',
+	  data: params
+	})
+}
+// 查物流下商品信息
+export const queryOrderGoods = params => {
+	return axios.request({
+	  url: `orderGoods/findByExpressNo/${params.id}`,
+	  method: 'get',
+	})
+}
+// 取消订单
+export const cancleOrder = params => {
+	return axios.request({
+	  url: `order/cancel/${params.id}`,
+	  method: 'get',
+	})
+}

+ 35 - 0
api/receiveAddress.js

@@ -0,0 +1,35 @@
+import axios from '@/libs/axios.js';
+
+// 根据客户编号查找收货地址
+export const findAddressBycustomerNo = params => {
+  return axios.request({
+    url: `receiveaddress/findBycustomerNo`,
+    method: 'get'
+  })
+};
+
+// 收货地址删除
+export const deleteAddress = params => {
+  return axios.request({
+    url: `receiveaddress/delete/${params.id}`,
+    method: 'get'
+  })
+};
+
+// 收货地址列表查询(不分页)
+export const getAddressList = params => {
+  return axios.request({
+    url: `receiveaddress/querylist`,
+    method: 'post',
+	data: params
+  })
+};
+
+// 收货地址保存
+export const saveAddress = params => {
+  return axios.request({
+    url: `receiveaddress/save`,
+    method: 'post',
+	data: params
+  })
+};

+ 36 - 0
api/shoppingCart.js

@@ -0,0 +1,36 @@
+import axios from '@/libs/axios.js';
+
+// 添加商品到购物车
+export const addGoodsToCart = params => {
+  return axios.request({
+    url: `shoppingCart/addGoods`,
+    method: 'post',
+	data: params
+  })
+};
+
+// 从购物车删除商品
+export const deleteGoodsFormCart = params => {
+  return axios.request({
+    url: `shoppingCart/deleteGoods`,
+    method: 'post',
+	data: params
+  })
+};
+
+// 查询购物车列表
+export const getCartList = params => {
+  return axios.request({
+    url: `shoppingCart/queryList`,
+    method: 'post',
+	data: params
+  })
+};
+
+// 更新商品购买数量
+export const updateGoodsBuyQty = params => {
+  return axios.request({
+    url: `shoppingCart/updateGoodsBuyQty/${params.id}/${params.buyQty}`,
+    method: 'get'
+  })
+};

+ 24 - 0
api/station.js

@@ -0,0 +1,24 @@
+import axios from '@/libs/axios.js';
+
+// 获取首页网点列表
+export const stationList = params => {
+  return axios.request({
+    url: `station/query`,
+    method: 'post',
+	data: params
+  })
+}
+// 网点详情
+export const stationDetail = params => {
+  return axios.request({
+    url: `station/findById/${params.id}`,
+    method: 'get'
+  })
+}
+// 查询网点的设备列表
+export const stationDevice = params => {
+  return axios.request({
+    url: `device/findById/${params.stationNo}`,
+    method: 'get'
+  })
+}

+ 42 - 0
api/store.js

@@ -0,0 +1,42 @@
+import axios from '@/libs/axios.js';
+
+// 获取首页网点列表
+export const getMapStoreList = params => {
+  return axios.request({
+    url: `store/findList`,
+    method: 'post',
+	data: params
+  },true)
+};
+
+// 获取网点列表
+export const getStoreList = params => {
+  return axios.request({
+    url: `store/findSortList`,
+    method: 'post',
+	data: params
+  })
+};
+// 获取网点详情
+export const getStoreDetail = params => {
+  return axios.request({
+    url: `store/findDetail`,
+    method: 'post',
+	data: params
+  })
+};
+// 获取当前排队情况照片
+export const getWaitPhoto = params => {
+  return axios.request({
+    url: `device/image/${params.deviceNo}`,
+    method: 'get'
+  })
+};
+
+// 查看网点营业状态
+export const getStoreStatus = params => {
+  return axios.request({
+    url: `store/validateStoreAndDevice?storeId=${params.storeId}&deviceNo=${params.deviceNo}`,
+    method: 'get'
+  })
+};

+ 62 - 0
api/user.js

@@ -0,0 +1,62 @@
+import axios from '@/libs/axios.js'
+ 
+// 获取图片验证码
+export const getCaptcha = randomCode => {
+  return axios.request({
+    url: `captcha/create/${randomCode}`,
+    method: 'get',
+    responseType: 'blob',
+  }, true)
+}
+// 发送短信验证码
+export const sendVerifyCode = params => {
+  return axios.request({
+    url: 'sendVerifiCode',
+    method: 'post',
+    data: params
+  }, true)
+}
+export const loginPhone = params => {
+  return axios.request({
+    url: `loginPhone`,
+    method: 'post',
+	data: params
+  },true)
+};
+// 乐豆使用明细
+export const ldUsedQuery = params => { 
+  let url = `customerGoldLog/queryByMonth/${params.pageNo}/${params.pageSize}`
+  delete params.pageNo
+  delete params.pageSize
+  return axios.request({
+    url: url,
+    data: params,
+    method: 'post'
+  })
+}
+
+// 核销
+export const sellerReceive = params => { 
+  let url = `seller/receive`
+  return axios.request({
+    url: url,
+    data: params,
+    method: 'post'
+  })
+}
+// 查询销售商信息
+export const sellerFindByPartnerNo = params => { 
+  let url = `seller/findByPartnerNo?encryptedPartnerNo=${params.officialPartnerNo}`
+  return axios.request({
+    url: url,
+    method: 'get'
+  })
+}
+// 手机号登录 校验用户账号状态
+export const checkCustomerState = params => { 
+  let url = `checkCustomerState/${params.phone}`
+  return axios.request({
+    url: url,
+    method: 'get'
+  })
+}

+ 443 - 0
components/address.vue

@@ -0,0 +1,443 @@
+<template>
+	<view class="simple-address" v-if="showPopup">
+		<!-- 遮罩层 -->
+		<view
+			class="simple-address-mask"
+			v-if="maskClick"
+			:class="[ani + '-mask', animation ? 'mask-ani' : '']"
+			:style="{
+				'background-color': maskBgColor
+			}"
+			@tap="hideMask(true)"
+		></view>
+		
+		<!-- 省市区 -->
+		<view class="simple-address-content simple-address--fixed" :class="[type, ani + '-content', animation ? 'content-ani' : '']">
+			<view class="simple-address__header">
+				<view class="simple-address__header-btn-box" @click="pickerCancel">
+					<text class="simple-address__header-text" :style="{ color: cancelColor, fontSize: btnFontSize }">取消</text>
+				</view>
+				<view class="simple-address__header-btn-box" @click="pickerConfirm">
+					<text class="simple-address__header-text" :style="{ color: confirmColor, fontSize: btnFontSize }">确定</text>
+				</view>
+			</view>
+			<view class="simple-address__box">
+				<picker-view class="simple-address-view" :value="pickerValue" @change="pickerChange">
+					<picker-view-column>
+						<!-- #ifndef APP-NVUE -->
+						<view class="picker-item" :style="{ fontSize: fontSize }" v-for="(item, index) in provinceDataList" :key="index">{{ item.name }}</view>
+						<!-- #endif -->
+						<!-- #ifdef APP-NVUE -->
+						<text class="picker-item" :style="{ fontSize: fontSize }" v-for="(item, index) in provinceDataList" :key="index">{{ item.name }}</text>
+						<!-- #endif -->
+					</picker-view-column>
+					<picker-view-column>
+						<!-- #ifndef APP-NVUE -->
+						<view class="picker-item" :style="{ fontSize: fontSize }" v-for="(item, index) in cityDataList" :key="index">{{ item.name }}</view>
+						<!-- #endif -->
+						<!-- #ifdef APP-NVUE -->
+						<text class="picker-item" :style="{ fontSize: fontSize }" v-for="(item, index) in cityDataList" :key="index">{{ item.name }}</text>
+						<!-- #endif -->
+					</picker-view-column>
+					<picker-view-column>
+						<!-- #ifndef APP-NVUE -->
+						<view class="picker-item" :style="{ fontSize: fontSize }" v-for="(item, index) in areaDataList" :key="index">{{ item.name }}</view>
+						<!-- #endif -->
+						<!-- #ifdef APP-NVUE -->
+						<text class="picker-item" :style="{ fontSize: fontSize }" v-for="(item, index) in areaDataList" :key="index">{{ item.name }}</text>
+						<!-- #endif -->
+					</picker-view-column>
+				</picker-view>
+			</view>
+		</view>
+		
+	</view>
+</template>
+
+<script>
+	import { getProvince, getCityByPro, getDistrictByCity } from '@/api/data.js'
+	export default{
+		name: 'Address',
+		props: {
+			// 是否允许点击遮罩层关闭
+			maskClick: {
+				type: Boolean,
+				default: true
+			},
+			// 是否开启动画
+			animation: {
+				type: Boolean,
+				default: true
+			},
+			//  遮罩层背景颜色
+			maskBgColor: {
+				type: String,
+				default: 'rgba(0, 0, 0, 0.4)' //背景颜色 rgba(0, 0, 0, 0.4) 为空则调用 uni.scss
+			},
+			/* 弹出层类型,可选值;  暂时只支持底部弹出
+					bottom:底部弹出层
+				*/
+			type: {
+				type: String,
+				default: 'bottom'
+			},
+			cancelColor: {
+				type: String,
+				default: '' // 取消按钮颜色
+			},
+			confirmColor: {
+				type: String,
+				default: '' // 确认按钮颜色
+			},
+			fontSize: {
+				type: String,
+				default: '28rpx' // picker-item字体大小
+			},
+			btnFontSize: {
+				type: String,
+				default: '' // 按钮的字体大小
+			},
+		},
+		data(){
+			return{
+				showPopup: false,  //  是否显示省市区选择
+				ani: '',
+				pickerValue: [0, 0, 0],  //  默认值下标
+				pickerId: [],  //  省市区默认值id
+				provinceDataList: [],  //  省数据列表
+				cityDataList: [],  //  市数据列表
+				areaDataList: [],  //  区数据列表
+				isFirst: false,  //  是否首次进入组件
+			}
+		},
+		methods: {
+			//  初始化
+			init(){
+				this.provinceDataList = []
+				this.cityDataList = []
+				this.areaDataList = []
+				this.isFirst = false
+			},
+			//  点击遮罩层关闭选择省市区
+			hideMask() {
+				this._$emit('onCancel')
+				this.close()
+			},
+			//  打开省市区picker
+			open(idArr) {
+				//  初始化数据
+				this.init()
+				this.pickerId = idArr
+				//  idArr 省市区默认id
+				if(this.pickerId && this.pickerId.length > 0){
+					//  获取省下拉
+					this.getProvinceList(idArr)
+				}else{
+					//  获取省下拉
+					this.getProvinceList()
+				}
+				this.showPopup = true
+				this.$nextTick(() => {
+					setTimeout(() => {
+						this.ani = 'simple-' + this.type
+					}, 100)
+				});
+			},
+			//  关闭省市区picker
+			close(type) {
+				if (!this.maskClick && type) return
+				this.ani = ''
+				this.$nextTick(() => {
+					setTimeout(() => {
+						this.showPopup = false
+					}, 300)
+				})
+			},
+			//  取消按钮
+			pickerCancel() {
+				this._$emit('onCancel')
+				this.close()
+			},
+			//  确定按钮
+			pickerConfirm() {
+				this._$emit('onConfirm')
+				if(!this.isFirst){
+					//  首次
+					this.isFirst = true
+				}
+				this.close()
+			},
+			//  省市区  change
+			pickerChange(e) {
+				const _this = this
+				let changePickerValue = e.detail.value
+				
+				if (this.pickerValue[0] !== changePickerValue[0]) {
+					if(this.pickerId && this.pickerId.length > 0 && this.isFirst){
+						//  获取市下拉
+						this.getCityList(this.pickerId[0], this.pickerId)
+					}else{
+						// 第一级发生滚动,省 change重置市区下拉数据
+						this.getCityList(this.provinceDataList[changePickerValue[0]].id)
+						changePickerValue[1] = 0
+						changePickerValue[2] = 0
+					}
+				} else if (this.pickerValue[1] !== changePickerValue[1]) {
+					if(this.pickerId && this.pickerId.length > 0 && this.isFirst){
+						//  获取市下拉
+						this.getAreaList(this.pickerId[1], this.pickerId)
+					}else{
+						// 第二级滚动,市 change重置区县下拉数据
+						this.getAreaList(this.cityDataList[changePickerValue[1]].id)
+						changePickerValue[2] = 0
+					}
+				}
+				this.pickerValue = changePickerValue
+				//  setTimeout解决由于省市区接口请求数据有延缓导致获值报错
+				setTimeout(()=>{
+					_this._$emit('onChange')
+				},1000)
+			},
+			_$emit(emitName) {
+				let pickObj = {
+					label: this._getLabel(),
+					value: this.pickerValue,
+					cityCode: this._getCityCode(),
+					areaCode: this._getAreaCode(),
+					provinceCode: this._getProvinceCode(),
+					labelArr: this._getLabel().split('-')
+				}
+				this.$emit(emitName, pickObj)
+			},
+			_getLabel() {
+				//  name报错未发现页面影响(由于省市区接口请求数据延缓)
+				let pcikerLabel =
+					this.provinceDataList[this.pickerValue[0]].name + '-' + this.cityDataList[this.pickerValue[1]].name + '-' + this.areaDataList[this.pickerValue[2]].name
+				return pcikerLabel
+			},
+			_getCityCode() {
+				return this.cityDataList[this.pickerValue[1]].id
+			},
+			_getProvinceCode() {
+				return this.provinceDataList[this.pickerValue[0]].id
+			},
+			_getAreaCode() {
+				return this.areaDataList[this.pickerValue[2]].id
+			},
+			//  获取省下拉数据
+			getProvinceList(idArr){
+				const _this = this
+				getProvince().then(res => {
+				  if (res.code == 200 || res.status == 204 || res.status == 200) {
+				    _this.provinceDataList = res.data || []  //  省下拉数据
+					//  判断是否有默认值
+					if(idArr){
+						_this.getCityList(idArr[0], idArr)
+						_this.getAreaIndArr(idArr)
+					}else{
+						//  默认省数据第一条下的市下拉数据
+						_this.getCityList(_this.provinceDataList[0].id)
+					}
+					
+				  } else {
+				    _this.provinceDataList = []
+				  }
+				})
+			},
+			// 获取城市列表
+			getCityList (val, idArr) {
+				const _this = this
+				_this.cityDataList = []
+				_this.areaDataList = []
+				if (val != null && val != '') {
+					getCityByPro({id: val}).then(res => {
+						if (res.code == 200 || res.status == 204 || res.status == 200) {
+							_this.cityDataList = res.data || []  //  市下拉数据
+							//  判断是否有默认值
+							if(idArr){
+								_this.getAreaList(idArr[1], idArr)
+								_this.getAreaIndArr(idArr)
+							}else{
+								//  默认市数据第一条下的区县下拉数据
+								_this.getAreaList(_this.cityDataList[0].id)
+							}
+						} else {
+							_this.cityDataList = []
+						}
+					})
+				}
+			},
+			// 获取区县列表
+			getAreaList (val, idArr) {
+				const _this = this
+				this.areaDataList = []
+				if (val != null && val != '') {
+					getDistrictByCity({id: val}).then(res => {
+						if (res.code == 200 || res.status == 204 || res.status == 200) {
+							_this.areaDataList = res.data || []
+							//  判断是否有默认值
+							if(idArr){ _this.getAreaIndArr(idArr) }
+						} else {
+							_this.areaDataList = []
+						}
+					})
+				}
+			},
+			//  根据id 获取省市区 下标值
+			getAreaIndArr(idArr){
+				const _this = this
+				const provinceInd = _this.provinceDataList.findIndex(res => res['id'] == idArr[0])
+				const cityInd = _this.cityDataList.findIndex(res => res['id'] == idArr[1])
+				const districtInd = _this.areaDataList.findIndex(res => res['id'] == idArr[2])
+				setTimeout(()=>{
+					_this.pickerValue = [provinceInd, cityInd, districtInd]
+				},100)
+				// console.log(_this.pickerValue,'_this.pickerValue')
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+.simple-address {
+	/* #ifndef APP-NVUE */
+	display: flex;
+	/* #endif */
+	flex-direction: column;
+}
+
+.simple-address-mask {
+	position: fixed;
+	bottom: 0;
+	top: 0;
+	left: 0;
+	right: 0;
+
+	transition-property: opacity;
+	transition-duration: 0.3s;
+	opacity: 0;
+	/* #ifndef APP-NVUE */
+	z-index: 99999;
+	/* #endif */
+}
+
+.mask-ani {
+	transition-property: opacity;
+	transition-duration: 0.2s;
+}
+
+.simple-bottom-mask {
+	opacity: 1;
+}
+
+.simple-center-mask {
+	opacity: 1;
+}
+
+.simple-address--fixed {
+	position: fixed;
+	bottom: 0;
+	left: 0;
+	right: 0;
+	width: 100%;
+	transition-property: transform;
+	transition-duration: 0.3s;
+	transform: translateY(460rpx);
+	/* #ifndef APP-NVUE */
+	z-index: 999999;
+	/* #endif */
+}
+
+.simple-address-content {
+	background-color: #ffffff;
+}
+
+.simple-content-bottom {
+	bottom: 0;
+	left: 0;
+	right: 0;
+	transform: translateY(500rpx);
+}
+
+.content-ani {
+	transition-property: transform, opacity;
+	transition-duration: 0.2s;
+}
+
+.simple-bottom-content {
+	transform: translateY(0);
+}
+
+.simple-center-content {
+	transform: scale(1);
+	opacity: 1;
+}
+
+.simple-address__header {
+	position: relative;
+	/* #ifndef APP-NVUE */
+	display: flex;
+	/* #endif */
+	flex-direction: row;
+	flex-wrap: nowrap;
+	justify-content: space-between;
+	border-bottom-color: #f2f2f2;
+	border-bottom-style: solid;
+	border-bottom-width: 1rpx;
+}
+
+.simple-address--fixed-top {
+	/* #ifndef APP-NVUE */
+	display: flex;
+	/* #endif */
+	flex-direction: row;
+	justify-content: space-between;
+	border-top-color: $uni-border-color;
+	border-top-style: solid;
+	border-top-width: 1rpx;
+}
+
+.simple-address__header-btn-box {
+	/* #ifndef APP-NVUE */
+	display: flex;
+	/* #endif */
+	flex-direction: row;
+	align-items: center;
+	justify-content: center;
+	height: 70rpx;
+}
+
+.simple-address__header-text {
+	text-align: center;
+	font-size: $uni-font-size-base;
+	color: #1aad19;
+	line-height: 70rpx;
+	padding-left: 40rpx;
+	padding-right: 40rpx;
+}
+
+.simple-address__box {
+	position: relative;
+}
+
+.simple-address-view {
+	position: relative;
+	bottom: 0;
+	left: 0;
+	/* #ifndef APP-NVUE */
+	width: 100%;
+	/* #endif */
+	/* #ifdef APP-NVUE */
+	width: 750rpx;
+	/* #endif */
+	height: 408rpx;
+	background-color: rgba(255, 255, 255, 1);
+}
+
+.picker-item {
+	text-align: center;
+	line-height: 70rpx;
+	text-overflow: ellipsis;
+	font-size: 28rpx;
+}
+</style>

+ 721 - 0
components/almost-lottery/almost-lottery.vue

@@ -0,0 +1,721 @@
+<template>
+  <view class="almost-lottery" :style="{ width: canvasWidth + 40 + 'px', height: canvasHeight + 40 + 'px'}">
+    <view class="almost-lottery__wrap" :style="{width: canvasWidth + canvasMarginTotal + 'px', height: canvasHeight + canvasMarginTotal + 'px'}">
+      <!-- #ifdef MP-ALIPAY -->
+      <canvas :class="className" :id="canvasId" :width="canvasWidth" :height="canvasHeight" :style="{
+         width: canvasWidth + 'px',
+         height: canvasHeight + 'px'
+       }" />
+      <!-- #endif -->
+      <!-- #ifndef MP-ALIPAY -->
+      <canvas :class="className" :canvas-id="canvasId" :width="canvasWidth" :height="canvasHeight" :style="{
+         width: canvasWidth + 'px',
+         height: canvasHeight + 'px'
+       }" />
+      <!-- #endif -->
+      <image class="canvas-img" :src="lotteryImg" :style="{
+         width: canvasWidth + canvasMarginTotal + 'px',
+         height: canvasHeight + canvasMarginTotal + 'px',
+         transform: `rotate(${canvasAngle + targetAngle}deg)`,
+         transitionDuration: `${transitionDuration}s`
+       }"
+        v-if="lotteryImg"></image>
+      <view class="almost-lottery__action" :style="{
+         width: actionSize + 'px',
+         height: actionSize + 'px'
+       }" @click="handleActionStart"></view>
+      <!-- 为了兼容 app 端 ctx.measureText 所需的标签 -->
+      <text class="almost-lottery__measureText">{{ measureText }}</text>
+    </view>
+  </view>
+</template>
+
+<script>
+	import { pathToBase64 } from '@/almost-utils/image-tools.js'
+	import { getStore, setStore, clearStore, downloadFile } from '@/almost-utils/almost-utils.js'
+  export default {
+    name: 'AlmostLottery',
+    props: {
+      // canvas 宽度
+      canvasWidth: {
+        type: Number,
+        default: 240
+      },
+      // canvas 高度
+      canvasHeight: {
+        type: Number,
+        default: 240
+      },
+      // 奖品列表
+      prizeList: {
+        type: Array,
+        required: true,
+        validator: (value) => {
+          return value.length > 1
+        }
+      },
+      // 中奖奖品在列表中的下标
+      prizeIndex: {
+        type: Number,
+        required: true
+      },
+      // 奖品区块对应背景颜色
+      colors: {
+        type: Array,
+        default: () => [
+          '#FFFFFF',
+          '#FFE9AA'
+        ]
+      },
+      // 旋转动画时间 单位s
+      duration: {
+        type: Number,
+        default: 8
+      },
+      // 旋转的圈数
+      ringCount: {
+        type: Number,
+        default: 8
+      },
+      // 指针位置
+      pointerPosition: {
+        type: String,
+        default: 'edge',
+        validator: (value) => {
+          return value === 'edge' || value === 'middle'
+        }
+      },
+      // 字体颜色
+      fontColor: {
+        type: String,
+        default: '#C30B29'
+      },
+      // 文字的大小
+      fontSize: {
+        type: Number,
+        default: 12
+      },
+      // 奖品文字多行情况下的行高
+      lineHeight: {
+        type: Number,
+        default: 16
+      },
+      // 奖品名称所对应的 key 值
+      strKey: {
+        type: String,
+        default: 'name'
+      },
+      // 奖品文字总长度限制
+      strMaxLen: {
+        type: Number,
+        default: 12
+      },
+      // 奖品文字多行情况下第一行文字长度
+      strLineLen: {
+        type: Number,
+        default: 6
+      },
+      // 奖品图片的宽
+      imageWidth: {
+        type: Number,
+        default: 30
+      },
+      // 奖品图片的高
+      imageHeight: {
+        type: Number,
+        default: 30
+      },
+			// 转盘绘制成功的提示
+			successMsg: {
+				type: String,
+				default: '奖品准备就绪,快来参与抽奖吧'
+			},
+			// 转盘绘制失败的提示
+			failMsg: {
+				type: String,
+				default: '奖品仍在准备中,请稍后再来...'
+			},
+			// 是否开启画板的缓存
+			canvasCached: {
+				type: Boolean,
+				default: true
+			},
+			// 内圈与外圈的间距
+			canvasMargin: {
+        type: Number,
+        default: 5
+      }
+    },
+    data() {
+      return {
+        // 画板className
+        className: 'almost-lottery__canvas',
+        // 画板标识
+        canvasId: 'almostLotteryCanvas',
+        // 画板导出的图片
+        lotteryImg: '',
+        // 旋转到奖品目标需要的角度
+        targetAngle: 0,
+        // 旋转动画时间 单位 s
+        transitionDuration: 0,
+        // 是否正在旋转
+        isRotate: false,
+        // 当前停留在那个奖品的序号
+        stayIndex: 0,
+        // 当前中奖奖品的序号
+        targetIndex: 0,
+				// 是否存在可用的缓存转盘图
+				isCacheImg: false,
+				oldLotteryImg: '',
+        // 解决 app 不支持 measureText 的问题
+				// app 已在 2.9.3 的版本中提供了对 measureText 的支持,将在后续版本逐渐稳定后移除相关兼容代码
+        measureText: ''
+      }
+    },
+    computed: {
+      // 根据奖品列表计算 canvas 旋转角度
+      canvasAngle() {
+        let prizeCount = this.prizeList.length
+        let prizeClip = 360 / prizeCount
+        let result = 0
+
+        let diffNum = 90 / prizeClip
+        if (this.pointerPosition === 'edge') {
+          result = -(prizeClip * diffNum)
+        } else {
+          result = -(prizeClip * diffNum + prizeClip / 2)
+        }
+        return result
+      },
+      // 外圆的半径
+      outsideRadius() {
+        return this.canvasWidth / 2
+      },
+      // 内圆的半径
+      insideRadius() {
+        return 20
+      },
+      // 字体的半径
+      textRadius() {
+        return this.fontSize / 2
+      },
+      // 根据画板的宽度计算奖品文字与中心点的距离
+      textDistance() {
+        const textZeroY = Math.round(this.outsideRadius - (this.insideRadius / 2))
+        return textZeroY - this.textRadius
+      },
+      // 设备像素密度
+      pixelRatio() {
+        return uni.getSystemInfoSync().pixelRatio
+      },
+			// 内圈与外圈的距离
+			canvasMarginTotal () {
+				let diffNum = 5
+				let margin = this.canvasMargin * 2
+				if (this.canvasWidth > 240) {
+					return -(this.canvasWidth / 240 * 2) - margin
+				} else if (this.canvasWidth < 240) {
+					return diffNum + (this.canvasWidth / 240 * 2) - margin
+				} else {
+					return diffNum - margin
+				}
+			},
+			// 抽奖按钮的宽高
+			actionSize () {
+				return this.canvasWidth / 2.4
+			}
+    },
+    watch: {
+      // 监听获奖序号的变动
+      prizeIndex(newVal, oldVal) {
+        if (newVal > -1) {
+          this.targetIndex = newVal
+          this.onRotateStart()
+        } else {
+          console.info('旋转结束,prizeIndex 已重置')
+        }
+      }
+    },
+    methods: {
+	  resetAngle(){
+		  let prizeCount = this.prizeList.length
+		  let prizeClip = 360 / prizeCount
+		  this.targetAngle = this.targetAngle - prizeClip/2
+	  },
+      // 开始旋转
+      onRotateStart() {
+        if (this.isRotate) return
+        this.isRotate = true
+        // 奖品总数
+        let prizeCount = this.prizeList.length
+        let baseAngle = 360 / prizeCount
+        let angles = 0
+        if (this.targetAngle === 0) {
+          // 第一次旋转
+          // 因为第一个奖品是从0°开始的,即水平向右方向
+          // 第一次旋转角度 = 270度 - (停留的序号-目标序号) * 每个奖品区间角度 - 每个奖品区间角度的一半 - canvas自身旋转的度数
+          angles = (270 - (this.targetIndex - this.stayIndex) * baseAngle - baseAngle / 2) - this.canvasAngle
+        } else {
+          // 后续旋转
+          // 后续继续旋转 就只需要计算停留的位置与目标位置的角度
+          angles = -(this.targetIndex - this.stayIndex) * baseAngle
+        }
+        // 更新目前序号
+        this.stayIndex = this.targetIndex
+        // 转 8 圈,圈数越多,转的越快
+        this.targetAngle += angles + 360 * this.ringCount
+
+        // 计算转盘结束的时间,预加一些延迟确保转盘停止后触发结束事件
+        let endTime = this.transitionDuration * 1000 + 100
+        let endTimer = setTimeout(() => {
+          clearTimeout(endTimer)
+          endTimer = null
+
+          this.isRotate = false
+          this.$emit('draw-end')
+        }, endTime)
+
+        let resetPrizeTimer = setTimeout(() => {
+          clearTimeout(resetPrizeTimer)
+          resetPrizeTimer = null
+
+          // 每次抽奖结束后都要重置父级附件的 prizeIndex
+          this.$emit('reset-index')
+        }, endTime + 50)
+      },
+      // 点击 开始抽奖 按钮
+      handleActionStart() {
+        this.$emit('draw-start')
+      },
+      // 渲染转盘
+      async onCreateCanvas() {
+        // 获取 canvas 画布
+        const canvasId = this.canvasId
+        const ctx = uni.createCanvasContext(canvasId, this)
+
+        // canvas 的宽高
+        let canvasW = this.canvasWidth
+        let canvasH = this.canvasHeight
+
+        // 根据奖品个数计算 角度
+        let prizeCount = this.prizeList.length
+        let baseAngle = Math.PI * 2 / prizeCount
+
+        // 设置描边颜色
+        ctx.setStrokeStyle('#FFBE04')
+
+        // 设置字体和字号
+        // #ifndef MP
+        let fontFamily =
+          '-apple-system, BlinkMacSystemFont, \'PingFang SC\', \'Helvetica Neue\', STHeiti, \'Microsoft Yahei\', Tahoma, Simsun, sans-serif'
+        ctx.font = `${this.fontSize}px ${fontFamily}`
+        // #endif
+        // #ifdef MP
+        ctx.setFontSize(this.fontSize)
+        // #endif
+
+        // 注意,开始画的位置是从0°角的位置开始画的。也就是水平向右的方向。
+        // 画具体内容
+        for (let i = 0; i < prizeCount; i++) {
+					let prizeItem = this.prizeList[i]
+          // 当前角度
+          let angle = i * baseAngle
+
+          // 保存当前画布的状态
+          ctx.save()
+
+          // 开始画内容
+          ctx.beginPath()
+
+          // 开始画圆弧
+          // x => 圆弧对应的圆心横坐标 x
+          // y => 圆弧对应的圆心横坐标 y
+          // radius => 圆弧的半径大小
+          // startAngle => 圆弧开始的角度,单位是弧度
+          // endAngle => 圆弧结束的角度,单位是弧度
+          // anticlockwise(可选) => 绘制方向,true 为逆时针,false 为顺时针
+          ctx.arc(canvasW * 0.5, canvasH * 0.5, this.outsideRadius, angle, angle + baseAngle, false)
+          ctx.arc(canvasW * 0.5, canvasH * 0.5, this.insideRadius, angle + baseAngle, angle, true)
+
+          // 开始链接线条
+          ctx.stroke()
+          // 每个奖品区块背景填充颜色
+          if (this.colors.length === 2) {
+            ctx.setFillStyle(this.colors[i % 2])
+          } else {
+            ctx.setFillStyle(this.colors[i])
+          }
+          // 填充颜色
+          ctx.fill()
+
+          // 开始绘制奖品内容
+          // 重新映射画布上的 (0,0) 位置
+          let translateX = canvasW * 0.5 + Math.cos(angle + baseAngle / 2) * this.textDistance
+          let translateY = canvasH * 0.5 + Math.sin(angle + baseAngle / 2) * this.textDistance
+          ctx.translate(translateX, translateY)
+
+          // 绘制奖品名称
+          ctx.setFillStyle(this.fontColor)
+          let rewardName = this.strLimit(prizeItem[this.strKey])
+
+          // rotate方法旋转当前的绘图,因为文字是和当前扇形中心线垂直的
+          ctx.rotate(angle + (baseAngle / 2) + (Math.PI / 2))
+
+          // 设置文本位置并处理换行
+          // 是否需要换行
+          let isLineBreak = rewardName.length > this.strLineLen
+          let textOffsetX = this.fontSize === 12 ? 0 : this.textRadius
+          if (isLineBreak) {
+            // 获得多行文本数组
+            rewardName = rewardName.substring(0, this.strLineLen) + ',' + rewardName.substring(this.strLineLen)
+            let rewardNames = rewardName.split(',')
+
+            // 循环文本数组,计算每一行的文本宽度
+            for (let j = 0; j < rewardNames.length; j++) {
+              if (ctx.measureText && ctx.measureText(rewardNames[j]).width) {
+                // 文本的宽度信息
+                let tempStrSize = ctx.measureText(rewardNames[j])
+                ctx.fillText(rewardNames[j], -(tempStrSize.width / 2 + textOffsetX), j * this.lineHeight)
+              } else {
+                this.measureText = rewardNames[j]
+
+                // 等待页面重新渲染
+                await this.$nextTick()
+
+                let textWidth = await this.getTextWidth()
+
+                ctx.fillText(rewardNames[j], -(textWidth / 2 + textOffsetX), j * this.lineHeight)
+                // console.log(rewardNames[j], textWidth, i)
+              }
+            }
+          } else {
+            if (ctx.measureText && ctx.measureText(rewardName).width) {
+              // 文本的宽度信息
+              let tempStrSize = ctx.measureText(rewardName)
+              ctx.fillText(rewardName, -(tempStrSize.width / 2 + textOffsetX), 0)
+            } else {
+              this.measureText = rewardName
+
+              // 等待页面重新渲染
+              await this.$nextTick()
+
+              let textWidth = await this.getTextWidth()
+              ctx.fillText(rewardName, -(textWidth / 2 + textOffsetX), 0)
+            }
+          }
+
+          // 绘制奖品图片
+          if (prizeItem.prizeImage) {
+						// App-Android平台 系统 webview 更新到 Chrome84+ 后 canvas 组件绘制本地图像 uni.canvasToTempFilePath 会报错
+						// 统一将图片处理成 base64
+						// https://ask.dcloud.net.cn/question/103303
+						let reg = /^(https|http)/g
+						// 处理远程图片
+						if (reg.test(prizeItem.prizeImage)) {
+							console.warn('###当前数据列表中的奖品图片为网络图片,开始下载图片...###')
+							let res = await downloadFile(prizeItem.prizeImage)
+							console.log('处理远程图片', res)
+							if (res.ok) {
+								let tempFilePath = res.tempFilePath
+								// #ifndef MP
+								prizeItem.prizeImage = await pathToBase64(tempFilePath)
+								// #endif
+								// #ifdef MP
+								prizeItem.prizeImage = tempFilePath
+								// #endif
+							}
+						} else {
+							// #ifndef MP
+							prizeItem.prizeImage = await pathToBase64(prizeItem.prizeImage)
+							// #endif
+						}
+						
+            ctx.drawImage(prizeItem.prizeImage, -(this.imageWidth / 2), canvasW / 10, this.imageWidth, this.imageHeight)
+          }
+
+          ctx.restore()
+        }
+
+        // 保存绘图并导出图片
+        ctx.draw(true, () => {
+          let drawTimer = setTimeout(() => {
+            clearTimeout(drawTimer)
+            drawTimer = null
+
+            // #ifdef MP-ALIPAY
+            ctx.toTempFilePath({
+              destWidth: this.canvasWidth * this.pixelRatio,
+              destHeight: this.canvasHeight * this.pixelRatio,
+              success: (res) => {
+                // console.log(res.apFilePath)
+                this.handlePrizeImg({
+									ok: true,
+									data: res.apFilePath,
+									msg: '画布导出生成图片成功'
+								})
+              },
+							fail: (err) => {
+                this.handlePrizeImg({
+									ok: false,
+									data: err,
+									msg: '画布导出生成图片失败'
+								})
+							}
+            })
+            // #endif
+            // #ifndef MP-ALIPAY
+            uni.canvasToTempFilePath({
+              destWidth: this.canvasWidth * this.pixelRatio,
+              destHeight: this.canvasHeight * this.pixelRatio,
+              canvasId: this.canvasId,
+              success: (res) => {
+                // 在 H5 平台下,tempFilePath 为 base64
+                // console.log(res.tempFilePath)
+                this.handlePrizeImg({
+									ok: true,
+									data: res.tempFilePath,
+									msg: '画布导出生成图片成功'
+								})
+              },
+							fail: (err) => {
+                this.handlePrizeImg({
+									ok: false,
+									data: err,
+									msg: '画布导出生成图片失败'
+								})
+							}
+            }, this)
+            // #endif
+          }, 500)
+        })
+      },
+      // 处理导出的图片
+      handlePrizeImg(res) {
+				if (res.ok) {
+					let data = res.data
+					
+					if (!this.canvasCached) {
+						this.lotteryImg = data
+						this.handlePrizeImgSuc(res)
+						return
+					}
+					
+					// #ifndef H5
+					if (this.isCacheImg) {
+						uni.getSavedFileList({
+							success: (sucRes) => {
+								let fileList = sucRes.fileList
+								// console.log('getSavedFileList Cached', fileList)
+								
+								let cached = false
+								for (let i = 0; i < fileList.length; i++) {
+									let item = fileList[i]
+									if (item.filePath === data) {
+										cached = true
+										this.lotteryImg = data
+										
+										console.info('经查,本地缓存中存在转盘图可用,本次将不再绘制转盘')
+										this.handlePrizeImgSuc(res)
+										break
+									}
+								}
+								
+								if (!cached) {
+									console.info('经查,本地缓存中存在转盘图不可用,需要重新初始化转盘绘制')
+									this.initCanvasDraw()
+								}
+							},
+							fail: (err) => {
+								this.initCanvasDraw()
+							}
+						})
+					} else {
+						uni.saveFile({
+							tempFilePath: data,
+							success: (sucRes) => {
+								let filePath = sucRes.savedFilePath
+								// console.log('saveFile', filePath)
+								setStore('lotteryImg', filePath)
+								this.lotteryImg = filePath
+								this.handlePrizeImgSuc({
+									ok: true,
+									data: filePath,
+									msg: '画布导出生成图片成功'
+								})
+							},
+							fail: (err) => {
+								this.handlePrizeImg({
+									ok: false,
+									data: err,
+									msg: '画布导出生成图片失败'
+								})
+							}
+						})
+					}
+					// #endif
+					// #ifdef H5
+					console.info('当前为 H5 端,直接使用导出的/缓存中的 base64 图')
+					setStore('lotteryImg', data)
+					this.lotteryImg = data
+					this.handlePrizeImgSuc(res)
+					// #endif
+				} else {
+					console.error('处理导出的图片失败', res)
+					uni.hideLoading()
+					// #ifdef H5
+					console.error('###当前为 H5 端,下载网络图片需要后端配置允许跨域###')
+					// #endif
+					// #ifdef MP
+					console.error('###当前为小程序端,下载网络图片需要配置域名白名单###')
+					// #endif
+				}
+      },
+			// 处理图片完成
+			handlePrizeImgSuc (res) {
+				uni.hideLoading()
+				this.$emit('finish', {
+					ok: res.ok,
+					data: res.data,
+					msg: res.ok ? this.successMsg : this.failMsg
+				})
+			},
+      // 兼容 app 端不支持 ctx.measureText
+      // 已知问题:初始绘制时,低端安卓机 平均耗时 2s
+      getTextWidth() {
+        return new Promise((resolve, reject) => {
+          uni.createSelectorQuery().in(this).select('.almost-lottery__measureText').fields({
+            size: true,
+          }, (res) => {
+            resolve(res.width)
+          }).exec()
+        })
+      },
+      // 处理文字溢出
+      strLimit(value) {
+        let maxLength = this.strMaxLen
+        if (!value || !maxLength) return value
+        return value.length > maxLength ? value.slice(0, maxLength - 1) + '...' : value
+      },
+			// 检查本地缓存中是否存在转盘图
+			checkCacheImg () {
+				console.log('检查本地缓存中是否存在转盘图')
+				// 检查是否已有缓存的转盘图
+				// 检查是否与本次奖品数据相同
+				this.oldLotteryImg = getStore('lotteryImg')
+				let oldPrizeList = getStore('prizeList')
+				let newPrizeList = JSON.stringify(this.prizeList)
+				if (this.oldLotteryImg) {
+					if (oldPrizeList === newPrizeList) {
+						console.log(`经查,本地缓存中存在转盘图 => ${this.oldLotteryImg}`)
+						this.isCacheImg = true
+						
+						console.log('需要继续判断这张缓存图是否可用')
+						this.handlePrizeImg({
+							ok: true,
+							data: this.oldLotteryImg,
+							msg: '画布导出生成图片成功'
+						})
+						return
+					}
+				}
+				
+				console.log('经查,本地缓存中不存在转盘图')
+				this.initCanvasDraw()
+			},
+      // 初始化绘制
+      initCanvasDraw () {
+				console.log('开始初始化转盘绘制')
+				this.isCacheImg = false
+				this.lotteryImg = ''
+				clearStore('lotteryImg')
+        setStore('prizeList', this.prizeList)
+        this.onCreateCanvas()
+      }
+    },
+    mounted() {
+      this.$nextTick(() => {
+        let stoTimer = setTimeout(() => {
+          clearTimeout(stoTimer)
+          stoTimer = null
+					
+					if (this.canvasCached) {
+						this.checkCacheImg()
+					} else {
+						this.onCreateCanvas()
+					}
+          this.transitionDuration = this.duration
+        }, 50)
+      })
+    }
+  }
+</script>
+
+<style lang="scss" scoped>
+  $lotteryBgUrl: '~static/almost-lottery/almost-lottery__bg';
+  $actionBgUrl: '~static/almost-lottery/almost-lottery__action';
+
+  .almost-lottery {
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    margin: 0 auto;
+    background-repeat: no-repeat;
+    background-position: center center;
+    background-size: contain;
+    background-image: url($lotteryBgUrl + ".png");
+
+    @media (-webkit-min-device-pixel-ratio: 2), (min-device-pixel-ratio: 2) {
+      background-image: url($lotteryBgUrl + "2x.png");
+    }
+
+    @media (-webkit-min-device-pixel-ratio: 3), (min-device-pixel-ratio: 3) {
+      background-image: url($lotteryBgUrl + "3x.png");
+    }
+  }
+	
+	.almost-lottery__wrap {
+    position: relative;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+	}
+
+  .almost-lottery__canvas {
+    position: absolute;
+    left: -9999px;
+    opacity: 0;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+  }
+
+  .almost-lottery__action {
+    position: absolute;
+    background-repeat: no-repeat;
+    background-position: center center;
+    background-size: contain;
+    background-image: url($actionBgUrl + ".png");
+
+    @media (-webkit-min-device-pixel-ratio: 2), (min-device-pixel-ratio: 2) {
+      background-image: url($actionBgUrl + "2x.png");
+    }
+
+    @media (-webkit-min-device-pixel-ratio: 3), (min-device-pixel-ratio: 3) {
+      background-image: url($actionBgUrl + "3x.png");
+    }
+  }
+
+  .almost-lottery__measureText {
+    position: absolute;
+    left: 0;
+    top: 0;
+    white-space: nowrap;
+    font-size: 12px;
+    opacity: 0;
+  }
+
+  .canvas-img {
+		display: block;
+    transition: transform cubic-bezier(.34, .12, .05, .95);
+  }
+</style>

+ 96 - 0
components/uni-icons/icons.js

@@ -0,0 +1,96 @@
+export default {
+	'contact': '\ue100',
+	'person': '\ue101',
+	'personadd': '\ue102',
+	'contact-filled': '\ue130',
+	'person-filled': '\ue131',
+	'personadd-filled': '\ue132',
+	'phone': '\ue200',
+	'email': '\ue201',
+	'chatbubble': '\ue202',
+	'chatboxes': '\ue203',
+	'phone-filled': '\ue230',
+	'email-filled': '\ue231',
+	'chatbubble-filled': '\ue232',
+	'chatboxes-filled': '\ue233',
+	'weibo': '\ue260',
+	'weixin': '\ue261',
+	'pengyouquan': '\ue262',
+	'chat': '\ue263',
+	'qq': '\ue264',
+	'videocam': '\ue300',
+	'camera': '\ue301',
+	'mic': '\ue302',
+	'location': '\ue303',
+	'mic-filled': '\ue332',
+	'speech': '\ue332',
+	'location-filled': '\ue333',
+	'micoff': '\ue360',
+	'image': '\ue363',
+	'map': '\ue364',
+	'compose': '\ue400',
+	'trash': '\ue401',
+	'upload': '\ue402',
+	'download': '\ue403',
+	'close': '\ue404',
+	'redo': '\ue405',
+	'undo': '\ue406',
+	'refresh': '\ue407',
+	'star': '\ue408',
+	'plus': '\ue409',
+	'minus': '\ue410',
+	'circle': '\ue411',
+	'checkbox': '\ue411',
+	'close-filled': '\ue434',
+	'clear': '\ue434',
+	'refresh-filled': '\ue437',
+	'star-filled': '\ue438',
+	'plus-filled': '\ue439',
+	'minus-filled': '\ue440',
+	'circle-filled': '\ue441',
+	'checkbox-filled': '\ue442',
+	'closeempty': '\ue460',
+	'refreshempty': '\ue461',
+	'reload': '\ue462',
+	'starhalf': '\ue463',
+	'spinner': '\ue464',
+	'spinner-cycle': '\ue465',
+	'search': '\ue466',
+	'plusempty': '\ue468',
+	'forward': '\ue470',
+	'back': '\ue471',
+	'left-nav': '\ue471',
+	'checkmarkempty': '\ue472',
+	'home': '\ue500',
+	'navigate': '\ue501',
+	'gear': '\ue502',
+	'paperplane': '\ue503',
+	'info': '\ue504',
+	'help': '\ue505',
+	'locked': '\ue506',
+	'more': '\ue507',
+	'flag': '\ue508',
+	'home-filled': '\ue530',
+	'gear-filled': '\ue532',
+	'info-filled': '\ue534',
+	'help-filled': '\ue535',
+	'more-filled': '\ue537',
+	'settings': '\ue560',
+	'list': '\ue562',
+	'bars': '\ue563',
+	'loop': '\ue565',
+	'paperclip': '\ue567',
+	'eye': '\ue568',
+	'arrowup': '\ue580',
+	'arrowdown': '\ue581',
+	'arrowleft': '\ue582',
+	'arrowright': '\ue583',
+	'arrowthinup': '\ue584',
+	'arrowthindown': '\ue585',
+	'arrowthinleft': '\ue586',
+	'arrowthinright': '\ue587',
+	'pulldown': '\ue588',
+	'closefill': '\ue589',
+	'sound': '\ue590',
+	'scan': '\ue612'
+}

文件差異過大導致無法顯示
+ 10 - 0
components/uni-icons/uni-icons.vue


+ 178 - 0
components/uni-numbers-box/uni-numbers-box.vue

@@ -0,0 +1,178 @@
+<template>
+	<view class="uni-numbox">
+		<view @click="_calcValue('minus')" class="uni-numbox__minus">
+			<text class="uni-numbox--text" :class="{ 'uni-numbox--disabled': inputValue <= min || disabled }">-</text>
+		</view>
+		<input :disabled="disabled" @blur="_onBlur" class="uni-numbox__value" type="number" v-model="inputValue" />
+		<view @click="_calcValue('plus')" class="uni-numbox__plus">
+			<text class="uni-numbox--text" :class="{ 'uni-numbox--disabled': inputValue >= max || disabled }">+</text>
+		</view>
+	</view>
+</template>
+<script>
+	export default {
+		name: "UniNumbersBox",
+		props: {
+			index:{
+				type: String,
+				default: ''
+			},
+			value: {
+				type: [Number, String],
+				default: 1
+			},
+			min: {
+				type: Number,
+				default: 0
+			},
+			max: {
+				type: Number,
+				default: 100
+			},
+			step: {
+				type: Number,
+				default: 1
+			},
+			disabled: {
+				type: Boolean,
+				default: false
+			}
+		},
+		data() {
+			return {
+				inputValue: this.value
+			};
+		},
+		watch: {
+			value(newVal,oldVal) {
+				console.log(newVal,oldVal,'000')
+				this.inputValue = +newVal;
+			},
+			inputValue(newVal, oldVal) {
+				if (+newVal !== +oldVal && this.max >= +newVal && this.min <= +newVal) {
+					this.$emit("change", newVal, this.index);
+				}
+			}
+		},
+		created() {
+			this.inputValue = +this.value;
+		},
+		methods: {
+			_calcValue(type) {
+				if (this.disabled) {
+					return;
+				}
+				const scale = this._getDecimalScale();
+				let value = this.inputValue * scale;
+				let step = this.step * scale;
+				if (type === "minus") {
+					value -= step;
+					if (value < this.min) {
+						return;
+					}
+					if(value > this.max){
+						value = this.max
+					}
+				} else if (type === "plus") {
+					value += step;
+					if (value > this.max) {
+						return;
+					}
+					if(value < this.min){
+						value = this.min
+					}
+				}
+
+				this.inputValue = String(value / scale);
+				this.$emit('input',this.inputValue)
+			},
+			_getDecimalScale() {
+				let scale = 1;
+				// 浮点型
+				if (~~this.step !== this.step) {
+					scale = Math.pow(10, (this.step + "").split(".")[1].length);
+				}
+				return scale;
+			},
+			_onBlur(event) {
+				let value = event.detail.value;
+				if (!value) {
+					this.inputValue = this.min;
+				}
+				value = +value;
+				if (value > this.max) {
+					value = this.max;
+				} else if (value < this.min) {
+					value = this.min;
+				}
+				this.inputValue = value;
+				this.$emit('input',this.inputValue)
+			},
+		}
+	};
+</script>
+<style lang="scss" scoped>
+	$box-height: 30px;
+	/* #ifdef APP-NVUE */
+	$box-line-height: 30px;
+	/* #endif */
+	$box-line-height: 26px;
+	$box-width: 30px;
+
+	.uni-numbox {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		width: 120px;
+	}
+
+	.uni-numbox__value {
+		background-color: $uni-bg-color;
+		width: 40px;
+		height: $box-height;
+		line-height: $box-height;
+		text-align: center;
+		font-size: $uni-font-size-lg;
+		margin: 0 10px;
+	}
+
+	.uni-numbox__minus {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		align-items: center;
+		justify-content: center;
+		width: $box-width;
+		height: $box-height;
+		line-height: $box-height;
+		font-size: 20px;
+		color: $uni-text-color;
+		background-color: $uni-bg-color-grey;
+		border-radius: $box-width;
+	}
+
+	.uni-numbox__plus {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		align-items: center;
+		justify-content: center;
+		width: $box-width;
+		height: $box-height;
+		line-height: $box-height;
+		background-color: $uni-bg-color-grey;
+		border-radius: $box-width;
+	}
+
+	.uni-numbox--text {
+		font-size: 36rpx;
+		color: $uni-text-color;
+	}
+
+	.uni-numbox--disabled {
+		color: $uni-text-color-disable;
+	}
+</style>

+ 122 - 0
components/uni-popup/image-txt-popup.vue

@@ -0,0 +1,122 @@
+<template>
+	<view class="image-txt-wrap">
+		<image :src="captchaBase" class="image" @click="getCode"></image>
+		<text class="tip-txt">* 点击图片即可切换验证码</text>
+		<input v-model="captcha" @input="inpChange" class="item-inp" name="captcha" type="text" maxlength="4" placeholder="请输入上方的图形验证码" />
+		<button :type="btnType" size="mini" class="popup-button-yz" @click="verify">验证</button>
+	</view>
+</template>
+
+<script>
+	import { getCaptcha } from '@/api/user.js'
+	export default{
+		name: 'ImageTxtPopup',
+		props: {
+			changeImg: {
+				type: Boolean,
+				default: false
+			},
+			//  applyImageTxt申请试用   forgetImageTxt忘记密码
+			type: {
+				type: String,
+				default: ''
+			},
+		},
+		watch: {
+			changeImg(newV, oldV){
+				if(newV === true){
+					this.getCode()
+				}
+			}
+		},
+		data(){
+			return{
+				captchaBase: '',  //  图片验证码base64
+				captcha: '',  //  图文验证码
+				nowRandomCode: '',  // 当前随机码
+				btnType: 'default',  //  验证按钮type
+			}
+		},
+		created() {
+			// this.getCode()
+		},
+		methods: {
+			//  获取验证码
+			getCode(){
+			  const _this = this
+			  //  6位随机数
+			  this.nowRandomCode = Math.random().toString().slice(-6)
+			  getCaptcha(this.nowRandomCode).then(res => {
+				// console.log(res.data)
+			    //  获取图片路径
+			    _this.captchaBase = 'data:image/png;base64,' + res.data
+			    // 显示图文验证码弹框
+			    _this.captcha = ''  //  清空图文输入内容
+			  })
+			},
+			//  验证码输入监听
+			inpChange(){
+				if(this.captcha.length == 4){
+					this.btnType = 'primary'
+				}else{
+					this.btnType = 'default'
+				}
+			},
+			//  验证
+			verify(){
+				if(!this.captcha){
+					uni.showToast({icon: 'none',title: '请输入上方的图形验证码'})
+					return
+				}else{
+					if(this.captcha.length != 4){
+						uni.showToast({icon: 'none',title: '请输入4位图形验证码'})
+						return
+					}
+					this.$emit('captchaImg', this.captcha, this.nowRandomCode)
+				}
+			},
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.image-txt-wrap{
+		line-height: 1.8;
+		.tip-txt{
+			display: block;
+			font-size: 24upx;
+			color: #999;
+			margin-bottom: 40upx;
+			text-align: center;
+		}
+		.item-inp{
+			background-color: #fff;
+			border: 2upx solid #dcdee2;
+			border-radius: 8upx;
+			color: #666;
+			display: inline-block;
+			font-size: 24upx;
+			height: 64upx;
+			line-height: 1.5;
+			padding: 8upx 14upx;
+			width: 100%;
+			box-sizing: border-box;
+		}
+		.image{
+			max-width: 100%;
+			width: 400upx;
+			height: 160upx;
+			margin: 30upx auto 20upx;
+			display: block;
+			img{
+				max-width: 100%;
+			}
+		}
+		.popup-button-yz{
+			display: block;
+			margin: 40upx auto 50upx;
+			width: 30%;
+			line-height: 2.8;
+		}
+	}
+</style>

+ 22 - 0
components/uni-popup/message.js

@@ -0,0 +1,22 @@
+export default {
+	created() {
+		if (this.type === 'message') {
+			// 不显示遮罩
+			this.maskShow = false 
+			// 获取子组件对象
+			this.childrenMsg = null
+		}
+	},
+	methods: {
+		customOpen() {
+			if (this.childrenMsg) {
+				this.childrenMsg.open()
+			}
+		},
+		customClose() {
+			if (this.childrenMsg) {
+				this.childrenMsg.close()
+			}
+		}
+	}
+}

+ 96 - 0
components/uni-popup/popup-con.vue

@@ -0,0 +1,96 @@
+<template>
+	<view class="popup-con">
+		<text class="popup-header">{{title}}</text>
+		<view class="popup-content">
+			<image-txt-popup :type="type" :changeImg="changeImg" @captchaImg="captchaImg"></image-txt-popup>
+		</view>
+		<view class="popup-footer">
+			<text class="popup-button" v-for="(item, index) in popBtn" :key="index" :style="{ color: item.color }">{{item.name}}</text>
+		</view>
+	</view>
+</template>
+
+<script>
+	import imageTxtPopup from '@/components/uni-popup/image-txt-popup.vue'
+	export default{
+		name: 'PopupCon',
+		props: {
+			//  applyImageTxt申请试用   forgetImageTxt忘记密码
+			type: {
+				type: String,
+				default: ''
+			},
+			title: {
+				type: String,
+				default: '标题'
+			},
+			//  操作按钮
+			popBtn: {
+				type: Array,
+				default: function (){
+				  return []
+				}
+			},
+			changeImg: {
+				type: Boolean,
+				default: false
+			}
+		},
+		components: {
+			imageTxtPopup
+		},
+		data(){
+			return{}
+		},
+		methods: {
+			captchaImg(captcha, nowRandomCode){
+				this.$emit('captchaImg', captcha, nowRandomCode)
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.popup-con{
+		box-sizing: border-box;
+		position: absolute;
+		top: 50%;
+		left: 50%;
+		transform: translate(-50%, -50%);
+		box-shadow: 0 8upx 24upx rgba(0,0,0,.15);
+		display: flex;
+		flex-direction: column;
+		padding: 30upx;
+		width: 600upx;
+		background-color: #fff;
+		border-radius: 20upx;
+		.popup-header{
+			margin-bottom: 20upx;
+			text-align: center;
+			font-weight: 700;
+			font-size: 32upx;
+			color: #333;
+			line-height: 1.8;
+		}
+		.popup-content{
+			font-size: 28upx;
+			color: #666;
+		}
+		.popup-footer{
+			display: flex;
+			flex-direction: row;
+			margin-top: 40upx;
+			.popup-button{
+				flex: 1;
+				text-align: center;
+				font-size: 28upx;
+				color: #3b4144;
+				line-height: 1.8;
+			}
+			.popup-confirm{
+				color: #57a3f3;
+			}
+		}
+	}
+	
+</style>

+ 25 - 0
components/uni-popup/popup.js

@@ -0,0 +1,25 @@
+import message from './message.js';
+// 定义 type 类型:弹出类型:top/bottom/center
+const config = {
+	// 顶部弹出
+	top:'top',
+	// 底部弹出
+	bottom:'bottom',
+	// 居中弹出
+	center:'center',
+	// 消息提示
+	message:'top',
+	// 对话框
+	dialog:'center',
+	// 分享
+	share:'bottom',
+}
+
+export default {
+	data(){
+		return {
+			config:config
+		}
+	},
+	mixins: [message],
+}

+ 243 - 0
components/uni-popup/uni-popup-dialog.vue

@@ -0,0 +1,243 @@
+<template>
+	<view class="uni-popup-dialog">
+		<view class="uni-dialog-title">
+			<text class="uni-dialog-title-text" :class="['uni-popup__'+dialogType]">{{title}}</text>
+		</view>
+		<view class="uni-dialog-content">
+			<text class="uni-dialog-content-text" v-if="mode === 'base'">{{content}}</text>
+			<input v-else class="uni-dialog-input" v-model="val" type="text" :placeholder="placeholder" :focus="focus" >
+		</view>
+		<view class="uni-dialog-button-group">
+			<view class="uni-dialog-button" @click="close">
+				<text class="uni-dialog-button-text">取消</text>
+			</view>
+			<view class="uni-dialog-button uni-border-left" @click="onOk">
+				<text class="uni-dialog-button-text uni-button-color">确定</text>
+			</view>
+		</view>
+
+	</view>
+</template>
+
+<script>
+	/**
+	 * PopUp 弹出层-对话框样式
+	 * @description 弹出层-对话框样式
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=329
+	 * @property {String} value input 模式下的默认值
+	 * @property {String} placeholder input 模式下输入提示
+	 * @property {String} type = [success|warning|info|error] 主题样式
+	 *  @value success 成功
+	 * 	@value warning 提示
+	 * 	@value info 消息
+	 * 	@value error 错误
+	 * @property {String} mode = [base|input] 模式、
+	 * 	@value base 基础对话框
+	 * 	@value input 可输入对话框
+	 * @property {String} content 对话框内容
+	 * @property {Boolean} beforeClose 是否拦截取消事件
+	 * @event {Function} confirm 点击确认按钮触发
+	 * @event {Function} close 点击取消按钮触发
+	 */
+
+	export default {
+		name: "uniPopupDialog",
+		props: {
+			value: {
+				type: [String, Number],
+				default: ''
+			},
+			placeholder: {
+				type: [String, Number],
+				default: '请输入内容'
+			},
+			/**
+			 * 对话框主题 success/warning/info/error	  默认 success
+			 */
+			type: {
+				type: String,
+				default: 'error'
+			},
+			/**
+			 * 对话框模式 base/input
+			 */
+			mode: {
+				type: String,
+				default: 'base'
+			},
+			/**
+			 * 对话框标题
+			 */
+			title: {
+				type: String,
+				default: '提示'
+			},
+			/**
+			 * 对话框内容
+			 */
+			content: {
+				type: String,
+				default: ''
+			},
+			/**
+			 * 拦截取消事件 ,如果拦截取消事件,必须监听close事件,执行 done()
+			 */
+			beforeClose: {
+				type: Boolean,
+				default: false
+			}
+		},
+		data() {
+			return {
+				dialogType: 'error',
+				focus: false,
+				val: ""
+			}
+		},
+		inject: ['popup'],
+		watch: {
+			type(val) {
+				this.dialogType = val
+			},
+			mode(val) {
+				if (val === 'input') {
+					this.dialogType = 'info'
+				}
+			},
+			value(val) {
+				this.val = val
+			}
+		},
+		created() {
+			// 对话框遮罩不可点击
+			this.popup.mkclick = false
+			if (this.mode === 'input') {
+				this.dialogType = 'info'
+				this.val = this.value
+			} else {
+				this.dialogType = this.type
+			}
+		},
+		mounted() {
+			this.focus = true
+		},
+		methods: {
+			/**
+			 * 点击确认按钮
+			 */
+			onOk() {
+				this.$emit('confirm', () => {
+					this.popup.close()
+					if (this.mode === 'input') this.val = this.value
+				}, this.mode === 'input' ? this.val : '')
+			},
+			/**
+			 * 点击取消按钮
+			 */
+			close() {
+				if (this.beforeClose) {
+					this.$emit('close', () => {
+						this.popup.close()
+					})
+					return
+				}
+				this.popup.close()
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.uni-popup-dialog {
+		width: 300px;
+		border-radius: 15px;
+		background-color: #fff;
+	}
+
+	.uni-dialog-title {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		justify-content: center;
+		padding-top: 15px;
+		padding-bottom: 5px;
+	}
+
+	.uni-dialog-title-text {
+		font-size: 16px;
+		font-weight: 500;
+	}
+
+	.uni-dialog-content {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		justify-content: center;
+		align-items: center;
+		padding: 5px 15px 15px 15px;
+	}
+
+	.uni-dialog-content-text {
+		font-size: 14px;
+		color: #6e6e6e;
+	}
+
+	.uni-dialog-button-group {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		border-top-color: #f5f5f5;
+		border-top-style: solid;
+		border-top-width: 1px;
+	}
+
+	.uni-dialog-button {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+
+		flex: 1;
+		flex-direction: row;
+		justify-content: center;
+		align-items: center;
+		height: 45px;
+	}
+
+	.uni-border-left {
+		border-left-color: #f0f0f0;
+		border-left-style: solid;
+		border-left-width: 1px;
+	}
+
+	.uni-dialog-button-text {
+		font-size: 14px;
+	}
+
+	.uni-button-color {
+		color: $uni-color-primary;
+	}
+
+	.uni-dialog-input {
+		flex: 1;
+		font-size: 14px;
+	}
+
+	.uni-popup__success {
+		color: $uni-color-success;
+	}
+
+	.uni-popup__warn {
+		color: $uni-color-warning;
+	}
+
+	.uni-popup__error {
+		color: $uni-color-error;
+	}
+
+	.uni-popup__info {
+		color: #909399;
+	}
+</style>

+ 116 - 0
components/uni-popup/uni-popup-message.vue

@@ -0,0 +1,116 @@
+<template>
+	<view class="uni-popup-message" :class="'uni-popup__'+[type]">
+		<text class="uni-popup-message-text" :class="'uni-popup__'+[type]+'-text'">{{message}}</text>
+	</view>
+</template>
+
+<script>
+	
+	/**
+	 * PopUp 弹出层-消息提示
+	 * @description 弹出层-消息提示
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=329
+	 * @property {String} type = [success|warning|info|error] 主题样式
+	 *  @value success 成功
+	 * 	@value warning 提示
+	 * 	@value info 消息
+	 * 	@value error 错误
+	 * @property {String} message 消息提示文字
+	 * @property {String} duration 显示时间,设置为 0 则不会自动关闭
+	 */
+	
+	export default {
+		name: 'UniPopupMessage',
+		props: {
+			/**
+			 * 主题 success/warning/info/error	  默认 success
+			 */
+			type: {
+				type: String,
+				default: 'success'
+			},
+			/**
+			 * 消息文字
+			 */
+			message: {
+				type: String,
+				default: ''
+			},
+			/**
+			 * 显示时间,设置为 0 则不会自动关闭
+			 */
+			duration: {
+				type: Number,
+				default: 3000
+			}
+		},
+		inject: ['popup'],
+		data() {
+			return {}
+		},
+		created() {
+			this.popup.childrenMsg = this
+		},
+		methods: {
+			open() {
+				if (this.duration === 0) return
+				clearTimeout(this.popuptimer)
+				this.popuptimer = setTimeout(() => {
+					this.popup.close()
+				}, this.duration)
+			},
+			close() {
+				clearTimeout(this.popuptimer)
+			}
+		}
+	}
+</script>
+<style lang="scss" scoped>
+	.uni-popup-message {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		background-color: #e1f3d8;
+		padding: 10px 15px;
+		border-color: #eee;
+		border-style: solid;
+		border-width: 1px;
+	}
+	.uni-popup-message-text {
+		font-size: 14px;
+		padding: 0;
+	}
+
+	.uni-popup__success {
+		background-color: #e1f3d8;
+	}
+
+	.uni-popup__success-text {
+		color: #67C23A;
+	}
+
+	.uni-popup__warn {
+		background-color: #faecd8;
+	}
+
+	.uni-popup__warn-text {
+		color: #E6A23C;
+	}
+
+	.uni-popup__error {
+		background-color: #fde2e2;
+	}
+
+	.uni-popup__error-text {
+		color: #F56C6C;
+	}
+
+	.uni-popup__info {
+		background-color: #F2F6FC;
+	}
+
+	.uni-popup__info-text {
+		color: #909399;
+	}
+</style>

+ 165 - 0
components/uni-popup/uni-popup-share.vue

@@ -0,0 +1,165 @@
+<template>
+	<view class="uni-popup-share">
+		<view class="uni-share-title"><text class="uni-share-title-text">{{title}}</text></view>
+		<view class="uni-share-content">
+			<view class="uni-share-content-box">
+				<view class="uni-share-content-item" v-for="(item,index) in bottomData" :key="index" @click.stop="select(item,index)">
+					<image class="uni-share-image" :src="item.icon" mode="aspectFill"></image>
+					<text class="uni-share-text">{{item.text}}</text>
+				</view>
+
+			</view>
+		</view>
+		<view class="uni-share-button-box">
+			<button class="uni-share-button" @click="close">取消</button>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		name: 'UniPopupShare',
+		props: {
+			title: {
+				type: String,
+				default: '分享到'
+			}
+		},
+		inject: ['popup'],
+		data() {
+			return {
+				bottomData: [{
+						text: '微信',
+						icon: 'https://img-cdn-qiniu.dcloud.net.cn/uni-ui/grid-2.png',
+						name: 'wx'
+					},
+					{
+						text: '支付宝',
+						icon: 'https://img-cdn-qiniu.dcloud.net.cn/uni-ui/grid-8.png',
+						name: 'wx'
+					},
+					{
+						text: 'QQ',
+						icon: 'https://img-cdn-qiniu.dcloud.net.cn/uni-ui/gird-3.png',
+						name: 'qq'
+					},
+					{
+						text: '新浪',
+						icon: 'https://img-cdn-qiniu.dcloud.net.cn/uni-ui/grid-1.png',
+						name: 'sina'
+					},
+					{
+						text: '百度',
+						icon: 'https://img-cdn-qiniu.dcloud.net.cn/uni-ui/grid-7.png',
+						name: 'copy'
+					},
+					{
+						text: '其他',
+						icon: 'https://img-cdn-qiniu.dcloud.net.cn/uni-ui/grid-5.png',
+						name: 'more'
+					}
+				]
+			}
+		},
+		created() {},
+		methods: {
+			/**
+			 * 选择内容
+			 */
+			select(item, index) {
+				this.$emit('select', {
+					item,
+					index
+				}, () => {
+					this.popup.close()
+				})
+			},
+			/**
+			 * 关闭窗口
+			 */
+			close() {
+				this.popup.close()
+			}
+		}
+	}
+</script>
+<style lang="scss" scoped>
+	.uni-popup-share {
+		background-color: #fff;
+	}
+	.uni-share-title {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		align-items: center;
+		justify-content: center;
+		height: 40px;
+	}
+	.uni-share-title-text {
+		font-size: 14px;
+		color: #666;
+	}
+	.uni-share-content {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		justify-content: center;
+		padding-top: 10px;
+	}
+	
+	.uni-share-content-box {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		flex-wrap: wrap;
+		width: 360px;
+	}
+	
+	.uni-share-content-item {
+		width: 90px;
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: column;
+		justify-content: center;
+		padding: 10px 0;
+		align-items: center;
+	}
+	
+	.uni-share-content-item:active {
+		background-color: #f5f5f5;
+	}
+	
+	.uni-share-image {
+		width: 30px;
+		height: 30px;
+	}
+	
+	.uni-share-text {
+		margin-top: 10px;
+		font-size: 14px;
+		color: #3B4144;
+	}
+	
+	.uni-share-button-box {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		padding: 10px 15px;
+	}
+	
+	.uni-share-button {
+		flex: 1;
+		border-radius: 50px;
+		color: #666;
+		font-size: 16px;
+	}
+	
+	.uni-share-button::after {
+		border-radius: 50px;
+	}
+</style>

+ 295 - 0
components/uni-popup/uni-popup.vue

@@ -0,0 +1,295 @@
+<template>
+	<view v-if="showPopup" class="uni-popup" :class="[popupstyle]" @touchmove.stop.prevent="clear">
+		<uni-transition v-if="maskShow" :mode-class="['fade']" :styles="maskClass" :duration="duration" :show="showTrans"
+		 @click="onTap" />
+		<uni-transition :mode-class="ani" :styles="transClass" :duration="duration" :show="showTrans" @click="onTap">
+			<view class="uni-popup__wrapper-box" @click.stop="clear">
+				<slot />
+			</view>
+		</uni-transition>
+	</view>
+</template>
+
+<script>
+	import uniTransition from '../uni-transition/uni-transition.vue'
+	import popup from './popup.js'
+	/**
+	 * PopUp 弹出层
+	 * @description 弹出层组件,为了解决遮罩弹层的问题
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=329
+	 * @property {String} type = [top|center|bottom] 弹出方式
+	 * 	@value top 顶部弹出
+	 * 	@value center 中间弹出
+	 * 	@value bottom 底部弹出
+	 * 	@value message 消息提示
+	 * 	@value dialog 对话框
+	 * 	@value share 底部分享示例
+	 * @property {Boolean} animation = [ture|false] 是否开启动画
+	 * @property {Boolean} maskClick = [ture|false] 蒙版点击是否关闭弹窗
+	 * @event {Function} change 打开关闭弹窗触发,e={show: false}
+	 */
+
+	export default {
+		name: 'UniPopup',
+		components: {
+			uniTransition
+		},
+		props: {
+			// 开启动画
+			animation: {
+				type: Boolean,
+				default: true
+			},
+			// 弹出层类型,可选值,top: 顶部弹出层;bottom:底部弹出层;center:全屏弹出层
+			// message: 消息提示 ; dialog : 对话框
+			type: {
+				type: String,
+				default: 'center'
+			},
+			// maskClick
+			maskClick: {
+				type: Boolean,
+				default: true
+			}
+		},
+		provide() {
+			return {
+				popup: this
+			}
+		},
+		mixins: [popup],
+		watch: {
+			/**
+			 * 监听type类型
+			 */
+			type: {
+				handler: function(newVal) {
+					this[this.config[newVal]]()
+				},
+				immediate: true
+			},
+			/**
+			 * 监听遮罩是否可点击
+			 * @param {Object} val
+			 */
+			maskClick(val) {
+				this.mkclick = val
+				this.$emit("close")
+			}
+		},
+		data() {
+			return {
+				duration: 300,
+				ani: [],
+				showPopup: false,
+				showTrans: false,
+				maskClass: {
+					'position': 'fixed',
+					'bottom': 0,
+					'top': 0,
+					'left': 0,
+					'right': 0,
+					'backgroundColor': 'rgba(0, 0, 0, 0.4)'
+				},
+				transClass: {
+					'position': 'fixed',
+					'left': 0,
+					'right': 0,
+				},
+				maskShow: true,
+				mkclick: true,
+				popupstyle: 'top'
+			}
+		},
+		created() {
+			this.mkclick = this.maskClick
+			if (this.animation) {
+				this.duration = 300
+			} else {
+				this.duration = 0
+			}
+		},
+		methods: {
+			clear(e) {
+				// TODO nvue 取消冒泡
+				e.stopPropagation()
+			},
+			open() {
+				this.showPopup = true
+				this.$nextTick(() => {
+					new Promise(resolve => {
+						clearTimeout(this.timer)
+						this.timer = setTimeout(() => {
+							this.showTrans = true
+							// fixed by mehaotian 兼容 app 端
+							this.$nextTick(() => {
+								resolve();
+							})
+						}, 50);
+					}).then(res => {
+						// 自定义打开事件
+						clearTimeout(this.msgtimer)
+						this.msgtimer = setTimeout(() => {
+							this.customOpen && this.customOpen()
+						}, 100)
+						this.$emit('change', {
+							show: true,
+							type: this.type
+						})
+					})
+				})
+			},
+			close(type) {
+				this.showTrans = false
+				this.$nextTick(() => {
+					this.$emit('change', {
+						show: false,
+						type: this.type
+					})
+					clearTimeout(this.timer)
+					// 自定义关闭事件
+					this.customOpen && this.customClose()
+					this.timer = setTimeout(() => {
+						this.showPopup = false
+					}, 300)
+				})
+			},
+			onTap() {
+				if (!this.mkclick) return
+				this.close()
+			},
+			/**
+			 * 顶部弹出样式处理
+			 */
+			top() {
+				this.popupstyle = 'top'
+				this.ani = ['slide-top']
+				this.transClass = {
+					'position': 'fixed',
+					'left': 0,
+					'right': 0,
+				}
+			},
+			/**
+			 * 底部弹出样式处理
+			 */
+			bottom() {
+				this.popupstyle = 'bottom'
+				this.ani = ['slide-bottom']
+				this.transClass = {
+					'position': 'fixed',
+					'left': 0,
+					'right': 0,
+					'bottom': 0
+				}
+			},
+			/**
+			 * 中间弹出样式处理
+			 */
+			center() {
+				this.popupstyle = 'center'
+				this.ani = ['zoom-out', 'fade']
+				this.transClass = {
+					'position': 'fixed',
+					/* #ifndef APP-NVUE */
+					'display': 'flex',
+					'flexDirection': 'column',
+					/* #endif */
+					'bottom': 0,
+					'left': 0,
+					'right': 0,
+					'top': 0,
+					'justifyContent': 'center',
+					'alignItems': 'center'
+				}
+			}
+		}
+	}
+</script>
+<style lang="scss" scoped>
+	.uni-popup {
+		position: fixed;
+		/* #ifndef APP-NVUE */
+		z-index: 99;
+		/* #endif */
+	}
+
+	.uni-popup__mask {
+		position: absolute;
+		top: 0;
+		bottom: 0;
+		left: 0;
+		right: 0;
+		background-color: $uni-bg-color-mask;
+		opacity: 0;
+	}
+
+	.mask-ani {
+		transition-property: opacity;
+		transition-duration: 0.2s;
+	}
+
+	.uni-top-mask {
+		opacity: 1;
+	}
+
+	.uni-bottom-mask {
+		opacity: 1;
+	}
+
+	.uni-center-mask {
+		opacity: 1;
+	}
+
+	.uni-popup__wrapper {
+		/* #ifndef APP-NVUE */
+		display: block;
+		/* #endif */
+		position: absolute;
+	}
+
+	.top {
+		/* #ifdef H5 */
+		top: var(--window-top);
+		/* #endif */
+		/* #ifndef H5 */
+		top: 0;
+		/* #endif */
+	}
+
+	.bottom {
+		bottom: 0;
+	}
+
+	.uni-popup__wrapper-box {
+		/* #ifndef APP-NVUE */
+		display: block;
+		/* #endif */
+		position: relative;
+		/* iphonex 等安全区设置,底部安全区适配 */
+		/* #ifndef APP-NVUE */
+		padding-bottom: constant(safe-area-inset-bottom);
+		padding-bottom: env(safe-area-inset-bottom);
+		/* #endif */
+	}
+
+	.content-ani {
+		// transition: transform 0.3s;
+		transition-property: transform, opacity;
+		transition-duration: 0.2s;
+	}
+
+
+	.uni-top-content {
+		transform: translateY(0);
+	}
+
+	.uni-bottom-content {
+		transform: translateY(0);
+	}
+
+	.uni-center-content {
+		transform: scale(1);
+		opacity: 1;
+	}
+</style>

+ 279 - 0
components/uni-transition/uni-transition.vue

@@ -0,0 +1,279 @@
+<template>
+	<view v-if="isShow" ref="ani" class="uni-transition" :class="[ani.in]" :style="'transform:' +transform+';'+stylesObject"
+	 @click="change">
+		 <slot></slot>
+	</view>
+</template>
+
+<script>
+	// #ifdef APP-NVUE
+	const animation = uni.requireNativePlugin('animation');
+	// #endif
+	/**
+	 * Transition 过渡动画
+	 * @description 简单过渡动画组件
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=985
+	 * @property {Boolean} show = [false|true] 控制组件显示或隐藏
+     * @property {Array} modeClass = [fade|slide-top|slide-right|slide-bottom|slide-left|zoom-in|zoom-out] 过渡动画类型
+     *  @value fade 渐隐渐出过渡
+     *  @value slide-top 由上至下过渡
+     *  @value slide-right 由右至左过渡
+     *  @value slide-bottom 由下至上过渡
+     *  @value slide-left 由左至右过渡
+     *  @value zoom-in 由小到大过渡
+     *  @value zoom-out 由大到小过渡
+	 * @property {Number} duration 过渡动画持续时间
+	 * @property {Object} styles 组件样式,同 css 样式,注意带’-‘连接符的属性需要使用小驼峰写法如:`backgroundColor:red`
+	 */
+	export default {
+		name: 'uniTransition',
+		props: {
+			show: {
+				type: Boolean,
+				default: false
+			},
+			modeClass: {
+				type: Array,
+				default () {
+					return []
+				}
+			},
+			duration: {
+				type: Number,
+				default: 300
+			},
+			styles: {
+				type: Object,
+				default () {
+					return {}
+				}
+			}
+		},
+		data() {
+			return {
+				isShow: false,
+				transform: '',
+				ani: { in: '',
+					active: ''
+				}
+			};
+		},
+		watch: {
+			show: {
+				handler(newVal) {
+					if (newVal) {
+						this.open()
+					} else {
+						this.close()
+					}
+				},
+				immediate: true
+			}
+		},
+		computed: {
+			stylesObject() {
+				let styles = {
+					...this.styles,
+					'transition-duration': this.duration / 1000 + 's'
+				}
+				let transfrom = ''
+				for (let i in styles) {
+					let line = this.toLine(i)
+					transfrom += line + ':' + styles[i] + ';'
+				}
+				return transfrom
+			}
+		},
+		created() {
+			// this.timer = null
+			// this.nextTick = (time = 50) => new Promise(resolve => {
+			// 	clearTimeout(this.timer)
+			// 	this.timer = setTimeout(resolve, time)
+			// 	return this.timer
+			// });
+		},
+		methods: {
+			change() {
+				this.$emit('click', {
+					detail: this.isShow
+				})
+			},
+			open() {
+				clearTimeout(this.timer)
+				this.isShow = true
+				this.transform = ''
+				this.ani.in = ''
+				for (let i in this.getTranfrom(false)) {
+					if (i === 'opacity') {
+						this.ani.in = 'fade-in'
+					} else {
+						this.transform += `${this.getTranfrom(false)[i]} `
+					}
+				}
+				this.$nextTick(() => {
+					setTimeout(() => {
+						this._animation(true)
+					}, 50)
+				})
+
+			},
+			close(type) {
+				clearTimeout(this.timer)
+				this._animation(false)
+			},
+			_animation(type) {
+				let styles = this.getTranfrom(type)
+				// #ifdef APP-NVUE
+				if(!this.$refs['ani']) return
+				animation.transition(this.$refs['ani'].ref, {
+					styles,
+					duration: this.duration, //ms
+					timingFunction: 'ease',
+					needLayout: false,
+					delay: 0 //ms
+				}, () => {
+					if (!type) {
+						this.isShow = false
+					}
+					this.$emit('change', {
+						detail: this.isShow
+					})
+				})
+				// #endif
+				// #ifndef APP-NVUE
+				this.transform = ''
+				for (let i in styles) {
+					if (i === 'opacity') {
+						this.ani.in = `fade-${type?'out':'in'}`
+					} else {
+						this.transform += `${styles[i]} `
+					}
+				}
+				this.timer = setTimeout(() => {
+					if (!type) {
+						this.isShow = false
+					}
+					this.$emit('change', {
+						detail: this.isShow
+					})
+
+				}, this.duration)
+				// #endif
+
+			},
+			getTranfrom(type) {
+				let styles = {
+					transform: ''
+				}
+				this.modeClass.forEach((mode) => {
+					switch (mode) {
+						case 'fade':
+							styles.opacity = type ? 1 : 0
+							break;
+						case 'slide-top':
+							styles.transform += `translateY(${type?'0':'-100%'}) `
+							break;
+						case 'slide-right':
+							styles.transform += `translateX(${type?'0':'100%'}) `
+							break;
+						case 'slide-bottom':
+							styles.transform += `translateY(${type?'0':'100%'}) `
+							break;
+						case 'slide-left':
+							styles.transform += `translateX(${type?'0':'-100%'}) `
+							break;
+						case 'zoom-in':
+							styles.transform += `scale(${type?1:0.8}) `
+							break;
+						case 'zoom-out':
+							styles.transform += `scale(${type?1:1.2}) `
+							break;
+					}
+				})
+				return styles
+			},
+			_modeClassArr(type) {
+				let mode = this.modeClass
+				if (typeof(mode) !== "string") {
+					let modestr = ''
+					mode.forEach((item) => {
+						modestr += (item + '-' + type + ',')
+					})
+					return modestr.substr(0, modestr.length - 1)
+				} else {
+					return mode + '-' + type
+				}
+			},
+			// getEl(el) {
+			// 	console.log(el || el.ref || null);
+			// 	return el || el.ref || null
+			// },
+			toLine(name) {
+				return name.replace(/([A-Z])/g, "-$1").toLowerCase();
+			}
+		}
+	}
+</script>
+
+<style>
+	.uni-transition {
+		transition-timing-function: ease;
+		transition-duration: 0.3s;
+		transition-property: transform, opacity;
+	}
+
+	.fade-in {
+		opacity: 0;
+	}
+
+	.fade-active {
+		opacity: 1;
+	}
+
+	.slide-top-in {
+		/* transition-property: transform, opacity; */
+		transform: translateY(-100%);
+	}
+
+	.slide-top-active {
+		transform: translateY(0);
+		/* opacity: 1; */
+	}
+
+	.slide-right-in {
+		transform: translateX(100%);
+	}
+
+	.slide-right-active {
+		transform: translateX(0);
+	}
+
+	.slide-bottom-in {
+		transform: translateY(100%);
+	}
+
+	.slide-bottom-active {
+		transform: translateY(0);
+	}
+
+	.slide-left-in {
+		transform: translateX(-100%);
+	}
+
+	.slide-left-active {
+		transform: translateX(0);
+		opacity: 1;
+	}
+
+	.zoom-in-in {
+		transform: scale(0.8);
+	}
+
+	.zoom-out-active {
+		transform: scale(1);
+	}
+
+	.zoom-out-in {
+		transform: scale(1.2);
+	}
+</style>

+ 82 - 0
libs/axios.js

@@ -0,0 +1,82 @@
+import { objToUrl } from '@/libs/tools.js';
+const baseUrl = getApp().globalData.baseUrl
+//带Token请求
+const request = (opts, hasToken) => {
+	const authorization = getApp().globalData.token
+	// console.log(authorization,'authorization')
+	//  hasToken是否传入token,为true则不传token
+	const header = {
+			'content-Type': 'application/json;charset=UTF-8'
+		}
+	if(!hasToken&&authorization){
+		header['X-AUTH-TOKEN'] = authorization
+	}	
+	//此token是登录成功后后台返回保存在storage中的
+	let DefaultOpts = {
+		url: baseUrl+opts.url,
+		data: opts.data,
+		method: opts.method,
+		header: opts.header ? opts.header : header
+	}
+	let promise = new Promise(function(resolve, reject) {
+		uni.request(DefaultOpts).then(
+			(res) => {
+				console.log(res,'pppp')
+				if(res[0]&&res[0].errMsg.indexOf('request:fail')>=0){
+					resolve({
+						status: 6000,
+						message: "网络请求失败"
+					})
+				}
+				let ret = res[1]
+				if(res[1].data.status){
+					ret = res[1].data
+				}else if(res[1].status){
+					ret = res[1]
+				}
+				if(ret.status == '200'){
+					resolve(ret)
+				}else{
+					if(ret.status != '401'){
+						uni.showToast({
+							icon:'none',
+							title: ret.message,
+							mask: true
+						})
+					}
+					resolve(ret)
+				}
+				resolve(ret)
+			}
+		).catch(
+			(response) => {
+				reject(response)
+			}
+		)
+	})
+	return promise
+}
+function getRoutePath() {
+  // eslint-disable-next-line no-undef
+  const pages = getCurrentPages(); // 获取加载的页面
+  const currentPage = pages[pages.length - 1]; // 获取当前页面的对象
+  let url = currentPage.route; // 当前页面url
+  const options = objToUrl(currentPage.options); // 如果要获取url中所带的参数可以查看options
+
+  const lanuch = [
+    'pages/index/index'
+  ].includes(url);
+  url = '/' + url;
+  if (options.length) {
+    url += '?' + options;
+  }
+  return {
+    url,
+    lanuch
+  };
+}
+
+export default {
+	baseUrl,
+	request
+}

+ 85 - 0
libs/getDate.js

@@ -0,0 +1,85 @@
+// 引入 moment 时间插件
+import moment from 'moment'
+// 获取今日/昨日/本周/上周/本月/上月 时间
+export default {
+  // 获取今日的开始结束时间
+  getToday () {
+    const obj = {
+      starttime: '',
+      endtime: ''
+    }
+    obj.starttime = moment(moment().startOf('day').valueOf()).format('YYYY-MM-DD')
+    obj.endtime = moment(moment().valueOf()).format('YYYY-MM-DD')
+    return obj
+  },
+  // 获取昨日的开始结束时间
+  getYesterday () {
+    const obj = {
+      starttime: '',
+      endtime: ''
+    }
+    obj.starttime = moment(moment().add(-1, 'days').startOf('day').valueOf()).format('YYYY-MM-DD')
+    obj.endtime = moment(moment().add(-1, 'days').endOf('day').valueOf()).format('YYYY-MM-DD')
+    return obj
+  },
+  // 获取当前周的开始到当天结束时间
+  getCurrWeekDays () {
+    const obj = {
+      starttime: '',
+      endtime: ''
+    }
+    obj.starttime = moment(moment().week(moment().week()).startOf('week').add('days').valueOf()).format('YYYY-MM-DD')
+    obj.endtime = moment(moment().valueOf()).format('YYYY-MM-DD')
+    return obj
+  },
+  // 获取上一周的开始结束时间
+  getLastWeekDays () {
+    const obj = {
+      starttime: '',
+      endtime: ''
+    }
+    obj.starttime = moment(moment().week(moment().week() - 1).startOf('week').add('days').valueOf()).format('YYYY-MM-DD')
+    obj.endtime = moment(moment().week(moment().week() - 1).endOf('week').add('days').valueOf()).format('YYYY-MM-DD')
+    return obj
+  },
+  // 获取当前月的开始到当天结束时间
+  getCurrMonthDays () {
+    const obj = {
+      starttime: '',
+      endtime: ''
+    }
+    obj.starttime = moment(moment().month(moment().month()).startOf('month').valueOf()).format('YYYY-MM-DD')
+    obj.endtime = moment(moment().valueOf()).format('YYYY-MM-DD')
+    return obj
+  },
+  // 获取上一个月的开始结束时间
+  getLastMonthDays () {
+    const obj = {
+      starttime: '',
+      endtime: ''
+    }
+    obj.starttime = moment(moment().month(moment().month() - 1).startOf('month').valueOf()).format('YYYY-MM-DD')
+    obj.endtime = moment(moment().month(moment().month() - 1).endOf('month').valueOf()).format('YYYY-MM-DD')
+    return obj
+  },
+  // 获取上2个月的开始结束时间
+  getLastTwoMonthDays () {
+    const obj = {
+      starttime: '',
+      endtime: ''
+    }
+    obj.starttime = moment(moment().month(moment().month() - 2).startOf('month').valueOf()).format('YYYY-MM-DD')
+    obj.endtime = moment(moment().month(moment().month() - 2).endOf('month').valueOf()).format('YYYY-MM-DD')
+    return obj
+  },
+  // 获取近3个月到今天的开始结束时间
+  getThreeMonthDays () {
+    const obj = {
+      starttime: '',
+      endtime: ''
+    }
+    obj.starttime = moment(moment().month(moment().month() - 2).startOf('month').valueOf()).format('YYYY-MM-DD')
+    obj.endtime = moment(moment().valueOf()).format('YYYY-MM-DD')
+    return obj
+  }
+}

+ 29 - 0
libs/tools.js

@@ -0,0 +1,29 @@
+export function objToUrl(obj) {
+  let uri = '';
+  let keys = Object.keys(obj);
+  keys.forEach(item => {
+    uri += '&' + item + '=' + obj[item];
+  });
+
+  uri = uri.substr(1);
+
+  return uri;
+}
+
+export function parseQueryString(url) {
+    var obj = {};
+    var keyvalue = [];
+    var key = "",
+        value = "";
+    var paraString = url.substring(url.indexOf("?") + 1, url.length).split("&");
+    for (var i in paraString) {
+        keyvalue = paraString[i].split("=");
+        key = keyvalue[0];
+        value = keyvalue[1];
+        obj[key] = value;
+    }
+    return obj;
+}
+
+
+  

+ 5 - 0
libs/validate.js

@@ -0,0 +1,5 @@
+//  正则有效手机号码
+export const isvalidPhone = function (str) {
+  const reg = /^1[0-9]{10}$/
+  return reg.test(str)
+}

+ 17 - 0
main.js

@@ -0,0 +1,17 @@
+import Vue from 'vue'
+import App from './App'
+import store from '@/store'
+Vue.config.productionTip = false
+
+App.mpType = 'app'
+import uView from "uview-ui"
+Vue.use(uView)
+
+let vuexStore = require("@/store/$u.mixin.js")
+Vue.mixin(vuexStore)
+
+const app = new Vue({
+	store,
+    ...App
+})
+app.$mount()

+ 85 - 0
manifest.json

@@ -0,0 +1,85 @@
+{
+    "name" : "乐色管家",
+    "appid" : "__UNI__CFE91AD",
+    "description" : "智享亿家乐色管家小程序",
+    "versionName" : "1.0.0",
+    "versionCode" : "100",
+    "transformPx" : false,
+    /* 5+App特有相关 */
+    "app-plus" : {
+        "usingComponents" : true,
+        "nvueCompiler" : "uni-app",
+        "compilerVersion" : 3,
+        "splashscreen" : {
+            "alwaysShowBeforeRender" : true,
+            "waiting" : true,
+            "autoclose" : true,
+            "delay" : 0
+        },
+        /* 模块配置 */
+        "modules" : {},
+        /* 应用发布信息 */
+        "distribute" : {
+            /* android打包配置 */
+            "android" : {
+                "permissions" : [
+                    "<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
+                    "<uses-permission android:name=\"android.permission.READ_CONTACTS\"/>",
+                    "<uses-permission android:name=\"android.permission.VIBRATE\"/>",
+                    "<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
+                    "<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
+                    "<uses-permission android:name=\"android.permission.WRITE_CONTACTS\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.CAMERA\"/>",
+                    "<uses-permission android:name=\"android.permission.RECORD_AUDIO\"/>",
+                    "<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
+                    "<uses-permission android:name=\"android.permission.MODIFY_AUDIO_SETTINGS\"/>",
+                    "<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
+                    "<uses-permission android:name=\"android.permission.CALL_PHONE\"/>",
+                    "<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_COARSE_LOCATION\"/>",
+                    "<uses-feature android:name=\"android.hardware.camera\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\"/>",
+                    "<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
+                ]
+            },
+            /* ios打包配置 */
+            "ios" : {},
+            /* SDK配置 */
+            "sdkConfigs" : {}
+        }
+    },
+    /* 快应用特有相关 */
+    "quickapp" : {},
+    /* 小程序特有相关 */
+    "mp-weixin" : {
+        "appid" : "wx623b3d89df212494",
+        "setting" : {
+            "urlCheck" : false,
+            "es6" : true,
+            "minified" : true
+        },
+        "usingComponents" : true,
+        "permission" : {
+            "scope.userLocation" : {
+                "desc" : "需要获取用户位置信息定位门店的位置"
+            }
+        }
+    },
+    "mp-alipay" : {
+        "usingComponents" : true
+    },
+    "mp-baidu" : {
+        "usingComponents" : true
+    },
+    "mp-toutiao" : {
+        "usingComponents" : true
+    },
+    "uniStatistics" : {
+        "enable" : false
+    }
+}

+ 56 - 0
package-lock.json

@@ -0,0 +1,56 @@
+{
+  "name": "zxyj-mini-html",
+  "version": "1.0.0",
+  "lockfileVersion": 1,
+  "requires": true,
+  "dependencies": {
+    "charenc": {
+      "version": "0.0.2",
+      "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz",
+      "integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc="
+    },
+    "crypt": {
+      "version": "0.0.2",
+      "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz",
+      "integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs="
+    },
+    "extend": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+      "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
+    },
+    "is-buffer": {
+      "version": "1.1.6",
+      "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
+      "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
+    },
+    "md5": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz",
+      "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==",
+      "requires": {
+        "charenc": "0.0.2",
+        "crypt": "0.0.2",
+        "is-buffer": "~1.1.6"
+      }
+    },
+    "moment": {
+      "version": "2.29.1",
+      "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz",
+      "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ=="
+    },
+    "uview-ui": {
+      "version": "1.7.9",
+      "resolved": "https://registry.npmjs.org/uview-ui/-/uview-ui-1.7.9.tgz",
+      "integrity": "sha512-YSZvz69Yt/7qoJohc9+tWVQsb4fUiE30OnvTZhUA0H+SkxhQXK6MgNsmL/lZ7RVqFLwawQBzDths2ReluvFOcA=="
+    },
+    "weapp-qrcode": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/weapp-qrcode/-/weapp-qrcode-1.0.0.tgz",
+      "integrity": "sha512-4sa3W0rGDVJ9QaeZpAKlAuUxVyjhDwiUqHyGK/jJMsRMXnhb4yO8qWU/pZruMo+iT5J6CraS67lDMFb1VY+RaA==",
+      "requires": {
+        "extend": "^3.0.2"
+      }
+    }
+  }
+}

+ 23 - 0
package.json

@@ -0,0 +1,23 @@
+{
+  "name": "zxyj-mini-html",
+  "version": "1.0.0",
+  "description": "",
+  "main": "main.js",
+  "scripts": {
+    "test": "echo \"Error: no test specified\" && exit 1"
+  },
+  "repository": {
+    "type": "git",
+    "url": "http://git.chelingzhu.com/chelingzhu-web/zxyj-mini-html.git"
+  },
+  "keywords": [],
+  "author": "",
+  "license": "ISC",
+  "dependencies": {
+    "md5": "^2.3.0",
+    "moment": "^2.29.1",
+    "uview-ui": "^1.7.9",
+    "weapp-qrcode": "^1.0.0"
+  },
+  "devDependencies": {}
+}

+ 127 - 0
pages.json

@@ -0,0 +1,127 @@
+{
+	"easycom": {
+		"^u-(.*)": "uview-ui/components/u-$1/u-$1.vue"
+	},
+	"pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
+		{
+			"path": "pages/index/index", // 首页
+			"style": {
+				"navigationBarTitleText": "乐色管家采集端",
+				"enablePullDownRefresh": false
+			}
+		},
+		
+		{
+			"path": "pages/login/login",
+			"style": {
+				"navigationBarTitleText": "授权登录"
+			}
+		},
+		{
+			"path": "pages/login/phoneLogin",
+			"style": {
+				"navigationBarTitleText": "手机号登录"
+			}
+		},
+		{
+			"path": "pages/userCenter/index",
+			"style": {
+				"navigationBarTitleText": "个人中心",
+				"navigationBarBackgroundColor": "#01c9b2",
+				"navigationBarTextStyle": "white"
+			}
+		},
+		{
+			"path": "pages/userCenter/userInfo/userInfo",
+			"style": {
+				"navigationBarTitleText": "个人信息"
+			}
+		},
+		{
+			"path": "pages/userCenter/userInfo/smsVerification",
+			"style": {
+				"navigationBarTitleText": "短信验证"
+			}
+		},
+		{
+			"path": "pages/userCenter/userInfo/paymentPwd",
+			"style": {
+				"navigationBarTitleText": "支付密码"
+			}
+		},
+		{
+			"path": "pages/userCenter/userInfo/changePayPwd",
+			"style": {
+				"navigationBarTitleText": "修改支付密码"
+			}
+		},
+		{
+			"path": "pages/userCenter/aboutUs",
+			"style": {
+				"navigationBarTitleText": "关于我们"
+			}
+		},
+		{
+			"path": "pages/userCenter/question",
+			"style": {
+				"navigationBarTitleText": "常见问题"
+			}
+		},
+		{
+			"path": "pages/agreement/agreement",
+			"style": {
+				"navigationBarTitleText": "注意事项及服务协议"
+			}
+		},
+       {
+            "path" : "pages/userCenter/myCode/myCode",
+            "style" :                                                                                    
+            {
+                "navigationBarTitleText": "我的投递码",
+                "enablePullDownRefresh": false
+            }
+            
+        }
+    ],
+	// "subPackages": [{
+	// 	"root": "pagesA",
+	// 	"pages": [
+ //            ]
+	// }],
+	// "preloadRule": {
+	// 	"pages/index/index": {
+	// 		"network": "all",
+	// 		"packages": ["pagesA"]
+	// 	}
+	// },
+	"globalStyle": {
+		"navigationBarTextStyle": "black",
+		"navigationBarTitleText": "uni-app",
+		"navigationBarBackgroundColor": "#FFFFFF",
+		"backgroundColor": "#f2f2f2",
+		"app-plus": {
+			// 将回弹属性关掉
+			"bounce": "none",
+			"scrollIndicator": "none"
+		}
+	},
+	"tabBar": {
+		"color": "#7A7E83",
+		"selectedColor": "#01c9b2",
+		"backgroundColor": "#ffffff",
+		"fontSize": "14px",
+		"list": [
+		{
+			"pagePath": "pages/index/index",
+			"iconPath": "static/tabbar/home.png",
+			"selectedIconPath": "static/tabbar/home-active.png",
+			"text": "采集"
+		}, 
+		{
+			"pagePath": "pages/userCenter/index",
+			"iconPath": "static/tabbar/user.png",
+			"selectedIconPath": "static/tabbar/user-active.png",
+			"text": "我的"
+		}]
+	}
+}

+ 223 - 0
pages/agreement/agreement.vue

@@ -0,0 +1,223 @@
+<template>
+	<view class="content">
+		<view class="page-content">
+			<view class="page-care agreement">
+				<view class="title">-乐色管家用户服务协议(声明)-</view>
+				<view class="xy-cons">
+					<view class="MsoNormal">
+						“乐色管家用户小程序”(以下简称“用户小程序”)由陕西智享亿家环保科技有限公司(以下简称“本公司”)提供。用户在登录用户小程序时,均被视为已经仔细阅读本条款并完全同意。凡以任何方式登录本用户小程序,包括但不限于接受用户小程序服务、直接或间接使用本用户小程序资料、第三方链接等,均被视为自愿接受本用户小程序相关声明和用户服务协议的约束。
+					</view>
+					<view class="MsoNormal">
+						一、用户小程序使用
+					</view>
+					<view class="MsoNormal">
+						本用户小程序之设计、信息内容等任何组成部分均不得被复制、抄袭或被任何未经本公司允许的任何商业目的所使用。若本公司怀疑或确定用户行为违法或有损本公司利益,则本公司保留采取相关法律措施的权利,包括但不限于拒绝提供服务、冻结或删除用户帐户等。
+					</view>
+					<view class="MsoNormal">
+						二、链接
+					</view>
+						<view class="MsoNormal">(一) 本用户小程序对第三方网站的链接</view>
+						<view class="MsoNormal">
+							本用户小程序必要时将提供第三方经营的其他网站的超级链接或指引服务,或者通过帧联及其他方式在本用户小程序上提供第三方网站相关内容,此“内容”包括但不限于本用户小程序上包含或可获得的任何软件、资料、音频、信息以及本用户小程序上提供的其他任何信息
+						</view>
+						<view class="MsoNormal">
+							对于第三方网站的超级链接或指引服务,仅限于方便之用。本公司保留任何时段终止对第三方网站超级链接的权利。所链接的网站、网站之内容及其网站所提供的产品与服务均不受本公司的控制,因此本公司不对任何该等网站及其内容、产品或服务(包括对第三方网站的进一步链接)负责,并且不对其作出任何认可、建议、批准或介绍。若您一旦登录或使用任何所链接网站,请仔细阅读该网站免责条款,同时自行采取防护措施。由于您自行登录或使用所链接的第三方网站所产生的任何风险和损失,责任自担 ; 除非本公司另行明确规定或同意,否则,本公司将不是您和任何所链网站提供者之间达成的任何协议的当事方。
+						</view>
+					<view class="MsoNormal">
+						本用户小程序上出现对第三方网站的链接并不表示本公司或本用户小程序与其相关联,或已获得合法授权使用,包括但不限于被链接网站中显示的或可通过其链接到的任何商标、商号、标识或著作权等。
+					</view>
+					<view class="MsoNormal">
+						<view class="MsoNormal">(二) 第三方网站对本用户小程序的链接</view>
+						<view class="MsoNormal">
+							第三方网站对本用户小程序的链接,并不必然表示本公司对第三方作出了某种认可、授权、赞助,或表明其属于本公司的关联企业、合资企业、合作企业或业务伙伴。多数情况下,本公司并未意识到或授权第三方提供对本用户小程序的链接。
+						</view>
+						
+					</view>
+					<view class="MsoNormal">
+						三、前瞻性陈述
+					</view>
+					<view class="MsoNormal">
+						本用户小程序可能包含某些前瞻性陈述。这类前瞻性陈述包括但不限于:关于本公司业务经营策略的陈述;关于供应链服务网络扩建计划(包括与之有关的拟作出的资本投资)的陈述;关于计划发展的供应链服务及其相关应用的陈述;关于费用变动对本公司经营业绩的预期影响的陈述;关于竞争对本公司经营业绩的影响的陈述;关于供应链行业未来发展(包括行业结构调整及政府政策的变化)的陈述;以及与本公司未来业务发展、经营及业绩有关的其他陈述。本公司在使用“预期”、“相信”、“预测”、“期望”、“打算”及其他类似词语进行陈述时,旨在表明相关陈述属于前瞻性陈述
+					</view>
+					<view class="MsoNormal">
+						这些前瞻性陈述本质上具有相当风险和不确定性。本公司不能保证会及时或不断地更新这类前瞻性陈述。这类前瞻性陈述反映的是本公司当前状态下对未来事件的预见或规划,并不属于对未来本公司经营和业务的保证。未来本公司的实际经营和业务很有可能会因诸多客观因素而与某些前瞻性陈述有重大差异,这些因素包括但不限于:供应链行业结构的进一步调整;所需政府批准及许可条件、政策、资本投资的优先程度等监管政策的变化;行业竞争对本公司服务的需求或价格的影响;以及中国内地的政治、经济、文化、法律以及社会各方面情况的变化(包括经济增长、通货膨胀、外汇及可动用的信用额度等政策的变化)
+					</view>
+					<view class="MsoNormal">
+						此外,本公司未来往来供应链的扩建或收缩、资金投资、发展计划和规划,均取决于诸多种因素,包括但不限于是否能以可接受的条款获得足够融资;我们的发展和获得新技术及其应用的能力;是否有合格的管理及技术人员等诸多因素
+					</view>
+					<view class="MsoNormal">
+						四、用户隐私权的保护
+					</view>
+						<view class="MsoNormal">(一) 用户资料的收集</view>
+						<view class="MsoNormal">
+							本用户小程序用户在匿名的状态下即可访问并获取相关信息,本用户小程序请求用户提供相关用户信息之前,本用户小程序会释明用户相关信息用途。本公司承诺仅在与本公司经营活动和经营业务有关的合法目的下,并经用户同意以合法的方式收集必要的用户信息。
+						</view>
+						<view class="MsoNormal">
+							用户使用本用户小程序需注册后方提供的服务,用户需同意以下事项:依本用户小程序注册表之提示提供用户本人真实有效、正确、完整及最新的用户信息,并保证个人信息和资料的适时更新;本企业因用户所提供的不准确、不完整或未及时更新的用户信息给用户造成的任何损害,本公司不承担任何法律和赔偿责任。本公司因用户提供的不准确、不完整或未及时更新的用户信息给本公司造成损害的,本公司保留法律追责权利。
+						</view>
+						<view class="MsoNormal">(二) 用户资料的使用</view>
+						<view class="MsoNormal">
+							本公司有权为内部业务目的使用由您提供的用户信息,这类内部业务目的包括但不限于:促进本公司业务或本公司提供给客户的服务、产品的日常管理和运营;监测本用户小程序的使用及安全情况,实施内部控制、制备数据统计、进行规划和研究等;优化设计、服务和产品;市场调研和推广;记录、核对、确认及维护用户或其他相关人员的联络名单;解决争议、排除问题和执行本法律声明;服务本公司所涉及的任何经营活动或日常业务;或与上述任何一项目的直接相关的其它目的
+						</view>
+						<view class="MsoNormal">
+							基于以上目的,对用户信息有使用权的人,除本公司外,还包括经本公司合法授权、与本公司业务和经营活动有关的本公司的任何附属公司、联营公司、业务伙伴和专业顾问,以及向本公司提供或与本公司业务和经营活动有关的行政管理、电信服务、收付款服务、计算机信息服务或其它服务的任何代理人、承包商或第三方服务提供者。
+						</view>
+						<view class="MsoNormal">(三) 用户资料的披露</view>
+						<view class="MsoNormal">本用户小程序将采取合理有效的安全手段保护用户所提供的用户信息,在未经得用户许可前,本公司承诺并保证绝不将用户的任何个人信息和资料提供给任何无关第三方,但有下列情形之一的除外:</view>
+						<view class="MsoNormal">
+							<text>(</text>1<text>)根据法律、行政法规的规定,有权机关要求提供的</text>
+						</view>
+						<view class="MsoNormal">
+							<text>(</text>2<text>)由于用户对自身信息保密不当,导致用户非公开信息泄露的</text>
+						</view>
+						<view class="MsoNormal">
+							<text>(</text>3<text>)由于网络线路、黑客攻击、计算机病毒等原因造成的用户信息和资料泄露、丢失、被盗用或被篡改的</text>
+						</view>
+						<view class="MsoNormal">
+							<text>(</text>4<text>)为了保护本用户小程序用户的权利或财产</text>
+						</view>
+						<view class="MsoNormal">
+							<text>(</text>5<text>)在紧急的情况下,为了保护本用户小程序及其用户本人或公众安全的</text>
+						</view>
+						<view class="MsoNormal">
+							<text>(</text>6<text>)其他特殊或紧急情况</text>
+						</view>
+					<view class="MsoNormal">
+						五、知识产权保护
+					</view>
+					<view class="MsoNormal">
+						【本用户小程序所载述的域名、商标、商号、信息、文字、图形、图像、声音、链接、软件及所有其它内容或组成部分均由本公司、内容或组成部分提供商或第三方许可人提供。上述内容的知识产权依具体情况分别为本公司、内容或组成部分提供商或第三方许可人所拥有。
+					</view>
+					<view class="MsoNormal">
+						未经本公司或商标权人事先书面许可,任何单位或个人不得以任何方式或理由对本用户小程序的商标、商号或标识的任何部分进行使用、复制、修改、传播或与其它产品捆绑使用
+					</view>
+					<view class="MsoNormal">
+						除法律另有规定或双方另有约定外,未经本公司和/或其他权利人事先书面许可,任何人不得以任何形式通过任何方式(包括但不限于电子、机械复印或录制等方式)复制、展示、修改、转让、分发、重新发布、下载、张贴或传输本用户小程序的内容。
+					</view>
+					<view class="MsoNormal">
+						六、免责声明
+					</view>
+					<view class="MsoNormal">
+						本用户小程序所提供的信息,仅供参考之用,不宜用于任何研究或商业用途。本用户小程序并不保证该等信息的准确性、有效性、及时性或完整性。本公司及其员工无需以任何方式就任何信息传递或传送的失误、不准确或错误对用户或任何其他人士负任何直接或间接责任。在法律和本用户小程序允许的范围内,本公司不承担用户或任何第三方就使用或未能使用本用户小程序所提供的信息或任何链接所引致的任何直接、间接的损害赔偿(包括但不限于收益、预期利润的损失或失去的业务、未实现预期的节减)。本公司保留自行决定及修改、更正本用户小程序中任何内容和组成部分的权利(包括任何遗漏或错误的信息和内容)。本公司可以在任何时间对本用户小程序、本用户小程序上所描述的内容、产品、项目、服务或价格作出任何修改,而无需事前通知和事后说明,有需要的情况除外
+					</view>
+					<view class="MsoNormal">
+						本用户小程序、本用户小程序所述的内容、信息和材料以及在本用户小程序上可获得的软件等均“依原样”提供,不包含任何明示或暗示性质的陈述或保证。在法律许可或不禁止范围内,本公司承诺不作出任何明示或暗示性质的陈述和保证,包括但不限于有关安全性、准确性、完整性、适销性、无侵权、适合某项特别目的或没有计算机病毒的保证。您应为使用本用户小程序承担全部责任和风险。
+					</view>
+					<view class="MsoNormal">如发生以下情况,本用户小程序不对用户的直接或间接损失承担法律责任:</view>
+					<view class="MsoNormal">
+						<text>(</text>1<text>)营业中断</text>
+					</view>
+					<view class="MsoNormal">
+						<text>(</text>2<text>)无法登录本用户小程序,或登录本用户小程序延迟或中断</text>
+					</view>
+					<view class="MsoNormal">
+						<text>(</text>3<text>)数据无法传送、错误传送、毁损、灭失或其他修改</text>
+					</view>
+					<view class="MsoNormal">
+						<text>(</text>4<text>)因处理本用户小程序上对其它用户小程序的链接或本用户小程序上存在对其它用户小程序的链接所遭受的任何种类的损失或损害</text>
+					</view>
+					<view class="MsoNormal">
+						<text>(</text>5<text>)就客户使用本用户小程序(包括链接到第三方用户小程序或自第三方用户小程序链接)而可能产生的计算机病毒、系统失灵或功能紊乱</text>
+					</view>
+					<view class="MsoNormal">
+						<text>(</text>6<text>)本法律声明第四条第三项中规定的六种情形所引起的用户个人资料的披露或泄露</text>
+					</view>
+					<view class="MsoNormal">
+						<text>(</text>7<text>)因不可抗力或本公司无法合理控制的其他原因所导致的用户损失</text>
+					</view>
+					<view class="MsoNormal">
+						七、服务变更、暂停、中断或终止
+					</view>
+					<view class="MsoNormal">
+						如因系统升级或维护需要而暂停本用户小程序网络服务时,本用户小程序将尽可能事先通告。
+					</view>
+					<view class="MsoNormal">
+						如发生下列情形之一,本用户小程序有权随时暂停、中断或终止向用户提供本用户小程序用户服务而无需通知,并有权拒绝用户于当前、未来某时段或永久使用本用户小程序所提供服务之全部或任何部分:(1)用户提供任何错误、不实、过时、不完整或具有误导性的信息和资料,或本用户小程序有理由或有依据怀疑前述信息和资料为错误、不实、过时、不完整或具误导性的;(2)用户违反本法律声明或相关使用协议中规定的任何使用规则。
+					</view>
+					<view class="MsoNormal">
+						除上述情形外,本用户小程序同时保留在不事先通知用户的情况下随时暂停、中断或终止部分或全部网络服务的权利,对于所有服务的暂停、中断或终止而造成的任何损失,除法律另有规定或双方另有约定外,本公司不对用户或任何第三方承担任何法律责任。
+					</view>
+					<view class="MsoNormal">
+						八、条款修改
+					</view>
+					<view class="MsoNormal">
+						本公司保留在任何时间自行修改、增删本法律声明中的任何内容和条款的权利。您每次登录或使用本用户小程序时,均视为同意接受当时已生效的用户声明条款的制约。因此,建议您每次登录或使用本用户小程序时查看本法律声明是否修订或增删及修订或增删的情况
+					</view>
+					<view class="MsoNormal">
+						九、法律适用与司法管辖
+					</view>
+					<view class="MsoNormal">
+						本法律声明的制定、执行、解释及争议问题的解决和处理均适用中华人民共和国法律、法规。双方因使用本用户小程序所产生的争议,若协商不成,任何一方可向本公司所在地人民法院提起诉讼。
+					</view>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		components: {
+		},
+		data() {
+			return {
+			}
+		},
+		onLoad(option) {
+			uni.setNavigationBarTitle({
+				title: "服务协议"
+			})
+			// 开启分享
+			uni.showShareMenu({
+				withShareTicket: true,
+				menus: ['shareAppMessage', 'shareTimeline']
+			})
+		},
+		methods: {
+		}
+	}
+</script>
+
+<style lang="scss">
+	page{
+		width: 100%;
+		height: 100%;
+	}
+	.content{
+		padding: 0;
+		width: 100%;
+		height: 100%;
+		display: flex;
+		flex-direction: column;
+		.page-content{
+			flex: 1;
+			overflow-y: scroll;
+		}
+		.title{
+			font-size: 30rpx;
+			font-weight: 600;
+		}
+		.xy-cons{
+			padding: 30rpx;
+			line-height: 42rpx;
+			.MsoNormal{
+				text-indent: 52rpx;
+				margin-bottom: 14rpx;
+			}
+		}
+		.page-care{
+			width: 100%;
+			display: flex;
+			flex-direction: column;
+			align-items: center;
+			padding-top: 20rpx;
+		}
+		.footer{
+			width: 100%;
+			text-align: center;
+			line-height: 100rpx;
+			background-color: rgb(255,0,0);
+			color: #fff;
+		}
+	}
+</style>

+ 174 - 0
pages/index/index.vue

@@ -0,0 +1,174 @@
+<template>
+	<view class="content">
+		<view class="header">
+			<view>
+				<text>2021-03-15至2021-06-02</text>
+			</view>
+			<text>总计:其它垃圾2000,厨余垃圾1000,建筑垃圾0。</text>
+		</view>
+		<scroll-view class="scroll-con" scroll-y @scrolltolower="onreachBottom">
+			<!-- 列表数据 -->
+			<view class="cell-item" v-for="item in listdata">
+				<view class="cell-item-title" >
+					<view>
+						<text>2020-02-23 15:12:30</text>
+					</view>
+					<view>
+						<span>查看详情</span>
+						<u-icon name="arrow-right" size="28"></u-icon>
+					</view>
+				</view>
+				<view class="cell-item-cont">
+					<view class="cont-item">
+						<text>其它垃圾</text>
+						<text>1000kg</text>
+					</view>
+					<view class="cont-item">
+						<text>厨余垃圾</text>
+						<text>1000kg</text>
+					</view>
+					<view class="cont-item">
+						<text>建筑垃圾</text>
+						<text>1000kg</text>
+					</view>
+				</view>
+			</view>
+			<view class="nodata" v-if="listdata.length==0 && status!='loading'">
+				<u-empty :text="noDataText" mode="list"></u-empty>
+			</view>
+			<view class="loadmore">
+				<u-loadmore v-if="total>pageSize || status=='loading'" :status="status" />
+			</view>
+		</scroll-view>
+		<view class="footer">
+			<u-icon name="edit-pen" size="30"></u-icon>
+			<text>垃圾数据录入</text>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				listdata: [{},{},{},{}],
+				pageNo: 1,  //  当前页码
+				pageSize: 10,  //  每页条数
+				total: 0,  //  数据总条数
+				noDataText: '暂无数据',  //  列表请求状态提示语
+				status: 'loadmore',  //  加载中状态
+			}
+		},
+		onLoad() {
+			
+		},
+		onUnload() {
+			
+		},
+		onShareAppMessage(res) {
+		},
+		onShareTimeline(res) {
+		},
+		onShow() {
+		},
+		methods: {
+			// scroll-view到底部加载更多
+			onreachBottom() {
+				console.log(111111)
+				if(this.listdata.length < this.total){
+					this.pageNo += 1
+					// this.searchHandle()
+				}else{
+					uni.showToast({ title: '已经到底了', icon: 'none' })
+					this.status = "nomore"
+				}
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	page{
+	}
+	.content {
+		width: 100%;
+		height: 100%;
+		display: flex;
+		flex-direction: column;
+		justify-content: center;
+		align-items: center;
+		.header{
+			background-color: #FFFFFF;
+			display: flex;
+			flex-direction: column;
+			border-radius: 15upx;
+			padding: 20upx;
+			box-shadow: 0upx 3upx 6upx #eee;
+			margin-bottom: 20upx;
+			> text {
+				line-height: 50upx;
+			}
+		}
+		.scroll-con{
+			flex: 1;
+			width: 100%;
+			.cell-item{
+				width: 100%;
+				background-color: #fff;
+				border-radius: 15upx;
+				// padding: 20upx;
+				box-shadow: 0upx 3upx 6upx #eee;
+				.cell-item-title{
+					padding: 20upx 20upx 10upx;
+					width: 100%;
+					display: flex;
+					justify-content: space-between;
+					border-bottom: 1px solid #e6e6e6;
+				}
+				.cell-item-cont{
+					width: 100%;
+					display: flex;
+					justify-content: space-between;
+					padding: 20upx;
+					margin-bottom: 20upx;
+					.cont-item{
+						display: flex;
+						flex-direction: column;
+						align-items: center;
+						width: 33%;
+						border-right: 1px solid #e6e6e6;
+						:first-child {
+							padding: 26upx;
+							width: 100upx;
+							height: 100upx;
+							background-color: red;
+							border-radius: 50%;
+							font-size: 24upx;
+							display: flex;
+							align-items: center;
+							justify-content: center;
+							color: #fff;
+						}
+					}
+					:last-child {
+						border: none;
+					}
+				}
+			}
+		}
+		.footer{
+			z-index: 9999;
+			position: fixed;
+			bottom: 80upx;
+			width: 80%;
+			height: 80upx;
+			background-color: red;
+			opacity: 0.8;
+			border-radius: 40upx;
+			color: #fff;
+			display: flex;
+			align-items: center;
+			justify-content: center;
+		}
+	}
+</style>

+ 85 - 0
pages/login/login.scss

@@ -0,0 +1,85 @@
+.login-wrap, .forget-wrap, .apply-wrap{
+	background-color: #FFFFFF;
+	width: 100%;
+	height: 100vh;
+	box-sizing: border-box;
+	.logo{
+		display: flex;
+		justify-content: center;
+		padding: 100upx 0;
+	}
+	.login-form{
+		display: block;
+		width: 80%;
+		margin: 0 auto;
+		.form-item{
+			margin-bottom: 20upx;
+			position: relative;
+			display: flex;
+			align-items: center;
+			border: 1upx solid #dcdee2;
+			border-radius: 15upx;
+			.require-tip{
+				position: absolute;
+				left: 0;
+				top: 26upx;
+				color: red;
+			}
+			.item-inp{
+				flex-grow: 1;
+			}
+			.item-inp, .item-picker{
+				font-size: 30upx;
+				padding: 14upx 20upx;
+			}
+			.getcode-btn{
+				position: absolute;
+				right: 0upx;
+				top: 0;
+				font-size: 24upx;
+				background: #999;
+				color: #FFFFFF;
+				border: 0;
+				border-top-left-radius: 0;
+				border-bottom-left-radius: 0;
+				line-height: 3;
+				z-index: 10;
+			}
+		}
+		.flex-box{
+			display: flex;
+			justify-content: space-between;
+			font-size: 26upx;
+		}
+		.form-btn-con{
+			margin-top: 60upx;
+			.form-btn{
+				font-size: 32upx;
+				display: block;
+				line-height: 75upx;
+				margin: 0 auto;
+				background-color: #07c160;
+				color: #fff;
+			}
+			uni-button.form-btn:after{
+				border: none;
+			}
+			.apply-btn{
+				display: block;
+				text-align: center;
+				margin-top: 50upx;
+			}
+		}
+	}
+	
+	.authUserYs{
+		text-align: center;
+		position: absolute;
+		bottom: 5%;
+		width: 100%;
+		font-size: 24upx;
+		text{
+			color: #ff0000;
+		}
+	}
+}

+ 199 - 0
pages/login/login.vue

@@ -0,0 +1,199 @@
+<template>
+	<view class="content">
+		<view>
+			<view>
+				<view class="logo">
+					<u-image src="/static/logo.png" width="140" height="140" ></u-image>
+				</view>
+				<view class="login-btns">
+					<u-button shape="circle" :custom-style="wxButton" class="login-btn" type="success" :hair-line="false" open-type="getPhoneNumber" @getphonenumber="getPhoneNumber">
+						微信登录
+					</u-button>
+					<u-gap height="30"></u-gap>
+					<u-button shape="circle" :custom-style="phoneButton" class="login-btn" @click="phoneLogin" type="primary">
+						手机号码登录
+					</u-button>
+				</view>
+			</view>
+			<view class="nologin">
+				<u-button shape="circle" size="mini" plain lass="login-btn" @click="cansel" type="info">
+					暂不登录
+				</u-button>
+			</view>
+		</view>
+		<view @click="toYs">登录即代表同意<text>隐私政策</text>和<text>服务条款</text></view>
+	</view>
+</template>
+
+<script>
+	import {
+		login
+	} from "@/api/login"
+	export default {
+		components: {},
+		data() {
+			return {
+				path: '/pages/index/index',
+				lanuch: false,
+				code: '',
+				url:'', // 二维码参数
+				wxButton: {
+					background: '#07c160'
+				},
+				phoneButton: {
+					background: '#00aaff'
+				}
+			}
+		},
+		mounted() {},
+		onLoad(option) {
+			if(option.q !=undefined){
+				this.url=decodeURIComponent(option.q)
+			}
+			// 开启分享
+			uni.showShareMenu({
+				withShareTicket: true,
+				menus: ['shareAppMessage', 'shareTimeline']
+			})
+			// 为解决首次登录异常问题 进入页面先调微信登录获取code
+			this.wxLogin()
+			const {
+				path,
+				lanuch
+			} = option;
+			this.lanuch = (lanuch + '') === 'true';
+			if (path) {
+				this.path = decodeURIComponent(path);
+			}
+		},
+		methods: {
+			cansel(){
+				this.$store.state.vuex_noLogin = true
+				uni.reLaunch({
+					url: '/pages/index/index'
+				})
+			},
+			toYs(){
+				uni.navigateTo({
+					url: '/pages/agreement/agreement'
+				})
+			},
+			getPhoneNumber(e) {
+				let _this = this
+				uni.showLoading({
+					mask: true,
+					title: '加载中'
+				});
+				if (!this.code) {
+					this.wxLogin()
+				} 
+				console.log(this.code, e)
+				if (e.target.errMsg === 'getPhoneNumber:ok') {
+					setTimeout(()=>{
+						const params={
+							code: _this.code,
+							encryptedData: e.target.encryptedData,
+							iv: e.target.iv,
+						}
+						if(_this.url){
+							params.encode=_this.url
+						}
+						login(params).then(res => {
+							uni.hideLoading();
+							_this.code = ''
+							console.log(res,_this.path, 'login data')
+							if (res.status == '200') {
+								getApp().globalData.token = res.data
+								_this.$u.vuex('vuex_token',res.data)
+								uni.$emit("getUserInfo")
+								if (_this.path === '/pages/index/index' || _this.lanuch) {
+									uni.reLaunch({
+										url: _this.path
+									});
+								} else {
+									uni.redirectTo({
+										url: _this.path
+									});
+								}
+							} else {
+								uni.showToast({
+									title: res.message,
+									icon: 'none',
+									duration: 2500
+								});
+							}
+						});
+					},300)
+				} else {
+					uni.showToast({
+						title: '授权失败',
+						icon: 'none',
+						duration: 2500
+					});
+				}
+			},
+			// 调微信登录 获取code
+			wxLogin () {
+				let _this = this
+				uni.login({
+					provider: 'weixin',
+					success(res) {
+						console.log(res.code)
+						_this.code = res.code
+					}
+				})
+			},
+			// 手机号登录
+			phoneLogin () {
+				if(this.url){
+					wx.navigateTo({
+						url: '/pages/login/phoneLogin?path='+this.path+'&lanuch='+this.lanuch+'&encode='+this.url 
+					})
+				}else{
+					wx.navigateTo({
+						url: '/pages/login/phoneLogin?path='+this.path+'&lanuch='+this.lanuch
+					})
+				}
+			  
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	.content {
+		width: 100%;
+		height: 100%;
+		padding: 40upx;
+		display: flex;
+		flex-direction: column;
+		> view{
+			text-align: center;
+			&:first-child{
+				flex-grow: 1;
+				height: 80vh;
+				padding: 20% 10%;
+				display: flex;
+				flex-direction: column;
+				justify-content: space-between;
+			}
+			&:last-child{
+				font-size: 24upx;
+				text{
+					color: #07c160;
+				}
+			}
+			.logo{
+				text-align: center;
+				display: flex;
+				justify-content: center;
+			}
+			.login-btns{
+				padding: 60upx 0;
+				.login-btn{
+					margin: 30upx 0;
+				}
+			}
+		}
+	}
+</style>

+ 212 - 0
pages/login/phoneLogin.vue

@@ -0,0 +1,212 @@
+<template>
+	<view class="forget-wrap">
+		<view class="logo">
+			<u-image src="/static/logo.png" width="140" height="140" ></u-image>
+		</view>
+		<form class="login-form" @submit="formSubmit">
+			<view class="form-item">
+				<input v-model="form.phone" class="item-inp" name="phone" type="number" maxlength="11" placeholder="请输入登录手机号" />
+			</view>
+			<view class="form-item">
+				<input v-model="form.verifyCode" class="item-inp" name="verifyCode" type="number" maxlength="6" placeholder="请输入短信验证码" />
+				<button class="getcode-btn" :disabled="isDisable" @click="getCodeValidate">{{nowVal}}</button>
+			</view>
+			<view class="form-btn-con">
+				<button class="form-btn" @click="submitFn">登录</button>
+			</view>
+		</form>
+		<!-- 图文验证码 -->
+		<uni-popup ref="imageTxtPopup" type="center">
+			<popup-con title="图文验证码" :popBtn="popBtn" :changeImg="changeImg" @captchaImg="captchaVerify"></popup-con>
+		</uni-popup>
+	</view>
+</template>
+
+<script>
+	import { isvalidPhone } from '@/libs/validate.js'
+	import uniPopup from '@/components/uni-popup/uni-popup.vue'
+	import popupCon from '@/components/uni-popup/popup-con.vue'
+	import { loginPhone, sendVerifyCode, checkCustomerState } from '@/api/user.js'
+	export default{
+		components: {
+			uniPopup,
+			popupCon
+		},
+		data(){
+			return{
+				form: {
+					phone: '',
+					verifyCode: '',
+					password: '',
+					passwords: ''
+				},
+				nowVal: '获取验证码',  //  按钮显示内容
+				count: '',  //  当前秒数
+				timer: null,  //  倒计时
+				isDisable: false,  //  是否禁止获取验证码
+				popBtn: [],  //  name为操作按钮名称,color为操作按钮颜色值
+				changeImg: false,  //  是否重新更换图形验证码
+				randomCode: '',  //  图片验证码随机码
+				wxcode: '',
+				path: '/pages/index/index',
+				lanuch: false,
+				encode:'' // 二维码地址
+			}
+		},
+		onLoad(opts) {
+			console.log(opts,'----参数')
+			this.encode=opts.encode
+			console.log(opts,this.encode,'----参数')
+			// 开启分享
+			uni.showShareMenu({
+				withShareTicket: true,
+				menus: ['shareAppMessage', 'shareTimeline']
+			})
+			const {
+				path,
+				lanuch,
+				encode
+			} = option;
+			// console.log(option,'----参数')
+			this.lanuch = (lanuch + '') === 'true';
+		},
+		onUnload() {
+			uni.$off('getUserInfo')
+		},
+		methods: {
+			//   登录
+			submitFn(){
+				const _this = this
+				//  表单校验
+				if(this.form.phone == ''){
+					uni.showToast({icon: 'none',title: '请输入登录手机号'})
+					return
+				}
+				if(!isvalidPhone(this.form.phone)){
+				  uni.showToast({icon: 'none',title: '请输入正确的手机号码'})
+				  return
+				}
+				if(this.form.verifyCode == ''){
+					uni.showToast({icon: 'none',title: '请输入验证码'})
+					return
+				}
+				// 校验短信验证码是否正确
+				const obj = {}
+				obj.phone = _this.form.phone
+				obj.random = _this.randomCode  // 当前随机码
+				obj.verifiCode = _this.form.verifyCode
+				if(this.encode){
+					obj.encode=this.encode
+				}
+				uni.login({
+					provider: 'weixin',
+					success(res) {
+						console.log(res)
+						_this.wxcode = res.code
+						obj.code = res.code
+						loginPhone(obj).then(res => {
+						  if(res.status == 200){
+							console.log(res,'login success')
+							getApp().globalData.token = res.data
+							_this.$u.vuex('vuex_token',res.data)
+							uni.$emit("getUserInfo")
+						    if (_this.path === '/pages/index/index' || _this.lanuch) {
+						    	uni.reLaunch({
+						    		url: _this.path
+						    	});
+						    } else {
+						    	uni.redirectTo({
+						    		url: _this.path
+						    	});
+						    }
+						  }else{
+							  uni.showToast({icon: 'none',title: res.message})
+						  }
+						})
+					}
+				})
+			},
+			//  单击获取验证码
+			getCodeValidate(){
+				if(this.form.phone){
+					if(!isvalidPhone(this.form.phone)){
+						uni.showToast({icon: 'none',title: '请输入正确的手机号码'})
+						return
+					}else{
+						// 先校验手机号用户是否禁用
+						checkCustomerState({phone:this.form.phone}).then(res=>{
+							if(res.status == 200) {
+								this.retsetCode()
+								//  图文验证码
+								this.$refs.imageTxtPopup.open()
+							} else {
+								uni.showToast({
+									title: res.message,
+									icon: 'none'
+								})
+							}
+						})
+					}
+				}else{
+					uni.showToast({icon: 'none',title: '请先输入手机号'})
+				}
+			},
+			//  开始倒计时
+			getCodeFun(){
+				const TIME_COUNT = 60
+				if (!this.timer) {
+				  this.count = TIME_COUNT
+				  this.timer = setInterval(() => {
+				    if (this.count > 0 && this.count <= TIME_COUNT) {
+				      this.count--
+				      this.nowVal = this.count + 's后重发'
+				      this.isDisable = true
+				    } else {
+				      this.nowVal = '重新获取验证码'
+				      clearInterval(this.timer)
+				      this.timer = null
+				      this.isDisable = false
+				    }
+				  }, 1000)
+				}
+			},
+			//  验证图片验证码
+			captchaVerify(code, nowRandomCode){
+				const _this = this
+				let obj = {}
+				obj.phone = this.form.phone
+				obj.random = nowRandomCode
+				obj.code = code
+				//  发送短信验证码
+				sendVerifyCode(obj).then(res => {
+					console.log(JSON.stringify(res))
+					if(res.status == 200){  //  验证码输入正确
+						_this.randomCode = nowRandomCode  //  图片验证码随机码
+						//  关闭   图形验证码 弹框
+						_this.$refs.imageTxtPopup.close()
+						//  开启倒计时
+						_this.getCodeFun()
+						uni.showToast({icon: 'none',title: '验证码发送成功'})
+					}else {  //  验证码输入错误
+						_this.retsetCode()
+						uni.showToast({icon: 'none',title: res.message})
+					}
+				})
+			},
+			// 重新触发获取图片验证码
+			retsetCode(){
+				const _this = this
+				_this.form.verifyCode = ''
+				_this.randomCode = ''
+				//  切换验证码重新校验
+				_this.changeImg = false
+				_this.$nextTick(function(){
+					_this.changeImg = true
+				})
+			}
+		}
+	}
+</script>
+<style scoped lang="scss">
+	@import './login.scss';
+</style>

+ 71 - 0
pages/userCenter/aboutUs.vue

@@ -0,0 +1,71 @@
+<template>
+	<view class="container">
+		<view class="logoContent">
+			<view class="logoBorder">
+				<image class="logoImg" src="/static/logo.png"></image>
+			</view>
+		</view>
+		<view class="textContent">
+			<view>智享亿家作为中国出色的垃圾分类专家和平台,致力于改变中国社区环保现状,通过多年的耕耘和积累,打造的城市垃圾分类一体化智能解决方案已获得咸阳、西安航天等多地市的环保垃圾处理资格,并成为全国住建部在咸阳市的环保模范单位。</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		onLoad() {
+			// 开启分享
+			uni.showShareMenu({
+				withShareTicket: true,
+				menus: ['shareAppMessage', 'shareTimeline']
+			})
+		},
+	}
+</script>
+
+<style lang="less">
+	page{
+		background: #FFFFFF;
+	}
+	.container{
+		font-size: 32rpx;
+		padding:0 50rpx;
+		text-align: center;
+		color:#666;
+		.logoContent{
+			.logoBorder{
+				width:200rpx;
+				height:200rpx;
+				border:2px solid #eee;
+				border-radius: 20rpx;
+				margin:50rpx 0 15rpx 0;
+				display: inline-block;
+				position: relative;
+				.logoImg{
+					width:180rpx;
+					height:180rpx;
+					position: absolute;
+					top:0;
+					left:0;
+					right:0;
+					bottom:0;
+					margin:auto;
+				}
+			}
+		}
+		.textContent{
+			text-align: left;
+			margin-top: 30rpx;
+			font-size: 32rpx;
+			.tetxTitle{
+				margin-bottom: 10rpx;
+				font-size: 32rpx;
+			}
+			> view{
+				padding: 16upx 0;
+				line-height: 50upx;
+				text-indent: 60rpx;
+			}
+		}
+	}
+</style>

+ 166 - 0
pages/userCenter/deliveryRecord.vue

@@ -0,0 +1,166 @@
+<template>
+	<view class="deliveryRecord-container">
+		<scroll-view class="scroll-con" scroll-y @scrolltolower="onreachBottom">
+			<!-- 列表数据 -->
+			<view class="cell-item-con">
+				<u-gap height="15"></u-gap>
+				<view class="cell-item" v-for="(item, index) in listdata" :key="index">
+					<text class="cell-item-date">{{item.deliveryTime}}</text>
+					<view class="cell-item-main">
+						<u-image mode="scaleToFill" shape="circle" width="80rpx" height="80rpx" :src="`/static/${item.rubbishType}.png`" class="cell-item-pic"></u-image>
+						<view class="cell-item-r">
+							<view class="cell-item-head">
+								<view class="cell-item-val">
+									<text class="cell-item-num">{{item.rubbishWeight}}</text>克
+							    </view>
+								<view class="cell-item-ledu">
+									<text>+{{item.goldNum}}</text>
+								    <u-image mode="scaleToFill" width="34rpx" height="34rpx" src="/static/ledou.png"></u-image>
+								</view>
+							</view>
+							<text class="cell-item-des">{{item.stationName}}</text>
+						</view>
+					</view>
+				</view>
+			</view>
+			<u-empty class="nodata" :text="noDataText" v-if="listdata.length==0 && status!='loading'" mode="list"></u-empty>
+			<view class="loadmore">
+				<u-loadmore v-if="total>pageSize || status=='loading'" :status="status" />
+			</view>
+		</scroll-view>
+	</view>
+</template>
+
+<script>
+	import {
+		getDeliveryLogList
+	} from '@/api/deliveryRecord.js'
+	export default {
+		data() {
+			return {
+				listdata: [],
+				pageNo: 1,  //  当前页码
+				pageSize: 10,  //  每页条数
+				total: 0,  //  数据总条数
+				noDataText: '暂无数据',  //  列表请求状态提示语
+				status: 'loadmore',  //  加载中状态
+			}
+		},
+		onLoad() {
+			// 开启分享
+			uni.showShareMenu({
+				withShareTicket: true,
+				menus: ['shareAppMessage', 'shareTimeline']
+			})
+		},
+		onShow() {
+			this.searchHandle(1)
+		},
+		methods:{
+			// 搜索查询
+			searchHandle (pageNo,suppName) {
+				this.status = "loading"
+				pageNo ? this.pageNo = pageNo : null
+				getDeliveryLogList({
+					pageNo: this.pageNo,
+					pageSize: this.pageSize,
+				}).then(res => {
+					if (res.status == 200) {
+						if(this.pageNo>1){
+							this.listdata = this.listdata.concat(res.data.list || [])
+						}else{
+							this.listdata = res.data.list || []
+						}
+						this.total = res.data.count || 0
+					} else {
+						this.noDataText = res.message
+						this.listdata = []
+						this.total = 0
+					}
+					this.status = "loadmore"
+				})
+			},
+			// scroll-view到底部加载更多
+			onreachBottom() {
+				if(this.listdata.length < this.total){
+					this.pageNo += 1
+					this.searchHandle()
+				}else{
+					uni.showToast({ title: '已经到底了', icon: 'none' })
+					this.status = "nomore"
+				}
+			}
+		}
+	}
+</script>
+
+<style lang="less">
+	page{
+		height: 100%;
+	}
+	.deliveryRecord-container{
+		width: 100%;
+		height: 100%;
+		//  列表
+		.scroll-con{
+			height: 100%;
+			overflow: auto;
+		}
+		//  列表样式
+		.cell-item-con{
+			.cell-item{
+				background-color: #fff;
+				color: #606266;
+				padding: 20rpx 32rpx 22rpx;
+				border-bottom: 1rpx solid #EEEEEE;
+				&:last-child{
+					border:0;
+				}
+				.cell-item-date{
+					display: block;
+					color: #909399;
+					font-size: 24rpx;
+				}
+				.cell-item-main{
+					display: flex;
+					justify-content: space-around;
+					align-items: center;
+					padding-top: 14rpx;
+					.cell-item-pic{
+						flex-shrink: 0;
+						margin-right: 20rpx;
+					}
+					.cell-item-r{
+						flex-grow: 1;
+						.cell-item-head{
+							display: flex;
+							justify-content: space-between;
+							align-items: center;
+							.cell-item-val{
+								font-size: 30rpx;
+								font-weight: bold;
+								.cell-item-num{
+									display: inline-block;
+									margin-right: 6rpx;
+								}
+							}
+							.cell-item-ledu{
+								display: flex;
+								justify-content: space-between;
+								align-items: center;
+								text{
+									font-size: 40rpx;
+									color: #01c9b2;
+								}
+							}
+						}
+						.cell-item-des{
+							color: #909399;
+							font-size: 26rpx;
+						}
+					}
+				}
+			}
+		}
+	}
+</style>

+ 40 - 0
pages/userCenter/fenlei.vue

@@ -0,0 +1,40 @@
+<template>
+	<view class="zn-container">
+		<view>
+      <u-image width="750rpx" height="1988rpx" src="https://lese.sxzxyj.net/img/2020/11/28/4cbe4bf9213a4a5c8d658e4903ec30e0.jpg"></u-image>
+      <u-image width="750rpx" height="2211rpx" src="https://lese.sxzxyj.net/img/2020/11/28/83daa070fe8f4bb9a8c583cd05bfa17b.jpg"></u-image>
+      <u-image width="750rpx" height="2219rpx" src="https://lese.sxzxyj.net/img/2020/11/28/da1d20d4003f4d8e8b0d836d0e4dfbed.jpg"></u-image>
+      <u-image width="750rpx" height="2277rpx" src="https://lese.sxzxyj.net/img/2020/11/28/50723317eb0b43908019709ac183ad6a.jpg"></u-image>
+      <u-image width="750rpx" height="1905rpx" src="https://lese.sxzxyj.net/img/2020/11/28/c0acd89eb40a4050a7af8ac4f8855489.jpg"></u-image>
+      <u-image width="750rpx" height="1958rpx" src="https://lese.sxzxyj.net/img/2020/11/28/df135dea14014a25b93d5ae0bb260beb.jpg"></u-image>
+    </view>
+	</view>
+</template>
+
+<script>
+	export default{
+		data() {
+			return {
+				baseUrl: ''
+			}
+		},
+		onLoad() {
+			// 开启分享
+			uni.showShareMenu({
+				withShareTicket: true,
+				menus: ['shareAppMessage', 'shareTimeline']
+			})
+		}
+	}
+</script>
+
+<style lang="less">
+	.zn-container{
+		width: 100%;
+    background-color: #FFFFFF;
+    > view{
+      width: 100%;
+      margin: 0 auto;
+    }
+	}
+</style>

+ 194 - 0
pages/userCenter/index.vue

@@ -0,0 +1,194 @@
+<template>
+	<view class="user-content">
+		<!-- 头部 -->
+		<view class="user-head" @click="toUserPage">
+			<view class="user-photo">
+				<open-data type="userAvatarUrl"></open-data>
+				<u-image v-show="!hasLogin" src="/static/logo.png" width="100" height="100"></u-image>
+			</view>
+			<view class="user-info">
+				<view v-if="hasLogin" class="user-info-item">{{user.mobile||'--'}}</view>
+				<view v-if="hasLogin" class="user-info-item">注册时间:{{user.registerTime||'--'}}</view>
+				<u-button v-else="!hasLogin" plain size="mini" shape="circle" @click="toLoginPage">立即登录</u-button>
+			</view>
+			<view v-if="hasLogin">
+				<u-icon name="arrow-right" color="#fff"></u-icon>
+			</view>
+		</view>
+		<view class="my-ledou" v-if="hasLogin">
+			<view class="user-jifen">
+				<view>我的乐豆:<text>{{user.currentGold||0}}</text></view>
+				<u-image mode="scaleToFill" width="35rpx" height="35rpx" src="/static/ledou.png"></u-image>
+			</view>
+			<view>
+				<u-button size="mini" type="warning" shape="circle" @click="toLdPage">查看明细</u-button>
+			</view>
+		</view>
+		<!-- 列表 -->
+		<view class="user-list">
+			<u-cell-group>
+				<u-cell-item icon="question-circle" icon-size="39" :icon-style="{color:'#19be6b'}" index="2" @click="toPage" title="常见问题"></u-cell-item>
+				<u-cell-item icon="phone" icon-size="40" :icon-style="{color:'#f00'}" @click="callPhone" title="联系客服"></u-cell-item>
+				<u-cell-item icon="file-text" icon-size="40" :icon-style="{color:'#ffaa00'}" index="3" @click="toPage" title="服务协议"></u-cell-item>
+				<u-cell-item icon="info-circle" icon-size="38" :icon-style="{color:'#55aa00'}" index="4" @click="toPage" title="关于我们"></u-cell-item>
+			</u-cell-group>
+		</view>
+	</view>
+</template>
+
+<script>
+	import { checkLogin, getUserInfo } from '@/api/login.js'
+	export default {
+		data() {
+			return {
+				hasLogin: false,
+				userData: {}
+			};
+		},
+		onShow() {
+			checkLogin().then(res => {
+				this.hasLogin = res.status == 200
+				if(this.hasLogin){
+					this.getUserInfo()
+				}else{
+					// uni.navigateTo({
+					// 	url:"/pages/login/login"
+					// })
+				}
+			})
+		},
+		computed: {
+			user() {
+				return this.userData 
+			}
+		},
+		onLoad() {
+			// 开启分享
+			uni.showShareMenu({
+				withShareTicket: true,
+				menus: ['shareAppMessage', 'shareTimeline']
+			})
+		},
+		methods:{
+			// 获取用户信息
+			getUserInfo(){
+				getUserInfo().then(res => {
+					console.log(res,'getUserInfo')
+					if(res.status == 200){
+						this.$u.vuex("vuex_userData",res.data)
+						this.userData = res.data
+						console.log(this.userData,'-----用户信息')
+					}
+				})
+			},
+			// 打开对应的页面
+			toPage(index){
+				console.log(index)
+				let pageUrl=[
+					'/pagesA/order/order',
+					'/pages/userCenter/deliveryRecord',
+					'/pages/userCenter/question',
+					'/pages/agreement/agreement?fromPage=usercenter',
+					'/pages/userCenter/aboutUs',
+					'/pagesA/luckDraw/myJp?fromPage=usercenter',
+					'/pagesA/giftCard/giftCard'
+					]
+				uni.navigateTo({
+					url: pageUrl[index]
+				})
+			},
+			// 电话客服
+			callPhone () {
+				uni.makePhoneCall({
+				    phoneNumber: this.$store.state.vuex_kfMobile
+				})
+			},
+			//  跳转
+			toUserPage(){
+				//  未登录跳转登录,已登录跳转用户信息页
+				if(this.hasLogin){
+					uni.navigateTo({
+						url: '/pages/userCenter/userInfo/userInfo'
+					})
+				}else{
+					this.toLoginPage()
+				}
+			},
+			//  乐豆明细
+			toLdPage(){
+				uni.navigateTo({
+					url: '/pages/userCenter/ldDetailed'
+				})
+			},
+			bindGetUserInfo(res){
+				console.log(res)
+			},
+			toLoginPage(){
+				uni.navigateTo({
+					url: '/pages/login/login'
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	.user-content{
+		width: 100%;
+		.user-head{
+			background: #01c9b2;
+			display: flex;
+			align-items: center;
+			padding: 35rpx 55rpx;
+			.user-info{
+				flex-grow: 1;
+				padding: 0 20rpx;
+				color: #FFFFFF;
+				.user-info-item{
+					margin: 10rpx 0;
+				}
+			}
+			.user-photo{
+				width: 100rpx;
+				height: 100rpx;
+				border-radius: 50%;
+				overflow: hidden;
+				margin-right: 5rpx;
+			}
+		}
+		.my-ledou{
+			background: #FFFFFF;
+			border-radius: 15upx;
+			display: flex;
+			align-items: center;
+			justify-content: space-between;
+			padding: 20upx;
+			margin: 20upx;
+			box-shadow: 0upx 3upx 6upx #eee;
+			.user-jifen{
+				display: flex;
+				align-items: center;
+				text{
+					font-size: 40upx;
+					font-weight: bold;
+					color: red;
+				}
+				>view{
+					&:first-child{
+						display: flex;
+						align-items: center;
+					}
+				}
+			}
+		}
+		.user-list{
+			border-radius: 15upx;
+			margin: 20upx;
+			overflow: hidden;
+			box-shadow: 0upx 3upx 6upx #eee;
+		}
+		.user-btn{
+			padding: 85rpx 200rpx;
+		}
+	}
+</style>

+ 254 - 0
pages/userCenter/ldDetailed.vue

@@ -0,0 +1,254 @@
+<template>
+	<view class="ldDetailed-container">
+		<!-- 筛选菜单 -->
+		<u-dropdown ref="uDropdown" class="filter-dropdown">
+			<u-dropdown-item v-model="queryParam.changeType" :title="titType" :options="optionsType" @change="searchHandle(1)"></u-dropdown-item>
+			<u-dropdown-item v-model="timeScope" :title="titScope" :options="optionsScope" @change="searchHandle(1)"></u-dropdown-item>
+		</u-dropdown>
+		<scroll-view class="scroll-con" scroll-y @scrolltolower="onreachBottom">
+			<!-- 列表数据 -->
+			<view class="cell-item-con">
+				<view class="cell-item" v-for="(item, index) in listdata" :key="index">
+					<!-- <text class="cell-item-month" v-if="timeScope == 'threeMonth'">{{item.month}}</text> -->
+					<view class="cell-item-main" >
+						<view :class="['cell-item-pic']">
+							<u-image mode="scaleToFill" v-if="item.changeType == 'ADD'" shape="circle" width="70rpx" height="70rpx" src="/static/tudi.png"></u-image>
+							<u-image mode="scaleToFill" v-else shape="circle" width="70rpx" height="70rpx" src="/static/bued.png"></u-image>
+						</view>
+						<view class="cell-item-c">
+							<text class="cell-item-des">{{item.operateTypeDictValue||'--'}}</text>
+							<text class="cell-item-date">{{item.createDate}}</text>
+						</view>
+						<view :class="['cell-item-score', item.changeType == 'ADD' ? 'black' : 'red']">
+							<text>{{item.changeType == 'ADD' ? '+'+item.changeNum : '-'+item.changeNum}}</text>
+							<u-image mode="scaleToFill" width="40rpx" height="40rpx" src="/static/ledou.png"></u-image>
+						</view>
+					</view>
+					<!-- <view class="nodata" v-if="item.monthDataList.length==0 && status!='loading'">
+						<u-empty :text="noDataText" mode="list"></u-empty>
+					</view> -->
+				</view>
+			</view>
+			<view class="nodata" v-if="listdata.length==0 && status!='loading'">
+				<u-empty :text="noDataText" mode="list"></u-empty>
+			</view>
+			<view class="loadmore">
+				<u-loadmore v-if="total>pageSize || status=='loading'" :status="status" />
+			</view>
+		</scroll-view>
+	</view>
+</template>
+
+<script>
+	import {ldUsedQuery} from '@/api/user.js'
+	import getDate from '@/libs/getDate'
+	import moment from 'moment'
+	export default {
+		data() {
+			return {
+				listdata: [],
+				pageNo: 1,  //  当前页码
+				pageSize: 10,  //  每页条数
+				total: 0,  //  数据总条数
+				noDataText: '暂无数据',  //  列表请求状态提示语
+				status: 'loadmore',  //  加载中状态
+				timeScope: 'threeMonth', // 时间类别 1:近三个月 2:本月 3:本月前一月 4:本月前2月
+				queryParam: {  //  过滤条件
+					changeType: '', // 金币变更类型
+				},
+				optionsType: [
+					{ label: '所有记录', value: '', },
+					{ label: '获取记录', value: 'ADD', },
+					{ label: '使用记录', value: 'SUB', }
+				]
+			}
+		},
+		onShow() {
+			this.searchHandle(1)
+		},
+		onLoad() {
+			this.timeScope = 'threeMonth'
+			this.queryParam.changeType = ''
+			// 开启分享
+			uni.showShareMenu({
+				withShareTicket: true,
+				menus: ['shareAppMessage', 'shareTimeline']
+			})
+		},
+		computed: {
+			// 时间过滤条件
+			optionsScope(){  //  过滤条件  近3个月
+				let arr = [
+					{ label: '近3个月', value: 'threeMonth' },
+					{ label: '本月', value: 'thisMonth' }
+				]
+				arr[2] = { label: new Date().getMonth() + '月', value: 'lastMonth' }
+				arr[3] = { label: new Date().getMonth()-1 + '月', value: 'lastTwoMonth' }
+				return arr
+			},
+			titType(){  //  过滤条件 所有记录 title
+				const row = this.optionsType.find(item => item.value == this.queryParam.changeType)
+				return row ? row.label : this.optionsType[0].label
+			},
+			titScope(){  //  过滤条件 近3个月 title
+				const row = this.optionsScope.find(item => item.value == this.timeScope)
+				return row ? row.label : this.optionsScope[0].label
+			},
+			// 查询时间段
+			queryDate() {
+				var dt = new Date();
+				const threeTime = dt.setMonth( dt.getMonth()-2 )
+				const quickType = {
+				  threeMonth: [moment(getDate.getThreeMonthDays().starttime), moment(getDate.getThreeMonthDays().endtime)],
+				  lastMonth: [moment(getDate.getLastMonthDays().starttime), moment(getDate.getLastMonthDays().endtime)],
+				  thisMonth: [moment(getDate.getCurrMonthDays().starttime), moment(getDate.getCurrMonthDays().endtime)],
+				  lastTwoMonth: [moment(getDate.getLastTwoMonthDays().starttime), moment(getDate.getLastTwoMonthDays().endtime)],
+				}
+				let queryTime = {
+					beginDate: quickType[this.timeScope][0].format('YYYY-MM-DD'),
+					endDate: quickType[this.timeScope][1].format('YYYY-MM-DD')
+				}
+				return queryTime
+			},
+			
+		},
+		methods:{
+			// 搜索查询
+			searchHandle (pageNo) {
+				this.pageNo = pageNo ? pageNo : this.pageNo
+				if(this.pageNo == 1){
+					this.listdata = []
+				}
+				this.status = "loading"
+				let params = Object.assign(this.queryParam,this.queryDate)
+				params.pageNo = this.pageNo
+				params.pageSize = this.pageSize
+				console.log(params,'pppppppp')
+				ldUsedQuery(params).then(res => {
+					console.log(res,'pppppppp')
+					if (res.status == 200) {
+						if(this.pageNo>1){
+							this.listdata = this.listdata.concat(res.data.list || [])
+						}else{
+							this.listdata = res.data.list || []
+						}
+						this.total = res.data.count || 0
+					} else {
+						this.noDataText = res.message
+						this.listdata = []
+						this.total = 0
+					}
+					this.status = "loadmore"
+				})
+			},
+			// scroll-view到底部加载更多
+			onreachBottom() {
+				console.log(111111)
+				if(this.listdata.length < this.total){
+					this.pageNo += 1
+					this.searchHandle()
+				}else{
+					uni.showToast({ title: '已经到底了', icon: 'none' })
+					this.status = "nomore"
+				}
+			}
+		}
+	}
+</script>
+
+<style lang="less">
+	page{
+		height: 100%;
+		background-color: #fff;
+	}
+	.ldDetailed-container{
+		width: 100%;
+		height: 100%;
+		display: flex;
+		flex-direction: column;
+		.nodata{
+			margin: 50upx 0;
+		}
+		// 筛选菜单
+		.filter-dropdown{
+			background-color: #fff!important;
+			.u-dropdown__menu__item{
+				background-color: #fff!important;
+			}
+			
+		}
+		> u-dropdown{
+			flex: none !important;
+		}
+		//  列表
+		.scroll-con{
+			// height: calc(100% - 40);
+			flex: 1;
+			overflow: auto;
+		}
+		//  列表样式
+		.cell-item-con{
+			.cell-item{
+				background-color: #fff;
+				color: #606266;
+				.cell-item-month{
+					display: block;
+					color: #909399;
+					font-size: 26rpx;
+					padding: 16rpx 30rpx;
+					background-color: #f8f8f8;
+				}
+				.cell-item-main{
+					display: flex;
+					align-items: center;
+					padding: 20rpx 30rpx;
+					border-bottom: 1rpx solid #e9e9e9;
+					.cell-item-pic{
+						flex-shrink: 0;
+						width: 90rpx;
+						height: 90rpx;
+						line-height: 90rpx;
+						background-color: #FFFFFF;
+						border-radius: 50%;
+						text-align: center;
+						display: flex;
+						align-items: center;
+					}
+					.cell-item-c{
+						flex-grow: 1;
+						.cell-item-des{
+							display: block;
+							color: #606266;
+							font-size: 30rpx;
+							margin-bottom: 10rpx;
+						}
+						.cell-item-date{
+							display: block;
+							color: #909399;
+							font-size: 24rpx;
+						}
+					}
+					.cell-item-score{
+						flex-shrink: 0;
+						font-size: 40rpx;
+						display: flex;
+						align-items: center;
+					}
+					.black{
+						color: #01c9b2;
+					}
+					.red{
+						color: red;
+					}
+					.orange{
+						background-color: #fcbd71;
+						color: #fff;
+					}
+				}
+				.cell-item-main:last-child{
+					border: none;
+				}
+			}
+		}
+	}
+</style>

+ 63 - 0
pages/userCenter/myCode/myCode.vue

@@ -0,0 +1,63 @@
+<template>
+	<view class="codes">
+		<view class="canvasbox">
+			<canvas style="width: 200px;height: 200px;" canvas-id="myQrcode"></canvas>
+		</view>
+		<view class="text">
+			请将二维码对准设备摄像头
+		</view>
+	</view>
+</template>
+
+<script>
+	import drawQrcode from 'weapp-qrcode'
+	export default {
+		data() {
+			return {
+				qrcode: null
+			}
+		},
+		onLoad() {
+			this.creatQrCode()
+			// 开启分享
+			uni.showShareMenu({
+				withShareTicket: true,
+				menus: ['shareAppMessage', 'shareTimeline']
+			})
+		},
+		methods: {
+			creatQrCode() {
+			  let userData = this.$store.state.vuex_userData
+			  console.log(userData)
+			  drawQrcode({
+			    width: 200,
+			    height: 200,
+			    canvasId: 'myQrcode',
+			    text: `userId=${userData.customNo}&type=user`
+			  })
+			}
+		}
+	}
+</script>
+
+<style>
+.codes{
+	width: 100%;
+	padding: 50rpx;
+}
+.canvasbox{
+	box-shadow: 3upx 3upx 6upx #eee;
+	background: #FFFFFF;
+	height: 230px;
+	width: 230px;
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	margin: 50rpx auto;
+}
+.text{
+	text-align: center;
+	padding: 20rpx;
+	color: #999999;
+}
+</style>

+ 70 - 0
pages/userCenter/question.vue

@@ -0,0 +1,70 @@
+<template>
+	<view class="lc" >
+		<view class="lc-container">
+			<!-- 常见问题 -->
+			<view class="lc-container-xs"  style="font-size: 32rpx;">
+				<view class="lc-container-title" >Q&A常见问题</view>
+				<view class="lc-container-step" >Q: 为什么投放垃圾后乐豆没有到账 ?</view>
+				<view class="lc-container-step-item">A: 每次需要投放最小计算单位以上的重量才会有乐豆奖励。</view>
+				<view class="lc-container-step" >Q: 如何修改支付密码 ?</view>
+				<view class="lc-container-step-item">A: 点击“我的”,前往[个人中心],进入“支付密码”页面可修改支付密码。</view>
+				<view class="lc-container-step" >Q: 乐豆余额不足时该如何兑换商品 ?</view>
+				<view class="lc-container-step-item">A: 余额不足时不可兑换商品,需要通过各种方式增加乐豆积累后可照常兑换商品</view>
+				<view class="lc-container-step" >Q: 乐豆余额可以提现吗 ?</view>
+				<view class="lc-container-step-item">A: 当前版本暂不支持乐豆余额提现,乐豆仅供线上商城、乐享便利兑换使用。</view>
+				<view class="lc-container-step" >Q: 如何完成垃圾投放 ?</view>
+				<view class="lc-container-step-item">A: 通过首页地图找到附近的乐色箱,通过手机号认证身份后,按下开箱按钮,将分类好的垃圾投放进箱內。</view>
+				<view class="lc-container-step" >Q: 如何查看已兑换商品订单的物流状态 ?</view>
+				<view class="lc-container-step-item">A: 点击“我的”,前往[我的订单],通过订单显示的快递公司和快递单号,可自行查询物流信息。</view>
+				<view class="lc-container-step" >Q: 如何进行线下兑换 ?</view>
+				<view class="lc-container-step-item">A: 可根据首页地图找到附近的乐享便利店,选择好所需商品后,向店员支付对应数量的乐豆,完成乐豆抵扣兑换商品。</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		onLoad() {
+			// 开启分享
+			uni.showShareMenu({
+				withShareTicket: true,
+				menus: ['shareAppMessage', 'shareTimeline']
+			})
+		},
+	}
+</script>
+
+<style lang="scss">
+	.lc{
+		padding: 15px;
+		line-height: 170%;
+		.lc-container{
+			font-size: 32rpx;
+			.lc-container-xs{
+				margin-bottom: 50rpx;
+				.lc-container-title{
+					text-align: center;
+					font-weight: bold;
+					font-size: 45rpx;
+					margin-bottom:40rpx ;
+				}
+				.lc-container-step{
+					color:#217330;
+					font-size: 38rpx;
+					margin: 10rpx 0;
+				.lc-container-step-name{
+					font-weight: bold;
+				}
+				.lc-container-step-item{
+					text{
+						margin-right: 5rpx;
+						font-size: 35rpx;
+						color: #DD524D;
+					}
+				}
+			}
+		}
+		}
+	}
+</style>

+ 102 - 0
pages/userCenter/userInfo/changePayPwd.vue

@@ -0,0 +1,102 @@
+<template>
+	<view class="changePayPwd-container">
+		<text class="pwd-describe">请输入当前支付密码</text>
+		<view class="pwd-item">
+			<input v-model="oldPwd" border type="password" placeholder="请输入支付密码" :maxlength="30" class="inp-pwd" />
+		</view>
+		<view class="btn-con">
+			<u-button class="handle-btn" type="info" size="medium" @click="cancel">取消</u-button>
+			<u-button class="handle-btn" type="primary" size="medium" @click="submit">确定</u-button>
+		</view>
+		<view class="linkbtn-con">
+			<text class="link-btn" @click="goPage">忘记支付密码</text>
+		</view>
+	</view>
+</template>
+
+<script>
+	import { checkPayPwd } from '@/api/order.js'
+	import md5 from 'md5'
+	export default {
+		data() {
+			return {
+				oldPwd: ''  //  旧密码
+			}
+		},
+		onShow() {
+		},
+		onLoad() {
+		},
+		methods:{
+			//  确定
+			submit(){
+				if(this.oldPwd){
+					checkPayPwd({oldPayPassword:md5(this.oldPwd)}).then(res=>{
+						if(res.status == 200) {
+							//  跳转到设置密码
+							uni.redirectTo({
+								url: `/pages/userCenter/userInfo/paymentPwd?oldPwd=${this.oldPwd}`
+							})
+						} 
+					})
+					
+				}else{
+					uni.showToast({ title: '请输入支付密码', icon: 'none' })
+				}
+			},
+			//  取消
+			cancel(){
+				uni.navigateBack({ delta: 1 })
+			},
+			//  忘记支付密码
+			goPage(){
+				uni.redirectTo({
+					url: '/pages/userCenter/userInfo/smsVerification'
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="less">
+	.changePayPwd-container{
+		width: 100%;
+		padding: 0 30rpx;
+		box-sizing: border-box;
+		background-color: #fff;
+		.pwd-describe{
+			display: block;
+			color: #606266;
+			padding: 80rpx 0 70rpx;
+			text-align: center;
+		}
+		.pwd-item{
+			.inp-pwd{
+				padding: 10upx 0;
+				border: 1px solid #cacaca;
+				text-align: center;
+			}
+		}
+		.btn-con{
+			display: flex;
+			justify-content: space-between;
+			padding-top: 130rpx;
+			.handle-btn{
+				display: inline-block;
+				width: 45%;
+				margin: 0 auto;
+				text-align: center;
+			}
+		}
+		.linkbtn-con{
+			text-align: center;
+			.link-btn{
+				display: inline-block;
+				font-size: 28rpx;
+				color: #298bf2;
+				margin-top: 200rpx;
+			}
+		}
+	}
+</style>
+

+ 175 - 0
pages/userCenter/userInfo/paymentPwd.vue

@@ -0,0 +1,175 @@
+<template>
+	<view class="paymentPwd-container">
+		<text class="pwd-describe">{{nowSetp == 1 ? '请设置6位数字支付密码' : nowSetp == 2 ? '请再次输入密码' : ''}}</text>
+		<view class="sms-item" @click="showKeyboard=!showKeyboard">
+			<u-message-input mode="bottomLine" :maxlength="6" v-model="value" :dot-fill="true" :disabled-keyboard="true"></u-message-input>
+		</view>
+		<!-- 数字键盘 -->
+		<u-keyboard ref="uKeyboard" :dot-enabled="false" :mask="false" :tooltip="false" v-model="showKeyboard" @change="valChange" @backspace="backspace"></u-keyboard>
+	</view>
+</template>
+
+<script>
+	import {setPayPwd, changePayPwd} from '@/api/order.js'
+	import md5 from 'md5'
+	export default {
+		data() {
+			return {
+				nowSetp: 1,  //  当前第几次设置密码
+				value: '', //输入的内容
+				showKeyboard: false,  //  是否打开键盘
+				oPwd: '',  //  第一次密码
+				tPwd: '',  //  第二次密码
+				oldPwd: '', // 原密码
+				mobile: '', // 用户手机号
+				verifiCode: '', // 短信验证码
+			}
+		},
+		onShow() {
+		},
+		onLoad(option) {
+			if (option.code){
+				this.mobile = option.mobile
+				this.verifiCode = option.code
+				this.oldPwd = ''
+			}
+			if (option.oldPwd){
+				this.oldPwd = option.oldPwd
+			}
+			//因为此时实例没有挂载完成,需要延迟操作
+			setTimeout(() => {
+			  this.showKeyboard = true
+			}, 50)
+		},
+		watch: {
+			// 监听输入框输入长度
+			 value: {
+				handler(newVal) {
+					if(newVal.length==6) {
+						if(this.nowSetp == 1){
+							this.oPwd = this.value
+							if(this.oldPwd && this.oPwd === this.oldPwd) {
+								uni.showToast({
+									title: '新密码与原密码相同,请重新输入!',
+									icon: 'none'
+								})
+								this.value = ''
+								return false
+							} else {
+								this.value = ''
+								this.nowSetp++
+								this.showKeyboard = true
+							}
+						}else if(this.nowSetp == 2){
+							this.tPwd = this.value
+							this.showKeyboard = false
+							this.checkPwd()
+						}
+					}
+				},
+				deep: true
+			}
+		},
+		methods:{
+			// 按键被点击(点击退格键不会触发此事件)
+			valChange(val) {
+				// 将每次按键的值拼接到value变量中,注意+=写法
+				if(this.value.length < 6){
+					this.value += val
+				}
+			},
+			// 退格键被点击
+			backspace() {
+				// 删除value的最后一个字符
+				if(this.value.length) this.value = this.value.substr(0, this.value.length - 1)
+			},
+			//  密码校验
+			checkPwd(){
+				if(this.oPwd == this.tPwd){
+					if(this.oldPwd) {
+						this.changePsw()
+					} else{
+						//  设置密码 请求接口
+						this.setPsw()
+					}
+					
+				}else{
+					uni.showToast({ title: '两次密码不一致请重新输入', icon: 'none' })
+					this.value = ''
+					this.showKeyboard = true
+				}
+			},
+			// 首次设置密码
+			setPsw () {
+				let params = {
+					mobile: this.mobile,
+					verifiCode: this.verifiCode,
+					payPassword: md5(this.value)
+				}
+				console.log(params,'--------mima')
+				setPayPwd(params).then(res=>{
+					if(res.status==200) {
+						uni.showToast({ title: res.message, icon: 'none' })
+						 uni.$emit('setPswSuccess', {success: true})
+						setTimeout(()=>{
+							uni.navigateBack({
+							    delta: 1
+							})
+						}, 2000)
+					} 
+				})
+			},
+			// 修改密码
+			changePsw () {
+				let params = {
+					oldPayPassword: md5(this.oldPwd),
+					payPassword: md5(this.value)
+				}
+				changePayPwd(params).then(res=>{
+					if(res.status==200) {
+						uni.showToast({ title: res.message, icon: 'none' })
+						setTimeout(()=>{
+							uni.navigateBack({
+							    delta: 1
+							})
+						}, 2000)
+					} 
+				})
+			},
+			//  取消
+			cancel(){
+				this.oPwd = ''
+				this.tPwd = ''
+				this.value = ''
+				this.nowSetp = 1
+			}
+		}
+	}
+</script>
+
+<style lang="less">
+	.paymentPwd-container{
+		width: 100%;
+		padding: 0 30rpx;
+		box-sizing: border-box;
+		background-color: #fff;
+		.pwd-describe{
+			display: block;
+			color: #606266;
+			padding: 80rpx 0 70rpx;
+			text-align: center;
+		}
+		.btn-con{
+			display: flex;
+			justify-content: space-between;
+			padding-top: 130rpx;
+			.handle-btn{
+				display: inline-block;
+				width: 45%;
+				margin: 0 auto;
+				text-align: center;
+			}
+		}
+	}
+</style>
+

+ 210 - 0
pages/userCenter/userInfo/smsVerification.vue

@@ -0,0 +1,210 @@
+<template>
+	<view class="smsVerification-container">
+		<view v-if="sendCode" class="">
+			<view class="sms-title">输入短信验证码</view>
+			<text class="sms-describe">验证码已发送至{{userPhone}},请在下方输入框内输入6位数字验证码</text>
+			<view class="sms-item" @click="showKeyboard=!showKeyboard">
+				<u-message-input mode="bottomLine" :maxlength="6" v-model="value" :disabled-keyboard="true"></u-message-input>
+			</view>
+			<view class="sendcode-con">
+				<text :class="['getcode-btn', isDisable ? 'grey-btn' : '']" @click="isDisable ? null : pageInit()">{{nowVal}}</text>
+			</view>
+		</view>
+		<!-- 数字键盘 -->
+		<u-keyboard ref="uKeyboard" :dot-enabled="false" :mask="false" :tooltip="false" v-model="showKeyboard" @change="valChange" @backspace="backspace"></u-keyboard>
+		<!-- 图文验证码 -->
+		<uni-popup :maskClick="false" ref="imageTxtPopup" type="center">
+			<popup-con title="图文验证码" :popBtn="popBtn" :changeImg="changeImg" @captchaImg="captchaVerify"></popup-con>
+		</uni-popup>
+	</view>
+</template>
+
+<script>
+	import uniPopup from '@/components/uni-popup/uni-popup.vue'
+	import popupCon from '@/components/uni-popup/popup-con.vue'
+	import { sendVerifyCode } from '@/api/user.js'
+	import { checkCode } from '@/api/order.js'
+	export default {
+		components: {
+			uniPopup,
+			popupCon
+		},
+		data() {
+			return {
+				nowVal: '获取验证码',  //  按钮显示内容
+				count: '',  //  当前秒数
+				timer: null,  //  倒计时
+				isDisable: false,  //  是否禁止获取验证码
+				value: '', //输入的内容
+				phone: '', // 用户手机号
+				showKeyboard: false,  //  是否打开键盘
+				popBtn: [],  //  name为操作按钮名称,color为操作按钮颜色值
+				changeImg: false,  //  是否重新更换图形验证码
+				verifyCode: '',
+				randomCode: '',  //  图片验证码随机码
+				sendCode: false  // 短信是否发送成功
+			}
+		},
+		onLoad() {
+			this.phone = this.$store.state.vuex_userData.mobile
+			console.log(this.$store.state.vuex_userData,'his.$store.state.vuex_OrderAddress')
+			//因为此时实例没有挂载完成,需要延迟操作
+			setTimeout(() => {
+			  this.pageInit()
+			}, 50)
+		},
+		computed: {
+			userPhone () {
+				return this.phone.substr(0,3) + "****" + this.phone.substr(7)
+			}
+		},
+		watch: {
+			// 监听输入框输入长度
+			 value: {
+				handler(newVal) {
+					// console.log(newVal.length,'NNNNNNNNNNNN')
+					if(newVal.length==6) {
+						let params = {
+							mobile: this.phone,
+							verifiCode: newVal
+						}
+						checkCode(params).then(res=>{
+							if(res.status==200) {
+								//  跳转到设置密码
+								uni.redirectTo({
+									url: `/pages/userCenter/userInfo/paymentPwd?code=${this.value}&mobile=${this.phone}`
+								})
+							} 
+						})
+						
+					}
+				},
+				deep: true
+			}
+		},
+		methods:{
+			pageInit () {
+				this.value = ''
+				this.sendCode = false
+				this.showKeyboard = false
+				this.retsetCode()
+				//  图文验证码
+				this.$refs.imageTxtPopup.open()
+			},
+			//  验证图片验证码
+			captchaVerify(code, nowRandomCode){
+				let obj = {}
+				obj.phone = this.phone
+				obj.random = nowRandomCode
+				obj.code = code
+				//  短信验证码
+				sendVerifyCode(obj).then(res => {
+					console.log(JSON.stringify(res))
+					if(res.status == 200){  //  验证码输入正确
+						this.randomCode = nowRandomCode  //  图片验证码随机码
+						//  关闭   图形验证码 弹框
+						this.$refs.imageTxtPopup.close()
+						//  开启倒计时
+						this.getCodeFun()
+						this.sendCode = true
+						this.showKeyboard = true
+						uni.showToast({icon: 'none',title: '验证码发送成功'})
+					}else {  //  验证码输入错误
+						this.retsetCode()
+						uni.showToast({icon: 'none',title: res.message})
+					}
+				})
+			},
+			// 重新触发获取图片验证码
+			retsetCode(){
+				const _this = this
+				_this.verifyCode = ''
+				_this.randomCode = ''
+				//  切换验证码重新校验
+				_this.changeImg = false
+				_this.$nextTick(function(){
+					_this.changeImg = true
+				})
+			},
+			// 按键被点击(点击退格键不会触发此事件)
+			valChange(val) {
+				// 将每次按键的值拼接到value变量中,注意+=写法
+				if(this.value.length < 6){
+					this.value += val
+				}
+			},
+			// 退格键被点击
+			backspace() {
+				// 删除value的最后一个字符
+				if(this.value.length) this.value = this.value.substr(0, this.value.length - 1)
+			},
+			//  开始倒计时
+			getCodeFun(){
+				const TIME_COUNT = 60
+				if (!this.timer) {
+				  this.count = TIME_COUNT
+				  this.timer = setInterval(() => {
+				    if (this.count > 0 && this.count <= TIME_COUNT) {
+				      this.count--
+				      this.nowVal = this.count + '秒后重新发送'
+				      this.isDisable = true
+				    } else {
+				      this.nowVal = '重新获取验证码'
+				      clearInterval(this.timer)
+				      this.timer = null
+				      this.isDisable = false
+				    }
+				  }, 1000)
+				}
+			},
+			//  取消
+			cancel(){
+				uni.navigateBack({
+				    delta: 1
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="less">
+	.smsVerification-container{
+		width: 100%;
+		padding: 0 30rpx;
+		box-sizing: border-box;
+		background-color: #fff;
+		.sms-title {
+		  margin: 50rpx 0 30rpx;
+		  font-size: 36rpx;
+		  font-weight: bold;
+		}
+		.sms-describe{
+			display: block;
+			color: #606266;
+			margin: 20rpx 0 50rpx;
+		}
+		.sendcode-con{
+			text-align: center;
+			padding-top: 70rpx;
+			.getcode-btn {
+				display: block;
+				font-size: 28rpx;
+				color: #298bf2;
+			}
+			.grey-btn{
+				color: #999;
+			}
+		}
+		.btn-con{
+			display: flex;
+			justify-content: space-between;
+			padding-top: 130rpx;
+			.handle-btn{
+				display: inline-block;
+				width: 45%;
+				margin: 0 auto;
+				text-align: center;
+			}
+		}
+	}
+</style>

+ 139 - 0
pages/userCenter/userInfo/userInfo.vue

@@ -0,0 +1,139 @@
+<template>
+	<view class="userInfo-container">
+		<u-cell-group>
+			<u-cell-item title="头像" :arrow="false" center>
+				<view class="user-photo"><open-data type="userAvatarUrl"></open-data></view>
+			</u-cell-item>
+			<u-cell-item title="手机号码" :value="userDate.mobile" :arrow="false"></u-cell-item>
+			<u-cell-item title="收货地址" :value="address?'修改收货地址':'设置收货地址'" index="0" @click="editAddress"></u-cell-item>
+			<u-cell-item title="支付密码" :value="existPayPwd?'修改密码':'设置密码'" @click="setPayPwd"></u-cell-item>
+		</u-cell-group>
+		<!-- 退出 -->
+		<view class="user-btn">
+			<u-button type="success" :custom-style="quitButton" @click="quitSys" shape="circle">退出登录</u-button>
+		</view>
+	</view>
+</template>
+
+<script>
+	import {existPayPwd} from '@/api/order.js'
+	import {findAddressBycustomerNo} from '@/api/receiveAddress.js'
+	import {logout} from '@/api/login.js'
+	export default {
+		data() {
+			return {
+				quitButton: {
+					borderRadius:'100rpx',
+					fontSize:'30rpx',
+					background: '#01c9b2'
+				},
+				userDate: {},
+				existPayPwd: null,
+				address: null,
+				userInfo: {}
+			}
+		},
+		onLoad() {
+			// 开启分享
+			uni.showShareMenu({
+				withShareTicket: true,
+				menus: ['shareAppMessage', 'shareTimeline']
+			})
+		},
+		onShow() {
+			this.userDate = this.$store.state.vuex_userData
+			this.getPayPwd()
+			this.getAddress()
+		},
+		methods:{
+			// 获取用户位置
+			getAddress() {
+				 findAddressBycustomerNo({}).then(res => {
+					 console.log(res)
+					 if (res.status == 200&&res.data.length) {
+						 this.address = res.data[0].receiveAreasName
+						 this.userInfo = res.data[0]
+					 } else {
+						 this.address = ''
+						 this.userInfo = {}
+					 }
+				 })
+			},
+			// 判断是否设置过支付密码
+			getPayPwd(){
+				existPayPwd().then(res=>{
+					if(res.status == 200) {
+						if(res.data) {
+							this.existPayPwd = true
+						} else {
+							this.existPayPwd = false
+						}
+					}
+				})
+			},
+			// 修改收货地址
+			editAddress(index){
+				this.$u.vuex("vuex_OrderAddress",this.userInfo)
+				uni.navigateTo({
+					url:"/pagesA/toOrder/editAddress"
+				})
+			},
+			// 设置密码
+			setPayPwd(){
+				// 设置过支付密码,则修改密码
+				if(this.existPayPwd){
+					uni.navigateTo({ url: '/pages/userCenter/userInfo/changePayPwd' })
+				}else{
+					// 没设置过支付密码,提示设置密码
+					uni.navigateTo({ url: '/pages/userCenter/userInfo/smsVerification' })
+				}
+			},
+			// 退出登录
+			quitSys(){
+				let _this = this
+				uni.showModal({
+				  title: '提示',
+				  content: '您确定要退出登录吗?',
+				  success(res) {
+				    if (res.confirm) {
+				      logout({}).then(res => {
+				       getApp().globalData.token = ''
+				       _this.$u.vuex('vuex_token','')
+					   _this.$u.vuex('vuex_userData','')
+					   _this.$u.vuex('vuex_cartList','')
+					   _this.$u.vuex('vuex_OrderAddress','')
+					   _this.$u.vuex('vuex_toOrderList','')
+					   // 关闭socket
+					   _this.$store.commit("$closeWebsocket")
+				        uni.reLaunch({
+				        	url: '/pages/index/index'
+				        })
+				      })
+				    }
+				  }
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="less">
+	.userInfo-container{
+		width: 100%;
+		.logout-btn{
+			width: 60%;
+			margin-top: 200rpx;
+		}
+		.user-btn{
+			padding: 85rpx 200rpx;
+		}
+		.user-photo{
+			width: 100rpx;
+			height: 100rpx;
+			border-radius: 50%;
+			overflow: hidden;
+			margin-right: 5rpx;
+			float: right;
+		}
+	}
+</style>

+ 169 - 0
pages/userCenter/zhinan.vue

@@ -0,0 +1,169 @@
+<template>
+	<view class="lc" >
+		<view class="lc-container">
+			<!-- 新手引导 -->
+			<view class="lc-container-xs">
+				<view class="lc-container-title" style="margin-top: 0;">新手引导</view>
+				<view class="lc-container-step" style="margin-top: 0;">(<text>一</text>)、新手入驻</view>
+				<view class="lc-container-step-name" style="font-weight: bold; font-size: 32rpx;margin: 20rpx 0;">用户登录</view>
+				<view class="lc-container-step-item">
+					(<text>1</text>)
+					<text style="margin-left:10rpx ;">用户进入小程序,点击微信登录,授权手机号后将自动登录。</text>
+				</view>
+				<view class="lc-container-step-item">
+					(<text>2</text>)
+					<text style="margin-left:10rpx ;">用户进入小程序,点击手机号验证登录,可通过手机验证码登录小程序。</text>
+				</view>
+				<view class="lc-container-step-name" style="font-weight: bold;font-size: 32rpx;margin: 20rpx 0;">我的</view>
+				<view class="lc-container-step-item">
+					(<text>1</text>)
+					<text style="margin-left:10rpx ;">点击右下角「我的」,点击头像进入「个人中心」。</text>
+				</view>
+				<view class="lc-container-step-item">
+					(<text>2</text>)
+					<text style="margin-left:10rpx ;"> 进入“支付密码”页面,可设置/修改支付密码。</text>
+				</view>
+				<view class="lc-container-step-item">
+					(<text>3</text>)
+					<text style="margin-left:10rpx ;"> 进入“收货地址”页面,进行收货地址管理。</text>
+				</view>
+				<view class="lc-container-step">(<text>二</text>)、使用手册</view>
+				<view class="lc-container-step-name" style="font-weight: bold;font-size: 32rpx;margin: 20rpx 0;">垃圾投放</view>
+				<view class="lc-container-step-item">
+					(<text>1</text>)
+					<text style="margin-left:10rpx ;">进入首页,可查看距离自己当前位置最近的3个回收网点。</text>
+				</view>
+				<view class="lc-container-step-item">
+					(<text>2</text>)
+					<text style="margin-left:10rpx ;">在自动回收机上输入手机号进行身份验证后,选择对应的投递类型。</text>
+				</view>
+				<view class="lc-container-step-item">
+					(<text>3</text>)
+					<text style="margin-left:10rpx ;">按下设备的开箱按钮,将分类装好的垃圾投放进相应的回收箱内。</text>
+				</view>
+				<view class="lc-container-step-item">
+					(<text>4</text>)
+					<text style="margin-left:10rpx ;">系统自动称重,获得相应的乐豆积分。</text>
+				</view>
+				<view class="lc-container-step-name" style="font-weight: bold;font-size: 32rpx;margin: 20rpx 0;">兑换</view>
+				<view class="lc-container-step-name" style="font-weight: bold;font-size: 32rpx;margin: 20rpx 0;">乐豆商城</view>
+				<view class="lc-container-step-item">
+					(<text>1</text>)
+					<text style="margin-left:10rpx ;">进入小程序,点击「商城」,可查看商城商品。</text>
+					</view>
+				<view class="lc-container-step-item">
+					(<text>2</text>)
+					<text style="margin-left:10rpx ;">点击商品右侧的“购物车”添加商品,点击“去兑换”进入订单页面。</text>
+				</view>
+				<view class="lc-container-step-item">
+					(<text>3</text>)
+					<text style="margin-left:10rpx ;">确认订单无误后,点击“支付"跳转至支付页面,输入支付密码,完成兑换。</text>
+					</view>
+				<view class="lc-container-step-item">
+					(<text>4</text>)
+					<text style="margin-left:10rpx ;">收到货后要进行确认收货。</text>
+				</view>
+				<view class="lc-container-step-name" style="font-weight: bold;font-size: 32rpx;margin: 20rpx 0;">乐享亭</view>
+				<view class="lc-container-step-item">
+					(<text>1</text>)
+					<text style="margin-left:10rpx ;">找到附近的乐享亭,选择自己需要兑换的商品。</text>
+				</view>
+				<view class="lc-container-step-item">
+					(<text>2</text>)
+					<text style="margin-left:10rpx ;">同乐享亭店员沟通确认兑换该商品需要支付的乐豆。</text>
+				</view>
+				<view class="lc-container-step-item">
+					(<text>3</text>)
+					<text style="margin-left:10rpx ;">点击首页中间的“扫一扫”图标,输入需要支付的乐豆数字,点击“支付”输入支付密码后完成兑换。</text>
+				</view>
+			</view>
+			
+			<!-- 乐豆使用说明 -->
+			<view class="lc-container-xs" style="font-size: 32rpx;">
+				<view class="lc-container-title" >Q&A乐豆使用说明</view>
+				<view class="lc-container-step" >Q: 什么是乐豆 ?</view>
+				<view class="lc-container-step-item">A: 乐豆是用于乐色管家用户使用乐色管家各种服务及增值服务的种类、数量或时间等的一种统计代码,并非任何代币票券,不能用于乐色管家服务及增值服务以外的任何商品或服务。</view>
+				<view class="lc-container-step" >Q: 如何使用乐豆 ?</view>
+				<view class="lc-container-step-item">A: 乐豆可以用于兑换积分商城丰富的商品、服务,以及参与线上线下活动等。</view>
+				<view class="lc-container-step" >Q: 如何获得乐豆 ?</view>
+				<view class="lc-container-step-item">A: 用户可通过完成「垃圾投放」「系统任务」等方式来获取乐豆。</view>
+				<view class="lc-container-step" >Q: 乐豆的有效期限是多长 ?</view>
+				<view class="lc-container-step-item">A: 用户获得但未使用的乐豆长期有效。</view>
+				<view class="lc-container-step" >Q: 如何查看我的乐豆数量 ?</view>
+				<view class="lc-container-step-item">A: 点击“我的”,可查看乐豆余额;点击“乐豆明细”小图标可查看乐豆明细。</view>
+				<view class="lc-container-step" >Q: 乐豆的使用范围 ?</view>
+				<view class="lc-container-step-item">A: 乐色管家小程序+乐享亭。</view>
+			</view>
+			
+			<!-- 常见问题 -->
+			<view class="lc-container-xs"  style="font-size: 32rpx;">
+				<view class="lc-container-title" >Q&A常见问题</view>
+				<view class="lc-container-step" >Q: 为什么投放垃圾后乐豆没有到账 ?</view>
+				<view class="lc-container-step-item">A: 每次需要投放最小计算单位以上的重量才会有乐豆奖励。</view>
+				<view class="lc-container-step" >Q: 如何修改支付密码 ?</view>
+				<view class="lc-container-step-item">A: 点击“我的”,前往[个人中心],进入“支付密码”页面可修改支付密码。</view>
+				<view class="lc-container-step" >Q: 乐豆余额不足时该如何兑换商品 ?</view>
+				<view class="lc-container-step-item">A: 余额不足时不可兑换商品,需要通过各种方式增加乐豆积累后可照常兑换商品</view>
+				<view class="lc-container-step" >Q: 乐豆余额可以提现吗 ?</view>
+				<view class="lc-container-step-item">A: 当前版本暂不支持乐豆余额提现,乐豆仅供线上商城、乐享便利兑换使用。</view>
+				<view class="lc-container-step" >Q: 如何完成垃圾投放 ?</view>
+				<view class="lc-container-step-item">A: 通过首页地图找到附近的乐色箱,通过手机号认证身份后,按下开箱按钮,将分类好的垃圾投放进箱內。</view>
+				<view class="lc-container-step" >Q: 如何查看已兑换商品订单的物流状态 ?</view>
+				<view class="lc-container-step-item">A: 点击“我的”,前往[我的订单],通过订单显示的快递公司和快递单号,可自行查询物流信息。</view>
+				<view class="lc-container-step" >Q: 如何进行线下兑换 ?</view>
+				<view class="lc-container-step-item">A: 可根据首页地图找到附近的乐享便利店,选择好所需商品后,向店员支付对应数量的乐豆,完成乐豆抵扣兑换商品。</view>
+			</view>
+			<view style="font-size: 32rpx;">
+				温馨提示:使用过程中,如有其他问题,可前往[我的],点击“联系客服”图标,拨打客服电话进行沟通。
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		onLoad() {
+			// 开启分享
+			uni.showShareMenu({
+				withShareTicket: true,
+				menus: ['shareAppMessage', 'shareTimeline']
+			})
+		},
+	}
+</script>
+
+<style lang="scss">
+	.lc{
+		padding: 15px;
+		line-height: 170%;
+		.lc-container{
+			font-size: 32rpx;
+			.lc-container-xs{
+				margin-bottom: 50rpx;
+				.lc-container-title{
+					text-align: center;
+					font-weight: bold;
+					font-size: 45rpx;
+					margin:40rpx ;
+				}
+				.lc-container-step{
+					color:#217330;
+					font-size: 38rpx;
+					margin: 30rpx 0;
+				.lc-container-step-name{
+					font-weight: bold;
+					margin: 20rpx 0;
+				}
+				.lc-container-step-item{
+					margin: 20rpx 0;
+					text{
+						margin-right: 5rpx;
+						font-size: 35rpx;
+						color: #DD524D;
+					}
+				}
+			}
+		}
+		}
+	}
+</style>

二進制
static/CLOTHES.png


二進制
static/FIRST_DELIVERY.png


二進制
static/FIRST_ONLINE_EXCHANGE.png


二進制
static/GLASS.png


二進制
static/HARM.png


二進制
static/METAL.png


二進制
static/MET_PLA.png


二進制
static/NEW_CUSTOMER.png


二進制
static/NEW_FIRST_DELIVER.png


二進制
static/OLD_FIRST_DELIVER.png


二進制
static/PAPER.png


二進制
static/PLASTIC.png


二進制
static/RECYCLING.png


二進制
static/SHARE_FRIEND.png


二進制
static/add-cart.png


二進制
static/addCart.png


二進制
static/almost-lottery/almost-lottery__action.png


二進制
static/almost-lottery/almost-lottery__action2x.png


二進制
static/almost-lottery/almost-lottery__action3x.png


二進制
static/almost-lottery/almost-lottery__bg.png


二進制
static/almost-lottery/almost-lottery__bg2x.png


二進制
static/almost-lottery/almost-lottery__bg3x.png


二進制
static/bued.png


二進制
static/cart.png


二進制
static/checked.png


二進制
static/closeLottery.png


二進制
static/dz-icon.png


二進制
static/edit.png


二進制
static/full-bg.png


二進制
static/giftcard/bg01.jpg


二進制
static/giftcard/bg02.jpg


二進制
static/giftcard/input-bg.png


二進制
static/giftcard/leaf-left-pic.png


二進制
static/giftcard/leaf-right-pic.png


二進制
static/giftcard/module-footer-bg.png


二進制
static/giftcard/module-header-bg.png


二進制
static/giftcard/pop-bg.png


二進制
static/giftcard/red-envelopes.png


二進制
static/giftcard/tit-left.png


二進制
static/giftcard/tit-right.png


二進制
static/hexiao.png


二進制
static/imgError.png


二進制
static/ledou.png


二進制
static/liucheng.png


部分文件因文件數量過多而無法顯示