Browse Source

初始化登录

孙旺 2 years ago
parent
commit
732cc27be2
100 changed files with 30169 additions and 7 deletions
  1. 16 0
      .gitignore
  2. 79 0
      App.vue
  3. 17 4
      LICENSE
  4. 1 3
      README.md
  5. 118 0
      api/commodity/goods.js
  6. 66 0
      api/commodity/goodsMode.js
  7. 50 0
      api/commodity/mercGoods.js
  8. 57 0
      api/commodity/point.js
  9. 88 0
      api/commoditylist/commoditylist.js
  10. 373 0
      api/device/device.js
  11. 9 0
      api/dict.js
  12. 11 0
      api/download.js
  13. 50 0
      api/login.js
  14. 144 0
      api/order/order.js
  15. 50 0
      api/order/riskorder.js
  16. 9 0
      api/oss.js
  17. 38 0
      api/point/area.js
  18. 56 0
      api/point/line.js
  19. 57 0
      api/point/point.js
  20. 156 0
      api/replenishment/replenishment.js
  21. 49 0
      api/system/employee.js
  22. 22 0
      api/system/menu.js
  23. 50 0
      api/system/user.js
  24. 687 0
      components/classify/index.vue
  25. 108 0
      components/tki-tree/style.css
  26. 50 0
      components/tki-tree/tki-tree.vue
  27. 160 0
      components/xy-button/index.vue
  28. 1369 0
      components/xy-imgResiz/index.vue
  29. 114 0
      components/xy-popup/index.vue
  30. 30 0
      config.js
  31. 36 0
      main.js
  32. 81 0
      manifest.json
  33. 5 0
      package.json
  34. 392 0
      pages.json
  35. 221 0
      pages/activeDevice/bindAliAcc.vue
  36. 215 0
      pages/activeDevice/bindAliDev.vue
  37. 171 0
      pages/activeDevice/bindDevice.vue
  38. 377 0
      pages/activeDevice/deviceManage.vue
  39. 972 0
      pages/commodity/addCom.vue
  40. 507 0
      pages/commodity/allGoodsSearch.vue
  41. 223 0
      pages/commodity/application.vue
  42. 273 0
      pages/commodity/auditList.vue
  43. 300 0
      pages/commodity/comEdit.vue
  44. 523 0
      pages/commodity/comListEdit.vue
  45. 359 0
      pages/commodity/commoditylist.vue
  46. 163 0
      pages/commodity/components/imgUpload/index.vue
  47. 100 0
      pages/commodity/components/imgUploadCut/index.vue
  48. 310 0
      pages/commodity/publicCom.vue
  49. 455 0
      pages/commodity/publicSearch.vue
  50. 247 0
      pages/commodity/search.vue
  51. 608 0
      pages/equipment/addCom.vue
  52. 296 0
      pages/equipment/addComList.vue
  53. 456 0
      pages/equipment/comManage.vue
  54. 352 0
      pages/equipment/components/xy-filtedrop/index.vue
  55. 1779 0
      pages/equipment/detail.vue
  56. 341 0
      pages/equipment/search.vue
  57. 691 0
      pages/equipment/statistics.vue
  58. 349 0
      pages/equipment/statisticsMore.vue
  59. 76 0
      pages/globalPages/agreement.vue
  60. 661 0
      pages/globalPages/components/account.vue
  61. 315 0
      pages/globalPages/components/commodity.vue
  62. 362 0
      pages/globalPages/components/equipment.vue
  63. 654 0
      pages/globalPages/components/home.vue
  64. 159 0
      pages/globalPages/errCode.vue
  65. 187 0
      pages/globalPages/home.vue
  66. 79 0
      pages/globalPages/logs.vue
  67. 919 0
      pages/globalPages/moreData.vue
  68. 121 0
      pages/globalPages/notice.vue
  69. 92 0
      pages/globalPages/setting.vue
  70. 804 0
      pages/globalPages/statistics.vue
  71. 457 0
      pages/globalPages/statisticsMore.vue
  72. 41 0
      pages/globalPages/test.vue
  73. 291 0
      pages/login.vue
  74. 100 0
      pages/order/components/xvideo.vue
  75. 432 0
      pages/order/orderDel.vue
  76. 705 0
      pages/order/orderDetails.vue
  77. 56 0
      pages/order/orderLogs.vue
  78. 888 0
      pages/order/orderQuery.vue
  79. 854 0
      pages/order/refundList.vue
  80. 1010 0
      pages/order/riskOrder.vue
  81. 432 0
      pages/order/riskOrderDel.vue
  82. 184 0
      pages/order/userInfo.vue
  83. 0 0
      pages/point/components/Winglau14-lotusAddress/Winglau14-lotusAddress.js
  84. 360 0
      pages/point/components/Winglau14-lotusAddress/Winglau14-lotusAddress.vue
  85. 361 0
      pages/point/lineDetail.vue
  86. 407 0
      pages/point/point.vue
  87. 394 0
      pages/replenish/invSearch.vue
  88. 182 0
      pages/replenish/invSearchDetail.vue
  89. 343 0
      pages/replenish/inventoryQueryExport1.vue
  90. 1017 0
      pages/replenish/replenishmentHomePage.vue
  91. 572 0
      pages/replenish/replenishmentManagement.vue
  92. 252 0
      pages/replenish/replenishmentRecord.vue
  93. 155 0
      pages/replenish/replenishmentRecordDetails.vue
  94. 251 0
      pages/system/addEmployee.vue
  95. 427 0
      pages/system/deviceManage.vue
  96. 285 0
      pages/system/empDetail.vue
  97. 282 0
      pages/system/employee.vue
  98. 39 0
      permission.js
  99. 60 0
      plugins/auth.js
  100. 17 0
      plugins/index.js

+ 16 - 0
.gitignore

@@ -0,0 +1,16 @@
+######################################################################
+# Build Tools
+
+/unpackage/*
+/node_modules/*
+
+######################################################################
+# Development Tools
+
+/.idea/*
+/.vscode/*
+/.hbuilderx/*
+
+package-lock.json
+yarn.lock
+

+ 79 - 0
App.vue

@@ -0,0 +1,79 @@
+<script>
+	import config from './config'
+	import {
+		getToken
+	} from '@/utils/auth'
+
+	export default {
+		onLaunch: function() {
+			this.initApp();
+		},
+		onShow() {},
+		methods: {
+			// 初始化应用
+			initApp() {
+				console.log('初始化应用开始~')
+				// 是否有版本更新
+				this.isUpdate()
+				// 初始化应用配置
+				this.initConfig()
+				// 免登录
+				this.checkLogin()
+			},
+
+			initConfig() {
+				this.globalData.config = config
+			},
+			async checkLogin() {
+				console.log('检测是否登录开始!')
+				if (getToken() && uni.getStorageSync('sysId')) {
+					console.log('已登录!')
+					await this.$store.dispatch('GetPermis')
+					//判断用户是否有任一菜单权限
+					if (this.$store.state.permission.permissions_menu && this.$store.state.permission
+						.permissions_menu != '[]') {
+						// 开发模式下取消免登录,直接跳转调试页面,生产免登录跳转主页
+						if (process.env.NODE_ENV == 'production') {
+							this.$tab.reLaunch('/pages/globalPages/home')
+						}
+					} else {
+						this.$modal.msg('该用户无权限~')
+					}
+				} else {
+					console.log('未登录!')
+				}
+			},
+
+			isUpdate() {
+				console.log('检测更新开始~')
+				const updateManager = wx.getUpdateManager()
+				updateManager.onCheckForUpdate(function(res) {
+					// 请求完新版本信息的回调
+					console.log(res.hasUpdate)
+				})
+
+				updateManager.onUpdateReady(function() {
+					wx.showModal({
+						title: '更新提示',
+						content: '新版本已经准备好,是否重启应用?',
+						success(res) {
+							if (res.confirm) {
+								// 新的版本已经下载好,调用 applyUpdate 应用新版本并重启
+								updateManager.applyUpdate()
+							}
+						}
+					})
+				})
+
+				updateManager.onUpdateFailed(function() {
+					// 新版本下载失败
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	@import "@/uni_modules/uview-ui/index.scss";
+	@import '@/static/scss/index.scss'
+</style>

+ 17 - 4
LICENSE

@@ -1,8 +1,21 @@
 MIT License
-Copyright (c) <year> <copyright holders>
 
-Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+Copyright (c) 2022 若依
 
-The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
 
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 1 - 3
README.md

@@ -1,3 +1 @@
-# OpenDoorManageMini
-
-平台端管理系统小程序
+### 兴元商家管理小程序

+ 118 - 0
api/commodity/goods.js

@@ -0,0 +1,118 @@
+import request from '@/utils/request'
+
+// 商品分页
+export function ownerGoodsList(data) {
+  return request({
+    url: '/goods/merc-mini/goods/page',
+    method: 'post',
+	data:data
+  })
+}
+
+// 商品类目
+export function goodsCategory(data) {
+  return request({
+    url: '/goods/merc-mini/goods/categoryLevel1 ',
+    method: 'post',
+    data:data
+  })
+}
+
+// 列表(内部服务调用) 
+export function listById(data) {
+  return request({
+    url: '/mini/mercLine/listById',
+    method: 'post',
+    data:data
+  })
+}
+
+// 列表
+export function lineListByMerc(data) {
+  return request({
+    url: '/mini/mercLine/listByMerc',
+    method: 'post',
+    data:data
+  })
+}
+
+// 删除
+export function lineDel(data) {
+  return request({
+    url: '/mini/mercLine/del',
+    method: 'post',
+    data:data
+  })
+}
+//设备 分类列表
+export function categoryList(data) {
+  return request({
+    url: '/goods/merc-mini/goodsMerc/categoryList',
+    method: 'post',
+    data:data
+  })
+}
+//设备  商品类别
+export function list(data) {
+  return request({
+    url: '/goods/merc-mini/goodsDevice/list',
+    method: 'post',
+    data:data
+  })
+}
+
+//销售统计汇总
+export function sumCount(data) {
+  return request({
+    url: '/goods/merc-mini/goodsData/sumCount',
+    method: 'post',
+    data:data
+  })
+}
+
+//销售统计列表
+export function sumPage(data) {
+  return request({
+    url: '/goods/merc-mini/goodsData/sumPage',
+    method: 'post',
+    data:data
+  })
+}
+
+//设备商品公库分页
+export function pageByGoods(data) {
+  return request({
+    url: '/goods/merc-mini/goodsDevice/pageByGoods',
+    method: 'post',
+    data:data
+  })
+}
+
+//设备商品私库分页
+export function pageByGoodsMerc(data) {
+  return request({
+    url: '/goods/merc-mini/goodsDevice/pageByGoodsMerc',
+    method: 'post',
+    data:data
+  })
+}
+
+//私库到设备
+export function bindDeviceByMercGoods(data) {
+  return request({
+    url: '/goods/merc-mini/goodsDevice/bindDeviceByMercGoods',
+    method: 'post',
+    data:data
+  })
+}
+
+//公库到设备
+export function bindDeviceByGoods(data) {
+  return request({
+    url: '/goods/merc-mini/goodsDevice/bindDeviceByGoods',
+    method: 'post',
+    data:data
+  })
+}
+
+

+ 66 - 0
api/commodity/goodsMode.js

@@ -0,0 +1,66 @@
+import request from '@/utils/request'
+
+// 新增
+export function save(data) {
+  return request({
+    url: '/goods/merc-mini/goodsMode/save',
+    method: 'post',
+	data:data
+  })
+}
+
+// 修改
+export function update(data) {
+  return request({
+    url: '/goods/merc-mini/goodsMode/update',
+    method: 'post',
+	data:data
+  })
+}
+
+// 对象查询
+export function searchObj(data) {
+  return request({
+    url: '/goods/merc-mini/goodsMode/obj',
+    method: 'post',
+	data:data
+  })
+}
+
+// 分页
+export function goodsPage(data) {
+  return request({
+    url: '/goods/merc-mini/goodsMode/page',
+    method: 'post',
+	data:data
+  })
+}
+
+// 商品类目
+export function categoryTree(data) {
+  return request({
+    url: '/goods/merc-mini/goods/category/tree',
+    method: 'post',
+	data:data
+  })
+}
+
+// 商品规格
+export function listIdName(data) {
+  return request({
+    url: '/goods/merc-mini/goods/unit/listIdName',
+    method: 'post',
+	data:data
+  })
+}
+
+// 设备商品价格修改
+export function changePrice(data) {
+  return request({
+    url: '/goods/merc-mini/goodsDevice/update',
+    method: 'post',
+	data:data
+  })
+}
+
+

+ 50 - 0
api/commodity/mercGoods.js

@@ -0,0 +1,50 @@
+import request from '@/utils/request'
+
+// 商品分页
+export function ownerGoodsList(data) {
+  return request({
+    url: '/goods/merc-mini/goodsMerc/page',
+    method: 'post',
+	data:data
+  })
+}
+
+// 商品类目
+export function goodsCategory(data) {
+  return request({
+    url: '/goods/merc-mini/goodsMerc/categoryList',
+    method: 'post',
+    data:data
+  })
+}
+
+// 对象查询
+export function searchObj(data) {
+  return request({
+    url: '/goods/merc-mini/goodsMerc/obj',
+    method: 'post',
+	data:data
+  })
+}
+
+// 商品添加到私库
+export function bindMerc(data) {
+  return request({
+    url: '/goods/merc-mini/goodsMerc/bindMerc',
+    method: 'post',
+	data:data
+  })
+}
+
+// 商品修改
+export function update(data) {
+  return request({
+    url: '/goods/merc-mini/goodsMerc/update',
+    method: 'post',
+	data:data
+  })
+}
+
+
+
+

+ 57 - 0
api/commodity/point.js

@@ -0,0 +1,57 @@
+import request from '@/utils/request'
+
+// 分页
+export function pointPage(data) {
+  return request({
+    url: '/mini/mercPlace/page',
+    method: 'post',
+	data:data
+  })
+}
+
+// 新增修改点位
+export function pointSave(data) {
+  return request({
+    url: '/mini/mercPlace/save',
+    method: 'post',
+	data:data
+  })
+}
+
+
+
+// 绑定/解绑设备 
+export function bindDevice(data) {
+  return request({
+    url: '/mini/mercPlace/bindDevice',
+    method: 'post',
+    data:data
+  })
+}
+
+// 列表(内部服务调用) 
+export function listById(data) {
+  return request({
+    url: '/mini/mercPlace/listById',
+    method: 'post',
+    data:data
+  })
+}
+
+// 列表
+export function pointListByMerc(data) {
+  return request({
+    url: '/mini/mercPlace/listByMerc',
+    method: 'post',
+    data:data
+  })
+}
+
+// 删除
+export function pointDel(data) {
+  return request({
+    url: '/mini/mercPlace/del',
+    method: 'post',
+    data:data
+  })
+}

+ 88 - 0
api/commoditylist/commoditylist.js

@@ -0,0 +1,88 @@
+import request from '@/utils/request'
+
+// 分页查询
+export function page(data) {
+	return request({
+		url: '/goods/merc-mini/goodsMercModel/page',
+		method: 'post',
+		data: data
+	})
+}
+//清单关联商品列表
+export function refGoods(data) {
+	return request({
+		url: '/goods/merc-mini/goodsMercModel/refGoods',
+		method: 'post',
+		data: data
+	})
+}
+// 新增商品清单 
+export function save(data) {
+	return request({
+		url: '/goods/merc-mini/goodsMercModel/save',
+		method: 'post',
+		data: data
+	})
+}
+// 商品清单应用设备
+export function saveGoodsDevice(data) {
+	return request({
+		url: '/goods/merc-mini/goodsMercModel/saveGoodsDevice',
+		method: 'post',
+		data: data
+	})
+}
+// 修改商品清单 
+export function update(data) {
+	return request({
+		url: '/goods/merc-mini/goodsMercModel/update',
+		method: 'post',
+		data: data
+	})
+}
+// 保存清单商品(每次传全量数据)
+export function saveListingGoods(data) {
+	return request({
+		url: '/goods/merc-mini/goodsMercModel/saveListingGoods',
+		method: 'post',
+		data: data
+	})
+}
+
+//获取区域下设备列表
+export function regionDevices(data) {
+	return request({
+		url: '/merc/mini/mercLine/regionDevices',
+		method: 'post',
+		data: data
+	})
+}
+//获取商户线路列表
+export function mercLineDevices(data) {
+	return request({
+		url: '/goods/merc-mini/goodsMercModel/mercLineDevices',
+		method: 'post',
+		data: data
+	})
+}
+
+//商品清单应用到设备
+export function goodsListApply(data) {
+	return request({
+		url: '/goods/goodsDevice/save',
+		method: 'post',
+		data: data
+	})
+}
+
+//删除清单
+export function del(data) {
+	return request({
+		url: '/goods/goods-merc-model/del ',
+		method: 'post',
+		data: data
+	})
+}
+
+
+

+ 373 - 0
api/device/device.js

@@ -0,0 +1,373 @@
+import request from '@/utils/request'
+// 设备列表
+export function mercHomeList(data) {
+  return request({
+    url: '/merc/mini/device/mercHomeList',
+    method: 'post',
+	data:data
+  })
+}
+
+// 商户设备首页统计
+export function mercHomeStatistical(data) {
+  return request({
+    url: '/device/merc-mini/device/mercHomeStatistical',
+    method: 'post',
+	data:data
+  })
+}
+
+// 增加商品到设备  
+export function bindDevice(data) {
+  return request({
+    url: '/goods/merc-mini/goodsDevice/bindDevice',
+    method: 'post',
+    data:data
+  })
+}
+
+// 设备商品列表
+export function goodsList(data) {
+  return request({
+    url: '/goods/merc-mini/goodsDevice/page',
+    method: 'post',
+    data:data
+  })
+}
+
+// 商户设备列表
+export function searchPage(data) {
+  return request({
+    url: '/device/merc-mini/device/searchPage',
+    method: 'post',
+    data:data
+  })
+}
+
+// 商户设备详情
+export function detail(data) {
+  return request({
+    url: '/device/merc-mini/device/detail',
+    method: 'post',
+    data:data
+  })
+}
+
+// 数据统计
+export function dataCount(data) {
+  return request({
+    url: '/device/merc-mini/device/dataCount',
+    method: 'post',
+    data:data
+  })
+}
+
+// 设备操作记录
+export function indexDeviceRecords(data) {
+  return request({
+    url: '/device/merc-mini/deviceRecords/indexDeviceRecords',
+    method: 'post',
+    data:data
+  })
+}
+
+// 设备联网记录
+export function indexDeviceNetRecords(data) {
+  return request({
+    url: '/device/merc-mini/deviceRecords/indexDeviceNetRecords',
+    method: 'post',
+    data:data
+  })
+}
+
+// 设备故障情况
+export function indexDeviceError(data) {
+  return request({
+    url: '/device/merc-mini/deviceRecords/indexDeviceError',
+    method: 'post',
+    data:data
+  })
+}
+
+// 设备温度记录
+export function indexDeviceTempRecords(data) {
+  return request({
+    url: '/device/merc-mini/deviceRecords/indexDeviceTempRecords ',
+    method: 'post',
+    data:data
+  })
+}
+
+// 设备激活
+export function deviceActive(data) {
+  return request({
+    url: '/device/merc-mini/device/active',
+    method: 'post',
+    data:data
+  })
+}
+
+// 设备详情
+export function deviceDetail(data) {
+  return request({
+    url: '/device/device-info/obj',
+    method: 'post',
+    data:data
+  })
+}
+
+// 运营状态修改
+export function modifyBusyStage(data) {
+  return request({
+    url: '/device/merc-mini/device/modifyBusyStage',
+    method: 'post',
+    data:data
+  })
+}
+
+// 首页统计数据
+export function allCount(data) {
+  return request({
+    url: '/order/order-merc-sales-count-more/count',
+    method: 'post',
+    data:data
+  })
+}
+
+// 设备商品分类
+export function categoryList(data) {
+  return request({
+    url: '/goods/merc-mini/goodsDevice/categoryList',
+    method: 'post',
+    data:data
+  })
+}
+
+// 修改设备信息
+export function updateInfo(data) {
+  return request({
+    url: '/device/merc-mini/device/updateInfo',
+    method: 'post',
+    data:data
+  })
+}
+
+export function sendCommand(data) {
+  return request({
+    url: '/device/mqtt/senCommand',
+    method: 'post',
+    data:data
+  })
+}
+//查询指令操作结果
+export function queryCommandResult(data) {
+  return request({
+    url: '/device/mqtt/snByCmdAndResult',
+    method: 'post',
+    data:data
+  })
+}
+
+//销售统计汇总
+export function sumCount(data) {
+  return request({
+    url: '/device/merc-mini/deviceData/sumCount',
+    method: 'post',
+    data:data
+  })
+}
+
+//销售统计列表
+export function sumPage(data) {
+  return request({
+    url: '/device/merc-mini/deviceData/sumPage',
+    method: 'post',
+    data:data
+  })
+}
+
+//设备激活情况
+export function getActiveInfo(data) {
+  return request({
+    url: '/device/merc-mini/device/getActiveInfo',
+    method: 'post',
+    data:data
+  })
+}
+
+//设备是否属于商户
+export function isMerc(data) {
+  return request({
+    url: '/device/merc-mini/device/isMerc',
+    method: 'post',
+    data:data
+  })
+}
+
+
+//设备二维码
+export function getQrCode(data) {
+  return request({
+    url: '/device/device-quality/getQrCode',
+    method: 'post',
+    data:data
+  })
+}
+
+//导出设备二维码
+export function exportQrCode(data) {
+  return request({
+    url: '/device/device-create-ids/exportQrCode',
+    method: 'post',
+    data:data
+  })
+}
+
+//清除故障
+export function abort(data) {
+  return request({
+    url: '/order/activity-info/abort',
+    method: 'post',
+    data:data
+  })
+}
+
+//统计数据图表
+export function salesData(data) {
+  return request({
+    url: '/merc/mini/data/salesData',
+    method: 'post',
+    data:data
+  })
+}
+
+//支付宝设备激活分页
+export function aliDeviceActivePage(data) {
+  return request({
+    url: '/merc/mini/device/aliDeviceActivePage',
+    method: 'post',
+    data:data
+  })
+}
+
+//获取商家信息
+export function userInfoBySelf(data) {
+  return request({
+    url: '/merc/mini/mercUser/userInfoBySelf',
+    method: 'post',
+    data:data
+  })
+}
+
+//绑定支付宝
+export function updateByAli(data) {
+  return request({
+    url: '/merc/mini/mercUser/updateByAli',
+    method: 'post',
+    data:data
+  })
+}
+
+//支付宝设备详情查询
+export function aliDeviceObj(data) {
+  return request({
+    url: '/merc//mini/device/aliDeviceObj',
+    method: 'post',
+    data:data
+  })
+}
+
+
+//商品销售统计汇总
+export function goodSumCount(data) {
+  return request({
+    url: '/goods/goodsDeviceData/sumCount',
+    method: 'post',
+    data:data
+  })
+}
+
+//商品销售统计列表
+export function goodSumPage(data) {
+  return request({
+    url: '/goods/goodsDeviceData/sumPage',
+    method: 'post',
+    data:data
+  })
+}
+
+export function mercDeviceList(data) {
+  return request({
+    url: '/merc/mini/mercUserDevice/mercDeviceList',
+    method: 'post',
+    data:data
+  })
+}
+
+export function addDevice(data) {
+  return request({
+    url: '/merc/mini/mercUserDevice/add',
+    method: 'post',
+    data:data
+  })
+}
+
+export function userDeviceList(data) {
+  return request({
+    url: '/merc/mini/mercUserDevice/userDeviceList',
+    method: 'post',
+    data:data
+  })
+}
+
+export function delDevice(data) {
+  return request({
+    url: '/merc/mini/mercUserDevice/del',
+    method: 'post',
+    data:data
+  })
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 9 - 0
api/dict.js

@@ -0,0 +1,9 @@
+import request from '@/utils/request'
+//数据字典
+export function list2(data) {
+	return request({
+		url: '/sys/sys-dict/list2',
+		method: 'post',
+		data: data
+	})
+}

+ 11 - 0
api/download.js

@@ -0,0 +1,11 @@
+import {
+	downLoadReq
+} from '@/utils/request'
+
+export function exportQrCode(data) {
+	return downLoadReq({
+		url: '/device/device-create-ids/exportQrCode',
+		method: 'post',
+		data: data
+	})
+}

+ 50 - 0
api/login.js

@@ -0,0 +1,50 @@
+import request from '@/utils/request'
+
+// 登录方法
+export function login(loginName, password, code, clientType,pointJson) {
+  const data = {
+    loginName,
+    password,
+    code,
+    clientType,
+	pointJson
+  }
+  return request({
+    'url': '/merc/mini/login',
+    headers: {
+      isToken: false
+    },
+    'method': 'post',
+    'data': data
+  })
+}
+
+// 获取用户详细信息
+export function getInfo() {
+  return request({
+    'url': '/getInfo',
+    'method': 'get'
+  })
+}
+
+// 退出方法
+export function logout() {
+  return request({
+    'url': '/authorize/sysWorkUser/logout',
+    'method': 'post'
+  })
+}
+
+// 获取验证码
+export function getCodeImg() {
+  return request({
+    'url': '/captchaImage',
+    headers: {
+      isToken: false
+    },
+    method: 'get',
+    timeout: 20000
+  })
+}
+
+

+ 144 - 0
api/order/order.js

@@ -0,0 +1,144 @@
+import request from '@/utils/request'
+
+//商户订单查询 
+export function page(data) {
+	return request({
+		url: '/merc/mini/orders/page',
+		method: 'post',
+		data: data
+	})
+}
+
+
+
+
+//订单详情
+export function byId(data) {
+	return request({
+		url: '/order/order-merc-mini/byId',
+		method: 'post',
+		data: data
+	})
+}
+
+//退款列表
+export function refundList(data) {
+	return request({
+		url: '/order/order-refund-merc-mini/page',
+		method: 'post',
+		data: data
+	})
+}
+
+//订单列表
+export function orderPage(data) {
+	return request({
+		url: '/order/orders/mini/page',
+		method: 'post',
+		data: data
+	})
+}
+
+//订单列表-统计 
+export function orderPageCount(data) {
+	return request({
+		url: '/device/merc-mini/device/order/count',
+		method: 'post',
+		data: data
+	})
+}
+
+//退款处理
+export function hendel(data) {
+	return request({
+		url: '/order/order-refund-merc-mini/hendel',
+		method: 'post',
+		data: data
+	})
+}
+
+//退款情况
+export function refundDetail(data) {
+	return request({
+		url: '/order/order-refund-merc-mini/detail',
+		method: 'post',
+		data: data
+	})
+}
+
+//订单日志
+export function orderLogs(data) {
+	return request({
+		url: '/order/activity-info-og/list',
+		method: 'post',
+		data: data
+	})
+}
+
+//拉黑
+export function setBlacklist(data) {
+	return request({
+		url: '/merc/mini/member/setBlacklist',
+		method: 'post',
+		data: data
+	})
+}
+
+//拉黑解除
+export function removeBlackList(data) {
+	return request({
+		url: '/merc/mini/member/blackListRemove ',
+		method: 'post',
+		data: data
+	})
+}
+
+//订单用户信息
+export function userInfo(data) {
+	return request({
+		url: '/merc/mini/member/obj',
+		method: 'post',
+		data: data
+	})
+}
+
+//首页角标
+export function tipsCount(data) {
+	return request({
+		url: '/order/order-merc-homepage-mini/tipsCount',
+		method: 'post',
+		data: data
+	})
+}
+
+//首页(本月销售)统计
+export function countByMonth(data) {
+	return request({
+		url: '/order/order-merc-sales-count-more/countByMonth',
+		method: 'post',
+		data: data
+	})
+}
+
+//首页(今日销售)统计
+export function countByDay(data) {
+	return request({
+		url: '/order/order-merc-sales-count-more/dayCountDetail',
+		method: 'post',
+		data: data
+	})
+}
+
+//订单主动退款
+export function refundByMerc(data) {
+	return request({
+		url: '/order/order-refund-merc-mini/refundByMerc',
+		method: 'post',
+		data: data
+	})
+}
+
+
+
+
+

+ 50 - 0
api/order/riskorder.js

@@ -0,0 +1,50 @@
+import request from '@/utils/request'
+
+// 异常单补扣申请 
+export function apply(data) {
+	return request({
+		url: '/order/order-riskCut-merc-mini/apply',
+		method: 'post',
+		data: data
+	})
+}
+
+//风险订单-分页 
+export function page(data) {
+	return request({
+		url: '/order/order-riskCut-merc-mini/page',
+		method: 'post',
+		data: data
+	})
+}
+
+//所有待处理异常单
+export function todoNum(data) {
+	return request({
+		url: '/order/order-riskCut-merc-mini/todoNum',
+		method: 'post',
+		data: data
+	})
+}
+
+//风险订单结束
+export function cancelOrder(data) {
+	return request({
+		url: '/order/order-pay-mini/riskOrder/cancel',
+		method: 'post',
+		data: data
+	})
+}
+
+//风险订单补扣申请撤回
+export function rollback(data) {
+	return request({
+		url: '/order/order-riskCut-merc-mini/rollback ',
+		method: 'post',
+		data: data
+	})
+}
+
+
+
+

+ 9 - 0
api/oss.js

@@ -0,0 +1,9 @@
+import request from '@/utils/request'
+
+// 获取用户详细信息
+export function ossInfo() {
+  return request({
+    'url': '/sys/oss/aliOSS/policy',
+    'method': 'get'
+  })
+}

+ 38 - 0
api/point/area.js

@@ -0,0 +1,38 @@
+import request from '@/utils/request'
+
+// 新增区域
+export function areaSave(data) {
+  return request({
+    url: '/merc/mini/mercRegion/save',
+    method: 'post',
+	data:data
+  })
+}
+
+// 区域树
+export function areaTree() {
+  return request({
+    'url': '/merc/mini/mercRegion/tree',
+    'method': 'post'
+  })
+}
+
+// 区域删除
+export function areaDel(data) {
+  return request({
+    url: '/merc/mini/mercRegion/del',
+    method: 'post',
+    data:data
+  })
+}
+
+// 新区域线路接口
+export function allLineWithRegion(data) {
+  return request({
+    url: '/merc/mini/mercLine/allLineWithRegion',
+    method: 'post',
+    data:data
+  })
+}
+
+

+ 56 - 0
api/point/line.js

@@ -0,0 +1,56 @@
+import request from '@/utils/request'
+// 新增修改线路
+export function lineSave(data) {
+  return request({
+    url: '/merc/mini/mercLine/save',
+    method: 'post',
+	data:data
+  })
+}
+
+// 分页
+export function linePage(data) {
+  return request({
+    url: '/merc/mini/mercLine/page',
+    method: 'post',
+	data:data
+  })
+}
+
+// 绑定/解绑设备 
+export function bindDevice(data) {
+  return request({
+    url: '/merc/mini/mercLine/bindDevice',
+    method: 'post',
+    data:data
+  })
+}
+
+// 列表(内部服务调用) 
+export function listById(data) {
+  return request({
+    url: '/merc/mini/mercLine/listById',
+    method: 'post',
+    data:data
+  })
+}
+
+// 列表
+export function lineListByMerc(data) {
+  return request({
+    url: '/merc/mini/mercLine/listByMerc',
+    method: 'post',
+    data:data
+  })
+}
+
+// 删除
+export function lineDel(data) {
+  return request({
+    url: '/merc/mini/mercLine/del',
+    method: 'post',
+    data:data
+  })
+}
+
+

+ 57 - 0
api/point/point.js

@@ -0,0 +1,57 @@
+import request from '@/utils/request'
+
+// 分页
+export function pointPage(data) {
+  return request({
+    url: '/merc/mini/mercPlace/page',
+    method: 'post',
+	data:data
+  })
+}
+
+// 新增修改点位
+export function pointSave(data) {
+  return request({
+    url: '/merc/mini/mercPlace/save',
+    method: 'post',
+	data:data
+  })
+}
+
+
+
+// 绑定/解绑设备 
+export function bindDevice(data) {
+  return request({
+    url: '/merc/mini/mercPlace/bindDevice',
+    method: 'post',
+    data:data
+  })
+}
+
+// 列表(内部服务调用) 
+export function listById(data) {
+  return request({
+    url: '/merc/mini/mercPlace/listById',
+    method: 'post',
+    data:data
+  })
+}
+
+// 列表
+export function pointListByMerc(data) {
+  return request({
+    url: '/merc/mini/mercPlace/listByMerc',
+    method: 'post',
+    data:data
+  })
+}
+
+// 删除
+export function pointDel(data) {
+  return request({
+    url: '/merc/mini/mercPlace/del',
+    method: 'post',
+    data:data
+  })
+}

+ 156 - 0
api/replenishment/replenishment.js

@@ -0,0 +1,156 @@
+import request from '@/utils/request'
+
+// 补货首页
+export function supplyPage(data) {
+	return request({
+		url: '/goods/merc-mini/goodsDevice/supplyPage',
+		method: 'post',
+		data: data
+	})
+}
+
+//补货删除
+export function delGoods(data) {
+	return request({
+		url: '/goods/merc-mini/goodsDevice/delGoods',
+		method: 'post',
+		data: data
+	})
+}
+// 补货保存
+export function save(data) {
+	return request({
+		url: '/merc/mini/mercSupply/save',
+		method: 'post',
+		data: data
+	})
+}
+// 一键开柜
+export function saveByOpenDoor(data) {
+	return request({
+		url: '/merc/mini/mercSupply/saveByOpenDoor',
+		method: 'post',
+		data: data
+	})
+}
+
+//补货记录分页查询
+export function page(data) {
+	return request({
+		url: '/merc/mini/mercSupply/page',
+		method: 'post',
+		data: data
+	})
+}
+//补货记录对象查询
+export function obj(data) {
+	return request({
+		url: '/merc/mini/mercSupply/obj',
+		method: 'post',
+		data: data
+	})
+}
+
+// 效验
+export function check(data) {
+	return request({
+		'url': '/order/activity-info/check',
+		'method': 'post',
+		'data': data
+	})
+}
+// 按线路+商品分组 
+export function stockByLineAndGoods(data) {
+	return request({
+		'url': '/goods/merc-mini/stockData/stockByLineAndGoods',
+		'method': 'post',
+		'data': data
+	})
+}
+
+// 按设备+商品分组 
+export function stockByDeviceAndGoods(data) {
+	return request({
+		'url': '/goods/merc-mini/stockData/stockByDeviceAndGoods',
+		'method': 'post',
+		'data': data
+	})
+}
+
+// 按设备分组 
+export function stockByDevice(data) {
+	return request({
+		'url': '/goods/merc-mini/stockData/stockByDevice',
+		'method': 'post',
+		'data': data
+	})
+}
+
+// 按商品分组 
+export function stockByGoods(data) {
+	return request({
+		'url': '/goods/merc-mini/stockData/stockByGoods',
+		'method': 'post',
+		'data': data
+	})
+}
+
+// 按线路分组 
+export function stockByLine(data) {
+	return request({
+		'url': '/goods/merc-mini/stockData/StockByLine',
+		'method': 'post',
+		'data': data
+	})
+}
+
+// 补货首页全部列表
+export function deviceStockList(data) {
+	return request({
+		'url': '/goods/merc-mini/goodsDevice/deviceStockList',
+		'method': 'post',
+		'data': data
+	})
+}
+
+// 补货首页缺货列表
+export function deviceStockOutList(data) {
+	return request({
+		'url': '/goods/merc-mini/goodsDevice/deviceStockOutList',
+		'method': 'post',
+		'data': data
+	})
+}
+
+// 补货首页缺货列表
+export function create(data) {
+	return request({
+		'url': '/order/activity-info/create',
+		'method': 'post',
+		'data': data
+	})
+}
+
+// 库存详情设备
+export function stockByDeviceDetail(data) {
+	return request({
+		'url': '/goods/merc-mini/stockData/stockByDeviceDetail',
+		'method': 'post',
+		'data': data
+	})
+}
+
+// 库存详情商品
+export function stockByGoodsDetail(data) {
+	return request({
+		'url': '/goods/merc-mini/stockData/stockByGoodsDetail',
+		'method': 'post',
+		'data': data
+	})
+}
+
+
+
+
+
+

+ 49 - 0
api/system/employee.js

@@ -0,0 +1,49 @@
+import request from '@/utils/request'
+
+// 获取用户菜单
+export function save(data) {
+  return request({
+    url: '/merc/mini/mercUser/save',
+    method: 'post',
+	data:data
+  })
+}
+
+// 获取用户列表
+export function list(data) {
+  return request({
+    url: '/merc/mini/mercUser/list',
+    method: 'post',
+	data:data
+  })
+}
+
+// 角色列表
+export function roleList(data) {
+  return request({
+    url: '/authorize/sysRole/page',
+    method: 'post',
+	data:data
+  })
+}
+
+// 用户详情
+export function objByUserId(data) {
+  return request({
+    url: '/merc/mini/mercUser/objByUserId',
+    method: 'post',
+	data:data
+  })
+}
+
+// 更新用户
+export function update(data) {
+  return request({
+    url: '/merc/mini/mercUser/update',
+    method: 'post',
+	data:data
+  })
+}
+
+
+

+ 22 - 0
api/system/menu.js

@@ -0,0 +1,22 @@
+import upload from '@/utils/upload'
+import request from '@/utils/request'
+
+// 获取系统权限菜单
+export function workMenuList(data) {
+  return request({
+    url: '/authorize/sys-menu/workMenuList',
+    method: 'post',
+    data: data
+  })
+}
+
+// 获取系统集合
+export function sysList(data) {
+  return request({
+    url: '/authorize/sys-system/list',
+    method: 'post',
+    data: data
+  })
+}
+
+

+ 50 - 0
api/system/user.js

@@ -0,0 +1,50 @@
+import request from '@/utils/request'
+
+// 获取用户菜单
+export function workMenuList() {
+  return request({
+    url: '/authorize/sys-menu/workMenuList',
+    method: 'post'
+  })
+}
+
+// 获取用户信息
+export function userInfo() {
+  return request({
+    url: '/merc/mini/orders/my',
+    method: 'post'
+  })
+}
+
+// 协议
+export function agreement(data) {
+  return request({
+    url: '/sys/sys-agreement/obj',
+    method: 'post',
+	data:data
+  })
+}
+
+// 协议列表
+export function agreementPage(data) {
+  return request({
+    url: '/sys/sys-agreement/page',
+    method: 'post',
+	data:data
+  })
+}
+
+// 修改用户密码等信息
+export function updateUserInfo(data) {
+  return request({
+    url: '/merc/mini/orders/updateUserInfo',
+    method: 'post',
+	data:data
+  })
+}
+
+
+
+
+
+

+ 687 - 0
components/classify/index.vue

@@ -0,0 +1,687 @@
+<template>
+	<view class="wrap">
+		<view class="u-menu-wrap">
+			<scroll-view :style="{height:height}" scroll-y scroll-with-animation class="u-tab-view menu-scroll-view"
+				:scroll-top="scrollTop" v-if="leftShow">
+				<view v-for="(item,index) in newTabList" :key="index" class="u-tab-item"
+					:class="[current==index ? 'u-tab-item-active' : '']" :data-current="index"
+					@tap.stop="swichMenu(index,item)">
+					<text class="u-line-1">{{item.categoryName}}</text>
+					<view class="u-tab-num">
+						({{item.num}})
+					</view>
+					<u-badge type="error" max="99" :value="item.selectNum" :absolute="true" :offset="[4,4]"></u-badge>
+				</view>
+			</scroll-view>
+
+			<scroll-view lower-threshold="150" :style="{height:height}" scroll-y class="right-box"
+				@scrolltolower="lowerBottom">
+				<view class="page-view">
+					<view class="class-item" v-if="newCommList.length>0">
+						<view class="item-container">
+							<view class="thumb-box" v-for="(item, index) in newCommList" :key="index"
+								@click.stop="commItemClick(item)">
+								<view v-if="selectShow">
+									<image class="select-img"
+										src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/commodity/no_selected.png"
+										mode="widthFix" v-show="item.noSelect"></image>
+									<image class="select-img"
+										src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/commodity/selected.png"
+										mode="widthFix" v-show="!item.noSelect&&item.checked"></image>
+									<image class="select-img"
+										src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/commodity/select.png"
+										mode="widthFix" v-show="!item.noSelect&&!item.checked"></image>
+								</view>
+
+								<view class="check-content">
+									<view class="comm-img">
+										<u--image radius="4" width="130rpx" height="130rpx" :src="item.cover"
+											mode="aspectFit" :lazy-lord="true"></u--image>
+									</view>
+									<view class="comm-main">
+										<view>
+											{{item.name}}
+										</view>
+										<view>
+											条形码:{{item.barcode}}
+										</view>
+										<!-- <view>
+											商品类型:{{item.categoryName}}
+										</view> -->
+										<view v-if="item.price!=null">
+											价格:<text>¥{{item.price}}</text>
+										</view>
+									</view>
+								</view>
+							</view>
+							<u-loadmore :status="status" v-if="newCommList.length>=1" />
+						</view>
+					</view>
+					<view class="empty" v-else>
+						<u-empty></u-empty>
+					</view>
+				</view>
+
+			</scroll-view>
+
+			<view class="cart" v-if="selectShow" @click="cartShow=true">
+				<image class="cart-img"
+					src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/commodity/buy.png"
+					mode="widthFix"></image>
+				<u-badge type="error" max="99" :value="selectList.length" :absolute="true" :offset="[4,4]"></u-badge>
+			</view>
+		</view>
+
+		<xpopup :show="cartShow" @close="close" :showBtn="false">
+			<view class="pop-top" slot="title">
+				<view class="left">
+					已选商品
+				</view>
+				<xbutton size="mini" class="clear" @click="clearCart">清空</xbutton>
+			</view>
+			<view class="pop-content">
+				<u-list height="440rpx">
+					<u-list-item v-for="(item, index) in selectList" :key="item.id">
+						<view class="list-item">
+							<view class="comm-item">
+								<view class="comm-img">
+									<u--image radius="4" width="110rpx" height="110rpx" :src="item.cover"
+										mode="aspectFit" :lazy-lord="true"></u--image>
+								</view>
+								<view class="item-content">
+									<view>
+										{{item.name}}
+									</view>
+									<view>
+										条形码:{{item.barcode}}
+									</view>
+									<view>
+										商品类型:{{item.categoryName}}
+									</view>
+									<view v-if="item.price">
+										价格:<text>¥{{item.price}}</text>
+									</view>
+								</view>
+							</view>
+							<xbutton color="red" size="mini" bgColor="#fff" borderColor="red" @click="delCom(item)">删除
+							</xbutton>
+						</view>
+					</u-list-item>
+					<view class="empty" v-if="selectList.length==0">
+						<u-empty mode="car" text="没有商品!"></u-empty>
+					</view>
+				</u-list>
+			</view>
+		</xpopup>
+
+		<xpopup :show="delPopShow" :showBtn="true" @confirm="delSure" @close="delClose">
+			<view class="del-content">
+				是否确定要{{delTitle}}?
+			</view>
+		</xpopup>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				scrollTop: 0, //tab标题的滚动条位置
+				current: 0, // 预设当前项的值
+				menuHeight: 0, // 左边菜单的高度
+				menuItemHeight: 0, // 左边菜单item的高度
+				keyword: '',
+				checkedList: [], //当前类目选中商品
+				cartShow: false, //购物车
+
+				delPopShow: false, //删除弹框
+				delTitle: undefined, //删除或清空提示
+				selectList: [], //已选中商品
+			}
+		},
+		props: {
+			value: {
+				type: Array,
+				required: false
+			},
+			selectShow: {
+				type: Boolean,
+				default: false,
+				require: false
+			},
+			leftShow: {
+				type: Boolean,
+				default: true,
+				require: false
+			},
+			height: {
+				type: String,
+				require: false,
+				default: 'auto'
+			},
+
+			tabList: {
+				type: Array,
+				require: true,
+				default () {
+					return []
+				}
+			},
+
+			commList: {
+				type: Array,
+				require: true,
+				default () {
+					return []
+				}
+			},
+
+			status: {
+				type: String,
+				require: false,
+				default: 'loadmore'
+			},
+
+			storeName: {
+				type: String,
+				require: false,
+				default: 'commStor'
+			},
+			
+			isModal:{
+				type: Boolean,
+				require: false,
+				default: false
+			},
+		},
+
+		computed: {
+			newTabList() {
+				if (this.tabList.length > 0) {
+					let newTabList = JSON.parse(JSON.stringify(this.tabList));
+					if (this.selectShow) {
+						const idMapping = newTabList.reduce((acc, el, i) => {
+							el.selectNum = 0;
+							acc[el.categoryCode] = i;
+							return acc;
+						}, {});
+						this.selectList.forEach(i => {
+							if (i.categoryCode == null) {
+								i.categoryCode = '0'
+							}
+							newTabList[idMapping[i.categoryCode]].selectNum++
+						})
+						return newTabList
+					} else {
+						return newTabList
+					}
+				} else {
+					return []
+				}
+			},
+
+			newCommList() {
+				let newCommList = [];
+				if (this.selectList && this.selectList.length > 0) {
+					let selectList = this.selectList
+					const idMapping = selectList.reduce((acc, el, i) => {
+						acc[el.id] = i;
+						return acc;
+					}, {});
+					this.commList.forEach(i => {
+						if (idMapping[i.id] != undefined) { //重复值
+							i.checked = true
+						} else {
+							i.checked = false
+						}
+						newCommList.push(i)
+					})
+				} else {
+					newCommList = this.commList.map(i => {
+						i.checked = false;
+						return i
+					})
+				}
+				return newCommList
+			}
+		},
+
+		watch: {
+			commList: {
+				handler(newVal, oldVal) {
+
+				},
+				immediate: true,
+				deep: true,
+			},
+		},
+
+		created() {
+			this.onshow()
+		},
+
+		methods: {
+			//搜索页来回跳转,刷新已选商品数据
+			onshow() {
+				if (uni.getStorageSync(this.storeName) && JSON.parse(uni.getStorageSync(
+						this.storeName)).length > 0) {
+					let commStor = JSON.parse(uni.getStorageSync(this.storeName))
+					this.selectList = commStor;
+				}
+			},
+
+			// 商品点击
+			commItemClick(e) {
+				if (e.noSelect) {
+					this.$modal.msg('当前商品已存在!')
+					return
+				}
+				if (this.selectShow) { //有选中框
+					if (this.isModal) { //私库中新建模商品,需要设置价格
+						if (e.price == null || e.price == undefined) {
+							this.$emit('comClick', e)
+							return
+						}
+					}
+
+					if (!e.checked && this.selectList && this.selectList.length >= 10) {
+						this.$modal.msg('一次最多添加10个商品!')
+						return
+					}
+
+					e.checked = !e.checked
+					//选中商品存储到内存中,以便取用
+					let commStor = []
+					if (e.checked) { //选中添加到存储中
+						if (uni.getStorageSync(this.storeName)) {
+							commStor = JSON.parse(uni.getStorageSync(this.storeName));
+						}
+						commStor.push(e);
+					} else { //取消选中删除,并且从存储中删除
+						commStor = JSON.parse(uni.getStorageSync(this.storeName));
+						for (let i = 0; i < commStor.length; i++) {
+							let item = commStor[i]
+							if (item.id == e.id) {
+								commStor.splice(i, 1);
+								break
+							}
+						}
+					}
+					//收集选中商品,更新存储
+					this.selectList = commStor;
+					commStor.length > 0 ? uni.setStorageSync(this.storeName, JSON.stringify(commStor)) : uni
+						.setStorageSync(
+							this.storeName, '')
+					if (commStor.length > 0) {
+						console.log('commStor', commStor)
+						uni.setStorageSync(this.storeName, JSON.stringify(commStor))
+					}
+				} else { //无选中框
+					this.$emit('comClick', e)
+				}
+			},
+
+			lowerBottom() {
+				this.$emit('lowerBottom')
+			},
+
+			// 点击左边的栏目切换
+			async swichMenu(index, item) {
+				if (index == this.current) return;
+				this.current = index;
+				this.$emit('switchMenu', item)
+				// 如果为0,意味着尚未初始化
+				if (this.menuHeight == 0 || this.menuItemHeight == 0) {
+					await this.getElRect('menu-scroll-view', 'menuHeight');
+					await this.getElRect('u-tab-item', 'menuItemHeight');
+				}
+				// 将菜单菜单活动item垂直居中
+				this.scrollTop = index * this.menuItemHeight + this.menuItemHeight / 2 - this.menuHeight / 2;
+			},
+			// 获取一个目标元素的高度
+			getElRect(elClass, dataVal) {
+				new Promise((resolve, reject) => {
+					const query = uni.createSelectorQuery().in(this);
+					query.select('.' + elClass).fields({
+						size: true
+					}, res => {
+						// 如果节点尚未生成,res值为null,循环调用执行
+						if (!res) {
+							setTimeout(() => {
+								this.getElRect(elClass);
+							}, 10);
+							return;
+						}
+						this[dataVal] = res.height;
+					}).exec();
+				})
+			},
+
+			// 关闭购物车
+			close() {
+				this.cartShow = false
+			},
+
+			// 删除当前商品
+			delCom(item) {
+				//删除选中商品,更新存储
+				let commStor = JSON.parse(uni.getStorageSync(this.storeName));
+				for (let i = 0; i < commStor.length; i++) {
+					let stor = commStor[i];
+					if (stor.id == item.id) {
+						commStor.splice(i, 1);
+						break
+					}
+				}
+				this.selectList = commStor;
+				uni.setStorageSync(this.storeName, JSON.stringify(commStor));
+				this.$modal.msg('删除成功~');
+			},
+
+			//清空购物车
+			clearCart() {
+				if (this.selectList.length > 0) {
+					this.delPopShow = true
+					this.delTitle = '清空已选商品'
+				} else {
+					this.$modal.msg('您没有选择任何商品!');
+				}
+			},
+
+			delSure() {
+				this.delPopShow = false
+				this.selectList = [];
+				uni.setStorageSync(this.storeName, '')
+				this.close()
+				this.$modal.msg('清空成功~');
+			},
+
+			delClose() {
+				this.delPopShow = false
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.u-menu-wrap {
+		flex: 1;
+		display: flex;
+		overflow: hidden;
+	}
+
+	.u-tab-view {
+		width: 200rpx;
+		height: 100%;
+	}
+
+	.u-tab-item {
+		height: 110rpx;
+		background: #f6f6f6;
+		box-sizing: border-box;
+		display: flex;
+		flex-direction: column;
+		align-items: center;
+		justify-content: center;
+		font-size: 26rpx;
+		color: #444;
+		font-weight: 400;
+		line-height: 1;
+		position: relative;
+
+		&::after {
+			content: '';
+			height: 1rpx;
+			width: 100%;
+			background-color: #fafafa;
+			position: absolute;
+			left: 0;
+			bottom: 0;
+		}
+
+		.u-tab-num {
+			font-size: 20rpx;
+			margin-top: 6rpx;
+		}
+	}
+
+	.u-tab-item-active {
+		position: relative;
+		background: #fff;
+		color: #000;
+		font-weight: bold;
+	}
+
+	.u-tab-item-active::before {
+		content: "";
+		position: absolute;
+		border-left: 4px solid $uni-color-primary;
+		height: 32rpx;
+		left: 0;
+		top: 39rpx;
+	}
+
+	.u-tab-view {
+		height: 100%;
+	}
+
+	.right-box {
+		background-color: rgb(250, 250, 250);
+		padding-bottom: 20rpx;
+		position: relative;
+	}
+
+	.page-view {
+		padding: 16rpx 16rpx 0;
+	}
+
+	.class-item {
+		background-color: #fff;
+		padding: 16rpx;
+		border-radius: 8rpx;
+	}
+
+	.item-title {
+		font-size: 30rpx;
+		color: $u-main-color;
+		font-weight: bold;
+		margin: 10rpx 0;
+	}
+
+	.item-menu-name {
+		font-weight: normal;
+		font-size: 24rpx;
+		color: $u-main-color;
+	}
+
+	.item-container {
+		display: flex;
+		flex-direction: column;
+		flex-wrap: wrap;
+	}
+
+	.empty {
+		position: absolute;
+		left: 50%;
+		top: 50%;
+		transform: translate(-50%, -50%);
+	}
+
+	.item-menu-image {
+		width: 120rpx;
+		height: 120rpx;
+		border-radius: 100rpx;
+	}
+
+	.thumb-box {
+		display: flex;
+		flex-flow: row nowrap;
+		padding: 12rpx 0;
+		align-items: center;
+
+		&:not(:last-child) {
+			border-bottom: 1rpx solid #f4f4f4;
+			margin-bottom: 10rpx;
+		}
+	}
+
+	.select-img {
+		width: 40rpx;
+		height: 40rpx;
+	}
+
+	.check-content {
+		display: flex;
+		flex-direction: row;
+		align-items: center;
+		padding-left: 12rpx;
+
+		.comm-img {
+			width: 130rpx;
+			height: 130rpx;
+			display: flex;
+			flex-direction: row;
+			align-items: center;
+			justify-content: space-around;
+
+			image {
+				width: 100%;
+			}
+		}
+
+		.comm-main {
+			box-sizing: border-box;
+			padding-left: 18rpx;
+			color: #999;
+
+			>view {
+				padding: 4rpx 0;
+			}
+
+			>view:nth-child(1) {
+				font-size: 28rpx;
+				font-weight: bold;
+				color: #333;
+			}
+
+			>view:nth-child(2) {
+				font-size: 24rpx;
+				max-width: 380rpx;
+			}
+
+			>view:nth-child(3) {
+				font-size: 24rpx;
+			}
+
+			>view:nth-child(4) {
+				font-size: 24rpx;
+
+				text {
+					font-weight: bold;
+					color: red;
+				}
+			}
+		}
+	}
+
+	.cart {
+		width: 120rpx;
+		height: 120rpx;
+		display: flex;
+		flex-direction: row;
+		justify-content: center;
+		align-items: center;
+		border-radius: 120rpx;
+		position: fixed;
+		right: 24rpx;
+		bottom: 250rpx;
+
+		.cart-img {
+			width: 80rpx;
+			height: 80rpx;
+		}
+	}
+
+	.pop-top {
+		padding: 24rpx;
+		position: relative;
+
+		.left {
+			font-size: 28rpx;
+			font-weight: bold;
+		}
+
+		.clear {
+			position: absolute;
+			right: 24rpx;
+			top: 24rpx;
+		}
+	}
+
+	.pop-content {
+		// height: 200rpx;
+		padding: 0 24rpx;
+	}
+
+	.list-item {
+		display: flex;
+		flex-flow: row nowrap;
+		justify-content: space-between;
+		align-items: center;
+		width: 100%;
+	}
+
+	.comm-item {
+		display: flex;
+		flex-direction: row;
+		justify-content: flex-start;
+		align-items: center;
+		background-color: #fff;
+		margin-bottom: 12rpx;
+		border-radius: 12rpx;
+		box-sizing: border-box;
+		padding: 12rpx;
+
+		.comm-img {
+			width: 110rpx;
+		}
+
+		.item-content {
+			width: 480rpx;
+			padding-left: 24rpx;
+			color: #999;
+
+			>view:nth-child(1) {
+				font-size: 28rpx;
+				font-weight: bold;
+				color: #333;
+			}
+
+			>view:nth-child(2) {
+				font-size: 24rpx;
+				margin-top: 12rpx;
+			}
+
+			>view:nth-child(3) {
+				font-size: 24rpx;
+				margin-top: 12rpx;
+			}
+
+			>view:nth-child(4) {
+				font-size: 24rpx;
+				margin-top: 12rpx;
+
+				text {
+					font-weight: bold;
+					color: red;
+				}
+			}
+		}
+	}
+
+	.del-content {
+		padding: 48rpx;
+		font-size: 32rpx;
+		text-align: center;
+	}
+</style>

+ 108 - 0
components/tki-tree/style.css

@@ -0,0 +1,108 @@
+.tki-tree-mask {
+  position: fixed;
+  top: 0rpx;
+  right: 0rpx;
+  bottom: 0rpx;
+  left: 0rpx;
+  z-index: 9998;
+  background-color: rgba(0, 0, 0, 0.6);
+  opacity: 0;
+  transition: all 0.3s ease;
+  visibility: hidden;
+}
+.tki-tree-mask.show {
+  visibility: visible;
+  opacity: 1;
+}
+.tki-tree-cnt {
+  position: fixed;
+  top: 0rpx;
+  right: 0rpx;
+  bottom: 0rpx;
+  left: 0rpx;
+  z-index: 9999;
+  top: 160rpx;
+  transition: all 0.3s ease;
+  transform: translateY(100%);
+}
+.tki-tree-cnt.show {
+  transform: translateY(0);
+}
+.tki-tree-bar {
+  background-color: #fff;
+  height: 72rpx;
+  padding-left: 20rpx;
+  padding-right: 20rpx;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  box-sizing: border-box;
+  border-bottom-width: 1rpx !important;
+  border-bottom-style: solid;
+  border-bottom-color: #f5f5f5;
+  font-size: 32rpx;
+  color: #757575;
+  line-height: 1;
+}
+.tki-tree-bar-confirm {
+  color: #07bb07;
+}
+.tki-tree-view {
+  position: absolute;
+  top: 0rpx;
+  right: 0rpx;
+  bottom: 0rpx;
+  left: 0rpx;
+  top: 72rpx;
+  background-color: #fff;
+  padding-top: 20rpx;
+  padding-right: 20rpx;
+  padding-bottom: 20rpx;
+  padding-left: 20rpx;
+}
+.tki-tree-view-sc {
+  height: 100%;
+  overflow: hidden;
+}
+.tki-tree-item {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  font-size: 26rpx;
+  color: #757575;
+  line-height: 1;
+  height: 0;
+  opacity: 0;
+  transition: 0.2s;
+  position: relative;
+  overflow: hidden;
+}
+.tki-tree-item.show {
+  height: 80rpx;
+  opacity: 1;
+}
+.tki-tree-item.showchild:before {
+  transform: rotate(90deg);
+}
+.tki-tree-item.last:before {
+  opacity: 0;
+}
+.tki-tree-icon {
+  width: 26rpx;
+  height: 26rpx;
+  margin-right: 8rpx;
+}
+.tki-tree-label {
+  flex: 1;
+  display: flex;
+  align-items: center;
+  height: 100%;
+  line-height: 1.2;
+}
+.tki-tree-check {
+  width: 40px;
+  height: 40px;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}

File diff suppressed because it is too large
+ 50 - 0
components/tki-tree/tki-tree.vue


+ 160 - 0
components/xy-button/index.vue

@@ -0,0 +1,160 @@
+<template>
+	<button @click="btnClick" :class="['cu-btn',size,icon?'has-icon':'']"
+		:style="{backgroundColor:bgColor,padding:padding,color:color,borderRadius:round,width:delWidth,border:delBorder}">
+		<slot></slot>
+		<view class="image" v-if="icon">
+			<u--image width="32rpx" height="32rpx" :src="icon" mode="widthFix"
+				:lazy-load="true">
+			</u--image>
+		</view>
+	</button>
+</template>
+
+<script>
+	import {
+		debounce
+	} from '@/utils/common.js'
+	export default {
+		data() {
+			return {
+				bflag: false,
+				timer: null,
+			}
+		},
+		props: {
+			text: {
+				type: String,
+				require: true,
+				default: ''
+			},
+			color: {
+				type: String,
+				require: true,
+				default: '#fff'
+			},
+			bgColor: {
+				type: String,
+				require: true,
+				default: '#2C6FF3'
+			},
+			padding: {
+				type: String
+			},
+			round: {
+				type: [Number, String],
+				default: 'none'
+			},
+			size: {
+				type: String,
+				require: true,
+				default: 'normal'
+			},
+			width: {
+				type: String
+			},
+			borderColor: {
+				type: String
+			},
+			delay: {
+				type: [Number, String],
+				require: false,
+				default: 0
+			},
+			icon:{
+				type: String,
+				require: false
+			}
+		},
+
+		computed: {
+			delWidth() {
+				let width = this.width;
+				if (this.size == 'mini' && !this.width) {
+					width = "auto"
+				}
+				if (this.size != 'mini' && !this.width) {
+					width = "100%"
+				}
+				return width
+			},
+			delBorder() {
+				let border = this.borderColor;
+				if (!this.borderColor) {
+					border = '0'
+				} else {
+					border = `1rpx solid ${border}`
+				}
+				return border
+			}
+		},
+
+		methods: {
+			btnClick() {
+				if(this.bflag) return
+				console.log('确定提交')
+				this.$emit('click')
+				this.bflag=true;
+				this.timer=setTimeout(()=>{
+					this.bflag=false;
+				},this.delay)
+			}
+		},
+		
+		destroyed() {
+			if(this.timer){
+				clearTimeout(this.timer)
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.mini {
+		font-size: 24rpx;
+		border-radius: 6rpx;
+		padding: 0 12rpx;
+		height: 40rpx;
+		line-height: 38rpx;
+		display: inline-block;
+		width: 100rpx;
+	}
+	.medium {
+		font-size: 24rpx;
+		border-radius: 8rpx;
+		padding: 0 12rpx;
+		height: 50rpx;
+		line-height: 48rpx;
+		display: inline-block;
+		width: 100rpx;
+	}
+	.normal {
+		padding: 0 14rpx;
+		font-size: 26rpx;
+		height: 60rpx;
+		line-height: 58rpx;
+		border-radius: 8rpx;
+	}
+
+	.large {
+		border-radius: 12rpx;
+		height:80rpx;
+		line-height: 80rpx;
+		font-size: 28rpx;
+	}
+	
+	.cu-btn{
+		&.has-icon{
+			padding-left: 54rpx;
+			padding-right: 22rpx;
+			position: relative;
+			.image{
+				width:32rpx;
+				height:32rpx;
+				position: absolute;
+				left:13rpx;
+				top:50%;
+				transform: translateY(-50%);
+			}
+		}
+	}
+</style>

+ 1369 - 0
components/xy-imgResiz/index.vue

@@ -0,0 +1,1369 @@
+<template name="xy-imgResiz">
+	<view>
+		<canvas canvas-id="avatar-canvas" id="avatar-canvas" class="my-canvas" :style="{top: sT, height: csH}"
+			disable-scroll="false"></canvas>
+		<canvas canvas-id="oper-canvas" id="oper-canvas" class="oper-canvas" :style="{top: sT, height: csH}"
+			disable-scroll="false" @touchstart="fStart" @touchmove="fMove" @touchend="fEnd"></canvas>
+		<canvas canvas-id="prv-canvas" id="prv-canvas" class="prv-canvas" disable-scroll="false" @touchstart="fHideImg"
+			:style="{ height: csH, top: pT }"></canvas>
+		<view class="oper-wrapper" :style="{display: sD, top:tp}">
+			<view class="oper">
+				<view class="btn-wrapper" v-if="sO">
+					<view @click="fSelect" hover-class="hover" :style="{width: bW}"><text>重选</text></view>
+					<view @click="fClose" hover-class="hover" :style="{width: bW}"><text>关闭</text></view>
+					<view @click="fRotate" hover-class="hover" :style="{width: bW, display: bD}"><text>旋转</text></view>
+					<view @click="fPreview" hover-class="hover" :style="{width: bW}"><text>预览</text></view>
+					<view @click="fUpload" hover-class="hover" :style="{width: bW}"><text>上传</text></view>
+				</view>
+				<view class="clr-wrapper" v-else>
+					<slider class="my-slider" @change="fColorChange" block-size="25" value="0" min="-100" max="100"
+						activeColor="red" backgroundColor="green" block-color="grey" show-value></slider>
+					<view @click="fPrvUpload" hover-class="hover" :style="{width: bW}"><text>上传</text></view>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	"use strict";
+	const tH = 50;
+	export default {
+		name: "xy-imgResiz",
+		data() {
+			return {
+				csH: '0px',
+				sD: 'none',
+				sT: '-10000px',
+				pT: '-10000px',
+				iS: {},
+				sS: {},
+				sO: true,
+				bW: '19%',
+				bD: 'flex',
+				tp: 0,
+				imgSrc: {
+					imgSrc: ''
+				},
+			};
+		},
+		watch: {
+			avatarSrc() {
+				this.imgSrc.imgSrc = this.avatarSrc;
+			}
+		},
+		props: {
+			avatarSrc: '',
+			avatarStyle: '',
+			selWidth: '',
+			selHeight: '',
+			expWidth: '',
+			expHeight: '',
+			minScale: '',
+			maxScale: '',
+			canScale: '',
+			canRotate: '',
+			lockWidth: '',
+			lockHeight: '',
+			stretch: '',
+			lock: '',
+			fileType: '',
+			noTab: '',
+			inner: '',
+			quality: '',
+			index: '',
+			bgImage: '',
+		},
+		created() {
+			this.cc = uni.createCanvasContext('avatar-canvas', this);
+			this.cco = uni.createCanvasContext('oper-canvas', this);
+			this.ccp = uni.createCanvasContext('prv-canvas', this);
+			this.qlty = parseFloat(this.quality) || 1;
+			this.imgSrc.imgSrc = this.avatarSrc;
+			this.letRotate = (this.canRotate === false || this.inner === true || this.inner === 'true' || this
+				.canRotate === 'false') ? 0 : 1;
+			this.letScale = (this.canScale === false || this.canScale === 'false') ? 0 : 1;
+			this.isin = (this.inner === true || this.inner === 'true') ? 1 : 0;
+			this.indx = this.index || undefined;
+			this.mnScale = parseFloat(this.minScale) || 0.3;
+			this.mxScale = parseFloat(this.maxScale) || 4;
+			this.noBar = (this.noTab === true || this.noTab === 'true') ? 1 : 0;
+			this.stc = this.stretch;
+			this.lck = this.lock;
+			this.fType = this.fileType === 'jpg' ? 'jpg' : 'png';
+			if (this.isin || !this.letRotate) {
+				this.bW = '24%';
+				this.bD = 'none';
+			} else {
+				this.bW = '19%';
+				this.bD = 'flex';
+			}
+
+			if (this.noBar) {
+				this.fWindowResize();
+			} else {
+				uni.showTabBar({
+					fail: () => {
+						this.noBar = 1;
+					},
+					success: () => {
+						this.noBar = 0;
+					},
+					complete: (res) => {
+						this.fWindowResize();
+					}
+				});
+			}
+		},
+		methods: {
+			fWindowResize() {
+				let sysInfo = uni.getSystemInfoSync();
+				this.platform = sysInfo.platform;
+				this.wW = sysInfo.windowWidth;
+
+				// #ifdef H5
+				this.drawTop = sysInfo.windowTop;
+				// #endif
+				// #ifndef H5
+				this.drawTop = 0;
+				// #endif
+
+				// #ifdef MP-ALIPAY
+				this.wH = sysInfo.screenHeight - sysInfo.statusBarHeight - sysInfo.titleBarHeight;
+				this.csH = this.wH - tH + 'px';
+				// #endif
+
+				// #ifndef MP-ALIPAY
+				this.wH = sysInfo.windowHeight;
+				if (!this.noBar) this.wH += tH;
+				this.csH = this.wH - tH + 'px';
+				// #endif
+
+				this.tp = this.csH;
+				// #ifdef H5
+				this.tp = sysInfo.windowTop + parseInt(this.csH) + 'px';
+				// #endif
+
+				this.pxRatio = this.wW / 750;
+
+				let style = this.avatarStyle;
+				if (style && style !== true && (style = style.trim())) {
+					style = style.split(';');
+					let obj = {};
+					for (let v of style) {
+						if (!v) continue;
+						v = v.trim().split(':');
+						if (v[1].toString().indexOf('upx') >= 0) {
+							let arr = v[1].trim().split(' ');
+							for (let k in arr) {
+								if (!arr[k]) continue;
+								if (arr[k].toString().indexOf('upx') >= 0) {
+									arr[k] = parseFloat(arr[k]) * this.pxRatio + 'px';
+								}
+							}
+							v[1] = arr.join(' ');
+						}
+						obj[v[0].trim()] = v[1].trim();
+					}
+					this.iS = obj;
+				}
+
+				this.expWidth && (this.eW = this.expWidth.toString().indexOf('upx') >= 0 ? parseInt(this.expWidth) * this
+					.pxRatio :
+					parseInt(this.expWidth));
+				this.expHeight && (this.eH = this.expHeight.toString().indexOf('upx') >= 0 ? parseInt(this.expHeight) *
+					this.pxRatio :
+					parseInt(this.expHeight));
+
+				if (this.sD === 'flex') {
+					this.fDrawInit(true);
+				}
+				this.fHideImg();
+			},
+			fSelect() {
+				if (this.fSelecting) return;
+				this.fSelecting = true;
+				setTimeout(() => {
+					this.fSelecting = false;
+				}, 500);
+
+				uni.chooseImage({
+					count: 1,
+					sizeType: ['original', 'compressed'],
+					sourceType: ['album', 'camera'],
+					success: (r) => {
+						// #ifdef MP-ALIPAY
+						uni.showLoading();
+						// #endif
+						// #ifndef MP-ALIPAY
+						uni.showLoading({
+							title: '加载中...',
+							mask: true
+						});
+						// #endif
+
+						let path = this.imgPath = r.tempFilePaths[0];
+						uni.getImageInfo({
+							src: path,
+							success: r => {
+								this.imgWidth = r.width;
+								this.imgHeight = r.height;
+								this.path = path;
+								if (!this.hasSel) {
+									let style = this.sS || {};
+									if (this.selWidth && this.selHeight) {
+										let sW = this.selWidth.toString().indexOf('upx') >= 0 ?
+											parseInt(this.selWidth) * this.pxRatio : parseInt(
+												this.selWidth),
+											sH = this.selHeight.toString().indexOf('upx') >= 0 ?
+											parseInt(this.selHeight) * this.pxRatio : parseInt(
+												this.selHeight);
+										style.width = sW + 'px';
+										style.height = sH + 'px';
+										style.top = ((this.wH - sH - tH) | 0) / 2 + 'px';
+										style.left = ((this.wW - sW) | 0) / 2 + 'px';
+									} else {
+										uni.showModal({
+											title: '裁剪框的宽或高没有设置',
+											showCancel: false
+										})
+										return;
+									}
+									this.sS = style;
+								}
+
+								if (this.noBar) {
+									this.fDrawInit(true);
+								} else {
+									uni.hideTabBar({
+										complete: () => {
+											this.fDrawInit(true);
+										}
+									});
+								}
+							},
+							fail: () => {
+								uni.showToast({
+									title: "请选择正确图片",
+									duration: 2000,
+								})
+							},
+							complete() {
+								uni.hideLoading();
+							}
+						});
+
+					}
+				})
+			},
+			fUpload() {
+				if (this.fUploading) return;
+				this.fUploading = true;
+				setTimeout(() => {
+					this.fUploading = false;
+				}, 1000)
+
+				let style = this.sS,
+					x = parseInt(style.left),
+					y = parseInt(style.top),
+					width = parseInt(style.width),
+					height = parseInt(style.height),
+					expWidth = this.eW || (width * this.pixelRatio),
+					expHeight = this.eH || (height * this.pixelRatio);
+
+				// #ifdef MP-ALIPAY
+				uni.showLoading();
+				// #endif
+				// #ifndef MP-ALIPAY
+				uni.showLoading({
+					title: '加载中...',
+					mask: true
+				});
+				// #endif
+
+				this.sD = 'none';
+				this.sT = '-10000px';
+				this.hasSel = false;
+				this.fHideImg();
+				// #ifdef MP-ALIPAY
+				this.cc.toTempFilePath({
+					x: x,
+					y: y,
+					width: width,
+					height: height,
+					destWidth: expWidth,
+					destHeight: expHeight,
+					fileType: this.fType,
+					quality: this.qlty,
+					success: (r) => {
+						r = r.apFilePath;
+						this.$emit("upload", {
+							avatar: this.imgSrc,
+							path: r,
+							index: this.indx,
+							data: this.rtn,
+							base64: this.base64 || null
+						});
+					},
+					fail: (res) => {
+						uni.showToast({
+							title: "error1",
+							duration: 2000,
+						})
+					},
+					complete: () => {
+						uni.hideLoading();
+						this.noBar || uni.showTabBar();
+						this.$emit("end");
+					}
+				});
+				// #endif
+				// #ifndef MP-ALIPAY
+				uni.canvasToTempFilePath({
+					x: x,
+					y: y,
+					width: width,
+					height: height,
+					destWidth: expWidth,
+					destHeight: expHeight,
+					canvasId: 'avatar-canvas',
+					fileType: this.fType,
+					quality: this.qlty,
+					success: (r) => {
+						r = r.tempFilePath;
+						// #ifdef H5
+						this.btop(r).then((r) => {
+							this.$emit("upload", {
+								avatar: this.imgSrc,
+								path: r,
+								index: this.indx,
+								data: this.rtn,
+								base64: this.base64 || null
+							});
+							return;
+						})
+						// #endif
+						// #ifndef H5
+						this.$emit("upload", {
+							avatar: this.imgSrc,
+							path: r,
+							index: this.indx,
+							data: this.rtn,
+							base64: this.base64 || null
+						});
+						// #endif
+					},
+					fail: (res) => {
+						uni.showToast({
+							title: "error1",
+							duration: 2000,
+						})
+					},
+					complete: () => {
+						uni.hideLoading();
+						this.noBar || uni.showTabBar();
+						this.$emit("end");
+					}
+				}, this);
+				// #endif
+			},
+			fPrvUpload() {
+				if (this.fPrvUploading) return;
+				this.fPrvUploading = true;
+				setTimeout(() => {
+					this.fPrvUploading = false;
+				}, 1000)
+
+				let style = this.sS,
+					destWidth = parseInt(style.width),
+					destHeight = parseInt(style.height),
+					prvX = this.prvX,
+					prvY = this.prvY,
+					prvWidth = this.prvWidth,
+					prvHeight = this.prvHeight,
+					expWidth = this.eW || (parseInt(style.width) * this.pixelRatio),
+					expHeight = this.eH || (parseInt(style.height) * this.pixelRatio);
+
+				// #ifdef MP-ALIPAY
+				uni.showLoading();
+				// #endif
+				// #ifndef MP-ALIPAY
+				uni.showLoading({
+					title: '加载中...',
+					mask: true
+				});
+				// #endif
+
+				this.sD = 'none';
+				this.sT = '-10000px';
+				this.hasSel = false;
+				this.fHideImg();
+				// #ifdef MP-ALIPAY
+				this.ccp.toTempFilePath({
+					x: prvX,
+					y: prvY,
+					width: prvWidth,
+					height: prvHeight,
+					destWidth: expWidth,
+					destHeight: expHeight,
+					fileType: this.fType,
+					quality: this.qlty,
+					success: (r) => {
+						r = r.apFilePath;
+						this.$emit("upload", {
+							avatar: this.imgSrc,
+							path: r,
+							index: this.indx,
+							data: this.rtn,
+							base64: this.base64 || null
+						});
+					},
+					fail: () => {
+						uni.showToast({
+							title: "error_prv",
+							duration: 2000,
+						})
+					},
+					complete: () => {
+						uni.hideLoading();
+						this.noBar || uni.showTabBar();
+						this.$emit("end");
+					}
+				});
+				// #endif
+				// #ifndef MP-ALIPAY
+				uni.canvasToTempFilePath({
+					x: prvX,
+					y: prvY,
+					width: prvWidth,
+					height: prvHeight,
+					destWidth: expWidth,
+					destHeight: expHeight,
+					canvasId: 'prv-canvas',
+					fileType: this.fType,
+					quality: this.qlty,
+					success: (r) => {
+						r = r.tempFilePath;
+						// #ifdef H5
+						this.btop(r).then((r) => {
+							this.$emit("upload", {
+								avatar: this.imgSrc,
+								path: r,
+								index: this.indx,
+								data: this.rtn,
+								base64: this.base64 || null
+							});
+						})
+						// #endif
+						// #ifndef H5
+						this.$emit("upload", {
+							avatar: this.imgSrc,
+							path: r,
+							index: this.indx,
+							data: this.rtn,
+							base64: this.base64 || null
+						});
+						// #endif
+					},
+					fail: () => {
+						uni.showToast({
+							title: "error_prv",
+							duration: 2000,
+						})
+					},
+					complete: () => {
+						uni.hideLoading();
+						this.noBar || uni.showTabBar();
+						this.$emit("end");
+					}
+				}, this);
+				// #endif
+			},
+			fDrawInit(ini = false) {
+				let allWidth = this.wW,
+					allHeight = this.wH,
+					imgWidth = this.imgWidth,
+					imgHeight = this.imgHeight,
+					imgRadio = imgWidth / imgHeight,
+					useWidth = allWidth - 40,
+					useHeight = allHeight - tH - 80,
+					useRadio = useWidth / useHeight,
+					sW = parseInt(this.sS.width),
+					sH = parseInt(this.sS.height);
+
+				this.fixWidth = 0;
+				this.fixHeight = 0;
+				this.lckWidth = 0;
+				this.lckHeight = 0;
+				switch (this.stc) {
+					case 'x':
+						this.fixWidth = 1;
+						break;
+					case 'y':
+						this.fixHeight = 1;
+						break;
+					case 'long':
+						if (imgRadio > 1) this.fixWidth = 1;
+						else this.fixHeight = 1;
+						break;
+					case 'short':
+						if (imgRadio > 1) this.fixHeight = 1;
+						else this.fixWidth = 1;
+						break;
+					case 'longSel':
+						if (sW > sH) this.fixWidth = 1;
+						else this.fixHeight = 1;
+						break;
+					case 'shortSel':
+						if (sW > sH) this.fixHeight = 1;
+						else this.fixWidth = 1;
+						break;
+				}
+				switch (this.lck) {
+					case 'x':
+						this.lckWidth = 1;
+						break;
+					case 'y':
+						this.lckHeight = 1;
+						break;
+					case 'long':
+						if (imgRadio > 1) this.lckWidth = 1;
+						else this.lckHeight = 1;
+						break;
+					case 'short':
+						if (imgRadio > 1) this.lckHeight = 1;
+						else this.lckWidth = 1;
+						break;
+					case 'longSel':
+						if (sW > sH) this.lckWidth = 1;
+						else this.lckHeight = 1;
+						break;
+					case 'shortSel':
+						if (sW > sH) this.lckHeight = 1;
+						else this.lckWidth = 1;
+						break;
+				}
+				if (this.fixWidth) {
+					useWidth = sW;
+					useHeight = useWidth / imgRadio;
+				} else if (this.fixHeight) {
+					useHeight = sH;
+					useWidth = useHeight * imgRadio;
+				} else if (imgRadio < useRadio) {
+					if (imgHeight < useHeight) {
+						useWidth = imgWidth;
+						useHeight = imgHeight;
+					} else {
+						useWidth = useHeight * imgRadio;
+					}
+				} else {
+					if (imgWidth < useWidth) {
+						useWidth = imgWidth;
+						useHeight = imgHeight;
+					} else {
+						useHeight = useWidth / imgRadio;
+					}
+				}
+				if (this.isin) {
+					if (useWidth < sW) {
+						useWidth = sW;
+						useHeight = useWidth / imgRadio;
+						this.lckHeight = 0;
+					}
+					if (useHeight < sH) {
+						useHeight = sH;
+						useWidth = useHeight * imgRadio;
+						this.lckWidth = 0;
+					}
+				}
+
+				this.scaleSize = 1;
+				this.rotateDeg = 0;
+				this.posWidth = (allWidth - useWidth) / 2 | 0;
+				this.posHeight = (allHeight - useHeight - tH) / 2 | 0;
+				this.useWidth = useWidth | 0;
+				this.useHeight = useHeight | 0;
+				this.centerX = this.posWidth + useWidth / 2;
+				this.centerY = this.posHeight + useHeight / 2;
+				this.focusX = 0;
+				this.focusY = 0;
+
+				let style = this.sS,
+					left = parseInt(style.left),
+					top = parseInt(style.top),
+					width = parseInt(style.width),
+					height = parseInt(style.height),
+					canvas = this.canvas,
+					canvasOper = this.canvasOper,
+					cc = this.cc,
+					cco = this.cco;
+
+				cco.beginPath();
+				cco.setLineWidth(3);
+				cco.setGlobalAlpha(1);
+				cco.setStrokeStyle('white');
+				cco.strokeRect(left, top, width, height);
+
+				cco.setFillStyle('black');
+				cco.setGlobalAlpha(0.5);
+				cco.fillRect(0, 0, this.wW, top);
+				cco.fillRect(0, top, left, height);
+				cco.fillRect(0, top + height, this.wW, this.wH - height - top - tH);
+				cco.fillRect(left + width, top, this.wW - width - left, height);
+
+				cco.setGlobalAlpha(1);
+				cco.setStrokeStyle('red');
+				cco.moveTo(left + 15, top);
+				cco.lineTo(left, top);
+				cco.lineTo(left, top + 15);
+				cco.moveTo(left + width - 15, top);
+				cco.lineTo(left + width, top);
+				cco.lineTo(left + width, top + 15);
+				cco.moveTo(left + 15, top + height);
+				cco.lineTo(left, top + height);
+				cco.lineTo(left, top + height - 15);
+				cco.moveTo(left + width - 15, top + height);
+				cco.lineTo(left + width, top + height);
+				cco.lineTo(left + width, top + height - 15);
+				cco.stroke();
+
+				cco.draw(false, () => {
+					if (ini) {
+						this.sD = 'flex';
+						this.sT = this.drawTop + 'px';
+						this.fDrawImage(true);
+					}
+				});
+				this.$emit("init");
+			},
+			fDrawImage(ini = false) {
+				let tm_now = Date.now();
+				if (tm_now - this.drawTm < 20) return;
+				this.drawTm = tm_now;
+
+				let cc = this.cc,
+					imgWidth = this.useWidth * this.scaleSize,
+					imgHeight = this.useHeight * this.scaleSize;
+
+				// #ifdef MP-ALIPAY	
+				cc.save();
+				// #endif
+
+				if (this.bgImage) {
+					// #ifdef MP-ALIPAY
+					cc.clearRect(0, 0, this.wW, this.wH - tH);
+					// #endif
+					// #ifndef MP-ALIPAY
+					cc.drawImage(this.bgImage, 0, 0, this.wW, this.wH - tH);
+					// #endif
+				} else {
+					cc.fillRect(0, 0, this.wW, this.wH - tH);
+				}
+
+				if (this.isin) {
+					let cx = this.focusX * (this.scaleSize - 1),
+						cy = this.focusY * (this.scaleSize - 1);
+
+					cc.translate(this.centerX, this.centerY);
+					cc.rotate(this.rotateDeg * Math.PI / 180);
+					cc.drawImage(this.imgPath, this.posWidth - this.centerX - cx, this.posHeight - this.centerY - cy,
+						imgWidth, imgHeight);
+
+				} else {
+					cc.translate(this.posWidth + imgWidth / 2, this.posHeight + imgHeight / 2);
+					cc.rotate(this.rotateDeg * Math.PI / 180);
+					cc.drawImage(this.imgPath, -imgWidth / 2, -imgHeight / 2, imgWidth, imgHeight);
+				}
+
+				cc.draw(false);
+
+				// #ifdef MP-ALIPAY
+				cc.restore();
+				// #endif
+			},
+			fPreview() {
+				if (this.fPreviewing) return;
+				this.fPreviewing = true;
+				setTimeout(() => {
+					this.fPreviewing = false;
+				}, 1000);
+
+				let style = this.sS,
+					x = parseInt(style.left),
+					y = parseInt(style.top),
+					width = parseInt(style.width),
+					height = parseInt(style.height);
+
+				// #ifdef MP-ALIPAY
+				uni.showLoading();
+				// #endif
+				// #ifndef MP-ALIPAY
+				uni.showLoading({
+					title: '加载中...',
+					mask: true
+				});
+				// #endif
+
+				// #ifdef MP-ALIPAY
+				this.cc.toTempFilePath({
+					x: x,
+					y: y,
+					width: width,
+					height: height,
+					expWidth: width * this.pixelRatio,
+					expHeight: height * this.pixelRatio,
+					fileType: this.fType,
+					quality: this.qlty,
+					success: (r) => {
+						this.prvImgTmp = r = r.apFilePath;
+						let ccp = this.ccp,
+							prvX = this.wW,
+							prvY = parseInt(this.csH),
+							prvWidth = parseInt(this.sS.width),
+							prvHeight = parseInt(this.sS.height),
+							useWidth = prvX - 40,
+							useHeight = prvY - 80,
+							radio = useWidth / prvWidth,
+							rHeight = prvHeight * radio;
+						if (rHeight < useHeight) {
+							prvWidth = useWidth;
+							prvHeight = rHeight;
+						} else {
+							radio = useHeight / prvHeight;
+							prvWidth *= radio;
+							prvHeight = useHeight;
+						}
+						ccp.fillRect(0, 0, prvX, prvY);
+						this.prvX = prvX = ((prvX - prvWidth) / 2) | 0;
+						this.prvY = prvY = ((prvY - prvHeight) / 2) | 0;
+						this.prvWidth = prvWidth = prvWidth | 0;
+						this.prvHeight = prvHeight = prvHeight | 0;
+						ccp.drawImage(r, prvX, prvY, prvWidth, prvHeight);
+						ccp.draw(false);
+
+						this.sO = false;
+						this.pT = '0';
+					},
+					fail: () => {
+						uni.showToast({
+							title: "error2",
+							duration: 2000,
+						})
+					},
+					complete: () => {
+						uni.hideLoading();
+					}
+				});
+				// #endif
+
+				// #ifndef MP-ALIPAY
+				uni.canvasToTempFilePath({
+					x: x,
+					y: y,
+					width: width,
+					height: height,
+					expWidth: width * this.pixelRatio,
+					expHeight: height * this.pixelRatio,
+					canvasId: 'avatar-canvas',
+					fileType: this.fType,
+					quality: this.qlty,
+					success: (r) => {
+						this.prvImgTmp = r = r.tempFilePath;
+
+						let ccp = this.ccp,
+							prvX = this.wW,
+							prvY = parseInt(this.csH);
+
+						// #ifndef H5||MP-WEIXIN||APP-PLUS
+						prvY += tH;
+						// #endif
+						// #ifdef APP-PLUS
+						if (this.platform === 'android') {
+							prvY += tH;
+						}
+						// #endif
+
+						let prvWidth = parseInt(this.sS.width),
+							prvHeight = parseInt(this.sS.height),
+							useWidth = prvX - 40,
+							useHeight = prvY - 80,
+							radio = useWidth / prvWidth,
+							rHeight = prvHeight * radio;
+						if (rHeight < useHeight) {
+							prvWidth = useWidth;
+							prvHeight = rHeight;
+						} else {
+							radio = useHeight / prvHeight;
+							prvWidth *= radio;
+							prvHeight = useHeight;
+						}
+
+						ccp.fillRect(0, 0, prvX, prvY);
+						this.prvX = prvX = ((prvX - prvWidth) / 2) | 0;
+						this.prvY = prvY = ((prvY - prvHeight) / 2) | 0;
+						this.prvWidth = prvWidth = prvWidth | 0;
+						this.prvHeight = prvHeight = prvHeight | 0;
+						ccp.drawImage(r, prvX, prvY, prvWidth, prvHeight);
+						ccp.draw(false);
+
+						// #ifdef H5
+						this.btop(r).then((r) => {
+							this.sO = false;
+							this.pT = this.drawTop + 'px';
+						})
+						// #endif
+
+						this.sO = false;
+						// if (this.platform === 'android') this.sO = false;
+						this.pT = this.drawTop + 'px';
+					},
+					fail: () => {
+						uni.showToast({
+							title: "error2",
+							duration: 2000,
+						})
+					},
+					complete: () => {
+						uni.hideLoading();
+					}
+				}, this);
+				// #endif
+			},
+			fChooseImg(params = undefined, data = undefined, index = undefined, ) {
+				if (params) {
+					let sW = params.selWidth,
+						sH = params.selHeight,
+						expWidth = params.expWidth,
+						expHeight = params.expHeight,
+						quality = params.quality,
+						canRotate = params.canRotate,
+						canScale = params.canScale,
+						minScale = params.minScale,
+						maxScale = params.maxScale,
+						stretch = params.stretch,
+						fileType = params.fileType,
+						inner = params.inner,
+						lock = params.lock;
+
+					expWidth && (this.eW = expWidth.toString().indexOf('upx') >= 0 ? parseInt(expWidth) * this.pxRatio :
+						parseInt(
+							expWidth));
+					expHeight && (this.eH = expHeight.toString().indexOf('upx') >= 0 ? parseInt(expHeight) * this.pxRatio :
+						parseInt(
+							expHeight));
+					this.letRotate = (canRotate === false || inner === true || inner === 'true' || canRotate === 'false') ?
+						0 : 1;
+					this.letScale = (canScale === false || canScale === 'false') ? 0 : 1;
+					this.qlty = parseFloat(quality) || 1;
+					this.mnScale = parseFloat(minScale) || 0.3;
+					this.mxScale = parseFloat(maxScale) || 4;
+					this.stc = stretch;
+					this.isin = (inner === true || inner === 'true') ? 1 : 0;
+					this.fType = fileType === 'jpg' ? 'jpg' : 'png';
+					this.lck = lock;
+					if (this.isin || !this.letRotate) {
+						this.bW = '24%';
+						this.bD = 'none';
+					} else {
+						this.bW = '19%';
+						this.bD = 'flex';
+					}
+
+					if (sW && sH) {
+						sW = sW.toString().indexOf('upx') >= 0 ? parseInt(sW) * this.pxRatio : parseInt(sW);
+						sH = sH.toString().indexOf('upx') >= 0 ? parseInt(sH) * this.pxRatio : parseInt(sH);
+						this.sS.width = sW + 'px';
+						this.sS.height = sH + 'px';
+						this.sS.top = ((this.wH - sH - tH) | 0) / 2 + 'px';
+						this.sS.left = ((this.wW - sW) | 0) / 2 + 'px';
+						this.hasSel = true;
+					}
+				}
+				this.rtn = data;
+				this.indx = index;
+				this.fSelect();
+			},
+			fRotate() {
+				this.rotateDeg += 90 - this.rotateDeg % 90;
+				this.fDrawImage();
+			},
+			fStart(e) {
+				let touches = e.touches,
+					touch0 = touches[0],
+					touch1 = touches[1];
+
+				this.touch0 = touch0;
+				this.touch1 = touch1;
+
+				if (touch1) {
+					let x = touch1.x - touch0.x,
+						y = touch1.y - touch0.y;
+					this.fgDistance = Math.sqrt(x * x + y * y);
+				}
+			},
+			fMove(e) {
+				let touches = e.touches,
+					touch0 = touches[0],
+					touch1 = touches[1];
+
+				if (touch1) {
+					let x = touch1.x - touch0.x,
+						y = touch1.y - touch0.y,
+						fgDistance = Math.sqrt(x * x + y * y),
+						scaleSize = 0.005 * (fgDistance - this.fgDistance),
+						beScaleSize = this.scaleSize + scaleSize;
+
+					do {
+						if (!this.letScale) break;
+						if (beScaleSize < this.mnScale) break;
+						if (beScaleSize > this.mxScale) break;
+
+						let growX = this.useWidth * scaleSize / 2,
+							growY = this.useHeight * scaleSize / 2;
+						if (this.isin) {
+							let imgWidth = this.useWidth * beScaleSize,
+								imgHeight = this.useHeight * beScaleSize,
+								l = this.posWidth - growX,
+								t = this.posHeight - growY,
+								r = l + imgWidth,
+								b = t + imgHeight,
+								left = parseInt(this.sS.left),
+								top = parseInt(this.sS.top),
+								width = parseInt(this.sS.width),
+								height = parseInt(this.sS.height),
+								right = left + width,
+								bottom = top + height,
+								cx, cy;
+
+							if (imgWidth <= width || imgHeight <= height) break;
+							this.cx = cx = this.focusX * beScaleSize - this.focusX,
+								this.cy = cy = this.focusY * beScaleSize - this.focusY;
+							this.posWidth -= growX;
+							this.posHeight -= growY;
+							if (this.posWidth - cx > left) {
+								this.posWidth = left + cx;
+							}
+							if (this.posWidth + imgWidth - cx < right) {
+								this.posWidth = right - imgWidth + cx;
+							}
+							if (this.posHeight - cy > top) {
+								this.posHeight = top + cy;
+							}
+							if (this.posHeight + imgHeight - cy < bottom) {
+								this.posHeight = bottom - imgHeight + cy;
+							}
+						} else {
+							this.posWidth -= growX;
+							this.posHeight -= growY;
+						}
+
+						this.scaleSize = beScaleSize;
+					} while (0);
+					this.fgDistance = fgDistance;
+					if (touch1.x !== touch0.x && this.letRotate) {
+						x = (this.touch1.y - this.touch0.y) / (this.touch1.x - this.touch0.x);
+						y = (touch1.y - touch0.y) / (touch1.x - touch0.x);
+						this.rotateDeg += Math.atan((y - x) / (1 + x * y)) * 180 / Math.PI;
+						this.touch0 = touch0;
+						this.touch1 = touch1;
+					}
+
+					this.fDrawImage();
+				} else if (this.touch0) {
+					let x = touch0.x - this.touch0.x,
+						y = touch0.y - this.touch0.y,
+						beX = this.posWidth + x,
+						beY = this.posHeight + y;
+					if (this.isin) {
+						let imgWidth = this.useWidth * this.scaleSize,
+							imgHeight = this.useHeight * this.scaleSize,
+							l = beX,
+							t = beY,
+							r = l + imgWidth,
+							b = t + imgHeight,
+							left = parseInt(this.sS.left),
+							top = parseInt(this.sS.top),
+							right = left + parseInt(this.sS.width),
+							bottom = top + parseInt(this.sS.height),
+							cx, cy;
+
+						this.cx = cx = this.focusX * this.scaleSize - this.focusX;
+						this.cy = cy = this.focusY * this.scaleSize - this.focusY;
+
+						if (!this.lckWidth && Math.abs(x) < 100) {
+							if (left < l - cx) {
+								this.posWidth = left + cx;
+							} else if (right > r - cx) {
+								this.posWidth = right - imgWidth + cx;
+							} else {
+								this.posWidth = beX;
+								this.focusX -= x;
+							}
+						}
+						if (!this.lckHeight && Math.abs(y) < 100) {
+							if (top < t - cy) {
+								this.focusY -= (top + cy - this.posHeight);
+								this.posHeight = top + cy;
+							} else if (bottom > b - cy) {
+								this.focusY -= (bottom + cy - (this.posHeight + imgHeight));
+								this.posHeight = bottom - imgHeight + cy;
+							} else {
+								this.posHeight = beY;
+								this.focusY -= y;
+							}
+						}
+					} else {
+						if (Math.abs(x) < 100 && !this.lckWidth) this.posWidth = beX;
+						if (Math.abs(y) < 100 && !this.lckHeight) this.posHeight = beY;
+						this.focusX -= x;
+						this.focusY -= y;
+					}
+
+					this.touch0 = touch0;
+					this.fDrawImage();
+				}
+			},
+			fEnd(e) {
+				let touches = e.touches,
+					touch0 = touches && touches[0],
+					touch1 = touches && touches[1];
+				if (touch0) {
+					this.touch0 = touch0;
+				} else {
+					this.touch0 = null;
+					this.touch1 = null;
+				}
+			},
+			fHideImg() {
+				this.prvImg = '';
+				this.pT = '-10000px';
+				this.sO = true;
+				this.prvImgData = null;
+				this.target = null;
+			},
+			fClose() {
+				this.sD = 'none';
+				this.sT = '-10000px';
+				this.hasSel = false;
+				this.fHideImg();
+				this.noBar || uni.showTabBar();
+				this.$emit("end");
+			},
+			fGetImgData() {
+				return new Promise((resolve, reject) => {
+					let prvX = this.prvX,
+						prvY = this.prvY,
+						prvWidth = this.prvWidth,
+						prvHeight = this.prvHeight;
+					// #ifdef MP-ALIPAY
+					this.ccp.getImageData({
+						x: prvX,
+						y: prvY,
+						width: prvWidth,
+						height: prvHeight,
+						success(res) {
+							resolve(res.data);
+						},
+						fail(err) {
+							reject(err);
+						}
+					}, this);
+					// #endif
+					// #ifndef MP-ALIPAY
+					uni.canvasGetImageData({
+						canvasId: 'prv-canvas',
+						x: prvX,
+						y: prvY,
+						width: prvWidth,
+						height: prvHeight,
+						success(res) {
+							resolve(res.data);
+						},
+						fail(err) {
+							reject(err);
+						}
+					}, this);
+					// #endif
+				});
+			},
+			async fColorChange(e) {
+				let tm_now = Date.now();
+				if (tm_now - this.prvTm < 100) return;
+				this.prvTm = tm_now;
+
+				// #ifdef MP-ALIPAY
+				uni.showLoading();
+				// #endif
+				// #ifndef MP-ALIPAY
+				uni.showLoading({
+					title: '加载中...',
+					mask: true
+				});
+				// #endif
+
+				if (!this.prvImgData) {
+					if (!(this.prvImgData = await this.fGetImgData().catch(() => {
+							uni.showToast({
+								title: "error_read",
+								duration: 2000,
+							})
+						}))) return;
+
+					this.target = new Uint8ClampedArray(this.prvImgData.length);
+				}
+
+				let data = this.prvImgData,
+					target = this.target,
+					i = e.detail.value,
+					r, g, b, a, h, s, l, d, p, q, t, min, max, hK, tR, tG, tB;
+
+				if (i === 0) {
+					target = data;
+				} else {
+					i = (i + 100) / 200;
+					if (i < 0.005) i = 0;
+					if (i > 0.995) i = 1;
+					for (let n = data.length - 1; n >= 0; n -= 4) {
+						r = data[n - 3] / 255;
+						g = data[n - 2] / 255;
+						b = data[n - 1] / 255;
+						max = Math.max(r, g, b);
+						min = Math.min(r, g, b);
+						d = max - min;
+						if (max === min) {
+							h = 0;
+						} else if (max === r && g >= b) {
+							h = 60 * ((g - b) / d);
+						} else if (max === r && g < b) {
+							h = 60 * ((g - b) / d) + 360;
+						} else if (max === g) {
+							h = 60 * ((b - r) / d) + 120;
+						} else if (max === b) {
+							h = 60 * ((r - g) / d) + 240;
+						}
+						l = (max + min) / 2;
+						if (l === 0 || max === min) {
+							s = 0;
+						} else if (0 < l && l <= 0.5) {
+							s = d / (2 * l);
+						} else if (l > 0.5) {
+							s = d / (2 - 2 * l);
+						}
+						data[n] && (a = data[n]);
+
+						if (i < 0.5) {
+							s = s * i / 0.5;
+						} else if (i > 0.5) {
+							s = 2 * s + 2 * i - (s * i / 0.5) - 1;
+						}
+
+						if (s === 0) {
+							r = g = b = Math.round(l * 255);
+						} else {
+							if (l < 0.5) {
+								q = l * (1 + s);
+							} else if (l >= 0.5) {
+								q = l + s - (l * s);
+							}
+							p = 2 * l - q;
+							hK = h / 360;
+							tR = hK + 1 / 3;
+							tG = hK;
+							tB = hK - 1 / 3;
+							let correctRGB = (t) => {
+								if (t < 0) {
+									return t + 1.0;
+								}
+								if (t > 1) {
+									return t - 1.0;
+								}
+								return t;
+							};
+							let createRGB = (t) => {
+								if (t < (1 / 6)) {
+									return p + ((q - p) * 6 * t);
+								} else if (t >= (1 / 6) && t < (1 / 2)) {
+									return q;
+								} else if (t >= (1 / 2) && t < (2 / 3)) {
+									return p + ((q - p) * 6 * ((2 / 3) - t));
+								}
+								return p;
+							};
+							r = tR = Math.round(createRGB(correctRGB(tR)) * 255);
+							g = tG = Math.round(createRGB(correctRGB(tG)) * 255);
+							b = tB = Math.round(createRGB(correctRGB(tB)) * 255);
+						}
+						a && (target[n] = a);
+						target[n - 3] = r;
+						target[n - 2] = g;
+						target[n - 1] = b;
+					}
+				}
+
+				let prvX = this.prvX,
+					prvY = this.prvY,
+					prvWidth = this.prvWidth,
+					prvHeight = this.prvHeight;
+
+				// #ifdef MP-ALIPAY
+				this.ccp.putImageData({
+					x: prvX,
+					y: prvY,
+					width: prvWidth,
+					height: prvHeight,
+					data: target,
+					fail() {
+						uni.showToast({
+							title: 'error_put',
+							duration: 2000
+						})
+					},
+					complete() {
+						uni.hideLoading();
+					}
+				}, this);
+				// #endif
+
+				// #ifndef MP-ALIPAY
+				uni.canvasPutImageData({
+					canvasId: 'prv-canvas',
+					x: prvX,
+					y: prvY,
+					width: prvWidth,
+					height: prvHeight,
+					data: target,
+					fail() {
+						uni.showToast({
+							title: 'error_put',
+							duration: 2000
+						})
+					},
+					complete() {
+						uni.hideLoading();
+					}
+				}, this);
+				// #endif
+			},
+			btop(base64) {
+				this.base64 = base64;
+				return new Promise(function(resolve, reject) {
+					var arr = base64.split(','),
+						mime = arr[0].match(/:(.*?);/)[1],
+						bstr = atob(arr[1]),
+						n = bstr.length,
+						u8arr = new Uint8Array(n);
+					while (n--) {
+						u8arr[n] = bstr.charCodeAt(n);
+					}
+					return resolve((window.URL || window.webkitURL).createObjectURL(new Blob([u8arr], {
+						type: mime
+					})));
+				});
+			},
+		}
+	}
+</script>
+
+<style>
+	.my-canvas {
+		display: flex;
+		position: fixed !important;
+		background: #000000;
+		left: 0;
+		z-index: 100000;
+		width: 100%;
+	}
+
+	.my-avatar {
+		width: 160rpx;
+		height: 160rpx;
+	}
+
+	.oper-canvas {
+		display: flex;
+		position: fixed !important;
+		left: 0;
+		z-index: 100001;
+		width: 100%;
+	}
+
+	.prv-canvas {
+		display: flex;
+		position: fixed !important;
+		background: #000000;
+		left: 0;
+		z-index: 200000;
+		width: 100%;
+	}
+
+	.oper-wrapper {
+		height: 50px;
+		position: fixed !important;
+		box-sizing: border-box;
+		border: 1px solid #F1F1F1;
+		background: #ffffff;
+		width: 100%;
+		left: 0;
+		bottom: 0;
+		z-index: 100009;
+		flex-direction: row;
+	}
+
+	.oper {
+		display: flex;
+		flex-direction: column;
+		justify-content: center;
+		padding: 10upx 20upx;
+		width: 100%;
+		height: 100%;
+		box-sizing: border-box;
+		align-self: center;
+	}
+
+	.btn-wrapper {
+		display: flex;
+		flex-direction: row;
+		/* #ifndef H5 */
+		flex-grow: 1;
+		/* #endif */
+		/* #ifdef H5 */
+		height: 50px;
+		/* #endif */
+		justify-content: space-between;
+	}
+
+	.btn-wrapper view {
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		font-size: 16px;
+		color: #333;
+		border: 1px solid #f1f1f1;
+		border-radius: 6%;
+	}
+
+	.hover {
+		background: #f1f1f1;
+		border-radius: 6%;
+	}
+
+	.clr-wrapper {
+		display: flex;
+		flex-direction: row;
+		flex-grow: 1;
+	}
+
+	.clr-wrapper view {
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		font-size: 16px;
+		color: #333;
+		border: 1px solid #f1f1f1;
+		border-radius: 6%;
+	}
+
+	.my-slider {
+		flex-grow: 1;
+	}
+</style>

+ 114 - 0
components/xy-popup/index.vue

@@ -0,0 +1,114 @@
+<template>
+	<u-popup :show="show" @close="close" :safeAreaInsetBottom="safeAreaInsetBottom&&mode=='bottom'" @open="open" :mode="mode">
+		<view class="pop-content">
+			<slot name="title">
+				<view class="popup-title" v-if="title">
+					{{title}}
+				</view>
+			</slot>
+			<slot />
+			<slot name="botton">
+				<view class="popup-btn" v-if="showBtn">
+					<xbutton width="200rpx" size="large" bgColor="#fff" color="#2C6FF3" borderColor="#2C6FF3" @click="close">取消</xbutton>
+					<xbutton delay="1500" width="200rpx" size="large" @click="submit">确定</xbutton>
+				</view>
+			</slot>
+		</view>
+	</u-popup>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+
+			}
+		},
+		props: {
+			mode: {
+				type: String,
+				require: false,
+				default: 'bottom'
+			},
+			show: {
+				type: Boolean,
+				require: true,
+				default: false
+			},
+			showBtn: {
+				type: Boolean,
+				require: true,
+				default: true
+			},
+			title: {
+				type: String,
+				require: true,
+				default: ''
+			},
+			safeAreaInsetBottom:{
+				type: Boolean,
+				require: false,
+				default: true
+			}
+		},
+
+		model: {
+			prop: 'value',
+			event: 'change'
+		},
+
+		watch: {
+			value: {
+				handler(newVal, oldVal) {
+					this.fileList = newVal
+				},
+				immediate: true,
+				deep: true
+			}
+		},
+
+		methods: {
+			close() {
+				this.$emit('close', false)
+			},
+			open() {
+				this.$emit('open', true)
+			},
+			
+			submit(){
+				this.$emit('confirm')
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.pop-content{
+		box-shadow: 0 2rpx 10rpx 0 rgba(0,0,0,0.1);
+	}
+	
+	.popup-title {
+		font-size: 32rpx;
+		font-weight: bold;
+		padding: 24rpx;
+	}
+
+	.popup-btn {
+		display: flex;
+		flex-flow: row nowrap;
+		justify-content: space-around;
+		width: 100%;
+		padding:50rpx 24rpx 24rpx;
+
+		.cu-btn {
+			background-color: #2C6FF3;
+			color: #fff;
+			width: 200rpx;
+		}
+
+		.cu-btn1 {
+			background-color: green;
+		}
+
+	}
+</style>

+ 30 - 0
config.js

@@ -0,0 +1,30 @@
+// 应用全局配置
+module.exports = {
+	baseUrl: process.env.NODE_ENV=='production'?'https://api.mxrvending.com:9050':'https://ai.tanbin.vip', 
+	// baseUrl: 'https://api.mxrvending.com:9050',
+	//mock数据
+	// baseUrl: 'http://119.96.213.127:9010/mock',
+	// 应用信息
+	appInfo: {
+		// 应用名称
+		name: "MXR-app",
+		// 应用版本
+		version: "1.0.0",
+		// 应用logo
+		logo: "/static/logo.png",
+		// 官方网站
+		site_url: "http://www.xyvending.com/",
+		// 政策协议
+		agreements: [
+			{
+				title: "隐私政策",
+				url: "https://ruoyi.vip/protocol.html"
+			},
+			
+			{
+				title: "用户服务协议",
+				url: "https://ruoyi.vip/protocol.html"
+			}
+		]
+	}
+}

+ 36 - 0
main.js

@@ -0,0 +1,36 @@
+import Vue from 'vue'
+import App from './App'
+import store from './store' // store
+import plugins from './plugins' // plugins
+import './permission' // permission
+import xbutton from '@/components/xy-button' //自定义按钮组件
+import xpopup from '@/components/xy-popup' //自定义弹框组件
+import {
+	checkPermi
+} from '@/utils/permission.js'
+
+import getdict from './utils/getDict.js'
+Vue.use(plugins)
+
+// 引入:uView-UI
+import uView from '@/uni_modules/uview-ui';
+Vue.use(uView);
+
+import share from './utils/share.js'
+Vue.mixin(share)
+
+Vue.component('xbutton', xbutton)
+Vue.component('xpopup', xpopup)
+Vue.prototype.getDict = getdict;
+Vue.prototype.$store = store
+Vue.prototype.checkPermi = checkPermi
+
+Vue.config.productionTip = false
+
+App.mpType = 'app'
+
+const app = new Vue({
+	...App
+})
+
+app.$mount()

+ 81 - 0
manifest.json

@@ -0,0 +1,81 @@
+{
+    "name" : "开门柜B端",
+    "appid" : "__UNI__70676CE",
+    "description" : "",
+    "versionName" : "1.0.0",
+    "versionCode" : "100",
+    "transformPx" : false,
+    "app-plus" : {
+        "usingComponents" : true,
+        "nvueCompiler" : "uni-app",
+        "splashscreen" : {
+            "alwaysShowBeforeRender" : true,
+            "waiting" : true,
+            "autoclose" : true,
+            "delay" : 0
+        },
+        "modules" : {},
+        "distribute" : {
+            "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.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.ACCESS_NETWORK_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.CAMERA\"/>",
+                    "<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
+                    "<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.FLASHLIGHT\"/>",
+                    "<uses-feature android:name=\"android.hardware.camera\"/>",
+                    "<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
+                ]
+            },
+            "ios" : {},
+            "sdkConfigs" : {}
+        }
+    },
+    "quickapp" : {},
+    "mp-weixin" : {
+        "appid" : "wxecb8522186879691",
+        "setting" : {
+            "checkSiteMap" : false,
+            "urlCheck" : false,
+            "es6" : false,
+            "minified" : true,
+            "postcss" : true
+        },
+        "optimization" : {
+            "subPackages" : true
+        },
+        "usingComponents" : true,
+        "lazyCodeLoading" : "requiredComponents",
+        "permission" : {
+            "scope.userLocation" : {
+                "desc" : "上传机器坐标位置"
+            }
+        }
+    },
+    "vueVersion" : "2",
+    "h5" : {
+        "template" : "static/index.html",
+        "devServer" : {
+            "port" : 9090,
+            "https" : false
+        },
+        "title" : "喵星人商户端",
+        "router" : {
+            "mode" : "hash",
+            "base" : "./"
+        },
+        "optimization" : {
+            "treeShaking" : {
+                "enable" : true
+            }
+        }
+    }
+}

+ 5 - 0
package.json

@@ -0,0 +1,5 @@
+{
+  "dependencies": {
+    "crypto-js": "^4.1.1"
+  }
+}

+ 392 - 0
pages.json

@@ -0,0 +1,392 @@
+{
+	"easycom": {
+		"^u-(.*)": "@/uni_modules/uview-ui/components/u-$1/u-$1.vue"
+	},
+	"pages": [{
+		"path": "pages/login",
+		"style": {
+			"navigationBarTitleText": "登录",
+			"navigationStyle": "custom"
+		}
+	}],
+
+	"subPackages": [ //分包
+		{
+			"root": "pages/globalPages", //首页
+			"pages": [{
+				"path": "home",
+				"style": {
+					"navigationBarTitleText": "首页",
+					"navigationStyle": "custom"
+				}
+			}, {
+				"path": "statistics",
+				"style": {
+					"navigationBarTitleText": "销售统计",
+					"navigationStyle": "custom"
+				}
+			}, {
+				"path": "statisticsMore",
+				"style": {
+					"navigationBarTitleText": "销售统计",
+					"navigationStyle": "custom"
+				}
+			}, {
+				"path": "moreData",
+				"style": {
+					"navigationBarTitleText": "更多数据",
+					"navigationStyle": "custom"
+				}
+			}, {
+				"path": "logs",
+				"style": {
+					"navigationBarTitleText": "日志",
+					"navigationStyle": "custom"
+				}
+			}, {
+				"path": "errCode",
+				"style": {
+					"navigationBarTitleText": "常见错误码",
+					"navigationStyle": "custom"
+				}
+			}, {
+				"path": "notice",
+				"style": {
+					"navigationBarTitleText": "通知",
+					"navigationStyle": "custom"
+				}
+			}, {
+				"path": "agreement",
+				"style": {
+					"navigationBarTitleText": "协议",
+					"navigationStyle": "custom"
+				}
+			}, {
+				"path": "setting",
+				"style": {
+					"navigationBarTitleText": "设置",
+					"navigationStyle": "custom"
+				}
+			}, {
+				"path": "test",
+				"style": {
+					"navigationBarTitleText": "测试",
+					"navigationStyle": "custom"
+				}
+			}]
+		},
+		{
+			"root": "pages/equipment", //设备
+			"pages": [{
+				"path": "search",
+				"style": {
+					"navigationBarTitleText": "设备搜索",
+					"navigationStyle": "custom"
+				}
+			}, {
+				"path": "detail",
+				"style": {
+					"navigationBarTitleText": "设备详情",
+					"navigationStyle": "custom"
+				}
+			}, {
+				"path": "comManage",
+				"style": {
+					"navigationBarTitleText": "商品管理",
+					"navigationStyle": "custom"
+				}
+			}, {
+				"path": "addCom",
+				"style": {
+					"navigationBarTitleText": "添加商品",
+					"navigationStyle": "custom"
+				}
+			}, {
+				"path": "addComList",
+				"style": {
+					"navigationBarTitleText": "添加商品",
+					"navigationStyle": "custom"
+				}
+			}, {
+				"path": "statistics",
+				"style": {
+					"navigationBarTitleText": "设备销售统计",
+					"navigationStyle": "custom"
+				}
+			}, {
+				"path": "statisticsMore",
+				"style": {
+					"navigationBarTitleText": "商品销售统计",
+					"navigationStyle": "custom"
+				}
+			}]
+		},
+		{
+			"root": "pages/commodity", //商品
+			"pages": [{
+					"path": "comEdit",
+					"style": {
+						"navigationBarTitleText": "商品编辑",
+						"navigationStyle": "custom"
+					}
+				},
+				{
+					"path": "search",
+					"style": {
+						"navigationBarTitleText": "商品搜索",
+						"navigationStyle": "custom"
+					}
+				},
+				{
+					"path": "publicCom",
+					"style": {
+						"navigationBarTitleText": "商品公库",
+						"navigationStyle": "custom"
+					}
+				},
+				{
+					"path": "addCom",
+					"style": {
+						"navigationBarTitleText": "新品建模",
+						"navigationStyle": "custom"
+					}
+				},
+				{
+					"path": "auditList",
+					"style": {
+						"navigationBarTitleText": "审核列表",
+						"navigationStyle": "custom"
+					}
+				},
+				{
+					"path": "publicSearch",
+					"style": {
+						"navigationBarTitleText": "公库搜索",
+						"navigationStyle": "custom"
+					}
+				},
+				{
+					"path": "commoditylist",
+					"style": {
+						"navigationBarTitleText": "商品清单",
+						"navigationStyle": "custom"
+					}
+				},
+				{
+					"path": "comListEdit",
+					"style": {
+						"navigationBarTitleText": "关联商品",
+						"navigationStyle": "custom"
+					}
+				},
+				{
+					"path": "application",
+					"style": {
+						"navigationBarTitleText": "应用",
+						"navigationStyle": "custom"
+					}
+				},
+				{
+					"path": "allGoodsSearch",
+					"style": {
+						"navigationBarTitleText": "所有商品列表",
+						"navigationStyle": "custom"
+					}
+				}
+			]
+		},
+		{
+			"root": "pages/order", //订单
+			"pages": [{
+					"path": "userInfo",
+					"style": {
+						"navigationBarTitleText": "用户信息",
+						"navigationStyle": "custom"
+					}
+				},
+				{
+					"path": "orderQuery",
+					"style": {
+						"navigationBarTitleText": "订单查询",
+						"navigationStyle": "custom"
+					}
+				},
+				{
+					"path": "refundList",
+					"style": {
+						"navigationBarTitleText": "退款列表",
+						"navigationStyle": "custom"
+					}
+				},
+				{
+					"path": "orderDetails",
+					"style": {
+						"navigationBarTitleText": "订单详情",
+						"navigationStyle": "custom"
+					}
+				},
+				{
+					"path": "riskOrder",
+					"style": {
+						"navigationBarTitleText": "风险订单",
+						"navigationStyle": "custom"
+					}
+				}, {
+					"path": "riskOrderDel",
+					"style": {
+						"navigationBarTitleText": "风险订单补扣",
+						"navigationStyle": "custom"
+					}
+				},
+				{
+					"path": "orderLogs",
+					"style": {
+						"navigationBarTitleText": "交易日志",
+						"navigationStyle": "custom"
+					}
+				},
+				{
+					"path": "orderDel",
+					"style": {
+						"navigationBarTitleText": "订单补扣",
+						"navigationStyle": "custom"
+					}
+				}
+			]
+		},
+		{
+			"root": "pages/replenish", //补货
+			"pages": [
+				{
+					"path": "replenishmentManagement",
+					"style": {
+						"navigationBarTitleText": "补货管理首页",
+						"navigationStyle": "custom",
+						"enablePullDownRefresh": true
+					}
+				},
+				{
+					"path": "replenishmentHomePage",
+					"style": {
+						"navigationBarTitleText": "补货首页",
+						"navigationStyle": "custom"
+					}
+				},
+				{
+					"path": "replenishmentRecordDetails",
+					"style": {
+						"navigationBarTitleText": "补货记录详情",
+						"navigationStyle": "custom"
+					}
+				},
+				{
+					"path": "replenishmentRecord",
+					"style": {
+						"navigationBarTitleText": "补货记录",
+						"navigationStyle": "custom"
+					}
+				},
+				{
+					"path": "invSearch",
+					"style": {
+						"navigationBarTitleText": "库存管理",
+						"navigationStyle": "custom"
+					}
+				},
+				{
+					"path": "invSearchDetail",
+					"style": {
+						"navigationBarTitleText": "库存管理详情",
+						"navigationStyle": "custom"
+					}
+				}
+			]
+		},
+		{
+			"root": "pages/point", //区域点位线路
+			"pages": [{
+				"path": "point",
+				"style": {
+					"navigationBarTitleText": "",
+					"navigationStyle": "custom"
+				}
+			}, {
+				"path": "lineDetail",
+				"style": {
+					"navigationBarTitleText": "",
+					"navigationStyle": "custom"
+				}
+			}]
+		},
+		{
+			"root": "pages/system", //人员系统配置
+			"pages": [{
+				"path": "employee",
+				"style": {
+					"navigationBarTitleText": "",
+					"navigationStyle": "custom"
+				}
+			}, {
+				"path": "addEmployee",
+				"style": {
+					"navigationBarTitleText": "",
+					"navigationStyle": "custom"
+				}
+			}, {
+				"path": "empDetail",
+				"style": {
+					"navigationBarTitleText": "",
+					"navigationStyle": "custom"
+				}
+			}, {
+				"path": "deviceManage",
+				"style": {
+					"navigationBarTitleText": "",
+					"navigationStyle": "custom"
+				}
+			}]
+		},
+		{
+			"root": "pages/activeDevice", //设备激活
+			"pages": [{
+				"path": "bindDevice",
+				"style": {
+					"navigationBarTitleText": "设备激活",
+					"navigationStyle": "custom"
+				}
+			}, {
+				"path": "deviceManage",
+				"style": {
+					"navigationBarTitleText": "设备管理",
+					"navigationStyle": "custom"
+				}
+			}, {
+				"path": "bindAliAcc",
+				"style": {
+					"navigationBarTitleText": "绑定支付宝",
+					"navigationStyle": "custom"
+				}
+			}, {
+				"path": "bindAliDev",
+				"style": {
+					"navigationBarTitleText": "设备激活",
+					"navigationStyle": "custom"
+				}
+			}]
+		}
+	],
+	"preloadRule": {
+		"pages/equipment/comManage": {
+			"network": "wifi",
+			"packages": ["pages/commodity"]
+		}
+	},
+	"condition": { //模式配置,仅开发期间生效
+		"current": 0, //当前激活的模式(list 的索引项)
+		"list": [{
+			"name": "", //模式名称
+			"path": "", //启动页面,必选
+			"query": "" //启动参数,在页面的onLoad函数里面得到
+		}]
+	}
+}

+ 221 - 0
pages/activeDevice/bindAliAcc.vue

@@ -0,0 +1,221 @@
+<template>
+	<view class="container">
+		<u-navbar leftIconColor="#fff" titleStyle="color:#fff;fontSize:36rpx;" :autoBack="true" bgColor="#2C6FF3"
+			:placeholder="true" title="账号绑定">
+		</u-navbar>
+
+		<view class="content flex flex-direction justify-between" :style="{height:fullHeight}">
+			<view class="top flex flex-direction align-center">
+				<view class="img">
+					<u-image width="400rpx" height="400rpx"
+						src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/alipayUid.png"
+						mode="widthFix" :lazy-load="true">
+					</u-image>
+				</view>
+				<view class="btn flex justify-between">
+					<view @click="save">保存二维码到手机相册</view>
+					<view>帮助</view>
+				</view>
+
+				<view class="input flex">
+					<input type="text" placeholder="请输入" placeholder-class="place-class" v-model="aliAcc" />
+					<view class="bind" @click="bind">
+						绑定
+					</view>
+				</view>
+			</view>
+
+			<view class="tips">
+				支付宝刷脸AI柜,需要绑定操作员的支付宝账号,操作员刷脸操作货柜时,系统将对操作员的支付宝账号进行授权。
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import {
+		updateByAli
+	} from "@/api/device/device.js"
+	import {
+		update
+	} from "@/api/system/employee.js"
+
+	import {
+		imgDownLoad
+	} from '@/utils/download.js'
+
+	export default {
+		data() {
+			return {
+				fullHeight: 0,
+				aliAcc: null,
+				userId: null,
+			}
+		},
+		onLoad(o) {
+			let _this = this;
+			const query = uni.createSelectorQuery().in(this);
+			query.select(".content").boundingClientRect((data) => {
+				uni.getSystemInfo({
+					success(res) {
+						// 针对iPhone X等机型底部安全距离做适配
+						const model = res.model;
+						const modelInclude = [
+							"iPhone X",
+							'iPhone XR',
+							"iPhone XS",
+							"iPhone XS MAX",
+							"iPhone 12/13 mini",
+							"iPhone 12/13 (Pro)",
+							"iPhone 12/13 Pro Max",
+							"iPhone 14 Pro Max"
+						];
+						let safeDistance = modelInclude.includes(model)
+						//动态设置商品区域高度
+						console.log(res.windowHeight, data.top)
+						if (safeDistance) {
+							_this.fullHeight = res.windowHeight - data.top - 54 + 'px';
+						} else {
+							_this.fullHeight = res.windowHeight - data.top - 20 + 'px';
+						}
+					},
+				});
+			}).exec();
+
+			if (o.userId) {
+				this.userId = o.userId
+			}
+		},
+		methods: {
+			async bind() {
+				if (!this.aliAcc) {
+					this.$modal.msg('请输入支付宝用户id')
+					return
+				}
+				if (!this.userId) {
+					//商户绑定
+					await this.mercUpdate()
+				} else {
+					//员工绑定
+					await this.partnerUpdate()
+				}
+				console.log('成功')
+				this.$modal.msg('绑定成功~')
+				setTimeout(() => {
+					this.$tab.navigateBack()
+				}, 1000)
+			},
+
+			mercUpdate() {
+				return new Promise((resolve, reject) => {
+					updateByAli({
+						aliUserId: this.aliAcc,
+					}).then(res => {
+						resolve(res)
+					}).catch(err => {
+						reject(res)
+					})
+				})
+			},
+
+			partnerUpdate() {
+				return new Promise((resolve, reject) => {
+					update({
+						aliUserId: this.aliAcc,
+						userId: this.userId
+					}).then(res => {
+						resolve(res)
+					}).catch(err => {
+						reject(res)
+					})
+				})
+			},
+
+			save() {
+				let imgUrl = 'https://ossfile.mxrvending.com/assets/xy_merc_mini/images/alipayUid.png'
+				imgDownLoad(imgUrl).then(res=>{
+					uni.showToast({
+						title: '保存成功~',
+					})
+				}).catch(err=>{
+					uni.showToast({
+						title: '保存失败,请重试!',
+					})
+				})
+			},
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.container {
+		position: relative;
+
+		.content {
+			background-color: #fff;
+			width: 724rpx;
+			margin: 20rpx 13rpx 0;
+			overflow: hidden;
+			border-radius: 12rpx;
+
+			.top {
+				.btn {
+					width: 400rpx;
+					color: #3876f3;
+					text-decoration: underline;
+					margin-top: 50rpx;
+
+				}
+
+				.input {
+					width: 460rpx;
+					line-height: 80rpx;
+					height: 80rpx;
+					margin-top: 70rpx;
+
+					>input {
+						line-height: 80rpx;
+						height: 80rpx;
+						width: 76%;
+						background-color: #f6f8fb;
+						padding: 0 30rpx;
+						border-top-left-radius: 18rpx;
+						border-bottom-left-radius: 18rpx;
+					}
+
+					>view {
+						width: 24%;
+						text-align: center;
+						border-top-right-radius: 18rpx;
+						border-bottom-right-radius: 18rpx;
+						background-color: #3876f3;
+						color: #fff;
+					}
+				}
+
+				.place-class {
+					color: #dcdcdc;
+				}
+			}
+
+			.tips {
+				background-color: #ebf2ff;
+				padding: 16rpx 20rpx;
+				color: #3876f3;
+				font-size: 26rpx;
+				line-height: 40rpx;
+			}
+		}
+
+		.img {
+			width: 400rpx;
+			height: 400rpx;
+			margin-top: 70rpx;
+		}
+
+		.canvas-box {
+			position: absolute;
+			left: -1000rpx;
+		}
+	}
+</style>

+ 215 - 0
pages/activeDevice/bindAliDev.vue

@@ -0,0 +1,215 @@
+<template>
+	<view class="container">
+		<u-navbar leftIconColor="#fff" titleStyle="color:#fff;fontSize:36rpx;" :autoBack="true" bgColor="#2C6FF3"
+			:placeholder="true" title="设备激活">
+		</u-navbar>
+
+		<view class="device-img flex flex-direction">
+			<view class="tips flex justify-between align-center" v-if="!isBindAli">
+				<view class="bind-tip">
+					您还没绑定支付宝
+				</view>
+				<xbutton round="50rpx" width="160rpx" @click="bindAliAcc">立即绑定</xbutton>
+			</view>
+			<view class="img">
+				<u-image width="147rpx" src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/device.png"
+					mode="widthFix" :lazy-load="true">
+				</u-image>
+			</view>
+		</view>
+
+		<view class="device-msg">
+			<view class="msg-item">
+				<view class="">
+					激活状态
+				</view>
+				<view class="">
+					{{device.activeState==1?'已激活':'未激活'}}
+				</view>
+			</view>
+			<view class="msg-item">
+				<view class="">
+					工控SN
+				</view>
+				<view class="">
+					{{device.deviceSn}}
+				</view>
+			</view>
+			<view class="msg-item">
+				<view class="">
+					商户前缀
+				</view>
+				<view class="">
+					{{device.mercPrefix}}
+				</view>
+			</view>
+			<view class="msg-item">
+				<view class="">
+					设备编号
+				</view>
+				<view class="">
+					{{device.deviceId}}
+				</view>
+			</view>
+
+		</view>
+
+		<view class="btn flex justify-between safe-bottom">
+			<view style="width:48%">
+				<xbutton size="large" round="82rpx" @click="refesh">刷新状态</xbutton>
+			</view>
+			<view style="width:48%">
+				<xbutton size="large" round="82rpx" @click="help">激活帮助</xbutton>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import {
+		userInfoBySelf,
+		aliDeviceObj
+	} from "@/api/device/device.js"
+	export default {
+		data() {
+			return {
+				isBindAli: true,
+				deviceId: null,
+				device: {
+
+				}
+			}
+		},
+		onLoad(o) {
+			this.deviceId = o.id;
+		},
+
+		onShow() {
+			this.getUserMsg()
+			this.getDeviceMsg()
+		},
+
+		methods: {
+			getDeviceMsg() {
+				aliDeviceObj({
+					deviceId: this.deviceId
+				}).then(res => {
+					this.device = res.data
+				}).catch(err => {
+
+				})
+			},
+
+			getUserMsg() {
+				userInfoBySelf().then(res => {
+					let data = res.data
+					this.isBindAli = data.aliUserId ? true : false;
+				})
+			},
+
+			refesh() {
+				this.getDeviceMsg()
+			},
+
+			help() {
+
+			},
+
+			bindAliAcc() {
+				this.$tab.navigateTo('/pages/activeDevice/bindAliAcc')
+			},
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.container {
+		position: relative;
+		text-align: center;
+
+
+
+		.device-img {
+			width: 724rpx;
+			margin-left: 13rpx;
+			background-color: #F5F5F5;
+			border-radius: 14rpx;
+			margin-top: 20rpx;
+			padding-bottom: 40rpx;
+
+			.tips {
+				border-top-left-radius: 14rpx;
+				border-top-right-radius: 14rpx;
+				line-height: 80rpx;
+				font-size: 26rpx;
+				height: 80rpx;
+				padding: 0 40rpx;
+				background: linear-gradient(180deg, #DEE9FF, rgba(217,231,255,1));
+			}
+
+			.img {
+				width: 147rpx;
+				height: 281rpx;
+				margin-top: 75rpx;
+				margin-left: 292rpx;
+			}
+		}
+
+		.device-msg {
+			width: 724rpx;
+			margin-left: 13rpx;
+			margin-top: 20rpx;
+			background-color: #F5F5F5;
+			border-radius: 14rpx;
+			padding: 0 37rpx;
+
+			.msg-item {
+				line-height: 92rpx;
+				font-size: 28rpx;
+				color: #333;
+				display: flex;
+				justify-content: space-between;
+
+				&+.msg-item {
+					border-top: 1rpx solid #E2E2E2;
+				}
+
+				>view:nth-child(1) {}
+
+				>view:nth-child(2) {
+					color: #777777;
+				}
+			}
+		}
+
+		.device-code {
+			font-size: 54rpx;
+			font-weight: bold;
+			margin-top: 46%;
+			color: #333;
+		}
+
+		.device-tips {
+			font-size: 28rpx;
+			margin-top: 30rpx;
+		}
+
+		.device-err {
+			font-size: 22rpx;
+			color: red;
+			margin-top: 40rpx;
+		}
+
+		.scan {
+			margin-top: 36rpx;
+		}
+
+		.btn {
+			width: 100%;
+			position: fixed;
+			left: 0;
+			bottom: 24rpx;
+			padding: 0 24rpx;
+		}
+	}
+</style>

+ 171 - 0
pages/activeDevice/bindDevice.vue

@@ -0,0 +1,171 @@
+<template>
+	<view class="container">
+		<u-navbar leftIconColor="#fff" titleStyle="color:#fff;fontSize:36rpx;" :autoBack="true" bgColor="#2C6FF3"
+			:placeholder="true" title="设备激活">
+		</u-navbar>
+
+		<view class="img">
+			<u-image width="147rpx" src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/device.png"
+				mode="widthFix" :lazy-load="true">
+			</u-image>
+		</view>
+
+		<view class="device-msg">
+			<view class="msg-item">
+				<view class="">
+					设备编号
+				</view>
+				<view class="">
+					{{deviceId}}
+				</view>
+			</view>
+			<view class="msg-item">
+				<view class="">
+					机器状态
+				</view>
+				<view class="" style="color: #2C6FF3;">
+					{{err}}
+				</view>
+			</view>
+		</view>
+
+		<view class="btn safe-bottom" v-if="!isActive">
+			<xbutton size="large" @click="activeDeviceNow">激活货柜</xbutton>
+		</view>
+	</view>
+</template>
+
+<script>
+	import {
+		deviceActive,
+		getActiveInfo
+	} from "@/api/device/device.js"
+	export default {
+		data() {
+			return {
+				deviceId: '请扫描二维码',
+				err: '未激活',
+				isActive: false //true已激活false未激活
+			}
+		},
+		onLoad(o) {
+			this.deviceId = o.id;
+			this.getDeviceStatus()
+		},
+		methods: {
+			getDeviceStatus() {
+				getActiveInfo({
+					deviceId: this.deviceId
+				}).then(res => {
+					this.isActive = false
+					this.err = '未激活'
+				}).catch(err => {
+					this.isActive = true
+					if(err.length>10){
+						this.err = '请重新扫码~'
+					}else{
+						this.err = err
+					}
+				})
+			},
+
+			scan() {
+				uni.scanCode({
+					success: (res) => {
+						console.log('条码内容:' + res.result);
+						let deviceId = res.result.split('=')[1]
+						this.deviceId = deviceId
+					}
+				});
+			},
+
+			activeDeviceNow() {
+				deviceActive({
+					deviceId: this.deviceId
+				}).then(res => {
+					this.$modal.msg('激活成功~')
+					setTimeout(() => {
+						this.$tab.redirectTo(`/pages/equipment/detail?id=${this.deviceId}`)
+					}, 1500)
+				}).catch(err => {
+					if (err.indeOf('com.netflix') == -1) {
+						this.err = err
+					} else {
+						this.err = '获取失败'
+					}
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.container {
+		position: relative;
+		text-align: center;
+
+		.img {
+			width: 147rpx;
+			height: 281rpx;
+			margin-top: 75rpx;
+			margin-left: 292rpx;
+		}
+
+		.device-msg {
+			width: 724rpx;
+			margin-left: 13rpx;
+			margin-top: 115rpx;
+			background-color: #F5F5F5;
+			border-radius: 14rpx;
+			padding: 0 37rpx;
+
+			.msg-item {
+				line-height: 92rpx;
+				font-size: 28rpx;
+				color: #333;
+				display: flex;
+				justify-content: space-between;
+
+				&+.msg-item {
+					border-top: 1rpx solid #E2E2E2;
+				}
+
+				>view:nth-child(1) {}
+
+				>view:nth-child(2) {
+					color: #777777;
+				}
+			}
+		}
+
+		.device-code {
+			font-size: 54rpx;
+			font-weight: bold;
+			margin-top: 46%;
+			color: #333;
+		}
+
+		.device-tips {
+			font-size: 28rpx;
+			margin-top: 30rpx;
+		}
+
+		.device-err {
+			font-size: 22rpx;
+			color: red;
+			margin-top: 40rpx;
+		}
+
+		.scan {
+			margin-top: 36rpx;
+		}
+
+		.btn {
+			width: 100%;
+			position: fixed;
+			left: 0;
+			bottom: 24rpx;
+			padding: 0 24rpx;
+		}
+	}
+</style>

+ 377 - 0
pages/activeDevice/deviceManage.vue

@@ -0,0 +1,377 @@
+<template>
+	<view class="container">
+		<u-navbar leftIconColor="#fff" titleStyle="color:#fff;fontSize:36rpx;" :autoBack="true" bgColor="#2C6FF3"
+			:placeholder="true" title="设备管理"></u-navbar>
+		<view class="content">
+			<view class="search">
+				<u-search animation placeholder="搜索设备" :clearabled="false" v-model="keyword" :showAction="false"
+					@search="search"></u-search>
+				<view class="scan-icon" @click="scan">
+					<u-icon name="scan" size="22" color="#909399"></u-icon>
+				</view>
+			</view>
+			<view class="tab-wrap">
+				<view class="tab">
+					<u-tabs :list="tabList" :activeStyle="{color: '#333',fontWeight: 'bold',fontSize:'36rpx'}"
+						:inactiveStyle="{fontSize:'32rpx'}" :scrollable="false" :current="current" @click="tabClick"
+						lineColor="#2C6FF3">
+					</u-tabs>
+				
+				</view>
+			</view>
+
+			<scroll-view class="scrollview" :scroll-with-animation="true" scroll-y lower-threshold="100"
+				@scrolltolower="scrolltolower" :style="{height:fullHeight}">
+
+				<view class="list" v-if="list&&list.length>0">
+					<block v-for="(item, index) in list" :key="index">
+						<view class="thumb-box" @click="detail(item)">
+							<!-- <view>
+								<image class="select-img"
+									src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/commodity/no_selected.png"
+									mode="widthFix" v-show="item.noSelect"></image>
+								<image class="select-img"
+									src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/commodity/selected.png"
+									mode="widthFix" v-show="!item.noSelect&&item.checked"></image>
+								<image class="select-img"
+									src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/commodity/select.png"
+									mode="widthFix" v-show="!item.noSelect&&!item.checked"></image>
+							</view> -->
+							<view class="check-content">
+								<!-- <view class="comm-img">
+									<u--image width="130rpx" height="130rpx" :src="item.cover" mode="widthFix"
+										:lazy-lord="true"></u--image>
+								</view> -->
+								<view class="comm-main">
+									<view>
+										{{item.deviceName||'/'}}
+									</view>
+									<view v-if="item.activeState==2">
+										商户前缀:{{item.mercPrefix}} 
+										<!-- <text class="under-line-text" @click.stop="copy(item.mercPrefix)">复制</text> -->
+									</view>
+									<view>
+										设备id:{{item.deviceId}} 
+										<!-- <text class="under-line-text"
+											@click.stop="copy(item.deviceId)">复制</text> -->
+									</view>
+									<view>
+										设备sn:{{item.deviceSn}} 
+									<!-- 	<text class="under-line-text"
+											@click.stop="copy(item.deviceSn)">复制</text> -->
+									</view>
+									<view v-if="item.activeState==1">
+										激活时间:{{item.activeTime}}
+									</view>
+								</view>
+							</view>
+						</view>
+					</block>
+					<view class="more" style="overflow: hidden;">
+						<u-loadmore :status="status" v-if="list.length>=1" />
+					</view>
+				</view>
+				<view class="empty" v-if="list.length==0">
+					<u-empty mode="list" text="没有设备!"></u-empty>
+				</view>
+			</scroll-view>
+		</view>
+
+		<!--<view class="btn">
+			<xbutton size="large" @click="submit">{{btnName}}({{selectList.length}})</xbutton>
+		</view> -->
+	</view>
+</template>
+
+<script>
+	import {
+		aliDeviceActivePage
+	} from "@/api/device/device.js"
+	export default {
+		data() {
+			return {
+				page: 1, //商品分页
+				size: 10,
+
+				status: 'loadmore', //加载更多
+
+				tabList: [{
+						name: '未激活'
+					},
+					{
+						name: '已激活'
+					}
+				],
+				current: 0,
+				fullHeight: 0,
+
+				list: []
+			}
+		},
+
+		computed: {
+			activeState() {
+				let val = 1;
+				if (this.current == 0) {
+					val = 2
+				} else {
+					val = 1
+				}
+				return val
+			}
+		},
+
+		onLoad(o) {
+			let _this = this;
+			const query = uni.createSelectorQuery().in(this);
+			query.select(".scrollview").boundingClientRect((data) => {
+				uni.getSystemInfo({
+					success(res) {
+						// 针对iPhone X等机型底部安全距离做适配
+						const model = res.model;
+						const modelInclude = [
+							"iPhone X",
+							'iPhone XR',
+							"iPhone XS",
+							"iPhone XS MAX",
+							"iPhone 12/13 mini",
+							"iPhone 12/13 (Pro)",
+							"iPhone 12/13 Pro Max",
+							"iPhone 14 Pro Max"
+						];
+						let safeDistance = modelInclude.includes(model)
+						//动态设置商品区域高度
+						if (safeDistance) {
+							_this.fullHeight = res.windowHeight - data.top - 34 + 'px';
+						} else {
+							_this.fullHeight = res.windowHeight - data.top + 'px';
+						}
+					},
+				});
+			}).exec();
+
+			this.getList()
+		},
+		methods: {
+			search(val) {
+				this.reset();
+				this.getList()
+			},
+
+			tabClick(e) {
+				console.log(e.index)
+				this.current = e.index
+				this.search()
+			},
+
+			//扫码
+			scan() {
+				uni.scanCode({
+					success: (res) => {
+						this.keyword = res.result;
+						this.getList()
+					}
+				});
+			},
+
+			//根据类目获取商品列表
+			getList(id) {
+				aliDeviceActivePage({
+					page: {
+						current: this.page,
+						size: this.size
+					},
+					activeState: this.activeState
+				}).then(res => {
+					let newData = res.data.records;
+					if (newData.length < 10) {
+						this.status = "nomore"
+					} else {
+						this.status = "loadmore"
+					}
+					this.list = this.list.concat(newData)
+				})
+			},
+
+			//触底加载更多
+			scrolltolower() {
+				if (this.status == 'nomore') return
+				this.page++
+				this.getList()
+			},
+
+			reset() {
+				this.status == 'loadmore'
+				this.page = 1;
+				this.size = 10;
+				this.list = [];
+			},
+
+			copy(text) {
+				uni.setClipboardData({
+					data: toString(text),
+					success: (data) => {
+						uni.showToast({
+							title: '复制成功'
+						})
+					},
+					fail: function(err) {
+						console.log(err)
+					},
+					complete: function(res) {
+						console.log(res)
+					}
+				})
+			},
+
+			submit() {
+
+			},
+			
+			detail(item){
+				this.$tab.navigateTo(`/pages/activeDevice/bindAliDev?id=${item.deviceId}`)
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.container {
+		.content {
+
+			.search {
+				padding: 24rpx 24rpx 12rpx;
+				background-color: #fff;
+				position: relative;
+
+				.scan-icon {
+					position: absolute;
+					right: 36rpx;
+					top: 38rpx;
+					z-index: 2;
+				}
+
+				.search-history {
+
+					.history-item {
+						margin-right: 24rpx;
+						padding: 0 12rpx;
+						background-color: #f2f2f2;
+						color: #333;
+						font-size: 24rpx;
+						line-height: 40rpx;
+						border-radius: 40rpx;
+						margin-top: 24rpx;
+					}
+				}
+			}
+
+			.tab-wrap {
+				background-color: #fff;
+				.tab{
+					width:50%;
+				}
+			}
+
+			.list {
+				width: 100%;
+				padding: 0rpx 13rpx 12rpx;
+				padding-bottom: calc(110rpx + env(safe-area-inset-bottom) / 2);
+				overflow: hidden;
+
+				.thumb-box {
+					border-bottom: 1rpx solid #f4f4f4;
+					display: flex;
+					flex-flow: row nowrap;
+					padding: 20rpx 30rpx;
+					align-items: center;
+					background-color: #fff;
+					border-radius: 12rpx;
+					margin-top: 12rpx;
+				}
+
+				.select-img {
+					width: 40rpx;
+					height: 40rpx;
+				}
+
+				.check-content {
+					display: flex;
+					flex-direction: row;
+					align-items: center;
+
+					.comm-img {
+						width: 130rpx;
+						height: 130rpx;
+						display: flex;
+						flex-direction: row;
+						align-items: center;
+						justify-content: space-around;
+
+						image {
+							width: 100%;
+						}
+					}
+
+					.comm-main {
+						box-sizing: border-box;
+						color: #333;
+						font-size: 28rpx;
+
+						.under-line-text {
+							font-size: 26rpx !important;
+							font-weight: 500;
+							font-style: italic;
+							text-decoration: underline;
+							color: #2C6FF3 !important;
+							margin-left: 24rpx;
+							background-color: #fff !important;
+						}
+
+						>view {
+							padding: 4rpx 0;
+							line-height: 50rpx;
+						}
+
+						>view:nth-child(1) {
+							font-size: 30rpx;
+							font-weight: bold;
+							color: #333;
+						}
+
+						>view:nth-child(2) {
+							// font-size: 28rpx;
+						}
+
+						>view:nth-child(3) {
+							// font-size: 28rpx;
+						}
+
+						>view:nth-child(4) {
+							// font-size: 28rpx;
+
+							text {
+								font-weight: bold;
+								color: red;
+							}
+						}
+					}
+				}
+			}
+		}
+
+		.btn {
+			width: 100%;
+			position: fixed;
+			left: 0;
+			padding: 0 24rpx;
+		}
+
+		.empty {
+			position: absolute;
+			left: 50%;
+			top: 50%;
+			transform: translate(-50%, -50%);
+		}
+	}
+</style>

+ 972 - 0
pages/commodity/addCom.vue

@@ -0,0 +1,972 @@
+<template>
+	<view class="container">
+		<u-navbar leftIconColor="#fff" titleStyle="color:#fff;fontSize:36rpx;" :autoBack="true" bgColor="#2C6FF3"
+			:placeholder="true" title="商品建模"></u-navbar>
+		<view class="content">
+			<u--form labelPosition="left" :model="form" :rules="rules" ref="form" errorType="toast">
+				<!-- <u-form-item label="类型" borderBottom ref="item2">
+					<u-radio-group v-model="form.type">
+						<u-radio :customStyle="{marginRight: '16rpx'}" label="标准商品" name="1">
+						</u-radio>
+						<u-radio :customStyle="{marginRight: '16rpx'}" label="非标商品" name="2">
+						</u-radio>
+					</u-radio-group>
+				</u-form-item> -->
+				<u-form-item required label="名称" prop="goodsName" borderBottom ref="item1">
+					<u--input v-model="form.goodsName" placeholder="请输入商品名称" border="none"></u--input>
+				</u-form-item>
+				<view class="bar-code">
+					<u-form-item required label="条码" prop="barcode" borderBottom ref="item1">
+						<u--input v-model="form.barcode" placeholder="请输入条码" border="none"></u--input>
+					</u-form-item>
+					<view class="scan-icon" @click.stop="scan">
+						<u-icon name="scan" size="22" color="#909399"></u-icon>
+					</view>
+				</view>
+				<u-form-item required label="类目" prop="categoryName" borderBottom @click="chooseCategoty" ref="item1">
+					<u--input v-model="form.categoryName" disabled placeholder="请选择商品类目" disabledColor="#ffffff"
+						border="none"></u--input>
+					<u-icon slot="right" name="arrow-right"></u-icon>
+				</u-form-item>
+				<u-form-item required label="品牌" prop="brandName" borderBottom ref="item1">
+					<u--input v-model="form.brandName" placeholder="请输入商品品牌" border="none"></u--input>
+				</u-form-item>
+				<!-- 	<u-form-item required label="规格" prop="unitName" borderBottom ref="item1">
+					<u--input v-model="form.unitName" placeholder="请输入计量单位" border="none"></u--input>
+				</u-form-item> -->
+				<u-form-item required label="规格" borderBottom @click="showSelect('unitName')" ref="item1">
+					<u--input v-model="form.unitName" disabled placeholder="请选择商品规格" disabledColor="#ffffff"
+						border="none">
+					</u--input>
+					<u-icon slot="right" name="arrow-right"></u-icon>
+				</u-form-item>
+
+				<u-row customStyle="margin-top: 20px">
+					<u-col span="5">
+						<u-form-item labelPosition="top" labelWidth="120" label="69码示例">
+							<view class="example-img">
+								<image @click="preview"
+									data-src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/barcode.png"
+									src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/barcode.png"
+									mode="widthFix"></image>
+							</view>
+						</u-form-item>
+					</u-col>
+					<u-col span="7">
+						<view class="form-item">
+							<u-form-item required labelPosition="top" prop="imgBarcode" labelWidth="350" label="商品69码">
+								<view class="img-upload">
+									<u--image radius="4" width="160rpx" height="160rpx"
+										:src="form.imgs.imgBarcode||imgBg" mode="aspectFit" :lazy-lord="true"
+										@click="imgClick('imgBarcode')"></u--image>
+								</view>
+							</u-form-item>
+						</view>
+					</u-col>
+				</u-row>
+
+				<u-row customStyle="margin-top: 20px">
+					<u-col span="5">
+						<u-form-item labelPosition="top" labelWidth="120" label="封面示例">
+							<view class="example-img">
+								<image @click="preview"
+									data-src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/commodity/imgPositive.png"
+									src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/commodity/imgPositive.png?x-oss-process=image/resize,m_fill,h_300,w_300"
+									mode="widthFix"></image>
+							</view>
+						</u-form-item>
+					</u-col>
+					<u-col span="7">
+						<view class="form-item">
+							<u-form-item required labelPosition="top" prop="cover" labelWidth="350" label="封面图片"
+								ref="item1">
+								<view class="img-upload">
+									<u--image radius="4" width="160rpx" height="160rpx" :src="form.cover||imgBg"
+										mode="aspectFit" :lazy-lord="true" @click="imgClick('cover')"></u--image>
+								</view>
+							</u-form-item>
+							<view class="tips">
+								小程序商品陈列展示用
+							</view>
+						</view>
+					</u-col>
+				</u-row>
+
+				<u-row customStyle="margin-top: 20px">
+					<u-col span="5">
+						<u-form-item labelPosition="top" labelWidth="120" label="商品图片(示例)">
+							<view class="example-img">
+								<image @click="preview"
+									data-src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/commodity/imgPositive.png"
+									src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/commodity/imgPositive.png?x-oss-process=image/resize,m_fill,h_300,w_300"
+									mode="widthFix"></image>
+							</view>
+						</u-form-item>
+					</u-col>
+					<u-col span="7">
+						<u-form-item required labelPosition="top" prop="imgPositive" labelWidth="80" label="正面图片"
+							ref="item1">
+							<view class="img-upload">
+								<u--image radius="4" width="160rpx" height="160rpx" :src="form.imgs.imgPositive||imgBg"
+									mode="aspectFit" :lazy-lord="true" @click="imgClick('imgPositive')"></u--image>
+							</view>
+						</u-form-item>
+					</u-col>
+				</u-row>
+
+				<u-row customStyle="margin-top: 20px">
+					<u-col span="5">
+						<u-form-item labelPosition="top" labelWidth="120" label="商品图片(示例)">
+							<view class="example-img">
+								<image @click="preview"
+									data-src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/commodity/imgBack.png"
+									src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/commodity/imgBack.png?x-oss-process=image/resize,m_fill,h_300,w_300"
+									mode="widthFix"></image>
+							</view>
+						</u-form-item>
+					</u-col>
+					<u-col span="7">
+						<u-form-item required labelPosition="top" prop="imgBack" labelWidth="80" label="背面图片">
+							<view class="img-upload">
+								<u--image radius="4" width="160rpx" height="160rpx" :src="form.imgs.imgBack||imgBg"
+									mode="aspectFit" :lazy-lord="true" @click="imgClick('imgBack')"></u--image>
+							</view>
+						</u-form-item>
+					</u-col>
+				</u-row>
+
+				<u-row customStyle="margin-top: 20px">
+					<u-col span="5">
+						<u-form-item labelPosition="top" labelWidth="120" label="商品图片(示例)">
+							<view class="example-img">
+								<image @click="preview"
+									data-src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/commodity/imgLeft.png"
+									src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/commodity/imgLeft.png?x-oss-process=image/resize,m_fill,h_300,w_300"
+									mode="widthFix"></image>
+							</view>
+						</u-form-item>
+					</u-col>
+					<u-col span="7">
+						<u-form-item required labelPosition="top" prop="imgLeft" labelWidth="80" label="左面图片">
+							<view class="img-upload">
+								<u--image radius="4" width="160rpx" height="160rpx" :src="form.imgs.imgLeft||imgBg"
+									mode="aspectFit" :lazy-lord="true" @click="imgClick('imgLeft')"></u--image>
+							</view>
+						</u-form-item>
+					</u-col>
+				</u-row>
+
+				<u-row customStyle="margin-top: 20px">
+					<u-col span="5">
+						<u-form-item labelPosition="top" labelWidth="120" label="商品图片(示例)">
+							<view class="example-img">
+								<image @click="preview"
+									data-src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/commodity/imgRight.png"
+									src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/commodity/imgRight.png?x-oss-process=image/resize,m_fill,h_300,w_300"
+									mode="widthFix"></image>
+							</view>
+						</u-form-item>
+					</u-col>
+					<u-col span="7">
+						<u-form-item required labelPosition="top" prop="imgRight" labelWidth="80" label="右面图片">
+							<view class="img-upload">
+								<u--image radius="4" width="160rpx" height="160rpx" :src="form.imgs.imgRight||imgBg"
+									mode="aspectFit" :lazy-lord="true" @click="imgClick('imgRight')"></u--image>
+							</view>
+						</u-form-item>
+					</u-col>
+				</u-row>
+
+				<u-row customStyle="margin-top: 20px">
+					<u-col span="5">
+						<u-form-item labelPosition="top" labelWidth="120" label="商品图片(示例)">
+							<view class="example-img">
+								<image @click="preview"
+									data-src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/commodity/imgTop.png"
+									src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/commodity/imgTop.png?x-oss-process=image/resize,m_fill,h_300,w_300"
+									mode="widthFix"></image>
+							</view>
+						</u-form-item>
+					</u-col>
+					<u-col span="7">
+						<u-form-item required labelPosition="top" prop="imgTop" labelWidth="80" label="顶部图片">
+							<view class="img-upload">
+								<u--image radius="4" width="160rpx" height="160rpx" :src="form.imgs.imgTop||imgBg"
+									mode="aspectFit" :lazy-lord="true" @click="imgClick('imgTop')"></u--image>
+							</view>
+						</u-form-item>
+					</u-col>
+				</u-row>
+
+				<u-row customStyle="margin-top: 20px">
+					<u-col span="5">
+						<u-form-item labelPosition="top" labelWidth="120" label="商品图片(示例)">
+							<view class="example-img">
+								<image @click="preview"
+									data-src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/commodity/imgBottom.png"
+									src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/commodity/imgBottom.png?x-oss-process=image/resize,m_fill,h_300,w_300"
+									mode="widthFix"></image>
+							</view>
+						</u-form-item>
+					</u-col>
+					<u-col span="7">
+						<u-form-item required labelPosition="top" prop="imgBottom" labelWidth="80" label="底部图片">
+							<view class="img-upload">
+								<u--image radius="4" width="160rpx" height="160rpx" :src="form.imgs.imgBottom||imgBg"
+									mode="aspectFit" :lazy-lord="true" @click="imgClick('imgBottom')"></u--image>
+							</view>
+						</u-form-item>
+					</u-col>
+				</u-row>
+
+				<u-row customStyle="margin-top: 20px">
+					<u-col span="5">
+						<u-form-item labelPosition="top" labelWidth="120" label="商品图片(示例)">
+							<view class="example-img">
+								<image @click="preview"
+									data-src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/commodity/imgPositiveBias.png"
+									src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/commodity/imgPositiveBias.png?x-oss-process=image/resize,m_fill,h_300,w_300"
+									mode="widthFix"></image>
+							</view>
+						</u-form-item>
+					</u-col>
+					<u-col span="7">
+						<u-form-item required labelPosition="top" prop="imgPositiveBias" labelWidth="80" label="正斜图片">
+							<view class="img-upload">
+								<u--image radius="4" width="160rpx" height="160rpx"
+									:src="form.imgs.imgPositiveBias||imgBg" mode="aspectFit" :lazy-lord="true"
+									@click="imgClick('imgPositiveBias')"></u--image>
+							</view>
+						</u-form-item>
+					</u-col>
+				</u-row>
+
+				<u-row customStyle="margin-top: 20px">
+					<u-col span="5">
+						<u-form-item labelPosition="top" labelWidth="120" label="商品图片(示例)">
+							<view class="example-img">
+								<image @click="preview"
+									data-src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/commodity/imgLeftBias.png"
+									src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/commodity/imgLeftBias.png?x-oss-process=image/resize,m_fill,h_300,w_300"
+									mode="widthFix"></image>
+							</view>
+						</u-form-item>
+					</u-col>
+					<u-col span="7">
+						<u-form-item required labelPosition="top" prop="imgLeftBias" labelWidth="80" label="左斜图片">
+							<view class="img-upload">
+								<u--image radius="4" width="160rpx" height="160rpx" :src="form.imgs.imgLeftBias||imgBg"
+									mode="aspectFit" :lazy-lord="true" @click="imgClick('imgLeftBias')"></u--image>
+							</view>
+						</u-form-item>
+					</u-col>
+				</u-row>
+
+				<u-row customStyle="margin-top: 20px">
+					<u-col span="5">
+						<u-form-item labelPosition="top" labelWidth="120" label="商品图片(示例)">
+							<view class="example-img">
+								<image @click="preview"
+									data-src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/commodity/imgRightBias.png"
+									src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/commodity/imgRightBias.png?x-oss-process=image/resize,m_fill,h_300,w_300"
+									mode="widthFix"></image>
+							</view>
+						</u-form-item>
+					</u-col>
+					<u-col span="7">
+						<u-form-item required labelPosition="top" prop="imgRightBias" labelWidth="80" label="右斜图片">
+							<view class="img-upload">
+								<u--image radius="4" width="160rpx" height="160rpx" :src="form.imgs.imgRightBias||imgBg"
+									mode="aspectFit" :lazy-lord="true" @click="imgClick('imgRightBias')"></u--image>
+							</view>
+						</u-form-item>
+					</u-col>
+				</u-row>
+
+				<u-row customStyle="margin-top: 20px;">
+					<u-col span="5">
+						<u-form-item labelPosition="top" labelWidth="120" label="商品图片(示例)">
+							<view class="example-img">
+								<image @click="preview"
+									data-src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/commodity/imgRight.png"
+									src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/commodity/imgRight.png?x-oss-process=image/resize,m_fill,h_300,w_300"
+									mode="widthFix"></image>
+							</view>
+						</u-form-item>
+					</u-col>
+					<u-col span="7">
+						<u-form-item labelPosition="top" labelWidth="80" label="其他图片">
+							<view class="img-upload">
+								<ImgUpload :maxCount="4" v-model="form.imgs.others" />
+							</view>
+						</u-form-item>
+					</u-col>
+				</u-row>
+
+
+			</u--form>
+			<u-picker :show="show" :columns="actions" :closeOnClickOverlay="true" keyName="name" @close="show=false"
+				@confirm="actionSelect" @cancel="show = false">
+			</u-picker>
+		</view>
+
+		<view class="btn safe-bottom" v-if="checkStatus=='1'">
+			<xbutton bgColor="#5ac725" size="large">审核通过</xbutton>
+		</view>
+		<view class="btn safe-bottom" v-else-if="checkStatus=='-1'">
+			<xbutton delay="1500" size="large" @click="submit" throttleTime="1500">重新提交</xbutton>
+		</view>
+		<view class="btn safe-bottom" v-else-if="checkStatus=='0'">
+			<xbutton bgColor="#f9ae3d" delay="1500" size="large" throttleTime="1500">审核中</xbutton>
+		</view>
+		<view class="btn safe-bottom" v-else>
+			<xbutton delay="1500" size="large" @click="submit" throttleTime="1500">提交审核</xbutton>
+		</view>
+
+		<!-- 弹框 -->
+		<xpopup :show="popShow" @close="popClose" :showBtn="false" title="选择类目">
+			<!-- 类目选择 -->
+			<scroll-view style="height: 600rpx;" scroll-y scroll-with-animation>
+				<view class="popup-content">
+					<tki-tree style="width:100%;" :range="categoryOptions" :foldAll="false" rangeKey="name"
+						idKey="categoryCode" buttonName="选中" @btnClick="categorySubmit">
+					</tki-tree>
+				</view>
+			</scroll-view>
+		</xpopup>
+
+		<ImgUploadCut @success="imgSuccess" ref="uploadImg" />
+	</view>
+</template>
+
+<script>
+	import ImgUploadCut from "./components/imgUploadCut"
+	import ImgUpload from "./components/imgUpload"
+	import tkiTree from "@/components/tki-tree/tki-tree.vue";
+	import {
+		save,
+		update,
+		searchObj,
+		categoryTree,
+		listIdName
+	} from '@/api/commodity/goodsMode.js'
+	import {
+		goodsCategory
+	} from '@/api/commodity/goods.js'
+	//验证规则
+	let rulesObj = {
+		type: 'string',
+		required: true,
+		message: '必填项',
+		trigger: ['change']
+	}
+
+	export default {
+		components: {
+			ImgUpload,
+			ImgUploadCut,
+			tkiTree
+		},
+		data() {
+			return {
+				form: {
+					goodsName: undefined, //商品名称
+					barcode: undefined, //条形码
+					brandName: undefined, //品牌
+					categoryName: undefined, //分类
+					unitName: undefined, //规格
+					cover: undefined, //封面图片
+					imgs: {
+						imgBarcode: undefined, //69码图片
+						imgPositive: undefined, //正面
+						imgBack: undefined, //背面
+						imgLeft: undefined, //左边
+						imgRight: undefined, //右边
+						imgTop: undefined, //顶部
+						imgBottom: undefined, //底部图片
+						imgPositiveBias: undefined, //正斜图片
+						imgLeftBias: undefined, //左斜图片
+						imgRightBias: undefined, //右斜图片
+						others: []
+					}
+
+				},
+				rules: {
+					'unitName': {
+						type: 'string',
+						required: true,
+						message: '请选择商品规格~',
+						trigger: ['change']
+					},
+					'goodsName': {
+						type: 'string',
+						required: true,
+						message: '请填入商品名称~',
+						trigger: ['change']
+					},
+					'barcode': {
+						type: 'string',
+						required: true,
+						message: '请填入商品条码~',
+						trigger: ['change']
+					},
+					'brandName': {
+						type: 'string',
+						required: true,
+						message: '请填入商品品牌~',
+						trigger: ['change']
+					},
+					'categoryName': {
+						type: 'string',
+						required: true,
+						message: '请选择商品类目~',
+						trigger: ['change']
+					},
+					'cover': {
+						validator: (rule, value, callback) => {
+							if (!this.form.cover || this.form.cover.length == 0) {
+								return false
+							} else {
+								return true
+							}
+						},
+						message: '请上传封面图片~',
+						trigger: ['change', 'blur']
+					},
+					'imgBarcode': {
+						validator: (rule, value, callback) => {
+							if (!this.form.cover || this.form.cover.length == 0) {
+								return false
+							} else {
+								return true
+							}
+						},
+						message: '请上传69码图片~',
+						trigger: ['change', 'blur']
+					},
+					'imgPositive': {
+						validator: (rule, value, callback) => {
+							if (!this.form.imgs.imgPositive || this.form.imgs.imgPositive.length == 0) {
+								return false
+							} else {
+								return true
+							}
+						},
+						message: '请上传正面图片~',
+						trigger: ['change', 'blur']
+					},
+					'imgBack': {
+						validator: (rule, value, callback) => {
+							if (!this.form.imgs.imgBack || this.form.imgs.imgBack.length == 0) {
+								return false
+							} else {
+								return true
+							}
+						},
+						message: '请上传背面图片~',
+						trigger: ['change', 'blur']
+					},
+					'imgLeft': {
+						validator: (rule, value, callback) => {
+							if (!this.form.imgs.imgLeft || this.form.imgs.imgLeft.length == 0) {
+								return false
+							} else {
+								return true
+							}
+						},
+						message: '请上传左面图片~',
+						trigger: ['change', 'blur']
+					},
+					'imgRight': {
+						validator: (rule, value, callback) => {
+							if (!this.form.imgs.imgRight || this.form.imgs.imgRight.length == 0) {
+								return false
+							} else {
+								return true
+							}
+						},
+						message: '请上传右面图片~',
+						trigger: ['change', 'blur']
+					},
+					'imgTop': {
+						validator: (rule, value, callback) => {
+							if (!this.form.imgs.imgTop || this.form.imgs.imgTop.length == 0) {
+								return false
+							} else {
+								return true
+							}
+						},
+						message: '请上传顶部图片~',
+						trigger: ['change', 'blur']
+					},
+					'imgBottom': {
+						validator: (rule, value, callback) => {
+							if (!this.form.imgs.imgBottom || this.form.imgs.imgBottom.length == 0) {
+								return false
+							} else {
+								return true
+							}
+						},
+						message: '请上传底部图片~',
+						trigger: ['change', 'blur']
+					},
+					'imgPositiveBias': {
+						validator: (rule, value, callback) => {
+							if (!this.form.imgs.imgPositiveBias || this.form.imgs.imgPositiveBias.length == 0) {
+								return false
+							} else {
+								return true
+							}
+						},
+						message: '请上传正斜图片~',
+						trigger: ['change', 'blur']
+					},
+					'imgLeftBias': {
+						validator: (rule, value, callback) => {
+							if (!this.form.imgs.imgLeftBias || this.form.imgs.imgLeftBias.length == 0) {
+								return false
+							} else {
+								return true
+							}
+						},
+						message: '请上传左斜图片~',
+						trigger: ['change', 'blur']
+					},
+					'imgRightBias': {
+						validator: (rule, value, callback) => {
+							if (!this.form.imgs.imgRightBias || this.form.imgs.imgRightBias.length == 0) {
+								return false
+							} else {
+								return true
+							}
+						},
+						message: '请上传右斜图片~',
+						trigger: ['change', 'blur']
+					}
+				},
+				imgBg: 'https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/commodity/upload-bg.png',
+				show: false,
+				selectType: 'categoryCode',
+				actions: [{
+						name: '全部',
+					},
+					{
+						name: '饮料',
+					},
+					{
+						name: '槟榔',
+					},
+				],
+
+				radio: '',
+				switchVal: false,
+
+				categoryOptions: [], //类目options
+				unitOptions: [], //规格options
+				title: null, //picker标题
+
+				popShow: false,
+				type: 'add', //add添加、edit编辑
+				checkStatus: null,
+				imgType: null,
+			};
+		},
+
+		onLoad(o) {
+			this.getCategory()
+			this.getUnit()
+			if (o.id) { //编辑
+				this.type = 'edit'
+				this.getDetail(o.id)
+			} else { //新增暂存数据反显
+				if (uni.getStorageSync('goodsAddModal')) {
+					this.form = JSON.parse(uni.getStorageSync('goodsAddModal'))
+				}
+			}
+		},
+
+		watch: {
+			form: {
+				handler(newVal, oldVal) {
+					console.log('暂存建模内容',newVal)
+					if (this.type != 'edit') {
+						uni.setStorageSync('goodsAddModal', JSON.stringify(newVal))
+					}
+				},
+				deep: true
+			}
+		},
+
+		methods: {
+			//获取详情
+			getDetail(id) {
+				searchObj({
+					id: id
+				}).then(res => {
+					let data = res.data;
+					if (data) {
+						this.checkStatus = data.status; //审核状态
+						// 组装反显参数
+						let newData = {
+							id: data.id,
+							goodsName: data.goodsName, //商品名称
+							barcode: data.barcode, //条形码
+							brandName: data.brandName, //品牌
+							categoryCode: data.categoryCode, //分类
+							categoryName: data.categoryName, //分类名称
+							unitName: data.unitName, //规格
+							cover: [data.cover], //封面图片
+							imgs: {
+								imgBarcode: undefined, //69码图片
+								imgPositive: undefined, //正面
+								imgBack: undefined, //背面
+								imgLeft: undefined, //左边
+								imgRight: undefined, //右边
+								imgTop: undefined, //顶部
+								imgBottom: undefined, //底部图片
+								imgPositiveBias: undefined, //正斜图片
+								imgLeftBias: undefined, //左斜图片
+								imgRightBias: undefined, //右斜图片
+								others: []
+							}
+						}
+
+						data.imgs.forEach(i => {
+							if (i.type == 'barcode') {
+								newData.imgs.imgBarcode = i.imgUrl
+							}
+							if (i.type == 'top') {
+								newData.imgs.imgTop = i.imgUrl
+							}
+							if (i.type == 'back') {
+								newData.imgs.imgBack = i.imgUrl
+							}
+							if (i.type == 'left') {
+								newData.imgs.imgLeft = i.imgUrl
+							}
+							if (i.type == 'positive') {
+								newData.imgs.imgPositive = i.imgUrl
+							}
+							if (i.type == 'right') {
+								newData.imgs.imgRight = i.imgUrl
+							}
+							if (i.type == 'bottom') {
+								newData.imgs.imgBottom = i.imgUrl
+							}
+							if (i.type == 'positive_bias') {
+								newData.imgs.imgPositiveBias = i.imgUrl
+							}
+							if (i.type == 'left_bias') {
+								newData.imgs.imgLeftBias = i.imgUrl
+							}
+							if (i.type == 'right_bias') {
+								newData.imgs.imgRightBias = i.imgUrl
+							}
+							if (i.type == 'others') {
+								newData.imgs.others.push(i.imgUrl)
+							}
+
+						})
+
+						this.form = JSON.parse(JSON.stringify(newData))
+					}
+				})
+			},
+
+			fixImgSize(url) {
+				return url + '?x-oss-process=image/resize,m_fill,h_300,w_300'
+			},
+
+			imgClick(type) {
+				this.imgType = type;
+				let obj={
+					selWidth: '500upx',
+					selHeight: '800upx',
+					inner: true
+				}
+				
+				switch (type){
+					case 'cover':
+						obj={
+							selWidth: '500upx',
+							selHeight: '526upx',
+							inner: true
+						}
+						break;
+					default:
+						break;
+				}
+				this.$refs.uploadImg.fChooseImg(obj)
+			},
+
+			imgSuccess(url) {
+				console.log('图片路径',url)
+				switch (this.imgType) {
+					case 'imgBarcode':
+						this.form.imgs.imgBarcode = url
+						break;
+					case 'cover':
+						this.form.cover = url
+						break;
+					case 'imgPositive':
+						this.form.imgs.imgPositive = url
+						break;
+					case 'imgBack':
+						this.form.imgs.imgBack = url
+						break;
+					case 'imgLeft':
+						this.form.imgs.imgLeft = url
+						break;
+					case 'imgRight':
+						this.form.imgs.imgRight = url
+						break;
+					case 'imgTop':
+						this.form.imgs.imgTop = url
+						break;
+					case 'imgBottom':
+						this.form.imgs.imgBottom = url
+						break;
+					case 'imgPositiveBias':
+						this.form.imgs.imgPositiveBias = url
+						break;
+					case 'imgLeftBias':
+						this.form.imgs.imgLeftBias = url
+						break;
+					case 'imgRightBias':
+						this.form.imgs.imgRightBias = url
+						break;
+					default:
+						break;
+				}
+				this.form=JSON.parse(JSON.stringify(this.form))
+			},
+
+			//扫码
+			scan() {
+				uni.scanCode({
+					success: (res) => {
+						this.form.barcode = res.result;
+					}
+				});
+			},
+
+			// 获取商品类目
+			getCategory() {
+				categoryTree({}).then(res => {
+					this.categoryOptions = res.data;
+				})
+			},
+
+			chooseCategoty() {
+				this.popShow = true
+			},
+
+			popClose() {
+				this.popShow = false
+			},
+
+			categorySubmit(item) {
+				this.form.categoryCode = item.id;
+				this.form.categoryName = item.name;
+				this.popClose()
+			},
+
+			//获取规格
+			getUnit() {
+				listIdName().then(res => {
+					let data = res.data;
+					let newData = data.map(i => {
+						return {
+							id: i.id,
+							name: i.unitName
+						}
+					})
+					this.unitOptions = newData;
+				})
+			},
+
+			// 显示下拉框
+			showSelect(type) {
+				this.selectType = type;
+				if (type == 'categoryCode') {
+					this.title = '请选择类目';
+					this.actions = [this.categoryOptions];
+				} else {
+					this.title = '请选择规格';
+					this.actions = [this.unitOptions];
+				}
+				this.show = true;
+			},
+
+			// 下拉选择
+			actionSelect(e) {
+				console.log(e)
+				if (this.selectType === 'categoryCode') { // 分类
+					this.form.categoryCode = e.value[0].name;
+					this.$refs.form.validateField('categoryCode');
+				} else { //规格
+					this.form.unitName = e.value[0].name;
+					this.$refs.form.validateField('unitName');
+				}
+				this.show = false
+			},
+
+			//表单提交
+			submit() {
+				console.log(this.form)
+				this.$refs.form.validate().then(res => {
+					let params = {
+						barcode: this.form.barcode,
+						brandName: this.form.brandName,
+						categoryCode: this.form.categoryCode,
+						goodsName: this.form.goodsName,
+						type: this.form.type,
+						unitName: this.form.unitName,
+						cover: this.form.cover,
+						imgs: {
+							imgBarcode: this.form.imgs.imgBarcode,
+							imgPositive: this.form.imgs.imgPositive, //正面
+							imgBack: this.form.imgs.imgBack, //背面
+							imgLeft: this.form.imgs.imgLeft, //左边
+							imgRight: this.form.imgs.imgRight, //右边
+							imgTop: this.form.imgs.imgTop, //顶部
+							imgBottom: this.form.imgs.imgBottom, //底部图片
+							imgPositiveBias: this.form.imgs.imgPositiveBias, //正斜图片
+							imgLeftBias: this.form.imgs.imgLeftBias, //左斜图片
+							imgRightBias: this.form.imgs.imgRightBias, //右斜图片
+							others: this.form.imgs.others
+						}
+					}
+					if (this.form.id) {
+						params.id = this.form.id
+						update(params).then(res => {
+							this.$modal.msg('修改成功~')
+							setTimeout(() => {
+								this.$tab.navigateTo('/pages/commodity/auditList')
+							}, 1000)
+						}).catch(err => {
+
+						})
+					} else {
+						save(params).then(res => {
+							uni.setStorageSync('goodsAddModal', '') //清空临时存储
+							this.$modal.msg('新建成功~')
+							setTimeout(() => {
+								this.$tab.navigateTo('/pages/commodity/auditList')
+							}, 1000)
+						}).catch(err => {
+
+						})
+					}
+				}).catch(errors => {
+					console.log(errors)
+				})
+			},
+
+			// 预览图片
+			preview(event) {
+				console.log(event)
+				let currentUrl = event.target.dataset.src
+				uni.previewImage({
+					current: currentUrl, // 当前显示图片的http链接
+					urls: [currentUrl] // 需要预览的图片http链接列表
+				})
+			}
+		},
+		onReady() {
+			this.$refs.form.setRules(this.rules)
+		},
+	};
+</script>
+
+<style scoped lang="scss">
+	.container {
+		height: 100vh;
+		background-color: #fff;
+
+		.content {
+			padding: 24rpx 36rpx 260rpx;
+			background-color: #fff;
+
+			.fail-msg {
+				color: red;
+				font-size: 28rpx;
+				line-height: 32rpx;
+				margin-top: 12rpx;
+				margin-bottom: 24rpx;
+				width: 726rpx;
+				margin-left: -24rpx;
+				background-color: skyblue;
+				padding: 24rpx 12rpx;
+				box-sizing: border-box;
+				border-radius: 12rpx;
+			}
+
+			.example-img {
+				height: 212rpx;
+				padding: 24rpx 0 12rpx;
+				display: flex;
+				flex-flow: row nowrap;
+				justify-content: flex-start;
+
+				>image {
+					width: 160rpx;
+					margin-right: 66rpx;
+					border-radius: 4rpx;
+				}
+			}
+
+			.img-upload {
+				height: 212rpx;
+				padding: 24rpx 0 12rpx;
+			}
+
+			.bar-code {
+				position: relative;
+
+				.scan-icon {
+					position: absolute;
+					right: 0;
+					top: 50%;
+					transform: translateY(-50%);
+					z-index: 9999;
+				}
+			}
+
+			.form-item {
+				position: relative;
+
+				.tips {
+					position: absolute;
+					right: 24rpx;
+					top: 24rpx;
+					color: red;
+					font-size: 24rpx;
+				}
+			}
+		}
+
+		.btn {
+			width: 100%;
+			position: fixed;
+			left: 0;
+			bottom: 24rpx;
+			padding: 0 24rpx;
+			z-index: 999;
+
+			.cu-btn {
+				width: 100%;
+				background-color: #2C6FF3;
+				color: #fff;
+			}
+		}
+
+		.popup-content {
+			padding: 36rpx 24rpx;
+			display: flex;
+			flex-flow: row nowrap;
+			justify-content: flex-start;
+			align-items: center;
+		}
+	}
+</style>

+ 507 - 0
pages/commodity/allGoodsSearch.vue

@@ -0,0 +1,507 @@
+<template>
+	<view class="container">
+		<u-navbar leftIconColor="#fff" titleStyle="color:#fff;fontSize:36rpx;" :autoBack="true" bgColor="#2C6FF3"
+			:placeholder="true" :title="title"></u-navbar>
+		<view class="content">
+			<view class="search">
+				<u-search animation placeholder="商品搜索" :clearabled="false" v-model="keyword" :showAction="false"
+					@search="search"></u-search>
+				<view class="scan-icon" @click="scan">
+					<u-icon name="scan" size="22" color="#909399"></u-icon>
+				</view>
+
+				<view class="search-history flex flex-wrap flex-start">
+					<view class="history-item" v-for="(item,index) in historyList" :key="index"
+						@click="searchFast(item)">
+						{{item}}
+					</view>
+				</view>
+			</view>
+
+			<view class="xy-card tab-list" style="padding:0 0 24rpx;">
+				<view class="tab-left">
+					<u-tabs :list="tabList" :scrollable="false" :current="current" @click="tabClick"
+						lineColor="#2C6FF3">
+					</u-tabs>
+				</view>
+				<!-- 	<view class="use-list" @click="$tab.navigateTo('/pages/commodity/commoditylist')">
+					常用商品清单
+				</view> -->
+			</view>
+
+			<view class="list" v-if="newCommList&&newCommList.length>0">
+				<view class="thumb-box" v-for="(item, index) in newCommList" :key="index"
+					@click.stop="commItemSelect(item)">
+					<view>
+						<image class="select-img"
+							src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/commodity/no_selected.png"
+							mode="widthFix" v-show="item.noSelect"></image>
+						<image class="select-img"
+							src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/commodity/selected.png"
+							mode="widthFix" v-show="!item.noSelect&&item.checked"></image>
+						<image class="select-img"
+							src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/commodity/select.png"
+							mode="widthFix" v-show="!item.noSelect&&!item.checked"></image>
+					</view>
+					<view class="check-content">
+						<view class="comm-img">
+							<u--image width="130rpx" height="130rpx" :src="item.cover" mode="widthFix"
+								:lazy-lord="true"></u--image>
+						</view>
+						<view class="comm-main">
+							<view>
+								{{item.name}}
+							</view>
+							<view>
+								条形码:{{item.barcode}}
+							</view>
+							<view>
+								商品类型:{{item.categoryName}}
+							</view>
+							<view v-if="type==1">
+								价格:<text>¥{{item.price}}</text>
+							</view>
+						</view>
+					</view>
+				</view>
+				<u-loadmore :status="status" v-if="newCommList.length>=1" />
+			</view>
+
+			<view class="empty" v-if="newCommList.length==0">
+				<u-empty mode="car" text="没有商品!"></u-empty>
+			</view>
+		</view>
+
+		<view class="btn safe-bottom">
+			<xbutton size="large" @click="submit">添加到设备({{selectList.length}})</xbutton>
+		</view>
+	</view>
+</template>
+
+<script>
+	import {
+		ownerGoodsList as perOwnerGoodsList
+	} from "@/api/commodity/mercGoods.js"
+	import {
+		ownerGoodsList as pubOwnerGoodsList,
+		pageByGoods,
+		pageByGoodsMerc,
+		bindDeviceByMercGoods
+	} from "@/api/commodity/goods.js"
+	export default {
+		data() {
+			return {
+				keyword: '',
+				commList: [], //商品列表
+
+				page: 1, //商品分页
+				size: 10,
+
+				status: 'loadmore', //加载更多
+				type: null,
+				storeName: null,
+				title: '私有商品库',
+				selectList: [],
+				id: null,
+				historyList: [],
+
+				tabList: [{
+						name: '私有商品库'
+					},
+					{
+						name: '官方商品库'
+					}
+				],
+				current: 0,
+			}
+		},
+
+		computed: {
+			newCommList() {
+				let newCommList = [];
+				if (this.selectList && this.selectList.length > 0) {
+					let selectList = this.selectList
+					const idMapping = selectList.reduce((acc, el, i) => {
+						acc[el.id] = i;
+						return acc;
+					}, {});
+					this.commList.forEach(i => {
+						if (idMapping[i.id] != undefined) { //重复值
+							i.checked = true
+						} else {
+							i.checked = false
+						}
+						newCommList.push(i)
+					})
+				} else {
+					newCommList = this.commList.map(i => {
+						i.checked = false;
+						return i
+					})
+				}
+				console.log('newCommList', newCommList)
+				return newCommList
+			},
+		},
+
+		onLoad(o) {
+			if (uni.getStorageSync('goods')) {
+				this.historyList = JSON.parse(uni.getStorageSync('goods'))
+			}
+			this.id = o.id;
+			this.switchType(o.type) //current:0私库 、1公库
+			// this.getCommList();
+		},
+		
+		onShow() {
+			this.reset();
+			this.getCommList();
+		},
+		
+		methods: {
+			search(val) {
+				this.saveKeyWord('goods', this.keyword)
+				this.reset();
+				this.getCommList()
+			},
+
+			tabClick(e) {
+				this.switchType(e.index)
+				this.search()
+			},
+
+			switchType(type) {
+				this.current = type; //0私库、1公库
+				this.title = type == 0 ? '私有商品库' : '官方商品库';
+				this.storeName = type == 0 ? 'perStor' : 'pubStor';
+				if (uni.getStorageSync(this.storeName) && JSON.parse(uni.getStorageSync(
+						this.storeName)).length > 0) {
+					let commStor = JSON.parse(uni.getStorageSync(this.storeName))
+					console.log('commStor', commStor)
+					this.selectList = commStor;
+				} else {
+					this.selectList = [];
+				}
+			},
+
+			saveKeyWord(type, val) {
+				if (val) {
+					let arr = []
+					if (uni.getStorageSync(type)) {
+						let arr = JSON.parse(uni.getStorageSync(type))
+						if (arr.indexOf(val) != -1) {
+							console.log('arr.indexOf(val)', arr.indexOf(val))
+							arr.splice(arr.indexOf(val), 1)
+						}
+						arr.unshift(val)
+						if (arr.length > 6) {
+							arr.pop()
+						}
+						this.historyList = JSON.parse(JSON.stringify(arr))
+						uni.setStorageSync(type, JSON.stringify(arr))
+					} else {
+						arr.unshift(val)
+						this.historyList = JSON.parse(JSON.stringify(arr))
+						uni.setStorageSync(type, JSON.stringify(arr))
+					}
+				} else {
+					return
+				}
+			},
+
+			searchFast(e) {
+				this.keyword = e
+				this.search()
+			},
+
+			//扫码
+			scan() {
+				uni.scanCode({
+					success: (res) => {
+						this.keyword = res.result;
+						this.getCommList()
+					}
+				});
+			},
+
+			//根据类目获取商品列表
+			getCommList(id) {
+				if (this.current == 0) { //私库到设备
+					pageByGoodsMerc({
+						page: {
+							current: this.page,
+							size: this.size
+						},
+						keyword: this.keyword,
+						deviceId: this.id
+					}).then(res => {
+						let data = res.data.records;
+						if (data && data.length > 0) {
+							data = data.map(i => {
+								i.noSelect = i.isBind;
+								i.name = i.goodsName;
+								i.barcode = i.goodsBarcode;
+								i.cover = i.goodsCover;
+								i.price = Number(i.price) / 100;
+								i.categoryName = i.capacity == null ? '未分类' : i.capacity;
+								return i
+							})
+						}
+						if (data.length < 10) {
+							this.status = "nomore"
+						} else {
+							this.status = "loadmore"
+						}
+						this.commList = this.commList.concat(data)
+					})
+				}
+
+				if (this.current == 1) { //公库设备
+					pageByGoods({
+						page: {
+							current: this.page,
+							size: this.size
+						},
+						keyword: this.keyword,
+						deviceId: this.id
+					}).then(res => {
+						let data = res.data.records;
+						let newData = data.map(i => {
+							i.noSelect = i.isBind;
+							i.categoryCode = i.categoryCodeLevel1;
+							return i
+						})
+						if (newData.length < 10) {
+							this.status = "nomore"
+						} else {
+							this.status = "loadmore"
+						}
+						this.commList = this.commList.concat(newData)
+					})
+				}
+			},
+
+			//触底加载更多
+			onReachBottom() {
+				if (this.status == 'nomore') return
+				this.page++
+				this.getCommList()
+			},
+
+			reset() {
+				this.status == 'loadmore'
+				this.page = 1;
+				this.size = 10;
+				this.commList = [];
+			},
+
+			// 商品选中状态改变
+			commItemSelect(e) {
+				if (e.noSelect) {
+					this.$modal.msg('当前商品已存在!')
+					return
+				}
+
+				if (this.type == 0) { //私库需要判断是否新建模商品
+					if (e.price == null || e.price == undefined) {
+						uni.showModal({
+							title: '提示',
+							content: '当前商品为新商品需先设置商品价格,是否前往设置?',
+							success: res => {
+								if (res.confirm) {
+									this.$tab.navigateTo('/pages/commodity/comEdit?id=' + e.id)
+								} else if (res.cancel) {
+									console.log('用户点击取消');
+								}
+							}
+						});
+						return
+					}
+				}
+
+				e.checked = !e.checked
+				//选中商品存储到内存中,以便取用
+				let commStor = []
+				if (e.checked) { //选中添加到存储中
+					if (uni.getStorageSync(this.storeName)) {
+						commStor = JSON.parse(uni.getStorageSync(this.storeName));
+					}
+					commStor.push(e);
+				} else { //取消选中删除,并且从存储中删除
+					commStor = JSON.parse(uni.getStorageSync(this.storeName));
+					for (let i = 0; i < commStor.length; i++) {
+						let item = commStor[i]
+						if (item.id == e.id) {
+							commStor.splice(i, 1);
+							break
+						}
+					}
+				}
+				//收集选中商品,更新存储
+				this.selectList = commStor;
+				commStor.length > 0 ? uni.setStorageSync(this.storeName, JSON.stringify(commStor)) : uni
+					.setStorageSync(
+						this.storeName, '')
+				if (commStor.length > 0) {
+					uni.setStorageSync(this.storeName, JSON.stringify(commStor))
+				}
+			},
+
+			submit() {
+				if (uni.getStorageSync(this.storeName) && JSON.parse(uni.getStorageSync(this.storeName)).length > 0) {
+					if (this.current == 0) { //私库到设备
+						let commList = JSON.parse(uni.getStorageSync(this.storeName))
+						let goodsIdList = commList.map(i => {
+							return i.goodsId
+						})
+						bindDeviceByMercGoods({
+							deviceIds: [this.id],
+							goodsIdList: goodsIdList
+						}).then(res => {
+							if (res.code == 200) {
+								this.$modal.msg('添加成功~')
+								uni.setStorageSync(this.storeName, '') //清空购物车
+								setTimeout(() => {
+									// this.$tab.redirectTo(`/pages/equipment/comManage?id=${this.id}`)
+									uni.navigateBack({
+										delta: 2
+									})
+								}, 1000)
+							}
+						})
+					} else {
+						this.$tab.navigateTo(
+							`/pages/equipment/addComList?storeName=${this.storeName}&type=2&id=${this.id}`)
+					}
+				} else {
+					uni.$u.toast('请先选择商品!')
+				}
+
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.container {
+		.content {
+			padding-bottom: 88rpx;
+
+			.search {
+				padding: 24rpx 24rpx 12rpx;
+				background-color: #fff;
+				position: relative;
+
+				.scan-icon {
+					position: absolute;
+					right: 36rpx;
+					top: 38rpx;
+					z-index: 2;
+				}
+
+				.search-history {
+
+					.history-item {
+						margin-right: 24rpx;
+						padding: 0 12rpx;
+						background-color: #f2f2f2;
+						color: #333;
+						font-size: 24rpx;
+						line-height: 40rpx;
+						border-radius: 40rpx;
+						margin-top: 24rpx;
+					}
+				}
+			}
+
+			.list {
+				width: 100%;
+				padding: 12rpx 24rpx;
+
+				.thumb-box {
+					margin-bottom: 12rpx;
+					border-bottom: 1rpx solid #f4f4f4;
+					display: flex;
+					flex-flow: row nowrap;
+					padding: 12rpx 12rpx;
+					align-items: center;
+					background-color: #fff;
+					border-radius: 12rpx;
+				}
+
+				.select-img {
+					width: 40rpx;
+					height: 40rpx;
+				}
+
+				.check-content {
+					display: flex;
+					flex-direction: row;
+					align-items: center;
+					padding-left: 12rpx;
+
+					.comm-img {
+						width: 130rpx;
+						height: 130rpx;
+						display: flex;
+						flex-direction: row;
+						align-items: center;
+						justify-content: space-around;
+
+						image {
+							width: 100%;
+						}
+					}
+
+					.comm-main {
+						box-sizing: border-box;
+						padding-left: 18rpx;
+						color: #999;
+
+						>view {
+							padding: 4rpx 0;
+						}
+
+						>view:nth-child(1) {
+							font-size: 28rpx;
+							font-weight: bold;
+							color: #333;
+						}
+
+						>view:nth-child(2) {
+							font-size: 26rpx;
+						}
+
+						>view:nth-child(3) {
+							font-size: 26rpx;
+						}
+
+						>view:nth-child(4) {
+							font-size: 26rpx;
+
+							text {
+								font-weight: bold;
+								color: red;
+							}
+						}
+					}
+				}
+			}
+		}
+
+		.btn {
+			width: 100%;
+			position: fixed;
+			left: 0;
+			bottom: 24rpx;
+			padding: 0 24rpx;
+		}
+
+		.empty {
+			position: absolute;
+			left: 50%;
+			top: 50%;
+			transform: translate(-50%, -50%);
+		}
+	}
+</style>

+ 223 - 0
pages/commodity/application.vue

@@ -0,0 +1,223 @@
+<template>
+	<view class="container">
+		<u-navbar leftIconColor="#fff" titleStyle="color:#fff;fontSize:36rpx;" :autoBack="true" bgColor="#2C6FF3"
+			:placeholder="true" :title="modeName"></u-navbar>
+		<view class="content">
+			<view class="search">
+				<u-search :animation="true" searchIcon="search" placeholder="输入设备名称" v-model="keyword"
+					:showAction="false" @search="search"></u-search>
+			</view>
+			<view class="list" v-if="deviceList&&deviceList.length>0">
+				<view class="thumb-box" v-for="(item, index) in deviceList" :key="item.deviceId"
+					@click.stop="commItemSelect(item)">
+					<view>
+						<image class="select-img"
+							src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/commodity/selected.png"
+							mode="widthFix" v-show="item.checked"></image>
+						<image class="select-img"
+							src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/commodity/select.png"
+							mode="widthFix" v-show="!item.checked"></image>
+					</view>
+					<view class="check-content">
+						<!-- <view class="comm-img">
+							<u--image width="130rpx" height="130rpx" :src="item.cover" mode="widthFix"
+								:lazy-load="true"></u--image>
+						</view> -->
+						<view class="comm-main">
+							<view>
+								{{item.deviceName}}
+							</view>
+							<view>
+								状态:{{item.deviceStateLName}}
+							</view>
+							<view>
+								状态:{{item.deviceStateLName}}
+							</view>
+						</view>
+					</view>
+				</view>
+
+				<u-loadmore :status="status" v-if="deviceList.length>=1" />
+			</view>
+
+			<view class="empty" v-else>
+				<u-empty></u-empty>
+			</view>
+		</view>
+
+		<view class='btn safe-bottom'>
+			<xbutton size='large' width='100%' @tap='applyToDevice'>确定应用到设备</xbutton>
+		</view>
+	</view>
+</template>
+
+<script>
+	import {
+		searchPage
+	} from '@/api/device/device.js'
+	
+	import {
+		saveGoodsDevice,
+	} from "@/api/commoditylist/commoditylist.js"
+
+	export default {
+		data() {
+			return {
+				keyword: '',
+				deviceList: [],
+				page: 1,
+				size: 10,
+				modelId:null,
+				modeName:null
+			}
+		},
+		onLoad(o) {
+			if (o.modelId) {
+				this.modelId=modelId
+				this.modeName=modeName
+			}
+		},
+
+		onShow() {
+			this.getList()
+		},
+
+		methods: {
+			search(val) {
+				this.reset()
+				this.getList()
+			},
+			// 商品选中状态改变
+			commItemSelect(e) {
+				e.checked = !e.checked;
+			},
+
+			reset() {
+				this.status == 'loadmore'
+				this.page = 1;
+				this.deviceList = [];
+			},
+
+			getList() {
+				searchPage({
+					page: {
+						current: this.page,
+						size: this.size
+					},
+					keywords: this.keyword,
+				}).then(res => {
+					let data = res.data.records;
+					for (let i = 0; i < data.length; i++) {
+						let item = data[i];
+						item.checked = false;
+					}
+					if (data.length < 10) {
+						this.status = "nomore"
+					} else {
+						this.status = "loadmore"
+					}
+					this.deviceList = this.deviceList.concat(data)
+				})
+			},
+
+			onReachBottom() {
+				if (this.status == 'nomore') return
+				this.page++
+				this.getList()
+			},
+
+			applyToDevice() {
+				saveGoodsDevice({
+					"deviceIds": [], //设备信息id
+					"mercId": 0, //商户ID
+					"modelId": 0 //商品清单ID
+				}).then(res => {})
+			},
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.container {
+		.content {
+			padding-bottom: 88rpx;
+
+			.search {
+				padding: 24rpx 24rpx;
+				background-color: #fff;
+			}
+
+			.list {
+				width: 100%;
+				padding: 12rpx 24rpx;
+
+				.thumb-box {
+					margin-bottom: 12rpx;
+					border-bottom: 1rpx solid #f4f4f4;
+					display: flex;
+					flex-flow: row nowrap;
+					padding: 12rpx 12rpx;
+					align-items: center;
+					background-color: #fff;
+					border-radius: 12rpx;
+				}
+
+				.select-img {
+					width: 40rpx;
+					height: 40rpx;
+				}
+
+				.check-content {
+					width: 100%;
+					display: flex;
+					flex-direction: row;
+					align-items: center;
+					padding-left: 12rpx;
+					position: relative;
+
+					.comm-img {
+						width: 130rpx;
+						height: 130rpx;
+						display: flex;
+						flex-direction: row;
+						align-items: center;
+						justify-content: space-around;
+
+						image {
+							width: 100%;
+						}
+					}
+
+					.comm-main {
+						box-sizing: border-box;
+						padding-left: 18rpx;
+						color: #999;
+
+						>view {
+							padding: 8rpx 0;
+							width: 330rpx;
+							font-size: 28rpx;
+						}
+
+					}
+				}
+			}
+		}
+
+		.empty {
+			position: absolute;
+			left: 50%;
+			top: 50%;
+			transform: translate(-50%, -50%);
+		}
+
+		.btn {
+			width: 724rpx;
+			position: fixed;
+			left: 13rpx;
+			bottom: calc(24rpx + env(safe-area-inset-bottom) / 2);
+			border-radius: 88rpx;
+			overflow: hidden;
+		}
+	}
+</style>

+ 273 - 0
pages/commodity/auditList.vue

@@ -0,0 +1,273 @@
+<template>
+	<view class="container">
+		<u-navbar leftIconColor="#fff" titleStyle="color:#fff;fontSize:36rpx;" :autoBack="true" bgColor="#2C6FF3"
+			:placeholder="true" title="商品建模"></u-navbar>
+		<view class="content">
+			<view class="search">
+				<u-search animation placeholder="商品搜索" :clearabled="false" v-model="keyword" :showAction="false"
+					@search="search"></u-search>
+				<view class="scan-icon" @click="scan">
+					<u-icon name="scan" size="22" color="#909399"></u-icon>
+				</view>
+			</view>
+			<view class="tab-list">
+				<u-tabs :list="tabList" :scrollable="false" :current="pos" @click="tabClick" lineColor="#2C6FF3">
+				</u-tabs>
+			</view>
+			<view class="list">
+				<u-list @scrolltolower="scrolltolower" :height="fullHeight" v-if="commList.length>0">
+					<u-list-item class="list-item" v-for="(item, index) in commList" :key="item.id"
+						@click.native="goDetail(item)">
+						<view class="comm-item">
+							<view class="image">
+								<u--image width="130rpx" height="130rpx" :src="item.cover" mode="widthFix"
+									:lazy-load="true"></u--image>
+							</view>
+							<view class="item-content">
+								<view>
+									{{item.goodsName}}
+								</view>
+								<view>
+									{{item.brandName}}
+								</view>
+								<view>
+									{{item.createTime}}
+								</view>
+								<view class="failedMsg" v-if="item.status==-1">
+									驳回原因:{{item.failedMsg}}
+								</view>
+							</view>
+							<view class="tag">
+								<u-tag text="已通过" plain size="mini" type="success" v-if="item.status==1"></u-tag>
+								<u-tag text="已驳回" plain size="mini" type="error" v-if="item.status==-1"></u-tag>
+								<u-tag text="审核中" plain size="mini" type="warning" v-if="item.status==0"></u-tag>
+							</view>
+						</view>
+
+					</u-list-item>
+					<u-loadmore :status="status" v-if="commList.length>=1" />
+				</u-list>
+				<view class="empty" v-else>
+					<u-empty></u-empty>
+				</view>
+			</view>
+		</view>
+		<view class="btn safe-bottom">
+			<xbutton size="large" @click="$tab.navigateTo('/pages/commodity/addCom')">新品建模</xbutton>
+		</view>
+
+	</view>
+</template>
+
+<script>
+	import {
+		goodsPage
+	} from "@/api/commodity/goodsMode.js"
+	export default {
+		data() {
+			return {
+				keyword: '',
+				tabList: [{
+						name: '已通过',
+						id: 1
+					},
+					{
+						name: '审核中',
+						id: 0
+					},
+					{
+						name: '已驳回',
+						id: -1
+					}
+				],
+
+				page: 1,
+				size: 10,
+				keywords: '',
+
+				pos: 1,
+				current: 0,
+				status: 'loadmore',
+				commList: [],
+				fullHeight: 0
+			}
+		},
+		onLoad() {
+			let _this = this;
+			const query = uni.createSelectorQuery().in(this);
+			query.select(".list").boundingClientRect((data) => {
+				uni.getSystemInfo({
+					success(res) {
+						_this.fullHeight = res.windowHeight-218 + 'px';
+					},
+				});
+			}).exec();
+			this.getList()
+		},
+		methods: {
+			tabClick(e) {
+				console.log(e)
+				this.pos = e.index;
+				this.current = e.id;
+				this.reset();
+				this.getList();
+			},
+
+			reset() {
+				this.status == 'loadmore'
+				this.page = 1;
+				this.size = 10;
+				this.commList = [];
+			},
+
+			getList() {
+				goodsPage({
+					page: {
+						current: this.page,
+						size: this.size
+					},
+					keywords: this.keyword,
+					status: this.current
+				}).then(res => {
+					let data = res.data.records;
+					if (data.length < 10) {
+						this.status = "nomore"
+					} else {
+						this.status = "loadmore"
+					}
+					this.commList = this.commList.concat(data)
+				})
+			},
+
+			scrolltolower() {
+				if (this.status == 'nomore') return
+				this.page++
+				this.getList()
+			},
+
+			search(val) {
+				this.reset()
+				this.getList()
+			},
+
+			scan() {
+				uni.scanCode({
+					success: function(res) {
+						console.log('条码类型:' + res.scanType);
+						console.log('条码内容:' + res.result);
+					}
+				});
+			},
+
+			// 详情
+			goDetail(e) {
+				this.$tab.navigateTo('/pages/commodity/addCom?id=' + e.id)
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	page {
+		height: 100% !important;
+	}
+
+	.container {
+		height: 100% !important;
+
+		.content {
+			// padding-bottom: 88rpx;
+
+			.search {
+				padding: 24rpx 24rpx;
+				background-color: #fff;
+				position: relative;
+
+				.scan-icon {
+					position: absolute;
+					right: 36rpx;
+					top: 50%;
+					transform: translateY(-50%);
+					z-index: 2;
+				}
+			}
+
+			.tab-list {
+				width: 100%;
+				padding: 0 24rpx;
+				background-color: #fff;
+				margin-bottosearchm: 12rpx;
+			}
+
+			.empty {
+				margin: 40% auto 0;
+			}
+
+			.list {
+				width: 100%;
+				padding: 0 24rpx;
+				margin-top: 16rpx;
+
+				.comm-item {
+					display: flex;
+					flex-direction: row;
+					justify-content: flex-start;
+					align-items: center;
+					background-color: #fff;
+					margin-bottom: 12rpx;
+					border-radius: 12rpx;
+					box-sizing: border-box;
+					padding: 12rpx;
+					position: relative;
+
+					.image {
+						width: 130rpx;
+						height: 130rpx;
+					}
+
+					.item-content {
+						padding-left: 24rpx;
+						color: #999;
+
+						>view:nth-child(1) {
+							font-size: 28rpx;
+							font-weight: bold;
+							color: #333;
+
+						}
+
+						>view:nth-child(2) {
+							font-size: 24rpx;
+							padding: 12rpx 0;
+						}
+
+						>view:nth-child(3) {
+							font-size: 24rpx;
+						}
+						
+						.failedMsg{
+							color: red;
+							font-size: 24rpx;
+							line-height: 30rpx;
+							margin-top: 10rpx;
+						}
+					}
+
+					.tag {
+						position: absolute;
+						right: 12rpx;
+						top: 12rpx;
+					}
+				}
+			}
+		}
+
+		.btn {
+			width: 100%;
+			position: fixed;
+			left: 0;
+			bottom: 24rpx;
+			padding: 0 24rpx;
+		}
+	}
+</style>

+ 300 - 0
pages/commodity/comEdit.vue

@@ -0,0 +1,300 @@
+<template>
+	<view class="container">
+		<u-navbar leftIconColor="#fff" titleStyle="color:#fff;fontSize:36rpx;" :autoBack="true" bgColor="#2C6FF3"
+			:placeholder="true" title="商品编辑"></u-navbar>
+
+		<swiper class="card-swiper round-dot" previous-margin="1rpx" :indicator-dots="false" :circular="true"
+			:autoplay="true" interval="5000" duration="500" @change="cardSwiper" indicator-color="#ffffff"
+			indicator-active-color="#ffffff" style="margin-top: 24rpx;">
+			<swiper-item v-for="(item,index) in swiperList" :key="index" :class="cardCur == index ? 'cur':''">
+				<view class="swiper-item shadow-shop" style="border-radius: 20rpx 20rpx 22rpx 22rpx;">
+					<image :src="item" mode="aspectFill"></image>
+				</view>
+			</swiper-item>
+		</swiper>
+		<view class="content">
+			<view class="xy-card">
+				<view class="xy-list-item">
+					<view>商品名称</view>
+					<view>{{comDetail.goodsName}}</view>
+				</view>
+				<!-- <view class="xy-list-item">
+					<view>可用机型</view>
+					<view>{{comDetail.name}}</view>
+				</view> -->
+				<view class="xy-list-item">
+					<view>商品条码</view>
+					<view>{{comDetail.goodsBarcode}}</view>
+				</view>
+				<view class="xy-list-item">
+					<view>商品尺寸</view>
+					<view>{{comDetail.capacity}}</view>
+				</view>
+				<view class="xy-list-item">
+					<view>所属分类</view>
+					<view>{{comDetail.categoryCodeName||'其他'}}</view>
+				</view>
+			</view>
+			<view class="xy-card">
+				<u--form labelPosition="left" :model="form" :rules="rules" ref="form" errorType="toast">
+					<u-form-item required labelWidth="90" label="进货价(元)" prop="priceCost" borderBottom ref="item1">
+						<u--input type="digit" v-model="form.priceCost" border="none"></u--input>
+					</u-form-item>
+					<u-form-item required labelWidth="90" label="零售价(元)" prop="price" borderBottom ref="item1">
+						<u--input type="digit" v-model="form.price" border="none"></u--input>
+					</u-form-item>
+					<!-- <u-form-item required labelWidth="90" label="单机容量" prop="capacity" borderBottom ref="item1">
+						<u--input type="number" v-model="form.capacity" border="none"></u--input>
+					</u-form-item> -->
+					<u-form-item required labelWidth="90" label="库存预警" prop="warning" borderBottom ref="item1">
+						<u--input type="number" v-model="form.warning" border="none"></u--input>
+					</u-form-item>
+					<u-form-item required labelWidth="90" label="上架状态" prop="warning" borderBottom ref="item1">
+						<u-switch v-model="form.status"></u-switch>
+					</u-form-item>
+				</u--form>
+			</view>
+		</view>
+
+		<view class="btn safe-bottom">
+			<xbutton delay="1500" size="large" @click='submit'>保存</xbutton>
+		</view>
+	</view>
+</template>
+
+<script>
+	import {
+		searchObj,
+		update
+	} from "@/api/commodity/mercGoods.js"
+	export default {
+		data() {
+			return {
+				cardCur: 0,
+				form: {
+					price: undefined,
+					warning: undefined,
+					capacity: undefined,
+					priceCost: undefined,
+					status: true //上架状态
+				},
+				rules: {
+					'price': {
+						type: 'number',
+						required: true,
+						message: '请输入商品价格~',
+						trigger: ['blur', 'change']
+					},
+					'warning': {
+						type: 'number',
+						required: true,
+						message: '请输入预警值~',
+						trigger: ['blur', 'change']
+					},
+					// 'capacity': {
+					// 	type: 'number',
+					// 	required: true,
+					// 	message: '请输入容量~',
+					// 	trigger: ['blur', 'change']
+					// },
+					'priceCost': {
+						type: 'number',
+						required: true,
+						message: '请输入商品进货价~',
+						trigger: ['blur', 'change']
+					},
+				},
+				show: false,
+				actions: [{
+						name: '全部',
+					},
+					{
+						name: '饮料',
+					},
+					{
+						name: '槟榔',
+					},
+				],
+
+				radio: '',
+				switchVal: false,
+
+				comDetail: null, //商品详情
+				id: null,
+
+
+			};
+		},
+
+		computed: {
+			swiperList() {
+				let imgArr = []
+				if (this.comDetail) {
+					imgArr = this.comDetail.imgUrls.split('|')
+				}
+				return imgArr
+			}
+		},
+
+		onLoad(o) {
+			this.id = o.id;
+			this.getDetail(o.id)
+		},
+
+		methods: {
+			getDetail(id) {
+				searchObj({
+					id: id
+				}).then(res => {
+					this.comDetail = res.data
+					this.form = {
+						"price": Number(res.data.price) / 100,
+						"warning": res.data.warning,
+						"capacity": res.data.capacity,
+						"priceCost": Number(res.data.priceCost) / 100,
+						"status": res.data.status == '1' ? true : false
+					}
+				})
+			},
+
+			cardSwiper(e) {
+				this.cardCur = e.detail.current
+			},
+			// 下拉框点击事件
+			showSelect(type) {
+				if (type == 'class') {
+					this.actions = [{
+							name: '全部',
+						},
+						{
+							name: '饮料',
+						},
+						{
+							name: '槟榔',
+						}
+					]
+				} else {
+					this.actions = [{
+							name: '大瓶',
+						},
+						{
+							name: '中瓶',
+						},
+						{
+							name: '小瓶',
+						}
+					]
+				}
+				this.show = true;
+			},
+
+			//表单提交
+			submit() {
+				this.$refs.form.validate().then(res => {
+					this.form.id = this.id;
+					let params = {
+						id: this.id,
+						price: this.form.price * 100,
+						warning: this.form.warning,
+						capacity: this.form.capacity,
+						priceCost: this.form.priceCost * 100,
+						status: this.form.status ? '1' : '0'
+					}
+					update(params).then(res => {
+						this.$modal.msg('保存成功~')
+						setTimeout(() => {
+							this.$tab.navigateBack()
+						}, 1000)
+					})
+				}).catch(errors => {})
+			}
+		},
+		onReady() {
+			setTimeout(() => {
+				this.$refs.form.setRules(this.rules)
+			}, 500)
+		},
+	};
+</script>
+
+<style scoped lang="scss">
+	.container {
+
+		.card-swiper {
+			height: 350rpx !important;
+		}
+
+		.card-swiper swiper-item {
+			width: 260upx !important;
+			left: 245upx;
+			box-sizing: border-box;
+			padding: 0upx 15upx 50upx 15upx;
+			overflow: initial;
+			/* margin: 100rpx 0; */
+		}
+
+		.card-swiper swiper-item .swiper-item {
+			width: 100%;
+			display: block;
+			height: 100%;
+			border-radius: 6upx;
+			transform: scale(0.7);
+			transition: all 0.2s ease-in 0s;
+			overflow: hidden;
+		}
+
+		.card-swiper swiper-item.cur .swiper-item {
+			transform: none;
+			transition: all 0.2s ease-in 0s;
+		}
+
+		.content {
+			padding: 24rpx 24rpx 100rpx;
+
+			.example-img {
+				padding: 24rpx 24rpx 12rpx;
+				display: flex;
+				flex-flow: row nowrap;
+				justify-content: space-between;
+
+				>image {
+					width: 100rpx;
+				}
+			}
+
+			.img-upload {
+				padding: 24rpx 24rpx 12rpx;
+			}
+
+			.xy-card {
+				margin-bottom: 24rpx;
+			}
+
+			.xy-list-item {
+				display: flex;
+				flex-flow: row nowrap;
+				justify-content: flex-start;
+				border-bottom: 1upx solid #eee;
+				line-height: 80rpx;
+
+				>view:nth-child(1) {
+					width: 180rpx;
+				}
+
+				>view:nth-child(2) {
+					color: #666;
+					font-size: 24rpx;
+				}
+			}
+		}
+
+		.btn {
+			width: 100%;
+			position: fixed;
+			left: 0;
+			bottom: 24rpx;
+			padding: 0 24rpx;
+			z-index: 999;
+		}
+	}
+</style>

+ 523 - 0
pages/commodity/comListEdit.vue

@@ -0,0 +1,523 @@
+<template>
+	<view class="container">
+		<u-navbar leftIconColor="#fff" titleStyle="color:#fff;fontSize:36rpx;" :autoBack="true" bgColor="#2C6FF3"
+			:placeholder="true" :title="title"></u-navbar>
+		<view class="flex align-center justify-around margintop">
+			<view class="">共关联({{comList.length}})件商品 </view>
+			<view class="color">点击图片可以移除</view>
+			<view class="">
+				<xbutton size="size" @tap='save'>保存</xbutton>
+			</view>
+		</view>
+		<view class="margintop">
+			<view style="height: 230rpx;overflow: hidden;" v-if="comList&&comList.length>0">
+				<u-scroll-list :indicator="indicator" indicatorColor="#fff0f0" indicatorActiveColor="#f56c6c">
+					<view class="flex justify-around">
+						<view class="" v-for="(item,index) in comList" :key="item.id">
+							<view class="flex flex-direction align-center justify-center image-dele-container"
+								@tap="clean(item)">
+								<view class="image">
+									<u--image radius="4" width="120rpx" height="120rpx" :src="item.img"
+										mode="aspectFit" :lazy-lord="true"></u--image>
+								</view>
+								<view class="">{{item.name}}</view>
+							</view>
+						</view>
+					</view>
+				</u-scroll-list>
+			</view>
+			<view class="no-com" v-else  style="height: 230rpx;overflow: hidden;">
+				请添加商品
+			</view>
+		</view>
+		<view class="search" @click="searchComm">
+			<view class="search-input">
+				<u-search placeholder="商品搜索" actionText="取消" :actionStyle="{color:'#2C6FF3'}" :showAction="!leftShow"
+					:clearabled="false" v-model="keyword" @search="search" @custom="cancle"></u-search>
+				<view @click="scan" :class="[leftShow?'scan-icon scan-left-show':'scan-icon scan-left-hidden']">
+					<u-icon name="scan" size="22" color="#909399"></u-icon>
+				</view>
+			</view>
+
+			<view class="search-history flex flex-wrap flex-start" v-if="!leftShow">
+				<view class="history-item" v-for="(item,index) in historyList" :key="index" @click="searchFast(item)">
+					{{item}}
+				</view>
+			</view>
+		</view>
+		<view class="content">
+			<view class="swiperitem-content">
+				<view class="classify-wrap">
+					<Classify :tabList="perTabList" :status="perStatus"
+						:commList="perCommList" @switchMenu="perSwitchMenu" @lowerBottom="perLowerBottom"
+						:isModal="true" :height="fullHeight" @comClick="comClick" :leftShow="leftShow" />
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import Classify from "@/components/classify/index.vue"
+	import {
+		goodsCategory as perGoodsCategory,
+		ownerGoodsList as pageByGoodsMerc
+	} from "@/api/commodity/mercGoods.js"
+	import {
+		goodsCategory as pubGoodsCategory,
+		ownerGoodsList as pageByGoods
+	} from "@/api/commodity/goods.js"
+	import {
+		saveListingGoods,
+		refGoods
+	} from "@/api/commoditylist/commoditylist.js"
+	export default {
+		components: {
+			Classify
+		},
+		data() {
+			return {
+				modelId: '', //清单id
+
+				goodsId: [], //商品id列表
+
+				fullHeight: "0",
+
+				// 私库
+				perTabList: [], //商品类目
+				perCommList: [], //商品列表
+				perPage: 1, //商品分页
+				perSize: 10,
+				perStatus: 'loadmore', //加载更多
+
+				id: null, //设备id
+				categoryCode: null,
+
+				leftShow: true,
+				historyList: [],
+				keyword: '',
+				
+				oldList:[],
+				newList:[],
+				
+				title:null,
+				deviceId:null,
+			}
+		},
+		async onLoad(o) {
+			let _this = this;
+			const query = uni.createSelectorQuery().in(this);
+			query.select(".classify-wrap").boundingClientRect((data) => {
+				_this.top = data.top;
+				uni.getSystemInfo({
+					success(res) {
+						// 针对iPhone X等机型底部安全距离做适配
+						const model = res.model;
+						const modelInclude = [
+							"iPhone X",
+							'iPhone XR',
+							"iPhone XS",
+							"iPhone XS MAX",
+							"iPhone 12/13 mini",
+							"iPhone 12/13 (Pro)",
+							"iPhone 12/13 Pro Max",
+							"iPhone 14 Pro Max"
+						];
+						let safeDistance = modelInclude.includes(model)
+						//动态设置商品区域高度
+						if (safeDistance) {
+							_this.fullHeight = res.windowHeight - _this.top - 34 + 'px';
+						} else {
+							_this.fullHeight = res.windowHeight - _this.top + 'px';
+						}
+					},
+				});
+			}).exec();
+
+			this.title = o.title
+			this.modelId = o.modelId
+			this.deviceId=o.deviceId
+			if (uni.getStorageSync('goods')) {
+				this.historyList = JSON.parse(uni.getStorageSync('goods'))
+			}
+
+			await this.getrefGoods()
+			this.getPerCategory()
+		},
+
+		async onShow() {
+			this.perReset()
+			await this.getPerCommList()
+		},
+		
+		computed:{
+			comList(){
+				let list=[]
+				list=[...this.oldList,...this.newList]
+				return list
+			},
+		},
+
+		methods: {
+			searchComm() {
+				this.leftShow = false
+			},
+
+			search(val) {
+				this.saveKeyWord('goods', this.keyword)
+				this.perReset();
+				this.getPerCommList()
+			},
+
+			cancle(val) {
+				this.keyword = ''
+				this.leftShow = true
+				this.search()
+			},
+
+			saveKeyWord(type, val) {
+				if (val) {
+					let arr = []
+					if (uni.getStorageSync(type)) {
+						let arr = JSON.parse(uni.getStorageSync(type))
+						if (arr.indexOf(val) != -1) {
+							console.log('arr.indexOf(val)', arr.indexOf(val))
+							arr.splice(arr.indexOf(val), 1)
+						}
+						arr.unshift(val)
+						if (arr.length > 6) {
+							arr.pop()
+						}
+						this.historyList = JSON.parse(JSON.stringify(arr))
+						uni.setStorageSync(type, JSON.stringify(arr))
+					} else {
+						arr.unshift(val)
+						this.historyList = JSON.parse(JSON.stringify(arr))
+						uni.setStorageSync(type, JSON.stringify(arr))
+					}
+				} else {
+					return
+				}
+			},
+
+			searchFast(e) {
+				this.keyword = e
+				this.search()
+			},
+
+			//扫码
+			scan() {
+				uni.scanCode({
+					success: (res) => {
+						this.keyword = res.result;
+						this.search()
+					}
+				});
+			},
+
+			//获取类目列表
+			getPerCategory() {
+				return new Promise((resolve, reject) => {
+					perGoodsCategory().then(res => {
+						if (res.data && res.data.length > 0) {
+							this.perTabList = res.data.map(i => {
+								if (i.categoryCode == null) {
+									i.categoryName = '未定义'
+									return i
+								} else {
+									return i
+								}
+							});
+							if (this.perTabList && this.perTabList.length > 0) {
+								this.categoryCode = this.perTabList[0].categoryCode
+							}
+						}
+						resolve(res)
+					}).catch(err => {
+						reject(err)
+					})
+				})
+			},
+
+			//商品类目切换
+			perSwitchMenu(item) {
+				this.categoryCode = item.categoryCode
+				this.perReset()
+				this.getPerCommList()
+			},
+
+			//根据类目获取商品列表
+			getPerCommList() {
+				let params = {}
+				if (this.leftShow) { //搜索
+					params = {
+						categoryCode: this.categoryCode,
+						page: {
+							current: this.perPage,
+							size: this.perSize
+						},
+					}
+				} else { //非搜索
+					params = {
+						page: {
+							current: this.perPage,
+							size: this.perSize
+						},
+						keyword: this.keyword
+					}
+				}
+				return new Promise((resolve, reject) => {
+					pageByGoodsMerc(params).then(res => {
+						let data = res.data.records;
+						if (data && data.length > 0) {
+							data = data.map(i => {
+								i.name = i.goodsName;
+								i.barcode = i.goodsBarcode;
+								i.cover = i.goodsCover;
+								i.price = i.price != null ? Number(i.price) / 100 : null;
+								i.categoryName = i.categoryCodeName;
+								i.noSelect = i.isBind;
+								return i
+							})
+						}
+						if (data.length < 10) {
+							this.perStatus = "nomore"
+						} else {
+							this.perStatus = "loadmore"
+						}
+						this.perCommList = this.perCommList.concat(data)
+
+						resolve(res)
+					}).catch(err => {
+						reject(err)
+					})
+				})
+			},
+
+			//触底加载更多
+			perLowerBottom() {
+				if (this.perStatus == 'nomore') return
+				this.perPage++
+				this.getPerCommList()
+			},
+
+			//重置
+			perReset() {
+				this.perStatus == 'loadmore'
+				this.perPage = 1;
+				this.perSize = 10;
+				this.perCommList = [];
+			},
+			
+			//反显清单图片
+			getrefGoods() {
+				return new Promise((resolve, reject) => {
+					refGoods({
+						modelId: this.modelId
+					}).then(res => {
+						res.data.forEach(item => {
+							this.oldList.push({
+								name: item.goodsName,
+								id: item.goodsId,
+								img:item.goodsImgUrl
+							})
+						})
+						resolve(res)
+					}).catch(err => {
+						reject(err)
+					})
+				})
+			},
+			
+			//点击商品
+			comClick(e) {
+				if(e.price==null){
+					uni.showModal({
+						title: '提示',
+						content: '当前商品为新商品需先设置商品价格,是否前往设置?',
+						success: res => {
+							if (res.confirm) {
+								this.$tab.navigateTo('/pages/commodity/comEdit?id=' + e.id)
+							} else if (res.cancel) {
+								console.log('用户点击取消');
+							}
+						}
+					});
+				}else{
+					this.addToList(e)
+				}
+			},
+			
+			//添加商品至关联
+			addToList(e){
+				if(this.comList.some(i=>i.id==e.goodsId)){
+					this.$modal.msg('该商品已存在~')
+					return
+				}
+				this.newList.push({
+					name: e.goodsName,
+					id: e.goodsId,
+					img:e.imgUrls
+				})
+			},
+			
+			clean(e){
+				if(e.name=='未知商品'||e.name=='非友好购物商品'){
+					this.$modal.msg('预设商品不能删除!!!')
+					return
+				}
+				
+				for (let i = 0; i < this.oldList.length; i++) {
+					let item=this.oldList[i]
+					if(item.id==e.id){
+						this.oldList.splice(i,1)
+						return
+					}
+				}
+				
+				for (let i = 0; i < this.newList.length; i++) {
+					let item=this.newList[i]
+					if(item.id==e.id){
+						this.newList.splice(i,1)
+						return
+					}
+				}
+				
+			},
+			
+			save() {
+				if (!this.comList.length) {
+					uni.$u.toast('请选择商品')
+					return;
+				}
+				var comList = []
+				this.comList.forEach(item => {
+					comList.push(item.id)
+				})
+				saveListingGoods({
+					modelId: this.modelId, //清单ID
+					goodsId: comList //关联的商品id,不传则表示清空
+				}).then(res => {
+					if (res.code == 200) {
+						uni.$u.toast(res.msg)
+						setTimeout(() => {
+							this.$tab.navigateBack()
+						}, 1000)
+					}
+				})
+			},
+		}
+	}
+</script>
+<style lang="scss" scoped>
+	.container {
+		.margin {
+			margin: 10rpx 20rpx;
+		}
+
+		.margintop {
+			margin-top: 10rpx;
+		}
+
+		.marginleft {
+			margin-left: 10rpx;
+		}
+
+		.active {
+			background-color: #ffffff;
+		}
+
+		.un-active {
+			background-color: #e5e5e5;
+		}
+
+		.color {
+			color: red;
+		}
+
+		.image-dele-container {
+			position: relative;
+			width: 200rpx;
+			height: 200rpx;
+			margin-left: 10rpx;
+			background-color: #f6f6f6;
+			border-radius: 15rpx;
+
+			view {
+				text-overflow: ellipsis;
+				overflow: hidden;
+				white-space: nowrap;
+				width: 120rpx;
+			}
+
+			.image {
+				width: 120rpx;
+				height: 120rpx;
+			}
+		}
+
+		.image-container {
+			width: 150rpx;
+			height: 200rpx;
+			background-color: #d9d9d9;
+
+			image {
+				width: 90rpx;
+				height: 100rpx;
+			}
+		}
+		
+		.no-com{
+			height:228rpx;
+			text-align: center;
+			line-height: 228rpx;
+		}
+
+		.search {
+			padding: 24rpx 24rpx;
+			background-color: #fff;
+
+			.search-input {
+				position: relative;
+
+				.scan-icon {
+					position: absolute;
+					top: 50%;
+					transform: translateY(-50%);
+					z-index: 2;
+
+					&.scan-left-show {
+						right: 36rpx;
+					}
+
+					&.scan-left-hidden {
+						right: 100rpx;
+					}
+				}
+			}
+
+			.search-history {
+
+				.history-item {
+					margin-right: 24rpx;
+					padding: 0 12rpx;
+					background-color: #f2f2f2;
+					color: #333;
+					font-size: 24rpx;
+					line-height: 40rpx;
+					border-radius: 40rpx;
+					margin-top: 24rpx;
+				}
+			}
+		}
+
+		.btn {
+			width: 724rpx;
+			position: fixed;
+			left: 13rpx;
+			bottom: calc(24rpx + env(safe-area-inset-bottom) / 2);
+			border-radius: 88rpx;
+			overflow: hidden;
+		}
+	}
+</style>

+ 359 - 0
pages/commodity/commoditylist.vue

@@ -0,0 +1,359 @@
+<template>
+	<view class="container">
+		<u-navbar leftIconColor="#fff" titleStyle="color:#fff;fontSize:36rpx;" :autoBack="true" bgColor="#2C6FF3"
+			:placeholder="true" :title="deviceName"></u-navbar>
+		<view class="title">常用商品清单</view>
+		<scroll-view class="scrollview" scroll-y="true" scroll-with-animation="true" lower-threshold="100"
+			@scrolltolower="loadMore" :style="{height:fullHeight}">
+			<view class="scroll-content" v-if="commodityList&&commodityList.length>0">
+				<block v-for="(item,index) in commodityList" :key="item.modelId">
+					<view class="commoditylist-container xy-card">
+						<view class="commoditylist-content flex flex-direction justify-between">
+							<view class="flex-sub">清单名称:{{item.name}}</view>
+							<view class="flex-sub">设备类型:{{item.deviceTypeName}}</view>
+							<view class="flex-sub">关联商品:{{item.goodsNum}}件</view>
+						</view>
+						<view class="flex align-center justify-end btn-box martop">
+							<view class='marleft'>
+								<xbutton size="medium" bgColor="red" round='25rpx' padding='0rpx 20rpx' @click='deleted(item)'>删除</xbutton>
+							</view>
+							<view class='marleft'>
+								<xbutton size="medium" round='25rpx' padding='0rpx 20rpx' @click='modify(item)'>修改</xbutton>
+							</view>
+							<view class='marleft'>
+								<xbutton size="medium" round='25rpx' padding='0rpx 20rpx' @click="relatedgoods(item)">关联商品
+								</xbutton>
+							</view>
+							<view class='marleft'>
+								<xbutton size="medium" round='25rpx' padding='0rpx 20rpx' @click="application(item)">应用到设备</xbutton>
+							</view>
+						</view>
+					</view>
+				</block>
+				<u-loadmore :status="loadmoreStatus" />
+			</view>
+
+			<view style="margin-top: 120rpx;" v-else>
+				<u-empty mode="data" text="数据为空"></u-empty>
+			</view>
+		</scroll-view>
+
+		<view class="btn safe-bottom">
+			<xbutton size='large' width='100%' @click="addcommoditylist">新增商品清单</xbutton>
+		</view>
+
+		<xpopup :show="addcommoditylistShow" @open="open" @close="cancel" @confirm="submit" :title="popTitle">
+			<view class="addcommoditylist-container">
+
+			<!-- 	<u--input class='martop' clearable suffixIcon="arrow-down" v-model="equipmentTypename"
+					suffixIconStyle="color: #909399" placeholder="设备类型" border="surround" @focus="actionSheetShow=true">
+				</u--input> -->
+				<view class='martop'>
+					<u--input placeholder="请输入商品清单名称" v-model="name" border="surround"></u--input>
+				</view>
+			</view>
+		</xpopup>
+
+		<!-- <u-action-sheet :show="actionSheetShow" :actions="equipmentTypeList" title="请选择设备类型"
+			@close="actionSheetShow = false" @select="actionsheetSelect($event)"></u-action-sheet> -->
+	</view>
+</template>
+
+<script>
+	import request from '@/utils/request'
+	import {
+		page,
+		save,
+		update,
+		saveGoodsDevice,
+		del
+	} from "@/api/commoditylist/commoditylist.js"
+	import {
+		detail
+	} from "@/api/device/device.js"
+	export default {
+		data() {
+			return {
+				fullHeight: 0,
+				commodityList: [], //商品清单
+				name: '',
+				loadmoreStatus: 'loadmore',
+				popTitle: '新增清单',
+				type: '',
+				equipmentTypename: '',
+				page: 1, //当前分页
+				size: 10, //分页数据条数
+				deviceType: '',
+				actionSheetShow: false,
+				addcommoditylistShow: false,
+				
+				deviceId:null,
+				deviceName:null,
+				
+				equipmentTypeList:[]
+			}
+		},
+
+		onLoad(o) {
+			let _this = this;
+			const query = uni.createSelectorQuery().in(this);
+			query.select(".scrollview").boundingClientRect((data) => {
+				uni.getSystemInfo({
+					success(res) {
+						// 针对iPhone X等机型底部安全距离做适配
+						const model = res.model;
+						const modelInclude = ["iPhone X", 'iPhone XR', "iPhone XS", "iPhone XS MAX",
+							"iPhone XS Max"
+						];
+						let safeDistance = modelInclude.includes(model)
+						//动态设置商品区域高度
+						console.log(res.windowHeight, data.top)
+						if (safeDistance) {
+							_this.fullHeight = res.windowHeight - data.top - 40 + 'px';
+						} else {
+							_this.fullHeight = res.windowHeight - data.top + 'px';
+						}
+					},
+				});
+			}).exec();
+			this.deviceId=o.id
+		},
+
+		async onShow() {
+			await this.getDeviceType()
+			this.reset()
+			this.getpage()
+		},
+		methods: {
+			getDeviceType(){
+				return new Promise((resolve,reject)=>{
+					detail({
+						deviceId: this.deviceId,
+						isSysinfo: false,
+						isStatus: false,
+						isRegister: false
+					}).then(res => {
+						this.deviceType = res.data.deviceType
+						this.deviceName=res.data.deviceName
+						resolve(res)
+					}).catch(err=>{
+						resolve(err)
+					})
+				})
+			},
+			
+			//分页查询
+			getpage() {
+				page({
+					page: {
+						current: this.page,
+						size: this.size,
+					},
+					deviceType:this.deviceType
+				}).then(res => {
+					let dicts=res.data.dicts[0]
+					for (let key in dicts) {
+						let item=dicts[key]
+						this.equipmentTypeList.push(item)
+					}
+					let newList=res.data.records
+					newList.forEach(i=>{
+						this.equipmentTypeList.forEach(j=>{
+							if(i.deviceType==j.value){
+								i.deviceTypeName=j.msg
+							}
+						})
+					})
+					
+					this.commodityList = this.commodityList.concat(newList)
+					console.log(this.commodityList)
+					if (newList.length<10) {
+						this.loadmoreStatus = 'nomore'
+					} else {
+						this.loadmoreStatus = 'loadmore'
+					}
+				})
+			},
+			
+			reset(){
+				this.page=1
+				this.commodityList=[]
+			},
+			
+			loadMore(e) {
+				if (this.loadmoreStatus == 'nomore') return
+				this.page++
+				this.getpage()
+			},
+
+
+			open() {
+				this.addcommoditylistShow = true
+			},
+			
+			//新增商品清单
+			addcommoditylist() {
+				this.popTitle = '新增清单'
+				this.addcommoditylistShow = true
+			},
+			
+			actionsheetSelect(e) {
+				this.equipmentTypename = e.name
+				this.deviceType = e.deviceType
+			},
+			cancel() {
+				this.addcommoditylistShow = false
+			},
+			deleted(item) {
+				uni.showModal({
+					title: '提示',
+					content: '是否确认删除',
+					success: res=> {
+						if (res.confirm) {
+							del({
+								ids: [item.modelId]
+							}).then(res => {
+								if (res.code == 200) {
+									this.addcommoditylistShow = false
+									this.reset()
+									this.getpage()
+									setTimeout(()=>{
+										this.$modal.msg('删除成功~')
+									},500)
+								}
+							})
+						} else if (res.cancel) {
+							console.log('用户点击取消');
+						}
+					}
+				});
+			},
+			
+			modify(item) {
+				this.popTitle = '修改清单'
+				this.name = item.name
+				this.modelId=item.modelId
+				this.addcommoditylistShow = true
+			},
+			
+			// 新增商品清单
+			submit() {
+				if (!this.name) {
+					uni.$u.toast('请填写清单名称')
+					return;
+				}
+				if (this.popTitle == '新增清单') {
+					save({
+						name: this.name, //清单名称
+						deviceType: this.deviceType, //设备类型
+					}).then(res => {
+						if (res.code == 200) {
+							this.addcommoditylistShow = false
+							this.reset()
+							this.getpage()
+							setTimeout(()=>{
+								this.$modal.msg('新增成功~')
+							},500)
+						}
+					})
+				}
+				if (this.popTitle == '修改清单') {
+					update({
+						modelId: this.modelId, //id
+						name: this.name, //清单名称
+						deviceType: this.deviceType //设备类型
+					}).then(res => {
+						if (res.code == 200) {
+							this.addcommoditylistShow = false
+							this.reset()
+							this.getpage()
+							setTimeout(()=>{
+								this.$modal.msg('修改成功~')
+							},500)
+						}
+					})
+				}
+			
+			},
+			
+			//清单关联商品
+			relatedgoods(item) {
+				this.$tab.navigateTo(`comListEdit?modelId=${item.modelId}&title=${item.name}`)
+			},
+			
+			//清单应用到设备
+			application(item) {
+				uni.showModal({
+					title: '提示',
+					content: `是否确认应用清单至设备${this.deviceName}`,
+					success: res=> {
+						if (res.confirm) {
+							saveGoodsDevice({
+								deviceIds: [this.deviceId], //设备信息id
+								modelId: item.modelId //商品清单ID
+							}).then(res => {
+								this.$tab.navigateBack()
+							})
+						} else if (res.cancel) {
+							console.log('用户点击取消');
+						}
+					}
+				});
+				// this.$tab.navigateTo(`application?modelId=${item.modelId}&title=${item.name}`)
+			},
+
+			
+		}
+	}
+</script>
+<style lang="scss" scoped>
+	.container {
+		.martop {
+			margin-top: 20rpx;
+		}
+
+		.marleft {
+			margin-left: 20rpx;
+		}
+
+		.title {
+			margin: 20rpx;
+		}
+		
+		.scroll-content{
+			overflow: hidden;
+			padding-bottom: calc(120rpx + env(safe-area-inset-bottom) / 2);
+		}
+
+		.commoditylist-container {
+			margin: 0 13rpx;
+			&+.commoditylist-container{
+				margin-top: 20rpx;
+			}
+
+			.commoditylist-content {
+				padding: 20rpx;
+				>view{
+					line-height: 50rpx;
+				}
+			}
+			
+			.btn-box{
+				
+			}
+		}
+
+		.btn {
+			width: 724rpx;
+			position: fixed;
+			left: 13rpx;
+			bottom: calc(24rpx + env(safe-area-inset-bottom) / 2);
+			border-radius: 88rpx;
+			overflow: hidden;
+		}
+
+		.addcommoditylist-container {
+			padding: 20rpx 30rpx 40rpx;
+		}
+	}
+</style>

+ 163 - 0
pages/commodity/components/imgUpload/index.vue

@@ -0,0 +1,163 @@
+<template>
+	<u-upload sizeType="original" :fileList="fileList" :previewFullImage="true" @afterRead="afterRead" @delete="deletePic" multiple
+		accept="image" :maxCount="maxCount">
+		<view class="upload-fix flex justify-around align-center">
+			<view class="flex">
+				<u-icon name="camera" color="#cacaca" size="24"></u-icon>
+			</view>
+		</view>
+	</u-upload>
+</template>
+
+<script>
+	import {
+		ossInfo
+	} from '@/api/oss.js'
+	import {
+		getRandomNum
+	} from '@/utils/common.js'
+	import imgUpload from '@/utils/upload.js'
+	export default {
+		data() {
+			return {
+				uploadData: { //oss返回数据
+					policy: undefined,
+					Signature: undefined,
+					OSSAccessKeyId: undefined,
+					uploadImgUrl: undefined,
+					dir: undefined,
+					key: undefined
+				},
+				fileList: []
+			}
+		},
+		props: {
+			value: {
+				type: Array,
+				require: true,
+				default () {
+					return []
+				}
+			},
+
+			maxCount: {
+				type: Number,
+				require: false,
+				default: 4
+			},
+		},
+
+		watch: {
+			value: {
+				handler(newVal, oldVal) {
+					let newList = newVal.map(i => {
+						return {
+							url: i
+						}
+					})
+					this.fileList = newList
+				},
+				immediate: true,
+				deep: true
+			}
+		},
+
+		methods: {
+			oss() {
+				return new Promise((resolve, reject) => {
+					ossInfo().then(response => {
+						this.uploadData.policy = response.data.policy;
+						this.uploadData.Signature = response.data.signature;
+						this.uploadData.OSSAccessKeyId = response.data.accessid;
+						this.uploadData.uploadImgUrl = response.data.domain;
+						this.uploadData.dir = response.data.dir;
+						resolve(response)
+					}).catch(err => {
+						reject(err)
+					})
+				})
+			},
+			// 删除图片
+			deletePic(event) {
+				this.fileList.splice(event.index, 1);
+				let newList = this.fileList.map(i => {
+					return i.url
+				})
+				this.$emit('input', newList)
+			},
+			
+			// 新增图片
+			async afterRead(event) {
+				// 当设置 multiple 为 true 时, file 为数组格式,否则为对象格式
+				let lists = [].concat(event.file)
+				let fileListLen = this.fileList.length
+				lists.map((item) => {
+					this.fileList.push({
+						...item,
+						status: 'uploading',
+						message: '上传中'
+					})
+				})
+
+				await this.oss() //获取oss数据
+
+				for (let i = 0; i < lists.length; i++) {
+					const result = await this.uploadFilePromise(lists[i])
+					let item = this.fileList[fileListLen]
+					this.fileList.splice(fileListLen, 1, Object.assign(item, {
+						status: 'success',
+						message: '成功',
+						url: result
+					}))
+					fileListLen++
+				}
+
+				let newList = this.fileList.map(i => {
+					return i.url
+				})
+				this.$emit('input', newList)
+			},
+			uploadFilePromise(file) {
+				//组装上传数据
+				let timestamp = new Date().getTime()
+				let randomNum = getRandomNum(10000, 99999)
+				let imgType = file.url.substr(file.url.indexOf('.'))
+				this.uploadData.key = `${this.uploadData.dir}${timestamp}${randomNum}${imgType}`
+				let params = {
+					policy: this.uploadData.policy,
+					Signature: this.uploadData.Signature,
+					OSSAccessKeyId: this.uploadData.OSSAccessKeyId,
+					key: this.uploadData.key
+				}
+
+				return new Promise((resolve, reject) => {
+					imgUpload({
+						url: this.uploadData.uploadImgUrl,
+						header: {
+							isToken: true
+						},
+						formData: params,
+						filePath: file.url
+					}).then((res) => {
+						console.log('res',res)
+						let url = this.uploadData.uploadImgUrl + '/' + this.uploadData
+							.key //图片路径需要前端组装
+						console.log('图片路径', url)
+						resolve(url)
+					}).catch(err => {
+						reject(err)
+					})
+				})
+			},
+		}
+	}
+</script>
+
+<style lang="scss">
+.upload-fix{
+	width:160rpx;
+	height:160rpx;
+	border: 1rpx dashed #dddddd;
+	border-radius: 12rpx;
+}
+</style>

+ 100 - 0
pages/commodity/components/imgUploadCut/index.vue

@@ -0,0 +1,100 @@
+<template>
+	<ximgresiz @upload="doUpload" :avatarSrc="value" quality="1" ref="avatar" />
+</template>
+
+<script>
+	import {
+		ossInfo
+	} from '@/api/oss.js'
+	import {
+		getRandomNum
+	} from '@/utils/common.js'
+	import imgUpload from '@/utils/upload.js'
+	import ximgresiz from '@/components/xy-imgResiz/index.vue'
+	export default {
+		components: {
+			ximgresiz
+		},
+		data() {
+			return {
+				uploadData: { //oss返回数据
+					policy: undefined,
+					Signature: undefined,
+					OSSAccessKeyId: undefined,
+					uploadImgUrl: undefined,
+					dir: undefined,
+					key: undefined
+				},
+			}
+		},
+		props: {
+			value: {
+				type: String,
+				default () {
+					return ''
+				}
+			},
+		},
+
+
+		methods: {
+			fChooseImg(obj) {
+				this.$refs.avatar.fChooseImg(obj);
+			},
+
+			oss() {
+				return new Promise((resolve, reject) => {
+					ossInfo().then(response => {
+						this.uploadData.policy = response.data.policy;
+						this.uploadData.Signature = response.data.signature;
+						this.uploadData.OSSAccessKeyId = response.data.accessid;
+						this.uploadData.uploadImgUrl = response.data.domain;
+						this.uploadData.dir = response.data.dir;
+						resolve(response)
+					}).catch(err => {
+						reject(err)
+					})
+				})
+			},
+
+			async doUpload(rsp) {
+				await this.oss() //获取oss数据
+				await this.uploadFilePromise(rsp.path).then(res => {
+					this.$emit('success', res)
+				})
+			},
+
+			uploadFilePromise(url) {
+				//组装上传数据
+				let timestamp = new Date().getTime()
+				let randomNum = getRandomNum(10000, 99999)
+				let imgType = url.substr(url.indexOf('.'))
+				this.uploadData.key = `${this.uploadData.dir}${timestamp}${randomNum}${imgType}`
+				let params = {
+					policy: this.uploadData.policy,
+					Signature: this.uploadData.Signature,
+					OSSAccessKeyId: this.uploadData.OSSAccessKeyId,
+					key: this.uploadData.key
+				}
+
+				return new Promise((resolve, reject) => {
+					imgUpload({
+						url: this.uploadData.uploadImgUrl,
+						header: {
+							isToken: true
+						},
+						formData: params,
+						filePath: url
+					}).then((res) => {
+						console.log('res', res)
+						let url = this.uploadData.uploadImgUrl + '/' + this.uploadData
+							.key //图片路径需要前端组装
+						resolve(url)
+					}).catch(err => {
+						reject(err)
+					})
+				})
+			},
+		}
+	}
+</script>

+ 310 - 0
pages/commodity/publicCom.vue

@@ -0,0 +1,310 @@
+<template>
+	<view class="container">
+		<u-navbar leftIconColor="#fff" titleStyle="color:#fff;fontSize:36rpx;" :autoBack="true" bgColor="#2C6FF3"
+			:placeholder="true" title="官方商品库"></u-navbar>
+		<!-- <view class="u-search-box" @click="searchComm">
+			<view class="u-search-inner">
+				<u-icon name="search" color="#909399" :size="28"></u-icon>
+				<text class="u-search-text">搜索商品</text>
+			</view>
+		</view> -->
+		<view class="search" @click="searchComm">
+			<view class="search-input">
+				<u-search placeholder="商品搜索" actionText="取消" :actionStyle="{color:'#2C6FF3'}" :showAction="!leftShow"
+					:clearabled="false" v-model="keyword" @search="search" @custom="cancle"></u-search>
+				<view @click="scan" :class="[leftShow?'scan-icon scan-left-show':'scan-icon scan-left-hidden']">
+					<u-icon name="scan" size="22" color="#909399"></u-icon>
+				</view>
+			</view>
+
+			<view class="search-history flex flex-wrap flex-start" v-if="!leftShow">
+				<view class="history-item" v-for="(item,index) in historyList" :key="index" @click="searchFast(item)">
+					{{item}}
+				</view>
+			</view>
+		</view>
+
+		<view class="classify-wrap">
+			<Classify ref="classify" :storeName="storeName" :selectShow="true" :tabList="tabList" :status="status"
+				:commList="commList" @switchMenu="switchMenu" @comClick='detail' @lowerBottom="lowerBottom"
+				:height="fullHeight" :leftShow="leftShow" />
+		</view>
+		<view class="btn safe-bottom">
+			<xbutton delay="1500" size="large" @click="addToPer">添加到商品私库</xbutton>
+		</view>
+	</view>
+</template>
+
+<script>
+	import Classify from "@/components/classify/index.vue"
+
+	import {
+		goodsCategory,
+		ownerGoodsList
+	} from "@/api/commodity/goods.js"
+	export default {
+		components: {
+			Classify
+		},
+		data() {
+			return {
+				fullHeight: '0',
+				tabList: [], //商品类目
+				commList: [], //商品列表
+
+				page: 1, //商品分页
+				size: 10,
+
+				status: 'loadmore', //加载更多
+				storeName: 'pubToPerStor', //存储名称
+
+				keyword: '', //搜索
+
+				leftShow: true,
+
+				historyList: []
+			}
+		},
+		onLoad() {
+			let _this = this;
+			const query = uni.createSelectorQuery().in(this);
+			query.select(".classify-wrap").boundingClientRect((data) => {
+				_this.top = data.top;
+				uni.getSystemInfo({
+					success(res) {
+						// 针对iPhone X等机型底部安全距离做适配
+						const model = res.model;
+						const modelInclude = [
+							"iPhone X",
+							'iPhone XR',
+							"iPhone XS",
+							"iPhone XS MAX",
+							"iPhone 12/13 mini",
+							"iPhone 12/13 (Pro)",
+							"iPhone 12/13 Pro Max",
+							"iPhone 14 Pro Max"
+						];
+						let safeDistance = modelInclude.includes(model)
+						//动态设置商品区域高度
+						if (safeDistance) {
+							_this.fullHeight = res.windowHeight - _this.top - 84 + 'px';
+						} else {
+							_this.fullHeight = res.windowHeight - _this.top - 50 + 'px';
+						}
+					},
+				});
+			}).exec();
+
+			if (uni.getStorageSync('goods')) {
+				this.historyList = JSON.parse(uni.getStorageSync('goods'))
+			}
+			this.getCategory()
+		},
+
+		onShow() {
+			//搜索页来回跳转,刷新已选商品数据
+			this.$refs.classify.onshow()
+		},
+
+		methods: {
+			// 搜索商品
+			// searchComm() {
+			// 	this.$tab.navigateTo('/pages/commodity/publicSearch?type=0&storeName=' + this.storeName)
+			// },
+			search(val) {
+				this.saveKeyWord('goods', this.keyword)
+				this.reset();
+				this.getCommList()
+			},
+
+			cancle(val) {
+				this.keyword = ''
+				this.leftShow = true
+				this.search()
+			},
+
+			saveKeyWord(type, val) {
+				if (val) {
+					let arr = []
+					if (uni.getStorageSync(type)) {
+						let arr = JSON.parse(uni.getStorageSync(type))
+						if (arr.indexOf(val) != -1) {
+							console.log('arr.indexOf(val)', arr.indexOf(val))
+							arr.splice(arr.indexOf(val), 1)
+						}
+						arr.unshift(val)
+						if (arr.length > 6) {
+							arr.pop()
+						}
+						this.historyList = JSON.parse(JSON.stringify(arr))
+						uni.setStorageSync(type, JSON.stringify(arr))
+					} else {
+						arr.unshift(val)
+						this.historyList = JSON.parse(JSON.stringify(arr))
+						uni.setStorageSync(type, JSON.stringify(arr))
+					}
+				} else {
+					return
+				}
+			},
+
+			reset() {
+				this.status == 'loadmore'
+				this.page = 1;
+				this.size = 10;
+				this.commList = [];
+			},
+
+			searchFast(e) {
+				this.keyword = e
+				this.search()
+			},
+
+			//扫码
+			scan() {
+				uni.scanCode({
+					success: (res) => {
+						this.keyword = res.result;
+						this.search()
+					}
+				});
+			},
+
+			searchComm() {
+				this.leftShow = false
+			},
+
+			//获取类目列表
+			getCategory() {
+				goodsCategory({
+					deviceId:''
+				}).then(res => {
+					this.tabList = res.data
+					if (this.tabList && this.tabList.length > 0) {
+						this.switchMenu(this.tabList[0])
+					}
+				})
+			},
+
+			//商品类目切换
+			switchMenu(item) {
+				this.reset()
+				this.getCommList(item.categoryCode)
+			},
+
+			//根据类目获取商品列表
+			getCommList(id) {
+				let params = {}
+				if (this.leftShow) { //搜索
+					params = {
+						categoryCode: id,
+						page: {
+							current: this.page,
+							size: this.size
+						}
+					}
+				} else { //非搜索
+					params = {
+						page: {
+							current: this.page,
+							size: this.size
+						},
+						keyword: this.keyword
+					}
+				}
+				ownerGoodsList(params).then(res => {
+					let data = res.data.records;
+					let newData = data.map(i => {
+						i.noSelect = i.mercGoodsId != null ? true : false;
+						i.categoryCode = i.categoryCodeLevel1;
+						return i
+					})
+					if (newData.length < 10) {
+						this.status = "nomore"
+					} else {
+						this.status = "loadmore"
+					}
+					this.commList = this.commList.concat(newData)
+				})
+			},
+
+			//触底加载更多
+			lowerBottom() {
+				if (this.status == 'nomore') return
+				this.page++
+				this.getCommList()
+			},
+
+			//添加商品到私库
+			addToPer() {
+				if (uni.getStorageSync(this.storeName) && JSON.parse(uni.getStorageSync(
+						this.storeName)).length > 0) {
+					this.$tab.navigateTo(`/pages/equipment/addComList?storeName=${this.storeName}&type=0`)
+				} else {
+					uni.$u.toast('请先选择商品!')
+				}
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.container {
+		height: calc(100vh);
+		/* #ifdef H5 */
+		height: calc(100vh - var(--window-top));
+		/* #endif */
+		display: flex;
+		flex-direction: column;
+
+		.search {
+			padding: 24rpx 24rpx;
+			background-color: #fff;
+
+			.search-input {
+				position: relative;
+
+				.scan-icon {
+					position: absolute;
+					top: 50%;
+					transform: translateY(-50%);
+					z-index: 2;
+
+					&.scan-left-show {
+						right: 36rpx;
+					}
+
+					&.scan-left-hidden {
+						right: 100rpx;
+					}
+				}
+			}
+
+			.search-history {
+
+				.history-item {
+					margin-right: 24rpx;
+					padding: 0 12rpx;
+					background-color: #f2f2f2;
+					color: #333;
+					font-size: 24rpx;
+					line-height: 40rpx;
+					border-radius: 40rpx;
+					margin-top: 24rpx;
+				}
+			}
+		}
+	}
+
+	.classify-wrap {
+		// padding-bottom: 200rpx;
+	}
+
+	.btn {
+		width: 100%;
+		position: fixed;
+		left: 0;
+		bottom: 24rpx;
+		padding: 0 24rpx;
+	}
+</style>

+ 455 - 0
pages/commodity/publicSearch.vue

@@ -0,0 +1,455 @@
+<template>
+	<view class="container">
+		<u-navbar leftIconColor="#fff" titleStyle="color:#fff;fontSize:36rpx;" :autoBack="true" bgColor="#2C6FF3"
+			:placeholder="true" :title="title"></u-navbar>
+		<view class="content">
+			<view class="search">
+				<u-search animation placeholder="商品搜索" :clearabled="false" v-model="keyword" :showAction="false"
+					@search="search"></u-search>
+				<view class="scan-icon" @click="scan">
+					<u-icon name="scan" size="22" color="#909399"></u-icon>
+				</view>
+
+				<view class="search-history flex flex-wrap flex-start">
+					<view class="history-item" v-for="(item,index) in historyList" :key="index"
+						@click="searchFast(item)">
+						{{item}}
+					</view>
+				</view>
+			</view>
+			<view class="list" v-if="newCommList&&newCommList.length>0">
+				<view class="thumb-box" v-for="(item, index) in newCommList" :key="index"
+					@click.stop="commItemSelect(item)">
+					<view>
+						<image class="select-img"
+							src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/commodity/no_selected.png"
+							mode="widthFix" v-show="item.noSelect"></image>
+						<image class="select-img"
+							src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/commodity/selected.png"
+							mode="widthFix" v-show="!item.noSelect&&item.checked"></image>
+						<image class="select-img"
+							src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/commodity/select.png"
+							mode="widthFix" v-show="!item.noSelect&&!item.checked"></image>
+					</view>
+					<view class="check-content">
+						<view class="comm-img">
+							<u--image width="130rpx" height="130rpx" :src="item.cover" mode="widthFix"
+								:lazy-lord="true"></u--image>
+						</view>
+						<view class="comm-main">
+							<view>
+								{{item.name}}
+							</view>
+							<view>
+								条形码:{{item.barcode}}
+							</view>
+							<view>
+								商品类型:{{item.categoryName}}
+							</view>
+							<view v-if="type==1">
+								价格:<text>¥{{item.price}}</text>
+							</view>
+						</view>
+					</view>
+				</view>
+				<u-loadmore :status="status" v-if="newCommList.length>=1" />
+			</view>
+
+			<view class="empty" v-if="newCommList.length==0">
+				<u-empty mode="car" text="没有商品!"></u-empty>
+			</view>
+		</view>
+
+		<view class="btn safe-bottom">
+			<xbutton size="large" @click="submit">{{btnName}}({{selectList.length}})</xbutton>
+		</view>
+	</view>
+</template>
+
+<script>
+	import {
+		ownerGoodsList as perOwnerGoodsList
+	} from "@/api/commodity/mercGoods.js"
+	import {
+		ownerGoodsList as pubOwnerGoodsList,
+		pageByGoods,
+		pageByGoodsMerc
+	} from "@/api/commodity/goods.js"
+	export default {
+		data() {
+			return {
+				keyword: '',
+				commList: [], //商品列表
+
+				page: 1, //商品分页
+				size: 10,
+
+				status: 'loadmore', //加载更多
+				type: null,
+				storeName: null,
+				title: '私有商品库',
+				btnName: '添加到商品私库',
+				selectList: [],
+				id: null,
+				historyList: []
+			}
+		},
+
+		computed: {
+			newCommList() {
+				let newCommList = [];
+				if (this.selectList && this.selectList.length > 0) {
+					let selectList = this.selectList
+					const idMapping = selectList.reduce((acc, el, i) => {
+						acc[el.id] = i;
+						return acc;
+					}, {});
+					this.commList.forEach(i => {
+						if (idMapping[i.id] != undefined) { //重复值
+							i.checked = true
+						} else {
+							i.checked = false
+						}
+						newCommList.push(i)
+					})
+				} else {
+					newCommList = this.commList.map(i => {
+						i.checked = false;
+						return i
+					})
+				}
+				console.log('newCommList', newCommList)
+				return newCommList
+			},
+		},
+
+		onLoad(o) {
+			if (uni.getStorageSync('goods')) {
+				this.historyList = JSON.parse(uni.getStorageSync('goods'))
+			}
+			if (o.type) {
+				this.id = o.type != 0 ? o.id : null;
+				this.type = o.type; //0公库到私库、1私库到设备、2公库到设备
+				this.title = o.type == 1 ? '私有商品库' : '官方商品库';
+				this.btnName = o.storeName == 'pubToPerStor' ? '添加到商品私库' : '添加到设备';
+				this.storeName = o.storeName;
+
+				if (uni.getStorageSync(this.storeName) && JSON.parse(uni.getStorageSync(
+						this.storeName)).length > 0) {
+					let commStor = JSON.parse(uni.getStorageSync(this.storeName))
+					this.selectList = commStor;
+				}
+
+				this.getCommList();
+			}
+		},
+		methods: {
+			search(val) {
+				this.saveKeyWord('goods', this.keyword)
+				this.reset();
+				this.getCommList()
+			},
+
+			saveKeyWord(type, val) {
+				if (val) {
+					let arr = []
+					if (uni.getStorageSync(type)) {
+						let arr = JSON.parse(uni.getStorageSync(type))
+						if (arr.indexOf(val) != -1) {
+							console.log('arr.indexOf(val)', arr.indexOf(val))
+							arr.splice(arr.indexOf(val), 1)
+						}
+						arr.unshift(val)
+						if (arr.length > 6) {
+							arr.pop()
+						}
+						this.historyList = JSON.parse(JSON.stringify(arr))
+						uni.setStorageSync(type, JSON.stringify(arr))
+					} else {
+						arr.unshift(val)
+						this.historyList = JSON.parse(JSON.stringify(arr))
+						uni.setStorageSync(type, JSON.stringify(arr))
+					}
+				} else {
+					return
+				}
+			},
+
+			searchFast(e) {
+				this.keyword = e
+				this.search()
+			},
+
+			//扫码
+			scan() {
+				uni.scanCode({
+					success: (res) => {
+						this.keyword = res.result;
+						this.getCommList()
+					}
+				});
+			},
+
+			//根据类目获取商品列表
+			getCommList(id) {
+				if (this.type == 0) { //公库到私库
+					pubOwnerGoodsList({
+						page: {
+							current: this.page,
+							size: this.size
+						},
+						keyword: this.keyword
+					}).then(res => {
+						let data = res.data.records;
+						let newData = data.map(i => {
+							i.noSelect = i.mercGoodsId != null ? true : false;
+							i.categoryCode = i.categoryCodeLevel1;
+							return i
+						})
+						if (newData.length < 10) {
+							this.status = "nomore"
+						} else {
+							this.status = "loadmore"
+						}
+						this.commList = this.commList.concat(newData)
+					})
+				}
+				if (this.type == 1) { //私库到设备
+					pageByGoodsMerc({
+						page: {
+							current: this.page,
+							size: this.size
+						},
+						keyword: this.keyword,
+						deviceId: this.id
+					}).then(res => {
+						let data = res.data.records;
+						if (data && data.length > 0) {
+							data = data.map(i => {
+								i.noSelect = i.isBind;
+								i.name = i.goodsName;
+								i.barcode = i.goodsBarcode;
+								i.cover = i.goodsCover;
+								i.price = Number(i.price) / 100;
+								i.categoryName = i.capacity == null ? '未分类' : i.capacity;
+								return i
+							})
+						}
+						if (data.length < 10) {
+							this.status = "nomore"
+						} else {
+							this.status = "loadmore"
+						}
+						this.commList = this.commList.concat(data)
+					})
+				}
+
+				if (this.type == 2) { //公库设备
+					pageByGoods({
+						page: {
+							current: this.page,
+							size: this.size
+						},
+						keyword: this.keyword,
+						deviceId: this.id
+					}).then(res => {
+						let data = res.data.records;
+						let newData = data.map(i => {
+							i.noSelect = i.isBind;
+							i.categoryCode = i.categoryCodeLevel1;
+							return i
+						})
+						if (newData.length < 10) {
+							this.status = "nomore"
+						} else {
+							this.status = "loadmore"
+						}
+						this.commList = this.commList.concat(newData)
+					})
+				}
+			},
+
+			//触底加载更多
+			onReachBottom() {
+				if (this.status == 'nomore') return
+				this.page++
+				this.getCommList()
+			},
+
+			reset() {
+				this.status == 'loadmore'
+				this.page = 1;
+				this.size = 10;
+				this.commList = [];
+			},
+
+			// 商品选中状态改变
+			commItemSelect(e) {
+				if (e.noSelect) {
+					this.$modal.msg('当前商品已存在!')
+					return
+				}
+				e.checked = !e.checked
+				//选中商品存储到内存中,以便取用
+				let commStor = []
+				if (e.checked) { //选中添加到存储中
+					if (uni.getStorageSync(this.storeName)) {
+						commStor = JSON.parse(uni.getStorageSync(this.storeName));
+					}
+					commStor.push(e);
+				} else { //取消选中删除,并且从存储中删除
+					commStor = JSON.parse(uni.getStorageSync(this.storeName));
+					for (let i = 0; i < commStor.length; i++) {
+						let item = commStor[i]
+						if (item.id == e.id) {
+							commStor.splice(i, 1);
+							break
+						}
+					}
+				}
+				//收集选中商品,更新存储
+				this.selectList = commStor;
+				commStor.length > 0 ? uni.setStorageSync(this.storeName, JSON.stringify(commStor)) : uni
+					.setStorageSync(
+						this.storeName, '')
+				if (commStor.length > 0) {
+					uni.setStorageSync(this.storeName, JSON.stringify(commStor))
+				}
+			},
+
+			submit() {
+				if (uni.getStorageSync(this.storeName) && JSON.parse(uni.getStorageSync(this.storeName)).length > 0) {
+					this.$tab.navigateTo(
+						`/pages/equipment/addComList?storeName=${this.storeName}&type=${this.type}&id=${this.id}`)
+
+				} else {
+					uni.$u.toast('请先选择商品!')
+				}
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.container {
+		.content {
+			padding-bottom: 88rpx;
+
+			.search {
+				padding: 24rpx 24rpx 12rpx;
+				background-color: #fff;
+				position: relative;
+
+				.scan-icon {
+					position: absolute;
+					right: 36rpx;
+					top: 38rpx;
+					z-index: 2;
+				}
+
+				.search-history {
+
+					.history-item {
+						margin-right: 24rpx;
+						padding: 0 12rpx;
+						background-color: #f2f2f2;
+						color: #333;
+						font-size: 24rpx;
+						line-height: 40rpx;
+						border-radius: 40rpx;
+						margin-top: 24rpx;
+					}
+				}
+			}
+
+			.list {
+				width: 100%;
+				padding: 12rpx 24rpx;
+
+				.thumb-box {
+					margin-bottom: 12rpx;
+					border-bottom: 1rpx solid #f4f4f4;
+					display: flex;
+					flex-flow: row nowrap;
+					padding: 12rpx 12rpx;
+					align-items: center;
+					background-color: #fff;
+					border-radius: 12rpx;
+				}
+
+				.select-img {
+					width: 40rpx;
+					height: 40rpx;
+				}
+
+				.check-content {
+					display: flex;
+					flex-direction: row;
+					align-items: center;
+					padding-left: 12rpx;
+
+					.comm-img {
+						width: 130rpx;
+						height: 130rpx;
+						display: flex;
+						flex-direction: row;
+						align-items: center;
+						justify-content: space-around;
+
+						image {
+							width: 100%;
+						}
+					}
+
+					.comm-main {
+						box-sizing: border-box;
+						padding-left: 18rpx;
+						color: #999;
+
+						>view {
+							padding: 4rpx 0;
+						}
+
+						>view:nth-child(1) {
+							font-size: 28rpx;
+							font-weight: bold;
+							color: #333;
+						}
+
+						>view:nth-child(2) {
+							font-size: 26rpx;
+						}
+
+						>view:nth-child(3) {
+							font-size: 26rpx;
+						}
+
+						>view:nth-child(4) {
+							font-size: 26rpx;
+
+							text {
+								font-weight: bold;
+								color: red;
+							}
+						}
+					}
+				}
+			}
+		}
+
+		.btn {
+			width: 100%;
+			position: fixed;
+			left: 0;
+			bottom: 24rpx;
+			padding: 0 24rpx;
+		}
+
+		.empty {
+			position: absolute;
+			left: 50%;
+			top: 50%;
+			transform: translate(-50%, -50%);
+		}
+	}
+</style>

+ 247 - 0
pages/commodity/search.vue

@@ -0,0 +1,247 @@
+<template>
+	<view class="container">
+		<u-navbar leftIconColor="#fff" titleStyle="color:#fff;fontSize:36rpx;" :autoBack="true" bgColor="#2C6FF3"
+			:placeholder="true" title="商品私库"></u-navbar>
+		<view class="content">
+			<view class="search">
+				<u-search animation placeholder="商品搜索" :clearabled="false" v-model="keyword" :showAction="false"
+					@search="search"></u-search>
+				<view class="scan-icon" @click="scan">
+					<u-icon name="scan" size="22" color="#909399"></u-icon>
+				</view>
+
+				<view class="search-history flex flex-wrap flex-start">
+					<view class="history-item" v-for="(item,index) in historyList" :key="index"
+						@click="searchFast(item)">
+						{{item}}
+					</view>
+				</view>
+			</view>
+			<view class="list">
+				<u-list @scrolltolower="scrolltolower" :height="fullHeight">
+					<u-list-item class="list-item" v-for="(item, index) in commList" :key="index">
+						<view class="comm-item" @click="$tab.navigateTo('/pages/commodity/comEdit?id='+item.id)">
+							<u--image radius="4" width="110rpx" height="110rpx" :src="item.goodsCover" mode="widthFix"
+								:lazy-lord="true"></u--image>
+							<view class="item-content">
+								<view>
+									{{item.goodsName}}
+								</view>
+								<view>
+									69码:{{item.goodsBarcode}}
+								</view>
+								<view>
+									商品类型:{{item.categoryCode}}
+								</view>
+							</view>
+							<view class="status">
+								<u-tag text="已上架" type="success" plain v-if="item.status=='1'"> </u-tag>
+								<u-tag text="已下架" type="error" plain v-else></u-tag>
+							</view>
+						</view>
+					</u-list-item>
+					<u-loadmore :status="status" v-if="commList.length>0" />
+				</u-list>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import {
+		ownerGoodsList
+	} from "@/api/commodity/mercGoods.js"
+	import {
+		saveKeyWord
+	} from '@/utils/common.js'
+	export default {
+		data() {
+			return {
+				keyword: '',
+				page: 1, //商品分页
+				size: 10,
+
+				status: 'loadmore', //加载更多
+				commList: [], //商品列表
+				fullHeight: 0,
+				historyList: []
+			}
+		},
+		onLoad() {
+			let _this = this;
+			const query = uni.createSelectorQuery().in(this);
+			query.select(".list").boundingClientRect((data) => {
+				_this.top = data.top;
+				uni.getSystemInfo({
+					success(res) {
+						// 针对iPhone X等机型底部安全距离做适配
+						const model = res.model;
+						const modelInclude = [
+							"iPhone X", 
+							'iPhone XR', 
+							"iPhone XS", 
+							"iPhone XS MAX",
+							"iPhone 12/13 mini",
+							"iPhone 12/13 (Pro)",
+							"iPhone 12/13 Pro Max",
+							"iPhone 14 Pro Max"
+						];
+						let safeDistance = modelInclude.includes(model)
+						//动态设置商品区域高度
+						if (safeDistance) {
+							_this.fullHeight = res.windowHeight - _this.top - 34 + 'px';
+						} else {
+							_this.fullHeight = res.windowHeight - _this.top + 'px';
+						}
+					},
+				});
+			}).exec();
+
+			if (uni.getStorageSync('goods')) {
+				this.historyList = JSON.parse(uni.getStorageSync('goods'))
+			}
+		},
+
+		onShow() {
+			this.search()
+		},
+
+		methods: {
+			search(val) {
+				if(val){
+					this.historyList = saveKeyWord('goods', this.keyword)
+				}
+				this.reset();
+				this.getCommList()
+			},
+
+			searchFast(e) {
+				this.keyword = e
+				this.search()
+			},
+
+			//扫码
+			scan() {
+				uni.scanCode({
+					success: (res) => {
+						this.keyword = res.result;
+						this.getCommList()
+					}
+				});
+			},
+
+			//根据类目获取商品列表
+			getCommList(id) {
+				ownerGoodsList({
+					page: {
+						current: this.page,
+						size: this.size
+					},
+					keyword: this.keyword
+				}).then(res => {
+					let data = res.data.records;
+					if (data.length < 10) {
+						this.status = "nomore"
+					} else {
+						this.status = "loadmore"
+					}
+					this.commList = this.commList.concat(data)
+				})
+			},
+
+			reset() {
+				this.status == 'loadmore'
+				this.page = 1;
+				this.size = 10;
+				this.commList = [];
+			},
+
+			scrolltolower() {
+				if (this.status == 'nomore') return
+				this.page++
+				this.getCommList()
+			},
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.container {
+		.content {
+			.search {
+				padding: 24rpx 24rpx 12rpx;
+				background-color: #fff;
+				position: relative;
+
+				.scan-icon {
+					position: absolute;
+					right: 36rpx;
+					top: 38rpx;
+					z-index: 2;
+				}
+
+				.search-history {
+
+					.history-item {
+						margin-right: 12rpx;
+						padding: 0 12rpx;
+						background-color: #f2f2f2;
+						color: #333;
+						font-size: 24rpx;
+						line-height: 40rpx;
+						border-radius: 40rpx;
+						margin-top: 12rpx;
+					}
+				}
+			}
+
+			.list {
+				width: 100%;
+				padding: 12rpx 24rpx;
+
+				.comm-item {
+					display: flex;
+					flex-direction: row;
+					justify-content: flex-start;
+					align-items: center;
+					background-color: #fff;
+					margin-bottom: 12rpx;
+					border-radius: 12rpx;
+					box-sizing: border-box;
+					padding: 12rpx;
+					position: relative;
+
+					.status {
+						position: absolute;
+						right: 12rpx;
+						top: 12rpx;
+					}
+
+					.image {
+						width: 110rpx;
+					}
+
+					.item-content {
+						padding-left: 24rpx;
+						color: #999;
+
+						>view:nth-child(1) {
+							font-size: 28rpx;
+							font-weight: bold;
+							color: #333;
+						}
+
+						>view:nth-child(2) {
+							font-size: 26rpx;
+							padding: 12rpx 0;
+						}
+
+						>view:nth-child(3) {
+							font-size: 26rpx;
+						}
+					}
+				}
+			}
+		}
+	}
+</style>

+ 608 - 0
pages/equipment/addCom.vue

@@ -0,0 +1,608 @@
+<template>
+	<view class="container">
+		<u-navbar leftIconColor="#fff" titleStyle="color:#fff;fontSize:36rpx;" :autoBack="true" bgColor="#2C6FF3"
+			:placeholder="true" title="增加商品"></u-navbar>
+		<!-- <view class="search" @click.native="searchClick">
+			<u-search animation placeholder="商品搜索" disabled v-model="keyword" :showAction="false" search="search">
+			</u-search>
+		</view> -->
+
+		<view class="search" @click="searchComm">
+			<view class="search-input">
+				<u-search placeholder="商品搜索" actionText="取消" :actionStyle="{color:'#2C6FF3'}" :showAction="!leftShow"
+					:clearabled="false" v-model="keyword" @search="search" @custom="cancle"></u-search>
+				<view @click="scan" :class="[leftShow?'scan-icon scan-left-show':'scan-icon scan-left-hidden']">
+					<u-icon name="scan" size="22" color="#909399"></u-icon>
+				</view>
+			</view>
+
+			<view class="search-history flex flex-wrap flex-start" v-if="!leftShow">
+				<view class="history-item" v-for="(item,index) in historyList" :key="index" @click="searchFast(item)">
+					{{item}}
+				</view>
+			</view>
+		</view>
+
+		<view class="xy-card tab-list" style="padding:0 0 24rpx;">
+			<view class="tab-left">
+				<u-tabs :list="tabList" :scrollable="false" :current="current" @click="tabClick" lineColor="#2C6FF3">
+				</u-tabs>
+			</view>
+			<view class="use-list" @click="$tab.navigateTo(`/pages/commodity/commoditylist?id=${id}`)">
+				常用商品清单
+			</view>
+		</view>
+		<view class="content">
+			<view class="swiperitem-content" v-if="current==0">
+				<view class="classify-wrap">
+					<Classify storeName="perStor" :selectShow="true" :tabList="perTabList" :status="perStatus"
+						:commList="perCommList" @switchMenu="perSwitchMenu" @lowerBottom="perLowerBottom"
+						:isModal="true" :height="fullHeight" @comClick="comClick" :leftShow="leftShow" />
+				</view>
+				<view class="btn safe-bottom">
+					<xbutton size="large" @click="addCom">添加到设备</xbutton>
+				</view>
+			</view>
+			<view class="swiperitem-content" v-if="current==1">
+				<view class="classify-wrap">
+					<Classify storeName="pubStor" :selectShow="true" :tabList="pubTabList" :status="pubStatus"
+						:commList="pubCommList" @switchMenu="pubSwitchMenu" @lowerBottom="pubLowerBottom"
+						:height="fullHeight" :leftShow="leftShow" />
+				</view>
+				<view class="btn">
+					<xbutton size="large" @click="addCom">添加到设备</xbutton>
+				</view>
+			</view>
+			<!-- 选项卡内容轮播滑动显示,current为当前第几个swiper子项 -->
+			<!-- <swiper @change="change" :current="current" class="swiper-content" :style="{height:height}">
+				<swiper-item class="swiperitem-content">
+					<view class="search">
+						<u-search animation placeholder="商品搜索" v-model="keyword" :showAction="false" search="search">
+						</u-search>
+					</view>
+					<view class="classify-wrap">
+						<Classify storeName="perStor1" :height="fullHeight" :selectShow="true"
+							v-model="commodityList" />
+
+					</view>
+					<view class="btn">
+						<xbutton size="large" @click="addCom">已选中(21)样商品</xbutton>
+					</view>
+				</swiper-item>
+				<swiper-item class="swiperitem-content">
+					<view class="search">
+						<u-search animation placeholder="商品搜索" v-model="keyword" :showAction="false" search="search">
+						</u-search>
+					</view>
+					<view class="classify-wrap">
+						<Classify storeName="perStor2" :height="fullHeight" :selectShow="true"
+							v-model="commodityList" />
+					</view>
+					<view class="btn">
+						<xbutton size="large" @click="addCom">已选中(21)样商品</xbutton>
+					</view>
+				</swiper-item>
+			</swiper> -->
+		</view>
+	</view>
+</template>
+
+<script>
+	import Classify from "@/components/classify/index.vue"
+	import {
+		goodsCategory as perGoodsCategory,
+	} from "@/api/commodity/mercGoods.js"
+	import {
+		goodsCategory as pubGoodsCategory,
+		pageByGoods,
+		pageByGoodsMerc,
+		bindDeviceByMercGoods
+	} from "@/api/commodity/goods.js"
+	export default {
+		components: {
+			Classify
+		},
+		data() {
+			return {
+				tabList: [{
+						name: '私有商品库'
+					},
+					{
+						name: '官方商品库'
+					}
+				],
+				current: 0,
+				fullHeight: "0",
+
+				// 私库
+				perTabList: [], //商品类目
+				perCommList: [], //商品列表
+				perPage: 1, //商品分页
+				perSize: 10,
+				perStatus: 'loadmore', //加载更多
+
+				// 公库
+				pubTabList: [], //商品类目
+				pubCommList: [], //商品列表
+				pubPage: 1, //商品分页
+				pubSize: 10,
+				pubStatus: 'loadmore', //加载更多
+
+				id: null, //设备id
+				categoryCode: null,
+
+				leftShow: true,
+				historyList: [],
+				keyword: ''
+			}
+		},
+
+		onLoad(o) {
+			if (o.id) {
+				this.id = o.id;
+			}
+			let _this = this;
+			const query = uni.createSelectorQuery().in(this);
+			query.select(".classify-wrap").boundingClientRect((data) => {
+				_this.top = data.top;
+				uni.getSystemInfo({
+					success(res) {
+						// 针对iPhone X等机型底部安全距离做适配
+						const model = res.model;
+						const modelInclude = [
+							"iPhone X",
+							'iPhone XR',
+							"iPhone XS",
+							"iPhone XS MAX",
+							"iPhone 12/13 mini",
+							"iPhone 12/13 (Pro)",
+							"iPhone 12/13 Pro Max",
+							"iPhone 14 Pro Max"
+						];
+						let safeDistance = modelInclude.includes(model)
+						//动态设置商品区域高度
+						if (safeDistance) {
+							_this.fullHeight = res.windowHeight - _this.top - 84 + 'px';
+						} else {
+							_this.fullHeight = res.windowHeight - _this.top - 54 + 'px';
+						}
+					},
+				});
+			}).exec();
+
+			if (uni.getStorageSync('goods')) {
+				this.historyList = JSON.parse(uni.getStorageSync('goods'))
+			}
+			this.getPerCategory()
+			this.getPubCategory()
+		},
+
+		async onShow() {
+			this.perReset()
+			this.pubReset()
+			await this.getPerCommList()
+			await this.getPubCommList()
+		},
+
+		methods: {
+			searchComm() {
+				this.leftShow = false
+			},
+
+			search(val) {
+				this.saveKeyWord('goods', this.keyword)
+				if (this.current == 0) { //私库
+					this.perReset();
+					this.getPerCommList()
+				} else { //公库
+					this.pubReset();
+					this.getPubCommList()
+				}
+			},
+
+			cancle(val) {
+				this.keyword = ''
+				this.leftShow = true
+				this.search()
+			},
+
+			saveKeyWord(type, val) {
+				if (val) {
+					let arr = []
+					if (uni.getStorageSync(type)) {
+						let arr = JSON.parse(uni.getStorageSync(type))
+						if (arr.indexOf(val) != -1) {
+							console.log('arr.indexOf(val)', arr.indexOf(val))
+							arr.splice(arr.indexOf(val), 1)
+						}
+						arr.unshift(val)
+						if (arr.length > 6) {
+							arr.pop()
+						}
+						this.historyList = JSON.parse(JSON.stringify(arr))
+						uni.setStorageSync(type, JSON.stringify(arr))
+					} else {
+						arr.unshift(val)
+						this.historyList = JSON.parse(JSON.stringify(arr))
+						uni.setStorageSync(type, JSON.stringify(arr))
+					}
+				} else {
+					return
+				}
+			},
+
+			searchFast(e) {
+				this.keyword = e
+				this.search()
+			},
+
+			//扫码
+			scan() {
+				uni.scanCode({
+					success: (res) => {
+						this.keyword = res.result;
+						this.search()
+					}
+				});
+			},
+
+			/************************ 私库方法 ********************************/
+			// 搜索商品
+			searchClick() {
+				let storeName = this.current == 0 ? 'perStor' : 'pubStor'
+				this.$tab.navigateTo('/pages/commodity/allGoodsSearch?type=' + this.current + '&storeName=' + storeName +
+					'&id=' +
+					this.id)
+			},
+
+			//获取类目列表
+			getPerCategory() {
+				return new Promise((resolve, reject) => {
+					perGoodsCategory().then(res => {
+						if (res.data && res.data.length > 0) {
+							this.perTabList = res.data.map(i => {
+								if (i.categoryCode == null) {
+									i.categoryName = '未定义'
+									return i
+								} else {
+									return i
+								}
+							});
+							if (this.perTabList && this.perTabList.length > 0) {
+								this.categoryCode = this.perTabList[0].categoryCode
+							}
+						}
+						resolve(res)
+					}).catch(err => {
+						reject(err)
+					})
+				})
+			},
+
+			//商品类目切换
+			perSwitchMenu(item) {
+				this.categoryCode = item.categoryCode
+				this.perReset()
+				this.getPerCommList()
+			},
+
+			//根据类目获取商品列表
+			getPerCommList() {
+				let params = {}
+				if (this.leftShow) { //搜索
+					params = {
+						categoryCode: this.categoryCode,
+						page: {
+							current: this.perPage,
+							size: this.perSize
+						},
+						deviceId: this.id
+					}
+				} else { //非搜索
+					params = {
+						page: {
+							current: this.perPage,
+							size: this.perSize
+						},
+						deviceId: this.id,
+						keyword: this.keyword
+					}
+				}
+				return new Promise((resolve, reject) => {
+					pageByGoodsMerc(params).then(res => {
+						let data = res.data.records;
+						if (data && data.length > 0) {
+							data = data.map(i => {
+								i.name = i.goodsName;
+								i.barcode = i.goodsBarcode;
+								i.cover = i.goodsCover;
+								i.price = i.price != null ? Number(i.price) / 100 : null;
+								i.categoryName = i.categoryCodeName;
+								i.noSelect = i.isBind;
+								return i
+							})
+						}
+						if (data.length < 10) {
+							this.perStatus = "nomore"
+						} else {
+							this.perStatus = "loadmore"
+						}
+						this.perCommList = this.perCommList.concat(data)
+
+						resolve(res)
+					}).catch(err => {
+						reject(err)
+					})
+				})
+			},
+
+			//触底加载更多
+			perLowerBottom() {
+				if (this.perStatus == 'nomore') return
+				this.perPage++
+				this.getPerCommList()
+			},
+
+			//重置
+			perReset() {
+				this.perStatus == 'loadmore'
+				this.perPage = 1;
+				this.perSize = 10;
+				this.perCommList = [];
+			},
+
+			//新建模块商品先需要设置价格
+			comClick(e) {
+				uni.showModal({
+					title: '提示',
+					content: '当前商品为新商品需先设置商品价格,是否前往设置?',
+					success: res => {
+						if (res.confirm) {
+							this.$tab.navigateTo('/pages/commodity/comEdit?id=' + e.id)
+						} else if (res.cancel) {
+							console.log('用户点击取消');
+						}
+					}
+				});
+			},
+
+
+			/********************** 公库方法 *************************/
+			//获取类目列表
+			getPubCategory() {
+				return new Promise((resolve, reject) => {
+					pubGoodsCategory({
+						deviceId:this.id
+					}).then(res => {
+						if (res.data && res.data.length > 0) {
+							this.pubTabList = res.data.map(i => {
+								if (i.categoryCode == null) {
+									i.categoryName = '未定义'
+									return i
+								} else {
+									return i
+								}
+							});
+							if (this.pubTabList && this.pubTabList.length > 0) {
+								this.categoryCode = this.pubTabList[0].categoryCode
+							}
+						}
+						resolve(res)
+					}).catch(err => {
+						reject(err)
+					})
+				})
+			},
+
+			//商品类目切换
+			pubSwitchMenu(item) {
+				this.categoryCode = item.categoryCode
+				this.pubReset()
+				this.getPubCommList()
+			},
+
+			//根据类目获取商品列表
+			getPubCommList() {
+				let params = {}
+				if (this.leftShow) { //搜索
+					params = {
+						categoryCodeLevel1: this.categoryCode,
+						page: {
+							current: this.pubPage,
+							size: this.pubSize
+						},
+						deviceId: this.id
+					}
+				} else { //非搜索
+					params = {
+						page: {
+							current: this.pubPage,
+							size: this.pubSize
+						},
+						deviceId: this.id,
+						keyword: this.keyword
+					}
+				}
+				return new Promise((resolve, reject) => {
+					pageByGoods(params).then(res => {
+						let data = res.data.records;
+						let newData = data.map(i => {
+							i.categoryCode = i.categoryCodeLevel1;
+							i.noSelect = i.isBind;
+							return i
+						})
+						if (data.length < 10) {
+							this.pubStatus = "nomore"
+						} else {
+							this.pubStatus = "loadmore"
+						}
+						this.pubCommList = this.pubCommList.concat(newData)
+						resolve(res)
+					}).catch(err => {
+						reject(err)
+					})
+				})
+			},
+
+			//触底加载更多
+			pubLowerBottom() {
+				if (this.pubStatus == 'nomore') return
+				this.pubPage++
+				this.getPubCommList()
+			},
+
+			//重置
+			pubReset() {
+				this.pubStatus == 'loadmore'
+				this.pubPage = 1;
+				this.pubSize = 10;
+				this.pubCommList = [];
+			},
+
+			tabClick(e) {
+				this.current = e.index
+				this.search()
+			},
+
+			change(e) {
+				this.current = e.detail.current
+			},
+
+			/**
+			 * 添加商品
+			 * 私库添加到设备,不需要设置参数
+			 */
+			addCom() {
+				let storeName = this.current == 0 ? 'perStor' : 'pubStor';
+				if (uni.getStorageSync(storeName) && JSON.parse(uni.getStorageSync(
+						storeName)).length > 0) {
+					if (this.current == 0) { //私库
+						let commList = JSON.parse(uni.getStorageSync(storeName))
+						let goodsIdList = commList.map(i => {
+							return i.goodsId
+						})
+						bindDeviceByMercGoods({
+							deviceIds: [this.id],
+							goodsIdList: goodsIdList
+						}).then(res => {
+							if (res.code == 200) {
+								this.$modal.msg('添加成功~')
+								uni.setStorageSync(storeName, '') //清空购物车
+								setTimeout(() => {
+									// this.$tab.redirectTo(`/pages/equipment/comManage?id=${this.id}`)
+									uni.navigateBack({
+										delta: 1
+									})
+								}, 1000)
+							}
+						})
+					}
+					if (this.current == 1) { //公库
+						let url = '';
+						let type = this.current == 0 ? 1 : 2;
+						url = `/pages/equipment/addComList?type=${type}&storeName=${storeName}&id=${this.id}`
+						this.$tab.navigateTo(url)
+					}
+				} else {
+					uni.$u.toast('请先选择商品!')
+					return
+				}
+			},
+
+			//私库添加至设备
+			perToDev() {
+				uni.showModal({
+					title: '提示',
+					content: '是否确认添加商品',
+					success: res => {
+						if (res.confirm) {
+
+						} else if (res.cancel) {
+							console.log('用户点击取消');
+						}
+					}
+				});
+			},
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.container {
+		height: calc(100vh);
+		/* #ifdef H5 */
+		height: calc(100vh - var(--window-top));
+		/* #endif */
+		display: flex;
+		flex-direction: column;
+
+		.search {
+			padding: 24rpx 24rpx;
+			background-color: #fff;
+
+			.search-input {
+				position: relative;
+
+				.scan-icon {
+					position: absolute;
+					top: 50%;
+					transform: translateY(-50%);
+					z-index: 2;
+
+					&.scan-left-show {
+						right: 36rpx;
+					}
+
+					&.scan-left-hidden {
+						right: 100rpx;
+					}
+				}
+			}
+
+			.search-history {
+
+				.history-item {
+					margin-right: 24rpx;
+					padding: 0 12rpx;
+					background-color: #f2f2f2;
+					color: #333;
+					font-size: 24rpx;
+					line-height: 40rpx;
+					border-radius: 40rpx;
+					margin-top: 24rpx;
+				}
+			}
+		}
+
+		.tab-list {
+			display: flex;
+			flex-flow: row nowrap;
+			justify-content: space-around;
+
+			.tab-left {
+				width: 60%;
+			}
+
+			.use-list {
+				font-size: 30rpx;
+				color: #606266;
+				line-height: 88rpx;
+				padding-right: 24rpx;
+			}
+		}
+
+		.content {}
+	}
+
+	.swiperitem-content {
+		// position: relative;
+	}
+
+	.btn {
+		width: 100%;
+		position: fixed;
+		left: 0;
+		bottom: 24rpx;
+		padding: 0 24rpx;
+	}
+</style>

+ 296 - 0
pages/equipment/addComList.vue

@@ -0,0 +1,296 @@
+<template>
+	<view class="container">
+		<u-navbar leftIconColor="#fff" titleStyle="color:#fff;fontSize:36rpx;" :autoBack="true" bgColor="#2C6FF3"
+			:placeholder="true" title="增加商品"></u-navbar>
+		<view class="content">
+			<view class="xy-card" v-for="(item,index) in commList" :key="item.id">
+				<view class="comm-item">
+					<view class="image">
+						<u--image radius="4" width="130rpx" height="130rpx" :src="item.cover" mode="widthFix"
+							:lazy-load="true">
+						</u--image>
+					</view>
+					<view class="item-content">
+						<view class="item-top">
+							<view>
+								{{item.name}}
+							</view>
+							
+						</view>
+						<view class="item-input-wrap">
+							<view class="item-input" v-if="type!=1">
+								<view class="input-label require">
+									条形码:
+								</view>
+								<view class="input-box">
+									<text>{{item.barcode}}</text>
+								</view>
+							</view>
+							
+							<view class="item-input" v-if="type!=1">
+								<view class="input-label require">
+									成本价
+								</view>
+								<view class="input-box">
+									<input type="digit" class="input" placeholder="请输入" border="surround" v-model="item.priceCost" />
+								</view>
+							</view>
+							<view class="item-input require">
+								<view class="input-label">
+									零售价
+								</view>
+								<view class="input-box">
+									<input type="digit" class="input" placeholder="请输入" border="surround" v-model="item.price" />
+								</view>
+							</view>
+							<!-- <view class="item-input">
+								<view class="input-label">
+									商品容量
+								</view>
+								<view class="input-box">
+									<input type="number" class="input" placeholder="请输入" border="surround" v-model="item.capacity" />
+								</view>
+							</view> -->
+							<view class="item-input">
+								<view class="input-label">
+									库存预警
+								</view>
+								<view class="input-box">
+									<input type="number" class="input" placeholder="请输入" border="surround" v-model="item.warning" />
+								</view>
+							</view>
+						</view>
+					</view>
+				</view>
+			</view>
+		</view>
+		<view class="btn safe-bottom">
+			<xbutton delay="1500" size="large" @click="sure">完成</xbutton>
+		</view>
+	</view>
+</template>
+
+<script>
+	import {
+		bindMerc
+	} from "@/api/commodity/mercGoods.js"
+	import {
+		bindDeviceByGoods
+	} from "@/api/commodity/goods.js"
+	export default {
+		data() {
+			return {
+				commList: [],
+				type: null,
+				storeName: null,
+				id: null
+			}
+		},
+		onLoad(o) {
+			if (o.type == 0) { //公库添加到私库
+				this.type = 0
+			} else if (o.type == 1) { //私库添加到设备
+				this.type = 1;
+				this.id = o.id;
+			} else if (o.type == 2) { //公库添加到设备
+				this.type = 2;
+				this.id = o.id;
+			}
+			this.storeName = o.storeName;
+			let commStor = JSON.parse(uni.getStorageSync(o.storeName))
+			this.commList = commStor.map(i => {
+				i.capacity = 100
+				i.warning = 5
+				return i
+			})
+		},
+		methods: {
+			sure() {
+				if (this.type == 0) { //添加到私库
+					let params = this.delParams()
+					if (!params) return
+					bindMerc(params).then(res => {
+						if (res.code == 200) {
+							this.$modal.msg('添加成功~')
+							uni.setStorageSync(this.storeName, '') //清空购物车
+							this.$tab.reLaunch('/pages/globalPages/home?tabName=商品')
+						}
+					})
+				} else { //添加到设备
+					let params = this.delParams()
+					if (!params) return
+					let assignParams = {
+						deviceIds: [this.id],
+						goodsList: params,
+					}
+					bindDeviceByGoods(assignParams).then(res => {
+						if (res.code == 200) {
+							this.$modal.msg('添加成功~')
+							uni.setStorageSync(this.storeName, '') //清空购物车
+							setTimeout(() => {
+								// this.$tab.redirectTo(`/pages/equipment/comManage?id=${this.id}`)
+								let pageList=getCurrentPages()
+								let delta=2
+								pageList.forEach(i=>{
+									if(i.route=='pages/globalPages/allGoodsSearch'){
+										delta=3
+									}
+								})
+								uni.navigateBack({
+									delta: delta
+								})
+							}, 1000)
+						}
+					})
+				}
+			},
+
+			delParams() {
+				let params = [];
+				for (let i = 0; i < this.commList.length; i++) {
+					let item = this.commList[i];
+					let obj = {};
+					obj.goodsId = this.type == 1 ? item.goodsId : item.id;
+					if (this.type != 1) { //非私库添加需要输入成本价格
+						if (item.priceCost != undefined && item.priceCost != null) {
+							obj.priceCost = item.priceCost * 100;
+						} else {
+							this.$modal.msg('请输入成本价!')
+							return false
+						}
+					}
+
+					if (item.price != undefined && item.price != null) {
+						obj.price = item.price * 100;
+					} else {
+						this.$modal.msg('请输入零售价格!')
+						return false
+					}
+
+					if (item.price != undefined && item.price != null && item.priceCost != undefined && item.priceCost !=
+						null) {
+						obj.price = item.price * 100;
+						if (obj.price < obj.priceCost) {
+							this.$modal.msg('零售价不能低于成本价!')
+							return false
+						}
+					}
+
+					if (item.capacity != undefined && item.capacity != null) {
+						obj.capacity = item.capacity;
+					} else {
+						// this.$modal.msg('请输入商品容量!')
+						// return false
+					}
+					if (item.warning != undefined && item.warning != null) {
+						obj.warning = item.warning;
+					} else {
+						// this.$modal.msg('请输入预警值!')
+						// return false
+					}
+					params.push(obj)
+				}
+				return params
+			},
+
+
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.container {
+		position: relative;
+
+		.content {
+			padding: 12rpx 24rpx 108rpx;
+
+			.xy-card+.xy-card {
+				margin-top: 18rpx;
+			}
+
+			.tag {
+				position: absolute;
+				right: 12rpx;
+				top: 12rpx;
+			}
+
+			.comm-item {
+				display: flex;
+				flex-direction: row;
+				justify-content: flex-start;
+				align-items: center;
+				background-color: #fff;
+				border-radius: 12rpx;
+				box-sizing: border-box;
+				padding: 12rpx;
+
+				.image {
+					width: 130rpx;
+				}
+
+				.item-content {
+					padding-left: 24rpx;
+					color: #666;
+					width: 530rpx;
+
+					.item-top {
+						>view:nth-child(1) {
+							font-size: 30rpx;
+							font-weight: bold;
+							color: #333;
+						}
+
+						>view:nth-child(2) {
+							font-size: 28rpx;
+							margin-top: 12rpx;
+						}
+
+					}
+
+					.item-input-wrap {
+						display: flex;
+						flex-flow: row wrap;
+						justify-content: space-between;
+
+						.item-input {
+							width: 100%;
+							display: flex;
+							flex-flow: row nowrap;
+							align-items: center;
+							justify-content: flex-start;
+							margin-top: 12rpx;
+
+							.input-label {
+								width: 160rpx;
+								font-size: 30rpx;
+								line-height: 40rpx;
+							}
+
+							.input-box {
+								width: 320rpx;
+								height: 70rpx;
+								line-height: 70rpx;
+
+								.input {
+									height: 70rpx;
+									border: 1rpx solid #eee;
+									box-sizing: border-box;
+									padding: 0 12rpx;
+								}
+							}
+						}
+					}
+				}
+			}
+		}
+
+		.btn {
+			position: fixed;
+			width: 100%;
+			padding: 24rpx;
+			left: 0;
+			bottom: 0;
+		}
+	}
+</style>

+ 456 - 0
pages/equipment/comManage.vue

@@ -0,0 +1,456 @@
+<template>
+	<view class="container">
+		<u-navbar leftIconColor="#fff" titleStyle="color:#fff;fontSize:36rpx;" :autoBack="true" bgColor="#2C6FF3"
+			:placeholder="true" :title="title"></u-navbar>
+		<view class="content">
+			<!-- <view class="search">
+				<u-search animation placeholder="商品搜索" v-model="keyword" :showAction="false" search="search"></u-search>
+			</view> -->
+			<view class="list" v-if="commList&&commList.length>0">
+				<view class="thumb-box" v-for="(item, index) in commList" :key="item.id"
+					@click.stop="commItemSelect(item)">
+					<view>
+						<image class="select-img"
+							src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/commodity/selected.png"
+							mode="widthFix" v-show="item.checked"></image>
+						<image class="select-img"
+							src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/commodity/select.png"
+							mode="widthFix" v-show="!item.checked"></image>
+					</view>
+					<view class="check-content">
+						<view class="comm-img">
+							<u--image width="130rpx" height="130rpx" :src="item.cover" mode="aspectFit"
+								:lazy-load="true"></u--image>
+						</view>
+						<view class="comm-main">
+							<view>
+								{{item.name}}
+							</view>
+							<view>
+								条形码:{{item.barcode}}
+							</view>
+							<view>
+								商品ID:{{item.id}}
+							</view>
+							<view>
+								商品SKUID:{{item.skuId}}
+							</view>
+							<!-- <view class="c-cx">
+								促销活动:
+							</view> -->
+							<view class="c-pri">
+								价格:<text>¥{{$xy.delMoney(item.price)}}</text>
+								<view @click.stop="edit(item)">修改</view>
+							</view>
+						</view>
+
+						<view class="status" v-if="item.name">
+							<view class="s-name">
+								在售/补后
+							</view>
+							<view class="s-num">
+								{{item.stock}}/{{item.fillCount}}
+							</view>
+						</view>
+
+						<view class="status" v-else>
+							补货时请移除
+						</view>
+
+						<!-- <view class="sale" v-if="item.name">
+							<view>
+								今日销售额
+							</view>
+							<view>
+								¥<text>{{item.todaySalesMoney||0}}</text>
+							</view>
+						</view>
+
+						<view class="sale" v-else>
+							已删除商品
+						</view> -->
+					</view>
+				</view>
+
+				<u-loadmore :status="status" v-if="commList.length>=1" />
+			</view>
+
+			<view class="empty" v-else>
+				<u-empty></u-empty>
+			</view>
+		</view>
+
+		<view class="refresh" @click="search">
+			刷新
+		</view>
+
+		<view class="btn safe-bottom">
+			<xbutton width="340rpx" size="large" @click="del">删除商品</xbutton>
+			<xbutton width="340rpx" size="large" @click="add">增加商品</xbutton>
+		</view>
+
+		<xpopup :show="editPriceShow" @close="editPriceClose" @confirm="submit" :showBtn="true" title="修改价格">
+			<view class="pop-content">
+				<u--input placeholder="请输入内容" type="digit" border="surround" v-model="price"></u--input>
+			</view>
+		</xpopup>
+	</view>
+</template>
+
+<script>
+	import {
+		goodsList
+	} from '@/api/device/device.js'
+	import {
+		delGoods
+	} from "@/api/replenishment/replenishment.js"
+	import {
+		changePrice
+	} from '@/api/commodity/goodsMode.js'
+
+	export default {
+		data() {
+			return {
+				keyword: '',
+				commList: [],
+				id: null, //设备id
+				page: 1,
+				size: 10,
+				editPriceShow: false,
+				price: 0,
+				goodsId: null,
+				deviceName: null,
+				title: null
+			}
+		},
+		onLoad(o) {
+			if (o.id) {
+				this.id = o.id;
+				this.deviceName = o.deviceName;
+				this.title = '商品列表-' + this.deviceName
+
+			}
+		},
+
+		onShow() {
+			this.reset()
+			this.getList()
+		},
+
+		methods: {
+			search(val) {
+				this.reset()
+				this.getList()
+			},
+			// 商品选中状态改变
+			commItemSelect(e) {
+				if (e.name == '未知商品' || e.name == '非友好购物商品') {
+					this.$modal.msg('预设商品不能删除!!!')
+					return
+				}
+				e.checked = !e.checked;
+			},
+
+			edit(item) {
+				this.goodsId = item.id;
+				this.editPriceShow = true;
+				this.price = (Number(item.price) / 100).toFixed(2)
+			},
+
+			//删除商品
+			del() {
+				let delComList = [];
+				this.commList.forEach(i => {
+					if (i.checked) {
+						delComList.push(i.goodsId)
+					}
+				})
+
+				if (delComList.length == 0) {
+					this.$modal.msg('请选择需要删除的商品~')
+					return
+				}
+				uni.showModal({
+					title: '提示',
+					content: '是否确认删除',
+					success: res => {
+						if (res.confirm) {
+							delGoods({
+								deviceId: this.id,
+								goodsIds: delComList
+							}).then(res => {
+								this.$modal.confirm('删除成功,请拿走货架上当前商品。').then(resolve => {
+									console.log('确认拿走~')
+								})
+								this.reset()
+								this.getList()
+							})
+						} else if (res.cancel) {
+							console.log('用户点击取消');
+						}
+					}
+				});
+			},
+
+			add() {
+				this.$tab.navigateTo(`/pages/equipment/addCom?id=${this.id}`)
+			},
+
+			reset() {
+				this.status == 'loadmore'
+				this.page = 1;
+				this.commList = [];
+			},
+
+			getList() {
+				goodsList({
+					page: {
+						current: this.page,
+						size: this.size
+					},
+					deviceId: this.id
+				}).then(res => {
+					let data = res.data.records;
+					for (let i = 0; i < data.length; i++) {
+						let item = data[i];
+						item.checked = false;
+					}
+					if (data.length < 10) {
+						this.status = "nomore"
+					} else {
+						this.status = "loadmore"
+					}
+					this.commList = this.commList.concat(data)
+				})
+			},
+
+			onReachBottom() {
+				if (this.status == 'nomore') return
+				this.page++
+				this.getList()
+			},
+
+			editPriceClose() {
+				this.editPriceShow = false;
+			},
+
+			submit() {
+				if (this.price) {
+					changePrice({
+						id: this.goodsId,
+						price: Number(this.price) * 100
+					}).then(res => {
+						if (res.code == 200) {
+							this.$modal.msg('修改成功~')
+							this.reset()
+							this.getList()
+						}
+					}).catch(err => {})
+				} else {
+					this.$modal.msg('商品价格不能为空!')
+				}
+				this.editPriceClose()
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.container {
+		.content {
+			padding-bottom: 88rpx;
+
+			.search {
+				padding: 24rpx 24rpx;
+				background-color: #fff;
+			}
+
+			.list {
+				width: 100%;
+				padding: 12rpx 24rpx;
+
+				.thumb-box {
+					margin-bottom: 12rpx;
+					border-bottom: 1rpx solid #f4f4f4;
+					display: flex;
+					flex-flow: row nowrap;
+					padding: 12rpx 12rpx;
+					align-items: center;
+					background-color: #fff;
+					border-radius: 12rpx;
+				}
+
+				.select-img {
+					width: 40rpx;
+					height: 40rpx;
+				}
+
+				.check-content {
+					width: 100%;
+					display: flex;
+					flex-direction: row;
+					align-items: center;
+					padding-left: 12rpx;
+					position: relative;
+
+					.comm-img {
+						width: 130rpx;
+						height: 130rpx;
+						display: flex;
+						flex-direction: row;
+						align-items: center;
+						justify-content: space-around;
+
+						image {
+							width: 100%;
+						}
+					}
+
+					.comm-main {
+						box-sizing: border-box;
+						padding-left: 18rpx;
+						color: #999;
+
+						>view {
+							padding: 8rpx 0;
+							width: 330rpx;
+						}
+
+						>view:nth-child(1) {
+							font-size: 28rpx;
+							font-weight: bold;
+							color: #333;
+							padding: 12rpx 0 8rpx 0;
+						}
+
+						>view:nth-child(2) {
+							width: 450rpx;
+							font-size: 26rpx;
+						}
+
+						>view:nth-child(3) {
+							width: 450rpx;
+							font-size: 26rpx;
+						}
+
+						>view:nth-child(4) {
+							width: 450rpx;
+							font-size: 26rpx;
+							padding: 0;
+							line-height: 40rpx;
+						}
+
+						>.c-cx {
+							font-size: 26rpx;
+						}
+
+						>.c-pri {
+							font-size: 26rpx;
+
+							text {
+								font-weight: bold;
+								color: red;
+								font-size: 30rpx;
+							}
+
+							>view {
+								display: inline-block;
+								font-size: 26rpx;
+								color: #2C6FF3;
+								text-decoration: underline;
+								margin-left: 12rpx;
+							}
+						}
+					}
+
+					.status {
+						width: 120rpx;
+						height: 120rpx;
+						box-sizing: border-box;
+						border-radius: 120rpx;
+						// border: 6rpx solid #2C6FF3;
+						text-align: center;
+						display: flex;
+						flex-flow: column;
+						justify-content: space-around;
+						align-items: center;
+						position: absolute;
+						right: 12rpx;
+						top: -12rpx;
+
+						.s-name {
+							font-size: 24rpx;
+							padding-top: 16rpx;
+							font-weight: bold;
+						}
+
+						.s-num {
+							font-size: 28rpx;
+							padding-bottom: 20rpx;
+						}
+					}
+
+					.sale {
+						position: absolute;
+						right: 12rpx;
+						bottom: 12rpx;
+						text-align: center;
+						color: #999;
+
+						>view:nth-child(1) {
+							font-size: 24rpx;
+						}
+
+						>view:nth-child(2) {
+							font-size: 32rpx;
+							color: #2C6FF3;
+							font-weight: bold;
+						}
+					}
+				}
+			}
+		}
+
+		.empty {
+			position: absolute;
+			left: 50%;
+			top: 50%;
+			transform: translate(-50%, -50%);
+		}
+
+		.refresh {
+			width: 80rpx;
+			height: 80rpx;
+			border-radius: 80rpx;
+			text-align: center;
+			line-height: 80rpx;
+			position: fixed;
+			right: 24rpx;
+			bottom: 230rpx;
+			background-color: #999;
+			color: #fff;
+			opacity: .8;
+		}
+
+		.btn {
+			width: 100%;
+			position: fixed;
+			bottom: 24rpx;
+			left: 0;
+			display: flex;
+			flex-flow: row nowrap;
+			justify-content: space-between;
+			padding: 0 24rpx;
+
+			.cu-btn {
+				background-color: #2C6FF3;
+				color: #fff;
+				width: 48%;
+			}
+		}
+
+		.pop-content {
+			padding: 24rpx;
+		}
+	}
+</style>

+ 352 - 0
pages/equipment/components/xy-filtedrop/index.vue

@@ -0,0 +1,352 @@
+<template>
+	<view>
+		<view class="HMfilterDropdown" :class="{'setDropdownBottom':maskVisibility}" :style="{'top':menuTop+'rpx'}"
+			@touchmove.stop.prevent="discard" @tap.stop="discard">
+			<!-- 顶部菜单 -->
+			<view class="nav">
+				<block v-for="(item,index) in menu" :key="index">
+					<view class="first-menu" :class="{'on':showPage==index}" @tap="togglePage(index)">
+						<text class="name">{{item}}</text>
+						<text class="iconfont triangle" :style="'transform:rotate('+getDeg(index)+'deg);'"></text>
+					</view>
+				</block>
+			</view>
+		</view>
+		<!-- 弹框 -->
+		<xpopup :show="popShow" @close="close" @confirm="submit" :showBtn="false" :title="'请选择区域'">
+			<!-- 区域选择 -->
+			<scroll-view style="height: 600rpx;" scroll-y scroll-with-animation>
+				<view class="popup-content">
+					<tki-tree v-if="areaList&&areaList.length>0" style="width:100%;" :range="areaList" :foldAll="false"
+						rangeKey="name" idKey="name" buttonName="选中" @btnClick="areaSubmit">
+					</tki-tree>
+					<view class="empty" v-else>
+						<u-empty mode="data" text="您还未规划区域~"></u-empty>
+					</view>
+				</view>
+			</scroll-view>
+		</xpopup>
+
+		<!-- 线路/标签 -->
+		<u-picker :show="pickerShow" @confirm="pickerConfirm" :closeOnClickOverlay="true" :columns="columns"
+			keyName="label" @close="pickerClose" @cancel="pickerClose"></u-picker>
+	</view>
+</template>
+<script>
+	import tkiTree from "@/components/tki-tree/tki-tree.vue";
+
+	import {
+		areaTree
+	} from "@/api/point/area"
+
+	import {
+		linePage
+	} from "@/api/point/line"
+
+	export default {
+		name: 'FilterDrop',
+		components: {
+			tkiTree
+		},
+		data() {
+			return {
+				menu: ['类型', '缺货状态', '区域', '线路'], //顶部菜单数据
+				showPage: -1, //菜单页面显示/隐藏动画控制
+				popShow: false,
+				areaList: [], //区域
+				areaId: undefined, //区域id
+				regionName: undefined, //区域名称
+				lineId: undefined, //线路id
+				columns: [], //options
+				pickerShow: false,
+
+				confirmData: ['', '', '', '']
+			}
+		},
+		props: {
+			menuTop: {
+				type: Number,
+				require: false,
+				default: 0
+			}
+		},
+		created() {
+			//获取区域树
+			this.getTree()
+		},
+		methods: {
+			//获取区域树
+			getTree() {
+				areaTree().then(res => {
+					this.areaList = res.data
+				})
+			},
+			//菜单开关
+			togglePage(index) {
+				this.showPage = this.showPage == index ? -1 : index;
+				switch (index) {
+					case 0:
+						this.pickerShow = true;
+						this.columns = [
+							[{
+									name: '',
+									label: '全部'
+								},
+								{
+									name: 1,
+									label: '开门柜'
+								},
+								{
+									name: 2,
+									label: '重力柜'
+								}
+							]
+						]
+						break
+					case 1:
+						this.columns = [
+							[{
+									name: '',
+									label: '全部'
+								},
+								{
+									name: 1,
+									label: '缺货'
+								},
+								{
+									name: 2,
+									label: '不缺'
+								}
+							]
+						]
+						this.pickerShow = true;
+						break
+					case 2:
+						this.popShow = true;
+						break
+					case 3:
+						if (this.areaId) {
+							this.pickerShow = true;
+						} else {
+							this.showPage = -1;
+							this.$modal.msg('请先选择线路~')
+						}
+						break
+					default:
+				}
+			},
+
+			//获取三角形角度
+			getDeg(index) {
+				let deg = 0;
+				deg = this.showPage == index ? 180 : 0;
+				return deg
+			},
+
+			//区域选择提交
+			areaSubmit(res) {
+				this.menu[this.showPage] = res.name;
+				this.regionName = res.name;
+				this.areaId = res.key; //id
+				this.confirmData[2] = res.key;
+				this.popShow = false;
+				this.showPage = -1;
+				this.columns = [];
+				this.menu[3] = '线路';
+				this.getLineOption()
+				this.confirm()
+			},
+
+			//获取线路options
+			getLineOption() {
+				linePage({
+					page: {
+						current: 1,
+						size: 1000,
+					},
+					regionName: this.regionName
+				}).then(res => {
+					let data = res.data.records;
+					let newData = data.map(i => {
+						return {
+							id: i.id,
+							label: i.lineName
+						}
+					})
+					this.columns = [newData];
+				})
+			},
+
+			//弹框关闭
+			close() {
+				this.popShow = false;
+				this.showPage = -1;
+			},
+
+			//picker弹框确认
+			pickerConfirm(e) {
+				console.log('eeeeeeeeeeeee', e)
+				this.menu[this.showPage] = e.value[0].label;
+				this.confirmData[this.showPage] = e.value[0].name;
+				if (this.showPage == 3) {
+					this.menu[this.showPage] = e.value[0].label;
+					this.confirmData[this.showPage] = e.value[0].id;
+				}
+				this.pickerShow = false;
+				this.showPage = -1;
+				this.confirm()
+			},
+
+			//picker关闭
+			pickerClose() {
+				this.pickerShow = false;
+				this.showPage = -1;
+			},
+
+			confirm() {
+				// 输出
+				this.$emit('confirm', this.confirmData);
+			},
+
+			discard() {
+
+			}
+		}
+	}
+</script>
+<style lang="scss">
+	.HMfilterDropdown {
+		flex-shrink: 0;
+		width: 100%;
+		position: fixed;
+		// position: sticky;
+		z-index: 997;
+		flex-wrap: nowrap;
+		display: flex;
+		flex-direction: row;
+		top: var(--window-top);
+		left: 0;
+		// top:100px;
+		overflow-y: hidden;
+
+		&.setDropdownBottom {
+			// height: 345px;
+			bottom: 0;
+		}
+
+		view {
+			display: flex;
+			flex-wrap: nowrap;
+		}
+	}
+
+	.region {
+		flex: 1;
+		height: 44px;
+	}
+
+	.nav {
+		width: 100%;
+		height: 44px;
+		border-bottom: solid 1rpx #eee;
+		z-index: 12;
+		background-color: #ffffff;
+		flex-direction: row;
+
+		.first-menu {
+			width: 100%;
+			font-size: 13px;
+			color: #757575;
+			flex-direction: row;
+			align-items: center;
+			justify-content: center;
+			transition: color .2s linear;
+
+			&.on {
+				color: #2C6FF3;
+
+				.iconfont {
+					color: #2C6FF3;
+				}
+			}
+
+			.name {
+				height: 20px;
+				text-align: center;
+				text-overflow: clip;
+				overflow: hidden;
+			}
+
+			.iconfont {
+				width: 13px;
+				height: 13px;
+				align-items: center;
+				justify-content: center;
+				transition: transform .2s linear, color .2s linear;
+			}
+		}
+	}
+
+	@font-face {
+		font-family: "HM-FD-font";
+		src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAALAAAsAAAAABpQAAAJzAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCDBgp4gQIBNgIkAwwLCAAEIAWEbQc5G8sFERWMIbIfCbbzqA4hp7InSBibVsYGb4J42o82b3e/nJlHMw/NHbGOlwKJRCRpwzPtpAECCOZubdqxjYpQLMlVg+70/08edrgQOtx2ukpVyApZn+dyehPoQObHo3O85rYx9vOjXoBxQIHugW2yIkqIW2QXcScu4jwE8CSWbKSmrqUHFwOaJoCsLM5P4haSGIxRcRHshrUGucLCVcfqI3AZfV/+USguKCwNmtsxVztDxU/n55C+3W0Z4QQpEOTNFqCBbMCAjDUWB9CIwWk87aa70cYgqLkyd3dEmm+18R8eKATEBrV7A5CulBT8dKiWOYZk412XNcDdKSEKSGODnyKIDl+dmVt9/Dx4pu/xyeutkMlHISGPTsPCnoTNP9nOT6wTtDdlO6dPr47efvj942lkYuQzrhMKEjq9N6y98P3340gmlJ/RStUD6F31CAEEPtUW94/7rf+7XgaAz57X0ZHXAGsFFwVgw38yALuMb0IBbVyNamFYEw4oKMDTj3AHRQP5Pt4dci9VwSVkRNQh5r7CLskZadhsWHhRDBsXczk8ZYk3ewnCxmQeQKa3BOHvA8XXO2j+vqRhf7CE+sPmn4anvoL29JLa4qqaUQkmoK+QG2osCckq7txi2leK86aIPyJ3eQZ8xytXYmyQ51jQndJAxIJlqiGSLsOqImiZCjTiZCJt6Lq26U2OoXqwUo0hRaAE0K5AziANy/uLVeXzWyjVqyjcoeupjxDr5MMDn8MDkLG9Aenu5ZrOSSoghAUsRmogkkahSoWAtnlUARnCkY3It0Iu7mWhdmd9Z/19BwBP6GidEi0G56opckXTGZVSPxgAAAA=');
+	}
+
+	.iconfont {
+		font-family: "HM-FD-font" !important;
+		font-size: 13px;
+		font-style: normal;
+		color: #757575;
+
+		&.triangle {
+			&:before {
+				content: "\e65a";
+			}
+		}
+
+		&.selected {
+			&:before {
+				content: "\e607";
+			}
+		}
+	}
+
+	.popup-content {
+		padding: 36rpx 24rpx;
+		display: flex;
+		flex-flow: row nowrap;
+		justify-content: flex-start;
+		align-items: center;
+		position: relative;
+
+		input {
+			border: 1rpx solid #999;
+			border-radius: 6rpx;
+			width: 530rpx;
+			padding: 0 24rpx;
+		}
+
+		&.edit-point {
+			flex-direction: column;
+
+			>view {
+				display: flex;
+				flex-flow: row nowrap;
+				justify-content: flex-start;
+				align-items: center;
+
+				&+view {
+					margin-top: 12rpx;
+				}
+
+				>view {
+					width: 170rpx;
+				}
+			}
+		}
+
+		.empty {
+			margin: 0 auto;
+		}
+	}
+</style>

+ 1779 - 0
pages/equipment/detail.vue

@@ -0,0 +1,1779 @@
+<template>
+	<view class="container">
+		<u-navbar leftIconColor="#fff" titleStyle="color:#fff;fontSize:36rpx;" :autoBack="true" bgColor="#2C6FF3"
+			:placeholder="true" title="设备详情">
+		</u-navbar>
+		<view class="content">
+			<view class="xy-card">
+				<view class="top">
+					<view>
+						<view class="t-left">
+							<view v-if="detail.deviceName">{{detail.deviceName}}<text
+									style="color: #666;font-size: 26rpx;">({{detail.deviceId}})</text></view>
+							<view v-else>{{detail.deviceId}}</view>
+						</view>
+
+						<view class="flex" style="white-space: nowrap;">
+							<view :class="[detail.netStateName=='在线'?'t-right':'t-right off']">
+								{{detail.netStateName||'离线'}}
+							</view>
+							<view :class="[detail.busyState==1?'t-right':'t-right off']">
+								{{detail.busyState==1?'运营中':'已停运'}}
+							</view>
+						</view>
+
+					</view>
+					<view style="margin-top: 24rpx;">
+						<xbutton size="mini" style="margin-right:12rpx;" bgColor="#fff" borderColor="#2C6FF3"
+							color="#2C6FF3" @click="downloadQr">下载柜门二维码</xbutton>
+						<xbutton size="mini" @click="btnClick('编辑')">编辑</xbutton>
+					</view>
+				</view>
+				<view :class="[isMore?'center center-more':'center']">
+					<view class="d-line">
+						<view class="c-item">
+							<view class="name">
+								编号:
+							</view>
+							<view class="val">
+								{{detail.deviceId||'/'}}
+								<text @click="copy(detail.deviceId)" v-if="detail.mercDeviceCode">复制</text>
+							</view>
+						</view>
+						<view class="c-item">
+							<view class="name">
+								商户:
+							</view>
+							<view class="val">
+								{{detail.mercName||'/'}}
+							</view>
+						</view>
+					</view>
+					<view class="d-line">
+						<view class="c-item">
+							<view class="name">
+								温度:
+							</view>
+							<view class="val">
+								{{detail.deviceStatus.tempValue}}℃
+							</view>
+						</view>
+						<view class="c-item">
+							<view class="name">
+								信号强度:
+							</view>
+							<view class="val net">
+								{{detail.deviceStatus.netDbm||'/'}}d<text
+									v-if="detail.deviceStatus.netDbm<-90">(弱)</text>
+								<text
+									v-else-if="detail.deviceStatus.netDbm>-90&&detail.deviceStatus.netDbm<-40">(中)</text>
+								<text
+									v-else-if="detail.deviceStatus.netDbm>-40&&detail.deviceStatus.netDbm<0">(强)</text>
+								<text v-else-if="detail.deviceStatus.netDbm==0">(无)</text>
+							</view>
+						</view>
+					</view>
+					<view class="c-item">
+						<view class="name">
+							软件版本:
+						</view>
+						<view class="val">
+							{{detail.deviceSysinfo.appUpmVersion||'/'}}
+						</view>
+					</view>
+					<view class="c-item">
+						<view class="name">
+							最后更新时间:
+						</view>
+						<view class="val">
+							{{detail.updateTime||'/'}}
+						</view>
+					</view>
+					<view class="c-item">
+						<view class="name">
+							激活状态:
+						</view>
+						<view class="val" v-if="detail.activeState==1">
+							已激活
+						</view>
+						<view class="val" v-else>
+							未激活
+						</view>
+					</view>
+					<view class="c-item">
+						<view class="name">
+							激活时间:
+						</view>
+						<view class="val">
+							{{detail.activeTime||'/'}}
+						</view>
+					</view>
+					<view class="c-item">
+						<view class="name">
+							区域:
+						</view>
+						<view class="val">
+							{{detail.districtName||'/'}}
+						</view>
+					</view>
+					<view class="d-line">
+						<view class="c-item">
+							<view class="name">
+								点位:
+							</view>
+							<view class="val">
+								{{detail.placeName||'/'}}
+							</view>
+						</view>
+						<view class="c-item">
+							<view class="name">
+								线路:
+							</view>
+							<view class="val">
+								{{detail.placeLineName||'/'}}
+							</view>
+						</view>
+					</view>
+
+					<view class="c-item">
+						<view class="name">
+							设备位置:
+						</view>
+						<view class="val" @click="showPos" style="width:120rpx;">
+							<u-icon slot="right" size="20" name="map"></u-icon>
+						</view>
+					</view>
+
+					<view class="unfold" @click="isMore=!isMore">
+						<u-icon :name="isMore?'arrow-down':'arrow-up'" color="#999" size="20"></u-icon>
+					</view>
+				</view>
+
+				<view class="bot">
+					<view>
+						<button class="cu-btn cu-btn1" style="width:150rpx;" @click="btnClick('设备重启')">设备重启</button>
+					</view>
+					<view>
+						<button class="cu-btn cu-btn2" style="width:140rpx;" @click="btnClick('声音设置')">声音设置</button>
+					</view>
+					<view>
+						<button class="cu-btn cu-btn2" style="width: 140rpx;" @click="btnClick('温度设置')">温度设置</button>
+					</view>
+					<view>
+						<button class="cu-btn cu-btn2" style="width: 140rpx;" @click="btnClick('灯光设置')">灯光设置</button>
+					</view>
+
+				</view>
+				<view class="bot">
+					<view>
+						<button class="cu-btn cu-btn2" style="width: 140rpx;" @click="btnClick('清除故障')">清除故障</button>
+					</view>
+					<view>
+						<button class="cu-btn cu-btn3" style="width: 140rpx;"
+							@click="btnClick(btnState)">{{btnState}}</button>
+					</view>
+				</view>
+			</view>
+
+			<view class="xy-card" style="width:750rpx;margin-left: -24rpx;border-radius: 0;">
+				<u-tabs :list="chartTab" :scrollable="false" @click="tabClick" lineColor="#2C6FF3"></u-tabs>
+			</view>
+			<view class="xy-card">
+				<view class="chart-content">
+					<view class="total" v-if="current=='经营数据'">
+						<view style="width:200rpx;">
+							<u-subsection activeColor="#2C6FF3" :list="totalTab" :current="time" @change="totalChange">
+							</u-subsection>
+						</view>
+						<view class="t-content">
+							<view class="t-item">
+								<view class="t-name">
+									销售额
+								</view>
+								<view class="t-num">
+									¥<text>{{$xy.delMoney(countData.salesMoney)}}</text>
+								</view>
+							</view>
+							<view class="t-item">
+								<view class="t-name">
+									订单数
+								</view>
+								<view class="t-num">
+									<text>{{countData.salesCount||0}}</text>
+								</view>
+							</view>
+							<view class="t-item">
+								<view class="t-name">
+									退款
+								</view>
+								<view class="t-num">
+									¥<text>{{$xy.delMoney(countData.refundMoney)}}</text>
+								</view>
+							</view>
+						</view>
+
+						<view class="more-data">
+							<xbutton width="140" @click="more">
+								销售统计
+							</xbutton>
+						</view>
+					</view>
+					<view class="chart" style="height: 600rpx;" v-else>
+						<qiun-data-charts canvasId="canvasString" :canvas2d="true" :ontouch="true" type="line"
+							:opts="opts" :chartData="chartData" :errorMessage="errorMessage" />
+					</view>
+				</view>
+			</view>
+
+			<view class="xy-card">
+				<view class="title">
+					商品管理
+				</view>
+				<view class="t-content">
+					<view class="t-item">
+						<view class="t-name">
+							在售商品种类
+						</view>
+						<view class="t-num">
+							<text>{{goodsManageData.categoryNum||0}}</text>种
+						</view>
+					</view>
+					<view class="t-item">
+						<view class="t-name">
+							在售库存
+						</view>
+						<view class="t-num">
+							<text>{{goodsManageData.stock||0}}</text>
+						</view>
+					</view>
+					<view class="t-item">
+						<view class="t-name">
+							上次补货后库存
+						</view>
+						<view class="t-num">
+							<text>{{goodsManageData.afterFillStock||0}}</text>
+						</view>
+					</view>
+				</view>
+				<view class="bot bot1">
+					<button class="cu-btn cu-btn2" style="width:140rpx;"
+						@click="$tab.navigateTo(`/pages/equipment/comManage?id=${id}&deviceName=${detail.deviceName}`)">管理商品</button>
+					<button class="cu-btn cu-btn2" style="width: 140rpx;margin:0 12rpx;"
+						@click="$tab.navigateTo(`/pages/replenish/replenishmentRecord?id=${id}`)">补货记录</button>
+					<button class="cu-btn cu-btn3" style="width: 140rpx;"
+						@click="$tab.navigateTo(`/pages/replenish/replenishmentHomePage?id=${id}&&deviceName=${detail.deviceName}`)">补货</button>
+				</view>
+			</view>
+
+			<xpopup :show="qrcodeShow" mode="center" @close="qrcodeClose" :showBtn="false">
+				<view class="qrcode-content flex flex-direction align-center">
+					<view class="canvas-box">
+						<u--image width="400rpx" height="400rpx" :src="qrCodeImg" mode="aspectFit"
+							:lazy-load="true"></u--image>
+					</view>
+					<view class="save-qrcode" slot="botton">
+						<xbutton @click="handleImgSave">保存二维码</xbutton>
+					</view>
+				</view>
+			</xpopup>
+
+			<!-- <view class="xy-card">
+				<view class="title">
+					在线情况
+				</view>
+				<scroll-view style="height: 410rpx;" scroll-y>
+					<view class="l-content" v-if="netRecords&&netRecords.length>0">
+						<view class="l-item" v-for="(item,index) in netRecords" :key="item.id">
+							<view class="l-time">
+								{{item.createTime}}
+							</view>
+							<view class="l-status">
+								{{item.netStatus}}
+							</view>
+						</view>
+					</view>
+					<view class="l-content" v-else>
+						<u-empty text="没有记录~"></u-empty>
+					</view>
+				</scroll-view>
+			</view>
+
+			<view class="xy-card">
+				<view class="title">
+					锁异常记录
+				</view>
+				<scroll-view style="height: 410rpx;" scroll-y>
+					<view class="l-content" v-if="errRecordLock&&errRecordLock.length>0">
+						<view class="l-item" v-for="(item,index) in errRecordLock" :key="item.id">
+							<view class="l-time">
+								{{item.createTime}}
+							</view>
+							<view class="l-status">
+								{{item.errorDescript}}
+							</view>
+						</view>
+					</view>
+					<view class="l-content" v-else>
+						<u-empty text="设备状态很好,没有异常~"></u-empty>
+					</view>
+				</scroll-view>
+			</view>
+
+			<view class="xy-card">
+				<view class="title">
+					摄像头故障记录
+				</view>
+				<scroll-view style="height: 410rpx;" scroll-y>
+					<view class="l-content" v-if="errRecordCamera&&errRecordCamera.length>0">
+						<view class="l-item" v-for="(item,index) in errRecordCamera" :key="item.id">
+							<view class="l-time">
+								{{item.createTime}}
+							</view>
+							<view class="l-status">
+								{{item.errorDescript}}
+							</view>
+						</view>
+					</view>
+					<view class="l-content" v-else>
+						<u-empty text="设备状态很好,没有任何故障~"></u-empty>
+					</view>
+				</scroll-view>
+			</view>
+
+			<view class="xy-card">
+				<view class="title">
+					温度记录
+				</view>
+				<scroll-view style="height: 410rpx;" scroll-y>
+					<view class="l-content" v-if="deviceTempRecords&&deviceTempRecords.length>0">
+						<view class="l-item" v-for="(item,index) in deviceTempRecords" :key="item.id">
+							<view class="l-time">
+								{{item.createTime}}
+							</view>
+							<view class="l-status">
+								温度为:{{item.tempValue}}
+							</view>
+						</view>
+					</view>
+					<view class="l-content" v-else>
+						<u-empty text="没有温度信息~"></u-empty>
+					</view>
+				</scroll-view>
+			</view>
+
+			<view class="xy-card">
+				<view class="title">
+					操作记录
+				</view>
+				<scroll-view style="height: 410rpx;" scroll-y>
+					<view class="l-content" v-if="operaRecords&&operaRecords.length>0">
+						<view class="l-item" v-for="(item,index) in operaRecords" :key="item.id">
+							<view class="l-time">
+								{{item.createTime}}
+							</view>
+							<view class="l-status">
+								{{item.changeBefore}}变更为{{item.changeAfter}}
+							</view>
+						</view>
+					</view>
+					<view class="l-content" v-else>
+						<u-empty text="没有任何记录~"></u-empty>
+					</view>
+				</scroll-view>
+			</view> -->
+
+			<xpopup :show="show" @close="close" @confirm="submit" :showBtn="true" :title="title">
+				<!-- 设备编辑 -->
+				<view class="popup-content" v-if="title=='编辑'">
+					<u--form labelPosition="left" :model="editForm">
+						<u-form-item labelWidth="90" label="名称" prop="name" borderBottom ref="item1">
+							<u--input placeholder="设置机器名称" v-model="editForm.name" border="none"></u--input>
+						</u-form-item>
+						<u-form-item labelWidth="90" label="区域" prop="areaName" borderBottom ref="item1"
+							@click.native="areaChoose">
+							<u--input placeholder="设置机器区域" v-model="editForm.areaName" readonly
+								border="none"></u--input>
+						</u-form-item>
+						<u-form-item labelWidth="90" label="线路" prop="lineName" borderBottom ref="item1"
+							@click.native="lineChoose">
+							<u--input placeholder="设置机器线路" v-model="editForm.lineName" readonly
+								border="none"></u--input>
+						</u-form-item>
+						<u-form-item labelWidth="90" label="点位" prop="placeName" borderBottom ref="item1"
+							@click.native="placeChoose">
+							<u--input placeholder="设置机器点位" v-model="editForm.placeName" readonly
+								border="none"></u--input>
+						</u-form-item>
+						<u-form-item labelWidth="90" label="位置" prop="lon" borderBottom @click.native="getLocation">
+							<u--input placeholder="设置机器位置" readonly v-model="devicePos" border="none"></u--input>
+							<u-icon slot="right" size="26" name="map"></u-icon>
+						</u-form-item>
+					</u--form>
+				</view>
+
+				<!-- 设备重启 -->
+				<view class="popup-content restart" v-if="title=='设备重启'">
+					是否确定重启设备?
+				</view>
+
+				<!-- 暂停营业 -->
+				<view class="popup-content restart" v-if="title=='暂停营业'||title=='开始营业'">
+					是否确定{{btnState}}?
+				</view>
+
+				<!-- 声音设置 -->
+				<view class="popup-content" v-if="title=='声音设置'">
+					<u-slider :showValue="true" min="0" max="25" v-model="voice"></u-slider>
+				</view>
+
+				<!-- 温度设置 -->
+				<view class="popup-content temp" v-if="title=='温度设置'">
+					<view class="flex" style="margin-top: 0;">
+						<view class="lab" style="margin-top: -10rpx;">工作模式:</view>
+						<u-radio-group v-model="tempDetail.selTempWorkModel" placement="row">
+							<u-radio :customStyle="{marginBottom:'16rpx',marginLeft:'16rpx'}"
+								v-for="(item, index) in tempWorkModels" :key="index" :label="item.name" :name="item.id">
+							</u-radio>
+						</u-radio-group>
+					</view>
+					<view class="flex" style="margin-top: 0rpx; margin-bottom: 20rpx;">
+						<view class="lab">工作温度(℃):</view>
+						<view style=" margin-left: 20rpx;">
+							<u-number-box button-size="30" v-model="tempDetail.targetTemp" class='martop'>
+							</u-number-box>
+						</view>
+
+					</view>
+
+					<view class="info">时段设置为X点至Y点,结束时间需大于起始时间</view>
+					<view class="flex align-center">
+						<view class="lab">时段1:</view>
+						<view class="flex align-center  date-container marleft"
+							style="padding-left: 10rpx; padding-right: 10rpx;" @tap="pickerTimes('temp',0)">
+							<view class="time-box">
+								{{tempDetail.start1}}
+							</view>
+							<view class="lab to">至</view>
+							<view class="time-box">{{tempDetail.end1}}</view>
+						</view>
+					</view>
+					<view class="flex align-center">
+						<view class="lab">时段2:</view>
+						<view class="flex align-center  date-container marleft"
+							style="padding-left: 10rpx; padding-right: 10rpx;" @tap="pickerTimes('temp',1)">
+							<view class="time-box">
+								{{tempDetail.start2}}
+							</view>
+							<view class="lab to">至</view>
+							<view class="time-box">{{tempDetail.end2}}</view>
+						</view>
+					</view>
+					<view class="flex align-center">
+						<view class="lab">时段3:</view>
+						<view class="flex align-center  date-container marleft"
+							style="padding-left: 10rpx; padding-right: 10rpx;" @tap="pickerTimes('temp',2)">
+							<view class="time-box">
+								{{tempDetail.start3}}
+							</view>
+							<view class="lab to">至</view>
+							<view class="time-box">{{tempDetail.end3}}</view>
+						</view>
+					</view>
+					<view style="height: 20rpx;"></view>
+				</view>
+
+				<!-- 灯光设置 -->
+				<view class="popup-content temp" v-if="title=='灯光设置'">
+					<!-- 					<view class="flex" style="margin-top: 0;">
+						<view class="lab" style="margin-top: -10rpx;">是否打开:</view>
+						<u-switch v-model="lightDetail.open"></u-switch>
+					</view> -->
+
+					<view class="info">时段设置为X点至Y点,结束时间需大于起始时间</view>
+					<view class="flex align-center">
+						<view class="lab">时段1:</view>
+						<view class="flex align-center  date-container marleft"
+							style="padding-left: 10rpx; padding-right: 10rpx;" @tap="pickerTimes('light',0)">
+							<view class="time-box">
+								{{lightDetail.start1}}
+							</view>
+							<view class="lab to">至</view>
+							<view class="time-box">{{lightDetail.end1}}</view>
+						</view>
+					</view>
+					<view class="flex align-center">
+						<view class="lab">时段2:</view>
+						<view class="flex align-center  date-container marleft"
+							style="padding-left: 10rpx; padding-right: 10rpx;" @tap="pickerTimes('light',1)">
+							<view class="time-box">
+								{{lightDetail.start2}}
+							</view>
+							<view class="lab to">至</view>
+							<view class="time-box">{{lightDetail.end2}}</view>
+						</view>
+					</view>
+					<view class="flex align-center">
+						<view class="lab">时段3:</view>
+						<view class="flex align-center  date-container marleft"
+							style="padding-left: 10rpx; padding-right: 10rpx;" @tap="pickerTimes('light',2)">
+							<view class="time-box">
+								{{lightDetail.start3}}
+							</view>
+							<view class="lab to">至</view>
+							<view class="time-box">{{lightDetail.end3}}</view>
+						</view>
+					</view>
+					<view style="height: 20rpx;"></view>
+				</view>
+
+				<!-- 清除故障 -->
+				<view class="popup-content restart" v-if="title=='清除故障'">
+					是否确定清除故障?
+				</view>
+			</xpopup>
+
+
+			<!-- 区域选择弹框 -->
+			<xpopup :show="areaShow" @close="areaClose" :showBtn="false" title="选择区域">
+				<!-- 类目选择 -->
+				<scroll-view style="height: 600rpx;" scroll-y scroll-with-animation>
+					<view class="popup-content">
+						<tki-tree style="width:100%;" :range="areaList" :foldAll="false" rangeKey="name" idKey="name"
+							buttonName="选中" @btnClick="areaSubmit">
+						</tki-tree>
+					</view>
+				</scroll-view>
+			</xpopup>
+
+			<!-- 线路/点位 -->
+			<u-picker :show="pickerShow" @confirm="pickerConfirm" :closeOnClickOverlay="true" :columns="columns"
+				keyName="label" @close="pickerClose" @cancel="pickerClose"></u-picker>
+			<!-- 	温度时间选择 -->
+			<u-picker :show="showTimeSelPicker" ref="uPicker" :columns="times" @confirm="pickerTimeConfirm"
+				@change="pickerTimeChangeHandler"></u-picker>
+
+		</view>
+
+		<u-back-top :scroll-top="scrollTop" top="400"></u-back-top>
+	</view>
+</template>
+
+<script>
+	import {
+		detail,
+		dataCount,
+		indexDeviceRecords,
+		indexDeviceNetRecords,
+		indexDeviceTempRecords,
+		indexDeviceError,
+		modifyBusyStage,
+		updateInfo,
+		sendCommand,
+		queryCommandResult,
+		exportQrCode,
+		getQrCode,
+		abort
+	} from "@/api/device/device.js"
+
+	import {
+		areaTree
+	} from "@/api/point/area"
+
+	import {
+		linePage,
+		bindDevice
+	} from "@/api/point/line"
+
+	import config from '@/config'
+
+	import {
+		pointPage
+	} from "@/api/point/point"
+
+	import {
+		bindDevice as bindDevicePoint
+	} from "@/api/point/point"
+
+	import {
+		base64DownLoad
+	} from '@/utils/download.js'
+
+	export default {
+		data() {
+			return {
+				opts: {
+					padding: [15, 10, 0, 15],
+					dataLabel: false,
+					color: ['#2D54EC', '#F49B37'],
+					// 开启图表可拖拽滚动
+					// enableScroll: true,
+					dataPointShape: false, //是否显示折线上的点
+					xAxis: {
+						// 开启图表可拖拽滚动后 必配置(显示多少个)
+						// itemCount: 8,
+						rotateLabel: true,
+						labelCount: 10,
+						marginTop: 10,
+					},
+					yAxis: {
+						splitNumber: 5,
+						gridType: "dash",
+						dashLength: 8,
+						gridColor: "rgba(0, 0, 0, 0.15)",
+						data: [{
+							axisLine: false, //坐标轴轴线是否显示
+						}]
+					},
+					extra: {
+						column: {
+							width: 15,
+							seriesGap: 2,
+							barBorderRadius: [10, 10, 10, 10],
+						},
+						line: {
+							type: "curve",
+							width: 2,
+							activeType: "hollow",
+							linearType: "custom",
+							onShadow: true,
+							animation: "horizontal"
+						},
+					}
+				},
+				current: '经营数据',
+				chartTab: [{
+						name: '经营数据'
+					},
+					{
+						name: '经营图表'
+					},
+					{
+						name: '温度图表'
+					},
+					{
+						name: '信号图表'
+					}
+				],
+				chartData: {
+					series: [],
+					categories: []
+				},
+				errorMessage: '无数据',
+
+				tempDetail: {
+					targetTemp: 4,
+					selTempWorkModel: 2,
+					start1: 0,
+					end1: 0,
+					start2: 0,
+					end2: 0,
+					start3: 0,
+					end3: 0,
+					curTimes: 0
+				},
+
+				tempWorkModels: [{
+					name: '制热',
+					id: 1
+				}, {
+					name: '制冷',
+					id: 2
+				}, {
+					name: '恒温',
+					id: 3
+				}, {
+					name: '关闭',
+					id: 4
+				}],
+				showTimeSelPicker: false,
+				timeSelPickerType: undefined,
+				times: [
+					[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24],
+					[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24]
+				],
+				lightDetail: {
+					open: true,
+					start1: 0,
+					end1: 0,
+					start2: 0,
+					end2: 0,
+					start3: 0,
+					end3: 0,
+					curTimes: 0
+				},
+				totalTab: ['今日', '本月'],
+				time: 0,
+				show: false,
+				popupTitle: '编辑商品',
+
+				editForm: {
+					name: null,
+					areaName: null,
+					areaId: null,
+					lineId: null,
+					placeId: null,
+					lineName: null,
+					placeName: null,
+					lon: null,
+					lat: null,
+					pos: null
+				},
+				editRules: {
+					'code': {
+						type: 'string',
+						required: true,
+						message: '必填项',
+						trigger: ['blur', 'change']
+					},
+				},
+
+				title: '', //弹框标题
+				voice: 4, //声音设置
+				light: 50, //灯光设置
+				id: null, //设备id
+				detail: {}, //设备详情
+
+				allCountData: {}, //经营统计数据显示
+
+				scrollTop: 0, //滚动距离顶部
+
+				operaRecordsFlag: false, //操作记录
+				netRecordsFlag: false, //在线情况
+				errRecordCameraFlag: false, //摄像头故障情况
+				errRecordLockFlag: false, //锁故障数据情况
+				tempRecordsFlag: false, //温度数据情况
+
+				operaRecords: [], //设备操作记录
+				netRecords: [], //在线情况记录
+				errRecordLock: [], //锁故障数据
+				errRecordCamera: [], //摄像头故障数据
+				deviceTempRecords: [], //温度记录
+
+				isMore: true,
+
+				// 编辑弹框
+				areaShow: false,
+				pickerShow: false,
+
+				areaList: [], //区域数据
+
+				columns: [], //picker数据
+
+				pickerType: 1, //1线路2点位
+
+				goodsManageData: {
+					categoryNum: 0,
+					stock: 0,
+					afterFillStock: 0
+				},
+
+				countData: {
+					salesMoney: 0,
+					salesCount: 0,
+					refundMoney: 0
+				},
+
+				pointColumns: [],
+				lineColumns: [],
+
+				qrcodeShow: false,
+				qrCodeImg: null
+			}
+		},
+
+		computed: {
+			btnState() {
+				let str = this.detail.busyState == 1 ? '暂停营业' : '开始营业';
+				return str
+			},
+
+			devicePos() {
+				let pos = '';
+				if (this.editForm.lat || this.editForm.lon) {
+					pos = this.editForm.lat + ',' + this.editForm.lon
+				}
+				return pos
+			}
+		},
+
+		async onLoad(o) {
+			if (o.id) {
+				this.id = o.id;
+
+				//设备详情
+				this.getDetail()
+
+				// 经营数据
+				this.allCountData = await this.getCountData(1)
+				this.countData = this.allCountData.dayBusinessData;
+
+				//商品管理数据
+				let tempData = await this.getCountData(5)
+				this.goodsManageData = tempData.goodsData;
+			}
+		},
+
+		methods: {
+			// 获取设备详情
+			getDetail() {
+				detail({
+					deviceId: this.id,
+					isSysinfo: true,
+					isStatus: true,
+					isRegister: true
+				}).then(res => {
+					this.detail = res.data
+				})
+			},
+
+			//获取统计数据
+			getCountData(type) {
+				return new Promise((resolve, reject) => {
+					dataCount({
+							deviceId: this.id,
+							type: type
+						}).then(res => {
+							resolve(res.data)
+						})
+						.catch(err => {
+							reject(err)
+						})
+				})
+			},
+
+			// 下载柜门二维码
+			downloadQr() {
+				this.qrcodeShow = true;
+				this.drawQr(this.id)
+			},
+
+			//绘制二维码
+			drawQr(id) {
+				getQrCode({
+					deviceId: id
+				}).then(res => {
+					this.qrCodeImg = res.data
+				})
+			},
+
+			qrcodeClose() {
+				this.qrcodeShow = false;
+			},
+
+			// 保存图片
+			handleImgSave() {
+				base64DownLoad(this.qrCodeImg).then(res=>{
+					this.qrcodeClose()
+					uni.showToast({
+						title: '保存成功!',
+						icon: "none",
+						duration: 3000
+					})
+				}).catch(err=>{
+					this.qrcodeClose()
+				})
+			},
+
+			// 复制文字
+			copy(e) {
+				uni.setClipboardData({
+					data: e
+				});
+			},
+
+			// tab切换
+			async tabClick(e) {
+				console.log(e)
+				this.current = e.name
+				if (e.name == '经营数据') {
+					this.allCountData = await this.getCountData(1)
+				} else if (e.name == '经营图表') {
+					let tempChartData = {};
+					let data = await this.getCountData(2)
+					tempChartData.series = data.businessChart ? data.businessChart.series : []
+					tempChartData.categories = data.businessChart ? data.businessChart.categories : []
+					this.chartData = JSON.parse(JSON.stringify(tempChartData))
+				} else if (e.name == '温度图表') {
+					let tempChartData = {};
+					let data = await this.getCountData(3)
+					tempChartData.series = data.temperatureChart ? data.temperatureChart.series : []
+					tempChartData.categories = data.temperatureChart ? data.temperatureChart.categories : []
+					this.chartData = JSON.parse(JSON.stringify(tempChartData))
+				} else if (e.name == '信号图表') {
+					let tempChartData = {};
+					let data = await this.getCountData(4)
+					tempChartData.series = data.signalChart ? data.signalChart.series : []
+					tempChartData.categories = data.signalChart ? data.signalChart.categories : []
+					this.chartData = JSON.parse(JSON.stringify(tempChartData))
+				}
+			},
+
+			// 今日/本月切换
+			totalChange(e) {
+				this.time = e
+				if (this.time == 0) { //今日
+					this.countData = this.allCountData.dayBusinessData
+				} else { //今月
+					this.countData = this.allCountData.monthBusinessData
+				}
+			},
+
+			//销量更多数据
+			more() {
+				let deviceName = this.detail.deviceName ? this.detail.deviceName : this.detail.deviceId
+				this.$tab.navigateTo(`/pages/equipment/statistics?title=${deviceName}&id=${this.id}`)
+			},
+
+			// 显示弹框
+			async btnClick(e) {
+				this.title = e
+				this.show = true;
+				if (this.title == '编辑') {
+					// 加载区域数据
+					this.getAreaTree()
+					//数据反显
+					this.editForm = {
+						name: this.detail.deviceName,
+						areaName: this.detail.districtName,
+						areaId: this.detail.districtId,
+						lineId: this.detail.placeLineId,
+						placeId: this.detail.placeId,
+						lineName: this.detail.placeLineName,
+						placeName: this.detail.placeName,
+						lon: this.detail.lon,
+						lat: this.detail.lat,
+					}
+				} else if (this.title == '设备重启') {
+
+				} else if (this.title == '声音设置') {
+
+				} else if (this.title == '灯光设置') {
+
+				} else if (this.title == '清除故障') {
+
+				} else { //暂停营业
+
+				}
+			},
+
+			// 关闭弹框
+			close(e) {
+				this.show = false
+			},
+
+			// 弹框确定
+			async submit() {
+
+				let templet = null;
+				let reSn = null;
+				if (this.title == '编辑') {
+					// if (this.editForm.areaName && this.editForm.lineId) {
+					// 	await this.lineBind() //绑定线路
+					// 	if (this.editForm.placeId) { //绑定点位
+					// 		await this.placeBind()
+					// 	}
+					// 	this.getDetail()
+					// } else {
+					// 	this.$modal.msg('请选择区域和线路~')
+					// }
+					await this.updateDevice()
+					this.getDetail()
+				} else if (this.title == '设备重启') {
+					await this.getDict('mqtt_cmd_templet_task', 'appTask').then(res => {
+						console.log(res);
+						templet = JSON.parse(res[0].value);
+						templet.data.task = "restart";
+						templet.data.appId = undefined;
+						templet.data.src = undefined;
+					});
+					await sendCommand([{
+						deviceId: this.id,
+						templet: templet
+					}]).then(res => {
+						this.$modal.showToast('重启成功~')
+						reSn = res.data[0].v1;
+					});
+				} else if (this.title == '声音设置') {
+					await this.getDict('mqtt_cmd_templet_sets', 'deviceVoice').then(res => {
+						console.log(res);
+						templet = JSON.parse(res[0].value);
+						templet.data.alound = this.voice;
+					});
+					await sendCommand([{
+						deviceId: this.id,
+						templet: templet
+					}]).then(res => {
+						reSn = res.data[0].v1;
+					});
+				} else if (this.title == '灯光设置') {
+					await this.getDict('mqtt_cmd_templet_sets', 'light').then(res => {
+
+						templet = JSON.parse(res[0].value);
+						templet.data.light0.start1 = this.lightDetail.start1;
+						templet.data.light0.end1 = this.lightDetail.end1;
+
+						templet.data.light0.start2 = this.lightDetail.start2;
+						templet.data.light0.end2 = this.lightDetail.end2;
+
+						templet.data.light0.start3 = this.lightDetail.start3;
+						templet.data.light0.end3 = this.lightDetail.end3;
+
+					});
+					console.log(this.id, templet);
+					sendCommand([{
+						deviceId: this.id,
+						templet: templet
+					}]).then(res => {
+						reSn = res.data[0].v1;
+					});
+				} else if (this.title == '温度设置') {
+					if (this.vilidateParams()) return //参数校验
+
+					await this.getDict('mqtt_cmd_templet_sets', 'temperature').then(res => {
+
+						templet = JSON.parse(res[0].value);
+						templet.data.workModel = this.tempDetail.selTempWorkModel;
+						templet.data.target = this.tempDetail.targetTemp;
+						templet.data.start1 = this.tempDetail.start1;
+						templet.data.end1 = this.tempDetail.end1;
+
+						templet.data.start2 = this.tempDetail.start2;
+						templet.data.end2 = this.tempDetail.end2;
+
+						templet.data.start3 = this.tempDetail.start3;
+						templet.data.end3 = this.tempDetail.end3;
+
+					});
+					console.log(this.id, templet);
+					sendCommand([{
+						deviceId: this.id,
+						templet: templet
+					}]).then(res => {
+						reSn = res.data[0].v1;
+					});
+				} else if (this.title == '清除故障') {
+					this.abort()
+				} else { //暂停营业
+					this.changeState()
+				}
+				this.close()
+			},
+
+			vilidateParams() {
+				if (this.tempDetail.start1 > this.tempDetail.end1 || this.tempDetail.start2 > this.tempDetail.end2 ||
+					this
+					.tempDetail.start2 > this.tempDetail.end2) {
+					this.$modal.msg('结束时间需大于等于起始时间!')
+					return true
+				} else {
+					return false
+				}
+			},
+
+			abort() {
+				abort({
+					deviceId: Number(this.id),
+				}).then(res => {
+					this.$modal.msg('清除故障成功~')
+				});
+			},
+
+			//修改设备信息
+			updateDevice() {
+				let params = {
+					deviceId: Number(this.id),
+					deviceName: this.editForm.name,
+					districtId: this.editForm.areaId,
+					placeLineId: this.editForm.lineId,
+					placeId: this.editForm.placeId,
+					lon: this.editForm.lon,
+					lat: this.editForm.lat
+				}
+				return new Promise((resolve, reject) => {
+					updateInfo(params).then(res => {
+						this.$modal.msg('修改成功~')
+						resolve(res)
+					}).catch(err => {
+						reject(err)
+					})
+				})
+			},
+
+			// 线路绑定设备
+			lineBind() {
+				return new Promise((resolve, reject) => {
+					bindDevice({
+						isBind: true,
+						deviceIds: [this.id],
+						id: this.editForm.lineId
+					}).then(res => {
+						this.$modal.msg('绑定线路成功~')
+						resolve(res)
+					}).catch(err => {
+						reject(err)
+					})
+				})
+			},
+
+			// 点位绑定设备
+			placeBind() {
+				return new Promise((resolve, reject) => {
+					bindDevicePoint({
+						isBind: true,
+						deviceIds: [this.id],
+						id: this.editForm.placeId
+					}).then(res => {
+						this.$modal.msg('绑定点位成功~')
+						resolve(res)
+					}).catch(err => {
+						reject(err)
+					})
+				})
+			},
+
+			changeState() {
+				let busyState = this.detail.busyState == 1 ? 2 : 1
+				modifyBusyStage({
+					deviceId: this.id,
+					busyState: busyState
+				}).then(async res => {
+					if (res.code == 200) {
+						this.$modal.msg('操作成功~')
+						this.detail.busyState = busyState;
+						let templet = '';
+						await this.getDict('mqtt_cmd_templet_sets', 'lockDevice').then(res => {
+							console.log(res);
+							templet = JSON.parse(res[0].value);
+							templet.data.id = 0;
+							templet.data.opt = (busyState == 1 ? 'unlock' : 'lock');
+						});
+						await sendCommand([{
+							deviceId: this.id,
+							templet: templet
+						}]).then(res => {
+							//reSn = res.data[0].v1;
+						});
+					} else {
+						this.$modal.msg('操作失败,请重试~')
+					}
+				})
+			},
+
+			// 获取设备操作记录
+			getDeviceRecords() {
+				this.operaRecordsFlag = true;
+				indexDeviceRecords({
+					deviceId: this.id
+				}).then(res => {
+					this.operaRecords = res.data
+				})
+			},
+
+			//获取设备联网记录
+			getDeviceNetRecords() {
+				this.netRecordsFlag = true;
+				indexDeviceNetRecords({
+					deviceId: this.id
+				}).then(res => {
+					this.deviceNetRecords = res.data;
+				})
+			},
+
+			//获取设备温度记录
+			getDeviceTempRecords() {
+				this.tempRecordsFlag = true;
+				indexDeviceTempRecords({
+					deviceId: this.id
+				}).then(res => {
+					this.deviceTempRecords = res.data;
+				})
+			},
+
+			//获取故障记录
+			getDeviceErrRecords(type) {
+				switch (type) {
+					case 2:
+						this.errRecordLockFlag = true;
+						break
+					case 5:
+						this.errRecordCameraFlag = true;
+						break
+					default:
+						break
+				}
+				indexDeviceError({
+					deviceId: this.id,
+					errorType: type
+				}).then(res => {
+					switch (type) {
+						case 2:
+							this.errRecordLock = res.data;
+							break
+						case 5:
+							this.errRecordCamera = res.data;
+							break
+						default:
+							break
+					}
+				})
+			},
+
+			// onPageScroll(e) {
+			// 	this.scrollTop = e.scrollTop;
+			// 	if (this.scrollTop > 140 && !this.netRecordsFlag) { //联网记录
+			// 		this.getDeviceNetRecords()
+			// 	}
+
+			// 	if (this.scrollTop > 400 && !this.errRecordLockFlag) { //门锁故障情况 2
+			// 		this.getDeviceErrRecords(2)
+			// 	}
+
+			// 	if (this.scrollTop > 660 && !this.errRecordCameraFlag) { //摄像头故障情况 5
+			// 		this.getDeviceErrRecords(5)
+			// 	}
+
+			// 	if (this.scrollTop > 850 && !this.tempRecordsFlag) { //操作记录
+			// 		this.getDeviceTempRecords()
+			// 	}
+
+			// 	if (this.scrollTop > 1110 && !this.operaRecordsFlag) { //操作记录
+			// 		this.getDeviceRecords()
+			// 	}
+			// },
+
+			//获取区域树
+			getAreaTree() {
+				areaTree().then(res => {
+					this.areaList = res.data
+				})
+			},
+
+			//获取线路options
+			getLineOption(regionName) {
+				linePage({
+					page: {
+						current: 1,
+						size: 1000,
+					},
+					regionName: regionName
+				}).then(res => {
+					let data = res.data.records;
+					let newData = data.map(i => {
+						return {
+							id: i.id,
+							label: i.lineName
+						}
+					})
+					this.lineColumns = [newData];
+				})
+			},
+
+			//获取点位分页
+			getPointList(lineId) {
+				pointPage({
+					page: {
+						current: 1,
+						size: 1000,
+					},
+					lineId: lineId
+				}).then(res => {
+					let data = res.data.records;
+					let newData = data.map(i => {
+						return {
+							id: i.id,
+							label: i.placeName
+						}
+					})
+					this.pointColumns = [newData];
+				})
+			},
+
+			//区域选择
+			areaChoose() {
+				this.areaShow = true
+			},
+
+			areaClose() {
+				this.areaShow = false
+			},
+
+			//区域选择提交
+			areaSubmit(res) {
+				this.editForm.areaName = res.name;
+				this.editForm.areaId = res.key;
+				console.log(res)
+				this.getLineOption(res.name)
+				this.areaClose()
+
+				//清空线路,点位信息
+				this.editForm.lineId = null
+				this.editForm.lineName = null
+				this.editForm.placeId = null
+				this.editForm.placeName = null
+			},
+
+			//线路选择
+			lineChoose() {
+				this.columns = this.lineColumns;
+				this.pickerType = 1
+
+				if (this.columns && this.columns.length > 0 && this.columns[0].length > 0) {
+					this.pickerShow = true
+				} else {
+					this.$modal.msg('当前无线路~')
+				}
+			},
+
+			//点位选择
+			placeChoose() {
+				this.columns = this.pointColumns;
+				this.pickerType = 2
+				if (this.columns && this.columns.length > 0 && this.columns[0].length > 0) {
+					this.pickerShow = true
+				} else {
+					this.$modal.msg('当前无点位~')
+				}
+			},
+
+			getLocation() {
+				uni.chooseLocation({
+					success: (res) => {
+						console.log('位置名称:' + res.name);
+						console.log('详细地址:' + res.address);
+						console.log('纬度:' + res.latitude);
+						console.log('经度:' + res.longitude);
+						this.editForm.lat = res.latitude;
+						this.editForm.lon = res.longitude;
+					}
+				})
+			},
+
+			showPos() {
+				if (this.detail.lat && this.detail.lon) {
+					uni.openLocation({ //​使用微信内置地图查看位置。
+						latitude: Number(this.detail.lat), //要去的纬度-地址 
+						longitude: Number(this.detail.lon), //要去的经度-地址
+						success: res => {
+							console.log('success');
+						}
+					})
+				} else {
+					this.$modal.msg('请先编辑设备位置信息~')
+				}
+			},
+
+			//picker弹框确认
+			pickerConfirm(e) {
+				if (e.value[0]) {
+					if (this.pickerType == 1) { //线路
+						this.editForm.lineId = e.value[0].id
+						this.editForm.lineName = e.value[0].label
+						this.getPointList(e.value[0].id)
+
+						//清空点位信息
+						this.editForm.placeId = null
+						this.editForm.placeName = null
+					} else { //点位
+						this.editForm.placeId = e.value[0].id
+						this.editForm.placeName = e.value[0].label
+					}
+				}
+				this.pickerClose()
+			},
+
+			pickerClose() {
+				this.pickerShow = false
+			},
+
+			pickerTimes(type, o) {
+				this.timeSelPickerType = type;
+				this.showTimeSelPicker = true;
+				if (this.timeSelPickerType === 'light') {
+					this.lightDetail.curTimes = o;
+				} else if (this.timeSelPickerType === 'temp') {
+					this.tempDetail.curTimes = o;
+				}
+			},
+			pickerTimeChangeHandler(e) {
+				const {
+					columnIndex,
+					value,
+					values, // values为当前变化列的数组内容
+					index,
+					// 微信小程序无法将picker实例传出来,只能通过ref操作
+					picker = this.$refs.uPicker
+				} = e
+
+				//picker.setColumnValues(0, this.times[0][0])
+				// picker.setColumnValues(1,  this.times[1][0])
+				// 当第一列值发生变化时,变化第二列(后一列)对应的选项
+				//    if (columnIndex === 0) {
+				//            // picker为选择器this实例,变化第二列对应的选项
+				// 	var col1 = values.slice(index,23)
+				//        picker.setColumnValues(1, values.slice(index,23))
+
+				//    }
+			},
+			pickerTimeConfirm(e) {
+				console.log('confirm', this.timeSelPickerType, e.value[0])
+				this.showTimeSelPicker = false
+				if (this.timeSelPickerType === 'light') {
+					if (0 == this.lightDetail.curTimes) {
+						this.lightDetail.start1 = e.value[0];
+						this.lightDetail.end1 = e.value[1];
+					} else if (1 == this.lightDetail.curTimes) {
+						this.lightDetail.start2 = e.value[0];
+						this.lightDetail.end2 = e.value[1];
+					} else {
+						this.lightDetail.start3 = e.value[0];
+						this.lightDetail.end3 = e.value[1];
+					}
+				} else if (this.timeSelPickerType === 'temp') {
+					if (0 == this.tempDetail.curTimes) {
+						this.tempDetail.start1 = e.value[0];
+						this.tempDetail.end1 = e.value[1];
+					} else if (1 == this.tempDetail.curTimes) {
+						this.tempDetail.start2 = e.value[0];
+						this.tempDetail.end2 = e.value[1];
+					} else {
+						this.tempDetail.start3 = e.value[0];
+						this.tempDetail.end3 = e.value[1];
+					}
+				}
+
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.container {
+		.content {
+			padding: 24rpx;
+
+			.xy-card {
+				margin-bottom: 24rpx;
+			}
+
+			.top {
+
+				>view:nth-child(1) {
+					display: flex;
+					flex-flow: row nowrap;
+					justify-content: space-between;
+
+					.t-left {
+						width: 380rpx;
+
+						>view:nth-child(1) {
+							font-size: 36rpx;
+							font-weight: bold;
+						}
+
+						>view:nth-child(2) {
+							font-size: 26rpx;
+							color: #2C6FF3;
+							border: 1rpx solid #2C6FF3;
+							border-radius: 6rpx;
+							padding: 0 8rpx;
+							background-color: rgb(243, 249, 525);
+							margin-left: 12rpx;
+						}
+					}
+
+					.t-right {
+						font-size: 30rpx;
+						color: #f56c6c;
+						position: relative;
+						margin-left: 24rpx;
+
+						&::before {
+							content: '';
+							display: inline-block;
+							background-color: green;
+							width: 16rpx;
+							height: 16rpx;
+							border-radius: 16rpx;
+							margin-right: 12rpx;
+						}
+
+						&.off {
+							color: #666;
+
+							&::before {
+								background-color: #666;
+							}
+						}
+					}
+				}
+
+				>view:nth-child(2) {
+					display: flex;
+					flex-direction: row;
+					justify-content: flex-end;
+					margin-top: 12rpx;
+
+					>.cu-btn {
+						height: 40rpx;
+					}
+
+					.down-qr {
+						font-size: 26rpx;
+						color: #2C6FF3;
+						border: 1rpx solid #2C6FF3;
+						border-radius: 6rpx;
+						padding: 4rpx 8rpx;
+						background-color: rgb(243, 249, 525);
+					}
+
+					.edit {
+						font-size: 26rpx;
+						color: #fff;
+						border: 1rpx solid #2C6FF3;
+						border-radius: 6rpx;
+						padding: 4rpx 8rpx;
+						background-color: rgb(243, 249, 525);
+						margin-left: 12rpx;
+						background-color: #2C6FF3;
+					}
+				}
+			}
+
+			.center {
+				border-radius: 8rpx;
+				background-color: rgb(245, 248, 251);
+				padding: 24rpx 12rpx 42rpx;
+				font-size: 30rpx;
+				margin-top: 24rpx;
+				overflow: hidden;
+				position: relative;
+				height: 560rpx;
+				transition: all 0.5s ease 0s;
+
+				.unfold {
+					position: absolute;
+					bottom: 8rpx;
+					right: 24rpx;
+				}
+
+				&.center-more {
+					height: 170rpx;
+				}
+
+				.d-line {
+					display: flex;
+					flex-flow: row nowrap;
+					justify-content: flex-start;
+
+					>view:nth-child(1) {
+						width: 50%;
+					}
+
+					>view:nth-child(2) {
+						width: 50%;
+					}
+				}
+
+				.c-item {
+					display: flex;
+					flex-flow: row nowrap;
+					justify-content: flex-start;
+					font-size: 28rpx;
+					margin-bottom: 18rpx;
+					align-items: flex-end;
+
+					.name {
+						min-width: 100rpx;
+						color: #999;
+					}
+
+					.val {
+						color: #333;
+						padding-left: 6rpx;
+						overflow: hidden;
+						text-overflow: ellipsis;
+						white-space: nowrap;
+
+						text {
+							margin-left: 12rpx;
+							text-decoration: underline;
+							color: #2C6FF3;
+						}
+
+						&.net {
+							text {
+								text-decoration: none;
+							}
+						}
+					}
+				}
+
+
+			}
+
+			.bot {
+				display: flex;
+				flex-flow: row nowrap;
+				justify-content: space-between;
+				margin-top: 24rpx;
+
+				.cu-btn {
+					padding: 0 12rpx;
+					font-size: 26rpx;
+					height: 60rpx;
+					line-height: 50rpx;
+				}
+
+				.cu-btn1 {
+					background-color: red;
+					color: #fff;
+				}
+
+				.cu-btn2 {
+					background-color: #fff;
+					color: #2C6FF3;
+					border: 1rpx solid #2C6FF3;
+				}
+
+				.cu-btn3 {
+					background-color: #2C6FF3;
+					color: #fff;
+				}
+			}
+
+			.qrcode-content {
+				padding: 24rpx;
+
+				.save-qrcode {
+					margin-top: 24rpx;
+				}
+			}
+
+			.chart-content {
+				width: 100%;
+
+				.total {
+					display: flex;
+					flex-direction: column;
+					align-items: center;
+				}
+
+				.more {
+					background-color: #2C6FF3;
+					padding: 0 24rpx;
+					font-size: 26rpx;
+					height: 50rpx;
+					line-height: 50rpx;
+					margin-top: 24rpx;
+				}
+			}
+
+			.t-content {
+				display: flex;
+				flex-flow: row nowrap;
+				width: 100%;
+				box-sizing: border-box;
+				text-align: center;
+
+				.t-item {
+					width: 50%;
+					margin-top: 24rpx;
+
+					.t-name {
+						font-size: 28rpx;
+						line-height: 40rpx;
+					}
+
+					.t-num {
+						font-size: 28rpx;
+						line-height: 46rpx;
+						padding: 12rpx 0;
+
+						text {
+							font-size: 40rpx;
+							font-weight: bold;
+						}
+					}
+				}
+			}
+
+			.title {
+				font-size: 34rpx;
+				font-weight: bold;
+				color: #333;
+				line-height: 54rpx;
+
+			}
+
+			.bot1 {
+				display: flex;
+				justify-content: flex-end;
+			}
+
+			.l-content {
+				padding-top: 24rpx;
+
+				.l-item {
+					font-size: 28rpx;
+					line-height: 50rpx;
+					display: flex;
+					flex-direction: row;
+
+					.l-status {
+						margin-left: 24rpx;
+					}
+				}
+			}
+
+			.popup-content {
+				padding: 0 24rpx;
+			}
+
+			.restart {
+				padding: 40rpx 0;
+				font-size: 34rpx;
+				text-align: center;
+			}
+		}
+	}
+
+	.temp {
+		.info {
+			color: red;
+			height: 60rpx;
+			line-height: 60rpx;
+			text-align: center;
+		}
+
+		.flex {
+			margin-top: 20rpx;
+			height: 60rpx;
+
+			.lab {
+				line-height: 60rpx;
+				height: 60rpx;
+				text-align: center;
+				font-weight: 600;
+				margin-top: 20rpx;
+			}
+
+			.to {
+				width: 60rpx;
+				margin-left: 20rpx;
+				margin-top: 0;
+				font-weight: 500;
+			}
+
+			.time-box {
+				height: 50rpx;
+				width: 200rpx;
+				line-height: 50rpx;
+				margin-left: 20rpx;
+				text-align: center;
+				border-radius: 10rpx;
+				border-style: solid;
+				border-width: 1px;
+			}
+		}
+	}
+</style>

+ 341 - 0
pages/equipment/search.vue

@@ -0,0 +1,341 @@
+<template>
+	<view class="container">
+		<u-navbar bgColor="#2C6FF3" :autoBack="true" :placeholder="true" leftIconColor="#fff">
+			<view slot="center" class="nav-center">
+				<u-search :animation="true" bgColor="#fff" height="56rpx" placeholder="输入设备名称搜索" v-model="keyword"
+					:showAction="false" @search="search"></u-search>
+			</view>
+		</u-navbar>
+		<FilterDrop menuTop="148" @confirm="confirm" />
+		<view class="tab-list">
+			<!-- <u-subsection bgColor="#fff" activeColor="#2C6FF3" :list="tabList" :current="current" @change="tabChange"></u-subsection> -->
+			<u-tabs :list="tabList" @click="tabChange" :current="current" :scrollable="false" bgColor="#fff"
+				lineColor="#2C6FF3" ></u-tabs>
+		</view>
+
+		<view class="content">
+			<view class="xy-card" v-for="(item,index) in list" :key="item.id" @click="$tab.navigateTo(`/pages/equipment/detail?id=${item.deviceId}`)">
+				<view class="title">
+					{{item.deviceName}}
+					<view>
+						<u-tag text="运营中" type="success" plain v-if="item.busyState==1"> </u-tag>
+						<u-tag text="已停运" type="info" plain v-else> </u-tag>
+					</view>
+				</view>
+				<view class="total">
+					<view class="t-left">
+						<view>
+							<view>{{item.daySalesPrice}}</view>
+							<text>今日销售(元)</text>
+						</view>
+						<view>
+							<view>{{item.dayOrderNum}}</view>
+							<text>今日订单</text>
+						</view>
+						<view>
+							<view><text>{{item.deviceStatus.stock}}</text>/{{item.deviceStatus.afterFillStock}}</view>
+							<text>库存/补后</text>
+						</view>
+					</view>
+<!-- 					<view class="t-right">
+						机器详情
+					</view> -->
+				</view>
+				<view class="t-content">
+					<view class="c-item-t">
+						<view class="c-item">
+							<view>编号:</view>
+							<view>{{item.mercDeviceCode}}</view>
+						</view>
+						<view class="c-item">
+							<view>商户:</view>
+							<view>{{item.mercName}}</view>
+						</view>
+					</view>
+					<view class="c-item-t">
+						<view class="c-item">
+							<view>温度:</view>
+							<view>{{item.deviceStatus.tempValue}}</view>
+						</view>
+						<view class="c-item">
+							<view>信号强度:</view>
+							<view>{{item.deviceStatus.netDbm}}</view>
+						</view>
+					</view>
+					
+					<view class="c-item">
+						<view>软件版本:</view>
+						<view>{{item.deviceSysinfo.appUpmVersion}}</view>
+					</view>
+					<view class="c-item">
+						<view>最后更新时间:</view>
+						<view>{{item.updateTime}}</view>
+					</view>
+					<!-- <view class="c-item" v-if="current==3">
+						<view>故障原因:</view>
+						<view style="color: red;">锁不能正常开启</view>
+					</view> -->
+				</view>
+				<view class="btn">
+					<button class="cu-btn" style="width:180rpx;">故障日志</button>
+					<button class="cu-btn" style="width: 180rpx;margin: 0 12rpx;">流量卡</button>
+					<button class="cu-btn" style="width: 180rpx;">温度曲线</button>
+				</view>
+			</view>
+
+			<u-loadmore :status="status" v-if="list.length>=1" />
+		</view>
+		<view class="empty" v-if="list.length==0">
+			<u-empty text="没有设备!"></u-empty>
+		</view>
+	</view>
+</template>
+
+<script>
+	import FilterDrop from './components/xy-filtedrop/index.vue'
+	import {
+		searchPage
+	} from "@/api/device/device.js"
+	import {
+		areaTree
+	} from "@/api/point/area"
+
+	export default {
+		components: {
+			FilterDrop
+		},
+		data() {
+			return {
+				keyword: '',
+				tabList: [{
+						name: '在线'
+					},
+					{
+						name: '离线'
+					},
+					{
+						name: '停运'
+					},
+					{
+						name: '故障'
+					}
+				],
+				current: 0,
+
+				status: 'loadmore',
+
+				list: [],
+
+				page: 1,
+				size: 10,
+				
+				searchParams:{},//筛选条件
+			}
+		},
+
+		onLoad() {
+			this.getList()
+		},
+
+		methods: {
+			search(){
+				this.reset()
+				this.getList()
+			},
+			//接收菜单结果
+			confirm(e) {
+				this.searchParams=e;
+				this.reset()
+				this.getList()
+			},
+
+			tabChange(e) {
+				this.current = e.index
+				this.reset()
+				this.getList()
+			},
+
+			reset() {
+				this.status == 'loadmore'
+				this.page = 1;
+				this.list = [];
+			},
+
+			getList() {
+				searchPage({
+					page: {
+						current: this.page,
+						size: this.size
+					},
+					keywords: this.keyword,
+					fault: this.current==3?true:'',
+					netState:this.current==0?1:this.current==1?2:'',
+					busyState: this.current==2?2:'',
+					deviceType:this.searchParams[0],
+					stockStatus:this.searchParams[1],
+					districtId:this.searchParams[2],
+					placeLineId:this.searchParams[3],
+				}).then(res => {
+					let data = res.data.records;
+					if (data.length < 10) {
+						this.status = "nomore"
+					} else {
+						this.status = "loadmore"
+					}
+					this.list = this.list.concat(data)
+				})
+			},
+
+			scrolltolower() {
+				if (this.status == 'nomore') return
+				this.page++
+				this.getList()
+			},
+			
+			detail(id){
+				this.$tab.navigateTo(`/pages/equipment/detail?id=${id}`)
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.container {
+		position: relative;
+
+		.nav-center {
+			width: 450rpx;
+			margin-left: -120rpx;
+		}
+
+		.tab-list {
+			position: fixed;
+			top: 230rpx;
+			left: 0;
+			width: 100%;
+			background-color: #fff;
+			z-index: 999;
+
+			/deep/ .u-subsection--button__bar {
+				background-color: #f3f3f3;
+			}
+		}
+
+		.content {
+			margin-top: 180rpx;
+			padding: 12rpx 24rpx 24rpx;
+
+			.xy-card+.xy-card {
+				margin-top: 18rpx;
+			}
+
+			.title {
+				font-size: 28rpx;
+				font-weight: bold;
+				color: #333;
+				position: relative;
+
+				>view{
+					position: absolute;
+					right:0;
+					top:-12rpx;
+				}
+			}
+
+			.total {
+				margin-top: 28rpx;
+
+				.t-left {
+					font-size: 26rpx;
+					display: flex;
+					flex-flow: row nowrap;
+					justify-content: space-between;
+					text-align: center;
+
+					>view {
+						>view {
+							color: red;
+							font-weight: bold;
+							font-size: 28rpx;
+
+							>text {
+								color: #333;
+							}
+						}
+
+						>text {
+							margin-top: 12rpx;
+						}
+					}
+				}
+
+				.t-right {
+					display: inline-block;
+					line-height: 34rpx;
+					font-size: 26rpx;
+					background-color: #2C6FF3;
+					color: #fff;
+					border-radius: 8rpx;
+					padding: 0rpx 12rpx;
+					position: absolute;
+					right: 0;
+					bottom: 0;
+				}
+			}
+
+			.t-content {
+				border-radius: 8rpx;
+				background-color: rgb(245, 248, 251);
+				box-sizing: border-box;
+				padding: 24rpx 12rpx;
+				margin-top: 24rpx;
+				font-size: 26rpx;
+
+				.c-item {
+					display: flex;
+					flex-direction: row;
+					margin-bottom: 12rpx;
+					align-items: center;
+
+					>view:nth-child(1) {
+						color: #666;
+						width: 200rpx;
+					}
+
+					>view:nth-child(2) {
+						color: #333;
+					}
+				}
+
+				.c-item-t {
+					display: flex;
+					flex-flow: row nowrap;
+
+					.c-item {
+						width: 50%;
+					}
+				}
+
+			}
+
+			.btn {
+				display: flex;
+				flex-flow: row nowrap;
+				justify-content: flex-end;
+				margin-top: 24rpx;
+
+				.cu-btn {
+					background-color: #2C6FF3;
+					color: #fff;
+				}
+			}
+		}
+
+		.empty {
+			position: fixed;
+			left: 50%;
+			top: 50%;
+			transform: translate(-50%, -50%);
+		}
+	}
+</style>

+ 691 - 0
pages/equipment/statistics.vue

@@ -0,0 +1,691 @@
+<template>
+	<view class="container">
+		<u-navbar leftIconColor="#fff" titleStyle="color:#fff;fontSize:36rpx;" :autoBack="true" bgColor="#2C6FF3"
+			:placeholder="true" :title="navTitle"></u-navbar>
+		<view class="content">
+			<view class="card">
+				<view class="select">
+					<view class="time-tab flex justify-between">
+						<block v-for="(item,index) in timeList" :key="item.id">
+							<view class="time-tab-item" :class="[timeTabCurrent==item.id?'time-tab-show':'']"
+								@click="timeTabClick(item.id)">
+								{{item.name}}
+							</view>
+						</block>
+					</view>
+					<view class="time-select flex justify-around">
+						<view class="flex  align-center">
+							<view @click="timePickerShow('start')">
+								{{timeStart?timeStart:'开始时间'}}
+							</view>
+							<view>
+								一
+							</view>
+							<view @click="timePickerShow('end')">
+								{{timeEnd?timeEnd:'结束时间'}}
+							</view>
+						</view>
+					</view>
+				</view>
+				<view class="total flex justify-around">
+					<view class="flex flex-direction align-center">
+						<view class="total-name">
+							总销售额(元)
+						</view>
+						<view class="total-num" style="color: #2C6FF3;">
+							{{$xy.delMoney(total.salesMoney)}}
+						</view>
+					</view>
+					<view class="flex flex-direction align-center">
+						<view class="total-name">
+							商品销售数量(个)
+						</view>
+						<view class="total-num" style="color: #F9B237;">
+							{{total.salesCount||0}}
+						</view>
+					</view>
+					<view class="flex flex-direction align-center">
+						<view class="total-name">
+							平均商品价(元)
+						</view>
+						<view class="total-num" v-if="total.salesCount>0" style="color: #E94F4F;">
+							{{($xy.delMoney(total.salesMoney)/total.salesCount).toFixed(2)||0}}
+						</view>
+						<view class="total-num" v-else style="color:#E94F4F;">
+							0
+						</view>
+					</view>
+				</view>
+
+				<view class="chart" style="height: 600rpx;">
+					<qiun-data-charts type="qy-line-gradual" :opts="opts" :chartData="chartData"
+						:errorMessage="errorMessage" />
+				</view>
+			</view>
+
+			<view class="goods-table card">
+				<view class="table">
+					<view class="table-title flex justify-between align-center">
+						<view class="title">
+							商品销售排行
+						</view>
+						<view class="sort-type flex">
+							<view :class="[goodsSortType==0?'sort-type-item sort-type-show':'sort-type-item']"
+								@click="goodsSort(0)">
+								销售额
+							</view>
+							<view :class="[goodsSortType==1?'sort-type-item sort-type-show':'sort-type-item']"
+								@click="goodsSort(1)">
+								销量
+							</view>
+						</view>
+					</view>
+					<uni-table :border="false" :stripe="false" emptyText="暂无更多数据">
+						<!-- 表头行 -->
+						<uni-tr>
+							<uni-th align="center" width="52">排名</uni-th>
+							<uni-th align="center" width="110">上榜商品</uni-th>
+							<uni-th align="center" width="92">销售额</uni-th>
+							<uni-th align="center" width="92">销量</uni-th>
+						</uni-tr>
+						<!-- 表格数据行 -->
+						<uni-tr v-for="(item,index) in list2" :key="item.goodsId">
+							<uni-td v-if="index==0">
+								<view class="table-td">
+									<image class="table-img" src="../../static/images/global/first-class.png"
+										mode="widthFix"></image>
+								</view>
+							</uni-td>
+							<uni-td v-else-if="index==1">
+								<view class="table-td">
+									<image class="table-img" src="../../static/images/global/second-class.png"
+										mode="widthFix"></image>
+								</view>
+							</uni-td>
+							<uni-td v-else-if="index==2">
+								<view class="table-td">
+									<image class="table-img" src="../../static/images/global/third-class.png"
+										mode="widthFix"></image>
+								</view>
+							</uni-td>
+							<uni-td v-else>
+								<view class="table-td">
+									{{index+1}}
+								</view>
+							</uni-td>
+							<uni-td>
+								<view class="table-td table-td-name">{{item.goodsName}}</view>
+							</uni-td>
+							<uni-td>
+								<view class="table-td">¥{{$xy.delMoney(item.salesMoney)}}</view>
+							</uni-td>
+							<uni-td>
+								<view class="table-td">{{item.salesCount}}件</view>
+							</uni-td>
+						</uni-tr>
+					</uni-table>
+				</view>
+				<view class="more" @click="more('goods')" v-if="list2.length==5">
+					查看更多<view><u-icon name="arrow-right" color="#2C6FF3"></u-icon></view>
+				</view>
+			</view>
+		</view>
+
+		<u-datetime-picker :show="timeShow" :mode="timeMode" v-model="time" @confirm="confirm"
+			:closeOnClickOverlay="true" @close="close" @cancel="close"></u-datetime-picker>
+		<u-picker :show="pickerShow" :columns="columns" @confirm="pickerComfirm" :closeOnClickOverlay="true"
+			@close="pickerClose" @cancel="pickerClose"></u-picker>
+	</view>
+</template>
+
+<script>
+	import {
+		salesData,
+		goodSumCount,
+		goodSumPage
+	} from "@/api/device/device.js"
+
+	export default {
+		data() {
+			return {
+				timeType: ['日', '月', '年'],
+				timeCurrent: 0,
+				timeStart: '',
+				timeEnd: '',
+				deviceSortType: 0,
+				goodsSortType: 0,
+				timeShow: false,
+				timeMode: 'date',
+				startOrEnd: '',
+				time: new Date(),
+				pickerType: 'year',
+
+				pickerShow: false,
+				// typeColumns: [
+				// 	['销售额从高到低', '销售额从低到高', '销售数量从高到低', '销售数量从低到高']
+				// ],
+
+				typeColumns: [
+					['销售额', '订单笔数']
+				],
+
+				page: 1, //商品分页
+				size: 10,
+
+				status: 'loadmore', //加载更多
+				list1: [], //设备列表
+				list2: [], //商品列表
+				fullHeight: 0,
+
+				total: {
+					goodsCount: 0,
+					salesMoney: 0
+				},
+
+
+				tabCurrent: 0,
+				timeList: [{
+						id: 0,
+						name: '昨天'
+					},
+					{
+						id: 1,
+						name: '今天'
+					},
+					{
+						id: 2,
+						name: '近7日'
+					},
+					{
+						id: 3,
+						name: '近30日'
+					},
+					{
+						id: 4,
+						name: '本月'
+					},
+				],
+				timeTabCurrent: 1,
+				title: '设备销售额排行',
+
+				opts: {
+					enableScroll: false,
+					legend: {
+						position: 'top',
+						float: 'right',
+						padding: 20,
+						itemGap: 20
+					},
+					xAxis: {
+						disableGrid: true,
+						labelCount: 6,
+					},
+					yAxis: {
+						gridType: "solid",
+						dashLength: 2,
+						showTitle: true,
+						axisLineColor: '#fff',
+						data: [{
+								position: "left",
+								title: "/元"
+							},
+							{
+								position: "right",
+								title: "/单",
+								textAlign: "left"
+							},
+						]
+					},
+					extra: {
+						area: {
+							type: "curve",
+							opacity: 0.2,
+							addLine: true,
+							width: 2,
+							gradient: true,
+							activeType: "hollow"
+						}
+					}
+				},
+
+				errorMessage: '无数据',
+				chartData: {
+					categories: [],
+					series: []
+				},
+				navTitle: ''
+			}
+		},
+
+		onLoad(o) {
+			this.navTitle = o.title;
+			this.id = o.id
+		},
+
+		onShow() {
+			let timeObj = this.setResetTime(this.timeTabCurrent)
+			this.timeStart = timeObj.start
+			this.timeEnd = timeObj.end
+			this.getData()
+		},
+		methods: {
+			getData() {
+				this.getList2(this.goodsSortType)
+				this.getDeviceTotal(this.deviceSortType)
+				this.getCountData()
+			},
+
+			timeTabClick(e) {
+				this.timeTabCurrent = e
+				let timeObj = this.setResetTime(this.timeTabCurrent)
+				this.timeStart = timeObj.start
+				this.timeEnd = timeObj.end
+				this.getData()
+			},
+
+
+			setResetTime(type) {
+				let date = new Date()
+				let time = {
+					start: uni.$u.timeFormat(date, 'yyyy-mm-dd'),
+					end: uni.$u.timeFormat(date, 'yyyy-mm-dd')
+				}
+				switch (type) {
+					case 0: //昨天
+						time = {
+							start: uni.$u.timeFormat(date - 24 * 60 * 60 * 1000, 'yyyy-mm-dd'),
+							end: uni.$u.timeFormat(date - 24 * 60 * 60 * 1000, 'yyyy-mm-dd')
+						}
+						break;
+					case 1: //今天
+						time = {
+							start: uni.$u.timeFormat(date, 'yyyy-mm-dd'),
+							end: uni.$u.timeFormat(date, 'yyyy-mm-dd')
+						}
+						break;
+					case 2: //近7日
+						time = {
+							start: uni.$u.timeFormat(date - 7 * 24 * 60 * 60 * 1000, 'yyyy-mm-dd'),
+							end: uni.$u.timeFormat(date, 'yyyy-mm-dd')
+						}
+						break;
+					case 3: //近30日
+						time = {
+							start: uni.$u.timeFrom(date - 30 * 24 * 60 * 60 * 1000, 'yyyy-mm-dd'),
+							end: uni.$u.timeFrom(date, 'yyyy-mm-dd')
+						}
+						break;
+					case 4: //本月
+						let start = uni.$u.timeFrom(date, 'yyyy-mm-dd')
+						time = {
+							start: start.substr(0, 8) + '01',
+							end: uni.$u.timeFrom(date, 'yyyy-mm-dd')
+						}
+						break;
+					default:
+						break;
+				}
+				return time
+			},
+
+			timePickerShow(type) {
+				this.pickerType = 'year';
+				this.startOrEnd = type
+				if (this.timeCurrent == 2) {
+					this.columns = yearList
+					this.pickerShow = true
+				} else {
+					this.timeShow = true;
+				}
+			},
+
+			goodsSort(type) {
+				this.goodsSortType = type
+				this.getList2(type)
+			},
+
+			close() {
+				this.timeShow = false
+			},
+
+			confirm(e) {
+				let time = '';
+				time = uni.$u.timeFormat(e.value, 'yyyy-mm-dd')
+
+				if (this.startOrEnd == 'start') {
+					this.timeStart = time
+				}
+				if (this.startOrEnd == 'end') {
+					this.timeEnd = time
+				}
+				this.getData()
+				this.timeShow = false;
+			},
+
+			pickerClose() {
+				this.pickerShow = false
+			},
+
+			//获取统计数据
+			getCountData(type) {
+				salesData({
+					type: "day",
+					beginDate: this.timeStart,
+					endDate: this.timeEnd,
+					deviceId: this.id
+				}).then(res => {
+					let data = res.data;
+					let tempChartData = {};
+					this.delData(data);
+					tempChartData.series = data.series ? data.series : [];
+					tempChartData.categories = data.categories ? data.categories : [];
+					this.chartData = JSON.parse(JSON.stringify(tempChartData));
+				}).catch(err => {
+
+				})
+			},
+
+			// 处理统计图表数据
+			delData(data) {
+				let arr = data.categories.map(item => {
+					if (item.length < 3) {
+						item = item + '点'
+					} else {
+						item = item.substr(4, 2) + '月' + item.substr(6, 2) + '日'
+					}
+					return item
+				})
+
+				data.categories = arr
+			},
+
+			getParams(type) {
+				let orderByKey = "";
+				let orderBy = "";
+				switch (type) {
+					case '销售额':
+						orderBy = 'desc';
+						orderByKey = 'sales_money';
+						break;
+					case '订单笔数':
+						orderBy = 'desc';
+						orderByKey = 'sales_count';
+						break;
+					case '销量':
+						orderBy = 'desc';
+						orderByKey = 'sales_count';
+						break;
+					default:
+						break;
+				}
+
+				let params = {
+					type: 'day',
+					orderByKey: orderByKey,
+					orderBy: orderBy,
+					beginDate: this.timeStart,
+					endDate: this.timeEnd,
+					deviceId: this.id
+				}
+				return params
+			},
+
+			//设备统计总数居
+			getDeviceTotal(type) {
+				let params = this.getParams(type)
+				goodSumCount(params).then(res => {
+					this.total = res.data
+				})
+			},
+
+			//获取商品排行
+			getList2(e) {
+				let type = e == 0 ? '销售额' : '销量';
+				let dataParams = this.getParams(type)
+				let pageParams = {
+					page: {
+						current: 1,
+						size: 5
+					}
+				}
+				let params = Object.assign(dataParams, pageParams)
+				goodSumPage(params).then(res => {
+					if (res.data) {
+						this.list2 = res.data.records;
+					} else {
+						this.list2 = []
+					}
+				})
+			},
+
+			more(type) {
+				let sortType = this.goodsSortType
+				this.$tab.navigateTo(
+					`/pages/equipment/statisticsMore?timeStart=${this.timeStart}&&timeEnd=${this.timeEnd}&&sortType=${sortType}&&id=${this.id}`
+				)
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.container {
+		.content {
+			padding-bottom: 24rpx;
+
+			.card {
+				width: 724rpx;
+				margin-left: 13rpx;
+				background-color: #fff;
+				margin-top: 20rpx;
+				box-shadow: 0px 0px 10rpx 0px rgba(174, 201, 255, 0.2);
+				border-radius: 14rpx;
+			}
+
+			.tab {
+				padding: 24rpx;
+				// background-color: #fff;
+			}
+
+			.select {
+				padding-top: 34rpx;
+
+				.time-tab {
+					padding: 0 36rpx;
+
+					.time-tab-item {
+						width: 20%;
+						border: 1rpx solid #CCCCCC;
+						border-left: none;
+						text-align: center;
+						font-size: 26rpx;
+						line-height: 58rpx;
+						color: #777777;
+
+						&:first-of-type {
+							border-left: 1rpx solid #CCCCCC;
+							border-radius: 6rpx 0px 0px 6rpx;
+						}
+
+						&:last-of-type {
+							border-radius: 0px 6rpx 6rpx 0px;
+						}
+
+						&.time-tab-show {
+							background: #F4F8FF;
+							color: #2C6FF3;
+						}
+					}
+				}
+
+				.time-select {
+					padding: 34rpx 36rpx;
+					color: rgb(144, 144, 144);
+					font-size: 28rpx;
+
+					>view {
+						>view:nth-child(1) {
+							width: 160rpx;
+							text-align: right;
+						}
+
+						>view:nth-child(2) {
+							width: 80rpx;
+							padding: 0 24rpx;
+						}
+					}
+				}
+			}
+
+			.total {
+				padding: 12rpx 0;
+				background-color: #fff;
+
+				.total-name {
+					color: #333;
+					font-size: 28rpx;
+					line-height: 28rpx;
+					margin-top: 48rpx;
+				}
+
+				.total-num {
+					color: #eab09a;
+					font-size: 42rpx;
+					font-weight: bold;
+					line-height: 41rpx;
+					margin-top: 24rpx;
+				}
+			}
+
+			.search {
+				padding: 24rpx 24rpx;
+				background-color: #fff;
+				position: relative;
+				font-size: 26rpx;
+
+				.time-type {
+					width: 180rpx;
+				}
+
+				.time-select {
+					width: 340rpx;
+					line-height: 64rpx;
+					background-color: #eeeeef;
+					border-radius: 6rpx;
+				}
+
+
+			}
+
+			.chart {
+				background-color: #fff;
+			}
+
+			.table {
+				width: 724rpx;
+				background-color: #fff;
+				margin-top: 18rpx;
+				color: #333;
+
+				.table-title {
+					font-size: 32rpx;
+					line-height: 50rpx;
+					padding: 24rpx;
+					font-weight: 800;
+					color: #333333;
+					border-bottom: 1rpx solid #ebeef5;
+				}
+
+				.table-td {
+					width: 100%;
+					text-align: center;
+
+
+					&.table-td-name {
+						width: 230rpx;
+						white-space: wrap;
+					}
+				}
+
+				.table-img {
+					width: 40rpx;
+					height: 40rpx;
+					position: relative;
+				}
+
+				.sort-type {
+					width: 250rpx;
+					height: 50rpx;
+					line-height: 48rpx;
+					text-align: center;
+					font-weight: normal;
+
+					.sort-type-item {
+						width: 50%;
+						font-size: 26rpx;
+						color: #555555;
+						background-color: #fff;
+
+						&.sort-type-item:nth-child(1) {
+							border-top-left-radius: 6rpx;
+							border-bottom-left-radius: 6rpx;
+							border-top: 1rpx solid #CCCCCC;
+							border-left: 1rpx solid #CCCCCC;
+							border-bottom: 1rpx solid #CCCCCC;
+
+							&.sort-type-show {
+								color: #fff;
+								border-top: 1rpx solid #2C6FF3;
+								border-left: 1rpx solid #2C6FF3;
+								border-bottom: 1rpx solid #2C6FF3;
+								background-color: #2C6FF3;
+							}
+						}
+
+						&.sort-type-item:nth-child(2) {
+							border-top-right-radius: 6rpx;
+							border-bottom-right-radius: 6rpx;
+							border-top: 1rpx solid #CCCCCC;
+							border-right: 1rpx solid #CCCCCC;
+							border-bottom: 1rpx solid #CCCCCC;
+
+							&.sort-type-show {
+								color: #fff;
+								border-top: 1rpx solid #2C6FF3;
+								border-right: 1rpx solid #2C6FF3;
+								border-bottom: 1rpx solid #2C6FF3;
+								background-color: #2C6FF3;
+							}
+						}
+					}
+
+				}
+
+				/deep/.uni-table-th {
+					color: #333;
+					font-weight: normal;
+				}
+
+				/deep/.uni-table-td {
+					vertical-align: middle;
+				}
+			}
+
+			.more {
+				text-align: right;
+				padding-right: 40rpx;
+				position: relative;
+				color: #2C6FF3;
+				line-height: 80rpx;
+				background-color: #fff;
+
+				>view {
+					position: absolute;
+					right: 12rpx;
+					top: 50%;
+					transform: translateY(-50%);
+				}
+			}
+		}
+	}
+</style>

+ 349 - 0
pages/equipment/statisticsMore.vue

@@ -0,0 +1,349 @@
+<template>
+	<view class="container">
+		<u-navbar leftIconColor="#fff" titleStyle="color:#fff;fontSize:36rpx;" :autoBack="true" bgColor="#2C6FF3"
+			:placeholder="true" title="销售统计"></u-navbar>
+		<view class="content">
+			<view class="goods-table">
+				<view class="table">
+					<view class="table-title flex justify-between align-center">
+						<view class="title">
+							商品销售排行
+						</view>
+						<view class="sort-type flex">
+							<view :class="[goodsSortType==0?'sort-type-item sort-type-show':'sort-type-item']"
+								@click="goodsSort(0)">
+								销售额
+							</view>
+							<view :class="[goodsSortType==1?'sort-type-item sort-type-show':'sort-type-item']"
+								@click="goodsSort(1)">
+								销量
+							</view>
+						</view>
+					</view>
+					<uni-table :border="false" :stripe="false" emptyText="暂无更多数据">
+						<!-- 表头行 -->
+						<uni-tr>
+							<uni-th align="center" width="52">排名</uni-th>
+							<uni-th align="center" width="110">上榜商品</uni-th>
+							<uni-th align="center" width="92">销售额</uni-th>
+							<uni-th align="center" width="92">销量</uni-th>
+						</uni-tr>
+						<!-- 表格数据行 -->
+						<uni-tr v-for="(item,index) in list2" :key="item.goodsId">
+							<uni-td v-if="index==0">
+								<view class="table-td">
+									<image class="table-img" src="../../static/images/global/first-class.png"
+										mode="widthFix"></image>
+								</view>
+							</uni-td>
+							<uni-td v-else-if="index==1">
+								<view class="table-td">
+									<image class="table-img" src="../../static/images/global/second-class.png"
+										mode="widthFix"></image>
+								</view>
+							</uni-td>
+							<uni-td v-else-if="index==2">
+								<view class="table-td">
+									<image class="table-img" src="../../static/images/global/third-class.png"
+										mode="widthFix"></image>
+								</view>
+							</uni-td>
+							<uni-td v-else>
+								<view class="table-td">
+									{{index+1}}
+								</view>
+							</uni-td>
+							<uni-td>
+								<view class="table-td table-td-name">{{item.goodsName}}</view>
+							</uni-td>
+							<uni-td>
+								<view class="table-td">¥{{$xy.delMoney(item.salesMoney)}}</view>
+							</uni-td>
+							<uni-td>
+								<view class="table-td">{{item.salesCount}}件</view>
+							</uni-td>
+						</uni-tr>
+					</uni-table>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import {
+		goodSumPage
+	} from "@/api/device/device.js"
+
+	export default {
+		data() {
+			return {
+				timeStart: '',
+				timeEnd: '',
+
+				typeColumns: [
+					['销售额', '订单笔数']
+				],
+				status: 'loadmore', //加载更多
+				list1: [], //设备列表
+				list2: [], //商品列表
+				deviceSortType: 0,
+				goodsSortType: 0,
+				id: null
+			}
+		},
+
+		onLoad(o) {
+			this.id = o.id
+			this.timeStart = o.timeStart
+			this.timeEnd = o.timeEnd
+			this.goodsSortType = o.sortType
+		},
+
+		onShow() {
+			this.getData()
+		},
+
+
+
+		methods: {
+			getData() {
+				this.getList(this.goodsSortType)
+			},
+
+			goodsSort(type) {
+				this.goodsSortType = type
+				this.getList(type)
+			},
+
+			getParams(type) {
+				let orderByKey = "";
+				let orderBy = "";
+				switch (type) {
+					case '销售额':
+						orderBy = 'desc';
+						orderByKey = 'sales_money';
+						break;
+					case '销量':
+						orderBy = 'desc';
+						orderByKey = 'sales_count';
+						break;
+					default:
+						break;
+				}
+
+				let params = {
+					type: 'day',
+					orderByKey: orderByKey,
+					orderBy: orderBy,
+					beginDate: this.timeStart,
+					endDate: this.timeEnd,
+					deviceId: this.id
+				}
+				return params
+			},
+
+			//获取商品排行
+			getList(e) {
+				let type = e == 0 ? '销售额' : '销量';
+				let dataParams = this.getParams(type)
+				let pageParams = {
+					page: {
+						current: 1,
+						size: 1000
+					}
+				}
+				let params = Object.assign(dataParams, pageParams)
+				goodSumPage(params).then(res => {
+					if (res.data) {
+						this.list2 = res.data.records;
+					} else {
+						this.list2 = []
+					}
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.container {
+		.content {
+			.select {
+				.time-tab {
+					padding: 0 36rpx;
+
+					.time-tab-item {
+						padding: 0 24rpx;
+						background-color: #fff;
+						border: 1rpx solid #e0b4a2;
+						text-align: center;
+						line-height: 50rpx;
+						color: #e0b4a2;
+						border-radius: 40rpx;
+
+						&.time-tab-show {
+							background-color: #eab09a;
+							color: #d98a6d;
+						}
+					}
+				}
+
+				.time-select {
+					padding: 24rpx 36rpx;
+					color: rgb(144, 144, 144);
+					font-size: 28rpx;
+
+					>view:nth-child(2) {
+						width: 160rpx;
+						text-align: right;
+					}
+
+					>view:nth-child(3) {
+						width: 80rpx;
+						padding: 0 24rpx;
+					}
+				}
+			}
+
+			.total {
+				padding: 12rpx 0;
+				background-color: #fff;
+
+				.total-name {
+					color: #333;
+					font-size: 28rpx;
+					line-height: 50rpx;
+				}
+
+				.total-num {
+					color: #eab09a;
+					font-size: 32rpx;
+					font-weight: bold;
+					line-height: 50rpx;
+				}
+			}
+
+			.search {
+				padding: 24rpx 24rpx;
+				background-color: #fff;
+				position: relative;
+				font-size: 26rpx;
+
+				.time-type {
+					width: 180rpx;
+				}
+
+				.time-select {
+					width: 340rpx;
+					line-height: 64rpx;
+					background-color: #eeeeef;
+					border-radius: 6rpx;
+				}
+
+
+			}
+
+			.table {
+				width: 100%;
+				background-color: #fff;
+				color: #333;
+
+				.table-title {
+					line-height: 50rpx;
+					padding: 24rpx;
+					border-bottom: 1rpx solid #ebeef5;
+				}
+
+				.table-td {
+					width: 100%;
+					text-align: center;
+
+					&.table-td-name {
+						width: 270rpx;
+						white-space: wrap;
+					}
+				}
+
+				.table-img {
+					width: 40rpx;
+					height: 40rpx;
+					position: relative;
+				}
+
+				.sort-type {
+					width: 250rpx;
+					height: 50rpx;
+					line-height: 48rpx;
+					text-align: center;
+					font-weight: normal;
+
+					.sort-type-item {
+						width: 50%;
+						font-size: 26rpx;
+						color: #555555;
+						background-color: #fff;
+
+						&.sort-type-item:nth-child(1) {
+							border-top-left-radius: 6rpx;
+							border-bottom-left-radius: 6rpx;
+							border-top: 1rpx solid #CCCCCC;
+							border-left: 1rpx solid #CCCCCC;
+							border-bottom: 1rpx solid #CCCCCC;
+
+							&.sort-type-show {
+								color: #fff;
+								border-top: 1rpx solid #2C6FF3;
+								border-left: 1rpx solid #2C6FF3;
+								border-bottom: 1rpx solid #2C6FF3;
+								background-color: #2C6FF3;
+							}
+						}
+
+						&.sort-type-item:nth-child(2) {
+							border-top-right-radius: 6rpx;
+							border-bottom-right-radius: 6rpx;
+							border-top: 1rpx solid #CCCCCC;
+							border-right: 1rpx solid #CCCCCC;
+							border-bottom: 1rpx solid #CCCCCC;
+
+							&.sort-type-show {
+								color: #fff;
+								border-top: 1rpx solid #2C6FF3;
+								border-right: 1rpx solid #2C6FF3;
+								border-bottom: 1rpx solid #2C6FF3;
+								background-color: #2C6FF3;
+							}
+						}
+					}
+
+				}
+
+				/deep/.uni-table-th {
+					color: #333;
+					font-weight: normal;
+				}
+
+				/deep/.uni-table-td {
+					vertical-align: middle;
+				}
+			}
+
+			.more {
+				text-align: right;
+				padding-right: 40rpx;
+				position: relative;
+				color: #333;
+				line-height: 80rpx;
+				background-color: #fff;
+
+				>view {
+					position: absolute;
+					right: 12rpx;
+					top: 50%;
+					transform: translateY(-50%);
+				}
+			}
+		}
+	}
+</style>

+ 76 - 0
pages/globalPages/agreement.vue

@@ -0,0 +1,76 @@
+<template>
+	<view class="container">
+		<u-navbar leftIconColor="#fff" titleStyle="color:#fff;fontSize:36rpx;" :autoBack="true" bgColor="#2C6FF3"
+			:placeholder="true" title="入驻协议"></u-navbar>
+		<view class="content safe-bottom">
+			<pre v-html="content" v-if="content!=''">
+			</pre>
+			<view class="empty" v-else>
+				<u-empty mode="list" text="没有任何内容!"></u-empty>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import {
+		agreement,
+		agreementPage
+	} from "@/api/system/user.js"
+	export default {
+		data() {
+			return {
+				content: ''
+			}
+		},
+
+		onLoad() {
+			this.detail()
+		},
+
+		methods: {
+			agreementList() {
+				agreement({
+					page: {
+						current: 1,
+						size: 100
+					},
+					orders: [{
+						asc: false,
+						column: "create_time"
+					}]
+				}).then(res => {
+					if (res.data && res.data.content) {
+						this.content = res.data.content
+					} else {
+						this.content = ''
+					}
+				})
+			},
+
+			detail() {
+				agreement({
+					type: 3
+				}).then(res => {
+					this.content = res.data.content
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.container {
+		overflow: hidden;
+		background-color: #fff;
+
+		.content {
+			padding: 30rpx;
+			min-height: 100vh;
+
+			.empty {
+				margin-top: 40%;
+			}
+		}
+	}
+</style>

+ 661 - 0
pages/globalPages/components/account.vue

@@ -0,0 +1,661 @@
+<template>
+	<view class="container">
+		<!-- 个人信息 -->
+		<view class="user">
+			<view class="nav-bar">
+				<u-navbar bgColor="rgba(0,0,0,0)" :autoBack="false" :placeholder="true">
+					<view slot="left" style="color:#fff;font-size: 36rpx;">
+						我的
+					</view>
+				</u-navbar>
+			</view>
+			<view class="user-content flex align-center flex-start">
+				<view class="head-img" @click="userDetail">
+					<image src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/globalPages/head-exam.png"
+						mode="widthFix">
+					</image>
+				</view>
+				<view class="user-name" @click="searchLog(2)">
+					{{info.name}}
+				</view>
+
+				<view class="share" @share="share">
+					<view class="share-image">
+						<image src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/globalPages/fx.png"
+							mode="widthFix">
+						</image>
+					</view>
+					<button open-type="share"></button>
+				</view>
+
+				<view class="login-out" @click="setting">
+					设置
+				</view>
+			</view>
+		</view>
+
+		<!-- 用户信息 -->
+		<view class="menu menu-userinfo">
+			<!-- 			<view class="menu-title">
+				用户信息
+			</view> -->
+			<view class="u-content">
+				<view class="u-item flex align-center flex-direction" @click="searchLog(1)">
+					<view class="u-name">
+						{{info.mercName}}
+					</view>
+					<view class="u-val">
+						<image src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/globalPages/sh.png"
+							mode="widthFix"></image>
+						商户
+					</view>
+				</view>
+				<view class="u-item">
+					<view class="u-name">
+						{{info.roleNames}}
+					</view>
+					<view class="u-val">
+						<image src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/globalPages/js.png"
+							mode="widthFix"></image>
+						角色
+					</view>
+				</view>
+				<view class="u-item">
+					<view class="u-name">
+						{{info.tel}}
+					</view>
+					<view class="u-val">
+						<image src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/globalPages/dh.png"
+							mode="widthFix"></image>
+						电话
+					</view>
+				</view>
+			</view>
+
+			<!-- <xbutton class="edit" size="mini" @click="btnClick('编辑')">修改</xbutton> -->
+		</view>
+
+		<!-- <view class="menu">
+			<view class="menu-title">
+				系统参数设置
+			</view>
+			<view class="s-content">
+				<view class="s-item">
+					自动处理未上架订单:<text>是</text>
+				</view>
+				<view class="s-item">
+					开启预定模式:<text>是</text>
+				</view>
+				<view class="s-item">
+					是否强制盘点:<text>是</text>
+				</view>
+			</view>
+
+			<xbutton class="edit" size="mini" @click="btnClick('编辑')">修改</xbutton>
+		</view> -->
+
+		<!--团队角色管理 -->
+		<view class="menu convenient" v-if="roleManageMenu.length>0">
+			<!--<view class="menu-title">
+				团队角色管理
+			</view> -->
+			<view v-if="roleManageMenu.length>0" class="flex flex-wrap justify-start">
+				<view class="menu-item" v-for="(item,index) in roleManageMenu" :key="item.id" @click="menuClick(item)">
+					<view class="image">
+						<u-image width="74rpx" height="74rpx" :src="iconList[item.name]" mode="widthFix"
+							:lazy-load="true"></u-image>
+					</view>
+					<view>{{item.name}}</view>
+				</view>
+			</view>
+			<view class="empty" v-else>
+				<u-empty mode="permission"></u-empty>
+			</view>
+		</view>
+
+		<!--应用管理 -->
+		<view class="menu convenient" v-if="costManageMenu.length>0">
+			<!-- 	<view class="menu-title">
+				费用/提现管理
+			</view> -->
+			<view v-if="costManageMenu.length>0" class="flex flex-wrap justify-start">
+				<view class="menu-item" v-for="(item,index) in costManageMenu" :key="item.id" @click="menuClick(item)">
+					<view class="image">
+						<u-image width="74rpx" height="74rpx" :src="iconList[item.name]" mode="widthFix"
+							:lazy-load="true"></u-image>
+					</view>
+					<view>{{item.name}}</view>
+				</view>
+			</view>
+			<view class="empty" v-else>
+				<u-empty mode="permission"></u-empty>
+			</view>
+		</view>
+
+		<!-- <view class="chang-password" @click="changePwd">
+			修改密码
+		</view> -->
+
+		<xpopup :show="qrcodeShow" mode="center" @close="qrcodeClose" :showBtn="false">
+			<view class="qrcode-content flex flex-direction align-center">
+				<view class="qrcode-img">
+					<u-image width="400rpx" height="400rpx"
+						src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/globalPages/miniQrcode.jpg"
+						mode="widthFix" :lazy-load="true">
+					</u-image>
+				</view>
+				<view class="save-qrcode" slot="botton">
+					<xbutton @click="save">保存二维码</xbutton>
+				</view>
+			</view>
+		</xpopup>
+
+		<xpopup :show="pwdShow" @close="pwdClose" @confirm="pwdSubmit" :showBtn="true" title="修改密码">
+			<view class="pwd-popup-content flex align-center">
+				<view>新密码:</view>
+				<view>
+					<u--input placeholder="请输入新密码" type="password" border="surround" v-model="newpassword"></u--input>
+				</view>
+			</view>
+		</xpopup>
+	</view>
+</template>
+
+<script>
+	import {
+		removeToken
+	} from '@/utils/auth'
+	// import Xypopup from '@/components/xy-popup'
+	import {
+		userInfo,
+		updateUserInfo
+	} from "@/api/system/user.js"
+
+	export default {
+		// components: {
+		// 	Xypopup
+		// },
+		data() {
+			return {
+				tabArr: [{
+						name: '今日',
+						id: 0
+					},
+					{
+						name: '本月',
+						id: 1
+					}
+				],
+				current: 0,
+
+				//菜单对应图片路径
+				iconList: {
+					'退款审核': 'https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/home/refund.png',
+					'风险订单': 'https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/home/risk.png',
+					'客诉处理': 'https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/home/cc.png',
+					'补货管理': 'https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/home/replenishment.png',
+					'增加设备': 'https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/home/menu-icon (9).png',
+					'增加点位': 'https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/home/menu-icon (8).png',
+					'团队管理': 'https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/globalPages/tdgl.png',
+					'加盟商': 'https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/globalPages/jms.png',
+					'流量卡管理': 'https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/home/menu-icon (5).png',
+					'会员管理': 'https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/globalPages/hygl.png',
+					'运营仓': 'https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/home/menu-icon (7).png',
+					'黑名单': 'https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/globalPages/hmd.png',
+					'营销管理': 'https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/home/menu-icon (6).png',
+					'费用账单': 'https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/home/menu-icon (10).png',
+					'小程序二维码': 'https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/globalPages/xcxewm.png',
+					'通知': 'https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/globalPages/tz.png',
+					'客服': 'https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/globalPages/kf.png',
+					'入驻协议': 'https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/globalPages/rzxy.png',
+				},
+
+				info: {
+					mercName: "",
+					name: "sunny",
+					roleNames: "",
+					tel: ""
+				},
+				timer: null,
+				logNum: 0,
+
+				qrcodeShow: false,
+
+				newpassword: '',
+				pwdShow: false,
+			}
+		},
+
+		computed: {
+			//团队角色管理
+			roleManageMenu() {
+				return this.getMenu('我的', '团队角色管理')
+			},
+			//应用管理
+			costManageMenu() {
+				return this.getMenu('我的', '应用管理')
+			},
+
+			//用户名
+			name() {
+				return this.$store.state.user.name
+			},
+		},
+
+		onShareAppMessage() {
+			return {
+				title: '喵星人商家助手',
+				path: '/pages/login',
+				imageUrl: 'https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/merc_qrcode.jpg',
+				success: function(res) {
+					// 转发成功之后的回调
+					if (res.errMsg == 'shareAppMessage:ok') {
+
+					}
+				},
+				fail: function() {
+					// 转发失败之后的回调
+					if (res.errMsg == 'shareAppMessage:fail cancel') {
+						// 用户取消转发
+					} else if (res.errMsg == 'shareAppMessage:fail') {
+						// 转发失败,其中 detail message 为详细失败信息
+					}
+				},
+			}
+		},
+
+		created() {
+			this.getUserInfo()
+		},
+
+		methods: {
+			searchLog(type) {
+				this.logNum++
+				if (this.timer) {
+					if (this.logNum > 6) {
+						this.logNum = 0
+						clearTimeout(this.timer)
+						this.timer = null
+						let url=type==1?'/pages/globalPages/logs':'/pages/globalPages/test'
+						this.$tab.navigateTo(url)
+					}
+				} else {
+					this.timer = setTimeout(() => {
+						this.logNum = 0
+						clearTimeout(this.timer)
+						this.timer = null
+					}, 2000)
+				}
+
+			},
+
+			getUserInfo() {
+				userInfo().then(res => {
+					if (res.code == 200) {
+						this.info = res.data
+					} else {
+						this.info = null
+					}
+				})
+			},
+
+			getMenu(menu1, menu2) {
+				let menu = []
+				if (this.$store.state.permission.permissions_menu && this.$store.state.permission.permissions_menu !=
+					'[]') {
+					let allMenu = JSON.parse(this.$store.state.permission.permissions_menu);
+					if (allMenu.find(i => i.name == menu1) && allMenu.find(i => i.name == menu1).children) {
+						let homeMenu = allMenu.find(i => i.name == menu1).children;
+						if (homeMenu.find(i => i.name == menu2)) {
+							if (homeMenu.find(i => i.name == menu2).children) {
+								menu = homeMenu.find(i => i.name == menu2).children
+							}
+						}
+					}
+				}
+				return menu
+			},
+
+			btnClick(e) {
+
+			},
+
+			menuClick(e) {
+				if (e.name == '团队管理') {
+					this.$tab.navigateTo('/pages/system/employee')
+					return
+				}
+				if (e.name == '小程序二维码') {
+					this.qrcodeShow = true;
+					return
+				}
+				if (e.name == '通知') {
+					this.$tab.navigateTo('/pages/globalPages/notice')
+					return
+				}
+				if (e.name == '客服') {
+					this.phone('88888888')
+					return
+				}
+				if (e.name == '入驻协议') {
+					this.$tab.navigateTo('/pages/globalPages/agreement')
+					return
+				}
+
+				this.$modal.msg('功能开发中,尽请期待~')
+			},
+
+			loginOut() {
+				this.$store.dispatch('LogOut').then(res => {
+					this.$tab.reLaunch('/pages/login')
+				})
+			},
+
+			qrcodeClose() {
+				this.qrcodeShow = false;
+			},
+
+			save() {
+				let imgUrl = 'https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/globalPages/miniQrcode.jpg'
+				wx.getSetting({
+					success: function(res) {
+						if (!res.authSetting['scope.writePhotosAlbum']) { //判断是否开启相册权限
+							uni.authorize({
+								scope: 'scope.writePhotosAlbum',
+								success: function(res) {
+									uni.downloadFile({
+										url: imgUrl,
+										success: function(res) {
+											uni.saveImageToPhotosAlbum({
+												filePath: res.tempFilePath,
+												success: function(res) {
+													wx.showToast({
+														title: '保存成功',
+													})
+												}
+											})
+										}
+									})
+								},
+								fail(res) {
+									uni.showModal({
+										title: '提示',
+										content: '您未开启保存图片到相册的权限,请点击确定去开启权限!',
+										success(res) {
+											if (res.confirm) {
+												uni.openSetting()
+											}
+										}
+									})
+								}
+							})
+						} else {
+							uni.downloadFile({
+								url: imgUrl,
+								success: function(res) {
+									uni.saveImageToPhotosAlbum({
+										filePath: res.tempFilePath,
+										success: function(res) {
+											wx.showToast({
+												title: '保存成功',
+											})
+										}
+									})
+								}
+							})
+
+						}
+					},
+					fail(res) {}
+				})
+			},
+
+			//拨打电话
+			phone(tel) {
+				uni.makePhoneCall({
+					phoneNumber: tel
+				})
+			},
+			
+			setting(){
+				this.$tab.navigateTo('/pages/globalPages/setting')
+			},
+
+			changePwd() {
+				this.pwdShow = true
+			},
+
+			pwdSubmit() {
+				if (this.newpassword) {
+					updateUserInfo({
+						password: this.newpassword
+					}).then(res => {
+						this.$modal.showToast('修改成功~')
+					})
+					this.pwdClose()
+				} else {
+					this.$modal.msg('请输入新密码!')
+				}
+			},
+
+			pwdClose() {
+				this.pwdShow = false
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.container {
+		padding-bottom: 100rpx;
+
+		// 个人信息
+		.user {
+			background: #2C6FF3 url('https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/globalPages/user-bg.png') no-repeat left top;
+			background-size: 100%;
+			padding-bottom: 145rpx;
+			position: relative;
+			width: 100%;
+
+			.user-content {
+				margin-top: 34rpx;
+				width: 100%;
+
+				.head-img {
+					width: 100rpx;
+					height: 100rpx;
+					border-radius: 100rpx;
+					margin-left: 26rpx;
+				}
+
+				.user-name {
+					font-size: 28rpx;
+					color: #fff;
+					margin-left: 33rpx;
+				}
+
+				.share {
+					width: 30rpx;
+					height: 30rpx;
+					position: relative;
+					margin-left: 20rpx;
+
+					.share-image {
+						width: 30rpx;
+						height: 30rpx;
+						position: absolute;
+						left: 0;
+						top: 0;
+
+						>image {
+							width: 30rpx;
+							height: 30rpx;
+						}
+					}
+
+					button {
+						width: 40rpx;
+						height: 40rpx;
+						position: absolute;
+						left: 0;
+						top: 0;
+						opacity: 0;
+					}
+				}
+
+				.login-out {
+					width: 140rpx;
+					line-height: 52rpx;
+					height: 52rpx;
+					text-align: center;
+					font-size: 24rpx;
+					color: #2C6FF3;
+					background-color: #fff;
+					position: absolute;
+					right: 0;
+					top: 185rpx;
+					z-index: 99;
+					border-top-left-radius: 52rpx;
+					border-bottom-left-radius: 52rpx;
+				}
+			}
+		}
+
+
+		// 常用菜单
+		.menu {
+			width: 724rpx;
+			margin-left: 14rpx;
+			background: #FFFFFF;
+			box-shadow: 0px 0px 10rpx 0px rgba(174, 201, 255, 0.2);
+			border-radius: 14rpx;
+			margin-top: 24rpx;
+			position: relative;
+			padding: 20rpx 36rpx 42rpx;
+
+			&.menu-userinfo {
+				padding: 36rpx;
+				margin-top: -102rpx;
+			}
+
+			.menu-title {
+				font-size: 30rpx;
+				font-weight: 800;
+				color: #000000;
+				position: absolute;
+				left: 28rpx;
+				top: 28rpx;
+			}
+
+			.u-content {
+				display: flex;
+				flex-flow: row nowrap;
+				justify-content: space-around;
+				text-align: center;
+
+				.u-item {
+					line-height: 50rpx;
+
+					.u-name {
+						font-weight: bold;
+						font-size: 30rpx;
+					}
+
+					.u-val {
+						font-size: 28rpx;
+						color: #666;
+						position: relative;
+						padding-left: 30rpx;
+						display: inline-block;
+
+						>image {
+							width: 21rpx;
+							height: 21rpx;
+							position: absolute;
+							left: 0;
+							top: 50%;
+							transform: translateY(-50%);
+						}
+					}
+				}
+			}
+
+			.cu-btn {
+				padding: 0 12rpx;
+				font-size: 22rpx;
+				height: 40rpx;
+				line-height: 40rpx;
+				background-color: #2C6FF3;
+				color: #fff;
+
+			}
+
+			.edit {
+				position: absolute;
+				right: 28rpx;
+				top: 28rpx;
+			}
+
+			.s-content {
+				.s-item {
+					line-height: 50rpx;
+
+					>text {
+						color: red;
+					}
+				}
+			}
+
+			.menu-item {
+				display: flex;
+				flex-direction: column;
+				align-items: center;
+				width: 25%;
+
+				>.image {
+					width: 74rpx;
+					height: 74rpx;
+				}
+
+				>view {
+					color: #333333;
+					font-size: 26rpx;
+					line-height: 26rpx;
+					margin-top: 16rpx;
+				}
+			}
+		}
+
+		.qrcode-content {
+			padding: 24rpx;
+
+			.qrcode-img {
+				width: 400rpx;
+				height: 400rpx;
+			}
+
+			.save-qrcode {
+				margin-top: 24rpx;
+			}
+		}
+
+		.chang-password {
+			text-align: center;
+			line-height: 60rpx;
+			color: #2C6FF3;
+			text-decoration: underline;
+		}
+
+		.pwd-popup-content {
+			padding: 24rpx;
+
+			>view:nth-child(1) {
+				width: 160rpx;
+			}
+
+			>view:nth-child(2) {
+				width: 100%;
+			}
+		}
+	}
+</style>

+ 315 - 0
pages/globalPages/components/commodity.vue

@@ -0,0 +1,315 @@
+<template>
+	<view class="container">
+		<u-navbar bgColor="#2C6FF3" :placeholder="true" :autoBack="false">
+			<view slot="left" style="color:#fff;font-size: 36rpx;">
+				商品私库
+			</view>
+		</u-navbar>
+		<view class="content">
+			<view class="btn-wrap flex justify-between">
+				<xbutton bgColor="#F7F7F7" color="#777777" @click="$tab.navigateTo('/pages/commodity/search')">商品上下架
+				</xbutton>
+				<xbutton bgColor="#F7F7F7" color="#777777" @click="$tab.navigateTo('/pages/commodity/addCom')">新品建模
+				</xbutton>
+				<xbutton bgColor="#F7F7F7" color="#777777" @click="$tab.navigateTo('/pages/commodity/publicCom')">官方商品库
+				</xbutton>
+			</view>
+			<view class="search" @click="searchComm">
+				<view class="search-input">
+					<u-search placeholder="商品搜索" actionText="取消" :actionStyle="{color:'#2C6FF3'}"
+						:showAction="!leftShow" :clearabled="false" v-model="keyword" @search="search"
+						@custom="cancle"></u-search>
+					<view @click="scan" :class="[leftShow?'scan-icon scan-left-show':'scan-icon scan-left-hidden']">
+						<u-icon name="scan" size="22" color="#909399"></u-icon>
+					</view>
+				</view>
+
+				<view class="search-history flex flex-wrap flex-start" v-if="!leftShow">
+					<view class="history-item" v-for="(item,index) in historyList" :key="index"
+						@click="searchFast(item)">
+						{{item}}
+					</view>
+				</view>
+			</view>
+			<view class="classify-wrap">
+				<Classify storeName="perStor" :tabList="tabList" :status="status" :commList="commList"
+					@switchMenu="switchMenu" @comClick='detail' @lowerBottom="lowerBottom" :height="fullHeight"
+					:leftShow="leftShow" />
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import Classify from "@/components/classify/index.vue"
+
+	import {
+		goodsCategory,
+		ownerGoodsList
+	} from "@/api/commodity/mercGoods.js"
+	export default {
+		components: {
+			Classify
+		},
+		data() {
+			return {
+				fullHeight: '0',
+				tabList: [], //商品类目
+				commList: [], //商品列表
+
+				page: 1, //商品分页
+				size: 10,
+				categoryCode: null,
+
+				status: 'loadmore', //加载更多
+
+				leftShow: true,
+				keyword: '',
+				historyList: []
+			}
+		},
+		created() {
+			let _this = this;
+			const query = uni.createSelectorQuery().in(this);
+			query.select(".classify-wrap").boundingClientRect((data) => {
+				_this.top = data.top;
+
+				uni.getSystemInfo({
+					success(res) {
+						// 针对iPhone X等机型底部安全距离做适配
+						const model = res.model;
+						const modelInclude = [
+							"iPhone X", 
+							'iPhone XR', 
+							"iPhone XS", 
+							"iPhone XS MAX",
+							"iPhone 12/13 mini",
+							"iPhone 12/13 (Pro)",
+							"iPhone 12/13 Pro Max",
+							"iPhone 14 Pro Max"
+						];
+						let safeDistance = modelInclude.includes(model)
+						//动态设置商品区域高度
+						if (safeDistance) {
+							_this.fullHeight = res.windowHeight - _this.top - 84 + 'px';
+						} else {
+							_this.fullHeight = res.windowHeight - _this.top - 50 + 'px';
+						}
+					},
+				});
+			}).exec();
+
+			if (uni.getStorageSync('goods')) {
+				this.historyList = JSON.parse(uni.getStorageSync('goods'))
+			}
+			this.getCategory()
+		},
+
+		methods: {
+			onshow() {
+				this.reset()
+				this.getCommList()
+			},
+
+			search(val) {
+				this.saveKeyWord('goods', this.keyword)
+				this.reset();
+				this.getCommList()
+			},
+
+			cancle(val) {
+				this.keyword = ''
+				this.leftShow = true
+				this.search()
+			},
+
+			saveKeyWord(type, val) {
+				if (val) {
+					let arr = []
+					if (uni.getStorageSync(type)) {
+						let arr = JSON.parse(uni.getStorageSync(type))
+						if (arr.indexOf(val) != -1) {
+							console.log('arr.indexOf(val)', arr.indexOf(val))
+							arr.splice(arr.indexOf(val), 1)
+						}
+						arr.unshift(val)
+						if (arr.length > 6) {
+							arr.pop()
+						}
+						this.historyList = JSON.parse(JSON.stringify(arr))
+						uni.setStorageSync(type, JSON.stringify(arr))
+					} else {
+						arr.unshift(val)
+						this.historyList = JSON.parse(JSON.stringify(arr))
+						uni.setStorageSync(type, JSON.stringify(arr))
+					}
+				} else {
+					return
+				}
+			},
+
+			searchFast(e) {
+				this.keyword = e
+				this.search()
+			},
+
+			//扫码
+			scan() {
+				uni.scanCode({
+					success: (res) => {
+						this.keyword = res.result;
+						this.search()
+					}
+				});
+			},
+
+			searchComm() {
+				this.leftShow = false
+			},
+
+			//获取类目列表
+			getCategory() {
+				goodsCategory().then(res => {
+					this.tabList = res.data
+					if (this.tabList && this.tabList.length > 0) {
+						this.switchMenu(this.tabList[0])
+					} else {
+						this.reset()
+					}
+				})
+			},
+
+			//商品类目切换
+			switchMenu(item) {
+				this.categoryCode = item.categoryCode
+				this.reset()
+				this.getCommList()
+			},
+
+			//根据类目获取商品列表
+			getCommList() {
+				let params = {}
+				if (this.leftShow) { //搜索
+					params = {
+						categoryCode: this.categoryCode,
+						page: {
+							current: this.page,
+							size: this.size
+						},
+						status: '1' //0下架1上架
+					}
+				} else { //非搜索
+					params = {
+						page: {
+							current: this.page,
+							size: this.size
+						},
+						status: '1', //0下架1上架
+						keyword: this.keyword
+					}
+				}
+				ownerGoodsList(params).then(res => {
+					let data = res.data.records;
+					if (data && data.length > 0) {
+						data = data.map(i => {
+							i.name = i.goodsName;
+							i.barcode = i.goodsBarcode;
+							i.cover = i.goodsCover;
+							i.price = i.price / 100;
+							i.categoryName = i.capacity == null ? '未分类' : i.capacity;
+							return i
+						})
+					}
+					if (data.length < 10) {
+						this.status = "nomore"
+					} else {
+						this.status = "loadmore"
+					}
+					this.commList = this.commList.concat(data)
+				})
+			},
+
+			//触底加载更多
+			lowerBottom() {
+				if (this.status == 'nomore') return
+				this.page++
+				this.getCommList()
+			},
+
+			reset() {
+				this.status == 'loadmore'
+				this.page = 1;
+				this.size = 10;
+				this.commList = [];
+			},
+
+			detail(e) {
+				this.$tab.navigateTo('/pages/commodity/comEdit?id=' + e.id)
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.btn-wrap {
+		padding: 24rpx;
+		background-color: #fff;
+	}
+
+	.search {
+		padding: 0 24rpx 24rpx;
+		background-color: #fff;
+
+		.search-input {
+			position: relative;
+
+			.scan-icon {
+				position: absolute;
+				top: 50%;
+				transform: translateY(-50%);
+				z-index: 2;
+
+				&.scan-left-show {
+					right: 36rpx;
+				}
+
+				&.scan-left-hidden {
+					right: 100rpx;
+				}
+			}
+		}
+
+		.search-history {
+
+			.history-item {
+				margin-right: 24rpx;
+				padding: 0 12rpx;
+				background-color: #f2f2f2;
+				color: #333;
+				font-size: 24rpx;
+				line-height: 40rpx;
+				border-radius: 40rpx;
+				margin-top: 24rpx;
+			}
+		}
+	}
+
+	.classify-wrap {
+		// padding-bottom: 200rpx;
+	}
+
+	.btn {
+		width: 100%;
+		position: fixed;
+		bottom: 120rpx;
+		left: 0;
+		display: flex;
+		flex-flow: row nowrap;
+		justify-content: space-between;
+		padding: 0 24rpx;
+
+		&.safa-btn {
+			bottom: 180rpx;
+		}
+	}
+</style>

+ 362 - 0
pages/globalPages/components/equipment.vue

@@ -0,0 +1,362 @@
+<template>
+	<view class="container">
+		<u-navbar bgColor="#2C6FF3" :placeholder="true" :autoBack="false">
+			<view slot="left" style="color:#fff;font-size: 36rpx;">
+				设备管理
+			</view>
+		</u-navbar>
+		<view class="total">
+			<view class="total-item">
+				<view class="num">
+					{{totalData.onlineNum}}/{{totalData.offlineNum}}
+				</view>
+				<view class="name">
+					在线/离线
+				</view>
+			</view>
+			<view class="total-item">
+				<view class="num">
+					{{totalData.operatingNum}}/{{totalData.closedNum}}
+				</view>
+				<view class="name">
+					运营/停业
+				</view>
+			</view>
+			<view class="total-item" @click="$tab.navigateTo('/pages/replenish/replenishmentManagement')">
+				<view class="num">
+					{{totalData.needToFillNum}}
+				</view>
+				<view class="name">
+					待补货
+				</view>
+			</view>
+		</view>
+		<view class="search">
+			<!-- <u-search animation placeholder="请输入设备名称/编号搜索" :showAction="false"
+				disabled @click.native="$tab.navigateTo('/pages/equipment/search')">
+			</u-search> -->
+			<u-search animation placeholder="请输入设备名称/编号搜索" :showAction="false"
+				 @change="search">
+			</u-search>
+		</view>
+		<view class="content">
+			<view class="xy-card" v-for="(item,index) in list" :key="item.placeLineId">
+				<view class="eq-line-title">
+					{{item.placeLineName}}({{item.deviceNum}}台)
+					<view class="arrow-right">
+						<u-icon name="arrow-right" size="14"></u-icon>
+					</view>
+				</view>
+				<view class="eq-item" @click="$tab.navigateTo(`/pages/equipment/detail?id=${item1.deviceId}`)"
+					v-for="(item1,index1) in item.deviceInfos" :key="item1.deviceId">
+					<view class="eq-content">
+						<view class="eq-wrap">
+							<view class="eq-name flex justify-between">
+								<view class="eq-title" v-if="item1.deviceName">{{item1.deviceName}}<text style="color: #666;">({{item1.deviceId}})</text></view>
+								<view class="eq-title" v-else>{{item1.deviceId}}</view>
+								<view class="eq-status-box flex align-center">
+									<view class="eq-status" :class="[item1.netState==1?'online':'']"><text></text>{{item1.netStateName}}
+									</view>
+									<view class="eq-status" :class="[item1.busyState==1?'online':'']"><text></text>{{item1.busyStateName}}
+									</view>
+								</view>
+							</view>
+							<view class="eqeq-type">
+								<view>
+									设备类型:
+								</view>
+								<view>
+									{{item1.deviceTypeName}}
+								</view>
+							</view>
+							<view class="eqeq-type">
+								<view>
+									资产编号:
+								</view>
+								<view>
+									{{item1.mercDeviceCode||'无'}}
+								</view>
+							</view>
+							<view class="eqeq-type">
+								<view>
+									今日交易笔数:
+								</view>
+								<view>
+									{{item1.dayOrderNum}}
+								</view>
+							</view>
+							<view class="eqeq-type">
+								<view>
+									今日销售额:
+								</view>
+								<view>
+									¥{{$xy.delMoney(item1.daySalesPrice)}}
+								</view>
+							</view>
+						</view>
+
+						<view class="status">
+							<view class="s-name">
+								在售/补货
+							</view>
+							<view class="s-num">
+								{{item1.onSaleNum||0}}/{{item1.fillNum||0}}
+							</view>
+						</view>
+					</view>
+				</view>
+				<!-- <view class="more" @click="$tab.navigateTo('/pages/equipment/search')">
+					查看全部
+				</view> -->
+			</view>
+
+			<view class="empty" v-if="list.length==0">
+				<u-empty></u-empty>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import {
+		mercHomeList,
+		mercHomeStatistical,
+	} from "@/api/device/device.js"
+	export default {
+		components: {},
+		data() {
+			return {
+				online: true,
+				list: [],
+				totalData: {
+					closedNum: 0,
+					needToFillNum: 0,
+					offlineNum: 0,
+					onlineNum: 0,
+					operatingNum: 0,
+					keyWords:''
+				},
+			}
+		},
+
+		created() {
+			this.getTotalData('')
+			this.getList('')
+		},
+
+		methods: {
+			onshow(){
+				this.getList('')
+			},
+			
+			search(e) {
+				console.log(e)
+				this.keyWords=e
+				this.getTotalData(this.keyWords)
+				this.getList(this.keyWords)
+			},
+
+			getTotalData(deviceId) {
+				mercHomeStatistical({
+					deviceName: '',
+					deviceId: deviceId
+				}).then(res => {
+					this.totalData = res.data;
+				})
+			},
+
+			getList(deviceId) {
+				mercHomeList({
+					deviceName: '',
+					deviceId: deviceId
+				}).then(res => {
+					this.list = res.data;
+				})
+			},
+		}
+	}
+</script>
+
+<style lang="scss">
+	.container {
+		.nav-style {
+			font-size: 32rpx;
+			font-weight: bold;
+			color: #fff;
+		}
+
+		.search {
+			padding: 24rpx 24rpx;
+			background-color: #fff;
+		}
+
+		.total {
+			display: flex;
+			flex-flow: row nowrap;
+			justify-content: space-around;
+			align-items: center;
+			text-align: center;
+			color: #fff;
+			background-color: #2C6FF3;
+			padding: 40rpx 24rpx ;
+
+			.total-item {
+				.num {
+					font-weight: bold;
+					font-size: 52rpx;
+				}
+
+				.name {
+					font-size: 26rpx;
+				}
+			}
+		}
+
+		.content {
+			padding: 24rpx;
+			padding-bottom:calc(124rpx + env(safe-area-inset-bottom) / 2);
+
+			.xy-card {
+				margin-bottom: 24rpx;
+			}
+
+			.eq-line-title {
+				font-size: 32rpx;
+				padding-bottom: 24rpx;
+				position: relative;
+
+				.arrow-right {
+					position: absolute;
+					right: 0;
+					top: 0;
+				}
+			}
+
+			.eq-item {
+				position: relative;
+
+				&+.eq-item {
+					padding-top: 12rpx;
+				}
+
+				.eq-content {
+
+					.eq-wrap {
+						border-radius: 8rpx;
+						background-color: rgb(245, 248, 251);
+						box-sizing: border-box;
+						padding: 24rpx 12rpx;
+						font-size: 26rpx;
+
+						.eq-name {
+							font-size: 32rpx;
+							font-weight: bold;
+							margin-bottom: 24rpx;
+							position: relative;
+							
+							>.eq-title{
+								width: 420rpx;
+								>text{
+									font-size: 30rpx;
+									color: #666;
+									font-weight: normal;
+								}
+							}
+							
+							.eq-status-box{
+								float: right;
+								position: absolute;
+								right:0;
+								top:0;
+							}
+
+							.eq-status {
+								font-size: 28rpx;
+								color: #666;
+								font-weight: normal;
+								margin-left: 12rpx;
+
+								>text {
+									display: inline-block;
+									background-color: #666;
+									width: 16rpx;
+									height: 16rpx;
+									border-radius: 16rpx;
+									margin-right: 12rpx;
+
+								}
+
+								&.online {
+									color: #f56c6c;
+
+									>text {
+										background-color: green;
+									}
+								}
+							}
+						}
+					}
+
+					.eqeq-type {
+						display: flex;
+						flex-direction: row;
+						align-items: center;
+						font-size: 28rpx;
+
+						>view:nth-child(1) {
+							color: #000;
+							width: 200rpx;
+						}
+
+						>view:nth-child(2) {
+							color: #666;
+							padding-left: 6rpx;
+						}
+					}
+					
+					.eqeq-type+.eqeq-type{
+						margin-top: 16rpx;
+					}
+
+					.status {
+						width: 130rpx;
+						height: 120rpx;
+						box-sizing: border-box;
+						border-radius: 120rpx;
+						// border: 6rpx solid #2C6FF3;
+						text-align: center;
+						display: flex;
+						flex-flow: column;
+						justify-content: space-around;
+						align-items: center;
+						position: absolute;
+						right: 12rpx;
+						bottom: 24rpx;
+
+						.s-name {
+							font-size: 28rpx;
+							padding-top: 20rpx;
+							font-weight: bold;
+						}
+
+						.s-num {
+							font-size: 32rpx;
+							padding-bottom: 20rpx;
+						}
+					}
+				}
+			}
+
+			.more {
+				text-align: center;
+				font-size: 28rpx;
+				color: #2C6FF3;
+				line-height: 80rpx;
+			}
+		}
+
+		.empty {
+			margin: 40% auto 0;
+		}
+	}
+</style>

+ 654 - 0
pages/globalPages/components/home.vue

@@ -0,0 +1,654 @@
+<template>
+	<view class="container">
+		<u-navbar bgColor="#fff" :placeholder="true" @leftClick="scan">
+			<view slot="left">
+				<u-icon name="scan" color="#333" size="32"></u-icon>
+			</view>
+			<view slot="center" style="color:#333;font-size: 36rpx;">
+				开门柜管理
+			</view>
+		</u-navbar>
+
+		<!-- 统计信息 -->
+		<view class="total">
+			<view class="total-sale flex flex-start ">
+				<view class="sale-item">
+					<view class="sale-name">
+						今日总收益(元)
+					</view>
+					<view class="sale-num">
+						{{$xy.delMoney(total.day.salesPrice)}}
+					</view>
+				</view>
+				<view class="sale-item">
+					<view class="sale-name">
+						本月总收益(元)
+					</view>
+					<view class="sale-num">
+						{{$xy.delMoney(total.month.salesPrice)}}
+					</view>
+				</view>
+
+				<view class="sale-more" @click="$tab.navigateTo('/pages/globalPages/moreData')">
+					更多数据
+					<image src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/home/white-more.png"
+						mode="widthFix"></image>
+				</view>
+
+				<view @click="update" :class="[load?'update load':'update']">
+					<image src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/home/update.png"
+						mode="heightFix"></image>
+				</view>
+			</view>
+			<view class="refund-order flex flex-start">
+				<view class="refund-item" @click="$tab.navigateTo('/pages/order/refundList')">
+					<view class="refund-num">
+						¥{{$xy.delMoney(total.day.refundMoney)}}
+					</view>
+					<view class="refund-name">
+						今日退款
+					</view>
+				</view>
+				<view class="refund-item" @click="$tab.navigateTo('/pages/order/orderQuery')">
+					<view class="refund-num">
+						{{total.day.orderNum}}
+					</view>
+					<view class="refund-name">
+						今日订单数
+					</view>
+				</view>
+			</view>
+			<view class="notice" @click="goRisk">
+				<view>
+					<u-notice-bar :text="notice" color="#CB7506" bgColor="#fff" speed="80" mode="link"
+						@close="noticeClose">
+					</u-notice-bar>
+				</view>
+				<!-- <view class="notice-tips flex justify-around align-center" v-if="tipsCount.riskOrderNum>0">
+					<view>
+						{{tipsCount.riskOrderNum}}
+					</view>
+				</view> -->
+
+				<view class="notice-tips flex justify-around align-center" v-if="tipsCount.riskOrderNum>0">
+					<u-badge numberType="overflow" bgColor="#E60012" max="99" :value="tipsCount.riskOrderNum"></u-badge>
+				</view>
+			</view>
+		</view>
+
+		<!-- 常用菜单 -->
+		<view class="useful-menu" v-if="operMenu.length>0">
+			<view  class="flex justify-between flex-wrap">
+				<view class="useful-menu-item flex justify-between align-center" v-for="(item,index) in operMenu"
+					:key="item.id" @click="menuClick(item)">
+					<view class="image">
+						<u-image width="75rpx" height="75rpx" :src="iconList[item.name]" mode="widthFix"
+							:lazy-load="true">
+						</u-image>
+					</view>
+					<view class="menu-name">{{item.name}}</view>
+					<!-- <view class="tips flex justify-around align-center" v-if="tips(item.name)>0">
+						<view>
+							{{tips(item.name)}}
+						</view>
+					</view> -->
+					<u-badge absolute :offset="[-2,-2]" is-dot bgColor="#E60012" v-if="tips(item.name)>0"></u-badge>
+					<!-- <u-badge numberType="overflow" :absolute="true" :offset="[-4,-4]" bgColor="#E60012" max="99" :value="tips(item.name)"></u-badge> -->
+				</view>
+			</view>
+		</view>
+
+		<!--便捷功能 -->
+		<view class="menu"  v-if="usefMenu.length>0">
+			<view class="menu-title">
+				便捷功能
+			</view>
+			<view class="menu-content flex justify-start">
+				<view class="menu-item" v-for="(item,index) in usefMenu" :key="item.id" @click="menuClick(item)">
+					<view class="image">
+						<u-image width="57rpx" height="57rpx" :src="iconList[item.name]" mode="widthFix"
+							:lazy-load="true">
+						</u-image>
+					</view>
+					<view>{{item.name}}</view>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import {
+		allCount
+	} from "@/api/device/device.js"
+
+	import {
+		tipsCount
+	} from "@/api/order/order.js"
+	tipsCount
+
+	export default {
+		data() {
+			return {
+				tabArr: [{
+						name: '今日',
+						id: 0
+					},
+					{
+						name: '本月',
+						id: 1
+					}
+				],
+				current: 0,
+
+				//菜单对应图片路径
+				iconList: {
+					'退款审核': 'https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/home/refund.png',
+					'异常订单': 'https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/home/risk.png',
+					'客诉处理': 'https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/home/cc.png',
+					'补货管理': 'https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/home/replenishment.png',
+					'增加设备': 'https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/home/menu-icon (9).png',
+					'增加点位': 'https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/home/menu-icon (8).png',
+					'团队管理': 'https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/home/menu-icon (4).png',
+					'加盟商': 'https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/home/menu-icon (2).png',
+					'流量卡管理': 'https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/home/menu-icon (5).png',
+					'会员管理': 'https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/home/menu-icon (1).png',
+					'运营仓': 'https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/home/menu-icon (7).png',
+					'黑名单管理': 'https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/home/menu-icon (3).png',
+					'营销管理': 'https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/home/menu-icon (6).png',
+					'费用账单': 'https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/home/menu-icon (10).png',
+					'商品建模': 'https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/home/goods-model.png',
+					'订单管理': 'https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/home/order-man.png',
+					// '商品统计': 'https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/home/goods-total.png',
+					'销售统计': 'https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/home/device-total.png',
+					
+					//暂时使用
+					'激活管理': 'https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/home/menu-icon (6).png',
+					'商品清单': 'https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/home/menu-icon (7).png',
+					'常见错误码': 'https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/home/menu-icon (3).png',
+				},
+
+				tempTotal: {},
+
+				total: {
+					day: {
+						orderNum: 0,
+						refundMoney: 0,
+						salesPrice: 0
+					},
+					month: {
+						orderNum: 0,
+						refundMoney: 0,
+						salesPrice: 0
+					}
+				},
+
+				riskNum: 0,
+				noticeShow: false,
+				load: false,
+				timer: null,
+				tipsCount: {
+					refundNum: 0,
+					complaintsNum: 0,
+					outOfStockNum: 0,
+					riskOrderNum: 0
+				}
+			}
+		},
+
+		computed: {
+			//运营任务菜单
+			operMenu() {
+				return this.getMenu('首页', '运营任务')
+			},
+			//便捷菜单
+			usefMenu() {
+				return this.getMenu('首页', '便捷功能')
+			},
+			//消息
+			notice() {
+				let msg = '您有新的消息~'
+				if (this.tipsCount.riskOrderNum > 0) {
+					msg = `您有${this.tipsCount.riskOrderNum}个待办订单需处理!`
+				} else {
+					msg = '无待办事项~'
+				}
+				return msg
+			}
+		},
+
+		created() {
+			this.getTotalData() //顶部统计数据
+			this.getNum() //常用菜单角标
+		},
+
+		methods: {
+			//更新统计数据
+			update() {
+				if (this.timer) {
+					clearTimeout(this.timer)
+				}
+				this.load = true;
+				this.timer = setTimeout(() => {
+					this.load = false;
+				}, 1000)
+				this.getTotalData()
+				this.getNum() //常用菜单角标
+			},
+
+			onshow() {
+				this.getTotalData()
+				this.getNum()
+			},
+			//统计数据
+			getTotalData() {
+				allCount().then(res => {
+					this.total = res.data
+				})
+			},
+
+			tips(name) {
+				let num = 0;
+				switch (name) {
+					case '退款审核':
+						num = this.tipsCount.refundNum
+						break;
+					case '异常订单':
+						num = this.tipsCount.riskOrderNum
+						break;
+					case '客诉处理':
+						num = this.tipsCount.complaintsNum
+						break;
+					case '补货管理':
+						num = this.tipsCount.outOfStockNum
+						break;
+					default:
+						break;
+				}
+				return num
+			},
+
+			//常用功能角标数据
+			getNum() {
+				tipsCount().then(res => {
+					this.tipsCount = res.data
+				}).catch(err => {
+					this.tipsCount = {
+						refundNum: 0,
+						complaintsNum: 0,
+						outOfStockNum: 0,
+						riskOrderNum: 0
+					}
+				})
+			},
+
+			getMenu(menu1, menu2) {
+				let menu = []
+				if (this.$store.state.permission.permissions_menu && this.$store.state.permission.permissions_menu != '[]') {
+					let allMenu = JSON.parse(this.$store.state.permission.permissions_menu);
+					if (allMenu.find(i => i.name == menu1) && allMenu.find(i => i.name == menu1).children) {
+						let homeMenu = allMenu.find(i => i.name == menu1).children;
+						if (homeMenu.find(i => i.name == menu2) && homeMenu.find(i => i.name == menu2).children) {
+							menu = homeMenu.find(i => i.name == menu2).children
+						}
+					}
+				}
+				return menu
+			},
+			scan() {
+				uni.scanCode({
+					success: (res) => {
+						let deviceId = res.result.split('=')[1]
+						if (deviceId) {
+							this.$tab.navigateTo(`/pages/activeDevice/bindDevice?id=${deviceId}`)
+						} else {
+							this.$modal.msg('该二维码无效~')
+						}
+					}
+				});
+			},
+			tabChange(e) {
+				this.current = e.id
+				this.switchTotal(this.current)
+			},
+			menuClick(e) {
+				if (e.name == '补货管理') {
+					this.$tab.navigateTo('/pages/replenish/replenishmentManagement')
+					return
+				}
+				if (e.name == '退款审核') {
+					this.$tab.navigateTo('/pages/order/refundList')
+					return
+				}
+				if (e.name == '异常订单') {
+					this.$tab.navigateTo('/pages/order/riskOrder')
+					return
+				}
+				if (e.name == '增加点位') {
+					this.$tab.navigateTo('/pages/point/point')
+					return
+				}
+				if (e.name == '商品建模') {
+					this.$tab.navigateTo('/pages/commodity/auditList')
+					return
+				}
+				if (e.name == '订单管理') {
+					this.$tab.navigateTo('/pages/order/orderQuery')
+					return
+				}
+				if (e.name == '销售统计') {
+					this.$tab.navigateTo('/pages/globalPages/statistics')
+					return
+				}
+				if (e.name == '设备统计') {
+					this.$tab.navigateTo('/pages/globalPages/deviceStatistics')
+					return
+				}
+				if (e.name == '激活管理') {
+					this.$tab.navigateTo('/pages/activeDevice/deviceManage')
+					return
+				}
+				if (e.name == '商品清单') {
+					this.$tab.navigateTo('/pages/commodity/commoditylist')
+					return
+				}
+				if (e.name == '常见错误码') {
+					this.$tab.navigateTo('/pages/globalPages/errCode')
+					return
+				}
+				this.$modal.msg('功能开发中,尽请期待~')
+			},
+
+			clearStor() {
+				try {
+					uni.clearStorageSync();
+				} catch (e) {
+					console.log(e)
+				}
+			},
+
+			noticeClose() {
+				this.noticeShow = false
+			},
+
+			goRisk() {
+				if (this.tipsCount.riskOrderNum > 0) {
+					this.$tab.navigateTo('/pages/order/riskOrder')
+				}
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	@keyframes rotate {
+		from {
+			transform: rotate(0deg);
+		}
+
+		to {
+			transform: rotate(359deg);
+		}
+	}
+
+	/deep/.u-notice-bar {
+		padding: 9px 12rpx !important;
+	}
+
+	.container {
+		padding-bottom: 88rpx;
+		position: relative;
+
+		.nav-style {
+			font-size: 32rpx;
+			font-weight: bold;
+			color: #fff;
+		}
+
+		// 统计信息
+		.total {
+			width: 724rpx;
+			margin-left: 14rpx;
+			overflow: hidden;
+			border-radius: 14rpx;
+			background-color: #fff;
+			margin-top: 10rpx;
+
+			.total-sale {
+				width: 724rpx;
+				height: 205rpx;
+				background: url('https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/home/home-bg.png') no-repeat left top;
+				background-size: 100% 100%;
+				position: relative;
+
+				.sale-item {
+					margin-top: 46rpx;
+					padding-left: 30rpx;
+
+					.sale-name {
+						font-size: 26rpx;
+						font-weight: 500;
+						color: #EEEDFF;
+						line-height: 26rpx;
+					}
+
+					.sale-num {
+						line-height: 50rpx;
+						font-size: 50rpx;
+						font-weight: 800;
+						color: #FFFFFF;
+						margin-top: 55rpx;
+					}
+				}
+
+				.sale-more {
+					font-size: 26rpx;
+					font-weight: 500;
+					color: #FFFFFF;
+					padding-right: 18rpx;
+					position: absolute;
+					right: 12rpx;
+					top: 22rpx;
+					line-height: 26rpx;
+
+					image {
+						width: 24rpx;
+						height: 24rpx;
+						position: absolute;
+						right: 0;
+						top: 0;
+					}
+				}
+
+				.update {
+					width: 50rpx;
+					height: 50rpx;
+					position: absolute;
+					right: 16rpx;
+					bottom: 16rpx;
+
+					&.load {
+						animation: rotate 1s linear;
+						animation-iteration-count: 3;
+					}
+
+					image {
+						width: 50rpx;
+						height: 50rpx;
+					}
+				}
+			}
+
+			.refund-order {
+				padding-left: 34rpx;
+				overflow: hidden;
+				padding-bottom: 26rpx;
+
+				.refund-item {
+					text-align: center;
+					color: #2E2E2E;
+					margin-top: 68rpx;
+
+					&:nth-child(1) {
+						margin-right: 70rpx;
+					}
+
+					.refund-num {
+						font-size: 36rpx;
+						font-weight: 800;
+						line-height: 32rpx;
+					}
+
+					.refund-name {
+						font-size: 28rpx;
+						font-weight: 500;
+						line-height: 28rpx;
+						margin-top: 27rpx;
+					}
+				}
+			}
+
+			.notice {
+				width: 100%;
+				position: relative;
+
+				// .notice-tips {
+				// 	padding: 10rpx;
+				// 	color: #fff;
+				// 	font-size: 24rpx;
+				// 	text-align: center;
+				// 	position: absolute;
+				// 	right: 56rpx;
+				// 	top: 50%;
+				// 	transform: translateY(-50%);
+				// 	z-index: 999;
+				// 	background-color: #fff;
+
+				// 	>view {
+				// 		width: 36rpx;
+				// 		height: 36rpx;
+				// 		background-color: #E60012;
+				// 		border-radius: 36rpx;
+				// 		line-height: 36rpx;
+				// 	}
+				// }
+
+				.notice-tips {
+					padding: 10rpx;
+					text-align: center;
+					position: absolute;
+					right: 40rpx;
+					top: 50%;
+					transform: translateY(-54%);
+					z-index: 999;
+					background-color: #fff;
+				}
+			}
+		}
+
+		//常用菜单
+		.useful-menu {
+			width: 724rpx;
+			margin-left: 14rpx;
+
+			.useful-menu-item {
+				margin-top: 20rpx;
+				width: 352rpx;
+				height: 168rpx;
+				background: #FFFFFF;
+				box-shadow: 0px 0px 10px 0px rgba(174, 201, 255, 0.2);
+				border-radius: 14rpx;
+				padding: 0 66rpx 0 46rpx;
+				position: relative;
+
+				>.image {
+					width: 75rpx;
+					height: 75rpx;
+				}
+
+				>.menu-name {
+					font-size: 32rpx;
+					font-weight: 500;
+					color: #333333;
+				}
+
+				.tips {
+					// width: 40rpx;
+					// height: 40rpx;
+					// background-color: #E60012;
+					// color: #fff;
+					// font-size: 24rpx;
+					// text-align: center;
+					// line-height: 28rpx;
+					// border-radius: 40rpx;
+					// position: absolute;
+					// right: -8rpx;
+					// top: -8rpx;
+					// padding: 6rpx;
+
+					width: 40rpx;
+					height: 40rpx;
+					position: absolute;
+					right: -8rpx;
+					top: -8rpx;
+					padding: 6rpx;
+				}
+			}
+		}
+
+		// 便捷功能
+		.menu {
+			width: 724rpx;
+			margin-left: 14rpx;
+			background: #FFFFFF;
+			box-shadow: 0px 0px 10rpx 0px rgba(174, 201, 255, 0.2);
+			border-radius: 14rpx;
+			display: flex;
+			flex-flow: row nowrap;
+			justify-content: space-around;
+			margin-top: 20rpx;
+			position: relative;
+			padding: 101rpx 36rpx 0rpx;
+
+			.menu-title {
+				font-size: 28rpx;
+				font-weight: 800;
+				color: #000;
+				position: absolute;
+				left: 28rpx;
+				top: 28rpx;
+			}
+
+			.menu-item {
+				margin-bottom: 48rpx;
+			}
+
+			.menu-content {
+				width: 100%;
+				flex-flow: row wrap;
+			}
+
+			.menu-item {
+				text-align: center;
+				width: 25%;
+				display: flex;
+				flex-direction: column;
+				align-items: center;
+
+				>.image {
+					width: 57rpx;
+					height: 57rpx;
+				}
+
+				>view {
+					color: #333;
+					font-size: 24rpx;
+					line-height: 24rpx;
+					font-size: 28rpx;
+					line-height: 28rpx;
+					margin-top: 16rpx;
+				}
+			}
+		}
+	}
+</style>

+ 159 - 0
pages/globalPages/errCode.vue

@@ -0,0 +1,159 @@
+<template>
+	<view class="container">
+		<u-navbar leftIconColor="#fff" titleStyle="color:#fff;fontSize:36rpx;" :autoBack="true" bgColor="#2C6FF3"
+			:placeholder="true" title="常见错误码"></u-navbar>
+		<scroll-view scroll-y="true" scroll-with-animation="true" lower-threshold="100">
+			<view class="log-item" v-for="(item,index) in logs" :key="index">
+				<view class="logs">
+					<view class="name">错误码</view>
+					<view class="val">{{item.code}}</view>
+				</view>
+				<view class="logs">
+					<view class="name">错误描述</view>
+					<view class="val">{{item.describe}}</view>
+				</view>
+				<view class="logs">
+					<view class="name">建议处理方案</view>
+					<view class="val">{{item.deal}}</view>
+				</view>
+			</view>
+		</scroll-view>
+	</view>
+</template>
+
+<script>
+	import {
+		byId,
+		orderLogs,
+		refundDetail
+	} from "@/api/order/order.js"
+	export default {
+		data() {
+			return {
+				logs: [
+					{
+						code:'IPC1001',
+						describe:'IPC子板无法连接',
+						deal:'请重启设备,若故障未恢复,请报修;'
+					},
+					{
+						code:'IPC1002',
+						describe:'IPC子板网络连接正常,但无法通讯',
+						deal:'请重启设备,若故障未恢复,请报修;'
+					},
+					{
+						code:'IPC1101',
+						describe:'IPC无法识别摄像头',
+						deal:'请重启设备,若故障未恢复,请报修;'
+					},
+					{
+						code:'CTRL1001',
+						describe:'控制板无法连接',
+						deal:'请重启设备,若故障未恢复,请报修;'
+					},
+					{
+						code:'CTRL1002',
+						describe:'控制板网络连接正常,但无法通讯',
+						deal:'请重启设备,若故障未恢复,请报修;'
+					},
+					{
+						code:'CTRL1102',
+						describe:'电插锁发送关门命令后锁芯无法落下',
+						deal:'请检查柜门是否关到位,如无法顺畅关门,请报修;'
+					},
+					{
+						code:'TRA1001',
+						describe:'在非交易状态下检测到门处于开启状态',
+						deal:'请检查柜门是否关到位,如无法顺畅关门,请报修;'
+					},
+					{
+						code:'Z1002',
+						describe:'刷脸摄像头异常,请重启设备后再试(Z1002)',
+						deal:'请重启设备,若故障未恢复,请报修;'
+					},
+					{
+						code:'Z1020',
+						describe:'刷脸摄像头异常,请重启设备后再试(Z1020)',
+						deal:'请重启设备,若故障未恢复,请报修;'
+					},
+					{
+						code:'Z1021',
+						describe:'刷脸摄像头异常,请重启设备后再试(Z1021)',
+						deal:'请重启设备,若故障未恢复,请报修;'
+					},
+					{
+						code:'LCN1001',
+						describe:'网络问题/后台服务器无法连接',
+						deal:'请检查网络连接,切换可用网络;'
+					},
+					{
+						code:'HRD1002',
+						describe:'设备断电',
+						deal:'设备断电,请到现场恢复供电'
+					},
+					{
+						code:'33019',
+						describe:'用户授权异常(上一笔交易未结束,或存在未解约的协议)',
+						deal:'账号存在未解约协议,请联系商户处理'
+					},
+					{
+						code:'33004',
+						describe:'找不到商品列表',
+						deal:'设备未配置商品'
+					},
+					{
+						code:'33005',
+						describe:'商品列表下没有商品',
+						deal:''
+					},
+					{
+						code:'33015',
+						describe:'授权处理超时',
+						deal:''
+					}
+				]
+			}
+		},
+		onLoad(o) {
+			
+		},
+		methods: {
+
+		},
+	}
+</script>
+
+<style scoped lang="scss">
+	.container {
+		padding: 24rpx;
+		line-height: 50rpx;
+
+		.text {
+			color: #2C6FF3;
+		}
+
+		.log-item {
+			width: 702rpx;
+			margin-bottom: 50rpx;
+
+			.logs {
+				overflow: hidden;
+				width: 100%;
+			}
+
+			.name {
+				float: left;
+				margin-right: 24rpx;
+				width: 26%;
+				color: gray;
+			}
+
+			.val {
+				float: left;
+				word-wrap: break-word;
+				word-break: break-all;
+				width: 70%;
+			}
+		}
+	}
+</style>

+ 187 - 0
pages/globalPages/home.vue

@@ -0,0 +1,187 @@
+<template>
+	<view class="container">
+		<view class="pages">
+			<Home ref="home" v-if="tabName=='首页'" />
+			<Equipment ref="equipment" v-if="tabName=='设备'" />
+			<Commodity ref="commodity" v-if="tabName=='商品'" />
+			<Account v-if="tabName=='我的'" />
+		</view>
+
+		<u-tabbar v-if="menu&&menu.length>0" :value="current" @change="tabChange" :fixed="true" :placeholder="false"
+			:safeAreaInsetBottom="true">
+			<u-tabbar-item :text="item.name" v-for="(item,index) in menu" :key="item.id" @click="tabClick(item)">
+				<image class="u-page__item__slot-icon" slot="inactive-icon" :src="tabIcon[item.name][0]"></image>
+				<image class="u-page__item__slot-icon" slot="active-icon" :src="tabIcon[item.name][1]"></image>
+			</u-tabbar-item>
+		</u-tabbar>
+	</view>
+</template>
+
+<script>
+	import Home from './components/home.vue'
+	import Equipment from './components/equipment.vue'
+	import Commodity from './components/commodity.vue'
+	import Account from './components/account.vue'
+	let mqtt = require('../../static/js/mqtt.min.js')
+
+	export default {
+		components: {
+			Home,
+			Equipment,
+			Commodity,
+			Account
+		},
+
+		data() {
+			return {
+				current: 0,
+				tabIcon: {
+					'首页': [require('../../static/images/tabbar/home.png'), require(
+						'../../static/images/tabbar/home_.png')],
+					'设备': [require('../../static/images/tabbar/equipment.png'), require(
+						'../../static/images/tabbar/equipment_.png')],
+					'商品': [require('../../static/images/tabbar/commodity.png'), require(
+						'../../static/images/tabbar/commodity_.png')],
+					'我的': [require('../../static/images/tabbar/mine.png'), require(
+						'../../static/images/tabbar/mine_.png')],
+				},
+				client: null,
+				tabName: null
+			}
+		},
+		onShow() {
+			// 版本自动更新代码
+			const updateManager = wx.getUpdateManager()
+			updateManager.onUpdateReady(function() {
+				wx.showModal({
+					title: '更新检测',
+					content: '检测到新版本,是否重启小程序?',
+					success: function(res) {
+						if (res.confirm) {
+							// 新的版本已经下载好,调用 applyUpdate 应用新版本并重启
+							updateManager.applyUpdate()
+						}
+					}
+				})
+			})
+			updateManager.onUpdateFailed(function() {
+				// 新的版本下载失败
+				wx.showModal({
+					title: '已有新版本咯',
+					content: '请您删除当前小程序,重新打开呦~',
+					showCancel: false
+				})
+			})
+
+			//onshow刷新设备状态
+			if(this.current==0){
+				this.$refs.home.onshow()
+			}
+			if (this.current == 1) {
+				this.$refs.equipment.onshow()
+			}
+			if(this.current==2){
+				this.$refs.commodity.onshow()
+			}
+		},
+
+		computed: {
+			menu() {
+				return JSON.parse(this.$store.state.permission.permissions_menu)
+			},
+		},
+
+		watch: {
+			menu: {
+				handler(newVal, oldVal) {
+					if (newVal.length > 0) {
+						this.tabName = newVal[0].name
+					} else {
+						this.tabName = null
+					}
+				},
+				deep: true,
+				immediate: true
+			}
+		},
+
+		onLoad(o) {
+			if (o.tabName) { //公库添加商品到私库完毕,显示商品界面
+				this.tabName = o.tabName;
+				if (this.menu && this.menu.length > 0) {
+					this.menu.forEach((item, index) => {
+						if (item.name == o.tabName) {
+							this.current = index
+						}
+					})
+				}
+
+			}
+			// this.mqttConnect()
+		},
+
+		methods: {
+			mqttConnect() {
+				let _this = this;
+
+				//测试用,生产通过接口(/sys/user-info/webUserMqtt)获取options
+				const options = {
+					clean: true, // true: 清除会话, false: 保留会话
+					connectTimeout: 4000, // 超时时间
+					// 认证信息
+					clientId: 'web-user-wxc-1',
+					username: 'webuser',
+					password: 'xy20220101',
+				}
+				// 连接字符串, 通过协议指定使用的连接方式
+				// ws 未加密 WebSocket 连接
+				// wss 加密 WebSocket 连接
+				// mqtt 未加密 TCP 连接
+				// mqtts 加密 TCP 连接
+				// wxs 微信小程序连接
+				// alis 支付宝小程序连接
+				const connectUrl = 'wxs://mqtt.mxrvending.com:8084/mqtt'
+				this.client = mqtt.connect(connectUrl, options)
+				this.client.on('connect', function() {
+					//订阅消息
+					_this.client.subscribe('web-user-wxc-1', function(err) {
+						if (!err) {
+							console.log('订阅成功', err)
+						} else {
+							console.log('订阅失败', err)
+						}
+					})
+				}).on('reconnect', function() {
+					console.log('正在重连')
+				}).on('error', function() {
+					console.log('错误')
+				}).on('end', function() {
+					console.log('连接结束')
+				}).on('message', function(topic, message) {
+					console.log('接收到的消息==========>>>>>>>>', message.toString())
+				})
+			},
+
+			tabChange(e) {
+				this.current = e
+			},
+
+			tabClick(e) {
+				this.tabName = e.name
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	.container {
+		.pages {
+			height: 100%;
+		}
+
+		.u-page__item__slot-icon {
+			width: 44rpx;
+			height: 44rpx;
+		}
+	}
+</style>

+ 79 - 0
pages/globalPages/logs.vue

@@ -0,0 +1,79 @@
+<template>
+	<view class="container">
+		<u-navbar leftIconColor="#fff" titleStyle="color:#fff;fontSize:36rpx;" :autoBack="true" bgColor="#2C6FF3"
+			:placeholder="true" title="接口日志"></u-navbar>
+		<scroll-view scroll-y="true" scroll-with-animation="true" lower-threshold="100">
+			<view class="log-item" v-for="(item,index) in logs" :key="index">
+				<view class="logs">
+					<view class="name">请求时间</view>
+					<view class="val">{{item.time}}</view>
+				</view>
+				<view class="logs">
+					<view class="name">请求路径</view>
+					<view class="val">{{item.url}}</view>
+				</view>
+				<view class="logs">
+					<view class="name">请求参数</view>
+					<view class="val">{{item.params}}</view>
+				</view>
+				<view class="logs">
+					<view class="name">返回结果</view>
+					<view class="val">{{item.res}}</view>
+				</view>
+			</view>
+		</scroll-view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				logs: []
+			}
+		},
+		onLoad(o) {
+			if (uni.getStorageSync('logs')) {
+				this.logs = JSON.parse(uni.getStorageSync('logs'))
+			}
+		},
+		methods: {
+
+		},
+	}
+</script>
+
+<style scoped lang="scss">
+	.container {
+		padding: 24rpx;
+		line-height: 50rpx;
+
+		.text {
+			color: #2C6FF3;
+		}
+
+		.log-item {
+			width: 702rpx;
+			margin-bottom: 50rpx;
+
+			.logs {
+				overflow: hidden;
+				width: 100%;
+			}
+
+			.name {
+				float: left;
+				margin-right: 24rpx;
+				width: 20%;
+				color: gray;
+			}
+
+			.val {
+				float: left;
+				word-wrap: break-word;
+				word-break: break-all;
+				width: 76%;
+			}
+		}
+	}
+</style>

+ 919 - 0
pages/globalPages/moreData.vue

@@ -0,0 +1,919 @@
+<template>
+	<view class="container">
+		<u-navbar titleStyle="color:#333;fontSize:36rpx;" :autoBack="true" bgColor="#fff" :placeholder="true"
+			title="更多数据"></u-navbar>
+		<view class="content">
+			<view class="tab-wrap">
+				<view class="tab">
+					<u-tabs :list="tabList" :activeStyle="{color: '#333',fontWeight: 'bold',fontSize:'36rpx'}"
+						:inactiveStyle="{fontSize:'32rpx'}" :scrollable="false" :current="current" @click="tabClick"
+						lineColor="#2C6FF3">
+					</u-tabs>
+				</view>
+			</view>
+
+			<view class="total-content today" v-if="current==0">
+				<view class="time-tab flex align-center justify-between">
+					<!-- <view class="time-item" :class="[dayTimeCurrent==0?'time-item time-show':'time-item']"
+						@click="dayTimeClick(0)">
+						今日
+					</view> -->
+					<!-- <view class="time-item" :class="[dayTimeCurrent==1?'time-item time-show':'time-item']" -->
+					<view class="time-item time-show" @click="dayTimeClick(0)">
+						{{date}}
+					</view>
+
+					<view class="flex">
+						<view class="change-day" @tap="changeDay(0)">
+							<xbutton>今日</xbutton>
+						</view>
+						<view class="change-day" @tap="changeDay(-1)">
+							<xbutton>前一天</xbutton>
+						</view>
+						<view class="change-day" @tap="changeDay(1)">
+							<xbutton>后一天</xbutton>
+						</view>
+					</view>
+				</view>
+
+				<u-datetime-picker :show="timeShow" mode="date" v-model="datePicker" @confirm="confirm"
+					:closeOnClickOverlay="true" @close="close" @cancel="close"></u-datetime-picker>
+
+				<view class="today-receive">
+					<view>今日总收益</view>
+					<view>
+						¥<view>{{$xy.delMoney(dayData.dayTotalRevenue)}}</view>
+					</view>
+					<view>
+						今日销售+今日补收
+					</view>
+				</view>
+
+				<view class="sale-refund card">
+					<view class="sr-title">
+						<image src="../../static/images/global/total-before.png" mode="widthFix"></image>
+						订单销售
+					</view>
+					<view class="sr-head">
+						<view>
+							¥<view>{{$xy.delMoney(dayData.dayOrderRealMoney)}}</view>
+						</view>
+						<view>
+							到账金额
+						</view>
+					</view>
+					<view class="sr-total flex justify-between">
+						<view class="sr-item">
+							<view class="sr-num" style="color:#2C6FF3;">
+								¥<text>{{$xy.delMoney(dayData.dayOrderTotalMoney)}}</text>
+							</view>
+							<view class="sr-name">
+								订单金额
+							</view>
+
+						</view>
+						<view class="sr-item" style="color: #FF0000;">
+							<view class="sr-num">
+								¥<text>{{$xy.delMoney(dayData.dayRefundMoney)}}</text>
+							</view>
+							<view class="sr-name">
+								退款金额
+							</view>
+
+						</view>
+						<view class="sr-item">
+							<view class="sr-num">
+								¥<text>{{$xy.delMoney(dayData.dayPayFailedMoney)}}</text>
+							</view>
+							<view class="sr-name">
+								挂账金额
+							</view>
+
+						</view>
+						<view class="sr-item">
+							<view class="sr-num">
+								<text>{{dayData.dayToBeConfirmedNum||0}}</text>笔
+							</view>
+							<view class="sr-name">
+								待确认
+							</view>
+						</view>
+					</view>
+
+					<view class="sr-total flex justify-between">
+						<view class="sr-item">
+							<view class="sr-num">
+								<text>{{dayData.dayOrderNum||0}}</text>笔
+							</view>
+							<view class="sr-name">
+								订单数量
+							</view>
+						</view>
+						<view class="sr-item">
+							<view class="sr-num">
+								<text>{{dayData.dayRefundNum||0}}</text>笔
+							</view>
+							<view class="sr-name">
+								退款数量
+							</view>
+
+						</view>
+						<view class="sr-item">
+							<view class="sr-num">
+								<text>{{dayData.dayPayFailedNum||0}}</text>
+							</view>
+							<view class="sr-name">
+								挂账数量
+							</view>
+
+						</view>
+						<view class="sr-item">
+							<view class="sr-num">
+								<text>{{dayData.dayAbnormalOrderNum||0}}</text>笔
+							</view>
+							<view class="sr-name">
+								异常数量
+							</view>
+
+						</view>
+					</view>
+				</view>
+
+				<view class="sale-refund card" style="padding:30rpx 20rpx;">
+					<view class="sr-title" style="margin-left: 14rpx;">
+						<image src="../../static/images/global/total-before.png" mode="widthFix"></image>
+						历史订单补退和补收
+					</view>
+					<view class="sr-head">
+						<view>
+							¥<view>{{$xy.delMoney(dayData.dayHisFillMoney-dayData.dayHisRefundMoney)}}</view>
+						</view>
+						<view>
+							补收合计
+						</view>
+					</view>
+					<view class="sr-content flex justify-between">
+						<view class="sr-content-item sr-content-left">
+							<view class="sc-top">
+								今日补收
+							</view>
+							<view class="sc-bot flex">
+								<view class="sc-item">
+									<view class="sc-num">
+										¥<text>{{$xy.delMoney(dayData.dayHisFillMoney)}}</text>
+									</view>
+									<view class="sc-name">
+										金额
+									</view>
+								</view>
+								<view class="sc-item">
+									<view class="sc-num">
+										<text>{{dayData.dayHisFillNum||0}}</text>笔
+									</view>
+									<view class="sc-name">
+										订单数量
+									</view>
+								</view>
+							</view>
+						</view>
+						<view class="sr-content-item sr-content-right">
+							<view class="sc-top">
+								今日补退
+							</view>
+							<view class="sc-bot flex">
+								<view class="sc-item">
+									<view class="sc-num">
+										¥<text>{{$xy.delMoney(dayData.dayHisRefundMoney)}}</text>
+									</view>
+									<view class="sc-name">
+										金额
+									</view>
+								</view>
+								<view class="sc-item">
+									<view class="sc-num">
+										<text>{{dayData.dayHisRefundNum||0}}</text>笔
+									</view>
+									<view class="sc-name">
+										订单数量
+									</view>
+								</view>
+							</view>
+						</view>
+					</view>
+				</view>
+
+				<view class="sale-refund card" style="padding:30rpx 20rpx;">
+					<view class="sr-title" style="margin-left: 14rpx;">
+						<image src="../../static/images/global/total-before.png" mode="widthFix"></image>
+						风险订单
+					</view>
+					<view class="sr-head" @click="$tab.navigateTo('/pages/order/riskOrder')">
+						<view style="color: #FF0000;">
+							<view style="padding-right:12rpx;">{{dayData.toDoRiskCount}}</view>笔
+						</view>
+						<view>
+							待处理风险订单
+						</view>
+					</view>
+					<view class="sr-content flex justify-between">
+						<view class="sr-content-item sr-content-left1">
+							<view class="sc-top">
+								今日新增
+							</view>
+							<view class="sc-val">
+								{{dayData.dayRiskAddCount}}
+							</view>
+						</view>
+						<view class="sr-content-item sr-content-right1">
+							<view class="sc-top">
+								今日处理
+							</view>
+							<view class="sc-val">
+								{{dayData.dayRiskHandleCount}}
+							</view>
+						</view>
+					</view>
+				</view>
+			</view>
+
+			<view class="total-content month" v-else>
+				<view class="time-tab flex align-center justify-between">
+					<view class="time-item time-show" @click="dayTimeClick(0)">
+						{{month}}
+					</view>
+
+					<view class="flex">
+						<view class="change-day" @tap="changeMonth(0)">
+							<xbutton>本月</xbutton>
+						</view>
+						<view class="change-day" @tap="changeMonth(-1)">
+							<xbutton>上一月</xbutton>
+						</view>
+						<view class="change-day" @tap="changeMonth(1)">
+							<xbutton>下一月</xbutton>
+						</view>
+					</view>
+				</view>
+
+				<u-datetime-picker :show="monthShow" mode="month" v-model="monthPicker" @confirm="monthConfirm"
+					:closeOnClickOverlay="true" @close="monthClose" @cancel="monthClose"></u-datetime-picker>
+
+				<view class="today-receive">
+					<view>本月总收益</view>
+					<view>
+						¥<view>{{$xy.delMoney(monthData.monthTotalRevenue)}}</view>
+					</view>
+				</view>
+
+				<view class="chart card">
+					<view class="chart-title">
+						日收益明细
+					</view>
+					<view class="chart-content">
+						<block v-for="(item,index) in monthData.revenueList" :key="item.date">
+							<view class="chart-item flex align-center" @click="detail(item.date)">
+								<view class="chart-time">
+									{{item.date}}
+								</view>
+								<view class="chart-bar-box flex justify-between align-center">
+									<view class="chart-bar" :style="{width:item.width}"></view>
+									<view class="chart-num">
+										¥{{$xy.delMoney(item.dayTotalRevenue)}}
+									</view>
+								</view>
+							</view>
+						</block>
+					</view>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import {
+		countByMonth,
+		countByDay
+	} from "@/api/order/order.js"
+
+	export default {
+		data() {
+			return {
+				tabList: [{
+						name: '今日统计'
+					},
+					{
+						name: '本月统计'
+					}
+				],
+				current: 0,
+
+				dayData: {
+					"dayTotalRevenue": 0,
+					"dayOrderRealMoney": 0,
+					"dayOrderTotalMoney": 0,
+					"dayOrderNum": 0,
+					"dayRefundMoney": 0,
+					"dayRefundNum": 0,
+					"dayPayFailedMoney": 0,
+					"dayPayFailedNum": 0,
+					"dayToBeConfirmedNum": 0,
+					"dayAbnormalOrderNum": 0,
+					"dayHisFillMoney": 0,
+					"dayHisFillNum": 0,
+					"dayHisRefundMoney": 0,
+					"dayHisRefundNum": 0,
+					"toDoRiskCount": 0,
+					"dayRiskHandleCount": 0,
+					"dayRiskAddCount": 0
+				},
+				monthData: {
+					"monthTotalRevenue": 0,
+					"revenueList": []
+				},
+				monthTimeCurrent: 0,
+				timeShow: false,
+				date: null,
+				datePicker: null,
+				dateDx: 0,
+
+				monthDx: 0,
+				month: null,
+				monthShow: false,
+				monthPicker: null,
+			}
+		},
+
+		onLoad(o) {
+			this.datePicker = new Date()
+			this.date = uni.$u.timeFormat(this.datePicker, 'yyyy-mm-dd')
+			this.month = uni.$u.timeFormat(this.datePicker, 'yyyy-mm')
+			this.tabList[0].name = `今日统计`
+			this.tabList[1].name = `本月统计`
+		},
+
+		onShow() {
+			this.getData()
+		},
+
+
+		methods: {
+			tabClick(e) {
+				this.current = e.index
+				this.getData()
+			},
+
+			getData() {
+				if (this.current == 0) {
+					this.getDayData()
+				} else {
+					this.getMonthData()
+				}
+			},
+
+			dayTimeClick(e) {
+				this.timeShow = true
+			},
+
+			confirm(e) {
+				this.date = uni.$u.timeFormat(e.value, 'yyyy-mm-dd')
+				this.isToday()
+				this.getData()
+				this.timeShow = false;
+			},
+
+			close() {
+				this.timeShow = false
+			},
+
+			// 是否今日
+			isToday(day) {
+				let nowDate = uni.$u.timeFormat(new Date(), 'yyyy-mm-dd')
+				if (this.date == nowDate) {
+					this.tabList[0].name = `今日统计`
+				} else {
+					this.tabList[0].name = `按日统计`
+				}
+			},
+
+			isNowMonth(month) {
+				let nowMonth = uni.$u.timeFormat(new Date(), 'yyyy-mm')
+				if (this.month == nowMonth) {
+					this.tabList[1].name = `本月统计`
+				} else {
+					this.tabList[1].name = `按月统计`
+				}
+			},
+
+			changeDay(add) {
+				if (add == 0) {
+					this.dateDx = 0;
+				} else {
+					//处理超过当前时间的情况
+					if (add > 0) { 
+						if (this.dateDx == 0) {
+							this.$modal.msg('别点了,没有数据啦~')
+							return
+						}
+					}
+					this.dateDx += add;
+				}
+
+				let date = new Date().getTime();
+				let newDate = date + this.dateDx * (24 * 60 * 60 * 1000);
+				this.date = uni.$u.timeFormat(newDate, 'yyyy-mm-dd')
+				this.isToday()
+				this.getData()
+			},
+
+			changeMonth(add) {
+				if (add == 0) {
+					this.monthDx = 0;
+				} else {
+					//处理超过当前时间的情况
+					if (add > 0) {
+						if (this.monthDx == 0) {
+							this.$modal.msg('别点了,没有数据啦~')
+							return
+						}
+					}
+					this.monthDx += add;
+				}
+
+				let today = new Date();
+				let month = new Date(today.getFullYear(), today.getMonth() + this.monthDx, today.getDate());
+				this.month = uni.$u.timeFormat(month, 'yyyy-mm')
+				this.isNowMonth()
+				this.getData()
+			},
+
+			monthTimeClick(e) {
+				this.monthTimeCurrent = e
+			},
+
+			getMonthData() {
+				countByMonth({
+					date: this.month + '-01'
+				}).then(res => {
+					let data = res.data;
+					let max = 0;
+					let revenueList = data.revenueList
+					for (let i = 0; i < revenueList.length; i++) {
+						let item = revenueList[i];
+						if (item.dayTotalRevenue > max) {
+							max = item.dayTotalRevenue
+						}
+					}
+					for (let i = 0; i < revenueList.length; i++) {
+						let item = revenueList[i];
+						if (item.dayTotalRevenue > 0) {
+							item.width = (item.dayTotalRevenue / (max / 0.78)) * 100 + '%'
+						} else {
+							item.width = '0%'
+						}
+					}
+					this.monthData = data
+					console.log(this.monthData)
+				})
+			},
+
+			getDayData() {
+				countByDay({
+					date: this.date
+				}).then(res => {
+					if (res.data) {
+						this.dayData = res.data
+					} else {
+						this.dayData = {
+							"dayTotalRevenue": 0,
+							"dayOrderRealMoney": 0,
+							"dayOrderTotalMoney": 0,
+							"dayOrderNum": 0,
+							"dayRefundMoney": 0,
+							"dayRefundNum": 0,
+							"dayPayFailedMoney": 0,
+							"dayPayFailedNum": 0,
+							"dayToBeConfirmedNum": 0,
+							"dayAbnormalOrderNum": 0,
+							"dayHisFillMoney": 0,
+							"dayHisFillNum": 0,
+							"dayHisRefundMoney": 0,
+							"dayHisRefundNum": 0,
+							"toDoRiskCount": 0,
+							"dayRiskHandleCount": 0,
+							"dayRiskAddCount": 0
+						}
+					}
+				})
+			},
+
+			detail(time) {
+				this.current = 0;
+				this.date = time;
+				this.tabList[0].name = `按日统计`
+				this.getData()
+			},
+
+			monthConfirm(e) {
+				this.month = uni.$u.timeFormat(e.value, 'yyyy-mm')
+				// this.isToday()
+				// this.getData()
+				this.monthShow = false;
+			},
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.container {
+		min-height: 100vh;
+		background-color: #fff !important;
+
+		.content {
+			color: #333;
+
+			.tab-wrap {
+				background-color: #fff;
+
+				.tab {
+					width: 90%;
+					margin-left: -12rpx;
+				}
+			}
+
+			.time-tab {
+				margin-top: 24rpx;
+				padding: 0 13rpx;
+
+				.time-item {
+					padding: 0 64rpx;
+					height: 62rpx;
+					background: #F7F7F7;
+					border-radius: 10rpx;
+					font-size: 28rpx;
+					font-weight: 500;
+					color: #777777;
+					margin-right: 20rpx;
+					line-height: 62rpx;
+
+					&.time-show {
+						background: #F4F8FF;
+						color: #2C6FF3;
+					}
+				}
+
+				.change-day {
+					margin-left: 12rpx;
+				}
+			}
+
+			.total-content {
+
+				.today-receive {
+					width: 724rpx;
+					margin-left: 13rpx;
+					background: #2C6FF3;
+					box-shadow: 0px 0px 10rpx 0px rgba(174, 201, 255, 0.2);
+					border-radius: 14rpx;
+					color: #fff;
+					padding: 28rpx 18rpx;
+					margin-top: 20rpx;
+
+					>view:nth-child(1) {
+						font-size: 30rpx;
+						line-height: 30rpx;
+						font-weight: 500;
+					}
+
+					>view:nth-child(2) {
+						font-size: 32rpx;
+						line-height: 64rpx;
+						margin-top: 32rpx;
+						text-align: center;
+
+						>view {
+							font-size: 64rpx;
+							padding-left: 12rpx;
+							display: inline-block;
+							font-weight: bold;
+						}
+					}
+
+					>view:nth-child(3) {
+						font-size: 26rpx;
+						line-height: 26rpx;
+						font-weight: 500;
+						margin-top: 28rpx;
+						text-align: center;
+					}
+				}
+
+				.risk-order {
+					padding: 0 12rpx;
+
+					.risk-item {
+						width: 230rpx;
+						height: 120rpx;
+						background: #F1F6FF;
+						border-radius: 14rpx;
+						padding: 24rpx 10rpx 18rpx 12rpx;
+
+						.risk-name {
+							font-size: 26rpx;
+							padding-left: 16rpx;
+							position: relative;
+							line-height: 26rpx;
+
+
+							&::before {
+								content: '';
+								width: 6rpx;
+								height: 20rpx;
+								position: absolute;
+								left: 0;
+								top: 2rpx;
+								background-color: #2C6FF3;
+							}
+
+							&.risk-name2::before {
+								background-color: #FB360F;
+							}
+
+							&.risk-name3::before {
+								background-color: #5804E2;
+							}
+
+						}
+
+						.risk-num {
+							font-size: 32rpx;
+							text-align: center;
+							margin-top: 26rpx;
+							line-height: 32rpx;
+							font-weight: bold;
+						}
+
+						&.to-month {
+							width: 354rpx;
+							height: 149rpx;
+							background: #F1F6FF;
+							border-radius: 14rpx;
+
+							.risk-num {
+								font-size: 24rpx;
+								margin-top: 40rpx;
+								line-height: 32rpx;
+								text-align: left;
+								padding-left: 12rpx;
+
+								>text {
+									font-size: 32rpx;
+									font-weight: bold;
+								}
+							}
+						}
+					}
+				}
+
+				.card {
+					box-shadow: 0px 0px 10px 0px rgba(174, 201, 255, 0.2);
+					border-radius: 14rpx;
+				}
+
+				.sale-refund {
+					width: 724rpx;
+					background: #FFFFFF;
+					margin-left: 12rpx;
+					margin-top: 30rpx;
+					padding: 34rpx 34rpx 47rpx;
+
+					.sr-title {
+						font-size: 30rpx;
+						font-weight: 800;
+						line-height: 30rpx;
+						position: relative;
+						padding-left: 24rpx;
+
+						>image {
+							width: 14rpx;
+							height: 25rpx;
+							position: absolute;
+							left: 0;
+							top: 2rpx;
+						}
+					}
+
+					.sr-head {
+						color: #333;
+
+						>view:nth-child(1) {
+							font-size: 24rpx;
+							line-height: 44rpx;
+							margin-top: 43rpx;
+							font-weight: bold;
+							text-align: center;
+
+							>view {
+								font-size: 44rpx;
+								display: inline-block;
+								padding-left: 12rpx;
+							}
+						}
+
+						>view:nth-child(2) {
+							font-size: 26rpx;
+							line-height: 26rpx;
+							margin-top: 22rpx;
+							text-align: center;
+						}
+					}
+
+					.sr-content {
+						margin-top: 20rpx;
+
+						.sr-content-item {
+							width: 334rpx;
+							background: #F1F6FF;
+							border-radius: 14rpx;
+							padding: 24rpx 18rpx;
+
+							&.sr-content-right {
+								background-color: #FFF7F7;
+							}
+
+							&.sr-content-left1 {
+								background-color: #FFF7F7;
+								padding-bottom: 58rpx;
+							}
+
+							&.sr-content-right1 {
+								background-color: #F9F9F9;
+								padding-bottom: 58rpx;
+							}
+
+							.sc-top {
+								font-size: 28rpx;
+								line-height: 28rpx;
+								font-weight: 800;
+							}
+
+							.sc-bot {
+								.sc-item {
+									width: 228rpx;
+									text-align: center;
+									margin-top: 40rpx;
+
+									.sc-name {
+										font-size: 26rpx;
+										color: #555555;
+										line-height: 26rpx;
+										margin-top: 24rpx;
+									}
+
+									.sc-num {
+										font-size: 24rpx;
+										font-weight: 800;
+										line-height: 32rpx;
+
+										>text {
+											font-size: 32rpx;
+										}
+									}
+								}
+							}
+
+							.sc-val {
+								font-size: 32rpx;
+								line-height: 32rpx;
+								margin-top: 45rpx;
+								font-weight: 800;
+								color: #333333;
+								text-align: center;
+							}
+						}
+					}
+
+					.sr-total {
+						margin-top: 42rpx;
+
+						.sr-item {
+							width: 25%;
+							text-align: center;
+
+							.sr-name {
+								font-size: 26rpx;
+								color: #555555;
+								line-height: 26rpx;
+								margin-top: 24rpx;
+							}
+
+							.sr-num {
+								font-size: 24rpx;
+								font-weight: 800;
+								line-height: 32rpx;
+
+								>text {
+									font-size: 32rpx;
+								}
+							}
+						}
+					}
+				}
+
+				.sub-rec-ref {
+					width: 724rpx;
+					margin-left: 13rpx;
+					margin-top: 30rpx;
+
+					.card {
+						width: 354rpx;
+						padding: 26rpx 22rpx;
+
+						&.srr-rec {
+							background-color: #2C6FF3;
+							color: #fff;
+						}
+
+						.srr-title {
+							font-size: 30rpx;
+							line-height: 30rpx;
+						}
+
+						.srr-total {
+							margin-top: 36rpx;
+
+							.srr-item {
+								width: 182rpx;
+
+								.srr-name {
+									font-size: 26rpx;
+									line-height: 26rpx;
+								}
+
+								.srr-num {
+									font-size: 24rpx;
+									font-weight: 800;
+									line-height: 32rpx;
+									margin-top: 24rpx;
+
+									>text {
+										font-size: 32rpx;
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+
+			.month {
+				.chart {
+					width: 724rpx;
+					margin-left: 13rpx;
+					margin-top: 12rpx;
+					padding-bottom: 24rpx;
+
+					.chart-title {
+						font-size: 28rpx;
+						font-weight: 500;
+						color: #333333;
+						padding: 30rpx 25rpx 20rpx;
+					}
+
+					.chart-content {
+						padding: 0 15rpx;
+
+						.chart-item {
+							height: 54rpx;
+							margin-top: 10rpx;
+
+							.chart-time {
+								margin-right: 10rpx;
+								width: 190rpx;
+							}
+
+							.chart-bar-box {
+								background: #F6F6F6;
+								border-radius: 8rpx 27rpx 27rpx 8rpx;
+								width: 620rpx;
+
+								.chart-bar {
+									height: 54rpx;
+									border-radius: 8rpx 27rpx 27rpx 8rpx;
+									background-color: #98C0FC;
+									transition: all 1s linear;
+								}
+
+								.chart-num {
+									color: #df6b73;
+									margin-right: 12rpx;
+									font-size: 26rpx;
+								}
+							}
+						}
+					}
+				}
+			}
+		}
+	}
+</style>

+ 121 - 0
pages/globalPages/notice.vue

@@ -0,0 +1,121 @@
+<template>
+	<view class="container">
+		<u-navbar leftIconColor="#fff" titleStyle="color:#fff;fontSize:36rpx;" :autoBack="true" bgColor="#2C6FF3"
+			:placeholder="true" title="通知"></u-navbar>
+		<view class="content">
+
+			<scroll-view class="scrollview" :scroll-with-animation="true" scroll-y lower-threshold="100"
+				:style="{height:fullHeight}">
+				<view class="list" v-if="list&&list.length>0">
+					<block v-for="(item, index) in list" :key="index">
+						<view class="comm-main flex justify-between">
+							
+						</view>
+					</block>
+					<view class="more" style="overflow: hidden;">
+						<u-loadmore :status="status" v-if="list.length>=1" />
+					</view>
+				</view>
+				<view class="empty" v-if="list.length==0">
+					<u-empty mode="list" text="没有任何通知!"></u-empty>
+				</view>
+			</scroll-view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import {
+		list,
+		update
+	} from "@/api/system/employee.js"
+	export default {
+		data() {
+			return {
+				list: [], //列表
+				fullHeight: 0,
+			}
+		},
+
+		onLoad() {
+			let _this = this;
+			const query = uni.createSelectorQuery().in(this);
+			query.select(".scrollview").boundingClientRect((data) => {
+				uni.getSystemInfo({
+					success(res) {
+						// 针对iPhone X等机型底部安全距离做适配
+						const model = res.model;
+						const modelInclude = [
+							"iPhone X",
+							'iPhone XR',
+							"iPhone XS",
+							"iPhone XS MAX",
+							"iPhone 12/13 mini",
+							"iPhone 12/13 (Pro)",
+							"iPhone 12/13 Pro Max",
+							"iPhone 14 Pro Max"
+						];
+						let safeDistance = modelInclude.includes(model)
+						//动态设置商品区域高度
+						if (safeDistance) {
+							_this.fullHeight = res.windowHeight - data.top - 34 + 'px';
+						} else {
+							_this.fullHeight = res.windowHeight - data.top + 'px';
+						}
+					},
+				});
+			}).exec();
+		},
+
+		methods: {
+			
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.container {
+		overflow: hidden;
+
+		.content {
+
+			.list {
+				width: 100%;
+				padding: 0rpx 13rpx 12rpx;
+				padding-bottom: calc(110rpx + env(safe-area-inset-bottom) / 2);
+				overflow: hidden;
+
+				.comm-img {
+					width: 130rpx;
+					height: 130rpx;
+					display: flex;
+					flex-direction: row;
+					align-items: center;
+					justify-content: space-around;
+
+					image {
+						width: 130rpx;
+						height: 130rpx;
+					}
+				}
+
+				.comm-main {
+					padding: 20rpx 30rpx;
+					background-color: #fff;
+					border-radius: 12rpx;
+					margin-top: 12rpx;
+					box-sizing: border-box;
+					color: #999;
+					line-height: 60rpx;
+					position: relative;
+					
+					
+				}
+			}
+		}
+	}
+
+	.empty {
+		margin-top: 40%;
+	}
+</style>

+ 92 - 0
pages/globalPages/setting.vue

@@ -0,0 +1,92 @@
+<template>
+	<view class="container">
+		<u-navbar titleStyle="color:#333;fontSize:36rpx;" :autoBack="true" bgColor="#fff" :placeholder="true"
+			title="设置"></u-navbar>
+		<view class="content">
+			<u-cell-group :border="false">
+				<u-cell title="修改密码" :isLink="true" @click="pwdShow=true">
+				</u-cell>
+				<u-cell title="退出登录" :isLink="true" @click="loginOut">
+				</u-cell>
+			</u-cell-group>
+		</view>
+
+		<xpopup :show="pwdShow" @close="pwdClose" @confirm="pwdSubmit" :showBtn="true" title="修改密码">
+			<view class="pwd-popup-content flex align-center">
+				<view>新密码:</view>
+				<view>
+					<u--input placeholder="请输入新密码" type="password" border="surround" v-model="newpassword"></u--input>
+				</view>
+			</view>
+		</xpopup>
+	</view>
+</template>
+
+<script>
+	import {
+		updateUserInfo
+	} from "@/api/system/user.js"
+	export default {
+		data() {
+			return {
+				newpassword: '',
+				pwdShow: false,
+			}
+		},
+		methods: {
+			changePwd() {
+				this.pwdShow = true
+			},
+
+			pwdSubmit() {
+				if (this.newpassword) {
+					updateUserInfo({
+						password: this.newpassword
+					}).then(res => {
+						this.$modal.showToast('修改成功~')
+					})
+					this.pwdClose()
+				} else {
+					this.$modal.msg('请输入新密码!')
+				}
+			},
+			
+			pwdClose() {
+				this.pwdShow = false
+			},
+
+			loginOut() {
+				this.$store.dispatch('LogOut').then(res => {
+					this.$tab.reLaunch('/pages/login')
+				})
+			},
+		},
+	}
+</script>
+
+<style scoped lang="scss">
+	.container {
+
+		.content {
+			min-height: 100vh;
+			background-color: #fff;
+			padding:24rpx;
+
+			/deep/ .u-cell__body {
+				padding: 30rpx 6rpx;
+			}
+		}
+		
+		.pwd-popup-content {
+			padding: 24rpx;
+		
+			>view:nth-child(1) {
+				width: 160rpx;
+			}
+		
+			>view:nth-child(2) {
+				width: 100%;
+			}
+		}
+	}
+</style>

+ 804 - 0
pages/globalPages/statistics.vue

@@ -0,0 +1,804 @@
+<template>
+	<view class="container">
+		<u-navbar leftIconColor="#fff" titleStyle="color:#fff;fontSize:36rpx;" :autoBack="true" bgColor="#2C6FF3"
+			:placeholder="true" title="销售统计"></u-navbar>
+		<view class="content">
+			<view class="card">
+				<view class="select">
+					<view class="time-tab flex justify-between">
+						<block v-for="(item,index) in timeList" :key="item.id">
+							<view class="time-tab-item" :class="[timeTabCurrent==item.id?'time-tab-show':'']"
+								@click="timeTabClick(item.id)">
+								{{item.name}}
+							</view>
+						</block>
+					</view>
+					<view class="time-select flex justify-around">
+						<view class="flex  align-center">
+							<view @click="timePickerShow('start')">
+								{{timeStart?timeStart:'开始时间'}}
+							</view>
+							<view>
+								一
+							</view>
+							<view @click="timePickerShow('end')">
+								{{timeEnd?timeEnd:'结束时间'}}
+							</view>
+						</view>
+					</view>
+				</view>
+				<view class="total flex justify-around">
+					<view class="flex flex-direction align-center">
+						<view class="total-name">
+							总销售额(元)
+						</view>
+						<view class="total-num" style="color: #2C6FF3;">
+							{{$xy.delMoney(total.salesMoney)}}
+						</view>
+					</view>
+					<view class="flex flex-direction align-center">
+						<view class="total-name">
+							订单量(笔)
+						</view>
+						<view class="total-num" style="color: #F9B237;">
+							{{total.salesCount||0}}
+						</view>
+					</view>
+					<view class="flex flex-direction align-center">
+						<view class="total-name">
+							客单价(元)
+						</view>
+						<view class="total-num" v-if="total.salesCount>0" style="color: #E94F4F;">
+							{{($xy.delMoney(total.salesMoney)/total.salesCount).toFixed(2)||0}}
+						</view>
+						<view class="total-num" v-else style="color:#E94F4F;">
+							0
+						</view>
+					</view>
+				</view>
+
+				<view class="chart" style="height: 600rpx;">
+					<qiun-data-charts type="qy-line-gradual" :opts="opts" :chartData="chartData"
+						:errorMessage="errorMessage" />
+				</view>
+			</view>
+
+			<view class="device-table card">
+				<view class="table">
+					<view class="table-title flex justify-between align-center">
+						<view class="title">
+							设备销售排行
+						</view>
+						<view class="sort-type flex">
+							<view :class="[deviceSortType==0?'sort-type-item sort-type-show':'sort-type-item']"
+								@click="deviceSort(0)">
+								销售额
+							</view>
+							<view :class="[deviceSortType==1?'sort-type-item sort-type-show':'sort-type-item']"
+								@click="deviceSort(1)">
+								订单笔数
+							</view>
+						</view>
+					</view>
+					<uni-table :border="false" :stripe="false" emptyText="暂无更多数据">
+						<!-- 表头行 -->
+						<uni-tr>
+							<uni-th align="center" width="52">排名</uni-th>
+							<uni-th align="center" width="110">上榜设备</uni-th>
+							<uni-th align="center" width="92">销售额</uni-th>
+							<uni-th align="center" width="92">订单笔数</uni-th>
+						</uni-tr>
+						<!-- 表格数据行 -->
+						<uni-tr v-for="(item,index) in list1" :key="item.deviceId">
+							<uni-td v-if="index==0">
+								<view class="table-td">
+									<image class="table-img" src="../../static/images/global/first-class.png"
+										mode="widthFix"></image>
+								</view>
+							</uni-td>
+							<uni-td v-else-if="index==1">
+								<view class="table-td">
+									<image class="table-img" src="../../static/images/global/second-class.png"
+										mode="widthFix"></image>
+								</view>
+							</uni-td>
+							<uni-td v-else-if="index==2">
+								<view class="table-td">
+									<image class="table-img" src="../../static/images/global/third-class.png"
+										mode="widthFix"></image>
+								</view>
+							</uni-td>
+							<uni-td v-else>
+								<view class="table-td">
+									{{index+1}}
+								</view>
+							</uni-td>
+							<uni-td>
+								<view class="table-td table-td-name">{{item.deviceName||item.deviceId}}</view>
+							</uni-td>
+							<uni-td>
+								<view class="table-td">¥{{$xy.delMoney(item.salesMoney)}}</view>
+							</uni-td>
+							<uni-td>
+								<view class="table-td">{{item.salesCount}}笔</view>
+							</uni-td>
+						</uni-tr>
+					</uni-table>
+				</view>
+				<view class="more" @click="more('device')" v-if="list1.length==5">
+					查看更多<view><u-icon name="arrow-right" color="#2C6FF3"></u-icon></view>
+				</view>
+			</view>
+
+			<view class="goods-table card">
+				<view class="table">
+					<view class="table-title flex justify-between align-center">
+						<view class="title">
+							商品销售排行
+						</view>
+						<view class="sort-type flex">
+							<view :class="[goodsSortType==0?'sort-type-item sort-type-show':'sort-type-item']"
+								@click="goodsSort(0)">
+								销售额
+							</view>
+							<view :class="[goodsSortType==1?'sort-type-item sort-type-show':'sort-type-item']"
+								@click="goodsSort(1)">
+								销量
+							</view>
+						</view>
+					</view>
+					<uni-table :border="false" :stripe="false" emptyText="暂无更多数据">
+						<!-- 表头行 -->
+						<uni-tr>
+							<uni-th align="center" width="52">排名</uni-th>
+							<uni-th align="center" width="110">上榜商品</uni-th>
+							<uni-th align="center" width="92">销售额</uni-th>
+							<uni-th align="center" width="92">销量</uni-th>
+						</uni-tr>
+						<!-- 表格数据行 -->
+						<uni-tr v-for="(item,index) in list2" :key="item.goodsId">
+							<uni-td v-if="index==0">
+								<view class="table-td">
+									<image class="table-img" src="../../static/images/global/first-class.png"
+										mode="widthFix"></image>
+								</view>
+							</uni-td>
+							<uni-td v-else-if="index==1">
+								<view class="table-td">
+									<image class="table-img" src="../../static/images/global/second-class.png"
+										mode="widthFix"></image>
+								</view>
+							</uni-td>
+							<uni-td v-else-if="index==2">
+								<view class="table-td">
+									<image class="table-img" src="../../static/images/global/third-class.png"
+										mode="widthFix"></image>
+								</view>
+							</uni-td>
+							<uni-td v-else>
+								<view class="table-td">
+									{{index+1}}
+								</view>
+							</uni-td>
+							<uni-td>
+								<view class="table-td table-td-name">{{item.goodsName}}</view>
+							</uni-td>
+							<uni-td>
+								<view class="table-td">¥{{$xy.delMoney(item.salesMoney)}}</view>
+							</uni-td>
+							<uni-td>
+								<view class="table-td">{{item.goodsCount}}件</view>
+							</uni-td>
+						</uni-tr>
+					</uni-table>
+				</view>
+				<view class="more" @click="more('goods')" v-if="list2.length==5">
+					查看更多<view><u-icon name="arrow-right" color="#2C6FF3"></u-icon></view>
+				</view>
+			</view>
+		</view>
+
+		<u-datetime-picker :show="timeShow" :mode="timeMode" v-model="time" @confirm="confirm"
+			:closeOnClickOverlay="true" @close="close" @cancel="close"></u-datetime-picker>
+		<u-picker :show="pickerShow" :columns="columns" @confirm="pickerComfirm" :closeOnClickOverlay="true"
+			@close="pickerClose" @cancel="pickerClose"></u-picker>
+	</view>
+</template>
+
+<script>
+	import {
+		sumCount,
+		sumPage
+	} from "@/api/commodity/goods.js"
+
+	import {
+		sumCount as deviceSumCount,
+		sumPage as deviceSumPage,
+		salesData
+	} from "@/api/device/device.js"
+
+	export default {
+		data() {
+			return {
+				timeType: ['日', '月', '年'],
+				timeCurrent: 0,
+				timeStart: '',
+				timeEnd: '',
+				deviceSortType: 0,
+				goodsSortType: 0,
+				timeShow: false,
+				timeMode: 'date',
+				startOrEnd: '',
+				time: new Date(),
+				pickerType: 'year',
+
+				pickerShow: false,
+				// typeColumns: [
+				// 	['销售额从高到低', '销售额从低到高', '销售数量从高到低', '销售数量从低到高']
+				// ],
+
+				typeColumns: [
+					['销售额', '订单笔数']
+				],
+
+				page: 1, //商品分页
+				size: 10,
+
+				status: 'loadmore', //加载更多
+				list1: [], //设备列表
+				list2: [], //商品列表
+				fullHeight: 0,
+
+				total: {
+					goodsCount: 0,
+					salesMoney: 0
+				},
+
+
+				tabCurrent: 0,
+				timeList: [{
+						id: 0,
+						name: '昨天'
+					},
+					{
+						id: 1,
+						name: '今天'
+					},
+					{
+						id: 2,
+						name: '近7日'
+					},
+					{
+						id: 3,
+						name: '近30日'
+					},
+					{
+						id: 4,
+						name: '本月'
+					},
+				],
+				timeTabCurrent: 1,
+				title: '设备销售额排行',
+
+				opts: {
+					enableScroll: false,
+					legend: {
+						position: 'top',
+						float: 'right',
+						padding: 20,
+						itemGap: 20
+					},
+					xAxis: {
+						disableGrid: true,
+						labelCount: 6,
+					},
+					yAxis: {
+						gridType: "solid",
+						dashLength: 2,
+						showTitle: true,
+						axisLineColor: '#fff',
+						data: [{
+								position: "left",
+								title: "/元"
+							},
+							{
+								position: "right",
+								title: "/单",
+								textAlign: "left"
+							},
+						]
+					},
+					extra: {
+						area: {
+							type: "curve",
+							opacity: 0.2,
+							addLine: true,
+							width: 2,
+							gradient: true,
+							activeType: "hollow"
+						}
+					}
+				},
+
+				errorMessage: '无数据',
+				chartData: {
+					categories: [],
+					series: []
+				},
+			}
+		},
+
+		watch: {
+			// tabCurrent: {
+			// 	handler(newVal, oldVal) {
+			// 		if (newVal == 0) {
+			// 			this.title = '设备销售额排行'
+			// 		}
+			// 		if (newVal == 1) {
+			// 			this.title = '商品销售额排行'
+			// 		}
+
+			// 	},
+			// 	deep: true
+			// }
+		},
+
+		onShow() {
+			let timeObj = this.setResetTime(this.timeTabCurrent)
+			this.timeStart = timeObj.start
+			this.timeEnd = timeObj.end
+			this.getData()
+		},
+		methods: {
+			getData() {
+				this.getList1(this.deviceSortType)
+				this.getList2(this.goodsSortType)
+				this.getDeviceTotal(this.deviceSortType)
+				this.getCountData()
+			},
+
+			timeTabClick(e) {
+				this.timeTabCurrent = e
+				let timeObj = this.setResetTime(this.timeTabCurrent)
+				this.timeStart = timeObj.start
+				this.timeEnd = timeObj.end
+				this.getData()
+			},
+
+
+			setResetTime(type) {
+				let date = new Date()
+				let time = {
+					start: uni.$u.timeFormat(date, 'yyyy-mm-dd'),
+					end: uni.$u.timeFormat(date, 'yyyy-mm-dd')
+				}
+				switch (type) {
+					case 0: //昨天
+						time = {
+							start: uni.$u.timeFormat(date - 24 * 60 * 60 * 1000, 'yyyy-mm-dd'),
+							end: uni.$u.timeFormat(date - 24 * 60 * 60 * 1000, 'yyyy-mm-dd')
+						}
+						break;
+					case 1: //今天
+						time = {
+							start: uni.$u.timeFormat(date, 'yyyy-mm-dd'),
+							end: uni.$u.timeFormat(date, 'yyyy-mm-dd')
+						}
+						break;
+					case 2: //近7日
+						time = {
+							start: uni.$u.timeFormat(date - 7 * 24 * 60 * 60 * 1000, 'yyyy-mm-dd'),
+							end: uni.$u.timeFormat(date, 'yyyy-mm-dd')
+						}
+						break;
+					case 3: //近30日
+						time = {
+							start: uni.$u.timeFrom(date - 30 * 24 * 60 * 60 * 1000, 'yyyy-mm-dd'),
+							end: uni.$u.timeFrom(date, 'yyyy-mm-dd')
+						}
+						break;
+					case 4: //本月
+						let start = uni.$u.timeFrom(date, 'yyyy-mm-dd')
+						time = {
+							start: start.substr(0, 8) + '01',
+							end: uni.$u.timeFrom(date, 'yyyy-mm-dd')
+						}
+						break;
+					default:
+						break;
+				}
+				return time
+			},
+
+			timePickerShow(type) {
+				this.pickerType = 'year';
+				this.startOrEnd = type
+				if (this.timeCurrent == 2) {
+					this.columns = yearList
+					this.pickerShow = true
+				} else {
+					this.timeShow = true;
+				}
+			},
+
+			deviceSort(type) {
+				this.deviceSortType = type
+				this.getList1(type)
+			},
+
+			goodsSort(type) {
+				this.goodsSortType = type
+				this.getList2(type)
+			},
+
+			close() {
+				this.timeShow = false
+			},
+
+			confirm(e) {
+				let time = '';
+				time = uni.$u.timeFormat(e.value, 'yyyy-mm-dd')
+
+				if (this.startOrEnd == 'start') {
+					this.timeStart = time
+				}
+				if (this.startOrEnd == 'end') {
+					this.timeEnd = time
+				}
+				this.getData()
+				this.timeShow = false;
+			},
+
+			pickerClose() {
+				this.pickerShow = false
+			},
+
+			//获取统计数据
+			getCountData(type) {
+				salesData({
+					type: "day",
+					beginDate: this.timeStart,
+					endDate: this.timeEnd
+				}).then(res => {
+					let data = res.data;
+					let tempChartData = {};
+					this.delData(data);
+					tempChartData.series = data.series ? data.series : [];
+					tempChartData.categories = data.categories ? data.categories : [];
+					this.chartData = JSON.parse(JSON.stringify(tempChartData));
+				}).catch(err => {
+
+				})
+			},
+
+			// 处理统计图表数据
+			delData(data) {
+				let arr = data.categories.map(item => {
+					if (item.length < 3) {
+						item = item + '点'
+					} else {
+						item = item.substr(4, 2) + '月' + item.substr(6, 2) + '日'
+					}
+					return item
+				})
+
+				data.categories = arr
+			},
+
+			getParams(type) {
+				let orderByKey = "";
+				let orderBy = "";
+				switch (type) {
+					case '销售额':
+						orderBy = 'desc';
+						orderByKey = 'sales_money';
+						break;
+					case '订单笔数':
+						orderBy = 'desc';
+						orderByKey = 'sales_count';
+						break;
+					case '销量':
+						orderBy = 'desc';
+						orderByKey = 'goods_count';
+						break;
+					default:
+						break;
+				}
+
+				let params = {
+					type: 'day',
+					orderByKey: orderByKey,
+					orderBy: orderBy,
+					beginDate: this.timeStart,
+					endDate: this.timeEnd
+				}
+				return params
+			},
+
+			//商品统计总数居
+			getGoodsTotal() {
+				let params = this.getParams()
+				sumCount(params).then(res => {
+					this.total = res.data
+				})
+			},
+
+			//设备统计总数居
+			getDeviceTotal(type) {
+				let params = this.getParams(type)
+				deviceSumCount(params).then(res => {
+					this.total = res.data
+				})
+			},
+
+			//获取设备排行
+			getList1(e) {
+				let type = e == 0 ? '销售额' : '订单笔数';
+				let dataParams = this.getParams(type)
+				let pageParams = {
+					page: {
+						current: 1,
+						size: 5
+					}
+				}
+				let params = Object.assign(dataParams, pageParams)
+				deviceSumPage(params).then(res => {
+					if (res.data) {
+						this.list1 = res.data.records;
+					} else {
+						this.list1 = []
+					}
+				})
+			},
+
+			//获取商品排行
+			getList2(e) {
+				let type = e == 0 ? '销售额' : '销量';
+				let dataParams = this.getParams(type)
+				let pageParams = {
+					page: {
+						current: 1,
+						size: 5
+					}
+				}
+				let params = Object.assign(dataParams, pageParams)
+				sumPage(params).then(res => {
+					if (res.data) {
+						this.list2 = res.data.records;
+					} else {
+						this.list2 = []
+					}
+				})
+			},
+
+			more(type) {
+				let sortType = type == 'device' ? this.deviceSortType : this.goodsSortType
+				this.$tab.navigateTo(
+					`/pages/globalPages/statisticsMore?type=${type}&&timeStart=${this.timeStart}&&timeEnd=${this.timeEnd}&&sortType=${sortType}`
+				)
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.container {
+		.content {
+			padding-bottom: 24rpx;
+
+			.card {
+				width: 724rpx;
+				margin-left: 13rpx;
+				background-color: #fff;
+				margin-top: 20rpx;
+				box-shadow: 0px 0px 10rpx 0px rgba(174, 201, 255, 0.2);
+				border-radius: 14rpx;
+			}
+
+			.tab {
+				padding: 24rpx;
+				// background-color: #fff;
+			}
+
+			.select {
+				padding-top: 34rpx;
+
+				.time-tab {
+					padding: 0 36rpx;
+
+					.time-tab-item {
+						width: 20%;
+						border: 1rpx solid #CCCCCC;
+						border-left: none;
+						text-align: center;
+						font-size: 26rpx;
+						line-height: 58rpx;
+						color: #777777;
+
+						&:first-of-type {
+							border-left: 1rpx solid #CCCCCC;
+							border-radius: 6rpx 0px 0px 6rpx;
+						}
+
+						&:last-of-type {
+							border-radius: 0px 6rpx 6rpx 0px;
+						}
+
+						&.time-tab-show {
+							background: #F4F8FF;
+							color: #2C6FF3;
+						}
+					}
+				}
+
+				.time-select {
+					padding: 34rpx 36rpx;
+					color: rgb(144, 144, 144);
+					font-size: 28rpx;
+
+					>view {
+						>view:nth-child(1) {
+							width: 160rpx;
+							text-align: right;
+						}
+
+						>view:nth-child(2) {
+							width: 80rpx;
+							padding: 0 24rpx;
+						}
+					}
+				}
+			}
+
+			.total {
+				padding: 12rpx 0;
+				background-color: #fff;
+
+				.total-name {
+					color: #333;
+					font-size: 28rpx;
+					line-height: 28rpx;
+					margin-top: 48rpx;
+				}
+
+				.total-num {
+					color: #eab09a;
+					font-size: 42rpx;
+					font-weight: bold;
+					line-height: 41rpx;
+					margin-top: 24rpx;
+				}
+			}
+
+			.search {
+				padding: 24rpx 24rpx;
+				background-color: #fff;
+				position: relative;
+				font-size: 26rpx;
+
+				.time-type {
+					width: 180rpx;
+				}
+
+				.time-select {
+					width: 340rpx;
+					line-height: 64rpx;
+					background-color: #eeeeef;
+					border-radius: 6rpx;
+				}
+
+
+			}
+
+			.chart {
+				background-color: #fff;
+			}
+
+			.table {
+				width: 724rpx;
+				background-color: #fff;
+				margin-top: 18rpx;
+				color: #333;
+
+				.table-title {
+					font-size: 32rpx;
+					line-height: 50rpx;
+					padding: 24rpx;
+					font-weight: 800;
+					color: #333333;
+					border-bottom: 1rpx solid #ebeef5;
+				}
+
+				.table-td {
+					width: 100%;
+					text-align: center;
+
+
+					&.table-td-name {
+						width: 230rpx;
+						white-space: wrap;
+					}
+				}
+
+				.table-img {
+					width: 40rpx;
+					height: 40rpx;
+					position: relative;
+				}
+
+				.sort-type {
+					width: 250rpx;
+					height: 50rpx;
+					line-height: 48rpx;
+					text-align: center;
+					font-weight: normal;
+
+					.sort-type-item {
+						width: 50%;
+						font-size: 26rpx;
+						color: #555555;
+						background-color: #fff;
+
+						&.sort-type-item:nth-child(1) {
+							border-top-left-radius: 6rpx;
+							border-bottom-left-radius: 6rpx;
+							border-top: 1rpx solid #CCCCCC;
+							border-left: 1rpx solid #CCCCCC;
+							border-bottom: 1rpx solid #CCCCCC;
+
+							&.sort-type-show {
+								color: #fff;
+								border-top: 1rpx solid #2C6FF3;
+								border-left: 1rpx solid #2C6FF3;
+								border-bottom: 1rpx solid #2C6FF3;
+								background-color: #2C6FF3;
+							}
+						}
+
+						&.sort-type-item:nth-child(2) {
+							border-top-right-radius: 6rpx;
+							border-bottom-right-radius: 6rpx;
+							border-top: 1rpx solid #CCCCCC;
+							border-right: 1rpx solid #CCCCCC;
+							border-bottom: 1rpx solid #CCCCCC;
+
+							&.sort-type-show {
+								color: #fff;
+								border-top: 1rpx solid #2C6FF3;
+								border-right: 1rpx solid #2C6FF3;
+								border-bottom: 1rpx solid #2C6FF3;
+								background-color: #2C6FF3;
+							}
+						}
+					}
+
+				}
+
+				/deep/.uni-table-th {
+					color: #333;
+					font-weight: normal;
+				}
+
+				/deep/.uni-table-td {
+					vertical-align: middle;
+				}
+			}
+
+			.more {
+				text-align: right;
+				padding-right: 40rpx;
+				position: relative;
+				color: #2C6FF3;
+				line-height: 80rpx;
+				background-color: #fff;
+
+				>view {
+					position: absolute;
+					right: 12rpx;
+					top: 50%;
+					transform: translateY(-50%);
+				}
+			}
+		}
+	}
+</style>

+ 457 - 0
pages/globalPages/statisticsMore.vue

@@ -0,0 +1,457 @@
+<template>
+	<view class="container">
+		<u-navbar leftIconColor="#fff" titleStyle="color:#fff;fontSize:36rpx;" :autoBack="true" bgColor="#2C6FF3"
+			:placeholder="true" title="销售统计"></u-navbar>
+		<view class="content">
+			<view class="device-table" v-if="type=='device'">
+				<view class="table">
+					<view class="table-title flex justify-between align-center">
+						<view class="title">
+							设备销售排行
+						</view>
+						<view class="sort-type flex">
+							<view :class="[deviceSortType==0?'sort-type-item sort-type-show':'sort-type-item']"  @click="deviceSort(0)">
+								销售额
+							</view>
+							<view :class="[deviceSortType==1?'sort-type-item sort-type-show':'sort-type-item']"  @click="deviceSort(1)">
+								订单笔数
+							</view>
+						</view>
+					</view>
+					<uni-table :border="false" :stripe="false" emptyText="暂无更多数据">
+						<!-- 表头行 -->
+						<uni-tr>
+							<uni-th align="center" width="52">排名</uni-th>
+							<uni-th align="center" width="110">上榜设备</uni-th>
+							<uni-th align="center" width="92">销售额</uni-th>
+							<uni-th align="center" width="96">订单笔数</uni-th>
+						</uni-tr>
+						<!-- 表格数据行 -->
+						<uni-tr v-for="(item,index) in list1" :key="item.deviceId">
+							<uni-td v-if="index==0">
+								<view class="table-td">
+									<image class="table-img" src="../../static/images/global/first-class.png"
+										mode="widthFix"></image>
+								</view>
+							</uni-td>
+							<uni-td v-else-if="index==1">
+								<view class="table-td">
+									<image class="table-img" src="../../static/images/global/second-class.png"
+										mode="widthFix"></image>
+								</view>
+							</uni-td>
+							<uni-td v-else-if="index==2">
+								<view class="table-td">
+									<image class="table-img" src="../../static/images/global/third-class.png"
+										mode="widthFix"></image>
+								</view>
+							</uni-td>
+							<uni-td v-else>
+								<view class="table-td">
+									{{index+1}}
+								</view>
+							</uni-td>
+							<uni-td>
+								<view class="table-td table-td-name">{{item.deviceName||item.deviceId}}</view>
+							</uni-td>
+							<uni-td>
+								<view class="table-td">¥{{$xy.delMoney(item.salesMoney)}}</view>
+							</uni-td>
+							<uni-td>
+								<view class="table-td">{{item.salesCount}}笔</view>
+							</uni-td>
+						</uni-tr>
+					</uni-table>
+				</view>
+			</view>
+			
+			<view class="goods-table" v-else>
+				<view class="table">
+					<view class="table-title flex justify-between align-center">
+						<view class="title">
+							商品销售排行
+						</view>
+						<view class="sort-type flex">
+							<view :class="[goodsSortType==0?'sort-type-item sort-type-show':'sort-type-item']"  @click="goodsSort(0)">
+								销售额
+							</view>
+							<view :class="[goodsSortType==1?'sort-type-item sort-type-show':'sort-type-item']"  @click="goodsSort(1)">
+								销量
+							</view>
+						</view>
+					</view>
+					<uni-table :border="false" :stripe="false" emptyText="暂无更多数据">
+						<!-- 表头行 -->
+						<uni-tr>
+							<uni-th align="center" width="52">排名</uni-th>
+							<uni-th align="center" width="110">上榜商品</uni-th>
+							<uni-th align="center" width="92">销售额</uni-th>
+							<uni-th align="center" width="92">销量</uni-th>
+						</uni-tr>
+						<!-- 表格数据行 -->
+						<uni-tr v-for="(item,index) in list2" :key="item.goodsId">
+							<uni-td v-if="index==0">
+								<view class="table-td">
+									<image class="table-img" src="../../static/images/global/first-class.png"
+										mode="widthFix"></image>
+								</view>
+							</uni-td>
+							<uni-td v-else-if="index==1">
+								<view class="table-td">
+									<image class="table-img" src="../../static/images/global/second-class.png"
+										mode="widthFix"></image>
+								</view>
+							</uni-td>
+							<uni-td v-else-if="index==2">
+								<view class="table-td">
+									<image class="table-img" src="../../static/images/global/third-class.png"
+										mode="widthFix"></image>
+								</view>
+							</uni-td>
+							<uni-td v-else>
+								<view class="table-td">
+									{{index+1}}
+								</view>
+							</uni-td>
+							<uni-td>
+								<view class="table-td table-td-name">{{item.goodsName}}</view>
+							</uni-td>
+							<uni-td>
+								<view class="table-td">¥{{$xy.delMoney(item.salesMoney)}}</view>
+							</uni-td>
+							<uni-td>
+								<view class="table-td">{{item.goodsCount}}件</view>
+							</uni-td>
+						</uni-tr>
+					</uni-table>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import {
+		sumPage
+	} from "@/api/commodity/goods.js"
+
+	import {
+		sumPage as deviceSumPage
+	} from "@/api/device/device.js"
+
+	export default {
+		data() {
+			return {
+				timeStart: '',
+				timeEnd: '',
+
+				typeColumns: [
+					['销售额', '订单笔数']
+				],
+				status: 'loadmore', //加载更多
+				list1: [], //设备列表
+				list2: [], //商品列表
+				type:'device',
+				deviceSortType: 0,
+				goodsSortType: 0,
+			}
+		},
+
+		onLoad(o) {
+			let _this = this;
+			const query = uni.createSelectorQuery().in(this);
+			query.select(".list").boundingClientRect((data) => {
+				uni.getSystemInfo({
+					success(res) {
+						_this.fullHeight = 2 * (res.windowHeight - data.top - 24) + 'rpx';
+					},
+				});
+			}).exec();
+			this.type=o.type
+			this.timeStart=o.timeStart
+			this.timeEnd=o.timeEnd
+			if(this.type=='device'){
+				this.deviceSortType=o.sortType
+			}else{
+				this.goodsSortType=o.sortType
+			}
+		},
+
+		onShow() {
+			this.getData()
+		},
+
+
+
+		methods: {
+			getData() {
+				if(this.type=='device'){
+					this.getList1(this.deviceSortType)
+				}else{
+					this.getList2(this.goodsSortType)
+				}
+			},
+
+			deviceSort(type){
+				this.deviceSortType=type
+				this.getList1(type)
+			},
+			
+			goodsSort(type){
+				this.goodsSortType=type
+				this.getList2(type)
+			},
+
+			getParams(type) {
+				let orderByKey = "";
+				let orderBy = "";
+				switch (type) {
+					case '销售额':
+						orderBy = 'desc';
+						orderByKey = 'sales_money';
+						break;
+					case '订单笔数':
+						orderBy = 'desc';
+						orderByKey = 'sales_count';
+						break;
+					case '销量':
+						orderBy = 'desc';
+						orderByKey = 'goods_count';
+						break;
+					default:
+						break;
+				}
+
+				let params = {
+					type: 'day',
+					orderByKey: orderByKey,
+					orderBy: orderBy,
+					beginDate: this.timeStart,
+					endDate: this.timeEnd
+				}
+				return params
+			},
+
+			//获取设备排行
+			getList1(e) {
+				let type=e==0?'销售额':'订单笔数';
+				let dataParams = this.getParams(type)
+				let pageParams = {
+					page: {
+						current: 1,
+						size: 1000
+					}
+				}
+				let params = Object.assign(dataParams, pageParams)
+				deviceSumPage(params).then(res => {
+					if (res.data) {
+						this.list1 = res.data.records;
+					} else {
+						this.list1 = []
+					}
+				})
+			},
+
+			//获取商品排行
+			getList2(e) {
+				let type=e==0?'销售额':'销量';
+				let dataParams = this.getParams(type)
+				let pageParams = {
+					page: {
+						current: 1,
+						size: 1000
+					}
+				}
+				let params = Object.assign(dataParams, pageParams)
+				sumPage(params).then(res => {
+					if (res.data) {
+						this.list2 = res.data.records;
+					} else {
+						this.list2 = []
+					}
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.container {
+		.content {
+			.select {
+				.time-tab {
+					padding: 0 36rpx;
+
+					.time-tab-item {
+						padding: 0 24rpx;
+						background-color: #fff;
+						border: 1rpx solid #e0b4a2;
+						text-align: center;
+						line-height: 50rpx;
+						color: #e0b4a2;
+						border-radius: 40rpx;
+
+						&.time-tab-show {
+							background-color: #eab09a;
+							color: #d98a6d;
+						}
+					}
+				}
+
+				.time-select {
+					padding: 24rpx 36rpx;
+					color: rgb(144, 144, 144);
+					font-size: 28rpx;
+
+					>view:nth-child(2) {
+						width: 160rpx;
+						text-align: right;
+					}
+
+					>view:nth-child(3) {
+						width: 80rpx;
+						padding: 0 24rpx;
+					}
+				}
+			}
+
+			.total {
+				padding: 12rpx 0;
+				background-color: #fff;
+
+				.total-name {
+					color: #333;
+					font-size: 28rpx;
+					line-height: 50rpx;
+				}
+
+				.total-num {
+					color: #eab09a;
+					font-size: 32rpx;
+					font-weight: bold;
+					line-height: 50rpx;
+				}
+			}
+
+			.search {
+				padding: 24rpx 24rpx;
+				background-color: #fff;
+				position: relative;
+				font-size: 26rpx;
+
+				.time-type {
+					width: 180rpx;
+				}
+
+				.time-select {
+					width: 340rpx;
+					line-height: 64rpx;
+					background-color: #eeeeef;
+					border-radius: 6rpx;
+				}
+
+
+			}
+
+			.table {
+				width: 100%;
+				background-color: #fff;
+				color:#333;
+
+				.table-title {
+					line-height: 50rpx;
+					padding: 24rpx;
+					border-bottom: 1rpx solid #ebeef5;
+				}
+
+				.table-td {
+					width: 100%;
+					text-align: center;
+					
+					&.table-td-name{
+						width:270rpx;
+						white-space: wrap;
+					}
+				}
+
+				.table-img {
+					width: 40rpx;
+					height: 40rpx;
+					position: relative;
+				}
+
+				.sort-type {
+					width: 250rpx;
+					height: 50rpx;
+					line-height: 48rpx;
+					text-align: center;
+					font-weight: normal;
+				
+					.sort-type-item {
+						width: 50%;
+						font-size: 26rpx;
+						color: #555555;
+						background-color: #fff;
+				
+						&.sort-type-item:nth-child(1) {
+							border-top-left-radius: 6rpx;
+							border-bottom-left-radius: 6rpx;
+							border-top: 1rpx solid #CCCCCC;
+							border-left: 1rpx solid #CCCCCC;
+							border-bottom: 1rpx solid #CCCCCC;
+				
+							&.sort-type-show {
+								color: #fff;
+								border-top: 1rpx solid #2C6FF3;
+								border-left: 1rpx solid #2C6FF3;
+								border-bottom: 1rpx solid #2C6FF3;
+								background-color: #2C6FF3;
+							}
+						}
+				
+						&.sort-type-item:nth-child(2) {
+							border-top-right-radius: 6rpx;
+							border-bottom-right-radius: 6rpx;
+							border-top: 1rpx solid #CCCCCC;
+							border-right: 1rpx solid #CCCCCC;
+							border-bottom: 1rpx solid #CCCCCC;
+				
+							&.sort-type-show {
+								color: #fff;
+								border-top: 1rpx solid #2C6FF3;
+								border-right: 1rpx solid #2C6FF3;
+								border-bottom: 1rpx solid #2C6FF3;
+								background-color: #2C6FF3;
+							}
+						}
+					}
+				
+				}
+				
+				/deep/.uni-table-th {
+					color: #333;
+					font-weight: normal;
+				}
+				
+				/deep/.uni-table-td{
+					vertical-align: middle;
+				}
+			}
+			
+			.more{
+				text-align: right;
+				padding-right: 40rpx;
+				position: relative;
+				color:#333;
+				line-height: 80rpx;
+				background-color: #fff;
+				>view{
+					position: absolute;
+					right:12rpx;
+					top:50%;
+					transform: translateY(-50%);
+				}
+			}
+		}
+	}
+</style>

+ 41 - 0
pages/globalPages/test.vue

@@ -0,0 +1,41 @@
+<template>
+	<view class="container">
+		<button style="margin-top: 40%;" @click="downLoad">下载测试</button>
+	</view>
+</template>
+
+<script>
+	import {
+		exportQrCode
+	} from '@/api/download.js'
+	import config from '@/config'
+	export default {
+		data() {
+			return {
+				logs: []
+			}
+		},
+		onLoad(o) {
+
+		},
+		methods: {
+			downLoad() {
+				exportQrCode({
+					"isWhere": false,
+					"deviceIds": [2305000127]
+				}).then(res => {
+					console.log(res)
+				})
+			}
+		},
+	}
+</script>
+
+<style scoped lang="scss">
+	.container {
+		padding: 24rpx;
+		line-height: 50rpx;
+
+
+	}
+</style>

+ 291 - 0
pages/login.vue

@@ -0,0 +1,291 @@
+<!-- 蓝色登录页面2 -->
+<template>
+	<view style="height:100vh;background: #fff;">
+		<view class="img-a">
+			<view class="t-b">
+				您好,
+				<br />
+				欢迎使用,喵星人智能货柜管理平台
+			</view>
+		</view>
+		<view class="login-view" style="">
+			<view class="t-login">
+				<form class="cl">
+					<view class="t-a">
+						<text class="txt">手机号</text>
+						<input placeholder="请输入账号" v-model="loginForm.username" name="input" />
+					</view>
+					<view class="t-a">
+						<text class="txt">密码</text>
+						<input v-model="loginForm.password" type="password" placeholder="请输入密码" name="input"></input>
+					</view>
+
+					<view class="remember">
+						<u-checkbox-group v-model="isRemember" @change="remember">
+							<u-checkbox size="16" labelSize="12" labelColor="#666" name="1" label="记住密码"></u-checkbox>
+						</u-checkbox-group>
+					</view>
+
+					<button @click="handleLogin">登 录</button>
+					<!-- <view class="reg" @tap="reg()">注 册</view> -->
+				</form>
+				<!-- <view class="t-f"><text>—————— 第三方账号登录 ——————</text></view>
+				<view class="t-e cl">
+					<view class="t-g" @tap="wxLogin()">
+						<image src="https://zhoukaiwen.com/img/loginImg/wx.png"></image>
+					</view>
+					<view class="t-g" @tap="zfbLogin()">
+						<image src="https://zhoukaiwen.com/img/loginImg/qq.png"></image>
+					</view>
+				</view> -->
+			</view>
+		</view>
+	</view>
+</template>
+<script>
+	import {
+		sysList
+	} from '@/api/system/menu.js'
+	export default {
+		data() {
+			return {
+				loginForm: {
+					username: "",
+					password: "",
+					code: "",
+					uuid: ''
+				},
+				isRemember: []
+			};
+		},
+		onLoad() {
+			if (uni.getStorageSync('account')) {
+				this.isRemember = ['1']
+				let account = JSON.parse(uni.getStorageSync('account'))
+				this.loginForm.username = account.username
+				this.loginForm.password = account.password
+			}
+		},
+		methods: {
+			remember(e) {
+				this.isRemember = e
+			},
+
+			// 登录方法
+			async handleLogin() {
+				if (!this.loginForm.username) {
+					this.$modal.msg('请输入账号~')
+					return
+				}
+				if (!this.loginForm.password) {
+					this.$modal.msg('请输入密码~')
+					return
+				}
+				this.pwdLogin()
+			},
+
+			// 密码登录
+			async pwdLogin(params) {
+				await this.$store.dispatch('Login', this.loginForm).then(() => {
+					if (this.isRemember.length > 0 && this.isRemember[0]) { //记住密码
+						let account = {
+							username: this.loginForm.username,
+							password: this.loginForm.password
+						}
+						uni.setStorageSync('account', JSON.stringify(account))
+					} else {
+						uni.setStorageSync('account', '')
+					}
+				})
+
+				await this.getSysId()
+
+				await this.$store.dispatch('GetPermis')
+				//判断用户是否有任一菜单权限
+
+				if (this.$store.state.permission.permissions_menu && this.$store.state.permission.permissions_menu !=
+					'[]') {
+					this.$tab.reLaunch('/pages/globalPages/home')
+				} else {
+					this.$modal.msg('该用户无权限~')
+				}
+
+			},
+
+			// 获取系统id
+			getSysId() {
+				return new Promise((resolve, reject) => {
+					sysList({}).then(res => {
+						let data = res.data;
+						let sysId = 381638941857029; //默认系统id
+						for (var i = 0; i < data.length; i++) {
+							let item = data[i];
+							if (item.code == 'xy_merc_mini') {
+								sysId = item.id
+								console.log('系统id:', sysId)
+							}
+						}
+						uni.setStorageSync('sysId', sysId)
+						resolve(res)
+					}).catch(err => {
+						reject(err)
+					})
+				})
+			},
+
+			//注册按钮点击
+			reg() {
+				// uni.showToast({ title: '注册跳转', icon: 'none' });
+			},
+			//等三方微信登录
+			wxLogin() {
+				// uni.showToast({ title: '微信登录', icon: 'none' });
+			},
+			//第三方支付宝登录
+			zfbLogin() {
+				// uni.showToast({ title: '支付宝登录', icon: 'none' });
+			}
+		}
+	};
+</script>
+<style>
+	.txt {
+		font-size: 32rpx;
+		font-weight: bold;
+		color: #333333;
+	}
+
+	.img-a {
+		width: 100%;
+		height: 450rpx;
+		background-image: url(https://cdn.ossfile.mxrvending.com/assets/xy_mana_mini/images/global/opendoor-manage-mini.png);
+		background-size: 100%;
+	}
+
+	.reg {
+		font-size: 28rpx;
+		color: #fff;
+		height: 90rpx;
+		line-height: 90rpx;
+		border-radius: 50rpx;
+		font-weight: bold;
+		background: #f5f6fa;
+		color: #000000;
+		text-align: center;
+		margin-top: 30rpx;
+	}
+
+	.login-view {
+		width: 100%;
+		position: relative;
+		margin-top: -120rpx;
+		background-color: #ffffff;
+		border-radius: 8% 8% 0% 0;
+	}
+
+	.t-login {
+		width: 600rpx;
+		margin: 0 auto;
+		font-size: 28rpx;
+		padding-top: 80rpx;
+	}
+
+	.t-login button {
+		font-size: 28rpx;
+		background: #2796f2;
+		color: #fff;
+		height: 90rpx;
+		line-height: 90rpx;
+		border-radius: 50rpx;
+		font-weight: bold;
+	}
+
+	.t-login input {
+		height: 90rpx;
+		line-height: 90rpx;
+		margin-bottom: 50rpx;
+		border-bottom: 1px solid #e9e9e9;
+		font-size: 28rpx;
+	}
+
+	.t-login .t-a {
+		position: relative;
+	}
+
+	.t-b {
+		text-align: left;
+		font-size: 42rpx;
+		color: #ffffff;
+		padding: 130rpx 0 0 70rpx;
+		font-weight: bold;
+		line-height: 70rpx;
+	}
+
+	.t-login .t-c {
+		position: absolute;
+		right: 22rpx;
+		top: 22rpx;
+		background: #5677fc;
+		color: #fff;
+		font-size: 24rpx;
+		border-radius: 50rpx;
+		height: 50rpx;
+		line-height: 50rpx;
+		padding: 0 25rpx;
+	}
+
+	.t-login .t-d {
+		text-align: center;
+		color: #999;
+		margin: 80rpx 0;
+	}
+
+	.t-login .t-e {
+		text-align: center;
+		width: 250rpx;
+		margin: 80rpx auto 0;
+	}
+
+	.t-login .t-g {
+		float: left;
+		width: 50%;
+	}
+
+	.t-login .t-e image {
+		width: 50rpx;
+		height: 50rpx;
+	}
+
+	.t-login .t-f {
+		text-align: center;
+		margin: 150rpx 0 0 0;
+		color: #666;
+	}
+
+	.t-login .t-f text {
+		margin-left: 20rpx;
+		color: #aaaaaa;
+		font-size: 27rpx;
+	}
+
+	.t-login .uni-input-placeholder {
+		color: #aeaeae;
+	}
+
+	.cl {
+		zoom: 1;
+	}
+
+	.cl:after {
+		clear: both;
+		display: block;
+		visibility: hidden;
+		height: 0;
+		content: '\20';
+	}
+	
+	.remember {
+		margin-top: 24rpx;
+		margin-bottom: 40rpx;
+	}
+</style>

+ 100 - 0
pages/order/components/xvideo.vue

@@ -0,0 +1,100 @@
+<template>
+	<view class="video_content">
+		<view v-if="show" class="popup_content">
+			<view class="flex">
+				<video id="myVideo" :src="videoUrl" style="width:100%;height: 372rpx; margin: 10rpx 10rpx;"></video>
+			</view>
+			<view class="flex" style="margin-top: 10rpx;" v-if="showBtn">
+				<view>
+					<xbutton size='medium' round='25rpx' padding='0rpx 20rpx' bgColor='#2C6FF3' @tap="playVideo(0)">主视频
+					</xbutton>
+				</view>
+				<view style="margin-left: 24rpx;">
+					<xbutton size='medium' round='25rpx' padding='0rpx 20rpx' bgColor='#2C6FF3' @tap="playVideo(1)">副视频
+					</xbutton>
+				</view>
+			</view>
+		</view>
+		<view class="popup_overlay" v-if="show" @click="closeVideoView()"></view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				show: false,
+				videoUrl: null,
+			}
+		},
+		props: {
+			showBtn: {
+				type: Boolean,
+				require: true,
+				default: false
+			},
+			list: {
+				type: [Array,String],
+				require: true,
+				default () {
+					return ''
+				}
+			},
+		},
+		
+		watch:{
+			 list:{
+				handler(newVal,oldVal){
+					if(newVal!=null){
+						if(typeof(newVal)=='object'){
+							this.videoUrl=newVal[0]
+						}else{
+							this.videoUrl=newVal
+						}
+					}
+				},
+				deep:true,
+				immediate:true
+			}
+		},
+
+		methods: {
+			closeVideoView() {
+				this.show = false
+			},
+			
+			playVideo(type){
+				this.videoUrl=this.list[type]
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.popup_content {
+		position: fixed;
+		top: 50%;
+		left: 50%;
+		width: 700rpx;
+		height: 500rpx;
+		margin-left: -350rpx;
+		margin-top: -250rpx;
+		border: 10px solid white;
+		background-color: white;
+		z-index: 1002;
+		overflow: auto;
+	}
+	
+	.popup_overlay {
+		position: fixed;
+		top: 0%;
+		left: 0%;
+		width: 100%;
+		height: 100%;
+		background-color: black;
+		z-index: 1001;
+		-moz-opacity: 0.8;
+		opacity: .80;
+		filter: alpha(opacity=88);
+	}
+</style>

+ 432 - 0
pages/order/orderDel.vue

@@ -0,0 +1,432 @@
+<template>
+	<view class="container">
+		<u-navbar leftIconColor="#fff" titleStyle="color:#fff;fontSize:36rpx;" :autoBack="true" bgColor="#2C6FF3"
+			:placeholder="true" title="手动扣款"></u-navbar>
+		<view class="video-container">
+			<video :src="videoUrl" style="width: 100%;"></video>
+			<view class="flex align-center justify-between" style="padding:0 24rpx;">
+				<view class="video-tab">
+					<u-subsection :list="videoType" activeColor="#2C6FF3" :current="videoCurrent"
+						@change="sectionChange">
+					</u-subsection>
+				</view>
+				<view class="flex">
+					<view>
+						<xbutton size="mini" padding="0 20rpx" @tap='addCom(deviceId)'>添加商品</xbutton>
+					</view>
+					<view style="margin-left: 24rpx;">
+						<xbutton size="mini" width='180rpx' @tap='submit'>提交补扣申请</xbutton>
+					</view>
+				</view>
+			</view>
+		</view>
+		<view class="goods-select" v-if="goodsList.length>0">
+			<u-scroll-list :indicator="indicator" indicatorColor="#fff0f0" indicatorActiveColor="#f56c6c">
+				<view class="flex justify-around">
+					<view class="goods-item" v-for="(item,index) in goodsList" :key="index">
+						<view class="flex flex-direction align-center justify-center image-dele-container goodContainer"
+							@tap="clean(index,item)">
+							<!-- <view class="flex align-center justify-center numberContainer">
+								{{value}}
+							</view> -->
+							<view class="image">
+								<u--image radius="4" width="100rpx" height="100rpx" :src="item.goodsImg" mode="aspectFit"
+									:lazy-lord="true"></u--image>
+							</view>
+							<view class="goods-select-name">{{item.name}}</view>
+						</view>
+						<view class="flex align-center justify-center" style="margin-top: 8rpx;">
+							<view class="flex align-center justify-center">
+								<view class="minus" @tap="reduce(item,index)">
+									<u-icon name="minus" size="12"></u-icon>
+								</view>
+								<text style="width: 50rpx;text-align: center;" class="input">{{item.number}}</text>
+								<view class="plus" @tap="add(item)">
+									<u-icon name="plus" color="#FFFFFF" size="12"></u-icon>
+								</view>
+							</view>
+						</view>
+					</view>
+				</view>
+			</u-scroll-list>
+		</view>
+
+		<view class="goods-select-empty" v-else>
+			添加需要补扣的商品~
+		</view>
+
+		<view class="classify-wrap">
+			<Classify :status="status" :commList="commList"
+				@comClick='detail' :height="fullHeight" :leftShow="false" />
+		</view>
+	</view>
+</template>
+
+<script>
+	import {
+		ownerGoodsList,
+		list
+	} from "@/api/commodity/goods.js"
+	import {
+		apply
+	} from "@/api/order/riskorder.js"
+	import {
+		categoryList
+	} from "@/api/device/device.js"
+	import Classify from "@/components/classify/index.vue"
+	export default {
+		components: {
+			Classify
+		},
+		data() {
+			return {
+				scrollintoview: '',
+				fullHeight: '0',
+				tabList: [], //商品类目
+				commList: [], //商品列表
+				goodsList: [], //商品id列表
+				status: 'nomore', //加载更多
+				riskId: '',
+				deviceId: '',
+				video: {
+					url1: '',
+					url2: ''
+				},
+				videoUrl: null,
+				videoType: ['主视频', '副视频'],
+				videoCurrent: 0,
+				statGoodsList: [], //平台识别补扣商品
+			}
+		},
+
+		onLoad(e) {
+			let _this = this;
+			const query = uni.createSelectorQuery().in(this);
+			query.select(".classify-wrap").boundingClientRect((data) => {
+				uni.getSystemInfo({
+					success(res) {
+						// 针对iPhone X等机型底部安全距离做适配
+						const model = res.model;
+						const modelInclude = [
+							"iPhone X", 
+							'iPhone XR', 
+							"iPhone XS", 
+							"iPhone XS MAX",
+							"iPhone 12/13 mini",
+							"iPhone 12/13 (Pro)",
+							"iPhone 12/13 Pro Max",
+							"iPhone 14 Pro Max"
+						];
+						let safeDistance = modelInclude.includes(model)
+						//动态设置商品区域高度
+						if (safeDistance) {
+							_this.fullHeight = res.windowHeight - data.top - 34 + 'px';
+						} else {
+							_this.fullHeight = res.windowHeight - data.top  + 'px';
+						}
+					},
+				});
+			}).exec();
+			// uni.getSystemInfo({
+			// 	success(res) {
+			// 		_this.fullHeight = 2 * (res.windowHeight - res.statusBarHeight - 44 - 143) + 'rpx';
+			// 	},
+			// });
+			this.riskId = e.id
+			this.deviceId = e.deviceId
+			this.initOrder()
+		},
+		
+		onShow() {
+			this.getCommList()
+		},
+
+		methods: {
+			//初始化页面信息
+			initOrder() {
+				let orderDetail = JSON.parse(uni.getStorageSync('riskOrder'));
+				if (orderDetail.video) {
+					this.video = {
+						url1: orderDetail.video.split(',')[0],
+						url2: orderDetail.video.split(',')[1]
+					}
+					this.videoUrl = this.video.url1
+				} else {
+					this.video = {
+						url1: '',
+						url2: ''
+					}
+				}
+
+
+				let tempGoodsList = orderDetail.orderGoods.map(i => {
+					return {
+						goodsImg: i.goodsImgUrl,
+						spid: i.goodsId,
+						name: i.goodsName,
+						price: Number(i.totalMoney) / 100,
+						number: Number(i.totalNumber),
+						initNum: Number(i.totalNumber)
+					}
+				})
+
+				this.statGoodsList = JSON.parse(JSON.stringify(tempGoodsList))
+				this.goodsList = JSON.parse(JSON.stringify(tempGoodsList))
+			},
+
+			sectionChange(e) {
+				this.videoCurrent = e;
+				if (e == 0) {
+					this.videoUrl = this.video.url1
+				} else {
+					this.videoUrl = this.video.url2
+				}
+			},
+
+			//根据类目获取商品列表
+			getCommList() {
+				list({
+					deviceId: this.deviceId,
+				}).then(res => {
+					let data = res.data;
+					let newData = data.map(i => {
+						i.price = (Number(i.price) / 100).toFixed(2)
+						return i
+					})
+					this.commList = newData
+				})
+			},
+
+			statGoodsTips() {
+				this.$modal.msg('该商品为平台算法识别异常商品,为防止您的无辜损失,无法再删减!')
+			},
+
+			clean(e, item) { //点击移除商品
+				//校验是否算法识别商品
+				if (this.isStatGoods(item)) {
+					this.statGoodsTips()
+					return
+				}
+				this.goodsList.splice(e, 1)
+			},
+
+			// 校验是否算法识别商品
+			isStatGoods(e) {
+				console.log(e)
+				console.log(this.statGoodsList)
+				for (let i = 0; i < this.statGoodsList.length; i++) {
+					let item = this.statGoodsList[i];
+					if (item.spid == e.spid) {
+						return true
+					}
+				}
+				return false
+			},
+
+			//校验是否减少了算法识别商品
+			isDelStatGoods(e) {
+				for (let i = 0; i < this.statGoodsList.length; i++) {
+					let item = this.statGoodsList[i];
+					if (item.spid == e.spid && item.number == e.number) {
+						return true
+					}
+				}
+				return false
+			},
+
+			detail(e) {
+				this.goodsList.forEach(item => {
+					if (e.goodsId == item.spid) {
+						this.add(item)
+					}
+				})
+
+				for (var i = 0; i < this.goodsList.length; i++) {
+					if (this.goodsList[i].spid == e.goodsId) {
+						return;
+					}
+				}
+
+				this.goodsList.push({
+					goodsImg: e.cover,
+					spid: e.goodsId,
+					name: e.name,
+					price: e.price,
+					number: 1,
+					initNum: 0
+				})
+
+			},
+			//新增
+			add(e) {
+				e.number++
+			},
+			//减少
+			reduce(e, index) {
+				if (this.isDelStatGoods(e)) { //校验是否减少了算法识别商品
+					this.statGoodsTips()
+					return
+				}
+				e.number--
+				if (e.number == 0) {
+					this.goodsList.splice(index, 1)
+				}
+			},
+			reset() {
+				this.status == 'loadmore'
+				this.page = 1;
+				this.size = 10;
+				this.commList = [];
+			},
+			
+			// 添加商品至机器
+			addCom(deviceId){
+				this.$tab.navigateTo('/pages/equipment/addCom?id='+deviceId)
+			},
+			
+			//提交补扣申请
+			submit() {
+				if (!this.goodsList.length) {
+					uni.$u.toast('请选择商品')
+					return;
+				}
+
+				var goodsId = []
+				this.goodsList.forEach(item => {
+					if (item.number - item.initNum > 0) { //除去算法识别商品
+						goodsId.push({
+							goodsId: item.spid,
+							goodsName: item.name,
+							price: (Number(item.price)) * 100,
+							totalNumber: item.number - item.initNum
+						})
+					}
+				})
+				apply({
+					"cutGoods": goodsId,
+					"riskId": this.riskId
+				}).then(res => {
+					this.$modal.msg('提交申请成功~')
+					setTimeout(() => {
+						this.$tab.navigateBack()
+					}, 800)
+				})
+			}
+		},
+
+		onUnload() {
+			uni.setStorage('riskOrder', '')
+		},
+	}
+</script>
+
+<style lang="scss" scoped>
+	.container {
+		.martop {
+			margin-top: 20rpx;
+		}
+
+		.margin {
+			margin: 10rpx 20rpx;
+		}
+
+		.margintop {
+			margin-top: 10rpx;
+		}
+
+		.video-container {
+			.video-tab {
+				width: 300rpx;
+			}
+		}
+
+		.box {
+			padding: 20rpx 24rpx;
+		}
+
+		.goods-item+.goods-item {
+			margin-left: 12rpx;
+		}
+
+		.image-dele-container {
+			position: relative;
+			width: 140rpx;
+			height: 140rpx;
+			background-color: #f6f6f6;
+			border-radius: 15rpx;
+			padding: 12rpx;
+
+			view {
+				text-overflow: ellipsis;
+				overflow: hidden;
+				white-space: nowrap;
+				width: 100rpx;
+			}
+
+			.image {
+				width: 100rpx;
+				height: 100rpx;
+			}
+
+			.goods-select-name {
+				font-size: 24rpx;
+			}
+		}
+
+		.goods-select {
+			margin: 0 24rpx;
+			height: 218rpx;
+			padding-top: 10rpx;
+		}
+
+		.goods-select-empty {
+			height: 218rpx;
+			text-align: center;
+			line-height: 218rpx;
+		}
+
+		.goodContainer {
+			position: relative;
+
+			.numberContainer {
+				width: 35rpx;
+				height: 35rpx;
+				border-radius: 100%;
+				color: #fff;
+				background-color: red;
+				position: absolute;
+				right: -5rpx;
+				top: -5rpx;
+			}
+		}
+
+		.minus {
+			width: 22px;
+			height: 22px;
+			border-width: 1px;
+			border-color: #E6E6E6;
+			border-style: solid;
+			border-top-left-radius: 100px;
+			border-top-right-radius: 100px;
+			border-bottom-left-radius: 100px;
+			border-bottom-right-radius: 100px;
+			@include flex;
+			justify-content: center;
+			align-items: center;
+		}
+
+
+
+		.plus {
+			width: 18px;
+			height: 18px;
+			background-color: #FF0000;
+			border-radius: 50%;
+			/* #ifndef APP-NVUE */
+			display: flex;
+			/* #endif */
+			justify-content: center;
+			align-items: center;
+		}
+	}
+</style>

+ 705 - 0
pages/order/orderDetails.vue

@@ -0,0 +1,705 @@
+<template>
+	<view class="container">
+		<u-navbar leftIconColor="#fff" titleStyle="color:#fff;fontSize:36rpx;" :autoBack="true" bgColor="#2C6FF3"
+			:placeholder="true" title="订单详情"></u-navbar>
+
+		<view class="content">
+			<view v-if="detail.orderGoods&&detail.orderGoods.length>0" class="box">
+				<view class="title">商品信息</view>
+				<view class="goods-container martop">
+					<view class="goods-item" v-for="(item,index) in detail.orderGoods" :key="item.id">
+						<view class="flex align-center">
+							<view class="spxx-image">
+								<u--image radius="4" width="130rpx" height="130rpx" :src="item.goodsImgUrl"
+									mode="aspectFit" :lazy-lord="true"></u--image>
+							</view>
+							<view class="flex flex-direction justify-between goods-msg" style="width:600rpx;">
+								<view class="flex justify-between">
+									<view style="width:360rpx;" class="goods-name">{{item.goodsName}}
+										*{{item.totalNumber}}</view>
+									<view class="text" style="color: red;" v-if="item.refundMoney&&item.refundMoney>0">
+										已退款:¥{{$xy.delMoney(item.refundMoney)}}
+									</view>
+								</view>
+								<view class="flex align-center justify-between">
+									<view class="goods-price">
+										单价:¥{{(Number(item.totalMoney)/Number(item.totalNumber))/100}}</view>
+									<!--<view class="">应收:¥2.00</view>
+											<view class="">实收:¥2.00</view> -->
+								</view>
+								<!-- <view class="">优惠活动:暂无</view> -->
+							</view>
+						</view>
+					</view>
+				</view>
+			</view>
+
+			<view class="box">
+				<view class="title">订单信息</view>
+				<view class="orderinformation-container martop">
+					<view class="flex align-center">
+						<view class="">
+							<view class="oc-name">订单编号:</view>{{detail.id||'/'}}<text class="under-line-text"
+								@tap="copy(detail.id)">复制</text>
+						</view>
+					</view>
+					<view class="flex align-center">
+						<view class="">
+							<view class="oc-name">商户单号:</view>{{detail.payQueryOrderId||'/'}}<text
+								class="under-line-text" @tap="copy(detail.payQueryOrderId)">复制</text>
+						</view>
+					</view>
+					<view class="flex align-center">
+						<view class="oc-name">设备:</view>{{detail.deviceId||'/'}}
+					</view>
+					<view class="flex align-center">
+						<view class="oc-name">交易时间:</view>{{detail.payTime||'/'}}
+					</view>
+					<view class="flex align-center justify-between">
+						<view class="phone">
+							<view class="oc-name">手机号:</view>{{detail.memberTel||'/'}}<text class="under-line-text"
+								@click="$tab.navigateTo(`/pages/order/userInfo?id=${detail.memberId}`)">会员信息</text><text
+								v-if="detail.memberIsBlacklist&&detail.memberTel">已拉黑</text>
+						</view>
+						<view class="block" v-if="detail.memberTel&&!detail.memberIsBlacklist" @click="block">
+							拉黑
+						</view>
+					</view>
+					<view class="flex align-center">
+						<view class="oc-name">订单状态:</view>{{detail.statusName||'/'}}
+					</view>
+					<view class="flex align-center">
+						<view class="oc-name">商品总价:</view>¥{{$xy.delMoney(detail.orderTotalMoney)}}
+					</view>
+					<view class="">
+						<view class="oc-name">优惠金额:</view>¥{{$xy.delMoney(detail.discountMoney)}}
+					</view>
+					<view class="">
+						<view class="oc-name">订单总金额:</view>¥{{$xy.delMoney(detail.orderTotalMoney)}}
+					</view>
+					<view class="flex align-center justify-end" style="margin-top: 26rpx;">
+						<view class="marleft">
+							<xbutton bgColor="#F4F8FF" color="#2C6FF3" @tap="showlogs">交易日志</xbutton>
+						</view>
+						<view class="marleft">
+							<xbutton bgColor="#F4F8FF" color="#2C6FF3">通知付款</xbutton>
+						</view>
+						<view class="marleft" v-if="detail.status==4">
+							<xbutton bgColor="#F4F8FF" color="#2C6FF3" @tap="open">发起退款</xbutton>
+						</view>
+						<view class="marleft">
+							<xbutton bgColor="#F4F8FF" color="#2C6FF3" @tap="showVideoView">查看交易视频</xbutton>
+						</view>
+					</view>
+				</view>
+			</view>
+
+			<view class="box">
+				<view class="title">支付信息</view>
+				<view class="payments-container martop">
+					<view class="flex align-center">
+						<view class="oc-name">支付时间:</view>{{detail.payTime||'/'}}
+					</view>
+					<view class="">
+						<view class="oc-name">支付方式:</view>{{$xy.getPayType(detail.payType)}}
+					</view>
+					<view class="">
+						<view class="oc-name">支付金额:</view>¥{{$xy.delMoney(detail.payMoney)}}
+					</view>
+					<view class="flex align-center">
+						<view class="">
+							<view class="oc-name">支付订单号:</view>{{detail.payOrderId||'/'}}<text class="under-line-text"
+								@tap="copy(detail.payOrderId)">复制</text>
+						</view>
+					</view>
+					<view class="">
+						<view class="oc-name">支付状态:</view>{{detail.payStatusName||'/'}}
+					</view>
+				</view>
+			</view>
+
+			<view class="box" v-if="refundDetail.id">
+				<view class="title">用户退款记录</view>
+				<view class="userrefundrecord-container martop">
+					<view class="title1 martop">客户申请</view>
+					<view class="">
+						<view class="oc-name">申请时间:</view>{{refundDetail.createTime||'/'}}
+					</view>
+					<!-- <view class="">退款金额:¥{{refundDetail.createTime}}</view> -->
+					<view class="">
+						<view class="oc-name">用户备注:</view>{{refundDetail.remark||'/'}}
+					</view>
+					<view class="">
+						<view class="oc-name">退款原因:</view>{{refundDetail.reason||'/'}}
+					</view>
+					<view class="title1 martop">退款产品</view>
+					<block v-for="(item,index) in refundDetail.goodsList" :key="item.id">
+						<u--image radius="4" width="130rpx" height="110rpx" :src="item.goodsImgUrl" mode="widthFix"
+							:lazy-lord="true"></u--image>
+						<view class="">{{item.goodsName}} *{{item.totalNumber}}</view>
+						<!-- 	<view class="flex align-center justify-between">
+							<view class="">商品原价:¥{{item.totalNumber}}</view>
+							<view class="">商品卖价:¥{{item.sellPrice}}</view>
+							<view class="">商品总价:¥{{item.totalMoney}}</view>
+						</view> -->
+					</block>
+					<view class="">
+						<view class="oc-name">优惠活动:</view>无
+					</view>
+					<view class="title1 martop">商家处理</view>
+					<view class="">
+						<view class="oc-name">处理结果:</view>{{refundDetail.refundStatusDesc||'/'}}
+					</view>
+				</view>
+			</view>
+		</view>
+
+		<view :hidden="hiddenOrderVideos" class="popup_content">
+			<view class="flex">
+				<video id="myVideo" :src="videoUrl" style="width:100%;height: 372rpx; margin: 10rpx 10rpx;"></video>
+			</view>
+			<view class="flex" style="margin-top: 10rpx;">
+				<view class="marleft">
+					<xbutton size='medium' round='25rpx' padding='0rpx 20rpx' bgColor='#2C6FF3' @tap="playVideo(0)">主视频
+					</xbutton>
+				</view>
+				<view class="marleft">
+					<xbutton size='medium' round='25rpx' padding='0rpx 20rpx' bgColor='#2C6FF3' @tap="playVideo(1)">副视频
+					</xbutton>
+				</view>
+			</view>
+		</view>
+		<view class="popup_overlay" :hidden="hiddenOrderVideos" @click="closeVideoView()"></view>
+
+		<xpopup :show="show" @close="close" @confirm="submit" :showBtn="true" title="拉黑">
+			<!-- 拉黑 -->
+			<view class="pop-content">
+				是否确定拉黑该用户?
+			</view>
+		</xpopup>
+
+		<u-popup :show="refundShow" mode="center" :safeAreaInsetBottom="false" @close="refundShow=false">
+			<view class="refund-container">
+				<u-radio-group placement="row" v-model="radioType" @change="radioChange">
+					<u-radio :customStyle="{marginRight: '24rpx'}" activeColor="#2C6FF3" label="商品退款" name="2">
+					</u-radio>
+					<u-radio activeColor="#2C6FF3" label="金额退款" name="1"></u-radio>
+				</u-radio-group>
+				<view v-if="radioType=='2'">
+					<view class="martop" v-for="(item,index) in orderGoods" :key="item.id">
+						<view class="flex align-center" @tap="checked(item)">
+							<view class="checked">
+								<image v-if="item.checked"
+									src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/commodity/selected.png"
+									mode="widthFix">
+								</image>
+								<image v-else
+									src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/commodity/select.png"
+									mode="widthFix"></image>
+							</view>
+							<view class="">{{item.goodsName}}</view>
+						</view>
+						<view class="flex align-center" style="margin-top: 24rpx;">
+							<u-number-box button-size="30" v-model="item.totalNumber" class='martop'></u-number-box>
+							<view style="margin-left: 30rpx;">
+								¥{{item.totalNumber*item.price}}
+								<!-- 	<u--input placeholder="退款金额" @blur="refundGoodsChange" type="digit" disabled
+									v-model="item.totalMoney" border="surround">
+								</u--input> -->
+							</view>
+						</view>
+					</view>
+				</view>
+				<view class="flex align-center" style="margin-top: 24rpx;" v-else>
+					<view style="width:200rpx">
+						退款金额
+					</view>
+					<view class='marleft' style="width: 100%;">
+						<u--input placeholder="退款金额" @blur="refundGoodsChange" type="digit" v-model="refundMoney"
+							border="surround">
+						</u--input>
+					</view>
+				</view>
+				<view class='' style="margin-top: 24rpx;" @click="actionsheetChange">
+					<u--input clearable readonly suffixIcon="arrow-down" v-model="refundReasonName" border="surround"
+						suffixIconStyle="color: #909399" placeholder="退款理由">
+					</u--input>
+				</view>
+				<view class='' style="margin-top: 24rpx;">
+					<u--textarea v-model="description" placeholder="备注"></u--textarea>
+				</view>
+				<view class="" style="margin-top: 24rpx;" v-if="radioType=='2'">总退款金额:¥{{refundTotalMoney}}</view>
+				<view class="flex align-center justify-end" style="margin-top: 24rpx;">
+					<view class="">
+						<xbutton width='200rpx' @click='refundShow=false'>取消</xbutton>
+					</view>
+					<view class='marleft'>
+						<xbutton width='200rpx' @click="refundSure">确定</xbutton>
+					</view>
+				</view>
+			</view>
+		</u-popup>
+
+		<u-action-sheet :show="actionSheetShow" :actions="actions" :title="title" @close="actionSheetShow = false"
+			@select="actionsheetSelect($event)"></u-action-sheet>
+	</view>
+</template>
+
+<script>
+	import {
+		byId,
+		refundDetail,
+		setBlacklist,
+		refundByMerc
+	} from "@/api/order/order.js"
+	import getDict from "@/utils/getDict.js"
+	export default {
+		data() {
+			return {
+				id: null,
+				hiddenOrderVideos: true,
+				selOrder: undefined,
+				videoUrl: '',
+				detail: {},
+				refundDetail: {},
+				show: false,
+
+				refundShow: false,
+				radioType: '2',
+				remark: '',
+
+				orderGoods: null,
+				createTime: null,
+				description: null,
+				refundReason: null,
+				refundReasonName: null,
+				actions: [],
+				actionSheetShow: false,
+				refundMoney: ''
+			}
+		},
+
+		computed: {
+			refundTotalMoney() {
+				let num = 0;
+				if (this.orderGoods) {
+					this.orderGoods.forEach(i => {
+						if (i.checked) {
+							num += Number(i.totalNumber) * i.price
+						}
+					})
+				}
+				return num
+			}
+		},
+
+		onLoad(o) {
+			this.id = o.id;
+			this.createTime = o.createTime
+		},
+
+		onShow() {
+			this.getbyId()
+			this.getRefundDetail()
+		},
+
+		methods: {
+			copy(text) {
+				uni.setClipboardData({
+					data: text,
+					success: (data) => {
+						uni.showToast({
+							title: '复制成功'
+						})
+					},
+					fail: function(err) {
+
+					},
+					complete: function(res) {
+
+					}
+				})
+			},
+			getbyId() {
+				byId({
+					id: this.id
+				}).then(res => {
+					if (res.code == 200) {
+						this.detail = res.data
+					}
+				})
+			},
+
+			getRefundDetail() {
+				refundDetail({
+					orderId: this.id
+				}).then(res => {
+					if (res.code == 200) {
+						this.refundDetail = res.data
+					}
+				})
+			},
+			showVideoView() {
+
+				this.hiddenOrderVideos = false;
+				var urls = this.detail.video.split(',');
+				this.detail.url0 = urls[0];
+				this.detail.url1 = urls[1];
+				this.playVideo(0);
+			},
+			playVideo(o) {
+				if (0 == o) {
+					this.videoUrl = this.detail.url0;
+				} else {
+					this.videoUrl = this.detail.url1;
+				}
+			},
+			closeVideoView() {
+				this.videoUrl = '';
+				this.hiddenOrderVideos = true;
+			},
+
+			block() {
+				this.show = true
+			},
+
+			// 关闭弹框
+			close(e) {
+				this.show = false
+			},
+
+			// 弹框确定
+			submit() {
+				setBlacklist({
+					memberId: this.detail.memberId
+				}).then(res => {
+					this.$modal.msg('拉黑成功~')
+					this.getbyId()
+				}).catch(err => {
+
+				})
+				this.close()
+			},
+
+			showlogs() {
+				this.$tab.navigateTo('/pages/order/orderLogs?id=' + this.detail.activityId);
+			},
+
+			manualPayment() {
+				this.$tab.navigateTo('/pages/order/orderDel?id=' + this.detail.id + '&deviceId=' + this.detail
+					.deviceId)
+			},
+
+			open() {
+				this.refundShow = true
+				getDict('order_refund_refund_reason').then(res => {
+					let actions = res.map(i => {
+						return {
+							type: i.value,
+							name: i.msg
+						}
+					})
+					this.actions = actions
+				}).catch(err => {
+
+				})
+
+				this.orderGoods = JSON.parse(JSON.stringify(this.detail.orderGoods))
+				this.orderGoods.forEach(i => {
+					i.price = this.$xy.delMoney(i.totalMoney) / i.totalNumber
+					i.totalMoney = this.$xy.delMoney(i.totalMoney)
+				})
+			},
+
+			//退款选项
+			radioChange(e) {},
+
+			//选中商品
+			checked(item) {
+				this.orderGoods.forEach(i => {
+					if (i.id == item.id) {
+						i.checked = !i.checked
+					}
+				})
+				this.orderGoods = JSON.parse(JSON.stringify(this.orderGoods))
+			},
+
+			refundGoodsChange(e) {
+				if (!this.$xy.testPrice(e)) {
+					this.$modal.msg('输入价格最低为分!')
+				}
+			},
+
+			// 退款处理确认按钮
+			refundSure() {
+				let params = {}
+				if (this.radioType == 2) { //订单商品退款
+					let ordersGoods = [];
+					for (var i = 0; i < this.orderGoods.length; i++) {
+						let item = this.orderGoods[i]
+						if (item.checked) {
+							if (!this.$xy.testPrice(item.totalMoney)) {
+								this.$modal.msg('输入价格最低为分!')
+								return
+							}
+							ordersGoods.push({
+								orderGoodsId: item.id,
+								goodsNum: item.totalNumber,
+								price: (item.totalMoney / Number(item.totalNumber)) * 100
+							})
+						}
+					}
+					if (ordersGoods && ordersGoods.length == 0) {
+						this.$modal.msg('请选择退款商品!')
+						return
+					}
+					params = {
+						refundWay: this.radioType,
+						orderId: this.id,
+						createTime: this.createTime,
+						refundGoods: ordersGoods,
+						description: this.description,
+						reason: this.refundReason
+					}
+
+					console.log('params', params)
+
+				} else { //金额退款
+					if (!this.refundMoney) {
+						this.$modal.msg('请选择输入退款金额!')
+						return
+					}
+					params = {
+						refundWay: this.radioType,
+						orderId: this.id,
+						createTime: this.createTime,
+						description: this.description,
+						reason: this.refundReason,
+						refundMoney: this.refundMoney * 100
+					}
+				}
+
+				refundByMerc(params).then(res => {
+					if (res.code == 200) {
+						this.getbyId()
+						this.getRefundDetail()
+						setTimeout(() => {
+							this.$modal.showToast('订单处理成功~')
+						}, 1000)
+					} else {
+						this.$modal.showToast('订单处理失败~')
+					}
+					this.orderGoods = null;
+					this.refundShow = false;
+				})
+			},
+
+			actionsheetChange() {
+				console.log(123)
+				this.actionSheetShow = true;
+			},
+
+			actionsheetSelect(e) {
+				this.refundReason = e.type
+				this.refundReasonName = e.name
+			}
+		}
+	}
+</script>
+
+<style scoped lang="scss">
+	.container {
+		padding: 24rpx;
+		line-height: 50rpx;
+
+		.content {
+			overflow: hidden;
+		}
+
+		.text {
+			color: #2C6FF3;
+		}
+
+		.martop {
+			margin-top: 12rpx;
+		}
+
+		.marleft {
+			margin-left: 10rpx;
+		}
+
+
+
+		.xian {
+			border-bottom: 1px solid #5b5b5b;
+			margin: 20rpx 0;
+		}
+
+		.title {
+			font-size: 32rpx;
+			line-height: 32rpx;
+			font-weight: 800;
+			color: #333;
+		}
+
+		.title1 {
+			font-size: 28rpx;
+			font-weight: 700;
+			color: #333;
+			line-height: 50rpx;
+			position: relative;
+			padding-left: 16rpx;
+
+			&::before {
+				content: '';
+				width: 5rpx;
+				height: 24rpx;
+				background: #2C6FF3;
+				position: absolute;
+				left: 4rpx;
+				top: 50%;
+				transform: translateY(-60%);
+			}
+		}
+
+		.under-line-text {
+			font-size: 26rpx !important;
+
+			font-weight: 500;
+			font-style: italic;
+			text-decoration: underline;
+			color: #2C6FF3 !important;
+			margin-left: 24rpx;
+			background-color: #fff !important;
+		}
+
+		.oc-name {
+			display: inline-block;
+			width: 170rpx;
+		}
+
+		.payments-container,
+		.userrefundrecord-container,
+		.orderinformation-container {
+			background-color: #fff;
+			color: #777777;
+			padding: 20rpx 0;
+			border-radius: 15rpx;
+		}
+
+		.orderinformation-container {
+			.phone {
+				>text {
+					display: inline-block;
+					background-color: #5b5b5b;
+					color: #fff;
+					font-size: 22rpx;
+					padding: 0 12rpx;
+					border-radius: 6rpx;
+					margin-left: 12rpx;
+					line-height: 36rpx;
+				}
+			}
+
+			.block {
+				width: 92rpx;
+				height: 38rpx;
+				background: #F4F8FF;
+				border-radius: 8rpx;
+				font-size: 26rpx;
+				line-height: 38rpx;
+				font-weight: 500;
+				color: #2C6FF3;
+				text-align: center;
+			}
+		}
+
+		.box {
+			background-color: #fff;
+			padding: 28rpx 25rpx;
+			border-radius: 14rpx;
+			margin-top: 20rpx;
+
+			.goods-container {
+				background-color: #fff;
+				border-radius: 15rpx;
+				margin-top: 35rpx;
+
+				.goods-item {
+					background: #F6F7FA;
+					border-radius: 14rpx;
+					padding: 8rpx;
+					margin-top: 20rpx;
+
+					.goods-msg {
+						margin-left: 27rpx;
+
+						.goods-name {
+							font-size: 26rpx;
+							font-weight: 800;
+							color: #333333;
+						}
+
+						.goods-price {
+							font-size: 24rpx;
+							font-weight: 500;
+							color: #555555;
+						}
+					}
+				}
+
+				.spxx-image {
+					height: 130rpx;
+				}
+			}
+		}
+	}
+
+	.popup_overlay {
+		position: fixed;
+		top: 0%;
+		left: 0%;
+		width: 100%;
+		height: 100%;
+		background-color: black;
+		z-index: 1001;
+		-moz-opacity: 0.8;
+		opacity: .80;
+		filter: alpha(opacity=88);
+	}
+
+	.popup_content {
+		position: fixed;
+		top: 50%;
+		left: 50%;
+		width: 700rpx;
+		height: 500rpx;
+		margin-left: -350rpx;
+		margin-top: -250rpx;
+		border: 10px solid white;
+		background-color: white;
+		z-index: 1002;
+		overflow: auto;
+	}
+
+	.pop-content {
+		padding: 24rpx;
+	}
+
+	.refund-container {
+		background-color: #fff;
+		width: 600rpx;
+		padding: 20rpx 30rpx;
+
+		.checked {
+			width: 36rpx;
+			height: 36rpx;
+			margin-right: 12rpx;
+
+			image {
+				width: 36rpx;
+				height: 36rpx;
+			}
+		}
+	}
+</style>

+ 56 - 0
pages/order/orderLogs.vue

@@ -0,0 +1,56 @@
+<template>
+	<view class="container">
+		<u-navbar leftIconColor="#fff" titleStyle="color:#fff;fontSize:36rpx;" :autoBack="true" bgColor="#2C6FF3"
+			:placeholder="true" title="交易日志"></u-navbar>
+		<scroll-view   scroll-y="true" scroll-with-animation="true"
+			lower-threshold="100" @scrolltolower="loadMore">
+			<view class="" v-for="(item,index) in logList" :key="item.id">
+				<view class="flex">
+					<view class="" style="min-width: 200rpx;color: gray;">{{ item.activityTime.substring(5)}}</view>
+					<view class="">{{item.msg}}</view>			
+				</view>
+
+			</view>
+		</scroll-view>
+	</view>
+</template>
+
+<script>
+	import {
+		byId,
+		orderLogs,
+		refundDetail
+	} from "@/api/order/order.js"
+	export default {
+		data() {
+			return {
+				activityId: null,
+				logList:[]
+			}
+		},
+		onLoad(o) {
+			this.activityId = o.id;
+			this.queryLogs()
+		},
+		methods: {
+			queryLogs(){
+				orderLogs({
+					activityId:this.activityId
+				}).then(res => {
+					this.logList = res.data;
+				})
+			}
+		},
+	}
+</script>
+
+<style scoped lang="scss">
+	.container {
+		padding: 24rpx;
+		line-height: 50rpx;
+		.text {
+			color: #2C6FF3;
+		}
+	}
+</style>
+

+ 888 - 0
pages/order/orderQuery.vue

@@ -0,0 +1,888 @@
+<template>
+	<view class="container">
+		<u-navbar leftIconColor="#fff" titleStyle="color:#fff;fontSize:36rpx;" :autoBack="true" bgColor="#2C6FF3"
+			:placeholder="true" title="订单查询"></u-navbar>
+		<view class="time-choose">
+			<view class="flex align-center justify-between top">
+				<view class="con-btn" @tap="thedaybefore()">
+					前一天 </view>
+				<view class="flex align-center justify-between date" @tap="timeShow=true">
+					<view class="">{{searchQuery.orderDate}}</view>
+					<image src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/date.png" mode="widthFix">
+					</image>
+				</view>
+				<view class="con-btn" @tap="thenextday()">后一天</view>
+			</view>
+		</view>
+		<view class="search">
+			<u-search placeholder='输入机器编号或名称搜索' v-model="searchQuery.deviceSearch" :showAction="false" @search="search">
+			</u-search>
+		</view>
+		
+		<view class="flex align-center justify-between screen-container">
+					<view>{{Number(orderCount.orderNum)}}个订单,合计:¥{{Number(orderCount.orderTotalAmount/100)}}</view>
+					<view class="flex align-center">
+						<view class="show-zero flex justify-end align-center">
+							<view>零元单:</view>
+							<view>
+								<u-switch activeColor="#2C6FF3" size="14" v-model="searchQuery.showZero" @change="showZeroChange"></u-switch>
+							</view>
+						</view>
+						<view class="flex align-center justify-center" @tap="screen">
+							<view class="" style="font-size: 28rpx;font-weight: 500;color: #333333;">筛选</view>
+							<image src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/screen.png"
+								style="width: 32rpx;height: 32rpx;margin-left: 12rpx;" mode="widthFix"></image>
+						</view>
+					</view>
+				</view>
+		
+		
+
+		<view class="tab-wrap">
+			<view class="tab">
+				<u-tabs :list="tabList" :activeStyle="{color: '#333',fontWeight: 'bold',fontSize:'36rpx'}"
+					:inactiveStyle="{fontSize:'32rpx'}" :scrollable="false" :current="current" @click="tabClick"
+					lineColor="#2C6FF3">
+				</u-tabs>
+			</view>
+		</view>
+
+		<scroll-view class="scrollview" :scroll-with-animation="true" scroll-y lower-threshold="100"
+			@scrolltolower="scrolltolower" :style="{height:fullHeight}">
+			<view v-if="orderList.length>0">
+				<block v-for="(item,index) in orderList" :key="item.id">
+					<view class="equipment-container" @tap="details(item)">
+						<view class="flex align-center justify-between">
+							<view class="title" v-if="item.deviceName">
+								{{item.deviceName}}<text>({{item.deviceId}})</text>
+							</view>
+							<view class="title" v-else>{{item.deviceId}}</view>
+							<view>
+								<u-icon name="arrow-right" size="14"></u-icon>
+							</view>
+						</view>
+
+						<view class="order-detail-item">
+							<view>订单号:</view>{{item.id}}
+						</view>
+
+						<view class="order-detail-item">
+							<view>支付方式:</view>{{$xy.getPayType(item.payType)}}
+						</view>
+
+						<view class="order-detail-item">
+							<view>设备类型:</view>{{item.deviceType||'/'}}
+						</view>
+
+						<view class="order-detail-item">
+							<view>时间:</view>{{item.createTime}}
+						</view>
+						
+						<view class="order-detail-item" v-if="$xy.delMoney(item.payMoney)==0">
+							<view>零元单提示:</view><view style="color: red;">未拿商品</view>
+						</view>
+
+						<view class="goods-container sb-box" v-for="(item1,index1) in item.orderGoods" :key="item1.id">
+							<view class="flex align-center justify-center image-container">
+								<u--image radius="4" width="130rpx" height="130rpx" :src="item1.goodsImgUrl"
+									mode="aspectFit" :lazy-lord="true"></u--image>
+							</view>
+							<view class="details-container">
+								<view class="flex align-center goods-name-num justify-between">
+									<view class="goods-name">{{item1.goodsName}}</view>
+									<view class="goods-num">×{{item1.totalNumber}}</view>
+								</view>
+								<view class="gp-item">
+									单价:¥{{(Number(item1.totalMoney)/Number(item1.totalNumber))/100}}
+								</view>
+								<view class="gp-item" v-if="item1.refundMoney||item1.refundMoney!=0">
+									已退款:¥{{$xy.delMoney(item1.refundMoney)}}
+								</view>
+								<view class="goods-price flex">
+									<!-- <view class="gp-item">
+										应收:¥{{(Number(item1.totalMoney)-Number(item1.discountMoney))/100}}
+									</view> -->
+									<!-- <view class="gp-item">
+										实收:¥{{item1.totalMoney-item1.discountMoney}}
+									</view> -->
+								</view>
+								<!-- <view class="good-act">优惠活动:无</view> -->
+							</view>
+						</view>
+						<view class="martop" style="text-align: right;">共计{{Number(item.goodsNumber)}}件,实付款<text
+								style="color: red;font-weight: bold;">¥{{$xy.delMoney(item.orderTotalMoney)}}</text></view>
+					</view>
+				</block>
+				<view class="load-more" style="padding:24rpx;">
+					<u-loadmore v-if="orderList.length>0" :status="loadmoreStatus" />
+				</view>
+			</view>
+			<view v-else class='empty'>
+				<u-empty mode="data" text="数据为空"></u-empty>
+			</view>
+		</scroll-view>
+
+		<xpopup :show="screenShow" @close="close" @confirm="sure" :showBtn="true" title="筛选">
+			<view class="popup-container">
+				<view class='martop'>
+					<u--input placeholder="订单编号" v-model="searchQuery.id" border="surround"></u--input>
+				</view>
+				<view class='martop'>
+					<u--input placeholder="手机号码" v-model="searchQuery.phone" border="surround"></u--input>
+				</view>
+				<view class='martop'>
+					<u--input placeholder="支付订单号" v-model="searchQuery.payOrderNo" border="surround"></u--input>
+				</view>
+				<view class='martop' @click="actionsheetChange('sblx')">
+					<u--input clearable readonly suffixIcon="arrow-down" v-model="showQuery.deviceType"
+						suffixIconStyle="color: #909399" placeholder="设备类型" border="surround"></u--input>
+				</view>
+				<view class='martop' @click="actionsheetChange('xzqy')">
+					<u--input clearable readonly suffixIcon="arrow-down" v-model="searchQuery.regionName"
+						border="surround" suffixIconStyle="color: #909399" placeholder="行政区域">
+					</u--input>
+				</view>
+				<view class='martop' @click="actionsheetChange('lx')">
+					<u--input clearable readonly suffixIcon="arrow-down" v-model="showQuery.merLineId" border="surround"
+						suffixIconStyle="color: #909399" placeholder="路线"></u--input>
+				</view>
+				<!-- 	<view class='martop' @click="actionsheetChange('ddzt')">
+					<u--input clearable readonly suffixIcon="arrow-down" v-model="showQuery.payStatus" border="surround"
+						suffixIconStyle="color: #909399" placeholder="订单状态">
+					</u--input>
+				</view> -->
+				<view class='martop' @click="actionsheetChange('yczt')">
+					<u--input clearable readonly suffixIcon="arrow-down" v-model="showQuery.riskType" border="surround"
+						suffixIconStyle="color: #909399" placeholder="异常状态">
+					</u--input>
+				</view>
+				<!-- <view class='martop'>
+
+					<u--input clearable suffixIcon="arrow-down" v-model="searchQuery.regionName" border="surround"
+						suffixIconStyle="color: #909399" placeholder="拉黑操作" @focus="actionsheetChange('lhcz')">
+					</u--input>
+				</view>
+				<view class='martop'>
+					<u--input clearable suffixIcon="arrow-down" v-model="searchQuery.regionName" border="surround"
+						suffixIconStyle="color: #909399" placeholder="促销活动" @focus="actionsheetChange('cxhd')">
+					</u--input>
+				</view> -->
+			</view>
+		</xpopup>
+		<u-action-sheet :show="actionSheetShow" :actions="actions" :title="title" @close="actionSheetShow = false"
+			@select="actionsheetSelect($event)"></u-action-sheet>
+
+		<!-- 区域选择弹框 -->
+		<xpopup :show="areaShow" @close="areaClose" :showBtn="false" title="选择区域">
+			<!-- 类目选择 -->
+			<scroll-view style="height: 600rpx;" scroll-y scroll-with-animation>
+				<view class="popup-content">
+					<tki-tree style="width:100%;" :range="areaList" :foldAll="false" rangeKey="name" idKey="name"
+						buttonName="选中" @btnClick="areaSubmit">
+					</tki-tree>
+				</view>
+			</scroll-view>
+		</xpopup>
+
+		<!-- 时间选择 -->
+		<u-datetime-picker :show="timeShow" :closeOnClickOverlay="true" @close="timeShow=false" @confirm="timeSubmit"
+			@cancel="timeShow=false" v-model="timeStamp" mode="date"></u-datetime-picker>
+	</view>
+</template>
+
+<script>
+	import {
+		orderPage,
+		orderPageCount,
+		page
+	} from "@/api/order/order.js"
+	import {
+		areaTree
+	} from "@/api/point/area"
+	import {
+		linePage,
+	} from "@/api/point/line"
+
+	export default {
+		data() {
+			return {
+				keyword: '',
+				orderListlength: '',
+				orderList: [],
+				orderCount: {},
+				loadmoreStatus: 'loadmore',
+				isEmpty: false,
+				page: 1, //当前分页
+				size: 10, //分页数据条数
+				mode: 'single',
+				screenShow: false,
+				actionSheetShow: false,
+				currentDate: '',
+				nextDate: '',
+				preDate: '',
+				date: '',
+				actions: [],
+				title: '',
+				zeroOrderName: true,
+				zeroOrderList: [{
+					type: false,
+					name: '否'
+				}, {
+					type: true,
+					name: '是'
+				}],
+				equipmentTypename: '',
+				equipmentTypeList: [{
+					type: '',
+					name: '全部'
+				}, {
+					type: 1,
+					name: '开门柜'
+				}, {
+					type: 2,
+					name: '重力柜'
+				}],
+				administrativeDivisionname: '',
+				administrativeDivisionList: [{
+					type: 'xzqy',
+					name: '全部'
+				}, {
+					type: 'xzqy',
+					name: '省市区三级联动'
+				}],
+				routename: '',
+				routeList: [{
+					type: 'lx',
+					name: '全部'
+				}, {
+					type: 'lx',
+					name: '路线1'
+				}, {
+					type: 'lx',
+					name: '路线2'
+				}],
+				orderStatusname: '',
+				orderStatusList: [{
+						type: '',
+						name: '全部'
+					}, {
+						type: 1,
+						name: '交易中'
+					}, {
+						type: 2,
+						name: '交易异常'
+					},
+					{
+						type: 3,
+						name: '交易取消'
+					},
+					{
+						type: 4,
+						name: '交易完成'
+					},
+					{
+						type: 5,
+						name: '交易关闭'
+					},
+					{
+						type: 6,
+						name: '下次补单'
+					},
+					{
+						type: 7,
+						name: '支付中'
+					}
+				],
+				abnormalStatename: '',
+				abnormalStateList: [{
+						type: '',
+						name: '全部'
+					}, {
+						type: 2,
+						name: '发起识别失败'
+					}, {
+						type: 3,
+						name: '未拿商品'
+					}, {
+						type: 4,
+						name: '无法识别'
+					},
+					{
+						type: 5,
+						name: '故意遮挡'
+					}, {
+						type: 6,
+						name: '柜中无此sku'
+					}, {
+						type: 9,
+						name: '异物拿放'
+					}, {
+						type: 10,
+						name: '疑似设备故障'
+					},
+					{
+						type: 11,
+						name: '支付失败'
+					}, {
+						type: 12,
+						name: '超时'
+					},
+					{
+						type: 8,
+						name: '其他告警'
+					}
+				],
+				pullBlackOperationname: '',
+				pullBlackOperationList: [{
+					type: 'lhcz',
+					name: '全部'
+				}, {
+					type: 'lhcz',
+					name: '是'
+				}, {
+					type: 'lhcz',
+					name: '否'
+				}],
+				salesPromotionname: '',
+				salesPromotionList: [{
+					type: 'cxhd',
+					name: '全部'
+				}, {
+					type: 'cxhd',
+					name: '双十一活动'
+				}],
+
+				searchQuery: {
+					orderDate: '', //日期
+					deviceSearch: '', //搜索关键字
+					id: '', //订单编号
+					phone: '', //手机号码
+					payOrderNo: '', //支付单号
+					deviceType: '', //设备类型
+					regionName: '', //区域
+					merLineId: '', //线路
+					payStatus: '', //订单状态
+					riskType: '', //异常订单类型
+					showZero: false, //零元单
+				},
+
+				showQuery: {
+					deviceType: '',
+					merLineId: '',
+					payStatus: '',
+					riskType: '',
+					showZero: '',
+				},
+
+				pickerType: null,
+				minDate: null,
+
+				// 编辑弹框
+				areaShow: false,
+				areaList: [], //区域数据
+
+				timeShow: false, //时间选择弹框
+				timeStamp: new Date(), //时间picker显示时间
+
+				noLine: false, //是否有线路
+				dayNum: 0, //日期
+
+				fullHeight: 0,
+
+				tabList: [{
+						name: '全部'
+					},
+					{
+						name: '未支付'
+					}
+				],
+				current: 0,
+			}
+		},
+		onLoad() {
+			let _this = this;
+			const query = uni.createSelectorQuery().in(this);
+			query.select(".scrollview").boundingClientRect((data) => {
+				uni.getSystemInfo({
+					success(res) {
+						// 针对iPhone X等机型底部安全距离做适配
+						const model = res.model;
+						const modelInclude = [
+							"iPhone X",
+							'iPhone XR',
+							"iPhone XS",
+							"iPhone XS MAX",
+							"iPhone 12/13 mini",
+							"iPhone 12/13 (Pro)",
+							"iPhone 12/13 Pro Max",
+							"iPhone 14 Pro Max"
+						];
+						let safeDistance = modelInclude.includes(model)
+						//动态设置商品区域高度
+						console.log(res.windowHeight, data.top)
+						if (safeDistance) {
+							_this.fullHeight = res.windowHeight - data.top - 40 + 'px';
+						} else {
+							_this.fullHeight = res.windowHeight - data.top + 'px';
+						}
+					},
+				});
+			}).exec();
+
+			this.currentDate = new Date();
+			this.minDate = '2023-03-10'
+			this.searchQuery.orderDate = uni.$u.timeFormat(this.currentDate, 'yyyy-mm-dd')
+			this.getpage()
+			//改为分页接口获取 this.getCountData()
+		},
+		methods: {
+			// 时间选择
+			timeSubmit(e) {
+				this.searchQuery.orderDate = uni.$u.timeFormat(e.value, 'yyyy-mm-dd')
+				this.timeShow = false
+				this.reset()
+				this.getpage()
+				//改为分页接口获取 this.getCountData()
+			},
+
+			tabClick(e) {
+				this.current = e.index
+				if (this.current == 0) {
+					this.searchQuery.payStatus = ''
+				} else {
+					this.searchQuery.payStatus = 1
+				}
+				this.reset()
+				this.getpage()
+			},
+
+			//获取区域树
+			getAreaTree() {
+				areaTree().then(res => {
+					this.areaList = res.data
+				})
+			},
+
+			areaClose() {
+				this.areaShow = false
+			},
+
+			//区域选择提交
+			areaSubmit(res) {
+				this.searchQuery.regionName = res.name;
+				this.searchQuery.merLineId = null;
+				this.showQuery.merLineId = null;
+				this.getLineOption(res.name)
+				this.areaClose()
+			},
+
+			//获取线路options
+			getLineOption(regionName) {
+				linePage({
+					page: {
+						current: 1,
+						size: 1000,
+					},
+					regionName: regionName
+				}).then(res => {
+					let data = res.data.records;
+					if (data.length > 0) {
+						this.noLine = false;
+						let newData = data.map(i => {
+							return {
+								type: i.id,
+								name: i.lineName
+							}
+						})
+						this.actions = newData;
+					} else {
+						this.noLine = true; //没有线路
+						this.actions = [];
+					}
+				})
+			},
+
+			actionsheetChange(type) {
+				this.pickerType = type;
+				if (type == 'sblx') {
+					this.actions = this.equipmentTypeList
+					this.title = '请选择设备类型'
+					this.actionSheetShow = true
+				}
+				if (type == 'xzqy') {
+					this.areaShow = true;
+				}
+				if (type == 'lx') {
+					if (this.searchQuery.regionName) {
+						if (!this.noLine) {
+							this.title = '请选择路线'
+							this.actionSheetShow = true
+						} else {
+							this.$modal.msg('当前区域未建立线路~')
+						}
+					} else {
+						this.$modal.msg('请先选择区域~')
+					}
+				}
+				if (type == 'ddzt') {
+					this.actions = this.orderStatusList
+					this.title = '请选择订单状态'
+					this.actionSheetShow = true
+				}
+				if (type == 'yczt') {
+					this.actions = this.abnormalStateList
+					this.title = '请选择异常状态'
+					this.actionSheetShow = true
+				}
+				if (type == 'lhcz') {
+					this.actions = this.pullBlackOperationList
+					this.title = '请选择拉黑操作'
+					this.actionSheetShow = true
+				}
+				if (type == 'cxhd') {
+					this.actions = this.salesPromotionList
+					this.title = '请选择促销活动'
+					this.actionSheetShow = true
+				}
+			},
+			details(item) {
+				this.$tab.navigateTo(`/pages/order/orderDetails?id=${item.id}&createTime=${item.createTime}`)
+			},
+			actionsheetSelect(e) {
+				switch (this.pickerType) {
+					case 'sblx':
+						this.searchQuery.deviceType = e.type
+						this.showQuery.deviceType = e.name
+						break;
+					case 'xzqy':
+						this.searchQuery.regionName = e.name
+						break;
+					case 'lx':
+						this.searchQuery.merLineId = e.type
+						this.showQuery.merLineId = e.name
+						break;
+					case 'ddzt':
+						this.searchQuery.payStatus = e.type
+						this.showQuery.payStatus = e.name
+						break;
+					case 'yczt':
+						this.searchQuery.riskType = e.type
+						this.showQuery.riskType = e.name
+						break;
+						// case 'lhcz':
+						// 	this.pullBlackOperationname = e.name
+						// 	break;
+						// case 'cxhd':
+						// 	this.salesPromotionname = e.name
+						// 	break;
+					default:
+						break;
+				}
+			},
+			close() {
+				this.screenShow = false
+			},
+			confirm(e) {
+				this.searchQuery.orderDate = e[0]
+				this.show = false
+				this.search()
+				//改为分页接口获取 this.getCountData()
+			},
+			
+			//是否展示零元单
+			showZeroChange(){
+				this.search()
+			},
+			
+			thedaybefore() {
+				this.dayNum--
+				this.preDate = new Date(this.currentDate.getTime() + this.dayNum * (24 * 60 * 60 * 1000)); //前一天
+				this.searchQuery.orderDate = uni.$u.timeFormat(this.preDate, 'yyyy-mm-dd')
+				//改为分页接口获取 this.getCountData()
+				this.search()
+			},
+			thenextday() {
+				this.dayNum++
+				this.nextDate = new Date(this.currentDate.getTime() + this.dayNum * (24 * 60 * 60 * 1000)); //后一天
+				this.searchQuery.orderDate = uni.$u.timeFormat(this.nextDate, 'yyyy-mm-dd')
+				//改为分页接口获取 this.getCountData()
+				this.search()
+			},
+
+			//点击筛选
+			screen() {
+				this.screenShow = true
+				this.getAreaTree()
+
+			},
+			//统计数据
+			// getCountData() {
+			// 	let params = {
+			// 		orderDate: this.searchQuery.orderDate
+			// 	}
+			// 	Object.assign(params)
+			// 	orderPageCount(params).then(res => {
+			// 		let data = res.data;
+			//
+			// 		this.orderCount = data
+			// 	})
+			// },
+			//获取订单列表
+			getpage() {
+				let params = {
+					page: {
+						current: this.page,
+						size: this.size
+					}
+				}
+				if (this.current == 0) {
+					Object.assign(params, this.searchQuery)
+				} else {
+					let obj = JSON.parse(JSON.stringify(this.searchQuery))
+					obj.orderDate = ''
+
+					Object.assign(params, obj)
+				}
+				page(params).then(res => {
+					let data = res.data.records;
+					if (data.length < 10) {
+						this.loadmoreStatus = "nomore"
+					} else {
+						this.loadmoreStatus = "loadmore"
+					}
+					this.orderCount.orderNum = res.data.orderNum;
+					this.orderCount.orderTotalAmount = res.data.orderTotalAmount;
+					this.orderList = this.orderList.concat(data)
+				})
+			},
+
+			// 搜索
+			search() {
+				this.reset()
+				this.getpage()
+			},
+
+			// 重置数据
+			reset() {
+				this.loadmoreStatus == 'loadmore'
+				this.page = 1;
+				this.size = 10;
+				this.orderList = [];
+			},
+
+			// 触底加载
+			scrolltolower() {
+				if (this.loadmoreStatus == 'nomore') return
+				this.page++
+				this.getpage()
+			},
+
+			sure() {
+				this.reset()
+				this.getpage()
+				this.screenShow = false;
+			}
+		}
+	}
+</script>
+<style scoped lang="scss">
+	.container {
+		.empty {
+			margin-top: 40%;
+		}
+
+		.martop {
+			margin-top: 20rpx;
+		}
+
+		.search {
+			padding: 24rpx 13rpx;
+			background-color: #fff;
+		}
+		
+		.show-zero{
+			background-color: #fff;
+			color: #777;
+			margin-right: 18rpx;
+		}
+
+		.tab-wrap {
+			background-color: #fff;
+
+			.tab {
+				// width: 40%;
+			}
+		}
+
+		.marleft {
+			margin-left: 10rpx;
+		}
+
+		.scrollview {
+			overflow: hidden;
+		}
+
+		.time-choose {
+			background-color: #fff;
+
+			.top {
+				height: 80rpx;
+				color: #fff;
+				padding: 0 20rpx;
+				background-color: #2C6FF3;
+				border-radius: 0px 0px 50rpx 50rpx;
+
+				.con-btn {
+					padding: 0 36rpx;
+				}
+
+				.date {
+					height: 50rpx;
+					width: 300rpx;
+					border-radius: 10rpx;
+					background-color: #fff;
+					color: #000;
+					padding: 0 20rpx;
+
+					image {
+						width: 30rpx;
+						height: 30rpx;
+					}
+				}
+
+
+			}
+		}
+
+		.screen-container {
+			background-color: #fff;
+			padding: 0 13rpx;
+
+			>view:nth-child(1) {
+				font-size: 28rpx;
+				font-weight: 500;
+				color: #777777;
+			}
+		}
+
+		.equipment-container {
+			margin: 13rpx 13rpx 0;
+			padding: 12rpx 20rpx 24rpx;
+			border-radius: 14rpx;
+			background-color: #fff;
+			box-shadow: 0px 0px 10rpx 0px rgba(174, 201, 255, 0.2);
+
+			.sb-box {
+				padding: 24rpx 18rpx;
+				background-color: #f5f8fb;
+				border-radius: 8rpx;
+				margin-top: 12rpx;
+			}
+
+			.title {
+				height: 60rpx;
+				line-height: 60rpx;
+				font-size: 32rpx;
+				font-weight: bold;
+				color: #333;
+
+				>text {
+					font-size: 24rpx;
+					color: #333;
+				}
+			}
+
+			.goods-container {
+				height: 154rpx;
+				margin-top: 14rpx;
+				padding: 12rpx 12rpx 12rpx 164rpx;
+				box-sizing: border-box;
+				position: relative;
+
+				.image-container {
+					height: 130rpx;
+					width: 130rpx;
+					position: absolute;
+					left: 12rpx;
+					top: 50%;
+					transform: translateY(-50%);
+				}
+
+				.details-container {
+					position: relative;
+					padding: 12rpx;
+
+					.goods-name-num {
+						.goods-name {
+							font-size: 26rpx;
+							color: #333;
+							font-weight: bold;
+						}
+
+						.goods-num {
+							font-size: 26rpx;
+							color: red;
+							margin-right: 40rpx;
+							font-weight: bold;
+						}
+					}
+
+					.goods-price {
+						font-size: 28rpx;
+					}
+
+					.good-act {
+						margin-top: 12rpx;
+					}
+
+					.gp-item {
+						width: 50%;
+						margin-top: 12rpx;
+					}
+
+					.goodf-act {
+						font-size: 28rpx;
+						color: #333;
+						margin-top: 12rpx;
+					}
+
+					.goods-btn {
+						margin-top: 12rpx;
+					}
+
+					.refund {
+						position: absolute;
+						right: 0;
+						top: 0;
+						background-color: red;
+						color: #fff;
+						border-radius: 4rpx;
+						padding: 0 12rpx;
+						line-height: 40rpx;
+						font-size: 24rpx;
+					}
+				}
+
+			}
+
+			.order-detail-item {
+				font-size: 28rpx;
+				margin-top: 12rpx;
+				color: #777;
+
+				>view {
+					display: inline-block;
+					width: 170rpx;
+				}
+			}
+		}
+
+		.popup-container {
+			padding: 20rpx;
+		}
+
+		.popup-content {
+			padding: 0 24rpx;
+		}
+	}
+</style>

+ 854 - 0
pages/order/refundList.vue

@@ -0,0 +1,854 @@
+<template>
+	<view class="container">
+		<u-navbar leftIconColor="#fff" titleStyle="color:#fff;fontSize:36rpx;" :autoBack="true" bgColor="#2C6FF3"
+			:placeholder="true" title="退款列表"></u-navbar>
+		<view class="header-container">
+			<view class="search">
+				<u-search animation placeholder="请输入订单编号,设备号,用户手机号" :clearabled="false" v-model="keyword"
+					:showAction="false" @search="search"></u-search>
+				<view class="scan-icon" @click="scan">
+					<u-icon name="scan" size="22" color="#909399"></u-icon>
+				</view>
+			</view>
+
+			<view class="flex align-center justify-between" style="margin-top: 28rpx;">
+				<view class="exceptiontype-container" @tap="actionSheetShow=true">
+					{{orderStatus.name?orderStatus.name:'选择状态'}}
+				</view>
+
+				<view class="flex align-center  date-container">
+					<view class="" style="margin-right: 10rpx;" @tap="dateSelect('start')">
+						{{dateStart?dateStart:'开始日期'}}
+					</view>
+					<view class="" style="margin-right: 10rpx;">至</view>
+					<view class="" @tap="dateSelect('end')">{{dateend?dateend:'结束日期'}}</view>
+				</view>
+
+				<view>
+					<view class="change-month" @tap="changeMonth(-1)">
+						上一月
+					</view>
+					<view class="change-month" style="margin-left: 20rpx;" @tap="changeMonth(1)">
+						下一月
+					</view>
+				</view>
+			</view>
+		</view>
+		<scroll-view @scrolltolower="loadMore" class="scrollview" :scroll-with-animation="true" scroll-y
+			lower-threshold="100" :style="{height:fullHeight}">
+			<view class="order-content" v-if="list&&list.length>0">
+				<block v-for="(item,index) in list" :key="item.id">
+					<view class="scrollview-container">
+						<view class="flex align-center order-top">
+							<view class="">订单编号:{{item.orderId}}</view>
+							<view class="refund-status">
+								<view class="com-status" style="color: #FF1E1E;" v-if="item.refundStatus==1">待处理</view>
+								<view class="com-status" style="color: #aaff00;" v-if="item.refundStatus==2">退款中</view>
+								<view class="com-status" style="color: #bdbdbd;" v-if="item.refundStatus==3">已拒绝</view>
+								<view class="com-status" style="color: #22ca08;" v-if="item.refundStatus==4">已退款</view>
+								<view class="com-status" style="color: #ff5500;" v-if="item.refundStatus==5">退款失败</view>
+							</view>
+						</view>
+						<view class="ddxx-box">
+							<!-- <view class="flex align-center justify-between">
+								<view class="flex align-center">
+									<view class="flex align-end" v-for="(item1,index1) in item.orderRefundGoods"
+										:key="item1.id">
+										<u--image radius="4" width="130rpx" height="130rpx" :src="item1.cover" mode="widthFix"
+											:lazy-lord="true"></u--image>
+										<view class="">×{{item1.totalNumber}}</view>
+									</view>
+								</view>
+							</view> -->
+							<view class="flex align-center con-item">
+								<view class="oc-name">设备id:</view>{{item.deviceId}}
+							</view>
+							<view class="flex align-center con-item martop12">
+								<view class="oc-name">申请时间:</view>{{item.createTime}}
+							</view>
+							<view class="flex align-center con-item martop12">
+								<view class="oc-name">退款原因:</view>{{item.reason}}
+							</view>
+							<view class="flex align-center con-item martop12">
+								<view class="oc-name">商品及说明:</view>{{item.description||'/'}}
+							</view>
+							<view class="flex align-center martop12">
+								<view class="oc-name">联系电话:</view>{{item.memberTel}}
+								<view class="phone"
+									@click="$tab.navigateTo(`/pages/order/userInfo?id=${item.memberTel}`)">
+									会员信息
+								</view>
+							</view>
+
+							<!-- 	<view class="flex align-center con-item">
+								线路:{{item.placeName}}
+							</view> -->
+							<view class="flex align-center justify-between con-item martop12">
+								<view class="flex align-center ">
+									<view class="oc-name">订单金额:</view>¥{{(Number(item.refundTotal)/100).toFixed(2)}}
+								</view>
+
+								<view class="refundamount-text marleft"
+									v-if="item.refundStatus==2||item.refundStatus==4||item.refundStatus==5">
+									退款金额:¥{{(Number(item.refundMoney)/100).toFixed(2)}}
+								</view>
+							</view>
+							<!-- <view class="flex align-end martop">
+								<view class="image-box" v-for="(item2,index2) in item.imgUrls" :key="index2">
+									<u--image radius="4" width="160rpx" height="160rpx" :src="item2" mode="widthFix"
+										:lazy-lord="true"></u--image>
+								</view>
+							</view> -->
+
+							<view class="goods-container" v-for="(item1,index1) in item.orderRefundGoods"
+								:key="item1.id">
+								<view class="flex align-center justify-center image-container">
+									<u--image radius="4" width="130rpx" height="130rpx" :src="item1.cover"
+										mode="aspectFit" :lazy-lord="true"></u--image>
+								</view>
+								<view class="details-container">
+									<view class="flex align-center goods-name-num justify-between">
+										<view class="goods-name">{{item1.goodsName}}</view>
+										<view class="goods-num">×{{item1.totalNumber}}</view>
+									</view>
+									<view class="gp-item">
+										单价:¥{{(Number(item1.totalMoney)/Number(item1.totalNumber))/100}}
+									</view>
+								</view>
+							</view>
+						</view>
+						<view class="flex  align-center justify-end" style="padding-top:12rpx;">
+							<view class="">
+								<xbutton size='medium' round='25rpx' padding='0rpx 20rpx' bgColor='#43cf7c'
+									width='140rpx' @tap="showVideoView(item)">查看视频</xbutton>
+							</view>
+							<view style="margin-left: 24rpx;"
+								v-if="item.refundStatus==1||item.refundStatus==3||item.refundStatus==5">
+								<xbutton size='medium' round='25rpx' padding='0rpx 20rpx' width="150rpx"
+									@click='refund(item)'>
+									退款</xbutton>
+							</view>
+						</view>
+
+					</view>
+				</block>
+				<u-loadmore :status="loadmoreStatus" />
+			</view>
+
+			<view v-else class='empty'>
+				<u-empty mode="data" text="数据为空"></u-empty>
+			</view>
+		</scroll-view>
+
+		<u-popup :show="refundShow" mode="center" :safeAreaInsetBottom="false" @close="refundShow=false" @open="open">
+			<view class="refund-container">
+				<u-radio-group placement="row" v-model="radioType" @change="radioChange">
+					<u-radio :customStyle="{marginRight: '24rpx'}" activeColor="#2C6FF3" label="系统退款" name="2">
+					</u-radio>
+					<u-radio activeColor="#2C6FF3" label="拒绝退款" name="3"></u-radio>
+				</u-radio-group>
+				<view v-if="radioType=='2'">
+					<view class="martop" v-for="(item,index) in orderGoods" :key="item.id">
+						<view class="flex align-center" @tap="checked(item)">
+							<view class="checked">
+								<image v-if="item.checked"
+									src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/commodity/selected.png"
+									mode="widthFix">
+								</image>
+								<image v-else
+									src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/commodity/select.png"
+									mode="widthFix"></image>
+							</view>
+							<view class="">{{item.goodsName}}*{{item.totalNumber}}</view>
+						</view>
+						<view class="flex align-center" style="margin-top: 24rpx;">
+							<u-number-box button-size="30" v-model="item.refundNum" class='martop'></u-number-box>
+							<view class='marleft'>
+								<u--input placeholder="退款金额" @blur="refundGoodsChange" type="digit"
+									v-model="item.totalMoney" border="surround">
+								</u--input>
+							</view>
+						</view>
+					</view>
+				</view>
+				<view class='martop'>
+					<u--textarea v-model="remark" placeholder="备注"></u--textarea>
+				</view>
+				<view class="martop" v-if="radioType=='2'">总退款金额:¥{{refundTotalMoney}}</view>
+				<view class="flex align-center justify-end martop">
+					<view class="">
+						<xbutton width='200rpx' @click='refundShow=false'>取消</xbutton>
+					</view>
+					<view class='marleft'>
+						<xbutton width='200rpx' @click="refundSure">确定</xbutton>
+					</view>
+				</view>
+			</view>
+		</u-popup>
+		<u-datetime-picker :show="show" mode="date" v-model="nowDate" @confirm="confirm" :closeOnClickOverlay="true"
+			@close="close" @cancel="close"></u-datetime-picker>
+		<u-action-sheet :show="actionSheetShow" :actions="actions" title="请选择订单状态" @close="actionSheetShow = false"
+			@select="actionsheetSelect($event)"></u-action-sheet>
+		<xvideo :list="videoList" ref="video" showBtn></xvideo>
+	</view>
+</template>
+
+<script>
+	import {
+		refundList,
+		hendel
+	} from "@/api/order/order.js"
+
+	import xvideo from "./components/xvideo.vue"
+	export default {
+		components: {
+			xvideo
+		},
+		data() {
+			return {
+				refundList: [],
+				refundListlenght: '',
+				isEmpty: false,
+
+				refundShow: false,
+				show: false,
+				actionSheetShow: false,
+				mode: 'single',
+
+				index: 1,
+				unchecked: false,
+
+
+				actions: [{
+					name: "全部",
+					label: ""
+				}, {
+					name: "待处理",
+					label: 1
+				}, {
+					name: '已退款',
+					label: 2
+				}, {
+					name: '已拒绝',
+					label: 3
+				}],
+				orderStatus: {
+					name: '全部',
+					label: ''
+				}, //退款状态
+				date: '', //创建时间
+
+				list: [], //订单列表
+				page: 1, //当前分页
+				size: 10, //分页数据条数
+				loadmoreStatus: 'loadmore', //加载更多
+
+				type: '',
+				dateStart: '',
+				dateend: '',
+
+				keyword: '',
+
+				orderGoods: null,
+				radioType: '2',
+
+				remark: '',
+				id: null, //退款订单id
+
+				nowDate: Number(new Date()),
+
+				// refundTotalMoney: 0,
+				refundReason: [],
+				videoList: null,
+				fullHeight: 0,
+				dateDx:0
+			}
+		},
+
+		async onLoad() {
+			let _this = this;
+			const query = uni.createSelectorQuery().in(this);
+			query.select(".scrollview").boundingClientRect((data) => {
+				uni.getSystemInfo({
+					success(res) {
+						// 针对iPhone X等机型底部安全距离做适配
+						const model = res.model;
+						const modelInclude = [
+							"iPhone X", 
+							'iPhone XR', 
+							"iPhone XS", 
+							"iPhone XS MAX",
+							"iPhone 12/13 mini",
+							"iPhone 12/13 (Pro)",
+							"iPhone 12/13 Pro Max",
+							"iPhone 14 Pro Max"
+						];
+						let safeDistance = modelInclude.includes(model)
+						//动态设置商品区域高度
+						console.log(res.windowHeight, data.top)
+						if (safeDistance) {
+							_this.fullHeight = res.windowHeight - data.top - 34 + 'px';
+						} else {
+							_this.fullHeight = res.windowHeight - data.top + 'px';
+						}
+					},
+				});
+			}).exec();
+
+			await this.getRefundReason()
+			await this.getList()
+		},
+
+		computed: {
+			refundTotalMoney() {
+				let num = 0;
+				if (this.orderGoods) {
+					this.orderGoods.forEach(i => {
+						if (i.checked) {
+							num += Number(i.totalMoney)
+						}
+					})
+				}
+				return num
+			}
+		},
+
+		// watch: {
+		// 	orderGoods: {
+		// 		handler(newVal, oldVal) {
+		// 			let num = 0;
+		// 			if (newVal) {
+		// 				newVal.forEach(i => {
+		// 					if (i.checked) {
+		// 						num += Number(i.totalMoney)
+		// 					}
+		// 				})
+		// 			}
+		// 			this.refundTotalMoney = num
+		// 		},
+		// 		deep: true,
+		// 		immediate: true
+		// 	}
+		// },
+
+		methods: {
+			getRefundReason() {
+				return new Promise((resolve, reject) => {
+					this.getDict('order_refund_refund_reason').then(res => {
+						let newData = []
+						res.forEach(item => {
+							newData.push({
+								name: item.msg,
+								code: item.code
+							})
+						})
+						this.refundReason = newData;
+						resolve(res)
+					}).catch(err => {
+						reject(err)
+					})
+				})
+			},
+
+			changeMonth(add) {
+				this.dateDx += add;
+				var date = new Date();
+				var year = date.getFullYear(); //当前年:四位数字
+				var month = date.getMonth() + 1 + this.dateDx; //当前月:0-11
+				var yeardx = Math.ceil(month / 12) - 1
+				month %= 12;
+				if (month <= 0) month += 12;
+				year += yeardx;
+				month = month < 10 ? ('0' + month) : month;
+
+				this.dateStart = year + '-' + month + '-01';
+
+				var day = 28;
+				if (2 == month) {
+					if (year % 4 == 0) {
+						day = 29;
+					}
+				} else {
+					if (month < 8) {
+						if (1 == month % 2) {
+							day = 31;
+						} else {
+							day = 30;
+						}
+					} else {
+						if (1 == month % 2) {
+							day = 30;
+						} else {
+							day = 31;
+						}
+					}
+				}
+				this.dateend = year + '-' + month + '-' + (day < 10 ? ('0' + day) : day);
+				this.search();
+			},
+
+			dateSelect(type) {
+				if (type == 'start') {
+					this.type = type
+				}
+				if (type == 'end') {
+					this.type = type
+				}
+				this.show = true
+			},
+			confirm(e) {
+				this.show = false
+				if (this.type == 'start') {
+					this.dateStart = uni.$u.timeFormat(e.value, 'yyyy-mm-dd')
+				}
+				if (this.type == 'end') {
+					this.dateend = uni.$u.timeFormat(e.value, 'yyyy-mm-dd')
+				}
+				this.reset()
+				this.getList()
+			},
+
+			close() {
+				this.show = false
+			},
+
+			search() {
+				this.reset()
+				this.getList()
+			},
+
+			//扫码
+			scan() {
+				uni.scanCode({
+					success: (res) => {
+						this.keyword = res.result;
+						this.search()
+					}
+				});
+			},
+
+			open() {
+				this.refundShow = true
+			},
+
+			//获取数据列表
+			getList() {
+				refundList({
+					page: {
+						current: this.page,
+						size: this.size,
+					},
+					refundStatus: this.orderStatus.label, //退款状态
+					beginCreateTime: this.dateStart, //时间
+					endCreateTime: this.dateend
+				}).then(res => {
+					let data = res.data.records;
+					let newData = [];
+					console.log('res', data)
+					data.forEach(i => {
+						if (i.imgUrls) {
+							i.imgUrls = i.imgUrls.split(',')
+						}
+						let refundTotal = 0;
+						if (i.orderRefundGoods) { //处理无数据情况
+							i.orderRefundGoods.forEach(j => {
+								refundTotal += j.totalMoney
+							})
+						}
+						if (this.refundReason.length > 0) {
+							this.refundReason.forEach(j => {
+								if (i.reason == j.code) {
+									i.reason = j.name
+								}
+							})
+						}
+
+						i.refundTotal = refundTotal
+						newData.push(i)
+					})
+					if (newData.length < 10) {
+						this.loadmoreStatus = "nomore"
+					} else {
+						this.loadmoreStatus = "loadmore"
+					}
+					this.list = this.list.concat(newData)
+				})
+			},
+
+			//重置数据
+			reset() {
+				this.loadmoreStatus == 'loadmore'
+				this.page = 1;
+				this.size = 10;
+				this.list = [];
+			},
+
+			//上拉加载
+			loadMore(e) {
+				if (this.loadmoreStatus == 'nomore') return
+				this.page++
+				this.getList()
+			},
+
+			//退款按钮
+			refund(e) {
+				this.id = e.id;
+				let orderRefundGoods = JSON.parse(JSON.stringify(e.orderRefundGoods));
+				let arr = []
+				if (orderRefundGoods && orderRefundGoods.length > 0) {
+					arr = orderRefundGoods.map(i => {
+						i.checked = false;
+						i.refundNum = 1;
+						i.totalMoney = Number(i.totalMoney) / 100
+						return i
+					});
+				} else {
+					arr = []
+				}
+				this.orderGoods = JSON.parse(JSON.stringify(arr))
+				this.refundShow = true
+			},
+
+			//退款状态选择
+			actionsheetSelect(e) {
+				this.orderStatus = e
+				this.search()
+			},
+
+			//退款选项
+			radioChange(e) {},
+
+			//选中商品
+			checked(item) {
+				this.orderGoods.forEach(i => {
+					if (i.id == item.id) {
+						i.checked = !i.checked
+					}
+				})
+				this.orderGoods = JSON.parse(JSON.stringify(this.orderGoods))
+			},
+
+			refundGoodsChange(e) {
+				if (!this.$xy.testPrice(e)) {
+					this.$modal.msg('输入价格最低为分!')
+				}
+			},
+
+			// 退款处理确认按钮
+			refundSure() {
+				let ordersGoods = [];
+				if (this.radioType == 2) {
+					for (var i = 0; i < this.orderGoods.length; i++) {
+						let item = this.orderGoods[i]
+						if (item.checked) {
+							if (!this.$xy.testPrice(item.totalMoney)) {
+								this.$modal.msg('输入价格最低为分!')
+								return
+							}
+							ordersGoods.push({
+								goodsId: item.goodsId,
+								totalNumber: item.refundNum,
+								refundMoney: item.totalMoney * 100
+							})
+						}
+					}
+					if (ordersGoods && ordersGoods.length == 0) {
+						this.$modal.msg('请选择退款商品!')
+						return
+					}
+				}
+
+				let params = {
+					id: this.id,
+					refundStatus: Number(this.radioType),
+					ordersGoods: ordersGoods,
+					remark: this.remark
+				}
+
+				console.log('params', params)
+				hendel(params).then(res => {
+					if (res.code == 200) {
+						this.$modal.msg('订单处理成功~')
+						this.search()
+					} else {
+						this.$modal.msg('订单处理失败~')
+					}
+					this.orderGoods = null;
+					this.refundShow = false;
+				})
+			},
+
+			showVideoView(item) {
+				this.$refs.video.show = true
+				this.videoList = [
+					item.urlVideo0,
+					item.urlVideo1
+				]
+			},
+
+			playVideo(o) {
+				if (0 == o) {
+					this.videoUrl = this.selOrder.url0;
+				} else {
+					this.videoUrl = this.selOrder.url1;
+				}
+			},
+
+			closeVideoView() {
+				this.videoUrl = '';
+				this.hiddenOrderVideos = true;
+			},
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.container {
+		.martop {
+			margin-top: 30rpx;
+		}
+
+		.empty {
+			margin-top: 40%;
+		}
+
+		.marleft {
+			margin-left: 20rpx;
+		}
+
+		.scrollview {
+			width: 724rpx;
+			margin-left: 13rpx;
+		}
+
+		.header-container {
+			padding: 20rpx 13rpx;
+			background-color: #fff;
+
+			.search {
+				position: relative;
+
+				.scan-icon {
+					position: absolute;
+					right: 24rpx;
+					top: 50%;
+					transform: translateY(-50%);
+					z-index: 2;
+				}
+			}
+
+			.params-list {
+				.date-container {
+					background-color: #fff;
+					padding: 12rpx 24rpx;
+					border-radius: 15rpx;
+				}
+			}
+
+			.picker-container {
+				width: 150rpx;
+				padding: 12rpx 24rpx;
+				border-radius: 15rpx;
+				background-color: #fff;
+			}
+
+			.exceptiontype-container {
+				background-color: #fff;
+				border-radius: 10rpx;
+				padding: 0 10rpx;
+				width: 140rpx;
+				overflow: hidden;
+				text-overflow: ellipsis;
+				white-space: nowrap;
+				line-height: 50rpx;
+			}
+
+			.change-month {
+				display: inline-block;
+				width: 100rpx;
+				height: 45rpx;
+				border: 1px solid #CCCCCC;
+				border-radius: 6rpx;
+				line-height: 43rpx;
+				text-align: center;
+				border-radius: 10rpx;
+				text-align: center;
+				font-size: 22rpx;
+				color: #777777;
+			}
+		}
+
+		.order-content {
+			overflow: hidden;
+		}
+
+		.scrollview-container {
+			background-color: #fff;
+			margin-top: 20rpx;
+			padding: 20rpx;
+			border-radius: 15rpx;
+			box-shadow: 0 5rpx 4rpx rgba(179, 179, 179, 0.3);
+
+			.order-top {
+				position: relative;
+				font-size: 28rpx;
+				color: #333;
+				font-weight: bold;
+
+				.refund-status {
+					position: absolute;
+					right: 0;
+					top: 0;
+				}
+			}
+
+			.ddxx-box {
+				padding: 20rpx 13rpx;
+				border-radius: 12rpx;
+				font-size: 28rpx;
+				color: #777;
+
+				.martop12 {
+					margin-top: 12rpx;
+				}
+
+				.goods-container {
+					height: 154rpx;
+					padding: 12rpx 12rpx 12rpx 164rpx;
+					box-sizing: border-box;
+					position: relative;
+					border-radius: 8rpx;
+					background-color: #f5f8fb;
+					margin-top: 13rpx;
+
+					.image-container {
+						height: 130rpx;
+						width: 130rpx;
+						position: absolute;
+						left: 12rpx;
+						top: 50%;
+						transform: translateY(-50%);
+					}
+
+					.details-container {
+						position: relative;
+						padding: 12rpx;
+
+						.goods-name-num {
+							.goods-name {
+								font-size: 26rpx;
+								color: #333;
+								font-weight: bold;
+							}
+
+							.goods-num {
+								font-size: 26rpx;
+								color: red;
+								margin-right: 40rpx;
+								font-weight: bold;
+							}
+						}
+
+						.goods-price {
+							font-size: 28rpx;
+						}
+
+						.good-act {
+							margin-top: 12rpx;
+						}
+
+						.gp-item {
+							width: 50%;
+							margin-top: 12rpx;
+						}
+
+						.goodf-act {
+							font-size: 28rpx;
+							color: #333;
+							margin-top: 12rpx;
+						}
+
+						.goods-btn {
+							margin-top: 12rpx;
+						}
+
+						.refund {
+							position: absolute;
+							right: 0;
+							top: 0;
+							background-color: red;
+							color: #fff;
+							border-radius: 4rpx;
+							padding: 0 12rpx;
+							line-height: 40rpx;
+							font-size: 24rpx;
+						}
+					}
+				}
+
+				.oc-name {
+					display: inline-block;
+					width: 170rpx;
+				}
+
+				.phone {
+					display: inline-block;
+					font-size: 26rpx;
+					font-family: PingFang SC;
+					font-weight: 500;
+					color: #2C6FF3;
+					padding: 0 16rpx;
+					line-height: 38rpx;
+					background: #F4F8FF;
+					border-radius: 8rpx;
+					margin-left: 24rpx;
+				}
+			}
+
+			.con-item {
+				margin-top: 12rpx;
+			}
+
+
+
+			.untreated-btn {
+				color: red;
+
+			}
+
+			.refunded-text {
+				margin-left: 10rpx;
+				color: #5e90d9;
+			}
+
+			.refundamount-text {
+				color: red;
+			}
+
+			.image-box+.image-box {
+				margin-left: 12rpx;
+			}
+		}
+
+		.btn {
+			margin-top: 12rpx;
+		}
+
+		.refund-container {
+			background-color: #fff;
+			width: 600rpx;
+			padding: 20rpx 30rpx;
+
+			.checked {
+				width: 36rpx;
+				height: 36rpx;
+				margin-right: 12rpx;
+
+				image {
+					width: 36rpx;
+					height: 36rpx;
+				}
+			}
+		}
+	}
+</style>

+ 1010 - 0
pages/order/riskOrder.vue

@@ -0,0 +1,1010 @@
+<template>
+	<view class="container">
+		<u-navbar leftIconColor="#fff" titleStyle="color:#fff;fontSize:36rpx;" :autoBack="true" bgColor="#2C6FF3"
+			:placeholder="true" title="异常订单"></u-navbar>
+
+		<!-- <view class="time-choose">
+			<view class="flex align-center justify-between top">
+				<view class="con-btn" @tap="changeMonth(-1)">
+					上一月</view>
+				<view class="flex align-center justify-around date-container">
+					<view class="" style="margin-right: 10rpx;" @tap="dateSelect('start')">
+						{{dateStart?dateStart:'开始日期'}}
+					</view>
+					<view class="" style="margin-right: 10rpx;">至</view>
+					<view class="" @tap="dateSelect('end')">{{dateEnd?dateEnd:'结束日期'}}</view>
+				</view>
+				<view class="con-btn" @tap="changeMonth(1)">下一月</view>
+			</view>
+		</view> -->
+
+		<view class="header-container">
+			<view class="header-text flex align-center">
+				<image src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/order/risk-num.png"
+					mode="widthFix"></image>您有待处理异常订单:<text style="font-weight: bold;">{{orderNum}}</text>笔
+			</view>
+			<view class="search">
+				<u-search animation placeholder="可输入订单编号,设备号,用户手机号" :clearabled="false" v-model="keyword"
+					:showAction="false" @search="search"></u-search>
+			</view>
+
+			<view class="flex align-center justify-between" style="margin-top: 28rpx;">
+				<view class="exceptiontype-container" @tap="actionSheetShow=true">
+					{{status?status:'选择状态'}}
+				</view>
+
+				<view class="flex align-center  date-container">
+					<view class="" style="margin-right: 10rpx;" @tap="dateSelect('start')">
+						{{dateStart?dateStart:'开始日期'}}
+					</view>
+					<view class="" style="margin-right: 10rpx;">至</view>
+					<view class="" @tap="dateSelect('end')">{{dateEnd?dateEnd:'结束日期'}}</view>
+				</view>
+
+				<view>
+					<view class="change-month" @tap="changeMonth(-1)">
+						上一月
+					</view>
+					<view class="change-month" style="margin-left: 20rpx;" @tap="changeMonth(1)">
+						下一月
+					</view>
+				</view>
+			</view>
+			<!-- <view class='martop'>
+				<u-subsection :list="list" activeColor="#2C6FF3" :current="current"
+					@change="sectionChange"></u-subsection>
+			</view> -->
+			<view class="tab-wrap">
+				<view class="tab">
+					<u-tabs :list="list" :activeStyle="{color: '#333',fontWeight: 'bold',fontSize:'28rpx'}"
+						:inactiveStyle="{fontSize:'28rpx'}" :scrollable="false" :current="current"
+						@click="sectionChange" lineColor="#2C6FF3">
+					</u-tabs>
+				</view>
+			</view>
+		</view>
+		<scroll-view class="scrollview" scroll-y="true" scroll-with-animation="true" lower-threshold="100"
+			@scrolltolower="loadMore" :style="{height:fullHeight}">
+			<view class="scroll-content" v-if="commList&&commList.length>0">
+				<block v-for="(item,index) in commList" :key="item.id">
+					<view class="abnormal-container">
+						<view class="flex align-center justify-between">
+							<!-- 				<view class="flex align-center image-container">
+								<view style="margin-right: 16rpx;" v-for="(item1,index1) in item.orderGoods" :key="item1.id">
+									<u--image width="110rpx" :src="item1.goodslmgUrl" mode="widthFix" :lazy-lord="true">
+									</u--image>
+								</view>
+							</view> -->
+							<view class="flex align-center error-type">
+								<image
+									src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/order/risk-tip.png"
+									mode="widthFix"></image>{{item.riskTypeDesc}}
+							</view>
+							<view class="com-status" style="color: #FF1E1E;" v-if="item.status==1">待处理</view>
+							<view class="com-status" style="color: #22ca08;" v-if="item.status==2">已审核</view>
+							<view class="com-status" style="color: #ffaa00;" v-if="item.status==3">审核中</view>
+							<view class="com-status" style="color: #bdbdbd;" v-if="item.status==4">已结束</view>
+						</view>
+						<view class="fx-box">
+							<view class="device-name" v-if="item.deviceName">
+								{{item.deviceName}}<text>({{item.deviceId}})</text>
+							</view>
+							<view class="device-name" v-else>{{item.deviceId}}</view>
+							<view class="flex align-center martop12">
+								<view class="">
+									<view class="oc-name">订单编号:</view>{{item.orderId||'/'}}<text class="under-line-text"
+										@tap="copy(item.orderId)">复制</text>
+								</view>
+							</view>
+							
+							<view class="flex align-center martop12">
+								<view class="oc-name">支付类型:</view>{{item.payStatus||'/'}}
+							</view>
+							
+							<view class="order-detail-item martop12">
+								<view class="oc-name">设备类型:</view>{{item.deviceType||'/'}}
+							</view>
+
+							<view class="flex align-center martop12">
+								<view class="oc-name">订单状态:</view><text style="color: red;">{{item.orderStatusDesc||'/'}}</text>
+							</view>
+
+							<view class="flex align-center martop12">
+								<view class="oc-name">补扣金额:</view>¥{{$xy.delMoney(item.cutMoney)}}
+							</view>
+
+							<view class="flex align-center martop12">
+								<view class="oc-name">联系电话:</view>{{item.memberPhone}}
+								<view class="phone" @click="call(item.memberPhone)">
+									<image
+										src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/order/phone.png"
+										mode="widthFix"></image>拨打
+								</view>
+							</view>
+
+							<view class="flex align-center martop12">
+								<view class="oc-name">创建时间:</view>{{item.createTime}}
+							</view>
+
+							<view class="goods-container" v-for="(item1,index1) in item.orderGoods" :key="item1.id">
+								<view class="flex align-center justify-center image-container">
+									<u--image radius="4" width="130rpx" height="130rpx" :src="item1.goodsImgUrl"
+										mode="aspectFit" :lazy-lord="true"></u--image>
+								</view>
+								<view class="details-container">
+									<view class="flex align-center goods-name-num justify-between">
+										<view class="goods-name">{{item1.goodsName}}</view>
+										<view class="goods-num">×{{item1.totalNumber}}</view>
+									</view>
+									<view class="gp-item">
+										单价:¥{{(Number(item1.totalMoney)/Number(item1.totalNumber))/100}}
+									</view>
+									<view class="gp-item" v-if="item1.refundMoney||item1.refundMoney!=0">
+										已退款:¥{{$xy.delMoney(item1.refundMoney)}}
+									</view>
+									<view class="goods-price flex">
+									</view>
+								</view>
+							</view>
+
+							<!-- <view class="flex align-center justify-end" v-if="item.cutGoodsNumber!=null">
+								共计<text style="color: red;font-weight: bold;">{{item.cutGoodsNumber}}</text>件
+							</view> -->
+						</view>
+						
+						<view class="flex align-center martop12 re-back" v-if="item.status!=2&&item.status!=4">
+							<view class="oc-name">驳回原因:</view>{{item.orderStatusDesc||'/'}}
+						</view>
+
+						<view class="martop">
+							<view class="flex justify-start">
+								<view class="">
+									<xbutton size='medium' round='25rpx' padding='0rpx 20rpx' bgColor='#43cf7c'
+										width='140rpx' @tap="showlogs(item.activityId)">交易日志</xbutton>
+								</view>
+							<!-- 	<view class="marleft24">
+									<xbutton size='medium' round='25rpx' padding='0rpx 20rpx' width='200rpx'
+										bgColor='#43cf7c'>查看处理记录
+									</xbutton>
+								</view> -->
+								<view class="marleft24">
+									<xbutton size='medium' round='25rpx' padding='0rpx 20rpx' width='200rpx'
+										bgColor='#43cf7c' @tap="deviceCom(item)">查看设备商品
+									</xbutton>
+								</view>
+							</view>
+							<view class="flex justify-end martop">
+								<view class="marleft24">
+									<xbutton size='medium' round='25rpx' padding='0rpx 20rpx'
+										@click="block(item.memberId)">拉黑</xbutton>
+								</view>
+								<view v-if="item.status=='1'" class="marleft24">
+									<xbutton size='medium' round='25rpx' padding='0rpx 20rpx'
+										@tap='showFinishOrderView(item)'>结束</xbutton>
+								</view>
+								<view class="marleft24" v-if="item.status=='3'">
+									<xbutton size='medium' round='25rpx' padding='0rpx 20rpx' @tap='backApply(item)'>撤回
+									</xbutton>
+								</view>
+								<view class="marleft24">
+									<xbutton size='medium' round='25rpx' padding='0rpx 20rpx' width='140rpx'
+										@tap="showVideoView(item)">查看视频</xbutton>
+								</view>
+								<view class="marleft24" v-if="item.status=='1'">
+									<xbutton size='medium' round='25rpx' padding='0rpx 20rpx'
+										@tap='supplementaryDeduction(item)'>申请立即扣款</xbutton>
+								</view>
+							</view>
+						</view>
+					</view>
+				</block>
+				<view class="load-more" v-if="commList.length>0">
+					<u-loadmore :status="loadmoreStatus" />
+				</view>
+			</view>
+			<view v-else class='empty'>
+				<u-empty mode="data" text="数据为空"></u-empty>
+			</view>
+		</scroll-view>
+		<u-action-sheet :show="actionSheetShow" :actions="exceptionTypeList" title="请选择状态"
+			@close="actionSheetShow = false" @select="actionsheetSelect($event)"></u-action-sheet>
+		<!-- :minDate="minDate" :maxDate="maxDate" -->
+		<u-datetime-picker :show="show" v-model="timeStamp" mode="date" @confirm="confirm" @cancel="close">
+		</u-datetime-picker>
+		<view :hidden="hiddenOrderVideos" class="popup_content">
+			<view class="flex">
+				<video id="myVideo" :src="videoUrl" style="width:100%;height: 372rpx; margin: 10rpx 10rpx;"></video>
+			</view>
+			<view class="flex" style="margin-top: 10rpx;">
+				<view class="marleft">
+					<xbutton size='medium' round='25rpx' padding='0rpx 20rpx' bgColor='#2C6FF3' @tap="playVideo(0)">主视频
+					</xbutton>
+				</view>
+				<view class="marleft">
+					<xbutton size='medium' round='25rpx' padding='0rpx 20rpx' bgColor='#2C6FF3' @tap="playVideo(1)">副视频
+					</xbutton>
+				</view>
+			</view>
+		</view>
+		<view class="popup_overlay" :hidden="hiddenOrderVideos" @click="closeVideoView()"></view>
+		<xpopup :show="blockShow" @close="blockClose" @confirm="submit" :showBtn="true" :title="popTitle">
+			<!-- 拉黑 -->
+			<view class="pop-content restart" v-if="popTitle=='拉黑'">
+				是否确定拉黑该用户?
+			</view>
+			<!-- 撤回 -->
+			<view class="pop-content restart" v-if="popTitle=='撤回'">
+				是否确定撤回该申请?
+			</view>
+		</xpopup>
+		<xpopup :show="finishOrderDetail.show" @close="blockClose" @confirm="finshOrder" :showBtn="true" title="结束订单">
+			<view class="pop-content">
+				<view class="text-notice">
+					警告:订单结束后,将不能再进行补扣
+				</view>
+				<view class="input">
+					<u--textarea placeholder="请输入结束原因" border="surround" v-model="finishOrderDetail.reason" count
+						maxlength="140"></u--textarea>
+				</view>
+			</view>
+		</xpopup>
+	</view>
+</template>
+
+<script>
+	import {
+		cancelOrder,
+		page,
+		todoNum,
+		rollback
+	} from "@/api/order/riskorder.js"
+	import {
+		divide
+	} from "../../uni_modules/uview-ui/libs/function/digit";
+	import {
+		setBlacklist
+	} from "@/api/order/order.js"
+	export default {
+		data() {
+			return {
+				hiddenOrderVideos: true,
+				selOrder: undefined,
+				videoUrl: '',
+				minDate: '',
+				maxDate: '',
+				isEmpty: false,
+				page: 1, //当前分页
+				size: 10, //分页数据条数
+				actionSheetShow: false,
+				riskOrderList: [],
+				loadmoreStatus: 'loadmore',
+				exceptionTypeList: [],
+				show: false,
+				dateStart: '',
+				dateEnd: '',
+				dateDx: 0,
+				// beginTime: '',
+				// endTime: '',
+				type: '',
+				current: 0,
+				status: '',
+				list: [{
+					name: '待处理'
+				}, {
+					name: '审核中'
+				}, {
+					name: '已审核'
+				}, {
+					name: '已结束'
+				}],
+				commList: [],
+				orderNum: 0, //异常订单数量
+				code: '',
+				deviceId: '',
+				timeStamp: new Date(), //时间picker显示时间
+				memberId: null, //用户id
+				goodId: null, //商品id
+				finishOrderDetail: {
+					show: false,
+					reason: "未拿取商品",
+					id: undefined
+				},
+				blockShow: false, //拉黑弹框
+				keyword: '',
+				fullHeight: 0,
+				popTitle: '拉黑',
+
+			}
+		},
+
+		computed: {
+			orderStatus() {
+				let status = 1
+				switch (this.current) {
+					case 0:
+						status = 1
+						break;
+					case 1:
+						status = 3
+						break;
+					case 2:
+						status = 2
+						break;
+					case 3:
+						status = 4
+						break;
+					default:
+						break;
+				}
+				return status
+			}
+		},
+
+		onShow() {
+			this.getOrderNum()
+			this.search()
+		},
+
+		onLoad() {
+			let _this = this;
+			const query = uni.createSelectorQuery().in(this);
+			query.select(".scrollview").boundingClientRect((data) => {
+				uni.getSystemInfo({
+					success(res) {
+						// 针对iPhone X等机型底部安全距离做适配
+						const model = res.model;
+						const modelInclude = [
+							"iPhone X",
+							'iPhone XR',
+							"iPhone XS",
+							"iPhone XS MAX",
+							"iPhone 12/13 mini",
+							"iPhone 12/13 (Pro)",
+							"iPhone 12/13 Pro Max",
+							"iPhone 14 Pro Max"
+						];
+						let safeDistance = modelInclude.includes(model)
+						//动态设置商品区域高度
+						console.log(res.windowHeight, data.top)
+						if (safeDistance) {
+							_this.fullHeight = res.windowHeight - data.top - 34 + 'px';
+						} else {
+							_this.fullHeight = res.windowHeight - data.top + 'px';
+						}
+					},
+				});
+			}).exec();
+
+			this.getDict('order_risk_type').then(res => {
+				let newData = []
+				res.forEach(item => {
+					newData.push({
+						name: item.msg,
+						code: item.code
+					})
+				})
+				this.exceptionTypeList = newData;
+			})
+		},
+
+		methods: {
+
+			// function getLastMonth() {
+			//     var date = new Date();
+			//     var year = date.getFullYear();   //当前年:四位数字
+			//     var month = date.getMonth();     //当前月:0-11
+
+			//     if (month == 0) {   //如果是0,则说明是1月份,上一个月就是去年的12月
+			//         year -= 1;
+			//         month = 12;
+			//     }
+
+			//     month = month < 10 ? ('0' + month) : month;   //月份格式化:月份小于10则追加个0
+
+			//     let lastYearMonth = year + '-' + month;
+
+			//     return lastYearMonth;
+			// },
+			changeMonth(add) {
+				this.dateDx += add;
+				var date = new Date();
+				var year = date.getFullYear(); //当前年:四位数字
+				var month = date.getMonth() + 1 + this.dateDx; //当前月:0-11
+				var yeardx = Math.ceil(month / 12) - 1
+				month %= 12;
+				if (month <= 0) month += 12;
+				year += yeardx;
+				month = month < 10 ? ('0' + month) : month;
+
+				this.dateStart = year + '-' + month + '-01';
+
+				var day = 28;
+				if (2 == month) {
+					if (year % 4 == 0) {
+						day = 29;
+					}
+				} else {
+					if (month < 8) {
+						if (1 == month % 2) {
+							day = 31;
+						} else {
+							day = 30;
+						}
+					} else {
+						if (1 == month % 2) {
+							day = 30;
+						} else {
+							day = 31;
+						}
+					}
+				}
+				this.dateEnd = year + '-' + month + '-' + (day < 10 ? ('0' + day) : day);
+				this.search()
+			},
+			getOrderNum() {
+				todoNum().then(res => {
+					let orderNum = res.data
+					if (res.code == 200) {
+						this.orderNum = res.data
+					} else {
+						this.orderNum = 0
+					}
+				})
+			},
+
+			loadMore(e) {
+				if (this.loadmoreStatus == 'nomore') return
+				this.page++
+				this.getpage()
+			},
+			sectionChange(e) {
+				this.current = e.index
+				this.search()
+			},
+
+			// 搜索列表
+			search() {
+				this.reset();
+				this.getpage()
+			},
+
+			call(tell) {
+				uni.makePhoneCall({
+					phoneNumber: tell
+				})
+			},
+			actionsheetSelect(e) {
+				this.status = e.name
+				this.code = e.code
+				this.search()
+			},
+			copy(text) {
+				uni.setClipboardData({
+					data: text,
+					success: (data) => {
+						uni.showToast({
+							title: '复制成功'
+						})
+					},
+					fail: function(err) {
+
+					},
+					complete: function(res) {
+
+					}
+				})
+			},
+			dateSelect(type) {
+				if (type == 'start') {
+					this.type = type
+				}
+				if (type == 'end') {
+					this.type = type
+				}
+				this.show = true
+			},
+			confirm(e) {
+				console.log(e)
+				if (this.type == 'start') {
+					this.dateStart = uni.$u.timeFormat(e.value, 'yyyy-mm-dd')
+				}
+				if (this.type == 'end') {
+					this.dateEnd = uni.$u.timeFormat(e.value, 'yyyy-mm-dd')
+
+				}
+				if (this.dateStart && this.dateEnd) {
+					if (this.dateStart > this.dateEnd) {
+						uni.$u.toast('开始日期不能大于结束日期')
+						return;
+					}
+				}
+
+				this.search()
+				this.show = false
+			},
+			close() {
+				this.show = false
+			},
+			supplementaryDeduction(item) {
+				uni.setStorageSync('riskOrder', JSON.stringify(item)) //存储订单信息
+				this.$tab.navigateTo('/pages/order/riskOrderDel?id=' + item.id + '&deviceId=' + item
+					.deviceId)
+			},
+			getpage() {
+				page({
+					page: {
+						current: this.page,
+						size: this.size,
+					},
+					search: this.keyword,
+					riskType: this.code, //异常类型
+					status: this.orderStatus, //状态
+					beginDate: this.dateStart,
+					endDate: this.dateEnd
+				}).then(res => {
+					let data = res.data.records;
+					if (data.length < 10) {
+						this.loadmoreStatus = "nomore"
+					} else {
+						this.loadmoreStatus = "loadmore"
+					}
+					this.commList = this.commList.concat(data)
+					console.log()
+				})
+			},
+
+			reset() {
+				this.loadmoreStatus == 'loadmore'
+				this.page = 1;
+				this.size = 10;
+				this.commList = [];
+			},
+			showlogs(activtyId) {
+				this.$tab.navigateTo('/pages/order/orderLogs?id=' + activtyId);
+			},
+			showVideoView(item) {
+				console.log(item);
+				this.hiddenOrderVideos = false;
+				this.selOrder = item;
+				var urls = item.video.split(',');
+				this.selOrder.url0 = urls[0];
+				this.selOrder.url1 = urls[1];
+				this.playVideo(0);
+			},
+			playVideo(o) {
+				if (0 == o) {
+					this.videoUrl = this.selOrder.url0;
+				} else {
+					this.videoUrl = this.selOrder.url1;
+				}
+			},
+			closeVideoView() {
+				this.videoUrl = '';
+				this.hiddenOrderVideos = true;
+			},
+
+			block(e) {
+				this.popTitle = '拉黑'
+				this.memberId = e
+				this.blockShow = true;
+			},
+
+			// 关闭弹框
+			blockClose(e) {
+				this.blockShow = false;
+				this.finishOrderDetail.show = false;
+			},
+
+			// 弹框确定
+			submit() {
+				if (this.popTitle == '拉黑') {
+					setBlacklist({
+						memberId: this.memberId
+					}).then(res => {
+						this.$modal.msg('拉黑成功~')
+					}).catch(err => {
+
+					})
+				} else {
+					rollback({
+						riskId: this.goodId
+					}).then(res => {
+						this.$modal.msg('撤回成功~')
+						this.search();
+					})
+				}
+
+				this.blockClose()
+			},
+			showFinishOrderView(v) {
+				this.finishOrderDetail.id = v.id;
+				this.finishOrderDetail.reason = "未拿商品";
+				this.finishOrderDetail.show = true;
+			},
+			finshOrder() {
+				if (!uni.$u.test.rangeLength(this.finishOrderDetail.reason, [4, 140])) {
+					this.$modal.msg('请输入结束原因')
+					return;
+				}
+				cancelOrder({
+					"id": this.finishOrderDetail.id,
+					"reason": this.finishOrderDetail.reason
+				}).then(res => {
+					this.$modal.msg('提交申请成功~')
+					this.finishOrderDetail.show = false;
+					this.getOrderNum();
+					this.search();
+				})
+			},
+
+			// 撤回
+			backApply(item) {
+				this.popTitle = '撤回'
+				this.goodId = item.id
+				this.blockShow = true;
+			},
+			
+			// 设备商品查看
+			deviceCom(item){
+				this.$tab.navigateTo(`/pages/equipment/comManage?id=${item.deviceId}&deviceName=${item.deviceName}`)
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.container {
+
+		.marleft24 {
+			margin-left: 24rpx;
+		}
+
+		.martop {
+			margin-top: 20rpx;
+		}
+
+		.xian {
+			border-bottom: 1px solid #5b5b5b;
+			margin: 20rpx 0;
+		}
+
+		.marleft {
+			margin-left: 12rpx;
+		}
+
+		// .time-choose {
+		// 	background-color: #fff;
+
+		// 	.top {
+		// 		height: 80rpx;
+		// 		color: #fff;
+		// 		padding: 0 20rpx;
+		// 		background-color: #2C6FF3;
+		// 		border-radius: 0px 0px 50rpx 50rpx;
+
+		// 		.con-btn {
+		// 			padding: 0 36rpx;
+		// 		}
+
+		// 		.date-container {
+		// 			min-width:380rpx;
+		// 			height: 50rpx;
+		// 			background-color: #fff;
+		// 			padding: 0 20rpx;
+		// 			border-radius: 10rpx;
+		// 			line-height: 50rpx;
+		// 			color: #000;
+		// 		}
+
+		// 	}
+		// }
+
+		.header-container {
+			padding: 27rpx 13rpx 0;
+			background-color: #fff;
+
+			.header-text {
+				width: 716;
+				margin-left: 21rpx;
+				height: 52rpx;
+				font-size: 28rpx;
+				color: #333;
+				background: linear-gradient(90deg, #FEE2E2, #FFFFFF);
+
+				>image {
+					width: 52rpx;
+					height: 52rpx;
+					margin-right: 13rpx;
+					margin-left: -21rpx;
+				}
+
+				text {
+					color: #FF0000;
+					font-size: 36rpx;
+					padding: 0 12rpx;
+				}
+			}
+
+			.search {
+				margin-top: 37rpx;
+			}
+
+			.exceptiontype-container {
+				background-color: #fff;
+				border-radius: 10rpx;
+				padding: 0 10rpx;
+				width: 140rpx;
+				overflow: hidden;
+				text-overflow: ellipsis;
+				white-space: nowrap;
+				line-height: 50rpx;
+			}
+
+			.change-month {
+				display: inline-block;
+				width: 100rpx;
+				height: 45rpx;
+				border: 1px solid #CCCCCC;
+				border-radius: 6rpx;
+				line-height: 43rpx;
+				text-align: center;
+				border-radius: 10rpx;
+				text-align: center;
+				font-size: 22rpx;
+				color: #777777;
+			}
+
+			.tab-wrap {
+				margin-top: 20rpx;
+			}
+		}
+
+		.scrollview {
+			.scroll-content {
+				width: 724rpx;
+				margin-left: 13rpx;
+				overflow: hidden;
+			}
+
+			.load-more {
+				padding-bottom: 24rpx;
+			}
+
+			.empty {
+				margin-top: 40%;
+			}
+		}
+
+
+		.abnormal-container {
+			margin-top: 20rpx;
+			padding: 30rpx 32rpx;
+			border-radius: 15rpx;
+			background-color: #fff;
+			box-shadow: 0px 0px 10rpx 0px rgba(174, 201, 255, 0.2);
+
+			.error-type {
+				font-size: 32rpx;
+				font-weight: 800;
+				color: #333333;
+
+				>image {
+					width: 40rpx;
+					height: 40rpx;
+					margin-right: 22rpx;
+				}
+			}
+
+			.fx-box {
+				margin-top: 24rpx;
+				padding-left: 8rpx;
+				font-size: 28rpx;
+				color: #777;
+
+				.device-name {
+					font-size: 28rpx;
+					font-weight: bold;
+					color: #333;
+					margin-bottom: 12rpx;
+
+					>text {
+						font-size: 24rpx;
+					}
+				}
+
+				.under-line-text {
+					font-size: 26rpx !important;
+					font-weight: 500;
+					font-style: italic;
+					text-decoration: underline;
+					color: #2C6FF3 !important;
+					margin-left: 24rpx;
+					background-color: #fff !important;
+				}
+
+				.oc-name {
+					display: inline-block;
+					width: 170rpx;
+				}
+
+				.phone {
+					display: inline-block;
+					font-size: 26rpx;
+					font-family: PingFang SC;
+					font-weight: 500;
+					color: #2C6FF3;
+					padding: 0 16rpx;
+					line-height: 38rpx;
+					background: #F4F8FF;
+					border-radius: 8rpx;
+					margin-left: 24rpx;
+
+					>image {
+						width: 20rpx;
+						height: 20rpx;
+						margin-right: 13rpx;
+					}
+				}
+
+				.goods-container {
+					height: 154rpx;
+					margin-top: 14rpx;
+					padding: 12rpx 12rpx 12rpx 164rpx;
+					box-sizing: border-box;
+					position: relative;
+					border-radius: 8rpx;
+					background-color: #f5f8fb;
+
+					.image-container {
+						height: 130rpx;
+						width: 130rpx;
+						position: absolute;
+						left: 12rpx;
+						top: 50%;
+						transform: translateY(-50%);
+					}
+
+					.details-container {
+						position: relative;
+						padding: 12rpx;
+
+						.goods-name-num {
+							.goods-name {
+								font-size: 26rpx;
+								color: #333;
+								font-weight: bold;
+							}
+
+							.goods-num {
+								font-size: 26rpx;
+								color: red;
+								margin-right: 40rpx;
+								font-weight: bold;
+							}
+						}
+
+						.goods-price {
+							font-size: 28rpx;
+						}
+
+						.good-act {
+							margin-top: 12rpx;
+						}
+
+						.gp-item {
+							width: 50%;
+							margin-top: 12rpx;
+						}
+
+						.goodf-act {
+							font-size: 28rpx;
+							color: #333;
+							margin-top: 12rpx;
+						}
+
+						.goods-btn {
+							margin-top: 12rpx;
+						}
+
+						.refund {
+							position: absolute;
+							right: 0;
+							top: 0;
+							background-color: red;
+							color: #fff;
+							border-radius: 4rpx;
+							padding: 0 12rpx;
+							line-height: 40rpx;
+							font-size: 24rpx;
+						}
+					}
+
+				}
+			}
+
+			.image-container {
+				view {
+					width: 100rpx;
+					height: 100rpx;
+					background-color: #ccc;
+				}
+			}
+
+			.orderstatus {
+				padding: 20rpx;
+				background-color: #43cf7c;
+				border-radius: 80rpx;
+				color: #fff;
+			}
+		}
+		
+		.re-back{
+			background-color: #FFF7F7;
+			padding:10rpx 10rpx 36rpx;
+			line-height: 36rpx;
+			font-size: 28rpx;
+			border-radius: 14rpx;
+			
+			.oc-name {
+				display: inline-block;
+				width: 170rpx;
+			}
+		}
+	}
+
+	.popup_overlay {
+		position: fixed;
+		top: 0%;
+		left: 0%;
+		width: 100%;
+		height: 100%;
+		background-color: black;
+		z-index: 1001;
+		-moz-opacity: 0.8;
+		opacity: .80;
+		filter: alpha(opacity=88);
+	}
+
+	.popup_content {
+		position: fixed;
+		top: 50%;
+		left: 50%;
+		width: 700rpx;
+		height: 500rpx;
+		margin-left: -350rpx;
+		margin-top: -250rpx;
+		border: 10px solid white;
+		background-color: white;
+		z-index: 1002;
+		overflow: auto;
+	}
+
+	.pop-content {
+		padding: 24rpx;
+	}
+
+	.text-notice {
+		color: red;
+		font-size: 28rpx;
+		height: 40rpx;
+		line-height: 40rpx;
+	}
+
+	.input {
+		margin-top: 20rpx;
+		height: 200rpx;
+		width: 100%;
+		// border-style: solid;
+		// border-width: 1rpx;
+		// border-color: #2C6FF3;
+		// border-radius: 10rpx;
+	}
+
+	.marginTop {
+		margin-top: 24rpx;
+	}
+
+	.martop12 {
+		margin-top: 12rpx;
+	}
+</style>

+ 432 - 0
pages/order/riskOrderDel.vue

@@ -0,0 +1,432 @@
+<template>
+	<view class="container">
+		<u-navbar leftIconColor="#fff" titleStyle="color:#fff;fontSize:36rpx;" :autoBack="true" bgColor="#2C6FF3"
+			:placeholder="true" title="风险订单补扣"></u-navbar>
+		<view class="video-container">
+			<video :src="videoUrl" style="width: 100%;"></video>
+			<view class="flex align-center justify-between" style="padding:0 24rpx;">
+				<view class="video-tab">
+					<u-subsection :list="videoType" activeColor="#2C6FF3" :current="videoCurrent"
+						@change="sectionChange">
+					</u-subsection>
+				</view>
+				<view class="flex">
+					<view>
+						<xbutton size="mini" padding="0 20rpx" @tap='addCom(deviceId)'>添加商品</xbutton>
+					</view>
+					<view style="margin-left: 24rpx;">
+						<xbutton size="mini" width='180rpx' @tap='submit'>提交补扣申请</xbutton>
+					</view>
+				</view>
+			</view>
+		</view>
+		<view class="goods-select" v-if="goodsList.length>0">
+			<u-scroll-list :indicator="indicator" indicatorColor="#fff0f0" indicatorActiveColor="#f56c6c">
+				<view class="flex justify-around">
+					<view class="goods-item" v-for="(item,index) in goodsList" :key="index">
+						<view class="flex flex-direction align-center justify-center image-dele-container goodContainer"
+							@tap="clean(index,item)">
+							<!-- <view class="flex align-center justify-center numberContainer">
+								{{value}}
+							</view> -->
+							<view class="image">
+								<u--image radius="4" width="100rpx" height="100rpx" :src="item.goodsImg" mode="aspectFit"
+									:lazy-lord="true"></u--image>
+							</view>
+							<view class="goods-select-name">{{item.name}}</view>
+						</view>
+						<view class="flex align-center justify-center" style="margin-top: 8rpx;">
+							<view class="flex align-center justify-center">
+								<view class="minus" @tap="reduce(item,index)">
+									<u-icon name="minus" size="12"></u-icon>
+								</view>
+								<text style="width: 50rpx;text-align: center;" class="input">{{item.number}}</text>
+								<view class="plus" @tap="add(item)">
+									<u-icon name="plus" color="#FFFFFF" size="12"></u-icon>
+								</view>
+							</view>
+						</view>
+					</view>
+				</view>
+			</u-scroll-list>
+		</view>
+
+		<view class="goods-select-empty" v-else>
+			添加需要补扣的商品~
+		</view>
+
+		<view class="classify-wrap">
+			<Classify :status="status" :commList="commList"
+				@comClick='detail' :height="fullHeight" :leftShow="false" />
+		</view>
+	</view>
+</template>
+
+<script>
+	import {
+		ownerGoodsList,
+		list
+	} from "@/api/commodity/goods.js"
+	import {
+		apply
+	} from "@/api/order/riskorder.js"
+	import {
+		categoryList
+	} from "@/api/device/device.js"
+	import Classify from "@/components/classify/index.vue"
+	export default {
+		components: {
+			Classify
+		},
+		data() {
+			return {
+				scrollintoview: '',
+				fullHeight: '0',
+				tabList: [], //商品类目
+				commList: [], //商品列表
+				goodsList: [], //商品id列表
+				status: 'nomore', //加载更多
+				riskId: '',
+				deviceId: '',
+				video: {
+					url1: '',
+					url2: ''
+				},
+				videoUrl: null,
+				videoType: ['主视频', '副视频'],
+				videoCurrent: 0,
+				statGoodsList: [], //平台识别补扣商品
+			}
+		},
+
+		onLoad(e) {
+			let _this = this;
+			const query = uni.createSelectorQuery().in(this);
+			query.select(".classify-wrap").boundingClientRect((data) => {
+				uni.getSystemInfo({
+					success(res) {
+						// 针对iPhone X等机型底部安全距离做适配
+						const model = res.model;
+						const modelInclude = [
+							"iPhone X", 
+							'iPhone XR', 
+							"iPhone XS", 
+							"iPhone XS MAX",
+							"iPhone 12/13 mini",
+							"iPhone 12/13 (Pro)",
+							"iPhone 12/13 Pro Max",
+							"iPhone 14 Pro Max"
+						];
+						let safeDistance = modelInclude.includes(model)
+						//动态设置商品区域高度
+						if (safeDistance) {
+							_this.fullHeight = res.windowHeight - data.top - 34 + 'px';
+						} else {
+							_this.fullHeight = res.windowHeight - data.top  + 'px';
+						}
+					},
+				});
+			}).exec();
+			// uni.getSystemInfo({
+			// 	success(res) {
+			// 		_this.fullHeight = 2 * (res.windowHeight - res.statusBarHeight - 44 - 143) + 'rpx';
+			// 	},
+			// });
+			this.riskId = e.id
+			this.deviceId = e.deviceId
+			this.initOrder()
+		},
+		
+		onShow() {
+			this.getCommList()
+		},
+
+		methods: {
+			//初始化页面信息
+			initOrder() {
+				let orderDetail = JSON.parse(uni.getStorageSync('riskOrder'));
+				if (orderDetail.video) {
+					this.video = {
+						url1: orderDetail.video.split(',')[0],
+						url2: orderDetail.video.split(',')[1]
+					}
+					this.videoUrl = this.video.url1
+				} else {
+					this.video = {
+						url1: '',
+						url2: ''
+					}
+				}
+
+
+				let tempGoodsList = orderDetail.orderGoods.map(i => {
+					return {
+						goodsImg: i.goodsImgUrl,
+						spid: i.goodsId,
+						name: i.goodsName,
+						price: Number(i.totalMoney) / 100,
+						number: Number(i.totalNumber),
+						initNum: Number(i.totalNumber)
+					}
+				})
+
+				this.statGoodsList = JSON.parse(JSON.stringify(tempGoodsList))
+				this.goodsList = JSON.parse(JSON.stringify(tempGoodsList))
+			},
+
+			sectionChange(e) {
+				this.videoCurrent = e;
+				if (e == 0) {
+					this.videoUrl = this.video.url1
+				} else {
+					this.videoUrl = this.video.url2
+				}
+			},
+
+			//根据类目获取商品列表
+			getCommList() {
+				list({
+					deviceId: this.deviceId,
+				}).then(res => {
+					let data = res.data;
+					let newData = data.map(i => {
+						i.price = (Number(i.price) / 100).toFixed(2)
+						return i
+					})
+					this.commList = newData
+				})
+			},
+
+			statGoodsTips() {
+				this.$modal.msg('该商品为平台算法识别异常商品,为防止您的无辜损失,无法再删减!')
+			},
+
+			clean(e, item) { //点击移除商品
+				//校验是否算法识别商品
+				if (this.isStatGoods(item)) {
+					this.statGoodsTips()
+					return
+				}
+				this.goodsList.splice(e, 1)
+			},
+
+			// 校验是否算法识别商品
+			isStatGoods(e) {
+				console.log(e)
+				console.log(this.statGoodsList)
+				for (let i = 0; i < this.statGoodsList.length; i++) {
+					let item = this.statGoodsList[i];
+					if (item.spid == e.spid) {
+						return true
+					}
+				}
+				return false
+			},
+
+			//校验是否减少了算法识别商品
+			isDelStatGoods(e) {
+				for (let i = 0; i < this.statGoodsList.length; i++) {
+					let item = this.statGoodsList[i];
+					if (item.spid == e.spid && item.number == e.number) {
+						return true
+					}
+				}
+				return false
+			},
+
+			detail(e) {
+				this.goodsList.forEach(item => {
+					if (e.goodsId == item.spid) {
+						this.add(item)
+					}
+				})
+
+				for (var i = 0; i < this.goodsList.length; i++) {
+					if (this.goodsList[i].spid == e.goodsId) {
+						return;
+					}
+				}
+
+				this.goodsList.push({
+					goodsImg: e.cover,
+					spid: e.goodsId,
+					name: e.name,
+					price: e.price,
+					number: 1,
+					initNum: 0
+				})
+
+			},
+			//新增
+			add(e) {
+				e.number++
+			},
+			//减少
+			reduce(e, index) {
+				if (this.isDelStatGoods(e)) { //校验是否减少了算法识别商品
+					this.statGoodsTips()
+					return
+				}
+				e.number--
+				if (e.number == 0) {
+					this.goodsList.splice(index, 1)
+				}
+			},
+			reset() {
+				this.status == 'loadmore'
+				this.page = 1;
+				this.size = 10;
+				this.commList = [];
+			},
+			
+			// 添加商品至机器
+			addCom(deviceId){
+				this.$tab.navigateTo('/pages/equipment/addCom?id='+deviceId)
+			},
+			
+			//提交补扣申请
+			submit() {
+				if (!this.goodsList.length) {
+					uni.$u.toast('请选择商品')
+					return;
+				}
+
+				var goodsId = []
+				this.goodsList.forEach(item => {
+					if (item.number - item.initNum > 0) { //除去算法识别商品
+						goodsId.push({
+							goodsId: item.spid,
+							goodsName: item.name,
+							price: (Number(item.price)) * 100,
+							totalNumber: item.number - item.initNum
+						})
+					}
+				})
+				apply({
+					"cutGoods": goodsId,
+					"riskId": this.riskId
+				}).then(res => {
+					this.$modal.msg('提交申请成功~')
+					setTimeout(() => {
+						this.$tab.navigateBack()
+					}, 800)
+				})
+			}
+		},
+
+		onUnload() {
+			uni.setStorage('riskOrder', '')
+		},
+	}
+</script>
+
+<style lang="scss" scoped>
+	.container {
+		.martop {
+			margin-top: 20rpx;
+		}
+
+		.margin {
+			margin: 10rpx 20rpx;
+		}
+
+		.margintop {
+			margin-top: 10rpx;
+		}
+
+		.video-container {
+			.video-tab {
+				width: 300rpx;
+			}
+		}
+
+		.box {
+			padding: 20rpx 24rpx;
+		}
+
+		.goods-item+.goods-item {
+			margin-left: 12rpx;
+		}
+
+		.image-dele-container {
+			position: relative;
+			width: 140rpx;
+			height: 140rpx;
+			background-color: #f6f6f6;
+			border-radius: 15rpx;
+			padding: 12rpx;
+
+			view {
+				text-overflow: ellipsis;
+				overflow: hidden;
+				white-space: nowrap;
+				width: 100rpx;
+			}
+
+			.image {
+				width: 100rpx;
+				height: 100rpx;
+			}
+
+			.goods-select-name {
+				font-size: 24rpx;
+			}
+		}
+
+		.goods-select {
+			margin: 0 24rpx;
+			height: 218rpx;
+			padding-top: 10rpx;
+		}
+
+		.goods-select-empty {
+			height: 218rpx;
+			text-align: center;
+			line-height: 218rpx;
+		}
+
+		.goodContainer {
+			position: relative;
+
+			.numberContainer {
+				width: 35rpx;
+				height: 35rpx;
+				border-radius: 100%;
+				color: #fff;
+				background-color: red;
+				position: absolute;
+				right: -5rpx;
+				top: -5rpx;
+			}
+		}
+
+		.minus {
+			width: 22px;
+			height: 22px;
+			border-width: 1px;
+			border-color: #E6E6E6;
+			border-style: solid;
+			border-top-left-radius: 100px;
+			border-top-right-radius: 100px;
+			border-bottom-left-radius: 100px;
+			border-bottom-right-radius: 100px;
+			@include flex;
+			justify-content: center;
+			align-items: center;
+		}
+
+
+
+		.plus {
+			width: 18px;
+			height: 18px;
+			background-color: #FF0000;
+			border-radius: 50%;
+			/* #ifndef APP-NVUE */
+			display: flex;
+			/* #endif */
+			justify-content: center;
+			align-items: center;
+		}
+	}
+</style>

+ 184 - 0
pages/order/userInfo.vue

@@ -0,0 +1,184 @@
+<template>
+	<view class="container">
+		<u-navbar leftIconColor="#fff" titleStyle="color:#fff;fontSize:36rpx;" :autoBack="true" bgColor="#2C6FF3"
+			:placeholder="true" title="会员信息"></u-navbar>
+		<view class="content">
+			<view class="xy-card info-box">
+				<view class="flex justify-start">
+					<view class="flex justify-start">
+						<view class="name">会员id:</view>
+						<view>{{info.memberId}}</view>
+					</view>
+					<view style="margin-left: 24rpx;">
+						<xbutton size="mini" @tap="copy(info.memberId)">复制</xbutton>
+					</view>
+				</view>
+				<view class="flex justify-start">
+					<view class="name">昵称:</view>
+					<view>{{info.member.wechatNickname||info.member.alipayNickname||'/'}}</view>
+				</view>
+				<view class="flex justify-start phone">
+					<view class="flex justify-start">
+						<view class="name">手机:</view>
+						<view>{{info.member.tel}}</view>
+					</view>
+					<view style="margin-left: 24rpx;">
+						<xbutton size="mini" @click="block"  v-if="info.isBlacklist">解除黑名单</xbutton>
+						<xbutton size="mini" @click="block" v-else>拉黑</xbutton>
+					</view>
+				</view>
+			</view>
+		</view>
+		<xpopup :show="show" @close="close" @confirm="submit" :showBtn="true" title="黑名单">
+			<!-- 拉黑 -->
+			<view class="pop-content restart">
+				是否确定拉黑该用户?
+			</view>
+		</xpopup>
+	</view>
+</template>
+
+<script>
+	import {
+		userInfo,
+		setBlacklist,
+		removeBlackList
+	} from '@/api/order/order.js'
+	export default {
+		data() {
+			return {
+				id: null,
+				info: {},
+				show: false,
+			}
+		},
+
+		onLoad(o) {
+			this.id = o.id;
+			this.getInfo()
+		},
+
+		methods: {
+			getInfo() {
+				userInfo({
+					memberId: this.id
+				}).then(res => {
+					this.info = res.data
+				})
+			},
+
+			copy(text) {
+				uni.setClipboardData({
+					data: text,
+					success: (data) => {
+						uni.showToast({
+							title: '复制成功'
+						})
+					},
+					fail: function(err) {
+
+					},
+					complete: function(res) {
+
+					}
+				})
+			},
+
+			block() {
+				// this.show = true
+				if(this.info.isBlacklist){ //已拉黑,解除
+					removeBlackList({
+						memberId: this.id
+					}).then(res => {
+						this.getInfo()
+						this.close()
+						this.$modal.showToast('解除成功~')
+					}).catch(err => {
+						this.close()
+					})
+				}else{ //拉黑
+					setBlacklist({
+						memberId: this.id
+					}).then(res => {
+						this.getInfo()
+						this.close()
+						this.$modal.showToast('拉黑成功~')
+					}).catch(err => {
+						this.close()
+					})
+				}
+			},
+
+			// 关闭弹框
+			close(e) {
+				this.show = false
+			},
+
+			// 弹框确定
+			submit() {
+				if(this.info.isBlacklist){ //已拉黑,解除
+					removeBlackList({
+						memberId: this.id
+					}).then(res => {
+						this.$modal.msg('解除成功~')
+						this.getInfo()
+						this.close()
+					}).catch(err => {
+						this.close()
+					})
+				}else{ //拉黑
+					setBlacklist({
+						memberId: this.id
+					}).then(res => {
+						this.$modal.msg('拉黑成功~')
+						this.getInfo()
+						this.close()
+					}).catch(err => {
+						this.close()
+					})
+				}
+			},
+		}
+	}
+</script>
+
+<style lang="scss">
+	.container {
+		.content {
+			padding: 0 24rpx;
+			overflow: hidden;
+
+			.info-box {
+				margin-top: 24rpx;
+
+				>view {
+					line-height: 60rpx;
+				}
+
+				.name {
+					width: 120rpx;
+					font-size: 28rpx;
+					font-weight: bold;
+					color: #333;
+				}
+			}
+
+			.phone {
+				text {
+					display: inline-block;
+					background-color: #5b5b5b;
+					color: #fff;
+					font-size: 22rpx;
+					padding: 0 12rpx;
+					border-radius: 6rpx;
+					margin-left: 12rpx;
+					line-height: 36rpx;
+				}
+			}
+		}
+
+		.pop-content {
+			padding: 24rpx;
+		}
+	}
+</style>

File diff suppressed because it is too large
+ 0 - 0
pages/point/components/Winglau14-lotusAddress/Winglau14-lotusAddress.js


+ 360 - 0
pages/point/components/Winglau14-lotusAddress/Winglau14-lotusAddress.vue

@@ -0,0 +1,360 @@
+<template>
+	<!--地址picker-->
+	<view>
+		<view :status="checkStatus" v-if="lotusAddressData.visible" class="lotus-address-mask" @tap="cancelPicker"></view>
+		<view v-if="lotusAddressData.visible"
+			:class="lotusAddressData.visible?'lotus-address-box':'lotus-address-box lotus-address-box-out'">
+			<view class="lotus-address-action">
+				<text @tap="cancelPicker" class="lotus-address-action-cancel">取消</text>
+				<text @tap="chosedVal" class="lotus-address-action-affirm">确认</text>
+			</view>
+			<view class="lotus-address-picker-box">
+				<!--省-->
+				<scroll-view scroll-y :scroll-into-view="'pid'+pChoseIndex" class="lotus-address-picker-box-item">
+					<view @tap="clickPicker(0,pIndex,pItem);" :id="'pid'+pIndex"
+						:class="pIndex === pChoseIndex?'lotus-address-picker lotus-address-picker2':'lotus-address-picker'"
+						v-for="(pItem,pIndex) in province" :key="pIndex">{{pItem}}</view>
+				</scroll-view>
+				<!--市-->
+				<scroll-view scroll-y :scroll-into-view="'cid'+cChoseIndex" class="lotus-address-picker-box-item">
+					<view @tap="clickPicker(1,cIndex,cItem);" :id="'cid'+cIndex"
+						:class="cIndex === cChoseIndex?'lotus-address-picker lotus-address-picker2':'lotus-address-picker'"
+						v-for="(cItem,cIndex) in city" :key="cIndex">{{cItem}}</view>
+				</scroll-view>
+				<!--区-->
+				<scroll-view scroll-y :scroll-into-view="'tid'+tChoseIndex" class="lotus-address-picker-box-item">
+					<view @tap="clickPicker(2,tIndex,tItem);" :id="'tid'+tIndex"
+						:class="tIndex === tChoseIndex?'lotus-address-picker lotus-address-picker2':'lotus-address-picker'"
+						v-for="(tItem,tIndex) in town" :key="tIndex">{{tItem}}</view>
+				</scroll-view>
+				<!--区END-->
+			</view>
+		</view>
+	</view>
+
+	<!--地址picker END-->
+</template>
+
+<script>
+	import {
+		lotusAddressJson
+	} from "./Winglau14-lotusAddress.js";
+	export default {
+		props: ['lotusAddressData'],
+		data() {
+			return {
+				visible: false,
+				province: [],
+				city: [],
+				town: [],
+				provinceName: '',
+				cityName: '',
+				townName: '',
+				type: 0, //0新增1编辑
+				pChoseIndex: -1,
+				cChoseIndex: -1,
+				tChoseIndex: -1
+			};
+		},
+		methods: {
+			//取消
+			cancelPicker() {
+				this.$emit('close')
+			},
+			//获取最后选择的省市区的值
+			chosedVal() {
+				this.type = 1;
+				const provinceCode = this.getTarId(this.provinceName);
+				const cityCode = this.getTarId(this.cityName);
+				const townCode = this.getTarId(this.townName);
+				this.visible = false;
+				let id = null;
+				//省市区已选最末一项id
+				if (this.provinceName && this.cityName && this.townName) {
+					id = townCode
+				} else if (this.provinceName && this.cityName && !this.townName) {
+					id = cityCode
+				} else if (this.provinceName && !this.cityName && !this.townName) {
+					id = provinceCode
+				}
+				this.$emit("choseVal", {
+					province: this.provinceName,
+					provinceCode,
+					city: this.cityName,
+					cityCode,
+					town: this.townName,
+					townCode,
+					id,
+					visible: false
+				});
+			},
+			//获取省市区value
+			getTarId(name, type) {
+				let id = 0;
+				lotusAddressJson.map((item, index) => {
+					if (item.name === name) {
+						id = item.value;
+					}
+				});
+				return id;
+			},
+			//获取市数据
+			getCityArr(parentId) {
+				let city = [];
+				lotusAddressJson.map((item, index) => {
+					if (item.parent === parentId) {
+						city.push(item.name);
+					}
+				});
+				return city;
+			},
+			//获取区数据
+			getTownArr(parentId) {
+				let town = [];
+				lotusAddressJson.map((item, index) => {
+					if (index > 34 && item.parent === parentId) {
+						town.push(item.name);
+					}
+				});
+				return town;
+			},
+			//初始化数据
+			initFn() {
+				if (!this.province.length) {
+					lotusAddressJson.map((item, index) => {
+						if (item.parent <= 34) {
+							this.province.push(item.name);
+						}
+					});
+				}
+				//已选择省市区,高亮显示对应选择省市区
+				const p = this._props.lotusAddressData.provinceName;
+				const c = this._props.lotusAddressData.cityName;
+				const t = this._props.lotusAddressData.townName;
+				//已选省
+				if (p) {
+					this.pChoseIndex = this.getTarIndex(this.province, p);
+				}
+				//已选市
+				if (p && c) {
+					const pid = this.getTarId(p);
+					this.city = this.getCityArr(pid);
+					this.cChoseIndex = this.getTarIndex(this.city, c);
+				}
+				//已选区
+				if (p && c && t) {
+					const cid = this.getTarId(c);
+					this.town = this.getTownArr(cid);
+					this.tChoseIndex = this.getTarIndex(this.town, t);
+				}
+				//未选省市区
+				if (!p && !c && !t) {
+					this.pChoseIndex = -1;
+					this.cChoseIndex = -1;
+					this.tChoseIndex = -1;
+					this.city = [];
+					this.town = [];
+				}
+			},
+			//获取已选省市区
+			getChosedData() {
+				const pid = this.getTarId(this.provinceName, 'province');
+				this.city = this.getCityArr(pid);
+				const cid = this.getTarId(this.cityName, 'city');
+				this.town = this.getTownArr(cid);
+				//已选省市区获取对应index
+				if (this.provinceName) {
+					this.pChoseIndex = this.getTarIndex(this.province, this.provinceName);
+				}
+				if (this.cityName) {
+					this.cChoseIndex = this.getTarIndex(this.city, this.cityName);
+				}
+				if (this.townName) {
+					this.tChoseIndex = this.getTarIndex(this.town, this.townName);
+				}
+			},
+			//选择省市区交互
+			clickPicker(type, index, name) {
+				//省
+				if (type === 0) {
+					// this.pChoseIndex = this.pChoseIndex >= 0 ? -1 : index
+					// this.provinceName = this.pChoseIndex >= 0 ? name : "";
+					if (this.pChoseIndex === index) {
+						this.pChoseIndex = -1;
+						this.provinceName = "";
+					} else {
+						this.pChoseIndex = index;
+						this.provinceName = name;
+					}
+					this.cChoseIndex = -1;
+					this.tChoseIndex = -1;
+					this.cityName = '';
+					this.townName = '';
+				}
+				//市
+				if (type === 1) {
+					if (this.cChoseIndex === index) {
+						this.cChoseIndex = -1;
+						this.cityName = "";
+					} else {
+						this.cChoseIndex = index;
+						this.cityName = name;
+					}
+					this.tChoseIndex = -1;
+					this.townName = '';
+				}
+				//区
+				if (type === 2) {
+					if (this.tChoseIndex === index) {
+						this.tChoseIndex = -1;
+						this.townName = "";
+					} else {
+						this.tChoseIndex = index;
+						this.townName = name;
+					}
+				}
+				//获取省市区数据
+				this.getChosedData();
+			},
+			//获取已选省市区index
+			getTarIndex(arr, tarName) {
+				let cIndex = 0;
+				arr.map((item, index) => {
+					if (item === tarName) {
+						cIndex = index;
+					}
+				});
+				return cIndex;
+			}
+		},
+		computed: {
+			checkStatus() {
+				let t = null;
+				const _this = this;
+				if (!_this.visible) {
+					_this.visible = _this._props.lotusAddressData.visible;
+					//获取省市区
+					_this.provinceName = _this._props.lotusAddressData.provinceName;
+					_this.cityName = _this._props.lotusAddressData.cityName;
+					_this.townName = _this._props.lotusAddressData.townName;
+					//生成初始化数据
+					_this.initFn();
+					t = _this.visible;
+				}
+				return t;
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.lotus-address-picker {
+	  font-size: 26rpx;
+	  padding-top: 30rpx;
+	  overflow: hidden;
+	  text-overflow: ellipsis;
+	  display: -webkit-box;
+	  -webkit-line-clamp: 1;
+	  -webkit-box-orient: vertical;
+	  line-height: normal;
+	  padding-right: 30rpx;
+	  box-sizing: border-box;
+	}
+	.lotus-address-picker-box {
+	  /*display: -webkit-box;
+	  display: -webkit-flex;*/
+	  display: flex;
+	  align-items: center;
+	  justify-content: center;
+	  justify-content: flex-start;
+	  padding-top: 10rpx;
+	  padding-bottom: 10rpx;
+	}
+	.lotus-address-picker-box-item {
+	  height: 600upx;
+	  overflow-y: auto;
+	  width: 33.333%;
+	  padding-left: 20rpx;
+	  padding-right: 20rpx;
+	  box-sizing: border-box;
+	}
+	.lotus-address-picker2 {
+	  color: #2C6FF3;
+	  position: relative;
+	}
+	.lotus-address-picker2:after {
+	  content: '';
+	  position: absolute;
+	  right: 0;
+	  top: 65%;
+	  transform: translateY(-35%) rotate(-45deg);
+	  width: 20rpx;
+	  height: 10rpx;
+	  border-left-width: 4rpx;
+	  border-bottom-width: 4rpx;
+	  border-left-style: solid;
+	  border-bottom-style: solid;
+	  border-left-color: #2C6FF3;
+	  border-bottom-color: #2C6FF3;
+	}
+	.lotus-address-mask {
+	  position: fixed;
+	  left: 0;
+	  top: 0;
+	  width: 100%;
+	  height: 100%;
+	  z-index: 999;
+	  background: rgba(0, 0, 0, 0.5);
+	}
+	.lotus-address-box {
+	  background: #fff;
+	  position: fixed;
+	  left: 0;
+	  bottom: 0;
+	  width: 100%;
+	  height: auto;
+	  z-index: 10000;
+	}
+	.lotus-address-action {
+	  font-size: 30rpx;
+	  /*display: -webkit-box;
+	  display: -webkit-flex;*/
+	  display: flex;
+	  align-items: center;
+	  justify-content: center;
+	  justify-content: space-between;
+	  padding: 25rpx 30rpx;
+	  position: relative;
+	}
+	.lotus-address-action:after {
+	  content: " ";
+	  position: absolute;
+	  left: 0;
+	  top: 0;
+	  right: 0;
+	  height: 1px;
+	  border-top: 1px solid #eee;
+	  color: #eee;
+	  transform-origin: 0 0;
+	  transform: scaleY(0.5);
+	}
+	.lotus-address-action:before {
+	  content: " ";
+	  position: absolute;
+	  left: 0;
+	  bottom: 0;
+	  right: 0;
+	  height: 1px;
+	  border-bottom: 1px solid #eee;
+	  color: #eee;
+	  transform-origin: 0 100%;
+	  transform: scaleY(0.5);
+	}
+	.lotus-address-action-cancel {
+	  color: #969696;
+	}
+	.lotus-address-action-affirm {
+	  color: #2C6FF3;
+	}
+	
+</style>

+ 361 - 0
pages/point/lineDetail.vue

@@ -0,0 +1,361 @@
+<template>
+	<view class="container">
+		<u-navbar leftIconColor="#fff" titleStyle="color:#fff;fontSize:36rpx;" :autoBack="true" bgColor="#2C6FF3"
+			:placeholder="true" :title="lineName"></u-navbar>
+		<view class="content">
+			<view class="point">
+				<!-- <view class="area-pix">
+					<view class="point-name">
+						{{tagNamePicker}}
+					</view>
+					<xbutton width="140rpx" @click="chooseTag">选择标签</xbutton>
+				</view> -->
+				<view class="xy-card point-item" v-for="(item,index) in pointList" :key="item.id">
+					<view class="area-line">
+						{{item.placeName}}
+						<view>{{item.lineName}}</view>
+					</view>
+					<view class="point-content">
+						<view class="point-item-a">
+							<view>标签:</view>
+							<view>{{item.tagNames}}</view>
+						</view>
+						<view class="point-item-a">
+							<view>场景:</view>
+							<view>{{item.sceneNames}}</view>
+						</view>
+					</view>
+					<view class="point-btn">
+						<xbutton width="140rpx" bgColor="red" color="#fff" @click="delPoint(item)">删除
+						</xbutton>
+						<xbutton width="140rpx" style="margin-left:12rpx;" @click="add('editPoint',item)">编辑
+						</xbutton>
+						<!-- <xbutton width="140rpx">上传坐标</xbutton> -->
+					</view>
+				</view>
+				<view class="empty">
+					<u-empty v-if="pointList.length==0"></u-empty>
+				</view>
+				<u-loadmore :status="status" v-if="pointList.length>=1" />
+				<view class="btn">
+					<xbutton size="large" @click="add('addPoint')">新增点位</xbutton>
+				</view>
+			</view>
+
+			<!-- 弹框 -->
+			<xpopup :show="popShow" @close="close" @confirm="submit" showBtn :title="title">
+				<!-- 删除区域/线路/点位 -->
+				<view class="del-popup-content" v-if="title=='删除点位'">
+					确认删除?
+				</view>
+				<!-- 点位新增 -->
+				<!-- 	<view class="popup-content" v-if="title=='新增点位'">
+					<view>点位名称:</view>
+					<input type="text" v-model="pointName" />
+				</view> -->
+				<!-- 编辑点位 -->
+				<view class="popup-content edit-point" v-if="title=='新增点位'||title=='编辑点位'">
+					<view class="edit-point-item">
+						<view>场景:</view>
+						<u--input type="text" placeholder="多个场景逗号隔开" border="surround"
+							v-model="pointForm.sceneNames"></u--input>
+					</view>
+					<view class="edit-point-item">
+						<view>自定义标签:</view>
+						<u--input type="text" placeholder="多个标签逗号隔开" border="surround"
+							v-model="pointForm.tagNames"></u--input>
+					</view>
+					<view class="edit-point-item">
+						<view>点位名称:</view>
+						<u--input type="text" placeholder="请输入点位名称" border="surround"
+							v-model="pointForm.placeName"></u--input>
+					</view>
+				</view>
+
+			</xpopup>
+		</view>
+	</view>
+</template>
+
+<script>
+	import lotusAddress from "./components/Winglau14-lotusAddress/Winglau14-lotusAddress.vue";
+	import tkiTree from "@/components/tki-tree/tki-tree.vue";
+	import {
+		pointPage,
+		pointSave,
+		pointDel
+	} from "@/api/point/point"
+	export default {
+		components: {
+			lotusAddress,
+			tkiTree
+		},
+		data() {
+			return {
+				tabList: [{
+						name: '区域'
+					},
+					{
+						name: '线路'
+					},
+					{
+						name: '点位'
+					}
+				],
+				height: "0",
+
+				flod: false, //折叠/打开
+				list: [],
+
+				popShow: false, //新增线路弹框
+
+				pointName: '', //点位名称
+				id: null, //点选节点id
+
+				page: 1, //当前分页
+				size: 10, //分页数据条数
+				pointList: [], //点位列表
+				noMore: false, //没有更多数据
+				status: 'loadmore', //加载更多
+
+				lineNamePicker: '请选择线路', //线路选择
+				tagNamePicker: '请选择标签', //标签选择
+				lineOptions: [], //线路options
+				tagOptions: [],
+				columns: [], //picker选项
+
+
+				lineIdPicker: undefined, //picker选中线路id
+				tagIdPicker: undefined, //picker选中tag id
+				pickerType: undefined, //picker选择器类型
+
+				pointForm: { //新增点位
+					placeName: undefined,
+					sceneNames: undefined,
+					tagNames: undefined,
+				},
+				
+				lineId:null,
+				lineName:null,
+				title:'',
+			}
+		},
+
+		onLoad(o) {
+			this.lineId=o.id;
+			this.lineName=o.lineName;
+			this.getPointList()
+		},
+
+		methods: {
+			//重置点位分页请求参数
+			reset() {
+				this.page = 1;
+				this.pointList = [];
+				this.noMore = false;
+				this.status = "loadmore";
+			},
+
+			//获取点位分页
+			getPointList() {
+				pointPage({
+					page: {
+						current: this.page,
+						size: this.size,
+					},
+					lineId: this.lineId
+				}).then(res => {
+					let data = res.data.records;
+					this.pointList = this.pointList.concat(data)
+					if (data && data.length < 10) {
+						this.status = "nomore";
+						this.noMore = true;
+					} else {
+						this.status = "loading";
+					}
+				})
+			},
+
+			//滚动到底加载更多
+			onReachBottom() {
+				if (this.noMore) return
+				this.page++
+				this.getPointList()
+			},
+
+			//新增点位
+			add(type, item) {
+				if (type == 'addPoint') { //新增点位
+					this.title = '新增点位'
+				} else if (type == 'editPoint') { //新增点位
+					this.title = '编辑点位'
+					this.pointForm = JSON.parse(JSON.stringify(item))
+				}
+				this.popShow = true
+			},
+			
+			//删除点位
+			delPoint(item) {
+				this.title = "删除点位"
+				this.id = item.id
+				this.popShow = true
+			},
+
+			//弹框关闭
+			close() {
+				this.popShow = false
+			},
+
+			//弹框提交
+			submit() {
+				if (this.title == '删除点位') {
+					pointDel({
+						id: this.id
+					}).then(res => {
+						this.$modal.msg('删除成功~')
+						this.reset()
+						this.getPointList()
+					})
+				}else{
+					this.addOrEditPoint()
+				}
+				this.close()
+				this.id = null
+			},
+
+			// 新增编辑点位
+			addOrEditPoint() {
+				if (!this.pointForm.sceneNames) {
+					this.$modal.msg('请填写场景!')
+					return
+				}
+				if (!this.pointForm.tagNames) {
+					this.$modal.msg('请填写标签!')
+					return
+				}
+				if (!this.pointForm.placeName) {
+					this.$modal.msg('请填入点位名称!')
+					return
+				}
+				let params = {
+					...this.pointForm,
+					lineId: this.lineId
+				}
+				pointSave(params).then(res => {
+					this.$modal.msg('成功~')
+					this.pointForm = {
+						placeName: undefined,
+						sceneNames: undefined,
+						tagNames: undefined,
+					}
+					this.reset();
+					this.getPointList();
+				})
+			},
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.container {
+		.tab-wrap {
+			padding: 0 24rpx;
+			background-color: #fff;
+		}
+
+		.content {
+			// 点位
+			.point {
+				padding: 24rpx 24rpx 88rpx;
+			
+				.point-item {
+					margin-bottom: 24rpx;
+			
+					.area-line {
+						display: flex;
+						flex-flow: row nowrap;
+						justify-content: space-between;
+					}
+			
+					.point-content {
+						border-radius: 8rpx;
+						background-color: rgb(245, 248, 251);
+						box-sizing: border-box;
+						padding: 24rpx 12rpx;
+						font-size: 26rpx;
+						margin-top: 18rpx;
+						display: flex;
+						flex-flow: row wrap;
+			
+						.point-item-a {
+							width: 80%;
+							display: flex;
+							flex-flow: row nowrap;
+							line-height: 50rpx;
+						}
+					}
+			
+					.point-btn {
+						display: flex;
+						flex-direction: row;
+						justify-content: flex-end;
+						margin-top: 12rpx;
+					}
+				}
+			}
+		}
+	}
+
+	.empty {
+		margin-top: 40%;
+	}
+
+	.btn {
+		width: 100%;
+		position: fixed;
+		left: 0;
+		bottom: 24rpx;
+		padding: 0 24rpx;
+	}
+
+	.popup-content {
+		padding: 36rpx 24rpx;
+		display: flex;
+		flex-flow: row nowrap;
+		justify-content: flex-start;
+		align-items: center; 
+
+		input {
+			border: 1rpx solid #999;
+			border-radius: 6rpx;
+			width: 530rpx;
+			padding: 0 24rpx;
+		}
+
+		&.edit-point {
+			flex-direction: column;
+			align-items: flex-start;
+
+			.edit-point-item{
+				width: 100%;
+				padding-left: 170rpx;
+				position: relative;
+				&+.edit-point-item{
+					margin-top: 12rpx;
+				}
+				>view:nth-child(1){
+					position: absolute;
+					left:0;
+					top:50%;
+					transform: translateY(-50%);
+				}
+			}
+		}
+	}
+
+	.del-popup-content {
+		padding: 36rpx 24rpx;
+		text-align: center;
+		font-size: 32rpx;
+	}
+</style>

+ 407 - 0
pages/point/point.vue

@@ -0,0 +1,407 @@
+<template>
+	<view class="container">
+		<u-navbar leftIconColor="#fff" titleStyle="color:#fff;fontSize:36rpx;" :autoBack="true" bgColor="#2C6FF3"
+			:placeholder="true" title="点位管理"></u-navbar>
+		<view class="total">
+			<view class="total-item">
+				<view class="num">
+					{{total.regionNum||'0'}}
+				</view>
+				<view class="name">
+					区域总数
+				</view>
+			</view>
+			<view class="total-item">
+				<view class="num">
+					{{total.lineNum||'0'}}
+				</view>
+				<view class="name">
+					线路总数
+				</view>
+			</view>
+			<view class="total-item">
+				<view class="num">
+					{{total.placeNum||'0'}}
+				</view>
+				<view class="name">
+					点位总数
+				</view>
+			</view>
+		</view>
+		<!-- 		<view class="search">
+			<u-search animation placeholder="请输入区域/线路搜索" :showAction="false">
+			</u-search>
+		</view> -->
+		<view class="content">
+			<view class="xy-card" v-for="(item,index) in list" :key="item.mercRegionId">
+				<view class="flex justify-between">
+					<view class="eq-line-title">
+						{{item.mergename}}
+					</view>
+					<xbutton size="mini" @click="addLine(item.name)">新增线路</xbutton>
+				</view>
+				<view v-if="item.lineList&&item.lineList.length>0">
+					<block v-for="(item1,index1) in item.lineList" :key="item1.id">
+						<view class="eq-wrap eq-item"
+							@click="$tab.navigateTo(`/pages/point/lineDetail?id=${item1.id}&lineName=${item1.lineName}`)">
+							<view class="eq-name flex justify-between">
+								<view class="eq-title" v-if="item1.lineName">{{item1.lineName}}<text
+										style="color: #666;">({{item1.id}})</text></view>
+								<view class="eq-title" v-else>{{item1.lineName}}</view>
+							</view>
+							<view class="eqeq-type">
+								<view>
+									线路管理员:
+								</view>
+								<view>
+									{{item1.userInfoName||'/'}}
+								</view>
+							</view>
+
+							<view class="arrow-right">
+								<u-icon name="arrow-right" size="14"></u-icon>
+							</view>
+						</view>
+					</block>
+				</view>
+				<view class="none" v-else>
+					<u-empty text="当前区域没有线路" mode="search"></u-empty>
+				</view>
+				<!-- <view class="more" @click="$tab.navigateTo('/pages/equipment/search')">
+					查看全部
+				</view> -->
+			</view>
+
+			<view class="empty" v-if="list.length==0">
+				<u-empty></u-empty>
+			</view>
+		</view>
+
+		<view class="btn safe-bottom">
+			<xbutton size="large" @click="lotusAddressData.visible=true">新增区域</xbutton>
+		</view>
+
+		<!-- 级联地址 -->
+		<lotus-address v-on:choseVal="addressConfirm" @close="addressClose" :lotusAddressData="lotusAddressData">
+		</lotus-address>
+
+		<!-- 弹框 -->
+		<xpopup :show="popShow" @close="close" @confirm="submit" showBtn title="新增线路">
+			<!-- 线路新增 -->
+			<view class="popup-content">
+				<view>线路名称:</view>
+				<view>
+					<u--input placeholder="请输入内容" border="surround" v-model="lineName"></u--input>
+				</view>
+			</view>
+		</xpopup>
+	</view>
+</template>
+
+<script>
+	import {
+		allLineWithRegion,
+		areaSave
+	} from "@/api/point/area.js"
+	import {
+		lineSave,
+	} from "@/api/point/line"
+	import lotusAddress from "./components/Winglau14-lotusAddress/Winglau14-lotusAddress.vue";
+	export default {
+		components: {
+			lotusAddress
+		},
+		data() {
+			return {
+				list: [],
+				total: {
+					lineNum: 0,
+					placeNum: 0,
+					regionNum: 0,
+				},
+
+				lotusAddressData: {
+					visible: false,
+					provinceName: '',
+					cityName: '',
+					townName: '',
+				},
+
+				popShow: false,
+				lineName: '',
+				regionName: ''
+			}
+		},
+
+		created() {
+			// this.getTotalData()
+			this.getList()
+		},
+
+		methods: {
+			onshow() {
+				this.getList()
+			},
+
+			search() {
+
+			},
+
+			getList() {
+				allLineWithRegion({}).then(res => {
+					this.list = res.data.data;
+					this.total = {
+						lineNum: res.data.lineNum,
+						placeNum: res.data.placeNum,
+						regionNum: res.data.regionNum,
+					}
+				})
+			},
+
+			//级联地址选择确认
+			addressConfirm(res) {
+				if (res.id === null) {
+					this.$modal.msg('请选择区域!');
+					return
+				}
+				this.addArea(res.id)
+				this.addressClose() //关闭弹框
+			},
+
+			// 级联地址取消
+			addressClose() {
+				this.lotusAddressData.visible = false;
+			},
+
+			//新增区域
+			addArea(id) {
+				areaSave({
+					ids: [id]
+				}).then(res => {
+					this.$modal.msg('新增成功~')
+					this.getList()
+				})
+			},
+
+			addLine(e) {
+				this.regionName = e
+				this.popShow = true
+			},
+
+			//弹框关闭
+			close() {
+				this.popShow = false
+			},
+
+			//弹框提交
+			submit() {
+				lineSave({
+					regionName: this.regionName,
+					lineName: this.lineName
+				}).then(res => {
+					this.$modal.msg('新增成功~')
+					this.lineName = '';
+					this.getList();
+				})
+				this.close()
+			},
+		}
+	}
+</script>
+
+<style lang="scss">
+	.container {
+		.nav-style {
+			font-size: 32rpx;
+			font-weight: bold;
+			color: #fff;
+		}
+
+		.search {
+			padding: 24rpx 24rpx;
+			background-color: #fff;
+		}
+
+		.total {
+			display: flex;
+			flex-flow: row nowrap;
+			justify-content: space-around;
+			align-items: center;
+			text-align: center;
+			color: #fff;
+			background-color: #2C6FF3;
+			padding: 40rpx 24rpx;
+
+			.total-item {
+				.num {
+					font-weight: bold;
+					font-size: 52rpx;
+				}
+
+				.name {
+					font-size: 26rpx;
+				}
+			}
+		}
+
+		.content {
+			padding: 24rpx;
+			padding-bottom: calc(106rpx + env(safe-area-inset-bottom) / 2);
+
+			.xy-card {
+				margin-bottom: 24rpx;
+			}
+
+			.eq-line-title {
+				font-size: 32rpx;
+				padding-bottom: 24rpx;
+				position: relative;
+			}
+
+			.eq-item {
+				&+.eq-item {
+					margin-top: 12rpx;
+				}
+			}
+
+			.eq-wrap {
+				border-radius: 8rpx;
+				background-color: rgb(245, 248, 251);
+				box-sizing: border-box;
+				padding: 24rpx 12rpx;
+				font-size: 26rpx;
+				position: relative;
+
+				.arrow-right {
+					position: absolute;
+					right: 12rpx;
+					top: 24rpx;
+				}
+
+
+				.eq-name {
+					font-size: 32rpx;
+					font-weight: bold;
+					margin-bottom: 24rpx;
+					position: relative;
+
+					>.eq-title {
+						width: 420rpx;
+
+						>text {
+							font-size: 30rpx;
+							color: #666;
+							font-weight: normal;
+						}
+					}
+
+					.eq-status-box {
+						float: right;
+						position: absolute;
+						right: 0;
+						top: 0;
+					}
+
+					.eq-status {
+						font-size: 28rpx;
+						color: #666;
+						font-weight: normal;
+						margin-left: 12rpx;
+
+						>text {
+							display: inline-block;
+							background-color: #666;
+							width: 16rpx;
+							height: 16rpx;
+							border-radius: 16rpx;
+							margin-right: 12rpx;
+
+						}
+
+						&.online {
+							color: #f56c6c;
+
+							>text {
+								background-color: green;
+							}
+						}
+					}
+				}
+			}
+
+			.eqeq-type {
+				display: flex;
+				flex-direction: row;
+				align-items: center;
+				font-size: 28rpx;
+
+				>view:nth-child(1) {
+					color: #000;
+					width: 200rpx;
+				}
+
+				>view:nth-child(2) {
+					color: #666;
+					padding-left: 6rpx;
+				}
+			}
+
+			.eqeq-type+.eqeq-type {
+				margin-top: 16rpx;
+			}
+
+			.status {
+				width: 130rpx;
+				height: 120rpx;
+				box-sizing: border-box;
+				border-radius: 120rpx;
+				// border: 6rpx solid #2C6FF3;
+				text-align: center;
+				display: flex;
+				flex-flow: column;
+				justify-content: space-around;
+				align-items: center;
+				position: absolute;
+				right: 12rpx;
+				bottom: 24rpx;
+
+				.s-name {
+					font-size: 28rpx;
+					padding-top: 20rpx;
+					font-weight: bold;
+				}
+
+				.s-num {
+					font-size: 32rpx;
+					padding-bottom: 20rpx;
+				}
+			}
+		}
+
+		.empty {
+			margin: 40% auto 0;
+		}
+
+		.btn {
+			width: 100%;
+			position: fixed;
+			left: 0;
+			bottom: 24rpx;
+			padding: 0 24rpx;
+		}
+
+		.popup-content {
+			padding: 36rpx 24rpx;
+			display: flex;
+			flex-flow: row nowrap;
+			justify-content: flex-start;
+			align-items: center;
+			>view:nth-child(1){
+				width:150rpx;
+			}
+			>view:nth-child(2){
+				width: 600rpx;
+			}
+		}
+	}
+</style>

+ 394 - 0
pages/replenish/invSearch.vue

@@ -0,0 +1,394 @@
+<template>
+	<view class="container">
+		<u-navbar leftIconColor="#fff" titleStyle="color:#fff;fontSize:36rpx;" :autoBack="true" bgColor="#2C6FF3"
+			:placeholder="true" title="库存管理"></u-navbar>
+
+		<view class="tab-wrap">
+			<view class="tab">
+				<u-tabs :list="tabList" :activeStyle="{color: '#333',fontWeight: 'bold',fontSize:'36rpx'}"
+					:inactiveStyle="{fontSize:'32rpx'}" :scrollable="false" :current="current" @click="tabClick"
+					lineColor="#2C6FF3">
+				</u-tabs>
+			</view>
+		</view>
+
+		<view class="flex align-center justify-end screen-container">
+			<view style="margin-right: 24rpx;" @click="sortClick">{{sortName}}</view>
+			<view class="flex align-center justify-center" @tap="screen">
+				<view class="" style="font-size: 28rpx;font-weight: 500;color: #333333;">筛选</view>
+				<image src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/screen.png"
+					style="width: 32rpx;height: 32rpx;margin-left: 12rpx;" mode="widthFix"></image>
+			</view>
+		</view>
+
+
+
+		<scroll-view class="scrollview" :scroll-with-animation="true" scroll-y lower-threshold="100"
+			@scrolltolower="scrolltolower" :style="{height:fullHeight}">
+			<view v-if="list.length>0">
+				<block v-for="(item,index) in list" :key="index">
+					<view class="equipment-container" @click="details(item)">
+						<view class="flex align-center justify-between">
+							<view class="title" v-if="current==1">
+								<view v-if="item.deviceName">{{item.deviceName}}<text>({{item.deviceId}})</text></view>
+								<view v-else>{{item.deviceId}}</view>
+							</view>
+							<view class="title" v-else>{{item.goodsName}}</view>
+							<view>
+								<u-icon name="arrow-right" size="14"></u-icon>
+							</view>
+						</view>
+
+						<view class="order-detail-item">
+							<view>缺货:</view>{{item.lackSum}}
+						</view>
+
+						<view class="order-detail-item">
+							<view>库存:</view>{{item.stockSum}}
+						</view>
+
+						<view class="order-detail-item">
+							<view>容量:</view>{{item.capacitySum}}
+						</view>
+
+						<view class="order-detail-item percent">
+							<view>缺货占比:</view>{{item.percent}}%
+						</view>
+					</view>
+				</block>
+			</view>
+			<view v-else class='empty'>
+				<u-empty mode="data" text="数据为空"></u-empty>
+			</view>
+		</scroll-view>
+
+		<xpopup :show="screenShow" @close="close" @confirm="sure" :showBtn="true" title="筛选">
+			<view class="popup-container">
+				<view class="pop-item-name">
+					机器名称/机器编号:
+				</view>
+				<view class='martop'>
+					<u-checkbox-group v-model="searchQuery.deviceIdList" placement="row" @change="checkboxChange">
+						<u-checkbox :customStyle="{marginBottom: '8px',marginRight: '12px'}"
+							v-for="(item, index) in checkboxList" :key="item.id" :label="item.name" :name="item.id">
+						</u-checkbox>
+					</u-checkbox-group>
+				</view>
+				<view class="pop-item-name" style="margin-top: 30rpx;">
+					商品名称:
+				</view>
+				<view class='martop'>
+					<u--input placeholder="商品名称" v-model="searchQuery.goodsName" border="surround"></u--input>
+				</view>
+			</view>
+		</xpopup>
+		<u-action-sheet :show="actionSheetShow" :actions="actions" :title="title" @close="actionSheetShow = false"
+			@select="actionsheetSelect($event)"></u-action-sheet>
+	</view>
+</template>
+
+<script>
+	import {
+		stockByDevice, //按设备分组
+		stockByGoods, // 按商品分组
+	} from '@/api/replenishment/replenishment.js'
+
+	export default {
+		data() {
+			return {
+				sortName: '缺货率从多到少',
+				searchQuery: {
+					deviceIdList: [],
+					goodsName: null
+				},
+				screenShow: false,
+				list: [],
+				loadmoreStatus: 'loadmore',
+				fullHeight: 0,
+				tabList: [{
+						name: '商品'
+					},
+					{
+						name: '机器'
+					}
+				],
+				current: 0,
+
+				actionSheetShow: false,
+				actions: [{
+						type: '1',
+						name: '缺货率从多到少'
+					},
+					{
+						type: '2',
+						name: '缺货率从少到多'
+					},
+					{
+						type: '3',
+						name: '缺货数从多到少'
+					},
+					{
+						type: '4',
+						name: '缺货数从少到多'
+					}
+				],
+
+				checkboxList: [],
+			}
+		},
+		onLoad() {
+			let _this = this;
+			const query = uni.createSelectorQuery().in(this);
+			query.select(".scrollview").boundingClientRect((data) => {
+				uni.getSystemInfo({
+					success(res) {
+						// 针对iPhone X等机型底部安全距离做适配
+						const model = res.model;
+						const modelInclude = [
+							"iPhone X",
+							'iPhone XR',
+							"iPhone XS",
+							"iPhone XS MAX",
+							"iPhone 12/13 mini",
+							"iPhone 12/13 (Pro)",
+							"iPhone 12/13 Pro Max",
+							"iPhone 14 Pro Max"
+						];
+						let safeDistance = modelInclude.includes(model)
+						//动态设置商品区域高度
+						console.log(res.windowHeight, data.top)
+						if (safeDistance) {
+							_this.fullHeight = res.windowHeight - data.top - 40 + 'px';
+						} else {
+							_this.fullHeight = res.windowHeight - data.top + 'px';
+						}
+					},
+				});
+			}).exec();
+
+			this.search()
+			this.getDeviceList()
+		},
+		methods: {
+			search() {
+				let params = {
+					"deviceIdList": this.searchQuery.deviceIdList,
+					"goodsName": this.searchQuery.goodsName,
+					"orderBy": '',
+					"orderByKey": '',
+				}
+				switch (this.sortName) {
+					case '缺货率从多到少':
+						params.orderBy = 'desc';
+						params.orderByKey = 'percent'
+						break;
+					case '缺货率从少到多':
+						params.orderBy = 'asc';
+						params.orderByKey = 'percent'
+						break;
+					case '缺货数从多到少':
+						params.orderBy = 'desc';
+						params.orderByKey = 'lackSum'
+						break;
+					case '缺货数从少到多':
+						params.orderBy = 'asc';
+						params.orderByKey = 'lackSum'
+						break;
+					default:
+						break;
+				}
+
+				if (this.current == 0) { //商品
+					this.getListA(params)
+				} else { //机器
+					this.getListB(params)
+				}
+			},
+			
+			getDeviceList(){
+				deviceList().then(res => {
+					this.checkboxList=res.data
+				})
+			},
+
+			//按商品分组
+			getListA(params) {
+				stockByGoods(params).then(res => {
+					if (res.code == 200) {
+						if (res.data && res.data.length > 0) {
+							this.list = res.data
+						} else {
+							this.list = []
+						}
+					} else {
+						this.list = []
+					}
+				})
+			},
+
+			//按设备分组 
+			getListB(params) {
+				stockByDevice(params).then(res => {
+					if (res.code == 200) {
+						if (res.data && res.data.length > 0) {
+							this.list = res.data
+						} else {
+							this.list = []
+						}
+					} else {
+						this.list = []
+					}
+				})
+			},
+
+			//点击筛选
+			screen() {
+				this.screenShow = true
+			},
+
+			close() {
+				this.screenShow = false
+			},
+
+			confirm(e) {
+				this.show = false
+				this.search()
+			},
+
+			sortClick() {
+				this.actionSheetShow = true;
+			},
+
+			actionsheetSelect(e) {
+				this.sortName = e.name
+			},
+
+			checkboxChange(e) {
+				this.searchQuery.deviceIdList = e
+			},
+
+			sure() {
+				this.close()
+				this.search()
+			},
+
+			// 时间选择
+			timeSubmit(e) {
+				this.searchQuery.orderDate = uni.$u.timeFormat(e.value, 'yyyy-mm-dd')
+				this.timeShow = false
+				this.reset()
+				this.getpage()
+				//改为分页接口获取 this.getCountData()
+			},
+
+			tabClick(e) {
+				this.current = e.index
+				this.search()
+			},
+
+			details(item) {
+				let id = this.current == 0 ? item.goodsId : item.deviceId;
+				let title = this.current == 0 ? item.goodsName : item.deviceName ? item.deviceName : item.deviceId;
+				this.$tab.navigateTo(`/pages/replenish/invSearchDetail?type=${this.current}&id=${id}&title=${title}`)
+			},
+		}
+	}
+</script>
+<style scoped lang="scss">
+	.container {
+		.empty {
+			margin-top: 40%;
+		}
+
+		.martop {
+			margin-top: 20rpx;
+		}
+
+		.search {
+			padding: 24rpx 13rpx;
+			background-color: #fff;
+		}
+
+		.tab-wrap {
+			background-color: #fff;
+
+			.tab {
+				// width: 40%;
+			}
+		}
+
+		.marleft {
+			margin-left: 10rpx;
+		}
+
+		.scrollview {
+			overflow: hidden;
+		}
+
+		.screen-container {
+			background-color: #fff;
+			padding: 30rpx 13rpx;
+
+			>view:nth-child(1) {
+				font-size: 28rpx;
+				font-weight: 500;
+			}
+		}
+
+		.equipment-container {
+			margin: 13rpx 13rpx 0;
+			padding: 12rpx 20rpx 24rpx;
+			border-radius: 14rpx;
+			background-color: #fff;
+			box-shadow: 0px 0px 10rpx 0px rgba(174, 201, 255, 0.2);
+			position: relative;
+
+			.sb-box {
+				padding: 24rpx 18rpx;
+				background-color: #f5f8fb;
+				border-radius: 8rpx;
+				margin-top: 12rpx;
+			}
+
+			.title {
+				height: 60rpx;
+				line-height: 60rpx;
+				font-size: 32rpx;
+				font-weight: bold;
+				color: #333;
+
+				>text {
+					font-size: 24rpx;
+					color: #333;
+				}
+			}
+
+			.order-detail-item {
+				font-size: 28rpx;
+				margin-top: 12rpx;
+				color: #777;
+
+				>view {
+					display: inline-block;
+					width: 170rpx;
+				}
+			}
+
+			.percent {
+				position: absolute;
+				right: 40rpx;
+				bottom: 20rpx;
+				line-height: 140rpx;
+				color: red;
+
+				>view {
+					color: #777;
+				}
+			}
+		}
+
+		.popup-container {
+			padding: 20rpx;
+		}
+	}
+</style>

+ 182 - 0
pages/replenish/invSearchDetail.vue

@@ -0,0 +1,182 @@
+<template>
+	<view class="container">
+		<u-navbar leftIconColor="#fff" titleStyle="color:#fff;fontSize:36rpx;" :autoBack="true" bgColor="#2C6FF3"
+			:placeholder="true" :title="title"></u-navbar>
+		<view class="content">
+			<scroll-view class="scrollview" :scroll-with-animation="true" scroll-y lower-threshold="100"
+				@scrolltolower="scrolltolower" :style="{height:fullHeight}">
+				<view v-if="list.length>0">
+					<block v-for="(item,index) in list" :key="index">
+						<view class="equipment-container" @click="details(item)">
+							<view class="flex align-center justify-between">
+								<view class="title" v-if="type==0">
+									{{item.deviceName}}<text>{{item.deviceId}}</text>
+								</view>
+								<view class="title" v-else>{{item.goodsName}}</view>
+							</view>
+							
+							<view class="flex align-center">
+								<view class="" v-if="type==1" style="margin-right: 30rpx;margin-top: 20rpx;">
+									<u--image width="110rpx" height="110rpx" :src="item.cover" mode="widthFix" :lazy-lord="true">
+									</u--image>
+								</view>
+								<view class="">
+									<view class="order-detail-item">
+										<view>缺货:</view>{{item.lackSum}}
+									</view>
+														
+									<view class="order-detail-item">
+										<view>库存:</view>{{item.stockSum}}
+									</view>
+														
+									<view class="order-detail-item">
+										<view>容量:</view>{{item.capacitySum}}
+									</view>
+														
+									<view class="order-detail-item percent">
+										<view>缺货占比:</view>{{item.percent}}%
+									</view>
+								</view>
+							</view>
+						</view>
+					</block>
+				</view>
+				<view v-else class='empty'>
+					<u-empty mode="data" text="数据为空"></u-empty>
+				</view>
+			</scroll-view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import {
+		stockByGoodsDetail,
+		stockByDeviceDetail
+	} from '@/api/replenishment/replenishment.js'
+
+	export default {
+		data() {
+			return {
+				fullHeight: 0,
+				id: null,
+				type: null,
+				list: {},
+				title:null
+			}
+		},
+		onLoad(o) {
+			let _this = this;
+			const query = uni.createSelectorQuery().in(this);
+			query.select(".scrollview").boundingClientRect((data) => {
+				uni.getSystemInfo({
+					success(res) {
+						// 针对iPhone X等机型底部安全距离做适配
+						const model = res.model;
+						const modelInclude = [
+							"iPhone X",
+							'iPhone XR',
+							"iPhone XS",
+							"iPhone XS MAX",
+							"iPhone 12/13 mini",
+							"iPhone 12/13 (Pro)",
+							"iPhone 12/13 Pro Max",
+							"iPhone 14 Pro Max"
+						];
+						let safeDistance = modelInclude.includes(model)
+						//动态设置商品区域高度
+						console.log(res.windowHeight, data.top)
+						if (safeDistance) {
+							_this.fullHeight = res.windowHeight - data.top - 40 + 'px';
+						} else {
+							_this.fullHeight = res.windowHeight - data.top + 'px';
+						}
+					},
+				});
+			}).exec();
+
+			this.id = o.id
+			this.type = o.type
+			this.title=o.title
+			if (this.type == 0) {
+				this.getDetailByGoods()
+			} else {
+				this.getDetailByDev()
+			}
+		},
+		methods: {
+			getDetailByGoods() {
+				stockByGoodsDetail({
+					goodsId: this.id
+				}).then(res => {
+					this.list = res.data
+				})
+			},
+			getDetailByDev() {
+				stockByDeviceDetail({
+					deviceId: this.id
+				}).then(res => {
+					this.list = res.data
+				})
+			}
+		}
+	}
+</script>
+<style scoped lang="scss">
+	.container {
+		.content {
+			.equipment-container {
+				margin: 13rpx 13rpx 0;
+				padding: 12rpx 20rpx 24rpx;
+				border-radius: 14rpx;
+				background-color: #fff;
+				box-shadow: 0px 0px 10rpx 0px rgba(174, 201, 255, 0.2);
+				position: relative;
+			
+				.sb-box {
+					padding: 24rpx 18rpx;
+					background-color: #f5f8fb;
+					border-radius: 8rpx;
+					margin-top: 12rpx;
+				}
+			
+				.title {
+					height: 60rpx;
+					line-height: 60rpx;
+					font-size: 32rpx;
+					font-weight: bold;
+					color: #333;
+			
+					>text {
+						font-size: 24rpx;
+						color: #333;
+					}
+				}
+			
+				.order-detail-item {
+					font-size: 28rpx;
+					margin-top: 12rpx;
+					color: #777;
+			
+					>view {
+						display: inline-block;
+						width: 170rpx;
+					}
+				}
+			
+				.percent {
+					position: absolute;
+					right: 40rpx;
+					bottom: 20rpx;
+					line-height: 140rpx;
+					color: red;
+			
+					>view {
+						color: #777;
+						width: 150rpx;
+					}
+				}
+			}
+		}
+	}
+</style>

+ 343 - 0
pages/replenish/inventoryQueryExport1.vue

@@ -0,0 +1,343 @@
+<template>
+	<view class="container">
+		<u-navbar leftIconColor="#fff" titleStyle="color:#fff;fontSize:36rpx;" :autoBack="true" bgColor="#2C6FF3"
+			:placeholder="true" title="库存查询"></u-navbar>
+		<view class="content">
+			<!-- <view class="radio-container">
+				<u-tabs :list="typeList" @click="tabChange" lineColor="#2C6FF3" :scrollable="true"></u-tabs>
+			</view> -->
+			<!-- <view class="flex  justify-end radio-container">
+				<view class="flex-sub"></view>
+				<view class="">
+					<xbutton  size="large">查询</xbutton>
+				</view>
+			</view> -->
+			<view class="tab-list flex flex-wrap">
+				<block v-for="(item,index) in typeList" :key="item.name">
+					<view :class="[type==item.name?'tab-item tab-show':'tab-item']" @click="tabClick(item.name)">
+						{{item.name}}
+					</view>
+				</block>
+			</view>
+			<view class="title">按<text style="color: #2C6FF3;">{{type}}</text>盘点<text style="color:red;font-size: 24rpx;">(左右滑动查看更多数据)</text></view>
+
+			<view class="table card">
+				<uni-table border stripe emptyText="暂无更多数据" v-if="type=='商品'">
+					<!-- 表头行 -->
+					<uni-tr>
+						<uni-th align="left">商品名称</uni-th>
+						<uni-th align="center">库存数量</uni-th>
+						<uni-th align="center">缺货数量</uni-th>
+						<uni-th align="center">库存容量</uni-th>
+						<uni-th align="center">缺货占比</uni-th>
+					</uni-tr>
+					<!-- 表格数据行 -->
+					<uni-tr v-for="(item,index) in listA" :key="item.goodsId">
+						<uni-td>{{item.goodsName}}</uni-td>
+						<uni-td align="center">{{item.stockSum}}</uni-td>
+						<uni-td align="center">{{item.lackSum}}</uni-td>
+						<uni-td align="center">{{item.capacitySum}}</uni-td>
+						<uni-td align="center">{{item.percentText}}</uni-td>
+					</uni-tr>
+				</uni-table>
+
+				<uni-table border stripe emptyText="暂无更多数据" v-if="type=='设备'">
+					<!-- 表头行 -->
+					<uni-tr>
+						<uni-th align="left">设备名称</uni-th>
+						<uni-th align="center">库存数量</uni-th>
+						<uni-th align="center">缺货数量</uni-th>
+						<uni-th align="center">库存容量</uni-th>
+						<uni-th align="center">缺货占比</uni-th>
+					</uni-tr>
+					<!-- 表格数据行 -->
+					<uni-tr v-for="(item,index) in listB" :key="item.deviceId">
+						<uni-td>{{item.deviceName?item.deviceName:item.deviceId}}</uni-td>
+						<uni-td align="center">{{item.stockSum}}</uni-td>
+						<uni-td align="center">{{item.lackSum}}</uni-td>
+						<uni-td align="center">{{item.capacitySum}}</uni-td>
+						<uni-td align="center">{{item.percentText}}</uni-td>
+					</uni-tr>
+				</uni-table>
+
+				<uni-table border stripe emptyText="暂无更多数据" v-if="type=='线路'">
+					<!-- 表头行 -->
+					<uni-tr>
+						<uni-th align="left">线路名称</uni-th>
+						<uni-th align="left">库存数量</uni-th>
+						<uni-th align="left">缺货数量</uni-th>
+						<uni-th align="left">库存容量</uni-th>
+					</uni-tr>
+					<!-- 表格数据行 -->
+					<uni-tr v-for="(item,index) in listC" :key="item.placeLineId">
+						<uni-td>{{item.placeLineName&&item.placeLineName!=null?item.placeLineName:item.placeLineId}}</uni-td>
+						<uni-td>{{item.stockSum}}</uni-td>
+						<uni-td>{{item.lackSum}}</uni-td>
+						<uni-td>{{item.capacitySum}}</uni-td>
+					</uni-tr>
+				</uni-table>
+
+				<uni-table border stripe emptyText="暂无更多数据" v-if="type=='设备+商品'">
+					<!-- 表头行 -->
+					<uni-tr>
+						<uni-th align="left">设备名称</uni-th>
+						<uni-th align="left">商品名称</uni-th>
+						<uni-th align="left">库存数量</uni-th>
+						<uni-th align="left">缺货数量</uni-th>
+						<uni-th align="left">库存容量</uni-th>
+					</uni-tr>
+					<!-- 表格数据行 -->
+					<uni-tr v-for="(item,index) in listD" :key="item.goodsId">
+						<uni-td>{{item.deviceName?item.deviceName:item.deviceId}}</uni-td>
+						<uni-td>{{item.goodsName}}</uni-td>
+						<uni-td>{{item.stockSum}}</uni-td>
+						<uni-td>{{item.lackSum}}</uni-td>
+						<uni-td>{{item.capacitySum}}</uni-td>
+					</uni-tr>
+				</uni-table>
+
+				<uni-table border stripe emptyText="暂无更多数据" v-if="type=='线路+商品'">
+					<!-- 表头行 -->
+					<uni-tr>
+						<uni-th align="left">线路名称</uni-th>
+						<uni-th align="left">商品名称</uni-th>
+						<uni-th align="left">库存数量</uni-th>
+						<uni-th align="left">缺货数量</uni-th>
+						<uni-th align="left">库存容量</uni-th>
+					</uni-tr>
+					<!-- 表格数据行 -->
+					<uni-tr v-for="(item,index) in listE" :key="item.goodsId">
+						<uni-td>{{item.placeLineName&&item.placeLineName!=null?item.placeLineName:item.placeLineId}}</uni-td>
+						<uni-td>{{item.goodsName}}</uni-td>
+						<uni-td>{{item.stockSum}}</uni-td>
+						<uni-td>{{item.lackSum}}</uni-td>
+						<uni-td>{{item.capacitySum}}</uni-td>
+					</uni-tr>
+				</uni-table>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import {
+		stockByLineAndGoods, //按线路+商品分组 
+		stockByDeviceAndGoods, // 按设备+商品分组 
+		stockByDevice, //按设备分组
+		stockByGoods, // 按商品分组
+		stockByLine, //按线路分组
+	} from '@/api/replenishment/replenishment.js'
+	export default {
+		data() {
+			return {
+				// 基本案列数据
+				typeList: [{
+						name: '商品'
+					},
+					{
+						name: '设备'
+					},
+					// {
+					// 	name: '线路'
+					// }, {
+					// 	name: '设备+商品'
+					// }, {
+					// 	name: '线路+商品'
+					// }
+				],
+				type: '商品',
+
+				listA: [],
+				listB: [],
+				listC: [],
+				listD: [],
+				listE: [],
+			}
+		},
+
+		onShow() {
+			this.getData()
+		},
+
+		methods: {
+			tabClick(name) {
+				this.type = name;
+				this.getData()
+			},
+
+			getData() {
+				switch (this.type) {
+					case '商品':
+						this.getListA()
+						break;
+					case '设备':
+						this.getListB()
+						break;
+					case '线路':
+						this.getListC()
+						break;
+					case '设备+商品':
+						this.getListD()
+						break;
+					case '线路+商品':
+						this.getListE()
+						break;
+					default:
+						break;
+				}
+			},
+
+			//按商品分组
+			getListA() {
+				stockByGoods({
+					orderBy:'',
+					orderByKey:''
+				}).then(res => {
+					if (res.code == 200) {
+						if (res.data && res.data.length > 0) {
+							this.listA = res.data
+						} else {
+							this.listA = []
+						}
+					} else {
+						this.listA = []
+					}
+				})
+			},
+
+			//按设备分组 
+			getListB() {
+				stockByDevice({
+					orderBy:'',
+					orderByKey:''
+				}).then(res => {
+					if (res.code == 200) {
+						if (res.data && res.data.length > 0) {
+							this.listB = res.data
+						} else {
+							this.listB = []
+						}
+					} else {
+						this.listB = []
+					}
+				})
+			},
+
+			//按线路分组
+			getListC() {
+				stockByLine().then(res => {
+					if (res.code == 200) {
+						if (res.data && res.data.length > 0) {
+							this.listC = res.data
+						} else {
+							this.listC = []
+						}
+					} else {
+						this.listC = []
+					}
+				})
+			},
+
+			//按设备+商品分组
+			getListD() {
+				stockByDeviceAndGoods().then(res => {
+					if (res.code == 200) {
+						if (res.data && res.data.length > 0) {
+							this.listD = res.data
+						} else {
+							this.listD = []
+						}
+					} else {
+						this.listD = []
+					}
+				})
+			},
+
+			//按线路+商品分组
+			getListE() {
+				stockByLineAndGoods().then(res => {
+					if (res.code == 200) {
+						if (res.data && res.data.length > 0) {
+							this.listE = res.data
+							this.isEmpty = false
+						} else {
+							this.listE = []
+							this.isEmpty = false
+						}
+					} else {
+						this.listE = []
+						this.isEmpty = true
+					}
+				})
+			},
+
+		}
+	}
+</script>
+<style scoped lang="scss">
+	.container {
+		min-height: 100vh;
+		background-color: #fff;
+		padding-bottom: 24rpx;
+
+		// padding-top: 120rpx;
+		.tab-list {
+			width: 100%;
+			background-color: #fff;
+			padding: 12rpx;
+
+			.tab-item {
+				padding: 0 64rpx;
+				height: 62rpx;
+				background: #F7F7F7;
+				border-radius: 10rpx;
+				font-size: 28rpx;
+				font-weight: 500;
+				color: #777777;
+				margin-right: 20rpx;
+				line-height: 62rpx;
+				margin-bottom: 14rpx;
+
+				&.tab-show {
+					background: #F4F8FF;
+					color: #2C6FF3;
+				}
+			}
+		}
+
+		.title {
+			font-size: 32rpx;
+			line-height: 32rpx;
+			font-weight: bold;
+			padding: 12rpx 24rpx 12rpx 36rpx;
+			margin-top: 24rpx;
+		}
+
+		.card {
+			width: 724rpx;
+			margin-left: 13rpx;
+			margin-top: 28rpx;
+			background-color: #fff;
+			margin-top: 20rpx;
+			box-shadow: 0px 0px 10rpx 0px rgba(174, 201, 255, 0.2);
+			border-radius: 14rpx;
+		}
+
+		/deep/.uni-table-th {
+			font-size: 30rpx;
+			color: #555;
+		}
+
+		/deep/.uni-table-td {
+			color: #555;
+		}
+
+		/deep/.uni-table-scroll {
+			border: none;
+		}
+
+		/deep/.table--border {
+			border-right: none;
+		}
+	}
+</style>

+ 1017 - 0
pages/replenish/replenishmentHomePage.vue

@@ -0,0 +1,1017 @@
+<template>
+	<view class="container">
+		<u-navbar leftIconColor="#fff" titleStyle="color:#fff;fontSize:36rpx;" :autoBack="true" bgColor="#2C6FF3"
+			:placeholder="true" title="补货首页"></u-navbar>
+		<view class="machine-box">
+			机器编号:{{deviceId}}
+		</view>
+
+		<view class="top-wrap">
+			<view class="tab-wrap">
+				<view class="tab">
+					<u-tabs :list="tabList" :activeStyle="{color: '#333',fontWeight: 'bold',fontSize:'36rpx'}"
+						:inactiveStyle="{fontSize:'32rpx'}" :scrollable="false" :current="tabsIndex" @click="tabschange"
+						lineColor="#2C6FF3">
+					</u-tabs>
+				</view>
+			</view>
+			<view class="search-container">
+				<u-search animation placeholder="商品搜索" :clearabled="false" v-model="keyword" :showAction="false"
+					@search="search"></u-search>
+				<!-- 	<view class="scan-icon" @click="scan">
+					<u-icon name="scan" size="22" color="#909399"></u-icon>
+				</view> -->
+
+				<view class="search-history flex flex-wrap flex-start" v-if="historyList.length>0">
+					<view class="history-tips">最近搜索</view>
+					<view class="history-item" v-for="(item,index) in historyList" :key="index"
+						@click="searchFast(item)">
+						{{item}}
+					</view>
+				</view>
+			</view>
+
+			<view class="btns flex justify-between">
+				<view class="btn-left flex">
+					<view class="icon-btn" style="margin-right: 12rpx;">
+						<xbutton width="184rpx" round="14rpx" @click="addgoods"
+							icon="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/replenish/rep_home_add.png">
+							新增商品</xbutton>
+					</view>
+					<view class="icon-btn" style="margin-right: 12rpx;">
+						<xbutton width="184rpx" round="14rpx" @click="addlist"
+							icon="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/replenish/rep_home_add.png">
+							商品清单</xbutton>
+					</view>
+					<!-- <view class="add-btn">
+						<xbutton width="184rpx" round="14rpx"
+							icon="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/replenish/rep_home_full.png">
+							一键满货</xbutton>
+					</view> -->
+				</view>
+				<view class="btn-right">
+					<xbutton width="144rpx" round="14rpx" @click="keep">提交</xbutton>
+				</view>
+			</view>
+		</view>
+
+		<scroll-view class="scrollview" :scroll-with-animation="true" scroll-y :style="{'height':fullHeight}"
+			lower-threshold="100">
+			<view v-if="goodsList&&goodsList.length>0" class="scroll-content">
+				<view class="goods-box" v-for="(item,index) in goodsList" :key="item.id">
+					<view class="flex flex-sub align-center">
+						<view class="image">
+							<u--image radius='14rpx' width="120rpx" height="120rpx"
+								:src="item.cover?item.cover:'https://static.xynetweb.com/sysFile/defaultgoods.png'"
+								mode="aspectFit" :lazy-load="true">
+							</u--image>
+						</view>
+						<view class="flex flex-sub flex-direction justify-around goods-content">
+							<view class="goods-name-price flex justify-between align-start">
+								<view class="goods-name">
+									{{item.name}}
+								</view>
+								<view class="goods-price" v-if="item.newPrice==null">
+									¥{{$xy.delMoney(item.price)}}
+								</view>
+								<view class="goods-price flex" v-else>
+									<view style="color: #666;text-decoration: line-through;">
+										¥{{$xy.delMoney(item.price)}}
+									</view>
+									<view>
+										¥{{$xy.delMoney(item.newPrice)}}
+									</view>
+								</view>
+							</view>
+							
+							<view class="goods-stock flex">
+								<view>商品ID:{{item.id}}</view>
+							</view>
+							
+							<view class="goods-stock flex">
+								<view>商品SKUID:{{item.skuId}}</view>
+							</view>
+							
+							<view class="goods-stock flex">
+								<view>容量:{{item.capacity}}</view>
+								<view style="margin-left: 24rpx;" v-if="item.newCapacity">
+									新容量:{{item.newCapacity}}</view>
+							</view>
+							
+							<view class="goods-stock flex">
+								<view>库存:{{item.stock}}</view>
+								<view style="margin-left: 24rpx;" v-if="item.newStock!=0">
+									新库存:{{Number(item.stock)+Number(item.newStock)}}</view>
+							</view>
+							<view class="btn-input flex align-center flex-start" v-if="deviceInfo.deviceType!=5">
+								<view class="btn-input-left">补货:</view>
+								<view class="btn-input-right flex align-center">
+									<image
+										src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/replenish/rep_home_sub.png"
+										mode="widthFix" @click="sub(item)"></image>
+									<input type="doit" v-model="item.newStock">
+									<image
+										src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/replenish/rep_home_sup.png"
+										mode="widthFix" @click="sup(item)"></image>
+								</view>
+							</view>
+							<view style="height:20rpx;" v-if="deviceInfo.deviceType==5"></view>
+							<image class="edit-goods" @click.stop="editGoods(item,index)"
+								src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/replenish/rep_home_edit.png"
+								mode="widthFix"></image>
+						</view>
+					</view>
+
+					<view class="edit-box" v-if="item.show">
+						<view @click.stop="changePrice(item)">改价</view>
+						<view @click.stop="changeCapacity(item)">容量</view>
+						<view @click.stop="del(item)">删除</view>
+						<!-- <view>满货</view> -->
+					</view>
+					<view class="bg" v-if="editIndex!=null" @click.stop="editHidden"></view>
+				</view>
+			</view>
+			<view v-else class='empty'>
+				<u-empty mode="data" text="数据为空"></u-empty>
+			</view>
+			<!-- <u-loadmore v-if="isEmpty===false" :status="loadmoreStatus" /> -->
+		</scroll-view>
+
+		<view class="flex align-center justify-around bottom-btn">
+			<view class="bottom-view" @tap="gethomePage">
+				<image class="bottom-image"
+					src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/replenish/rep_home_upd.png"
+					mode="widthFix"></image>
+				<view class="bottom-text">刷新</view>
+			</view>
+			<view class="bottom-view" @tap="openCabinet">
+				<image class="bottom-image"
+					src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/replenish/rep_home_open.png"
+					mode="widthFix"></image>
+				<view class="bottom-text">一键开柜</view>
+			</view>
+			<view class="bottom-view" @tap="cper">
+				<image class="bottom-image"
+					src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/replenish/rep_home_rec.png"
+					mode="widthFix">
+				</image>
+				<view class="bottom-text">补货记录</view>
+			</view>
+		</view>
+
+		<xpopup :show="editPriceShow" @close="editPriceClose" @confirm="priceSet" :showBtn="true" :title="editTitle">
+			<view class="pop-content">
+				<u--input placeholder="请输入内容" type="digit" border="surround" v-model="editPrice" v-if="editTitle=='修改价格'"></u--input>
+				<u--input placeholder="请输入内容" type="digit" border="surround" v-model="editCapacity" v-else></u--input>
+			</view>
+		</xpopup>
+
+		<xpopup :show="backShow" @close="backClose" :safeAreaInsetBottom="false" :showBtn="false" @open="backOpen"
+			mode="center">
+			<view class="back-pop-content">
+				<block v-for="(item,index) in changeList" :key="item.goodsName">
+					<view class="back-price flex align-center" v-if="item.type=='price'">
+						<view>{{item.goodsName}}</view>
+						<view>价格:</view>
+						<view>{{$xy.delMoney(item.oldPrice)}}</view>
+						<view>更改为</view>
+						<view>{{$xy.delMoney(item.newPrice)}}</view>
+					</view>
+					<view class="back-stock flex align-center" v-if="item.type=='stock'">
+						<view>{{item.goodsName}}</view>
+						<view>库存:</view>
+						<view>{{item.oldStock}}</view>
+						<view>更改为</view>
+						<view>{{item.newStock}}</view>
+					</view>
+					<view class="back-capacity flex align-center"  v-if="item.type=='capacity'">
+						<view>{{item.goodsName}}</view>
+						<view>容量:</view>
+						<view>{{item.oldCapacity}}</view>
+						<view>更改为</view>
+						<view>{{item.newCapacity}}</view>
+					</view>
+				</block>
+			</view>
+		</xpopup>
+	</view>
+</template>
+
+<script>
+	import {
+		supplyPage,
+		delGoods,
+		save,
+		saveByOpenDoor,
+		check,
+		create
+	} from "@/api/replenishment/replenishment.js"
+
+	import {
+		saveKeyWord
+	} from '@/utils/common.js'
+	export default {
+		data() {
+			return {
+				goodsList: [],
+				page: 1, //当前分页
+				size: 10, //分页数据条数
+				tabsIndex: 0,
+				addGoodsShow: false,
+				active: '',
+				deviceId: null,
+				deviceName: null,
+				name: '', //商品名称/条形码
+
+				workNo: '',
+				fullHeight: 0,
+				historyList: [],
+				keyword: '',
+
+				tabList: [{
+						name: '全部'
+					},
+					{
+						name: '缺货'
+					},
+					{
+						name: '售罄'
+					}
+				],
+
+				editIndex: null,
+				editPriceShow: false,
+				editPrice: null,
+				editGoodsIndex: null,
+				changeList: [], //提交反显信息
+
+				backShow: false,
+				deviceInfo: {},
+				editTitle:'修改价格',
+				editCapacity:null
+			}
+		},
+		onLoad(o) {
+			let _this = this;
+			const query = uni.createSelectorQuery().in(this);
+			query.select(".scrollview").boundingClientRect((data) => {
+				uni.getSystemInfo({
+					success(res) {
+						// 针对iPhone X等机型底部安全距离做适配
+						const model = res.model;
+						const modelInclude = [
+							"iPhone X",
+							'iPhone XR',
+							"iPhone XS",
+							"iPhone XS MAX",
+							"iPhone 12/13 mini",
+							"iPhone 12/13 (Pro)",
+							"iPhone 12/13 Pro Max",
+							"iPhone 14 Pro Max"
+						];
+						let safeDistance = modelInclude.includes(model)
+						//动态设置商品区域高度
+						if (safeDistance) {
+							_this.fullHeight = res.windowHeight - data.top - 50 - 34 + 'px';
+						} else {
+							_this.fullHeight =res.windowHeight - data.top - 50 + 'px';
+						}
+					},
+				});
+			}).exec();
+
+			if (uni.getStorageSync('goods')) {
+				this.historyList = JSON.parse(uni.getStorageSync('goods'))
+			}
+			this.deviceId = o.id;
+			this.deviceName = o.deviceName;
+		},
+
+		onShow() {
+			this.gethomePage()
+		},
+
+		methods: {
+			search(val) {
+				if (val) {
+					this.historyList = saveKeyWord('goods', this.keyword)
+				}
+				this.gethomePage()
+			},
+
+			searchFast(e) {
+				this.keyword = e
+				this.search()
+			},
+
+
+			scanQRCode() {
+				uni.scanCode({
+					needResult: 1, //默认为0,扫描结果由微信处理,1则直接返回扫描结果,
+					scanType: ["qrCode", "barCode"], //// 可以指定扫二维码还是一维码,默认二者都有
+					success: (res) => {
+						var result = res.resultStr; //当needResult 为 1 时,扫码返回的结果  获取扫码信息
+						this.name = result
+						this.gethomePage()
+					},
+				});
+			},
+
+			tabschange(e) {
+				this.tabsIndex = e.index
+				this.gethomePage()
+			},
+
+			addgoods() {
+				this.$tab.navigateTo('/pages/equipment/addCom?id=' + this.deviceId)
+			},
+
+			addlist() {
+				this.$tab.navigateTo('/pages/commodity/commoditylist?id=' + this.deviceId)
+			},
+
+			//补货记录
+			cper() {
+				this.$tab.navigateTo('replenishmentRecord?id=' + this.deviceId)
+			},
+
+			//一键满货
+			allFull() {
+				uni.showModal({
+					title: '提示',
+					content: '是否确认一键满货',
+					success: res => {
+						if (res.confirm) {
+							for (var i = 0; i < this.goodsList.length; i++) {
+								this.goodsList[i].stock = this.goodsList[i].capacity
+								this.goodsList[i].newkcrl = true
+							}
+						} else if (res.cancel) {
+							console.log('用户点击取消');
+						}
+					}
+				});
+			},
+
+			// 一键开柜
+			openCabinet() {
+				uni.showModal({
+					title: '提示',
+					content: '是否确认一键开柜',
+					success: res => {
+						if (res.confirm) {
+							saveByOpenDoor({
+									"deviceId": this.deviceId
+								})
+								.then(res => {
+									this.workNo = res.data
+
+									this.checkWx()
+								})
+						} else if (res.cancel) {
+							console.log('用户点击取消');
+						}
+					}
+				});
+			},
+			//效验活动
+			checkWx() {
+				check({
+						"deviceId": this.deviceId,
+						"deviceType": 1,
+						"door": 0,
+						"isRestock": true
+					})
+					.then(res => {
+						this.createOrder(res.data)
+					})
+			},
+			//创建活动
+			createOrder(e) {
+				create({
+					"deviceId": this.deviceId,
+					"deviceType": 1,
+					"door": 0,
+					"payQueryOrderId": "",
+					"payType": '',
+					"sysType": 1,
+					"workNo": this.workNo,
+					"workType": 2
+				}).then(res => {
+					uni.showLoading({
+						title: '开门中...'
+					});
+
+					setTimeout(function() {
+						uni.showToast({
+							title: '开门成功',
+							duration: 2000
+						});
+						uni.hideLoading();
+					}, 2000);
+				}).catch(err => {
+					uni.showToast({
+						title: JSON.stringify(res),
+						duration: 10000
+					});
+				})
+			},
+
+			// 满货
+			fullCargo(val) {
+				uni.showModal({
+					title: '提示',
+					content: '是否确认满货',
+					success: res => {
+						if (res.confirm) {
+							for (var i = 0; i < this.goodsList.length; i++) {
+								if (this.goodsList[i].goodsId === val.goodsId) {
+									this.goodsList[i].stock = val.capacity
+									this.goodsList[i].newkcrl = true
+								}
+							}
+						} else if (res.cancel) {
+							console.log('用户点击取消');
+						}
+					}
+				});
+			},
+			//保存
+			keep() {
+				var goods = [] //收集提交改动商品参数
+				var changeList = [] //收集提交改动商品清单,做提醒结果用
+				this.goodsList.forEach(item => {
+					if (item.newPrice != null || item.newStock != 0||item.newCapacity!=null) {
+						goods.push({
+							"deviceGoodsId": item.id,
+							"oldStock": item.stock,
+							"newStock": Number(item.newStock) + Number(item.stock),
+							"oldPrice": item.price,
+							"newPrice": item.newPrice,
+							"oldCapacity": item.oldCapacity,
+							"newCapacity": item.newCapacity,
+							"oldWarning": item.warning,
+							"newWarning": item.warning,
+							"goodsId": item.goodsId,
+							"goodsName": item.name
+						})
+
+						if (item.newPrice != null) { //改价商品
+							changeList.push({
+								goodsName: item.name,
+								type: 'price',
+								oldPrice: item.price,
+								newPrice: item.newPrice
+							})
+						}
+						if (item.newStock != 0) { //改库存商品
+							changeList.push({
+								goodsName: item.name,
+								type: 'stock',
+								oldStock: item.stock,
+								newStock: Number(item.newStock) + Number(item.stock)
+							})
+						}
+						if (item.newCapacity != null) { //改容量商品
+							changeList.push({
+								goodsName: item.name,
+								type: 'capacity',
+								oldCapacity: item.capacity,
+								newCapacity: item.newCapacity
+							})
+						}
+					}
+				})
+
+				this.changeList = JSON.parse(JSON.stringify(changeList))
+
+				if (goods.length == 0) {
+					this.$modal.msg('您未对商品做任何改动~')
+					return
+				}
+				uni.showModal({
+					title: '提示',
+					content: '是否确认保存',
+					success: res => {
+						if (res.confirm) {
+							save({
+								id: this.workNo ? this.workNo : '',
+								deviceId: this.deviceId,
+								goods: goods
+							}).then(res => {
+								this.showChanged()
+								this.clearStorage()
+								this.gethomePage()
+							}).catch(err => {
+								uni.showModal({
+									title: '提示',
+									content: err,
+									success: res => {
+										if (res.confirm) {
+											console.log('确定')
+										}
+									}
+								})
+							})
+						} else if (res.cancel) {
+							console.log('用户点击取消');
+						}
+					}
+				});
+			},
+
+			//保存成功,显示改动结果
+			showChanged() {
+				this.backOpen()
+			},
+
+			// 保存成功,清空缓存
+			clearStorage() {
+				uni.setStorageSync(`replenish:${this.deviceId}`, '')
+			},
+
+			backClose() {
+				this.backShow = false
+			},
+
+			backOpen() {
+				this.backShow = true
+			},
+
+			//删除
+			del(val) {
+				this.editHidden()
+				let _this = this
+				uni.showModal({
+					title: '提示',
+					content: '是否确认删除',
+					success: function(res) {
+						if (res.confirm) {
+							delGoods({
+								"deviceId": _this.deviceId,
+								"goodsIds": [val.goodsId]
+							}).then(res => {
+								_this.gethomePage()
+							})
+						} else if (res.cancel) {
+							console.log('用户点击取消');
+						}
+					}
+				});
+
+			},
+			gethomePage() {
+				supplyPage({
+					page: {
+						size: -1
+					},
+					search: this.keyword,
+					deviceId: this.deviceId,
+					type: this.tabsIndex + 1, //类型
+					isRefresh: true
+				}).then(res => {
+					let deviceInfo = res.data.deviceInfo
+					let data = res.data.page.records
+					let sto = uni.getStorageSync(`replenish:${this.deviceId}`)
+					for (var i = 0; i < data.length; i++) {
+						data[i].oldCapacity = data[i].capacity
+						data[i].show = false
+						data[i].newPrice = null
+						data[i].newStock = 0
+
+						// 暂存补货数据
+						if (sto && sto != '') {
+							let replenishList = JSON.parse(sto);
+							replenishList.forEach(j => {
+								if (data[i].goodsId === j.goodsId) {
+									data[i].newPrice = j.newPrice
+									data[i].newStock = j.newStock
+								}
+							})
+						}
+					}
+					this.deviceInfo = deviceInfo
+					this.goodsList = data;
+				})
+			},
+
+			editGoods(item, index) {
+				item.show = true
+				this.editIndex = index
+				this.editGoodsIndex = index
+			},
+
+			editHidden() {
+				if (this.editIndex != null) {
+					this.goodsList[this.editIndex].show = false
+					this.editIndex = null
+				}
+			},
+
+			sub(item) {
+				if (Number(item.stock) + Number(item.newStock) == 0) {
+					this.$modal.msg('这个商品没库存啦~')
+					return
+				}
+				item.newStock--
+				this.goodsChange()
+			},
+
+			sup(item) {
+				item.newStock++
+				this.goodsChange()
+			},
+
+			// 库存调整,设置缓存
+			goodsChange(item) {
+				//设置缓存
+				let newListSto = []
+				this.goodsList.forEach(i => {
+					if (i.newPrice != null || i.newStock != 0) {
+						let obj = {
+							goodsId: i.goodsId,
+							newPrice: i.newPrice,
+							newStock: i.newStock
+						}
+						newListSto.push(obj)
+					}
+				})
+
+				uni.setStorageSync(`replenish:${this.deviceId}`, JSON.stringify(newListSto))
+			},
+
+			changePrice(item) {
+				this.editTitle='修改价格'
+				this.editPriceShow = true;
+				this.editHidden()
+			},
+			
+			changeCapacity(){
+				this.editTitle='修改容量'
+				this.editPriceShow = true;
+				this.editHidden()
+			},
+
+			editPriceClose() {
+				this.editPriceShow = false;
+			},
+
+			priceSet() {
+				if(this.editTitle=='修改价格'){
+					if (this.editPrice == null) {
+						this.$modal.msg('商品价格不能为空!')
+						return
+					}
+					let reg = /^(([1-9][0-9]*)|(([0]\.\d{1,2}|[1-9][0-9]*\.\d{1,2})))$/
+					if (!reg.test(this.editPrice)) {
+						uni.showToast({
+							icon: 'none',
+							title: '价格最多保留两位小数!'
+						})
+						return;
+					}
+					this.editPriceClose()
+					this.goodsList[this.editGoodsIndex].newPrice = this.editPrice * 100
+					this.goodsChange() //设置缓存
+					
+					this.editPrice = null
+					this.editGoodsIndex = null
+				}else{ //修改容量
+					if (this.editCapacity == null) {
+						this.$modal.msg('商品容量不能为空!')
+						return
+					}
+					let reg = /^([0-9]*)$/
+					if (!reg.test(this.editCapacity)) {
+						uni.showToast({
+							icon: 'none',
+							title: '容量必须为整数!'
+						})
+						return;
+					}
+					this.editPriceClose()
+					this.goodsList[this.editGoodsIndex].newCapacity = this.editCapacity
+					this.goodsChange() //设置缓存
+					
+					this.newCapacity = null
+					this.editGoodsIndex = null
+				}
+			}
+		}
+	}
+</script>
+<style>
+	page {
+		background-color: #eee;
+	}
+</style>
+<style scoped lang="scss">
+	.container {
+		height: 100vh;
+		background-color: #F4F4F4;
+		position: static;
+
+		.machine-box {
+			width: 750rpx;
+			height: 74rpx;
+			font-size: 32rpx;
+			line-height: 58rpx;
+			text-align: center;
+			background: #2C6FF3;
+			color: #fff;
+			border-radius: 0px 0px 50rpx 50rpx;
+		}
+
+		.top-wrap {
+			background: linear-gradient(0deg, #D7D7D7, #FFFFFF);
+		}
+
+		.tab-wrap {
+			background-color: #fff;
+			padding-left: 30rpx;
+
+			.tab {
+				width: 60%;
+			}
+		}
+
+		.search-container {
+			padding: 26rpx 13rpx 26rpx;
+			background-color: #fff;
+			position: relative;
+
+			.scan-icon {
+				position: absolute;
+				right: 36rpx;
+				top: 38rpx;
+				z-index: 2;
+			}
+
+			.search-history {
+				.history-tips {
+					font-size: 24rpx;
+					color: #333333;
+					line-height: 40rpx;
+					margin-right: 10rpx;
+					margin-top: 24rpx;
+				}
+
+				.history-item {
+					margin-right: 10rpx;
+					padding: 0 13rpx;
+					background: #F6F8FB;
+					color: #333;
+					font-size: 24rpx;
+					line-height: 40rpx;
+					border-radius: 40rpx;
+					margin-top: 24rpx;
+				}
+			}
+		}
+
+		.btns {
+			background-color: #fff;
+			padding: 20rpx 13rpx 20rpx;
+		}
+
+		.empty {
+			margin-top: 120rpx
+		}
+
+		.active {
+			background-color: #2C6FF3;
+			color: #fff;
+		}
+
+		.marleft {
+			margin-left: 10rpx;
+		}
+
+		.martop {
+			margin-top: 20rpx;
+		}
+
+		.scrollview {
+			padding-top: 10rpx;
+		}
+
+		.goods-box {
+			width: 724rpx;
+			margin-left: 13rpx;
+			background-color: #fff;
+			box-shadow: 0px 0px 10px 0px rgba(174, 201, 255, 0.2);
+			border-radius: 14rpx;
+			padding: 28rpx 12rpx 22rpx;
+			position: relative;
+
+			.edit-box {
+				width: 180rpx;
+				background-color: #fff;
+				font-size: 30rpx;
+				text-align: center;
+				line-height: 60rpx;
+				padding: 10rpx 0;
+				box-shadow: 0px 0px 10px 0px rgba(174, 201, 255, 0.2);
+				position: absolute;
+				right: 20rpx;
+				bottom: -120rpx;
+				z-index: 99999;
+			}
+
+			&+.goods-box {
+				margin-top: 10rpx;
+			}
+
+			.image {
+				margin-right: 35rpx;
+				height: 120rpx;
+				width: 120rpx;
+			}
+
+			.goods-content {
+				position: relative;
+
+				.goods-name-price {
+					.goods-name {
+						font-size: 28rpx;
+						font-weight: 800;
+						color: #333333;
+						line-height: 34rpx;
+					}
+
+					.goods-price {
+						font-size: 32rpx;
+						line-height: 32rpx;
+						font-weight: 800;
+						color: #FF0000;
+						margin-right: 10rpx;
+					}
+				}
+
+				.goods-stock {
+					font-size: 26rpx;
+					font-weight: 500;
+					color: #555555;
+					line-height: 26rpx;
+					margin-top: 30rpx;
+				}
+
+				.btn-input {
+					margin-top: 18rpx;
+
+					.btn-input-left {
+						font-size: 26rpx;
+					}
+
+					.btn-input-right {
+						justify-content: flex-end;
+						margin-left: 50rpx;
+
+						>image {
+							width: 56rpx;
+							height: 56rpx;
+						}
+
+						>input {
+							width: 100rpx;
+							height: 56rpx;
+							line-height: 56rpx;
+							border: 2rpx solid #2C6FF3;
+							border-radius: 10rpx;
+							text-align: center;
+							margin: 0 50rpx;
+						}
+					}
+				}
+
+				.edit-goods {
+					width: 56rpx;
+					height: 56rpx;
+					position: absolute;
+					right: 10rpx;
+					bottom: 0;
+				}
+			}
+		}
+
+		.pop-content {
+			padding: 24rpx;
+		}
+
+		.bottom-btn {
+			width: 100%;
+			position: fixed;
+			bottom: 0;
+			right: 0;
+			left: 0;
+			background-color: #fff;
+			height: calc(98rpx + env(safe-area-inset-bottom) / 2);
+			padding-bottom: calc(env(safe-area-inset-bottom) / 2);
+			box-shadow: 0px -2px 10px 0px rgba(174, 201, 255, 0.2);
+			z-index: 999;
+		}
+
+		.scroll-content {
+			padding-bottom: calc(160rpx + env(safe-area-inset-bottom) / 2);
+		}
+
+		.bottom-view {
+			display: flex;
+			flex-direction: column;
+			flex: 1;
+			text-align: center;
+			align-items: center;
+		}
+
+		.bottom-image {
+			height: 48rpx;
+			width: 48rpx;
+		}
+
+		.bottom-text {
+			font-size: 22rpx;
+			line-height: 22rpx;
+			margin-top: 8rpx;
+		}
+
+		.save {
+			position: fixed;
+			right: 15rpx;
+			bottom: 120rpx;
+			width: 140rpx;
+			height: 140rpx;
+			border-radius: 50%;
+			background-color: #f6d232;
+
+			image {
+				width: 80rpx;
+			}
+		}
+
+		.bg {
+			position: fixed;
+			width: 100%;
+			height: 100vh;
+			z-index: 99998;
+			left: 0;
+			top: 0;
+		}
+
+		.back-pop-content {
+			width: 700rpx;
+			padding: 24rpx;
+			line-height: 50rpx;
+
+			>view {
+				font-size: 30rpx;
+
+				>view {
+					display: inline-block;
+				}
+
+				>view:nth-child(1) {
+					width: 270rpx;
+					white-space: nowrap;
+					overflow: hidden;
+					text-overflow: ellipsis;
+				}
+
+				>view:nth-child(2) {
+					margin-left: 24rpx;
+				}
+
+				>view:nth-child(3) {
+					color: #38fb58;
+					margin-left: 24rpx;
+					margin-right: 24rpx;
+					width: 50rpx;
+				}
+
+				>view:nth-child(5) {
+					color: #ef9840;
+					margin-left: 24rpx;
+					width: 50rpx;
+				}
+			}
+
+			.back-price {
+
+				>view:nth-child(2) {
+					color: #2C6FF3;
+				}
+			}
+
+			.back-stock {
+				>view:nth-child(2) {
+					color: #ff67bd;
+				}
+			}
+			
+			.back-capacity{
+				>view:nth-child(2) {
+					color: #ff5500;
+				}
+			}
+		}
+	}
+</style>

+ 572 - 0
pages/replenish/replenishmentManagement.vue

@@ -0,0 +1,572 @@
+<template>
+	<view>
+		<view class="container">
+			<u-navbar titleStyle="fontSize:36rpx;" :autoBack="true" bgColor="#fff" :placeholder="true"
+				title="补货管理"></u-navbar>
+			<view style="background-color: #fff;">
+				<view class="flex align-center  box">
+					<!-- <view class="flex flex-direction align-center" @tap="startReplenishment()">
+						<image src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/replenish/bh.png" mode="widthFix"></image>
+						<view class="">开始补货</view>
+					</view> -->
+					<!-- <view class="flex flex-direction align-center"  @tap="commodityList()">
+						<image src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/replenish/qd.png" mode="widthFix"></image>
+						<view class="">商品清单</view>
+					</view> -->
+					<view class="flex justify-between align-center" @tap="replenishmentRecord()">
+						<image src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/replenish/jl.png"
+							mode="widthFix"></image>
+						<!-- <image src="../../static/favicon.ico" mode=""></image> -->
+						<view class="menu-name">补货记录</view>
+					</view>
+					<view class="flex justify-between align-center" @tap='inventoryManagement()'>
+						<image src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/replenish/kc.png"
+							mode="widthFix"></image>
+						<!-- <image src="../../static/favicon.ico" mode=""></image> -->
+						<view class="menu-name">库存管理</view>
+					</view>
+				</view>
+
+				<!-- <view class="tips">
+					补货说明补货说明补货说明
+				</view> -->
+
+				<!-- 设备列表 -->
+				<view class="search">
+					<u-search animation placeholder="请输入设备名称" bgColor="#F6F8FB" :clearabled="true" v-model="keyword"
+						:showAction="false" @search="search"></u-search>
+				</view>
+
+				<!-- 	<view class="tab-list">
+					<u-tabs :list="tabList" @click="tabChange" :current="current" :scrollable="false" bgColor="#fff"
+						lineColor="#2C6FF3"></u-tabs>
+				</view> -->
+
+				<view class="tab-wrap">
+					<view class="tab">
+						<u-tabs :list="tabList" :activeStyle="{color: '#333',fontWeight: 'bold',fontSize:'36rpx'}"
+							:inactiveStyle="{fontSize:'32rpx'}" :scrollable="false" :current="current"
+							@click="tabChange" lineColor="#2C6FF3">
+						</u-tabs>
+					</view>
+				</view>
+			</view>
+
+			<scroll-view class="scrollview" :scroll-with-animation="true" scroll-y lower-threshold="100"
+				@scrolltolower="scrolltolower" :style="{height:fullHeight}">
+				<view class="content">
+					<view class="xy-card" v-for="(item,index) in list" :key="item.deviceId"
+						@click="replenish(item.deviceId,item.deviceName)">
+						<view class="title flex justify-between">
+							<view class="flex">
+								<view>
+									<view class="device-name" v-if="item.deviceName">
+										{{item.deviceName}}<text>({{item.deviceId}})</text>
+									</view>
+									<view class="device-name" v-else>
+										{{item.deviceId}}
+									</view>
+								</view>
+								<view style="margin-left: 22rpx; margin-top: 8rpx;" v-if="item.deviceTypeName">
+									<view class="busy-state on">{{item.deviceTypeName}}</view>
+								</view>
+								<view style="margin-left: 22rpx; margin-top: 8rpx;">
+									<view v-if="item.busyState==1" class="busy-state on">运营中</view>
+									<view v-else class="busy-state off">已停运</view>
+								</view>
+							</view>
+
+							<view class="flex status">
+								<view style="margin-left: 24rpx;color: #FF0000;" v-if="item.goodsIdStockOutCount>0">
+									缺货
+								</view>
+								<view style="margin-left: 24rpx;color: green;" v-else>
+									正常
+								</view>
+							</view>
+						</view>
+
+						<view class="t-content flex flex-direction" v-if="current==0">
+							<view class="c-item-t">
+								<view class="c-item">
+									<view>剩余库存:</view>
+									<view>{{item.stockSum||0}}</view>
+								</view>
+								<view class="c-item">
+									<view>补后库存:</view>
+									<view>{{item.fillCountSum||0}}</view>
+								</view>
+							</view>
+							<view class="c-item-t">
+								<view class="c-item">
+									<view>在售商品种类:</view>
+									<view>{{item.goodsIdCount||0}}</view>
+								</view>
+								<view class="c-item" v-if="item.goodsIdStockOutCount>0">
+									<view>缺货商品种类:</view>
+									<view style="color: red;font-weight: bold;">{{item.goodsIdStockOutCount||0}}</view>
+								</view>
+							</view>
+						</view>
+
+						<view class="t-content flex flex-direction" v-else>
+							<view class="c-item-t">
+								<view class="c-item">
+									<view>剩余库存:</view>
+									<view>{{item.stockSum||0}}</view>
+								</view>
+								<view class="c-item">
+									<view>补后库存:</view>
+									<view>{{item.fillCountSum||0}}</view>
+								</view>
+							</view>
+							<view class="c-item-t">
+								<view class="c-item">
+									<view>缺货商品种类:</view>
+									<view style="color: red;font-weight: bold;">{{item.goodsIdStockOutCount||0}}</view>
+								</view>
+							</view>
+						</view>
+					</view>
+
+					<u-loadmore :status="status" v-if="list.length>=1" />
+				</view>
+				<view class="empty" v-if="list.length==0">
+					<u-empty text="没有设备!"></u-empty>
+				</view>
+			</scroll-view>
+
+			<!-- <view class="scan" @click="startReplenishment">
+				<view class="scan-icon-bot">
+					<u-icon name="scan" color="#2C6FF3" size="60"></u-icon>
+				</view>
+				<view>扫码补货</view>
+			</view> -->
+
+			<view class="btn">
+				<xbutton size="large" @click="startReplenishment">
+					<view class="btn-wrap flex align-center">
+						<image src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/replenish/rep-scan.png" mode=""></image>
+						扫码补货
+					</view>
+				</xbutton>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import {
+		deviceDetail,
+		searchPage,
+		isMerc
+	} from '@/api/device/device.js'
+
+	import {
+		deviceStockList,
+		deviceStockOutList
+	} from '@/api/replenishment/replenishment.js'
+	export default {
+		data() {
+			return {
+				page: 1,
+				size: 10,
+
+				status: 'loadmore',
+
+				current: 0,
+				tabList: [{
+						name: '全部',
+						value: ''
+					},
+					{
+						name: '缺货',
+						value: 1
+					}
+				],
+
+				list: [],
+
+				keyword: '',
+				
+				fullHeight:0
+			}
+		},
+
+		onPullDownRefresh() {
+			this.reset()
+			if (this.current == 0) {
+				this.getList1().then(res => {
+					uni.stopPullDownRefresh();
+				})
+			} else {
+				this.getList2().then(res => {
+					uni.stopPullDownRefresh();
+				})
+			}
+
+		},
+
+		onShow() {
+			this.search()
+		},
+		
+		onLoad() {
+			let _this = this;
+			const query = uni.createSelectorQuery().in(this);
+			query.select(".scrollview").boundingClientRect((data) => {
+				uni.getSystemInfo({
+					success(res) {
+						// 针对iPhone X等机型底部安全距离做适配
+						const model = res.model;
+						const modelInclude = [
+							"iPhone X",
+							'iPhone XR',
+							"iPhone XS",
+							"iPhone XS MAX",
+							"iPhone 12/13 mini",
+							"iPhone 12/13 (Pro)",
+							"iPhone 12/13 Pro Max",
+							"iPhone 14 Pro Max"
+						];
+						let safeDistance = modelInclude.includes(model)
+						//动态设置商品区域高度
+						console.log(res.windowHeight, data.top)
+						if (safeDistance) {
+							_this.fullHeight = res.windowHeight - data.top - 40 + 'px';
+						} else {
+							_this.fullHeight = res.windowHeight - data.top + 'px';
+						}
+					},
+				});
+			}).exec();
+		},
+
+		methods: {
+			startReplenishment() {
+				uni.scanCode({
+					success: (res) => {
+						let deviceId = res.result.split('=')[1];
+						if (deviceId) {
+							this.hasDevice(deviceId).then(res => {
+								this.$tab.navigateTo(
+									`/pages/replenish/replenishmentHomePage?id=${deviceId}`)
+							}).catch(err => {
+								if (err.indeOf('com.netflix') == -1) {
+									this.$modal.msg('err')
+								} else {
+									this.$modal.msg('网络繁忙,请重试!')
+								}
+							})
+						} else {
+							this.$modal.msg('该二维码无效~')
+						}
+					}
+				});
+			},
+
+			hasDevice(id) {
+				return new Promise((resolve, reject) => {
+					isMerc({
+						deviceId: id
+					}).then(res => {
+						console.log(res)
+						resolve(res)
+					}).catch(err => {
+						reject(err)
+					})
+				})
+			},
+
+			commodityList() {
+				this.$tab.navigateTo('../commodity/commoditylist')
+			},
+			replenishmentRecord() {
+				this.$tab.navigateTo('replenishmentRecord')
+			},
+			inventoryManagement() {
+				this.$tab.navigateTo('/pages/replenish/invSearch')
+			},
+
+			tabChange(e) {
+				this.current = e.index
+				this.reset()
+				this.getList()
+			},
+
+			getList() {
+				if (this.current == 0) {
+					this.getList1()
+				} else {
+					this.getList2()
+				}
+			},
+
+			reset() {
+				this.status == 'loadmore'
+				this.page = 1;
+				this.list = [];
+			},
+
+			getList1() {
+				return new Promise((resolve, reject) => {
+					deviceStockList({
+						page: {
+							current: this.page,
+							size: this.size
+						},
+						keyword: this.keyword,
+					}).then(res => {
+						let data = res.data.records;
+						if (data.length < 10) {
+							this.status = "nomore"
+						} else {
+							this.status = "loadmore"
+						}
+						this.list = this.list.concat(data)
+						resolve(data)
+					}).catch(err => {
+						reject(err)
+					})
+				})
+			},
+
+			getList2() {
+				return new Promise((resolve, reject) => {
+					deviceStockOutList({
+						page: {
+							current: this.page,
+							size: this.size
+						},
+						keyword: this.keyword,
+					}).then(res => {
+						let data = res.data.records;
+						if (data.length < 10) {
+							this.status = "nomore"
+						} else {
+							this.status = "loadmore"
+						}
+						this.list = this.list.concat(data)
+						resolve(data)
+					}).catch(err => {
+						reject(err)
+					})
+				})
+			},
+
+			search() {
+				this.reset()
+				this.getList()
+			},
+
+			scanDevice() {
+				uni.scanCode({
+					success: (res) => {
+						let deviceId = res.result.split('=')[1];
+						if (deviceId) {
+							this.keyword = deviceId;
+							this.getList()
+						} else {
+							this.$modal.msg('该二维码无效~')
+						}
+					}
+				});
+			},
+
+			replenish(id, name) {
+				this.$tab.navigateTo(`/pages/replenish/replenishmentHomePage?id=${id}&&deviceName=${name}`)
+			},
+
+			//触底加载更多
+			scrolltolower() {
+				if (this.status == 'nomore') return
+				this.page++
+				this.getList()
+			},
+		}
+	}
+</script>
+
+<style scoped lang="scss">
+	.container {
+
+		// padding-top: 120rpx;
+		image {
+			width: 50rpx;
+			height: 50rpx;
+		}
+
+		.box {
+			padding: 38rpx 12rpx 14rpx;
+			background: #fff;
+			font-size: 28rpx;
+			font-weight: 500;
+			color: #777777;
+			line-height: 62rpx;
+
+			>view {
+				background: #F7F7F7;
+				border-radius: 10rpx;
+				margin-right: 20rpx;
+				padding: 0 12rpx;
+			}
+
+			image {
+				width: 34rpx;
+				height: 34rpx;
+				margin-right: 15rpx;
+			}
+		}
+
+		.tips {
+			width: 724rpx;
+			padding: 10rpx 24rpx;
+			font-size: 28rpx;
+			line-height: 40rpx;
+			font-weight: 500;
+			color: #333333;
+			background: #F0FFFC;
+			border: 2rpx dashed #61D8BE;
+			border-radius: 14rpx;
+			margin-left: 13rpx;
+		}
+
+		.search {
+			padding: 0rpx 13rpx 24rpx;
+			background-color: #fff;
+			position: relative;
+			margin-top: 14rpx;
+
+			.scan-icon {
+				position: absolute;
+				right: 36rpx;
+				top: 50%;
+				transform: translateY(-50%);
+				z-index: 2;
+			}
+		}
+
+		.scan {
+			width: 100%;
+			position: fixed;
+			bottom: 0;
+			display: flex;
+			flex-direction: column;
+			justify-content: center;
+			align-items: center;
+			background-color: #fff;
+			box-shadow: 0 2rpx 10rpx 0 rgba(0, 0, 0, 0.1);
+
+			.scan-icon-bot {
+				width: 60px;
+			}
+
+			>view:nth-child(2) {
+				font-size: 24rpx;
+				color: #999;
+			}
+		}
+
+		.tab-wrap {
+			background-color: #fff;
+
+			.tab {
+				width: 40%;
+				margin-left: -12rpx;
+			}
+		}
+
+		.content {
+			margin: 0 13rpx;
+			padding-bottom: 60rpx;
+			overflow: hidden;
+
+			.xy-card {
+				margin-top: 20rpx;
+				padding: 32rpx;
+			}
+		}
+
+		.title {
+			font-size: 28rpx;
+			font-weight: bold;
+			line-height: 50rpx;
+
+			.device-name {
+				font-size: 28rpx;
+				font-weight: bold;
+				color: #333;
+				max-width: 180rpx;
+
+				>text {
+					font-size: 24rpx;
+					color: #666;
+					font-weight: normal;
+				}
+			}
+		}
+
+		.t-content {
+			margin-top: 12rpx;
+
+			.c-item-t {
+				display: flex;
+				justify-content: flex-start;
+				margin-top: 12rpx;
+
+				.c-item {
+					width: 50%;
+					display: flex;
+					justify-content: flex-start;
+					align-items: flex-end;
+				}
+			}
+		}
+
+		.busy-state {
+			font-size: 24rpx;
+			height: 38rpx;
+			padding: 0 16rpx;
+			line-height: 38rpx;
+			background: #F4F8FF;
+			border-radius: 8rpx;
+			text-align: center;
+			font-weight: normal;
+
+			&.on {
+				color: #2C6FF3;
+			}
+
+			&.off {
+				color: #666;
+			}
+		}
+
+		.status {
+			font-weight: normal;
+		}
+
+		.empty {
+			margin-top: 50rpx;
+		}
+		
+		.btn{
+			width:724rpx;
+			position: fixed;
+			left: 13rpx;
+			bottom:calc(24rpx + env(safe-area-inset-bottom) / 2);
+			border-radius: 88rpx;
+			overflow: hidden;
+			.btn-wrap{
+				text-align: center;
+				font-size: 36rpx;
+				font-weight: 500;
+				>image{
+					width:42rpx;
+					height: 42rpx;
+					margin-right: 20rpx;
+				}
+			}
+		}
+	}
+</style>

+ 252 - 0
pages/replenish/replenishmentRecord.vue

@@ -0,0 +1,252 @@
+<template>
+	<view class="container">
+		<u-navbar leftIconColor="#fff" titleStyle="color:#fff;fontSize:36rpx;" :autoBack="true" bgColor="#2C6FF3"
+			:placeholder="true" title="补货记录"></u-navbar>
+		<view class="search">
+			<u-search :clearabled="true" v-model="page.deviceId" placeholder='输入设备ID进行搜索' :showAction="true"
+				actionText="搜索" @custom='custom'></u-search>
+		</view>
+		<view class="flex align-center justify-between margin">
+			<view class="flex align-center">
+				<view class="flex align-center time">
+					<view class="" @tap="replenishmentdate('start')">{{dateStart?dateStart:'创建时间开始'}}</view>
+					<view class="">至</view>
+					<view class="" @tap="replenishmentdate('end')">{{dateend?dateend:'创建时间结束'}}</view>
+				</view>
+				<image class="date-image" src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/date.png" mode="widthFix"></image>
+			</view>
+			<view class="flex align-center" @tap="pickershow=true">
+				<view class="" style="font-size: 26rpx;">{{isopen?isopen:'是否开门'}}</view>
+				<image src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/selectIcon.png" mode="aspectFit"
+					style="width:24rpx;height: 24rpx;margin-left: 8rpx;"></image>
+
+				<!-- <view class="filter">筛选</view> -->
+			</view>
+		</view>
+		<scroll-view v-if="isEmpty===false" class="scrollview" :scroll-with-animation="true" scroll-y lower-threshold="100" :style="{'height':fullHeight}">
+			<view class="replenishmentrecord-container" v-for="item in replenishmentList" :key="item.id"  @tap="details(item)">
+				<view class="flex align-center justify-between">
+					<view class="">商户ID:{{item.mercId}}</view>
+					
+					<view class="">
+						<u-tag v-if="!item.isOpenDoor" text="补货" type="error"></u-tag>
+						<u-tag v-else text="开门补货"></u-tag>
+					</view>
+				</view>
+				<view class="flex">设备ID:{{item.deviceId}}</view>
+				<view class="flex" v-if="item.urlVideo0">视频路径:{{item.urlVideo0}}</view>
+				<view class="flex" v-if="item.urlVideo1">视频路径:{{item.urlVideo1}}</view>
+				<view class="flex">创建人姓名:{{item.createUserName}}</view>
+				<view class="flex">创建时间:{{item.createTime}}</view>
+			</view>
+		</scroll-view>
+		<view v-if="isEmpty===true" class='empty'>
+			<u-empty mode="data" text="数据为空"></u-empty>
+		</view>
+
+
+		<u-picker title='是否开门' :show="pickershow" :columns="isOpenDoor" keyName="value" @confirm="pickerconfirm"
+			@cancel='pickerclose'></u-picker>
+
+
+		<u-datetime-picker :show="show" :closeOnClickOverlay="true" mode="date" @confirm="confirm" @cancel="close" v-model="timeStamp"></u-datetime-picker>
+	</view>
+</template>
+
+<script>
+	import {
+		page
+	} from "@/api/replenishment/replenishment.js"
+	export default {
+		data() {
+			return {
+				page: {
+					deviceId: '',
+					isOpenDoor: '',
+					beginCreateTime: '',
+					endCreateTime: ''
+				},
+				dateStart: '',
+				dateend: '',
+				type: '',
+				replenishmentList: [],
+				mode: 'range',
+				show: false,
+				pickershow: false,
+				date: [],
+				index: 1,
+				isopen: '',
+				isEmpty: false,
+				isOpenDoor: [
+					[{
+							type: '',
+							value: '全部'
+						},
+						{
+							type: true,
+							value: '是'
+						}, {
+							type: false,
+							value: '否'
+						}
+					],
+				],
+				timeStamp: new Date(), //时间picker显示时间
+			}
+		},
+		onLoad(o) {
+			if(o.id){
+				this.page.deviceId=o.id
+			}
+			this.currentDate = new Date();
+			this.currentDate = new Date();
+			this.dateStart = uni.$u.timeFormat(this.currentDate, 'yyyy-mm-dd')
+			this.dateend = uni.$u.timeFormat(this.currentDate, 'yyyy-mm-dd')
+
+			this.page.beginCreateTime = this.dateStart + ' 00:00:00'
+			this.page.endCreateTime = this.dateend + ' 23:59:59'
+			this.getPage()
+		},
+		methods: {
+			franchiseePickerChange(e) {
+				this.index = e.detail.value
+			},
+			replenishmentdate(type) {
+				if (type == 'start') {
+					this.type = type
+				}
+				if (type == 'end') {
+					this.type = type
+				}
+				this.show = true
+			},
+			close() {
+				this.show = false
+			},
+			confirm(e) {
+				if (this.type == 'start') {
+					this.dateStart = uni.$u.timeFormat(e.value, 'yyyy-mm-dd')
+					this.page.beginCreateTime = this.dateStart + ' 00:00:00'
+				}
+				if (this.type == 'end') {
+					this.dateend = uni.$u.timeFormat(e.value, 'yyyy-mm-dd')
+					this.page.endCreateTime = this.dateend+ ' 23:59:59'
+				}
+				
+				if(this.dateStart>this.dateend){
+					uni.$u.toast('开始日期不能大于结束日期')
+					return;
+				}
+				
+				this.getPage()
+				this.show = false
+			},
+			custom() {
+				this.getPage()
+			},
+			pickerconfirm(e) {
+				this.isopen = e.value[0].value
+
+				this.page.isOpenDoor = e.value[0].type
+
+				this.getPage()
+				this.pickershow = false
+			},
+			pickerclose() {
+				this.pickershow = false
+			},
+			// 补货详情
+			details(val) {
+				this.$tab.navigateTo('replenishmentRecordDetails?id=' + val.id)
+			},
+			getPage() {
+				page({
+					page: {
+						size: -1,
+					},
+					"deviceId": this.page.deviceId,
+					"isOpenDoor": this.page.isOpenDoor,
+					"beginCreateTime": this.page.beginCreateTime,
+					"endCreateTime": this.page.endCreateTime
+				}).then(res => {
+					if (res.data.page == null) {
+						this.isEmpty = true
+					}
+					this.replenishmentList = res.data.records;
+					this.isEmpty = this.replenishmentList.length == 0;
+
+				})
+			}
+		}
+	}
+</script>
+<style>
+	page {
+		background-color: #f7f7f7;
+	}
+</style>
+<style lang="scss" scoped>
+	.container {
+		.margintop {
+			margin-top: 20rpx;
+		}
+
+		.marleft {
+			margin-left: 10rpx;
+		}
+
+		.scrollview {
+		
+		}
+
+		.empty {
+			margin-top: 120rpx
+		}
+
+		.search {
+			padding: 24rpx 24rpx;
+			background-color: #fff;
+		}
+
+		.time {
+			font-size: 26rpx;
+			font-weight: 400;
+			color: #333440;
+
+
+		}
+
+		.date-image {
+			width: 30rpx;
+			margin-left: 10rpx;
+		}
+
+		.filter {
+			font-size: 26rpx;
+			font-weight: 400;
+			color: #000000;
+			margin-left: 22rpx;
+		}
+
+
+
+		.date {
+			margin-left: 10rpx;
+			padding: 15rpx;
+			border-radius: 15rpx;
+			background-color: #fff;
+		}
+
+		.magin {
+			margin: 10rpx 20rpx;
+		}
+
+		.replenishmentrecord-container {
+			background-color: #fff;
+			padding: 20rpx;
+			border-radius: 15rpx;
+			margin:0 20rpx 20rpx 20rpx;
+			box-shadow: 0 5rpx 4rpx rgba(179, 179, 179, 0.3);
+		}
+	}
+</style>

+ 155 - 0
pages/replenish/replenishmentRecordDetails.vue

@@ -0,0 +1,155 @@
+<template>
+	<view class="container">
+		<u-navbar leftIconColor="#fff" titleStyle="color:#fff;fontSize:36rpx;" :autoBack="true" bgColor="#2C6FF3"
+			:placeholder="true" title="补货记录详情"></u-navbar>
+
+		<view class="topTip">总共补了{{replenishmentList.length}}种商品<!--  数量是{{total}} --></view>
+
+		<scroll-view class="scrollview" :scroll-with-animation="true" scroll-y lower-threshold="100" :style="{'height':fullHeight}">
+			<view class="replenishmentrecord-container" v-for="(item,index) in replenishmentList" :key="item.id">
+				<view class="">
+					设备商品:{{item.goodsName}}
+				</view>
+				<view class="" style="margin-top: 20rpx;">
+					设备商品Id:{{item.deviceGoodsId}}
+				</view>
+
+				<view class="flex align-center"
+					style="margin-top: 15rpx;background-color: #f5f8fb;padding: 10rpx;border-radius: 15rpx;">
+					<view class="">
+						<u--image radius='15rpx' :showLoading="true" :src="item.cover" width="80px" height="80px"
+							@click="click"></u--image>
+					</view>
+
+					<view class="flex flex-direction align-start" style="margin-left: 35rpx;">
+						<view class="flex align-center justify-around" v-if="item.oldStock!=item.newStock">
+							<view class="flex flex-sub width">原库存:{{item.oldStock}}</view>
+							<view class="flex">新库存:{{item.newStock}}</view>
+						</view>
+						<view class="flex align-center justify-around" v-if="item.oldStock!=item.newStock">
+							<view class="flex flex-sub" style="color: red;">补货数量:{{item.newStock-item.oldStock}}</view>
+						</view>
+						<view class="flex align-center justify-around" v-if="item.oldCapacity!=item.newCapacity">
+							<view class="flex flex-sub width">原容量:{{item.oldCapacity}}</view>
+							<view class="flex">新容量:{{item.newCapacity}}</view>
+						</view>
+						<view class="flex align-center justify-around" v-if="item.newPrice!=null&&item.oldPrice!=item.newPrice">
+							<view class="flex flex-sub width">原价格:¥{{(Number(item.oldPrice)/100).toFixed(2)}}</view>
+							<view class="flex">新价格:¥{{(Number(item.newPrice)/100).toFixed(2)}}</view>
+						</view>
+						<view class="flex align-center justify-around" v-if="item.oldWarning!=item.newWarning">
+							<view class="flex flex-sub width">原预警值:{{item.oldWarning}}</view>
+							<view class="flex">新预警值:{{item.newWarning}}</view>
+						</view>
+
+					</view>
+
+
+				</view>
+
+
+
+
+
+
+			</view>
+		</scroll-view>
+	</view>
+</template>
+
+<script>
+	import {
+		obj
+	} from "@/api/replenishment/replenishment.js"
+	export default {
+		data() {
+			return {
+				replenishmentList: [],
+				isEmpty: false,
+				id: '',
+				total: 0,
+				fullHeight:0,
+			}
+		},
+		onLoad(e) {
+			let _this = this;
+			const query = uni.createSelectorQuery().in(this);
+			query.select(".scrollview").boundingClientRect((data) => {
+				uni.getSystemInfo({
+					success(res) {
+						_this.fullHeight = 2 * (res.windowHeight - data.top) + 'rpx';
+					},
+				});
+			}).exec();
+
+			this.id = e.id
+			this.getObj()
+		},
+		methods: {
+			getObj() {
+				obj({
+					"id": this.id,
+					"activityId": 0
+				}).then(res => {
+					if (res.data.goods == null) {
+						this.isEmpty = true
+					}
+					this.replenishmentList = res.data.goods;
+					this.replenishmentList.forEach(item => {
+						this.total = 0
+						this.total += item.newStock - item.oldStock
+					})
+
+					this.isEmpty = this.replenishmentList.length == 0;
+
+				})
+			}
+		}
+	}
+</script>
+<style>
+	page {
+		background-color: #f7f7f7;
+	}
+</style>
+<style lang="scss" scoped>
+	.container {
+		.margintop {
+			margin-top: 20rpx;
+		}
+
+		.marleft {
+			margin-left: 10rpx;
+		}
+
+		.scrollview {}
+
+		.topTip {
+			padding: 15rpx;
+			text-align: center;
+			background-color: #fdf6ec;
+			color: #faba5a;
+		}
+
+		.empty {
+			margin-top: 120rpx
+		}
+
+		.width {
+			width: 220rpx;
+		}
+
+
+		.magin {
+			margin: 10rpx 20rpx;
+		}
+
+		.replenishmentrecord-container {
+			background-color: #fff;
+			padding: 20rpx;
+			border-radius: 15rpx;
+			margin: 0 20rpx 20rpx 20rpx;
+			box-shadow: 0 5rpx 4rpx rgba(179, 179, 179, 0.3);
+		}
+	}
+</style>

+ 251 - 0
pages/system/addEmployee.vue

@@ -0,0 +1,251 @@
+<template>
+	<view class="container">
+		<u-navbar leftIconColor="#fff" titleStyle="color:#fff;fontSize:36rpx;" :autoBack="true" bgColor="#2C6FF3"
+			:placeholder="true" title="人员管理"></u-navbar>
+		<view class="content">
+			<view class="list">
+				<u-cell-group :border="false">
+					<u-cell title="登录名">
+						<input slot="value" placeholder="请输入登录名" type="text" v-model="params.account"
+							class="u-slot-value"></input>
+					</u-cell>
+					<u-cell title="姓名">
+						<input slot="value" placeholder="请输入姓名" type="text" v-model="params.name"
+							class="u-slot-value"></input>
+					</u-cell>
+					<u-cell title="手机号">
+						<input slot="value" placeholder="请输入手机号" type="number" v-model="params.tel"
+							class="u-slot-value"></input>
+					</u-cell>
+					<u-cell title="邮箱">
+						<input slot="value" placeholder="请输入邮箱" type="" v-model="params.mail"
+							class="u-slot-value"></input>
+					</u-cell>
+					<u-cell title="密码">
+						<input slot="value" placeholder="请输入密码" type="password" v-model="params.password"
+							class="u-slot-value"></input>
+					</u-cell>
+					<u-cell title="角色" :isLink="true" @click="popShow=true">
+						<text slot="value">{{roleName}}</text>
+					</u-cell>
+					<u-cell title="备注" :border="false">
+						<input slot="value" placeholder="请输入备注" type="" v-model="params.remark"
+							class="u-slot-value"></input>
+					</u-cell>
+				</u-cell-group>
+			</view>
+			<view style="color: red;line-height: 60rpx;text-align: right;padding-right: 20rpx;">
+				{{err}}
+			</view>
+			<view class="btn safe-bottom">
+				<xbutton round="82rpx" size="large" @click="submit">创建</xbutton>
+			</view>
+		</view>
+
+		<xpopup :show="popShow" @close="popClose" @confirm="popSubmit" :showBtn="true" title="选择角色">
+			<view class="pop-content flex">
+				<!-- 多选 -->
+				<view class="u-page__tag-item" v-for="(item, index) in roleList" :key="item.name">
+					<u-tag :text="item.name" :plain="!item.checked" type="warning" :name="index" @click="checkboxClick">
+					</u-tag>
+				</view>
+			</view>
+		</xpopup>
+	</view>
+</template>
+
+<script>
+	import {
+		save,
+		roleList
+	} from "@/api/system/employee.js"
+	export default {
+		data() {
+			return {
+				params: {
+					"name": "",
+					"tel": "",
+					"mail": "",
+					"password": "",
+					"roleIds": "",
+					"account": "",
+					"remark":""
+				},
+				roleName: '',
+				popShow: false,
+				roleList: [],
+				err: ''
+			}
+		},
+
+		onLoad(o) {
+			this.getRoleList()
+		},
+
+		methods: {
+			getRoleList() {
+				roleList({
+					page: {
+						current: 1,
+						size: 10000
+					},
+					sysId: uni.getStorageSync('sysId')
+				}).then(res => {
+					let data = res.data.records;
+					if (data.length > 0) {
+						let newData = data.map(i => {
+							return {
+								id: i.id,
+								name: i.name,
+								checked: false
+							}
+						})
+						this.roleList = newData;
+					} else {
+						this.roleList = [];
+					}
+				})
+			},
+
+			//弹框提交
+			submit() {
+				save(this.params).then(res => {
+					this.$modal.msg('保存成功')
+					this.$tab.navigateBack()
+					this.err = ''
+				}).catch(err => {
+					if (err.length > 15) {
+						this.err = '请重试!'
+					} else {
+						this.err = err.substr(0, 12)
+					}
+				})
+			},
+
+			//滚动到底加载更多
+			onReachBottom() {
+				if (this.noMore) return
+				this.page++
+				this.getPointList()
+			},
+
+			//禁用
+			stop() {
+				this.popShow = true
+			},
+
+			//弹框关闭
+			popClose() {
+				this.popShow = false
+			},
+
+			//弹框提交
+			popSubmit() {
+				this.popClose()
+				let nameVal = []
+				let idVal = []
+				this.roleList.forEach(i => {
+					if (i.checked) {
+						nameVal.push(i.name)
+						idVal.push(i.id)
+					}
+				})
+				this.roleName = nameVal.join(',')
+				this.params.roleIds = idVal
+			},
+
+			checkboxClick(name) {
+				this.roleList[name].checked = !this.roleList[name].checked
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.container {
+		overflow: hidden;
+
+		.content {
+			padding: 0rpx 13rpx 12rpx;
+			overflow: hidden;
+
+			.list {
+				width: 100%;
+				margin-top: 24rpx;
+				background-color: #fff;
+				padding: 0rpx 30rpx;
+				border-radius: 12rpx;
+
+				input {
+					text-align: right;
+				}
+
+				/deep/ .u-cell__body {
+					padding: 30rpx 6rpx;
+				}
+			}
+		}
+	}
+
+	.empty {
+		margin-top: 40%;
+	}
+
+	.btn {
+		width: 100%;
+		position: fixed;
+		left: 0;
+		padding: 0 24rpx;
+	}
+
+	.popup-content {
+		padding: 36rpx 24rpx;
+		display: flex;
+		flex-flow: row nowrap;
+		justify-content: flex-start;
+		align-items: center;
+
+		input {
+			border: 1rpx solid #999;
+			border-radius: 6rpx;
+			width: 530rpx;
+			padding: 0 24rpx;
+		}
+
+		&.edit-point {
+			flex-direction: column;
+			align-items: flex-start;
+
+			.edit-point-item {
+				width: 100%;
+				padding-left: 170rpx;
+				position: relative;
+
+				&+.edit-point-item {
+					margin-top: 12rpx;
+				}
+
+				>view:nth-child(1) {
+					position: absolute;
+					left: 0;
+					top: 50%;
+					transform: translateY(-50%);
+				}
+			}
+		}
+	}
+
+	.del-popup-content {
+		padding: 36rpx 24rpx;
+		text-align: center;
+		font-size: 32rpx;
+	}
+
+	.pop-content {
+		padding: 24rpx;
+
+		.u-page__tag-item {
+			margin-right: 24rpx;
+		}
+	}
+</style>

+ 427 - 0
pages/system/deviceManage.vue

@@ -0,0 +1,427 @@
+<template>
+	<view class="container">
+		<u-navbar leftIconColor="#fff" titleStyle="color:#fff;fontSize:36rpx;" :autoBack="true" bgColor="#2C6FF3"
+			:placeholder="true" title="团队成员设备授权"></u-navbar>
+		<view class="content">
+			<view class="search">
+				<u-search animation placeholder="搜索设备" :clearabled="false" v-model="keyword" :showAction="false"
+					@search="search"></u-search>
+				<view class="scan-icon" @click="scan">
+					<u-icon name="scan" size="22" color="#909399"></u-icon>
+				</view>
+
+				<view class="search-history flex flex-wrap flex-start">
+					<view class="history-item" v-for="(item,index) in historyList" :key="index"
+						@click="searchFast(item)">
+						{{item}}
+					</view>
+				</view>
+			</view>
+			<view class="tab-wrap">
+				<view class="tab">
+					<u-tabs :list="tabList" :activeStyle="{color: '#333',fontWeight: 'bold',fontSize:'36rpx'}"
+						:inactiveStyle="{fontSize:'32rpx'}" :scrollable="false" :current="current" @click="tabClick"
+						lineColor="#2C6FF3">
+					</u-tabs>
+				</view>
+			</view>
+
+			<scroll-view class="scrollview" :scroll-with-animation="true" scroll-y lower-threshold="100"
+				@scrolltolower="scrolltolower" :style="{height:fullHeight}">
+
+				<view class="list" v-if="list&&list.length>0">
+					<block v-for="(item, index) in list" :key="index">
+						<view class="thumb-box" @click.stop="commItemSelect(item)">
+							<view>
+								<image class="select-img"
+									src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/commodity/no_selected.png"
+									mode="widthFix" v-show="item.noSelect"></image>
+								<image class="select-img"
+									src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/commodity/selected.png"
+									mode="widthFix" v-show="!item.noSelect&&item.checked"></image>
+								<image class="select-img"
+									src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/commodity/select.png"
+									mode="widthFix" v-show="!item.noSelect&&!item.checked"></image>
+							</view>
+							<view class="check-content">
+								<view class="comm-main">
+									<view>
+										{{item.deviceName}}
+									</view>
+									<view>
+										设备id:{{item.deviceId}}
+									</view>
+								</view>
+							</view>
+						</view>
+					</block>
+					<view class="more" style="overflow: hidden;">
+						<u-loadmore :status="status" v-if="list.length>=1" />
+					</view>
+				</view>
+				<view class="empty" v-if="list.length==0">
+					<u-empty mode="list" text="没有设备!"></u-empty>
+				</view>
+			</scroll-view>
+		</view>
+
+		<view class="btn safe-bottom">
+			<xbutton size="large" round="82rpx" @click="submit">{{current==0?'解绑':'绑定'}}</xbutton>
+		</view>
+	</view>
+</template>
+
+<script>
+	import {
+		mercDeviceList,
+		addDevice,
+		userDeviceList,
+		delDevice
+	} from "@/api/device/device.js"
+
+	export default {
+		data() {
+			return {
+				keyword: '',
+				list: [], //商品列表
+
+				page: 1, //商品分页
+				size: 10,
+
+				status: 'loadmore', //加载更多
+				type: null,
+				storeName: null,
+				title: '私有商品库',
+				btnName: '添加到商品私库',
+				selectList: [],
+				id: null,
+				historyList: [],
+
+				tabList: [{
+						name: '已授权'
+					},
+					{
+						name: '未授权'
+					}
+				],
+				current: 0,
+				fullHeight: 0,
+				userId: null,
+			}
+		},
+
+		onLoad(o) {
+			let _this = this;
+			const query = uni.createSelectorQuery().in(this);
+			query.select(".scrollview").boundingClientRect((data) => {
+				uni.getSystemInfo({
+					success(res) {
+						// 针对iPhone X等机型底部安全距离做适配
+						const model = res.model;
+						const modelInclude = [
+							"iPhone X",
+							'iPhone XR',
+							"iPhone XS",
+							"iPhone XS MAX",
+							"iPhone 12/13 mini",
+							"iPhone 12/13 (Pro)",
+							"iPhone 12/13 Pro Max",
+							"iPhone 14 Pro Max"
+						];
+						let safeDistance = modelInclude.includes(model)
+						//动态设置商品区域高度
+						if (safeDistance) {
+							_this.fullHeight = res.windowHeight - data.top - 34 + 'px';
+						} else {
+							_this.fullHeight = res.windowHeight - data.top + 'px';
+						}
+					},
+				});
+			}).exec();
+
+			this.userId = o.id;
+
+			this.getList()
+		},
+
+		computed: {
+			deviceIdList() {
+				let list = [];
+				this.list.forEach(i => {
+					if (i.checked) {
+						list.push(i.deviceId)
+					}
+				})
+				return list
+			}
+		},
+		methods: {
+			search(val) {
+				this.saveKeyWord('goods', this.keyword)
+				this.reset();
+				this.getList()
+			},
+
+			tabClick(e) {
+				this.current = e.index
+				this.search()
+			},
+
+			saveKeyWord(type, val) {
+				if (val) {
+					let arr = []
+					if (uni.getStorageSync(type)) {
+						let arr = JSON.parse(uni.getStorageSync(type))
+						if (arr.indexOf(val) != -1) {
+							arr.splice(arr.indexOf(val), 1)
+						}
+						arr.unshift(val)
+						if (arr.length > 6) {
+							arr.pop()
+						}
+						this.historyList = JSON.parse(JSON.stringify(arr))
+						uni.setStorageSync(type, JSON.stringify(arr))
+					} else {
+						arr.unshift(val)
+						this.historyList = JSON.parse(JSON.stringify(arr))
+						uni.setStorageSync(type, JSON.stringify(arr))
+					}
+				} else {
+					return
+				}
+			},
+
+			searchFast(e) {
+				this.keyword = e
+				this.search()
+			},
+
+			//扫码
+			scan() {
+				uni.scanCode({
+					success: (res) => {
+						this.keyword = res.result;
+						this.getList()
+					}
+				});
+			},
+
+			//根据类目获取商品列表
+			getList(id) {
+				if (this.current == 0) {
+					userDeviceList({
+						page: {
+							current: this.page,
+							size: this.size
+						},
+						keyword: this.keyword,
+						userInfoId: this.userId,
+					}).then(res => {
+						let newData = res.data.records;
+						newData.forEach(i => {
+							i.checked = false
+						})
+						if (newData.length < 10) {
+							this.status = "nomore"
+						} else {
+							this.status = "loadmore"
+						}
+						this.list = this.list.concat(newData)
+						console.log(this.list)
+					})
+				}
+				if (this.current == 1) {
+					mercDeviceList({
+						page: {
+							current: this.page,
+							size: this.size
+						},
+						keyword: this.keyword,
+						userInfoId: this.userId,
+					}).then(res => {
+						let newData = res.data;
+						newData.forEach(i => {
+							i.checked = false
+						})
+						if (newData.length < 10) {
+							this.status = "nomore"
+						} else {
+							this.status = "loadmore"
+						}
+						this.list = this.list.concat(newData)
+					})
+				}
+			},
+
+			//触底加载更多
+			scrolltolower() {
+				if (this.status == 'nomore') return
+				this.page++
+				this.getList()
+			},
+
+			reset() {
+				this.status == 'loadmore'
+				this.page = 1;
+				this.size = 10;
+				this.list = [];
+			},
+
+			// 商品选中状态改变
+			commItemSelect(e) {
+				e.checked = !e.checked
+			},
+
+			submit() {
+
+				uni.showModal({
+					title: '提示',
+					content: `是否确认${this.current==0?'解绑':'绑定'}`,
+					success: res => {
+						if (res.confirm) {
+							if (this.current == 0) {
+								delDevice({
+									"userInfoId": this.userId,
+									"deviceIdList": this.deviceIdList
+								}).then(res => {
+									this.$modal.msg('解绑成功')
+									this.search()
+								})
+							} else {
+								addDevice({
+									"userInfoId": this.userId,
+									"deviceIdList": this.deviceIdList
+								}).then(res => {
+									this.$modal.msg('绑定成功')
+									this.search()
+								})
+							}
+						} else if (res.cancel) {
+							console.log('用户点击取消');
+						}
+					}
+				});
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.container {
+		.content {
+			padding-bottom: 88rpx;
+
+			.search {
+				padding: 24rpx 24rpx 12rpx;
+				background-color: #fff;
+				position: relative;
+
+				.scan-icon {
+					position: absolute;
+					right: 36rpx;
+					top: 38rpx;
+					z-index: 2;
+				}
+
+				.search-history {
+
+					.history-item {
+						margin-right: 24rpx;
+						padding: 0 12rpx;
+						background-color: #f2f2f2;
+						color: #333;
+						font-size: 24rpx;
+						line-height: 40rpx;
+						border-radius: 40rpx;
+						margin-top: 24rpx;
+					}
+				}
+			}
+
+			.tab-wrap {
+				background-color: #fff;
+				.tab{
+					width:50%;
+				}
+			}
+
+			.list {
+				width: 100%;
+				padding: 0rpx 13rpx 12rpx;
+				padding-bottom: calc(110rpx + env(safe-area-inset-bottom) / 2);
+				overflow: hidden;
+
+				.thumb-box {
+					border-bottom: 1rpx solid #f4f4f4;
+					display: flex;
+					flex-flow: row nowrap;
+					padding: 20rpx;
+					background-color: #fff;
+					border-radius: 12rpx;
+					margin-top: 12rpx;
+				}
+
+				.select-img {
+					width: 40rpx;
+					height: 40rpx;
+					margin-top: 14rpx;
+				}
+
+				.check-content {
+					display: flex;
+					flex-direction: row;
+					align-items: center;
+					padding-left: 12rpx;
+
+					.comm-img {
+						width: 130rpx;
+						height: 130rpx;
+						display: flex;
+						flex-direction: row;
+						align-items: center;
+						justify-content: space-around;
+
+						image {
+							width: 100%;
+						}
+					}
+
+					.comm-main {
+						box-sizing: border-box;
+						padding-left: 18rpx;
+						color: #999;
+						line-height: 60rpx;
+
+						>view {
+							padding: 4rpx 0;
+						}
+
+						>view:nth-child(1) {
+							font-size: 30rpx;
+							font-weight: bold;
+							color: #333;
+						}
+
+						>view:nth-child(2) {
+							font-size: 28rpx;
+						}
+					}
+				}
+			}
+		}
+
+		.btn {
+			width: 100%;
+			position: fixed;
+			left: 0;
+			padding: 0 24rpx;
+		}
+
+		.empty {
+			position: absolute;
+			left: 50%;
+			top: 50%;
+			transform: translate(-50%, -50%);
+		}
+	}
+</style>

+ 285 - 0
pages/system/empDetail.vue

@@ -0,0 +1,285 @@
+<template>
+	<view class="container">
+		<u-navbar leftIconColor="#fff" titleStyle="color:#fff;fontSize:36rpx;" :autoBack="true" bgColor="#2C6FF3"
+			:placeholder="true" :title="title"></u-navbar>
+		<view class="content">
+			<view class="list">
+				<u-cell-group :border="false">
+					<u-cell title="登录名">
+						<input slot="value" placeholder="请输入登录名" type="text" v-model="params.account"
+							class="u-slot-value"></input>
+					</u-cell>
+					<u-cell title="姓名">
+						<input slot="value" placeholder="请输入姓名" type="text" v-model="params.name"
+							class="u-slot-value"></input>
+					</u-cell>
+					<u-cell title="密码">
+						<input slot="value" placeholder="请输入密码" type="password" v-model="params.password"
+							class="u-slot-value"></input>
+					</u-cell>
+					<u-cell title="手机号">
+						<input slot="value" placeholder="请输入手机号" type="number" v-model="params.tel"
+							class="u-slot-value"></input>
+					</u-cell>
+					<!-- <u-cell title="邮箱">
+						<input slot="value" placeholder="请输入邮箱" type="number" v-model="params.mail"
+							class="u-slot-value"></input>
+					</u-cell> -->
+					<u-cell title="角色" :isLink="true" @click="popShow=true">
+						<text slot="value">{{roleName}}</text>
+					</u-cell>
+					<u-cell title="备注">
+						<input slot="value" placeholder="请输入备注" type="" v-model="params.remark"
+							class="u-slot-value"></input>
+					</u-cell>
+					<u-cell title="可管理设备" :isLink="true" :url="`/pages/system/deviceManage?id=${params.userId}`">
+					</u-cell>
+					<u-cell title="支付宝" :isLink="true" @click="bindAli">
+						<text slot="value">{{params.aliUserId?'已绑定':'未绑定'}}</text>
+					</u-cell>
+					<u-cell title="状态" :border="false">
+						<view slot="value" @click="stop">
+							{{params.status?'启用':'禁用'}}
+						</view>
+					</u-cell>
+
+				</u-cell-group>
+			</view>
+			<view style="color: red;line-height: 60rpx;text-align: right;padding-right: 26rpx;">
+				{{err}}
+			</view>
+			<view class="btn safe-bottom">
+				<xbutton round="82rpx" size="large" @click="sure">保存</xbutton>
+			</view>
+		</view>
+
+		<xpopup :show="popShow" @close="popClose" @confirm="popSubmit" :showBtn="true" title="选择角色">
+			<view class="pop-content flex">
+				<!-- 多选 -->
+				<view class="u-page__tag-item" v-for="(item, index) in roleList" :key="item.name">
+					<u-tag :text="item.name" :plain="!item.checked" type="warning" :name="index" @click="checkboxClick">
+					</u-tag>
+				</view>
+			</view>
+		</xpopup>
+	</view>
+</template>
+
+<script>
+	import {
+		roleList,
+		objByUserId,
+		update
+	} from "@/api/system/employee.js"
+	import {
+		showConfirm
+	} from '@/utils/common'
+	export default {
+
+		data() {
+			return {
+				id: '',
+				title: '',
+
+				params: {
+					name: "",
+					tel: "",
+					// "mail": "",
+					password: "",
+					roleIds: "",
+					account: "",
+					status: true,
+					remark:""
+				},
+				popShow: false,
+				roleList: [],
+				err: ''
+			}
+		},
+
+		computed: {
+			roleName() {
+				let val = []
+				this.roleList.forEach(i => {
+					if (i.checked) {
+						val.push(i.name)
+					}
+				})
+				return val.join(',')
+			},
+			roleIds() {
+				let val = []
+				this.roleList.forEach(i => {
+					if (i.checked) {
+						val.push(i.id)
+					}
+				})
+				return val
+			}
+		},
+
+		onLoad(o) {
+			this.title = o.title;
+			this.id = o.id;
+		},
+
+		async onShow() {
+			await this.getRoleList()
+			if (this.id) {
+				await this.getDetail();
+			}
+		},
+
+		methods: {
+			getRoleList() {
+				return new Promise((resolve, reject) => {
+					roleList({
+						page: {
+							current: 1,
+							size: 10000
+						},
+						sysId: uni.getStorageSync('sysId')
+					}).then(res => {
+						let data = res.data.records;
+						if (data.length > 0) {
+							let newData = data.map(i => {
+								return {
+									id: i.id,
+									name: i.name,
+									checked: false
+								}
+							})
+							this.roleList = newData;
+						} else {
+							this.roleList = [];
+						}
+						resolve(data)
+					}).catch(err => {
+						reject(err)
+					})
+				})
+			},
+
+			getDetail(id) {
+				objByUserId({
+					userId: this.id
+				}).then(res => {
+					this.params = res.data;
+					//反显角色
+					this.roleList.forEach(i => {
+						res.data.roleNames.forEach(j => {
+							if (j == i.name) {
+								i.checked = true
+							}
+						})
+					})
+				})
+			},
+
+			//禁用
+			stop() {
+				this.params.status = !this.params.status
+			},
+
+			//弹框关闭
+			popClose() {
+				this.popShow = false
+			},
+
+			//弹框提交
+			popSubmit() {
+				update({
+					userId: this.params.userId,
+					roleIds: this.roleIds
+				}).then(res => {
+					this.popClose()
+				})
+				setTimeout(() => {
+					this.getDetail()
+				}, 500)
+			},
+
+			checkboxClick(name) {
+				this.roleList[name].checked = !this.roleList[name].checked
+			},
+
+			bindAli() {
+				this.$tab.navigateTo('/pages/activeDevice/bindAliAcc?userId=' + this.id)
+			},
+
+			sure() {
+				let params = {
+					userId: this.params.userInfoId,
+					name: this.params.name,
+					tel: this.params.tel,
+					account: this.params.account,
+					roleIds: this.roleIds,
+					status: this.params.status,
+					remark:this.params.remark
+				}
+				update(params).then(res => {
+					this.$modal.msg('保存成功')
+					this.$tab.navigateBack()
+				}).catch(err => {
+					if (err.length > 15) {
+						this.err = '请重试!'
+					} else {
+						this.err = err.substr(0, 12)
+					}
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.container {
+		overflow: hidden;
+
+		.content {
+			padding: 0rpx 13rpx 12rpx;
+			overflow: hidden;
+
+			.list {
+				width: 100%;
+				margin-top: 24rpx;
+				background-color: #fff;
+				padding: 0rpx 30rpx;
+				border-radius: 12rpx;
+
+				input {
+					text-align: right;
+				}
+
+				/deep/ .u-cell__body {
+					padding: 30rpx 6rpx;
+				}
+			}
+		}
+	}
+
+	.empty {
+		margin-top: 40%;
+	}
+
+	.btn {
+		width: 100%;
+		position: fixed;
+		left: 0;
+		padding: 0 24rpx;
+	}
+
+	.del-popup-content {
+		padding: 36rpx 24rpx;
+		text-align: center;
+		font-size: 32rpx;
+	}
+
+	.pop-content {
+		padding: 24rpx;
+
+		.u-page__tag-item {
+			margin-right: 24rpx;
+		}
+	}
+</style>

+ 282 - 0
pages/system/employee.vue

@@ -0,0 +1,282 @@
+<template>
+	<view class="container">
+		<u-navbar leftIconColor="#fff" titleStyle="color:#fff;fontSize:36rpx;" :autoBack="true" bgColor="#2C6FF3"
+			:placeholder="true" title="人员管理"></u-navbar>
+		<view class="content">
+
+			<scroll-view class="scrollview" :scroll-with-animation="true" scroll-y lower-threshold="100"
+				:style="{height:fullHeight}">
+				<view class="list" v-if="list&&list.length>0">
+					<block v-for="(item, index) in list" :key="index">
+						<view class="comm-main flex justify-between">
+							<view @click.stop="detail(item)">
+								<view class="flex align-center">
+									<view>
+										{{item.name}}
+									</view>
+									<view>
+										{{item.roleNames[0]||'无'}}
+									</view>
+								</view>
+								<view class="phone-user flex align-center" @click.stop="phone(item.tel)">
+									<image class="image" src="https://ossfile.mxrvending.com/assets/xy_merc_mini/images/system/tel.png" mode="widthFix"></image>
+									<view class="tel">
+										{{item.tel}}
+									</view>
+								</view>
+							</view>
+
+							<view class="stop">
+								<u-switch activeColor="#2C6FF3" size="20" v-model="item.status"
+									@change="stop(item)"></u-switch>
+							</view>
+						</view>
+					</block>
+				</view>
+				<view class="empty" v-if="list.length==0">
+					<u-empty mode="list" text="没有用户!"></u-empty>
+				</view>
+			</scroll-view>
+
+
+			<view class="btn safe-bottom">
+				<xbutton size="large" round="82rpx" @click="add">添加人员</xbutton>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import {
+		list,
+		update
+	} from "@/api/system/employee.js"
+	export default {
+		data() {
+			return {
+				list: [], //列表
+				fullHeight: 0,
+			}
+		},
+
+		onLoad() {
+			let _this = this;
+			const query = uni.createSelectorQuery().in(this);
+			query.select(".scrollview").boundingClientRect((data) => {
+				uni.getSystemInfo({
+					success(res) {
+						// 针对iPhone X等机型底部安全距离做适配
+						const model = res.model;
+						const modelInclude = [
+							"iPhone X",
+							'iPhone XR',
+							"iPhone XS",
+							"iPhone XS MAX",
+							"iPhone 12/13 mini",
+							"iPhone 12/13 (Pro)",
+							"iPhone 12/13 Pro Max",
+							"iPhone 14 Pro Max"
+						];
+						let safeDistance = modelInclude.includes(model)
+						//动态设置商品区域高度
+						if (safeDistance) {
+							_this.fullHeight = res.windowHeight - data.top - 34 + 'px';
+						} else {
+							_this.fullHeight = res.windowHeight - data.top + 'px';
+						}
+					},
+				});
+			}).exec();
+		},
+
+		onShow() {
+			this.getList()
+		},
+
+		methods: {
+			//获取分页
+			getList() {
+				list({}).then(res => {
+					this.list = res.data
+				})
+			},
+
+			detail(item) {
+				this.$tab.navigateTo(`/pages/system/empDetail?title=${item.name}&id=${item.userId}`)
+			},
+
+			stop(item) {
+				let msg = item.status ? '启用' : '禁用'
+				uni.showModal({
+					title: '提示',
+					content: `是否确认${msg}`,
+					success: res=> {
+						if (res.confirm) {
+							update({
+								userId: item.userInfoId,
+								status: item.status
+							}).then(res => {
+								this.getList()
+								setTimeout(()=>{
+									this.$modal.showToast(`${msg}成功~`)
+								},500)
+							}).catch(err => {
+								
+							})
+						} else if (res.cancel) {
+							item.status=!item.status
+						}
+					}
+				});
+			},
+
+			//添加人员
+			add() {
+				this.$tab.navigateTo('/pages/system/addEmployee')
+			},
+			
+			//拨打电话
+			phone(tel){
+				uni.makePhoneCall({
+					phoneNumber: tel
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.container {
+		overflow: hidden;
+
+		.content {
+
+			.list {
+				width: 100%;
+				padding: 0rpx 13rpx 12rpx;
+				padding-bottom: calc(110rpx + env(safe-area-inset-bottom) / 2);
+				overflow: hidden;
+
+				.comm-img {
+					width: 130rpx;
+					height: 130rpx;
+					display: flex;
+					flex-direction: row;
+					align-items: center;
+					justify-content: space-around;
+
+					image {
+						width: 130rpx;
+						height: 130rpx;
+					}
+				}
+
+				.comm-main {
+					padding: 20rpx 30rpx;
+					background-color: #fff;
+					border-radius: 12rpx;
+					margin-top: 12rpx;
+					box-sizing: border-box;
+					color: #999;
+					line-height: 60rpx;
+					position: relative;
+					
+					.image{
+						width:23rpx;
+						height:23rpx;
+						margin-right: 12rpx;
+					}
+
+					>view:nth-child(1){
+						width:580rpx;
+						>view {
+							padding: 4rpx 0;
+						}
+						
+						>view:nth-child(1) {
+						
+							>view:nth-child(1) {
+								font-size: 30rpx;
+								font-weight: bold;
+								color: #333;
+							}
+						
+							>view:nth-child(2) {
+								font-size: 28rpx;
+								margin-left: 24rpx;
+							}
+						}
+						
+						>view:nth-child(2) {
+							font-size: 28rpx;
+						}
+					}
+					
+					.phone-user{
+						width:250rpx;
+					}
+
+					.stop {
+						// position: absolute;
+						// right: 30rpx;
+						// top: 26rpx;
+					}
+				}
+			}
+		}
+	}
+
+	.empty {
+		margin-top: 40%;
+	}
+
+	.btn {
+		width: 100%;
+		position: fixed;
+		left: 0;
+		padding: 0 24rpx;
+	}
+
+	.popup-content {
+		padding: 36rpx 24rpx;
+		display: flex;
+		flex-flow: row nowrap;
+		justify-content: flex-start;
+		align-items: center;
+
+		input {
+			border: 1rpx solid #999;
+			border-radius: 6rpx;
+			width: 530rpx;
+			padding: 0 24rpx;
+		}
+
+		&.edit-point {
+			flex-direction: column;
+			align-items: flex-start;
+
+			.edit-point-item {
+				width: 100%;
+				padding-left: 170rpx;
+				position: relative;
+
+				&+.edit-point-item {
+					margin-top: 12rpx;
+				}
+
+				>view:nth-child(1) {
+					position: absolute;
+					left: 0;
+					top: 50%;
+					transform: translateY(-50%);
+				}
+			}
+		}
+	}
+
+	.del-popup-content {
+		padding: 36rpx 24rpx;
+		text-align: center;
+		font-size: 32rpx;
+	}
+</style>

+ 39 - 0
permission.js

@@ -0,0 +1,39 @@
+import { getToken } from '@/utils/auth'
+
+// 登录页面
+const loginPage = "/pages/login"
+  
+// 页面白名单
+const whiteList = [
+  '/pages/login'
+]
+
+// 检查地址白名单
+function checkWhite(url) {
+  const path = url.split('?')[0]
+  return whiteList.indexOf(path) !== -1
+}
+
+// 页面跳转验证拦截器
+let list = ["navigateTo", "redirectTo", "reLaunch", "switchTab"]
+list.forEach(item => {
+  uni.addInterceptor(item, {
+    invoke(to) {
+      if (getToken()) {
+        if (to.path === loginPage) {
+          uni.reLaunch({ url: "/" })
+        }
+        return true
+      } else {
+        if (checkWhite(to.url)) {
+          return true
+        }
+        uni.reLaunch({ url: loginPage })
+        return false
+      }
+    },
+    fail(err) {
+      console.log(err)
+    }
+  })
+})

+ 60 - 0
plugins/auth.js

@@ -0,0 +1,60 @@
+import store from '@/store'
+
+function authPermission(permission) {
+  const all_permission = "*:*:*"
+  const permissions = store.getters && store.getters.permissions
+  if (permission && permission.length > 0) {
+    return permissions.some(v => {
+      return all_permission === v || v === permission
+    })
+  } else {
+    return false
+  }
+}
+
+function authRole(role) {
+  const super_admin = "admin"
+  const roles = store.getters && store.getters.roles
+  if (role && role.length > 0) {
+    return roles.some(v => {
+      return super_admin === v || v === role
+    })
+  } else {
+    return false
+  }
+}
+
+export default {
+  // 验证用户是否具备某权限
+  hasPermi(permission) {
+    return authPermission(permission)
+  },
+  // 验证用户是否含有指定权限,只需包含其中一个
+  hasPermiOr(permissions) {
+    return permissions.some(item => {
+      return authPermission(item)
+    })
+  },
+  // 验证用户是否含有指定权限,必须全部拥有
+  hasPermiAnd(permissions) {
+    return permissions.every(item => {
+      return authPermission(item)
+    })
+  },
+  // 验证用户是否具备某角色
+  hasRole(role) {
+    return authRole(role)
+  },
+  // 验证用户是否含有指定角色,只需包含其中一个
+  hasRoleOr(roles) {
+    return roles.some(item => {
+      return authRole(item)
+    })
+  },
+  // 验证用户是否含有指定角色,必须全部拥有
+  hasRoleAnd(roles) {
+    return roles.every(item => {
+      return authRole(item)
+    })
+  }
+}

+ 17 - 0
plugins/index.js

@@ -0,0 +1,17 @@
+import tab from './tab'
+import auth from './auth'
+import modal from './modal'
+import xy from './xy'
+
+export default {
+  install(Vue) {
+    // 页签操作
+    Vue.prototype.$tab = tab
+    // 认证对象
+    Vue.prototype.$auth = auth
+    // 模态框对象
+    Vue.prototype.$modal = modal
+	// 处理数据
+	Vue.prototype.$xy = xy
+  }
+}

Some files were not shown because too many files changed in this diff