Explorar o código

初始化项目

admin hai 2 semanas
achega
bc7e2c3fb6
Modificáronse 100 ficheiros con 29877 adicións e 0 borrados
  1. BIN=BIN
      .DS_Store
  2. 1 0
      .gitignore
  3. 16 0
      .hbuilderx/launch.json
  4. 448 0
      App.vue
  5. 4914 0
      common/data.js
  6. 33 0
      common/filter.js
  7. 0 0
      common/md5.js
  8. 36 0
      common/pay.js
  9. 34 0
      common/share.js
  10. 30 0
      common/util.js
  11. 113 0
      components/drawer/drawer.vue
  12. 610 0
      components/gmy-img-cropper/gmy-img-cropper.vue
  13. 14 0
      components/gmy-img-cropper/package.json
  14. 75 0
      components/gmy-img-cropper/readme.md
  15. 360 0
      components/html2canvas.js
  16. 19 0
      components/html2canvas.min.js
  17. 150 0
      components/ithui-bullet/ithui-bullet-one.vue
  18. 200 0
      components/ithui-bullet/ithui-bullet-two.vue
  19. 1 0
      components/jquery-1.8.3.min.js
  20. 104 0
      components/pickerAddress.vue
  21. 278 0
      components/qrcode-poster/qrcode-poster.vue
  22. 1201 0
      components/tki-qrcode/qrcode.js
  23. 210 0
      components/tki-qrcode/tki-qrcode.vue
  24. 402 0
      components/uni-collapse-item/uni-collapse-item.vue
  25. 147 0
      components/uni-collapse/uni-collapse.vue
  26. 6 0
      components/uni-countdown/i18n/en.json
  27. 8 0
      components/uni-countdown/i18n/index.js
  28. 6 0
      components/uni-countdown/i18n/zh-Hans.json
  29. 6 0
      components/uni-countdown/i18n/zh-Hant.json
  30. 271 0
      components/uni-countdown/uni-countdown.vue
  31. 526 0
      components/uni-data-select/uni-data-select.vue
  32. 194 0
      components/uni-load-more/uni-load-more.vue
  33. 202 0
      components/uni-number-box.vue
  34. 263 0
      components/uni-popup/uni-popup.vue
  35. 269 0
      components/uni-steps/uni-steps.vue
  36. 302 0
      components/uni-swipe-action-item/bindingx.js
  37. 12 0
      components/uni-swipe-action-item/isPC.js
  38. 195 0
      components/uni-swipe-action-item/mpalipay.js
  39. 260 0
      components/uni-swipe-action-item/mpother.js
  40. 84 0
      components/uni-swipe-action-item/mpwxs.js
  41. 270 0
      components/uni-swipe-action-item/render.js
  42. 347 0
      components/uni-swipe-action-item/uni-swipe-action-item.vue
  43. 341 0
      components/uni-swipe-action-item/wx.wxs
  44. 60 0
      components/uni-swipe-action/uni-swipe-action.vue
  45. 279 0
      components/uni-transition/uni-transition.vue
  46. 27 0
      config/config.js
  47. 185 0
      main.js
  48. 93 0
      manifest.json
  49. 376 0
      pages.json
  50. BIN=BIN
      pages/.DS_Store
  51. 299 0
      pages/activity/add-coupon-goods.vue
  52. 365 0
      pages/activity/add-goods.vue
  53. 668 0
      pages/activity/advertising.vue
  54. 767 0
      pages/activity/coupon-item-edit.vue
  55. 452 0
      pages/activity/coupon-item-info.vue
  56. 162 0
      pages/activity/coupon-item.vue
  57. 207 0
      pages/activity/free-delivery.vue
  58. 210 0
      pages/activity/full-discounts.vue
  59. 94 0
      pages/activity/marketing.vue
  60. 203 0
      pages/activity/new-customer.vue
  61. 529 0
      pages/activity/overlord-meal-detail.vue
  62. 236 0
      pages/activity/overlord-meal.vue
  63. 406 0
      pages/activity/special-item-edit.vue
  64. 351 0
      pages/activity/special-item-info.vue
  65. 165 0
      pages/activity/special-item.vue
  66. 325 0
      pages/activity/update-full-discounts.vue
  67. 590 0
      pages/components/order.vue
  68. 62 0
      pages/components/p-checkbox.vue
  69. 175 0
      pages/components/popup-picker-view.vue
  70. 415 0
      pages/evaluate/index.vue
  71. 811 0
      pages/goods/goods-edit.vue
  72. 314 0
      pages/goods/goods-type.vue
  73. 530 0
      pages/goods/goods.vue
  74. 857 0
      pages/index/index.vue
  75. 271 0
      pages/inspect/detail.vue
  76. 239 0
      pages/inspect/list.vue
  77. 392 0
      pages/login/login.vue
  78. 523 0
      pages/mine/index.vue
  79. 277 0
      pages/order/index.vue
  80. 478 0
      pages/rectification/detail.vue
  81. 256 0
      pages/rectification/list.vue
  82. 144 0
      pages/search/collect.vue
  83. 133 0
      pages/search/index.vue
  84. 356 0
      pages/store/clerk.vue
  85. 150 0
      pages/store/codeinfo.vue
  86. 303 0
      pages/store/deliveryman.vue
  87. 278 0
      pages/store/distribution.vue
  88. 98 0
      pages/store/extensionCode.vue
  89. 88 0
      pages/store/index.vue
  90. 69 0
      pages/tools/bind.vue
  91. 17 0
      pages/tools/evaluation.vue
  92. 546 0
      pages/tools/hexiao_coupon.vue
  93. 464 0
      pages/tools/print.vue
  94. 285 0
      pages/tools/privacyAngument.vue
  95. 367 0
      pages/tools/safety-archives.vue
  96. 295 0
      pages/tools/setting.vue
  97. 227 0
      pages/tools/settle-info.vue
  98. 168 0
      pages/tools/settle-order.vue
  99. 173 0
      pages/tools/settle.vue
  100. 109 0
      pages/tools/sn.vue

BIN=BIN
.DS_Store


+ 1 - 0
.gitignore

@@ -0,0 +1 @@
+/unpackage

+ 16 - 0
.hbuilderx/launch.json

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

+ 448 - 0
App.vue

@@ -0,0 +1,448 @@
+<script>
+	/**
+	 * vuex管理登陆状态,具体可以参考官方登陆模板示例
+	 */
+	import {
+		mapMutations
+	} from 'vuex';
+	export default {
+		globalData: {
+			// 外网测试地址
+			//apiUrl: 'https://dd.meetingfuture.top',// api请求地址
+			//tuiguangmaUrl: 'https://dd.meetingfuture.top/storeCode/',
+            // 生产地址
+            apiUrl: 'http://127.0.0.1:8081',// 移动端api请求地址(client-app)
+            platApiUrl: 'http://127.0.0.1:8080',// 管理平台api请求地址(client-plat)
+            tuiguangmaUrl: 'http://127.0.0.1:8081/storeCode/',
+			// 本地地址
+			// apiUrl: 'http://127.0.0.1:8081',// 移动端api请求地址(client-app)
+            
+            storeInfo: null,
+			version: 'v1',
+		},
+		methods: {
+			...mapMutations(['login']),
+			getStoreInfo() {
+				//更新登陆状态
+				return uni.getStorageSync('storeInfo') || ''
+			},
+		},
+		
+		async onLaunch(options) {
+			let storeInfo = this.getStoreInfo();
+			if (storeInfo.storeId) {
+				this.login(storeInfo);
+				this.globalData.storeInfo = storeInfo
+			} else {
+				uni.redirectTo({
+					url: '/pages/login/login'
+				})
+			}
+		},
+		onLoad() {
+		},
+		onShow: function() {
+		},
+		onHide: function() {
+		},
+	}
+</script>
+
+<style lang='scss'>
+	@import "@/uni_modules/uview-ui/index.scss";
+
+	/*
+		全局公共样式和字体图标
+	*/
+	
+	@font-face {
+		font-family: iconfont;
+		font-weight: normal;
+		font-style: normal;
+		src: url('@/static/iconfont.ttf') format('truetype');
+	}
+
+	.iconfont {
+		font-family: "iconfont" !important;
+		font-size: 16px;
+		font-style: normal;
+		-webkit-font-smoothing: antialiased;
+		-moz-osx-font-smoothing: grayscale;
+	}
+	
+	.icon-fenlei:before {
+	  content: "\e653";
+	}
+	
+	.icon-23gaojingguize:before {
+	  content: "\e6a6";
+	}
+	
+	.icon-gengxin:before {
+	  content: "\e7cb";
+	}
+	
+	.icon-kefu:before {
+	  content: "\e625";
+	}
+	
+	.icon-yinxingqia3:before {
+	  content: "\e657";
+	}
+	
+	.icon-weibiaoti--:before {
+	  content: "\e60f";
+	}
+	
+	.icon-suoxiao:before {
+	  content: "\e7de";
+	}
+	
+	.icon-xuanzhuan:before {
+	  content: "\e630";
+	}
+	
+	.icon-add:before {
+	  content: "\e600";
+	}
+	
+	.icon-fangda2:before {
+	  content: "\e63f";
+	}
+	
+	.icon-c_icon3:before {
+	  content: "\e603";
+	}
+	
+	.icon-fensi:before {
+	  content: "\e74e";
+	}
+	
+	.icon-guanzhu:before {
+	  content: "\e610";
+	}
+	
+	.icon-shezhi:before {
+	  content: "\e601";
+	}
+	
+	.icon-daifahuo:before {
+	  content: "\e61a";
+	}
+	
+	.icon-daipingjia1:before {
+	  content: "\e635";
+	}
+	
+	.icon-tuikuan:before {
+	  content: "\e608";
+	}
+	
+	.icon-daishouhuo:before {
+	  content: "\e661";
+	}
+	
+	.icon-daifukuan2:before {
+	  content: "\e82f";
+	}
+	
+	.icon-jia:before {
+	  content: "\e60b";
+	}
+	
+	.icon-jian:before {
+	  content: "\e647";
+	}
+	
+	.icon-liangdu:before {
+	  content: "\e71b";
+	}
+	
+	.icon-liangdu1:before {
+	  content: "\e618";
+	}
+	
+	.icon-shouhuodizhi:before {
+	  content: "\e64e";
+	}
+	
+	.icon-weixinzhifu:before {
+	  content: "\e606";
+	}
+	
+	.icon-you:before {
+	  content: "\e642";
+	}
+	
+	.icon-xuanze:before {
+	  content: "\e640";
+	}
+	
+	.icon-xuanze1:before {
+	  content: "\e621";
+	}
+	
+	.icon-2xuanzhong:before {
+	  content: "\e605";
+	}
+	
+	.icon-pinpai:before {
+	  content: "\e602";
+	}
+	
+	.icon-shouji:before {
+	  content: "\e6a1";
+	}
+	
+	.icon-dizhi:before {
+	  content: "\e769";
+	}
+	
+	.icon-huati:before {
+	  content: "\e8c4";
+	}
+	
+	.icon-yue:before {
+	  content: "\e69d";
+	}
+	
+	.icon-tuijianren:before {
+	  content: "\e641";
+	}
+
+
+
+
+
+	view,
+	scroll-view,
+	swiper,
+	swiper-item,
+	cover-view,
+	cover-image,
+	icon,
+	text,
+	rich-text,
+	progress,
+	button,
+	checkbox,
+	form,
+	input,
+	label,
+	radio,
+	slider,
+	switch,
+	textarea,
+	navigator,
+	audio,
+	camera,
+	image,
+	video {
+		box-sizing: border-box;
+	}
+
+	/* 骨架屏替代方案 */
+	.Skeleton {
+		background: #f3f3f3;
+		padding: 20upx 0;
+		border-radius: 8upx;
+	}
+
+	/* 图片载入替代方案 */
+	.image-wrapper {
+		font-size: 0;
+		background: #f3f3f3;
+		border-radius: 4px;
+	}
+
+	.clamp {
+		overflow: hidden;
+		text-overflow: ellipsis;
+		white-space: nowrap;
+		display: block;
+	}
+
+	.common-hover {
+		background: #f5f5f5;
+	}
+
+	/*边框*/
+	.b-b:after,
+	.b-t:after {
+		position: absolute;
+		z-index: 3;
+		left: 0;
+		right: 0;
+		height: 0;
+		content: '';
+		transform: scaleY(.5);
+		border-bottom: 1px solid $border-color-base;
+	}
+
+	.b-b:after {
+		bottom: 0;
+	}
+
+	.b-t:after {
+		top: 0;
+	}
+
+	/* button样式改写 */
+	uni-button,
+	button {
+		font-size: $font-lg + 2upx;
+		font-weight: normal;
+		background: #fff;
+
+		&.no-border:before,
+		&.no-border:after {
+			border: none;
+		}
+	}
+
+	button::after {
+		border: none;
+	}
+
+	uni-button[type=default],
+	button[type=default] {
+		color: $font-color-dark;
+	}
+
+	/* 立即购买样式 */
+	.buyButton {
+		position: absolute;
+		right: 20upx;
+		bottom: 20upx;
+		height: 40upx;
+		line-height: 40upx;
+		font-size: 28upx;
+		background: $uni-color-button;
+		color: #fff;
+		padding: 4upx 10upx;
+		height: auto;
+		border: none;
+	}
+
+	/* 返利样式 */
+	.rebate {
+		color: $uni-color-word;
+		font-size: 30rpx;
+		white-space: nowrap;
+		padding: 0 4rpx;
+		margin: 0 20upx;
+		border-radius: 10rpx;
+	}
+
+	/* 已售样式 */
+	.sold {
+		color: gray;
+		font-size: 20rpx;
+		white-space: nowrap;
+		padding: 0 4rpx;
+		border-radius: 10rpx;
+	}
+
+	/* 优惠样式 */
+	.discount {
+		padding: 4upx 10upx;
+		border: 1px solid #d23540;
+		color: #d23540;
+		border-radius: 10upx;
+		font-size: 24upx;
+		display: inline;
+		float: left;
+		display: inline-block !important;
+	}
+
+	/* input 样式 */
+	.input-placeholder {
+		color: #999999;
+	}
+
+	.placeholder {
+		color: #999999;
+	}
+
+	/* 居中 */
+	.center {
+		display: flex;
+		justify-content: center;
+		align-items: center;
+	}
+
+	/* 垂直居中 */
+	.column {
+		display: flex;
+		flex-direction: column;
+		align-items: center;
+	}
+
+	.between {
+		display: flex;
+		justify-content: space-between;
+		align-items: center;
+	}
+
+	.onelines {
+		overflow: hidden;
+		white-space: nowrap;
+		text-overflow: ellipsis;
+	}
+
+	/* 最多显示两行超出显示点点点 */
+	.twolines {
+		text-overflow: -o-ellipsis-lastline;
+		overflow: hidden;
+		text-overflow: ellipsis;
+		display: -webkit-box;
+		-webkit-line-clamp: 2;
+		-webkit-box-orient: vertical;
+	}
+
+	.activityLogo {
+		position: absolute;
+		top: 0;
+		right: 0;
+		width: 100upx;
+		height: auto;
+	}
+
+	.sellLogo {
+		position: absolute;
+		top: 0;
+		left: 0;
+		width: 100upx;
+		height: auto;
+	}
+
+	/* ios底部安全距离-padding */
+	.savepadding {
+		padding-bottom: constant(safe-area-inset-bottom);
+		padding-bottom: env(safe-area-inset-bottom);
+		box-sizing: content-box;
+	}
+
+	/* ios底部安全距离-bottom */
+	.savebottom {
+		bottom: constant(safe-area-inset-bottom);
+		bottom: env(safe-area-inset-bottom);
+	}
+
+	.column-cen {
+		display: flex;
+		justify-content: center;
+		flex-direction: column;
+		align-items: center;
+	}
+	.zanwu {
+		padding-top: 300upx;
+		color: #cdcdcd;
+		
+		image{
+			margin-bottom: 40upx;
+		}
+	}
+	
+	
+</style>

+ 4914 - 0
common/data.js

@@ -0,0 +1,4914 @@
+export default [
+  {
+    "name": "北京市",
+    "city": [
+      {
+        "name": "北京市",
+        "area": [
+          "东城区",
+          "西城区",
+          "崇文区",
+          "宣武区",
+          "朝阳区",
+          "丰台区",
+          "石景山区",
+          "海淀区",
+          "门头沟区",
+          "房山区",
+          "通州区",
+          "顺义区",
+          "昌平区",
+          "大兴区",
+          "平谷区",
+          "怀柔区",
+          "密云县",
+          "延庆县"
+        ]
+      }
+    ]
+  },
+  {
+    "name": "天津市",
+    "city": [
+      {
+        "name": "天津市",
+        "area": [
+          "和平区",
+          "河东区",
+          "河西区",
+          "南开区",
+          "河北区",
+          "红桥区",
+          "塘沽区",
+          "汉沽区",
+          "大港区",
+          "东丽区",
+          "西青区",
+          "津南区",
+          "北辰区",
+          "武清区",
+          "宝坻区",
+          "宁河县",
+          "静海县",
+          "蓟  县"
+        ]
+      }
+    ]
+  },
+  {
+    "name": "河北省",
+    "city": [
+      {
+        "name": "石家庄市",
+        "area": [
+          "长安区",
+          "桥东区",
+          "桥西区",
+          "新华区",
+          "郊  区",
+          "井陉矿区",
+          "井陉县",
+          "正定县",
+          "栾城县",
+          "行唐县",
+          "灵寿县",
+          "高邑县",
+          "深泽县",
+          "赞皇县",
+          "无极县",
+          "平山县",
+          "元氏县",
+          "赵  县",
+          "辛集市",
+          "藁",
+          "晋州市",
+          "新乐市",
+          "鹿泉市"
+        ]
+      },
+      {
+        "name": "唐山市",
+        "area": [
+          "路南区",
+          "路北区",
+          "古冶区",
+          "开平区",
+          "新  区",
+          "丰润县",
+          "滦  县",
+          "滦南县",
+          "乐亭县",
+          "迁西县",
+          "玉田县",
+          "唐海县",
+          "遵化市",
+          "丰南市",
+          "迁安市"
+        ]
+      },
+      {
+        "name": "秦皇岛市",
+        "area": [
+          "海港区",
+          "山海关区",
+          "北戴河区",
+          "青龙满族自治县",
+          "昌黎县",
+          "抚宁县",
+          "卢龙县"
+        ]
+      },
+      {
+        "name": "邯郸市",
+        "area": [
+          "邯山区",
+          "丛台区",
+          "复兴区",
+          "峰峰矿区",
+          "邯郸县",
+          "临漳县",
+          "成安县",
+          "大名县",
+          "涉  县",
+          "磁  县",
+          "肥乡县",
+          "永年县",
+          "邱  县",
+          "鸡泽县",
+          "广平县",
+          "馆陶县",
+          "魏  县",
+          "曲周县",
+          "武安市"
+        ]
+      },
+      {
+        "name": "邢台市",
+        "area": [
+          "桥东区",
+          "桥西区",
+          "邢台县",
+          "临城县",
+          "内丘县",
+          "柏乡县",
+          "隆尧县",
+          "任  县",
+          "南和县",
+          "宁晋县",
+          "巨鹿县",
+          "新河县",
+          "广宗县",
+          "平乡县",
+          "威  县",
+          "清河县",
+          "临西县",
+          "南宫市",
+          "沙河市"
+        ]
+      },
+      {
+        "name": "保定市",
+        "area": [
+          "新市区",
+          "北市区",
+          "南市区",
+          "满城县",
+          "清苑县",
+          "涞水县",
+          "阜平县",
+          "徐水县",
+          "定兴县",
+          "唐  县",
+          "高阳县",
+          "容城县",
+          "涞源县",
+          "望都县",
+          "安新县",
+          "易  县",
+          "曲阳县",
+          "蠡  县",
+          "顺平县",
+          "博野",
+          "雄县",
+          "涿州市",
+          "定州市",
+          "安国市",
+          "高碑店市"
+        ]
+      },
+      {
+        "name": "张家口",
+        "area": [
+          "桥东区",
+          "桥西区",
+          "宣化区",
+          "下花园区",
+          "宣化县",
+          "张北县",
+          "康保县",
+          "沽源县",
+          "尚义县",
+          "蔚  县",
+          "阳原县",
+          "怀安县",
+          "万全县",
+          "怀来县",
+          "涿鹿县",
+          "赤城县",
+          "崇礼县"
+        ]
+      },
+      {
+        "name": "承德市",
+        "area": [
+          "双桥区",
+          "双滦区",
+          "鹰手营子矿区",
+          "承德县",
+          "兴隆县",
+          "平泉县",
+          "滦平县",
+          "隆化县",
+          "丰宁满族自治县",
+          "宽城满族自治县",
+          "围场满族蒙古族自治县"
+        ]
+      },
+      {
+        "name": "沧州市",
+        "area": [
+          "新华区",
+          "运河区",
+          "沧  县",
+          "青  县",
+          "东光县",
+          "海兴县",
+          "盐山县",
+          "肃宁县",
+          "南皮县",
+          "吴桥县",
+          "献  县",
+          "孟村回族自治县",
+          "泊头市",
+          "任丘市",
+          "黄骅市",
+          "河间市"
+        ]
+      },
+      {
+        "name": "廊坊市",
+        "area": [
+          "安次区",
+          "固安县",
+          "永清县",
+          "香河县",
+          "大城县",
+          "文安县",
+          "大厂回族自治县",
+          "霸州市",
+          "三河市"
+        ]
+      },
+      {
+        "name": "衡水市",
+        "area": [
+          "桃城区",
+          "枣强县",
+          "武邑县",
+          "武强县",
+          "饶阳县",
+          "安平县",
+          "故城县",
+          "景  县",
+          "阜城县",
+          "冀州市",
+          "深州市"
+        ]
+      }
+    ]
+  },
+  {
+    "name": "山西省",
+    "city": [
+      {
+        "name": "太原市",
+        "area": [
+          "小店区",
+          "迎泽区",
+          "杏花岭区",
+          "尖草坪区",
+          "万柏林区",
+          "晋源区",
+          "清徐县",
+          "阳曲县",
+          "娄烦县",
+          "古交市"
+        ]
+      },
+      {
+        "name": "大同市",
+        "area": [
+          "城  区",
+          "矿  区",
+          "南郊区",
+          "新荣区",
+          "阳高县",
+          "天镇县",
+          "广灵县",
+          "灵丘县",
+          "浑源县",
+          "左云县",
+          "大同县"
+        ]
+      },
+      {
+        "name": "阳泉市",
+        "area": [
+          "城  区",
+          "矿  区",
+          "郊  区",
+          "平定县",
+          "盂  县"
+        ]
+      },
+      {
+        "name": "长治市",
+        "area": [
+          "城  区",
+          "郊  区",
+          "长治县",
+          "襄垣县",
+          "屯留县",
+          "平顺县",
+          "黎城县",
+          "壶关县",
+          "长子县",
+          "武乡县",
+          "沁  县",
+          "沁源县",
+          "潞城市"
+        ]
+      },
+      {
+        "name": "晋城市",
+        "area": [
+          "城  区",
+          "沁水县",
+          "阳城县",
+          "陵川县",
+          "泽州县",
+          "高平市"
+        ]
+      },
+      {
+        "name": "朔州市",
+        "area": [
+          "朔城区",
+          "平鲁区",
+          "山阴县",
+          "应  县",
+          "右玉县",
+          "怀仁县"
+        ]
+      },
+      {
+        "name": "忻州市",
+        "area": [
+          "忻府区",
+          "原平市",
+          "定襄县",
+          "五台县",
+          "代  县",
+          "繁峙县",
+          "宁武县",
+          "静乐县",
+          "神池县",
+          "五寨县",
+          "岢岚县",
+          "河曲县",
+          "保德县",
+          "偏关县"
+        ]
+      },
+      {
+        "name": "吕梁市",
+        "area": [
+          "离石区",
+          "孝义市",
+          "汾阳市",
+          "文水县",
+          "交城县",
+          "兴  县",
+          "临  县",
+          "柳林县",
+          "石楼县",
+          "岚  县",
+          "方山县",
+          "中阳县",
+          "交口县"
+        ]
+      },
+      {
+        "name": "晋中市",
+        "area": [
+          "榆次市",
+          "介休市",
+          "榆社县",
+          "左权县",
+          "和顺县",
+          "昔阳县",
+          "寿阳县",
+          "太谷县",
+          "祁  县",
+          "平遥县",
+          "灵石县"
+        ]
+      },
+      {
+        "name": "临汾市",
+        "area": [
+          "临汾市",
+          "侯马市",
+          "霍州市",
+          "曲沃县",
+          "翼城县",
+          "襄汾县",
+          "洪洞县",
+          "古  县",
+          "安泽县",
+          "浮山县",
+          "吉  县",
+          "乡宁县",
+          "蒲  县",
+          "大宁县",
+          "永和县",
+          "隰  县",
+          "汾西县"
+        ]
+      },
+      {
+        "name": "运城市",
+        "area": [
+          "运城市",
+          "永济市",
+          "河津市",
+          "芮城县",
+          "临猗县",
+          "万荣县",
+          "新绛县",
+          "稷山县",
+          "闻喜县",
+          "夏  县",
+          "绛  县",
+          "平陆县",
+          "垣曲县"
+        ]
+      }
+    ]
+  },
+  {
+    "name": "内蒙古",
+    "city": [
+      {
+        "name": "呼和浩特市",
+        "area": [
+          "新城区",
+          "回民区",
+          "玉泉区",
+          "郊  区",
+          "土默特左旗",
+          "托克托县",
+          "和林格尔县",
+          "清水河县",
+          "武川县"
+        ]
+      },
+      {
+        "name": "包头市",
+        "area": [
+          "东河区",
+          "昆都伦区",
+          "青山区",
+          "石拐矿区",
+          "白云矿区",
+          "郊  区",
+          "土默特右旗",
+          "固阳县",
+          "达尔罕茂明安联合旗"
+        ]
+      },
+      {
+        "name": "乌海市",
+        "area": [
+          "海勃湾区",
+          "海南区",
+          "乌达区"
+        ]
+      },
+      {
+        "name": "赤峰市",
+        "area": [
+          "红山区",
+          "元宝山区",
+          "松山区",
+          "阿鲁科尔沁旗",
+          "巴林左旗",
+          "巴林右旗",
+          "林西县",
+          "克什克腾旗",
+          "翁牛特旗",
+          "喀喇沁旗",
+          "宁城县",
+          "敖汉旗"
+        ]
+      },
+      {
+        "name": "呼伦贝尔市",
+        "area": [
+          "海拉尔市",
+          "满洲里市",
+          "扎兰屯市",
+          "牙克石市",
+          "根河市",
+          "额尔古纳市",
+          "阿荣旗",
+          "莫力达瓦达斡尔族自治旗",
+          "鄂伦春自治旗",
+          "鄂温克族自治旗",
+          "新巴尔虎右旗",
+          "新巴尔虎左旗",
+          "陈巴尔虎旗"
+        ]
+      },
+      {
+        "name": "兴安盟",
+        "area": [
+          "乌兰浩特市",
+          "阿尔山市",
+          "科尔沁右翼前旗",
+          "科尔沁右翼中旗",
+          "扎赉特旗",
+          "突泉县"
+        ]
+      },
+      {
+        "name": "通辽市",
+        "area": [
+          "科尔沁区",
+          "霍林郭勒市",
+          "科尔沁左翼中旗",
+          "科尔沁左翼后旗",
+          "开鲁县",
+          "库伦旗",
+          "奈曼旗",
+          "扎鲁特旗"
+        ]
+      },
+      {
+        "name": "锡林郭勒盟",
+        "area": [
+          "二连浩特市",
+          "锡林浩特市",
+          "阿巴嘎旗",
+          "苏尼特左旗",
+          "苏尼特右旗",
+          "东乌珠穆沁旗",
+          "西乌珠穆沁旗",
+          "太仆寺旗",
+          "镶黄旗",
+          "正镶白旗",
+          "正蓝旗",
+          "多伦县"
+        ]
+      },
+      {
+        "name": "乌兰察布盟",
+        "area": [
+          "集宁市",
+          "丰镇市",
+          "卓资县",
+          "化德县",
+          "商都县",
+          "兴和县",
+          "凉城县",
+          "察哈尔右翼前旗",
+          "察哈尔右翼中旗",
+          "察哈尔右翼后旗",
+          "四子王旗"
+        ]
+      },
+      {
+        "name": "伊克昭盟",
+        "area": [
+          "东胜市",
+          "达拉特旗",
+          "准格尔旗",
+          "鄂托克前旗",
+          "鄂托克旗",
+          "杭锦旗",
+          "乌审旗",
+          "伊金霍洛旗"
+        ]
+      },
+      {
+        "name": "巴彦淖尔盟",
+        "area": [
+          "临河市",
+          "五原县",
+          "磴口县",
+          "乌拉特前旗",
+          "乌拉特中旗",
+          "乌拉特后旗",
+          "杭锦后旗"
+        ]
+      },
+      {
+        "name": "阿拉善盟",
+        "area": [
+          "阿拉善左旗",
+          "阿拉善右旗",
+          "额济纳旗"
+        ]
+      }
+    ]
+  },
+  {
+    "name": "辽宁省",
+    "city": [
+      {
+        "name": "沈阳市",
+        "area": [
+          "沈河区",
+          "皇姑区",
+          "和平区",
+          "大东区",
+          "铁西区",
+          "苏家屯区",
+          "东陵区",
+          "于洪区",
+          "新民市",
+          "法库县",
+          "辽中县",
+          "康平县",
+          "新城子区"
+        ]
+      },
+      {
+        "name": "大连市",
+        "area": [
+          "西岗区",
+          "中山区",
+          "沙河口区",
+          "甘井子区",
+          "旅顺口区",
+          "金州区",
+          "瓦房店市",
+          "普兰店市",
+          "庄河市",
+          "长海县"
+        ]
+      },
+      {
+        "name": "鞍山市",
+        "area": [
+          "铁东区",
+          "铁西区",
+          "立山区",
+          "千山区",
+          "海城市",
+          "台安县",
+          "岫岩满族自治县"
+        ]
+      },
+      {
+        "name": "抚顺市",
+        "area": [
+          "顺城区",
+          "新抚区",
+          "东洲区",
+          "望花区",
+          "抚顺县",
+          "清原满族自治县",
+          "新宾满族自治县"
+        ]
+      },
+      {
+        "name": "本溪市",
+        "area": [
+          "平山区",
+          "明山区",
+          "溪湖区",
+          "南芬区",
+          "本溪满族自治县",
+          "桓仁满族自治县"
+        ]
+      },
+      {
+        "name": "丹东市",
+        "area": [
+          "振兴区",
+          "元宝区",
+          "振安区",
+          "东港市",
+          "凤城市",
+          "宽甸满族自治县"
+        ]
+      },
+      {
+        "name": "锦州市",
+        "area": [
+          "太和区",
+          "古塔区",
+          "凌河区",
+          "凌海市",
+          "黑山县",
+          "义县",
+          "北宁市"
+        ]
+      },
+      {
+        "name": "营口市",
+        "area": [
+          "站前区",
+          "西市区",
+          "鲅鱼圈区",
+          "老边区",
+          "大石桥市",
+          "盖州市"
+        ]
+      },
+      {
+        "name": "阜新市",
+        "area": [
+          "海州区",
+          "新邱区",
+          "太平区",
+          "清河门区",
+          "细河区",
+          "彰武县",
+          "阜新蒙古族自治县"
+        ]
+      },
+      {
+        "name": "辽阳市",
+        "area": [
+          "白塔区",
+          "文圣区",
+          "宏伟区",
+          "太子河区",
+          "弓长岭区",
+          "灯塔市",
+          "辽阳县"
+        ]
+      },
+      {
+        "name": "盘锦",
+        "area": [
+          "双台子区",
+          "兴隆台区",
+          "盘山县",
+          "大洼县"
+        ]
+      },
+      {
+        "name": "铁岭市",
+        "area": [
+          "银州区",
+          "清河区",
+          "调兵山市",
+          "开原市",
+          "铁岭县",
+          "昌图县",
+          "西丰县"
+        ]
+      },
+      {
+        "name": "朝阳市",
+        "area": [
+          "双塔区",
+          "龙城区",
+          "凌源市",
+          "北票市",
+          "朝阳县",
+          "建平县",
+          "喀喇沁左翼蒙古族自治县"
+        ]
+      },
+      {
+        "name": "葫芦岛市",
+        "area": [
+          "龙港区",
+          "南票区",
+          "连山区",
+          "兴城市",
+          "绥中县",
+          "建昌县"
+        ]
+      }
+    ]
+  },
+  {
+    "name": "吉林省",
+    "city": [
+      {
+        "name": "长春市",
+        "area": [
+          "朝阳区",
+          "宽城区",
+          "二道区",
+          "南关区",
+          "绿园区",
+          "双阳区",
+          "九台市",
+          "榆树市",
+          "德惠市",
+          "农安县"
+        ]
+      },
+      {
+        "name": "吉林市",
+        "area": [
+          "船营区",
+          "昌邑区",
+          "龙潭区",
+          "丰满区",
+          "舒兰市",
+          "桦甸市",
+          "蛟河市",
+          "磐石市",
+          "永吉县"
+        ]
+      },
+      {
+        "name": "四平",
+        "area": [
+          "铁西区",
+          "铁东区",
+          "公主岭市",
+          "双辽市",
+          "梨树县",
+          "伊通满族自治县"
+        ]
+      },
+      {
+        "name": "辽源市",
+        "area": [
+          "龙山区",
+          "西安区",
+          "东辽县",
+          "东丰县"
+        ]
+      },
+      {
+        "name": "通化市",
+        "area": [
+          "东昌区",
+          "二道江区",
+          "梅河口市",
+          "集安市",
+          "通化县",
+          "辉南县",
+          "柳河县"
+        ]
+      },
+      {
+        "name": "白山市",
+        "area": [
+          "八道江区",
+          "江源区",
+          "临江市",
+          "靖宇县",
+          "抚松县",
+          "长白朝鲜族自治县"
+        ]
+      },
+      {
+        "name": "松原市",
+        "area": [
+          "宁江区",
+          "乾安县",
+          "长岭县",
+          "扶余县",
+          "前郭尔罗斯蒙古族自治县"
+        ]
+      },
+      {
+        "name": "白城市",
+        "area": [
+          "洮北区",
+          "大安市",
+          "洮南市",
+          "镇赉县",
+          "通榆县"
+        ]
+      },
+      {
+        "name": "延边朝鲜族自治州",
+        "area": [
+          "延吉市",
+          "图们市",
+          "敦化市",
+          "龙井市",
+          "珲春市",
+          "和龙市",
+          "安图县",
+          "汪清县"
+        ]
+      }
+    ]
+  },
+  {
+    "name": "黑龙江省",
+    "city": [
+      {
+        "name": "哈尔滨市",
+        "area": [
+          "松北区",
+          "道里区",
+          "南岗区",
+          "平房区",
+          "香坊区",
+          "道外区",
+          "呼兰区",
+          "阿城区",
+          "双城市",
+          "尚志市",
+          "五常市",
+          "宾县",
+          "方正县",
+          "通河县",
+          "巴彦县",
+          "延寿县",
+          "木兰县",
+          "依兰县"
+        ]
+      },
+      {
+        "name": "齐齐哈尔市",
+        "area": [
+          "龙沙区",
+          "昂昂溪区",
+          "铁锋区",
+          "建华区",
+          "富拉尔基区",
+          "碾子山区",
+          "梅里斯达斡尔族区",
+          "讷河市",
+          "富裕县",
+          "拜泉县",
+          "甘南县",
+          "依安县",
+          "克山县",
+          "泰来县",
+          "克东县",
+          "龙江县"
+        ]
+      },
+      {
+        "name": "鹤岗市",
+        "area": [
+          "兴山区",
+          "工农区",
+          "南山区",
+          "兴安区",
+          "向阳区",
+          "东山区",
+          "萝北县",
+          "绥滨县"
+        ]
+      },
+      {
+        "name": "双鸭山",
+        "area": [
+          "尖山区",
+          "岭东区",
+          "四方台区",
+          "宝山区",
+          "集贤县",
+          "宝清县",
+          "友谊县",
+          "饶河县"
+        ]
+      },
+      {
+        "name": "鸡西市",
+        "area": [
+          "鸡冠区",
+          "恒山区",
+          "城子河区",
+          "滴道区",
+          "梨树区",
+          "麻山区",
+          "密山市",
+          "虎林市",
+          "鸡东县"
+        ]
+      },
+      {
+        "name": "大庆市",
+        "area": [
+          "萨尔图区",
+          "红岗区",
+          "龙凤区",
+          "让胡路区",
+          "大同区",
+          "林甸县",
+          "肇州县",
+          "肇源县",
+          "杜尔伯特蒙古族自治县"
+        ]
+      },
+      {
+        "name": "伊春市",
+        "area": [
+          "伊春区",
+          "带岭区",
+          "南岔区",
+          "金山屯区",
+          "西林区",
+          "美溪区",
+          "乌马河区",
+          "翠峦区",
+          "友好区",
+          "上甘岭区",
+          "五营区",
+          "红星区",
+          "新青区",
+          "汤旺河区",
+          "乌伊岭区",
+          "铁力市",
+          "嘉荫县"
+        ]
+      },
+      {
+        "name": "牡丹江市",
+        "area": [
+          "爱民区",
+          "东安区",
+          "阳明区",
+          "西安区",
+          "绥芬河市",
+          "宁安市",
+          "海林市",
+          "穆棱市",
+          "林口县",
+          "东宁县"
+        ]
+      },
+      {
+        "name": "佳木斯市",
+        "area": [
+          "向阳区",
+          "前进区",
+          "东风区",
+          "郊区",
+          "同江市",
+          "富锦市",
+          "桦川县",
+          "抚远县",
+          "桦南县",
+          "汤原县"
+        ]
+      },
+      {
+        "name": "七台河市",
+        "area": [
+          "桃山区",
+          "新兴区",
+          "茄子河区",
+          "勃利县"
+        ]
+      },
+      {
+        "name": "黑河市",
+        "area": [
+          "爱辉区",
+          "北安市",
+          "五大连池市",
+          "逊克县",
+          "嫩江县",
+          "孙吴县"
+        ]
+      },
+      {
+        "name": "绥化市",
+        "area": [
+          "北林区",
+          "安达市",
+          "肇东市",
+          "海伦市",
+          "绥棱县",
+          "兰西县",
+          "明水县",
+          "青冈县",
+          "庆安县",
+          "望奎县"
+        ]
+      },
+      {
+        "name": "大兴安岭地区",
+        "area": [
+          "呼玛县",
+          "塔河县",
+          "漠河县",
+          "大兴安岭辖区"
+        ]
+      }
+    ]
+  },
+  {
+    "name": "上海市",
+    "city": [
+      {
+        "name": "上海市",
+        "area": [
+          "黄浦区",
+          "卢湾区",
+          "徐汇区",
+          "长宁区",
+          "静安区",
+          "普陀区",
+          "闸北区",
+          "虹口区",
+          "杨浦区",
+          "宝山区",
+          "闵行区",
+          "嘉定区",
+          "松江区",
+          "金山区",
+          "青浦区",
+          "南汇区",
+          "奉贤区",
+          "浦东新区",
+          "崇明县"
+        ]
+      }
+    ]
+  },
+  {
+    "name": "江苏省",
+    "city": [
+      {
+        "name": "南京市",
+        "area": [
+          "玄武区",
+          "白下区",
+          "秦淮区",
+          "建邺区",
+          "鼓楼区",
+          "下关区",
+          "栖霞区",
+          "雨花台区",
+          "浦口区",
+          "江宁区",
+          "六合区",
+          "溧水县",
+          "高淳县"
+        ]
+      },
+      {
+        "name": "苏州市",
+        "area": [
+          "金阊区",
+          "平江区",
+          "沧浪区",
+          "虎丘区",
+          "吴中区",
+          "相城区",
+          "常熟市",
+          "张家港市",
+          "昆山市",
+          "吴江市",
+          "太仓市"
+        ]
+      },
+      {
+        "name": "无锡市",
+        "area": [
+          "崇安区",
+          "南长区",
+          "北塘区",
+          "滨湖区",
+          "锡山区",
+          "惠山区",
+          "江阴市",
+          "宜兴市"
+        ]
+      },
+      {
+        "name": "常州市",
+        "area": [
+          "钟楼区",
+          "天宁区",
+          "戚墅堰区",
+          "新北区",
+          "武进区",
+          "金坛市",
+          "溧阳市"
+        ]
+      },
+      {
+        "name": "镇江市",
+        "area": [
+          "京口区",
+          "润州区",
+          "丹徒区",
+          "丹阳市",
+          "扬中市",
+          "句容市"
+        ]
+      },
+      {
+        "name": "南通市",
+        "area": [
+          "崇川区",
+          "港闸区",
+          "通州市",
+          "如皋市",
+          "海门市",
+          "启东市",
+          "海安县",
+          "如东县"
+        ]
+      },
+      {
+        "name": "泰州市",
+        "area": [
+          "海陵区",
+          "高港区",
+          "姜堰市",
+          "泰兴市",
+          "靖江市",
+          "兴化市"
+        ]
+      },
+      {
+        "name": "扬州市",
+        "area": [
+          "广陵区",
+          "维扬区",
+          "邗江区",
+          "江都市",
+          "仪征市",
+          "高邮市",
+          "宝应县"
+        ]
+      },
+      {
+        "name": "盐城市",
+        "area": [
+          "亭湖区",
+          "盐都区",
+          "大丰市",
+          "东台市",
+          "建湖县",
+          "射阳县",
+          "阜宁县",
+          "滨海县",
+          "响水县"
+        ]
+      },
+      {
+        "name": "连云港市",
+        "area": [
+          "新浦区",
+          "海州区",
+          "连云区",
+          "东海县",
+          "灌云县",
+          "赣榆县",
+          "灌南县"
+        ]
+      },
+      {
+        "name": "徐州市",
+        "area": [
+          "云龙区",
+          "鼓楼区",
+          "九里区",
+          "泉山区",
+          "贾汪区",
+          "邳州市",
+          "新沂市",
+          "铜山县",
+          "睢宁县",
+          "沛县",
+          "丰县"
+        ]
+      },
+      {
+        "name": "淮安市",
+        "area": [
+          "清河区",
+          "清浦区",
+          "楚州区",
+          "淮阴区",
+          "涟水县",
+          "洪泽县",
+          "金湖县",
+          "盱眙县"
+        ]
+      },
+      {
+        "name": "宿迁市",
+        "area": [
+          "宿城区",
+          "宿豫区",
+          "沭阳县",
+          "泗阳县",
+          "泗洪县"
+        ]
+      }
+    ]
+  },
+  {
+    "name": "浙江省",
+    "city": [
+      {
+        "name": "杭州市",
+        "area": [
+          "拱墅区",
+          "西湖区",
+          "上城区",
+          "下城区",
+          "江干区",
+          "滨江区",
+          "余杭区",
+          "萧山区",
+          "建德市",
+          "富阳市",
+          "临安市",
+          "桐庐县",
+          "淳安县"
+        ]
+      },
+      {
+        "name": "宁波市",
+        "area": [
+          "海曙区",
+          "江东区",
+          "江北区",
+          "镇海区",
+          "北仑区",
+          "鄞州区",
+          "余姚市",
+          "慈溪市",
+          "奉化市",
+          "宁海县",
+          "象山县"
+        ]
+      },
+      {
+        "name": "温州市",
+        "area": [
+          "鹿城区",
+          "龙湾区",
+          "瓯海区",
+          "瑞安市",
+          "乐清市",
+          "永嘉县",
+          "洞头县",
+          "平阳县",
+          "苍南县",
+          "文成县",
+          "泰顺县"
+        ]
+      },
+      {
+        "name": "嘉兴市",
+        "area": [
+          "秀城区",
+          "秀洲区",
+          "海宁市",
+          "平湖市",
+          "桐乡市",
+          "嘉善县",
+          "海盐县"
+        ]
+      },
+      {
+        "name": "湖州市",
+        "area": [
+          "吴兴区",
+          "南浔区",
+          "长兴县",
+          "德清县",
+          "安吉县"
+        ]
+      },
+      {
+        "name": "绍兴市",
+        "area": [
+          "越城区",
+          "诸暨市",
+          "上虞市",
+          "嵊州市",
+          "绍兴县",
+          "新昌县"
+        ]
+      },
+      {
+        "name": "金华市",
+        "area": [
+          "婺城区",
+          "金东区",
+          "兰溪市",
+          "义乌市",
+          "东阳市",
+          "永康市",
+          "武义县",
+          "浦江县",
+          "磐安县"
+        ]
+      },
+      {
+        "name": "衢州市",
+        "area": [
+          "柯城区",
+          "衢江区",
+          "江山市",
+          "龙游县",
+          "常山县",
+          "开化县"
+        ]
+      },
+      {
+        "name": "舟山市",
+        "area": [
+          "定海区",
+          "普陀区",
+          "岱山县",
+          "嵊泗县"
+        ]
+      },
+      {
+        "name": "台州市",
+        "area": [
+          "椒江区",
+          "黄岩区",
+          "路桥区",
+          "临海市",
+          "温岭市",
+          "玉环县",
+          "天台县",
+          "仙居县",
+          "三门县"
+        ]
+      },
+      {
+        "name": "丽水市",
+        "area": [
+          "莲都区",
+          "龙泉市",
+          "缙云县",
+          "青田县",
+          "云和县",
+          "遂昌县",
+          "松阳县",
+          "庆元县",
+          "景宁畲族自治县"
+        ]
+      }
+    ]
+  },
+  {
+    "name": "安徽省",
+    "city": [
+      {
+        "name": "合肥市",
+        "area": [
+          "庐阳区",
+          "瑶海区",
+          "蜀山区",
+          "包河区",
+          "长丰县",
+          "肥东县",
+          "肥西县"
+        ]
+      },
+      {
+        "name": "芜湖市",
+        "area": [
+          "镜湖区",
+          "弋江区",
+          "鸠江区",
+          "三山区",
+          "芜湖县",
+          "南陵县",
+          "繁昌县"
+        ]
+      },
+      {
+        "name": "蚌埠市",
+        "area": [
+          "蚌山区",
+          "龙子湖区",
+          "禹会区",
+          "淮上区",
+          "怀远县",
+          "固镇县",
+          "五河县"
+        ]
+      },
+      {
+        "name": "淮南市",
+        "area": [
+          "田家庵区",
+          "大通区",
+          "谢家集区",
+          "八公山区",
+          "潘集区",
+          "凤台县"
+        ]
+      },
+      {
+        "name": "马鞍山市",
+        "area": [
+          "雨山区",
+          "花山区",
+          "金家庄区",
+          "当涂县"
+        ]
+      },
+      {
+        "name": "淮北市",
+        "area": [
+          "相山区",
+          "杜集区",
+          "烈山区",
+          "濉溪县"
+        ]
+      },
+      {
+        "name": "铜陵市",
+        "area": [
+          "铜官山区",
+          "狮子山区",
+          "郊区",
+          "铜陵县"
+        ]
+      },
+      {
+        "name": "安庆市",
+        "area": [
+          "迎江区",
+          "大观区",
+          "宜秀区",
+          "桐城市",
+          "宿松县",
+          "枞阳县",
+          "太湖县",
+          "怀宁县",
+          "岳西县",
+          "望江县",
+          "潜山县"
+        ]
+      },
+      {
+        "name": "黄山市",
+        "area": [
+          "屯溪区",
+          "黄山区",
+          "徽州区",
+          "休宁县",
+          "歙县",
+          "祁门县",
+          "黟县"
+        ]
+      },
+      {
+        "name": "滁州市",
+        "area": [
+          "琅琊区",
+          "南谯区",
+          "天长市",
+          "明光市",
+          "全椒县",
+          "来安县",
+          "定远县",
+          "凤阳县"
+        ]
+      },
+      {
+        "name": "阜阳市",
+        "area": [
+          "颍州区",
+          "颍东区",
+          "颍泉区",
+          "界首市",
+          "临泉县",
+          "颍上县",
+          "阜南县",
+          "太和县"
+        ]
+      },
+      {
+        "name": "宿州市",
+        "area": [
+          "埇桥区",
+          "萧县",
+          "泗县",
+          "砀山县",
+          "灵璧县"
+        ]
+      },
+      {
+        "name": "巢湖市",
+        "area": [
+          "居巢区",
+          "含山县",
+          "无为县",
+          "庐江县",
+          "和县"
+        ]
+      },
+      {
+        "name": "六安市",
+        "area": [
+          "金安区",
+          "裕安区",
+          "寿县",
+          "霍山县",
+          "霍邱县",
+          "舒城县",
+          "金寨县"
+        ]
+      },
+      {
+        "name": "亳州市",
+        "area": [
+          "谯城区",
+          "利辛县",
+          "涡阳县",
+          "蒙城县"
+        ]
+      },
+      {
+        "name": "池州市",
+        "area": [
+          "贵池区",
+          "东至县",
+          "石台县",
+          "青阳县"
+        ]
+      },
+      {
+        "name": "宣城市",
+        "area": [
+          "宣州区",
+          "宁国市",
+          "广德县",
+          "郎溪县",
+          "泾县",
+          "旌德县",
+          "绩溪县"
+        ]
+      }
+    ]
+  },
+  {
+    "name": "福建省",
+    "city": [
+      {
+        "name": "福州市",
+        "area": [
+          "鼓楼区",
+          "台江区",
+          "仓山区",
+          "马尾区",
+          "晋安区",
+          "福清市",
+          "长乐市",
+          "闽侯县",
+          "闽清县",
+          "永泰县",
+          "连江县",
+          "罗源县",
+          "平潭县"
+        ]
+      },
+      {
+        "name": "厦门市",
+        "area": [
+          "思明区",
+          "海沧区",
+          "湖里区",
+          "集美区",
+          "同安区",
+          "翔安区"
+        ]
+      },
+      {
+        "name": "莆田市",
+        "area": [
+          "城厢区",
+          "涵江区",
+          "荔城区",
+          "秀屿区",
+          "仙游县"
+        ]
+      },
+      {
+        "name": "三明市",
+        "area": [
+          "梅列区",
+          "三元区",
+          "永安市",
+          "明溪县",
+          "将乐县",
+          "大田县",
+          "宁化县",
+          "建宁县",
+          "沙县",
+          "尤溪县",
+          "清流县",
+          "泰宁县"
+        ]
+      },
+      {
+        "name": "泉州市",
+        "area": [
+          "鲤城区",
+          "丰泽区",
+          "洛江区",
+          "泉港区",
+          "石狮市",
+          "晋江市",
+          "南安市",
+          "惠安县",
+          "永春县",
+          "安溪县",
+          "德化县",
+          "金门县"
+        ]
+      },
+      {
+        "name": "漳州市",
+        "area": [
+          "芗城区",
+          "龙文区",
+          "龙海市",
+          "平和县",
+          "南靖县",
+          "诏安县",
+          "漳浦县",
+          "华安县",
+          "东山县",
+          "长泰县",
+          "云霄县"
+        ]
+      },
+      {
+        "name": "南平市",
+        "area": [
+          "延平区",
+          "建瓯市",
+          "邵武市",
+          "武夷山市",
+          "建阳市",
+          "松溪县",
+          "光泽县",
+          "顺昌县",
+          "浦城县",
+          "政和县"
+        ]
+      },
+      {
+        "name": "龙岩市",
+        "area": [
+          "新罗区",
+          "漳平市",
+          "长汀县",
+          "武平县",
+          "上杭县",
+          "永定县",
+          "连城县"
+        ]
+      },
+      {
+        "name": "宁德市",
+        "area": [
+          "蕉城区",
+          "福安市",
+          "福鼎市",
+          "寿宁县",
+          "霞浦县",
+          "柘荣县",
+          "屏南县",
+          "古田县",
+          "周宁县"
+        ]
+      }
+    ]
+  },
+  {
+    "name": "江西省",
+    "city": [
+      {
+        "name": "南昌市",
+        "area": [
+          "东湖区",
+          "西湖区",
+          "青云谱区",
+          "湾里区",
+          "青山湖区",
+          "新建县",
+          "南昌县",
+          "进贤县",
+          "安义县"
+        ]
+      },
+      {
+        "name": "景德镇市",
+        "area": [
+          "珠山区",
+          "昌江区",
+          "乐平市",
+          "浮梁县"
+        ]
+      },
+      {
+        "name": "萍乡市",
+        "area": [
+          "安源区",
+          "湘东区",
+          "莲花县",
+          "上栗县",
+          "芦溪县"
+        ]
+      },
+      {
+        "name": "九江市",
+        "area": [
+          "浔阳区",
+          "庐山区",
+          "瑞昌市",
+          "九江县",
+          "星子县",
+          "武宁县",
+          "彭泽县",
+          "永修县",
+          "修水县",
+          "湖口县",
+          "德安县",
+          "都昌县"
+        ]
+      },
+      {
+        "name": "新余市",
+        "area": [
+          "渝水区",
+          "分宜县"
+        ]
+      },
+      {
+        "name": "鹰潭市",
+        "area": [
+          "月湖区",
+          "贵溪市",
+          "余江县"
+        ]
+      },
+      {
+        "name": "赣州市",
+        "area": [
+          "章贡区",
+          "瑞金市",
+          "南康市",
+          "石城县",
+          "安远县",
+          "赣县",
+          "宁都县",
+          "寻乌县",
+          "兴国县",
+          "定南县",
+          "上犹县",
+          "于都县",
+          "龙南县",
+          "崇义县",
+          "信丰县",
+          "全南县",
+          "大余县",
+          "会昌县"
+        ]
+      },
+      {
+        "name": "吉安市",
+        "area": [
+          "吉州区",
+          "青原区",
+          "井冈山市",
+          "吉安县",
+          "永丰县",
+          "永新县",
+          "新干县",
+          "泰和县",
+          "峡江县",
+          "遂川县",
+          "安福县",
+          "吉水县",
+          "万安县"
+        ]
+      },
+      {
+        "name": "宜春市",
+        "area": [
+          "袁州区",
+          "丰城市",
+          "樟树市",
+          "高安市",
+          "铜鼓县",
+          "靖安县",
+          "宜丰县",
+          "奉新县",
+          "万载县",
+          "上高县"
+        ]
+      },
+      {
+        "name": "抚州市",
+        "area": [
+          "临川区",
+          "南丰县",
+          "乐安县",
+          "金溪县",
+          "南城县",
+          "东乡县",
+          "资溪县",
+          "宜黄县",
+          "广昌县",
+          "黎川县",
+          "崇仁县"
+        ]
+      },
+      {
+        "name": "上饶市",
+        "area": [
+          "信州区",
+          "德兴市",
+          "上饶县",
+          "广丰县",
+          "鄱阳县",
+          "婺源县",
+          "铅山县",
+          "余干县",
+          "横峰县",
+          "弋阳县",
+          "玉山县",
+          "万年县"
+        ]
+      }
+    ]
+  },
+  {
+    "name": "山东省",
+    "city": [
+      {
+        "name": "济南市",
+        "area": [
+          "市中区",
+          "历下区",
+          "天桥区",
+          "槐荫区",
+          "历城区",
+          "长清区",
+          "章丘市",
+          "平阴县",
+          "济阳县",
+          "商河县"
+        ]
+      },
+      {
+        "name": "青岛市",
+        "area": [
+          "市南区",
+          "市北区",
+          "城阳区",
+          "四方区",
+          "李沧区",
+          "黄岛区",
+          "崂山区",
+          "胶南市",
+          "胶州市",
+          "平度市",
+          "莱西市",
+          "即墨市"
+        ]
+      },
+      {
+        "name": "淄博市",
+        "area": [
+          "张店区",
+          "临淄区",
+          "淄川区",
+          "博山区",
+          "周村区",
+          "桓台县",
+          "高青县",
+          "沂源县"
+        ]
+      },
+      {
+        "name": "枣庄市",
+        "area": [
+          "市中区",
+          "山亭区",
+          "峄城区",
+          "台儿庄区",
+          "薛城区",
+          "滕州市"
+        ]
+      },
+      {
+        "name": "东营市",
+        "area": [
+          "东营区",
+          "河口区",
+          "垦利县",
+          "广饶县",
+          "利津县"
+        ]
+      },
+      {
+        "name": "烟台市",
+        "area": [
+          "芝罘区",
+          "福山区",
+          "牟平区",
+          "莱山区",
+          "龙口市",
+          "莱阳市",
+          "莱州市",
+          "招远市",
+          "蓬莱市",
+          "栖霞市",
+          "海阳市",
+          "长岛县"
+        ]
+      },
+      {
+        "name": "潍坊市",
+        "area": [
+          "潍城区",
+          "寒亭区",
+          "坊子区",
+          "奎文区",
+          "青州市",
+          "诸城市",
+          "寿光市",
+          "安丘市",
+          "高密市",
+          "昌邑市",
+          "昌乐县",
+          "临朐县"
+        ]
+      },
+      {
+        "name": "济宁市",
+        "area": [
+          "市中区",
+          "任城区",
+          "曲阜市",
+          "兖州市",
+          "邹城市",
+          "鱼台县",
+          "金乡县",
+          "嘉祥县",
+          "微山县",
+          "汶上县",
+          "泗水县",
+          "梁山县"
+        ]
+      },
+      {
+        "name": "泰安市",
+        "area": [
+          "泰山区",
+          "岱岳区",
+          "新泰市",
+          "肥城市",
+          "宁阳县",
+          "东平县"
+        ]
+      },
+      {
+        "name": "威海市",
+        "area": [
+          "环翠区",
+          "乳山市",
+          "文登市",
+          "荣成市"
+        ]
+      },
+      {
+        "name": "日照市",
+        "area": [
+          "东港区",
+          "岚山区",
+          "五莲县",
+          "莒县"
+        ]
+      },
+      {
+        "name": "莱芜市",
+        "area": [
+          "莱城区",
+          "钢城区"
+        ]
+      },
+      {
+        "name": "临沂市",
+        "area": [
+          "兰山区",
+          "罗庄区",
+          "河东区",
+          "沂南县",
+          "郯城县",
+          "沂水县",
+          "苍山县",
+          "费县",
+          "平邑县",
+          "莒南县",
+          "蒙阴县",
+          "临沭县"
+        ]
+      },
+      {
+        "name": "德州市",
+        "area": [
+          "德城区",
+          "乐陵市",
+          "禹城市",
+          "陵县",
+          "宁津县",
+          "齐河县",
+          "武城县",
+          "庆云县",
+          "平原县",
+          "夏津县",
+          "临邑县"
+        ]
+      },
+      {
+        "name": "聊城市",
+        "area": [
+          "东昌府区",
+          "临清市",
+          "高唐县",
+          "阳谷县",
+          "茌平县",
+          "莘县",
+          "东阿县",
+          "冠县"
+        ]
+      },
+      {
+        "name": "滨州市",
+        "area": [
+          "滨城区",
+          "邹平县",
+          "沾化县",
+          "惠民县",
+          "博兴县",
+          "阳信县",
+          "无棣县"
+        ]
+      },
+      {
+        "name": "菏泽市",
+        "area": [
+          "牡丹区",
+          "鄄城县",
+          "单县",
+          "郓城县",
+          "曹县",
+          "定陶县",
+          "巨野县",
+          "东明县",
+          "成武县"
+        ]
+      }
+    ]
+  },
+  {
+    "name": "河南省",
+    "city": [
+      {
+        "name": "郑州市",
+        "area": [
+          "中原区",
+          "金水区",
+          "二七区",
+          "管城回族区",
+          "上街区",
+          "惠济区",
+          "巩义市",
+          "新郑市",
+          "新密市",
+          "登封市",
+          "荥阳市",
+          "中牟县"
+        ]
+      },
+      {
+        "name": "开封市",
+        "area": [
+          "鼓楼区",
+          "龙亭区",
+          "顺河回族区",
+          "禹王台区",
+          "金明区",
+          "开封县",
+          "尉氏县",
+          "兰考县",
+          "杞县",
+          "通许县"
+        ]
+      },
+      {
+        "name": "洛阳市",
+        "area": [
+          "西工区",
+          "老城区",
+          "涧西区",
+          "瀍河回族区",
+          "洛龙区",
+          "吉利区",
+          "偃师市",
+          "孟津县",
+          "汝阳县",
+          "伊川县",
+          "洛宁县",
+          "嵩县",
+          "宜阳县",
+          "新安县",
+          "栾川县"
+        ]
+      },
+      {
+        "name": "平顶山市",
+        "area": [
+          "新华区",
+          "卫东区",
+          "湛河区",
+          "石龙区",
+          "汝州市",
+          "舞钢市",
+          "宝丰县",
+          "叶县",
+          "郏县",
+          "鲁山县"
+        ]
+      },
+      {
+        "name": "安阳市",
+        "area": [
+          "北关区",
+          "文峰区",
+          "殷都区",
+          "龙安区",
+          "林州市",
+          "安阳县",
+          "滑县",
+          "内黄县",
+          "汤阴县"
+        ]
+      },
+      {
+        "name": "鹤壁市",
+        "area": [
+          "淇滨区",
+          "山城区",
+          "鹤山区",
+          "浚县",
+          "淇县"
+        ]
+      },
+      {
+        "name": "新乡市",
+        "area": [
+          "卫滨区",
+          "红旗区",
+          "凤泉区",
+          "牧野区",
+          "卫辉市",
+          "辉县市",
+          "新乡县",
+          "获嘉县",
+          "原阳县",
+          "长垣县",
+          "封丘县",
+          "延津县"
+        ]
+      },
+      {
+        "name": "焦作市",
+        "area": [
+          "解放区",
+          "中站区",
+          "马村区",
+          "山阳区",
+          "沁阳市",
+          "孟州市",
+          "修武县",
+          "温县",
+          "武陟县",
+          "博爱县"
+        ]
+      },
+      {
+        "name": "濮阳市",
+        "area": [
+          "华龙区",
+          "濮阳县",
+          "南乐县",
+          "台前县",
+          "清丰县",
+          "范县"
+        ]
+      },
+      {
+        "name": "许昌市",
+        "area": [
+          "魏都区",
+          "禹州市",
+          "长葛市",
+          "许昌县",
+          "鄢陵县",
+          "襄城县"
+        ]
+      },
+      {
+        "name": "漯河市",
+        "area": [
+          "源汇区",
+          "郾城区",
+          "召陵区",
+          "临颍县",
+          "舞阳县"
+        ]
+      },
+      {
+        "name": "三门峡市",
+        "area": [
+          "湖滨区",
+          "义马市",
+          "灵宝市",
+          "渑池县",
+          "卢氏县",
+          "陕县"
+        ]
+      },
+      {
+        "name": "南阳市",
+        "area": [
+          "卧龙区",
+          "宛城区",
+          "邓州市",
+          "桐柏县",
+          "方城县",
+          "淅川县",
+          "镇平县",
+          "唐河县",
+          "南召县",
+          "内乡县",
+          "新野县",
+          "社旗县",
+          "西峡县"
+        ]
+      },
+      {
+        "name": "商丘市",
+        "area": [
+          "梁园区",
+          "睢阳区",
+          "永城市",
+          "宁陵县",
+          "虞城县",
+          "民权县",
+          "夏邑县",
+          "柘城县",
+          "睢县"
+        ]
+      },
+      {
+        "name": "信阳市",
+        "area": [
+          "浉河区",
+          "平桥区",
+          "潢川县",
+          "淮滨县",
+          "息县",
+          "新县",
+          "商城县",
+          "固始县",
+          "罗山县",
+          "光山县"
+        ]
+      },
+      {
+        "name": "周口市",
+        "area": [
+          "川汇区",
+          "项城市",
+          "商水县",
+          "淮阳县",
+          "太康县",
+          "鹿邑县",
+          "西华县",
+          "扶沟县",
+          "沈丘县",
+          "郸城县"
+        ]
+      },
+      {
+        "name": "驻马店市",
+        "area": [
+          "驿城区",
+          "确山县",
+          "新蔡县",
+          "上蔡县",
+          "西平县",
+          "泌阳县",
+          "平舆县",
+          "汝南县",
+          "遂平县",
+          "正阳县"
+        ]
+      },
+      {
+        "name": "焦作市",
+        "area": [
+          "济源市"
+        ]
+      }
+    ]
+  },
+  {
+    "name": "湖北省",
+    "city": [
+      {
+        "name": "武汉市",
+        "area": [
+          "江岸区",
+          "武昌区",
+          "江汉区",
+          "硚口区",
+          "汉阳区",
+          "青山区",
+          "洪山区",
+          "东西湖区",
+          "汉南区",
+          "蔡甸区",
+          "江夏区",
+          "黄陂区",
+          "新洲区"
+        ]
+      },
+      {
+        "name": "黄石市",
+        "area": [
+          "黄石港区",
+          "西塞山区",
+          "下陆区",
+          "铁山区",
+          "大冶市",
+          "阳新县"
+        ]
+      },
+      {
+        "name": "十堰市",
+        "area": [
+          "张湾区",
+          "茅箭区",
+          "丹江口市",
+          "郧县",
+          "竹山县",
+          "房县",
+          "郧西县",
+          "竹溪县"
+        ]
+      },
+      {
+        "name": "荆州市",
+        "area": [
+          "沙市区",
+          "荆州区",
+          "洪湖市",
+          "石首市",
+          "松滋市",
+          "监利县",
+          "公安县",
+          "江陵县"
+        ]
+      },
+      {
+        "name": "宜昌市",
+        "area": [
+          "西陵区",
+          "伍家岗区",
+          "点军区",
+          "猇亭区",
+          "夷陵区",
+          "宜都市",
+          "当阳市",
+          "枝江市",
+          "秭归县",
+          "远安县",
+          "兴山县",
+          "五峰土家族自治县",
+          "长阳土家族自治县"
+        ]
+      },
+      {
+        "name": "襄樊市",
+        "area": [
+          "襄城区",
+          "樊城区",
+          "襄阳区",
+          "老河口市",
+          "枣阳市",
+          "宜城市",
+          "南漳县",
+          "谷城县",
+          "保康县"
+        ]
+      },
+      {
+        "name": "鄂州市",
+        "area": [
+          "鄂城区",
+          "华容区",
+          "梁子湖区"
+        ]
+      },
+      {
+        "name": "荆门市",
+        "area": [
+          "东宝区",
+          "掇刀区",
+          "钟祥市",
+          "京山县",
+          "沙洋县"
+        ]
+      },
+      {
+        "name": "孝感市",
+        "area": [
+          "孝南区",
+          "应城市",
+          "安陆市",
+          "汉川市",
+          "云梦县",
+          "大悟县",
+          "孝昌县"
+        ]
+      },
+      {
+        "name": "黄冈市",
+        "area": [
+          "黄州区",
+          "麻城市",
+          "武穴市",
+          "红安县",
+          "罗田县",
+          "浠水县",
+          "蕲春县",
+          "黄梅县",
+          "英山县",
+          "团风县"
+        ]
+      },
+      {
+        "name": "咸宁市",
+        "area": [
+          "咸安区",
+          "赤壁市",
+          "嘉鱼县",
+          "通山县",
+          "崇阳县",
+          "通城县"
+        ]
+      },
+      {
+        "name": "随州市",
+        "area": [
+          "曾都区",
+          "广水市"
+        ]
+      },
+      {
+        "name": "恩施土家族苗族自治州",
+        "area": [
+          "恩施市",
+          "利川市",
+          "建始县",
+          "来凤县",
+          "巴东县",
+          "鹤峰县",
+          "宣恩县",
+          "咸丰县"
+        ]
+      },
+      {
+        "name": "仙桃市",
+        "area": [
+          "仙桃"
+        ]
+      },
+      {
+        "name": "天门市",
+        "area": [
+          "天门"
+        ]
+      },
+      {
+        "name": "潜江市",
+        "area": [
+          "潜江"
+        ]
+      },
+      {
+        "name": "神农架林区",
+        "area": [
+          "神农架林区"
+        ]
+      }
+    ]
+  },
+  {
+    "name": "湖南省",
+    "city": [
+      {
+        "name": "长沙市",
+        "area": [
+          "岳麓区",
+          "芙蓉区",
+          "天心区",
+          "开福区",
+          "雨花区",
+          "浏阳市",
+          "长沙县",
+          "望城县",
+          "宁乡县"
+        ]
+      },
+      {
+        "name": "株洲市",
+        "area": [
+          "天元区",
+          "荷塘区",
+          "芦淞区",
+          "石峰区",
+          "醴陵市",
+          "株洲县",
+          "炎陵县",
+          "茶陵县",
+          "攸县"
+        ]
+      },
+      {
+        "name": "湘潭市",
+        "area": [
+          "岳塘区",
+          "雨湖区",
+          "湘乡市",
+          "韶山市",
+          "湘潭县"
+        ]
+      },
+      {
+        "name": "衡阳市",
+        "area": [
+          "雁峰区",
+          "珠晖区",
+          "石鼓区",
+          "蒸湘区",
+          "南岳区",
+          "耒阳市",
+          "常宁市",
+          "衡阳县",
+          "衡东县",
+          "衡山县",
+          "衡南县",
+          "祁东县"
+        ]
+      },
+      {
+        "name": "邵阳市",
+        "area": [
+          "双清区",
+          "大祥区",
+          "北塔区",
+          "武冈市",
+          "邵东县",
+          "洞口县",
+          "新邵县",
+          "绥宁县",
+          "新宁县",
+          "邵阳县",
+          "隆回县",
+          "城步苗族自治县"
+        ]
+      },
+      {
+        "name": "岳阳市",
+        "area": [
+          "岳阳楼区",
+          "云溪区",
+          "君山区",
+          "临湘市",
+          "汨罗市",
+          "岳阳县",
+          "湘阴县",
+          "平江县",
+          "华容县"
+        ]
+      },
+      {
+        "name": "常德市",
+        "area": [
+          "武陵区",
+          "鼎城区",
+          "津市市",
+          "澧县",
+          "临澧县",
+          "桃源县",
+          "汉寿县",
+          "安乡县",
+          "石门县"
+        ]
+      },
+      {
+        "name": "张家界市",
+        "area": [
+          "永定区",
+          "武陵源区",
+          "慈利县",
+          "桑植县"
+        ]
+      },
+      {
+        "name": "益阳市",
+        "area": [
+          "赫山区",
+          "资阳区",
+          "沅江市",
+          "桃江县",
+          "南县",
+          "安化县"
+        ]
+      },
+      {
+        "name": "郴州市",
+        "area": [
+          "北湖区",
+          "苏仙区",
+          "资兴市",
+          "宜章县",
+          "汝城县",
+          "安仁县",
+          "嘉禾县",
+          "临武县",
+          "桂东县",
+          "永兴县",
+          "桂阳县"
+        ]
+      },
+      {
+        "name": "永州市",
+        "area": [
+          "冷水滩区",
+          "零陵区",
+          "祁阳县",
+          "蓝山县",
+          "宁远县",
+          "新田县",
+          "东安县",
+          "江永县",
+          "道县",
+          "双牌县",
+          "江华瑶族自治县"
+        ]
+      },
+      {
+        "name": "怀化市",
+        "area": [
+          "鹤城区",
+          "洪江市",
+          "会同县",
+          "沅陵县",
+          "辰溪县",
+          "溆浦县",
+          "中方县",
+          "新晃侗族自治县",
+          "芷江侗族自治县",
+          "通道侗族自治县",
+          "靖州苗族侗族自治县",
+          "麻阳苗族自治县"
+        ]
+      },
+      {
+        "name": "娄底市",
+        "area": [
+          "娄星区",
+          "冷水江市",
+          "涟源市",
+          "新化县",
+          "双峰县"
+        ]
+      },
+      {
+        "name": "湘西土家族苗族自治州",
+        "area": [
+          "吉首市",
+          "古丈县",
+          "龙山县",
+          "永顺县",
+          "凤凰县",
+          "泸溪县",
+          "保靖县",
+          "花垣县"
+        ]
+      }
+    ]
+  },
+  {
+    "name": "广东省",
+    "city": [
+      {
+        "name": "广州市",
+        "area": [
+          "越秀区",
+          "荔湾区",
+          "海珠区",
+          "天河区",
+          "白云区",
+          "黄埔区",
+          "番禺区",
+          "花都区",
+          "南沙区",
+          "萝岗区",
+          "增城市",
+          "从化市"
+        ]
+      },
+      {
+        "name": "深圳市",
+        "area": [
+          "福田区",
+          "罗湖区",
+          "南山区",
+          "宝安区",
+          "龙岗区",
+          "盐田区"
+        ]
+      },
+      {
+        "name": "东莞市",
+        "area": [
+          "莞城",
+          "常平",
+          "塘厦",
+          "塘厦",
+          "塘厦"
+        ]
+      },
+      {
+        "name": "中山市",
+        "area": [
+          "中山"
+        ]
+      },
+      {
+        "name": "潮州市",
+        "area": [
+          "湘桥区",
+          "潮安县",
+          "饶平县"
+        ]
+      },
+      {
+        "name": "揭阳市",
+        "area": [
+          "榕城区",
+          "揭东县",
+          "揭西县",
+          "惠来县",
+          "普宁市"
+        ]
+      },
+      {
+        "name": "云浮市",
+        "area": [
+          "云城区",
+          "新兴县",
+          "郁南县",
+          "云安县",
+          "罗定市"
+        ]
+      },
+      {
+        "name": "珠海市",
+        "area": [
+          "香洲区",
+          "斗门区",
+          "金湾区"
+        ]
+      },
+      {
+        "name": "汕头市",
+        "area": [
+          "金平区",
+          "濠江区",
+          "龙湖区",
+          "潮阳区",
+          "潮南区",
+          "澄海区",
+          "南澳县"
+        ]
+      },
+      {
+        "name": "韶关市",
+        "area": [
+          "浈江区",
+          "武江区",
+          "曲江区",
+          "乐昌市",
+          "南雄市",
+          "始兴县",
+          "仁化县",
+          "翁源县",
+          "新丰县",
+          "乳源瑶族自治县"
+        ]
+      },
+      {
+        "name": "佛山市",
+        "area": [
+          "禅城区",
+          "南海区",
+          "顺德区",
+          "三水区",
+          "高明区"
+        ]
+      },
+      {
+        "name": "江门市",
+        "area": [
+          "蓬江区",
+          "江海区",
+          "新会区",
+          "恩平市",
+          "台山市",
+          "开平市",
+          "鹤山市"
+        ]
+      },
+      {
+        "name": "湛江市",
+        "area": [
+          "赤坎区",
+          "霞山区",
+          "坡头区",
+          "麻章区",
+          "吴川市",
+          "廉江市",
+          "雷州市",
+          "遂溪县",
+          "徐闻县"
+        ]
+      },
+      {
+        "name": "茂名市",
+        "area": [
+          "茂南区",
+          "茂港区",
+          "化州市",
+          "信宜市",
+          "高州市",
+          "电白县"
+        ]
+      },
+      {
+        "name": "肇庆市",
+        "area": [
+          "端州区",
+          "鼎湖区",
+          "高要市",
+          "四会市",
+          "广宁县",
+          "怀集县",
+          "封开县",
+          "德庆县"
+        ]
+      },
+      {
+        "name": "惠州市",
+        "area": [
+          "惠城区",
+          "惠阳区",
+          "博罗县",
+          "惠东县",
+          "龙门县"
+        ]
+      },
+      {
+        "name": "梅州市",
+        "area": [
+          "梅江区",
+          "兴宁市",
+          "梅县",
+          "大埔县",
+          "丰顺县",
+          "五华县",
+          "平远县",
+          "蕉岭县"
+        ]
+      },
+      {
+        "name": "汕尾市",
+        "area": [
+          "城区",
+          "陆丰市",
+          "海丰县",
+          "陆河县"
+        ]
+      },
+      {
+        "name": "河源市",
+        "area": [
+          "源城区",
+          "紫金县",
+          "龙川县",
+          "连平县",
+          "和平县",
+          "东源县"
+        ]
+      },
+      {
+        "name": "阳江市",
+        "area": [
+          "江城区",
+          "阳春市",
+          "阳西县",
+          "阳东县"
+        ]
+      },
+      {
+        "name": "清远市",
+        "area": [
+          "清城区",
+          "英德市",
+          "连州市",
+          "佛冈县",
+          "阳山县",
+          "清新县",
+          "连山壮族瑶族自治县",
+          "连南瑶族自治县"
+        ]
+      }
+    ]
+  },
+  {
+    "name": "广西",
+    "city": [
+      {
+        "name": "南宁市",
+        "area": [
+          "青秀区",
+          "兴宁区",
+          "西乡塘区",
+          "良庆区",
+          "江南区",
+          "邕宁区",
+          "武鸣县",
+          "隆安县",
+          "马山县",
+          "上林县",
+          "宾阳县",
+          "横县"
+        ]
+      },
+      {
+        "name": "柳州市",
+        "area": [
+          "城中区",
+          "鱼峰区",
+          "柳北区",
+          "柳南区",
+          "柳江县",
+          "柳城县",
+          "鹿寨县",
+          "融安县",
+          "融水苗族自治县",
+          "三江侗族自治县"
+        ]
+      },
+      {
+        "name": "桂林市",
+        "area": [
+          "象山区",
+          "秀峰区",
+          "叠彩区",
+          "七星区",
+          "雁山区",
+          "阳朔县",
+          "临桂县",
+          "灵川县",
+          "全州县",
+          "平乐县",
+          "兴安县",
+          "灌阳县",
+          "荔浦县",
+          "资源县",
+          "永福县",
+          "龙胜各族自治县",
+          "恭城瑶族自治县"
+        ]
+      },
+      {
+        "name": "梧州市",
+        "area": [
+          "万秀区",
+          "蝶山区",
+          "长洲区",
+          "岑溪市",
+          "苍梧县",
+          "藤县",
+          "蒙山县"
+        ]
+      },
+      {
+        "name": "北海市",
+        "area": [
+          "海城区",
+          "银海区",
+          "铁山港区",
+          "合浦县"
+        ]
+      },
+      {
+        "name": "防城港市",
+        "area": [
+          "港口区",
+          "防城区",
+          "东兴市",
+          "上思县"
+        ]
+      },
+      {
+        "name": "钦州市",
+        "area": [
+          "钦南区",
+          "钦北区",
+          "灵山县",
+          "浦北县"
+        ]
+      },
+      {
+        "name": "贵港市",
+        "area": [
+          "港北区",
+          "港南区",
+          "覃塘区",
+          "桂平市",
+          "平南县"
+        ]
+      },
+      {
+        "name": "玉林市",
+        "area": [
+          "玉州区",
+          "北流市",
+          "容县",
+          "陆川县",
+          "博白县",
+          "兴业县"
+        ]
+      },
+      {
+        "name": "百色市",
+        "area": [
+          "右江区",
+          "凌云县",
+          "平果县",
+          "西林县",
+          "乐业县",
+          "德保县",
+          "田林县",
+          "田阳县",
+          "靖西县",
+          "田东县",
+          "那坡县",
+          "隆林各族自治县"
+        ]
+      },
+      {
+        "name": "贺州市",
+        "area": [
+          "八步区",
+          "钟山县",
+          "昭平县",
+          "富川瑶族自治县"
+        ]
+      },
+      {
+        "name": "河池市",
+        "area": [
+          "金城江区",
+          "宜州市",
+          "天峨县",
+          "凤山县",
+          "南丹县",
+          "东兰县",
+          "都安瑶族自治县",
+          "罗城仫佬族自治县",
+          "巴马瑶族自治县",
+          "环江毛南族自治县",
+          "大化瑶族自治县"
+        ]
+      },
+      {
+        "name": "来宾市",
+        "area": [
+          "兴宾区",
+          "合山市",
+          "象州县",
+          "武宣县",
+          "忻城县",
+          "金秀瑶族自治县"
+        ]
+      },
+      {
+        "name": "崇左市",
+        "area": [
+          "江州区",
+          "凭祥市",
+          "宁明县",
+          "扶绥县",
+          "龙州县",
+          "大新县",
+          "天等县"
+        ]
+      }
+    ]
+  },
+  {
+    "name": "海南省",
+    "city": [
+      {
+        "name": "海口市",
+        "area": [
+          "龙华区",
+          "秀英区",
+          "琼山区",
+          "美兰区"
+        ]
+      },
+      {
+        "name": "三亚市",
+        "area": [
+          "三亚市"
+        ]
+      },
+      {
+        "name": "五指山市",
+        "area": [
+          "五指山"
+        ]
+      },
+      {
+        "name": "琼海市",
+        "area": [
+          "琼海"
+        ]
+      },
+      {
+        "name": "儋州市",
+        "area": [
+          "儋州"
+        ]
+      },
+      {
+        "name": "文昌市",
+        "area": [
+          "文昌"
+        ]
+      },
+      {
+        "name": "万宁市",
+        "area": [
+          "万宁"
+        ]
+      },
+      {
+        "name": "东方市",
+        "area": [
+          "东方"
+        ]
+      },
+      {
+        "name": "澄迈县",
+        "area": [
+          "澄迈县"
+        ]
+      },
+      {
+        "name": "定安县",
+        "area": [
+          "定安县"
+        ]
+      },
+      {
+        "name": "屯昌县",
+        "area": [
+          "屯昌县"
+        ]
+      },
+      {
+        "name": "临高县",
+        "area": [
+          "临高县"
+        ]
+      },
+      {
+        "name": "白沙黎族自治县",
+        "area": [
+          "白沙黎族自治县"
+        ]
+      },
+      {
+        "name": "昌江黎族自治县",
+        "area": [
+          "昌江黎族自治县"
+        ]
+      },
+      {
+        "name": "乐东黎族自治县",
+        "area": [
+          "乐东黎族自治县"
+        ]
+      },
+      {
+        "name": "陵水黎族自治县",
+        "area": [
+          "陵水黎族自治县"
+        ]
+      },
+      {
+        "name": "保亭黎族苗族自治县",
+        "area": [
+          "保亭黎族苗族自治县"
+        ]
+      },
+      {
+        "name": "琼中黎族苗族自治县",
+        "area": [
+          "琼中黎族苗族自治县"
+        ]
+      }
+    ]
+  },
+  {
+    "name": "重庆市",
+    "city": [
+      {
+        "name": "重庆市",
+        "area": [
+          "渝中区",
+          "大渡口区",
+          "江北区",
+          "南岸区",
+          "北碚区",
+          "渝北区",
+          "巴南区",
+          "长寿区",
+          "双桥区",
+          "沙坪坝区",
+          "万盛区",
+          "万州区",
+          "涪陵区",
+          "黔江区",
+          "永川区",
+          "合川区",
+          "江津区",
+          "九龙坡区",
+          "南川区",
+          "綦江县",
+          "潼南县",
+          "荣昌县",
+          "璧山县",
+          "大足县",
+          "铜梁县",
+          "梁平县",
+          "开县",
+          "忠县",
+          "城口县",
+          "垫江县",
+          "武隆县",
+          "丰都县",
+          "奉节县",
+          "云阳县",
+          "巫溪县",
+          "巫山县",
+          "石柱土家族自治县",
+          "秀山土家族苗族自治县",
+          "酉阳土家族苗族自治县",
+          "彭水苗族土家族自治县"
+        ]
+      }
+    ]
+  },
+  {
+    "name": "四川省",
+    "city": [
+      {
+        "name": "成都市",
+        "area": [
+          "青羊区",
+          "锦江区",
+          "金牛区",
+          "武侯区",
+          "成华区",
+          "龙泉驿区",
+          "青白江区",
+          "新都区",
+          "温江区",
+          "都江堰市",
+          "彭州市",
+          "邛崃市",
+          "崇州市",
+          "金堂县",
+          "郫县",
+          "新津县",
+          "双流县",
+          "蒲江县",
+          "大邑县"
+        ]
+      },
+      {
+        "name": "自贡市",
+        "area": [
+          "大安区",
+          "自流井区",
+          "贡井区",
+          "沿滩区",
+          "荣县",
+          "富顺县"
+        ]
+      },
+      {
+        "name": "攀枝花市",
+        "area": [
+          "仁和区",
+          "米易县",
+          "盐边县",
+          "东区",
+          "西区"
+        ]
+      },
+      {
+        "name": "泸州市",
+        "area": [
+          "江阳区",
+          "纳溪区",
+          "龙马潭区",
+          "泸县",
+          "合江县",
+          "叙永县",
+          "古蔺县"
+        ]
+      },
+      {
+        "name": "德阳市",
+        "area": [
+          "旌阳区",
+          "广汉市",
+          "什邡市",
+          "绵竹市",
+          "罗江县",
+          "中江县"
+        ]
+      },
+      {
+        "name": "绵阳市",
+        "area": [
+          "涪城区",
+          "游仙区",
+          "江油市",
+          "盐亭县",
+          "三台县",
+          "平武县",
+          "安县",
+          "梓潼县",
+          "北川羌族自治县"
+        ]
+      },
+      {
+        "name": "广元市",
+        "area": [
+          "元坝区",
+          "朝天区",
+          "青川县",
+          "旺苍县",
+          "剑阁县",
+          "苍溪县",
+          "市中区"
+        ]
+      },
+      {
+        "name": "遂宁市",
+        "area": [
+          "船山区",
+          "安居区",
+          "射洪县",
+          "蓬溪县",
+          "大英县"
+        ]
+      },
+      {
+        "name": "内江市",
+        "area": [
+          "市中区",
+          "东兴区",
+          "资中县",
+          "隆昌县",
+          "威远县"
+        ]
+      },
+      {
+        "name": "乐山市",
+        "area": [
+          "市中区",
+          "五通桥区",
+          "沙湾区",
+          "金口河区",
+          "峨眉山市",
+          "夹江县",
+          "井研县",
+          "犍为县",
+          "沐川县",
+          "马边彝族自治县",
+          "峨边彝族自治县"
+        ]
+      },
+      {
+        "name": "南充",
+        "area": [
+          "顺庆区",
+          "高坪区",
+          "嘉陵区",
+          "阆中市",
+          "营山县",
+          "蓬安县",
+          "仪陇县",
+          "南部县",
+          "西充县"
+        ]
+      },
+      {
+        "name": "眉山市",
+        "area": [
+          "东坡区",
+          "仁寿县",
+          "彭山县",
+          "洪雅县",
+          "丹棱县",
+          "青神县"
+        ]
+      },
+      {
+        "name": "宜宾市",
+        "area": [
+          "翠屏区",
+          "宜宾县",
+          "兴文县",
+          "南溪县",
+          "珙县",
+          "长宁县",
+          "高县",
+          "江安县",
+          "筠连县",
+          "屏山县"
+        ]
+      },
+      {
+        "name": "广安市",
+        "area": [
+          "广安区",
+          "华蓥市",
+          "岳池县",
+          "邻水县",
+          "武胜县"
+        ]
+      },
+      {
+        "name": "达州市",
+        "area": [
+          "通川区",
+          "万源市",
+          "达县",
+          "渠县",
+          "宣汉县",
+          "开江县",
+          "大竹县"
+        ]
+      },
+      {
+        "name": "雅安市",
+        "area": [
+          "雨城区",
+          "芦山县",
+          "石棉县",
+          "名山县",
+          "天全县",
+          "荥经县",
+          "宝兴县",
+          "汉源县"
+        ]
+      },
+      {
+        "name": "巴中市",
+        "area": [
+          "巴州区",
+          "南江县",
+          "平昌县",
+          "通江县"
+        ]
+      },
+      {
+        "name": "资阳市",
+        "area": [
+          "雁江区",
+          "简阳市",
+          "安岳县",
+          "乐至县"
+        ]
+      },
+      {
+        "name": "阿坝藏族羌族自治州",
+        "area": [
+          "马尔康县",
+          "九寨沟县",
+          "红原县",
+          "汶川县",
+          "阿坝县",
+          "理县",
+          "若尔盖县",
+          "小金县",
+          "黑水县",
+          "金川县",
+          "松潘县",
+          "壤塘县",
+          "茂县"
+        ]
+      },
+      {
+        "name": "甘孜藏族自治州",
+        "area": [
+          "康定县",
+          "丹巴县",
+          "炉霍县",
+          "九龙县",
+          "甘孜县",
+          "雅江县",
+          "新龙县",
+          "道孚县",
+          "白玉县",
+          "理塘县",
+          "德格县",
+          "乡城县",
+          "石渠县",
+          "稻城县",
+          "色达县",
+          "巴塘县",
+          "泸定县",
+          "得荣县"
+        ]
+      },
+      {
+        "name": "凉山彝族自治州",
+        "area": [
+          "西昌市",
+          "美姑县",
+          "昭觉县",
+          "金阳县",
+          "甘洛县",
+          "布拖县",
+          "雷波县",
+          "普格县",
+          "宁南县",
+          "喜德县",
+          "会东县",
+          "越西县",
+          "会理县",
+          "盐源县",
+          "德昌县",
+          "冕宁县",
+          "木里藏族自治县"
+        ]
+      }
+    ]
+  },
+  {
+    "name": "贵州省",
+    "city": [
+      {
+        "name": "贵阳市",
+        "area": [
+          "南明区",
+          "云岩区",
+          "花溪区",
+          "乌当区",
+          "白云区",
+          "小河区",
+          "清镇市",
+          "开阳县",
+          "修文县",
+          "息烽县"
+        ]
+      },
+      {
+        "name": "六盘水市",
+        "area": [
+          "钟山区",
+          "水城县",
+          "盘县",
+          "六枝特区"
+        ]
+      },
+      {
+        "name": "遵义市",
+        "area": [
+          "红花岗区",
+          "汇川区",
+          "赤水市",
+          "仁怀市",
+          "遵义县",
+          "绥阳县",
+          "桐梓县",
+          "习水县",
+          "凤冈县",
+          "正安县",
+          "余庆县",
+          "湄潭县",
+          "道真仡佬族苗族自治县",
+          "务川仡佬族苗族自治县"
+        ]
+      },
+      {
+        "name": "安顺市",
+        "area": [
+          "西秀区",
+          "普定县",
+          "平坝县",
+          "镇宁布依族苗族自治县",
+          "紫云苗族布依族自治县",
+          "关岭布依族苗族自治县"
+        ]
+      },
+      {
+        "name": "铜仁地区",
+        "area": [
+          "铜仁市",
+          "德江县",
+          "江口县",
+          "思南县",
+          "石阡县",
+          "玉屏侗族自治县",
+          "松桃苗族自治县",
+          "印江土家族苗族自治县",
+          "沿河土家族自治县",
+          "万山特区"
+        ]
+      },
+      {
+        "name": "毕节地区",
+        "area": [
+          "毕节市",
+          "黔西县",
+          "大方县",
+          "织金县",
+          "金沙县",
+          "赫章县",
+          "纳雍县",
+          "威宁彝族回族苗族自治县"
+        ]
+      },
+      {
+        "name": "黔西南布依族苗族自治州",
+        "area": [
+          "兴义市",
+          "望谟县",
+          "兴仁县",
+          "普安县",
+          "册亨县",
+          "晴隆县",
+          "贞丰县",
+          "安龙县"
+        ]
+      },
+      {
+        "name": "黔东南苗族侗族自治州",
+        "area": [
+          "凯里市",
+          "施秉县",
+          "从江县",
+          "锦屏县",
+          "镇远县",
+          "麻江县",
+          "台江县",
+          "天柱县",
+          "黄平县",
+          "榕江县",
+          "剑河县",
+          "三穗县",
+          "雷山县",
+          "黎平县",
+          "岑巩县",
+          "丹寨县"
+        ]
+      },
+      {
+        "name": "黔南布依族苗族自治州",
+        "area": [
+          "都匀市",
+          "福泉市",
+          "贵定县",
+          "惠水县",
+          "罗甸县",
+          "瓮安县",
+          "荔波县",
+          "龙里县",
+          "平塘县",
+          "长顺县",
+          "独山县",
+          "三都水族自治县"
+        ]
+      }
+    ]
+  },
+  {
+    "name": "云南省",
+    "city": [
+      {
+        "name": "昆明市",
+        "area": [
+          "盘龙区",
+          "五华区",
+          "官渡区",
+          "西山区",
+          "东川区",
+          "安宁市",
+          "呈贡县",
+          "晋宁县",
+          "富民县",
+          "宜良县",
+          "嵩明县",
+          "石林彝族自治县",
+          "禄劝彝族苗族自治县",
+          "寻甸回族彝族自治县"
+        ]
+      },
+      {
+        "name": "曲靖市",
+        "area": [
+          "麒麟区",
+          "宣威市",
+          "马龙县",
+          "沾益县",
+          "富源县",
+          "罗平县",
+          "师宗县",
+          "陆良县",
+          "会泽县"
+        ]
+      },
+      {
+        "name": "玉溪市",
+        "area": [
+          "红塔区",
+          "江川县",
+          "澄江县",
+          "通海县",
+          "华宁县",
+          "易门县",
+          "峨山彝族自治县",
+          "新平彝族傣族自治县",
+          "元江哈尼族彝族傣族自治县"
+        ]
+      },
+      {
+        "name": "保山市",
+        "area": [
+          "隆阳区",
+          "施甸县",
+          "腾冲县",
+          "龙陵县",
+          "昌宁县"
+        ]
+      },
+      {
+        "name": "昭通市",
+        "area": [
+          "昭阳区",
+          "鲁甸县",
+          "巧家县",
+          "盐津县",
+          "大关县",
+          "永善县",
+          "绥江县",
+          "镇雄县",
+          "彝良县",
+          "威信县",
+          "水富县"
+        ]
+      },
+      {
+        "name": "丽江市",
+        "area": [
+          "古城区",
+          "永胜县",
+          "华坪县",
+          "玉龙纳西族自治县",
+          "宁蒗彝族自治县"
+        ]
+      },
+      {
+        "name": "普洱市",
+        "area": [
+          "思茅区",
+          "普洱哈尼族彝族自治县",
+          "墨江哈尼族自治县",
+          "景东彝族自治县",
+          "景谷傣族彝族自治县",
+          "镇沅彝族哈尼族拉祜族自治县",
+          "江城哈尼族彝族自治县",
+          "孟连傣族拉祜族佤族自治县",
+          "澜沧拉祜族自治县",
+          "西盟佤族自治县"
+        ]
+      },
+      {
+        "name": "临沧市",
+        "area": [
+          "临翔区",
+          "凤庆县",
+          "云县",
+          "永德县",
+          "镇康县",
+          "双江拉祜族佤族布朗族傣族自治县",
+          "耿马傣族佤族自治县",
+          "沧源佤族自治县"
+        ]
+      },
+      {
+        "name": "德宏傣族景颇族自治州",
+        "area": [
+          "潞西市",
+          "瑞丽市",
+          "梁河县",
+          "盈江县",
+          "陇川县"
+        ]
+      },
+      {
+        "name": "怒江傈僳族自治州",
+        "area": [
+          "泸水县",
+          "福贡县",
+          "贡山独龙族怒族自治县",
+          "兰坪白族普米族自治县"
+        ]
+      },
+      {
+        "name": "迪庆藏族自治州",
+        "area": [
+          "香格里拉县",
+          "德钦县",
+          "维西傈僳族自治县"
+        ]
+      },
+      {
+        "name": "大理白族自治州",
+        "area": [
+          "大理市",
+          "祥云县",
+          "宾川县",
+          "弥渡县",
+          "永平县",
+          "云龙县",
+          "洱源县",
+          "剑川县",
+          "鹤庆县",
+          "漾濞彝族自治县",
+          "南涧彝族自治县",
+          "巍山彝族回族自治县"
+        ]
+      },
+      {
+        "name": "楚雄彝族自治州",
+        "area": [
+          "楚雄市",
+          "双柏县",
+          "牟定县",
+          "南华县",
+          "姚安县",
+          "大姚县",
+          "永仁县",
+          "元谋县",
+          "武定县",
+          "禄丰县"
+        ]
+      },
+      {
+        "name": "红河哈尼族彝族自治州",
+        "area": [
+          "蒙自县",
+          "个旧市",
+          "开远市",
+          "绿春县",
+          "建水县",
+          "石屏县",
+          "弥勒县",
+          "泸西县",
+          "元阳县",
+          "红河县",
+          "金平苗族瑶族傣族自治县",
+          "河口瑶族自治县",
+          "屏边苗族自治县"
+        ]
+      },
+      {
+        "name": "文山壮族苗族自治州",
+        "area": [
+          "文山县",
+          "砚山县",
+          "西畴县",
+          "麻栗坡县",
+          "马关县",
+          "丘北县",
+          "广南县",
+          "富宁县"
+        ]
+      },
+      {
+        "name": "西双版纳傣族自治州",
+        "area": [
+          "景洪市",
+          "勐海县",
+          "勐腊县"
+        ]
+      }
+    ]
+  },
+  {
+    "name": "西藏",
+    "city": [
+      {
+        "name": "拉萨市",
+        "area": [
+          "城关区",
+          "林周县",
+          "当雄县",
+          "尼木县",
+          "曲水县",
+          "堆龙德庆县",
+          "达孜县",
+          "墨竹工卡县"
+        ]
+      },
+      {
+        "name": "那曲地区",
+        "area": [
+          "那曲县",
+          "嘉黎县",
+          "比如县",
+          "聂荣县",
+          "安多县",
+          "申扎县",
+          "索县",
+          "班戈县",
+          "巴青县",
+          "尼玛县"
+        ]
+      },
+      {
+        "name": "昌都地区",
+        "area": [
+          "昌都县",
+          "江达县",
+          "贡觉县",
+          "类乌齐县",
+          "丁青县",
+          "察雅县",
+          "八宿县",
+          "左贡县",
+          "芒康县",
+          "洛隆县",
+          "边坝县"
+        ]
+      },
+      {
+        "name": "林芝地区",
+        "area": [
+          "林芝县",
+          "工布江达县",
+          "米林县",
+          "墨脱县",
+          "波密县",
+          "察隅县",
+          "朗县"
+        ]
+      },
+      {
+        "name": "山南地区",
+        "area": [
+          "乃东县",
+          "扎囊县",
+          "贡嘎县",
+          "桑日县",
+          "琼结县",
+          "曲松县",
+          "措美县",
+          "洛扎县",
+          "加查县",
+          "隆子县",
+          "错那县",
+          "浪卡子县"
+        ]
+      },
+      {
+        "name": "日喀则地区",
+        "area": [
+          "日喀则市",
+          "南木林县",
+          "江孜县",
+          "定日县",
+          "萨迦县",
+          "拉孜县",
+          "昂仁县",
+          "谢通门县",
+          "白朗县",
+          "仁布县",
+          "康马县",
+          "定结县",
+          "仲巴县",
+          "亚东县",
+          "吉隆县",
+          "聂拉木县",
+          "萨嘎县",
+          "岗巴县"
+        ]
+      },
+      {
+        "name": "阿里地区",
+        "area": [
+          "噶尔县",
+          "普兰县",
+          "札达县",
+          "日土县",
+          "革吉县",
+          "改则县",
+          "措勤县"
+        ]
+      }
+    ]
+  },
+  {
+    "name": "陕西省",
+    "city": [
+      {
+        "name": "西安市",
+        "area": [
+          "莲湖区",
+          "新城区",
+          "碑林区",
+          "雁塔区",
+          "灞桥区",
+          "未央区",
+          "阎良区",
+          "临潼区",
+          "长安区",
+          "高陵县",
+          "蓝田县",
+          "户县",
+          "周至县"
+        ]
+      },
+      {
+        "name": "铜川市",
+        "area": [
+          "耀州区",
+          "王益区",
+          "印台区",
+          "宜君县"
+        ]
+      },
+      {
+        "name": "宝鸡市",
+        "area": [
+          "渭滨区",
+          "金台区",
+          "陈仓区",
+          "岐山县",
+          "凤翔县",
+          "陇县",
+          "太白县",
+          "麟游县",
+          "扶风县",
+          "千阳县",
+          "眉县",
+          "凤县"
+        ]
+      },
+      {
+        "name": "咸阳市",
+        "area": [
+          "秦都区",
+          "渭城区",
+          "杨陵区",
+          "兴平市",
+          "礼泉县",
+          "泾阳县",
+          "永寿县",
+          "三原县",
+          "彬县",
+          "旬邑县",
+          "长武县",
+          "乾县",
+          "武功县",
+          "淳化县"
+        ]
+      },
+      {
+        "name": "渭南市",
+        "area": [
+          "临渭区",
+          "韩城市",
+          "华阴市",
+          "蒲城县",
+          "潼关县",
+          "白水县",
+          "澄城县",
+          "华县",
+          "合阳县",
+          "富平县",
+          "大荔县"
+        ]
+      },
+      {
+        "name": "延安市",
+        "area": [
+          "宝塔区",
+          "安塞县",
+          "洛川县",
+          "子长县",
+          "黄陵县",
+          "延川县",
+          "富县",
+          "延长县",
+          "甘泉县",
+          "宜川县",
+          "志丹县",
+          "黄龙县",
+          "吴起县"
+        ]
+      },
+      {
+        "name": "汉中市",
+        "area": [
+          "汉台区",
+          "留坝县",
+          "镇巴县",
+          "城固县",
+          "南郑县",
+          "洋县",
+          "宁强县",
+          "佛坪县",
+          "勉县",
+          "西乡县",
+          "略阳县"
+        ]
+      },
+      {
+        "name": "榆林市",
+        "area": [
+          "榆阳区",
+          "清涧县",
+          "绥德县",
+          "神木县",
+          "佳县",
+          "府谷县",
+          "子洲县",
+          "靖边县",
+          "横山县",
+          "米脂县",
+          "吴堡县",
+          "定边县"
+        ]
+      },
+      {
+        "name": "安康市",
+        "area": [
+          "汉滨区",
+          "紫阳县",
+          "岚皋县",
+          "旬阳县",
+          "镇坪县",
+          "平利县",
+          "石泉县",
+          "宁陕县",
+          "白河县",
+          "汉阴县"
+        ]
+      },
+      {
+        "name": "商洛市",
+        "area": [
+          "商州区",
+          "镇安县",
+          "山阳县",
+          "洛南县",
+          "商南县",
+          "丹凤县",
+          "柞水县"
+        ]
+      }
+    ]
+  },
+  {
+    "name": "甘肃省",
+    "city": [
+      {
+        "name": "兰州市",
+        "area": [
+          "城关区",
+          "七里河区",
+          "西固区",
+          "安宁区",
+          "红古区",
+          "永登县",
+          "皋兰县",
+          "榆中县"
+        ]
+      },
+      {
+        "name": "嘉峪关市",
+        "area": [
+          "嘉峪关市"
+        ]
+      },
+      {
+        "name": "金昌市",
+        "area": [
+          "金川区",
+          "永昌县"
+        ]
+      },
+      {
+        "name": "白银市",
+        "area": [
+          "白银区",
+          "平川区",
+          "靖远县",
+          "会宁县",
+          "景泰县"
+        ]
+      },
+      {
+        "name": "天水市",
+        "area": [
+          "清水县",
+          "秦安县",
+          "甘谷县",
+          "武山县",
+          "张家川回族自治县",
+          "北道区",
+          "秦城区"
+        ]
+      },
+      {
+        "name": "武威市",
+        "area": [
+          "凉州区",
+          "民勤县",
+          "古浪县",
+          "天祝藏族自治县"
+        ]
+      },
+      {
+        "name": "酒泉市",
+        "area": [
+          "肃州区",
+          "玉门市",
+          "敦煌市",
+          "金塔县",
+          "肃北蒙古族自治县",
+          "阿克塞哈萨克族自治县",
+          "安西县"
+        ]
+      },
+      {
+        "name": "张掖市",
+        "area": [
+          "甘州区",
+          "民乐县",
+          "临泽县",
+          "高台县",
+          "山丹县",
+          "肃南裕固族自治县"
+        ]
+      },
+      {
+        "name": "庆阳市",
+        "area": [
+          "西峰区",
+          "庆城县",
+          "环县",
+          "华池县",
+          "合水县",
+          "正宁县",
+          "宁县",
+          "镇原县"
+        ]
+      },
+      {
+        "name": "平凉市",
+        "area": [
+          "崆峒区",
+          "泾川县",
+          "灵台县",
+          "崇信县",
+          "华亭县",
+          "庄浪县",
+          "静宁县"
+        ]
+      },
+      {
+        "name": "定西市",
+        "area": [
+          "安定区",
+          "通渭县",
+          "临洮县",
+          "漳县",
+          "岷县",
+          "渭源县",
+          "陇西县"
+        ]
+      },
+      {
+        "name": "陇南市",
+        "area": [
+          "武都区",
+          "成县",
+          "宕昌县",
+          "康县",
+          "文县",
+          "西和县",
+          "礼县",
+          "两当县",
+          "徽县"
+        ]
+      },
+      {
+        "name": "临夏回族自治州",
+        "area": [
+          "临夏市",
+          "临夏县",
+          "康乐县",
+          "永靖县",
+          "广河县",
+          "和政县",
+          "东乡族自治县",
+          "积石山保安族东乡族撒拉族自治县"
+        ]
+      },
+      {
+        "name": "甘南藏族自治州",
+        "area": [
+          "合作市",
+          "临潭县",
+          "卓尼县",
+          "舟曲县",
+          "迭部县",
+          "玛曲县",
+          "碌曲县",
+          "夏河县"
+        ]
+      }
+    ]
+  },
+  {
+    "name": "青海省",
+    "city": [
+      {
+        "name": "西宁市",
+        "area": [
+          "城中区",
+          "城东区",
+          "城西区",
+          "城北区",
+          "湟源县",
+          "湟中县",
+          "大通回族土族自治县"
+        ]
+      },
+      {
+        "name": "海东地区",
+        "area": [
+          "平安县",
+          "乐都县",
+          "民和回族土族自治县",
+          "互助土族自治县",
+          "化隆回族自治县",
+          "循化撒拉族自治县"
+        ]
+      },
+      {
+        "name": "海北藏族自治州",
+        "area": [
+          "海晏县",
+          "祁连县",
+          "刚察县",
+          "门源回族自治县"
+        ]
+      },
+      {
+        "name": "海南藏族自治州",
+        "area": [
+          "共和县",
+          "同德县",
+          "贵德县",
+          "兴海县",
+          "贵南县"
+        ]
+      },
+      {
+        "name": "黄南藏族自治州",
+        "area": [
+          "同仁县",
+          "尖扎县",
+          "泽库县",
+          "河南蒙古族自治县"
+        ]
+      },
+      {
+        "name": "果洛藏族自治州",
+        "area": [
+          "玛沁县",
+          "班玛县",
+          "甘德县",
+          "达日县",
+          "久治县",
+          "玛多县"
+        ]
+      },
+      {
+        "name": "玉树藏族自治州",
+        "area": [
+          "玉树县",
+          "杂多县",
+          "称多县",
+          "治多县",
+          "囊谦县",
+          "曲麻莱县"
+        ]
+      },
+      {
+        "name": "海西蒙古族藏族自治州",
+        "area": [
+          "德令哈市",
+          "格尔木市",
+          "乌兰县",
+          "都兰县",
+          "天峻县"
+        ]
+      }
+    ]
+  },
+  {
+    "name": "宁夏",
+    "city": [
+      {
+        "name": "银川市",
+        "area": [
+          "兴庆区",
+          "西夏区",
+          "金凤区",
+          "灵武市",
+          "永宁县",
+          "贺兰县"
+        ]
+      },
+      {
+        "name": "石嘴山市",
+        "area": [
+          "大武口区",
+          "惠农区",
+          "平罗县"
+        ]
+      },
+      {
+        "name": "吴忠市",
+        "area": [
+          "利通区",
+          "青铜峡市",
+          "盐池县",
+          "同心县"
+        ]
+      },
+      {
+        "name": "固原市",
+        "area": [
+          "原州区",
+          "西吉县",
+          "隆德县",
+          "泾源县",
+          "彭阳县"
+        ]
+      },
+      {
+        "name": "中卫市",
+        "area": [
+          "沙坡头区",
+          "中宁县",
+          "海原县"
+        ]
+      }
+    ]
+  },
+  {
+    "name": "新疆",
+    "city": [
+      {
+        "name": "乌鲁木齐市",
+        "area": [
+          "天山区",
+          "沙依巴克区",
+          "新市区",
+          "水磨沟区",
+          "头屯河区",
+          "达坂城区",
+          "东山区",
+          "乌鲁木齐县"
+        ]
+      },
+      {
+        "name": "克拉玛依市",
+        "area": [
+          "克拉玛依区",
+          "独山子区",
+          "白碱滩区",
+          "乌尔禾区"
+        ]
+      },
+      {
+        "name": "吐鲁番地区",
+        "area": [
+          "吐鲁番市",
+          "托克逊县",
+          "鄯善县"
+        ]
+      },
+      {
+        "name": "哈密地区",
+        "area": [
+          "哈密市",
+          "伊吾县",
+          "巴里坤哈萨克自治县"
+        ]
+      },
+      {
+        "name": "和田地区",
+        "area": [
+          "和田市",
+          "和田县",
+          "洛浦县",
+          "民丰县",
+          "皮山县",
+          "策勒县",
+          "于田县",
+          "墨玉县"
+        ]
+      },
+      {
+        "name": "阿克苏地区",
+        "area": [
+          "阿克苏市",
+          "温宿县",
+          "沙雅县",
+          "拜城县",
+          "阿瓦提县",
+          "库车县",
+          "柯坪县",
+          "新和县",
+          "乌什县"
+        ]
+      },
+      {
+        "name": "喀什地区",
+        "area": [
+          "喀什市",
+          "巴楚县",
+          "泽普县",
+          "伽师县",
+          "叶城县",
+          "岳普湖县",
+          "疏勒县",
+          "麦盖提县",
+          "英吉沙县",
+          "莎车县",
+          "疏附县",
+          "塔什库尔干塔吉克自治县"
+        ]
+      },
+      {
+        "name": "克孜勒苏柯尔克孜自治州",
+        "area": [
+          "阿图什市",
+          "阿合奇县",
+          "乌恰县",
+          "阿克陶县"
+        ]
+      },
+      {
+        "name": "巴音郭楞蒙古自治州",
+        "area": [
+          "库尔勒市",
+          "和静县",
+          "尉犁县",
+          "和硕县",
+          "且末县",
+          "博湖县",
+          "轮台县",
+          "若羌县",
+          "焉耆回族自治县"
+        ]
+      },
+      {
+        "name": "昌吉回族自治州",
+        "area": [
+          "昌吉市",
+          "阜康市",
+          "奇台县",
+          "玛纳斯县",
+          "吉木萨尔县",
+          "呼图壁县",
+          "木垒哈萨克自治县",
+          "米泉市"
+        ]
+      },
+      {
+        "name": "博尔塔拉蒙古自治州",
+        "area": [
+          "博乐市",
+          "精河县",
+          "温泉县"
+        ]
+      },
+      {
+        "name": "石河子",
+        "area": [
+          "石河子"
+        ]
+      },
+      {
+        "name": "阿拉尔",
+        "area": [
+          "阿拉尔"
+        ]
+      },
+      {
+        "name": "图木舒克",
+        "area": [
+          "图木舒克"
+        ]
+      },
+      {
+        "name": "五家渠",
+        "area": [
+          "五家渠"
+        ]
+      },
+      {
+        "name": "伊犁哈萨克自治州",
+        "area": [
+          "伊宁市",
+          "奎屯市",
+          "伊宁县",
+          "特克斯县",
+          "尼勒克县",
+          "昭苏县",
+          "新源县",
+          "霍城县",
+          "巩留县",
+          "察布查尔锡伯自治县",
+          "塔城地区",
+          "阿勒泰地区"
+        ]
+      }
+    ]
+  },
+  {
+    "name": "台湾省",
+    "city": [
+      {
+        "name": "台北市",
+        "area": [
+          "内湖区",
+          "南港区",
+          "中正区",
+          "万华区",
+          "大同区",
+          "中山区",
+          "松山区",
+          "大安区",
+          "信义区",
+          "文山区",
+          "士林区",
+          "北投区"
+        ]
+      },
+      {
+        "name": "新北市",
+        "area": [
+          "板桥区",
+          "汐止区",
+          "新店区"
+        ]
+      },
+      {
+        "name": "桃园市",
+        "area": [
+          "其他"
+        ]
+      },
+      {
+        "name": "台中市",
+        "area": [
+          "其他"
+        ]
+      },
+      {
+        "name": "台南市",
+        "area": [
+          "其他"
+        ]
+      },
+      {
+        "name": "高雄市",
+        "area": [
+          "其他"
+        ]
+      }
+    ]
+  },
+  {
+    "name": "澳门",
+    "city": [
+      {
+        "name": "澳门",
+        "area": [
+          "花地玛堂区",
+          "圣安多尼堂区",
+          "大堂区",
+          "望德堂区",
+          "风顺堂区",
+          "嘉模堂区",
+          "圣方济各堂区",
+          "路凼"
+        ]
+      }
+    ]
+  },
+  {
+    "name": "香港",
+    "city": [
+      {
+        "name": "香港",
+        "area": [
+          "深水埗区",
+          "油尖旺区",
+          "九龙城区",
+          "黄大仙区",
+          "观塘区",
+          "北区",
+          "大埔区",
+          "沙田区",
+          "西贡区",
+          "元朗区",
+          "屯门区",
+          "荃湾区",
+          "葵青区",
+          "离岛区",
+          "中西区",
+          "湾仔区",
+          "东区",
+          "南区"
+        ]
+      }
+    ]
+  }
+]

+ 33 - 0
common/filter.js

@@ -0,0 +1,33 @@
+const filter = {
+    formatDate: function(value,args) {
+        var dt = new Date(value);
+        if(args == 'yyyy-M-d') {
+            let year = dt.getFullYear();
+            let month = dt.getMonth() + 1;
+            let date = dt.getDate();
+            return `${year}-${month}-${date}`;
+        } else if(args == 'yyyy-M-d H:m:s'){
+            let year = dt.getFullYear();
+            let month = dt.getMonth() + 1;
+            let date = dt.getDate();
+            let hour = dt.getHours();
+            let minute = dt.getMinutes();
+            let second = dt.getSeconds();
+            return `${year}-${month}-${date} ${hour}:${minute}:${second}`;
+        } else if(args == 'MM.dd') {
+            let month = (dt.getMonth() + 1).toString().padStart(2,'0');
+            let date = dt.getDate().toString().padStart(2,'0');
+            return `${month}.${date}`;
+        } else {// yyyy-MM-dd HH:mm:ss
+            let year = dt.getFullYear();
+            let month = (dt.getMonth() + 1).toString().padStart(2,'0');
+            let date = dt.getDate().toString().padStart(2,'0');
+            let hour = dt.getHours().toString().padStart(2,'0');
+            let minute = dt.getMinutes().toString().padStart(2,'0');
+            let second = dt.getSeconds().toString().padStart(2,'0');
+            return `${year}-${month}-${date} ${hour}:${minute}:${second}`;
+        }
+    }
+}
+
+export default filter

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
common/md5.js


+ 36 - 0
common/pay.js

@@ -0,0 +1,36 @@
+/**
+ * 微信小程序支付,仅支持微信支付(后续可能集成网页支付宝支付web-view)
+ *
+ * @param : provider(String) ->付款商家
+ * @param : timeStamp(String) ->时间戳(当前支付时间)
+ * @param : nonceStr(String) ->支付密匙
+ * @param : packages(String) ->支付id
+ * @param : signType(String) ->加密方式(默认MD5)
+ * @param : paySign(String)
+ *
+ *
+ *
+ * 小程序支付调用
+ *
+ * wePay(provider, timeStamp, nonceStr, packages, signType, paySign,res=>{},fail=>{})
+ */
+ 
+const wePay = (provider, timeStamp, nonceStr, packages, signType, paySign,success,fail) => (
+    uni.requestPayment({
+        provider,
+        timeStamp,
+        nonceStr,
+        package: packages,
+        signType,
+        paySign,
+        success(res) {
+            success(JSON.stringify(res));
+        },
+        fail(err) {
+            fail(JSON.stringify(err))
+        }
+    });
+)
+ 
+ 
+export default wePay;

+ 34 - 0
common/share.js

@@ -0,0 +1,34 @@
+export default{
+    data(){
+        return {
+                       //设置默认的分享参数
+            share:{
+                title:'商城',
+                path:'/pages/index/index',
+                imageUrl:'',
+                desc:'',
+                content:''
+            }
+        }
+    },
+    onShareAppMessage(res) {
+        return {
+            title:this.share.title,
+            path:this.share.path,
+            imageUrl:this.share.imageUrl,
+            desc:this.share.desc,
+            content:this.share.content,
+            success(res){
+                uni.showToast({
+                    title:'分享成功'
+                })
+            },
+            fail(res){
+                uni.showToast({
+                    title:'分享失败',
+                    icon:'none'
+                })
+            }
+        }
+    }
+}

+ 30 - 0
common/util.js

@@ -0,0 +1,30 @@
+const requst = (opt) => {
+    opt = opt || {};
+    opt.url = opt.url || '';
+    opt.data = opt.data || null;
+    opt.method = opt.method || 'POST';
+    opt.header = opt.header || {
+        "Content-Type": "application/x-www-form-urlencoded"
+    };
+    opt.success = opt.success || function () {};
+
+    uni.request({
+        url: opt.url,
+        data: opt.data,
+        method: opt.method,
+        header: opt.header,
+        dataType: 'json',
+        success: function (res) {
+            opt.success(res);
+        },
+        fail: function () {
+            uni.showToast({
+                title: '请稍后重试'
+            });
+        }
+    })
+}
+
+export {
+    requst
+}

+ 113 - 0
components/drawer/drawer.vue

@@ -0,0 +1,113 @@
+<template>
+	<view :class="['side-nav',{'right':direction == 'right'},{'left':direction == 'left'},{'show':show}]">
+		<view class="item">
+			<view class="body">
+				<slot />
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import {
+		mapState
+	} from 'vuex';
+	export default {
+		props: {
+			show: {
+				type: Boolean,
+				default: false
+			},
+			direction: {
+				type: String,
+				default: 'right'
+			}
+		},
+		data() {
+			return {}
+		},
+		onLoad() {
+
+		},
+		methods: {
+			month() {
+				this.$emit('month');
+			},
+		}
+
+	}
+</script>
+
+<style lang="less">
+	/* flex */
+	.flex-box {
+		display: flex;
+		flex-wrap: wrap;
+	}
+
+	.flex-box>.item-2 {
+		flex: 0 0 50%;
+	}
+
+	.side-nav {
+		position: fixed;
+		z-index: 99;
+		right: 0;
+		top: 180upx;
+		height: 400upx;
+		width: 300upx;
+		overflow-x: hidden;
+		transition: 0.5s;
+		display: flex;
+		flex-direction: column;
+		color: #fff;
+		&.right{
+			transform: translateX(600upx);
+			&.show {
+				transform: translateX(0);
+			}
+			
+		}
+		
+		&.left{
+			transform: translateX(-600upx);
+			&.show {
+				transform: translateX(300upx);
+			}
+		}
+
+	
+		.body {
+			height: 100%;
+			overflow-y: auto;
+		}
+
+
+		.item:nth-of-type(1) {
+			height: 0;
+		}
+
+		.item:nth-of-type(1) {
+			flex: 1 0 auto;
+		}
+
+
+		.action-area {
+			.item-2 {
+				button {
+					border-radius: 0;
+					line-height: 96upx;
+				}
+			}
+		}
+	}
+
+
+
+	/* #ifdef H5 */
+	.side-nav {
+		box-sizing: border-box;
+	}
+
+	/* #endif */
+</style>

+ 610 - 0
components/gmy-img-cropper/gmy-img-cropper.vue

@@ -0,0 +1,610 @@
+<template>
+	<view class="container" v-show="isShow">
+		<view class="page-body uni-content-info">
+			<view class='cropper-content'>
+				<view v-if="isShowImg" class="uni-corpper" :style="'width:'+cropperInitW+'px;height:'+cropperInitH+'px;background:#000'">
+					<view class="uni-corpper-content" :style="'width:'+cropperW+'px;height:'+cropperH+'px;left:'+cropperL+'px;top:'+cropperT+'px'">
+						<image :src="imageSrc" :style="'width:'+cropperW+'px;height:'+cropperH+'px'"></image>
+						<view class="uni-corpper-crop-box" @touchstart.stop="contentStartMove" @touchmove.stop="contentMoveing" @touchend.stop="contentTouchEnd"
+						    :style="'left:'+cutL+'px;top:'+cutT+'px;right:'+cutR+'px;bottom:'+cutB+'px'">
+							<view class="uni-cropper-view-box">
+								<view class="uni-cropper-dashed-h"></view>
+								<view class="uni-cropper-dashed-v"></view>
+								<view class="uni-cropper-line-t" data-drag="top" @touchstart.stop="dragStart" @touchmove.stop="dragMove"></view>
+								<view class="uni-cropper-line-r" data-drag="right" @touchstart.stop="dragStart" @touchmove.stop="dragMove"></view>
+								<view class="uni-cropper-line-b" data-drag="bottom" @touchstart.stop="dragStart" @touchmove.stop="dragMove"></view>
+								<view class="uni-cropper-line-l" data-drag="left" @touchstart.stop="dragStart" @touchmove.stop="dragMove"></view>
+								<view class="uni-cropper-point point-t" data-drag="top" @touchstart.stop="dragStart" @touchmove.stop="dragMove"></view>
+								<view class="uni-cropper-point point-tr" data-drag="topTight"></view>
+								<view class="uni-cropper-point point-r" data-drag="right" @touchstart.stop="dragStart" @touchmove.stop="dragMove"></view>
+								<view class="uni-cropper-point point-rb" data-drag="rightBottom" @touchstart.stop="dragStart" @touchmove.stop="dragMove"></view>
+								<view class="uni-cropper-point point-b" data-drag="bottom" @touchstart.stop="dragStart" @touchmove.stop="dragMove" @touchend.stop="dragEnd"></view>
+								<view class="uni-cropper-point point-bl" data-drag="bottomLeft"></view>
+								<view class="uni-cropper-point point-l" data-drag="left" @touchstart.stop="dragStart" @touchmove.stop="dragMove"></view>
+								<view class="uni-cropper-point point-lt" data-drag="leftTop"></view>
+							</view>
+						</view>
+					</view>
+				</view>
+			</view>
+			<view class='cropper-config'>
+				<view class="button-box" >
+					<button type="warn" @click="getImage" > 重选 </button>
+					<button type="warn" @click="getImageInfo"> 预览 </button>
+					<button type="warn" @click="finish"> 完成 </button>
+				</view>
+				
+			</view>
+			<canvas canvas-id="myCanvas" :style="'position:absolute;border: 2px solid red; width:'+imageW+'px;height:'+imageH+'px;top:-9999px;left:-9999px;'"></canvas>
+		</view>
+		<page-foot :name="name"></page-foot>
+	</view>
+</template>
+
+<script>
+	let sysInfo = uni.getSystemInfoSync();
+	let SCREEN_WIDTH = sysInfo.screenWidth;
+	let SCREEN_HEIGHT = sysInfo.windowHeight+40;
+	let PAGE_X, // 手按下的x位置
+		PAGE_Y, // 手按下y的位置 
+		PR = sysInfo.pixelRatio, // dpi
+		T_PAGE_X, // 手移动的时候x的位置
+		T_PAGE_Y, // 手移动的时候Y的位置
+		CUT_L, // 初始化拖拽元素的left值
+		CUT_T, // 初始化拖拽元素的top值
+		CUT_R, // 初始化拖拽元素的
+		CUT_B, // 初始化拖拽元素的
+		CUT_W, // 初始化拖拽元素的宽度
+		CUT_H, //  初始化拖拽元素的高度
+		IMG_RATIO, // 图片比例
+		IMG_REAL_W, // 图片实际的宽度
+		IMG_REAL_H, // 图片实际的高度
+		DRAFG_MOVE_RATIO = 1, //移动时候的比例,
+		INIT_DRAG_POSITION = 100, // 初始化屏幕宽度和裁剪区域的宽度之差,用于设置初始化裁剪的宽度
+		DRAW_IMAGE_W = sysInfo.screenWidth // 设置生成的图片宽度
+	export default {
+		/**
+		 * 页面的初始数据
+		 */
+		data() {
+			return {
+				isShow:false,
+				name:'杨大宝',
+				imageSrc: 'https://img-cdn-qiniu.dcloud.net.cn/demo_crop.jpg',
+				isShowImg: false,
+				// 初始化的宽高
+				cropperInitW: SCREEN_WIDTH,
+				cropperInitH: SCREEN_HEIGHT,
+				// 动态的宽高
+				cropperW: SCREEN_WIDTH,
+				cropperH: SCREEN_HEIGHT,
+				// 动态的left top值
+				cropperL: 0,
+				cropperT: 0,
+				transL: 0,
+				transT: 0,
+				// 图片缩放值
+				scaleP: 0,
+				imageW: 0,
+				imageH: 0,
+				// 裁剪框 宽高
+				cutL: 0,
+				cutT: 0,
+				cutB: SCREEN_WIDTH,
+				cutR: '100%',
+				qualityWidth: DRAW_IMAGE_W,
+				innerAspectRadio: DRAFG_MOVE_RATIO
+			}
+		},
+		methods: {
+			setData: function (obj) {
+				let that = this;
+				Object.keys(obj).forEach(function (key) {
+					that.$set(that.$data, key, obj[key])
+				});
+			},
+			getImage: function () {
+				var _this = this
+				uni.chooseImage({
+					success: function (res) {
+						_this.setData({
+							imageSrc: res.tempFilePaths[0],
+						})
+						_this.loadImage();
+						_this.isShow = true;
+					},
+				})
+			},
+			loadImage: function () {
+				var _this = this
+				uni.showLoading({
+					title: '图片加载中...',
+				})
+				uni.getImageInfo({
+					src: _this.imageSrc,
+					success: function success(res) {
+						let imgH = res.height;
+						let imgW = res.width;
+						let IMG_SCR_H_R = SCREEN_HEIGHT / imgH;
+						let IMG_SCR_W_R = SCREEN_WIDTH / imgW;
+						IMG_RATIO = imgW / imgH;
+						if(IMG_RATIO<1 && IMG_SCR_H_R<IMG_SCR_W_R ){ //图片更高一些
+							IMG_REAL_H = SCREEN_HEIGHT-40;
+							IMG_REAL_W = SCREEN_HEIGHT * IMG_RATIO>(SCREEN_WIDTH-40)? SCREEN_WIDTH-40 : SCREEN_HEIGHT * IMG_RATIO;
+						}
+						else{
+							IMG_REAL_W = SCREEN_WIDTH-40;
+							IMG_REAL_H = IMG_REAL_W / IMG_RATIO;
+						}
+						let cutT = 0;
+						let cutB = 0;
+						let cutL = 0;
+						let cutR = 0;
+						_this.setData({
+							cropperW: IMG_REAL_W,
+							cropperH: IMG_REAL_H,
+							// 初始化left right
+							cropperL: Math.ceil((SCREEN_WIDTH - IMG_REAL_W) / 2),
+							cropperT: Math.ceil((SCREEN_HEIGHT - IMG_REAL_H) / 2),
+							cutL: cutL,
+							cutT: cutT,
+							cutR: cutR,
+							cutB: cutB,
+							// 图片缩放值
+							imageW: IMG_REAL_W,
+							imageH: IMG_REAL_H,
+							scaleP: IMG_REAL_W / SCREEN_WIDTH,
+							qualityWidth: DRAW_IMAGE_W,
+							innerAspectRadio: IMG_RATIO
+						})
+						_this.setData({
+							isShowImg: true
+						})
+						uni.hideLoading()
+					}
+				})
+			},
+			// 拖动时候触发的touchStart事件
+			contentStartMove(e) {
+				PAGE_X = e.touches[0].pageX
+				PAGE_Y = e.touches[0].pageY
+			},
+			// 拖动时候触发的touchMove事件
+			contentMoveing(e) {
+				var _this = this
+				var dragLengthX = (PAGE_X - e.touches[0].pageX) * DRAFG_MOVE_RATIO
+				var dragLengthY = (PAGE_Y - e.touches[0].pageY) * DRAFG_MOVE_RATIO
+				// 左移
+				if (dragLengthX > 0) {
+					if (this.cutL - dragLengthX < 0) dragLengthX = this.cutL
+				} else {
+					if (this.cutR + dragLengthX < 0) dragLengthX = -this.cutR
+				}
+				if (dragLengthY > 0) {
+					if (this.cutT - dragLengthY < 0) dragLengthY = this.cutT
+				} else {
+					if (this.cutB + dragLengthY < 0) dragLengthY = -this.cutB
+				}
+				this.setData({
+					cutL: this.cutL - dragLengthX,
+					cutT: this.cutT - dragLengthY,
+					cutR: this.cutR + dragLengthX,
+					cutB: this.cutB + dragLengthY
+				})
+				PAGE_X = e.touches[0].pageX
+				PAGE_Y = e.touches[0].pageY
+			},
+			contentTouchEnd() {
+			},
+			// 获取图片
+			getImageInfo() {
+				var _this = this;
+				uni.showLoading({
+					title: '图片生成中...',
+				});
+				// 将图片写入画布
+				const ctx = uni.createCanvasContext('myCanvas',_this);
+				ctx.drawImage(_this.imageSrc, 0, 0, IMG_REAL_W, IMG_REAL_H);
+				ctx.draw(true, () => {
+					// 获取画布要裁剪的位置和宽度   均为百分比 * 画布中图片的宽度    保证了在微信小程序中裁剪的图片模糊  位置不对的问题 canvasT = (_this.cutT / _this.cropperH) * (_this.imageH / pixelRatio)
+					var canvasW = ((_this.cropperW - _this.cutL - _this.cutR) / _this.cropperW) * IMG_REAL_W;
+					var canvasH = ((_this.cropperH - _this.cutT - _this.cutB) / _this.cropperH) * IMG_REAL_H;
+					var canvasL = (_this.cutL / _this.cropperW) * IMG_REAL_W;
+					var canvasT = (_this.cutT / _this.cropperH) * IMG_REAL_H;
+					uni.canvasToTempFilePath({
+						x: canvasL,
+						y: canvasT,
+						width: canvasW,
+						height: canvasH,
+						destWidth: canvasW,
+						destHeight: canvasH,
+						quality: 0.5,
+						canvasId: 'myCanvas',
+						success: function (res) {
+							uni.hideLoading()
+							// 成功获得地址的地方
+							uni.previewImage({
+								current: '', // 当前显示图片的http链接
+								urls: [res.tempFilePath] // 需要预览的图片http链接列表
+							})
+						}
+					},_this);
+				});
+			},
+			finish:function(){
+				var _this = this;
+				uni.showLoading({
+					title: '图片生成中...',
+				});
+				// 将图片写入画布
+				const ctx = uni.createCanvasContext('myCanvas',_this);
+				ctx.drawImage(_this.imageSrc, 0, 0, IMG_REAL_W, IMG_REAL_H);
+				ctx.draw(true, () => {
+					// 获取画布要裁剪的位置和宽度   均为百分比 * 画布中图片的宽度    保证了在微信小程序中裁剪的图片模糊  位置不对的问题 canvasT = (_this.cutT / _this.cropperH) * (_this.imageH / pixelRatio)
+					var canvasW = ((_this.cropperW - _this.cutL - _this.cutR) / _this.cropperW) * IMG_REAL_W;
+					var canvasH = ((_this.cropperH - _this.cutT - _this.cutB) / _this.cropperH) * IMG_REAL_H;
+					var canvasL = (_this.cutL / _this.cropperW) * IMG_REAL_W;
+					var canvasT = (_this.cutT / _this.cropperH) * IMG_REAL_H;
+					uni.canvasToTempFilePath({
+						x: canvasL,
+						y: canvasT,
+						width: canvasW,
+						height: canvasH,
+						destWidth: canvasW,
+						destHeight: canvasH,
+						quality: 0.5,
+						canvasId: 'myCanvas',
+						success: function (res) {
+							uni.hideLoading()
+							// 成功获得地址的地方
+							_this.$emit('getImg',res.tempFilePath);
+							_this.isShow = false;
+						}
+					},_this);
+				});
+			},
+			// 设置大小的时候触发的touchStart事件
+			dragStart(e) {
+				T_PAGE_X = e.touches[0].pageX
+				T_PAGE_Y = e.touches[0].pageY
+				CUT_L = this.cutL
+				CUT_R = this.cutR
+				CUT_B = this.cutB
+				CUT_T = this.cutT
+			},
+			// 设置大小的时候触发的touchMove事件
+			dragMove(e) {
+				var _this = this
+				var dragType = e.target.dataset.drag
+				switch (dragType) {
+					case 'right':
+						var dragLength = (T_PAGE_X - e.touches[0].pageX) * DRAFG_MOVE_RATIO
+						if (CUT_R + dragLength < 0) dragLength = -CUT_R
+						this.setData({
+							cutR: CUT_R + dragLength
+						})
+						break;
+					case 'left':
+						var dragLength = (T_PAGE_X - e.touches[0].pageX) * DRAFG_MOVE_RATIO
+						if (CUT_L - dragLength < 0) dragLength = CUT_L
+						if ((CUT_L - dragLength) > (this.cropperW - this.cutR)) dragLength = CUT_L - (this.cropperW - this.cutR)
+						this.setData({
+							cutL: CUT_L - dragLength
+						})
+						break;
+					case 'top':
+						var dragLength = (T_PAGE_Y - e.touches[0].pageY) * DRAFG_MOVE_RATIO
+						if (CUT_T - dragLength < 0) dragLength = CUT_T
+						if ((CUT_T - dragLength) > (this.cropperH - this.cutB)) dragLength = CUT_T - (this.cropperH - this.cutB)
+						this.setData({
+							cutT: CUT_T - dragLength
+						})
+						break;
+					case 'bottom':
+						var dragLength = (T_PAGE_Y - e.touches[0].pageY) * DRAFG_MOVE_RATIO
+						if (CUT_B + dragLength < 0) dragLength = -CUT_B
+						this.setData({
+							cutB: CUT_B + dragLength
+						})
+						break;
+					case 'rightBottom':
+						var dragLengthX = (T_PAGE_X - e.touches[0].pageX) * DRAFG_MOVE_RATIO
+						var dragLengthY = (T_PAGE_Y - e.touches[0].pageY) * DRAFG_MOVE_RATIO
+						if (CUT_B + dragLengthY < 0) dragLengthY = -CUT_B
+						if (CUT_R + dragLengthX < 0) dragLengthX = -CUT_R
+						let cutB = CUT_B + dragLengthY;
+						let cutR = CUT_R + dragLengthX;
+						this.setData({
+							cutB: cutB,
+							cutR: cutR
+						})
+						break;
+					default:
+						break;
+				}
+			}
+		}
+	}
+</script>
+
+<style>
+	/* pages/uni-cropper/index.wxss */
+	.uni-content-info {
+		/* position: fixed;
+		top: 0;
+		left: 0;
+		right: 0;
+		bottom: 0;
+		display: block;
+		align-items: center;
+		flex-direction: column; */
+	}
+	.cropper-config {
+		width: 90%;
+		position: fixed;
+		bottom: 10upx;
+		z-index: 999;
+		padding: 20upx 40upx;
+	}
+	.button-box{
+		width: 100%;
+		display: flex;
+		flex-direction: row;
+		justify-content:space-between;
+	}
+	.button-box button{
+		height: 80upx;
+	}
+	.cropper-content {
+		min-height: 750upx;
+		width: 100%;
+	}
+	.uni-corpper {
+		margin: 4px auto;
+		position: relative;
+		overflow: hidden;
+		-webkit-user-select: none;
+		-moz-user-select: none;
+		-ms-user-select: none;
+		user-select: none;
+		-webkit-tap-highlight-color: transparent;
+		-webkit-touch-callout: none;
+		box-sizing: border-box;
+	}
+	.uni-corpper-content {
+		position: relative;
+	}
+	.uni-corpper-content image {
+		display: block;
+		width: 100%;
+		min-width: 0 !important;
+		max-width: none !important;
+		height: 100%;
+		min-height: 0 !important;
+		max-height: none !important;
+		image-orientation: 0deg !important;
+		margin: 0 auto;
+	}
+	/* 移动图片效果 */
+	.uni-cropper-drag-box {
+		position: absolute;
+		top: 0;
+		right: 0;
+		bottom: 0;
+		left: 0;
+		cursor: move;
+		background: rgba(0, 0, 0, 0.6);
+		z-index: 1;
+	}
+	/* 内部的信息 */
+	.uni-corpper-crop-box {
+		position: absolute;
+		background: rgba(255, 255, 255, 0.3);
+		z-index: 2;
+	}
+	.uni-corpper-crop-box .uni-cropper-view-box {
+		position: relative;
+		display: block;
+		width: 100%;
+		height: 100%;
+		overflow: visible;
+		outline: 5upx solid white;
+		outline-color: rgba(255,255 255, 1)
+	}
+	/* 横向虚线 */
+	.uni-cropper-dashed-h {
+		position: absolute;
+		top: 33.33333333%;
+		left: 0;
+		width: 100%;
+		height: 33.33333333%;
+		border-top: 1upx dashed rgba(255, 255, 255, 0.5);
+		border-bottom: 1upx dashed rgba(255, 255, 255, 0.5);
+	}
+	/* 纵向虚线 */
+	.uni-cropper-dashed-v {
+		position: absolute;
+		left: 33.33333333%;
+		top: 0;
+		width: 33.33333333%;
+		height: 100%;
+		border-left: 1upx dashed rgba(255, 255, 255, 0.5);
+		border-right: 1upx dashed rgba(255, 255, 255, 0.5);
+	}
+	/* 四个方向的线  为了之后的拖动事件*/
+	.uni-cropper-line-t {
+		position: absolute;
+		display: block;
+		width: 100%;
+		background-color: white;
+		top: 0;
+		left: 0;
+		height: 3upx;
+		opacity: 0.1;
+		cursor: n-resize;
+	}
+	.uni-cropper-line-t::before {
+		content: '';
+		position: absolute;
+		top: 50%;
+		right: 0upx;
+		width: 100%;
+		-webkit-transform: translate3d(0, -50%, 0);
+		transform: translate3d(0, -50%, 0);
+		bottom: 0;
+		height: 41upx;
+		background: transparent;
+		z-index: 11;
+	}
+	.uni-cropper-line-r {
+		position: absolute;
+		display: block;
+		background-color: white;
+		top: 0;
+		right: 0upx;
+		width: 3upx;
+		opacity: 0.1;
+		height: 100%;
+		cursor: e-resize;
+	}
+	.uni-cropper-line-r::before {
+		content: '';
+		position: absolute;
+		top: 0;
+		left: 50%;
+		width: 41upx;
+		-webkit-transform: translate3d(-50%, 0, 0);
+		transform: translate3d(-50%, 0, 0);
+		bottom: 0;
+		height: 100%;
+		background: transparent;
+		z-index: 11;
+	}
+	.uni-cropper-line-b {
+		position: absolute;
+		display: block;
+		width: 100%;
+		background-color: white;
+		bottom: 0;
+		left: 0;
+		height: 3upx;
+		opacity: 0.1;
+		cursor: s-resize;
+	}
+	.uni-cropper-line-b::before {
+		content: '';
+		position: absolute;
+		top: 50%;
+		right: 0upx;
+		width: 100%;
+		-webkit-transform: translate3d(0, -50%, 0);
+		transform: translate3d(0, -50%, 0);
+		bottom: 0;
+		height: 41upx;
+		background: transparent;
+		z-index: 11;
+	}
+	.uni-cropper-line-l {
+		position: absolute;
+		display: block;
+		background-color: white;
+		top: 0;
+		left: 0;
+		width: 3upx;
+		opacity: 0.1;
+		height: 100%;
+		cursor: w-resize;
+	}
+	.uni-cropper-line-l::before {
+		content: '';
+		position: absolute;
+		top: 0;
+		left: 50%;
+		width: 41upx;
+		-webkit-transform: translate3d(-50%, 0, 0);
+		transform: translate3d(-50%, 0, 0);
+		bottom: 0;
+		height: 100%;
+		background: transparent;
+		z-index: 11;
+	}
+	.uni-cropper-point {
+		width: 5upx;
+		height: 5upx;
+		background-color: white;
+		opacity: .75;
+		position: absolute;
+		z-index: 3;
+	}
+	.point-t {
+		top: -3upx;
+		left: 50%;
+		margin-left: -3upx;
+		cursor: n-resize;
+	}
+	.point-tr {
+		top: -3upx;
+		left: 100%;
+		margin-left: -3upx;
+		cursor: n-resize;
+	}
+	.point-r {
+		top: 50%;
+		left: 100%;
+		margin-left: -3upx;
+		margin-top: -3upx;
+		cursor: n-resize;
+	}
+	.point-rb {
+		left: 100%;
+		top: 100%;
+		-webkit-transform: translate3d(-50%, -50%, 0);
+		transform: translate3d(-50%, -50%, 0);
+		cursor: n-resize;
+		width: 36upx;
+		height: 36upx;
+		background-color: white;
+		position: absolute;
+		z-index: 1112;
+		opacity: 1;
+	}
+	.point-b {
+		left: 50%;
+		top: 100%;
+		margin-left: -3upx;
+		margin-top: -3upx;
+		cursor: n-resize;
+	}
+	.point-bl {
+		left: 0%;
+		top: 100%;
+		margin-left: -3upx;
+		margin-top: -3upx;
+		cursor: n-resize;
+	}
+	.point-l {
+		left: 0%;
+		top: 50%;
+		margin-left: -3upx;
+		margin-top: -3upx;
+		cursor: n-resize;
+	}
+	.point-lt {
+		left: 0%;
+		top: 0%;
+		margin-left: -3upx;
+		margin-top: -3upx;
+		cursor: n-resize;
+	}
+	/* 裁剪框预览内容 */
+	.uni-cropper-viewer {
+		position: relative;
+		width: 100%;
+		height: 100%;
+		overflow: hidden;
+	}
+	.uni-cropper-viewer image {
+		position: absolute;
+		z-index: 2;
+	}
+</style>

+ 14 - 0
components/gmy-img-cropper/package.json

@@ -0,0 +1,14 @@
+{
+  "name": "gmy-img-cropper",
+  "version": "1.1.0",
+  "description": "img-cropper cropper",
+  "main": "index.js",
+  "scripts": {
+    "test": "echo \"Error: no test specified\" && exit 1"
+  },
+  "keywords": [
+    "cropper"
+  ],
+  "author": "day_up",
+  "license": "ISC"
+}

+ 75 - 0
components/gmy-img-cropper/readme.md

@@ -0,0 +1,75 @@
+# gmy-img-cropper
+## NPM安装: npm i  gmy-img-cropper
+## 使用说明
+### 页面引入:import gmyList from "../../components/gmy-img-cropper/gmy-img-cropper.vue"
+### 页面使用:<gmy-img-cropper ref="gmyImgCropper" @getImg = "getImg"></gmy-img-cropper>
+### 参数说明
+* ref-->绑定组件
+* getImg-->获取组件截取的图片路径
+## 使用说明
+* 页面通过按钮等调用事件:
+chooseImg:function(){    //将chooseImg事件绑定到你的按钮或图标上
+	this.$refs.gmyImgCropper.getImage();
+	this.imgCropperShow = true;  //该标志用于控制页面在组件显示时主页面隐藏
+},
+* 备注:
+该组件会占据整个屏幕,因此建议在调用截图组件时隐藏主页面内容,待截取成功后在getImg中再将主页面显示出来
+
+##使用案例
+<template>
+	<view>
+		<view v-show="!imgCropperShow">
+			<button type="warn" @click="chooseImg" >选择图片</button>
+		</view>
+		<gmy-img-cropper ref="gmyImgCropper" @getImg = "getImg"></gmy-img-cropper>
+  	</view>
+</template>
+
+<script>
+	
+	class List {
+		constructor(code) {
+		    this.isShow = false;
+			this.code = code;
+		}
+		showCode(){
+			this.isShow = !this.isShow;
+		}
+	}
+	
+	import gmyImgCropper from '@/components/gmy-img-cropper/gmy-img-cropper.vue'
+	import marked from 'marked'
+	import uParse from '@/components/gaoyia-parse/parse.vue'
+	export default {
+		components:{
+			uParse,
+			gmyImgCropper
+		},
+		data() {
+			return {
+				imgSrc:'123',
+				imgCropperShow:false
+			}
+		},
+		methods: {
+			chooseImg:function(){
+				this.$refs.gmyImgCropper.getImage();
+				this.imgCropperShow = true;
+			},
+			getImg:function(e){
+				console.log("父页面拿到了图片",e);
+				this.imgCropperShow = false;
+			},
+			show:function(list){
+				this[list].showCode();
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	body{
+		background-color: $bgc;
+	}
+</style>
+

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 360 - 0
components/html2canvas.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 19 - 0
components/html2canvas.min.js


+ 150 - 0
components/ithui-bullet/ithui-bullet-one.vue

@@ -0,0 +1,150 @@
+/**
+* ITHUI®
+* ITHUI-Bullet 适用于升级&通知、任务说明、活动规则、置顶公告弹框,兼容小程序、APP、H5。
+* Copyright (c) 2023 ITHANG All rights reserved.
+* Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+* 复制使用请保留本段注释,感谢支持开源!
+*
+* 作者主页
+* https://ithang.cn/
+*
+* 开源地址
+* https://github.com/ithang-cn/ITHUI-Bullet
+* https://gitee.com/ithang-cn/ITHUI-Bullet
+*
+*/
+
+<template>
+	<view class="ithui-bullet">
+		<u-popup @click="handleCloseOnClickOverlay" :show="value" :zIndex="zIndex" :overlayOpacity="opacity"
+			:closeOnClickOverlay="closeOnClickOverlay" mode="center" bgColor="transparent">
+			<view class="bullet-box" :style="{borderColor,backgroundColor:bgColor}">
+				<view class="top">
+					<view class="title" :style="{color: titleColor}">{{title}}</view>
+				</view>
+				<scroll-view :scroll-top="scrollTop" :scroll-y="true" class="scroll-Y" @scroll="scroll">
+					<view class="content" :style="{color: color}">
+						<rich-text :nodes="content"></rich-text>
+					</view>
+				</scroll-view>
+			</view>
+			<view class="bullet-close">
+				<u-icon @click="handleClose" name="close-circle" color="#fff" size="34"></u-icon>
+			</view>
+		</u-popup>
+	</view>
+</template>
+
+<script>
+	export default {
+		name: "ithui-bullet-two",
+		props: {
+			value: {
+				type: Boolean,
+				default: false
+			},
+			zIndex: {
+				type: String | Number,
+				default: 10086
+			},
+			opacity: {
+				type: String | Number,
+				default: 0.5
+			},
+			closeOnClickOverlay: {
+				type: Boolean,
+				default: false
+			},
+			title: {
+				type: String,
+				default: '标题'
+			},
+			color: {
+				type: String,
+				default: '#333'
+			},
+			titleColor: {
+				type: String,
+				default: '#7d4802'
+			},
+			borderColor: {
+				type: String,
+				default: '#fad489'
+			},
+			bgColor: {
+				type: String,
+				default: '#fffdf1'
+			},
+			content: {
+				type: String,
+				default: ''
+			},
+		},
+		data() {
+			return {
+				scrollTop: 0,
+				old: {
+					scrollTop: 0
+				}
+			};
+		},
+		methods: {
+			handleOK() {
+				this.$emit('input', false);
+				this.$emit('confirm');
+			},
+			handleCloseOnClickOverlay() {
+				if (this.closeOnClickOverlay) {
+					this.$emit('input', false);
+					this.$emit('close');
+				}
+			},
+			handleClose() {
+				this.$emit('input', false);
+				this.$emit('cancel');
+			},
+			scroll(e) {
+				this.old.scrollTop = e.detail.scrollTop
+			},
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.ithui {
+		&-bullet {
+			.bullet {
+				&-box {
+					background-color: #fff;
+					width: 620rpx;
+					height: 60vh;
+					border-radius: 20rpx;
+					border-width: 10rpx;
+					border-style: solid;
+
+					.top {
+						font-size: 38rpx;
+						text-align: center;
+						font-weight: bold;
+						margin-top: 60rpx;
+					}
+
+					.scroll-Y {
+						margin-top: 20rpx;
+						height: 48vh;
+
+						.content {
+							margin: 0 30rpx;
+						}
+					}
+				}
+
+				&-close {
+					margin-top: 20rpx;
+					display: flex;
+					justify-content: center;
+				}
+			}
+		}
+	}
+</style>

+ 200 - 0
components/ithui-bullet/ithui-bullet-two.vue

@@ -0,0 +1,200 @@
+/**
+* ITHUI®
+* ITHUI-Bullet 适用于升级&通知、任务说明、活动规则、置顶公告弹框,兼容小程序、APP、H5。
+* Copyright (c) 2023 ITHANG All rights reserved.
+* Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+* 复制使用请保留本段注释,感谢支持开源!
+*
+* 作者主页
+* https://ithang.cn/
+*
+* 开源地址
+* https://github.com/ithang-cn/ITHUI-Bullet
+* https://gitee.com/ithang-cn/ITHUI-Bullet
+*
+*/
+
+<template>
+	<view class="ithui-bullet">
+		<u-popup @click="handleCloseOnClickOverlay" :show="value" :zIndex="zIndex" :overlayOpacity="opacity"
+			:closeOnClickOverlay="closeOnClickOverlay" mode="center" bgColor="transparent">
+			<view class="bullet-box">
+				<view class="top">
+					<view class="bg">
+						<image src="@/static/top-bg.png" />
+					</view>
+					<view class="sub">
+						<view class="icon">
+							<image src="@/static/notice.png" />
+						</view>
+						<view class="title">{{title}}</view>
+					</view>
+				</view>
+				<scroll-view :scroll-top="scrollTop" :scroll-y="true" class="scroll-Y" @scroll="scroll">
+					<view class="content">
+						<rich-text class="rich-val" :nodes="content"></rich-text>
+					</view>
+				</scroll-view>
+				<view class="button" v-if="showButton">
+					<u-button @click="handleOK" shape="circle" :color="buttonColor" :text="buttonText"></u-button>
+				</view>
+			</view>
+			<view class="bullet-close">
+				<u-icon @click="handleClose" name="close-circle" color="#fff" size="34"></u-icon>
+			</view>
+		</u-popup>
+	</view>
+</template>
+
+<script>
+	export default {
+		name: "ithui-bullet-two",
+		props: {
+			value: {
+				type: Boolean,
+				default: false
+			},
+			zIndex: {
+				type: String | Number,
+				default: 10086
+			},
+			opacity: {
+				type: String | Number,
+				default: 0.5
+			},
+			closeOnClickOverlay: {
+				type: Boolean,
+				default: false
+			},
+			icon: {
+				type: String,
+				default: ''
+			},
+			title: {
+				type: String,
+				default: '标题'
+			},
+			content: {
+				type: String,
+				default: ''
+			},
+			buttonColor: {
+				type: String,
+				default: 'linear-gradient(to right, #0db5f0, #2d95f1)'
+			},
+			buttonText: {
+				type: String,
+				default: ''
+			},
+			showButton: {
+				type: Boolean,
+				default: true
+			}
+		},
+		data() {
+			return {
+				scrollTop: 0,
+				old: {
+					scrollTop: 0
+				}
+			};
+		},
+		methods: {
+			handleOK() {
+				this.$emit('input', false);
+				this.$emit('confirm');
+			},
+			handleCloseOnClickOverlay() {
+				if (this.closeOnClickOverlay) {
+					this.$emit('input', false);
+					this.$emit('close');
+				}
+			},
+			handleClose() {
+				this.$emit('input', false);
+				this.$emit('cancel');
+			},
+			scroll(e) {
+				this.old.scrollTop = e.detail.scrollTop
+			},
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.ithui {
+		&-bullet {
+			.bullet {
+				&-box {
+					background-color: #fff;
+					width: 640rpx;
+					height: 65vh;
+					border-radius: 20rpx;
+
+					.top {
+						width: 640rpx;
+						height: 260rpx;
+
+						.bg {
+							width: 640rpx;
+							height: 260rpx;
+
+							image {
+								width: 100%;
+								height: 100%;
+								border-radius: 20rpx 20rpx 0 0;
+							}
+						}
+
+						.sub {
+							width: 640rpx;
+							height: 260rpx;
+							position: relative;
+							top: -260rpx;
+							text-align: center;
+							color: #fff;
+							font-size: 36rpx;
+							letter-spacing: 5rpx;
+
+							.icon {
+								margin-top: 20rpx;
+								margin-bottom: 10rpx;
+								border-radius: 120rpx;
+
+								image {
+									width: 120rpx;
+									height: 120rpx;
+									border-radius: 120rpx;
+								}
+							}
+						}
+					}
+
+
+					.scroll-Y {
+						margin-top: 15rpx;
+						height: 35vh;
+
+						.content {
+							margin: 0 30rpx;
+						}
+					}
+
+					.button {
+						margin: 40rpx 100rpx;
+					}
+				}
+
+				&-close {
+					margin-top: 20rpx;
+					display: flex;
+					justify-content: center;
+				}
+			}
+		}
+	}
+	.rich-val{
+		word-break: break-all;
+		white-space: pre-line;
+	}
+</style>

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 1 - 0
components/jquery-1.8.3.min.js


+ 104 - 0
components/pickerAddress.vue

@@ -0,0 +1,104 @@
+<template>
+    <picker @change="bindPickerChange" @columnchange="columnchange" :range="array" range-key="name" :value="value" mode="multiSelector">
+        <slot></slot>
+    </picker>
+</template>
+
+<script>
+	import AllAddress from '../common/data.js'
+	
+    let selectVal = ['','',''] 
+    
+    export default {
+        data() {
+            return{
+                value: [0,0,0],
+                array: [],
+                index: 0
+            }
+        },
+        created() {
+            this.initSelect()
+        },
+        methods:{
+            // 初始化地址选项
+            initSelect() {
+                this.updateSourceDate() // 更新源数据
+                .updateAddressDate() // 更新结果数据
+                .$forceUpdate()  // 触发双向绑定
+            },
+            // 地址控件改变控件
+            columnchange(d) {
+                this.updateSelectIndex(d.detail.column, d.detail.value) // 更新选择索引
+                .updateSourceDate() // 更新源数据
+                .updateAddressDate() // 更新结果数据
+                .$forceUpdate()  // 触发双向绑定
+            },
+            
+            /**
+             * 更新源数据
+             * */
+            updateSourceDate() {
+                this.array = []
+                this.array[0] = AllAddress.map(obj => {
+                    return {
+                        name: obj.name
+                    }
+                })
+                this.array[1] = AllAddress[this.value[0]].city.map(obj => {
+                    return {
+                        name: obj.name
+                    }
+                })
+                this.array[2] = AllAddress[this.value[0]].city[this.value[1]].area.map(obj => { 
+                    return {
+                        name: obj
+                    }
+                })
+                return this
+            },
+            
+            /**
+             * 更新索引
+             * */
+            updateSelectIndex(column, value){
+                let arr = JSON.parse(JSON.stringify(this.value)) 
+                arr[column] = value
+                if(column === 0 ) {
+                    arr[1] = 0
+                    arr[2] = 0
+                }
+                if(column === 1 ) {
+                    arr[2] = 0
+                }
+                this.value = arr
+                return this
+            },
+            
+            /**
+             * 更新结果数据 
+             * */
+            updateAddressDate() {
+                selectVal[0] = this.array[0][this.value[0]].name
+                selectVal[1] = this.array[1][this.value[1]].name 
+                selectVal[2] = this.array[2][this.value[2]].name 
+                return this
+            },
+            
+            /**
+             * 点击确定
+             * */
+            bindPickerChange(e) {
+                this.$emit('change', {
+                    index: this.value,
+                    data: selectVal
+                })
+                return this
+            }
+            
+        }
+    }
+</script>
+
+<style>
+</style>

+ 278 - 0
components/qrcode-poster/qrcode-poster.vue

@@ -0,0 +1,278 @@
+<template>
+	<view class="content" @touchmove.stop.prevent="moveHandle" v-if="isShow" @click.stop="isShow=false">
+		<canvas @click.stop="" :style="{ width: canvasW + 'px', height: canvasH + 'px' }" :width="canvasWW + 'px'" :height="canvasHH + 'px'" canvas-id="my-canvas"></canvas>
+		<view class="save-btn-wrap savepadding">
+			<view class="save-btn" @click.stop="saveImage">保存图片</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default{
+		props:{
+			backImagUrl: {
+				type: String,
+				default: ''
+			},	headerImgUrl:{
+				type: String,
+				default: ''
+			},
+			memberName:{
+				type: String,
+				default: '会员'
+			},
+			qdImageUrl:{
+				type: String,
+				default: ''
+			},
+			codeType:{
+				type: Number,
+				default: 0
+			},
+			goodsIamgeUrl:{
+				type: String,
+				default: ''
+			},
+			goodsName:{
+				type: String,
+				default: '精品'
+			},
+			goodsPrice:{
+				type:Number,
+				default: 0.00
+			},
+			goodsYPrice:{
+				type: Number,
+				default: 0.00
+			}
+		},
+		data(){
+			return {
+				canvasW: 0,
+				canvasH: 0,
+				canvasWW: 0,
+				canvasHH: 0,
+				ctx: null,
+				isShow: false,
+				qrcode: ''
+			}
+		},
+		methods:{
+			//显示
+			showCanvas(){
+				this.isShow = true
+				this.__init()
+			},
+			//初始化画布
+			async __init(){
+				uni.showLoading({
+				    title: '加载中...',
+					mask: true
+				})
+				this.ctx = uni.createCanvasContext('my-canvas',this)
+				this.canvasW = uni.upx2px(580);
+				this.canvasH = uni.upx2px(1020);
+				this.canvasWW = this.canvasW * 2;
+				this.canvasHH = this.canvasH * 2;
+				//设置画布背景透明
+				this.ctx.setFillStyle('rgba(255, 255, 255, 1)')
+				//设置画布大小
+				this.ctx.fillRect(0,0,this.canvasW,this.canvasH)
+				// 绘制背景图片
+				let backImag = await this.getImageInfo(this.backImagUrl);
+				this.ctx.drawImage(backImag.path,0,0, this.canvasW, this.canvasH);
+				
+				// 绘制会员名
+				// this.ctx.setFontSize(15); //设置标题字体大小
+				// this.ctx.setFillStyle('#333'); //设置标题文本颜色
+				// this.ctx.fillText(this.memberName,uni.upx2px(140), uni.upx2px(160))
+				// 绘制商品图片
+				// let goodsIamge = await this.getImageInfo(this.goodsIamgeUrl);
+				// this.roundedRectangle(this.ctx, uni.upx2px(17), uni.upx2px(55), uni.upx2px(549), uni.upx2px(549),10,goodsIamge.path);
+				//绘制价格
+				// this.ctx.setFontSize(12);
+				// this.ctx.setFillStyle('#ff631d');
+				// this.ctx.fillText('¥', uni.upx2px(395), uni.upx2px(740));
+				// this.ctx.setFontSize(18);
+				// this.ctx.setFillStyle('#ff631d');
+				// this.ctx.fillText((this.goodsPrice).toFixed(2), uni.upx2px(420), uni.upx2px(740));
+				// 绘制商品名称
+				// let hW = uni.upx2px(500);
+				// this.ctx.setFontSize(12);
+				// this.ctx.setFillStyle('#757575');
+				// let sWidth = this.ctx.measureText(this.goodsName).width
+				// if (this.goodsName.length < 14) {
+				// 	this.ctx.fillText(this.goodsName, uni.upx2px(40), uni.upx2px(730));
+				// } else{
+				// 	this.ctx.fillText(this.goodsName.slice(0, 14), uni.upx2px(40), uni.upx2px(730));
+				// 	this.ctx.fillText(this.goodsName.slice(14,26) + '..', uni.upx2px(40), uni.upx2px(765));
+				// }
+				// 绘制头像
+				//let headerImg = await this.getImageInfo(this.headerImgUrl);
+				//this.drawCircular(this.ctx, headerImg.path, uni.upx2px(60), uni.upx2px(800), uni.upx2px(75), uni.upx2px(75));
+				
+				// 绘制小程序码 // 不发胖
+				if (this.codeType == 1) {
+					let qdImage = await this.getImageInfo(this.qdImageUrl);
+					this.ctx.drawImage(qdImage.path, uni.upx2px(225), uni.upx2px(785), uni.upx2px(140), uni.upx2px(140));
+				} 
+				if (this.codeType == 2 || this.codeType == 6){
+					// 绘制小程序码 // 按时吃饭
+					let qdImage = await this.getImageInfo(this.qdImageUrl);
+					this.ctx.drawImage(qdImage.path, uni.upx2px(120), uni.upx2px(575), uni.upx2px(335), uni.upx2px(335));
+				}
+				if (this.codeType == 3) {
+					// 绘制小程序码 // 科技质感
+					let qdImage = await this.getImageInfo(this.qdImageUrl);
+					this.ctx.drawImage(qdImage.path, uni.upx2px(140), uni.upx2px(370), uni.upx2px(300), uni.upx2px(300));
+				}
+				if (this.codeType == 4) {
+					// 绘制小程序码 // 叮咚黄
+					let qdImage = await this.getImageInfo(this.qdImageUrl);
+					this.ctx.drawImage(qdImage.path, uni.upx2px(135), uni.upx2px(370), uni.upx2px(305), uni.upx2px(305));
+				}
+				if (this.codeType == 5) {
+					// 绘制小程序码 // 美味点心
+					let qdImage = await this.getImageInfo(this.qdImageUrl);
+					this.ctx.drawImage(qdImage.path, uni.upx2px(160), uni.upx2px(475), uni.upx2px(265), uni.upx2px(265));
+				}
+				
+				
+				
+				setTimeout(()=>{
+					this.ctx.draw(true,()=>{
+						uni.hideLoading()
+					})
+				},500)
+			},
+			//画圆形头像
+			drawCircular(ctx, url, x, y, width, height) {
+				ctx.save();
+				ctx.beginPath();
+				ctx.arc(width / 2 + x, height / 2 + y, width / 2, 0, Math.PI * 2, false);
+				ctx.clip();
+				ctx.drawImage(url, x, y, width, height);
+				ctx.restore();
+				ctx.closePath();
+			},
+			 roundedRectangle(ctx, x, y, width, height, r,url){
+				 ctx.save();
+				  ctx.beginPath();
+				  ctx.moveTo(x + r, y);
+				  ctx.lineTo(x + width - r, y);
+				  ctx.arc(x + width - r, y + r, r, Math.PI*1.5, Math.PI*2);
+				  ctx.lineTo(x + width, y + height - r);
+				  ctx.arc(x + width - r, y + height - r, r, 0, Math.PI*0.5);
+				  ctx.lineTo(x + r, y + height);
+				  ctx.arc(x + r, y + height - r, r, Math.PI*0.5, Math.PI);
+				  ctx.lineTo(x, y + r);
+				  ctx.arc(x + r, y + r, r, Math.PI, Math.PI*1.5);
+					ctx.clip();;
+				  ctx.drawImage(url, x, y, width, height);
+				  ctx.restore();
+				  ctx.closePath();
+			},
+			//获取图片
+			getImageInfo(imgSrc){
+				return new Promise((resolve, reject) => {
+					uni.getImageInfo({
+						src: imgSrc,
+						success: (image) => {
+							resolve(image);
+						},
+						fail: (err) => {
+							reject(err);
+							setTimeout(()=>{
+								uni.hideLoading()
+							},500)
+						}
+					});
+				});
+			},
+			moveHandle(){},
+			//保存图片到相册
+			saveImage(){
+				var that = this
+				uni.canvasToTempFilePath({
+					canvasId: 'my-canvas',
+					quality: 1,
+					complete: (res) => {
+						uni.saveImageToPhotosAlbum({
+							filePath: res.tempFilePath,
+							success(res) {
+								uni.showToast({
+									title: '已保存到相册',
+									icon: 'success',
+									duration: 2000
+								})
+								setTimeout(()=>{
+									that.isShow = false
+								},2000)
+							}
+						})
+					}
+				},this);
+			}
+		}
+	}
+</script>
+
+<style scoped lang="scss">
+.content{
+	position: fixed;
+	top: 0;
+	left: 0;
+	right: 0;
+	bottom: 90upx;
+	background: rgba(0,0,0,.4);
+	display: flex;
+	flex-direction: column;
+	justify-content: center;
+	align-items: center;
+	z-index: 99;
+	
+	.save-btn-wrap{
+		position: fixed;
+		left: 0;
+		bottom: 0;
+		height: 100upx;
+		background: #fff;
+		display: flex;
+		justify-content: center;
+		align-items: center;
+		width: 100%;
+	}
+	
+	.save-btn{
+		// color: #fff;
+		// // background-image: linear-gradient(#fce799, #e7c389);
+		// background-image: linear-gradient(#f2190c, #f2370c);
+		// padding: 15rpx 40rpx;
+		// border-radius: 50rpx;
+		width: 80%;
+		height: 80rpx;
+		border-radius: 40rpx;
+		position: fixed;
+		left: 10%;
+		bottom: calc(20rpx + env(safe-area-inset-bottom));
+		bottom: calc(20rpx + constant(safe-area-inset-bottom));
+		background-color: $light-color;
+		color: #fff;
+		font-size: 36rpx;
+		@include flexCenter;
+		padding: 15rpx 40rpx;
+	}
+}
+/* ios底部安全距离-padding */
+	.savepadding {
+		padding-bottom: constant(safe-area-inset-bottom);
+		padding-bottom: env(safe-area-inset-bottom);
+		box-sizing: content-box;
+	}
+
+	/* ios底部安全距离-bottom */
+	.savebottom {
+		bottom: constant(safe-area-inset-bottom);
+		bottom: env(safe-area-inset-bottom);
+	}
+</style>

+ 1201 - 0
components/tki-qrcode/qrcode.js

@@ -0,0 +1,1201 @@
+let QRCode = {};
+(function () {
+    /**
+     * 获取单个字符的utf8编码
+     * unicode BMP平面约65535个字符
+     * @param {num} code
+     * return {array}
+     */
+    function unicodeFormat8(code) {
+        // 1 byte
+        var c0, c1, c2;
+        if (code < 128) {
+            return [code];
+            // 2 bytes
+        } else if (code < 2048) {
+            c0 = 192 + (code >> 6);
+            c1 = 128 + (code & 63);
+            return [c0, c1];
+            // 3 bytes
+        } else {
+            c0 = 224 + (code >> 12);
+            c1 = 128 + (code >> 6 & 63);
+            c2 = 128 + (code & 63);
+            return [c0, c1, c2];
+        }
+    }
+    /**
+     * 获取字符串的utf8编码字节串
+     * @param {string} string
+     * @return {array}
+     */
+    function getUTF8Bytes(string) {
+        var utf8codes = [];
+        for (var i = 0; i < string.length; i++) {
+            var code = string.charCodeAt(i);
+            var utf8 = unicodeFormat8(code);
+            for (var j = 0; j < utf8.length; j++) {
+                utf8codes.push(utf8[j]);
+            }
+        }
+        return utf8codes;
+    }
+    /**
+     * 二维码算法实现
+     * @param {string} data              要编码的信息字符串
+     * @param {num} errorCorrectLevel 纠错等级
+     */
+    function QRCodeAlg(data, errorCorrectLevel) {
+        this.typeNumber = -1; //版本
+        this.errorCorrectLevel = errorCorrectLevel;
+        this.modules = null; //二维矩阵,存放最终结果
+        this.moduleCount = 0; //矩阵大小
+        this.dataCache = null; //数据缓存
+        this.rsBlocks = null; //版本数据信息
+        this.totalDataCount = -1; //可使用的数据量
+        this.data = data;
+        this.utf8bytes = getUTF8Bytes(data);
+        this.make();
+    }
+    QRCodeAlg.prototype = {
+        constructor: QRCodeAlg,
+        /**
+         * 获取二维码矩阵大小
+         * @return {num} 矩阵大小
+         */
+        getModuleCount: function () {
+            return this.moduleCount;
+        },
+        /**
+         * 编码
+         */
+        make: function () {
+            this.getRightType();
+            this.dataCache = this.createData();
+            this.createQrcode();
+        },
+        /**
+         * 设置二位矩阵功能图形
+         * @param  {bool} test 表示是否在寻找最好掩膜阶段
+         * @param  {num} maskPattern 掩膜的版本
+         */
+        makeImpl: function (maskPattern) {
+            this.moduleCount = this.typeNumber * 4 + 17;
+            this.modules = new Array(this.moduleCount);
+            for (var row = 0; row < this.moduleCount; row++) {
+                this.modules[row] = new Array(this.moduleCount);
+            }
+            this.setupPositionProbePattern(0, 0);
+            this.setupPositionProbePattern(this.moduleCount - 7, 0);
+            this.setupPositionProbePattern(0, this.moduleCount - 7);
+            this.setupPositionAdjustPattern();
+            this.setupTimingPattern();
+            this.setupTypeInfo(true, maskPattern);
+            if (this.typeNumber >= 7) {
+                this.setupTypeNumber(true);
+            }
+            this.mapData(this.dataCache, maskPattern);
+        },
+        /**
+         * 设置二维码的位置探测图形
+         * @param  {num} row 探测图形的中心横坐标
+         * @param  {num} col 探测图形的中心纵坐标
+         */
+        setupPositionProbePattern: function (row, col) {
+            for (var r = -1; r <= 7; r++) {
+                if (row + r <= -1 || this.moduleCount <= row + r) continue;
+                for (var c = -1; c <= 7; c++) {
+                    if (col + c <= -1 || this.moduleCount <= col + c) continue;
+                    if ((0 <= r && r <= 6 && (c == 0 || c == 6)) || (0 <= c && c <= 6 && (r == 0 || r == 6)) || (2 <= r && r <= 4 && 2 <= c && c <= 4)) {
+                        this.modules[row + r][col + c] = true;
+                    } else {
+                        this.modules[row + r][col + c] = false;
+                    }
+                }
+            }
+        },
+        /**
+         * 创建二维码
+         * @return {[type]} [description]
+         */
+        createQrcode: function () {
+            var minLostPoint = 0;
+            var pattern = 0;
+            var bestModules = null;
+            for (var i = 0; i < 8; i++) {
+                this.makeImpl(i);
+                var lostPoint = QRUtil.getLostPoint(this);
+                if (i == 0 || minLostPoint > lostPoint) {
+                    minLostPoint = lostPoint;
+                    pattern = i;
+                    bestModules = this.modules;
+                }
+            }
+            this.modules = bestModules;
+            this.setupTypeInfo(false, pattern);
+            if (this.typeNumber >= 7) {
+                this.setupTypeNumber(false);
+            }
+        },
+        /**
+         * 设置定位图形
+         * @return {[type]} [description]
+         */
+        setupTimingPattern: function () {
+            for (var r = 8; r < this.moduleCount - 8; r++) {
+                if (this.modules[r][6] != null) {
+                    continue;
+                }
+                this.modules[r][6] = (r % 2 == 0);
+                if (this.modules[6][r] != null) {
+                    continue;
+                }
+                this.modules[6][r] = (r % 2 == 0);
+            }
+        },
+        /**
+         * 设置矫正图形
+         * @return {[type]} [description]
+         */
+        setupPositionAdjustPattern: function () {
+            var pos = QRUtil.getPatternPosition(this.typeNumber);
+            for (var i = 0; i < pos.length; i++) {
+                for (var j = 0; j < pos.length; j++) {
+                    var row = pos[i];
+                    var col = pos[j];
+                    if (this.modules[row][col] != null) {
+                        continue;
+                    }
+                    for (var r = -2; r <= 2; r++) {
+                        for (var c = -2; c <= 2; c++) {
+                            if (r == -2 || r == 2 || c == -2 || c == 2 || (r == 0 && c == 0)) {
+                                this.modules[row + r][col + c] = true;
+                            } else {
+                                this.modules[row + r][col + c] = false;
+                            }
+                        }
+                    }
+                }
+            }
+        },
+        /**
+         * 设置版本信息(7以上版本才有)
+         * @param  {bool} test 是否处于判断最佳掩膜阶段
+         * @return {[type]}      [description]
+         */
+        setupTypeNumber: function (test) {
+            var bits = QRUtil.getBCHTypeNumber(this.typeNumber);
+            for (var i = 0; i < 18; i++) {
+                var mod = (!test && ((bits >> i) & 1) == 1);
+                this.modules[Math.floor(i / 3)][i % 3 + this.moduleCount - 8 - 3] = mod;
+                this.modules[i % 3 + this.moduleCount - 8 - 3][Math.floor(i / 3)] = mod;
+            }
+        },
+        /**
+         * 设置格式信息(纠错等级和掩膜版本)
+         * @param  {bool} test
+         * @param  {num} maskPattern 掩膜版本
+         * @return {}
+         */
+        setupTypeInfo: function (test, maskPattern) {
+            var data = (QRErrorCorrectLevel[this.errorCorrectLevel] << 3) | maskPattern;
+            var bits = QRUtil.getBCHTypeInfo(data);
+            // vertical
+            for (var i = 0; i < 15; i++) {
+                var mod = (!test && ((bits >> i) & 1) == 1);
+                if (i < 6) {
+                    this.modules[i][8] = mod;
+                } else if (i < 8) {
+                    this.modules[i + 1][8] = mod;
+                } else {
+                    this.modules[this.moduleCount - 15 + i][8] = mod;
+                }
+                // horizontal
+                var mod = (!test && ((bits >> i) & 1) == 1);
+                if (i < 8) {
+                    this.modules[8][this.moduleCount - i - 1] = mod;
+                } else if (i < 9) {
+                    this.modules[8][15 - i - 1 + 1] = mod;
+                } else {
+                    this.modules[8][15 - i - 1] = mod;
+                }
+            }
+            // fixed module
+            this.modules[this.moduleCount - 8][8] = (!test);
+        },
+        /**
+         * 数据编码
+         * @return {[type]} [description]
+         */
+        createData: function () {
+            var buffer = new QRBitBuffer();
+            var lengthBits = this.typeNumber > 9 ? 16 : 8;
+            buffer.put(4, 4); //添加模式
+            buffer.put(this.utf8bytes.length, lengthBits);
+            for (var i = 0, l = this.utf8bytes.length; i < l; i++) {
+                buffer.put(this.utf8bytes[i], 8);
+            }
+            if (buffer.length + 4 <= this.totalDataCount * 8) {
+                buffer.put(0, 4);
+            }
+            // padding
+            while (buffer.length % 8 != 0) {
+                buffer.putBit(false);
+            }
+            // padding
+            while (true) {
+                if (buffer.length >= this.totalDataCount * 8) {
+                    break;
+                }
+                buffer.put(QRCodeAlg.PAD0, 8);
+                if (buffer.length >= this.totalDataCount * 8) {
+                    break;
+                }
+                buffer.put(QRCodeAlg.PAD1, 8);
+            }
+            return this.createBytes(buffer);
+        },
+        /**
+         * 纠错码编码
+         * @param  {buffer} buffer 数据编码
+         * @return {[type]}
+         */
+        createBytes: function (buffer) {
+            var offset = 0;
+            var maxDcCount = 0;
+            var maxEcCount = 0;
+            var length = this.rsBlock.length / 3;
+            var rsBlocks = new Array();
+            for (var i = 0; i < length; i++) {
+                var count = this.rsBlock[i * 3 + 0];
+                var totalCount = this.rsBlock[i * 3 + 1];
+                var dataCount = this.rsBlock[i * 3 + 2];
+                for (var j = 0; j < count; j++) {
+                    rsBlocks.push([dataCount, totalCount]);
+                }
+            }
+            var dcdata = new Array(rsBlocks.length);
+            var ecdata = new Array(rsBlocks.length);
+            for (var r = 0; r < rsBlocks.length; r++) {
+                var dcCount = rsBlocks[r][0];
+                var ecCount = rsBlocks[r][1] - dcCount;
+                maxDcCount = Math.max(maxDcCount, dcCount);
+                maxEcCount = Math.max(maxEcCount, ecCount);
+                dcdata[r] = new Array(dcCount);
+                for (var i = 0; i < dcdata[r].length; i++) {
+                    dcdata[r][i] = 0xff & buffer.buffer[i + offset];
+                }
+                offset += dcCount;
+                var rsPoly = QRUtil.getErrorCorrectPolynomial(ecCount);
+                var rawPoly = new QRPolynomial(dcdata[r], rsPoly.getLength() - 1);
+                var modPoly = rawPoly.mod(rsPoly);
+                ecdata[r] = new Array(rsPoly.getLength() - 1);
+                for (var i = 0; i < ecdata[r].length; i++) {
+                    var modIndex = i + modPoly.getLength() - ecdata[r].length;
+                    ecdata[r][i] = (modIndex >= 0) ? modPoly.get(modIndex) : 0;
+                }
+            }
+            var data = new Array(this.totalDataCount);
+            var index = 0;
+            for (var i = 0; i < maxDcCount; i++) {
+                for (var r = 0; r < rsBlocks.length; r++) {
+                    if (i < dcdata[r].length) {
+                        data[index++] = dcdata[r][i];
+                    }
+                }
+            }
+            for (var i = 0; i < maxEcCount; i++) {
+                for (var r = 0; r < rsBlocks.length; r++) {
+                    if (i < ecdata[r].length) {
+                        data[index++] = ecdata[r][i];
+                    }
+                }
+            }
+            return data;
+
+        },
+        /**
+         * 布置模块,构建最终信息
+         * @param  {} data
+         * @param  {} maskPattern
+         * @return {}
+         */
+        mapData: function (data, maskPattern) {
+            var inc = -1;
+            var row = this.moduleCount - 1;
+            var bitIndex = 7;
+            var byteIndex = 0;
+            for (var col = this.moduleCount - 1; col > 0; col -= 2) {
+                if (col == 6) col--;
+                while (true) {
+                    for (var c = 0; c < 2; c++) {
+                        if (this.modules[row][col - c] == null) {
+                            var dark = false;
+                            if (byteIndex < data.length) {
+                                dark = (((data[byteIndex] >>> bitIndex) & 1) == 1);
+                            }
+                            var mask = QRUtil.getMask(maskPattern, row, col - c);
+                            if (mask) {
+                                dark = !dark;
+                            }
+                            this.modules[row][col - c] = dark;
+                            bitIndex--;
+                            if (bitIndex == -1) {
+                                byteIndex++;
+                                bitIndex = 7;
+                            }
+                        }
+                    }
+                    row += inc;
+                    if (row < 0 || this.moduleCount <= row) {
+                        row -= inc;
+                        inc = -inc;
+                        break;
+                    }
+                }
+            }
+        }
+    };
+    /**
+     * 填充字段
+     */
+    QRCodeAlg.PAD0 = 0xEC;
+    QRCodeAlg.PAD1 = 0x11;
+    //---------------------------------------------------------------------
+    // 纠错等级对应的编码
+    //---------------------------------------------------------------------
+    var QRErrorCorrectLevel = [1, 0, 3, 2];
+    //---------------------------------------------------------------------
+    // 掩膜版本
+    //---------------------------------------------------------------------
+    var QRMaskPattern = {
+        PATTERN000: 0,
+        PATTERN001: 1,
+        PATTERN010: 2,
+        PATTERN011: 3,
+        PATTERN100: 4,
+        PATTERN101: 5,
+        PATTERN110: 6,
+        PATTERN111: 7
+    };
+    //---------------------------------------------------------------------
+    // 工具类
+    //---------------------------------------------------------------------
+    var QRUtil = {
+        /*
+        每个版本矫正图形的位置
+         */
+        PATTERN_POSITION_TABLE: [
+            [],
+            [6, 18],
+            [6, 22],
+            [6, 26],
+            [6, 30],
+            [6, 34],
+            [6, 22, 38],
+            [6, 24, 42],
+            [6, 26, 46],
+            [6, 28, 50],
+            [6, 30, 54],
+            [6, 32, 58],
+            [6, 34, 62],
+            [6, 26, 46, 66],
+            [6, 26, 48, 70],
+            [6, 26, 50, 74],
+            [6, 30, 54, 78],
+            [6, 30, 56, 82],
+            [6, 30, 58, 86],
+            [6, 34, 62, 90],
+            [6, 28, 50, 72, 94],
+            [6, 26, 50, 74, 98],
+            [6, 30, 54, 78, 102],
+            [6, 28, 54, 80, 106],
+            [6, 32, 58, 84, 110],
+            [6, 30, 58, 86, 114],
+            [6, 34, 62, 90, 118],
+            [6, 26, 50, 74, 98, 122],
+            [6, 30, 54, 78, 102, 126],
+            [6, 26, 52, 78, 104, 130],
+            [6, 30, 56, 82, 108, 134],
+            [6, 34, 60, 86, 112, 138],
+            [6, 30, 58, 86, 114, 142],
+            [6, 34, 62, 90, 118, 146],
+            [6, 30, 54, 78, 102, 126, 150],
+            [6, 24, 50, 76, 102, 128, 154],
+            [6, 28, 54, 80, 106, 132, 158],
+            [6, 32, 58, 84, 110, 136, 162],
+            [6, 26, 54, 82, 110, 138, 166],
+            [6, 30, 58, 86, 114, 142, 170]
+        ],
+        G15: (1 << 10) | (1 << 8) | (1 << 5) | (1 << 4) | (1 << 2) | (1 << 1) | (1 << 0),
+        G18: (1 << 12) | (1 << 11) | (1 << 10) | (1 << 9) | (1 << 8) | (1 << 5) | (1 << 2) | (1 << 0),
+        G15_MASK: (1 << 14) | (1 << 12) | (1 << 10) | (1 << 4) | (1 << 1),
+        /*
+        BCH编码格式信息
+         */
+        getBCHTypeInfo: function (data) {
+            var d = data << 10;
+            while (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G15) >= 0) {
+                d ^= (QRUtil.G15 << (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G15)));
+            }
+            return ((data << 10) | d) ^ QRUtil.G15_MASK;
+        },
+        /*
+        BCH编码版本信息
+         */
+        getBCHTypeNumber: function (data) {
+            var d = data << 12;
+            while (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G18) >= 0) {
+                d ^= (QRUtil.G18 << (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G18)));
+            }
+            return (data << 12) | d;
+        },
+        /*
+        获取BCH位信息
+         */
+        getBCHDigit: function (data) {
+            var digit = 0;
+            while (data != 0) {
+                digit++;
+                data >>>= 1;
+            }
+            return digit;
+        },
+        /*
+        获取版本对应的矫正图形位置
+         */
+        getPatternPosition: function (typeNumber) {
+            return QRUtil.PATTERN_POSITION_TABLE[typeNumber - 1];
+        },
+        /*
+        掩膜算法
+         */
+        getMask: function (maskPattern, i, j) {
+            switch (maskPattern) {
+                case QRMaskPattern.PATTERN000:
+                    return (i + j) % 2 == 0;
+                case QRMaskPattern.PATTERN001:
+                    return i % 2 == 0;
+                case QRMaskPattern.PATTERN010:
+                    return j % 3 == 0;
+                case QRMaskPattern.PATTERN011:
+                    return (i + j) % 3 == 0;
+                case QRMaskPattern.PATTERN100:
+                    return (Math.floor(i / 2) + Math.floor(j / 3)) % 2 == 0;
+                case QRMaskPattern.PATTERN101:
+                    return (i * j) % 2 + (i * j) % 3 == 0;
+                case QRMaskPattern.PATTERN110:
+                    return ((i * j) % 2 + (i * j) % 3) % 2 == 0;
+                case QRMaskPattern.PATTERN111:
+                    return ((i * j) % 3 + (i + j) % 2) % 2 == 0;
+                default:
+                    throw new Error("bad maskPattern:" + maskPattern);
+            }
+        },
+        /*
+        获取RS的纠错多项式
+         */
+        getErrorCorrectPolynomial: function (errorCorrectLength) {
+            var a = new QRPolynomial([1], 0);
+            for (var i = 0; i < errorCorrectLength; i++) {
+                a = a.multiply(new QRPolynomial([1, QRMath.gexp(i)], 0));
+            }
+            return a;
+        },
+        /*
+        获取评价
+         */
+        getLostPoint: function (qrCode) {
+            var moduleCount = qrCode.getModuleCount(),
+                lostPoint = 0,
+                darkCount = 0;
+            for (var row = 0; row < moduleCount; row++) {
+                var sameCount = 0;
+                var head = qrCode.modules[row][0];
+                for (var col = 0; col < moduleCount; col++) {
+                    var current = qrCode.modules[row][col];
+                    //level 3 评价
+                    if (col < moduleCount - 6) {
+                        if (current && !qrCode.modules[row][col + 1] && qrCode.modules[row][col + 2] && qrCode.modules[row][col + 3] && qrCode.modules[row][col + 4] && !qrCode.modules[row][col + 5] && qrCode.modules[row][col + 6]) {
+                            if (col < moduleCount - 10) {
+                                if (qrCode.modules[row][col + 7] && qrCode.modules[row][col + 8] && qrCode.modules[row][col + 9] && qrCode.modules[row][col + 10]) {
+                                    lostPoint += 40;
+                                }
+                            } else if (col > 3) {
+                                if (qrCode.modules[row][col - 1] && qrCode.modules[row][col - 2] && qrCode.modules[row][col - 3] && qrCode.modules[row][col - 4]) {
+                                    lostPoint += 40;
+                                }
+                            }
+                        }
+                    }
+                    //level 2 评价
+                    if ((row < moduleCount - 1) && (col < moduleCount - 1)) {
+                        var count = 0;
+                        if (current) count++;
+                        if (qrCode.modules[row + 1][col]) count++;
+                        if (qrCode.modules[row][col + 1]) count++;
+                        if (qrCode.modules[row + 1][col + 1]) count++;
+                        if (count == 0 || count == 4) {
+                            lostPoint += 3;
+                        }
+                    }
+                    //level 1 评价
+                    if (head ^ current) {
+                        sameCount++;
+                    } else {
+                        head = current;
+                        if (sameCount >= 5) {
+                            lostPoint += (3 + sameCount - 5);
+                        }
+                        sameCount = 1;
+                    }
+                    //level 4 评价
+                    if (current) {
+                        darkCount++;
+                    }
+                }
+            }
+            for (var col = 0; col < moduleCount; col++) {
+                var sameCount = 0;
+                var head = qrCode.modules[0][col];
+                for (var row = 0; row < moduleCount; row++) {
+                    var current = qrCode.modules[row][col];
+                    //level 3 评价
+                    if (row < moduleCount - 6) {
+                        if (current && !qrCode.modules[row + 1][col] && qrCode.modules[row + 2][col] && qrCode.modules[row + 3][col] && qrCode.modules[row + 4][col] && !qrCode.modules[row + 5][col] && qrCode.modules[row + 6][col]) {
+                            if (row < moduleCount - 10) {
+                                if (qrCode.modules[row + 7][col] && qrCode.modules[row + 8][col] && qrCode.modules[row + 9][col] && qrCode.modules[row + 10][col]) {
+                                    lostPoint += 40;
+                                }
+                            } else if (row > 3) {
+                                if (qrCode.modules[row - 1][col] && qrCode.modules[row - 2][col] && qrCode.modules[row - 3][col] && qrCode.modules[row - 4][col]) {
+                                    lostPoint += 40;
+                                }
+                            }
+                        }
+                    }
+                    //level 1 评价
+                    if (head ^ current) {
+                        sameCount++;
+                    } else {
+                        head = current;
+                        if (sameCount >= 5) {
+                            lostPoint += (3 + sameCount - 5);
+                        }
+                        sameCount = 1;
+                    }
+                }
+            }
+            // LEVEL4
+            var ratio = Math.abs(100 * darkCount / moduleCount / moduleCount - 50) / 5;
+            lostPoint += ratio * 10;
+            return lostPoint;
+        }
+
+    };
+    //---------------------------------------------------------------------
+    // QRMath使用的数学工具
+    //---------------------------------------------------------------------
+    var QRMath = {
+        /*
+        将n转化为a^m
+         */
+        glog: function (n) {
+            if (n < 1) {
+                throw new Error("glog(" + n + ")");
+            }
+            return QRMath.LOG_TABLE[n];
+        },
+        /*
+        将a^m转化为n
+         */
+        gexp: function (n) {
+            while (n < 0) {
+                n += 255;
+            }
+            while (n >= 256) {
+                n -= 255;
+            }
+            return QRMath.EXP_TABLE[n];
+        },
+        EXP_TABLE: new Array(256),
+        LOG_TABLE: new Array(256)
+
+    };
+    for (var i = 0; i < 8; i++) {
+        QRMath.EXP_TABLE[i] = 1 << i;
+    }
+    for (var i = 8; i < 256; i++) {
+        QRMath.EXP_TABLE[i] = QRMath.EXP_TABLE[i - 4] ^ QRMath.EXP_TABLE[i - 5] ^ QRMath.EXP_TABLE[i - 6] ^ QRMath.EXP_TABLE[i - 8];
+    }
+    for (var i = 0; i < 255; i++) {
+        QRMath.LOG_TABLE[QRMath.EXP_TABLE[i]] = i;
+    }
+    //---------------------------------------------------------------------
+    // QRPolynomial 多项式
+    //---------------------------------------------------------------------
+    /**
+     * 多项式类
+     * @param {Array} num   系数
+     * @param {num} shift a^shift
+     */
+    function QRPolynomial(num, shift) {
+        if (num.length == undefined) {
+            throw new Error(num.length + "/" + shift);
+        }
+        var offset = 0;
+        while (offset < num.length && num[offset] == 0) {
+            offset++;
+        }
+        this.num = new Array(num.length - offset + shift);
+        for (var i = 0; i < num.length - offset; i++) {
+            this.num[i] = num[i + offset];
+        }
+    }
+    QRPolynomial.prototype = {
+        get: function (index) {
+            return this.num[index];
+        },
+        getLength: function () {
+            return this.num.length;
+        },
+        /**
+         * 多项式乘法
+         * @param  {QRPolynomial} e 被乘多项式
+         * @return {[type]}   [description]
+         */
+        multiply: function (e) {
+            var num = new Array(this.getLength() + e.getLength() - 1);
+            for (var i = 0; i < this.getLength(); i++) {
+                for (var j = 0; j < e.getLength(); j++) {
+                    num[i + j] ^= QRMath.gexp(QRMath.glog(this.get(i)) + QRMath.glog(e.get(j)));
+                }
+            }
+            return new QRPolynomial(num, 0);
+        },
+        /**
+         * 多项式模运算
+         * @param  {QRPolynomial} e 模多项式
+         * @return {}
+         */
+        mod: function (e) {
+            var tl = this.getLength(),
+                el = e.getLength();
+            if (tl - el < 0) {
+                return this;
+            }
+            var num = new Array(tl);
+            for (var i = 0; i < tl; i++) {
+                num[i] = this.get(i);
+            }
+            while (num.length >= el) {
+                var ratio = QRMath.glog(num[0]) - QRMath.glog(e.get(0));
+
+                for (var i = 0; i < e.getLength(); i++) {
+                    num[i] ^= QRMath.gexp(QRMath.glog(e.get(i)) + ratio);
+                }
+                while (num[0] == 0) {
+                    num.shift();
+                }
+            }
+            return new QRPolynomial(num, 0);
+        }
+    };
+
+    //---------------------------------------------------------------------
+    // RS_BLOCK_TABLE
+    //---------------------------------------------------------------------
+    /*
+    二维码各个版本信息[块数, 每块中的数据块数, 每块中的信息块数]
+     */
+    var RS_BLOCK_TABLE = [
+        // L
+        // M
+        // Q
+        // H
+        // 1
+        [1, 26, 19],
+        [1, 26, 16],
+        [1, 26, 13],
+        [1, 26, 9],
+
+        // 2
+        [1, 44, 34],
+        [1, 44, 28],
+        [1, 44, 22],
+        [1, 44, 16],
+
+        // 3
+        [1, 70, 55],
+        [1, 70, 44],
+        [2, 35, 17],
+        [2, 35, 13],
+
+        // 4
+        [1, 100, 80],
+        [2, 50, 32],
+        [2, 50, 24],
+        [4, 25, 9],
+
+        // 5
+        [1, 134, 108],
+        [2, 67, 43],
+        [2, 33, 15, 2, 34, 16],
+        [2, 33, 11, 2, 34, 12],
+
+        // 6
+        [2, 86, 68],
+        [4, 43, 27],
+        [4, 43, 19],
+        [4, 43, 15],
+
+        // 7
+        [2, 98, 78],
+        [4, 49, 31],
+        [2, 32, 14, 4, 33, 15],
+        [4, 39, 13, 1, 40, 14],
+
+        // 8
+        [2, 121, 97],
+        [2, 60, 38, 2, 61, 39],
+        [4, 40, 18, 2, 41, 19],
+        [4, 40, 14, 2, 41, 15],
+
+        // 9
+        [2, 146, 116],
+        [3, 58, 36, 2, 59, 37],
+        [4, 36, 16, 4, 37, 17],
+        [4, 36, 12, 4, 37, 13],
+
+        // 10
+        [2, 86, 68, 2, 87, 69],
+        [4, 69, 43, 1, 70, 44],
+        [6, 43, 19, 2, 44, 20],
+        [6, 43, 15, 2, 44, 16],
+
+        // 11
+        [4, 101, 81],
+        [1, 80, 50, 4, 81, 51],
+        [4, 50, 22, 4, 51, 23],
+        [3, 36, 12, 8, 37, 13],
+
+        // 12
+        [2, 116, 92, 2, 117, 93],
+        [6, 58, 36, 2, 59, 37],
+        [4, 46, 20, 6, 47, 21],
+        [7, 42, 14, 4, 43, 15],
+
+        // 13
+        [4, 133, 107],
+        [8, 59, 37, 1, 60, 38],
+        [8, 44, 20, 4, 45, 21],
+        [12, 33, 11, 4, 34, 12],
+
+        // 14
+        [3, 145, 115, 1, 146, 116],
+        [4, 64, 40, 5, 65, 41],
+        [11, 36, 16, 5, 37, 17],
+        [11, 36, 12, 5, 37, 13],
+
+        // 15
+        [5, 109, 87, 1, 110, 88],
+        [5, 65, 41, 5, 66, 42],
+        [5, 54, 24, 7, 55, 25],
+        [11, 36, 12],
+
+        // 16
+        [5, 122, 98, 1, 123, 99],
+        [7, 73, 45, 3, 74, 46],
+        [15, 43, 19, 2, 44, 20],
+        [3, 45, 15, 13, 46, 16],
+
+        // 17
+        [1, 135, 107, 5, 136, 108],
+        [10, 74, 46, 1, 75, 47],
+        [1, 50, 22, 15, 51, 23],
+        [2, 42, 14, 17, 43, 15],
+
+        // 18
+        [5, 150, 120, 1, 151, 121],
+        [9, 69, 43, 4, 70, 44],
+        [17, 50, 22, 1, 51, 23],
+        [2, 42, 14, 19, 43, 15],
+
+        // 19
+        [3, 141, 113, 4, 142, 114],
+        [3, 70, 44, 11, 71, 45],
+        [17, 47, 21, 4, 48, 22],
+        [9, 39, 13, 16, 40, 14],
+
+        // 20
+        [3, 135, 107, 5, 136, 108],
+        [3, 67, 41, 13, 68, 42],
+        [15, 54, 24, 5, 55, 25],
+        [15, 43, 15, 10, 44, 16],
+
+        // 21
+        [4, 144, 116, 4, 145, 117],
+        [17, 68, 42],
+        [17, 50, 22, 6, 51, 23],
+        [19, 46, 16, 6, 47, 17],
+
+        // 22
+        [2, 139, 111, 7, 140, 112],
+        [17, 74, 46],
+        [7, 54, 24, 16, 55, 25],
+        [34, 37, 13],
+
+        // 23
+        [4, 151, 121, 5, 152, 122],
+        [4, 75, 47, 14, 76, 48],
+        [11, 54, 24, 14, 55, 25],
+        [16, 45, 15, 14, 46, 16],
+
+        // 24
+        [6, 147, 117, 4, 148, 118],
+        [6, 73, 45, 14, 74, 46],
+        [11, 54, 24, 16, 55, 25],
+        [30, 46, 16, 2, 47, 17],
+
+        // 25
+        [8, 132, 106, 4, 133, 107],
+        [8, 75, 47, 13, 76, 48],
+        [7, 54, 24, 22, 55, 25],
+        [22, 45, 15, 13, 46, 16],
+
+        // 26
+        [10, 142, 114, 2, 143, 115],
+        [19, 74, 46, 4, 75, 47],
+        [28, 50, 22, 6, 51, 23],
+        [33, 46, 16, 4, 47, 17],
+
+        // 27
+        [8, 152, 122, 4, 153, 123],
+        [22, 73, 45, 3, 74, 46],
+        [8, 53, 23, 26, 54, 24],
+        [12, 45, 15, 28, 46, 16],
+
+        // 28
+        [3, 147, 117, 10, 148, 118],
+        [3, 73, 45, 23, 74, 46],
+        [4, 54, 24, 31, 55, 25],
+        [11, 45, 15, 31, 46, 16],
+
+        // 29
+        [7, 146, 116, 7, 147, 117],
+        [21, 73, 45, 7, 74, 46],
+        [1, 53, 23, 37, 54, 24],
+        [19, 45, 15, 26, 46, 16],
+
+        // 30
+        [5, 145, 115, 10, 146, 116],
+        [19, 75, 47, 10, 76, 48],
+        [15, 54, 24, 25, 55, 25],
+        [23, 45, 15, 25, 46, 16],
+
+        // 31
+        [13, 145, 115, 3, 146, 116],
+        [2, 74, 46, 29, 75, 47],
+        [42, 54, 24, 1, 55, 25],
+        [23, 45, 15, 28, 46, 16],
+
+        // 32
+        [17, 145, 115],
+        [10, 74, 46, 23, 75, 47],
+        [10, 54, 24, 35, 55, 25],
+        [19, 45, 15, 35, 46, 16],
+
+        // 33
+        [17, 145, 115, 1, 146, 116],
+        [14, 74, 46, 21, 75, 47],
+        [29, 54, 24, 19, 55, 25],
+        [11, 45, 15, 46, 46, 16],
+
+        // 34
+        [13, 145, 115, 6, 146, 116],
+        [14, 74, 46, 23, 75, 47],
+        [44, 54, 24, 7, 55, 25],
+        [59, 46, 16, 1, 47, 17],
+
+        // 35
+        [12, 151, 121, 7, 152, 122],
+        [12, 75, 47, 26, 76, 48],
+        [39, 54, 24, 14, 55, 25],
+        [22, 45, 15, 41, 46, 16],
+
+        // 36
+        [6, 151, 121, 14, 152, 122],
+        [6, 75, 47, 34, 76, 48],
+        [46, 54, 24, 10, 55, 25],
+        [2, 45, 15, 64, 46, 16],
+
+        // 37
+        [17, 152, 122, 4, 153, 123],
+        [29, 74, 46, 14, 75, 47],
+        [49, 54, 24, 10, 55, 25],
+        [24, 45, 15, 46, 46, 16],
+
+        // 38
+        [4, 152, 122, 18, 153, 123],
+        [13, 74, 46, 32, 75, 47],
+        [48, 54, 24, 14, 55, 25],
+        [42, 45, 15, 32, 46, 16],
+
+        // 39
+        [20, 147, 117, 4, 148, 118],
+        [40, 75, 47, 7, 76, 48],
+        [43, 54, 24, 22, 55, 25],
+        [10, 45, 15, 67, 46, 16],
+
+        // 40
+        [19, 148, 118, 6, 149, 119],
+        [18, 75, 47, 31, 76, 48],
+        [34, 54, 24, 34, 55, 25],
+        [20, 45, 15, 61, 46, 16]
+    ];
+
+    /**
+     * 根据数据获取对应版本
+     * @return {[type]} [description]
+     */
+    QRCodeAlg.prototype.getRightType = function () {
+        for (var typeNumber = 1; typeNumber < 41; typeNumber++) {
+            var rsBlock = RS_BLOCK_TABLE[(typeNumber - 1) * 4 + this.errorCorrectLevel];
+            if (rsBlock == undefined) {
+                throw new Error("bad rs block @ typeNumber:" + typeNumber + "/errorCorrectLevel:" + this.errorCorrectLevel);
+            }
+            var length = rsBlock.length / 3;
+            var totalDataCount = 0;
+            for (var i = 0; i < length; i++) {
+                var count = rsBlock[i * 3 + 0];
+                var dataCount = rsBlock[i * 3 + 2];
+                totalDataCount += dataCount * count;
+            }
+            var lengthBytes = typeNumber > 9 ? 2 : 1;
+            if (this.utf8bytes.length + lengthBytes < totalDataCount || typeNumber == 40) {
+                this.typeNumber = typeNumber;
+                this.rsBlock = rsBlock;
+                this.totalDataCount = totalDataCount;
+                break;
+            }
+        }
+    };
+
+    //---------------------------------------------------------------------
+    // QRBitBuffer
+    //---------------------------------------------------------------------
+    function QRBitBuffer() {
+        this.buffer = new Array();
+        this.length = 0;
+    }
+    QRBitBuffer.prototype = {
+        get: function (index) {
+            var bufIndex = Math.floor(index / 8);
+            return ((this.buffer[bufIndex] >>> (7 - index % 8)) & 1);
+        },
+        put: function (num, length) {
+            for (var i = 0; i < length; i++) {
+                this.putBit(((num >>> (length - i - 1)) & 1));
+            }
+        },
+        putBit: function (bit) {
+            var bufIndex = Math.floor(this.length / 8);
+            if (this.buffer.length <= bufIndex) {
+                this.buffer.push(0);
+            }
+            if (bit) {
+                this.buffer[bufIndex] |= (0x80 >>> (this.length % 8));
+            }
+            this.length++;
+        }
+    };
+
+
+
+    // xzedit
+    let qrcodeAlgObjCache = [];
+    /**
+     * 二维码构造函数,主要用于绘制
+     * @param  {参数列表} opt 传递参数
+     * @return {}
+     */
+    QRCode = function (opt) {
+        //设置默认参数
+        this.options = {
+            text: '',
+            size: 256,
+            correctLevel: 3,
+            background: '#ffffff',
+            foreground: '#000000',
+            pdground: '#000000',
+            image: '',
+            imageSize: 30,
+            canvasId: opt.canvasId,
+            context: opt.context,
+            usingComponents: opt.usingComponents,
+            showLoading: opt.showLoading,
+            loadingText: opt.loadingText,
+        };
+        if (typeof opt === 'string') { // 只编码ASCII字符串
+            opt = {
+                text: opt
+            };
+        }
+        if (opt) {
+            for (var i in opt) {
+                this.options[i] = opt[i];
+            }
+        }
+        //使用QRCodeAlg创建二维码结构
+        var qrCodeAlg = null;
+        for (var i = 0, l = qrcodeAlgObjCache.length; i < l; i++) {
+            if (qrcodeAlgObjCache[i].text == this.options.text && qrcodeAlgObjCache[i].text.correctLevel == this.options.correctLevel) {
+                qrCodeAlg = qrcodeAlgObjCache[i].obj;
+                break;
+            }
+        }
+        if (i == l) {
+            qrCodeAlg = new QRCodeAlg(this.options.text, this.options.correctLevel);
+            qrcodeAlgObjCache.push({
+                text: this.options.text,
+                correctLevel: this.options.correctLevel,
+                obj: qrCodeAlg
+            });
+        }
+        /**
+         * 计算矩阵点的前景色
+         * @param {Obj} config
+         * @param {Number} config.row 点x坐标
+         * @param {Number} config.col 点y坐标
+         * @param {Number} config.count 矩阵大小
+         * @param {Number} config.options 组件的options
+         * @return {String}
+         */
+        let getForeGround = function (config) {
+            var options = config.options;
+            if (options.pdground && (
+                (config.row > 1 && config.row < 5 && config.col > 1 && config.col < 5) ||
+                (config.row > (config.count - 6) && config.row < (config.count - 2) && config.col > 1 && config.col < 5) ||
+                (config.row > 1 && config.row < 5 && config.col > (config.count - 6) && config.col < (config.count - 2))
+            )) {
+                return options.pdground;
+            }
+            return options.foreground;
+        }
+        // 创建canvas
+        let createCanvas = function (options) {
+            if (options.showLoading) {
+                uni.showLoading({
+                    title: options.loadingText,
+                    mask: true
+                });
+            }
+            var ctx = uni.createCanvasContext(options.canvasId, options.context);
+            var count = qrCodeAlg.getModuleCount();
+            var ratioSize = options.size;
+            var ratioImgSize = options.imageSize;
+            //计算每个点的长宽
+            var tileW = (ratioSize / count).toPrecision(4);
+            var tileH = (ratioSize / count).toPrecision(4);
+            //绘制
+            for (var row = 0; row < count; row++) {
+                for (var col = 0; col < count; col++) {
+                    var w = (Math.ceil((col + 1) * tileW) - Math.floor(col * tileW));
+                    var h = (Math.ceil((row + 1) * tileW) - Math.floor(row * tileW));
+                    var foreground = getForeGround({
+                        row: row,
+                        col: col,
+                        count: count,
+                        options: options
+                    });
+                    ctx.setFillStyle(qrCodeAlg.modules[row][col] ? foreground : options.background);
+                    ctx.fillRect(Math.round(col * tileW), Math.round(row * tileH), w, h);
+                }
+            }
+            if (options.image) {
+                var x = Number(((ratioSize - ratioImgSize) / 2).toFixed(2));
+                var y = Number(((ratioSize - ratioImgSize) / 2).toFixed(2));
+                drawRoundedRect(ctx, x, y, ratioImgSize, ratioImgSize, 2, 6, true, true)
+                ctx.drawImage(options.image, x, y, ratioImgSize, ratioImgSize);
+                // 画圆角矩形
+                function drawRoundedRect(ctxi, x, y, width, height, r, lineWidth, fill, stroke) {
+                    ctxi.setLineWidth(lineWidth);
+                    ctxi.setFillStyle(options.background);
+                    ctxi.setStrokeStyle(options.background);
+                    ctxi.beginPath(); // draw top and top right corner 
+                    ctxi.moveTo(x + r, y);
+                    ctxi.arcTo(x + width, y, x + width, y + r, r); // draw right side and bottom right corner 
+                    ctxi.arcTo(x + width, y + height, x + width - r, y + height, r); // draw bottom and bottom left corner 
+                    ctxi.arcTo(x, y + height, x, y + height - r, r); // draw left and top left corner 
+                    ctxi.arcTo(x, y, x + r, y, r);
+                    ctxi.closePath();
+                    if (fill) {
+                        ctxi.fill();
+                    }
+                    if (stroke) {
+                        ctxi.stroke();
+                    }
+                }
+            }
+            setTimeout(() => {
+                ctx.draw(true, () => {
+                    // 保存到临时区域
+                    setTimeout(() => {
+                        uni.canvasToTempFilePath({
+                            width: options.width,
+                            height: options.height,
+                            destWidth: options.width,
+                            destHeight: options.height,
+                            canvasId: options.canvasId,
+                            quality: Number(1),
+                            success: function (res) {
+                                if (options.cbResult) {
+                                    options.cbResult(res.tempFilePath)
+                                }
+                            },
+                            fail: function (res) {
+                                if (options.cbResult) {
+                                    options.cbResult(res)
+                                }
+                            },
+                            complete: function () {
+                                if (options.showLoading){
+                                    uni.hideLoading();
+                                }
+                            },
+                        }, options.context);
+                    }, options.text.length + 100);
+                });
+            }, options.usingComponents ? 0 : 150);
+        }
+        createCanvas(this.options);
+        // 空判定
+        let empty = function (v) {
+            let tp = typeof v,
+                rt = false;
+            if (tp == "number" && String(v) == "") {
+                rt = true
+            } else if (tp == "undefined") {
+                rt = true
+            } else if (tp == "object") {
+                if (JSON.stringify(v) == "{}" || JSON.stringify(v) == "[]" || v == null) rt = true
+            } else if (tp == "string") {
+                if (v == "" || v == "undefined" || v == "null" || v == "{}" || v == "[]") rt = true
+            } else if (tp == "function") {
+                rt = false
+            }
+            return rt
+        }
+    };
+    QRCode.prototype.clear = function (fn) {
+        var ctx = uni.createCanvasContext(this.options.canvasId, this.options.context)
+        ctx.clearRect(0, 0, this.options.size, this.options.size)
+        ctx.draw(false, () => {
+            if (fn) {
+                fn()
+            }
+        })
+    };
+})()
+
+export default QRCode

+ 210 - 0
components/tki-qrcode/tki-qrcode.vue

@@ -0,0 +1,210 @@
+<template xlang="wxml" minapp="mpvue">
+	<view class="tki-qrcode">
+		<!-- #ifndef MP-ALIPAY -->
+		<canvas class="tki-qrcode-canvas" :canvas-id="cid" :style="{width:cpSize+'px',height:cpSize+'px'}" />
+		<!-- #endif -->
+		<!-- #ifdef MP-ALIPAY -->
+		<canvas :id="cid" :width="cpSize" :height="cpSize" class="tki-qrcode-canvas" />
+		<!-- #endif -->
+		<image v-show="show" :src="result" :style="{width:cpSize+'px',height:cpSize+'px'}" />
+	</view>
+</template>
+
+<script>
+import QRCode from "./qrcode.js"
+let qrcode
+export default {
+	name: "tki-qrcode",
+	props: {
+		cid: {
+			type: String,
+			default: 'tki-qrcode-canvas'
+		},
+		size: {
+			type: Number,
+			default: 200
+		},
+		unit: {
+			type: String,
+			default: 'upx'
+		},
+		show: {
+			type: Boolean,
+			default: true
+		},
+		val: {
+			type: String,
+			default: ''
+		},
+		background: {
+			type: String,
+			default: '#ffffff'
+		},
+		foreground: {
+			type: String,
+			default: '#000000'
+		},
+		pdground: {
+			type: String,
+			default: '#000000'
+		},
+		icon: {
+			type: String,
+			default: ''
+		},
+		iconSize: {
+			type: Number,
+			default: 40
+		},
+		lv: {
+			type: Number,
+			default: 3
+		},
+		onval: {
+			type: Boolean,
+			default: false
+		},
+		loadMake: {
+			type: Boolean,
+			default: false
+		},
+		usingComponents: {
+			type: Boolean,
+			default: true
+		},
+		showLoading: {
+			type: Boolean,
+			default: true
+		},
+		loadingText: {
+			type: String,
+			default: '二维码生成中'
+		},
+	},
+	data() {
+		return {
+			result: '',
+		}
+	},
+	methods: {
+		_makeCode() {
+			let that = this
+			if (!this._empty(this.val)) {
+				qrcode = new QRCode({
+					context: that, // 上下文环境
+					canvasId:that.cid, // canvas-id
+					usingComponents: that.usingComponents, // 是否是自定义组件
+					showLoading: that.showLoading, // 是否显示loading
+					loadingText: that.loadingText, // loading文字
+					text: that.val, // 生成内容
+					size: that.cpSize, // 二维码大小
+					background: that.background, // 背景色
+					foreground: that.foreground, // 前景色
+					pdground: that.pdground, // 定位角点颜色
+					correctLevel: that.lv, // 容错级别
+					image: that.icon, // 二维码图标
+					imageSize: that.iconSize,// 二维码图标大小
+					cbResult: function (res) { // 生成二维码的回调
+						that._result(res)
+					},
+				});
+			} else {
+				uni.showToast({
+					title: '二维码内容不能为空',
+					icon: 'none',
+					duration: 2000
+				});
+			}
+		},
+		_clearCode() {
+			this._result('')
+			qrcode.clear()
+		},
+		_saveCode() {
+			let that = this;
+			if (this.result != "") {
+				uni.saveImageToPhotosAlbum({
+					filePath: that.result,
+					success: function () {
+						uni.showToast({
+							title: '二维码保存成功',
+							icon: 'success',
+							duration: 2000
+						});
+					}
+				});
+			}
+		},
+		_result(res) {
+			this.result = res;
+			this.$emit('result', res)
+		},
+		_empty(v) {
+			let tp = typeof v,
+				rt = false;
+			if (tp == "number" && String(v) == "") {
+				rt = true
+			} else if (tp == "undefined") {
+				rt = true
+			} else if (tp == "object") {
+				if (JSON.stringify(v) == "{}" || JSON.stringify(v) == "[]" || v == null) rt = true
+			} else if (tp == "string") {
+				if (v == "" || v == "undefined" || v == "null" || v == "{}" || v == "[]") rt = true
+			} else if (tp == "function") {
+				rt = false
+			}
+			return rt
+		}
+	},
+	watch: {
+		size: function (n, o) {
+			if (n != o && !this._empty(n)) {
+				this.cSize = n
+				if (!this._empty(this.val)) {
+					setTimeout(() => {
+						this._makeCode()
+					}, 100);
+				}
+			}
+		},
+		val: function (n, o) {
+			if (this.onval) {
+				if (n != o && !this._empty(n)) {
+					setTimeout(() => {
+						this._makeCode()
+					}, 0);
+				}
+			}
+		}
+	},
+	computed: {
+		cpSize() {
+			if(this.unit == "upx"){
+				return uni.upx2px(this.size)
+			}else{
+				return this.size
+			}
+		}
+	},
+	mounted: function () {
+		if (this.loadMake) {
+			if (!this._empty(this.val)) {
+				setTimeout(() => {
+					this._makeCode()
+				}, 0);
+			}
+		}
+	},
+}
+</script>
+<style>
+.tki-qrcode {
+  position: relative;
+}
+.tki-qrcode-canvas {
+  position: fixed;
+  top: -99999upx;
+  left: -99999upx;
+  z-index: -99999;
+}
+</style>

+ 402 - 0
components/uni-collapse-item/uni-collapse-item.vue

@@ -0,0 +1,402 @@
+<template>
+	<view class="uni-collapse-item">
+		<!-- onClick(!isOpen) -->
+		<view @click="onClick(!isOpen)" class="uni-collapse-item__title"
+			:class="{'is-open':isOpen &&titleBorder === 'auto' ,'uni-collapse-item-border':titleBorder !== 'none'}">
+			<view class="uni-collapse-item__title-wrap">
+				<slot name="title">
+					<view class="uni-collapse-item__title-box" :class="{'is-disabled':disabled}">
+						<image v-if="thumb" :src="thumb" class="uni-collapse-item__title-img" />
+						<text class="uni-collapse-item__title-text">{{ title }}</text>
+					</view>
+				</slot>
+			</view>
+			<view v-if="showArrow"
+				:class="{ 'uni-collapse-item__title-arrow-active': isOpen, 'uni-collapse-item--animation': showAnimation === true }"
+				class="uni-collapse-item__title-arrow">
+				<uni-icons :color="disabled?'#ddd':'#bbb'" size="14" type="bottom" />
+			</view>
+		</view>
+		<view class="uni-collapse-item__wrap" :class="{'is--transition':showAnimation}"
+			:style="{height: (isOpen?height:0) +'px'}">
+			<view :id="elId" ref="collapse--hook" class="uni-collapse-item__wrap-content"
+				:class="{open:isheight,'uni-collapse-item--border':border&&isOpen}">
+				<slot></slot>
+			</view>
+		</view>
+
+	</view>
+</template>
+
+<script>
+	// #ifdef APP-NVUE
+	const dom = weex.requireModule('dom')
+	// #endif
+	/**
+	 * CollapseItem 折叠面板子组件
+	 * @description 折叠面板子组件
+	 * @property {String} title 标题文字
+	 * @property {String} thumb 标题左侧缩略图
+	 * @property {String} name 唯一标志符
+	 * @property {Boolean} open = [true|false] 是否展开组件
+	 * @property {Boolean} titleBorder = [true|false] 是否显示标题分隔线
+	 * @property {Boolean} border = [true|false] 是否显示分隔线
+	 * @property {Boolean} disabled = [true|false] 是否展开面板
+	 * @property {Boolean} showAnimation = [true|false] 开启动画
+	 * @property {Boolean} showArrow = [true|false] 是否显示右侧箭头
+	 */
+	export default {
+		name: 'uniCollapseItem',
+		props: {
+			// 列表标题
+			title: {
+				type: String,
+				default: ''
+			},
+			name: {
+				type: [Number, String],
+				default: ''
+			},
+			// 是否禁用
+			disabled: {
+				type: Boolean,
+				default: false
+			},
+			// #ifdef APP-PLUS
+			// 是否显示动画,app 端默认不开启动画,卡顿严重
+			showAnimation: {
+				type: Boolean,
+				default: false
+			},
+			// #endif
+			// #ifndef APP-PLUS
+			// 是否显示动画
+			showAnimation: {
+				type: Boolean,
+				default: true
+			},
+			// #endif
+			// 是否展开
+			open: {
+				type: Boolean,
+				default: false
+			},
+			// 缩略图
+			thumb: {
+				type: String,
+				default: ''
+			},
+			// 标题分隔线显示类型
+			titleBorder: {
+				type: String,
+				default: 'auto'
+			},
+			border: {
+				type: Boolean,
+				default: true
+			},
+			showArrow: {
+				type: Boolean,
+				default: true
+			}
+		},
+		data() {
+			// TODO 随机生生元素ID,解决百度小程序获取同一个元素位置信息的bug
+			const elId = `Uni_${Math.ceil(Math.random() * 10e5).toString(36)}`
+			return {
+				isOpen: false,
+				isheight: null,
+				height: 0,
+				elId,
+				nameSync: 0
+			}
+		},
+		watch: {
+			open(val) {
+				this.isOpen = val
+				this.onClick(val, 'init')
+			}
+		},
+		updated(e) {
+			this.$nextTick(() => {
+				this.init(true)
+			})
+		},
+		created() {
+			this.collapse = this.getCollapse()
+			this.oldHeight = 0
+			this.onClick(this.open, 'init')
+		},
+		// #ifndef VUE3
+		// TODO vue2
+		destroyed() {
+			if (this.__isUnmounted) return
+			this.uninstall()
+		},
+		// #endif
+		// #ifdef VUE3
+		// TODO vue3
+		unmounted() {
+			this.__isUnmounted = true
+			this.uninstall()
+		},
+		// #endif
+		mounted() {
+			if (!this.collapse) return
+			if (this.name !== '') {
+				this.nameSync = this.name
+			} else {
+				this.nameSync = this.collapse.childrens.length + ''
+			}
+			if (this.collapse.names.indexOf(this.nameSync) === -1) {
+				this.collapse.names.push(this.nameSync)
+			} else {
+				console.warn(`name 值 ${this.nameSync} 重复`);
+			}
+			if (this.collapse.childrens.indexOf(this) === -1) {
+				this.collapse.childrens.push(this)
+			}
+			this.init()
+		},
+		methods: {
+			init(type) {
+				// #ifndef APP-NVUE
+				this.getCollapseHeight(type)
+				// #endif
+				// #ifdef APP-NVUE
+				this.getNvueHwight(type)
+				// #endif
+			},
+			uninstall() {
+				if (this.collapse) {
+					this.collapse.childrens.forEach((item, index) => {
+						if (item === this) {
+							this.collapse.childrens.splice(index, 1)
+						}
+					})
+					this.collapse.names.forEach((item, index) => {
+						if (item === this.nameSync) {
+							this.collapse.names.splice(index, 1)
+						}
+					})
+				}
+			},
+			onClick(isOpen, type) {
+				if (this.disabled) return
+				this.isOpen = isOpen
+				if (this.isOpen && this.collapse) {
+					this.collapse.setAccordion(this)
+				}
+				if (type !== 'init') {
+					this.collapse.onChange(isOpen, this)
+				}
+			},
+			getCollapseHeight(type, index = 0) {
+				const views = uni.createSelectorQuery().in(this)
+				views
+					.select(`#${this.elId}`)
+					.fields({
+						size: true
+					}, data => {
+						// TODO 百度中可能获取不到节点信息 ,需要循环获取
+						if (index >= 10) return
+						if (!data) {
+							index++
+							this.getCollapseHeight(false, index)
+							return
+						}
+						// #ifdef APP-NVUE
+						this.height = data.height + 1
+						// #endif
+						// #ifndef APP-NVUE
+						this.height = data.height
+						// #endif
+						this.isheight = true
+						if (type) return
+						this.onClick(this.isOpen, 'init')
+					})
+					.exec()
+			},
+			getNvueHwight(type) {
+				const result = dom.getComponentRect(this.$refs['collapse--hook'], option => {
+					if (option && option.result && option.size) {
+						// #ifdef APP-NVUE
+						this.height = option.size.height + 1
+						// #endif
+						// #ifndef APP-NVUE
+						this.height = option.size.height
+						// #endif
+						this.isheight = true
+						if (type) return
+						this.onClick(this.open, 'init')
+					}
+				})
+			},
+			/**
+			 * 获取父元素实例
+			 */
+			getCollapse(name = 'uniCollapse') {
+				let parent = this.$parent;
+				let parentName = parent.$options.name;
+				while (parentName !== name) {
+					parent = parent.$parent;
+					if (!parent) return false;
+					parentName = parent.$options.name;
+				}
+				return parent;
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	.uni-collapse-item {
+		/* #ifndef APP-NVUE */
+		box-sizing: border-box;
+
+		/* #endif */
+		&__title {
+			/* #ifndef APP-NVUE */
+			display: flex;
+			width: 100%;
+			box-sizing: border-box;
+			/* #endif */
+			flex-direction: row;
+			align-items: center;
+			transition: border-bottom-color .3s;
+
+			// transition-property: border-bottom-color;
+			// transition-duration: 5s;
+			&-wrap {
+				width: 100%;
+				flex: 1;
+
+			}
+
+			&-box {
+				padding: 0 15px;
+				/* #ifndef APP-NVUE */
+				display: flex;
+				width: 100%;
+				box-sizing: border-box;
+				/* #endif */
+				flex-direction: row;
+				justify-content: space-between;
+				align-items: center;
+				height: 48px;
+				line-height: 48px;
+				background-color: #fff;
+				color: #303133;
+				font-size: 13px;
+				font-weight: 500;
+				/* #ifdef H5 */
+				cursor: pointer;
+				outline: none;
+
+				/* #endif */
+				&.is-disabled {
+					.uni-collapse-item__title-text {
+						color: #999;
+					}
+				}
+
+			}
+
+			&.uni-collapse-item-border {
+				border-bottom: 1px solid #ebeef5;
+			}
+
+			&.is-open {
+				border-bottom-color: transparent;
+			}
+
+			&-img {
+				height: 22px;
+				width: 22px;
+				margin-right: 10px;
+			}
+
+			&-text {
+				flex: 1;
+				font-size: 14px;
+				/* #ifndef APP-NVUE */
+				white-space: nowrap;
+				color: inherit;
+				/* #endif */
+				/* #ifdef APP-NVUE */
+				lines: 1;
+				/* #endif */
+				overflow: hidden;
+				text-overflow: ellipsis;
+			}
+
+			&-arrow {
+				/* #ifndef APP-NVUE */
+				display: flex;
+				box-sizing: border-box;
+				/* #endif */
+				align-items: center;
+				justify-content: center;
+				width: 20px;
+				height: 20px;
+				margin-right: 10px;
+				transform: rotate(0deg);
+
+				&-active {
+					transform: rotate(-180deg);
+				}
+			}
+
+
+		}
+
+		&__wrap {
+			/* #ifndef APP-NVUE */
+			will-change: height;
+			box-sizing: border-box;
+			/* #endif */
+			background-color: #fff;
+			overflow: hidden;
+			position: relative;
+			height: 0;
+
+			&.is--transition {
+				// transition: all 0.3s;
+				transition-property: height, border-bottom-width;
+				transition-duration: 0.3s;
+				/* #ifndef APP-NVUE */
+				will-change: height;
+				/* #endif */
+			}
+
+
+
+			&-content {
+				position: absolute;
+				font-size: 13px;
+				color: #303133;
+				// transition: height 0.3s;
+				border-bottom-color: transparent;
+				border-bottom-style: solid;
+				border-bottom-width: 0;
+
+				&.uni-collapse-item--border {
+					border-bottom-width: 1px;
+					border-bottom-color: red;
+					border-bottom-color: #ebeef5;
+				}
+
+				&.open {
+					position: relative;
+				}
+			}
+		}
+
+		&--animation {
+			transition-property: transform;
+			transition-duration: 0.3s;
+			transition-timing-function: ease;
+		}
+
+	}
+</style>

+ 147 - 0
components/uni-collapse/uni-collapse.vue

@@ -0,0 +1,147 @@
+<template>
+	<view class="uni-collapse">
+		<slot />
+	</view>
+</template>
+<script>
+	/**
+	 * Collapse 折叠面板
+	 * @description 展示可以折叠 / 展开的内容区域
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=23
+	 * @property {String|Array} value 当前激活面板改变时触发(如果是手风琴模式,参数类型为string,否则为array)
+	 * @property {Boolean} accordion = [true|false] 是否开启手风琴效果是否开启手风琴效果
+	 * @event {Function} change 切换面板时触发,如果是手风琴模式,返回类型为string,否则为array
+	 */
+	export default {
+		name: 'uniCollapse',
+		emits:['change','activeItem','input','update:modelValue'],
+		props: {
+			value: {
+				type: [String, Array],
+				default: ''
+			},
+			modelValue: {
+				type: [String, Array],
+				default: ''
+			},
+			accordion: {
+				// 是否开启手风琴效果
+				type: [Boolean, String],
+				default: false
+			},
+		},
+		data() {
+			return {}
+		},
+		computed: {
+			// TODO 兼容 vue2 和 vue3
+			dataValue() {
+				let value = (typeof this.value === 'string' && this.value === '') ||
+					(Array.isArray(this.value) && this.value.length === 0)
+				let modelValue = (typeof this.modelValue === 'string' && this.modelValue === '') ||
+					(Array.isArray(this.modelValue) && this.modelValue.length === 0)
+				if (value) {
+					return this.modelValue
+				}
+				if (modelValue) {
+					return this.value
+				}
+
+				return this.value
+			}
+		},
+		watch: {
+			dataValue(val) {
+				this.setOpen(val)
+			}
+		},
+		created() {
+			this.childrens = []
+			this.names = []
+		},
+		mounted() {
+			this.$nextTick(()=>{
+				this.setOpen(this.dataValue)
+			})
+		},
+		methods: {
+			setOpen(val) {
+				let str = typeof val === 'string'
+				let arr = Array.isArray(val)
+				this.childrens.forEach((vm, index) => {
+					if (str) {
+						if (val === vm.nameSync) {
+							if (!this.accordion) {
+								console.warn('accordion 属性为 false ,v-model 类型应该为 array')
+								return
+							}
+							vm.isOpen = true
+						}
+					}
+					if (arr) {
+						val.forEach(v => {
+							if (v === vm.nameSync) {
+								if (this.accordion) {
+									console.warn('accordion 属性为 true ,v-model 类型应该为 string')
+									return
+								}
+								vm.isOpen = true
+							}
+						})
+					}
+				})
+				this.emit(val)
+			},
+			setAccordion(self) {
+				if (!this.accordion) return
+				this.childrens.forEach((vm, index) => {
+					if (self !== vm) {
+						vm.isOpen = false
+					}
+				})
+			},
+			resize() {
+				this.childrens.forEach((vm, index) => {
+					// #ifndef APP-NVUE
+					vm.getCollapseHeight()
+					// #endif
+					// #ifdef APP-NVUE
+					vm.getNvueHwight()
+					// #endif
+				})
+			},
+			onChange(isOpen, self) {
+				let activeItem = []
+
+				if (this.accordion) {
+					activeItem = isOpen ? self.nameSync : ''
+				} else {
+					this.childrens.forEach((vm, index) => {
+						if (vm.isOpen) {
+							activeItem.push(vm.nameSync)
+						}
+					})
+				}
+				this.$emit('change', activeItem)
+				this.emit(activeItem)
+			},
+			emit(val){
+				this.$emit('input', val)
+				this.$emit('update:modelValue', val)
+			}
+		}
+	}
+</script>
+<style lang="scss" >
+	.uni-collapse {
+		/* #ifndef APP-NVUE */
+		width: 100%;
+		display: flex;
+		/* #endif */
+		/* #ifdef APP-NVUE */
+		flex: 1;
+		/* #endif */
+		flex-direction: column;
+		background-color: #fff;
+	}
+</style>

+ 6 - 0
components/uni-countdown/i18n/en.json

@@ -0,0 +1,6 @@
+{
+	"uni-countdown.day": "day",
+	"uni-countdown.h": "h",
+	"uni-countdown.m": "m",
+	"uni-countdown.s": "s"
+}

+ 8 - 0
components/uni-countdown/i18n/index.js

@@ -0,0 +1,8 @@
+import en from './en.json'
+import zhHans from './zh-Hans.json'
+import zhHant from './zh-Hant.json'
+export default {
+	en,
+	'zh-Hans': zhHans,
+	'zh-Hant': zhHant
+}

+ 6 - 0
components/uni-countdown/i18n/zh-Hans.json

@@ -0,0 +1,6 @@
+{
+	"uni-countdown.day": "天",
+	"uni-countdown.h": "时",
+	"uni-countdown.m": "分",
+	"uni-countdown.s": "秒"
+}

+ 6 - 0
components/uni-countdown/i18n/zh-Hant.json

@@ -0,0 +1,6 @@
+{
+	"uni-countdown.day": "天",
+	"uni-countdown.h": "時",
+	"uni-countdown.m": "分",
+	"uni-countdown.s": "秒"
+}

+ 271 - 0
components/uni-countdown/uni-countdown.vue

@@ -0,0 +1,271 @@
+<template>
+	<view class="uni-countdown">
+		<text v-if="showDay" :style="[timeStyle]" class="uni-countdown__number">{{ d }}</text>
+		<text v-if="showDay" :style="[splitorStyle]" class="uni-countdown__splitor">{{dayText}}</text>
+		<text :style="[timeStyle]" class="uni-countdown__number">{{ h }}</text>
+		<text :style="[splitorStyle]" class="uni-countdown__splitor">{{ showColon ? ':' : hourText }}</text>
+		<text :style="[timeStyle]" class="uni-countdown__number">{{ i }}</text>
+		<text :style="[splitorStyle]" class="uni-countdown__splitor">{{ showColon ? ':' : minuteText }}</text>
+		<text :style="[timeStyle]" class="uni-countdown__number">{{ s }}</text>
+		<text v-if="!showColon" :style="[splitorStyle]" class="uni-countdown__splitor">{{secondText}}</text>
+	</view>
+</template>
+<script>
+	import {
+		initVueI18n
+	} from '@dcloudio/uni-i18n'
+	import messages from './i18n/index.js'
+	const {
+		t
+	} = initVueI18n(messages)
+	/**
+	 * Countdown 倒计时
+	 * @description 倒计时组件
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=25
+	 * @property {String} backgroundColor 背景色
+	 * @property {String} color 文字颜色
+	 * @property {Number} day 天数
+	 * @property {Number} hour 小时
+	 * @property {Number} minute 分钟
+	 * @property {Number} second 秒
+	 * @property {Number} timestamp 时间戳
+	 * @property {Boolean} showDay = [true|false] 是否显示天数
+	 * @property {Boolean} show-colon = [true|false] 是否以冒号为分隔符
+	 * @property {String} splitorColor 分割符号颜色
+	 * @event {Function} timeup 倒计时时间到触发事件
+	 * @example <uni-countdown :day="1" :hour="1" :minute="12" :second="40"></uni-countdown>
+	 */
+	export default {
+		name: 'UniCountdown',
+		emits: ['timeup'],
+		props: {
+			showDay: {
+				type: Boolean,
+				default: true
+			},
+			showColon: {
+				type: Boolean,
+				default: true
+			},
+			start: {
+				type: Boolean,
+				default: true
+			},
+			backgroundColor: {
+				type: String,
+				default: ''
+			},
+			color: {
+				type: String,
+				default: '#333'
+			},
+			fontSize: {
+				type: Number,
+				default: 14
+			},
+			splitorColor: {
+				type: String,
+				default: '#333'
+			},
+			day: {
+				type: Number,
+				default: 0
+			},
+			hour: {
+				type: Number,
+				default: 0
+			},
+			minute: {
+				type: Number,
+				default: 0
+			},
+			second: {
+				type: Number,
+				default: 0
+			},
+			timestamp: {
+				type: Number,
+				default: 0
+			}
+		},
+		data() {
+			return {
+				timer: null,
+				syncFlag: false,
+				d: '00',
+				h: '00',
+				i: '00',
+				s: '00',
+				leftTime: 0,
+				seconds: 0
+			}
+		},
+		computed: {
+			dayText() {
+				return t("uni-countdown.day")
+			},
+			hourText(val) {
+				return t("uni-countdown.h")
+			},
+			minuteText(val) {
+				return t("uni-countdown.m")
+			},
+			secondText(val) {
+				return t("uni-countdown.s")
+			},
+			timeStyle() {
+				const {
+					color,
+					backgroundColor,
+					fontSize
+				} = this
+				return {
+					color,
+					backgroundColor,
+					fontSize: `${fontSize}px`,
+					width: `${fontSize * 22 / 14}px`, // 按字体大小为 14px 时的比例缩放
+ 					lineHeight: `${fontSize * 20 / 14}px`,
+					borderRadius: `${fontSize * 3 / 14}px`,
+				}
+			},
+			splitorStyle() {
+				const { splitorColor, fontSize, backgroundColor } = this
+				return {
+					color: splitorColor,
+					fontSize: `${fontSize * 12 / 14}px`,
+					margin: backgroundColor ? `${fontSize * 4 / 14}px` : ''
+				}
+			}
+		},
+		watch: {
+			day(val) {
+				this.changeFlag()
+			},
+			hour(val) {
+				this.changeFlag()
+			},
+			minute(val) {
+				this.changeFlag()
+			},
+			second(val) {
+				this.changeFlag()
+			},
+			start: {
+				immediate: true,
+				handler(newVal, oldVal) {
+					if (newVal) {
+						this.startData();
+					} else {
+						if (!oldVal) return
+						clearInterval(this.timer)
+					}
+				}
+
+			}
+		},
+		created: function(e) {
+			this.seconds = this.toSeconds(this.timestamp, this.day, this.hour, this.minute, this.second)
+			this.countDown()
+		},
+		// #ifndef VUE3
+		destroyed() {
+			clearInterval(this.timer)
+		},
+		// #endif
+		// #ifdef VUE3
+		unmounted() {
+			clearInterval(this.timer)
+		},
+		// #endif
+		methods: {
+			toSeconds(timestamp, day, hours, minutes, seconds) {
+				if (timestamp) {
+					return timestamp - parseInt(new Date().getTime() / 1000, 10)
+				}
+				return day * 60 * 60 * 24 + hours * 60 * 60 + minutes * 60 + seconds
+			},
+			timeUp() {
+				clearInterval(this.timer)
+				this.$emit('timeup')
+			},
+			countDown() {
+				let seconds = this.seconds
+				let [day, hour, minute, second] = [0, 0, 0, 0]
+				if (seconds > 0) {
+					day = Math.floor(seconds / (60 * 60 * 24))
+					hour = Math.floor(seconds / (60 * 60)) - (day * 24)
+					minute = Math.floor(seconds / 60) - (day * 24 * 60) - (hour * 60)
+					second = Math.floor(seconds) - (day * 24 * 60 * 60) - (hour * 60 * 60) - (minute * 60)
+				} else {
+					this.timeUp()
+				}
+				if (day < 10) {
+					day = '0' + day
+				}
+				if (hour < 10) {
+					hour = '0' + hour
+				}
+				if (minute < 10) {
+					minute = '0' + minute
+				}
+				if (second < 10) {
+					second = '0' + second
+				}
+				this.d = day
+				this.h = hour
+				this.i = minute
+				this.s = second
+			},
+			startData() {
+				this.seconds = this.toSeconds(this.timestamp, this.day, this.hour, this.minute, this.second)
+				if (this.seconds <= 0) {
+					this.seconds = this.toSeconds(0, 0, 0, 0, 0)
+					this.countDown()
+					return
+				}
+				clearInterval(this.timer)
+				this.countDown()
+				this.timer = setInterval(() => {
+					this.seconds--
+					if (this.seconds < 0) {
+						this.timeUp()
+						return
+					}
+					this.countDown()
+				}, 1000)
+			},
+			update(){
+				this.startData();
+			},
+			changeFlag() {
+				if (!this.syncFlag) {
+					this.seconds = this.toSeconds(this.timestamp, this.day, this.hour, this.minute, this.second)
+					this.startData();
+					this.syncFlag = true;
+				}
+			}
+		}
+	}
+</script>
+<style lang="scss" scoped>
+	$font-size: 14px;
+
+	.uni-countdown {
+		display: flex;
+		flex-direction: row;
+		justify-content: flex-start;
+		align-items: center;
+
+		&__splitor {
+			margin: 0 2px;
+			font-size: $font-size;
+			color: #333;
+		}
+
+		&__number {
+			border-radius: 3px;
+			text-align: center;
+			font-size: $font-size;
+		}
+	}
+</style>

+ 526 - 0
components/uni-data-select/uni-data-select.vue

@@ -0,0 +1,526 @@
+<template>
+	<view class="uni-stat__select">
+		<span v-if="label" class="uni-label-text hide-on-phone">{{label + ':'}}</span>
+		<view class="uni-stat-box" :class="{'uni-stat__actived': current}">
+			<view class="uni-select" :class="{'uni-select--disabled':disabled,'uni-select--border':border}">
+				<view class="uni-select__input-box" @click="toggleSelector">
+					<view v-if="current" class="uni-select__input-text">{{current}}</view>
+					<view v-else class="uni-select__input-text uni-select__input-placeholder">{{typePlaceholder}}</view>
+					<view v-if="current && clear && !disabled" @click.stop="clearVal" >
+						<uni-icons type="clear" color="#c0c4cc" size="24"/>
+					</view>
+					<view v-else>
+						<uni-icons :type="showSelector? 'top' : 'bottom'" size="14" color="#999" />
+					</view>
+				</view>
+				<view class="uni-select--mask" v-if="showSelector" @click="toggleSelector" />
+				<view class="uni-select__selector" v-if="showSelector">
+					<view class="uni-popper__arrow"></view>
+					<scroll-view scroll-y="true" class="uni-select__selector-scroll">
+						<view class="uni-select__selector-empty" v-if="mixinDatacomResData.length === 0">
+							<text>{{emptyTips}}</text>
+						</view>
+						<view v-else class="uni-select__selector-item" v-for="(item,index) in mixinDatacomResData" :key="index"
+							@click="change(item)">
+							<text :class="{'uni-select__selector__disabled': item.disable}">{{formatItemName(item)}}</text>
+						</view>
+					</scroll-view>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	/**
+	 * DataChecklist 数据选择器
+	 * @description 通过数据渲染的下拉框组件
+	 * @tutorial https://uniapp.dcloud.io/component/uniui/uni-data-select
+	 * @property {String} value 默认值
+	 * @property {Array} localdata 本地数据 ,格式 [{text:'',value:''}]
+	 * @property {Boolean} clear 是否可以清空已选项
+	 * @property {Boolean} emptyText 没有数据时显示的文字 ,本地数据无效
+	 * @property {String} label 左侧标题
+	 * @property {String} placeholder 输入框的提示文字
+	 * @property {Boolean} disabled 是否禁用
+	 * @event {Function} change  选中发生变化触发
+	 */
+
+	export default {
+		name: "uni-data-select",
+		mixins: [uniCloud.mixinDatacom || {}],
+		props: {
+			localdata: {
+				type: Array,
+				default () {
+					return []
+				}
+			},
+			value: {
+				type: [String, Number],
+				default: ''
+			},
+			modelValue: {
+				type: [String, Number],
+				default: ''
+			},
+			label: {
+				type: String,
+				default: ''
+			},
+			placeholder: {
+				type: String,
+				default: '请选择'
+			},
+			emptyTips: {
+				type: String,
+				default: '无选项'
+			},
+			clear: {
+				type: Boolean,
+				default: true
+			},
+			defItem: {
+				type: Number,
+				default: 0
+			},
+			disabled: {
+				type: Boolean,
+				default: false
+			},
+			border: {
+				type: Boolean,
+				default: true
+			},
+			// 格式化输出 用法 field="_id as value, version as text, uni_platform as label" format="{label} - {text}"
+			format: {
+				type: String,
+				default: ''
+			},
+		},
+		data() {
+			return {
+				showSelector: false,
+				current: '',
+				mixinDatacomResData: [],
+				apps: [],
+				channels: [],
+				cacheKey: "uni-data-select-lastSelectedValue",
+			};
+		},
+		created() {
+			this.debounceGet = this.debounce(() => {
+				this.query();
+			}, 300);
+			if (this.collection && !this.localdata.length) {
+				this.debounceGet();
+			}
+		},
+		computed: {
+			typePlaceholder() {
+				const text = {
+					'opendb-stat-app-versions': '版本',
+					'opendb-app-channels': '渠道',
+					'opendb-app-list': '应用'
+				}
+				const common = this.placeholder
+				const placeholder = text[this.collection]
+				return placeholder ?
+					common + placeholder :
+					common
+			},
+			valueCom(){
+				// #ifdef VUE3
+				return this.modelValue;
+				// #endif
+				// #ifndef VUE3
+				return this.value;
+				// #endif
+			}
+		},
+		watch: {
+			localdata: {
+				immediate: true,
+				handler(val, old) {
+					if (Array.isArray(val) && old !== val) {
+						this.mixinDatacomResData = val
+					}
+				}
+			},
+			valueCom(val, old) {
+				this.initDefVal()
+			},
+			mixinDatacomResData: {
+				immediate: true,
+				handler(val) {
+					if (val.length) {
+						this.initDefVal()
+					}
+				}
+			}
+		},
+		methods: {
+			debounce(fn, time = 100){
+				let timer = null
+				return function(...args) {
+					if (timer) clearTimeout(timer)
+					timer = setTimeout(() => {
+						fn.apply(this, args)
+					}, time)
+				}
+			},
+			// 执行数据库查询
+			query(){
+				this.mixinDatacomEasyGet();
+			},
+			// 监听查询条件变更事件
+			onMixinDatacomPropsChange(){
+				if (this.collection) {
+					this.debounceGet();
+				}
+			},
+			initDefVal() {
+				let defValue = ''
+				if ((this.valueCom || this.valueCom === 0) && !this.isDisabled(this.valueCom)) {
+					defValue = this.valueCom
+				} else {
+					let strogeValue
+					if (this.collection) {
+						strogeValue = this.getCache()
+					}
+					if (strogeValue || strogeValue === 0) {
+						defValue = strogeValue
+					} else {
+						let defItem = ''
+						if (this.defItem > 0 && this.defItem <= this.mixinDatacomResData.length) {
+							defItem = this.mixinDatacomResData[this.defItem - 1].value
+						}
+						defValue = defItem
+					}
+          if (defValue || defValue === 0) {
+					  this.emit(defValue)
+          }
+				}
+				const def = this.mixinDatacomResData.find(item => item.value === defValue)
+				this.current = def ? this.formatItemName(def) : ''
+			},
+
+			/**
+			 * @param {[String, Number]} value
+			 * 判断用户给的 value 是否同时为禁用状态
+			 */
+			isDisabled(value) {
+				let isDisabled = false;
+
+				this.mixinDatacomResData.forEach(item => {
+					if (item.value === value) {
+						isDisabled = item.disable
+					}
+				})
+
+				return isDisabled;
+			},
+
+			clearVal() {
+				this.emit('')
+				if (this.collection) {
+					this.removeCache()
+				}
+			},
+			change(item) {
+				if (!item.disable) {
+					this.showSelector = false
+					this.current = this.formatItemName(item)
+					this.emit(item.value)
+				}
+			},
+			emit(val) {
+				this.$emit('input', val)
+				this.$emit('update:modelValue', val)
+				this.$emit('change', val)
+				if (this.collection) {
+					this.setCache(val);
+				}
+			},
+			toggleSelector() {
+				if (this.disabled) {
+					return
+				}
+
+				this.showSelector = !this.showSelector
+			},
+			formatItemName(item) {
+				let {
+					text,
+					value,
+					channel_code
+				} = item
+				channel_code = channel_code ? `(${channel_code})` : ''
+
+				if (this.format) {
+					// 格式化输出
+					let str = "";
+					str = this.format;
+					for (let key in item) {
+						str = str.replace(new RegExp(`{${key}}`,"g"),item[key]);
+					}
+					return str;
+				} else {
+					return this.collection.indexOf('app-list') > 0 ?
+						`${text}(${value})` :
+						(
+							text ?
+							text :
+							`未命名${channel_code}`
+						)
+				}
+			},
+			// 获取当前加载的数据
+			getLoadData(){
+				return this.mixinDatacomResData;
+			},
+			// 获取当前缓存key
+			getCurrentCacheKey(){
+				return this.collection;
+			},
+			// 获取缓存
+			getCache(name=this.getCurrentCacheKey()){
+				let cacheData = uni.getStorageSync(this.cacheKey) || {};
+				return cacheData[name];
+			},
+			// 设置缓存
+			setCache(value, name=this.getCurrentCacheKey()){
+				let cacheData = uni.getStorageSync(this.cacheKey) || {};
+				cacheData[name] = value;
+				uni.setStorageSync(this.cacheKey, cacheData);
+			},
+			// 删除缓存
+			removeCache(name=this.getCurrentCacheKey()){
+				let cacheData = uni.getStorageSync(this.cacheKey) || {};
+				delete cacheData[name];
+				uni.setStorageSync(this.cacheKey, cacheData);
+			},
+		}
+	}
+</script>
+
+<style lang="scss">
+	$uni-base-color: #6a6a6a !default;
+	$uni-main-color: #333 !default;
+	$uni-secondary-color: #909399 !default;
+	$uni-border-3: #e5e5e5;
+
+
+	/* #ifndef APP-NVUE */
+	@media screen and (max-width: 500px) {
+		.hide-on-phone {
+			display: none;
+		}
+	}
+
+	/* #endif */
+	.uni-stat__select {
+		display: flex;
+		align-items: center;
+		// padding: 15px;
+		/* #ifdef H5 */
+		cursor: pointer;
+		/* #endif */
+		width: 100%;
+		flex: 1;
+		box-sizing: border-box;
+	}
+
+	.uni-stat-box {
+		width: 100%;
+		flex: 1;
+	}
+
+	.uni-stat__actived {
+		width: 100%;
+		flex: 1;
+		// outline: 1px solid #2979ff;
+	}
+
+	.uni-label-text {
+		font-size: 14px;
+		font-weight: bold;
+		color: $uni-base-color;
+		margin: auto 0;
+		margin-right: 5px;
+	}
+
+	.uni-select {
+		font-size: 14px;
+		// border: 1px solid $uni-border-3;
+		box-sizing: border-box;
+		border-radius: 4px;
+		padding: 0 5px;
+		padding-left: 10px;
+		position: relative;
+		/* #ifndef APP-NVUE */
+		display: flex;
+		user-select: none;
+		/* #endif */
+		flex-direction: row;
+		align-items: center;
+		// border-bottom: solid 1px $uni-border-3;
+		width: 100%;
+		flex: 1;
+		height: 35px;
+
+		&--disabled {
+			background-color: #f5f7fa;
+			cursor: not-allowed;
+		}
+		
+		&--border {
+			border: 1px solid $uni-border-3;
+			border-bottom: solid 1px $uni-border-3;
+		}
+	}
+
+	.uni-select__label {
+		font-size: 16px;
+		// line-height: 22px;
+		height: 35px;
+		padding-right: 10px;
+		color: $uni-secondary-color;
+	}
+
+	.uni-select__input-box {
+		height: 35px;
+		position: relative;
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex: 1;
+		flex-direction: row;
+		align-items: center;
+	}
+
+	.uni-select__input {
+		flex: 1;
+		font-size: 14px;
+		height: 22px;
+		line-height: 22px;
+	}
+
+	.uni-select__input-plac {
+		font-size: 14px;
+		color: $uni-secondary-color;
+	}
+
+	.uni-select__selector {
+		/* #ifndef APP-NVUE */
+		box-sizing: border-box;
+		/* #endif */
+		position: absolute;
+		top: calc(100% + 12px);
+		left: 0;
+		width: 100%;
+		background-color: #FFFFFF;
+		border: 1px solid #EBEEF5;
+		border-radius: 6px;
+		box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
+		z-index: 3;
+		padding: 4px 0;
+	}
+
+	.uni-select__selector-scroll {
+		/* #ifndef APP-NVUE */
+		max-height: 200px;
+		box-sizing: border-box;
+		/* #endif */
+	}
+
+	/* #ifdef H5 */
+	@media (min-width: 768px) {
+		.uni-select__selector-scroll {
+			max-height: 600px;
+		}
+	}
+	/* #endif */
+
+	.uni-select__selector-empty,
+	.uni-select__selector-item {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		cursor: pointer;
+		/* #endif */
+		line-height: 35px;
+		font-size: 14px;
+		text-align: center;
+		/* border-bottom: solid 1px $uni-border-3; */
+		padding: 0px 10px;
+	}
+
+	.uni-select__selector-item:hover {
+		background-color: #f9f9f9;
+	}
+
+	.uni-select__selector-empty:last-child,
+	.uni-select__selector-item:last-child {
+		/* #ifndef APP-NVUE */
+		border-bottom: none;
+		/* #endif */
+	}
+
+	.uni-select__selector__disabled {
+		opacity: 0.4;
+		cursor: default;
+	}
+
+	/* picker 弹出层通用的指示小三角 */
+	.uni-popper__arrow,
+	.uni-popper__arrow::after {
+		position: absolute;
+		display: block;
+		width: 0;
+		height: 0;
+		border-color: transparent;
+		border-style: solid;
+		border-width: 6px;
+	}
+
+	.uni-popper__arrow {
+		filter: drop-shadow(0 2px 12px rgba(0, 0, 0, 0.03));
+		top: -6px;
+		left: 10%;
+		margin-right: 3px;
+		border-top-width: 0;
+		border-bottom-color: #EBEEF5;
+	}
+
+	.uni-popper__arrow::after {
+		content: " ";
+		top: 1px;
+		margin-left: -6px;
+		border-top-width: 0;
+		border-bottom-color: #fff;
+	}
+
+	.uni-select__input-text {
+		// width: 280px;
+		width: 100%;
+		color: $uni-main-color;
+		white-space: nowrap;
+		text-overflow: ellipsis;
+		-o-text-overflow: ellipsis;
+		overflow: hidden;
+	}
+
+	.uni-select__input-placeholder {
+		color: $uni-base-color;
+		font-size: 12px;
+	}
+
+	.uni-select--mask {
+		position: fixed;
+		top: 0;
+		bottom: 0;
+		right: 0;
+		left: 0;
+		z-index: 2;
+	}
+</style>

+ 194 - 0
components/uni-load-more/uni-load-more.vue

@@ -0,0 +1,194 @@
+<template>
+	<view class="uni-load-more">
+		<view class="uni-load-more__img" v-show="status === 'loading' && showIcon">
+			<view class="load1">
+				<view :style="{background:color}"></view>
+				<view :style="{background:color}"></view>
+				<view :style="{background:color}"></view>
+				<view :style="{background:color}"></view>
+			</view>
+			<view class="load2">
+				<view :style="{background:color}"></view>
+				<view :style="{background:color}"></view>
+				<view :style="{background:color}"></view>
+				<view :style="{background:color}"></view>
+			</view>
+			<view class="load3">
+				<view :style="{background:color}"></view>
+				<view :style="{background:color}"></view>
+				<view :style="{background:color}"></view>
+				<view :style="{background:color}"></view>
+			</view>
+		</view>
+		<text class="uni-load-more__text" :style="{color:color}">{{status === 'more' ? contentText.contentdown : (status === 'loading' ? contentText.contentrefresh : contentText.contentnomore)}}</text>
+	</view>
+</template>
+
+<script>
+	export default {
+		name: "uni-load-more",
+		props: {
+			status: {
+				//上拉的状态:more-loading前;loading-loading中;noMore-没有更多了
+				type: String,
+				default: 'more'
+			},
+			showIcon: {
+				type: Boolean,
+				default: true
+			},
+			color: {
+				type: String,
+				default: "#777777"
+			},
+			contentText: {
+				type: Object,
+				default () {
+					return {
+						contentdown: "上拉显示更多",
+						contentrefresh: "正在加载...",
+						contentnomore: "没有更多数据了"
+					};
+				}
+			}
+		},
+		data() {
+			return {}
+		}
+	}
+</script>
+
+<style>
+	@charset "UTF-8";
+
+	.uni-load-more {
+		display: flex;
+		flex-direction: row;
+		height: 80upx;
+		align-items: center;
+		justify-content: center
+	}
+
+	.uni-load-more__text {
+		font-size: 28upx;
+		color: #999
+	}
+
+	.uni-load-more__img {
+		height: 24px;
+		width: 24px;
+		margin-right: 10px
+	}
+
+	.uni-load-more__img>view {
+		position: absolute
+	}
+
+	.uni-load-more__img>view view {
+		width: 6px;
+		height: 2px;
+		border-top-left-radius: 1px;
+		border-bottom-left-radius: 1px;
+		background: #999;
+		position: absolute;
+		opacity: .2;
+		transform-origin: 50%;
+		animation: load 1.56s ease infinite
+	}
+
+	.uni-load-more__img>view view:nth-child(1) {
+		transform: rotate(90deg);
+		top: 2px;
+		left: 9px
+	}
+
+	.uni-load-more__img>view view:nth-child(2) {
+		transform: rotate(180deg);
+		top: 11px;
+		right: 0
+	}
+
+	.uni-load-more__img>view view:nth-child(3) {
+		transform: rotate(270deg);
+		bottom: 2px;
+		left: 9px
+	}
+
+	.uni-load-more__img>view view:nth-child(4) {
+		top: 11px;
+		left: 0
+	}
+
+	.load1,
+	.load2,
+	.load3 {
+		height: 24px;
+		width: 24px
+	}
+
+	.load2 {
+		transform: rotate(30deg)
+	}
+
+	.load3 {
+		transform: rotate(60deg)
+	}
+
+	.load1 view:nth-child(1) {
+		animation-delay: 0s
+	}
+
+	.load2 view:nth-child(1) {
+		animation-delay: .13s
+	}
+
+	.load3 view:nth-child(1) {
+		animation-delay: .26s
+	}
+
+	.load1 view:nth-child(2) {
+		animation-delay: .39s
+	}
+
+	.load2 view:nth-child(2) {
+		animation-delay: .52s
+	}
+
+	.load3 view:nth-child(2) {
+		animation-delay: .65s
+	}
+
+	.load1 view:nth-child(3) {
+		animation-delay: .78s
+	}
+
+	.load2 view:nth-child(3) {
+		animation-delay: .91s
+	}
+
+	.load3 view:nth-child(3) {
+		animation-delay: 1.04s
+	}
+
+	.load1 view:nth-child(4) {
+		animation-delay: 1.17s
+	}
+
+	.load2 view:nth-child(4) {
+		animation-delay: 1.3s
+	}
+
+	.load3 view:nth-child(4) {
+		animation-delay: 1.43s
+	}
+
+	@-webkit-keyframes load {
+		0% {
+			opacity: 1
+		}
+
+		100% {
+			opacity: .2
+		}
+	}
+</style>

+ 202 - 0
components/uni-number-box.vue

@@ -0,0 +1,202 @@
+<template>
+	<view class="uni-numbox">
+		<view class="uni-numbox-minus" 
+			@click="_calcValue('subtract')"
+		>
+			<text class="iconfont icon-jian" :class="minDisabled?'uni-numbox-disabled': ''" ></text>
+		</view>
+		<input 
+			class="uni-numbox-value" 
+			type="number" 
+			:disabled="disabled"
+			:value="inputValue" 
+			
+			@blur="_onBlur"
+		>
+		<view 
+			class="uni-numbox-plus" 
+			@click="_calcValue('add')"
+		>
+			<text class="iconfont icon-jia" :class="maxDisabled?'uni-numbox-disabled': ''" ></text>
+		</view>
+	</view>
+</template>
+<script>
+	export default {
+		name: 'uni-number-box',
+		props: {
+			isMax: {
+				type: Boolean,
+				default: false
+			},
+			isMin: {
+				type: Boolean,
+				default: false
+			},
+			index: {
+				type: Number,
+				default: 0
+			},
+			value: {
+				type: Number,
+				default: 0
+			},
+			min: {
+				type: Number,
+				default: -Infinity
+			},
+			max: {
+				type: Number,
+				default: Infinity
+			},
+			step: {
+				type: Number,
+				default: 1
+			},
+			disabled: {
+				type: Boolean,
+				default: false
+			}
+		},
+		data() {
+			return {
+				inputValue: this.value,
+				minDisabled: false,
+				maxDisabled: false
+			}
+		},
+		created(){
+			this.maxDisabled = this.isMax;
+			this.minDisabled = this.isMin;
+		},
+		computed: {
+			
+		},
+		watch: {
+			value(val) {  
+				this.inputValue = val;  
+			}, 
+			inputValue (number) {
+				const data = {
+					number: number,
+					index: this.index
+				}
+				this.$emit('eventChange', data);
+			}
+		},
+		methods: {
+			_calcValue(type) {
+				const scale = this._getDecimalScale();
+				let value = this.inputValue * scale;
+				let newValue = 0;
+				let step = this.step * scale;
+				
+				if(type === 'subtract'){
+					newValue = value - step;
+					if (newValue <= this.min){
+						this.minDisabled = true;
+					}
+					if(newValue < this.min){
+						newValue = this.min
+					}
+					if(newValue < this.max && this.maxDisabled === true){
+						this.maxDisabled = false;
+					}
+				}else if(type === 'add'){
+					newValue = value + step;
+					if (newValue >= this.max){
+						this.maxDisabled = true;
+					}
+					if(newValue > this.max){
+						newValue = this.max
+					}
+					if(newValue > this.min && this.minDisabled === true){
+						this.minDisabled = false;
+					}
+				}
+				if(newValue === value){
+					return;
+				}
+				this.inputValue = newValue / scale;
+			},
+			_getDecimalScale() {
+				let scale = 1;
+				// 浮点型
+				if (~~this.step !== this.step) {
+					scale = Math.pow(10, (this.step + '').split('.')[1].length);
+				}
+				return scale;
+			},
+			_onBlur(event) {
+				let value = event.detail.value;
+				if (!value) {
+					this.inputValue = 0;
+					return
+				}
+				value = +value;
+				if (value > this.max) {
+					value = this.max;
+				} else if (value < this.min) {
+					value = this.min
+				}
+
+				this.inputValue = value
+			}
+		}
+	}
+</script>
+<style>
+	.uni-numbox {
+		position:absolute;
+		right: 20upx;
+		bottom:  20upx;
+		display: flex;
+		justify-content: flex-start;
+		align-items: center;
+		width:200upx;
+		height: 70upx;
+		background:#f5f5f5;
+	}
+
+	.uni-numbox-minus,
+	.uni-numbox-plus {
+		margin: 0;
+		background-color: #f5f5f5;
+		width: 70upx;
+		height: 100%;
+		line-height: 70upx;
+		text-align: center;
+		position: relative;
+	}
+	.uni-numbox-minus .iconfont,
+	.uni-numbox-plus .iconfont{
+		font-size: 36upx;
+		color: #555;
+	}
+
+	.uni-numbox-minus {
+		border-right: none;
+		border-top-left-radius: 6upx;
+		border-bottom-left-radius: 6upx;
+	}
+
+	.uni-numbox-plus {
+		border-left: none;
+		border-top-right-radius: 6upx;
+		border-bottom-right-radius: 6upx;
+	}
+
+	.uni-numbox-value {
+		position: relative;
+		background-color: #f5f5f5;
+		width: 90upx;
+		height: 50upx;
+		text-align: center;
+		padding: 0;
+		font-size: 30upx;
+	}
+
+	.uni-numbox-disabled.iconfont {
+		color: #d6d6d6;
+	}
+</style>

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

@@ -0,0 +1,263 @@
+<template>
+	<view v-if="showPopup" class="uni-popup" @touchmove.stop.prevent="clear">
+		<uni-transition :mode-class="['fade']" :styles="maskClass" :duration="duration" :show="showTrans" @click="onTap" />
+		<uni-transition :mode-class="ani" :styles="transClass" :duration="duration" :show="showTrans" @click="onTap">
+			<view class="uni-popup__wrapper-box" @click.stop="clear">
+				<slot />
+			</view>
+		</uni-transition>
+	</view>
+</template>
+
+<script>
+	import uniTransition from '../uni-transition/uni-transition.vue'
+
+	/**
+	 * PopUp 弹出层
+	 * @description 弹出层组件,为了解决遮罩弹层的问题
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=329
+	 * @property {String} type = [top|center|bottom] 弹出方式
+	 * 	@value top 顶部弹出
+	 * 	@value center 中间弹出
+	 * 	@value bottom 底部弹出
+	 * @property {Boolean} animation = [ture|false] 是否开启动画
+	 * @property {Boolean} maskClick = [ture|false] 蒙版点击是否关闭弹窗
+	 * @event {Function} change 打开关闭弹窗触发,e={show: false}
+	 */
+
+	export default {
+		name: 'UniPopup',
+		components: {
+			uniTransition
+		},
+		props: {
+			// 开启动画
+			animation: {
+				type: Boolean,
+				default: true
+			},
+			// 弹出层类型,可选值,top: 顶部弹出层;bottom:底部弹出层;center:全屏弹出层
+			type: {
+				type: String,
+				default: 'center'
+			},
+			// maskClick
+			maskClick: {
+				type: Boolean,
+				default: true
+			}
+		},
+		data() {
+			return {
+				duration: 300,
+				ani: [],
+				showPopup: false,
+				showTrans: false,
+				maskClass: {
+					'position': 'fixed',
+					'bottom': 0,
+					'top': 0,
+					'left': 0,
+					'right': 0,
+					'backgroundColor': 'rgba(0, 0, 0, 0.4)'
+				},
+				transClass: {
+					'position': 'fixed',
+					'left': 0,
+					'right': 0,
+				}
+			}
+		},
+		watch: {
+			type: {
+				handler: function(newVal) {
+					switch (this.type) {
+						case 'top':
+							this.ani = ['slide-top']
+							this.transClass = {
+								'position': 'fixed',
+								'left': 0,
+								'right': 0,
+							}
+							break
+						case 'bottom':
+							this.ani = ['slide-bottom']
+							this.transClass = {
+								'position': 'fixed',
+								'left': 0,
+								'right': 0,
+								'bottom': 0
+							}
+							break
+						case 'center':
+							this.ani = ['zoom-out', 'fade']
+							this.transClass = {
+								'position': 'fixed',
+								/* #ifndef APP-NVUE */
+								'display': 'flex',
+								'flexDirection': 'column',
+								/* #endif */
+								'bottom': 0,
+								'left': 0,
+								'right': 0,
+								'top': 0,
+								'justifyContent': 'center',
+								'alignItems': 'center'
+							}
+
+							break
+					}
+				},
+				immediate: true
+			}
+		},
+		created() {
+			if (this.animation) {
+				this.duration = 300
+			} else {
+				this.duration = 0
+			}
+		},
+		methods: {
+			clear(e) {
+				// TODO nvue 取消冒泡
+				e.stopPropagation()
+			},
+			open() {
+				this.showPopup = true
+				this.$nextTick(() => {
+					clearTimeout(this.timer)
+					this.timer = setTimeout(() => {
+						this.showTrans = true
+					}, 50);
+				})
+				this.$emit('change', {
+					show: true
+				})
+			},
+			close(type) {
+				this.showTrans = false
+				this.$nextTick(() => {
+					clearTimeout(this.timer)
+					this.timer = setTimeout(() => {
+						this.$emit('change', {
+							show: false
+						})
+						this.showPopup = false
+					}, 300)
+				})
+			},
+			onTap() {
+				if (!this.maskClick) return
+				this.close()
+			}
+		}
+	}
+</script>
+<style lang="scss" scoped>
+	.uni-popup {
+		position: fixed;
+		/* #ifdef H5 */
+		top: var(--window-top);
+		/* #endif */
+		/* #ifndef H5 */
+		top: 0;
+		/* #endif */
+		bottom: 0;
+		left: 0;
+		right: 0;
+		/* #ifndef APP-NVUE */
+		z-index: 99;
+		/* #endif */
+	}
+
+	.uni-popup__mask {
+		position: absolute;
+		top: 0;
+		bottom: 0;
+		left: 0;
+		right: 0;
+		background-color: $uni-bg-color-mask;
+		opacity: 0;
+	}
+
+	.mask-ani {
+		transition-property: opacity;
+		transition-duration: 0.2s;
+	}
+
+	.uni-top-mask {
+		opacity: 1;
+	}
+
+	.uni-bottom-mask {
+		opacity: 1;
+	}
+
+	.uni-center-mask {
+		opacity: 1;
+	}
+
+	.uni-popup__wrapper {
+		/* #ifndef APP-NVUE */
+		display: block;
+		/* #endif */
+		position: absolute;
+	}
+
+	.top {
+		top: 0;
+		left: 0;
+		right: 0;
+		transform: translateY(-500px);
+	}
+
+	.bottom {
+		bottom: 0;
+		left: 0;
+		right: 0;
+		transform: translateY(500px);
+	}
+
+	.center {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		flex-direction: column;
+		/* #endif */
+		bottom: 0;
+		left: 0;
+		right: 0;
+		top: 0;
+		justify-content: center;
+		align-items: center;
+		transform: scale(1.2);
+		opacity: 0;
+	}
+
+	.uni-popup__wrapper-box {
+		/* #ifndef APP-NVUE */
+		display: block;
+		/* #endif */
+		position: relative;
+	}
+
+	.content-ani {
+		// transition: transform 0.3s;
+		transition-property: transform, opacity;
+		transition-duration: 0.2s;
+	}
+
+
+	.uni-top-content {
+		transform: translateY(0);
+	}
+
+	.uni-bottom-content {
+		transform: translateY(0);
+	}
+
+	.uni-center-content {
+		transform: scale(1);
+		opacity: 1;
+	}
+</style>

+ 269 - 0
components/uni-steps/uni-steps.vue

@@ -0,0 +1,269 @@
+<template>
+	<view class="uni-steps">
+		<view :class="[direction==='column'?'uni-steps__column':'uni-steps__row']">
+			<view :class="[direction==='column'?'uni-steps__column-text-container':'uni-steps__row-text-container']">
+				<view v-for="(item,index) in options" :key="index"
+					:class="[direction==='column'?'uni-steps__column-text':'uni-steps__row-text']">
+					<text :style="{color:index === active?activeColor:deactiveColor}"
+						:class="[direction==='column'?'uni-steps__column-title':'uni-steps__row-title']">{{item.title}}</text>
+					<text :style="{color: deactiveColor}"
+						:class="[direction==='column'?'uni-steps__column-desc':'uni-steps__row-desc']">{{item.desc}}</text>
+				</view>
+			</view>
+			<view :class="[direction==='column'?'uni-steps__column-container':'uni-steps__row-container']">
+				<view :class="[direction==='column'?'uni-steps__column-line-item':'uni-steps__row-line-item']"
+					v-for="(item,index) in options" :key="index">
+					<view
+						:class="[direction==='column'?'uni-steps__column-line':'uni-steps__row-line',direction==='column'?'uni-steps__column-line--before':'uni-steps__row-line--before']"
+						:style="{backgroundColor:index<=active&&index!==0?activeColor:index===0?'transparent':deactiveColor}">
+					</view>
+					<view :class="[direction==='column'?'uni-steps__column-check':'uni-steps__row-check']"
+						v-if="index === active">
+						<uni-icons :color="activeColor" :type="activeIcon" size="14"></uni-icons>
+					</view>
+					<view v-else :class="[direction==='column'?'uni-steps__column-circle':'uni-steps__row-circle']"
+						:style="{backgroundColor:index<active?activeColor:deactiveColor}"></view>
+					<view
+						:class="[direction==='column'?'uni-steps__column-line':'uni-steps__row-line',direction==='column'?'uni-steps__column-line--after':'uni-steps__row-line--after']"
+						:style="{backgroundColor:index<active&&index!==options.length-1?activeColor:index===options.length-1?'transparent':deactiveColor}">
+					</view>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	/**
+	 * Steps 步骤条
+	 * @description 评分组件
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=34
+	 * @property {Number} active 当前步骤
+	 * @property {String} direction = [row|column] 当前步骤
+	 * 	@value row 横向
+	 * 	@value column 纵向
+	 * @property {String} activeColor 选中状态的颜色
+	 * @property {Array} options 数据源,格式为:[{title:'xxx',desc:'xxx'},{title:'xxx',desc:'xxx'}]
+	 */
+
+	export default {
+		name: 'UniSteps',
+		props: {
+			direction: {
+				// 排列方向 row column
+				type: String,
+				default: 'row'
+			},
+			activeColor: {
+				// 激活状态颜色
+				type: String,
+				default: '#1A78F6'
+			},
+			deactiveColor: {
+				// 未激活状态颜色
+				type: String,
+				default: '#B7BDC6'
+			},
+			active: {
+				// 当前步骤
+				type: Number,
+				default: 0
+			},
+			activeIcon: {
+				// 当前步骤
+				type: String,
+				default: 'checkbox-filled'
+			},
+			options: {
+				type: Array,
+				default () {
+					return []
+				}
+			} // 数据
+		},
+		data() {
+			return {}
+		}
+	}
+</script>
+
+<style lang="scss">
+	$uni-primary: #1A78F6 !default;
+	$uni-border-color:#EDEDED;
+	.uni-steps {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		width: 100%;
+		/* #endif */
+		/* #ifdef APP-NVUE */
+		flex: 1;
+		/* #endif */
+		flex-direction: column;
+	}
+
+	.uni-steps__row {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: column;
+	}
+
+	.uni-steps__column {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row-reverse;
+	}
+
+	.uni-steps__row-text-container {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		align-items: flex-end;
+		margin-bottom: 8px;
+	}
+
+	.uni-steps__column-text-container {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: column;
+		flex: 1;
+	}
+
+	.uni-steps__row-text {
+		/* #ifndef APP-NVUE */
+		display: inline-flex;
+		/* #endif */
+		flex: 1;
+		flex-direction: column;
+	}
+
+	.uni-steps__column-text {
+		padding: 6px 0px;
+		border-bottom-style: solid;
+		border-bottom-width: 1px;
+		border-bottom-color: $uni-border-color;
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: column;
+	}
+
+	.uni-steps__row-title {
+		font-size: 14px;
+		line-height: 16px;
+		text-align: center;
+	}
+
+	.uni-steps__column-title {
+		font-size: 14px;
+		text-align: left;
+		line-height: 18px;
+	}
+
+	.uni-steps__row-desc {
+		font-size: 12px;
+		line-height: 14px;
+		text-align: center;
+	}
+
+	.uni-steps__column-desc {
+		font-size: 12px;
+		text-align: left;
+		line-height: 18px;
+	}
+
+	.uni-steps__row-container {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+	}
+
+	.uni-steps__column-container {
+		/* #ifndef APP-NVUE */
+		display: inline-flex;
+		/* #endif */
+		width: 30px;
+		flex-direction: column;
+	}
+
+	.uni-steps__row-line-item {
+		/* #ifndef APP-NVUE */
+		display: inline-flex;
+		/* #endif */
+		flex-direction: row;
+		flex: 1;
+		height: 14px;
+		line-height: 14px;
+		align-items: center;
+		justify-content: center;
+	}
+
+	.uni-steps__column-line-item {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: column;
+		flex: 1;
+		align-items: center;
+		justify-content: center;
+	}
+
+	.uni-steps__row-line {
+		flex: 1;
+		height: 1px;
+		background-color: #B7BDC6;
+	}
+
+	.uni-steps__column-line {
+		width: 1px;
+		background-color: #B7BDC6;
+	}
+
+	.uni-steps__row-line--after {
+		transform: translateX(1px);
+	}
+
+	.uni-steps__column-line--after {
+		flex: 1;
+		transform: translate(0px, 1px);
+	}
+
+	.uni-steps__row-line--before {
+		transform: translateX(-1px);
+	}
+
+	.uni-steps__column-line--before {
+		height: 6px;
+		transform: translate(0px, -13px);
+	}
+
+	.uni-steps__row-circle {
+		width: 5px;
+		height: 5px;
+		border-radius: 50%;
+		background-color: #B7BDC6;
+		margin: 0px 3px;
+	}
+
+	.uni-steps__column-circle {
+		width: 5px;
+		height: 5px;
+		border-radius: 50%;
+		background-color: #B7BDC6;
+		margin: 4px 0px 5px 0px;
+	}
+
+	.uni-steps__row-check {
+		margin: 0px 6px;
+	}
+
+	.uni-steps__column-check {
+		height: 14px;
+		line-height: 14px;
+		margin: 2px 0px;
+	}
+</style>

+ 302 - 0
components/uni-swipe-action-item/bindingx.js

@@ -0,0 +1,302 @@
+let bindIngXMixins = {}
+
+// #ifdef APP-NVUE
+const BindingX = uni.requireNativePlugin('bindingx');
+const dom = uni.requireNativePlugin('dom');
+const animation = uni.requireNativePlugin('animation');
+
+bindIngXMixins = {
+	data() {
+		return {}
+	},
+
+	watch: {
+		show(newVal) {
+			if (this.autoClose) return
+			if (this.stop) return
+			this.stop = true
+			if (newVal) {
+				this.open(newVal)
+			} else {
+				this.close()
+			}
+		},
+		leftOptions() {
+			this.getSelectorQuery()
+			this.init()
+		},
+		rightOptions(newVal) {
+			this.init()
+		}
+	},
+	created() {
+		this.swipeaction = this.getSwipeAction()
+		if (this.swipeaction && Array.isArray(this.swipeaction.children)) {
+			this.swipeaction.children.push(this)
+		}
+	},
+	mounted() {
+		this.box = this.getEl(this.$refs['selector-box--hock'])
+		this.selector = this.getEl(this.$refs['selector-content--hock']);
+		this.leftButton = this.getEl(this.$refs['selector-left-button--hock']);
+		this.rightButton = this.getEl(this.$refs['selector-right-button--hock']);
+		this.init()
+	},
+	// beforeDestroy() {
+	// 	this.swipeaction.children.forEach((item, index) => {
+	// 		if (item === this) {
+	// 			this.swipeaction.children.splice(index, 1)
+	// 		}
+	// 	})
+	// },
+	methods: {
+		init() {
+			this.$nextTick(() => {
+				this.x = 0
+				this.button = {
+					show: false
+				}
+				setTimeout(() => {
+					this.getSelectorQuery()
+				}, 200)
+			})
+		},
+		onClick(index, item, position) {
+			this.$emit('click', {
+				content: item,
+				index,
+				position
+			})
+		},
+		touchstart(e) {
+			// fix by mehaotian 禁止滑动
+			if (this.disabled) return
+			// 每次只触发一次,避免多次监听造成闪烁
+			if (this.stop) return
+			this.stop = true
+			if (this.autoClose && this.swipeaction) {
+				this.swipeaction.closeOther(this)
+			}
+
+			const leftWidth = this.button.left.width
+			const rightWidth = this.button.right.width
+			let expression = this.range(this.x, -rightWidth, leftWidth)
+			let leftExpression = this.range(this.x - leftWidth, -leftWidth, 0)
+			let rightExpression = this.range(this.x + rightWidth, 0, rightWidth)
+
+			this.eventpan = BindingX.bind({
+				anchor: this.box,
+				eventType: 'pan',
+				props: [{
+					element: this.selector,
+					property: 'transform.translateX',
+					expression
+				}, {
+					element: this.leftButton,
+					property: 'transform.translateX',
+					expression: leftExpression
+				}, {
+					element: this.rightButton,
+					property: 'transform.translateX',
+					expression: rightExpression
+				}, ]
+			}, (e) => {
+				// nope
+				if (e.state === 'end') {
+					this.x = e.deltaX + this.x;
+					this.isclick = true
+					this.bindTiming(e.deltaX)
+				}
+			});
+		},
+		touchend(e) {
+			if (this.isopen !== 'none' && !this.isclick) {
+				this.open('none')
+			}
+		},
+		bindTiming(x) {
+			const left = this.x
+			const leftWidth = this.button.left.width
+			const rightWidth = this.button.right.width
+			const threshold = this.threshold
+			if (!this.isopen || this.isopen === 'none') {
+				if (left > threshold) {
+					this.open('left')
+				} else if (left < -threshold) {
+					this.open('right')
+				} else {
+					this.open('none')
+				}
+			} else {
+				if ((x > -leftWidth && x < 0) || x > rightWidth) {
+					if ((x > -threshold && x < 0) || (x - rightWidth > threshold)) {
+						this.open('left')
+					} else {
+						this.open('none')
+					}
+				} else {
+					if ((x < threshold && x > 0) || (x + leftWidth < -threshold)) {
+						this.open('right')
+					} else {
+						this.open('none')
+					}
+				}
+			}
+		},
+
+		/**
+		 * 移动范围
+		 * @param {Object} num
+		 * @param {Object} mix
+		 * @param {Object} max
+		 */
+		range(num, mix, max) {
+			return `min(max(x+${num}, ${mix}), ${max})`
+		},
+
+		/**
+		 * 开启swipe
+		 */
+		open(type) {
+			this.animation(type)
+		},
+
+		/**
+		 * 关闭swipe
+		 */
+		close() {
+			this.animation('none')
+		},
+
+		/**
+		 * 开启关闭动画
+		 * @param {Object} type
+		 */
+		animation(type) {
+			const time = 300
+			const leftWidth = this.button.left.width
+			const rightWidth = this.button.right.width
+			if (this.eventpan && this.eventpan.token) {
+				BindingX.unbind({
+					token: this.eventpan.token,
+					eventType: 'pan'
+				})
+			}
+
+			switch (type) {
+				case 'left':
+					Promise.all([
+						this.move(this.selector, leftWidth),
+						this.move(this.leftButton, 0),
+						this.move(this.rightButton, rightWidth * 2)
+					]).then(() => {
+						this.setEmit(leftWidth, type)
+					})
+					break
+				case 'right':
+					Promise.all([
+						this.move(this.selector, -rightWidth),
+						this.move(this.leftButton, -leftWidth * 2),
+						this.move(this.rightButton, 0)
+					]).then(() => {
+						this.setEmit(-rightWidth, type)
+					})
+					break
+				default:
+					Promise.all([
+						this.move(this.selector, 0),
+						this.move(this.leftButton, -leftWidth),
+						this.move(this.rightButton, rightWidth)
+					]).then(() => {
+						this.setEmit(0, type)
+					})
+
+			}
+		},
+		setEmit(x, type) {
+			const leftWidth = this.button.left.width
+			const rightWidth = this.button.right.width
+			this.isopen = this.isopen || 'none'
+			this.stop = false
+			this.isclick = false
+			// 只有状态不一致才会返回结果
+			if (this.isopen !== type && this.x !== x) {
+				if (type === 'left' && leftWidth > 0) {
+					this.$emit('change', 'left')
+				}
+				if (type === 'right' && rightWidth > 0) {
+					this.$emit('change', 'right')
+				}
+				if (type === 'none') {
+					this.$emit('change', 'none')
+				}
+			}
+			this.x = x
+			this.isopen = type
+		},
+		move(ref, value) {
+			return new Promise((resolve, reject) => {
+				animation.transition(ref, {
+					styles: {
+						transform: `translateX(${value})`,
+					},
+					duration: 150, //ms
+					timingFunction: 'linear',
+					needLayout: false,
+					delay: 0 //ms
+				}, function(res) {
+					resolve(res)
+				})
+			})
+
+		},
+
+		/**
+		 * 获取ref
+		 * @param {Object} el
+		 */
+		getEl(el) {
+			return el.ref
+		},
+		/**
+		 * 获取节点信息
+		 */
+		getSelectorQuery() {
+			Promise.all([
+				this.getDom('left'),
+				this.getDom('right'),
+			]).then((data) => {
+				let show = 'none'
+				if (this.autoClose) {
+					show = 'none'
+				} else {
+					show = this.show
+				}
+
+				if (show === 'none') {
+					// this.close()
+				} else {
+					this.open(show)
+				}
+
+			})
+
+		},
+		getDom(str) {
+			return new Promise((resolve, reject) => {
+				dom.getComponentRect(this.$refs[`selector-${str}-button--hock`], (data) => {
+					if (data) {
+						this.button[str] = data.size
+						resolve(data)
+					} else {
+						reject()
+					}
+				})
+			})
+		}
+	}
+}
+
+// #endif
+
+export default bindIngXMixins

+ 12 - 0
components/uni-swipe-action-item/isPC.js

@@ -0,0 +1,12 @@
+export function isPC() {
+	var userAgentInfo = navigator.userAgent;
+	var Agents = ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"];
+	var flag = true;
+	for (let v = 0; v < Agents.length - 1; v++) {
+		if (userAgentInfo.indexOf(Agents[v]) > 0) {
+			flag = false;
+			break;
+		}
+	}
+	return flag;
+}

+ 195 - 0
components/uni-swipe-action-item/mpalipay.js

@@ -0,0 +1,195 @@
+export default {
+	data() {
+		return {
+			x: 0,
+			transition: false,
+			width: 0,
+			viewWidth: 0,
+			swipeShow: 0
+		}
+	},
+	watch: {
+		show(newVal) {
+			if (this.autoClose) return
+			if (newVal && newVal !== 'none') {
+				this.transition = true
+				this.open(newVal)
+			} else {
+				this.close()
+			}
+		}
+	},
+	created() {
+		this.swipeaction = this.getSwipeAction()
+		if (this.swipeaction && Array.isArray(this.swipeaction.children)) {
+			this.swipeaction.children.push(this)
+		}
+	},
+	mounted() {
+		this.isopen = false
+		setTimeout(() => {
+			this.getQuerySelect()
+		}, 50)
+	},
+	methods: {
+		appTouchStart(e) {
+			const {
+				clientX
+			} = e.changedTouches[0]
+			this.clientX = clientX
+			this.timestamp = new Date().getTime()
+		},
+		appTouchEnd(e, index, item, position) {
+			const {
+				clientX
+			} = e.changedTouches[0]
+			// fixed by xxxx 模拟点击事件,解决 ios 13 点击区域错位的问题
+			let diff = Math.abs(this.clientX - clientX)
+			let time = (new Date().getTime()) - this.timestamp
+			if (diff < 40 && time < 300) {
+				this.$emit('click', {
+					content: item,
+					index,
+					position
+				})
+			}
+		},
+		/**
+		 * 移动触发
+		 * @param {Object} e
+		 */
+		onChange(e) {
+			this.moveX = e.detail.x
+			this.isclose = false
+		},
+		touchstart(e) {
+			this.transition = false
+			this.isclose = true
+			if (this.autoClose && this.swipeaction) {
+				this.swipeaction.closeOther(this)
+			}
+		},
+		touchmove(e) {},
+		touchend(e) {
+			// 0的位置什么都不执行
+			if (this.isclose && this.isopen === 'none') return
+			if (this.isclose && this.isopen !== 'none') {
+				this.transition = true
+				this.close()
+			} else {
+				this.move(this.moveX + this.leftWidth)
+			}
+		},
+
+		/**
+		 * 移动
+		 * @param {Object} moveX
+		 */
+		move(moveX) {
+			// 打开关闭的处理逻辑不太一样
+			this.transition = true
+			// 未打开状态
+			if (!this.isopen || this.isopen === 'none') {
+				if (moveX > this.threshold) {
+					this.open('left')
+				} else if (moveX < -this.threshold) {
+					this.open('right')
+				} else {
+					this.close()
+				}
+			} else {
+				if (moveX < 0 && moveX < this.rightWidth) {
+					const rightX = this.rightWidth + moveX
+					if (rightX < this.threshold) {
+						this.open('right')
+					} else {
+						this.close()
+					}
+				} else if (moveX > 0 && moveX < this.leftWidth) {
+					const leftX = this.leftWidth - moveX
+					if (leftX < this.threshold) {
+						this.open('left')
+					} else {
+						this.close()
+					}
+				}
+
+			}
+
+		},
+
+		/**
+		 * 打开
+		 */
+		open(type) {
+			this.x = this.moveX
+			this.animation(type)
+		},
+
+		/**
+		 * 关闭
+		 */
+		close() {
+			this.x = this.moveX
+			// TODO 解决 x 值不更新的问题,所以会多触发一次 nextTick ,待优化
+			this.$nextTick(() => {
+				this.x = -this.leftWidth
+				if (this.isopen !== 'none') {
+					this.$emit('change', 'none')
+				}
+				this.isopen = 'none'
+			})
+		},
+
+		/**
+		 * 执行结束动画
+		 * @param {Object} type
+		 */
+		animation(type) {
+			this.$nextTick(() => {
+				if (type === 'left') {
+					this.x = 0
+				} else {
+					this.x = -this.rightWidth - this.leftWidth
+				}
+
+				if (this.isopen !== type) {
+					this.$emit('change', type)
+				}
+				this.isopen = type
+			})
+
+		},
+		getSlide(x) {},
+		getQuerySelect() {
+			const query = uni.createSelectorQuery().in(this);
+			query.selectAll('.movable-view--hock').boundingClientRect(data => {
+				this.leftWidth = data[1].width
+				this.rightWidth = data[2].width
+				this.width = data[0].width
+				this.viewWidth = this.width + this.rightWidth + this.leftWidth
+				if (this.leftWidth === 0) {
+					// TODO 疑似bug ,初始化的时候如果x 是0,会导致移动位置错误,所以让元素超出一点
+					this.x = -0.1
+				} else {
+					this.x = -this.leftWidth
+				}
+				this.moveX = this.x
+				this.$nextTick(() => {
+					this.swipeShow = 1
+				})
+
+				if (!this.buttonWidth) {
+					this.disabledView = true
+				}
+
+				if (this.autoClose) return
+				if (this.show !== 'none') {
+					this.transition = true
+					this.open(this.shows)
+				}
+			}).exec();
+
+		}
+	}
+}

+ 260 - 0
components/uni-swipe-action-item/mpother.js

@@ -0,0 +1,260 @@
+let otherMixins = {}
+
+// #ifndef APP-PLUS|| MP-WEIXIN  ||  H5
+const MIN_DISTANCE = 10;
+otherMixins = {
+	data() {
+		// TODO 随机生生元素ID,解决百度小程序获取同一个元素位置信息的bug
+		const elClass = `Uni_${Math.ceil(Math.random() * 10e5).toString(36)}`
+		return {
+			uniShow: false,
+			left: 0,
+			buttonShow: 'none',
+			ani: false,
+			moveLeft: '',
+			elClass
+		}
+	},
+	watch: {
+		show(newVal) {
+			if (this.autoClose) return
+			this.openState(newVal)
+		},
+		left() {
+			this.moveLeft = `translateX(${this.left}px)`
+		},
+		buttonShow(newVal) {
+			if (this.autoClose) return
+			this.openState(newVal)
+		},
+		leftOptions() {
+			this.init()
+		},
+		rightOptions() {
+			this.init()
+		}
+	},
+	mounted() {
+		this.swipeaction = this.getSwipeAction()
+		if (this.swipeaction && Array.isArray(this.swipeaction.children)) {
+			this.swipeaction.children.push(this)
+		}
+		this.init()
+	},
+	methods: {
+		init() {
+			clearTimeout(this.timer)
+			this.timer = setTimeout(() => {
+				this.getSelectorQuery()
+			}, 100)
+			// 移动距离
+			this.left = 0
+			this.x = 0
+		},
+
+		closeSwipe(e) {
+			if (this.autoClose && this.swipeaction) {
+				this.swipeaction.closeOther(this)
+			}
+		},
+		appTouchStart(e) {
+			const {
+				clientX
+			} = e.changedTouches[0]
+			this.clientX = clientX
+			this.timestamp = new Date().getTime()
+		},
+		appTouchEnd(e, index, item, position) {
+			const {
+				clientX
+			} = e.changedTouches[0]
+			// fixed by xxxx 模拟点击事件,解决 ios 13 点击区域错位的问题
+			let diff = Math.abs(this.clientX - clientX)
+			let time = (new Date().getTime()) - this.timestamp
+			if (diff < 40 && time < 300) {
+				this.$emit('click', {
+					content: item,
+					index,
+					position
+				})
+			}
+		},
+		touchstart(e) {
+			if (this.disabled) return
+			this.ani = false
+			this.x = this.left || 0
+			this.stopTouchStart(e)
+			this.autoClose && this.closeSwipe()
+		},
+		touchmove(e) {
+			if (this.disabled) return
+			// 是否可以滑动页面
+			this.stopTouchMove(e);
+			if (this.direction !== 'horizontal') {
+				return;
+			}
+			this.move(this.x + this.deltaX)
+			return false
+		},
+		touchend() {
+			if (this.disabled) return
+			this.moveDirection(this.left)
+		},
+		/**
+		 * 设置移动距离
+		 * @param {Object} value
+		 */
+		move(value) {
+			value = value || 0
+			const leftWidth = this.leftWidth
+			const rightWidth = this.rightWidth
+			// 获取可滑动范围
+			this.left = this.range(value, -rightWidth, leftWidth);
+		},
+
+		/**
+		 * 获取范围
+		 * @param {Object} num
+		 * @param {Object} min
+		 * @param {Object} max
+		 */
+		range(num, min, max) {
+			return Math.min(Math.max(num, min), max);
+		},
+		/**
+		 * 移动方向判断
+		 * @param {Object} left
+		 * @param {Object} value
+		 */
+		moveDirection(left) {
+			const threshold = this.threshold
+			const isopen = this.isopen || 'none'
+			const leftWidth = this.leftWidth
+			const rightWidth = this.rightWidth
+			if (this.deltaX === 0) {
+				this.openState('none')
+				return
+			}
+			if ((isopen === 'none' && rightWidth > 0 && -left > threshold) || (isopen !== 'none' && rightWidth >
+					0 && rightWidth +
+					left < threshold)) {
+				// right
+				this.openState('right')
+			} else if ((isopen === 'none' && leftWidth > 0 && left > threshold) || (isopen !== 'none' && leftWidth >
+					0 &&
+					leftWidth - left < threshold)) {
+				// left
+				this.openState('left')
+			} else {
+				// default
+				this.openState('none')
+			}
+		},
+
+		/**
+		 * 开启状态
+		 * @param {Boolean} type
+		 */
+		openState(type) {
+			const leftWidth = this.leftWidth
+			const rightWidth = this.rightWidth
+			let left = ''
+			this.isopen = this.isopen ? this.isopen : 'none'
+			switch (type) {
+				case "left":
+					left = leftWidth
+					break
+				case "right":
+					left = -rightWidth
+					break
+				default:
+					left = 0
+			}
+
+
+			if (this.isopen !== type) {
+				this.throttle = true
+				this.$emit('change', type)
+			}
+
+			this.isopen = type
+			// 添加动画类
+			this.ani = true
+			this.$nextTick(() => {
+				this.move(left)
+			})
+			// 设置最终移动位置,理论上只要进入到这个函数,肯定是要打开的
+		},
+		close() {
+			this.openState('none')
+		},
+		getDirection(x, y) {
+			if (x > y && x > MIN_DISTANCE) {
+				return 'horizontal';
+			}
+			if (y > x && y > MIN_DISTANCE) {
+				return 'vertical';
+			}
+			return '';
+		},
+
+		/**
+		 * 重置滑动状态
+		 * @param {Object} event
+		 */
+		resetTouchStatus() {
+			this.direction = '';
+			this.deltaX = 0;
+			this.deltaY = 0;
+			this.offsetX = 0;
+			this.offsetY = 0;
+		},
+
+		/**
+		 * 设置滑动开始位置
+		 * @param {Object} event
+		 */
+		stopTouchStart(event) {
+			this.resetTouchStatus();
+			const touch = event.touches[0];
+			this.startX = touch.clientX;
+			this.startY = touch.clientY;
+		},
+
+		/**
+		 * 滑动中,是否禁止打开
+		 * @param {Object} event
+		 */
+		stopTouchMove(event) {
+			const touch = event.touches[0];
+			this.deltaX = touch.clientX - this.startX;
+			this.deltaY = touch.clientY - this.startY;
+			this.offsetX = Math.abs(this.deltaX);
+			this.offsetY = Math.abs(this.deltaY);
+			this.direction = this.direction || this.getDirection(this.offsetX, this.offsetY);
+		},
+
+		getSelectorQuery() {
+			const views = uni.createSelectorQuery().in(this)
+			views
+				.selectAll('.' + this.elClass)
+				.boundingClientRect(data => {
+					if (data.length === 0) return
+					let show = 'none'
+					if (this.autoClose) {
+						show = 'none'
+					} else {
+						show = this.show
+					}
+					this.leftWidth = data[0].width || 0
+					this.rightWidth = data[1].width || 0
+					this.buttonShow = show
+				})
+				.exec()
+		}
+	}
+}
+
+// #endif
+
+export default otherMixins

+ 84 - 0
components/uni-swipe-action-item/mpwxs.js

@@ -0,0 +1,84 @@
+let mpMixins = {}
+let is_pc = null
+// #ifdef H5
+import {
+	isPC
+} from "./isPC"
+is_pc = isPC()
+// #endif
+// #ifdef APP-VUE|| MP-WEIXIN || H5
+
+mpMixins = {
+	data() {
+		return {
+			is_show: 'none'
+		}
+	},
+	watch: {
+		show(newVal) {
+			this.is_show = this.show
+		}
+	},
+	created() {
+		this.swipeaction = this.getSwipeAction()
+		if (this.swipeaction && Array.isArray(this.swipeaction.children)) {
+			this.swipeaction.children.push(this)
+		}
+	},
+	mounted() {
+		this.is_show = this.show
+	},
+	methods: {
+		// wxs 中调用
+		closeSwipe(e) {
+			if (this.autoClose && this.swipeaction) {
+				this.swipeaction.closeOther(this)
+			}
+		},
+
+		change(e) {
+			this.$emit('change', e.open)
+			if (this.is_show !== e.open) {
+				this.is_show = e.open
+			}
+		},
+
+		appTouchStart(e) {
+			if (is_pc) return
+			const {
+				clientX
+			} = e.changedTouches[0]
+			this.clientX = clientX
+			this.timestamp = new Date().getTime()
+		},
+		appTouchEnd(e, index, item, position) {
+			if (is_pc) return
+			const {
+				clientX
+			} = e.changedTouches[0]
+			// fixed by xxxx 模拟点击事件,解决 ios 13 点击区域错位的问题
+			let diff = Math.abs(this.clientX - clientX)
+			let time = (new Date().getTime()) - this.timestamp
+			if (diff < 40 && time < 300) {
+				this.$emit('click', {
+					content: item,
+					index,
+					position
+				})
+			}
+		},
+		onClickForPC(index, item, position) {
+			if (!is_pc) return
+			// #ifdef H5
+			this.$emit('click', {
+				content: item,
+				index,
+				position
+			})
+			// #endif
+		}
+	}
+}
+
+// #endif
+export default mpMixins

+ 270 - 0
components/uni-swipe-action-item/render.js

@@ -0,0 +1,270 @@
+const MIN_DISTANCE = 10;
+export default {
+	showWatch(newVal, oldVal, ownerInstance, instance, self) {
+		var state = self.state
+		var $el = ownerInstance.$el || ownerInstance.$vm && ownerInstance.$vm.$el
+		if (!$el) return
+		this.getDom(instance, ownerInstance, self)
+		if (newVal && newVal !== 'none') {
+			this.openState(newVal, instance, ownerInstance, self)
+			return
+		}
+
+		if (state.left) {
+			this.openState('none', instance, ownerInstance, self)
+		}
+		this.resetTouchStatus(instance, self)
+	},
+
+	/**
+	 * 开始触摸操作
+	 * @param {Object} e
+	 * @param {Object} ins
+	 */
+	touchstart(e, ownerInstance, self) {
+		let instance = e.instance;
+		let disabled = instance.getDataset().disabled
+		let state = self.state;
+		this.getDom(instance, ownerInstance, self)
+		// fix by mehaotian, TODO 兼容 app-vue 获取dataset为字符串 , h5 获取 为 undefined 的问题,待框架修复
+		disabled = this.getDisabledType(disabled)
+		if (disabled) return
+		// 开始触摸时移除动画类
+		instance.requestAnimationFrame(function() {
+			instance.removeClass('ani');
+			ownerInstance.callMethod('closeSwipe');
+		})
+
+		// 记录上次的位置
+		state.x = state.left || 0
+		// 计算滑动开始位置
+		this.stopTouchStart(e, ownerInstance, self)
+	},
+
+	/**
+	 * 开始滑动操作
+	 * @param {Object} e
+	 * @param {Object} ownerInstance
+	 */
+	touchmove(e, ownerInstance, self) {
+		let instance = e.instance;
+		// 删除之后已经那不到实例了
+		if (!instance) return;
+		let disabled = instance.getDataset().disabled
+		let state = self.state
+		// fix by mehaotian, TODO 兼容 app-vue 获取dataset为字符串 , h5 获取 为 undefined 的问题,待框架修复
+		disabled = this.getDisabledType(disabled)
+		if (disabled) return
+		// 是否可以滑动页面
+		this.stopTouchMove(e, self);
+		if (state.direction !== 'horizontal') {
+			return;
+		}
+		if (e.preventDefault) {
+			// 阻止页面滚动
+			e.preventDefault()
+		}
+		let x = state.x + state.deltaX
+		this.move(x, instance, ownerInstance, self)
+	},
+
+	/**
+	 * 结束触摸操作
+	 * @param {Object} e
+	 * @param {Object} ownerInstance
+	 */
+	touchend(e, ownerInstance, self) {
+		let instance = e.instance;
+		let disabled = instance.getDataset().disabled
+		let state = self.state
+		// fix by mehaotian, TODO 兼容 app-vue 获取dataset为字符串 , h5 获取 为 undefined 的问题,待框架修复
+		disabled = this.getDisabledType(disabled)
+
+		if (disabled) return
+		// 滑动过程中触摸结束,通过阙值判断是开启还是关闭
+		// fixed by mehaotian 定时器解决点击按钮,touchend 触发比 click 事件时机早的问题 ,主要是 ios13
+		this.moveDirection(state.left, instance, ownerInstance, self)
+
+	},
+
+	/**
+	 * 设置移动距离
+	 * @param {Object} value
+	 * @param {Object} instance
+	 * @param {Object} ownerInstance
+	 */
+	move(value, instance, ownerInstance, self) {
+		value = value || 0
+		let state = self.state
+		let leftWidth = state.leftWidth
+		let rightWidth = state.rightWidth
+		// 获取可滑动范围
+		state.left = this.range(value, -rightWidth, leftWidth);
+		instance.requestAnimationFrame(function() {
+			instance.setStyle({
+				transform: 'translateX(' + state.left + 'px)',
+				'-webkit-transform': 'translateX(' + state.left + 'px)'
+			})
+		})
+
+	},
+
+	/**
+	 * 获取元素信息
+	 * @param {Object} instance
+	 * @param {Object} ownerInstance
+	 */
+	getDom(instance, ownerInstance, self) {
+		var state = self.state
+		var $el = ownerInstance.$el || ownerInstance.$vm && ownerInstance.$vm.$el
+		var leftDom = $el.querySelector('.button-group--left')
+		var rightDom = $el.querySelector('.button-group--right')
+
+		state.leftWidth = leftDom.offsetWidth || 0
+		state.rightWidth = rightDom.offsetWidth || 0
+		state.threshold = instance.getDataset().threshold
+	},
+
+	getDisabledType(value) {
+		return (typeof(value) === 'string' ? JSON.parse(value) : value) || false;
+	},
+
+	/**
+	 * 获取范围
+	 * @param {Object} num
+	 * @param {Object} min
+	 * @param {Object} max
+	 */
+	range(num, min, max) {
+		return Math.min(Math.max(num, min), max);
+	},
+
+
+	/**
+	 * 移动方向判断
+	 * @param {Object} left
+	 * @param {Object} value
+	 * @param {Object} ownerInstance
+	 * @param {Object} ins
+	 */
+	moveDirection(left, ins, ownerInstance, self) {
+		var state = self.state
+		var threshold = state.threshold
+		var position = state.position
+		var isopen = state.isopen || 'none'
+		var leftWidth = state.leftWidth
+		var rightWidth = state.rightWidth
+		if (state.deltaX === 0) {
+			this.openState('none', ins, ownerInstance, self)
+			return
+		}
+		if ((isopen === 'none' && rightWidth > 0 && -left > threshold) || (isopen !== 'none' && rightWidth > 0 &&
+				rightWidth +
+				left < threshold)) {
+			// right
+			this.openState('right', ins, ownerInstance, self)
+		} else if ((isopen === 'none' && leftWidth > 0 && left > threshold) || (isopen !== 'none' && leftWidth > 0 &&
+				leftWidth - left < threshold)) {
+			// left
+			this.openState('left', ins, ownerInstance, self)
+		} else {
+			// default
+			this.openState('none', ins, ownerInstance, self)
+		}
+	},
+
+
+	/**
+	 * 开启状态
+	 * @param {Boolean} type
+	 * @param {Object} ins
+	 * @param {Object} ownerInstance
+	 */
+	openState(type, ins, ownerInstance, self) {
+		let state = self.state
+		let leftWidth = state.leftWidth
+		let rightWidth = state.rightWidth
+		let left = ''
+		state.isopen = state.isopen ? state.isopen : 'none'
+		switch (type) {
+			case "left":
+				left = leftWidth
+				break
+			case "right":
+				left = -rightWidth
+				break
+			default:
+				left = 0
+		}
+
+		// && !state.throttle
+
+		if (state.isopen !== type) {
+			state.throttle = true
+			ownerInstance.callMethod('change', {
+				open: type
+			})
+
+		}
+
+		state.isopen = type
+		// 添加动画类
+		ins.requestAnimationFrame(() => {
+			ins.addClass('ani');
+			this.move(left, ins, ownerInstance, self)
+		})
+	},
+
+
+	getDirection(x, y) {
+		if (x > y && x > MIN_DISTANCE) {
+			return 'horizontal';
+		}
+		if (y > x && y > MIN_DISTANCE) {
+			return 'vertical';
+		}
+		return '';
+	},
+
+	/**
+	 * 重置滑动状态
+	 * @param {Object} event
+	 */
+	resetTouchStatus(instance, self) {
+		let state = self.state;
+		state.direction = '';
+		state.deltaX = 0;
+		state.deltaY = 0;
+		state.offsetX = 0;
+		state.offsetY = 0;
+	},
+
+	/**
+	 * 设置滑动开始位置
+	 * @param {Object} event
+	 */
+	stopTouchStart(event, ownerInstance, self) {
+		let instance = event.instance;
+		let state = self.state
+		this.resetTouchStatus(instance, self);
+		var touch = event.touches[0];
+		state.startX = touch.clientX;
+		state.startY = touch.clientY;
+	},
+
+	/**
+	 * 滑动中,是否禁止打开
+	 * @param {Object} event
+	 */
+	stopTouchMove(event, self) {
+		let instance = event.instance;
+		let state = self.state;
+		let touch = event.touches[0];
+
+		state.deltaX = touch.clientX - state.startX;
+		state.deltaY = touch.clientY - state.startY;
+		state.offsetY = Math.abs(state.deltaY);
+		state.offsetX = Math.abs(state.deltaX);
+		state.direction = state.direction || this.getDirection(state.offsetX, state.offsetY);
+	}
+}

+ 347 - 0
components/uni-swipe-action-item/uni-swipe-action-item.vue

@@ -0,0 +1,347 @@
+<template>
+	<!-- 在微信小程序 app vue端 h5 使用wxs 实现-->
+	<!-- #ifdef APP-VUE || MP-WEIXIN || H5 -->
+	<view class="uni-swipe">
+		<!--  #ifdef MP-WEIXIN || VUE3 -->
+		<view class="uni-swipe_box" :change:prop="wxsswipe.showWatch" :prop="is_show" :data-threshold="threshold"
+			:data-disabled="disabled" @touchstart="wxsswipe.touchstart" @touchmove="wxsswipe.touchmove"
+			@touchend="wxsswipe.touchend">
+			<!-- #endif -->
+			<!--  #ifndef MP-WEIXIN || VUE3 -->
+			<view class="uni-swipe_box" :change:prop="renderswipe.showWatch" :prop="is_show" :data-threshold="threshold"
+				:data-disabled="disabled+''" @touchstart="renderswipe.touchstart" @touchmove="renderswipe.touchmove"
+				@touchend="renderswipe.touchend">
+				<!-- #endif -->
+				<!-- 在微信小程序 app vue端 h5 使用wxs 实现-->
+				<view class="uni-swipe_button-group button-group--left">
+					<slot name="left">
+						<view v-for="(item,index) in leftOptions" :key="index" :style="{
+					  backgroundColor: item.style && item.style.backgroundColor ? item.style.backgroundColor : '#C7C6CD'
+					}" class="uni-swipe_button button-hock" @touchstart="appTouchStart"
+							@touchend="appTouchEnd($event,index,item,'left')"
+							@click.stop="onClickForPC(index,item,'left')">
+							<text class="uni-swipe_button-text"
+								:style="{color: item.style && item.style.color ? item.style.color : '#FFFFFF',fontSize: item.style && item.style.fontSize ? item.style.fontSize : '16px'}">{{ item.text }}</text>
+						</view>
+					</slot>
+				</view>
+				<view class="uni-swipe_text--center">
+					<slot></slot>
+				</view>
+				<view class="uni-swipe_button-group button-group--right">
+					<slot name="right">
+						<view v-for="(item,index) in rightOptions" :key="index" :style="{
+					  backgroundColor: item.style && item.style.backgroundColor ? item.style.backgroundColor : '#C7C6CD'
+					}" class="uni-swipe_button button-hock" @touchstart="appTouchStart"
+							@touchend="appTouchEnd($event,index,item,'right')"
+							@click.stop="onClickForPC(index,item,'right')"><text class="uni-swipe_button-text"
+								:style="{color: item.style && item.style.color ? item.style.color : '#FFFFFF',fontSize: item.style && item.style.fontSize ? item.style.fontSize : '16px'}">{{ item.text }}</text>
+						</view>
+					</slot>
+				</view>
+			</view>
+		</view>
+		<!-- #endif -->
+		<!-- app nvue端 使用 bindingx -->
+		<!-- #ifdef APP-NVUE -->
+		<view ref="selector-box--hock" class="uni-swipe" @horizontalpan="touchstart" @touchend="touchend">
+			<view ref='selector-left-button--hock' class="uni-swipe_button-group button-group--left">
+				<slot name="left">
+					<view v-for="(item,index) in leftOptions" :key="index" :style="{
+				  backgroundColor: item.style && item.style.backgroundColor ? item.style.backgroundColor : '#C7C6CD'
+				}" class="uni-swipe_button button-hock" @click.stop="onClick(index,item,'left')"><text
+							class="uni-swipe_button-text"
+							:style="{color: item.style && item.style.color ? item.style.color : '#FFFFFF', fontSize: item.style && item.style.fontSize ? item.style.fontSize : '16px'}">{{ item.text }}</text>
+					</view>
+				</slot>
+			</view>
+			<view ref='selector-right-button--hock' class="uni-swipe_button-group button-group--right">
+				<slot name="right">
+					<view v-for="(item,index) in rightOptions" :key="index" :style="{
+				  backgroundColor: item.style && item.style.backgroundColor ? item.style.backgroundColor : '#C7C6CD'
+				}" class="uni-swipe_button button-hock" @click.stop="onClick(index,item,'right')"><text
+							class="uni-swipe_button-text"
+							:style="{color: item.style && item.style.color ? item.style.color : '#FFFFFF',fontSize: item.style && item.style.fontSize ? item.style.fontSize : '16px'}">{{ item.text }}</text>
+					</view>
+				</slot>
+			</view>
+			<view ref='selector-content--hock' class="uni-swipe_box">
+				<slot></slot>
+			</view>
+		</view>
+		<!-- #endif -->
+		<!-- 其他平台使用 js ,长列表性能可能会有影响-->
+		<!-- #ifdef MP-ALIPAY || MP-BAIDU || MP-TOUTIAO || MP-QQ -->
+		<view class="uni-swipe">
+			<view class="uni-swipe_box" @touchstart="touchstart" @touchmove="touchmove" @touchend="touchend"
+				:style="{transform:moveLeft}" :class="{ani:ani}">
+				<view class="uni-swipe_button-group button-group--left" :class="[elClass]">
+					<slot name="left">
+						<view v-for="(item,index) in leftOptions" :key="index" :style="{
+					  backgroundColor: item.style && item.style.backgroundColor ? item.style.backgroundColor : '#C7C6CD',
+					  fontSize: item.style && item.style.fontSize ? item.style.fontSize : '16px'
+					}" class="uni-swipe_button button-hock" @touchstart="appTouchStart"
+							@touchend="appTouchEnd($event,index,item,'left')"><text class="uni-swipe_button-text"
+								:style="{color: item.style && item.style.color ? item.style.color : '#FFFFFF',}">{{ item.text }}</text>
+						</view>
+					</slot>
+				</view>
+				<slot></slot>
+				<view class="uni-swipe_button-group button-group--right" :class="[elClass]">
+					<slot name="right">
+						<view v-for="(item,index) in rightOptions" :key="index" :style="{
+					  backgroundColor: item.style && item.style.backgroundColor ? item.style.backgroundColor : '#C7C6CD',
+					  fontSize: item.style && item.style.fontSize ? item.style.fontSize : '16px'
+					}" @touchstart="appTouchStart" @touchend="appTouchEnd($event,index,item,'right')"
+							class="uni-swipe_button button-hock"><text class="uni-swipe_button-text"
+								:style="{color: item.style && item.style.color ? item.style.color : '#FFFFFF',}">{{ item.text }}</text>
+						</view>
+					</slot>
+				</view>
+			</view>
+		</view>
+		<!-- #endif -->
+
+</template>
+<script src="./wx.wxs" module="wxsswipe" lang="wxs"></script>
+
+<script module="renderswipe" lang="renderjs">
+	import render from './render.js'
+	export default {
+		mounted(e, ins, owner) {
+			this.state = {}
+		},
+		methods: {
+			showWatch(newVal, oldVal, ownerInstance, instance) {
+				render.showWatch(newVal, oldVal, ownerInstance, instance, this)
+			},
+			touchstart(e, ownerInstance) {
+				render.touchstart(e, ownerInstance, this)
+			},
+			touchmove(e, ownerInstance) {
+				render.touchmove(e, ownerInstance, this)
+			},
+			touchend(e, ownerInstance) {
+				render.touchend(e, ownerInstance, this)
+			}
+		}
+	}
+</script>
+<script>
+	import mpwxs from './mpwxs'
+	import bindingx from './bindingx.js'
+	import mpother from './mpother'
+
+	/**
+	 * SwipeActionItem 滑动操作子组件
+	 * @description 通过滑动触发选项的容器
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=181
+	 * @property {Boolean} show = [left|right|none] 	开启关闭组件,auto-close = false 时生效
+	 * @property {Boolean} disabled = [true|false] 		是否禁止滑动
+	 * @property {Boolean} autoClose = [true|false] 	滑动打开当前组件,是否关闭其他组件
+	 * @property {Number}  threshold 					滑动缺省值
+	 * @property {Array} leftOptions 					左侧选项内容及样式
+	 * @property {Array} rgihtOptions 					右侧选项内容及样式
+	 * @event {Function} click 							点击选项按钮时触发事件,e = {content,index} ,content(点击内容)、index(下标)
+	 * @event {Function} change 						组件打开或关闭时触发,left\right\none
+	 */
+
+	export default {
+		mixins: [mpwxs, bindingx, mpother],
+		emits: ['click', 'change'],
+		props: {
+			// 控制开关
+			show: {
+				type: String,
+				default: 'none'
+			},
+
+			// 禁用
+			disabled: {
+				type: Boolean,
+				default: false
+			},
+
+			// 是否自动关闭
+			autoClose: {
+				type: Boolean,
+				default: true
+			},
+
+			// 滑动缺省距离
+			threshold: {
+				type: Number,
+				default: 20
+			},
+
+			// 左侧按钮内容
+			leftOptions: {
+				type: Array,
+				default () {
+					return []
+				}
+			},
+
+			// 右侧按钮内容
+			rightOptions: {
+				type: Array,
+				default () {
+					return []
+				}
+			}
+
+		},
+		// #ifndef VUE3
+		// TODO vue2
+		destroyed() {
+			if (this.__isUnmounted) return
+			this.uninstall()
+		},
+		// #endif
+		// #ifdef VUE3
+		// TODO vue3
+		unmounted() {
+			this.__isUnmounted = true
+			this.uninstall()
+		},
+		// #endif
+
+		methods: {
+			uninstall() {
+				if (this.swipeaction) {
+					this.swipeaction.children.forEach((item, index) => {
+						if (item === this) {
+							this.swipeaction.children.splice(index, 1)
+						}
+					})
+				}
+			},
+			/**
+			 * 获取父元素实例
+			 */
+			getSwipeAction(name = 'uniSwipeAction') {
+				let parent = this.$parent;
+				let parentName = parent.$options.name;
+				while (parentName !== name) {
+					parent = parent.$parent;
+					if (!parent) return false;
+					parentName = parent.$options.name;
+				}
+				return parent;
+			}
+		}
+	}
+</script>
+<style lang="scss">
+	.uni-swipe {
+		position: relative;
+		/* #ifndef APP-NVUE */
+		overflow: hidden;
+		/* #endif */
+	}
+
+	.uni-swipe_box {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		flex-shrink: 0;
+		// touch-action: none;
+		/* #endif */
+		position: relative;
+	}
+
+	.uni-swipe_content {
+		// border: 1px red solid;
+	}
+
+	.uni-swipe_text--center {
+		width: 100%;
+		/* #ifndef APP-NVUE */
+		cursor: grab;
+		/* #endif */
+	}
+
+	.uni-swipe_button-group {
+		/* #ifndef APP-NVUE */
+		box-sizing: border-box;
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		position: absolute;
+		top: 0;
+		bottom: 0;
+		/* #ifdef H5 */
+		cursor: pointer;
+		/* #endif */
+	}
+
+	.button-group--left {
+		left: 0;
+		transform: translateX(-100%)
+	}
+
+	.button-group--right {
+		right: 0;
+		transform: translateX(100%)
+	}
+
+	.uni-swipe_button {
+		/* #ifdef APP-NVUE */
+		flex: 1;
+		/* #endif */
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		justify-content: center;
+		align-items: center;
+		padding: 0 20px;
+	}
+
+	.uni-swipe_button-text {
+		/* #ifndef APP-NVUE */
+		flex-shrink: 0;
+		/* #endif */
+		font-size: 14px;
+	}
+
+	.ani {
+		transition-property: transform;
+		transition-duration: 0.3s;
+		transition-timing-function: cubic-bezier(0.165, 0.84, 0.44, 1);
+	}
+
+	/* #ifdef MP-ALIPAY */
+	.movable-area {
+		/* width: 100%; */
+		height: 45px;
+	}
+
+	.movable-view {
+		display: flex;
+		/* justify-content: center; */
+		position: relative;
+		flex: 1;
+		height: 45px;
+		z-index: 2;
+	}
+
+	.movable-view-button {
+		display: flex;
+		flex-shrink: 0;
+		flex-direction: row;
+		height: 100%;
+		background: #C0C0C0;
+	}
+
+	/* .transition {
+		transition: all 0.3s;
+	} */
+
+	.movable-view-box {
+		flex-shrink: 0;
+		height: 100%;
+		background-color: #fff;
+	}
+
+	/* #endif */
+</style>

+ 341 - 0
components/uni-swipe-action-item/wx.wxs

@@ -0,0 +1,341 @@
+var MIN_DISTANCE = 10;
+
+/**
+ * 判断当前是否为H5、app-vue
+ */
+var IS_HTML5 = false
+if (typeof window === 'object') IS_HTML5 = true
+
+/**
+ * 监听页面内值的变化,主要用于动态开关swipe-action
+ * @param {Object} newValue
+ * @param {Object} oldValue
+ * @param {Object} ownerInstance
+ * @param {Object} instance
+ */
+function showWatch(newVal, oldVal, ownerInstance, instance) {
+	var state = instance.getState()
+	getDom(instance, ownerInstance)
+	if (newVal && newVal !== 'none') {
+		openState(newVal, instance, ownerInstance)
+		return
+	}
+
+	if (state.left) {
+		openState('none', instance, ownerInstance)
+	}
+	resetTouchStatus(instance)
+}
+
+/**
+ * 开始触摸操作
+ * @param {Object} e
+ * @param {Object} ins
+ */
+function touchstart(e, ownerInstance) {
+	var instance = e.instance;
+	var disabled = instance.getDataset().disabled
+	var state = instance.getState();
+	getDom(instance, ownerInstance)
+	// fix by mehaotian, TODO 兼容 app-vue 获取dataset为字符串 , h5 获取 为 undefined 的问题,待框架修复
+	disabled = (typeof(disabled) === 'string' ? JSON.parse(disabled) : disabled) || false;
+	if (disabled) return
+	// 开始触摸时移除动画类
+	instance.requestAnimationFrame(function() {
+		instance.removeClass('ani');
+		ownerInstance.callMethod('closeSwipe');
+	})
+
+	// 记录上次的位置
+	state.x = state.left || 0
+	// 计算滑动开始位置
+	stopTouchStart(e, ownerInstance)
+}
+
+/**
+ * 开始滑动操作
+ * @param {Object} e
+ * @param {Object} ownerInstance
+ */
+function touchmove(e, ownerInstance) {
+	var instance = e.instance;
+	var disabled = instance.getDataset().disabled
+	var state = instance.getState()
+	// fix by mehaotian, TODO 兼容 app-vue 获取dataset为字符串 , h5 获取 为 undefined 的问题,待框架修复
+	disabled = (typeof(disabled) === 'string' ? JSON.parse(disabled) : disabled) || false;
+	if (disabled) return
+	// 是否可以滑动页面
+	stopTouchMove(e);
+	if (state.direction !== 'horizontal') {
+		return;
+	}
+
+	if (e.preventDefault) {
+		// 阻止页面滚动
+		e.preventDefault()
+	}
+
+	move(state.x + state.deltaX, instance, ownerInstance)
+}
+
+/**
+ * 结束触摸操作
+ * @param {Object} e
+ * @param {Object} ownerInstance
+ */
+function touchend(e, ownerInstance) {
+	var instance = e.instance;
+	var disabled = instance.getDataset().disabled
+	var state = instance.getState()
+	// fix by mehaotian, TODO 兼容 app-vue 获取dataset为字符串 , h5 获取 为 undefined 的问题,待框架修复
+	disabled = (typeof(disabled) === 'string' ? JSON.parse(disabled) : disabled) || false;
+
+	if (disabled) return
+	// 滑动过程中触摸结束,通过阙值判断是开启还是关闭
+	// fixed by mehaotian 定时器解决点击按钮,touchend 触发比 click 事件时机早的问题 ,主要是 ios13
+	moveDirection(state.left, instance, ownerInstance)
+
+}
+
+/**
+ * 设置移动距离
+ * @param {Object} value
+ * @param {Object} instance
+ * @param {Object} ownerInstance
+ */
+function move(value, instance, ownerInstance) {
+	value = value || 0
+	var state = instance.getState()
+	var leftWidth = state.leftWidth
+	var rightWidth = state.rightWidth
+	// 获取可滑动范围
+	state.left = range(value, -rightWidth, leftWidth);
+	instance.requestAnimationFrame(function() {
+		instance.setStyle({
+			transform: 'translateX(' + state.left + 'px)',
+			'-webkit-transform': 'translateX(' + state.left + 'px)'
+		})
+	})
+
+}
+
+/**
+ * 获取元素信息
+ * @param {Object} instance
+ * @param {Object} ownerInstance
+ */
+function getDom(instance, ownerInstance) {
+	var state = instance.getState()
+	var leftDom = ownerInstance.selectComponent('.button-group--left')
+	var rightDom = ownerInstance.selectComponent('.button-group--right')
+	var leftStyles = {
+		width: 0
+	}
+	var rightStyles = {
+		width: 0
+	}
+	leftStyles = leftDom.getBoundingClientRect()
+	rightStyles = rightDom.getBoundingClientRect()
+
+	state.leftWidth = leftStyles.width || 0
+	state.rightWidth = rightStyles.width || 0
+	state.threshold = instance.getDataset().threshold
+}
+
+/**
+ * 获取范围
+ * @param {Object} num
+ * @param {Object} min
+ * @param {Object} max
+ */
+function range(num, min, max) {
+	return Math.min(Math.max(num, min), max);
+}
+
+
+/**
+ * 移动方向判断
+ * @param {Object} left
+ * @param {Object} value
+ * @param {Object} ownerInstance
+ * @param {Object} ins
+ */
+function moveDirection(left, ins, ownerInstance) {
+	var state = ins.getState()
+	var threshold = state.threshold
+	var position = state.position
+	var isopen = state.isopen || 'none'
+	var leftWidth = state.leftWidth
+	var rightWidth = state.rightWidth
+	if (state.deltaX === 0) {
+		openState('none', ins, ownerInstance)
+		return
+	}
+	if ((isopen === 'none' && rightWidth > 0 && -left > threshold) || (isopen !== 'none' && rightWidth > 0 &&
+			rightWidth +
+			left < threshold)) {
+		// right
+		openState('right', ins, ownerInstance)
+	} else if ((isopen === 'none' && leftWidth > 0 && left > threshold) || (isopen !== 'none' && leftWidth > 0 &&
+			leftWidth - left < threshold)) {
+		// left
+		openState('left', ins, ownerInstance)
+	} else {
+		// default
+		openState('none', ins, ownerInstance)
+	}
+}
+
+
+/**
+ * 开启状态
+ * @param {Boolean} type
+ * @param {Object} ins
+ * @param {Object} ownerInstance
+ */
+function openState(type, ins, ownerInstance) {
+	var state = ins.getState()
+	var leftWidth = state.leftWidth
+	var rightWidth = state.rightWidth
+	var left = ''
+	state.isopen = state.isopen ? state.isopen : 'none'
+	switch (type) {
+		case "left":
+			left = leftWidth
+			break
+		case "right":
+			left = -rightWidth
+			break
+		default:
+			left = 0
+	}
+
+	// && !state.throttle
+
+	if (state.isopen !== type) {
+		state.throttle = true
+		ownerInstance.callMethod('change', {
+			open: type
+		})
+
+	}
+
+	state.isopen = type
+	// 添加动画类
+	ins.requestAnimationFrame(function() {
+		ins.addClass('ani');
+		move(left, ins, ownerInstance)
+	})
+	// 设置最终移动位置,理论上只要进入到这个函数,肯定是要打开的
+}
+
+
+function getDirection(x, y) {
+	if (x > y && x > MIN_DISTANCE) {
+		return 'horizontal';
+	}
+	if (y > x && y > MIN_DISTANCE) {
+		return 'vertical';
+	}
+	return '';
+}
+
+/**
+ * 重置滑动状态
+ * @param {Object} event
+ */
+function resetTouchStatus(instance) {
+	var state = instance.getState();
+	state.direction = '';
+	state.deltaX = 0;
+	state.deltaY = 0;
+	state.offsetX = 0;
+	state.offsetY = 0;
+}
+
+/**
+ * 设置滑动开始位置
+ * @param {Object} event
+ */
+function stopTouchStart(event) {
+	var instance = event.instance;
+	var state = instance.getState();
+	resetTouchStatus(instance);
+	var touch = event.touches[0];
+	if (IS_HTML5 && isPC()) {
+		touch = event;
+	}
+	state.startX = touch.clientX;
+	state.startY = touch.clientY;
+}
+
+/**
+ * 滑动中,是否禁止打开
+ * @param {Object} event
+ */
+function stopTouchMove(event) {
+	var instance = event.instance;
+	var state = instance.getState();
+	var touch = event.touches[0];
+	if (IS_HTML5 && isPC()) {
+		touch = event;
+	}
+	state.deltaX = touch.clientX - state.startX;
+	state.deltaY = touch.clientY - state.startY;
+	state.offsetY = Math.abs(state.deltaY);
+	state.offsetX = Math.abs(state.deltaX);
+	state.direction = state.direction || getDirection(state.offsetX, state.offsetY);
+}
+
+function isPC() {
+	var userAgentInfo = navigator.userAgent;
+	var Agents = ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"];
+	var flag = true;
+	for (var v = 0; v < Agents.length - 1; v++) {
+		if (userAgentInfo.indexOf(Agents[v]) > 0) {
+			flag = false;
+			break;
+		}
+	}
+	return flag;
+}
+
+var movable = false
+
+function mousedown(e, ins) {
+	if (!IS_HTML5) return
+	if (!isPC()) return
+	touchstart(e, ins)
+	movable = true
+}
+
+function mousemove(e, ins) {
+	if (!IS_HTML5) return
+	if (!isPC()) return
+	if (!movable) return
+	touchmove(e, ins)
+}
+
+function mouseup(e, ins) {
+	if (!IS_HTML5) return
+	if (!isPC()) return
+	touchend(e, ins)
+	movable = false
+}
+
+function mouseleave(e, ins) {
+	if (!IS_HTML5) return
+	if (!isPC()) return
+	movable = false
+}
+
+module.exports = {
+	showWatch: showWatch,
+	touchstart: touchstart,
+	touchmove: touchmove,
+	touchend: touchend,
+	mousedown: mousedown,
+	mousemove: mousemove,
+	mouseup: mouseup,
+	mouseleave: mouseleave
+}

+ 60 - 0
components/uni-swipe-action/uni-swipe-action.vue

@@ -0,0 +1,60 @@
+<template>
+	<view>
+		<slot></slot>
+	</view>
+</template>
+
+<script>
+	/**
+	 * SwipeAction 滑动操作
+	 * @description 通过滑动触发选项的容器
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=181
+	 */
+	export default {
+		name:"uniSwipeAction",
+		data() {
+			return {};
+		},
+		created() {
+			this.children = [];
+		},
+		methods: {
+			// 公开给用户使用,重制组件样式
+			resize(){
+				// wxs 会自己计算组件大小,所以无需执行下面代码
+				// #ifndef APP-VUE || H5 || MP-WEIXIN
+				this.children.forEach(vm=>{
+					vm.init()
+				})
+				// #endif
+			},
+			// 公开给用户使用,关闭全部 已经打开的组件
+			closeAll(){
+				this.children.forEach(vm=>{
+					// #ifdef APP-VUE || H5 || MP-WEIXIN
+					vm.is_show = 'none'
+					// #endif
+
+					// #ifndef APP-VUE || H5 || MP-WEIXIN
+					vm.close()
+					// #endif
+				})
+			},
+			closeOther(vm) {
+				if (this.openItem && this.openItem !== vm) {
+					// #ifdef APP-VUE || H5 || MP-WEIXIN
+					this.openItem.is_show = 'none'
+					// #endif
+
+					// #ifndef APP-VUE || H5 || MP-WEIXIN
+					this.openItem.close()
+					// #endif
+				}
+				// 记录上一个打开的 swipe-action-item ,用于 auto-close
+				this.openItem = vm
+			}
+		}
+	};
+</script>
+
+<style></style>

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

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

+ 27 - 0
config/config.js

@@ -0,0 +1,27 @@
+const name = "遇见未来"; //全局商城name - 需要可配置
+export default {
+  name: name,
+  appid: "wx287bb4bfc3973523",
+  logo: "", //logo
+  serviceMobile: "110", //客服电话
+  baseWsUrl: "", // 后续需要socket可加入
+  enableGetClipboard: false, //粘贴板内容获取 scanAuthNavigation 可跳入对应页面
+  /**
+   * 更换主题 此处以及uni.scss中的全局颜色
+   */
+  themeColor: "#1A78F6", // 主题色
+  priceColor: "#FF5000", // 金额相关颜色
+  negativePriceColor: "#1A78F6", // 负金额相关颜色
+  columnColor: "#111111", // 栏目颜色
+  primaryColor: "#333333", // 主要内容颜色
+  descColor: "#999999", // 标题及次要内容字体颜色
+  iconColor: '#666666', // icon字体及图标颜色
+  // bigBtnBgColor: "liner-grdient(to right, #FF7001, #FF8805)", // 大按钮渐变
+  // smallBtnBgColor: "liner-grdient(to right, #FF8D0E, #FF5000)", // 小按钮渐变
+  // hollowColor: '#FF5000', // 镂空按钮
+  // tagPrimaryColor: '#FF5000', // 镂空标签 主色
+  // tagViceColor: '#FF9000', // 镂空标签 副色
+  // tagSecColor: '#999999', // 镂空标签 次要色
+
+  defaultUserPhoto: "", // 用户头像(默认)需要可配置
+};

+ 185 - 0
main.js

@@ -0,0 +1,185 @@
+import Vue from 'vue'
+import store from './store'
+import App from './App'
+import uView from '@/uni_modules/uview-ui';
+Vue.use(uView);
+
+// 下拉
+import drawer  from "@/components/drawer/drawer.vue"
+import md5 from "./common/md5.js"
+import mixin from "common/share.js"
+import filter from 'common/filter.js'
+import config from './config/config'
+
+// 引入全局uView
+// import uView from 'uview-ui'
+// Vue.use(uView);
+
+Vue.mixin(mixin)
+/**
+ *  因工具函数属于公司资产, 所以直接在Vue实例挂载几个常用的函数
+ *  所有测试用数据均存放于根目录json.js
+ *  
+ *  css部分使用了App.vue下的全局样式和iconfont图标,有需要图标库的可以留言。
+ *  示例使用了uni.scss下的变量, 除变量外已尽量移除特有语法,可直接替换为其他预处理器使用
+ */
+const msg = (title, duration=1500, mask=false, icon='none',)=>{
+	//统一提示方便全局修改
+	if(Boolean(title) === false){
+		return;
+	}
+	uni.showToast({
+		title,
+		duration,
+		mask,
+		icon,
+	});
+}
+const json = type=>{
+	//模拟异步请求数据
+	return new Promise(resolve=>{
+		resolve(Json[type]);
+	})
+}
+const prePage = ()=>{
+	let pages = getCurrentPages();
+	let prePage = pages[pages.length - 2];
+	// #ifdef H5
+	return prePage;
+	// #endif
+	return prePage.$vm;
+}
+
+const getSign = (str)=>{
+	return  md5(str + "A1B2C3");
+}
+
+const requst = (opt) => {
+    opt = opt || {};
+    opt.data = opt.data || null;
+    opt.method = opt.method || 'POST';
+    opt.header = opt.header || {
+        "Content-Type": "application/x-www-form-urlencoded"
+    };
+    opt.success = opt.success || function () {};
+	let url = opt.url
+	let json = getApp().globalData.storeInfo;
+	let baseUrl = getApp().globalData.apiUrl;
+	if (url.indexOf("/api/") == -1) {
+		// 生产环境通过nginx根据校区code路由,本地开发统一用a1(context-path)
+		if (baseUrl.indexOf('127.0.0.1') != -1 || baseUrl.indexOf('localhost') != -1) {
+			url = 'a1' + url
+		} else if (json) {
+			url = json.code + url
+		}
+	}
+	if (url.indexOf('http') != 0) {
+		// 去掉前导斜杠,避免 baseUrl + '/' + url 产生双斜杠 //
+		if (url.charAt(0) == '/') {
+			url = url.substring(1);
+		}
+		// /api/ 前缀的请求走管理平台服务(client-plat:8080),其他走移动端服务(client-app:8081)
+		if (url.indexOf('api/') == 0 && getApp().globalData.platApiUrl) {
+			url = getApp().globalData.platApiUrl + '/' + url;
+		} else {
+			url = baseUrl + '/' + url;
+		}
+	}
+
+    uni.request({
+        url: url,
+        data: opt.data,
+        method: opt.method,
+        header: opt.header,
+        dataType: 'json',
+        success: function (res) {
+			if (res && res.data && res.data.rtnBean) {
+				opt.success(res);
+			} else {
+				console.error('响应数据格式异常:', res);
+				uni.showToast({
+					title: '服务响应异常,请稍后重试',
+					icon: 'none'
+				});
+			}
+        },
+        fail: function (err) {
+			console.error('请求失败:', err);
+            uni.showToast({
+                title: '网络请求失败,请稍后重试',
+				icon: 'none'
+            });
+        }
+    })
+}
+
+const moneyConverter = (money)=> {
+	let m = Number(money) / Number(100);
+	return Math.round(m * 100) / 100;
+}
+
+const loginx = (code,storeUserId)=> {
+	uni.get
+	uni.login({
+		provider: 'weixin',
+		success: (loginRes) => {
+			uni.request({
+			    url: getApp().globalData.apiUrl + '/' + code + '/storeUserLoginApi/login',
+			    data: {
+			    	code: loginRes.code,
+					storeUserId: storeUserId
+			    },
+			    method: 'POST',
+			    header: {
+			    	'content-type': 'application/x-www-form-urlencoded',
+			    },
+			    dataType: 'json',
+			    success: function (res) {
+					// store.state.hasLogin = true;
+					// store.state.userInfo = res.data.obj;
+					// //缓存用户登陆状态
+					// uni.setStorage({
+					//     key: 'userInfo',  
+					//     data: res.data.obj  
+					// }) 
+			    },
+			    fail: function (err) {
+					console.log(err);
+			        uni.showLoading({
+			            title: '请稍后重试'
+			        });
+			    }
+			})
+		},
+	});
+}
+
+Vue.config.productionTip = false
+Vue.prototype.$fire = new Vue();
+Vue.prototype.requst = requst;
+Vue.prototype.moneyConverter = moneyConverter;
+Vue.prototype.loginx = loginx;
+Vue.prototype.$store = store;
+Vue.prototype.getSign = getSign;
+Vue.prototype.$api = {msg, prePage};
+
+Object.keys(filter).forEach(key=>{
+	Vue.filter(key, filter[key])
+})
+
+
+// 设置全局色号 config/config文件 配置色号
+Object.keys(config).forEach(i => {
+	if (i.includes('Color') || i.includes('color')) {
+		Vue.prototype['$' + i] = config[i]
+	}
+})
+
+Vue.component('drawer', drawer)
+App.mpType = 'app'
+
+ const app = new Vue({
+     ...App
+ })
+
+app.$mount()

+ 93 - 0
manifest.json

@@ -0,0 +1,93 @@
+{
+    "name" : "edupos_store",
+    "appid" : "__UNI__801AB40",
+    "description" : "",
+    "versionName" : "1.0.0",
+    "versionCode" : "100",
+    "transformPx" : false,
+    "sassImplementationName" : "node-sass",
+    /* 5+App特有相关 */
+    "app-plus" : {
+        "usingComponents" : true,
+        "nvueCompiler" : "uni-app",
+        "compilerVersion" : 3,
+        "splashscreen" : {
+            "alwaysShowBeforeRender" : true,
+            "waiting" : true,
+            "autoclose" : true,
+            "delay" : 0
+        },
+        "safearea" : {
+            "background" : "#FFFFFF",
+            "bottom" : {
+                "offset" : "auto"
+            },
+            "left" : {
+                "offset" : "auto"
+            },
+            "right" : {
+                "offset" : "auto"
+            }
+        },
+        /* 模块配置 */
+        "modules" : {},
+        /* 应用发布信息 */
+        "distribute" : {
+            /* android打包配置 */
+            "android" : {
+                "permissions" : [
+                    "<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
+                    "<uses-permission android:name=\"android.permission.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打包配置 */
+            "ios" : {},
+            /* SDK配置 */
+            "sdkConfigs" : {}
+        }
+    },
+    /* 快应用特有相关 */
+    "quickapp" : {},
+    /* 小程序特有相关 */
+    "mp-weixin" : {
+        "appid" : "wx287bb4bfc3973523", //遇见未来
+
+        //"appid" : "wx318d80091636b924",//遇见优品
+        "setting" : {
+            "urlCheck" : false,
+            "minified" : true,
+            "postcss" : true
+        },
+        "usingComponents" : true
+    },
+    "mp-alipay" : {
+        "usingComponents" : true
+    },
+    "mp-baidu" : {
+        "usingComponents" : true
+    },
+    "mp-toutiao" : {
+        "usingComponents" : true
+    },
+    "uniStatistics" : {
+        "enable" : false
+    },
+    "h5" : {
+        "devServer" : {
+            "port" : 80
+        }
+    }
+}

+ 376 - 0
pages.json

@@ -0,0 +1,376 @@
+{
+	"pages": [
+		{
+			"path": "pages/index/index",
+			"style": {
+				"navigationBarTitleText": "订单管理",
+				"enablePullDownRefresh": true
+			}
+		},
+		{
+			"path": "pages/login/login",
+			"style": {
+				"navigationBarTitleText": "商家登录"
+			}
+		},
+		{
+			"path": "pages/order/index",
+			"style": {
+				"navigationBarTitleText": "订单查询"
+			}
+		},
+		{
+			"path": "pages/mine/index",
+			"style": {
+				"navigationBarTitleText": "我的"
+			}
+		},
+		{
+			"path": "pages/inspect/list",
+			"style": {
+				"navigationBarTitleText": "巡检记录",
+				"enablePullDownRefresh": true
+			}
+		},
+		{
+			"path": "pages/inspect/detail",
+			"style": {
+				"navigationBarTitleText": "巡检报告"
+			}
+		},
+		{
+			"path": "pages/rectification/list",
+			"style": {
+				"navigationBarTitleText": "整改记录",
+				"enablePullDownRefresh": true
+			}
+		},
+		{
+			"path": "pages/rectification/detail",
+			"style": {
+				"navigationBarTitleText": "整改详情"
+			}
+		}
+    ],
+	"subPackages": [
+		{
+			"root": "pages/store",
+			"pages": [
+				{
+					"path": "index",
+					"style": {
+						"navigationBarTitleText": "店铺信息"
+					}
+				},
+				{
+					"path": "extensionCode",
+					"style": {
+						"navigationBarTitleText": "店铺推广码"
+					}
+				},
+				{
+					"path": "codeinfo",
+					"style": {
+						"navigationBarTitleText": "推广码详情"
+					}
+				},
+				{
+					"path": "clerk",
+					"style": {
+						"navigationBarTitleText": "店员管理",
+						"enablePullDownRefresh": true
+					}
+				},
+				{
+					"path": "distribution",
+					"style": {
+						"navigationBarTitleText": "配送管理"
+					}
+				},
+				{
+					"path": "deliveryman",
+					"style": {
+						"navigationBarTitleText": "专属配送员"
+					}
+				}
+			]
+		},
+        {
+            "root": "pages/activity",
+            "pages": [
+                {
+                    "path": "marketing",
+                    "style": {
+                        "navigationBarTitleText": "营销中心"
+                    }
+                },
+                {
+                    "path": "special-item",
+                    "style": {
+                        "navigationBarTitleText": "特价菜"
+                    }
+                },
+                {
+                    "path": "special-item-info",
+                    "style": {
+                        "navigationBarTitleText": "特价菜活动"
+                    }
+                },
+                {
+                    "path": "special-item-edit",
+                    "style": {
+                        "navigationBarTitleText": "编辑特价菜活动"
+                    }
+                },
+				{
+				    "path": "coupon-item",
+				    "style": {
+				        "navigationBarTitleText": "优惠券"
+				    }
+				},
+				{
+				    "path": "coupon-item-info",
+				    "style": {
+				        "navigationBarTitleText": "优惠券详情"
+				    }
+				},
+				{
+				    "path": "coupon-item-edit",
+				    "style": {
+				        "navigationBarTitleText": "编辑优惠券"
+				    }
+				},
+                {
+                	"path": "add-goods",
+                	"style": {
+                		"navigationBarTitleText": "菜品信息"
+                	}
+                },
+				{
+                	"path": "add-coupon-goods",
+                	"style": {
+                		"navigationBarTitleText": "优惠券指定商品"
+                	}
+                },
+                {
+                	"path": "full-discounts",
+                	"style": {
+                		"navigationBarTitleText": "店铺满减"
+                	}
+                },
+                {
+                	"path": "update-full-discounts",
+                	"style": {
+                		"navigationBarTitleText": "满减活动"
+                	}
+                },
+                {
+                	"path": "overlord-meal",
+                	"style": {
+                		"navigationBarTitleText": "霸王餐"
+                	}
+                },
+                {
+                	"path": "overlord-meal-detail",
+                	"style": {
+                		"navigationBarTitleText": "霸王餐详情"
+                	}
+                },
+                {
+                	"path": "advertising",
+                	"style": {
+                		"navigationBarTitleText": "广告位",
+						"enablePullDownRefresh": true
+                	}
+                },
+				{
+                	"path": "new-customer",
+                	"style": {
+                		"navigationBarTitleText": "新客满减设置"
+                	}
+                },
+				{
+					"path": "free-delivery",
+					"style": {
+						"navigationBarTitleText": "免配送费设置"
+					}
+				}
+            ]
+        },
+		{
+			"root": "pages/search",
+			"pages": [
+				{
+					"path": "index",
+					"style": {
+						"navigationBarTitleText": "订单搜索"
+					}
+				},
+				{
+					"path": "collect",
+					"style": {
+						"navigationBarTitleText": "当日汇总"
+					}
+				}
+			]
+		},
+		{
+			"root": "pages/goods",
+			"pages": [
+				{
+					"path": "goods",
+					"style": {
+						"navigationBarTitleText": "商品管理"
+					}
+				},
+				{
+					"path": "goods-edit",
+					"style": {
+						"navigationBarTitleText": "添加/编辑商品"
+					}
+				},
+				{
+					"path": "goods-type",
+					"style": {
+						"navigationBarTitleText": "管理分类"
+					}
+				}
+			]
+		},
+		{
+			"root": "pages/evaluate",
+			"pages": [
+				{
+					"path": "index",
+					"style": {
+						"navigationBarTitleText": "评价管理"
+					}
+				}
+			]
+		},
+		{
+			"root": "pages/webView",
+			"pages": [
+				{
+					"path": "web_view",
+					"style": {
+						"navigationBarTitleText": ""
+					}
+				}
+			]
+		},
+		{
+			"root": "pages/tools",
+			"pages": [
+				{
+					"path": "settle",
+					"style": {
+						"navigationBarTitleText": "结算管理"
+					}
+				},
+				{
+					"path": "settle-order",
+					"style": {
+						"navigationBarTitleText": "结算管理"
+					}
+				},
+				{
+					"path": "settle-info",
+					"style": {
+						"navigationBarTitleText": "收入明细"
+					}
+				},
+				{
+					"path": "evaluation",
+					"style": {
+						"navigationBarTitleText": "评价管理"
+					}
+				},
+				{
+					"path": "print",
+					"style": {
+						"navigationBarTitleText": "小票打印机",
+						"enablePullDownRefresh": true
+					}
+				},
+				{
+					"path": "bind",
+					"style": {
+						"navigationBarTitleText": "绑定公众号"
+					}
+				},
+				{
+					"path": "sn",
+					"style": {
+						"navigationBarTitleText": "兑换券"
+					}
+				},
+				{
+					"path": "setting",
+					"style": {
+						"navigationBarTitleText": "设置"
+					}
+				},
+				{
+					"path": "safety-archives",
+					"style": {
+						"navigationBarTitleText": "商品安全档案"
+					}
+				},
+				{
+					"path": "hexiao_coupon",
+					"style": {
+						"navigationBarTitleText": "霸王餐核销",
+						"enablePullDownRefresh": true
+					}
+				},
+				{
+					"path": "userAngument",
+					"style": {
+						"navigationBarTitleText": "用户协议"
+					}
+				},
+				{
+					"path": "privacyAngument",
+					"style": {
+						"navigationBarTitleText": "隐私政策"
+					}
+				}
+			]
+		}
+	],
+	"tabBar": {
+		"color": "#C0C4CC",
+		"selectedColor": "#1A78F6",
+		"backgroundColor":"rgba(5,4,9,0.3)",
+		"list": [
+			{
+				"pagePath": "pages/index/index",
+				"iconPath": "static/tab-home.png",
+				"selectedIconPath": "static/tab-home-current1.png",
+				"text": "订单管理"
+			},
+			{
+				"pagePath": "pages/order/index",
+				"iconPath": "static/tab-brand.png",
+				"selectedIconPath": "static/tab-brand-current1.png",
+				"text": "订单查询"
+			},
+			{
+				"pagePath": "pages/mine/index",
+				"iconPath": "static/tab-my.png",
+				"selectedIconPath": "static/tab-my-current1.png",
+				"text": "我的"
+			}
+		]
+	},
+	"globalStyle": {
+		"navigationBarTextStyle": "black",
+		"navigationBarTitleText": "uni-app",
+		"navigationBarBackgroundColor": "#FFF",
+		"backgroundColor": "#F5F5F5"
+	},
+	"easycom": {
+		"^u-(.*)": "@/uni_modules/uview-ui/components/u-$1/u-$1.vue"
+	}
+}

BIN=BIN
pages/.DS_Store


+ 299 - 0
pages/activity/add-coupon-goods.vue

@@ -0,0 +1,299 @@
+<template>
+    <view class="add-goods-page">
+        <scroll-view scroll-y class="goods-form">
+            <view class="item" @click="chooseGoods">
+                <view class="goodsLabel">{{form.goodsName ? form.goodsName : '选择商品'}}</view>
+                <view class="value">
+                    <text class="goods-name"></text>
+                    <u-icon v-if="!form.storeSpecialGoodsId" name="arrow-right" size="20" color="#6c6c6c"></u-icon>
+                </view>
+            </view>
+            <view class="item no-border">
+                <view class="label">缩略图</view>
+                <view class="value">
+                    <image class="cover" :src="form.pictureUrl" />
+                </view>
+            </view>
+        </scroll-view>
+
+        <u-modal
+            :show="chooseGoodsVisible"
+            :showConfirmButton="false"
+            closeOnClickOverlay
+            @close="chooseGoodsVisible = false">
+            <view class="modal-container">
+                <view class="search-wrap">
+                    <view class="input-wrap">
+                        <u-input border="none" fontSize="14" v-model="goodsName" placeholder="商品名称"></u-input>
+                    </view>
+                    <view class="btn" @click="getGoodsList">搜索</view>
+                </view>
+                <view class="goods-list">
+                    <view class="item" v-for="item in goodsList" :key="item.goodsId" @click="handItem(item)">
+                        <view class="cover">
+                            <image class="img" :src="item.pictureUrl"/>
+                        </view>
+                         <view class="cont">
+                            <view class="goods-name">
+                               {{item.goodsName}}
+                            </view>
+                            <view class="desc">
+                                库存: {{item.maxNum}}, 价格: ¥{{moneyConverter(item.costPrice)}}
+                            </view>
+                         </view>
+                    </view>
+                </view>
+            </view>
+        </u-modal>
+        <view class="update-events">
+            <view class="btn" @click="save">保存</view>
+        </view>
+    </view>
+</template>
+<script>
+import {mapState} from 'vuex';
+export default {
+    data () {
+        return {
+            getStoreSingleGoodsForStoreUrl: '/storeGoodsManageApi/getStoreSingleGoodsForSpecialForStore',
+			notCouponGoodsUrl: "/coupon/getStoreNotCouponGoodsList",
+            chooseGoodsVisible: false,
+            goodsName: '',
+            goodsList: [],
+            form: {
+                goodsId: '',
+                goodsName: '',
+                costPrice: '',
+                modelId: '',
+                pictureUrl: '',
+                activityPrice: '',
+                maxNum: '',
+                activityNum: ''
+            },
+            index : '',
+			idsStr: ''
+        }
+    },
+    computed: {
+        ...mapState(['storeInfo'])
+    },
+    methods: {
+        handItem(item) {
+            this.form.goodsId = item.goodsId
+            this.form.goodsName = item.goodsName;
+            this.chooseGoodsVisible = false
+        },
+		inventoryMaxNum(index,e){
+			// 只能输入数字
+			const inputType = /[^\d]/g
+			this.$nextTick(function () {
+				this.form.maxNum = e.replace(inputType,'');
+			})
+		},
+		inventoryInput(index,e){
+			// 只能输入数字
+			const inputType = /[^\d]/g
+			this.$nextTick(function () {
+				this.form.activityNum = e.replace(inputType,'');
+			})
+		},
+        chooseGoods() {
+            if (this.form.storeSpecialGoodsId) {
+                return;
+            }
+            this.goodsName = '';
+            this.getGoodsList();
+            this.chooseGoodsVisible = true;
+        },
+        getGoodsList() {
+			uni.showLoading({
+				title: '加载中'
+			});
+
+            var dateStr = (new Date()).getTime();
+            var sign = this.getSign('' + this.storeInfo.storeId + dateStr);
+            this.requst({
+            	url: this.notCouponGoodsUrl,
+            	data: {
+            		timestamp: dateStr,
+            		sign: sign,
+            		storeId: this.storeInfo.storeId,
+                    goodsName: this.goodsName
+            	},
+            	success: res => {
+                    this.goodsList = res.data.list;
+					uni.hideLoading()
+            	},
+            	fail: (e) => {
+            		console.log("接口调用失败:" + JSON.stringify(e));
+					uni.hideLoading();
+            	},
+            })
+        },
+        save() {
+			let regPrice = (/^[0-9]\d*(,\d{3})*(\.\d{1,2})?$|^0\.\d{1,2}$/);
+            if (!this.form.goodsId) {
+                this.$api.msg("请选择商品");
+                return;
+            }
+
+            let pages = getCurrentPages();
+            let prevpage = pages[pages.length - 2];
+            
+            let tempList = prevpage.$vm.goodsList;
+            for(var i = 0; i < tempList.length; i++) {
+                if (tempList[i].goodsId == this.form.goodsId && i != this.index){
+                    this.$api.msg("该商品已经选择了,请更换其他商品");
+                    return;
+                }
+            }
+            
+            if (this.index != '') {
+                prevpage.$vm.goodsList.splice(this.index, 1)
+            }
+			prevpage.$vm.goodsList = [];
+            prevpage.$vm.goodsList.push(this.form);
+            uni.navigateBack();
+        }
+    },
+    onShow () {
+        
+    },
+    onLoad (options) {
+        if ('goods' in options) {
+            this.form = JSON.parse(decodeURIComponent(options.goods));
+            this.form.activityPrice = this.form.activityPrice / 100;
+        }
+        if ('index' in options) {
+            this.index = options.index
+        } 
+		if ('idsStr' in options) {
+			this.idsStr = JSON.parse(decodeURIComponent(options.idsStr));
+		} 
+    }
+}
+</script>
+<style lang="scss" scoped>
+.add-goods-page {
+    border-top: 2rpx solid #f5f5f5;
+    min-height: 100vh;
+}
+.update-events {
+    width: 100vw;
+    background-color: #fff;
+    @include flexCenter;
+    margin-top: 40rpx;
+    .btn {
+        width: 80vw;
+        height: 80rpx;
+        border-radius: 40rpx;
+        background-color: $theme-color;
+        color: #fff;
+        @include flexCenter;
+    }
+}
+.goods-form {
+    background-color: #fff;
+    padding: 0 40rpx;
+    box-sizing: border-box;
+    height: calc(100vh - 120rpx - env(safe-area-inset-bottom));
+    height: calc(100vh - 120rpx - constant(safe-area-inset-bottom));
+    .item {
+        @include between;
+        height: 100rpx;
+        border-bottom: 2rpx solid #ececec;
+        font-size: $font-base;
+    }
+    .label {
+        width: 200rpx;
+    }
+	.goodsLabel {
+		width: 380rpx;
+		overflow: hidden;
+		text-overflow: ellipsis;
+		white-space: nowrap;
+	}
+    .value {
+        width: calc(100% - 200rpx);
+        @include flexEnd;
+        .cover {
+            width: 100rpx;
+            height: 100rpx;
+        }
+    }
+    .no-border {
+        border-bottom: none;
+        height: 140rpx;
+    }
+    .price {
+        color: $uni-color-button;
+    }
+    .fixx {
+        margin-left: 20rpx;
+    }
+}
+/deep/ .u-modal__content {
+    padding: 30rpx!important;
+    box-sizing: border-box;
+}
+.modal-container {
+    width: 100%;
+    .search-wrap {
+        width: 100%;
+        @include flexCenter;
+        border: 2rpx solid $theme-color;
+        color: $theme-color;
+        font-size: $font-base;
+        padding-left: 20rpx;
+        box-sizing: border-box;
+        margin-bottom: 20rpx;
+        .btn {
+            width: 128rpx;
+            height: 60rpx;
+            @include flexCenter;
+            border-left: 2rpx solid $theme-color;
+        }
+        .input-wrap {
+            flex: 1;
+            /deep/ .u-input {
+                width: 100%;
+            }
+        }
+    }
+    .goods-list {
+        height: 500rpx;
+        overflow-y: auto;
+        overflow-x: hidden;
+        .item {
+            margin-bottom: 20rpx;
+            @include flexStart;
+        }
+        .cover {
+            width: 120rpx;
+            height: 120rpx;
+            border-radius: 10rpx;
+            margin-right: 20rpx;
+            .img {
+                width: 120rpx;
+                height: 120rpx;
+            }
+        }
+        .cont {
+            font-size: $font-base;
+            .goods-name {
+                width: 100%;
+                text-overflow: -o-ellipsis-lastline;
+                overflow: hidden;
+                text-overflow: ellipsis;
+                display: -webkit-box;
+                -webkit-line-clamp: 2;
+                -webkit-box-orient: vertical;
+                color: $text-color;
+            }
+            .desc {
+                color: $desc-color;
+            }
+        }
+    }
+}
+</style>

+ 365 - 0
pages/activity/add-goods.vue

@@ -0,0 +1,365 @@
+<template>
+    <view class="add-goods-page">
+        <scroll-view scroll-y class="goods-form">
+            <view class="item" @click="chooseGoods">
+                <view class="goodsLabel">{{form.goodsName ? form.goodsName : '选择商品'}}</view>
+                <view class="value">
+                    <text class="goods-name"></text>
+                    <u-icon v-if="!form.storeSpecialGoodsId" name="arrow-right" size="20" color="#6c6c6c"></u-icon>
+                </view>
+            </view>
+            <view class="item no-border">
+                <view class="label">缩略图</view>
+                <view class="value">
+                    <image class="cover" :src="form.pictureUrl" />
+                </view>
+            </view>
+            <view class="item">
+                <view class="label">原价</view>
+                <view class="value price">{{moneyConverter(form.costPrice)}}元</view>
+            </view>
+            <view class="item">
+                <view class="label">活动价</view>
+                <view class="value">
+                    <u-input border="none" inputAlign="right" fontSize="14" v-model="form.activityPrice" placeholder="请填写活动价格"></u-input>
+                    <text class="fixx">元</text>
+                </view>
+            </view>
+            <view class="item">
+                <view class="label">每单限购</view>
+                <view class="value">
+                    <u-input border="none" inputAlign="right" type="number" @input="inventoryMaxNum(index,$event)" fontSize="14" v-model="form.maxNum" placeholder="限购数量"></u-input>
+                    <text class="fixx">份</text>
+                </view>
+            </view>
+            <view class="item">
+                <view class="label">活动库存</view>
+                <view class="value">
+                    <u-input border="none" inputAlign="right" type="number" @input="inventoryInput(index,$event)" fontSize="14" v-model="form.activityNum" placeholder="999"></u-input>
+                    <text class="fixx">份</text>
+                </view>
+            </view>
+        </scroll-view>
+
+        <u-modal
+            :show="chooseGoodsVisible"
+            :showConfirmButton="false"
+            closeOnClickOverlay
+            @close="chooseGoodsVisible = false">
+            <view class="modal-container">
+                <view class="search-wrap">
+                    <view class="input-wrap">
+                        <u-input border="none" fontSize="14" v-model="goodsName" placeholder="商品名称"></u-input>
+                    </view>
+                    <view class="btn" @click="getGoodsList">搜索</view>
+                </view>
+                <view class="goods-list">
+                    <view class="item" v-for="item in goodsList" :key="item.goodsId" @click="handItem(item)">
+                        <view class="cover">
+                            <image class="img" :src="item.pictureUrl"/>
+                        </view>
+                         <view class="cont">
+                            <view class="goods-name">
+                               {{item.goodsName}}
+                            </view>
+                            <view class="desc">
+                                库存: {{item.maxNum}}, 价格: ¥{{moneyConverter(item.costPrice)}}
+                            </view>
+                         </view>
+                    </view>
+                </view>
+            </view>
+        </u-modal>
+        <view class="update-events">
+            <view class="btn" @click="save">保存</view>
+        </view>
+    </view>
+</template>
+<script>
+import {mapState} from 'vuex';
+export default {
+    data () {
+        return {
+            getStoreSingleGoodsForStoreUrl: '/storeGoodsManageApi/getStoreSingleGoodsForSpecialForStore',
+            chooseGoodsVisible: false,
+            goodsName: '',
+            goodsList: [],
+            form: {
+                goodsId: '',
+                goodsName: '',
+                costPrice: '',
+                modelId: '',
+                pictureUrl: '',
+                activityPrice: '',
+                maxNum: '',
+                activityNum: ''
+            },
+            index : '',
+			idsStr: ''
+        }
+    },
+    computed: {
+        ...mapState(['storeInfo'])
+    },
+    methods: {
+        handItem(item) {
+            this.form.goodsId = item.goodsId
+            this.form.goodsName = item.goodsName;
+            this.form.costPrice = item.costPrice;
+            this.form.modelId = item.modelId;
+            this.form.pictureUrl = item.pictureUrl;
+            
+            this.chooseGoodsVisible = false
+        },
+		inventoryMaxNum(index,e){
+			// 只能输入数字
+			const inputType = /[^\d]/g
+			this.$nextTick(function () {
+				this.form.maxNum = e.replace(inputType,'');
+			})
+		},
+		inventoryInput(index,e){
+			// 只能输入数字
+			const inputType = /[^\d]/g
+			this.$nextTick(function () {
+				this.form.activityNum = e.replace(inputType,'');
+			})
+		},
+        chooseGoods() {
+            if (this.form.storeSpecialGoodsId) {
+                return;
+            }
+            this.goodsName = '';
+            this.getGoodsList();
+            this.chooseGoodsVisible = true;
+        },
+        getGoodsList() {
+			uni.showLoading({
+				title: '加载中'
+			});
+
+            var dateStr = (new Date()).getTime();
+            var sign = this.getSign('' + this.storeInfo.storeId + dateStr);
+            this.requst({
+            	url: this.getStoreSingleGoodsForStoreUrl,
+            	data: {
+            		timestamp: dateStr,
+            		sign: sign,
+            		storeId: this.storeInfo.storeId,
+                    goodsName: this.goodsName,
+					ids: this.idsStr || ''
+            	},
+            	success: res => {
+                    this.goodsList = res.data.list;
+					uni.hideLoading()
+            	},
+            	fail: (e) => {
+            		console.log("接口调用失败:" + JSON.stringify(e));
+					uni.hideLoading();
+            	},
+            })
+        },
+        save() {
+			let regPrice = (/^[0-9]\d*(,\d{3})*(\.\d{1,2})?$|^0\.\d{1,2}$/);
+            if (!this.form.goodsId) {
+                this.$api.msg("请选择商品");
+                return;
+            }
+            
+            if (!this.form.activityPrice) {
+                this.$api.msg("请输入活动价格");
+                return;
+            }
+			if (!regPrice.test(this.form.activityPrice)) {
+				this.$api.msg("活动价格只能是整数或小数点后两位");
+				return;
+			} 
+
+            if (this.form.activityPrice * 100 >= this.form.costPrice) {
+                this.$api.msg("活动价必须小于原价");
+                return;
+            }
+            
+            if (!this.form.maxNum) {
+                this.$api.msg("请输入每单限购");
+                return;
+            }
+			
+			if (Number(this.form.maxNum) === 0) {
+			    this.$api.msg("每单限购要大于0");
+			    return;
+			}
+            
+            if (!this.form.activityNum) {
+                this.$api.msg("请输入活动库存");
+                return;
+            }
+			if (Number(this.form.maxNum) > Number(this.form.activityNum)) {
+			    this.$api.msg("每单限购不能大于活动库存");
+			    return;
+			}
+			if (Number(this.form.activityNum) > Number(9999)) {
+                this.$api.msg("活动库存不能超过9999");
+                return;
+            }
+            
+            let pages = getCurrentPages();
+            let prevpage = pages[pages.length - 2];
+            
+            let tempList = prevpage.$vm.goodsList;
+            for(var i = 0; i < tempList.length; i++) {
+                if (tempList[i].goodsId == this.form.goodsId && i != this.index){
+                    this.$api.msg("该商品已经选择了,请更换其他商品");
+                    return;
+                }
+            }
+            
+            if (this.index != '') {
+                prevpage.$vm.goodsList.splice(this.index, 1)
+            }
+            this.form.activityPrice = this.form.activityPrice * 100;
+            prevpage.$vm.goodsList.push(this.form);
+            uni.navigateBack();
+        }
+    },
+    onShow () {
+        
+    },
+    onLoad (options) {
+        if ('goods' in options) {
+            this.form = JSON.parse(decodeURIComponent(options.goods));
+            this.form.activityPrice = this.form.activityPrice / 100;
+        }
+        if ('index' in options) {
+            this.index = options.index
+        } 
+		if ('idsStr' in options) {
+			this.idsStr = JSON.parse(decodeURIComponent(options.idsStr));
+		} 
+    }
+}
+</script>
+<style lang="scss" scoped>
+.add-goods-page {
+    border-top: 2rpx solid #f5f5f5;
+    min-height: 100vh;
+}
+.update-events {
+    width: 100vw;
+    background-color: #fff;
+    @include flexCenter;
+    margin-top: 40rpx;
+    .btn {
+        width: 80vw;
+        height: 80rpx;
+        border-radius: 40rpx;
+        background-color: $theme-color;
+        color: #fff;
+        @include flexCenter;
+    }
+}
+.goods-form {
+    background-color: #fff;
+    padding: 0 40rpx;
+    box-sizing: border-box;
+    height: calc(100vh - 120rpx - env(safe-area-inset-bottom));
+    height: calc(100vh - 120rpx - constant(safe-area-inset-bottom));
+    .item {
+        @include between;
+        height: 100rpx;
+        border-bottom: 2rpx solid #ececec;
+        font-size: $font-base;
+    }
+    .label {
+        width: 200rpx;
+    }
+	.goodsLabel {
+		width: 380rpx;
+		overflow: hidden;
+		text-overflow: ellipsis;
+		white-space: nowrap;
+	}
+    .value {
+        width: calc(100% - 200rpx);
+        @include flexEnd;
+        .cover {
+            width: 100rpx;
+            height: 100rpx;
+        }
+    }
+    .no-border {
+        border-bottom: none;
+        height: 140rpx;
+    }
+    .price {
+        color: $uni-color-button;
+    }
+    .fixx {
+        margin-left: 20rpx;
+    }
+}
+/deep/ .u-modal__content {
+    padding: 30rpx!important;
+    box-sizing: border-box;
+}
+.modal-container {
+    width: 100%;
+    .search-wrap {
+        width: 100%;
+        @include flexCenter;
+        border: 2rpx solid $theme-color;
+        color: $theme-color;
+        font-size: $font-base;
+        padding-left: 20rpx;
+        box-sizing: border-box;
+        margin-bottom: 20rpx;
+        .btn {
+            width: 128rpx;
+            height: 60rpx;
+            @include flexCenter;
+            border-left: 2rpx solid $theme-color;
+        }
+        .input-wrap {
+            flex: 1;
+            /deep/ .u-input {
+                width: 100%;
+            }
+        }
+    }
+    .goods-list {
+        height: 500rpx;
+        overflow-y: auto;
+        overflow-x: hidden;
+        .item {
+            margin-bottom: 20rpx;
+            @include flexStart;
+        }
+        .cover {
+            width: 120rpx;
+            height: 120rpx;
+            border-radius: 10rpx;
+            margin-right: 20rpx;
+            .img {
+                width: 120rpx;
+                height: 120rpx;
+            }
+        }
+        .cont {
+            font-size: $font-base;
+            .goods-name {
+                width: 100%;
+                text-overflow: -o-ellipsis-lastline;
+                overflow: hidden;
+                text-overflow: ellipsis;
+                display: -webkit-box;
+                -webkit-line-clamp: 2;
+                -webkit-box-orient: vertical;
+                color: $text-color;
+            }
+            .desc {
+                color: $desc-color;
+            }
+        }
+    }
+}
+</style>

+ 668 - 0
pages/activity/advertising.vue

@@ -0,0 +1,668 @@
+<template>
+    <view class="advertising-page">
+        <view class="cell-group">
+            <view class="cell" v-if="form.carousel">
+                <view class="label">轮播图</view>
+                <view class="cont">
+                    <image class="banner" :src="form.carousel.imgUrl || ''" />
+                </view>
+                <view class="setting" @click="handSetting(1)">设置</view>
+            </view>
+            <view class="cell" v-if="form.specialFoodOne && form.specialFoodOne.specialFoodId">
+                <view class="label">特价菜1</view>
+                <view class="cont">
+                    <image class="cover" :src="form.specialFoodOne.pictureUrl" />
+                    <view class="name">{{form.specialFoodOne.goodsName || ''}}</view>
+                    <view class="price" v-if="form.specialFoodOne.costPrice">¥{{moneyConverter(form.specialFoodOne.costPrice)}}</view>
+                    <!-- <view class="price">¥{{moneyConverter(form.specialFoodOne.costPrice)}}</view> -->
+                </view>
+                <view class="setting" @click="handSetting(2)">设置</view>
+            </view>
+            <view class="cell" v-if="form.specialFoodTwo && form.specialFoodTwo.specialFoodId">
+                <view class="label">特价菜2</view>
+                <view class="cont">
+                    <image class="cover" :src="form.specialFoodTwo.pictureUrl" />
+                    <view class="name">{{form.specialFoodTwo.goodsName || ''}}</view>
+                    <view class="price" v-if="form.specialFoodTwo.costPrice">¥{{moneyConverter(form.specialFoodTwo.costPrice)}}</view>
+                    <!-- <view class="price">¥{{moneyConverter(form.specialFoodTwo.costPrice)}}</view> -->
+                </view>
+                <view class="setting" @click="handSetting(3)">设置</view>
+            </view>
+
+            <view class="cell dubble" v-if="form.indexStore != null ">
+                <view class="label">优选店铺</view>
+                <view class="cont cont-dubble">
+                    <view class="row">
+                        <image class="cover" :src="form.goodsList[0].pictureUrl" />
+                        <view class="name">{{form.goodsList[0].goodsName || ''}}</view>
+                        <!-- <view class="price">¥{{moneyConverter(form.goodsList[0].costPrice)}}</view> -->
+						 <view class="price" v-if="form.goodsList[0] && form.goodsList[0].costPrice">¥{{ moneyConverter(form.goodsList[0].costPrice)}}</view>
+                    </view>
+                    <view class="row">
+                        <image class="cover" :src="form.goodsList[1].pictureUrl" />
+                        <view class="name">{{form.goodsList[1].goodsName || ''}}</view>
+                        <view class="price" v-if="form.goodsList[1] && form.goodsList[1].costPrice">¥{{ moneyConverter(form.goodsList[1].costPrice)}}</view>
+                    </view>
+                </view>
+                <view class="setting" @click="handSetting(4)">设置</view>
+            </view>
+        </view>
+        <u-modal
+            :show="visible"
+            title="轮播店铺图选择"
+            closeOnClickOverlay
+            showCancelButton
+            @confirm="confirm"
+            @cancel="visible = false"
+            @close="visible = false">
+            <view class="container">
+                <view class="item">
+                    <view class="label">轮播图片</view>
+                    <u-upload
+						:fileList="fileList"
+						@afterRead="afterRead"
+						@delete="deletePic"
+						name="1"
+						:previewFullImage="true"
+						:maxCount="1"
+					></u-upload>
+                </view>
+            </view>
+        </u-modal>
+        <u-popup
+            mode="right"
+            closeable
+            :zIndex="99"
+            :overlay="false"
+            :customStyle="{ width: '100vw', height: '100vh' }"
+            :show="popupVisible"
+            @close="popupClose"
+            @open="popupOpen">
+            <view class="popup-container" v-if="active == 2">
+                <view class="title">特价菜品选择</view>
+                <view class="item">
+                    <view class="label" v-if="!form.specialFoodOne.goodsId">选择商品</view>
+                    <view class="label" v-else>{{form.specialFoodOne.goodsName}}</view>
+                    <view class="value" @click="chooseGoods">
+                        <view class="name"></view>
+                        <u-icon name="arrow-right" size="18" color="#6c6c6c"></u-icon>
+                    </view>
+                </view>
+                <view class="item">
+                    <view class="label">商品图片</view>
+                    <view class="value">
+                        <image class="cover" :src="form.specialFoodOne.pictureUrl"></image>
+                    </view>
+                </view>
+            </view>
+			<view class="popup-container" v-if="active == 3">
+                <view class="title">特价菜品选择</view>
+                <view class="item">
+                    <view class="label" v-if="!form.specialFoodTwo.goodsId">选择商品</view>
+                    <view class="label" v-else>{{form.specialFoodTwo.goodsName}}</view>
+                    <view class="value" @click="chooseGoods">
+                        <view class="name"></view>
+                        <u-icon name="arrow-right" size="18" color="#6c6c6c"></u-icon>
+                    </view>
+                </view>
+                <view class="item">
+                    <view class="label">商品图片</view>
+                    <view class="value">
+                        <image class="cover" :src="form.specialFoodTwo.pictureUrl"></image>
+                    </view>
+                </view>
+            </view>
+            <view class="popup-container" v-if="active == 4">
+                <view class="title">优选店铺商品选择</view>
+                <view class="item">
+                    <!-- <view class="label">选择商品</view> -->
+					<view class="label" v-if="!form.goodsList[0].goodsId">选择商品</view>
+					<view class="label" v-else>{{form.goodsList[0].goodsName}}</view>
+                    <view class="value" @click="chooseIndexGoods(1)">
+                        <view class="name"></view>
+                        <u-icon name="arrow-right" size="18" color="#6c6c6c"></u-icon>
+                    </view>
+                </view>
+                <view class="item">
+                    <view class="label">商品1图片</view>
+                    <view class="value">
+                        <image class="cover" :src="form.goodsList[0].pictureUrl"></image>
+                    </view>
+                </view>
+                <view class="item">
+                   <view class="label" v-if="!form.goodsList[1].goodsId">选择商品</view>
+                   <view class="label" v-else>{{form.goodsList[1].goodsName}}</view>
+                    <view class="value" @click="chooseIndexGoods(2)">
+                        <view class="name"></view>
+                        <u-icon name="arrow-right" size="18" color="#6c6c6c"></u-icon>
+                    </view>
+                </view>
+                <view class="item">
+                    <view class="label">商品2图片</view>
+                    <view class="value">
+                        <image class="cover" :src="form.goodsList[1].pictureUrl"></image>
+                    </view>
+                </view>
+            </view>
+		</u-popup>
+        <u-popup
+            mode="bottom"
+            :zIndex="10076"
+            :show="popupGoodsVisible"
+            @close="popupGoodsClose"
+            @open="popupGoodsOpen">
+            <view class="modal-container">
+                <view class="search-wrap">
+                    <view class="input-wrap">
+                        <u-input border="none" fontSize="14" v-model="searchValue" placeholder="商品名称"></u-input>
+                    </view>
+                    <view class="btn" @click="getGoods()">搜索</view>
+                </view>
+                <scroll-view scroll-y class="goods-list">
+                    <view class="item" v-for="item in goodsList" :key="item.goodsId" @click="handItem(item)">
+                        <view class="cover">
+                            <image class="img" :src="item.pictureUrl"/>
+                        </view>
+                         <view class="cont">
+                            <view class="goods-name">
+                                {{item.goodsName}}
+                            </view>
+                            <view class="desc">
+                               库存: {{item.maxNum}}, 价格: ¥{{item.costPrice ? moneyConverter(item.costPrice) : ''}}
+                            </view>
+                         </view>
+                    </view>
+                </scroll-view>
+            </view>
+		</u-popup>
+    </view>
+</template>
+<script>
+import { mapState } from 'vuex';
+export default {
+    data () {
+        return {
+			infoUrl: '/storeAdvertisingManageApi/getAdvertisingInfo',
+			uploadUrl: '/storeAdvertisingManageApi/upload',
+			specialUrl: '/storeAdvertisingManageApi/getSpecialGoodsListByStoreIdForStore',
+			updateSpecialUrl: '/storeAdvertisingManageApi/chooseFood',
+			updateStoreUrl: '/storeAdvertisingManageApi/chooseIndexStoreGoods',
+			getAllGoodslUrl: '/storeAdvertisingManageApi/getAllGoodsListByStoreIdForStore',
+            popupGoodsVisible: false,
+            popupVisible: false,
+            bannerCount: 1,
+            visible: false,
+            modalTitle: '',
+            active: 1,
+            form: {},
+			fileList: [],
+            goodsList: [],
+			indexV: 0,
+			searchValue: '',
+			imgUrl: ''
+        }
+    },
+	computed: {
+		...mapState(['storeInfo'])
+	},
+    methods: {
+		loadData () {
+			uni.showLoading({
+				title: '加载中'
+			});
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign('' + this.storeInfo.storeUserId + dateStr);
+			this.requst({
+				url: this.infoUrl,
+				data: {
+					timestamp: dateStr,
+					sign: sign,
+					storeId: this.storeInfo.storeId,
+					storeUserId: this.storeInfo.storeUserId
+				},
+				success: res => {
+					if (res.data.rtnBean.rtnCode == 0) {
+						this.form = res.data.obj;
+						if (this.form && this.form.carousel && this.form.imgUrl){
+							let picUrl = this.form.carousel.imgUrl;
+							this.fileList.push({
+								url: picUrl
+							})
+						}
+						uni.stopPullDownRefresh()
+						uni.hideLoading()
+					}
+				},
+				fail: (e) => {
+					console.log("接口调用失败:" + JSON.stringify(e));
+					uni.hideLoading()
+				}
+			})
+		},
+		getGoods () {
+			if(this.active == 2 || this.active == 3){
+				this.getGoodsList(this.searchValue)
+			} else if (this.active == 4) {
+				this.getAllGoodsList(this.searchValue)
+			}
+		},
+		getGoodsList(name) {
+			uni.showLoading({
+				title: '加载中'
+			});
+		    var dateStr = (new Date()).getTime();
+		    var sign = this.getSign('' + this.storeInfo.storeUserId + dateStr);
+		    this.requst({
+		    	url: this.specialUrl,
+		    	data: {
+		    		timestamp: dateStr,
+		    		sign: sign,
+		    		storeId: this.storeInfo.storeId,
+		    		storeUserId: this.storeInfo.storeUserId,
+					goodsName: name || ''
+		    	},
+		    	success: res => {
+		            this.goodsList = res.data.list;
+					uni.hideLoading();
+		    	},
+		    	fail: (e) => {
+		    		console.log("接口调用失败:" + JSON.stringify(e));
+					uni.hideLoading();
+		    	},
+		    })
+		},
+		getAllGoodsList(name) {
+			uni.showLoading({
+				title: '加载中'
+			});
+		    var dateStr = (new Date()).getTime();
+		    var sign = this.getSign('' + this.storeInfo.storeUserId + dateStr);
+		    this.requst({
+		    	url: this.getAllGoodslUrl,
+		    	data: {
+		    		timestamp: dateStr,
+		    		sign: sign,
+		    		storeId: this.storeInfo.storeId,
+		    		storeUserId: this.storeInfo.storeUserId,
+					goodsName: name || ''
+		    	},
+		    	success: res => {
+		            this.goodsList = res.data.list;
+					uni.hideLoading();
+		    	},
+		    	fail: (e) => {
+		    		console.log("接口调用失败:" + JSON.stringify(e));
+					uni.hideLoading();
+		    	},
+		    })
+		},
+		// 删除图片
+		deletePic(event) {
+			this.fileList.splice(event.index, 1)
+			this.imgUrl = ''
+		},
+		// 新增图片
+		async afterRead(event) {
+			// 当设置 multiple 为 true 时, file 为数组格式,否则为对象格式
+			let file = event.file
+			let fileListLen = this.fileList.length
+			// lists.map((item) => {
+			this.fileList.push({
+				...file,
+				status: 'success',
+				// message: '上传中'
+			})
+			this.imgUrl = file.url
+		},
+
+        handSetting (v) {
+            this.active = v
+            if (v == 1) {
+				this.visible = true
+			} 
+            else {
+				
+				this.popupVisible = true
+			}
+        },
+        addBanner (e) {
+        },
+        removeBanner (e) {
+        },
+        confirm (e) {
+			uni.showLoading({
+				title: '保存中'
+			});
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign('' + this.storeInfo.storeUserId + dateStr);
+			return new Promise((resolve, reject) => {
+				let a = uni.uploadFile({
+					url: this.storeInfo.code + this.uploadUrl,
+					filePath: this.imgUrl,
+					name: 'file',
+					formData: {
+						timestamp: dateStr,
+						sign: sign,
+						storeId: this.storeInfo.storeId,
+						storeUserId: this.storeInfo.storeUserId,
+						carouselId: this.form.carousel.carouselId
+					},
+					success: (res) => {
+						let data = JSON.parse(res.data)
+						if (data.rtnBean.rtnCode == 0) {
+							uni.hideLoading();
+							uni.showToast({
+								title: "提交成功",
+								duration: 3000,
+								icon: 'none',
+							});
+							this.loadData()
+							this.visible = false
+						} else {
+							uni.hideLoading();
+							if (data.rtnBean.rtnMsg != '') {
+								this.$api.msg(data.rtnBean.rtnMsg);
+							}
+						}
+					},
+					fail: (res) => {
+						uni.hideLoading();
+					}
+				});
+			})
+        },
+        popupClose () {
+            this.popupVisible = false
+			this.loadData()
+        },
+        popupOpen () {
+        },
+        popupGoodsClose () {
+            this.popupGoodsVisible = false
+			this.searchValue = ''
+        },
+        popupGoodsOpen () {
+			if (this.active == 2 || this.active == 3){
+				this.getGoodsList()
+			}
+			if (this.active == 4) {
+				this.getAllGoodsList()
+			}
+            
+        },
+        chooseGoods () {
+            this.popupGoodsVisible = true
+        },
+		chooseIndexGoods (index) {
+			this.indexV = index
+			this.popupGoodsVisible = true
+			
+		},
+        handItem (item) {
+			// 特价菜一
+			if(this.active == 2) {
+				this.updateSpecial(item.goodsId,this.form.specialFoodOne.specialFoodId);
+				this.form.specialFoodOne.goodsName = item.goodsName;
+				this.form.specialFoodOne.pictureUrl = item.pictureUrl;
+				this.form.specialFoodOne.goodsId = item.goodsId;
+			}
+			// 特价菜二
+			if (this.active == 3) {
+				this.updateSpecial(item.goodsId,this.form.specialFoodTwo.specialFoodId);
+				this.form.specialFoodTwo.goodsName = item.goodsName;
+				this.form.specialFoodTwo.pictureUrl = item.pictureUrl;
+				this.form.specialFoodTwo.goodsId = item.goodsId;
+			}
+	
+			// 优选店铺
+			if (this.active == 4) {
+				if (this.indexV == 1){
+					if(this.form.goodsList[0]) {
+						this.form.goodsList[0].goodsName = item.goodsName;
+						this.form.goodsList[0].pictureUrl = item.pictureUrl;
+						this.form.goodsList[0].goodsId = item.goodsId;
+					} else {
+						this.form.goodsList.push({
+							goodsName: item.goodsName,
+							pictureUrl: item.pictureUrl,
+							goodsId: item.goodsId
+						})
+					}
+					
+				}
+				if (this.indexV == 2) {
+					if(this.form.goodsList[1]) {
+						this.form.goodsList[1].goodsName = item.goodsName;
+						this.form.goodsList[1].pictureUrl = item.pictureUrl;
+						this.form.goodsList[1].goodsId = item.goodsId;
+					} else {
+						this.form.goodsList.push({
+							goodsName: item.goodsName,
+							pictureUrl: item.pictureUrl,
+							goodsId: item.goodsId
+						})
+					}
+				}
+				
+				this.updateStore(item.goodsId);
+			}
+			this.popupGoodsVisible = false
+        },
+		updateSpecial (goodsId,sepicalFoodId) {
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign('' + this.storeInfo.storeUserId + dateStr);
+			this.requst({
+				url: this.updateSpecialUrl,
+				data: {
+					timestamp: dateStr,
+					sign: sign,
+					storeId: this.storeInfo.storeId,
+					storeUserId: this.storeInfo.storeUserId,
+					goodsId: goodsId,
+					id: sepicalFoodId
+				},
+				success: res => {
+				},
+				fail: (e) => {
+					console.log("接口调用失败:" + JSON.stringify(e));
+				},
+			})
+		},
+		updateStore (goodsId) {
+			let id = ''
+			if (this.indexV == 1) {
+				if (this.form.goodsList[0]) {
+					id = this.form.goodsList[0].indexStoreGoodsId || ''
+				}
+			}
+			if (this.indexV == 2) {
+				if (this.form.goodsList[1]) {
+					id = this.form.goodsList[1].indexStoreGoodsId || ''
+				}
+			}
+			
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign('' + this.storeInfo.storeUserId + dateStr);
+			this.requst({
+				url: this.updateStoreUrl,
+				data: {
+					timestamp: dateStr,
+					sign: sign,
+					storeId: this.storeInfo.storeId,
+					storeUserId: this.storeInfo.storeUserId,
+					goodsId: goodsId,
+					indexStoreGoodsId: id
+				},
+				success: res => {
+				},
+				fail: (e) => {
+					console.log("接口调用失败:" + JSON.stringify(e));
+				},
+			})
+		},
+		onPullDownRefresh() {
+			this.loadData();
+		}
+    },
+    onLoad () {
+		this.loadData();
+	}
+}
+</script>
+<style lang="scss" scoped>
+.advertising-page {
+    width: 100vw;
+    min-height: 100vh;
+    background-color: #f5f5f5;
+}
+.cell-group {
+    border-top: 2rpx solid #f5f5f5;
+    .cell {
+        width: 100%;
+        margin: 10rpx 0;
+        background-color: #fff;
+        height: 180rpx;
+        @include flexStart;
+        padding: 0 20rpx;
+        box-sizing: border-box;
+        font-size: $font-base;
+    }
+    .label {
+        width: 160rpx;
+        color: $text-color;
+    }
+    .cont {
+        @include between;
+        width: calc(100% - 160rpx - 160rpx);
+    }
+    .dubble {
+        height: 360rpx;
+    }
+    .banner {
+        width: 328rpx;
+        height: 128rpx;
+    }
+    .cover {
+        width: 128rpx;
+        height: 128rpx;
+    }
+    .cont-dubble {
+        display: flex;
+        flex-direction: column;
+        align-items: flex-start;
+        justify-content: center;
+    }
+    .row {
+        width: 100%;
+        @include between;
+    }
+    .row:first-child {
+        margin-bottom: 20rpx;
+    }
+    .setting {
+        width: 100rpx;
+        margin-left: 60rpx;
+        padding: 6rpx 0;
+        background-color: $theme-color;
+        color: #fff;
+        @include flexCenter;
+        border-radius: 10rpx;
+    }
+    .name, .price {
+        color: $title-color;
+    }
+}
+.container {
+    .item {
+        @include flexCenter;
+        .label {
+            margin-right: 40rpx;
+        }
+    }
+}
+.popup-container {
+    text-align: center;
+    font-weight: bold;
+    padding-top: 30rpx;
+    .title {
+        margin-bottom: 40rpx;
+    }
+    .item {
+        @include between;
+        padding: 20rpx;
+        box-sizing: border-box;
+        border-bottom: 2rpx solid #ececec;
+        .label {
+            margin-right: 40rpx;
+        }
+        .cover {
+            width: 120rpx;
+            height: 120rpx;
+        }
+        .value {
+            flex: 1;
+            @include flexEnd;
+        }
+    }
+}
+.modal-container {
+    padding: 40rpx 40rpx 0;
+    box-sizing: border-box;
+    .search-wrap {
+        width: 100%;
+        @include flexCenter;
+        border: 2rpx solid $theme-color;
+        color: $theme-color;
+        font-size: $font-base;
+        padding-left: 20rpx;
+        box-sizing: border-box;
+        margin-bottom: 20rpx;
+        .btn {
+            width: 128rpx;
+            height: 60rpx;
+            @include flexCenter;
+            border-left: 2rpx solid $theme-color;
+        }
+        .input-wrap {
+            flex: 1;
+            /deep/ .u-input {
+                width: 100%;
+            }
+        }
+    }
+    .goods-list {
+        height: 50vh;
+        margin-top: 20rpx;
+        .item {
+            margin-bottom: 20rpx;
+            @include flexStart;
+        }
+        .cover {
+            width: 120rpx;
+            height: 120rpx;
+            border-radius: 10rpx;
+            margin-right: 20rpx;
+            .img {
+                width: 120rpx;
+                height: 120rpx;
+            }
+        }
+        .cont {
+            font-size: $font-base;
+            .goods-name {
+                width: 100%;
+                text-overflow: -o-ellipsis-lastline;
+                overflow: hidden;
+                text-overflow: ellipsis;
+                display: -webkit-box;
+                -webkit-line-clamp: 2;
+                -webkit-box-orient: vertical;
+                color: $text-color;
+            }
+            .desc {
+                color: $desc-color;
+            }
+        }
+    }
+}
+</style>

+ 767 - 0
pages/activity/coupon-item-edit.vue

@@ -0,0 +1,767 @@
+<template>
+    <view class="special-item-edit-page">
+       <scroll-view scroll-y class="scroll-content">
+            <view class="cell-wrap">
+                <view class="item">
+                    <view class="label"><span class="required">*</span>优惠券标题</view>
+                    <view class="value">
+                        <input border="none" inputAlign="right" maxlength="15" fontSize="14" v-model="form.couponName" placeholder="请填写"></input>
+                    </view>
+                </view>
+                <view class="item">
+                    <view class="label"><span class="required">*</span>子标题</view>
+                    <view class="value">
+                        <input border="none" inputAlign="right" maxlength="15" fontSize="14" v-model="form.couponTitle" placeholder="请填写"></input>
+                    </view>
+                </view>
+				<view class="item dubble">
+					<view class="row">
+						<view class="label">优惠券图片</view>
+						<view class="value">
+							<u-upload :fileList="fileList" @afterRead="afterRead" @delete="deletePic" name="1"
+								:previewFullImage="true" :maxCount="1"></u-upload>
+						</view>
+					</view>
+				</view>
+				<view class="item">
+				    <view class="label"><span class="required">*</span>优惠类型</view>
+					<view class="value">
+						<uni-data-select
+							v-model="form.discountType"
+							:localdata="discountTypeList"
+							@change="changeDiscountType"
+							:border="false"
+						></uni-data-select>
+					</view>
+				</view>
+				<view class="item" v-if="form.discountType === 1">
+				    <view class="label"><span class="required">*</span>满减标准金额</view>
+				    <view class="value">
+				        <input border="none" inputAlign="right" maxlength="15" fontSize="14" v-model="form.couponFullPrice" placeholder="请填写"></input>
+				    </view>
+				</view>
+				<view class="item" v-if="form.discountType !== 3 && form.discountType !== 4">
+				    <view class="label"><span class="required">*</span>面额</view>
+				    <view class="value">
+				        <input border="none" inputAlign="right" maxlength="15" fontSize="14" v-model="form.price" placeholder="请填写"></input>
+				    </view>
+				</view>
+				<view class="item" v-if="form.discountType === 3">
+				    <view class="label"><span class="required">*</span>折扣(%)</view>
+				    <view class="value">
+				        <input border="none" inputAlign="right" maxlength="15" fontSize="14" v-model="form.couponDiscount" placeholder="请填写"></input>
+				    </view>
+				</view>
+				<view class="item">
+				    <view class="label"><span class="required">*</span>可领取人群</view>
+					
+				    <view class="value">
+						<uni-data-select
+							v-model="form.useScopeMember"
+							:localdata="useScopeMemberList"
+							@change="changeUseScopeMember"
+							:border="false"
+						></uni-data-select>
+				    </view>
+				</view>
+                <view class="item">
+                    <view class="label"><span class="required">*</span>活动时间</view>
+                    <view class="value" @click="selectDate">
+                        {{ form.startDate }} ~ {{ form.endDate }}
+                    </view>
+                </view>
+				<view class="item">
+				    <view class="label"><span class="required">*</span>优惠券状态</view>
+				    <view class="value">
+						<switch :checked="form.status" @change="changeStatus"  size="22" class="switch" style="transform:scale(0.7)"></switch>
+				    </view>
+				</view>
+				<view class="item">
+				    <view class="label"><span class="required">*</span>发行数量</view>
+				    <view class="value">
+				        <input border="none" inputAlign="right" maxlength="15" fontSize="14" v-model="form.publishNum" placeholder="请填写"></input>
+				    </view>
+				</view>
+				<view class="item">
+				    <view class="label">说明</view>
+				    <view class="value">
+				        <input border="none" inputAlign="right" maxlength="15" fontSize="14" v-model="form.remark" placeholder="请填写"></input>
+				    </view>
+				</view>
+			</view>
+            <view class="activities-title" v-if="form.discountType === 4"><span class="required">*</span>
+                参与的商品(不支持多规格商品)
+            </view>
+            <view class="goods-list" v-if="form.discountType === 4">
+                <uni-swipe-action>
+                    <uni-swipe-action-item :right-options="addFlag ? options : options1" :index="index" v-for="(item, index) in goodsList"
+                        @click="deleteGoods(index,item)" :key="item.goodsId">
+                        <view class="goods">
+                            <view class="cover">
+                                <image class="img" :src="item.pictureUrl"/>
+                            </view>
+                            <view class="cont">
+                                <view class="name">{{item.goodsName}}</view>
+                            </view>
+								<view >
+								    <!-- <view class="desc">每单限购{{item.maxNum}}份,活动库存{{item.activityNum}}份</view> -->
+								    <u-icon style="margin-right: 10rpx;" name="edit-pen" size="28" color="#6c6c6c" @click="handEditGoods(item, index)"></u-icon>
+								</view>
+                        </view>
+                    </uni-swipe-action-item>
+                </uni-swipe-action>
+                <view class="add-goods" v-if="form.discountType === 4 && addFlag && goodsList.length < 1" @click="handAddGoods">添加商品</view>
+            </view>
+       </scroll-view>
+        <u-modal
+            :show="updateStatusVisible"
+            title="注意"
+            closeOnClickOverlay
+            showCancelButton
+            @confirm="pushGoods"
+            @close="updateStatusVisible = false"
+            @cancel="updateStatusVisible = false">
+            <view class="container">
+                活动中的商品如果参加首页推荐,活动状态改变后将会在首页、店铺同步下架,您是否确定改变?
+            </view>
+        </u-modal>
+
+        <uni-calendar ref="calendar" class="uni-calendar--hook" :clear-date="true" :insert="false"  :range="true" @confirm="confirm" />
+        
+        <view class="update-events">
+            <view class="btn" @click="save">保存</view>
+        </view>
+    </view>
+</template>
+<script>
+import {mapState} from 'vuex';
+import { computed } from "vue";
+export default {
+    data () {
+        return {
+            getStoreCouponByIdForStore: '/coupon/getStoreCouponByIdForStore',
+            saveUrl: '/coupon/updateCouponByStore',
+            updateStatusVisible: false,
+            form: {
+                couponId: '',
+				couponName: '',
+				couponTitle: '',
+				discountType: '',
+				couponType: 2,
+				couponDiscount: '',
+				couponFullPrice: '',
+				imgUrl: '',
+				storeId: '',
+				goodsId: '',
+				useScopeMember: '',
+				startDate: '',
+				endDate: '',
+				status: 0,
+				price: '',
+				publishNum: '',
+				receivedNum: '',
+				usedNum: '',
+				remark: ''
+            },
+            goodsList: [],
+			fileList: [],
+            options: [
+                {
+                    text: '删除',
+            		style: {
+                        fontSize: '28rpx',
+            			backgroundColor: '#FF0000',
+						marginBottom: '20rpx'
+                    }
+                }
+            ],
+            options1:[],
+            addFlag: true,
+			file: {},
+			event: {},
+			discountTypeList: [
+				{ value: 1, text: "满减券" },
+				{ value: 2, text: "抵扣券" },
+				{ value: 3, text: "折扣券" },
+				{ value: 4, text: "兑换券" },
+			],
+			useScopeMemberList: [
+				{value: 1, text: '周期内限领'},
+				{value: 2, text: '注册周期内累计消费'},
+				{value: 3, text: '注册周期'},
+				{value: 4, text: '新用户'}
+			],
+        }
+    },
+    computed: {
+        ...mapState(['storeInfo'])
+    },
+    methods: {
+        loadData() {
+			uni.showLoading({
+				title: '加载中'
+			});
+            var dateStr = (new Date()).getTime();
+            var sign = this.getSign('' + this.storeInfo.storeId + dateStr);
+            this.requst({
+            	url: this.getStoreCouponByIdForStore,
+            	data: {
+            		timestamp: dateStr,
+            		sign: sign,
+            		storeId: this.storeInfo.storeId,
+                    id: this.form.couponId
+            	},
+            	success: res => {
+                    this.form = res.data.obj;
+					if (this.form.couponFullPrice) {
+						this.form.couponFullPrice = this.moneyConverter(this.form.couponFullPrice)
+					}
+					if (this.form.price) {
+						this.form.price = this.moneyConverter(this.form.price)
+					}
+					if (this.form.startDate) {
+						this.form.startDate = this.formatDate(this.form.startDate,'yyyy-M-d')
+					}
+					if (this.form.endDate) {
+						this.form.endDate = this.formatDate(this.form.endDate,'yyyy-M-d')
+					}
+                    if (this.form.goodsId) {
+                    	let goods = {
+                    		goodsId: this.form.goodsId,
+                    		goodsName: this.form.goodsName,
+                    		pictureUrl: this.form.pictureUrl
+                    	}
+                    	this.goodsList.push(goods)
+                    }
+                    console.log(this.goodsList)
+					uni.hideLoading();
+            	},
+            	fail: (e) => {
+            		console.log("接口调用失败:" + JSON.stringify(e));
+					uni.hideLoading();
+            	},
+            })
+        },
+		handCheckGoodsType () {
+			this.$refs.popupPickerView.show()
+		},
+        pushGoods() {
+            this.updateStatusVisible = false
+        },
+        handEditGoods (goods, index) {
+            const params = encodeURIComponent(JSON.stringify(goods)); 
+            uni.navigateTo({
+                url: `/pages/activity/add-coupon-goods?goods=${params}&index=${index}`
+            })
+        },
+        handAddGoods() {
+			if (this.goodsList.length > 0) {
+				const ids = this.goodsList.map(item => item.goodsId);
+				
+				const idsStr = ids.join(',');
+				const params = encodeURIComponent(JSON.stringify(idsStr)); 
+				uni.navigateTo({
+					url: `/pages/activity/add-coupon-goods?idsStr=${params}`
+				})
+			} else {
+				uni.navigateTo({
+					url: `/pages/activity/add-coupon-goods`
+				})
+			}
+        },
+		// 新增图片
+		async afterRead(event) {
+			this.event = event
+			this.file = event.file
+			let file1 = event.file
+			let fileListLen = this.fileList.length
+			// lists.map((item) => {
+			this.fileList.push({
+				...file1,
+				status: 'success',
+				// message: '上传中'
+			})
+			this.form.imgUrl = this.file.url
+		
+		},
+		// 删除图片
+		deletePic(event) {
+			this.fileList.splice(event.index, 1)
+			this.form.imgUrl = ''
+		},
+        selectDate() {
+            this.$refs.calendar.open();
+        },
+		changeDiscountType(e) {
+		    console.log("e:", e);
+			this.form.discountType = e
+		},
+		changeUseScopeMember(e) {
+			console.log("e:", e);
+			this.form.useScopeMember = e
+		},
+		changeStatus (e) {
+			if (e.detail.value){
+				this.form.status = 1
+			} else {
+				this.form.status = 0
+			}
+		},
+        confirm(e) {
+            this.form.startDate = e.range.before;
+            this.form.endDate = e.range.after;
+        },
+        save () {
+			if (this.form.discountType === 1) {
+			    this.form.couponDiscount = ''
+			}
+			if (this.form.discountType !== 1) {
+			    this.form.couponFullPrice = ''
+			}
+            
+			if (this.form.discountType !== 3){
+				this.form.couponDiscount = '';
+			}
+			if (this.form.discountType === 3 || this.form.discountType === 4){
+				this.form.price = '';
+			}
+			if (this.form.couponFullPrice === null){
+				this.form.couponFullPrice = '';
+			}
+			if (this.form.couponName === '' || !this.form.couponName) {
+				this.$api.msg('请输入优惠券标题');
+				return;
+			}
+			if (this.form.couponTitle === '' || !this.form.couponTitle) {
+				this.$api.msg('请输入优惠券子标题');
+				return;
+			}
+			if (this.form.discountType === '' || !this.form.discountType) {
+				this.$api.msg('请选择优惠类型');
+				return;
+			}
+			if (this.form.discountType === 3) {
+				if (this.form.couponDiscount === '' && !this.form.couponDiscount) {
+					this.$api.msg('请输入折扣');
+					return;
+				}
+			}       
+			if (this.form.discountType === 1) {
+				if (this.form.couponFullPrice === '' && !this.form.couponFullPrice) {
+					this.$api.msg('请输入满减标准金额');
+					return;
+				}
+			}
+			if (this.form.discountType !== 3 && this.form.discountType !== 4){
+				if (this.form.price === '' || !this.form.price) {
+					this.$api.msg('请输入面额');
+					return;
+				}
+			}
+			if (this.form.useScopeMember === '' || !this.form.useScopeMember) {
+				this.$api.msg('请选择可领取人群');
+				return;
+			}
+			
+			if (!this.form.startDate || !this.form.endDate) {
+                this.$api.msg("请选择活动时间范围");
+                return;
+            }
+			let start = new Date(this.form.startDate)
+			let end = new Date(this.form.endDate)
+			let startDates = this.formatDate(this.form.startDate,'yyyy-M-d H:m:s')
+			let endDates = this.formatDate(this.form.endDate,'yyyy-M-d H:m:s')
+			if (start > end) {
+			    this.$api.msg("开始时间不能大于结束时间");
+			    return;
+			}
+			if (end < new Date() && this.form.status === 1) {
+			  this.$api.msg('优惠券结束时间小于当前时间,无法上架,请先修改时间');
+			  return;
+			}
+			if (this.form.publishNum === '' || !this.form.publishNum) {
+				this.$api.msg('请输入发行数量');
+				return;
+			}
+			if (this.form.couponType === 2) {
+				if (this.form.discountType === 4) {
+					if (this.goodsList.length == 0) {
+						this.$api.msg('请请选择商品');
+						return;
+					} else {
+						this.form.goodsId = this.goodsList[0].goodsId
+					}
+				}
+			}
+
+			let find = false
+			let name = ''
+			for (var i = 0; i < this.goodsList.length; i++) {
+				for (var j = i + 1; j < this.goodsList.length; j++) {
+					if (this.goodsList[i].goodsId == this.goodsList[j].goodsId && this.goodsList[i].goodsName==this.goodsList[j].goodsName) { 
+						find = true; 
+						name = this.goodsList[i].goodsName;
+						break;
+					}
+				}
+				if (find) break;
+			}
+			
+			if (find) {
+			    this.$api.msg('商品:'+ name + "已存在请勿重复添加");
+			    return;
+			}
+			
+			uni.showLoading({
+				title: '保存中'
+			});
+			let that = this
+			var dateStr = (new Date()).getTime();
+			var sign = that.getSign('' + that.storeInfo.storeId + dateStr);
+			if (that.event.file) {
+				return new Promise((resolve, reject) => {
+					let a = uni.uploadFile({
+						url: that.storeInfo.code + this.saveUrl,
+						filePath: that.form.imgUrl,
+						name: 'file',
+						formData: {
+							timestamp: dateStr,
+							sign: sign,
+							storeId: that.storeInfo.storeId,
+							storeUserId: that.storeInfo.storeUserId,
+							couponId: that.form.couponId,
+							couponName: that.form.couponName,
+							couponTitle: that.form.couponTitle,
+							couponType: that.form.couponType,
+							discountType: that.form.discountType,
+							couponFullPrice: that.form.couponFullPrice ? this.form.couponFullPrice * 100 : this.form.couponFullPrice,
+							imgUrl: that.form.imgUrl,
+							goodsId: that.form.goodsId,
+							useScopeMember: that.form.useScopeMember,
+							startDate: startDates,
+							endDate: endDates,
+							goodsId: that.form.goodsId,
+							publishNum: that.form.publishNum,
+							status: that.form.status,
+							price: that.form.price ? this.form.price * 100 : this.form.price,
+							rmk: that.form.remark || '',
+						},
+						header: {
+							'content-type': 'multipart/form-data'
+						}, // header 值
+						success: (res) => {
+							if(res.data) {
+								let data = JSON.parse(res.data)
+								if (data.rtnBean.rtnCode == 0) {
+									uni.hideLoading();
+									uni.showToast({
+										title: "保存成功",
+										duration: 3000,
+										icon: 'none',
+									});
+								
+									var pages = getCurrentPages();
+									var prevPage = pages[pages.length - 2]; //上一个页面
+									prevPage.$vm.loadData()
+									uni.navigateBack();
+									// return;
+								} else {
+									uni.hideLoading();
+									if (data.rtnBean.rtnMsg != '') {
+										that.$api.msg(data.rtnBean.rtnMsg);
+									}
+								}
+							} else {
+								uni.hideLoading();
+								that.$api.msg('优惠券更新失败,请稍后重试');
+							}
+							
+						},
+						fail: (res) => {
+							uni.hideLoading();
+						}
+					});
+				})
+			} else {
+				that.requst({
+					url: that.saveUrl,
+					data: {
+						timestamp: dateStr,
+						sign: sign,
+						storeId: that.storeInfo.storeId,
+						storeUserId: that.storeInfo.storeUserId,
+						couponId: that.form.couponId,
+						couponName: that.form.couponName,
+						couponTitle: that.form.couponTitle,
+						couponType: that.form.couponType,
+						discountType: that.form.discountType,
+						couponFullPrice: that.form.couponFullPrice ? this.form.couponFullPrice * 100 : this.form.couponFullPrice,
+						imgUrl: that.form.imgUrl,
+						goodsId: that.form.goodsId,
+						useScopeMember: that.form.useScopeMember,
+						startDate: startDates,
+						endDate: endDates,
+						goodsId: that.form.goodsId,
+						publishNum: that.form.publishNum,
+						status: that.form.status,
+						price: that.form.price ? this.form.price * 100 : this.form.price,
+						rmk: that.form.remark || ''
+					},
+					success: res => {
+						if (res.data.rtnBean.rtnCode == 0) {
+							uni.hideLoading();
+							uni.showToast({
+								title: "保存成功",
+								duration: 3000,
+								icon: 'none',
+							});
+			
+							var pages = getCurrentPages();
+							var prevPage = pages[pages.length - 2]; //上一个页面
+							prevPage.$vm.loadData()
+							uni.navigateBack();
+							// return;
+						} else {
+							uni.hideLoading();
+							if (res.data.rtnBean.rtnMsg != '') {
+								that.$api.msg(res.data.rtnBean.rtnMsg);
+							}
+						}
+						
+					},
+					fail: (e) => {
+						that.isDisable = false;
+						console.log("接口调用失败:" + JSON.stringify(e));
+						uni.hideLoading();
+					},
+				});
+			}
+			
+            this.requst({
+            	url: this.saveStoreSpecialActivityUrl,
+            	data: {
+            		timestamp: dateStr,
+            		sign: sign,
+                    storeId: this.storeInfo.storeId,
+                    storeSpecialActivityId: this.form.storeSpecialActivityId,
+            		activityName: this.form.activityName,
+            		activityComment: this.form.activityComment,
+            		startDate: this.form.startDate,
+            		endDate: this.form.endDate,
+                    goodsListStr: JSON.stringify(this.goodsList)
+            	},
+            	success: res => {
+                    if (res.data.rtnBean.rtnCode == 0) {
+						 uni.hideLoading();
+                        uni.showToast({
+                        	title: "保存成功",
+                        	duration: 3000,
+                        	icon: 'none',
+                        });
+                        
+                        if (this.addFlag) {
+                            uni.redirectTo({
+                                url: '/pages/activity/special-item-info?id=' + res.data.obj
+                            })
+                        } else {
+                            let pages = getCurrentPages();
+                            let prevpage = pages[pages.length - 2];
+                            prevpage.$vm.loadData();
+                            uni.navigateBack();
+                        }
+                    } else {
+						 uni.hideLoading();
+                        this.$api.msg(res.data.rtnBean.rtnMsg);
+                    }
+                   
+            	},
+            	fail: (e) => {
+            		console.log("接口调用失败:" + JSON.stringify(e));
+					uni.hideLoading();
+            	},
+            })
+        },
+        deleteGoods(index, item) {
+            let that = this
+        	uni.showModal({
+        		title: '提示',
+        		content: '确定移除该商品吗?',
+        		success: function(res) {
+        			if (res.confirm) {
+        				that.goodsList.splice(index, 1);
+        			}
+        		}
+        	});
+        },
+		formatDate(value,args) {
+		    var dt = new Date(value);
+		    if(args == 'yyyy-M-d') {
+		        let year = dt.getFullYear();
+		        let month = dt.getMonth() + 1;
+		        let date = dt.getDate();
+		        return `${year}-${month}-${date}`;
+		    } else if(args == 'yyyy-M-d H:m:s'){
+		        let year = dt.getFullYear();
+		        let month = dt.getMonth() + 1;
+		        let date = dt.getDate();
+		        let hour = dt.getHours();
+		        let minute = dt.getMinutes();
+		        let second = dt.getSeconds();
+		        return `${year}-${month}-${date} ${hour}:${minute}:${second}`;
+		    } else if(args == 'MM.dd') {
+		        let month = (dt.getMonth() + 1).toString().padStart(2,'0');
+		        let date = dt.getDate().toString().padStart(2,'0');
+		        return `${month}.${date}`;
+		    } else {// yyyy-MM-dd HH:mm:ss
+		        let year = dt.getFullYear();
+		        let month = (dt.getMonth() + 1).toString().padStart(2,'0');
+		        let date = dt.getDate().toString().padStart(2,'0');
+		        let hour = dt.getHours().toString().padStart(2,'0');
+		        let minute = dt.getMinutes().toString().padStart(2,'0');
+		        let second = dt.getSeconds().toString().padStart(2,'0');
+		        return `${year}-${month}-${date} ${hour}:${minute}:${second}`;
+		    }
+		}
+    },
+    onShow () {
+    },
+    onLoad (options) {
+        if (options && options.id) {
+            uni.setNavigationBarTitle({
+                title:'编辑优惠券'
+            });
+            this.form.couponId = options.id;
+            this.loadData();
+            this.addFlag = false;
+        } else {
+            uni.setNavigationBarTitle({
+                title:'新增优惠券'
+            })
+            this.addFlag = true;
+        }
+    }
+}
+</script>
+<style lang="scss" scoped>
+.required {
+	color: red;
+	margin-left: 5px;
+}
+.special-item-edit-page {
+    background-color: #f5f5f5;
+    border-top: 2rpx solid #f5f5f5;
+    width: 100vw;
+    height: 100vh;
+}
+.scroll-content {
+    height: calc(100vh - 120rpx - env(safe-area-inset-bottom));
+    height: calc(100vh - 120rpx - constant(safe-area-inset-bottom));
+}
+.between {
+    @include between;
+}
+.cell-wrap {
+    width: 100vw;
+    padding: 0 40rpx;
+    box-sizing: border-box;
+    background-color: #fff;
+}
+.item {
+    @include between;
+    height: 100rpx;
+    background-color: #fff;
+    border-bottom: 2rpx solid #ececec;
+    font-size: $font-base;
+    .label {
+        width: 200rpx;
+        color: $title-color;
+    }
+    .value {
+        width: calc(100% - 200rpx);
+        color: $desc-color;
+        text-align: right;
+    }
+}
+.item.dubble {
+	height: 200rpx;
+}
+.activities-title {
+    height: 80rpx;
+    @include flexStart;
+    padding: 20rpx 40rpx 0;
+    box-sizing: border-box;
+    font-size: $font-base;
+    color: $title-color;
+}
+.goods-list {
+    .goods {
+        background-color: #fff;
+        @include flexStart;
+        padding: 20rpx;
+        box-sizing: border-box;
+        margin-bottom: 20rpx;
+        width: 100%;
+        .cover {
+            width: 166rpx;
+            height: 166rpx;
+            border-radius: 10rpx;
+            margin-right: 24rpx;
+            .img {
+                width: 166rpx;
+                height: 166rpx;
+            }
+        }
+        .cont {
+            width: calc(100% - 200rpx);
+            font-size: $font-base;
+            height: 166rpx;
+            overflow: hidden;
+        }
+        .name {
+            width: 100%;
+            text-overflow: -o-ellipsis-lastline;
+            overflow: hidden;
+            text-overflow: ellipsis;
+            display: -webkit-box;
+            -webkit-line-clamp: 2;
+            -webkit-box-orient: vertical;
+        }
+        .tools {
+            @include flexStart;
+        }
+        .price {
+            margin-right: 40rpx;
+            color: $uni-color-button;
+        }
+        .origin-price {
+            color: $desc-color;
+        }
+        .desc {
+            color: $desc-color; 
+        }
+    }
+    .add-goods {
+        margin-top: 40rpx;
+        width: 180rpx;
+        height: 60rpx;
+        border: 2rpx solid $theme-color;
+        color: $theme-color;
+        @include flexCenter;
+        margin-left: 40rpx;
+    }
+}
+
+.update-events {
+    width: 100vw;
+    @include flexCenter;
+    margin-top: 40rpx;
+    .btn {
+        width: 80vw;
+        height: 80rpx;
+        border-radius: 40rpx;
+        background-color: $theme-color;
+        color: #fff;
+        @include flexCenter;
+    }
+}
+</style>

+ 452 - 0
pages/activity/coupon-item-info.vue

@@ -0,0 +1,452 @@
+<template>
+    <view class="special-item-info-page">
+        <scroll-view scroll-y class="scroll-content">
+			<view class="cell-wrap">
+				<view class="item">
+					<view class="label">活动状态</view>
+					<view class="value between">
+						<view class="curr-status" :class="{ ing: form.status === 1 }">{{ form.status === 1 ? '进行中' : '停止' }}</view>
+						<view class="update-status" @click="handUpdateStatus">修改状态</view>
+					</view>
+				</view>
+				<view class="item">
+					<view class="label">优惠券标题</view>
+					<view class="value">
+						{{form.couponName}}
+					</view>
+				</view>
+				<view class="item">
+					<view class="label">子标题</view>
+					<view class="value">
+						{{form.couponTitle}}
+					</view>
+				</view>
+				<view class="item">
+					<view class="label">优惠券图片</view>
+					<view class="value">
+						<view class="cover">
+							<image class="img" :src="item.pictureUrl"/>
+						</view>
+					</view>
+				</view>
+				<view class="item">
+					<view class="label">优惠类型</view>
+					<view class="value">
+						<uni-data-select
+							v-model="form.discountType"
+							:localdata="discountTypeList"
+							@change="changeDiscountType"
+							:border="false"
+							:clear="false"
+							:disabled="true"
+						></uni-data-select>
+					</view>
+				</view>
+				<view class="item" v-if="form.discountType === 1">
+					<view class="label">满减标准金额</view>
+					<view class="value">
+						{{form.couponFullPrice}}
+					</view>
+				</view>
+				<view class="item" v-if="form.discountType !== 3 && form.discountType !== 4">
+					<view class="label">面额</view>
+					<view class="value">
+						{{form.price}}
+					</view>
+				</view>
+				<view class="item" v-if="form.discountType === 3">
+					<view class="label">折扣(%)</view>
+					<view class="value">
+						{{form.couponDiscount}}				    
+					</view>
+				</view>
+				<view class="item">
+					<view class="label">可领取人群</view>
+					
+					<view class="value">
+						<uni-data-select
+							v-model="form.useScopeMember"
+							:localdata="useScopeMemberList"
+							@change="changeUseScopeMember"
+							:border="false"
+							:clear="false"
+							:disabled="true"
+						></uni-data-select>
+					</view>
+				</view>
+				<view class="item">
+					<view class="label">活动时间</view>
+					<view class="value">
+						{{ form.startDate|formatDate('MM.dd') }} ~ {{ form.endDate|formatDate('MM.dd') }}
+					</view>
+				</view>
+				<view class="item">
+					<view class="label">优惠券状态</view>
+					<view class="value">
+						{{form.status}}
+					</view>
+				</view>
+				<view class="item">
+					<view class="label">发行数量</view>
+					<view class="value">
+						{{form.publishNum}}
+					</view>
+				</view>
+				<view class="item">
+						<view class="label">说明</view>
+						<view class="value">
+							{{form.remark || ''}}				    
+						</view>
+					</view>
+				
+				
+				<view class="goods-wrap" v-if="form.discountType === 4">
+					<view class="title">参与的商品(不支持多规格商品)</view>
+					<view class="goods" v-for="item in goodsList" :key="item.goodsId">
+						<view class="cover">
+							<image class="img" :src="item.pictureUrl"/>
+						</view>
+						<view class="cont">
+							<view class="name">{{item.goodsName}}</view>
+						</view>
+					</view>
+				</view>
+			</view>
+        </scroll-view>
+        
+        <view class="update-events">
+            <view class="btn" @click="deleteActivity">删除</view>
+            <view class="btn" @click="updateCouponItem">调整优惠券</view>
+        </view>
+
+    </view>
+</template>
+<script>
+import {mapState} from 'vuex';
+export default {
+    data () {
+        return {
+            getStoreCouponByIdUrl: '/coupon/getStoreCouponByIdForStore',
+            updateStoreCouponStatusUrl: '/coupon/changeCouponStatusForStore',
+            deleteStoreCouponUrl: '/coupon/deleteCouponByIdForStore',
+            form: {
+                couponId: '',
+                couponName: '',
+                couponTitle: '',
+                discountType: '',
+                couponType: 2,
+                couponDiscount: '',
+                couponFullPrice: '',
+                imgUrl: '',
+                storeId: '',
+                goodsId: '',
+                useScopeMember: '',
+                startDate: '',
+                endDate: '',
+                status: 0,
+                price: '',
+                publishNum: '',
+                receivedNum: '',
+                usedNum: '',
+                remark: '',
+				goodsName: '',
+				pictureUrl: ''
+            },
+            goodsList: [],
+			discountTypeList: [
+				{ value: 1, text: "满减券" },
+				{ value: 2, text: "抵扣券" },
+				{ value: 3, text: "折扣券" },
+				{ value: 4, text: "兑换券" },
+			],
+			useScopeMemberList: [
+				{value: 1, text: '周期内限领'},
+				{value: 2, text: '注册周期内累计消费'},
+				{value: 3, text: '注册周期'},
+				{value: 4, text: '新用户'}
+			],
+        }
+    },
+    computed: {
+        ...mapState(['storeInfo'])
+    },
+    methods: {
+        loadData() {
+			uni.showLoading({
+				title: '加载中'
+			});
+            var dateStr = (new Date()).getTime();
+            var sign = this.getSign('' + this.storeInfo.storeId + dateStr);
+            this.requst({
+            	url: this.getStoreCouponByIdUrl,
+            	data: {
+            		timestamp: dateStr,
+            		sign: sign,
+            		storeId: this.storeInfo.storeId,
+                    id: this.form.couponId
+            	},
+            	success: res => {
+					this.goodsList = []
+                    this.form = res.data.obj;
+					if (this.form.goodsId) {
+						let goods = {
+							goodsId: this.form.goodsId,
+							goodsName: this.form.goodsName,
+							pictureUrl: this.form.pictureUrl
+						}
+						this.goodsList.push(goods)
+					}
+					console.log(this.goodsList)
+					uni.hideLoading();
+            	},
+            	fail: (e) => {
+            		console.log("接口调用失败:" + JSON.stringify(e));
+					uni.hideLoading();
+            	},
+            })
+        },
+        deleteActivity() {
+            if (this.form.status == 1) {
+                this.$api.msg("进行中的活动不能删除,请先停止活动后再进行删除");
+                return;
+            }
+            let that = this;
+            uni.showModal({
+            	title: '提示',
+            	content: '确定删除这个优惠券吗?',
+            	success: function(res) {
+            		if (res.confirm) {
+						uni.showLoading({
+							title: '删除中'
+						});
+            			var dateStr = (new Date()).getTime();
+            			var sign = that.getSign('' + that.storeInfo.storeId + dateStr);
+            			that.requst({
+            				url: that.deleteStoreCouponUrl,
+            				data: {
+            					timestamp: dateStr,
+            					sign: sign,
+            					storeId: that.storeInfo.storeId,
+            			        id: that.form.couponId
+            				},
+            				success: res => {
+            			        if (res.data.rtnBean.rtnCode == 0) {
+            			            uni.showToast({
+            			            	title: "删除成功",
+            			            	duration: 3000,
+            			            	icon: 'none',
+            			            });
+            			            // uni.redirectTo({
+            			            //     url: '/pages/activity/special-item'
+            			            // })
+                                    let pages = getCurrentPages();
+                                    let prevpage = pages[pages.length - 2];
+                                    prevpage.$vm.getStoreCouponList();
+                                    uni.navigateBack();
+            			        } else {
+            			            that.$api.msg(res.data.rtnBean.rtnMsg);
+            			        }
+								uni.hideLoading();
+            				},
+            				fail: (e) => {
+            					console.log("接口调用失败:" + JSON.stringify(e));
+								uni.hideLoading();
+            				},
+            			})
+            		}
+            	}
+            });
+        },
+        handUpdateStatus() {
+            if (this.form.status == 1) {
+                if(new Date(this.form.endDate) < new Date()){
+					this.$api.msg('优惠券结束时间小于当前时间,无法上架,请先修改时间');
+					return
+				}
+            }
+            this.updateStatus();
+        },
+        updateStatus () {
+			uni.showLoading({
+				title: '更新中'
+			});
+            this.updateStatusVisible = false;
+            var dateStr = (new Date()).getTime();
+            var sign = this.getSign('' + this.storeInfo.storeId + dateStr);
+            this.requst({
+            	url: this.updateStoreCouponStatusUrl,
+            	data: {
+            		timestamp: dateStr,
+            		sign: sign,
+            		storeId: this.storeInfo.storeId,
+                    id: this.form.couponId,
+                    status: this.form.status == 1 ? 0 : 1
+            	},
+            	success: res => {
+                    if (res.data.rtnBean.rtnCode == 0) {
+                        this.$api.msg("更新成功");
+                    } else {
+                        this.$api.msg(res.data.rtnBean.rtnMsg);
+                    }
+                    this.loadData();
+					uni.hideLoading();
+            	},
+            	fail: (e) => {
+            		console.log("接口调用失败:" + JSON.stringify(e));
+					uni.hideLoading();
+            	},
+            })
+        },
+        updateCouponItem () {
+            if (this.form.status == 1) {
+                this.$api.msg("进行中的活动不能编辑,请先停止活动后再进行编辑");
+                return;
+            }
+            uni.navigateTo({
+                url: `/pages/activity/coupon-item-edit?id=${this.form.couponId}`
+            });
+        }
+    },
+    onLoad (options) {
+        if (options && options.id) {
+        	this.form.couponId = options.id;
+        	this.loadData();
+        }
+    }
+}
+</script>
+<style lang="scss" scoped>
+.special-item-info-page {
+    background-color: #f5f5f5;
+    border-top: 2rpx solid #f5f5f5;
+    width: 100vw;
+    height: 100vh;
+}
+.scroll-content {
+    height: calc(100vh - 120rpx - env(safe-area-inset-bottom));
+    height: calc(100vh - 120rpx - constant(safe-area-inset-bottom));
+}
+.cell-wrap {
+    width: 100vw;
+    padding: 0 40rpx;
+    box-sizing: border-box;
+    background-color: #fff;
+}
+.item:first-child {
+    padding-top: 40rpx;
+}
+.item {
+    @include flexStart;
+    background-color: #fff;
+    padding: 20rpx 40rpx;
+    box-sizing: border-box;
+    font-size: $font-base;
+    .label {
+        width: 240rpx;
+        color: $desc-color;
+    }
+    .value {
+        width: calc(100% - 240rpx);
+        @include between;
+        // overflow: hidden;
+        // text-overflow: ellipsis;
+        // word-break: break-all;
+        // white-space: nowrap;
+    }
+    .left {
+        @include flexStart;
+    }
+    .curr-status {
+        color: $theme-color;
+    }
+    .curr-status.ing {
+        color: $uni-color-button;
+    }
+    .update-status {
+        height: 54rpx;
+        width: 160rpx;
+        background-color: $theme-color;
+        color: #fff;
+        @include flexCenter;
+        border-radius: 27rpx;
+    }
+}
+
+.goods-wrap {
+    .title {
+        height: 80rpx;
+        @include flexStart;
+        padding-left: 40rpx;
+        box-sizing: border-box;
+        font-size: $font-base;
+        color: $text-color;
+    }
+}
+.img {
+	width: 200rpx;
+	height: 200rpx;
+}
+.goods {
+    background-color: #fff;
+    @include flexStart;
+    padding: 20rpx;
+    box-sizing: border-box;
+    .cover {
+        width: 166rpx;
+        height: 166rpx;
+        border-radius: 10rpx;
+        margin-right: 24rpx;
+        .img {
+            width: 166rpx;
+            height: 166rpx;
+        }
+    }
+    .cont {
+        width: calc(100% - 200rpx);
+        font-size: $font-base;
+        height: 166rpx;
+        overflow: hidden;
+    }
+    .name {
+        width: 100%;
+        text-overflow: -o-ellipsis-lastline;
+		overflow: hidden;
+		text-overflow: ellipsis;
+		display: -webkit-box;
+		-webkit-line-clamp: 2;
+		-webkit-box-orient: vertical;
+    }
+    .tools {
+        @include flexStart;
+    }
+    .price {
+        margin-right: 40rpx;
+        color: $uni-color-button;
+    }
+    .origin-price {
+        color: $desc-color;
+        text-decoration:line-through;
+    }
+    .desc {
+        color: $desc-color; 
+    }
+}
+.container {
+    font-size: $font-base;
+}
+.update-events {
+    width: 100vw;
+    @include flexCenter;
+    margin-top: 40rpx;
+    .btn {
+        width: 80vw;
+        height: 80rpx;
+        border-radius: 40rpx;
+        background-color: $theme-color;
+        color: #fff;
+        @include flexCenter;
+        margin-left: 20rpx;
+        margin-right: 20rpx;
+    }
+}
+</style>

+ 162 - 0
pages/activity/coupon-item.vue

@@ -0,0 +1,162 @@
+<template>
+    <view class="special-item-page">
+        <scroll-view class="list-wrap" scroll-y>
+            <view class="item" v-for="item in couponList" :key="item.couponId" @click="handItem(item)">
+                <view class="cover">
+                    <image class="icon" :src="item.imgUrl"/>
+                </view>
+                <view class="cont">
+                    <view class="row">
+                        <view class="name">{{item.couponName}}</view>
+                        <view class="date">{{item.startDate|formatDate('MM.dd')}} ~ {{item.endDate|formatDate('MM.dd')}}</view>
+                    </view>
+                    <view class="row">
+                        <view class="desc">
+                            {{item.rmk || ''}}
+                        </view>
+                        <view v-if="item.status==1" class="status">进行中</view>
+                        <view v-else class="status-unused">停止</view>
+                    </view>
+                </view>
+            </view>
+        </scroll-view>
+        <view class="add-coupon-item">
+            <view class="add" @click="addSpecialItem">添加优惠券</view>
+        </view>
+    </view>
+</template>
+<script>
+import {mapState} from 'vuex';
+export default {
+    data () {
+        return {
+			getStoreCouponListUrl: '/coupon/getStoreCouponList',
+            couponList: []
+        }
+    },
+    computed: {
+        ...mapState(['storeInfo'])
+    },
+    methods: {
+        handItem(item) {
+            uni.navigateTo({
+                url: '/pages/activity/coupon-item-info?id=' + item.couponId
+            });
+        },
+        addSpecialItem () {
+			uni.navigateTo({
+				url: '/pages/activity/coupon-item-edit'
+			});
+            
+        },
+        getStoreCouponList() {
+			uni.showLoading({
+				title: '加载中'
+			});
+            var dateStr = (new Date()).getTime();
+            var sign = this.getSign('' + this.storeInfo.storeId + dateStr);
+            this.requst({
+            	url: this.getStoreCouponListUrl,
+            	data: {
+            		timestamp: dateStr,
+            		sign: sign,
+            		storeId: this.storeInfo.storeId,
+					couponType: 2,
+            	},
+            	success: res => {
+                    this.couponList = res.data.list;
+					uni.hideLoading();
+            	},
+            	fail: (e) => {
+            		console.log("接口调用失败:" + JSON.stringify(e));
+					uni.hideLoading();
+            	},
+            })
+        }
+    },
+    onShow () {
+        this.getStoreCouponList();
+    }
+}
+</script>
+<style lang="scss" scoped>
+.special-item-page {
+    width: 100vw;
+    height: 100vh;
+    background-color: #f5f5f5;
+    overflow: hidden;
+}
+.list-wrap {
+    height: calc(100vh - 140rpx - env(safe-area-inset-bottom));
+    height: calc(100vh - 140rpx - constant(safe-area-inset-bottom));
+    margin-top: 20rpx;
+    .item {
+        margin-bottom: 20rpx;
+        background-color: #fff;
+        padding: 40rpx;
+        box-sizing: border-box;
+        @include flexStart;
+    }
+    .cover {
+        min-width: 60rpx;
+        width: 60rpx;
+        height: 60rpx;
+        margin-right: 40rpx;
+        border-radius: 50%;
+        overflow: hidden;
+        @include flexCenter;
+        box-shadow: 0 0 10rpx pink;
+    }
+    .icon {
+        width: 42rpx;
+        height: 42rpx;
+    }
+    .cont {
+        width: calc(100% - 100rpx);
+        font-size: $font-base;
+    }
+    .row {
+        width: 100%;
+        @include between;
+        margin-bottom: 10rpx;
+    }
+    .name {
+        color: $text-color;
+        width: calc(100% - 240rpx);
+        overflow: hidden;
+        text-overflow: ellipsis;
+        white-space: nowrap;
+        word-break: break-all;
+    }
+    .date {
+        width: 240rpx;
+        text-align: right;
+    }
+    .desc {
+        color: $desc-color;
+    }
+    .status {
+        width: 240rpx;
+        text-align: right;
+        color: $uni-color-button;
+    }
+    .status-unused {
+        width: 240rpx;
+        text-align: right;
+        color: $theme-color;
+    }
+}
+.add-coupon-item {
+    width: 100vw;
+    @include flexCenter;
+    margin-top: 40rpx;
+    .add {
+        width: 80vw;
+        height: 80rpx;
+        border-radius: 40rpx;
+        background-color: $theme-color;
+        color: #fff;
+        @include flexCenter;
+    }
+}
+</style>

+ 207 - 0
pages/activity/free-delivery.vue

@@ -0,0 +1,207 @@
+<template>
+    <view class="distribution-page">
+		<scroll-view scroll-y class="scroll-content">
+			<view class="title">配送费设置</view>
+			<view class="form-item">
+			    <view class="label flex-center">是否免配送费</view>
+			    <view class="value flex-start">
+			        <switch :checked="freeDeliveryFeeFlag" @change="changeDeliveryfeeFlag"  size="22" class="switch" style="transform:scale(0.7)"></switch>
+			    </view>
+			</view>
+			
+		</scroll-view>
+		<view class="footer-btn">
+			<view class="save" @click="updateFreeDeliveryFeeFlag">保存</view>
+		</view>
+    </view>
+</template>
+<script>
+import { mapState } from 'vuex';
+export default {
+    data () {
+        return {
+			newDiscountsUrl: '/storeInfoManageApi/getStoreFreeDeliveryFlg',
+			updateUrl: '/storeInfoManageApi/updateStoreFreeDeliveryFlg',
+			freeDeliveryFeeFlag: 0
+        }
+    },
+	computed: {
+		...mapState(['storeInfo'])
+	},
+    methods: {
+		loadData () {
+			uni.showLoading({
+				title: '加载中'
+			});
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign('' + this.storeInfo.storeId + dateStr);
+			this.requst({
+				url: this.newDiscountsUrl,
+				data: {
+					timestamp: dateStr,
+					sign: sign,
+					storeId: this.storeInfo.storeId
+				},
+				success: res => {
+					this.freeDeliveryFeeFlag = res.data.obj;
+					
+					uni.hideLoading();
+				},
+				fail: (e) => {
+					console.log("接口调用失败:" + JSON.stringify(e));
+					uni.hideLoading();
+				},
+			})
+		},
+		changeDeliveryfeeFlag (e) {
+			if (e.detail.value){
+				this.freeDeliveryFeeFlag = 1
+			} else {
+				this.freeDeliveryFeeFlag = 0
+			}
+		},
+		updateFreeDeliveryFeeFlag () {
+			uni.showLoading({
+				title: '保存中'
+			});
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign('' + this.storeInfo.storeId + dateStr);
+			this.requst({
+				url: this.updateUrl,
+				data: {
+					timestamp: dateStr,
+					sign: sign,
+					storeId: this.storeInfo.storeId,
+					flg: this.freeDeliveryFeeFlag
+				},
+				success:res => {
+					if (res.data.rtnBean.rtnCode == 0) {
+						uni.hideLoading();
+						uni.showToast({
+							title: "保存成功",
+							duration: 3000,
+							icon: 'none',
+						});
+						this.loadData();
+					} else {
+						uni.hideLoading();
+						if (res.data.rtnBean.rtnMsg != '') {
+							this.$api.msg(res.data.rtnBean.rtnMsg);
+						}
+					}
+					
+				},
+				fail: (e) => {
+					console.log("接口调用失败:" + JSON.stringify(e));
+					uni.hideLoading();
+				},
+			});
+		},
+		updateNewDiscounts () {
+			let regPrice = (/^[0-9]\d*(,\d{3})*(\.\d{1,2})?$|^0\.\d{1,2}$/);
+			if (this.newDiscounts == '' || this.newDiscounts == null) {
+				this.$api.msg("立减金额不能为空");
+				return;
+			}
+			if (!regPrice.test(this.newDiscounts)) {
+				this.$api.msg("价格只能是整数或小数点后两位");
+				return;
+			}
+			
+			
+		}
+    },
+    onShow () {
+    },
+    onLoad () {
+		this.loadData()
+	}
+}
+</script>
+<style lang="scss" scoped>
+.distribution-page {
+    height: 100vh;
+    background-color: #f5f5f5;
+}
+
+.scroll-content {
+    height: calc(100vh - 120rpx - env(safe-area-inset-bottom));
+    height: calc(100vh - 120rpx - constant(safe-area-inset-bottom));
+}
+.title {
+    font-size: 36rpx;
+    color: $title-color;
+    margin: 24rpx 0 16rpx;
+    padding: 0 20rpx;
+}
+.form-item {
+    background-color: #fff;
+    @include flexStart(flex-start);
+    padding: 20rpx 60rpx;
+    box-sizing: border-box;
+    .label {
+        margin-right: 20rpx;
+        margin-top: 20rpx;
+        font-size: $font-base;
+        color: $text-color;
+        width: 180rpx;
+    }
+    .label.flex-center {
+        margin-top: 6rpx;
+    }
+    .desc {
+        font-size: $font-sm;
+        color: $uni-color-button;
+    }
+    .flex-start {
+        @include flexStart;
+        .desc {
+            margin-left: 20rpx;
+            color: $text-color;
+        }
+    }
+}
+.form-item.pb0 {
+    padding-bottom: 0;
+}
+.form-item.flex-center {
+    @include flexStart(center);
+    .label {
+        margin-top: 0;
+    }
+    .btn {
+        font-size: $font-base;
+        color: #fff;
+        background-color: $theme-color;
+        @include flexCenter;
+        padding: 6rpx 20rpx;
+        border-radius: 10rpx;
+    }
+    .deliveryman {
+        height: 52rpx;
+        @include flexStart;
+        font-size: $font-base;
+        color: $text-color;
+    }
+    .del {
+        color: $uni-color-button;
+        margin-left: 20rpx;
+    }
+}
+/deep/ .u-radio {
+    margin-right: 50rpx;
+}
+.footer-btn {
+    width: 100vw;
+    margin-top: 40rpx;
+    .save {
+        margin: 0 10vw;
+        width: 80vw;
+        height: 80rpx;
+        border-radius: 40rpx;
+        background-color: $theme-color;
+        color: #fff;
+        @include flexCenter;
+    }
+}
+</style>

+ 210 - 0
pages/activity/full-discounts.vue

@@ -0,0 +1,210 @@
+<template>
+    <view class="coupon-page">
+        <scroll-view scroll-y class="form-wrap">
+            <view class="cell">
+                <view class="label">活动状态</view>
+                <view class="value">
+                    <view class="status-name" :class="{ ing: fullDiscounts.fullDiscountsStatus == 1 }" v-if=" this.fullDiscounts.roleList !=null && this.fullDiscounts.roleList.length > 0">{{ fullDiscounts.fullDiscountsStatus == 1 ? '进行中' : '已失效' }}</view>
+                    <view class="status-name" v-if="this.fullDiscounts.roleList ==null || this.fullDiscounts.roleList.length == 0">暂无满减活动</view>
+                    <view class="update-status" @click="changeStatus" v-if="this.fullDiscounts.roleList !=null && this.fullDiscounts.roleList.length > 0">修改状态</view>
+                    <view class="add-activity" @click="updateFull" v-if="this.fullDiscounts.roleList ==null || this.fullDiscounts.roleList.length == 0">添加活动</view>
+                </view>
+            </view>
+            <view class="cell">
+                <view class="label">活动时间</view>
+                <view class="value" v-if="fullDiscounts.fullDiscountsStartDateStr != null && fullDiscounts.fullDiscountsStartDateStr != undefined">
+					{{ fullDiscounts.fullDiscountsStartDateStr }} ~ {{ fullDiscounts.fullDiscountsEndDateStr }}
+				</view>
+            </view>
+            <view class="cell mb40" :style="'height:'+height+'rpx;'">
+                <view class="label">满减规则</view>
+                <view class="value column" >
+                    <view class="rule" v-for="(item, index) in fullDiscounts.roleList" :key="fullDiscountsRoleId" >满{{ item.conditionFee}}减{{ item.discountsFee }}</view>
+                    <!-- <view class="rule">满50减10</view> -->
+                </view>
+            </view>
+            <view class="cell">
+                <view class="label">创建日期</view>
+                <view class="value">{{ fullDiscounts.createDateStr || '' }}</view>
+            </view>
+        </scroll-view>
+        <view class="update-events" v-if=" this.fullDiscounts.roleList !=null && this.fullDiscounts.roleList.length > 0">
+            <view class="btn" @click="updateFull">调整满减活动</view>
+        </view>
+    </view>
+</template>
+<script>
+import { mapState } from 'vuex';
+export default {
+    data () {
+        return {
+			fullDiscountsUrl: '/storeFullDiscountsManageApi/getDiscountsInfo',
+			changeStatusUrl: '/storeFullDiscountsManageApi/changeStatus',
+            fullDiscounts: {},
+			height: 40
+        }
+    },
+	computed: {
+		...mapState(['storeInfo'])
+	},
+    methods: {
+		loadData () {
+			uni.showLoading({
+				title: '加载中'
+			});
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign('' + this.storeInfo.storeUserId + dateStr);
+			this.requst({
+				url: this.fullDiscountsUrl,
+				data: {
+					timestamp: dateStr,
+					sign: sign,
+					storeId: this.storeInfo.storeId,
+					storeUserId: this.storeInfo.storeUserId
+				},
+				success: res => {
+					if (res.data.rtnBean.rtnCode == 0) {
+						this.fullDiscounts = res.data.obj;
+						if(this.fullDiscounts.roleList != null && this.fullDiscounts.roleList.length > 0){
+							this.height = 40 * this.fullDiscounts.roleList.length
+						}
+					}
+					uni.hideLoading();
+				},
+				fail: (e) => {
+					console.log("接口调用失败:" + JSON.stringify(e));
+					uni.hideLoading();
+				}
+			})
+		},
+		changeStatus() {
+			uni.showLoading({
+				title: '加载中'
+			});
+			this.fullDiscounts.fullDiscountsStatus = this.fullDiscounts.fullDiscountsStatus == 1 ? 0 : 1
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign('' + this.storeInfo.storeUserId + dateStr);
+			this.requst({
+				url: this.changeStatusUrl,
+				data: {
+					timestamp: dateStr,
+					sign: sign,
+					storeId: this.storeInfo.storeId,
+					storeUserId: this.storeInfo.storeUserId,
+					status: this.fullDiscounts.fullDiscountsStatus
+				},
+				success: res => {
+					if (res.data.rtnBean.rtnCode == 0) {
+						this.loadData();
+					}
+					uni.hideLoading();
+				},
+				fail: (e) => {
+					console.log("接口调用失败:" + JSON.stringify(e));
+					uni.hideLoading();
+				}
+			})
+		},
+        updateFull () {
+			if(this.fullDiscounts.fullDiscountsStatus == 1 ){
+				this.$api.msg("进行中的活动无法调整");
+				return;
+			}
+            uni.navigateTo({
+                url: `/pages/activity/update-full-discounts`
+            });
+        }
+    },
+    onShow () {
+		
+	},
+	onLoad() {
+		this.loadData();
+	}
+}
+</script>
+<style lang="scss" scoped>
+.coupon-page {
+    border-top: 2rpx solid #f5f5f5;
+    height: 100vh;
+    background-color: #f5f5f5;
+}
+.update-events {
+    width: 100vw;
+    @include flexCenter;
+    margin-top: 40rpx;
+    .btn {
+        width: 80vw;
+        height: 80rpx;
+        border-radius: 40rpx;
+        background-color: $theme-color;
+        color: #fff;
+        @include flexCenter;
+    }
+}
+.form-wrap {
+    background-color: #fff;
+    padding: 40rpx;
+    box-sizing: border-box;
+    height: calc(100vh - 120rpx - env(safe-area-inset-bottom));
+    height: calc(100vh - 120rpx - constant(safe-area-inset-bottom));
+    .cell {
+        @include flexStart(flex-start);
+        height: 80rpx;
+        font-size: $font-base;
+    }
+    .label {
+        color: $desc-color;
+        width: 200rpx;
+    }
+    .status-name {
+        color: $theme-color;
+    }
+    .status-name.ing {
+        color: $uni-color-button;
+    }
+    .update-status {
+        width: 180rpx;
+        height: 52rpx;
+        border-radius: 26rpx;
+        @include flexCenter;
+        color: #fff;
+        background-color: $theme-color;
+    }
+	.add-activity {
+        width: 180rpx;
+        height: 52rpx;
+        border-radius: 26rpx;
+		margin-right: 10rpx;
+        @include flexCenter;
+		float: right;
+		// display: flex;
+		// justify-content: center;
+		// align-items: flex-start;
+        color: #fff;
+        background-color: $theme-color;
+    }
+    .value {
+        width: calc(100% - 200rpx);
+        @include between(flex-start);
+        color: $text-color;
+		// width: calc(100% - 200rpx);
+		// /* display: flex; */
+		// justify-content: space-between;
+		// align-items: flex-start;
+		// color: $text-color;
+    }
+    .rule {
+        margin-bottom: 10rpx;
+    }
+    .column {
+        display: flex;
+        flex-direction: column;
+        align-items: flex-start;
+        justify-content: flex-start;
+    }
+    .mb40 {
+        margin-bottom: 40rpx;
+    }
+}
+</style>

+ 94 - 0
pages/activity/marketing.vue

@@ -0,0 +1,94 @@
+<template>
+    <view class="marketing-page">
+        <view class="item" v-for="(item, idx) in marketings" :key="item.id" @click="handItem(item)">
+            <image class="cover" :src="item.icon" />
+            <view class="name">{{ item.name }}</view>
+        </view>
+    </view>
+</template>
+<script>
+export default {
+    data () {
+        return {
+            marketings: [
+                {
+                    id: 1,
+                    name: '特价菜',
+                    icon: require('../../static/imgs/mine/tjc.png'),
+                    path: '/pages/activity/special-item'
+                },
+                {
+                    id: 2,
+                    name: '店铺满减',
+                    icon: require('../../static/imgs/mine/dpmj.png'),
+                    path: '/pages/activity/full-discounts'
+                },
+                // {
+                //     id: 3,
+                //     name: '广告位',
+                //     icon: require('../../static/imgs/mine/ggw.png'),
+                //     path: '/pages/activity/advertising'
+                // },
+                {
+                    id: 4,
+                    name: '霸王餐',
+                    icon: require('../../static/imgs/mine/bwc.png'),
+                    path: '/pages/activity/overlord-meal'
+                },
+				{
+                    id: 5,
+                    name: '新客立减设置',
+                    icon: require('../../static/imgs/mine/new.png'),
+                    path: '/pages/activity/new-customer'
+                },
+				{
+                    id: 6,
+                    name: '免配送费设置',
+                    icon: require('../../static/imgs/mine/free.png'),
+                    path: '/pages/activity/free-delivery'
+                },
+				{
+                    id: 7,
+                    name: '优惠券设置',
+                    icon: require('../../static/imgs/mine/coupon.png'),
+                    path: '/pages/activity/coupon-item'
+                }
+            ]
+        }
+    },
+    methods: {
+        handItem (item) {
+            const list = ['special-item', 'coupon', 'advertising', 'overlord-meal', 'new-customer']
+            uni.navigateTo({
+                url: item.path
+            });
+        }
+    },
+    onLoad () {}
+}
+</script>
+<style lang="scss" scoped>
+.marketing-page {
+    display: flex;
+    flex-wrap: wrap;
+    align-items: flex-start;
+    justify-content: flex-start;
+    border-top: 2rpx solid #ECECEC;
+}
+.item {
+    width: 250rpx;
+    height: 250rpx;
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+    border-right: 2rpx solid #ECECEC;
+    border-bottom: 2rpx solid #ECECEC;
+    box-sizing: border-box;
+    .cover {
+        width: 72rpx;
+        height: 72rpx;
+        margin-bottom: 10rpx;
+    }
+}
+</style>

+ 203 - 0
pages/activity/new-customer.vue

@@ -0,0 +1,203 @@
+<template>
+    <view class="distribution-page">
+		<scroll-view scroll-y class="scroll-content">
+			<view class="title">新客立减设置</view>
+			<view class="form-item">
+				<view class="label">立减金额(元)</view>
+				<view class="value">
+					<u-input fontSize="14" v-model="newDiscounts"></u-input>
+					<text class="desc">金额在0~10之间,设置0元无新客立减</text>
+				</view>
+			</view>
+			
+		</scroll-view>
+		<view class="footer-btn">
+			<view class="save" @click="updateNewDiscounts">保存</view>
+		</view>
+    </view>
+</template>
+<script>
+import { mapState } from 'vuex';
+export default {
+    data () {
+        return {
+			newDiscountsUrl: '/storeInfoManageApi/getStoreNewDiscounts',
+			updateUrl: '/storeInfoManageApi/updateNewDiscounts',
+            newDiscounts: 0
+        }
+    },
+	computed: {
+		...mapState(['storeInfo'])
+	},
+    methods: {
+		loadData () {
+			uni.showLoading({
+				title: '加载中'
+			});
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign('' + this.storeInfo.storeId + dateStr);
+			this.requst({
+				url: this.newDiscountsUrl,
+				data: {
+					timestamp: dateStr,
+					sign: sign,
+					storeId: this.storeInfo.storeId
+				},
+				success: res => {
+					let price = res.data.obj;
+					if (price != null && price != '' && price != undefined){
+						this.newDiscounts = this.moneyConverter(price);
+					}
+					uni.hideLoading();
+				},
+				fail: (e) => {
+					console.log("接口调用失败:" + JSON.stringify(e));
+					uni.hideLoading();
+				},
+			})
+		},
+		updateNewDiscounts () {
+			let regPrice = (/^[0-9]\d*(,\d{3})*(\.\d{1,2})?$|^0\.\d{1,2}$/);
+			if (this.newDiscounts == '' || this.newDiscounts == null) {
+				this.$api.msg("立减金额不能为空");
+				return;
+			}
+			if (!regPrice.test(this.newDiscounts)) {
+				this.$api.msg("价格只能是正整数或小数点后两位");
+				return;
+			}
+			if (Number(this.newDiscounts) < 0 || Number(this.newDiscounts) > 10) {
+				this.$api.msg("立减金额在0到10之间");
+				return;
+			} 
+			uni.showLoading({
+				title: '保存中'
+			});
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign('' + this.storeInfo.storeId + dateStr);
+			this.requst({
+				url: this.updateUrl,
+				data: {
+					timestamp: dateStr,
+					sign: sign,
+					storeId: this.storeInfo.storeId,
+					newDiscounts: this.newDiscounts * 100
+				},
+				success:res => {
+					if (res.data.rtnBean.rtnCode == 0) {
+						uni.hideLoading();
+						uni.showToast({
+							title: "保存成功",
+							duration: 3000,
+							icon: 'none',
+						});
+						this.loadData();
+					} else {
+						uni.hideLoading();
+						if (res.data.rtnBean.rtnMsg != '') {
+							this.$api.msg(res.data.rtnBean.rtnMsg);
+						}
+					}
+					
+				},
+				fail: (e) => {
+					console.log("接口调用失败:" + JSON.stringify(e));
+					uni.hideLoading();
+				},
+			});
+		}
+    },
+    onShow () {
+    },
+    onLoad () {
+		this.loadData()
+	}
+}
+</script>
+<style lang="scss" scoped>
+.distribution-page {
+    height: 100vh;
+    background-color: #f5f5f5;
+}
+
+.scroll-content {
+    height: calc(100vh - 120rpx - env(safe-area-inset-bottom));
+    height: calc(100vh - 120rpx - constant(safe-area-inset-bottom));
+}
+.title {
+    font-size: 36rpx;
+    color: $title-color;
+    margin: 24rpx 0 16rpx;
+    padding: 0 20rpx;
+}
+.form-item {
+    background-color: #fff;
+    @include flexStart(flex-start);
+    padding: 20rpx 60rpx;
+    box-sizing: border-box;
+    .label {
+        margin-right: 20rpx;
+        margin-top: 20rpx;
+        font-size: $font-base;
+        color: $text-color;
+        width: 180rpx;
+    }
+    .label.flex-center {
+        margin-top: 6rpx;
+    }
+    .desc {
+        font-size: $font-sm;
+        color: $uni-color-button;
+    }
+    .flex-start {
+        @include flexStart;
+        .desc {
+            margin-left: 20rpx;
+            color: $text-color;
+        }
+    }
+}
+.form-item.pb0 {
+    padding-bottom: 0;
+}
+.form-item.flex-center {
+    @include flexStart(center);
+    .label {
+        margin-top: 0;
+    }
+    .btn {
+        font-size: $font-base;
+        color: #fff;
+        background-color: $theme-color;
+        @include flexCenter;
+        padding: 6rpx 20rpx;
+        border-radius: 10rpx;
+    }
+    .deliveryman {
+        height: 52rpx;
+        @include flexStart;
+        font-size: $font-base;
+        color: $text-color;
+    }
+    .del {
+        color: $uni-color-button;
+        margin-left: 20rpx;
+    }
+}
+/deep/ .u-radio {
+    margin-right: 50rpx;
+}
+.footer-btn {
+    width: 100vw;
+    margin-top: 40rpx;
+    .save {
+        margin: 0 10vw;
+        width: 80vw;
+        height: 80rpx;
+        border-radius: 40rpx;
+        background-color: $theme-color;
+        color: #fff;
+        @include flexCenter;
+    }
+}
+</style>

+ 529 - 0
pages/activity/overlord-meal-detail.vue

@@ -0,0 +1,529 @@
+<template>
+    <view class="overlord-meal-detail-page" @touchmove.stop.prevent="moveHandle">
+		<view class="scroll-content">
+			<image class="banner" :src="form.imgUrl"></image>
+			<view class="item">
+				<view class="label">活动状态</view>
+				<view class="value" :class="{ing: form.status == 1}">{{ form.status == 1 ? '进行中' : (form.status == 0 ? '未开始' : '已失效')}}</view>
+			</view>
+			<view class="item">
+				<view class="label">活动主题</view>
+				<view class="value">{{form.title}}</view>
+			</view>
+			<view class="item">
+				<view class="label">活动时间</view>
+				<view class="value">{{form.startDate}} ~ {{form.endDate}}</view>
+			</view>
+			<view class="item">
+				<view class="label">最低单价</view>
+				<view class="value">¥{{moneyConverter(form.price)}}</view>
+			</view>
+			<view class="item">
+				<view class="label">最低总价</view>
+				<view class="value">¥{{moneyConverter(form.activityPrice)}}</view>
+			</view>
+			<view class="item">
+				<view class="label">活动达标金额</view>
+				<view class="value">¥{{moneyConverter(form.reachPrice)}}</view>
+			</view>
+			<view v-if="doflg" class="cy-goods">参与的商品</view>
+			<view v-if="doflg" class="goods">
+				<view class="cover">
+					<image class="img" :src="form.pictureUrl"/>
+				</view>
+				<view class="cont">
+					<view class="name">{{form.goodsName}}</view>
+					<view class="tools">
+						<view class="price">¥{{moneyConverter(form.salePrice)}}</view>
+						<view class="origin-price" v-if="form.salePrice < form.costPrice">¥{{moneyConverter(form.costPrice)}}</view>
+					</view>
+					<view class="desc">
+						未兑换数量{{form.residueNum}}份,参加活动数量{{form.total}}份
+					</view>
+				</view>
+			</view>
+		</view>	
+		<view class="footer-btn" @touchmove.stop.prevent="moveHandle">
+		    <view class="logout" @click="signUp" v-if="joinBtnVisible">
+		        报名
+		    </view>
+		</view>
+		<u-modal
+			:show="joinActivityVisible"
+			title="报名活动"
+			showCancelButton
+			@confirm="joinActivity"
+			@cancel="joinActivityVisible = false"
+			@close="joinActivityVisible = false">
+			<view class="container">
+				<view class="item">
+					<view class="label">{{goods.goodsName ? goods.goodsName : '选择商品'}}</view>
+					<view class="value" @click="chooseGoods">
+						<u-icon name="arrow-right"></u-icon>
+					</view>
+				</view>
+				<view class="item">
+					<view class="label">价格</view>
+					<view class="value">
+						¥{{moneyConverter(goods.activityPrice)}}
+					</view>
+				</view>
+				<view class="item">
+					<view class="label">参加活动数量</view>
+					<view class="value">
+						<u-input border="none" inputAlign="right" fontSize="14" v-model="goods.activityNum" placeholder="数量"></u-input>
+						<text class="text">份</text>
+					</view>
+				</view>
+			</view>
+		</u-modal>
+		<u-modal
+			:show="chooseGoodsVisible"
+			:showConfirmButton="false"
+			closeOnClickOverlay
+			@close="chooseGoodsVisible = false">
+			<view class="modal-container">
+				<view class="search-wrap">
+					<view class="input-wrap">
+						<u-input border="none" fontSize="14" v-model="goodsName" placeholder="商品名称"></u-input>
+					</view>
+					<view class="btn" @click="getGoodsList">搜索</view>
+				</view>
+				<scroll-view scroll-y class="goods-list">
+					<view class="goods-item" v-for="item in goodsList" :key="item.goodsId" @click="handItem(item)">
+						<view class="cover">
+							<image class="img" :src="item.pictureUrl"/>
+						</view>
+						 <view class="cont">
+							<view class="goods-name">
+							   {{item.goodsName}}
+							</view>
+							<view class="desc">
+								库存: {{item.maxNum}}, 价格: ¥{{moneyConverter(item.activityPrice)}}
+							</view>
+						 </view>
+					</view>
+				</scroll-view>
+			</view>
+		</u-modal>
+        <!-- <view class="sign-up">
+            <view class="btn" @click="signUp" v-if="joinBtnVisible">报名</view>
+        </view> -->
+		
+    </view>
+</template>
+<script>
+import {mapState} from 'vuex';
+export default {
+    data () {
+        return {
+            getStoreSingleGoodsForStoreUrl: '/storeGoodsManageApi/getStoreSingleGoodsForStore',
+            getStoreDineAndDashInfoForStoreUrl: '/dineAndDashActivityApi/getStoreDineAndDashInfoForStore',
+            joinDineAndDashInfoForStoreUrl: '/dineAndDashActivityApi/joinDineAndDashInfoForStore',
+            getJoinedActivityStoreCountForStoreUrl: '/dineAndDashActivityApi/getJoinedActivityStoreCountForStore',
+            dineAndDashId: '',
+            joinedActivityStoreCount: 0,
+            form: {
+                dineAndDashId : '', 
+                title : '',
+                imgUrl : '',
+                activityPrice : '',
+                startDate : '',
+                endDate : '',
+                status : '',
+                price : '',
+                reachPrice : '',
+                maxNum : '',
+                storeId : '',
+                storeName	 : '',
+                goodsId : '',
+                goodsName : '',
+                residueNum : '',
+                total : '',
+                costPrice : '',
+                salePrice : '',
+                pictureUrl : '',
+            },
+            goods: {
+                goodsId: '',
+                goodsName: '',
+                costPrice: '',
+                modelId: '',
+                pictureUrl: '',
+                activityPrice: '',
+                activityNum: ''
+            },
+            doflg: false,
+            joinBtnVisible: false,
+            joinActivityVisible: false,
+            chooseGoodsVisible: false,
+            goodsList: [],
+        }
+    },
+    computed: {
+        ...mapState(['storeInfo'])
+    },
+    methods: {
+		moveHandle() {
+			return false
+		},
+        signUp () {
+			uni.showLoading({
+				title: '加载中'
+			});
+            var dateStr = (new Date()).getTime();
+            var sign = this.getSign('' + this.dineAndDashId + dateStr);
+            this.requst({
+            	url: this.getJoinedActivityStoreCountForStoreUrl,
+            	data: {
+            		timestamp: dateStr,
+            		sign: sign,
+            		dineAndDashId: this.dineAndDashId
+            	},
+            	success: res => {
+                    if (res.data.rtnBean.rtnCode == 0) {
+						uni.hideLoading();
+                        this.joinedActivityStoreCount = res.data.obj;
+                        if (Number(this.form.maxNum) > Number(this.joinedActivityStoreCount)) {
+                            this.joinActivityVisible = true;
+                        } else {
+                            this.$api.msg("报名商家已达到上限,请以后再参加,感谢您的配合!");
+                        }
+                    } else {
+						uni.hideLoading();
+					}
+					
+            	},
+            	fail: (e) => {
+            		console.log("接口调用失败:" + JSON.stringify(e));
+					uni.hideLoading();
+            	},
+            })
+            
+        },
+        handItem (item) {
+            if (Number(item.activityPrice) < Number(this.form.price)) {
+                this.$api.msg("参加活动的商品价格不能低于" + this.moneyConverter(this.form.price) + "元,请重新选择!");
+                return;
+            }
+            this.goods.goodsId = item.goodsId
+            this.goods.goodsName = item.goodsName;
+            this.goods.costPrice = item.costPrice;
+            this.goods.activityPrice = item.activityPrice;
+            this.goods.modelId = item.modelId;
+            this.goods.pictureUrl = item.pictureUrl;
+            
+            this.chooseGoodsVisible = false
+        },
+        chooseGoods() {
+            this.goodsName = '';
+            this.getGoodsList();
+            this.chooseGoodsVisible = true;
+        },
+        getGoodsList() {
+			uni.showLoading({
+				title: '加载中'
+			});
+            var dateStr = (new Date()).getTime();
+            var sign = this.getSign('' + this.storeInfo.storeId + dateStr);
+            this.requst({
+            	url: this.getStoreSingleGoodsForStoreUrl,
+            	data: {
+            		timestamp: dateStr,
+            		sign: sign,
+            		storeId: this.storeInfo.storeId,
+                    goodsName: this.goodsName
+            	},
+            	success: res => {
+                    this.goodsList = res.data.list;
+					uni.hideLoading();
+            	},
+            	fail: (e) => {
+            		console.log("接口调用失败:" + JSON.stringify(e));
+					uni.hideLoading();
+            	},
+            })
+        },
+        getData() {
+			uni.showLoading({
+				title: '加载中'
+			});
+            var dateStr = (new Date()).getTime();
+            var sign = this.getSign('' + this.storeInfo.storeId + dateStr);
+            this.requst({
+            	url: this.getStoreDineAndDashInfoForStoreUrl,
+            	data: {
+            		timestamp: dateStr,
+            		sign: sign,
+            		dineAndDashStoreId: this.dineAndDashId,
+                    storeId: this.storeInfo.storeId
+            	},
+            	success: res => {
+                    if (res.data.rtnBean.rtnCode == 0) {
+                        this.form = res.data.obj;
+                        if (this.form.storeId) {
+                            this.doflg = true;
+                        }
+                        if (!this.doflg && this.form.status == 0) {
+                            this.joinBtnVisible = true;
+                        } else {
+                            this.joinBtnVisible = false;
+                        }
+                    } else {
+                        this.$api.msg(res.data.rtnBean.rtnMsg);
+                        uni.navigateBack();
+                    }
+					uni.hideLoading();
+            	},
+            	fail: (e) => {
+            		console.log("接口调用失败:" + JSON.stringify(e));
+					uni.hideLoading();
+            	},
+            })
+        },
+        joinActivity() {
+            if (!this.goods.goodsId) {
+                this.$api.msg("请选择参加活动的商品");
+                return;
+            }
+            
+            if (!this.goods.activityNum) {
+                this.$api.msg("请输入参加活动的商品数量");
+                return;
+            }
+            if (Number(this.goods.activityPrice * this.goods.activityNum) < Number(this.form.activityPrice)) {
+                this.$api.msg("参加活动的商品总价值不能低于" + this.moneyConverter(this.form.activityPrice) + "元,请增加参加活动的商品数量!");
+                return;
+            }
+			uni.showLoading({
+				title: '加载中'
+			});
+            var dateStr = (new Date()).getTime();
+            var sign = this.getSign('' + this.storeInfo.storeId + dateStr);
+            this.requst({
+            	url: this.joinDineAndDashInfoForStoreUrl,
+            	data: {
+            		timestamp: dateStr,
+            		sign: sign,
+                    storeId: this.storeInfo.storeId,
+                    dineAndDashId: this.dineAndDashId,
+                    goodsId: this.goods.goodsId,
+                    total: this.goods.activityNum
+            	},
+            	success: res => {
+                    if (res.data.rtnBean.rtnCode == 0) {
+                        this.$api.msg("报名成功")
+                        this.getData();
+                    } else {
+                        this.$api.msg(res.data.rtnBean.rtnMsg);
+                    }
+                    this.joinActivityVisible = false;
+					uni.hideLoading();
+            	},
+            	fail: (e) => {
+            		console.log("接口调用失败:" + JSON.stringify(e));
+					uni.hideLoading();
+            	},
+            })
+        }
+    },
+    onLoad (opt) {
+        this.dineAndDashId = opt.id;
+        this.getData();
+    }
+}
+</script>
+<style lang="scss" scoped>
+.overlord-meal-detail-page {
+    width: 100vw;
+    height: 100vh;
+    background-color: #f5f5f5;
+}
+.scroll-content {
+    height: calc(100vh - 120rpx - env(safe-area-inset-bottom));
+    height: calc(100vh - 120rpx - constant(safe-area-inset-bottom));
+}
+.banner {
+    width: 100vw;
+    height: 212rpx;
+    margin-bottom: 20rpx;
+}
+.item {
+    padding: 20rpx 40rpx;
+    box-sizing: border-box;
+    @include flexStart;
+    font-size: $font-base;
+    .label {
+        width: 200rpx;
+        color: $desc-color;
+    }
+    .value {
+        width: calc(100% - 200rpx);
+    }
+}
+.cy-goods {
+    height: 80rpx;
+    padding: 0 40rpx;
+    box-sizing: border-box;
+    font-size: $font-base;
+    background-color: #f5f5f5;
+    @include flexStart; 
+}
+.goods {
+    background-color: #fff;
+    @include flexStart;
+    padding: 20rpx;
+    box-sizing: border-box;
+    .cover {
+        width: 166rpx;
+        height: 166rpx;
+        border-radius: 10rpx;
+        margin-right: 24rpx;
+        .img {
+            width: 166rpx;
+            height: 166rpx;
+        }
+    }
+    .cont {
+        width: calc(100% - 200rpx);
+        font-size: $font-base;
+        height: 166rpx;
+        overflow: hidden;
+    }
+    .name {
+        width: 100%;
+        text-overflow: -o-ellipsis-lastline;
+		overflow: hidden;
+		text-overflow: ellipsis;
+		display: -webkit-box;
+		-webkit-line-clamp: 2;
+		-webkit-box-orient: vertical;
+    }
+    .tools {
+        @include flexStart;
+    }
+    .price {
+        margin-right: 40rpx;
+        color: $uni-color-button;
+    }
+    .origin-price {
+        color: $desc-color;
+    }
+    .desc {
+        color: $desc-color; 
+    }
+}
+.sign-up {
+    width: 100vw;
+    position: fixed;
+    left: 0;
+    bottom: 0;
+    background-color: #fff;
+    @include flexCenter;
+    .btn {
+        width: 80vw;
+        height: 80rpx;
+        border-radius: 40rpx;
+        background-color: $theme-color;
+        color: #fff;
+        @include flexCenter;
+        margin: 20rpx 0 calc(20rpx + env(safe-area-inset-bottom));
+        margin: 20rpx 0 calc(20rpx + constant(safe-area-inset-bottom));
+    }
+}
+/deep/ .u-modal__content {
+    padding: 30rpx!important;
+    box-sizing: border-box;
+}
+.container {
+    width: 100%;
+    padding: 40rpx 20rpx;
+    box-sizing: border-box;
+    .item {
+        width: 100%;
+        height: 80rpx;
+        @include between;
+        border-bottom: 2rpx solid #f5f5f5;
+    }
+    .value {
+        @include flexEnd;
+        flex: 1;
+    }
+    .text {
+        margin-left: 20rpx;
+    }
+}
+.modal-container {
+    width: 100%;
+    .search-wrap {
+        width: 100%;
+        @include flexCenter;
+        border: 2rpx solid $theme-color;
+        color: $theme-color;
+        font-size: $font-base;
+        padding-left: 20rpx;
+        box-sizing: border-box;
+        margin-bottom: 20rpx;
+        .btn {
+            width: 128rpx;
+            height: 60rpx;
+            @include flexCenter;
+            border-left: 2rpx solid $theme-color;
+        }
+        .input-wrap {
+            flex: 1;
+            /deep/ .u-input {
+                width: 100%;
+            }
+        }
+    }
+    .goods-list {
+        height: 500rpx;
+        overflow-y: auto;
+        overflow-x: hidden;
+        .goods-item {
+            margin-bottom: 20rpx;
+            @include flexStart;
+        }
+        .cover {
+            width: 120rpx;
+            height: 120rpx;
+            border-radius: 10rpx;
+            margin-right: 20rpx;
+            .img {
+                width: 120rpx;
+                height: 120rpx;
+            }
+        }
+        .cont {
+            font-size: $font-base;
+            .goods-name {
+                width: 100%;
+                text-overflow: -o-ellipsis-lastline;
+                overflow: hidden;
+                text-overflow: ellipsis;
+                display: -webkit-box;
+                -webkit-line-clamp: 2;
+                -webkit-box-orient: vertical;
+                color: $text-color;
+            }
+            .desc {
+                color: $desc-color;
+            }
+        }
+    }
+}
+.footer-btn {
+    width: 100vw;
+	margin-top: 40rpx;
+    @include flexCenter;
+    .logout {
+        width: 80%;
+        height: 80rpx;
+        border-radius: 40rpx;
+        background-color: $theme-color;
+        color: #fff;
+        @include flexCenter;
+    }
+}
+</style>

+ 236 - 0
pages/activity/overlord-meal.vue

@@ -0,0 +1,236 @@
+<template>
+    <view class="coupon-page">
+        <u-sticky bgColor="#fff" class="tabs-wrap">
+            <u-tabs class="tabs" :list="tabs" :itemStyle="itemStyle" :is-scroll="false" :current="active" :activeStyle="{ color: '#1A78F6' }" @change="handTabchange">
+            </u-tabs>
+        </u-sticky>
+        <view class="list-wrap">
+            <view class="item" v-for="item in lists" :key="item.dineAndDashId" @click="activityInfo(item)">
+                <view class="icon">
+                    <image class="img" src="../../static/imgs/mine/tjc.png"/>
+                </view>
+                <view class="cont">
+                    <view class="row">
+                        <view class="name">{{item.title}}</view>
+                        <view class="date">{{item.startDate|formatDate('MM.dd')}} - {{item.endDate|formatDate('MM.dd')}}</view>
+                        <view class="status-doing" v-if="item.status == 1">进行中{{item.join == 1 ? '(已参加)' : '(未参加)'}}</view>
+                        <view v-else-if="item.status==-1">已失效{{item.join == 1 ? '(已参加)' : '(未参加)'}}</view>
+                        <view class="status-nostart" v-else>未开始{{item.join == 1 ? '(已参加)' : '(未参加)'}}</view>
+                    </view>
+                    <view class="row desc-row">
+                        <view class="desc-title">活动描述</view>
+                        <view class="desc-text">最低总价{{moneyConverter(item.activityPrice)}}/最低单价{{moneyConverter(item.price)}}/活动达标金额{{moneyConverter(item.reachPrice)}}</view>
+                    </view>
+                </view>
+            </view>
+        </view>
+    </view>
+</template>
+<script>
+import {mapState} from 'vuex';
+export default {
+    data () {
+        return {
+            getDineAndDashListForStoreUrl: '/dineAndDashActivityApi/getDineAndDashListForStore',
+            modalTitle: '活动名称',
+            active: 0,
+            itemStyle: {
+                width: '50vw',
+                height: '80rpx'
+            },
+            tabs: [
+                {
+                    name: '进行中'
+                },
+                {
+                    name: '已失效'
+                }
+            ],
+            lists: []
+        }
+    },
+	computed: {
+	    ...mapState(['storeInfo'])
+	},
+    methods: {
+        handTabchange(e) {
+            this.active = e.index;
+            this.getDataList();
+        },
+        getDataList() {
+			uni.showLoading({
+				title: '加载中'
+			});
+            var dateStr = (new Date()).getTime();
+            var sign = this.getSign('' + this.active + dateStr);
+            this.requst({
+            	url: this.getDineAndDashListForStoreUrl,
+            	data: {
+            		timestamp: dateStr,
+            		sign: sign,
+            		type: this.active,
+					storeId: this.storeInfo.storeId
+            	},
+            	success: res => {
+                    this.lists = res.data.list;
+					uni.hideLoading();
+            	},
+            	fail: (e) => {
+            		console.log("接口调用失败:" + JSON.stringify(e));
+					uni.hideLoading();
+            	},
+            })
+        },
+        activityInfo(item) {
+            uni.navigateTo({
+                url: `/pages/activity/overlord-meal-detail?id=${item.dineAndDashId || ''}`
+            })
+        }
+    },
+    onLoad () {
+        this.getDataList();
+    }
+}
+</script>
+<style lang="scss" scoped>
+.coupon-page {
+    width: 100vw;
+    height: 100vh;
+    background-color: #f5f5f5;
+    border-top: 2rpx solid #f5f5f5;
+}
+.status-doing {
+    color: red;
+}
+.status-nostart {
+    color: #1A78F6;
+}
+.list-wrap {
+    height: calc(100vh - 80rpx - 20rpx);
+    overflow-y: auto;
+    margin-top: 18rpx;
+    .item {
+        @include flexStart;
+        padding: 40rpx 20rpx;
+        box-sizing: border-box;
+        margin-bottom: 20rpx;
+        background-color: #fff;
+        font-size: $font-base;
+    }
+    .icon {
+        min-width: 52rpx;
+        width: 52rpx;
+        height: 52rpx;
+        margin-right: 40rpx;
+        .img {
+            width: 52rpx;
+            height: 52rpx;
+        }
+    }
+    .cont {
+        width: calc(100% - 92rpx);
+    }
+    .row {
+        @include between;
+    }
+    .desc-row {
+        @include between(flex-start);
+        margin-top: 20rpx;
+        color: $desc-color;
+    }
+    .desc-title {
+        width: 180rpx;
+    }
+    .btn {
+        padding: 6rpx 20rpx;
+        background-color: $theme-color;
+        color: #fff;
+        border-radius: 10rpx;
+    }
+}
+/deep/ .u-modal__content {
+    padding: 20rpx !important;
+}
+.container {
+    width: 100%;
+    padding: 40rpx 20rpx;
+    box-sizing: border-box;
+    .item {
+        width: 100%;
+        height: 80rpx;
+        @include between;
+        border-bottom: 2rpx solid #f5f5f5;
+    }
+    .value {
+        @include flexEnd;
+        flex: 1;
+    }
+    .text {
+        margin-left: 20rpx;
+    }
+}
+.modal-container {
+    padding: 20rpx 40rpx 0;
+    box-sizing: border-box;
+    .title {
+        height: 60rpx;
+        @include flexCenter;
+    }
+    .search-wrap {
+        width: 100%;
+        @include flexCenter;
+        border: 2rpx solid $theme-color;
+        color: $theme-color;
+        font-size: $font-base;
+        padding-left: 20rpx;
+        box-sizing: border-box;
+        margin-bottom: 20rpx;
+        .btn {
+            width: 128rpx;
+            height: 60rpx;
+            @include flexCenter;
+            border-left: 2rpx solid $theme-color;
+        }
+        .input-wrap {
+            flex: 1;
+            /deep/ .u-input {
+                width: 100%;
+            }
+        }
+    }
+    .goods-list {
+        height: 50vh;
+        margin-top: 20rpx;
+        .item {
+            margin-bottom: 20rpx;
+            @include flexStart;
+        }
+        .cover {
+            width: 120rpx;
+            height: 120rpx;
+            border-radius: 10rpx;
+            margin-right: 20rpx;
+            .img {
+                width: 120rpx;
+                height: 120rpx;
+            }
+        }
+        .cont {
+            font-size: $font-base;
+            .goods-name {
+                width: 100%;
+                text-overflow: -o-ellipsis-lastline;
+                overflow: hidden;
+                text-overflow: ellipsis;
+                display: -webkit-box;
+                -webkit-line-clamp: 2;
+                -webkit-box-orient: vertical;
+                color: $text-color;
+            }
+            .desc {
+                color: $desc-color;
+            }
+        }
+    }
+}
+</style>

+ 406 - 0
pages/activity/special-item-edit.vue

@@ -0,0 +1,406 @@
+<template>
+    <view class="special-item-edit-page">
+       <scroll-view scroll-y class="scroll-content">
+            <view class="cell-wrap">
+                <view class="item">
+                    <view class="label">活动主题</view>
+                    <view class="value">
+                        <input border="none" inputAlign="right" maxlength="15" fontSize="14" v-model="form.activityName" placeholder="请填写"></input>
+                    </view>
+                </view>
+                <view class="item">
+                    <view class="label">活动描述</view>
+                    <view class="value">
+                        <input border="none" inputAlign="right" maxlength="15" fontSize="14" v-model="form.activityComment" placeholder="请填写"></input>
+                    </view>
+                </view>
+                <view class="item">
+                    <view class="label">活动时间</view>
+                    <view class="value" @click="selectDate">
+                        {{ form.startDate }} ~ {{ form.endDate }}
+                    </view>
+                </view>
+        </view>
+            <view class="activities-title">
+                参与的活动(不支持多规格商品)
+            </view>
+            <view class="goods-list">
+                <uni-swipe-action>
+                    <uni-swipe-action-item :right-options="addFlag ? options : options1" :index="index" v-for="(item, index) in goodsList"
+                        @click="deleteGoods(index,item)" :key="item.goodsId">
+                        <view class="goods">
+                            <view class="cover">
+                                <image class="img" :src="item.pictureUrl"/>
+                            </view>
+                            <view class="cont">
+                                <view class="name">{{item.goodsName}}</view>
+                                <view class="tools">
+                                    <view class="price">¥{{moneyConverter(item.activityPrice)}}</view>
+                                    <view class="origin-price">¥{{moneyConverter(item.costPrice)}}</view>
+                                </view>
+                                <view class="desc between">
+                                    <view class="desc">每单限购{{item.maxNum}}份,活动库存{{item.activityNum}}份</view>
+                                    <u-icon name="edit-pen" size="28" color="#6c6c6c" @click="handEditGoods(item, index)"></u-icon>
+                                </view>
+                            </view>
+                        </view>
+                    </uni-swipe-action-item>
+                </uni-swipe-action>
+                <view class="add-goods" v-if="addFlag" @click="handAddGoods">添加商品</view>
+            </view>
+       </scroll-view>
+
+
+        <u-modal
+            :show="updateStatusVisible"
+            title="注意"
+            closeOnClickOverlay
+            showCancelButton
+            @confirm="pushGoods"
+            @close="updateStatusVisible = false"
+            @cancel="updateStatusVisible = false">
+            <view class="container">
+                活动中的商品如果参加首页推荐,活动状态改变后将会在首页、店铺同步下架,您是否确定改变?
+            </view>
+        </u-modal>
+
+        <uni-calendar ref="calendar" class="uni-calendar--hook" :clear-date="true" :insert="false"  :range="true" @confirm="confirm" />
+        
+        <view class="update-events">
+            <view class="btn" @click="save">保存</view>
+        </view>
+    </view>
+</template>
+<script>
+import {mapState} from 'vuex';
+import { computed } from "vue";
+export default {
+    data () {
+        return {
+            getStoreSpecialActivityByIdUrl: '/storeSpecialActivityApi/getStoreSpecialActivityByIdForStore',
+            saveStoreSpecialActivityUrl: '/storeSpecialActivityApi/saveStoreSpecialActivityForStore',
+            updateStatusVisible: false,
+            form: {
+                storeSpecialActivityId: '',
+                activityName: '',
+                activityComment: '',
+                startDate: '',
+                endDate: '',
+                status: ''
+            },
+            goodsList: [],
+            options: [
+                {
+                    text: '删除',
+            		style: {
+                        fontSize: '28rpx',
+            			backgroundColor: '#FF0000',
+						marginBottom: '20rpx'
+                    }
+                }
+            ],
+            options1:[],
+            addFlag: true
+        }
+    },
+    computed: {
+        ...mapState(['storeInfo'])
+    },
+    methods: {
+        loadData() {
+			uni.showLoading({
+				title: '加载中'
+			});
+            var dateStr = (new Date()).getTime();
+            var sign = this.getSign('' + this.storeInfo.storeId + dateStr);
+            this.requst({
+            	url: this.getStoreSpecialActivityByIdUrl,
+            	data: {
+            		timestamp: dateStr,
+            		sign: sign,
+            		storeId: this.storeInfo.storeId,
+                    id: this.form.storeSpecialActivityId
+            	},
+            	success: res => {
+                    this.form = res.data.obj;
+                    this.goodsList = res.data.list;
+					uni.hideLoading();
+            	},
+            	fail: (e) => {
+            		console.log("接口调用失败:" + JSON.stringify(e));
+					uni.hideLoading();
+            	},
+            })
+        },
+        pushGoods() {
+            this.updateStatusVisible = false
+        },
+        handEditGoods (goods, index) {
+            const params = encodeURIComponent(JSON.stringify(goods)); 
+            uni.navigateTo({
+                url: `/pages/activity/add-goods?goods=${params}&index=${index}`
+            })
+        },
+        handAddGoods() {
+			if (this.goodsList.length > 0) {
+				const ids = this.goodsList.map(item => item.goodsId);
+				
+				const idsStr = ids.join(',');
+				const params = encodeURIComponent(JSON.stringify(idsStr)); 
+				uni.navigateTo({
+					url: `/pages/activity/add-goods?idsStr=${params}`
+				})
+			} else {
+				uni.navigateTo({
+					url: `/pages/activity/add-goods`
+				})
+			}
+        },
+        selectDate() {
+            this.$refs.calendar.open();
+        },
+        confirm(e) {
+            this.form.startDate = e.range.before;
+            this.form.endDate = e.range.after;
+        },
+        save () {
+			
+            if (!this.form.activityName) {
+                this.$api.msg("请输入活动主题");
+                return;
+            }
+            if (!this.form.activityComment) {
+                this.$api.msg("请输入活动描述");
+                return;
+            }
+            if (!this.form.startDate || !this.form.endDate) {
+                this.$api.msg("请选择活动时间范围");
+                return;
+            }
+			let start = new Date(this.form.startDate)
+			let end = new Date(this.form.endDate)
+			if (start > end) {
+                this.$api.msg("开始时间不能大于结束时间");
+                return;
+            }
+            if (!this.goodsList || this.goodsList.length == 0) {
+                this.$api.msg("请选择特价菜商品列表");
+                return;
+            }
+			
+			let find = false
+			let name = ''
+			for (var i = 0; i < this.goodsList.length; i++) {
+				for (var j = i + 1; j < this.goodsList.length; j++) {
+					if (this.goodsList[i].goodsId == this.goodsList[j].goodsId && this.goodsList[i].goodsName==this.goodsList[j].goodsName) { 
+						find = true; 
+						name = this.goodsList[i].goodsName;
+						break;
+					}
+				}
+				if (find) break;
+			}
+			
+			if (find) {
+			    this.$api.msg('商品:'+ name + "已存在请勿重复添加");
+			    return;
+			}
+			
+			
+			uni.showLoading({
+				title: '保存中'
+			});
+            var dateStr = (new Date()).getTime();
+            var sign = this.getSign('' + this.storeInfo.storeId + dateStr);
+            this.requst({
+            	url: this.saveStoreSpecialActivityUrl,
+            	data: {
+            		timestamp: dateStr,
+            		sign: sign,
+                    storeId: this.storeInfo.storeId,
+                    storeSpecialActivityId: this.form.storeSpecialActivityId,
+            		activityName: this.form.activityName,
+            		activityComment: this.form.activityComment,
+            		startDate: this.form.startDate,
+            		endDate: this.form.endDate,
+                    goodsListStr: JSON.stringify(this.goodsList)
+            	},
+            	success: res => {
+                    if (res.data.rtnBean.rtnCode == 0) {
+						 uni.hideLoading();
+                        uni.showToast({
+                        	title: "保存成功",
+                        	duration: 3000,
+                        	icon: 'none',
+                        });
+                        
+                        if (this.addFlag) {
+                            uni.redirectTo({
+                                url: '/pages/activity/special-item-info?id=' + res.data.obj
+                            })
+                        } else {
+                            let pages = getCurrentPages();
+                            let prevpage = pages[pages.length - 2];
+                            prevpage.$vm.loadData();
+                            uni.navigateBack();
+                        }
+                    } else {
+						 uni.hideLoading();
+                        this.$api.msg(res.data.rtnBean.rtnMsg);
+                    }
+                   
+            	},
+            	fail: (e) => {
+            		console.log("接口调用失败:" + JSON.stringify(e));
+					uni.hideLoading();
+            	},
+            })
+        },
+        deleteGoods(index, item) {
+            let that = this
+        	uni.showModal({
+        		title: '提示',
+        		content: '确定移除该商品吗?',
+        		success: function(res) {
+        			if (res.confirm) {
+        				that.goodsList.splice(index, 1);
+        			}
+        		}
+        	});
+        },
+    },
+    onShow () {
+    },
+    onLoad (options) {
+        if (options && options.id) {
+            uni.setNavigationBarTitle({
+                title:'编辑特价菜活动'
+            });
+            this.form.storeSpecialActivityId = options.id;
+            this.loadData();
+            this.addFlag = false;
+        } else {
+            uni.setNavigationBarTitle({
+                title:'新增特价菜活动'
+            })
+            this.addFlag = true;
+        }
+    }
+}
+</script>
+<style lang="scss" scoped>
+.special-item-edit-page {
+    background-color: #f5f5f5;
+    border-top: 2rpx solid #f5f5f5;
+    width: 100vw;
+    height: 100vh;
+}
+.scroll-content {
+    height: calc(100vh - 120rpx - env(safe-area-inset-bottom));
+    height: calc(100vh - 120rpx - constant(safe-area-inset-bottom));
+}
+.between {
+    @include between;
+}
+.cell-wrap {
+    width: 100vw;
+    padding: 0 40rpx;
+    box-sizing: border-box;
+    background-color: #fff;
+}
+.item {
+    @include between;
+    height: 100rpx;
+    background-color: #fff;
+    border-bottom: 2rpx solid #ececec;
+    font-size: $font-base;
+    .label {
+        width: 200rpx;
+        color: $title-color;
+    }
+    .value {
+        width: calc(100% - 200rpx);
+        color: $desc-color;
+        text-align: right;
+    }
+}
+.activities-title {
+    height: 80rpx;
+    @include flexStart;
+    padding: 20rpx 40rpx 0;
+    box-sizing: border-box;
+    font-size: $font-base;
+    color: $title-color;
+}
+.goods-list {
+    .goods {
+        background-color: #fff;
+        @include flexStart;
+        padding: 20rpx;
+        box-sizing: border-box;
+        margin-bottom: 20rpx;
+        width: 100%;
+        .cover {
+            width: 166rpx;
+            height: 166rpx;
+            border-radius: 10rpx;
+            margin-right: 24rpx;
+            .img {
+                width: 166rpx;
+                height: 166rpx;
+            }
+        }
+        .cont {
+            width: calc(100% - 200rpx);
+            font-size: $font-base;
+            height: 166rpx;
+            overflow: hidden;
+        }
+        .name {
+            width: 100%;
+            text-overflow: -o-ellipsis-lastline;
+            overflow: hidden;
+            text-overflow: ellipsis;
+            display: -webkit-box;
+            -webkit-line-clamp: 2;
+            -webkit-box-orient: vertical;
+        }
+        .tools {
+            @include flexStart;
+        }
+        .price {
+            margin-right: 40rpx;
+            color: $uni-color-button;
+        }
+        .origin-price {
+            color: $desc-color;
+        }
+        .desc {
+            color: $desc-color; 
+        }
+    }
+    .add-goods {
+        margin-top: 40rpx;
+        width: 180rpx;
+        height: 60rpx;
+        border: 2rpx solid $theme-color;
+        color: $theme-color;
+        @include flexCenter;
+        margin-left: 20rpx;
+    }
+}
+
+.update-events {
+    width: 100vw;
+    @include flexCenter;
+    margin-top: 40rpx;
+    .btn {
+        width: 80vw;
+        height: 80rpx;
+        border-radius: 40rpx;
+        background-color: $theme-color;
+        color: #fff;
+        @include flexCenter;
+    }
+}
+</style>

+ 351 - 0
pages/activity/special-item-info.vue

@@ -0,0 +1,351 @@
+<template>
+    <view class="special-item-info-page">
+        <scroll-view scroll-y class="scroll-content">
+            <view class="item">
+                <view class="label">活动状态</view>
+                <view class="value between">
+                    <view class="curr-status" :class="{ ing: form.status == 1 }">{{ form.status == 1 ? '进行中' : '停止' }}</view>
+                    <view class="update-status" @click="handUpdateStatus">修改状态</view>
+                </view>
+            </view>
+            <view class="item">
+                <view class="label">活动主题</view>
+                <view class="value">
+                    {{form.activityName}}
+                </view>
+            </view>
+            <view class="item">
+                <view class="label">活动描述</view>
+                <view class="value">
+                    {{form.activityComment}}
+                </view>
+            </view>
+            <view class="item">
+                <view class="label">活动时间</view>
+                <view class="value">
+                    {{form.startDate}} ~ {{form.endDate}}
+                </view>
+            </view>
+            <view class="goods-wrap">
+                <view class="title">参与的商品(不支持多规格商品)</view>
+                <view class="goods" v-for="item in goodsList" :key="item.storeSpecialGoodsId">
+                    <view class="cover">
+                        <image class="img" :src="item.pictureUrl"/>
+                    </view>
+                    <view class="cont">
+                        <view class="name">{{item.goodsName}}</view>
+                        <view class="tools">
+                            <view class="price">¥{{moneyConverter(item.activityPrice)}}</view>
+                            <view class="origin-price">¥{{moneyConverter(item.costPrice)}}</view>
+                        </view>
+                        <view class="desc">
+                            每单限购{{item.maxNum}}份,活动库存{{item.activityNum}}份
+							<br>
+							剩余库存{{item.inventory || 0}}份
+                        </view>
+                    </view>
+                </view>
+            </view>
+        </scroll-view>
+        
+        <view class="update-events">
+            <view class="btn" @click="deleteActivity">删除</view>
+            <view class="btn" @click="updateSpecialItem">调整活动内容</view>
+        </view>
+
+        <u-modal
+            :show="updateStatusVisible"
+            title="注意"
+            closeOnClickOverlay
+            showCancelButton
+            @confirm="updateStatus"
+            @close="updateStatusVisible = false"
+            @cancel="updateStatusVisible = false">
+            <view class="container">
+                活动中的商品如果参加首页推荐,活动状态改变后将会在首页、店铺同步下架,您是否确定改变?
+            </view>
+        </u-modal>
+    </view>
+</template>
+<script>
+import {mapState} from 'vuex';
+export default {
+    data () {
+        return {
+            getStoreSpecialActivityByIdUrl: '/storeSpecialActivityApi/getStoreSpecialActivityByIdForStore',
+            updateStoreSpecialActivityStatusUrl: '/storeSpecialActivityApi/updateStoreSpecialActivityStatusForStore',
+            deleteStoreSpecialActivityUrl: '/storeSpecialActivityApi/deleteStoreSpecialActivityForStore',
+            updateStatusVisible: false,
+            form: {
+                storeSpecialActivityId: '',
+                activityName: '',
+                activityComment: '',
+                startDate: '',
+                endDate: '',
+                status: ''
+            },
+            goodsList: []
+        }
+    },
+    computed: {
+        ...mapState(['storeInfo'])
+    },
+    methods: {
+        loadData() {
+			uni.showLoading({
+				title: '加载中'
+			});
+            var dateStr = (new Date()).getTime();
+            var sign = this.getSign('' + this.storeInfo.storeId + dateStr);
+            this.requst({
+            	url: this.getStoreSpecialActivityByIdUrl,
+            	data: {
+            		timestamp: dateStr,
+            		sign: sign,
+            		storeId: this.storeInfo.storeId,
+                    id: this.form.storeSpecialActivityId
+            	},
+            	success: res => {
+                    this.form = res.data.obj;
+                    this.goodsList = res.data.list;
+					uni.hideLoading();
+            	},
+            	fail: (e) => {
+            		console.log("接口调用失败:" + JSON.stringify(e));
+					uni.hideLoading();
+            	},
+            })
+        },
+        deleteActivity() {
+            if (this.form.status == 1) {
+                this.$api.msg("进行中的活动不能删除,请先停止活动后再进行删除");
+                return;
+            }
+            let that = this;
+            uni.showModal({
+            	title: '提示',
+            	content: '确定删除这个特价菜活动吗?',
+            	success: function(res) {
+            		if (res.confirm) {
+						uni.showLoading({
+							title: '删除中'
+						});
+            			var dateStr = (new Date()).getTime();
+            			var sign = that.getSign('' + that.storeInfo.storeId + dateStr);
+            			that.requst({
+            				url: that.deleteStoreSpecialActivityUrl,
+            				data: {
+            					timestamp: dateStr,
+            					sign: sign,
+            					storeId: that.storeInfo.storeId,
+            			        id: that.form.storeSpecialActivityId
+            				},
+            				success: res => {
+            			        if (res.data.rtnBean.rtnCode == 0) {
+            			            uni.showToast({
+            			            	title: "删除成功",
+            			            	duration: 3000,
+            			            	icon: 'none',
+            			            });
+            			            // uni.redirectTo({
+            			            //     url: '/pages/activity/special-item'
+            			            // })
+                                    let pages = getCurrentPages();
+                                    let prevpage = pages[pages.length - 2];
+                                    prevpage.$vm.getStoreSpecialActivityList();
+                                    uni.navigateBack();
+            			        } else {
+            			            that.$api.msg(res.data.rtnBean.rtnMsg);
+            			        }
+								uni.hideLoading();
+            				},
+            				fail: (e) => {
+            					console.log("接口调用失败:" + JSON.stringify(e));
+								uni.hideLoading();
+            				},
+            			})
+            		}
+            	}
+            });
+        },
+        handUpdateStatus() {
+            if (this.form.status == 1) {
+                this.updateStatusVisible = true
+                return;
+            }
+            this.updateStatus();
+        },
+        updateStatus () {
+			uni.showLoading({
+				title: '更新中'
+			});
+            this.updateStatusVisible = false;
+            var dateStr = (new Date()).getTime();
+            var sign = this.getSign('' + this.storeInfo.storeId + dateStr);
+            this.requst({
+            	url: this.updateStoreSpecialActivityStatusUrl,
+            	data: {
+            		timestamp: dateStr,
+            		sign: sign,
+            		storeId: this.storeInfo.storeId,
+                    id: this.form.storeSpecialActivityId,
+                    status: this.form.status == 1 ? 0 : 1
+            	},
+            	success: res => {
+                    if (res.data.rtnBean.rtnCode == 0) {
+                        this.$api.msg("更新成功");
+                    } else {
+                        this.$api.msg(res.data.rtnBean.rtnMsg);
+                    }
+                    this.loadData();
+					uni.hideLoading();
+            	},
+            	fail: (e) => {
+            		console.log("接口调用失败:" + JSON.stringify(e));
+					uni.hideLoading();
+            	},
+            })
+        },
+        updateSpecialItem () {
+            if (this.form.status == 1) {
+                this.$api.msg("进行中的活动不能编辑,请先停止活动后再进行编辑");
+                return;
+            }
+            uni.navigateTo({
+                url: `/pages/activity/special-item-edit?id=${this.form.storeSpecialActivityId}`
+            });
+        }
+    },
+    onLoad (options) {
+        if (options && options.id) {
+        	this.form.storeSpecialActivityId = options.id;
+        	this.loadData();
+        }
+        
+    }
+}
+</script>
+<style lang="scss" scoped>
+.special-item-info-page {
+    background-color: #f5f5f5;
+    border-top: 2rpx solid #f5f5f5;
+    width: 100vw;
+    height: 100vh;
+}
+.scroll-content {
+    height: calc(100vh - 120rpx - env(safe-area-inset-bottom));
+    height: calc(100vh - 120rpx - constant(safe-area-inset-bottom));
+}
+.item:first-child {
+    padding-top: 40rpx;
+}
+.item {
+    @include flexStart;
+    background-color: #fff;
+    padding: 20rpx 40rpx;
+    box-sizing: border-box;
+    font-size: $font-base;
+    .label {
+        width: 240rpx;
+        color: $desc-color;
+    }
+    .value {
+        width: calc(100% - 240rpx);
+        @include between;
+        // overflow: hidden;
+        // text-overflow: ellipsis;
+        // word-break: break-all;
+        // white-space: nowrap;
+    }
+    .left {
+        @include flexStart;
+    }
+    .curr-status {
+        color: $theme-color;
+    }
+    .curr-status.ing {
+        color: $uni-color-button;
+    }
+    .update-status {
+        height: 54rpx;
+        width: 160rpx;
+        background-color: $theme-color;
+        color: #fff;
+        @include flexCenter;
+        border-radius: 27rpx;
+    }
+}
+.goods-wrap {
+    .title {
+        height: 80rpx;
+        @include flexStart;
+        padding-left: 40rpx;
+        box-sizing: border-box;
+        font-size: $font-base;
+        color: $text-color;
+    }
+}
+.goods {
+    background-color: #fff;
+    @include flexStart;
+    padding: 20rpx;
+    box-sizing: border-box;
+    .cover {
+        width: 166rpx;
+        height: 166rpx;
+        border-radius: 10rpx;
+        margin-right: 24rpx;
+        .img {
+            width: 166rpx;
+            height: 166rpx;
+        }
+    }
+    .cont {
+        width: calc(100% - 200rpx);
+        font-size: $font-base;
+        height: 166rpx;
+        overflow: hidden;
+    }
+    .name {
+        width: 100%;
+        text-overflow: -o-ellipsis-lastline;
+		overflow: hidden;
+		text-overflow: ellipsis;
+		display: -webkit-box;
+		-webkit-line-clamp: 2;
+		-webkit-box-orient: vertical;
+    }
+    .tools {
+        @include flexStart;
+    }
+    .price {
+        margin-right: 40rpx;
+        color: $uni-color-button;
+    }
+    .origin-price {
+        color: $desc-color;
+        text-decoration:line-through;
+    }
+    .desc {
+        color: $desc-color; 
+    }
+}
+.container {
+    font-size: $font-base;
+}
+.update-events {
+    width: 100vw;
+    @include flexCenter;
+    margin-top: 40rpx;
+    .btn {
+        width: 80vw;
+        height: 80rpx;
+        border-radius: 40rpx;
+        background-color: $theme-color;
+        color: #fff;
+        @include flexCenter;
+        margin-left: 20rpx;
+        margin-right: 20rpx;
+    }
+}
+</style>

+ 165 - 0
pages/activity/special-item.vue

@@ -0,0 +1,165 @@
+<template>
+    <view class="special-item-page">
+        <scroll-view class="list-wrap" scroll-y>
+            <view class="item" v-for="item in activityList" :key="item.storeSpecialActivityId" @click="handItem(item)">
+                <view class="cover">
+                    <image class="icon" src="../../static/imgs/mine/tjc.png"/>
+                </view>
+                <view class="cont">
+                    <view class="row">
+                        <view class="name">{{item.activityName}}</view>
+                        <view class="date">{{item.startDate|formatDate('MM.dd')}} ~ {{item.endDate|formatDate('MM.dd')}}</view>
+                    </view>
+                    <view class="row">
+                        <view class="desc">
+                            {{item.activityComment}}
+                        </view>
+                        <view v-if="item.status==1" class="status">进行中</view>
+                        <view v-else class="status-unused">停止</view>
+                    </view>
+                </view>
+            </view>
+        </scroll-view>
+        <view class="add-special-item">
+            <view class="add" @click="addSpecialItem">添加特价菜活动</view>
+        </view>
+    </view>
+</template>
+<script>
+import {mapState} from 'vuex';
+export default {
+    data () {
+        return {
+            getStoreSpecialActivityListUrl: '/storeSpecialActivityApi/getStoreSpecialActivityListForStore',
+            activityList: []
+        }
+    },
+    computed: {
+        ...mapState(['storeInfo'])
+    },
+    methods: {
+        handItem(item) {
+            uni.navigateTo({
+                url: '/pages/activity/special-item-info?id=' + item.storeSpecialActivityId
+            });
+        },
+        addSpecialItem () {
+            if (this.activityList.length >= 5) {
+                this.$api.msg("最多只能创建五个活动,请先删除不需要的活动");
+                return;
+            } else {
+                uni.navigateTo({
+                    url: '/pages/activity/special-item-edit'
+                });
+            }
+        },
+        getStoreSpecialActivityList() {
+			uni.showLoading({
+				title: '加载中'
+			});
+            var dateStr = (new Date()).getTime();
+            var sign = this.getSign('' + this.storeInfo.storeId + dateStr);
+            this.requst({
+            	url: this.getStoreSpecialActivityListUrl,
+            	data: {
+            		timestamp: dateStr,
+            		sign: sign,
+            		storeId: this.storeInfo.storeId
+            	},
+            	success: res => {
+                    this.activityList = res.data.list;
+					uni.hideLoading();
+            	},
+            	fail: (e) => {
+            		console.log("接口调用失败:" + JSON.stringify(e));
+					uni.hideLoading();
+            	},
+            })
+        }
+    },
+    onShow () {
+        this.getStoreSpecialActivityList();
+    }
+}
+</script>
+<style lang="scss" scoped>
+.special-item-page {
+    width: 100vw;
+    height: 100vh;
+    background-color: #f5f5f5;
+    overflow: hidden;
+}
+.list-wrap {
+    height: calc(100vh - 140rpx - env(safe-area-inset-bottom));
+    height: calc(100vh - 140rpx - constant(safe-area-inset-bottom));
+    margin-top: 20rpx;
+    .item {
+        margin-bottom: 20rpx;
+        background-color: #fff;
+        padding: 40rpx;
+        box-sizing: border-box;
+        @include flexStart;
+    }
+    .cover {
+        min-width: 60rpx;
+        width: 60rpx;
+        height: 60rpx;
+        margin-right: 40rpx;
+        border-radius: 50%;
+        overflow: hidden;
+        @include flexCenter;
+        box-shadow: 0 0 10rpx pink;
+    }
+    .icon {
+        width: 42rpx;
+        height: 42rpx;
+    }
+    .cont {
+        width: calc(100% - 100rpx);
+        font-size: $font-base;
+    }
+    .row {
+        width: 100%;
+        @include between;
+        margin-bottom: 10rpx;
+    }
+    .name {
+        color: $text-color;
+        width: calc(100% - 240rpx);
+        overflow: hidden;
+        text-overflow: ellipsis;
+        white-space: nowrap;
+        word-break: break-all;
+    }
+    .date {
+        width: 240rpx;
+        text-align: right;
+    }
+    .desc {
+        color: $desc-color;
+    }
+    .status {
+        width: 240rpx;
+        text-align: right;
+        color: $uni-color-button;
+    }
+    .status-unused {
+        width: 240rpx;
+        text-align: right;
+        color: $theme-color;
+    }
+}
+.add-special-item {
+    width: 100vw;
+    @include flexCenter;
+    margin-top: 40rpx;
+    .add {
+        width: 80vw;
+        height: 80rpx;
+        border-radius: 40rpx;
+        background-color: $theme-color;
+        color: #fff;
+        @include flexCenter;
+    }
+}
+</style>

+ 325 - 0
pages/activity/update-full-discounts.vue

@@ -0,0 +1,325 @@
+<template>
+    <view class="update-coupon-page">
+        <scroll-view scroll-y class="scroll-content">
+            <view class="note">
+                <view class="label">满减介绍</view>
+                <view class="text">用户下单金额达到满减活动设置的门槛金额,总支付金额即可减去对应的优惠金额。</view>
+            </view>
+
+            <view class="list-wrap">
+                <view class="row" v-for="item in list" :key="item.sort">
+                    <text class="m">满</text>
+                    <view class="input-wrap">
+                        <u-input
+                            border="none"
+                            inputAlign="center"
+                            fontSize="12"
+                            v-model="item.conditionFee"
+                            placeholder="门槛金额">
+                        </u-input>
+                    </view>
+                    <text class="y">元</text>
+                    <text class="j">减</text>
+                    <view class="input-wrap">
+                        <u-input
+                            border="none"
+                            inputAlign="center"
+                            fontSize="12"
+                            v-model="item.discountsFee"
+                            placeholder="力度金额">
+                        </u-input>
+                    </view>
+                    <text class="y">元</text>
+                    <u-icon name="minus-circle-fill" size="20" color="red" @click="handRemove(item)"></u-icon>
+                </view>
+                <view class="line"></view>
+                <view class="add-item" v-if="list.length < 5" @click="pushItem">+ 加一级满减,最多5级</view>
+            </view>
+            
+            <view class="choose-date">
+                <view class="label">选择活动执行期间</view>
+                <view class="value" @click="selectDate" v-if="form.fullDiscountsStartDate != null && form.fullDiscountsStartDate != ''">
+                    {{ form.fullDiscountsStartDate|formatDate('yyyy-M-d') }} ~ {{ form.fullDiscountsEndDate|formatDate('yyyy-M-d') }}
+                </view>
+                <view class="value" @click="selectDate" v-else>
+                    请选择活动时间
+                </view>
+                <!-- :formatter="formatter" -->
+            </view>
+        </scroll-view>
+
+        <uni-calendar ref="calendar" class="uni-calendar--hook" :clear-date="true" :insert="false" :range="true" @confirm="confirm" />
+
+        <view class="update-events">
+            <view class="btn" @click="submit">保存</view>
+        </view>
+    </view>
+</template>
+<script>
+import { mapState } from 'vuex';
+export default {
+    data () {
+        return {
+			fullDiscountsUrl: '/storeFullDiscountsManageApi/getDiscountsInfo',
+			updateUrl: '/storeFullDiscountsManageApi/updateFullDiscounts',
+            chooseDateVisible: false,
+            form: {
+				fullDiscountsStartDate: '',
+				fullDiscountsEndDate: ''
+			},
+			list: [
+				{
+					fullDiscountsRoleId: '',
+					conditionFee: '',
+					discountsFee: '',
+					sort: 1,
+				}
+			]
+        }
+    },
+	computed: {
+		...mapState(['storeInfo'])
+	},
+    methods: {
+		loadData () {
+			uni.showLoading({
+				title: '加载中'
+			});
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign('' + this.storeInfo.storeUserId + dateStr);
+			this.requst({
+				url: this.fullDiscountsUrl,
+				data: {
+					timestamp: dateStr,
+					sign: sign,
+					storeId: this.storeInfo.storeId,
+					storeUserId: this.storeInfo.storeUserId
+				},
+				success: res => {
+					if (res.data.rtnBean.rtnCode == 0) {
+						this.form = res.data.obj;
+						if (this.form.roleList.length > 0){
+							this.list = this.form.roleList
+						}
+					}
+					uni.hideLoading();
+				},
+				fail: (e) => {
+					console.log("接口调用失败:" + JSON.stringify(e));
+					uni.hideLoading();
+				}
+			})
+		},
+        formatter(day) {
+            const d = new Date()
+            let month = d.getMonth() + 1
+            const date = d.getDate()
+            if(day.month == month && day.day == date + 3)
+            {
+                day.bottomInfo = '有优惠'
+                day.dot = true
+            }
+            return day
+        },
+		selectDate() {
+		    this.$refs.calendar.open();
+		},
+        handRemove (item) {
+			if (this.list.length == 1) {
+				return;
+			}
+            const idx = this.list.findIndex(i => i.sort === item.sort)
+            this.list.splice(idx, 1)
+        },
+        pushItem () {
+            if (this.list.length >= 5) {
+                return
+            }
+            this.list.push({ sort: this.list[this.list.length - 1].sort + 1 })
+        },
+        confirm(e) {
+			
+			if (e.range.before > e.range.after){
+				this.$api.msg("开始时间不能大于结束时间");
+				return;
+			}
+			if (e.range.before != '' && e.range.before != undefined && e.range.before != null){
+				this.form.fullDiscountsStartDate = e.range.before;
+			} 
+			if (e.range.after != '' && e.range.after != undefined && e.range.after != null){
+				this.form.fullDiscountsEndDate = e.range.after;
+			}
+        },
+        submit () {
+			if (this.list.length > 0) {
+				for (var i = 0; i < this.list.length; i++) {
+					if (this.list[i].conditionFee == '' || this.list[i].conditionFee == undefined ||
+					 this.list[i].conditionFee == null || this.list[i].discountsFee == '' 
+					 || this.list[i].discountsFee == undefined || this.list[i].discountsFee == null) {
+						this.$api.msg("门槛金额,力度金额不能为空");
+						return;
+					}
+					if (Number(this.list[i].conditionFee) <= Number(this.list[i].discountsFee)) {
+						this.$api.msg("门槛金额必须大于力度金额");
+						return;
+					}
+					
+					if (i > 0 ) {
+						if (Number(this.list[i].conditionFee) == Number(this.list[i-1].conditionFee)) {
+							this.$api.msg("门槛金额不能相同");
+							return;
+						}
+					}
+				}
+			}
+			if (this.form.fullDiscountsStartDate == null || this.form.fullDiscountsStartDate == undefined || this.form.fullDiscountsStartDate == ""){
+				this.$api.msg("请选择开始时间");
+				return;
+			}
+			if (this.form.fullDiscountsEndDate == null || this.form.fullDiscountsEndDate == undefined || this.form.fullDiscountsEndDate == ""){
+				this.$api.msg("请选择结束时间");
+				return;
+			}
+			uni.showLoading({
+				title: '保存中'
+			});
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign('' + this.storeInfo.storeUserId + dateStr);
+			this.requst({
+				url: this.updateUrl,
+				data: {
+					timestamp: dateStr,
+					sign: sign,
+					storeId: this.storeInfo.storeId,
+					storeUserId: this.storeInfo.storeUserId,
+					fullDiscountsStartDate: this.form.fullDiscountsStartDate,
+					fullDiscountsEndDate: this.form.fullDiscountsEndDate,
+					roleListStr: JSON.stringify(this.list)
+				},
+				success: res => {
+					if (res.data.rtnBean.rtnCode == 0) {
+						uni.showToast({
+							title: "保存成功",
+							duration: 3000,
+							icon: 'none',
+						});
+						
+						var pages = getCurrentPages();
+						var prevPage = pages[pages.length - 2]; //上一个页面
+						prevPage.$vm.loadData()
+						uni.navigateBack();
+					} else {
+						if (res.data.rtnBean.rtnMsg != '') {
+							this.$api.msg(res.data.rtnBean.rtnMsg);
+						}
+					}
+					uni.hideLoading();
+				},
+				fail: (e) => {
+					this.isDisable = false;
+					console.log("接口调用失败:" + JSON.stringify(e));
+					uni.hideLoading();
+				},
+			});
+        }
+    },
+    onShow () {},
+	onLoad() {
+		this.loadData();
+	}
+}
+</script>
+<style lang="scss" scoped>
+.update-coupon-page {
+    border-top: 2rpx solid #f5f5f5;
+    height: 100vh;
+    background-color: #f5f5f5;
+}
+.update-events {
+    width: 100vw;
+    @include flexCenter;
+    margin-top: 40rpx;
+    .btn {
+        width: 80vw;
+        height: 80rpx;
+        border-radius: 40rpx;
+        background-color: $theme-color;
+        color: #fff;
+        @include flexCenter;
+    }
+}
+
+.scroll-content {
+    height: calc(100vh - 120rpx - env(safe-area-inset-bottom));
+    height: calc(100vh - 120rpx - constant(safe-area-inset-bottom));
+}
+
+.note {
+    width: calc(100% - 40rpx);
+    height: 148rpx;
+    border-radius: 10rpx;
+    background-color: $theme-color;
+    padding: 20rpx 30rpx;
+    box-sizing: border-box;
+    @include flexStart;
+    margin: 20rpx;
+    .label {
+        min-width: 100rpx;
+        font-weight: bold;
+        color: #FFF5A7;
+        font-size: 36rpx;
+    }
+    .text {
+        font-size: $font-base;
+        color: #fff;
+    }
+}
+
+.list-wrap {
+    background-color: #fff;
+    margin: 20rpx 0;
+    padding: 20rpx 0;
+    .row {
+        @include between;
+        padding: 0 40rpx;
+        box-sizing: border-box;
+        margin-bottom: 30rpx;
+    }
+    .input-wrap {
+        background-color: #f5f5f5;
+        width: 160rpx;
+    }
+    .y {
+        margin-right: 30rpx;
+    }
+    .line {
+        width: 100%;
+        height: 2rpx;
+        background-color: #f5f5f5;
+        margin: 30rpx 0;
+    }
+    .add-item {
+        font-size: $font-base;
+        color: $theme-color;
+        text-decoration: underline;
+        text-align: center;
+    }
+}
+.choose-date {
+    @include between;
+    height: 120rpx;
+    background-color: #fff;
+    padding: 0 40rpx;
+    box-sizing: border-box;
+    font-size: $font-base;
+    .label {
+        color: $text-color;
+    }
+    .value {
+        color: $title-color;
+        background-color: #f5f5f5;
+        padding: 10rpx 20rpx;
+        box-sizing: border-box;
+    }
+}
+</style>

+ 590 - 0
pages/components/order.vue

@@ -0,0 +1,590 @@
+<template>
+    <view class="item">
+        <view class="between mb10">
+            <view class="meal-no">{{ item.takeupCode }}</view>
+            <view class="type-name">{{ formatOrderType(item) }}</view>
+            <view class="status-name">{{ item.statusName }}</view>
+        </view>
+        <view class="time pd24 fs14 between">
+            <view class="left-time">
+                {{ formatOrderTime(item) }}
+            </view>
+            <view class="order-status">
+            </view>
+        </view>
+        <view class="pd24 fs14">
+            备注: {{ item.customerMes || '无' }}
+        </view>
+        <view class="between around mlr24">
+            <view class="col">
+                <view class="label desc">订单号:</view>
+                <view class="text">{{ item.orderCode }}</view>
+            </view>
+            <view class="col">
+                <view class="label desc">下单时间:</view>
+                <view class="text">{{ item.createDateStr }}</view>
+            </view>
+        </view>
+        <view class="desc mlr24 borbtm mt20">
+            <view class="row">
+                <view class="label">{{ item.orderName }} {{ item.orderTel }}</view>
+                <view class="phone-btn" @click="takePhone(item)">
+                    <u-icon name="phone-fill" color="#fff"></u-icon>
+                </view>
+            </view>
+            <view class="row" v-if="item.orderType == 1 || item.orderType == 2">
+                <view class="label">{{ item.orderAddress }}</view>
+            </view>
+        </view>
+        <view class="goods borbtm mlr24 fs14">
+            <view class="between w100">
+                <view class="label">商品</view>
+                <view class="btn" @click="handItemOrderDetail(item)">{{ !isShowGoods ? '查看' : '收起' }}</view>
+            </view>
+            <view class="goods-info" v-if="isShowGoods">
+                <view class="goods-row" v-for="detail in item.detailList">
+                    <view class="goods-name">{{ detail.goodsName }}  {{detail.propertys == '' ? '' : '(' + detail.propertys + ')'}}</view>
+                    <view class="goods-count">×{{ detail.buyCount }}</view>
+                    <view class="price">¥{{ moneyConverter(detail.salesPrice) }}</view>
+                </view>
+                <view class="params" >
+                    <view class="name">餐盒费</view>
+                    <view class="price">¥{{ moneyConverter(item.packingFee) }}</view>
+                </view>
+				<view class="params" v-if="(item.deliveryFee + item.sortingFee + item.climbingFee) > 0">
+				    <view class="name">配送费</view>
+				    <view class="price" :style="item.deliveryDiscountsFlag == 1 ? 'text-decoration: line-through' : ''">¥{{ moneyConverter(item.deliveryFee + item.sortingFee + item.climbingFee) }}</view>
+				</view>
+				<view class="params" v-if="item.newcusDiscounts > 0">
+				    <view class="name">新客立减</view>
+				    <view class="price">-¥{{ moneyConverter(item.newcusDiscounts) }}</view>
+				</view>
+				<view class="params" v-if="item.fullDiscounts > 0">
+				    <view class="name">满减</view>
+				    <view class="price">-¥{{ moneyConverter(item.fullDiscounts) }}</view>
+				</view>
+				<view class="params" v-if="item.couponCost > 0">
+				    <view class="name">优惠券</view>
+				    <view class="price">-¥{{ moneyConverter(item.couponCost) }}</view>
+				</view>
+            </view>
+        </view>
+        <view class="settle borbtm mlr24 fs14">
+            <view class="between w100">
+                <view class="label">结算信息</view>
+                <view class="btn" @click="handItemOrderSettle(item)">{{ !isShowSettle ? '查看' : '收起' }}</view>
+            </view>
+            <view class="settle-row" v-if="isShowSettle">
+                <view class="settle-item">
+                    <view class="name">顾客实付</view>
+                    <view class="price">¥<text>{{ moneyConverter(item.orderFee) }}</text></view>
+                </view>
+				<view class="settle-item">
+				    <view class="name">扣除配送费</view>
+				    <view class="price">-¥<text>{{ moneyConverter(item.deliveryFee + item.sortingFee + item.climbingFee) }}</text></view>
+				</view>
+				<view class="settle-item">
+				    <view class="name">微信扣点</view>
+				    <view class="price">-¥<text>{{ moneyConverter(calcPoints(item.wxPoint / 1000, item.orderFee)) }}</text></view>
+				</view>
+				<view class="settle-item">
+				    <view class="name">平台扣点</view>
+				    <view class="price">
+						-¥<text>
+						{{ moneyConverter(calcPoints(Number(item.commissionRate) / Number(1000) ,
+							item.orderFee - item.deliveryFee - item.sortingFee - item.climbingFee)) > 0 ? moneyConverter(calcPoints(Number(item.commissionRate) / Number(1000) ,
+							item.orderFee - item.deliveryFee - item.sortingFee - item.climbingFee)) : 0 }}
+						</text>
+					</view>
+				</view>
+				<view class="settle-item">
+				    <view class="name">本单预计收入</view>
+				    <view class="price">
+					{{ 
+						incomeFormatter(item)
+					}}
+					</view>
+				</view>
+            </view>
+        </view>
+        <view class="tools-btn mlr24 mt20" v-if="showBtn != 0">
+            <view class="taking" v-if="item.status == 10" @click="handItemTaking(item)">接单</view>
+            <view class="item-btn" v-if="item.status == 10" @click="handTurnDownOut(item)">拒绝接单</view>
+			<view class="item-btn" v-if="item.status == 30 && (item.orderType == 3 || item.orderType == 4)" @click="handComfirmPickOut(item)">确认自提</view>
+            <view class="item-btn" v-if="item.status > 10 && item.status < 60" @click="handAllPriceRefund(item)">全额退款</view>
+            <view class="item-btn" v-if="item.status == 8 || (item.status > 10 && item.status < 60)" @click="handPrintDenomination(item)">小票打印</view>
+        </view>
+    </view>
+</template>
+<script>
+import {mapState} from 'vuex';
+export default {
+    name: 'order',
+    props: ['item', 'showBtn'],
+    data () {
+        return {
+            isShowGoods: false,
+            isShowSettle: false,
+			takeUrl: '/storeOrderApi/receiveOrder',
+			refuseUrl: '/storeOrderApi/refuseOrder',
+			pickUrl: '/storeOrderApi/selfPickUp',
+			refundUrl: '/storeOrderApi/orderRefund',
+			printUrl: '/storeOrderApi/printOrder',
+			handle: false,
+        }
+		
+    },
+	computed: {
+		...mapState(['storeInfo'])
+	},
+    methods: {
+        // 堂食 / 外卖 渲染
+        formatOrderTime (item) {
+			let str = item.requireDateStr.slice(item.requireDateStr.length - 8, item.requireDateStr.length - 3);
+			let desc = "";
+
+			if (item.orderType == 4) {
+				desc = "堂食时间 "
+			} else if (item.orderType == 3) {
+				desc = "取餐时间 "
+			} else {
+				desc = "送达时间 "
+			}
+            return desc + str;
+        },
+		formatOrderType (item) {
+			if (item.orderType == 1 || item.orderType == 2) {
+				return '外卖订单'
+			} else if (item.orderType == 3) {
+				return '自提订单'
+			} else if (item.orderType == 4) {
+				return '堂食订单'
+			}
+ 		},
+		incomeFormatter(item) {
+			const orderFee = this.moneyConverter(item.orderFee);
+			const deliveryFee = this.moneyConverter(item.deliveryFee + item.sortingFee + item.climbingFee);
+			const wxPoints = this.moneyConverter(this.calcPoints(item.wxPoint / 1000, item.orderFee));
+			const commissionPoints = this.moneyConverter(this.calcPoints(Number(item.commissionRate) / Number(1000) ,
+							item.orderFee - item.deliveryFee - item.sortingFee - item.climbingFee)) > 0 ? this.moneyConverter(this.calcPoints(Number(item.commissionRate) / Number(1000) ,
+							item.orderFee - item.deliveryFee - item.sortingFee - item.climbingFee)) : 0
+			
+			const income = this.toFixed2(orderFee - deliveryFee - wxPoints - commissionPoints);
+			if (income > 0) {
+				return '¥' + income;
+			} else {
+				return '-¥' + Math.abs(income)
+			}
+		},
+        // 查看商品
+        handItemOrderDetail (item) {
+            this.isShowGoods = !this.isShowGoods
+        },
+        // 查看结算信息
+        handItemOrderSettle (item) {
+            this.isShowSettle = !this.isShowSettle
+        },
+        // taking 接单
+        handItemTaking (item) {
+			if (this.handle) {
+				return;
+			}
+			this.handle = true;
+			uni.showLoading({
+				title: '加载中'
+			})
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign(dateStr);
+			this.requst({
+				url: this.takeUrl,
+				data: {
+					timestamp: dateStr,
+					sign: sign,
+					orderId: this.item.orderId
+				},
+				success: res => {
+					this.handle = false;
+					if (res.data.rtnBean.rtnCode == 0) {
+						
+						if (res.data.obj == 0) {
+							this.$emit('loadOrder', res.data.rtnBean.rtnMsg)
+						} else {
+							this.$emit('loadOrder', '接单成功')
+						}
+					} else {
+						if (res.data.rtnBean.rtnMsg != '') {
+							this.$api.msg(res.data.rtnBean.rtnMsg);
+						}
+					}
+					uni.hideLoading()
+				},
+				fail: (e) => {
+					console.log("接口调用失败:" + JSON.stringify(e));
+					this.handle = false;
+					uni.hideLoading()
+				},
+			});
+        },
+		handTurnDownOut (item) {
+			let that = this
+			uni.showModal({
+				title: '提示',
+				content: '是否拒绝该订单?',
+				success: function (r) {
+					if (r.confirm) {
+						that.handTurnDown(item);
+					} else if (r.cancel) {
+					}
+				}	
+			})
+		},
+        // 拒单
+        handTurnDown (item) {
+			
+			if (this.handle) {
+				return;
+			}
+			this.handle = true;
+			uni.showLoading({
+				title: '加载中'
+			})
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign(dateStr);
+			this.requst({
+				url: this.refuseUrl,
+				data: {
+					timestamp: dateStr,
+					sign: sign,
+					orderId: this.item.orderId,
+					storeUserId: this.storeInfo.storeUserId
+				},
+				success: res => {
+					this.handle = false;
+					if (res.data.rtnBean.rtnCode == 0) {
+						this.$emit('loadOrder', '拒单成功')
+						
+					} else {
+						if (res.data.rtnBean.rtnMsg != '') {
+							this.$api.msg(res.data.rtnBean.rtnMsg);
+						}
+					}
+					uni.hideLoading()
+				},
+				fail: (e) => {
+					console.log("接口调用失败:" + JSON.stringify(e));
+					this.handle = false;
+					uni.hideLoading()
+				},
+			});
+        },
+		calcPoints(rate, orderFee) {
+		    return (Number(orderFee) * Number(rate));
+		},
+		handComfirmPickOut (e) {
+			let that = this
+			uni.showModal({
+				title: '提示',
+				content: '是否将该订单标记为自取?',
+				success: function (r) {
+					if (r.confirm) {
+						that.handComfirmPick(e);
+					} else if (r.cancel) {
+					}
+				}	
+			})
+		},
+        handComfirmPick (e) {
+			if (this.handle) {
+				return;
+			}
+			this.handle = true;
+			uni.showLoading({
+				title: '加载中'
+			})
+        	var dateStr = (new Date()).getTime();
+        	var sign = this.getSign(dateStr);
+        	this.requst({
+        		url: this.pickUrl,
+        		data: {
+        			timestamp: dateStr,
+        			sign: sign,
+        			orderId: e.orderId,
+        		},
+        		success: res => {
+					this.handle = false;
+        			if (res.data.rtnBean.rtnCode == 0) {
+        				this.$emit('loadOrder', '自取成功')
+        			} else {
+        				if (res.data.rtnBean.rtnMsg != '') {
+        					this.$api.msg(res.data.rtnBean.rtnMsg);
+        				}
+        			}
+					uni.hideLoading()
+        		},
+        		fail: (e) => {
+        			console.log("接口调用失败:" + JSON.stringify(e));
+					this.handle = false;
+					uni.hideLoading()
+        		},
+        	});
+        },
+        // 全额退款
+        handAllPriceRefund (item) {
+			let that = this
+			uni.showModal({
+				title: '提示',
+				content: '是否将本单全额退款?',
+				success: function (r) {
+					if (r.confirm) {
+						that.doRefund(item);
+					} else if (r.cancel) {
+					}
+				}
+			});
+        },
+		doRefund (item) {
+			if (this.handle) {
+				return;
+			}
+			this.handle = true;
+			uni.showLoading({
+				title: '加载中'
+			})
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign(dateStr);
+			this.requst({
+				url: this.refundUrl,
+				data: {
+					timestamp: dateStr,
+					sign: sign,
+					orderId: item.orderId,
+					storeUserId: this.storeInfo.storeUserId
+				},
+				success: res => {
+					this.handle = false;
+					if (res.data.rtnBean.rtnCode == 0) {
+						this.$emit('loadOrder', '退款成功')
+					} else {
+						if (res.data.rtnBean.rtnMsg != '') {
+							this.$api.msg(res.data.rtnBean.rtnMsg);
+						}
+					}
+					uni.hideLoading()
+				},
+				fail: (e) => {
+					console.log("接口调用失败:" + JSON.stringify(e));
+					this.handle = false;
+					uni.hideLoading()
+				},
+			});
+		},
+        // 小票打印
+        handPrintDenomination (item) {
+			if (this.handle) {
+				return;
+			}
+			this.handle = true;
+			uni.showLoading({
+				title: '加载中'
+			})
+            var dateStr = (new Date()).getTime();
+            var sign = this.getSign(dateStr);
+            this.requst({
+            	url: this.printUrl,
+            	data: {
+            		timestamp: dateStr,
+            		sign: sign,
+            		orderId: item.orderId,
+            	},
+            	success: res => {
+            		if (res.data.rtnBean.rtnCode == 0) {
+            			uni.showToast({
+            				title: '正在打印',
+            				icon: 'none'
+            			})
+            		} else {
+            			if (res.data.rtnBean.rtnMsg != '') {
+            				this.$api.msg(res.data.rtnBean.rtnMsg);
+            			}
+            		}
+					this.handle = false;
+					uni.hideLoading()
+            	},
+            	fail: (e) => {
+            		console.log("接口调用失败:" + JSON.stringify(e));
+					this.handle = false;
+					uni.hideLoading()
+            	},
+            });
+        },
+		toFixed2(money) {
+			return Math.round(money * 100) / 100
+		},
+		// 商家客服
+		takePhone(item){
+		    uni.makePhoneCall({
+		        phoneNumber: item.orderTel,
+		        success:function(e){
+		        },
+		        fail:function(e){
+		        }
+		    })
+		},
+    },
+    onLoad () {}
+}
+</script>
+<style lang="scss" scoped>
+.fs14 {
+    font-size: $font-base;
+}
+.pd24 {
+    padding: 0 24rpx;
+    box-sizing: border-box;
+}
+.mlr24 {
+    margin: 0 24rpx;
+}
+.ptb10 {
+    padding: 10rpx 0;
+    box-sizing: border-box;
+}
+.mt10 {
+    margin-top: 10rpx;
+}
+.mt20 {
+    margin-top: 20rpx;
+}
+.mb10 {
+    margin-bottom: 10rpx;
+}
+.w100 {
+    width: 100%;
+}
+.borbtm {
+    border-bottom: 2rpx solid #ececec;
+}
+.text {
+    color: $text-color;
+}
+.desc {
+    color: $desc-color;
+}
+.item {
+    background-color: #fff;
+    border-radius: 20rpx;
+    margin-bottom: 20rpx;
+    padding-bottom: 20rpx;
+    box-sizing: border-box;
+}
+.between {
+    height: 56rpx;
+    @include between;
+}
+.meal-no {
+    width: 156rpx;
+    height: 56rpx;
+    border-radius: 56rpx;
+    border-radius: 20rpx 28rpx 28rpx 0;
+    background-color: #000;
+    color: #fff;
+    @include flexCenter;
+    font-size: $font-base;
+}
+.type-name, .status-name {
+    height: 56rpx;
+    line-height: 56rpx;
+}
+.type-name {
+    flex: 1;
+    margin-left: 20rpx;
+}
+.status-name {
+    font-size: $font-base;
+    color: #101010;
+    margin-right: 24rpx;
+}
+.time {
+    color: $theme-color;
+}
+.order-status {
+    color: $icon-color;
+}
+.around {
+    width: calc(100% - 48rpx);
+    height: 80rpx;
+    font-size: $font-sm;
+    border-bottom: 2rpx solid #ececec;
+    .col {
+        flex: 1;
+    }
+}
+.phone-btn {
+    width: 40rpx;
+    height: 40rpx;
+    border-radius: 40rpx;
+    background-color: $theme-color;
+    @include flexCenter;
+}
+.row {
+    width: 100%;
+    @include between;
+    font-size: $font-sm;
+    margin-bottom: 10rpx;
+}
+.goods, .settle {
+    .between {
+        height: 80rpx;
+    }
+    .btn {
+        font-size: $font-sm;
+        color: $icon-color;
+        width: 80rpx;
+        height: 40rpx;
+        border-radius: 40rpx;
+        @include flexCenter;
+        border: 2rpx solid $icon-color;
+    }
+}
+.tools-btn {
+    @include flexStart;
+    .taking, .item-btn {
+        height: 52rpx;
+        @include flexCenter;
+        border-radius: 52rpx;
+        font-size: $font-sm;
+    }
+    .taking {
+        width: 140rpx;
+        background-color: $theme-color;
+        color: #fff;
+        margin-right: 20rpx;
+    }
+    .item-btn {
+        width: 200rpx;
+        color: $icon-color;
+        border: 2rpx solid $icon-color;
+        margin-right: 20rpx;
+    }
+}
+.goods-row {
+    @include between(flex-start);
+    font-size: $font-sm;
+    margin-bottom: 20rpx;
+    .goods-name {
+        flex: 2;
+    }
+    .goods-count, .price {
+        flex: 1;
+        text-align: right;
+    }
+}
+.params {
+    @include between;
+    margin-bottom: 10rpx;
+}
+.settle-item {
+    @include between;
+    margin-bottom: 10rpx;
+}
+</style>

+ 62 - 0
pages/components/p-checkbox.vue

@@ -0,0 +1,62 @@
+<template>
+  <view class="checkbox" :class="isOpen ? 'active' : ''" @click="handleToggle">
+    <text v-show="isOpen"></text>
+    <view class="circle"></view>
+    <text v-show="!isOpen"></text>
+  </view>
+</template>
+
+<script>
+export default {
+  props: {
+    isOpen: {
+      required: true,
+      type: Boolean,
+      default: false
+    }
+  },
+  data() {
+    return {};
+  },
+  methods: {
+    handleToggle() {
+      this.$emit('toggle');
+    }
+  }
+};
+</script>
+
+<style scoped lang="scss">
+.active {
+  background-color: #3c78ff !important;
+}
+.checkbox {
+  width: 100rpx;
+  height: 40rpx;
+  padding: 4rpx;
+  border-radius: 24rpx;
+  background-color: #c3c8cc;
+  display: flex;
+  justify-content: flex-start;
+  align-items: center;
+  > .circle {
+    width: 40rpx;
+    height: 40rpx;
+    background-color: white;
+    border-radius: 50%;
+  }
+  > text {
+    flex: 1;
+    font-size: 24rpx;
+    color: white;
+    &:first-of-type {
+      text-align: left;
+      margin-left: 16rpx;
+    }
+    &:last-of-type {
+      text-align: right;
+      margin-right: 16rpx;
+    }
+  }
+}
+</style>

+ 175 - 0
pages/components/popup-picker-view.vue

@@ -0,0 +1,175 @@
+<template>
+    <view class="popup-wrap" :class="{ show: visible }">
+        <view class="popup-mask" v-if="visible"></view>
+        <u-popup
+            mode="bottom"
+            closeable
+            :zIndex="99"
+            :overlay="false"
+            :show="visible"
+            safe-area-inset-bottom
+            :closeable="false"
+            @close="popupClose"
+            @open="popupOpen">
+            <view class="container">
+                <view class="top-tools">
+                    <view class="cancel" @click="visible = false">取消</view>
+                    <view class="confirm" :style="{ color: isConfirm ? '#000' : '#ececec' }" @click="handConfirm">确定</view>
+                </view>
+                <picker-view :value="checkValue" @pickstart="isConfirm = false" @pickend="isConfirm = true" @change="bindChange" class="picker-view">
+                    <picker-view-column v-for="(item, index) in list" :key="index">
+                        <view class="item" v-for="i in item" :key="i[itemKey]">{{ i[itemName] }}</view>
+                    </picker-view-column>
+                </picker-view>
+            </view>
+        </u-popup>
+    </view>
+</template>
+<script>
+export default {
+    name: 'popup-picker-view',
+    props: {
+        itemKey: {
+            type: String,
+            default: 'goodsTypeId'
+        },
+        itemName: {
+            type: String,
+            default: 'goodsTypeName'
+        },
+        list: {
+            type: Array,
+            default() {
+                return []
+            }
+        }
+    },
+    data () {
+        return {
+            visible: false,
+            isConfirm: true,
+            checkValue: 0,
+			tempPickerIndex: null,
+			pickerIndex: null
+        }
+    },
+    methods: {
+        show () {
+            this.visible = true
+        },
+        popupClose () {
+            this.visible = false
+        },
+        popupOpen () {
+        },
+        // bindChange ({ target: { value } }) {
+        //     this.isConfirm = true
+        //     this.checkValue = value
+        // },
+		bindChange(e) {
+			this.tempPickerIndex = e.detail.value
+		},
+        handConfirm () {
+			if (!this.isConfirm) {
+				// uni.showToast({
+				// 	title: '您的操作过快,请慢一点儿!',
+				// 	icon: 'none'
+				// })
+				return
+			}
+			if (this.tempPickerIndex === null) {
+				this.pickerIndex = 0
+			} else {
+				this.pickerIndex = this.tempPickerIndex;
+			}
+
+			let item = this.list[0][this.pickerIndex]
+
+			this.$emit('callback', item)
+			this.visible = false
+
+        }
+    }
+}
+</script>
+<style scoped lang="scss">
+.popup-wrap {
+    width: 100vw;
+    height: 100vh;
+    overflow: hidden;
+    z-index: -1;
+    position: fixed;
+    top: 0;
+    left: 0;
+}
+.popup-wrap.show {
+    z-index: 999;
+}
+.container {
+    width: 100%;
+    height: 35vh;
+    position: relative;
+    z-index: 999;
+}
+.top-tools {
+    height: 80rpx;
+    padding: 0 40rpx;
+    box-sizing: border-box;
+    @include between;
+    border-bottom: 2rpx solid #ececec;
+}
+.popup-mask {
+    position: fixed;
+    width: 100vw;
+    height: 100vh;
+    z-index: 1;
+    background-color: rgba(0,0,0,.5);
+}
+
+// 设置头部的取消和确定按钮
+picker-view .btns {
+    width: 100%;
+    height: 100rpx;
+    color: #baa076;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    position: absolute;
+    top: 0;
+    left: 0;
+    z-index: 5;
+}
+
+picker-view .btns view {
+    width: 20%;
+    text-align: center;
+    font-size: 32rpx;
+}
+
+picker-view {
+    background-color: #FFF;
+    text-align: center;
+    box-sizing: border-box;
+    width: 100%;
+    border-radius: 30rpx 30rpx 0 0;
+    z-index: 999;
+    height: calc(35vh - 80rpx);
+}
+
+// 添加弹出的过渡动画
+picker-view.show {
+    height: 36%;
+    transition: all 0.4s;
+}
+
+// 设置单列数据的样式
+picker-view-column {
+    border-radius: 30rpx 30rpx 0 0;
+    color: #000;
+    font-size: 32rpx;
+}
+
+.item {
+	line-height: 68rpx !important;
+}
+</style>

+ 415 - 0
pages/evaluate/index.vue

@@ -0,0 +1,415 @@
+<template>
+	<view>
+		<u-sticky bgColor="#fff" class="tabs-wrap">
+		    <u-tabs
+		        class="tabs"
+		        :list="tabs"
+		        :itemStyle="itemStyle"
+		        :is-scroll="false"
+		        :current="params.active"
+		        @change="handTabhange">
+		    </u-tabs>
+		</u-sticky>
+		<view class="content-wrap" v-if="params.active == 0" style="padding-top: 20rpx;">
+			<scroll-view scroll-y class="evaluation-info-wrap" @scrolltolower="scrolltolower">
+			    <view class="evaluation-list">
+			        <view class="item" v-for="item in evaluationList" :key="item.evaluateId">
+			            <view class="user-info">
+			                <view class="user">
+			                    <image class="avatr" :src="item.headimgurl || ''"/>
+			                    <view class="user-wrap">
+			                        <view class="top">
+			                            <view class="user-name">{{ item.memberName || '会员' }}</view>
+			                            <view class="date">{{ item.evaDateStr }}</view>
+			                        </view>
+									<!-- <view class="evaluation-rate">
+									    <text class="label" style="white-space: nowrap;">商品:</text>
+									    <view class="goods-name" style="margin-left: 8rpx;">{{ item.goodsName || '' }}</view>
+									</view> -->
+			                        <view class="evaluation-rate">
+			                            <text class="label" style="white-space: nowrap;">评分</text>
+			                            <u-rate size="20" readonly active-color="#F57119" v-model="item.goodsScore"></u-rate>
+			                        </view>
+			                    </view>
+			                </view>
+			            </view>
+			            <view class="evaluation-text">
+			                {{ item.content || '' }}
+			            </view>
+			            <view class="evaluation-image" v-if="item.pictureUrl">
+			                <image :src="item.pictureUrl" />
+			            </view>
+						<view style="margin-top: 10rpx;margin-left: 80rpx;">
+							<button type="primary" size="mini" @click="reply(item)">回复</button>
+						</view>
+			        </view>
+			    </view>
+			</scroll-view>
+		</view>
+		<view class="content-wrap" v-if="params.active == 1" style="padding-top: 20rpx;">
+			<scroll-view scroll-y class="evaluation-info-wrap" @scrolltolower="scrolltolower">
+			    <view class="evaluation-list">
+			        <view class="item" v-for="item in evaluationList" :key="item.evaluateId">
+			            <view class="user-info">
+			                <view class="user">
+			                    <image class="avatr" :src="item.headimgurl || ''"/>
+			                    <view class="user-wrap">
+			                        <view class="top">
+			                            <view class="user-name">{{ item.memberName || '会员' }}</view>
+			                            <view class="date">{{ item.evaDateStr }}</view>
+			                        </view>
+									<!-- <view class="evaluation-rate">
+									    <text class="label" style="white-space: nowrap;">商品:</text>
+									    <view class="goods-name" style="margin-left: 8rpx;">{{ item.goodsName || '' }}</view>
+									</view> -->
+			                        <view class="evaluation-rate">
+			                            <text class="label" style="white-space: nowrap;">评分</text>
+			                            <u-rate size="20" readonly active-color="#F57119" v-model="item.goodsScore"></u-rate>
+			                        </view>
+			                    </view>
+			                </view>
+			            </view>
+			           
+			            <view class="evaluation-text">
+			                {{ item.content }}
+			            </view>
+			            <view class="evaluation-image" v-if="item.pictureUrl">
+			                <image :src="item.pictureUrl" />
+			            </view>
+			            <view class="sotre-recover" v-if="item.reply && item.reply.length">
+			                <view class="name">商家回复:</view>
+			                <view>
+			                    {{ item.reply }}
+			                </view>
+			            </view>
+			        </view>
+			    </view>
+			</scroll-view>
+		</view>
+		<uni-popup ref="popup" :maskClick="true" @change="closePopup">
+			<view  style="background-color: #FFF;">
+				<view class="reply-title">回复内容</view>
+				<textarea :value="content" maxlength="45" class="reply-content" @input="inputChange"></textarea>
+				<button @click="sumbit">提交</button>
+			</view>
+		</uni-popup>
+	</view>
+</template>
+
+<script>
+import {mapState} from 'vuex';
+export default {
+	data() {
+		return {
+			evaUrl: '/storeEvaluationApi/getEvaluateList',
+			replyUrl: '/storeEvaluationApi/replyEvaluation',
+			params: {
+				active: 0
+			},
+			itemStyle: {
+				width: '50vw',
+				height: '80rpx'
+			},
+			tabs: [
+				{
+					name: '待回复',
+					value: 0,
+					badge: {
+						value: 0,
+					}
+				},
+				{
+					name: '已回复',
+					value: 10,
+					badge: {
+						value: 0,
+					}
+				},
+				
+			],
+			evaluationList:[],
+			page: 1,
+			content: '',
+			currentItem: null,
+			loadingType: 0,
+		}
+	},
+	computed: {
+		...mapState(['storeInfo'])
+	},
+	methods: {
+		closePopup () {
+			this.content = ''
+		},
+		handTabhange (e) {
+			this.params.active = e.index
+			this.loadData()
+		},
+		reply(item) {
+			this.currentItem = item;
+			this.$refs.popup.open();
+		},
+		inputChange(e) {
+			this.content = e.detail.value
+		},
+		sumbit() {
+			uni.showLoading({
+				title: '回复中'
+			});
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign(dateStr);
+			this.requst({
+				url: this.replyUrl,
+				data: {
+					timestamp: dateStr,
+					sign: sign,
+					evaluationId: this.currentItem.evaluateId,
+					content: this.content
+				},
+				success: res => {
+					uni.showToast({
+						title: '回复成功',
+						icon: 'none'
+					})
+					this.loadData();
+					this.$refs.popup.close();
+					uni.hideLoading();
+				},
+				fail: (e) => {
+					console.log("接口调用失败:" + JSON.stringify(e));
+					uni.hideLoading();
+				}
+			})
+		},
+		loadData() {
+			uni.showLoading({
+				title: '加载中'
+			});
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign(dateStr);
+			this.requst({
+				url: this.evaUrl,
+				data: {
+					timestamp: dateStr,
+					sign: sign,
+					currentPage: this.page,
+					storeId: this.storeInfo.storeId,
+					status: (this.params.active + 1)
+				},
+				success: res => {
+					if (res.data.rtnBean.rtnCode != undefined) {
+						if (res.data.rtnBean.rtnCode == 0) {
+							if (res.data.list != null && res.data.list.length > 0) {
+								if (res.data.list != null && res.data.list.length > 0 && res.data.list
+									.length <= 20) {
+									this.evaluationList = res.data.list;
+									if (res.data.page.pageCount == 1) {
+										this.isShow = false
+										this.loadingType = 2;
+									} else {
+										this.isShow = false
+										this.loadingType = 0;
+									}
+								} else {
+									this.isShow = true
+									this.loadingType = 2;
+								}
+							} else {
+								this.isShow = true
+								this.evaluationList = [];
+							}
+						} else {
+							this.isShow = true
+							this.evaluationList = [];
+						}
+					}
+					uni.hideLoading();
+				},
+				fail: (e) => {
+					console.log("接口调用失败:" + JSON.stringify(e));
+					uni.hideLoading();
+				}
+			})
+		},
+		scrolltolower() {
+			if (this.loadingType != 0) { // loadingType!=0;直接返回
+				return false
+			}
+			// this.loadingType_status = 'loading';
+			this.page = this.page + 1;
+			this.loadingType = 1;
+			uni.showLoading({
+				title: '加载中'
+			});
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign(dateStr);
+			this.requst({
+				url: this.evaUrl,
+				data: {
+					timestamp: dateStr,
+					sign: sign,
+					currentPage: this.page,
+					storeId: this.storeInfo.storeId,
+					status: (this.params.active + 1)
+				},
+				success: res => {
+					if (res.data.rtnBean.rtnCode == 0) {
+						uni.hideLoading();
+						if (res.data.list != "" && res.data.list != null && res.data.list.length > 0) {						
+							for (var i = 0; i < res.data.list.length; i++) {
+								this.evaluationList.push(res.data.list[i]);
+							}							
+							if (res.data.page.currentPage == res.data.page.pageCount) {
+								this.loadingType = 2;
+							}else {
+								this.isShow = false
+								this.loadingType = 0;
+							}
+						} else {
+							this.loadingType = 2;
+						}
+					} else {
+						uni.hideLoading();
+						if (res.data.rtnBean.rtnMsg != '') {
+							this.$api.msg(res.data.rtnBean.rtnMsg);
+						}
+					}
+					
+				},
+				fail: (e) => {
+					console.log("接口调用失败:" + JSON.stringify(e));
+					uni.hideLoading();
+				},
+			});
+		},
+	},
+	onLoad() {
+		this.loadData()
+	}
+}
+</script>
+
+<style lang="scss" scoped>
+	.content-wrap {
+		height: calc(100vh - 80rpx);
+	}
+	.evaluation-info-wrap {
+	    width: 100%;
+	    max-height: 100%;
+	    height: 100%;
+	    overflow-y: auto;
+	    background-color: #f5f5f5;
+	}
+	.evaluation-wrap {
+	    width: 100%;
+	    height: 246rpx;
+	    @include between;
+	    margin: 20rpx 0;
+	    background-color: #fff;
+	    .left, .right {
+	        flex: 1;
+	        text-align: center;
+	        .count {
+	            font-size: 56rpx;
+	            font-weight: bold;
+	            color: $light-color;
+	        }
+	        .name {
+	            font-size: $font-lg;
+	        }
+	    }
+	    .center {
+	        flex: 2;
+	        display: flex;
+	        flex-direction: column;
+	        .item {
+	            @include flexStart;
+	        }
+	    }
+	    .right {
+	    }
+	}
+	.evaluation-list {
+	    padding: 20rpx 0 0;
+	    box-sizing: border-box;
+	    background-color: #fff;
+	    .item {
+	        margin-bottom: 20rpx;
+	        border-bottom: 2px solid #f5f5f5;
+	        padding: 0 40rpx 20rpx;
+	        box-sizing: border-box;
+	    }
+	    .item:last-child {
+	        margin-bottom: 0;
+	        border-bottom: none;
+	    }
+	    .user-info {
+	        width: 100%;
+	        @include between;
+	    }
+	    .user {
+	        width: 100%;
+	        @include flexStart;
+	    }
+	    .avatr {
+	        width: 60rpx;
+	        min-width: 60rpx;
+	        height: 60rpx;
+	        border-radius: 60rpx;
+	        overflow: hidden;
+	        margin-right: 20rpx;
+	    }
+	    .user-wrap {
+	        width: calc(100% - 80rpx);
+	        margin-bottom: 20rpx;
+	        .top {
+	            width: 100%;
+	            @include between;
+	        }
+	        .date {
+	            font-size: $font-base;
+	        }
+	        .evaluation-rate {
+	            @include flexStart;
+	            font-size: $font-base;
+	        }
+	    }
+	    .evaluation-text, .evaluation-image, .sotre-recover {
+	        margin-left: 80rpx;
+			font-size: 26rpx;
+	    }
+	    .evaluation-image {
+	        margin-top: 20rpx;
+	        image {
+	            width: 200rpx;
+	            height: 200rpx;
+	            margin-right: 20rpx;
+	            margin-bottom: 20rpx;
+	        }
+	    }
+	    .sotre-recover {
+	        background-color: #f5f5f5;
+	        padding: 20rpx;
+			margin-top: 20rpx;
+	        box-sizing: border-box;
+	        font-size: $font-base;
+	        color: $icon-color;
+	    }
+	
+	}
+	.reply-title {
+		padding: 16rpx;
+		border-bottom: 2rpx solid #E6E6E6;
+	}
+	.reply-content {
+		border-bottom: 2rpx solid #E6E6E6;
+		padding: 20rpx;
+	}
+	.user-name {
+		 white-space: nowrap;
+		  overflow: hidden;
+		  text-overflow: ellipsis;
+		  display: inline-block;
+		  max-width: 6em;
+	}
+</style>

+ 811 - 0
pages/goods/goods-edit.vue

@@ -0,0 +1,811 @@
+<template>
+	<view class="goods-edit-page">
+		<scroll-view scroll-y class="scroll-content">
+			<view class="title">基本设置</view>
+			<view class="cell-group">
+				<view class="cell dubble">
+					<view class="row">
+						<view class="label"><span class="required">*</span>商品图片</view>
+						<view class="value">
+							<u-upload :fileList="fileList" @afterRead="afterRead" @delete="deletePic" name="1"
+								:previewFullImage="true" :maxCount="1"></u-upload>
+						</view>
+					</view>
+				</view>
+				<view class="cell">
+					<view class="row">
+						<view class="label"><span class="required">*</span>商品名称</view>
+						<view class="value">
+							<u-input border="none" inputAlign="left" fontSize="14" v-model="goodsInfo.goodsName"
+								placeholder="请输入商品名称"></u-input>
+						</view>
+					</view>
+				</view>
+				<view class="cell">
+					<view class="row">
+						<view class="label w280">最低购买数量</view>
+						<view class="value">
+							<u-input border="none" type="number" inputAlign="left" fontSize="14"
+								v-model="goodsInfo.miniNum"></u-input>
+						</view>
+					</view>
+					<view class="desc">默认 1 为没有限制,如设为 2 ,则用户最少买两份才能下单。</view>
+				</view>
+				<view class="cell">
+					<view class="row">
+						<view class="label">商品单位</view>
+						<view class="value">
+							<u-input border="none" inputAlign="left" fontSize="14" v-model="goodsInfo.utils"
+								placeholder="请输入商品单位,默认为:份,最多一个字"></u-input>
+						</view>
+					</view>
+				</view>
+				<view class="cell">
+					<view class="row">
+						<view class="label"><span class="required">*</span>商品分类</view>
+
+						<view class="value" @click="handCheckGoodsType">
+							<view class="class-name">{{ goodsInfo.goodsTypeName }}</view>
+							<u-icon name="arrow-right" size="20" color="#6c6c6c"></u-icon>
+						</view>
+					</view>
+				</view>
+				<view class="cell dubble h240">
+					<view class="row">
+						<view class="label">商品描述</view>
+						<view class="value">
+							<u--textarea v-model="goodsInfo.rmk" maxlength="100" placeholder="请输入商品描述"></u--textarea>
+						</view>
+					</view>
+				</view>
+			</view>
+			<view class="title">商品规格</view>
+			<view class="list-group">
+				<view class="item" v-for="(item,index) in modelBeanList" :key="item.id">
+					<view class="cell">
+						<view class="label"><span class="required">*</span>规格名称</view>
+						<view class="value">
+							<u-input border="none" maxlength="20" inputAlign="left" fontSize="14" v-model="item.modelName"
+								placeholder="请输入规格名称"></u-input>
+						</view>
+					</view>
+					<view class="cell">
+						<view class="label"><span class="required">*</span>价格</view>
+						<view class="value">
+							<u-input border="none" inputAlign="left" fontSize="14" v-model="item.costPrice"
+								placeholder="请输入价格"></u-input>
+						</view>
+					</view>
+					<view class="cell">
+						<view class="label"><span class="required">*</span>餐盒费</view>
+						<view class="value">
+							<u-input border="none" inputAlign="left" fontSize="14" v-model="item.packingFee"
+								placeholder="请输入餐盒费"></u-input>
+						</view>
+					</view>
+					<view class="cell">
+						<view class="label"><span class="required">*</span>库存</view>
+						<view class="value">
+							<u-input border="none" type="number" inputAlign="left" @input="inventoryInput(index,$event)" fontSize="14" v-model="item.inventory"
+								placeholder="请输入库存"></u-input>
+							<view class="btn" @click="reset(index)">沽清</view>
+							<view class="btn" @click="addAll(index)">置满</view>
+						</view>
+					</view>
+					<view class="cell">
+						<view class="label">最大库存</view>
+						<view class="value">
+							9999
+						</view>
+					</view>
+					<view class="cell no-bor">
+						<view class="del-btn" v-if="item.modelId == null || item.modelId == ''" @click="deleteModel(item)">删除该规格</view>
+					</view>
+				</view>
+				<view class="add-specificatin" @click="addModel">添加规格</view>
+			</view>
+			<view class="title">添加属性</view>
+			<view class="list-group">
+				<view class="item" v-for="(item,index) in propertyBeanList" :key="item.id">
+					<view class="cell">
+						<view class="label"><span class="required">*</span>属性名称</view>
+						<view class="value">
+							<u-input border="none" maxlength="5" inputAlign="left" fontSize="14" v-model="item.propertyName"
+								placeholder="请输入属性名称"></u-input>
+						</view>
+					</view>
+					<view class="cell dubble" style="height: 280rpx;">
+						<view class="label"><span class="required">*</span>属性描述</view>
+						<view class="value" style="border: 2rpx solid #ececec; border-radius: 6rpx;">
+							<u--textarea height="100" v-model="item.propertyList" maxlength="200" placeholder="多个属性用逗号隔开,例如:微辣,变态辣"></u--textarea>
+						</view>
+					</view>
+					<view class="cell no-bor">
+						<view class="del-btn" @click="deleteProperty(item)">删除该属性</view>
+					</view>
+				</view>
+				<view class="add-specificatin" @click="addProperty">添加属性</view>
+			</view>
+		</scroll-view>
+		<view class="save">
+			<view class="btn" @click="save">保存</view>
+		</view>
+		<popupPickerView :list="goodsTypeList" ref="popupPickerView" @callback="confirm" />
+	</view>
+</template>
+<script>
+	import { mapState } from 'vuex';
+	import popupPickerView from '../components/popup-picker-view.vue';
+	export default {
+		components: {
+			popupPickerView
+		},
+		data() {
+			return {
+				goodsTypeUrl: '/goodsTypeManageApi/getGoodsTypeList',
+				goodsInfoUrl: '/storeGoodsManageApi/getGoodsInfo',
+				saveUrl: '/storeGoodsManageApi/update',
+				checkSpecialUrl: '/storeGoodsManageApi/checkSpecial',
+				fileList: [],
+				goodsTypeList: [],
+				typeList: [],
+				modelBeanList: [{
+					id: 1,
+					modelId: '',
+					modelName: '',
+					packingFee: '',
+					salePrice: '',
+					costPrice: '',
+					inventory: ''
+				}],
+				propertyBeanList: [],
+				goodsInfo: {
+					goodsId: '',
+					pictureUrl: '',
+					goodsName: '',
+					miniNum: 1,
+					utils: '份',
+					goodsTypeId: '',
+					goodsTypeName: '',
+					rmk: '',
+					modelBeanList: [],
+					propertyBeanList: [],
+				},
+				specifications: [{
+					id: 1,
+					name: '',
+					price: 1,
+					package: 0,
+					inventory: 0,
+					maxInventory: 9999
+				}],
+				params: [{
+					id: 1,
+					name: '',
+					subdivide: ''
+				}],
+				file: {},
+				event: {},
+				goodsId: '',
+				isSpecial: false,
+				handleControl: true
+			}
+		},
+		computed: {
+			...mapState(['storeInfo'])
+		},
+		methods: {
+			getGoodsInfo(goodsId) {
+				uni.showLoading({
+					title: '加载中'
+				});
+				var dateStr = (new Date()).getTime();
+				var sign = this.getSign('' + this.storeInfo.storeUserId + dateStr);
+				this.requst({
+					url: this.goodsInfoUrl,
+					data: {
+						timestamp: dateStr,
+						sign: sign,
+						storeId: this.storeInfo.storeId,
+						storeUserId: this.storeInfo.storeUserId,
+						goodsId: goodsId
+					},
+					success: res => {
+						if (res.data.rtnBean.rtnCode == 0) {
+							this.goodsInfo = res.data.obj;
+							if (this.goodsInfo.utils === '' || this.goodsInfo.utils === null) {
+								this.goodsInfo.utils = '份'
+							}
+							this.modelBeanList = this.goodsInfo.modelBeanList;
+							
+							let that = this
+							if (that.modelBeanList.length > 0) {
+								for (var i = 0; i < that.modelBeanList.length; i++) {
+									that.modelBeanList[i].costPrice = that.moneyConverter(that.modelBeanList[i].costPrice)
+									that.modelBeanList[i].packingFee = that.moneyConverter(that.modelBeanList[i].packingFee)
+									that.$set(that.modelBeanList[i], 'id', i + 1)
+								}
+							}
+							if (that.goodsInfo.propertyBeanList != null) {
+								that.propertyBeanList = that.goodsInfo.propertyBeanList;
+								if (that.propertyBeanList != null && that.propertyBeanList.length > 0) {
+									for (var i = 0; i < that.propertyBeanList.length; i++) {
+										that.$set(that.propertyBeanList[i], 'id', i + 1)
+									}
+								}
+							}
+							
+							let picUrl = this.goodsInfo.pictureUrl;
+							this.fileList.push({
+								url: picUrl
+							})
+							this.check();
+						}
+						uni.hideLoading();
+					},
+					fail: (e) => {
+						console.log("接口调用失败:" + JSON.stringify(e));
+						uni.hideLoading();
+					}
+				})
+			},
+			check() {
+				var dateStr = (new Date()).getTime();
+				var sign = this.getSign('' + this.storeInfo.storeUserId + dateStr);
+				this.requst({
+					url: this.checkSpecialUrl,
+					data: {
+						timestamp: dateStr,
+						sign: sign,
+						storeId: this.storeInfo.storeId,
+						storeUserId: this.storeInfo.storeUserId,
+						goodsId: this.goodsId
+					},
+					success: res => {
+						if (res.data.rtnBean.rtnCode == 0) {
+							this.isSpecial = res.data.obj
+						}
+						uni.hideLoading();
+					},
+					fail: (e) => {
+						console.log("接口调用失败:" + JSON.stringify(e));
+						uni.hideLoading();
+					}
+				});
+			},
+			addModel() {
+				if (this.isSpecial) {
+					this.$api.msg("该商品已参加特价菜活动,只有一个规格");
+					return false;
+				}
+				let model = {
+					id: this.modelBeanList.length + 1,
+					modelId: '',
+					modelName: '',
+					packingFee: '',
+					salePrice: '',
+					costPrice: '',
+					inventory: ''
+				}
+				this.modelBeanList.push(model)
+			},
+			deleteModel(item) {
+				if (this.modelBeanList.length === 1) {
+					
+					uni.showToast({
+						title: "商品规格至少有一个",
+						duration: 3000,
+						icon: 'none',
+					});
+									
+					return false
+				}
+				let index = this.modelBeanList.indexOf(item)
+				if (index !== -1) {
+					this.modelBeanList.splice(index, 1)
+				} 
+			},
+			addProperty() {
+				// if (this.isSpecial) {
+				// 	this.$api.msg("该商品已参加特价菜活动,无法添加属性");
+				// 	return false;
+				// }
+				if (this.propertyBeanList.length == 4) {
+					uni.showToast({
+						title: "商品属性最多有四个",
+						duration: 3000,
+						icon: 'none',
+					});
+									
+					return false
+				}
+				let model = {
+					id: this.propertyBeanList.length + 1,
+					propertyId: '',
+					propertyName: '',
+					propertyList: '',
+					goodsId: ''
+				}
+				this.propertyBeanList.push(model)
+			},
+			deleteProperty(item) {
+				// if (this.propertyBeanList.length === 1) {
+				// 	return false
+				// }
+				let index = this.propertyBeanList.indexOf(item)
+				if (index !== -1) {
+					this.propertyBeanList.splice(index, 1)
+				} 
+			},
+			getGoodsTypeList() {
+				uni.showLoading({
+					title: '加载中'
+				});
+				var dateStr = (new Date()).getTime();
+				var sign = this.getSign('' + this.storeInfo.storeUserId + dateStr);
+				this.requst({
+					url: this.goodsTypeUrl,
+					data: {
+						timestamp: dateStr,
+						sign: sign,
+						storeId: this.storeInfo.storeId,
+						storeUserId: this.storeInfo.storeUserId
+					},
+					success: res => {
+						if (res.data.rtnBean.rtnCode == 0) {
+							let arr = []
+							arr.push(res.data.list);
+							this.typeList = res.data.list
+							this.goodsTypeList = arr;
+							}
+						uni.hideLoading();
+					},
+					fail: (e) => {
+						console.log("接口调用失败:" + JSON.stringify(e));
+						uni.hideLoading();
+					}
+				})
+			},
+			// 删除图片
+			deletePic(event) {
+				this.fileList.splice(event.index, 1)
+				this.goodsInfo.pictureUrl = ''
+			},
+			// 新增图片
+			async afterRead(event) {
+				this.event = event
+				this.file = event.file
+				let file1 = event.file
+				let fileListLen = this.fileList.length
+				// lists.map((item) => {
+				this.fileList.push({
+					...file1,
+					status: 'success',
+					// message: '上传中'
+				})
+				this.goodsInfo.pictureUrl = this.file.url
+
+			},
+			handCheckGoodsType () {
+				this.$refs.popupPickerView.show()
+			},
+			confirm(e) {
+				this.goodsInfo.goodsTypeId = e.goodsTypeId;
+				this.goodsInfo.goodsTypeName = e.goodsTypeName;
+			},
+			handChangeClass() {
+			},
+			addAll(index) {
+				this.modelBeanList[index].inventory = 9999;
+			},
+			reset(index) {
+				this.modelBeanList[index].inventory = 0;
+			},
+			save() {
+				uni.showLoading({
+					title: '保存中'
+				})
+		
+				this.goodsInfo.modelBeanList = this.modelBeanList;
+				this.goodsInfo.propertyBeanList= this.propertyBeanList;
+				let goodsType = ''
+				if (this.goodsInfo.goodsTypeId !== null || this.goodsInfo.goodsTypeId !== '') {
+					for (var i = 0; i < this.typeList.length; i++) {
+						if(this.typeList[i].goodsTypeId === this.goodsInfo.goodsTypeId) {
+							goodsType = this.typeList[i]
+						}
+					}
+					console.log(goodsType);
+				}
+				
+				if (this.goodsInfo.pictureUrl == null || this.goodsInfo.pictureUrl == undefined || this.goodsInfo.pictureUrl == '') {
+					this.$api.msg("请上传商品图片");
+					return;
+				} else if (this.goodsInfo.goodsName == null || this.goodsInfo.goodsName == '') {
+					this.$api.msg("请输入商品名称");
+					return;
+				} else if (this.goodsInfo.goodsName.length > 30) {
+					this.$api.msg("商品名称不能超过30个字符");
+					return;
+				} else if (this.goodsInfo.utils.length > 10 ) {
+					this.$api.msg("商品单位最多输入十个字符");
+					return;
+				} else if (this.goodsInfo.goodsTypeId == null || this.goodsInfo.goodsTypeId == '') {
+					this.$api.msg("请选择商品分类");
+					return;
+				} else if (this.isSpecial && goodsType !== '' && goodsType.type === 1) {
+					this.$api.msg("特价菜不能修改为必选商品");
+					return;
+				} else if (this.modelBeanList.length == 0 || this.modelBeanList == '') {
+					this.$api.msg("请添加商品规格");
+					return;
+				} else {
+					
+					let that = this
+					let aoo = true
+					if(that.propertyBeanList != null && that.propertyBeanList.length > 0) {
+						let num = false
+						if (that.propertyBeanList.length > 0 || that.propertyBeanList[0].propertyName || that.propertyBeanList[0].propertyList) {
+							num = true
+						}
+						for (var i = 0; i < that.propertyBeanList.length; i++) {
+							if (num){
+								if (that.propertyBeanList[i].propertyName == null || that.propertyBeanList[i].propertyName == '') {
+									that.$api.msg("请输入属性名称");
+									aoo = false;
+									return;
+								} else if (that.propertyBeanList[i].propertyList == null || that.propertyBeanList[i].propertyList == '') {
+									that.$api.msg("请输入属性描述");
+									aoo = false;
+									return;
+								} else if (that.propertyBeanList[i].propertyList.indexOf(',') == -1 && that.propertyBeanList[i].propertyList.indexOf(',') == -1) {
+									this.$api.msg("属性描述必须大于等于两个");
+									aoo = false;
+									return;
+								}
+								let str = that.propertyBeanList[i].propertyList.replace(/,/g,',')
+								
+								let arr = str.split(",").map(el => el.trim()).filter(item => item.trim() != '')
+								if(arr.length <=1){
+									this.$api.msg("属性描述必须大于等于两个");
+									aoo = false;
+									return;
+								}
+								that.propertyBeanList[i].propertyList = arr.join(',')
+							}
+						}
+					}
+					
+					
+					let boo = true;
+					
+					let regPrice = (/^[0-9]\d*(,\d{3})*(\.\d{1,2})?$|^0\.\d{1,2}$/);
+					// let regPrice = (/^\d+(?=\.{0,1}\d+$|$)/);
+					if (that.modelBeanList.length > 0) {
+						for (var i = 0; i < that.modelBeanList.length; i++) {
+							if (that.modelBeanList[i].modelName == null || that.modelBeanList[i].modelName == '') {
+								that.$api.msg("请输入规格名称");
+								boo = false;
+								return;
+							} else if (that.modelBeanList[i].costPrice === null || that.modelBeanList[i].costPrice === '') {
+								that.$api.msg("请输入价格");
+								boo = false;
+								return;
+							} else if (!regPrice.test(that.modelBeanList[i].costPrice)) {
+								that.$api.msg("价格只能是整数或小数点后两位");
+								boo = false;
+								return;
+							} else if (that.modelBeanList[i].packingFee === null || that.modelBeanList[i].packingFee ===
+								undefined || that.modelBeanList[i].packingFee === '') {
+								that.$api.msg("请输入餐盒费");
+								boo = false;
+								return;
+							} else if (that.modelBeanList[i].inventory === null || that.modelBeanList[i].inventory === '') {
+								that.$api.msg("请输入库存");
+								boo = false;
+								return;
+							} else if (that.modelBeanList[i].inventory > Number(9999)) {
+								that.$api.msg("库存不能超过最大库存数");
+								boo = false;
+								return;
+							} else if (that.isSpecial && Number(that.modelBeanList[i].costPrice * 100) < Number(that.modelBeanList[i].salePrice)) {
+								that.$api.msg("商品价格不能小于售价");
+								boo = false;
+								return;
+							}
+						}
+					}
+					if (aoo && boo) {
+						
+						var dateStr = (new Date()).getTime();
+						var sign = that.getSign('' + that.storeInfo.storeUserId + dateStr);
+						if (that.event.file) {
+							return new Promise((resolve, reject) => {
+								let a = uni.uploadFile({
+									url: that.storeInfo.code + this.saveUrl,
+									filePath: that.goodsInfo.pictureUrl,
+									name: 'file',
+									formData: {
+										timestamp: dateStr,
+										sign: sign,
+										storeId: that.storeInfo.storeId,
+										storeUserId: that.storeInfo.storeUserId,
+										goodsId: that.goodsInfo.goodsId,
+										pictureUrl: that.goodsInfo.pictureUrl,
+										goodsName: that.goodsInfo.goodsName,
+										miniNum: that.goodsInfo.miniNum || 1,
+										utils: that.goodsInfo.utils || '份',
+										goodsTypeId: that.goodsInfo.goodsTypeId,
+										rmk: that.goodsInfo.rmk || '',
+										modelBeanListJson: JSON.stringify(that.goodsInfo.modelBeanList),
+										propertyBeanListJson: JSON.stringify(that.goodsInfo.propertyBeanList)
+									},
+									header: {
+										'content-type': 'multipart/form-data'
+									}, // header 值
+									success: (res) => {
+										if(res.data) {
+											let data = JSON.parse(res.data)
+											if (data.rtnBean.rtnCode == 0) {
+												uni.hideLoading();
+												uni.showToast({
+													title: "保存成功",
+													duration: 3000,
+													icon: 'none',
+												});
+											
+												var pages = getCurrentPages();
+												var prevPage = pages[pages.length - 2]; //上一个页面
+												prevPage.$vm.loadData()
+												uni.navigateBack();
+												// return;
+											} else {
+												uni.hideLoading();
+												if (data.rtnBean.rtnMsg != '') {
+													that.$api.msg(data.rtnBean.rtnMsg);
+												}
+											}
+										} else {
+											uni.hideLoading();
+											that.$api.msg('商品更新失败,请稍后重试');
+										}
+										
+									},
+									fail: (res) => {
+										uni.hideLoading();
+									}
+								});
+							})
+						} else {
+							that.requst({
+								url: that.saveUrl,
+								data: {
+									timestamp: dateStr,
+									sign: sign,
+									storeId: that.storeInfo.storeId,
+									storeUserId: that.storeInfo.storeUserId,
+									goodsId: that.goodsInfo.goodsId,
+									pictureUrl: that.goodsInfo.pictureUrl,
+									goodsName: that.goodsInfo.goodsName,
+									miniNum: that.goodsInfo.miniNum || 1,
+									utils: that.goodsInfo.utils || '份',
+									goodsTypeId: that.goodsInfo.goodsTypeId,
+									rmk: that.goodsInfo.rmk || '',
+									modelBeanListJson: JSON.stringify(that.goodsInfo.modelBeanList),
+									propertyBeanListJson: JSON.stringify(that.goodsInfo.propertyBeanList)
+								},
+								success: res => {
+									if (res.data.rtnBean.rtnCode == 0) {
+										uni.hideLoading();
+										uni.showToast({
+											title: "保存成功",
+											duration: 3000,
+											icon: 'none',
+										});
+
+										var pages = getCurrentPages();
+										var prevPage = pages[pages.length - 2]; //上一个页面
+										prevPage.$vm.loadData()
+										uni.navigateBack();
+										// return;
+									} else {
+										uni.hideLoading();
+										if (res.data.rtnBean.rtnMsg != '') {
+											that.$api.msg(res.data.rtnBean.rtnMsg);
+										}
+									}
+									
+								},
+								fail: (e) => {
+									that.isDisable = false;
+									console.log("接口调用失败:" + JSON.stringify(e));
+									uni.hideLoading();
+								},
+							});
+						}
+
+					}
+				}
+				// uni.navigateBack();
+			},
+			inventoryInput(index,e){
+				// 只能输入数字
+				const inputType = /[^\d]/g
+				this.$nextTick(function () {
+					this.modelBeanList[index].inventory = e.replace(inputType,'');
+				})
+			}
+		},
+		onLoad(options) {
+			this.goodsId = options.goodsId
+			if (options.goodsId != undefined && options.goodsId != null && options.goodsId != '') {
+				this.getGoodsInfo(options.goodsId);
+			}
+			this.getGoodsTypeList();
+		}
+	}
+</script>
+<style lang="scss" scoped>
+	.required {
+		color: red;
+		margin-left: 5px;
+	}
+	.goods-edit-page {
+		background-color: #f5f5f5;
+		width: 100vw;
+		height: 100vh;
+		display: flex;
+		flex-direction: column;
+		align-items: center;
+		justify-content: flex-start;
+	}
+
+	.save {
+		width: 100vw;
+		@include flexCenter;
+		z-index: 99;
+		margin-top: 40rpx;
+
+		.btn {
+			width: 80vw;
+			height: 80rpx;
+			border-radius: 40rpx;
+			background-color: $theme-color;
+			color: #fff;
+			@include flexCenter;
+		}
+	}
+
+	.scroll-content {
+		height: calc(100vh - 120rpx - env(safe-area-inset-bottom));
+		height: calc(100vh - 120rpx - constant(safe-area-inset-bottom));
+	}
+
+	.title {
+		height: 80rpx;
+		padding: 0 40rpx;
+		box-sizing: border-box;
+		@include flexStart;
+		font-size: 36rpx;
+	}
+
+	.cell-group {
+		background-color: #fff;
+		border-top: 2px solid transparent;
+
+		.cell {
+			padding: 0 20rpx;
+			margin: 20rpx 0;
+			border-bottom: 2rpx solid #ececec;
+			font-size: $font-base;
+		}
+
+		.cell.dubble {
+			height: 200rpx;
+		}
+
+		.cell.h240 {
+			height: 240rpx;
+			margin-bottom: 0;
+		}
+		.cell.h280 {
+			height: 280rpx;
+			margin-bottom: 0;
+		}
+
+		.row {
+			@include flexStart;
+			padding: 20rpx 0;
+		}
+
+		.dubble {
+			.row {
+				@include flexStart(flex-start);
+			}
+		}
+
+		.label {
+			width: 180rpx;
+		}
+
+		.label.w280 {
+			width: 280rpx;
+		}
+
+		.value {
+			width: calc(100% - 180rpx);
+			@include between;
+		}
+
+		.class-name {
+			width: calc(100% - 100rpx);
+		}
+
+		.desc {
+			color: $uni-color-button;
+			margin-bottom: 20rpx;
+			font-size: $font-sm;
+		}
+	}
+
+	.list-group {
+		background-color: #fff;
+		padding: 20rpx;
+		box-sizing: border-box;
+		font-size: $font-base;
+
+		.item {
+			box-shadow: 0 0 10rpx #d1d1d1;
+			padding: 20rpx 20rpx 1rpx;
+			box-sizing: border-box;
+			margin-bottom: 20rpx;
+		}
+
+		.cell {
+			height: 60rpx;
+			@include flexStart;
+			margin-bottom: 20rpx;
+			border-bottom: 2rpx solid #ececec;
+		}
+
+		.label {
+			width: 160rpx;
+		}
+
+		.value {
+			width: calc(100% - 160rpx);
+			@include flexStart;
+
+			.btn {
+				margin-left: 50rpx;
+				color: $theme-color;
+			}
+		}
+
+		.no-bor {
+			border: none;
+		}
+
+		.del-btn {
+			width: 100%;
+			color: $uni-color-button;
+			text-align: center;
+		}
+
+		.dubble {
+			@include flexStart(flex-start);
+			padding-top: 20rpx;
+			box-sizing: border-box;
+		}
+
+		.h240 {
+			height: 240rpx;
+		}
+	}
+	.add-specificatin {
+		width: 100%;
+		text-align: center;
+		color: $theme-color;
+		font-size: $font-lg;
+	}
+</style>

+ 314 - 0
pages/goods/goods-type.vue

@@ -0,0 +1,314 @@
+<template>
+	<view class="goods-type-page">
+		<scroll-view scroll-y class="class-list">
+			<uni-swipe-action>
+				<uni-swipe-action-item :right-options="options" :index="index" v-for="(item, index) in goodsTypeList"
+					@click="swipeClick(index,item)" :key="item.goodsTypeId">
+					<view class="item" style="width: 100%;" >
+						<view class="name">{{ item.goodsTypeName }}</view>
+						<view class="pinned" style="transform: rotate(180deg);"
+							@click="changOrderKey(item.goodsTypeId,2)">
+							<u-icon v-if="index > 0" name="download" size="24" color="#6c6c6c"></u-icon>
+						</view>
+						<view class="up-move" @click="changOrderKey(item.goodsTypeId,1)">
+							<u-icon v-if="index > 0" name="arrow-upward" size="20" color="#6c6c6c"></u-icon>
+						</view>
+						<view class="edit" @click="handItemEdit(item)">
+							<u-icon name="edit-pen" size="20" color="#6c6c6c"></u-icon>
+						</view>
+					</view>
+				</uni-swipe-action-item>
+			</uni-swipe-action>
+		</scroll-view>
+
+		<u-modal :show="modalVisible" title="添加/编辑分类" closeOnClickOverlay showCancelButton @confirm="submit"
+			@close="closeModal" @cancel="closeModal">
+			<view class="container">
+				<view class="name">
+					<u-input inputAlign="left" border fontSize="14" v-model="goodsTypeName" placeholder="请输入分类名称"></u-input>
+				</view>
+				<view style="margin-top: 20rpx;">
+					<u-radio-group
+						v-model="type"
+						placement="row">
+						<u-radio activeColor="blue" :name="0" @change="radioChange" label="普通分类"></u-radio>
+						<u-radio activeColor="blue" :name="1" @change="radioChange" label="必点分类"></u-radio>
+					</u-radio-group>
+				</view>
+				
+				<view class="desc">
+					注:为避免显示不美观,分类名称不能超过14个字符。推荐4个字符。新建的分类排在最上面。必点分类每个菜单最多只能设置一个。
+				</view>
+				
+				
+			</view>
+		</u-modal>
+
+		<view class="footer-btns">
+			<view class="btn" @click="handAddClass">添加分类</view>
+		</view>
+	</view>
+</template>
+<script>
+	import {
+		mapState
+	} from 'vuex';
+	export default {
+		data() {
+			return {
+				goodsTypeUrl: '/goodsTypeManageApi/getGoodsTypeList',
+				changeOrderKeyUrl: '/goodsTypeManageApi/changeOrderKey',
+				updateUrl: '/goodsTypeManageApi/update',
+				deleteUrl: '/goodsTypeManageApi/deleteById',
+				modalVisible: false,
+				form: {},
+				goodsTypeList: [],
+				goodsTypeName: '',
+				goodsTypeId: '',
+				type: 0,
+				options: [
+				    {
+				        text: '删除',
+						style: {
+				            fontSize: '28rpx',
+							backgroundColor: '#FF0000'
+				        }
+				    }
+				],
+			}
+		},
+		computed: {
+			...mapState(['storeInfo'])
+		},
+		methods: {
+			radioChange(n) {
+				this.type = n
+			},
+			loadData() {
+				uni.showLoading({
+					title: '加载中'
+				});
+				var dateStr = (new Date()).getTime();
+				var sign = this.getSign('' + this.storeInfo.storeUserId + dateStr);
+				this.requst({
+					url: this.goodsTypeUrl,
+					data: {
+						timestamp: dateStr,
+						sign: sign,
+						storeId: this.storeInfo.storeId,
+						storeUserId: this.storeInfo.storeUserId
+					},
+					success: res => {
+						if (res.data.rtnBean.rtnCode == 0) {
+							this.goodsTypeList = res.data.list;
+						}
+						uni.hideLoading();
+					},
+					fail: (e) => {
+						console.log("接口调用失败:" + JSON.stringify(e));
+						uni.hideLoading();
+					}
+				})
+			},
+			changOrderKey(id, type) {
+				var dateStr = (new Date()).getTime();
+				var sign = this.getSign('' + this.storeInfo.storeUserId + dateStr);
+				this.requst({
+					url: this.changeOrderKeyUrl,
+					data: {
+						timestamp: dateStr,
+						sign: sign,
+						storeId: this.storeInfo.storeId,
+						storeUserId: this.storeInfo.storeUserId,
+						type: type,
+						id: id
+					},
+					success: res => {
+						if (res.data.rtnBean.rtnCode == 0) {
+							this.loadData();
+							// uni.setNavigationBarTitle({
+							//     title: this.info.storeName
+							// });
+						}
+					},
+					fail: (e) => {
+						console.log("接口调用失败:" + JSON.stringify(e));
+					}
+				})
+			},
+			handAddClass() {
+				this.modalVisible = true;
+			},
+			handItemEdit(item) {
+				this.modalVisible = true;
+				this.goodsTypeName = item.goodsTypeName;
+				this.goodsTypeId = item.goodsTypeId;
+				this.type = item.type;
+			},
+			closeModal() {
+				this.modalVisible = false;
+				this.goodsTypeName = '';
+				this.goodsTypeId = '';
+				this.type = 0
+			},
+			swipeClick (index, item) {
+			    let that = this
+				uni.showModal({
+					title: '提示',
+					content: '确定删除该分类吗?',
+					success: function(res) {
+						if (res.confirm) {
+							uni.showLoading({
+								title: '删除中'
+							});
+							var dateStr = (new Date()).getTime();
+							var sign = that.getSign('' + that.storeInfo.storeUserId + dateStr);
+							that.requst({
+								url: that.deleteUrl,
+								data: {
+									storeUserId: that.storeInfo.storeUserId,
+									storeId: that.storeInfo.storeId,
+									timestamp: dateStr,
+									sign: sign,
+									goodsTypeId: item.goodsTypeId
+								},
+								success: res => {
+									if (res.data.rtnBean.rtnCode == 0) {
+										uni.hideLoading();
+										uni.showToast({
+											title: "删除成功",
+											duration: 3000,
+											icon: 'none',
+										});
+										that.loadData();
+									} else {
+										uni.hideLoading();
+										if (res.data.rtnBean.rtnMsg != '') {
+											that.$api.msg(res.data.rtnBean.rtnMsg);
+										}
+									}
+									
+								},
+								fail: (e) => {
+									console.log("接口调用失败:" + JSON.stringify(e));
+									uni.hideLoading();
+								},
+							});
+						}
+					}
+				});
+			},
+			submit() {
+				
+				if (this.goodsTypeName == null || this.goodsTypeName == undefined || this.goodsTypeName == "") {
+					this.$api.msg("请输入分类名称");
+					return;
+				}
+				if (this.goodsTypeName.length > 14){
+					this.$api.msg("分类名称不能超过14个字符");
+					return;
+				}
+				if (this.type === null || this.type === ''){
+					this.$api.msg("请选择分类类型");
+					return;
+				}
+				uni.showLoading({
+					title: '保存中'
+				});
+				var dateStr = (new Date()).getTime();
+				var sign = this.getSign('' + this.storeInfo.storeUserId + dateStr);
+				this.requst({
+					url: this.updateUrl,
+					data: {
+						timestamp: dateStr,
+						sign: sign,
+						storeId: this.storeInfo.storeId,
+						storeUserId: this.storeInfo.storeUserId,
+						goodsTypeName: this.goodsTypeName,
+						goodsTypeId: this.goodsTypeId,
+						type: this.type
+					},
+					success: res => {
+						uni.hideLoading();
+						if (res.data.rtnBean.rtnCode == 0) {
+							this.loadData()
+							// uni.setNavigationBarTitle({
+							//     title: this.info.storeName
+							// });
+							this.modalVisible = false
+							this.goodsTypeId = ''
+							this.goodsTypeName = ''
+						} else {
+							if (res.data.rtnBean.rtnMsg != '') {
+								this.$api.msg(res.data.rtnBean.rtnMsg);
+							}
+						}
+						
+					},
+					fail: (e) => {
+						console.log("接口调用失败:" + JSON.stringify(e));
+						uni.hideLoading();
+					}
+				})
+			}
+		},
+		onLoad() {
+			this.loadData();
+		}
+	}
+</script>
+<style lang="scss" scoped>
+	.goods-type-page {
+		width: 100vw;
+		height: 100vh;
+		background-color: #f5f5f5;
+	}
+
+	.class-list {
+		height: calc(100vh - 120rpx - env(safe-area-inset-bottom));
+		height: calc(100vh - 120rpx - constant(safe-area-inset-bottom));
+
+		.item {
+			@include between;
+			background-color: #fff;
+			margin-bottom: 4rpx;
+			height: 80rpx;
+			padding: 0 20rpx;
+			box-sizing: border-box;
+			font-size: $font-base;
+		}
+
+		.name {
+			width: 50%;
+			margin-right: 40rpx;
+		}
+	}
+
+	.footer-btns {
+		width: 100vw;
+		@include flexCenter;
+		margin-top: 40rpx;
+
+		.btn {
+			width: 80vw;
+			height: 80rpx;
+			border-radius: 40rpx;
+			background-color: $theme-color;
+			color: #fff;
+			@include flexCenter;
+		}
+	}
+
+	.container {
+		.name {
+			border: 2rpx solid #ececec;
+			border-radius: 6rpx;
+		}
+		.desc {
+			font-size: $font-sm;
+			color: $uni-color-button;
+			margin-top: 40rpx;
+			margin-bottom: 5rpx;
+		}
+	}
+</style>

+ 530 - 0
pages/goods/goods.vue

@@ -0,0 +1,530 @@
+<template>
+    <view class="goods-page">
+        <view class="goods-cont">
+            <scroll-view scroll-y class="menus">
+                <view class="item" v-for="item in goodsList" :key="item.goodsTypeId" @click="handMenuChange(item)"
+                    :class="{ select: item.goodsTypeId == active }">
+                    <view class="new" v-if="item.children != null && item.children.filter(i => i.isNew).length > 0">上新</view>
+                    <view class="name">{{ item.goodsTypeName }}</view>
+                </view>
+            </scroll-view>
+            <scroll-view scroll-with-animation scroll-y class="list-wrap"  @scroll="asideScroll" :scroll-top="tabScrollTop">
+                <view class="class" v-for="item in goodsList" :key="item.goodsTypeId" :id="'goods' + item.goodsTypeId">
+                    <view class="title">{{ item.goodsTypeName }}</view>
+                    <view class="list">
+                        <view class="goods" v-for="(goods,index) in item.children" :key="goods.goodsId">
+                            <view class="cover">
+                                <image class="img" :src="goods.pictureUrl"></image>
+                            </view>
+                            <view class="cont">
+                                <view class="name">{{ goods.goodsName }}</view>
+                                <view class="desc">
+                                    月销 {{ goods.salesCount }} 库存 {{ goods.inventory }}
+                                </view>
+                                <view class="price-wrap">
+                                    <view class="price" v-if="goods.salePrice < goods.costPrice">¥{{ moneyConverter(goods.salePrice) }}</view>
+                                    <view class="costprice" v-if="goods.salePrice < goods.costPrice">¥{{ moneyConverter(goods.costPrice) }}</view>
+									<view class="price"  v-if="goods.salePrice >= goods.costPrice">¥{{ moneyConverter(goods.costPrice) }}</view>
+                                    <view class="old" v-if="goods.newGoodsFlg" @click="setNewGoods(goods)">取消新品</view>
+                                    <view class="new" v-if="!goods.newGoodsFlg" @click="setNewGoods(goods)">设置新品</view>
+                                </view>
+                                <view class="tools">
+                                    <view class="btn" @click="updateGoods(goods.goodsId)">编辑</view>
+                                    <view class="btn bgbtn" v-if="goods.status == 0" @click="updateStatus(goods.status,goods.goodsId)">上架</view>
+                                    <view class="btn" v-if="goods.status == 1" @click="updateStatus(goods.status,goods.goodsId)">下架</view>
+                                    <view class="btn" v-if="index!=0" @click="moveUp(goods.goodsId)">前移</view>
+									<view class="btn-red bgbtn-red" v-if="goods.status == 0" @click="deleteGoods(goods.goodsId)">删除</view>
+                                </view>
+                            </view>
+                        </view>
+                    </view>
+                </view>
+                <!-- <view class="empty-block" :style="{ height: emptyBlockHeight + 'px' }"></view> -->
+            </scroll-view>
+        </view>
+        <view class="footer-btns">
+            <view class="btn" @click="handChangeClass">分类管理</view>
+            <view class="btn" @click="updateGoods()">添加商品</view>
+        </view>
+		<!-- <u-picker :show="show" :columns="columns" keyName="label" @cancel="cancelNew" @confirm="confirmNewGoods"></u-picker> -->
+		<popupPickerView :list="columns" itemKey="id" itemName="label" ref="popupPickerView" @callback="confirmNewGoods" />
+    </view>
+</template>
+<script>
+import {mapState} from 'vuex';
+import popupPickerView from '../components/popup-picker-view.vue';
+export default {
+	components: {
+		popupPickerView
+	},
+    data () {
+        return {
+			getGoodsListUrl: '/storeGoodsManageApi/getStoreGoodsList',
+			moveUpUrl: '/storeGoodsManageApi/goodsMoveUp',
+			changeStatusUrl: '/storeGoodsManageApi/changeStatus',
+			setNewFlagUrl: '/storeGoodsManageApi/setNewFlag',
+			deleteUrl: '/storeGoodsManageApi/deleteGoods',
+            active: '0',
+            goodsList: [],
+			columns: [
+				[{
+					label: '7天',
+					id: 7
+				}, {
+					label: '15天',
+					id: 15
+				}, {
+					label: '30天',
+					id: 30
+				}]
+			],
+			show: false,
+			goodsId: '',
+            emptyBlockHeight: 0,
+            tabScrollTop: 0,
+            sizeCalcState: false,
+            changeState: true,
+			windowHeight: '',
+			tabbar: true,
+        }
+    },
+	computed: {
+		...mapState(['storeInfo'])
+	},
+    methods: {
+		handCheckGoodsType () {
+			this.$refs.popupPickerView.show()
+		},
+		loadData() {
+			uni.showLoading({
+				title: '加载中'
+			});
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign('' + this.storeInfo.storeUserId + dateStr);
+			this.requst({
+				url: this.getGoodsListUrl,
+				data: {
+					timestamp: dateStr,
+					sign: sign,
+					storeId: this.storeInfo.storeId,
+					storeUserId: this.storeInfo.storeUserId
+				},
+				success: res => {
+					if (res.data.rtnBean.rtnCode == 0) {
+						let obj = res.data.obj;
+						this.goodsList = res.data.list;
+						// uni.setNavigationBarTitle({
+						//     title: this.info.storeName
+						// });
+						setTimeout(()=>{
+							this.calcSize();
+						}, 300)
+					}
+					uni.hideLoading();
+				},
+				fail: (e) => {
+					console.log("接口调用失败:" + JSON.stringify(e));
+					uni.stopPullDownRefresh()
+					uni.hideLoading();
+				}
+			})
+		},
+		cancelNew() {
+			// 取消操作
+			this.goodsId = '';
+			this.show = false
+		},
+		updateStatus (status,goodsId) {
+			if (status == 0){
+				status = 1;
+			} else {
+				status = 0
+			}
+			uni.showLoading({
+				title: '加载中'
+			});
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign('' + this.storeInfo.storeUserId + dateStr);
+			this.requst({
+				url: this.changeStatusUrl,
+				data: {
+					timestamp: dateStr,
+					sign: sign,
+					storeId: this.storeInfo.storeId,
+					storeUserId: this.storeInfo.storeUserId,
+					status: status,
+					goodsId: goodsId
+				},
+				success: res => {
+					if (res.data.rtnBean.rtnCode == 0) {
+						this.loadData();
+						// uni.setNavigationBarTitle({
+						//     title: this.info.storeName
+						// });
+					}
+					uni.hideLoading();
+				},
+				fail: (e) => {
+					console.log("接口调用失败:" + JSON.stringify(e));
+					uni.hideLoading();
+				}
+			})
+		},
+		moveUp (goodsId) {
+			uni.showLoading({
+				title: '加载中'
+			});
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign('' + this.storeInfo.storeUserId + dateStr);
+			this.requst({
+				url: this.moveUpUrl,
+				data: {
+					timestamp: dateStr,
+					sign: sign,
+					storeId: this.storeInfo.storeId,
+					storeUserId: this.storeInfo.storeUserId,
+					goodsId: goodsId
+				},
+				success: res => {
+					if (res.data.rtnBean.rtnCode == 0) {
+						this.loadData();
+					}
+					uni.hideLoading();
+				},
+				fail: (e) => {
+					console.log("接口调用失败:" + JSON.stringify(e));
+					uni.hideLoading();
+				}
+			})
+		},
+		deleteGoods(goodsId) {
+			uni.showLoading({
+				title: '加载中'
+			});
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign('' + this.storeInfo.storeUserId + dateStr);
+			this.requst({
+				url: this.deleteUrl,
+				data: {
+					timestamp: dateStr,
+					sign: sign,
+					goodsId: goodsId,
+					storeUserId: this.storeInfo.storeUserId,
+				},
+				success: res => {
+					if (res.data.rtnBean.rtnCode == 0) {
+						this.loadData();
+					}
+					uni.hideLoading();
+				},
+				fail: (e) => {
+					console.log("接口调用失败:" + JSON.stringify(e));
+					uni.hideLoading();
+				}
+			})
+		},
+		confirmNewGoods(e) {
+			let dayCount = e.id;
+			
+			this.handDelete(dayCount);
+			
+		},
+		handDelete(dayCount) {
+			//设置新品
+			uni.showLoading({
+				title: '加载中'
+			});
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign('' + this.storeInfo.storeUserId + dateStr);
+			this.requst({
+				url: this.setNewFlagUrl,
+				data: {
+					timestamp: dateStr,
+					sign: sign,
+					storeId: this.storeInfo.storeId,
+					storeUserId: this.storeInfo.storeUserId,
+					goodsId: this.goodsId,
+					dayCount: dayCount || ''
+				},
+				success: res => {
+					if (res.data.rtnBean.rtnCode == 0) {
+						this.loadData();
+					}
+					this.show = false
+					
+					uni.hideLoading();
+				},
+				fail: (e) => {
+					console.log("接口调用失败:" + JSON.stringify(e));
+					uni.hideLoading();
+				}
+			})
+			
+		},
+		setNewGoods (goods) {
+			this.goodsId = goods.goodsId;
+			if (goods.newGoodsFlg != undefined && goods.newGoodsFlg == 1) {
+				this.handDelete();
+			} else {
+				this.$refs.popupPickerView.show()
+			}
+		},
+        handMenuChange (item) {
+            if (!this.sizeCalcState) {
+            	this.calcSize();
+            }
+            this.active = item.goodsTypeId;
+            let index = this.goodsList.findIndex(sitem => sitem.goodsTypeId === item.goodsTypeId);
+            this.tabScrollTop = this.goodsList[index].top;
+            this.changeState = false;
+        },
+        handChangeClass () {
+            uni.navigateTo({
+                url: '/pages/goods/goods-type'
+            });
+        },
+        updateGoods (id) {
+			if (id != null && id != undefined) {
+				uni.navigateTo({
+				    url: '/pages/goods/goods-edit?goodsId=' + id
+				});
+			} else {
+				uni.navigateTo({
+					url: '/pages/goods/goods-edit'
+				});
+			}
+        },
+        //右侧栏滚动
+        asideScroll(e) {
+        	if (!this.sizeCalcState) {
+        		this.calcSize();
+        	}
+        	let scrollTop = e.detail.scrollTop;
+        	let tabs = this.goodsList.filter(item => item.top <= scrollTop).reverse();
+        	if (this.changeState && tabs.length > 0) {
+        		this.active = tabs[0].goodsTypeId;
+        	}
+        	this.changeState = true;
+        },
+        //计算右侧栏每个tab的高度等信息
+        calcSize() {
+            // 获取视口高度
+            const wrap = uni.createSelectorQuery().in(this);
+            let wH = 0
+            wrap.select('.list-wrap').boundingClientRect(data => {
+                wH = data.height
+            }).exec();
+
+        	let h = 0;
+        	if(this.goodsList != '' || this.goodsList != null){
+        		this.goodsList.forEach((item, idx) => {
+        			let view = uni.createSelectorQuery().select("#goods" + item.goodsTypeId);
+        			view.fields({
+        				size: true
+        			}, data => {
+        				item.top = h;
+        				h += data.height;
+        				item.bottom = h;
+                        // 当遍历到最后一个分类商品时获取对应商品元素的top值 减去 视口高度 赋值给 商品列表底部高度补位元素
+                        // if (idx == this.goodsList.length - 1) {
+                        //     this.emptyBlockHeight = item.top - wH - 2
+                        // }
+        			}).exec();
+        		})
+        		this.sizeCalcState = true;
+        	} else {
+        		return
+        	}
+        },
+    },
+	onReady(){
+		setTimeout(()=>{
+		this.calcSize();
+			}, 300)
+	},
+    onShow () {
+		this.loadData()
+        // uni.$once('goods', goods => {
+        //     console.log('goods', goods);
+        // })
+		uni.getSystemInfo({
+			success: (res) => {
+				this.windowHeight = res.windowHeight;
+			}
+		});
+		uni.onWindowResize((res) => {
+			if (res.size.windowHeight < this.windowHeight) {
+				this.tabbar = false
+			} else {
+				this.tabbar = true
+			}
+		});
+		
+    },
+    onLoad () {
+		
+	}
+}
+</script>
+<style lang="scss" scoped>
+.goods-page {
+    background-color: #f5f5f5;
+    width: 100vw;
+    height: 100vh;
+}
+.footer-btns {
+    width: 100vw;
+    background-color: #fff;
+    @include between;
+    margin-bottom: env(safe-area-inset-bottom);
+    margin-bottom: constant(safe-area-inset-bottom);
+    .btn {
+        width: 49.5vw;
+        height: 80rpx;
+        background-color: $theme-color;
+        color: #fff;
+        @include flexCenter;
+    }
+}
+.goods-cont, .menus, .list-wrap {
+    height: calc(100vh - 80rpx - env(safe-area-inset-bottom));
+    height: calc(100vh - 80rpx - constant(safe-area-inset-bottom));
+}
+.goods-cont {
+    width: 100vw;
+    @include flexStart;
+    border-top: 2rpx solid #f5f5f5;
+    font-size: $font-base;
+}
+.menus {
+    width: 190rpx;
+    .item {
+        height: 140rpx;
+        position: relative;
+        padding: 0 20rpx;
+        box-sizing: border-box;
+        @include flexStart;
+        .new {
+            position: absolute;
+            right: 0;
+            top: 0;
+            width: 80rpx;
+            font-size: $font-sm;
+            color: #fff;
+            background-color: #ff5000;
+            @include flexCenter;
+            border-radius: 20rpx 0 20rpx 0;
+        }
+    }
+    .item.select {
+        background-color: #fff;
+    }
+}
+.list-wrap {
+    background-color: #fff;
+    width: calc(100vw - 190rpx);
+    padding: 20rpx;
+    box-sizing: border-box;
+    .title {
+        text-align: center;
+        font-weight: bold;
+        color: $title-color;
+        margin-bottom: 20rpx;
+    }
+    .list {
+        border-bottom: 2rpx solid #ececec;
+        margin-bottom: 20rpx;
+        padding-bottom: 20rpx;
+    }
+    .cover, .img {
+        width: 160rpx;
+        height: 160rpx;
+        border-radius: 10rpx;
+    }
+    .cover {
+        margin-right: 20rpx;
+    }
+    .goods {
+        @include flexStart(flex-start);
+        margin-bottom: 20rpx;
+    }
+    .cont {
+        width: calc(100% - 180rpx);
+    }
+    .name {
+        width: 100%;
+        height: 80rpx;
+        text-overflow: -o-ellipsis-lastline;
+        overflow: hidden;
+        text-overflow: ellipsis;
+        display: -webkit-box;
+        -webkit-line-clamp: 2;
+        -webkit-box-orient: vertical;
+        color: $text-color;
+        margin-bottom: 6rpx;
+    }
+    .desc {
+        color: $desc-color;
+        font-size: $font-sm;
+        margin-bottom: 6rpx;
+    }
+    .price-wrap {
+        @include flexStart;
+    }
+    .price {
+        margin-right: 20rpx;
+        color: #ff5000;
+    }
+	.costprice {
+		text-decoration: line-through;
+		margin-right: 20rpx;
+		color: $desc-color;
+	}
+    .new {
+        padding: 0 10rpx;
+		background-color: $desc-color;
+        color: #fff;
+        @include flexCenter;
+        border-radius: 10rpx;
+        font-size: $font-sm;
+    }
+	.old {
+        padding: 0 10rpx;
+        background-color: #ff5000;
+        color: #fff;
+        @include flexCenter;
+        border-radius: 10rpx;
+        font-size: $font-sm;
+    }
+    .tools {
+        @include flexStart;
+        margin-top: 16rpx;
+        .btn {
+            margin-right: 16rpx;
+            padding: 2rpx 10rpx;
+            border: 2rpx solid $theme-color;
+            color: $theme-color;
+            font-size: $font-sm;
+        }
+        .btn:last-child {
+            margin-right: 0;
+        }
+        .bgbtn {
+            background-color: $theme-color;
+            color: #fff;
+        }
+		.btn-red {
+		    margin-right: 16rpx;
+		    padding: 2rpx 10rpx;
+		    border: 2rpx solid red;
+		    color: $theme-color;
+		    font-size: $font-sm;
+			margin-right: 0;
+		}
+		.bgbtn-red {
+			background-color: red;
+			color: #fff;
+		}
+    }
+}
+</style>

+ 857 - 0
pages/index/index.vue

@@ -0,0 +1,857 @@
+<template>
+    <view class="index-page">
+        <u-sticky bgColor="#fff" class="tabs-wrap">
+            <u-tabs
+                class="tabs"
+                :list="tabs"
+                :itemStyle="itemStyle"
+                :is-scroll="false"
+                :current="params.active"
+                @change="handTabhange">
+            </u-tabs>
+        </u-sticky>
+		<view class="content-wrap" v-if="params.active == 0" style="padding-top: 20rpx;">
+		    <view class="list-scroll-wrap">
+		        <item v-for="item in orderList" @loadOrder="loadOrder" :item="item" :key="item.id"/>
+		    </view>
+		</view>
+        <view class="content-wrap" v-if="params.active == 1">
+            <view class="auto-taking-order">
+                <view class="label">自动接单</view>
+                <switch :checked="autoTaking" @change="handAutoTakingChange" color="#007AFF" class="switch" style="transform:scale(0.7)"></switch>
+            </view>
+            <view class="list-scroll-wrap">
+                <item v-for="item in orderList" @loadOrder="loadOrder" :item="item" :key="item.id"/>
+            </view>
+        </view>
+        <view class="content-wrap color-white" v-if="params.active == 2">
+            <view class="scan-code" @click="saoma">
+                <view class="label">扫码完成快速出餐</view>
+                <u-icon name="scan" size="24" color="#666"></u-icon>
+            </view>
+            <view class="take-food-wrap">
+                <view class="item" v-for="item in takeFoods" :key="item.date">
+                    <view class="item-date">
+                        {{ item.date }} ({{ item.list && item.list.length }})
+                    </view>
+                    <view class="item-food-no" v-for="no in item.list" :key="no.orderId">
+                        <view class="label">
+                            取餐号: {{ no.takeupCode }}
+                        </view>
+                        <view class="btn" @click="handComfirmTakeFoodOut(no)">确认出餐</view>
+                    </view>
+                </view>
+            </view>
+        </view>
+        <view class="content-wrap color-white h70" v-if="params.active == 3">
+            <view class="scan-code">
+				<input placeholder="请扫码或者输入取餐号" placeholder-class="input-placeholder" @input="inputChange" :value="searchValue" @blur="loadData" class="label"/>
+                <u-icon name="scan" size="24" color="#666" @click="saoma"></u-icon>
+            </view>
+            <view class="take-food-wrap">
+                <view class="item" v-for="item in takeFoods" :key="item.date">
+                    <view class="item-date">
+                        {{ item.date }} ({{ item.list && item.list.length }})
+                    </view>
+                    <view class="item-food-no" :style="{ backgroundImage: no.orderType == 4 ? 'linear-gradient(90.69deg, rgba(255,136,5,0.91) 0.28%,rgba(249,214,176,0.52) 0.28%,rgba(255,255,255,1) 98.81%);' : '' }" v-for="no in item.list" :key="no.orderId">
+                        <view class="label">
+                            <view class="top">
+                                {{ no.orderType == 3 ? '自取餐号' : '堂吃餐号' }}: {{ no.takeupCode }}
+                            </view>
+                            <view class="btm">
+                                {{ no.orderName }} {{ no.orderTel }}
+                            </view>
+                        </view>
+                        <view class="btn" @click="handComfirmPickOut(no)">确认自取</view>
+                    </view>
+                </view>
+            </view>
+        </view>
+		<view class="content">
+			<ithui-bullet-two v-if="notice.redirect === 1" v-model="show" :content="notice.content" :title="notice.title"
+				 buttonText="去参加" :closeOnClickOverlay="true"
+				@confirm="e=>{goToActivity()}" @cancel="e=>{confirm()}" @close="e=>{confirm()}" />
+			<ithui-bullet-two v-else v-model="show" :content="notice.content" :title="notice.title" :showButton="false"
+				:closeOnClickOverlay="true"
+				@cancel="e=>{confirm()}" @close="e=>{confirm()}" />
+		</view>
+		<view v-if="bindFlag">		
+			<button class="float-button" hover-class="hover-btn-bg" @click="navToBind()">
+				<!-- <uni-icons type="mic" size="40px" color="#fff" /> -->
+				<span style="color: #fff;">绑定</span>
+			</button>
+		</view>
+    </view>
+</template>
+<script>
+import item from '@/pages/components/order.vue'
+import ithuiBulletTwo from '@/components/ithui-bullet/ithui-bullet-two';
+import {mapState} from 'vuex';
+export default  {
+    name: 'Home',
+    components: {
+        item,
+		ithuiBulletTwo
+    },
+    data () {
+        return {
+			listUrl: '/storeOrderApi/getOrderListForStore',
+			updateUrl: '/storeOrderApi/updateStoreAutoGo',
+			autoGoUrl: '/storeOrderApi/getStoreAutoGo',
+			confirmUrl: '/storeOrderApi/confirmOrder',
+			pickUrl: '/storeOrderApi/selfPickUp',
+			checkCodeUrl: '/storeOrderApi/checkCode',
+			noticeUrl: '/storeNoticeApi/getNotice',
+			checkBindGzhUrl: '/storeUserLoginApi/checkWxBind',
+            params: {
+                active: 1,
+            },
+			autoTaking: false,
+            itemStyle: {
+                width: '25vw',
+                height: '80rpx',
+				fontSize: '28rpx',
+            },
+            tabs: [
+				{
+					name: '预约单',
+					value: 0,
+					badge: {
+					    value: 0,
+					}
+				},
+                {
+                    name: '待接单',
+					value: 10,
+                    badge: {
+                        value: 0,
+                    }
+                },
+                {
+                    name: '待出餐',
+					value: 20,
+                    badge: {
+                        value: 0,
+                    }
+                },
+                {
+                    name: '待自取',
+					value: 50,
+                    badge: {
+                        value: 0,
+                    }
+                }
+            ],
+            orderList: [],
+            takeFoods: [],
+			searchValue: '',
+			intervalId: null,
+			bindIntervalId: null,
+			handle: false,
+			show: false,
+			notice: {},
+			errorMsg: '',
+			bindFlag: true
+        }
+    },
+	computed: {
+		...mapState(['storeInfo'])
+	},
+	
+	created(){
+		this.bindDataRefreh();
+	   this.dataRefreh();
+	},
+	onHide() {
+	   // 销毁组件之前,清除计时器
+	   this.clear();
+	   this.bindClear();
+	},   
+    methods: {
+		navToBind(url) {
+			uni.navigateTo({
+			    url: `/pages/tools/bind`
+			})
+		},
+		// bindload (detail) {
+		// 	console.log(detail);
+		// },
+		// binderror (detail) {
+		// 	console.log(detail);
+		// },
+        // tab切换
+        handTabhange (e) {
+            this.params.active = e.index
+			this.orderList = [];
+			this.takeFoods = [];
+			this.loadData()
+        },
+        handSwiperChange (e) {
+            this.params.active = e.detail.current
+        },
+		loadOrder (msg) {
+			this.loadData(msg)
+		},
+        // 自动接单切换
+        handAutoTakingChange (e) {
+			if (this.handle) {
+				return;
+			}
+			this.handle = true;
+			this.autoTaking = e.detail.value
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign(dateStr);
+			this.requst({
+				url: this.updateUrl,
+				data: {
+					timestamp: dateStr,
+					sign: sign,
+					storeId: this.storeInfo.storeId,
+					autoGo: this.autoTaking ? 1 : 0
+				},
+				success: res => {
+					this.handle = false;
+					this.loadData()
+				},
+				fail: (e) => {
+					console.log("接口调用失败:" + JSON.stringify(e));
+					this.handle = false;
+				},
+			});
+        },
+		inputChange(e) {
+			this.searchValue = e.detail.value
+		},
+		// 定时刷新数据函数
+		dataRefreh() {
+			// 计时器正在进行中,退出函数
+			if (this.intervalId != null) {
+				return;
+			}
+			// 计时器为空,操作
+			this.intervalId = setInterval(() => {
+				this.loadData();
+			}, 60000);
+		}, 
+		// 停止定时器
+		clear() {
+			if (this.intervalId) {
+				clearInterval(this.intervalId);//清除计时器
+				this.intervalId = null; //设置为null
+			}
+		},
+		// 定时刷新数据函数
+		bindDataRefreh() {
+			// 计时器正在进行中,退出函数
+			if (this.bindIntervalId != null) {
+				return;
+			}
+			// 计时器为空,操作
+			this.bindIntervalId = setInterval(() => {
+				this.checkBindGzh();
+			}, 30000);
+		}, 
+		// 停止定时器
+		bindClear() {
+			if (this.bindIntervalId) {
+				clearInterval(this.bindIntervalId);//清除计时器
+				this.bindIntervalId = null; //设置为null
+			}
+		},
+		handComfirmTakeFoodOut (e) {
+			let that = this
+			uni.showModal({
+				title: '提示',
+				content: '是否将该订单标记为已出餐?',
+				success: function (r) {
+					if (r.confirm) {
+						that.handComfirmTakeFood(e);
+					} else if (r.cancel) {
+					}
+				}	
+			})
+		},
+        // 确认出餐
+        handComfirmTakeFood (e) {
+			if (this.handle) {
+				return;
+			}
+			this.handle = true;
+			uni.showLoading({
+				title: '加载中'
+			})
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign(dateStr);
+			this.requst({
+				url: this.confirmUrl,
+				data: {
+					timestamp: dateStr,
+					sign: sign,
+					orderId: e.orderId,
+				},
+				success: res => {
+					this.handle = false;
+					if (res.data.rtnBean.rtnCode == 0) {
+						uni.hideLoading();
+						if (res.data.rtnBean.rtnMsg != '') {
+							this.loadData(res.data.rtnBean.rtnMsg);
+						} else {
+							this.loadData('出餐成功');
+						}
+					} else {
+						uni.hideLoading();
+						if (res.data.rtnBean.rtnMsg != '') {
+							this.$api.msg(res.data.rtnBean.rtnMsg);
+						}
+					}
+				},
+				fail: (e) => {
+					uni.hideLoading()
+					console.log("接口调用失败:" + JSON.stringify(e));
+					this.handle = false;
+				},
+			});
+        },
+		handComfirmPickOut (e) {
+			let that = this
+			uni.showModal({
+				title: '提示',
+				content: '是否将该订单标记为已自取?',
+				success: function (r) {
+					if (r.confirm) {
+						that.handComfirmPick(e);
+					} else if (r.cancel) {
+					}
+				}	
+			})
+		},
+		handComfirmPick (e) {
+			if (this.handle) {
+				return;
+			}
+			this.handle = true;
+			uni.showLoading({
+				title: '加载中'
+			})
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign(dateStr);
+			this.requst({
+				url: this.pickUrl,
+				data: {
+					timestamp: dateStr,
+					sign: sign,
+					orderId: e.orderId,
+				},
+				success: res => {
+					
+					this.handle = false;
+					
+					if (res.data.rtnBean.rtnCode == 0) {
+						uni.hideLoading()
+						if (res.data.rtnBean.rtnMsg != '') {
+							this.loadData(res.data.rtnBean.rtnMsg);
+						} else {
+							this.loadData('自取成功');
+						}
+					} else {
+						uni.hideLoading()
+						if (res.data.rtnBean.rtnMsg != '') {
+							this.$api.msg(res.data.rtnBean.rtnMsg);
+						}
+					}
+					
+				},
+				fail: (e) => {
+					console.log("接口调用失败:" + JSON.stringify(e));
+					this.handle = false;
+					uni.hideLoading()
+				},
+			});
+		},
+		splitOrderList(list) {
+			if (!list || list.length == 0) {
+				return [];
+			}
+			let orderList = [];
+			list.map(item => {
+				const date = item.requireDateStr.slice(5,10);
+				if (orderList.length == 0) {
+					let arr = [];
+					arr.push(item)
+					const obj = {
+						date: date,
+						requireDate: item.requireDate,
+						list: arr
+					}
+					orderList.push(obj)
+				} else {
+					let obj = orderList.find(w => w.date == date);
+					if (obj) {
+						obj.list.push(item)
+					} else {
+						let arr = [];
+						arr.push(item)
+						const obj = {
+							date: date,
+							requireDate: item.requireDate,
+							list: arr
+						}
+						orderList.push(obj)
+					}
+				}
+			})
+			if (orderList.length > 0) {
+				orderList.sort((a, b) => {
+					return new Date(a.requireDate).getTime() - new Date(b.requireDate).getTime()
+				})
+				orderList.reverse()
+			}
+			return orderList;
+		},
+		loadData(msg) {
+			if (this.handle) {
+				return;
+			}
+			this.handle = true;
+			uni.showLoading({
+				title: '加载中'
+			})
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign(dateStr);
+			let status = 0;
+			let value = ""
+			if (this.params.active == 1) {
+				status = 10;
+			} else if (this.params.active == 2) {
+				status = 20;
+			} else if (this.params.active == 3) {
+				status = 50;
+				value = this.searchValue
+			} else {
+				status = 0;
+			}
+			this.requst({
+				url: this.listUrl,
+				data: {
+					timestamp: dateStr,
+					sign: sign,
+					storeId: this.storeInfo.storeId,
+					status: status,
+					searchValue: value
+				},
+				success: res => {
+					this.handle = false;
+					if (res.data.rtnBean.rtnCode == 0) {
+						if (this.params.active == 1 || this.params.active == 0) {
+							this.orderList = res.data.list;
+						} else{
+							this.takeFoods = this.splitOrderList(res.data.list);
+						}
+						const obj = res.data.obj;
+						if (obj.reserveNum > 99) {
+							this.tabs[0].badge.value = '...';
+						} else {
+							this.tabs[0].badge.value = obj.reserveNum;
+						}
+						if (obj.confirmedNum > 99) {
+							this.tabs[1].badge.value = '...';
+						} else {
+							this.tabs[1].badge.value = obj.confirmedNum;
+						}
+						if (obj.receivedNum > 99) {
+							this.tabs[2].badge.value = '...';
+						} else {
+							this.tabs[2].badge.value = obj.receivedNum;
+						}
+						if (obj.dinedNum > 99) {
+							this.tabs[3].badge.value = '...';
+						} else {
+							this.tabs[3].badge.value = obj.dinedNum;
+						}
+					} else {
+						if (res.data.rtnBean.rtnMsg != '') {
+							this.$api.msg(res.data.rtnBean.rtnMsg);
+						}
+					}
+					uni.stopPullDownRefresh()
+					uni.hideLoading()
+					if (msg != '' && msg != undefined && msg != null) {
+						uni.showToast({
+							icon: 'none',
+							title: msg,
+							duration: 1500
+						})
+					}
+					this.errorMsg = '';
+				},
+				fail: (e) => {
+					console.log("接口调用失败:" + JSON.stringify(e));
+					uni.stopPullDownRefresh()
+					this.handle = false;
+					uni.hideLoading();
+				},
+			});
+		},
+		getStoreAutoGo() {
+			uni.showLoading({
+				title: '加载中'
+			});
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign(dateStr);
+			this.requst({
+				url: this.autoGoUrl,
+				data: {
+					timestamp: dateStr,
+					sign: sign,
+					storeId: this.storeInfo.storeId
+				},
+				success: res => {
+					if (res.data.rtnBean.rtnCode == 0) {
+						uni.hideLoading();
+						this.autoTaking = res.data.obj.autoGo == 1;
+					} else {
+						uni.hideLoading();
+						if (res.data.rtnBean.rtnMsg != '') {
+							this.$api.msg(res.data.rtnBean.rtnMsg);
+						}
+					}
+					
+				},
+				fail: (e) => {
+					console.log("接口调用失败:" + JSON.stringify(e));
+					uni.hideLoading();
+				},
+			});
+		},
+		saoma() {
+			var that = this;
+			// 只允许从相机扫码
+			wx.scanCode({
+				onlyFromCamera: true,
+				scanType: 'qrCode',
+				success(res) {
+					that.checkDoneMealCode(encodeURIComponent(res.result));
+				},fail(res) {
+				}
+			})
+		},
+		// 判断是不是核销码类型
+		checkDoneMealCode(orderCode) {
+			if (this.handle) {
+				return;
+			}
+			this.handle = true;
+			uni.showLoading({
+				title: '加载中'
+			})
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign(decodeURIComponent(orderCode) + this.storeInfo.storeId + dateStr);
+			let status = 0;
+			if (this.params.active == 1) {
+				status = 10;
+			} else if (this.params.active == 2) {
+				status = 20;
+			} else if (this.params.active == 3) {
+				status = 30;
+			} else {
+				status = 0;
+			}
+			let that = this;
+			this.requst({
+				url: this.checkCodeUrl,
+				data: {
+					orderCode: decodeURIComponent(orderCode),
+					storeId: this.storeInfo.storeId,
+					status: status,
+					timestamp: dateStr,
+					sign: sign
+				},
+				success: res => {
+					this.handle = false;
+					uni.hideLoading()
+					if (res.data.rtnBean.rtnCode == 0) {
+						if (this.params.active == 1) {
+							uni.showModal({
+								title: '提示',
+								content: '是否将取餐号为' + res.data.obj.takeupCode + '的订单标记为已出餐?',
+								success: function (r) {
+									if (r.confirm) {
+										that.handComfirmTakeFood(res.data.obj)
+									} else if (r.cancel) {
+									}
+								}
+							});
+						} else if (this.params.active == 2) {
+							uni.showModal({
+								title: '提示',
+								content: '是否将取餐号为' + res.data.obj.takeupCode + '的订单标记为已自取?',
+								success: function (r) {
+									if (r.confirm) {
+										that.handComfirmPick(res.data.obj)
+									} else if (r.cancel) {
+									}
+								}
+							});
+						} 
+					} else {
+						if (res.data.rtnBean.rtnMsg != '') {
+							// setTimeout(() => {
+							// 	uni.showToast({
+							// 		icon: 'none',
+							// 		title: res.data.rtnBean.rtnMsg,
+							// 		duration: 2000
+							// 	})
+							// }, 1000)
+							this.errorMsg = res.data.rtnBean.rtnMsg;
+						}
+					}
+					
+				},
+				fail: (e) => {
+					uni.hideLoading()
+					console.log("接口调用失败:" + JSON.stringify(e));
+					this.handle = false;
+				},
+			})
+		},
+		getNotice() {
+			uni.showLoading({
+				title: '加载中'
+			});
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign('' + this.storeInfo.storeUserId + dateStr);
+			this.requst({
+				url: this.noticeUrl,
+				data: {
+					timestamp: dateStr,
+					sign: sign,
+					storeId: this.storeInfo.storeId,
+					storeUserId: this.storeInfo.storeUserId
+				},
+				success:res => {
+					if (res.data.rtnBean.rtnCode == 0) {
+						this.notice = res.data.obj
+						if (this.notice != '' && this.notice != null && this.notice != undefined){
+							this.show = true
+						}
+					}
+					uni.hideLoading();
+				},
+				fail: (e) => {
+					console.log("接口调用失败:" + JSON.stringify(e));
+					uni.hideLoading();
+				},
+			});
+		},
+		goToActivity() {
+			this.show = false
+			uni.navigateTo({
+				url: this.notice.url
+			})
+		},
+		confirm() {
+			this.show = false
+		},
+		// 判断用户是否绑定微信公众号
+		checkBindGzh() {
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign('' + this.storeInfo.storeUserId + dateStr);
+			this.requst({
+				url: this.checkBindGzhUrl,
+				data: {
+					timestamp: dateStr,
+					sign: sign,
+					openid2: this.storeInfo.openid2 || '',
+					storeUserId: this.storeInfo.storeUserId || ''
+				},
+				success:res => {
+					if (res.data.rtnBean.rtnCode == 0) {
+						this.bindFlag = res.data.obj
+						// if (!this.bindFlag) {
+						// 	this.bindClear();
+						// }
+					}
+				},
+				fail: (e) => {
+					console.log("接口调用失败:" + JSON.stringify(e));
+				},
+			});
+		}
+    },
+	onShow () {
+		this.getStoreAutoGo();
+		this.loadData(this.errorMsg);
+		this.checkBindGzh();
+	},
+    onLoad () {
+		this.getNotice();
+    },
+	onPullDownRefresh() {
+		this.loadData();
+	},
+	onReachBottom: function() {
+	}
+}
+</script>
+<style lang="scss" scoped>
+.fs14 {
+    font-size: $font-base;
+}
+.pd24 {
+    padding: 0 24rpx;
+    box-sizing: border-box;
+}
+.mlr24 {
+    margin: 0 24rpx;
+}
+.ptb10 {
+    padding: 10rpx 0;
+    box-sizing: border-box;
+}
+.mt10 {
+    margin-top: 10rpx;
+}
+.mt20 {
+    margin-top: 20rpx;
+}
+.mb10 {
+    margin-bottom: 10rpx;
+}
+.w100 {
+    width: 100%;
+}
+.borbtm {
+    border-bottom: 2rpx solid #ececec;
+}
+.text {
+    color: $text-color;
+}
+.desc {
+    color: $desc-color;
+}
+.index-page {
+    width: 100vw;
+    height: 100vh;
+    background-color: #f5f5f5;
+    ::-webkit-scrollbar {
+        display: none;
+    }
+}
+/deep/ .input-placeholder {
+	color: #666666
+}
+/deep/ .u-tabs__wrapper__nav__item__text.data-v-48634e29 {
+    font-size: 28rpx;
+    color: #606266;
+}
+.tabs-wrap {
+    padding-left: 5vw;
+    box-sizing: border-box;
+    background-color: #fff;
+	position: fixed;
+    .tabs {
+        margin-bottom: 20rpx;
+    }
+}
+.content-wrap {
+    height: calc(100vh - 80rpx);
+    .take-food-wrap {
+        background-color: #f5f5f5;
+    }
+
+    .scan-code {
+        @include between;
+        height: 60rpx;
+        margin: 0 20rpx 20rpx;
+        padding: 0 20rpx;
+        box-sizing: border-box;
+        background-color: #f5f5f5;
+        .label {
+            font-size: $font-base;
+            color: $icon-color;
+        }
+    }
+    .item-date {
+        height: 60rpx;
+        background-color: #f5f5f5;
+        @include flexStart;
+        font-size: $font-base;
+        padding: 0 20rpx;
+        box-sizing: border-box;
+    }
+    .take-food-wrap {
+        background-color: #fff;
+    }
+    .item-food-no {
+        padding: 0 20rpx;
+        box-sizing: border-box;
+        border-bottom: 2rpx solid #ececec;
+        height: 100rpx;
+        @include between;
+        font-size: $font-base;
+        color: $title-color;
+        font-weight: bold;
+        .btn {
+            font-weight: normal;
+            width: 140rpx;
+            height: 52rpx;
+            background-color: $base-color;
+            color: #fff;
+            border-radius: 26rpx;
+            @include flexCenter;
+            font-size: $font-sm;
+        }
+    }
+    .btm {
+        font-size: $font-sm;
+        color: #6c6c6c;
+    }
+}
+.auto-taking-order {
+    width: 100vw;
+    height: 80rpx;
+    @include between;
+    padding: 0 20rpx;
+    box-sizing: border-box;
+    /deep/ .u-switch {
+        transform: scale(.75) translateX(12rpx);
+    }
+}
+.list-scroll-wrap {
+    width: calc(100vw - 40rpx);
+    margin-left: 20rpx;
+    padding-bottom: 20rpx;
+}
+.color-white {
+    background-color: #fff;
+    padding-top: 20rpx;
+    box-sizing: border-box;
+}
+.h70 {
+    .item-food-no {
+        height: 140rpx;
+    }
+    .top {
+        margin-bottom: 10rpx;
+    }
+}
+.float-button {
+		position: fixed;
+		right: 15px;
+		bottom: 15px;
+		width: 130rpx;
+		height: 130rpx;
+		border-radius: 50%;
+		background: #1A78F6;
+		text-align: center;
+		display: flex;
+		align-items:center;
+		justify-content: center;
+		box-shadow: 4px 4px 15px rgba(0, 0, 0, 0.4);
+		z-index: 999;
+	}
+	.hover-btn-bg{
+		background-color: #1A78F6;
+		color: #fff;
+	}
+</style>

+ 271 - 0
pages/inspect/detail.vue

@@ -0,0 +1,271 @@
+<template>
+	<view class="inspect-detail">
+		<view class="summary" v-if="task">
+			<view class="head">
+				<view class="name">{{ task.planName || '巡检报告' }}</view>
+				<view class="pass" :class="{ good: task.passFlag == 1, bad: task.passFlag == 0 }">{{ passText }}</view>
+			</view>
+			<view class="date">巡检日期:{{ formatDate(task.taskDate) }}</view>
+			<view class="score-row">
+				<view class="score">
+					<text class="num">{{ fmtScore(task.totalScore) }}</text>
+					<text class="max">/{{ fmtScore(task.maxScore) }}</text>
+				</view>
+				<view class="inspector" v-if="task.inspectorName">巡检员:{{ task.inspectorName }}</view>
+			</view>
+			<view class="rect-link" v-if="task.rectId" @click="goRect(task.rectId)">
+				查看关联整改单 <text class="arrow">></text>
+			</view>
+		</view>
+
+		<view class="items" v-if="items.length">
+			<view class="section-title">逐项评分</view>
+			<view class="item" v-for="it in items" :key="it.taskItemId">
+				<view class="item-head">
+					<view class="left">
+						<text class="ind-name">{{ it.indicatorName }}</text>
+						<text class="type" :class="it.indicatorType == 1 ? 'auto' : 'manual'">{{ it.indicatorType == 1 ? '自动' : '人工' }}</text>
+					</view>
+					<view class="right">
+						<view class="pass" :class="{ good: it.passFlag == 1, bad: it.passFlag == 0 }">{{ it.passFlag == 1 ? '及格' : '不及格' }}</view>
+						<view class="score"><b>{{ fmtScore(it.score) }}</b>/{{ fmtScore(it.maxScore) }}</view>
+					</view>
+				</view>
+				<view class="deduction" v-if="it.deductionReason"><text class="label">扣分原因:</text>{{ it.deductionReason }}</view><view class="raw" v-if="it.rawValueText">原始数据:{{ it.rawValueText }}</view>
+				<view class="pics" v-if="picsOf(it).length">
+					<image v-for="(p, i) in picsOf(it)" :key="i" :src="p" mode="aspectFill" class="pic" @click="preview(it, i)" />
+				</view>
+				<view class="rmk" v-if="it.rmk">备注:{{ it.rmk }}</view>
+			</view>
+		</view>
+
+		<view class="empty" v-if="!task && !loading">
+			<text>报告不存在</text>
+		</view>
+	</view>
+</template>
+
+<script>
+import { mapState } from 'vuex';
+export default {
+	data() {
+		return {
+			apiUrl: '/inspectApi/getMyInspectDetail',
+			id: '',
+			task: null,
+			items: [],
+			loading: true
+		}
+	},
+	computed: {
+		...mapState(['storeInfo']),
+		passText() {
+			if (!this.task || this.task.status != 3) return '—'
+			return this.task.passFlag == 1 ? '及格' : '不及格'
+		}
+	},
+	methods: {
+		loadData() {
+			this.loading = true
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign('' + this.storeInfo.storeUserId + dateStr);
+			this.requst({
+				url: this.apiUrl,
+				data: {
+					id: this.id,
+					timestamp: dateStr,
+					sign: sign,
+					storeId: this.storeInfo.storeId,
+					storeUserId: this.storeInfo.storeUserId
+				},
+				success: res => {
+					if (res.data.rtnBean.rtnCode == 0) {
+						let obj = res.data.obj || {}
+						this.task = obj.task || null
+						this.items = obj.items || []
+					} else if (res.data.rtnBean.rtnMsg) {
+						this.$api.msg(res.data.rtnBean.rtnMsg)
+					}
+					this.loading = false
+				},
+				fail: () => {
+					this.loading = false
+				}
+			})
+		},
+		goRect(rectId) {
+			uni.navigateTo({ url: '/pages/rectification/detail?id=' + rectId })
+		},
+		picsOf(it) {
+			if (!it.pictureUrls) return []
+			return String(it.pictureUrls).split(',').filter(u => u && u.trim())
+		},
+		preview(it, index) {
+			let urls = this.picsOf(it)
+			uni.previewImage({ urls: urls, current: urls[index] })
+		},
+		formatDate(v) {
+			if (!v) return ''
+			let d = new Date(v)
+			let p = n => (n < 10 ? '0' + n : n)
+			return d.getFullYear() + '-' + p(d.getMonth() + 1) + '-' + p(d.getDate()) + ' ' + p(d.getHours()) + ':' + p(d.getMinutes())
+		},
+		fmtScore(v) {
+			return Number(v || 0)
+		}
+	},
+	onLoad(options) {
+		this.id = options.id || ''
+		this.loadData()
+	}
+}
+</script>
+
+<style lang="scss" scoped>
+.inspect-detail {
+	min-height: 100vh;
+	background-color: #f5f5f5;
+	padding-bottom: 40rpx;
+}
+.summary {
+	background: #fff;
+	padding: 30rpx;
+	border-radius: 0 0 20rpx 20rpx;
+	.head {
+		@include between;
+		margin-bottom: 16rpx;
+		.name {
+			font-size: 34rpx;
+			font-weight: bold;
+			color: $text-color;
+		}
+		.pass {
+			font-size: $font-sm;
+			padding: 4rpx 16rpx;
+			border-radius: 20rpx;
+			color: #909399;
+			background: #f4f4f5;
+			&.good {
+				color: #67c23a;
+				background: #f0f9eb;
+			}
+			&.bad {
+				color: #f56c6c;
+				background: #fef0f0;
+			}
+		}
+	}
+	.date {
+		color: $desc-color;
+		font-size: $font-sm;
+		margin-bottom: 16rpx;
+	}
+	.score-row {
+		@include between;
+		.score {
+			.num {
+				font-size: 52rpx;
+				font-weight: bold;
+				color: $theme-color;
+			}
+			.max {
+				color: $desc-color;
+				font-size: $font-sm;
+			}
+		}
+		.inspector {
+			color: $desc-color;
+			font-size: $font-sm;
+		}
+	}
+}
+.section-title {
+	font-size: 30rpx;
+	font-weight: bold;
+	color: $text-color;
+	padding: 30rpx 20rpx 10rpx;
+}
+.items {
+	padding: 0 20rpx;
+	.item {
+		background: #fff;
+		border-radius: 16rpx;
+		padding: 24rpx;
+		margin-bottom: 20rpx;
+		.item-head {
+			@include between;
+			margin-bottom: 12rpx;
+			.left {
+				@include flexStart;
+				.ind-name {
+					font-size: 30rpx;
+					font-weight: bold;
+					color: $text-color;
+				}
+				.type {
+					margin-left: 12rpx;
+					font-size: $font-sm;
+					padding: 2rpx 12rpx;
+					border-radius: 8rpx;
+					color: #409eff;
+					background: #ecf5ff;
+					&.manual {
+						color: #e6a23c;
+						background: #fdf6ec;
+					}
+				}
+			}
+			.right {
+				text-align: right;
+				.pass {
+					font-size: $font-sm;
+					margin-bottom: 6rpx;
+					&.good {
+						color: #67c23a;
+					}
+					&.bad {
+						color: #f56c6c;
+					}
+				}
+				.score {
+					color: $text-color;
+					b {
+						font-size: 34rpx;
+						color: $theme-color;
+					}
+				}
+			}
+		}
+		.raw {
+			color: $desc-color;
+			font-size: $font-base;
+			margin-bottom: 12rpx;
+		}
+		.pics {
+			display: flex;
+			flex-wrap: wrap;
+			margin-bottom: 12rpx;
+			.pic {
+				width: 160rpx;
+				height: 160rpx;
+				border-radius: 10rpx;
+				margin: 0 12rpx 12rpx 0;
+				background: #ececec;
+			}
+		}
+		.rmk {
+			color: $text-color;
+			font-size: $font-base;
+		}
+	}
+}
+.empty {
+	display: flex;
+	flex-direction: column;
+	align-items: center;
+	justify-content: center;
+	padding-top: 200rpx;
+	color: #cdcdcd;
+	font-size: $font-lg;
+}
+</style>

+ 239 - 0
pages/inspect/list.vue

@@ -0,0 +1,239 @@
+<template>
+	<view class="inspect-list">
+		<view class="list" v-if="records.length">
+			<view class="item" v-for="item in records" :key="item.taskId" @click="goDetail(item)">
+				<view class="top">
+					<view class="left">
+						<view class="name">{{ item.planName || '巡检任务' }}</view>
+						<text class="rect-tag" v-if="item.rectStatus === 1 || item.rectStatus === 2">待整改</text>
+						<text class="rect-tag done" v-if="item.rectStatus === 3">已整改</text>
+					</view>
+					<view class="pass" :class="{ good: item.passFlag == 1, bad: item.passFlag == 0 }">{{ passText(item) }}</view>
+				</view>
+				<view class="meta">
+					<text class="date">{{ formatDate(item.taskDate) }}</text>
+					<text class="inspector" v-if="item.inspectorName">巡检员:{{ item.inspectorName }}</text>
+				</view>
+				<view class="meta status-row">
+					<text class="status-tag" :style="statusStyle(item.status)">{{ statusText(item.status) }}</text>
+				</view>
+				<view class="score" v-if="item.totalScore != null">
+					综合得分:<b>{{ fmtScore(item.totalScore) }}</b>
+					<text class="max">/{{ fmtScore(item.maxScore) }}</text>
+				</view>
+				<view class="score" v-else>. . .</view>
+			</view>
+			<view class="loadmore" @click="loadMore" v-if="hasMore">{{ loading ? '加载中…' : '上拉加载更多' }}</view>
+			<view class="loadmore" v-else-if="records.length">已加载全部</view>
+		</view>
+		<view class="empty" v-else-if="!loading">
+			<image src="/static/imgs/empty/empty2x.png" class="empty-img" mode="widthFix"></image>
+			<text>暂无巡检记录</text>
+		</view>
+	</view>
+</template>
+<script>
+import { mapState } from 'vuex';
+export default {
+	data() {
+		return {
+			listUrl: '/inspectApi/getMyInspectList',
+			records: [],
+			currentPage: 1,
+			pageSize: 20,
+			total: 0,
+			loading: false
+		}
+	},
+	computed: {
+		...mapState(['storeInfo']),
+		hasMore() {
+			return this.total > this.records.length
+		}
+	},
+	methods: {
+		loadData() {
+			this.loading = true
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign('' + this.storeInfo.storeUserId + dateStr);
+			this.requst({
+				url: this.listUrl,
+				data: {
+					timestamp: dateStr,
+					sign: sign,
+					storeId: this.storeInfo.storeId,
+					storeUserId: this.storeInfo.storeUserId,
+					currentPage: this.currentPage,
+					pageSize: this.pageSize
+				},
+				success: res => {
+					if (res.data.rtnBean.rtnCode == 0) {
+						let list = (res.data.list || [])
+						let page = res.data.page || {}
+						this.total = page.totalRecords || list.length
+						this.records = this.currentPage === 1 ? list : this.records.concat(list)
+					} else if (res.data.rtnBean.rtnMsg) {
+						this.$api.msg(res.data.rtnBean.rtnMsg)
+					}
+					this.loading = false
+					uni.stopPullDownRefresh()
+				},
+				fail: () => {
+					this.loading = false
+					uni.stopPullDownRefresh()
+				}
+			})
+		},
+		loadMore() {
+			if (this.loading || !this.hasMore) return
+			this.currentPage++
+			this.loadData()
+		},
+		goDetail(item) {
+			uni.navigateTo({ url: '/pages/inspect/detail?id=' + item.taskId })
+		},
+		formatDate(v) {
+			if (!v) return ''
+			let d = new Date(v)
+			let p = n => (n < 10 ? '0' + n : n)
+			return d.getFullYear() + '-' + p(d.getMonth() + 1) + '-' + p(d.getDate())
+		},
+		fmtScore(v) {
+			return Number(v || 0)
+		},
+		passText(item) {
+			if (item.status != 3) return ''
+			return item.passFlag == 1 ? '及格' : '不及格'
+		},
+		statusText(v) {
+			return { 1: '待执行', 2: '执行中', 3: '已完成', 4: '已取消', 5: '已超期' }[v] || ''
+		},
+		statusStyle(v) {
+			var map = {
+				1: { color: '#909399', bg: '#f4f4f5' },
+				2: { color: '#409EFF', bg: '#ecf5ff' },
+				3: { color: '#67C23A', bg: '#f0f9eb' },
+				4: { color: '#F56C6C', bg: '#fef0f0' },
+				5: { color: '#E6A23C', bg: '#fdf6ec' }
+			}
+			var s = map[v] || { color: '#909399', bg: '#f4f4f5' }
+			return { color: s.color, background: s.bg }
+		}
+	},
+	onPullDownRefresh() {
+		this.currentPage = 1
+		this.records = []
+		this.loadData()
+	},
+	onReachBottom() {
+		this.loadMore()
+	},
+	onLoad() {
+		this.loadData()
+	}
+}
+</script>
+<style lang="scss" scoped>
+.inspect-list {
+	min-height: 100vh;
+	background-color: #f5f5f5;
+}
+.list {
+	padding: 20rpx;
+	.item {
+		background: #fff;
+		border-radius: 16rpx;
+		padding: 24rpx;
+		margin-bottom: 20rpx;
+		.top {
+			@include between;
+			margin-bottom: 12rpx;
+			.left {
+				@include flexStart;
+				flex: 1;
+				.name {
+					font-size: 32rpx;
+					font-weight: bold;
+					color: $text-color;
+				}
+				.rect-tag {
+					margin-left: 12rpx;
+					font-size: $font-sm;
+					padding: 2rpx 12rpx;
+					border-radius: 20rpx;
+					color: #E6A23C;
+					background: #fdf6ec;
+					&.done {
+						color: #67C23A;
+						background: #f0f9eb;
+					}
+				}
+			}
+			.pass {
+				flex-shrink: 0;
+				font-size: $font-sm;
+				padding: 4rpx 14rpx;
+				border-radius: 20rpx;
+				color: #909399;
+				background: #f4f4f5;
+				&.good {
+					color: #67c23a;
+					background: #f0f9eb;
+				}
+				&.bad {
+					color: #f56c6c;
+					background: #fef0f0;
+				}
+			}
+		}
+		.meta {
+			@include between;
+			color: $desc-color;
+			font-size: $font-sm;
+			margin-bottom: 6rpx;
+			.inspector {
+				text-align: right;
+			}
+			&.status-row {
+				margin-bottom: 10rpx;
+				.status-tag {
+					font-size: 22rpx;
+					padding: 2rpx 12rpx;
+					border-radius: 16rpx;
+				}
+			}
+		}
+		.score {
+			font-size: $font-base;
+			color: $text-color;
+			b {
+				font-size: 36rpx;
+				color: $theme-color;
+			}
+			.max {
+				color: $desc-color;
+				font-size: $font-sm;
+			}
+		}
+	}
+	.loadmore {
+		text-align: center;
+		color: $desc-color;
+		font-size: $font-sm;
+		padding: 20rpx 0;
+	}
+}
+.empty {
+	display: flex;
+	flex-direction: column;
+	align-items: center;
+	justify-content: center;
+	padding-top: 200rpx;
+	color: #cdcdcd;
+	font-size: $font-lg;
+	.empty-img {
+		width: 240rpx;
+		margin-bottom: 30rpx;
+	}
+}
+</style>

+ 392 - 0
pages/login/login.vue

@@ -0,0 +1,392 @@
+<template>
+	<view class="login-wrap">
+        <view class="account-login-wrap" :style="{ visibility: isCheckLogin ? 'visible' : 'hidden', zIndex: isCheckLogin ? 1 : -1 }">
+			<view style="margin-bottom: 60rpx;">
+				<uni-data-select
+			      v-model="areaId"
+			      :localdata="addressList"
+			      @change="change"
+				  placeholder="请选择校区"
+			    ></uni-data-select>
+			</view>
+            <u-input type="text" class="input" v-model="phoneNum" placeholder="请输入登录手机号"/>
+            <u-input type="text" class="input" password v-model="password" placeholder="请输入密码"/>
+        </view>
+        <button v-if="isCheckLogin" class="wechat-login" @click="pdwLogin" >
+            登录
+        </button>
+		<button v-if="!isCheckLogin && !isChecked" @click="phoneLogin()" class="wechat-login" >
+		    授权手机号登录
+		</button>
+        <button v-if="!isCheckLogin && isChecked" open-type="getPhoneNumber" @getphonenumber="authorization" class="wechat-login" >
+            授权手机号登录
+        </button>
+        <view class="argument">
+            <checkbox @click="isChecked = !isChecked" class="blue" :class="{ checked: isChecked }" :checked="isChecked ? true : false" style="transform:scale(0.8)"></checkbox>
+            <view class="title" @click="isChecked = !isChecked">请阅读并同意</view>
+            <text class="text-color" @click="navToWebView('/pages/webView/web_view?src=http://127.0.0.1:8081/fuwu.html')">《用户服务协议》</text>
+            <text class="text">和</text>
+            <text class="text-color" @click="navToWebView('/pages/webView/web_view?src=http://127.0.0.1:8081/yinsi.html')">《用户隐私条款》</text>
+        </view>
+        <view class="other-login-wrap">
+            <view v-if="!isCheckLogin" @click="changeClick" class="other-login" >
+                账号密码登录
+            </view>
+			<!-- <view v-else @click="changeClick" class="other-login" >
+                授权手机号登录
+            </view> -->
+			
+        </view>
+    </view>
+</template>
+
+<script>
+	import {
+		mapState
+	} from 'vuex';
+	import {
+		mapMutations
+	} from 'vuex';
+	export default {
+		data() {
+			return {
+				weixinLoginUrl: '/storeUserLoginApi/loginByWeixin',
+				loginurl: '/storeUserLoginApi/loginbyps', //手机号密码登录
+				addressUrl: (getApp().globalData.platApiUrl || getApp().globalData.apiUrl) + '/api/areaManage/getAreaListApp',
+				isChecked: false,
+                isCheckLogin: true,
+                params: {},
+				phoneNum: '',
+				password: '',
+				areaId: '',
+				addressList: [],
+				code: '',
+				list: []
+			}
+		},
+		computed:{
+			isPhone() { //手机号码校验正则
+				return /^1\d{10}$/.test(this.phone);
+			},
+		},
+		onShow() {
+			if (wx.canIUse("getUpdateManager")) {
+				const updateManager = wx.getUpdateManager();
+				updateManager.onCheckForUpdate(function(res) {
+					// console.log(res.hasUpdate);
+					// 请求完新版本信息的回调
+					if (res.hasUpdate) {
+						updateManager.onUpdateReady(function() {
+							wx.showModal({
+								title: "更新提示",
+								content: "新版本已经准备好,是否重启应用?",
+								success(res) {
+									if (res.confirm) {
+										// 新的版本已经下载好,调用 applyUpdate 应用新版本并重启
+										updateManager.applyUpdate();
+									}
+								}
+							});
+						});
+						updateManager.onUpdateFailed(function() {
+							// 新的版本下载失败
+							wx.showModal({
+								title: "已经有新版本了哟~",
+								content: "新版本已经上线啦~,请您删除当前小程序,重新搜索打开"
+							});
+						});
+					}
+				});
+			} else {
+				wx.showModal({
+					title: "提示",
+					content: "当前微信版本过低,无法使用该功能,请升级到最新微信版本后重试。"
+				});
+			}
+		},
+		methods: {
+			...mapMutations(['login']),
+			phoneLogin() {
+				if (!this.checkedFlag) {
+					uni.showToast({
+						title: '请先阅读并同意用户协议',
+						icon: 'none'
+					})
+					return
+				}
+			},
+			change(e) {
+				this.address = this.list.filter((i) => {
+					return i.areaId === e
+				})
+				this.areaId = this.address[0].areaId || ''
+				getApp().globalData.storeInfo = this.address[0];
+				this.code = this.address[0].code || ''
+				
+			},
+			changeClick() {
+				this.isCheckLogin = !this.isCheckLogin
+			},
+    //         linkOthers (v) {
+    //             console.log(v == 0 ? '服务协议' : '隐私条款');
+				// if (v == 0) {
+				// 	uni.navigateTo({
+				// 	    url: '/pages/tools/userAngument'
+				// 	})
+				// } else {
+				// 	uni.navigateTo({
+				// 	    url: '/pages/tools/privacyAngument'
+				// 	})
+				// }
+    //         },
+			navToWebView(url) {
+				uni.navigateTo({
+					url: url
+				})
+			},
+			pdwLogin () {
+				if (!this.isChecked) {
+					uni.showToast({
+						title: '请先阅读并同意用户协议',
+						icon: 'none'
+					})
+					return
+				}
+				if (this.areaId === '') {
+					uni.showToast({
+						title: '请选择校区',
+						icon: 'none'
+					})
+					return
+				}
+				if (this.phoneNum === '') {
+					uni.showToast({
+						title: '请填写手机号',
+						icon: 'none'
+					})
+					return
+				}
+				if (this.password === '') {
+					uni.showToast({
+						title: '请填写密码',
+						icon: 'none'
+					})
+					return
+				}
+				uni.showLoading({
+					title: "登录中"
+				});
+				let json = uni.getStorageSync('storeInfo')
+				if (json) {
+					uni.clearStorage()
+				}
+				var dateStr = (new Date()).getTime();
+				var sign = this.getSign(this.phoneNum + this.password + dateStr);
+				let that = this
+				this.requst({
+					url: that.loginurl,
+					data: {
+						phone: that.phoneNum,
+						password: that.password,
+						timestamp: dateStr,
+						sign: sign,
+						code: that.code,
+					},
+					success: res => {
+						if (res.data.rtnBean.rtnCode == 0) {
+							that.login(res.data.obj);
+							that.loginx(that.code,res.data.obj.storeUserId);
+							getApp().globalData.storeInfo = res.data.obj
+							uni.switchTab({
+								url: '/pages/index/index' //跳转到首页
+							});
+									
+								// }, 1500)
+							//}, 0);
+						} else {
+							uni.hideLoading();
+							if (res.data.rtnBean.rtnMsg != '') {
+								that.$api.msg(res.data.rtnBean.rtnMsg)
+								setTimeout(() => {
+									uni.hideToast();
+								}, 1500)
+								return;
+							}
+						}
+					},
+					fail: (e) => {
+						uni.showToast({
+							title: '失败,请稍后尝试!',
+							icon: 'none',
+							mask: 'true'
+						});
+						// this.close();
+						return;
+					},
+				})
+				
+			},
+            authorization(e) {
+				console.log('e :>> ', e);
+				if (e.detail.errMsg == "getPhoneNumber:ok") {
+					var dateStr = (new Date()).getTime();
+					var sign = this.getSign(e.detail.code + dateStr);
+					this.requst({
+						url: this.weixinLoginUrl, //服务器端地址
+						data: {
+							code: e.detail.code,
+							timestamp: dateStr,
+							sign: sign
+						},
+						success: (res) => {
+							if (res.data.rtnBean.rtnCode == 0) {
+								this.login(res.data.obj);
+								// setTimeout(() => {
+									// uni.showToast({
+									// 	icon: 'none',
+									// 	title: '登录成功',
+									// });
+									// setTimeout(() => {
+										uni.switchTab({
+											url: '/pages/index/index' //跳转到首页
+										});
+                                        console.log("########################");
+									// }, 1500)
+								// }, 0);
+							} else {
+								uni.hideLoading();
+								if (res.data.rtnBean.rtnMsg != '') {
+									this.$api.msg(res.data.rtnBean.rtnMsg)
+									setTimeout(() => {
+										uni.hideToast();
+									}, 1500)
+									return;
+								}
+							}
+						},
+						fail(res) {
+							uni.showToast({
+								title: '获取用户手机号失败,请稍后尝试!',
+								icon: 'none',
+								mask: 'true'
+							});
+							return;
+						}
+					});
+				}
+			},
+			// 加载校区
+			getAddress () {
+				uni.showLoading({
+					title: '加载中'
+				})
+				let that = this
+				this.requst({
+					url: this.addressUrl,
+					data: {},
+					success: res => {
+						if (res.data.rtnBean.rtnCode == 0) {
+							that.list = res.data.list
+							if (that.list.length) {
+								for (var i = 0; i < that.list.length; i++) {
+									that.list[i]
+									let address = {
+										value: that.list[i].areaId,
+										text: that.list[i].areaName
+									}
+									that.addressList.push(address)
+								}
+							}
+						}
+						uni.hideLoading()
+					},
+					fail: (e) => {
+						console.log("接口调用失败:" + JSON.stringify(e));
+						uni.hideLoading()
+					}
+				})
+			},
+			successLogin (res) {
+                // 处理用户信息
+                console.log('res', res);
+				// this.$refs.uToast.show({
+				// 	title: '登录成功',
+				// 	type: 'success'
+				// });
+				// setTimeout(() => {
+				// 	uni.redirectTo({
+				// 		url: "/pages/index/index",
+				// 	});
+				// }, 1000)
+			}
+		},
+		onReady () {
+			const token = uni.getStorageSync('token')
+			if (token) {
+				uni.redirectTo({
+					url: '/pages/index/index' //跳转到首页
+				})
+			}
+		},
+		onLoad() {
+			this.getAddress();
+		},
+		onHide() {
+			this.code = ''
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+    .login-wrap {
+        width: 100vw;
+        height: 100vh;
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+        justify-content: center;
+    }
+    .account-login-wrap {
+        width: 80%;
+        position: relative;
+        z-index: 1;
+        /deep/ .u-input {
+            margin-bottom: 60rpx;
+        }
+    }
+    .wechat-login {
+        width: 80%;
+        height: 80rpx;
+        border-radius: 80rpx;
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        background-color: #1A78F6;
+        color: #fff;
+        font-size: 32rpx;
+        margin-bottom: 20rpx;
+    }
+	.argument {
+		display: flex;
+		justify-content: center;
+		align-items: center;
+		font-size: 24rpx;
+		margin-bottom: 40rpx;
+        .text-color {
+            color: #1A78F6;
+        }
+	}
+    .other-login-wrap {
+        width: 100vw;
+        height: 60rpx;
+        position: fixed;
+        left: 0;
+		bottom: constant(safe-area-inset-bottom);
+		bottom: env(safe-area-inset-bottom);
+        @include flexCenter;
+        .other-login {
+            font-size: $font-base;
+            color: #0F40F5;
+        }
+    }
+</style>

+ 523 - 0
pages/mine/index.vue

@@ -0,0 +1,523 @@
+<template>
+    <view class="mine-page">
+        <view class="store-card">
+            <view class="cover">
+                <image class="avatr" widthFix :src="store.iconUrl" />
+            </view>
+            <view class="cont">
+                <view class="name" @click="handStoreInfo"><view class="text">{{ store.storeName }}</view><u-icon class="more" name="arrow-right" size="18" color="#666"></u-icon></view>
+                <view class="desc" @click="goToQrcodes">
+                    <text class="desc-text">{{ user.desc }}</text>
+                    <image class="qrcode" src="../../static/imgs/mine/qrcode.png" />
+                </view>
+            </view>
+            <view class="business-status" @click="businessToggleVisible = true">
+                <text class="status">{{ businessStatus }}</text>
+                <u-icon name="arrow-down" size="14" color="#1A78F6"></u-icon>
+            </view>
+        </view>
+        <view class="curr-day-manage-info">
+            <view class="title">今日经营信息</view>
+            <view class="manage-card">
+                <view class="item">
+                    <view class="count">¥{{(info.todayOrderFeeCount)}}</view>
+                    <view class="label">预计收入</view>
+                    <view class="compare" v-if="info.todayOrderFeeCount >= info.yesterdayOrderFeeCount">比昨日同时段 + {{Math.round((info.todayOrderFeeCount-info.yesterdayOrderFeeCount) * 100)/100}}</view>
+                    <view class="compare" v-else>比昨日同时段 - {{Math.round((info.yesterdayOrderFeeCount-info.todayOrderFeeCount) * 100)/100}}</view>
+                </view>
+                <view class="divider"></view>
+                <view class="item">
+                    <view class="count">{{info.todayOrderNum}}单</view>
+                    <view class="label">有效订单</view>
+                    <view class="compare" v-if="info.todayOrderNum >= info.yesterdayOrderNum">比昨日同时段 + {{info.todayOrderNum-info.yesterdayOrderNum}}单</view>
+                    <view class="compare" v-else>比昨日同时段 - {{info.yesterdayOrderNum-info.todayOrderNum}}单</view>
+                </view>
+            </view>
+        </view>
+        <view class="tools">
+            <view class="item" v-for="(item, idx) in tools" :key="item.name" @click="handItem(item, idx)">
+                <image :src="item.icon" class="cover" />
+                <view class="name">{{ item.name }}</view>
+            </view>
+        </view>
+        <u-modal :show="businessToggleVisible" title="店铺状态" closeOnClickOverlay @close="businessToggleVisible = false" :showConfirmButton="false">
+            <view class="container">
+                <view class="business-date" v-if="store.businessStartStr == null || store.businessStartStr == ''">营业时间: 00:00 - 23:59</view>
+                <view class="business-date" v-if="store.businessStartStr">营业时间: {{store.businessStartStr}} - {{store.businessEndStr}}</view>
+                <view class="basiness-list">
+                    <view class="item" v-for="item in businessList" :key="item.name" @click="handItemChange(item)">
+                        <view class="radio">
+                            <u-icon v-if="item.select" name="checkmark-circle-fill" size="28" color="#10B311"></u-icon>
+                        </view>
+                        <view class="cont">
+                            <view class="name">{{ item.name }}</view>
+                            <view class="desc">{{ item.desc }}</view>
+                        </view>
+                    </view>
+                </view>
+            </view>
+        </u-modal>
+		<view class="fixed-bottom"></view>
+    </view>
+</template>
+<script>
+import {mapState} from 'vuex';
+export default {
+    data () {
+        return {
+			storeUrl: '/storeManageApi/getStoreInfo',
+			infoUrl: '/storeManageApi/getBusinessInfo',
+			updateBusinessStatusUrl: '/storeManageApi/updateBusinessStatus',
+            businessToggleVisible: false,
+            user: {
+                id: '',
+                picture: require('../../static/tab-my.png'),
+                name: '',
+                desc: '店铺推广码'
+            },
+			store: {},
+			info: {
+				todayOrderFeeCount: 0,
+				todayOrderNum: 0,
+				yesterdayOrderFeeCount: 0,
+				yesterdayOrderNum: 0
+			},
+            businessStatus: '营业中',
+            tools: [
+     //            {
+     //                icon: require('../../static/imgs/mine/1.png'),
+     //                name: '结算管理',
+					// path: '../tools/settle'
+     //            },
+				{
+				    icon: require('../../static/imgs/mine/1.png'),
+				    name: '结算管理',
+					path: '../tools/settle-order'
+				},
+                {
+                    icon: require('../../static/imgs/mine/2.png'),
+                    name: '评价管理',
+					path: '../evaluate/index'
+                },
+                {
+                    icon: require('../../static/imgs/mine/3.png'),
+                    name: '店员管理',
+					path: '../store/clerk'
+                },
+                {
+                    icon: require('../../static/imgs/mine/4.png'),
+                    name: '小票打印机',
+					path: '../tools/print'
+                },
+                {
+                    icon: require('../../static/imgs/mine/5.png'),
+                    name: '配送管理',
+					path: '../store/distribution'
+                },
+                {
+                    icon: require('../../static/imgs/mine/6.png'),
+                    name: '商品管理',
+					path: '../goods/goods'
+                },
+                // {
+                //     icon: require('../../static/imgs/mine/7.png'),
+                //     name: '模板市场'
+                // },
+                {
+                    icon: require('../../static/imgs/mine/8.png'),
+                    name: '营销中心',
+                    path: '../activity/marketing'
+                },
+                {
+                    icon: require('../../static/imgs/mine/9.png'),
+                    name: '设置',
+					path: '../tools/setting'
+                },
+				{
+                    icon: require('../../static/imgs/mine/10.png'),
+                    name: '核销',
+					path: '../tools/hexiao_coupon'
+                },
+				{
+                    icon: require('../../static/imgs/mine/1.png'),
+                    name: '巡检结果',
+					path: '../inspect/list'
+                },
+				{
+                    icon: require('../../static/imgs/mine/2.png'),
+                    name: '整改',
+					path: '../rectification/list'
+                }
+            ],
+            businessList: [
+                {
+                    name: '正常营业',
+                    desc: '按营业时间自动开关店',
+                    select: false,
+					value: 1
+                },
+                {
+                    name: '紧急闭店',
+                    desc: '店铺紧急关闭,按开始营业时间自动恢复',
+                    select: false,
+					value: 0
+                },
+                {
+                    name: '暂停营业',
+                    desc: '店铺关闭直到手动恢复营业',
+                    select: false,
+					value: 2
+                }
+            ]
+        }
+    },
+	computed: {
+		...mapState(['storeInfo'])
+	},
+    methods: {
+		loadData () {
+			uni.showLoading({
+				title: '加载中'
+			});
+			// 获取店铺信息
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign('' + this.storeInfo.storeUserId + dateStr);
+			this.requst({
+				url: this.storeUrl,
+				data: {
+					storeUserId: this.storeInfo.storeUserId,
+					sign: sign,
+					timestamp: dateStr,
+					storeId: this.storeInfo.storeId
+				},
+				success: res => {
+					if (res.data.rtnBean.rtnCode == 0) {
+						this.store = res.data.obj;
+						
+						this.businessStatus = this.store.businessStatusName
+						
+						this.businessList.forEach(i => {
+							if (i.value == this.store.businessStatus){
+								i.select = true
+							}
+						})
+					}
+					uni.hideLoading();
+				},
+				fail: (e) => {
+					console.log("接口调用失败:" + JSON.stringify(e));
+					uni.hideLoading();
+				}
+			})
+			
+		},
+		
+		getInfo () {
+			uni.showLoading({
+				title: '加载中'
+			});
+			// 今日经营信息
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign('' + this.storeInfo.storeUserId + dateStr);
+			this.requst({
+				url: this.infoUrl,
+				data: {
+					storeUserId: this.storeInfo.storeUserId,
+					sign: sign,
+					timestamp: dateStr,
+					storeId: this.storeInfo.storeId
+				},
+				success: res => {
+					if (res.data.rtnBean.rtnCode == 0) {
+						this.info = res.data.obj;
+						
+					}
+					uni.hideLoading();
+				},
+				fail: (e) => {
+					console.log("接口调用失败:" + JSON.stringify(e));
+					uni.hideLoading();
+				}
+			})
+			
+		},
+        handStoreInfo () {
+            uni.navigateTo({
+                url: `/pages/store/index?data=` + encodeURIComponent(JSON.stringify(this.store))
+            });
+        },
+        handItem (item, idx) {
+            // const path = [
+            //     'settle',
+            //     'evaluation',
+            //     'clerk',
+            //     'print',
+            //     'distribution',
+            //     'goods',
+            //     'temp',
+            //     'marketing',
+            //     'setting',
+            // ]
+            // if (path[idx] == 'temp') {
+            //     uni.navigateTo({
+            //         url: '/pages/store/extensionCode?id=' + this.user.id
+            //     })
+            //     return
+            // }
+            uni.navigateTo({
+                url: item.path
+            });
+        },
+        handItemChange (item) {
+            if (item.select) {
+                return;
+            }
+			uni.showLoading({
+				title: '加载中'
+			});
+            this.businessList.forEach(i => i.select = false)
+            item.select = true
+            this.businessStatus = item.name
+            this.businessToggleVisible = false
+			
+			
+			// 修改店铺营业状态
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign('' + this.storeInfo.storeId + dateStr);
+			this.requst({
+				url: this.updateBusinessStatusUrl,
+				data: {
+					sign: sign,
+					timestamp: dateStr,
+					storeId: this.storeInfo.storeId,
+					businessStatus: item.value
+				},
+				success: res => {
+					if (res.data.rtnBean.rtnCode == 0) {
+						uni.showToast({
+							icon: 'none',
+							title: '修改成功',
+						});
+					}
+					uni.hideLoading();
+				},
+				fail: (e) => {
+					console.log("接口调用失败:" + JSON.stringify(e));
+					uni.hideLoading();
+				}
+			})
+			
+        },
+        goToQrcodes () {
+            uni.navigateTo({
+                url: '/pages/store/extensionCode'
+            });
+        }
+    },
+	onHide() {
+		this.businessList.forEach(i => i.select = false);
+	},
+    onLoad () {
+		// this.loadData();
+    },
+	onShow() {
+		this.loadData();
+		this.getInfo();
+	}
+}
+</script>
+<style lang="scss" scoped>
+.mine-page {
+    position: relative;
+    padding-bottom: 30rpx;
+    box-sizing: border-box;
+}
+.store-card {
+    width: 100vw;
+    @include between;
+    padding: 20rpx 20rpx 40rpx;
+    box-sizing: border-box;
+    border-bottom: 10rpx solid #f5f5f5;
+    .cover {
+        min-width: 120rpx;
+        width: 120rpx;
+        height: 120rpx;
+        margin-right: 10rpx;
+        // background-color: #D8E9FF;
+        border: 2rpx solid #ececec;
+        .avatr {
+            width: 120rpx;
+            height: 120rpx;
+        }
+    }
+    .business-status {
+        min-width: 160rpx;
+        width: 160rpx;
+        height: 60rpx;
+        border-radius: 10rpx;
+        margin-left: 20rpx;
+        color: $theme-color;
+        background-color: #D8E9FF;
+        @include flexCenter;
+        font-size: $font-sm;
+        border: 2rpx solid $theme-color;
+        padding: 0 10rpx;
+        box-sizing: border-box;
+    }
+    .status {
+        margin-right: 10rpx;
+        flex: 1;
+        text-align: center;
+    }
+    .cont {
+        width: calc(100vw - 40rpx - 130rpx - 180rpx);
+        height: 120rpx;
+        padding-top: 20rpx;
+        box-sizing: border-box;
+        .name {
+            font-size: 36rpx;
+            @include flexStart;
+        }
+        .text {
+            width: calc(100% - 50rpx);
+            overflow: hidden;
+            text-overflow: ellipsis;
+            white-space: nowrap;
+            word-break: break-all;
+        }
+        .more {
+            min-width: 40rpx;
+            margin-left: 10rpx;
+        }
+        .desc {
+            font-size: $font-base;
+            color: $icon-color;
+            margin-top: 10rpx;
+            @include flexStart;
+            .qrcode {
+                width: 32rpx;
+                height: 32rpx;
+                margin-left: 40rpx;
+            }
+        }
+    }
+}
+.curr-day-manage-info {
+    padding: 20rpx;
+    box-sizing: border-box;
+    .title {
+        font-size: $font-lg;
+        color: $text-color;
+        margin-bottom: 20rpx;
+    }
+    .manage-card {
+        background-color: $theme-color;
+        border-radius: 20rpx;
+        height: 208rpx;
+        @include flexCenter;
+        padding: 30rpx 40rpx;
+        .divider {
+            width: 4rpx;
+            height: 128rpx;
+            background-color: #fff;
+            margin: 0 30rpx;
+        }
+        .item {
+            flex: 1;
+            height: 100%;
+            color: #fff;
+            text-align: center;
+        }
+        .count {
+            font-size: 40rpx;
+            font-weight: bold;
+        }
+        .label {
+            font-size: $font-sm;
+            margin: 14rpx 0 10rpx;
+        }
+        .compare {
+            font-size: $font-base;
+        }
+    }
+}
+.tools {
+    display: flex;
+    flex-wrap: wrap;
+    align-items: flex-start;
+    justify-content: flex-start;
+    margin-top: 20rpx;
+    background-color: #fff;
+    .item {
+        width: 250rpx;
+        height: 200rpx;
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+        justify-content: center;
+        border-top: 2rpx solid #ececec;
+        .cover {
+            width: 72rpx;
+            height: 72rpx;
+            margin-bottom: 10rpx;
+        }
+        .name {
+            font-size: $font-lg;
+            color: $text-color;
+        }
+    }
+    .item:nth-child(2), .item:nth-child(5), .item:nth-child(8) {
+        border-left: 2rpx solid #ececec;
+        border-right: 2rpx solid #ececec;
+    }
+    .item:nth-child(7), .item:nth-child(8), .item:nth-child(9) {
+        border-bottom: 2rpx solid #ececec;
+    }
+    .item:nth-child(3n) {
+        margin-right: 0;
+    }
+}
+.container {
+    .business-date {
+        text-align: center;
+        margin-bottom: 20rpx;
+    }
+    .item {
+        @include flexCenter;
+        background-color: #FBF6F6;
+        margin-bottom: 20rpx;
+        padding: 20rpx 40rpx;
+        box-sizing: border-box;
+    }
+    .radio {
+        min-width: 60rpx;
+        width: 60rpx;
+        margin-right: 20rpx;
+    }
+    .cont {
+        width: calc(100% - 80rpx);
+    }
+    .name {
+        font-size: 36rpx;
+        font-weight: bold;
+        margin-bottom: 10rpx;
+    }
+    .desc {
+        font-size: $font-lg;
+        color: #111;
+    }
+}
+.bottom-content {
+	position: fixed;
+	bottom: 0;
+	left: 0;
+	width: 100%;
+	height: 300upx;
+	color: #fff;
+	display: flex;
+	justify-content: space-around;
+	align-items: center;
+	z-index: 2;
+}
+
+</style>

+ 277 - 0
pages/order/index.vue

@@ -0,0 +1,277 @@
+<template>
+    <view class="order-page">
+        <view class="tabs-wrap">
+            <view class="tabs">
+                <view class="tab" :class="{ curr: active == 1 }" @click="handTimeTabhange">近两日</view>
+                <view class="tab" :class="{ curr: active == 2 }" @click="handChangeDate">
+                    <view class="name" style="margin-right: 10rpx;">{{ params.searchDate || '选择日期' }}</view>
+                    <u-icon name="arrow-down" color="#fff" size="14" v-if="active == 2"></u-icon>
+                    <u-icon name="arrow-down" color="#0F40F5" size="14" v-if="active == 1"></u-icon>
+                </view>
+            </view>
+            <view class="search" @click="goToSearchPage">
+                <u-icon name="search" size="30" color="#6c6c6c"></u-icon>
+            </view>
+        </view>
+        <view class="tabs-switch-wrap">
+            <u-tabs
+                class="tabs"
+                :list="tabs"
+                :itemStyle="itemStyle"
+                :is-scroll="false"
+                :current="params.active"
+                @click="handTabhange"
+                @change="handTabhange">
+            </u-tabs>
+        </view>
+        <view class="scroll-list-wrap">
+            <view class="item" v-for="order in orderList" :key="order.date">
+                <view class="item-date">
+                    <view class="label">{{ order.date }}</view>
+                    <view class="collect" @click="handItemMoreCollect(order)">
+                        <view class="name">当日汇总</view>
+                        <u-icon name="arrow-right" size="14" color="#6c6c6c"></u-icon>
+                    </view>
+                </view>
+                <view class="scroll-list-content">
+                    <item v-for="item in order.list" :item="item" @loadOrder="loadOrder" :key="item.id" />
+                </view>
+            </view>
+        </view>
+		<uni-calendar 
+			ref="calendar"
+			:insert="pickVisible"
+			@confirm="orderDateConfirm"
+			@change="orderDateConfirm"
+			 />
+    </view>
+</template>
+<script>
+import item from '@/pages/components/order.vue'
+import {mapState} from 'vuex';
+export default {
+	components: {
+        item
+	},
+    data () {
+        return {
+			listUrl: '/storeOrderApi/getOrderListForStoreSearch',
+            active: 1,
+            pickVisible: false,
+            params: {
+                active: 0,
+                searchDate: ''
+            },
+            itemStyle: {
+                width: '25vw',
+                height: '80rpx'
+            },
+            tabs: [
+                {
+                    name: '全部'
+                },
+                {
+                    name: '进行中'
+                },
+                {
+                    name: '已完成'
+                },
+                {
+                    name: '退款'
+                }
+            ],
+            orderList: []
+        }
+    },
+	computed: {
+		...mapState(['storeInfo'])
+	},
+    methods: {
+        handChangeDate () {
+            this.$refs.calendar.open();
+        },
+        handTabhange (e) {
+            this.params.active = e.index
+			this.loadData();
+        },
+		loadOrder () {
+			this.loadData()
+		},
+		handTimeTabhange () {
+			this.active = 1;
+			this.params.searchDate = "";
+			this.loadData();
+		},
+        // 跳转至搜索页
+        goToSearchPage () {
+            uni.navigateTo({
+                url: '/pages/search/index'
+            })
+        },
+        // 当日汇总
+        handItemMoreCollect (item) {
+            uni.navigateTo({
+                url: '/pages/search/collect?day=' + item.requireDateStr
+            })
+        },
+        orderDateConfirm (e) {
+
+            this.params.searchDate = e.fulldate
+            this.pickVisible = false
+			this.active = 2
+			this.loadData()
+        },
+		loadData() {
+			uni.showLoading({
+				title: '加载中'
+			});
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign(dateStr);
+			let date1 = "";
+			if (this.active == 1) {
+				// 获取近两天
+				const date = new Date();
+				let month = ((date.getMonth() + 1) < 10 ? ("0" + (date.getMonth() + 1)) : (date.getMonth() + 1));
+				const startDate = date.getFullYear() + "-" + month + "-" + ((date.getDate() - 1) < 10 ? ("0" + (date.getDate() - 1)) : (date.getDate() - 1));
+				const endDate = date.getFullYear() + "-" + month + "-" + (date.getDate() < 10 ? ("0" + date.getDate()) : date.getDate());
+				date1 = startDate + "/" + endDate;
+			} else {
+				date1 = this.params.searchDate;
+			}
+			let that = this;
+			this.requst({
+				url: this.listUrl,
+				data: {
+					timestamp: dateStr,
+					sign: sign,
+					storeId: this.storeInfo.storeId,
+					status2: this.params.active,
+					dateStr: date1
+				},
+				success: res => {
+					if (res.data.rtnBean.rtnCode == 0) {
+						uni.hideLoading();
+						this.orderList = this.splitOrderList(res.data.list);
+					} else {
+						uni.hideLoading();
+						if (res.data.rtnBean.rtnMsg != '') {
+							this.$api.msg(res.data.rtnBean.rtnMsg);
+						}
+					}
+					
+				},
+				fail: (e) => {
+					console.log("接口调用失败:" + JSON.stringify(e));
+					uni.hideLoading();
+				},
+			})
+			
+		},
+		splitOrderList(list) {
+			if (!list || list.length == 0) {
+				return [];
+			}
+			let orderList = [];
+			list.map(item => {
+				const date = item.requireDateStr.slice(5,10);
+				if (orderList.length == 0) {
+					let arr = [];
+					arr.push(item)
+					const obj = {
+						date: date,
+						requireDate: item.requireDate,
+						requireDateStr: item.requireDateStr,
+						list: arr
+					}
+					orderList.push(obj)
+				} else {
+					let obj = orderList.find(w => w.date == date);
+					if (obj) {
+						obj.list.push(item)
+					} else {
+						let arr = [];
+						arr.push(item)
+						const obj = {
+							date: date,
+							requireDate: item.requireDate,
+							requireDateStr: item.requireDateStr,
+							list: arr
+						}
+						orderList.push(obj)
+					}
+				}
+			})
+			if (orderList.length > 0) {
+				orderList.sort((a, b) => {
+					return new Date(a.requireDate).getTime() - new Date(b.requireDate).getTime()
+				})
+				orderList.reverse()
+			}
+			return orderList;
+		}
+    },
+    onLoad () {
+    },
+	onShow () {
+		this.loadData();
+	}
+}
+</script>
+<style lang="scss" scoped>
+.order-page {
+    background-color: #f5f5f5;
+}
+.tabs-wrap {
+    position: relative;
+    height: 100rpx;
+    @include flexCenter;
+    .tabs {
+        width: 456rpx;
+        height: 60rpx;
+        @include flexCenter;
+        border-radius: 30rpx;
+        border: 2rpx solid $theme-color;
+        color: $theme-color;
+        overflow: hidden;
+        .tab {
+            flex: 1;
+            height: 60rpx;
+            @include flexCenter;
+            font-size: $font-base;
+        }
+        .tab.curr {
+            background-color: $theme-color;
+            color: #fff;
+        }
+    }
+    .search {
+        position: absolute;
+        right: 20rpx;
+        top: 0;
+        height: 100rpx;
+        @include flexCenter;
+    }
+}
+.tabs-switch-wrap {
+    width: 100vw;
+    height: 80rpx;
+    @include flexCenter;
+    background-color: #fff;
+}
+.scroll-list-wrap {
+    .item-date {
+        padding: 0 20rpx;
+        box-sizing: border-box;
+    }
+    .item-date, .collect {
+        @include between;
+        height: 80rpx;
+        font-size: $font-base;
+    }
+}
+.scroll-list-content {
+    width: calc(100vw - 40rpx);
+    margin-left: 20rpx;
+    padding-bottom: 2rpx;
+}
+</style>

+ 478 - 0
pages/rectification/detail.vue

@@ -0,0 +1,478 @@
+<template>
+	<view class="rect-detail">
+		<view class="card head-card" v-if="latest">
+			<view class="head">
+				<view class="left">
+					<text class="name">{{ latest.indicatorName }}</text>
+					<text class="round" v-if="rounds.length > 1">共{{ rounds.length }}轮</text>
+				</view>
+				<view class="tag" :style="{ color: statusMeta(latest.status).color, background: statusMeta(latest.status).bg }">
+					{{ statusMeta(latest.status).text }}
+				</view>
+			</view>
+			<view class="info">
+				<view class="row"><text class="label">巡检得分</text><text>{{ fmtScore(latest.inspectScore) }}(及格 {{ fmtScore(latest.passScore) }})</text></view>
+				<view class="row"><text class="label">整改截止</text><text>{{ formatDate(latest.deadlineDate, true) }}</text></view>
+			</view>
+			<view class="btn-row">
+				<button class="btn primary" v-if="latest.status == 1" @click="doStart(latest)">开始整改</button>
+			</view>
+		</view>
+
+		<!-- 时间轴 -->
+		<view class="card" v-if="rounds.length">
+			<view class="title">整改时间轴</view>
+			<view class="timeline">
+				<view class="tl-item" v-for="(rd, idx) in rounds" :key="rd.rectId">
+					<view class="tl-circle" :class="{ current: idx === rounds.length - 1 }"></view>
+					<view class="tl-body">
+						<view class="tl-title">
+							第{{ rd.rectRound }}轮整改
+							<text class="tl-tag" :style="{ color: statusMeta(rd.status).color, background: statusMeta(rd.status).bg }">{{ statusMeta(rd.status).text }}</text>
+						</view>
+						<view class="tl-line">下发整改通知 {{ formatDate(rd.createDate, true) }}</view>
+						<view class="tl-line" v-if="rd.noticeContent">通知内容:{{ rd.noticeContent }}</view>
+						<view class="tl-sub" v-if="rd.feedbackDate">
+							<text class="sub-icon">门店反馈</text>
+							<view class="sub-content">{{ rd.feedbackContent }}</view>
+							<view class="pics" v-if="picsOf(rd.feedbackPictures).length">
+								<image v-for="(u, i) in picsOf(rd.feedbackPictures)" :key="i" :src="u" mode="aspectFill" class="imgR" @click="preview(picsOf(rd.feedbackPictures), i)" />
+							</view>
+							<view class="tl-line">反馈时间:{{ formatDate(rd.feedbackDate, true) }}</view>
+						</view>
+						<view class="tl-sub recheck" v-if="rd.recheckDate">
+							<text class="sub-icon">巡检员复检</text>
+							<view class="tl-line">复检得分:{{ fmtScore(rd.recheckScore) }}</view>
+							<view class="tl-line" v-if="rd.recheckRmk">复检备注:{{ rd.recheckRmk }}</view>
+							<view class="tl-line">复检时间:{{ formatDate(rd.recheckDate, true) }}</view>
+						</view>
+					</view>
+				</view>
+			</view>
+		</view>
+
+		<!-- 提交整改反馈(仅整改中) -->
+		<view class="card form" v-if="latest && latest.status == 2">
+			<view class="title">提交整改反馈</view>
+			<textarea class="textarea" v-model="content" placeholder="请填写整改说明" :maxlength="500" />
+			<view class="pics">
+				<view class="pic" v-for="(p, i) in pics" :key="i">
+					<image :src="p.local" mode="aspectFill" class="img" @click="preview(pics.map(x => x.local), i)"></image>
+					<view class="del" @click="delPic(i)">×</view>
+				</view>
+				<view class="pic add" v-if="pics.length < 5" @click="choosePic">
+					<view class="plus">+</view>
+					<text>{{ pics.length }}/5</text>
+				</view>
+			</view>
+			<button class="btn primary submit" @click="doSubmit">提交反馈</button>
+		</view>
+
+		<view class="empty" v-if="!rounds.length && !loading">记录不存在</view>
+	</view>
+</template>
+
+<script>
+import { mapState } from 'vuex';
+export default {
+	data() {
+		return {
+			getUrl: '/rectificationApi/getRectDetail',
+			submitUrl: '/rectificationApi/submitRect',
+			startUrl: '/rectificationApi/startRect',
+			uploadUrl: '/rectificationApi/uploadPicture',
+			id: '',
+			rounds: [],
+			loading: true,
+			content: '',
+			pics: []   // {local, url}
+		}
+	},
+	computed: {
+		...mapState(['storeInfo']),
+		latest() {
+			return this.rounds.length ? this.rounds[this.rounds.length - 1] : null
+		}
+	},
+	methods: {
+		statusMeta(v) {
+			var map = {
+				1: { text: '待整改', color: '#E6A23C', bg: '#fdf6ec' },
+				2: { text: '整改中', color: '#409EFF', bg: '#ecf5ff' },
+				3: { text: '待复检', color: '#9c27b0', bg: '#f5eefb' },
+				4: { text: '复检通过', color: '#67C23A', bg: '#f0f9eb' },
+				5: { text: '复检不通过', color: '#F56C6C', bg: '#fef0f0' },
+				6: { text: '已超期', color: '#909399', bg: '#f4f4f5' },
+				7: { text: '最终不通过', color: '#F56C6C', bg: '#fef0f0' }
+			}
+			return map[v] || { text: '未知', color: '#666', bg: '#f4f4f5' }
+		},
+		loadData() {
+			this.loading = true
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign('' + this.storeInfo.storeUserId + dateStr);
+			this.requst({
+				url: this.getUrl,
+				data: {
+					id: this.id,
+					timestamp: dateStr,
+					sign: sign,
+					storeId: this.storeInfo.storeId,
+					storeUserId: this.storeInfo.storeUserId
+				},
+				success: res => {
+					if (res.data.rtnBean.rtnCode == 0) {
+						this.rounds = (res.data.obj && res.data.obj.rounds) || []
+						if (this.latest) this.content = this.latest.feedbackContent || ''
+					} else if (res.data.rtnBean.rtnMsg) {
+						this.$api.msg(res.data.rtnBean.rtnMsg)
+					}
+					this.loading = false
+				},
+				fail: () => {
+					this.loading = false
+				}
+			})
+		},
+		doStart(rd) {
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign('' + this.storeInfo.storeUserId + dateStr);
+			this.requst({
+				url: this.startUrl,
+				data: {
+					id: rd.rectId,
+					timestamp: dateStr,
+					sign: sign,
+					storeId: this.storeInfo.storeId,
+					storeUserId: this.storeInfo.storeUserId
+				},
+				success: res => {
+					if (res.data.rtnBean.rtnCode == 0) {
+						this.$api.msg('已开始整改')
+						this.loadData()
+					} else if (res.data.rtnBean.rtnMsg) {
+						this.$api.msg(res.data.rtnBean.rtnMsg)
+					}
+				}
+			})
+		},
+		choosePic() {
+			let remain = 5 - this.pics.length
+			if (remain <= 0) return
+			uni.chooseImage({
+				count: remain,
+				sizeType: ['compressed'],
+				success: res => {
+					res.tempFiles.forEach(f => { if (f.size > 5 * 1024 * 1024) { this.$api.msg('图片不能超过5MB'); return } this.uploadPic(f.path) })
+				}
+			})
+		},
+		uploadPic(filePath) {
+			uni.showLoading({ title: '上传中' })
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign('' + this.storeInfo.storeUserId + dateStr);
+			var uploadUrl = this.uploadUrl
+			var baseUrl = getApp().globalData.apiUrl
+			if (uploadUrl.indexOf('/api/') == -1) {
+				if (baseUrl.indexOf('127.0.0.1') != -1 || baseUrl.indexOf('localhost') != -1) {
+					uploadUrl = 'a1' + uploadUrl
+				} else {
+					var json = getApp().globalData.storeInfo
+					if (json) uploadUrl = json.code + uploadUrl
+				}
+			}
+			if (uploadUrl.indexOf('http') != 0) {
+				if (uploadUrl.charAt(0) == '/') uploadUrl = uploadUrl.substring(1)
+				uploadUrl = baseUrl + '/' + uploadUrl
+			}
+			uni.uploadFile({
+				url: uploadUrl,
+				filePath: filePath,
+				name: 'file',
+				formData: {
+					timestamp: dateStr,
+					sign: sign,
+					storeId: this.storeInfo.storeId,
+					storeUserId: this.storeInfo.storeUserId
+				},
+				success: res => {
+					try {
+						var data = JSON.parse(res.data)
+						if (data.rtnBean.rtnCode == 0 && data.obj) {
+							this.pics.push({ local: filePath, url: data.obj })
+						} else {
+							this.$api.msg((data.rtnBean && data.rtnBean.rtnMsg) || '上传失败')
+						}
+					} catch(e) {
+						this.$api.msg('上传失败,请检查网络')
+					}
+					uni.hideLoading()
+				},
+				fail: () => {
+					uni.hideLoading()
+					this.$api.msg('上传失败')
+				}
+			})
+		},
+		delPic(i) {
+			this.pics.splice(i, 1)
+		},
+		picsOf(str) {
+			if (!str) return []
+			return String(str).split(',').filter(u => u && u.trim())
+		},
+		preview(urls, i) {
+			uni.previewImage({ urls: urls, current: urls[i] })
+		},
+		doSubmit() {
+			if (this.latest && this.latest.rectRound >= 3 && this.latest.status == 5) {
+				return this.$api.msg('第3轮复检不通过,无法再次提交')
+			}
+			if (!this.latest) return
+			if (!this.content || !String(this.content).trim()) {
+				return this.$api.msg('请填写整改说明')
+			}
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign('' + this.storeInfo.storeUserId + dateStr);
+			let urls = this.pics.map(p => p.url)
+			this.requst({
+				url: this.submitUrl,
+				data: {
+					id: this.latest.rectId,
+					timestamp: dateStr,
+					sign: sign,
+					storeId: this.storeInfo.storeId,
+					storeUserId: this.storeInfo.storeUserId,
+					feedbackContent: String(this.content).trim(),
+					feedbackPictures: urls.join(',')
+				},
+				success: res => {
+					if (res.data.rtnBean.rtnCode == 0) {
+						this.$api.msg('已提交,等待复检')
+						this.pics = []
+						this.content = ''
+						this.loadData()
+					} else if (res.data.rtnBean.rtnMsg) {
+						this.$api.msg(res.data.rtnBean.rtnMsg)
+					}
+				},
+				fail: () => {}
+			})
+		},
+		formatDate(v, withTime) {
+			if (!v) return ''
+			let d = new Date(v)
+			let p = n => (n < 10 ? '0' + n : n)
+			let s = d.getFullYear() + '-' + p(d.getMonth() + 1) + '-' + p(d.getDate())
+			if (withTime) s += ' ' + p(d.getHours()) + ':' + p(d.getMinutes())
+			return s
+		},
+		fmtScore(v) {
+			return Number(v || 0)
+		}
+	},
+	onLoad(options) {
+		this.id = options.id || ''
+		this.loadData()
+	}
+}
+</script>
+
+<style lang="scss" scoped>
+.rect-detail {
+	min-height: 100vh;
+	background-color: #f5f5f5;
+	padding: 20rpx;
+	box-sizing: border-box;
+}
+.card {
+	background: #fff;
+	border-radius: 16rpx;
+	padding: 24rpx;
+	margin-bottom: 20rpx;
+	.head {
+		@include between;
+		margin-bottom: 16rpx;
+		.left {
+			@include flexStart;
+			.name {
+				font-size: 32rpx;
+				font-weight: bold;
+				color: $text-color;
+			}
+			.round {
+				margin-left: 12rpx;
+				font-size: $font-sm;
+				color: $theme-color;
+				padding: 2rpx 12rpx;
+				border: 2rpx solid $theme-color;
+				border-radius: 20rpx;
+			}
+		}
+		.tag {
+			font-size: $font-sm;
+			padding: 4rpx 16rpx;
+			border-radius: 20rpx;
+		}
+	}
+	.title {
+		font-size: 30rpx;
+		font-weight: bold;
+		color: $text-color;
+		margin-bottom: 16rpx;
+	}
+	.row {
+		@include flexStart;
+		color: $text-color;
+		font-size: $font-base;
+		padding: 8rpx 0;
+		.label {
+			min-width: 130rpx;
+			color: $desc-color;
+		}
+	}
+	.btn {
+		margin-top: 20rpx;
+		height: 76rpx;
+		line-height: 76rpx;
+		font-size: $font-lg;
+		color: #fff;
+		background: $theme-color;
+		border: none;
+		border-radius: 38rpx;
+	}
+}
+/* 时间轴 */
+.timeline {
+	padding-left: 8rpx;
+	.tl-item {
+		position: relative;
+		padding-left: 40rpx;
+		padding-bottom: 30rpx;
+		border-left: 2rpx solid #e5e5e5;
+		&:last-child {
+			border-left-color: transparent;
+		}
+		.tl-circle {
+			position: absolute;
+			left: -12rpx;
+			top: 4rpx;
+			width: 22rpx;
+			height: 22rpx;
+			border-radius: 50%;
+			background: #c8c9cc;
+			&.current {
+				background: $theme-color;
+			}
+		}
+		.tl-title {
+			font-size: $font-lg;
+			font-weight: bold;
+			color: $text-color;
+			margin-bottom: 8rpx;
+			.tl-tag {
+				margin-left: 12rpx;
+				font-size: $font-sm;
+				font-weight: normal;
+				padding: 2rpx 12rpx;
+				border-radius: 16rpx;
+			}
+		}
+		.tl-line {
+			color: $icon-color;
+			font-size: $font-base;
+			padding: 2rpx 0;
+		}
+		.tl-sub {
+			background: #f7f7f9;
+			border-radius: 10rpx;
+			padding: 16rpx;
+			margin-top: 10rpx;
+			.sub-icon {
+				font-size: $font-sm;
+				color: $theme-color;
+			}
+			.sub-content {
+				color: $text-color;
+				font-size: $font-base;
+				padding: 8rpx 0;
+			}
+			&.recheck {
+				margin-top: 8rpx;
+			}
+		}
+	}
+}
+/* 图片 */
+.pics {
+	display: flex;
+	flex-wrap: wrap;
+	margin-top: 16rpx;
+	.pic {
+		position: relative;
+		width: 160rpx;
+		height: 160rpx;
+		border-radius: 10rpx;
+		margin: 0 14rpx 14rpx 0;
+		overflow: hidden;
+		.img {
+			width: 100%;
+			height: 100%;
+			background: #ececec;
+		}
+		.del {
+			position: absolute;
+			top: 0;
+			right: 0;
+			width: 40rpx;
+			height: 40rpx;
+			line-height: 36rpx;
+			text-align: center;
+			background: rgba(0, 0, 0, 0.5);
+			color: #fff;
+			font-size: 30rpx;
+		}
+		&.add {
+			border: 2rpx dashed #ccc;
+			display: flex;
+			flex-direction: column;
+			align-items: center;
+			justify-content: center;
+			color: #bbb;
+			font-size: $font-sm;
+			.plus {
+				font-size: 50rpx;
+				line-height: 50rpx;
+			}
+		}
+	}
+	.imgR {
+		width: 160rpx;
+		height: 160rpx;
+		border-radius: 10rpx;
+		margin: 0 14rpx 14rpx 0;
+		background: #ececec;
+	}
+}
+/* 提交表单 */
+.form {
+	.textarea {
+		width: 100%;
+		height: 200rpx;
+		background: #f7f7f7;
+		border-radius: 10rpx;
+		padding: 16rpx;
+		box-sizing: border-box;
+		font-size: $font-base;
+	}
+}
+.empty {
+	display: flex;
+	flex-direction: column;
+	align-items: center;
+	justify-content: center;
+	padding-top: 200rpx;
+	color: #cdcdcd;
+	font-size: $font-lg;
+}
+</style>

+ 256 - 0
pages/rectification/list.vue

@@ -0,0 +1,256 @@
+<template>
+	<view class="rect-list">
+		<view class="filter-tabs">
+			<view v-for="(t, idx) in tabs" :key="t.label" class="tab" :class="{ active: activeIdx === idx }" @click="switchTab(idx)">
+				{{ t.label }}
+			</view>
+		</view>
+
+		<view class="list" v-if="showList.length">
+			<view class="item" v-for="item in showList" :key="item.rectId" @click="goDetail(item)">
+				<view class="top">
+					<view class="left">
+						<text class="name">{{ item.indicatorName }}</text>
+						<text class="round" v-if="item.rectRound > 1">第{{ item.rectRound }}轮</text>
+					</view>
+					<view class="tag" :style="{ color: statusMeta(item.status).color, background: statusMeta(item.status).bg }">
+						{{ statusMeta(item.status).text }}
+					</view>
+				</view>
+				<view class="meta">
+					<text>巡检得分:{{ fmtScore(item.inspectScore) }}(及格 {{ fmtScore(item.passScore) }})</text>
+				</view>
+				<view class="meta deadline">截止:{{ formatDate(item.deadlineDate) }}<text class="countdown" v-if="item.status == 1 || item.status == 2"> 剩余{{ remainDays(item.deadlineDate) }}天</text></view>
+				<view class="actions" v-if="item.status == 1 || item.status == 2">
+					<button class="btn" v-if="item.status == 1" @tap.stop="doStart(item)">开始整改</button>
+					<button class="btn primary" v-if="item.status == 2" @tap.stop="goSubmit(item)">提交反馈</button>
+				</view>
+			</view>
+		</view>
+
+		<view class="empty" v-else-if="!loading">
+			<image src="/static/imgs/empty/empty2x.png" class="empty-img" mode="widthFix"></image>
+			<text>暂无整改记录</text>
+		</view>
+	</view>
+</template>
+
+<script>
+import { mapState } from 'vuex';
+export default {
+	data() {
+		return {
+			listUrl: '/rectificationApi/getMyRectList',
+			startUrl: '/rectificationApi/startRect',
+			records: [],
+			loading: true,
+			activeIdx: 0,
+			// 按 PRD 状态分桶:待整改(1) / 进行中(2整改中,3待复检) / 已完成(4复检通过,5复检不通过,7最终不通过) / 已超期(6)
+			tabs: [
+				{ label: '待整改', value: [1] },
+				{ label: '进行中', value: [2, 3] },
+				{ label: '已完成', value: [4, 5, 7] },
+				{ label: '已超期', value: [6] }
+			]
+		}
+	},
+	computed: {
+		...mapState(['storeInfo']),
+		showList() {
+			var t = this.tabs[this.activeIdx]
+			return this.records.filter(r => t.value.indexOf(r.status) > -1)
+		}
+	},
+	methods: {
+		switchTab(idx) {
+			this.activeIdx = idx
+		},
+		loadData() {
+			this.loading = true
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign('' + this.storeInfo.storeUserId + dateStr);
+			this.requst({
+				url: this.listUrl,
+				data: {
+					timestamp: dateStr,
+					sign: sign,
+					storeId: this.storeInfo.storeId,
+					storeUserId: this.storeInfo.storeUserId
+				},
+				success: res => {
+					if (res.data.rtnBean.rtnCode == 0) {
+						this.records = res.data.list || []
+					} else if (res.data.rtnBean.rtnMsg) {
+						this.$api.msg(res.data.rtnBean.rtnMsg)
+					}
+					this.loading = false
+					uni.stopPullDownRefresh()
+				},
+				fail: () => {
+					this.loading = false
+					uni.stopPullDownRefresh()
+				}
+			})
+		},
+		doStart(item) {
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign('' + this.storeInfo.storeUserId + dateStr);
+			this.requst({
+				url: this.startUrl,
+				data: {
+					id: item.rectId,
+					timestamp: dateStr,
+					sign: sign,
+					storeId: this.storeInfo.storeId,
+					storeUserId: this.storeInfo.storeUserId
+				},
+				success: res => {
+					if (res.data.rtnBean.rtnCode == 0) {
+						this.$api.msg('已开始整改')
+						this.loadData()
+					} else if (res.data.rtnBean.rtnMsg) {
+						this.$api.msg(res.data.rtnBean.rtnMsg)
+					}
+				},
+				fail: () => {}
+			})
+		},
+		goDetail(item) {
+			uni.navigateTo({ url: '/pages/rectification/detail?id=' + item.rectId })
+		},
+		goSubmit(item) {
+			uni.navigateTo({ url: '/pages/rectification/detail?id=' + item.rectId + '&submit=1' })
+		},
+		statusMeta(v) {
+			var map = {
+				1: { text: '待整改', color: '#E6A23C', bg: '#fdf6ec' },
+				2: { text: '整改中', color: '#409EFF', bg: '#ecf5ff' },
+				3: { text: '待复检', color: '#9c27b0', bg: '#f5eefb' },
+				4: { text: '复检通过', color: '#67C23A', bg: '#f0f9eb' },
+				5: { text: '复检不通过', color: '#F56C6C', bg: '#fef0f0' },
+				6: { text: '已关闭', color: '#909399', bg: '#f4f4f5' },
+				7: { text: '最终不通过', color: '#F56C6C', bg: '#fef0f0' }
+			}
+			return map[v] || { text: '', color: '#666', bg: '#f4f4f5' }
+		},
+		formatDate(v) {
+			if (!v) return ''
+			let d = new Date(v)
+			let p = n => (n < 10 ? '0' + n : n)
+			return d.getFullYear() + '-' + p(d.getMonth() + 1) + '-' + p(d.getDate())
+		},
+		remainDays(deadline) {
+			if (!deadline) return '?'
+			let now = new Date()
+			let end = new Date(deadline)
+			let diff = Math.ceil((end - now) / (1000 * 60 * 60 * 24))
+			return diff > 0 ? diff : 0
+		},
+		fmtScore(v) {
+			return Number(v || 0)
+		}
+	},
+	onPullDownRefresh() {
+		this.loadData()
+	},
+	onLoad() {
+		this.loadData()
+	}
+}
+</script>
+
+<style lang="scss" scoped>
+.rect-list {
+	min-height: 100vh;
+	background-color: #f5f5f5;
+}
+.filter-tabs {
+	display: flex;
+	background-color: #fff;
+	padding: 20rpx;
+	.tab {
+		flex: 1;
+		text-align: center;
+		font-size: $font-lg;
+		color: $text-color;
+		&.active {
+			color: $theme-color;
+			font-weight: bold;
+		}
+	}
+}
+.list {
+	padding: 20rpx;
+	.item {
+		background: #fff;
+		border-radius: 16rpx;
+		padding: 24rpx;
+		margin-bottom: 20rpx;
+		.top {
+			@include between;
+			margin-bottom: 12rpx;
+			.left {
+				@include flexStart;
+				.name {
+					font-size: 30rpx;
+					font-weight: bold;
+					color: $text-color;
+				}
+				.round {
+					margin-left: 12rpx;
+					font-size: $font-sm;
+					color: $theme-color;
+					padding: 2rpx 12rpx;
+					border: 2rpx solid $theme-color;
+					border-radius: 20rpx;
+				}
+			}
+			.tag {
+				font-size: $font-sm;
+				padding: 4rpx 16rpx;
+				border-radius: 20rpx;
+			}
+		}
+		.meta {
+			color: $desc-color;
+			font-size: $font-base;
+			margin-bottom: 8rpx;
+			&.deadline {
+				color: $icon-color;
+			}
+		}
+		.actions {
+			display: flex;
+			justify-content: flex-end;
+			margin-top: 16rpx;
+			.btn {
+				margin: 0 0 0 20rpx;
+				padding: 0 30rpx;
+				height: 60rpx;
+				line-height: 60rpx;
+				font-size: $font-base;
+				color: #fff;
+				background: #909399;
+				border: none;
+				border-radius: 30rpx;
+				&.primary {
+					background: $theme-color;
+				}
+			}
+		}
+	}
+}
+.empty {
+	display: flex;
+	flex-direction: column;
+	align-items: center;
+	justify-content: center;
+	padding-top: 200rpx;
+	color: #cdcdcd;
+	font-size: $font-lg;
+	.empty-img {
+		width: 240rpx;
+		margin-bottom: 30rpx;
+	}
+}
+</style>

+ 144 - 0
pages/search/collect.vue

@@ -0,0 +1,144 @@
+<template>
+    <view class="collect-page">
+        <view class="info">
+            <view class="total-collect">
+                预计总收入: <text class="price">¥{{ moneyConverter(form.total) }}</text>
+            </view>
+            <view class="order">
+                <view class="col">已完成: <text class="num">{{ form.completedNum }}</text>单</view>
+                <view class="col">进行中: <text class="num">{{ form.progressNum }}</text>单</view>
+            </view>
+            <view class="income">
+                <view class="col">预计收入: <text class="price">{{ moneyConverter(form.completedMoney) }}</text></view>
+                <view class="col">预计收入: <text class="price">{{ moneyConverter(form.progressMoney) }}</text></view>
+            </view>
+            <view class="cancel">
+                已取消/退款: <text class="price">{{ form.refundNum }}</text>单
+            </view>
+            <view class="collect">
+                总计: <text class="price">¥{{ moneyConverter(form.refundMoney) }}</text>
+            </view>
+        </view>
+    </view>
+</template>
+<script>
+import {mapState} from 'vuex';
+export default {
+    data () {
+        return {
+			summaryUrl: '/storeOrderApi/orderSummary',
+			dateStr: '',
+            form: {
+                completedNum: 0,
+                progressNum: 0,
+                refundNum: 0,
+				completedMoney: 0,
+				progressMoney: 0,
+				refundMoney: 0,
+				total: 0,
+            }
+        }
+    },
+	computed: {
+		...mapState(['storeInfo'])
+	},
+    methods: {
+		loadData() {
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign(dateStr);
+			uni.showLoading({
+				title: '加载中'
+			});
+			let that = this;
+			this.requst({
+				url: this.summaryUrl,
+				data: {
+					timestamp: dateStr,
+					sign: sign,
+					dateStr: this.dateStr,
+					storeId: this.storeInfo.storeId
+				},
+				success: res => {
+					if (res.data.rtnBean.rtnCode == 0) {
+						uni.hideLoading();
+						this.form = res.data.obj
+					} else {
+						if (res.data.rtnBean.rtnMsg != '') {
+							uni.hideLoading();
+							this.$api.msg(res.data.rtnBean.rtnMsg);
+						}
+					}
+					
+				},
+				fail: (e) => {
+					console.log("接口调用失败:" + JSON.stringify(e));
+					uni.hideLoading();
+				},
+			})
+		}
+	},
+    onLoad (opt) {
+        if (!opt.day) return
+		let date = opt.day.slice(0, 10);
+		this.dateStr = date;
+		this.loadData();
+        uni.setNavigationBarTitle({
+            title: date + ' 营业汇总'
+        })
+    }
+}
+</script>
+<style lang="scss" scoped>
+.collect-page {
+    background-color: #f5f5f5;
+    min-height: 100vh;
+    border-top: 2rpx solid #f5f5f5;
+}
+.info {
+    width: calc(100vw - 40rpx);
+    margin: 20rpx;
+    background-color: #fff;
+    border-radius: 20rpx;
+    padding: 20rpx;
+    box-sizing: border-box;
+}
+.price {
+    font-size: 40rpx;
+    margin: 0 20rpx;
+}
+.total-collect {
+    margin: 30rpx 0 50rpx;
+    font-size: 40rpx;
+    .price {
+        color: $uni-color-button;
+        font-weight: bold;
+    }
+}
+.order, .income {
+	margin-top: 8rpx;
+    @include between;
+    .col {
+        flex: 1;
+    }
+    .num {
+        font-weight: bold;
+        margin: 0 20rpx;
+        font-size: 40rpx;
+        margin-bottom: 20rpx;
+    }
+    .price {
+        color: $uni-color-button;
+    }
+}
+.cancel {
+    margin: 40rpx 0 10rpx;
+    .price {
+        color: #000;
+        font-weight: bold;
+    }
+}
+.collect {
+    margin-bottom: 40rpx;
+}
+
+</style>

+ 133 - 0
pages/search/index.vue

@@ -0,0 +1,133 @@
+<template>
+    <view class="search-page">
+        <view class="search-wrap">
+            <view class="search-input">
+                <input
+                    v-model="searchValue"
+                    type="text"
+                    placeholder="请输入订单编号或手机号码进行查询"
+                    clearable
+                    placeholder-class="input-placeholder"
+                    @input="handInput"
+                    @blur="handSearch"
+					style="height: 56rpx;background-color: #FFF;font-size: 28rpx;"
+                    :customStyle="inputCustomStyle"
+                    maxlength="50">
+                </input>
+            </view>
+            <u-icon name="search" size="28" color="#6c6c6c" @click="handSearch"></u-icon>
+        </view>
+        <view class="result-num">共{{ orderList.length }}个结果</view>
+        <view class="result-list">
+            <item v-for="item in orderList" @loadOrder="loadData" :item="item" :key="item.id" />
+        </view>
+    </view>
+</template>
+<script>
+import item from '@/pages/components/order.vue'
+import {mapState} from 'vuex';
+export default {
+    components: {
+        item
+    },
+    data () {
+        return {
+			listUrl: '/storeOrderApi/getOrderListForStoreSearch',
+            inputCustomStyle: {
+                height: '30px',
+                background: '#fff',
+                border: 'none'
+            },
+            searchValue: '',
+            orderList: []
+        }
+    },
+	computed: {
+		...mapState(['storeInfo'])
+	},
+    methods: {
+		handInput (e) {
+			this.searchValue = e.detail.value
+		},
+        handSearch () {
+			if (!this.searchValue) {
+				uni.showToast({
+					title: "请输入搜索条件",
+					icon: "none"
+				})
+				return 
+			}
+			this.loadData();
+        },
+		loadData() {
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign(dateStr);
+			uni.showLoading({
+				title: '加载中'
+			});
+			let that = this;
+			this.requst({
+				url: this.listUrl,
+				data: {
+					timestamp: dateStr,
+					sign: sign,
+					storeId: this.storeInfo.storeId,
+					searchValue2: this.searchValue,
+				},
+				success: res => {
+					if (res.data.rtnBean.rtnCode == 0) {
+						uni.hideLoading();
+						this.orderList = res.data.list;
+					} else {
+						if (res.data.rtnBean.rtnMsg != '') {
+							uni.hideLoading();
+							this.$api.msg(res.data.rtnBean.rtnMsg);
+						}
+					}
+					
+				},
+				fail: (e) => {
+					console.log("接口调用失败:" + JSON.stringify(e));
+					uni.hideLoading();
+				},
+			})
+			
+		},
+		
+    },
+    onLoad (opt) {
+        if (opt.no) {
+            this.searchValue = opt.no
+            this.handSearch()
+        }
+    }
+}
+</script>
+<style lang="scss" scoped>
+/deep/ .input-placeholder {
+	font-size: 23rpx;
+	color: #666666
+}
+.search-page {
+    background-color: #f5f5f5;
+    min-height: 100vh;
+    border-top: 2rpx solid #f5f5f5;
+}
+.search-wrap {
+    @include between;
+    padding: 0 20rpx;
+    box-sizing: border-box;
+    margin: 20rpx 0;
+    .search-input {
+        height: 60rpx;
+        flex: 1;
+        margin-right: 40rpx;
+    }
+}
+.result-num {
+    padding: 0 20rpx;
+    box-sizing: border-box;
+    font-size: $font-base;
+    color: $text-color;
+}
+</style>

+ 356 - 0
pages/store/clerk.vue

@@ -0,0 +1,356 @@
+<template>
+	<view class="clerk-page">
+		<view scroll-y class="class-list">
+			<view class="item" v-for="item in clerks" :key="item.storeUserId">
+				<view class="row">
+					<view class="name">{{ item.userName }}</view>
+					<u-icon name="trash" size="26" color="#6c6c6c" @click="handItemRemove(item)"></u-icon>
+				</view>
+				<view class="row">
+					<view class="phone">登录手机号: {{ item.tel }}</view>
+					<u-icon name="edit-pen" size="26" color="#6c6c6c" @click="handItemEdit(item)"></u-icon>
+				</view>
+			</view>
+		</view>
+		<view class="add-clerk" @click="addCleak">
+			添加店员
+		</view>
+		<u-modal :show="deleteVisible" :title="modalTitle" closeOnClickOverlay showCancelButton confirmColor="#d64d3b"
+			@confirm="handDelete" @cancel="cancelDelete" @close="deleteVisible = false">
+		</u-modal>
+		<u-modal :show="formVisible" :title="modalTitle" closeOnClickOverlay showCancelButton @confirm="handAdd"
+			@cancel="cancelAdd" @close="closeAdd">
+			<view class="container">
+				<view class="name">
+					<u-input border v-model="formItem.userName" placeholder="请输入店员姓名"></u-input>
+				</view>
+				<view class="phone">
+					<u-input border v-model="formItem.tel" placeholder="请输入店员手机号"></u-input>
+				</view>
+			</view>
+		</u-modal>
+	</view>
+</template>
+<script>
+import { mapState } from 'vuex';
+export default {
+	data() {
+		return {
+			clerkUrl: '/storeClerkManageApi/getStoreClerkList',
+			updateUrl: '/storeClerkManageApi/update',
+			deleteUrl: '/storeClerkManageApi/delete',
+			getOneUrl: '/storeClerkManageApi/getStoreClerkById',
+			modalTitle: '',
+			deleteVisible: false,
+			delItem: null,
+			formItem: {
+				storeUserId: '',
+				userName: '',
+				tel: ''
+			},
+			formVisible: '',
+			clerks: [],
+			triggered: false
+		}
+	},
+	computed: {
+		...mapState(['storeInfo'])
+	},
+	methods: {
+		loadData() {
+			uni.showLoading({
+				title: '加载中'
+			})
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign('' + this.storeInfo.storeUserId + dateStr);
+			this.requst({
+				url: this.clerkUrl,
+				data: {
+					timestamp: dateStr,
+					sign: sign,
+					storeId: this.storeInfo.storeId,
+					storeUserId: this.storeInfo.storeUserId
+				},
+				success: res => {
+					if (res.data.rtnBean.rtnCode == 0) {
+						this.clerks = res.data.list;
+					}
+					uni.stopPullDownRefresh()
+					uni.hideLoading()
+				},
+				fail: (e) => {
+					console.log("接口调用失败:" + JSON.stringify(e));
+					uni.stopPullDownRefresh()
+					uni.hideLoading()
+				}
+			})
+		},
+		closeAdd () {
+			this.formVisible = false
+			this.formItem = {
+				storeUserId: '',
+				userName: '',
+				tel: ''
+			}
+		},
+		onPullDownRefresh() {
+			this.loadData();
+		},
+		cancelAdd () {
+			this.formVisible = false
+			this.formItem = {
+				storeUserId: '',
+				userName: '',
+				tel: ''
+			}
+		},
+		onRestore () {
+			this.triggered = 'restore'; // 需要重置
+			console.log("onRestore");
+		},
+		getClerk(id) {
+			uni.showLoading({
+				title: '加载中'
+			})
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign('' + this.storeInfo.storeUserId + dateStr);
+			this.requst({
+				url: this.getOneUrl,
+				data: {
+					timestamp: dateStr,
+					sign: sign,
+					storeId: this.storeInfo.storeId,
+					storeUserId: this.storeInfo.storeUserId,
+					id: id
+				},
+				success: res => {
+					if (res.data.rtnBean.rtnCode == 0) {
+						this.formItem = res.data.obj;
+						this.modalTitle = '添加/编辑店员'
+						this.formVisible = true;
+					}
+					uni.hideLoading()
+				},
+				fail: (e) => {
+					console.log("接口调用失败:" + JSON.stringify(e));
+					uni.hideLoading()
+				}
+			})
+		},
+		close() {
+			this.deleteVisible = false
+		},
+		handItemRemove(item) {
+			this.modalTitle = `确定要删除店员 [${item.userName}] 吗?`
+			this.delItem = item
+			this.deleteVisible = true
+		},
+		handDelete() {
+			uni.showLoading({
+				title: '加载中'
+			})
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign('' + this.storeInfo.storeUserId + dateStr);
+			this.requst({
+				url: this.deleteUrl,
+				data: {
+					storeUserId: this.storeInfo.storeUserId,
+					storeId: this.storeInfo.storeId,
+					timestamp: dateStr,
+					sign: sign,
+					id: this.delItem.storeUserId
+				},
+				success: res => {
+					if (res.data.rtnBean.rtnCode == 0) {
+						this.delItem = null
+						this.modalTitle = ''
+						this.deleteVisible = false
+						this.loadData();
+					} else {
+						if (res.data.rtnBean.rtnMsg != '') {
+							this.$api.msg(res.data.rtnBean.rtnMsg);
+						}
+					}
+					uni.hideLoading()
+				},
+				fail: (e) => {
+					console.log("接口调用失败:" + JSON.stringify(e));
+					uni.hideLoading()
+				},
+			});
+		},
+
+		cancelDelete() {
+			this.delItem = null
+			this.modalTitle = ''
+			this.deleteVisible = false
+		},
+		handItemEdit(item) {
+			console.log('item :>> ', item);
+			// this.formItem = item
+			this.getClerk(item.storeUserId)
+			
+		},
+		refresherrefresh() {
+			uni.showLoading({
+				title: '加载中'
+			})
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign('' + this.storeInfo.storeUserId + dateStr);
+			this.requst({
+				url: this.clerkUrl,
+				data: {
+					timestamp: dateStr,
+					sign: sign,
+					storeId: this.storeInfo.storeId,
+					storeUserId: this.storeInfo.storeUserId
+				},
+				success: res => {
+					this.triggered = false;
+					if (res.data.rtnBean.rtnCode == 0) {
+						this.clerks = res.data.list;
+					}
+					
+					uni.hideLoading()
+				},
+				fail: (e) => {
+					console.log("接口调用失败:" + JSON.stringify(e));
+					this.triggered = false;
+					uni.hideLoading()
+					
+				}
+			})
+			
+		},
+		addCleak() {
+			this.modalTitle = '添加/编辑店员'
+			this.formVisible = true
+		},
+		handAdd() {
+			let reg = /^[1][3,4,5,7,8,9][0-9]{9}$/ 
+			if (!this.formItem.userName) {
+				uni.showToast({
+					title: '请输入店员姓名',
+					icon: 'none'
+				})
+				return
+			}
+			if (!this.formItem.tel) {
+				uni.showToast({
+					title: '请输入店员手机号码',
+					icon: 'none'
+				})
+				return
+			}
+			if (!reg.test(this.formItem.tel)) {
+				uni.showToast({
+					title: '请输入正确的手机号',
+					icon: 'none'
+				})
+				return
+			}
+			uni.showLoading({
+				title: '保存中'
+			})
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign('' + this.storeInfo.storeUserId + dateStr);
+			this.requst({
+				url: this.updateUrl,
+				data: {
+					timestamp: dateStr,
+					sign: sign,
+					storeId: this.storeInfo.storeId,
+					id: this.storeInfo.storeUserId,
+					storeUserId: this.formItem.storeUserId,
+					userName: this.formItem.userName,
+					tel: this.formItem.tel
+				},
+				success: res => {
+					if (res.data.rtnBean.rtnCode == 0) {
+						uni.hideLoading()
+						this.formVisible = false
+						this.formItem = {
+							storeUserId: '',
+							userName: '',
+							tel: ''
+						}
+						this.loadData()
+						
+						this.modalVisible = false
+					} else {
+						uni.hideLoading()
+						if (res.data.rtnBean.rtnMsg != '') {
+							this.$api.msg(res.data.rtnBean.rtnMsg);
+						}
+					}
+					
+				},
+				fail: (e) => {
+					console.log("接口调用失败:" + JSON.stringify(e));
+					uni.hideLoading()
+				}
+			})
+			
+		}
+	},
+	onLoad() {
+		this.loadData();
+	}
+}
+</script>
+<style lang="scss" scoped>
+.clerk-page {
+	width: 100vw;
+	height: 100vh;
+	background-color: #fff;
+}
+
+.class-list {
+	height: calc(100vh - 120rpx - env(safe-area-inset-bottom));
+	height: calc(100vh - 120rpx - constant(safe-area-inset-bottom));
+
+	.item {
+		background-color: #fff;
+		margin-bottom: 20rpx;
+		border-radius: 10rpx;
+		padding: 20rpx;
+		display: flex;
+		flex-direction: column;
+	
+		.row {
+			@include between;
+			margin-bottom: 20rpx;
+			color: $text-color;
+		}
+	
+		.name {
+			font-size: 36rpx;
+		}
+	
+		.phone {
+			font-size: $font-lg;
+		}
+	}
+}
+
+.add-clerk {
+	position: fixed;
+	left: 10vw;
+	width: 80vw;
+	height: 80rpx;
+	border-radius: 40rpx;
+	@include flexCenter;
+	color: #fff;
+	background-color: $light-color;
+	bottom: calc(20rpx + env(safe-area-inset-bottom));
+	bottom: calc(20rpx + constant(safe-area-inset-bottom));
+}
+
+.container {
+	.name, .phone {
+		margin: 40rpx 0;
+		border: 2rpx solid #ececec;
+		border-radius: 6rpx;
+	}
+}
+</style>

+ 150 - 0
pages/store/codeinfo.vue

@@ -0,0 +1,150 @@
+<template>
+    <view class="qrcode-info">
+        <view class="cover-bg">
+            <image class="qrcode" :src="backImgUrl" />
+			<qrcode-poster ref="poster" :backImagUrl="backImgUrl" :memberName="haibao.memberName"
+				:qdImageUrl="code" :codeType="codeType"
+				></qrcode-poster>
+        </view>
+        <view class="desc">
+            注:可直接生成店铺专用小程序码,并可保存手机相册,用于店铺宣传。
+        </view>
+        <view class="save" @click="save">生成推广码</view>
+    </view>
+</template>
+<script>
+import QrcodePoster from '@/components/qrcode-poster/qrcode-poster.vue';
+import { mapState } from 'vuex';
+export default {
+	components: {
+		QrcodePoster
+	},
+    data () {
+        return {
+            getCodeUrl: (getApp().globalData.platApiUrl || getApp().globalData.apiUrl) + '/api/storeQrCodeManage/getStoreQrCode',
+			code: {},
+			backImgUrl: '',
+			codeType: ''
+        }
+    },
+	computed: {
+		...mapState(['storeInfo'])
+	},
+    methods: {
+		getCode() {
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign('' + this.storeInfo.storeUserId + dateStr);
+			uni.showLoading({
+				title: '获取小程序码中'
+			})
+			this.requst({
+				url: this.getCodeUrl,
+				data: {
+					timestamp: dateStr,
+					sign: sign,
+					storeId: this.storeInfo.storeId,
+					storeUserId: this.storeInfo.storeUserId,
+					code: this.storeInfo.code
+				},
+				success: res => {
+					uni.hideLoading()
+					if (res.data.rtnBean.rtnCode == 0) {
+						this.code = res.data.obj;
+						this.$refs.poster.showCanvas();
+						console.log('object :>> ', this.code);
+					}
+					
+				},
+				fail: (e) => {
+					console.log("接口调用失败:" + JSON.stringify(e));
+					uni.hideLoading()
+				}
+			})
+		},
+        save () {
+			this.getCode()
+			// this.$refs.poster.showCanvas();
+        },
+        // downLoadImg(){
+        //     uni.downloadFile({
+        //         url: this.url,
+        //         success: (res) =>{
+        //             if (res.statusCode === 200){
+        //                 uni.saveImageToPhotosAlbum({
+        //                     filePath: res.tempFilePath,
+        //                     success: function() {
+        //                         uni.showToast({
+        //                             title: "保存成功",
+        //                             icon: "none"
+        //                         });
+        //                     },
+        //                     fail: function() {
+        //                         uni.showToast({
+        //                             title: "保存失败,请稍后重试",
+        //                             icon: "none"
+        //                         });
+        //                     }
+        //                 });
+        //             }
+        //         }
+        //     })
+        // },
+        //引导用户开启权限
+        isAuth() {
+            uni.showModal({
+                content: '无法进行保存,请点击确定允许授权',
+                success: (res) => {
+                    if (res.confirm) {
+                        uni.openSetting({
+                            success: (result) => {
+                                console.log(result.authSetting);
+                            }
+                        });
+                    }
+                }
+            });
+        }
+    },
+    onLoad (opt) {
+        console.log('opt :>> ', opt);
+		this.backImgUrl = opt.src
+		this.codeType = opt.id
+		// if (opt.id != null && opt.id != '' && opt.id != undefined){
+			
+		// }
+    }
+}
+</script>
+<style lang="scss" scoped>
+.cover-bg {
+    height: 760rpx;
+    // background-color: #E5CB1E;
+    margin-bottom: 100rpx;
+    .qrcode {
+        width: 440rpx;
+        height: 774rpx;
+        position: absolute;
+        top: 50rpx;
+        left: calc((100% - 440rpx) / 2);
+        background-color: #fff;
+    }
+}
+.desc {
+    font-size: $font-base;
+    color: $desc-color;
+    padding: 0 30rpx;
+}
+.save {
+    width: 80%;
+    height: 80rpx;
+    border-radius: 40rpx;
+    position: fixed;
+    left: 10%;
+    bottom: calc(20rpx + env(safe-area-inset-bottom));
+    bottom: calc(20rpx + constant(safe-area-inset-bottom));
+    background-color: $light-color;
+    color: #fff;
+    font-size: 36rpx;
+    @include flexCenter;
+}
+</style>

+ 303 - 0
pages/store/deliveryman.vue

@@ -0,0 +1,303 @@
+<template>
+	<view class="clerk-page">
+		<view class="clerk-list">
+			<!-- <uni-swipe-action>
+				<uni-swipe-action-item :right-options="options" :index="index" v-for="(item, index) in riderList"
+					@click="swipeClick(index,item)" :key="item.riderId"> -->
+					<view class="item" v-for="(item, index) in riderList" :key="item.riderId" style="width: 100%;">
+						<view class="row">
+							<view class="name">{{ item.riderName }}</view>
+							<!-- <u-icon name="trash" size="26" color="#6c6c6c" @click="handItemRemove(item)"></u-icon> -->
+							<view class="value flex-start">
+								状态:
+							    <switch :checked="item.status" @change="changeStatus(item)"  size="22" class="switch" style="transform:scale(0.7)"></switch>
+							</view>
+						</view>
+						<view class="row">
+							<view class="phone">手机号: {{ item.telNo }}</view>
+							<u-icon name="edit-pen" size="26" color="#6c6c6c" @click="handItemEdit(item)"></u-icon>
+						</view>
+					</view>
+<!-- 				</uni-swipe-action-item>
+			</uni-swipe-action> -->
+		</view>
+		<view class="add-clerk" @click="addCleak">
+			添加专属配送员
+		</view>
+		<u-modal :show="deleteVisible" :title="modalTitle" closeOnClickOverlay showCancelButton confirmColor="#d64d3b"
+			@confirm="handDelete" @cancel="deleteVisible = false" @close="deleteVisible = false">
+		</u-modal>
+		<u-modal :show="formVisible" :title="modalTitle" closeOnClickOverlay showCancelButton @confirm="handAdd"
+			@cancel="formVisible = false" @close="formVisible = false">
+			<view class="container">
+				<view class="name">
+					<u-input v-model="formItem.riderName" placeholder="请输入配送员姓名"></u-input>
+				</view>
+				<view class="phone">
+					<u-input v-model="formItem.telNo" placeholder="请输入配送员手机号"></u-input>
+				</view>
+				<view class="password">
+					初始密码: 123456
+				</view>
+			</view>
+		</u-modal>
+	</view>
+</template>
+<script>
+import { mapState } from 'vuex';
+export default {
+	data() {
+		return {
+			riderUrl: '/storeDeliveryManageApi/getStoreRiderList',
+			updateUrl: '/storeDeliveryManageApi/updateRider',
+			getOneUrl: '/storeDeliveryManageApi/getStoreRiderById',
+			changeStatusUrl: '/storeDeliveryManageApi/changeStatus',
+			modalTitle: '',
+			deleteVisible: false,
+			delItem: null,
+			riderList: [],
+			formItem: {
+				riderId: '',
+				riderName: '',
+				telNo: ''
+			},
+			formVisible: '',
+			options: [
+			    {
+			        text: '删除',
+					style: {
+			            fontSize: '28rpx',
+						backgroundColor: '#FF0000'
+			        }
+			    }
+			],
+			
+		}
+	},
+	computed: {
+		...mapState(['storeInfo'])
+	},
+	methods: {
+		loadData () {
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign('' + this.storeInfo.storeUserId + dateStr);
+			this.requst({
+				url: this.riderUrl,
+				data: {
+					timestamp: dateStr,
+					sign: sign,
+					storeId: this.storeInfo.storeId,
+					storeUserId: this.storeInfo.storeUserId
+				},
+				success: res => {
+					if (res.data.rtnBean.rtnCode == 0) {
+						this.riderList = res.data.list;
+					}
+				},
+				fail: (e) => {
+					console.log("接口调用失败:" + JSON.stringify(e));
+				}
+			})
+		},
+		changeStatus (item) {
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign('' + this.storeInfo.storeId + dateStr);
+			let status = item.status == 1 ? 0 : 1
+			this.requst({
+				url: this.changeStatusUrl,
+				data: {
+					timestamp: dateStr,
+					sign: sign,
+					storeId: this.storeInfo.storeId,
+					riderId: item.riderId,
+					status: status
+				},
+				success: res => {
+					if (res.data.rtnBean.rtnCode == 0) {
+						this.loadData()
+					}
+				},
+				fail: (e) => {
+					console.log("接口调用失败:" + JSON.stringify(e));
+				}
+			})
+		},
+		close() {
+			this.deleteVisible = false
+		},
+		handItemRemove(item) {
+			this.modalTitle = `确定要删除配送员 [${item.riderName}] 吗?`
+			this.delItem = item
+			this.deleteVisible = true
+		},
+		handDelete() {
+			console.log('这里写删除接口: ', this.delItem);
+			this.delItem = null
+			this.modalTitle = ''
+			this.deleteVisible = false
+		},
+		handItemEdit(item) {
+			this.getRider(item.riderId)
+			
+		},
+		addCleak() {
+			this.modalTitle = '添加配送员'
+			this.formVisible = true
+		},
+		handAdd() {
+			let reg = /^[1][3,4,5,7,8,9][0-9]{9}$/ 
+			if (!this.formItem.riderName) {
+				uni.showToast({
+					title: '请输入配送员姓名',
+					icon: 'none'
+				})
+				return
+			}
+			if (!this.formItem.telNo) {
+				uni.showToast({
+					title: '请输入配送员手机号码',
+					icon: 'none'
+				})
+				return
+			}
+			if (!reg.test(this.formItem.telNo)) {
+				uni.showToast({
+					title: '请输入正确的手机号',
+					icon: 'none'
+				})
+				return
+			}
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign('' + this.storeInfo.storeUserId + dateStr);
+			this.requst({
+				url: this.updateUrl,
+				data: {
+					timestamp: dateStr,
+					sign: sign,
+					storeId: this.storeInfo.storeId,
+					storeUserId: this.storeInfo.storeUserId,
+					riderId: this.formItem.riderId,
+					riderName: this.formItem.riderName,
+					telNo: this.formItem.telNo
+				},
+				success: res => {
+					if (res.data.rtnBean.rtnCode == 0) {
+						this.formVisible = false
+						this.formItem = {
+							riderId: '',
+							riderName: '',
+							telNo: ''
+						}
+						this.loadData()
+						
+						this.modalVisible = false
+					} else {
+						if (res.data.rtnBean.rtnMsg != '') {
+							this.$api.msg(res.data.rtnBean.rtnMsg);
+						}
+					}
+				},
+				fail: (e) => {
+					console.log("接口调用失败:" + JSON.stringify(e));
+				}
+			})
+		},
+		getRider(id) {
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign('' + this.storeInfo.storeUserId + dateStr);
+			this.requst({
+				url: this.getOneUrl,
+				data: {
+					timestamp: dateStr,
+					sign: sign,
+					storeId: this.storeInfo.storeId,
+					storeUserId: this.storeInfo.storeUserId,
+					riderId: id
+				},
+				success: res => {
+					if (res.data.rtnBean.rtnCode == 0) {
+						this.formItem = res.data.obj;
+						this.modalTitle = '编辑配送员'
+						this.formVisible = true
+					}
+				},
+				fail: (e) => {
+					console.log("接口调用失败:" + JSON.stringify(e));
+				}
+			})
+		},
+	},
+	onLoad() {
+		this.loadData();
+	}
+}
+</script>
+<style lang="scss" scoped>
+.clerk-page {
+	width: 100vw;
+	height: 100vh;
+	background-color: #fff;
+}
+
+.clerk-list {
+	height: calc(100vh - env(safe-area-inset-bottom) - 120rpx);
+	height: calc(100vh - constant(safe-area-inset-bottom) - 120rpx);
+	background-color: #f5f5f5;
+	padding: 20rpx;
+	box-sizing: border-box;
+
+	.item {
+		background-color: #fff;
+		margin-bottom: 20rpx;
+		border-radius: 10rpx;
+		padding: 20rpx;
+		display: flex;
+		flex-direction: column;
+
+		.row {
+			@include between;
+			margin-bottom: 20rpx;
+			color: $text-color;
+		}
+
+		.name {
+			font-size: 36rpx;
+		}
+
+		.phone {
+			font-size: $font-lg;
+		}
+
+		.password {}
+	}
+}
+
+.add-clerk {
+	position: fixed;
+	left: 10vw;
+	width: 80vw;
+	height: 80rpx;
+	border-radius: 40rpx;
+	@include flexCenter;
+	color: #fff;
+	background-color: $light-color;
+	bottom: calc(20rpx + env(safe-area-inset-bottom));
+	bottom: calc(20rpx + constant(safe-area-inset-bottom));
+}
+
+.container {
+	.name {
+		margin: 40rpx 0;
+	}
+
+	.phone {
+		margin-bottom: 20rpx;
+	}
+
+	.password {
+		text-align: center;
+		font-size: $font-sm;
+		color: $uni-color-button;
+	}
+}
+</style>

+ 278 - 0
pages/store/distribution.vue

@@ -0,0 +1,278 @@
+<template>
+    <view class="distribution-page">
+        <view  class="scroll-content">
+            <view class="title">自取设置</view>
+            <view class="form-item pb0">
+                <view class="label flex-center">是否开启自取</view>
+                <view class="value flex-start">
+                    <switch :checked="form.pickupStatus" disabled="true" size="22" class="switch" style="transform:scale(0.7)"></switch>
+                    <text class="desc">若需修改请联系运营方</text>
+                </view>
+            </view>
+			<view class="form-item pb0">
+			    <view class="label flex-center">是否开启堂食</view>
+			    <view class="value flex-start">
+			        <switch :checked="form.dineStatus" disabled="true" size="22" class="switch" style="transform:scale(0.7)"></switch>
+			        <text class="desc">若需修改请联系运营方</text>
+			    </view>
+			</view>
+            <view class="title">外卖设置</view>
+            <view class="form-item pb0">
+                <view class="label flex-center">是否开启外卖</view>
+                <view class="value flex-start">
+                    <switch :checked="form.takeoutStatus" disabled="true" @change="changeTakeoutStatus"  size="22" class="switch" style="transform:scale(0.7)"></switch>
+					<text class="desc">若需修改请联系运营方</text>
+				</view>
+            </view>
+            <view class="form-item flex-center mr10-0" v-if="form.takeoutStatus == 1">
+                <view class="label">配送方式</view>
+                <view class="value">
+                    <view style="font-size: 26rpx;">
+						{{form.deliveryType == 0 ? "自配送" : "平台配送"}}
+					</view>
+					<!-- <u-radio-group v-model="form.deliveryType" activeColor="#04be02" disabled="true" placement="row">
+                        <u-radio label="平台配送" :name="1"></u-radio>
+                        <u-radio label="自配送" :name="0"></u-radio>
+                    </u-radio-group> -->
+                </view>
+            </view>
+            <view class="form-item flex-center mt10" v-if="form.deliveryType == 0">
+                <view class="label">专属配送员</view>
+                <view class="value">
+                    <view class="btn" @click="handSettingClerk">配置专属配送员</view>
+                </view>
+            </view>
+        </view>
+        <!-- <view class="footer-btn">
+            <view class="save" @click="handSave">保存</view>
+        </view> -->
+    </view>
+</template>
+<script>
+import { mapState } from 'vuex';
+export default {
+    data () {
+        return {
+			deliveryUrl: '/storeDeliveryManageApi/getDeliveryInfo',
+			updateUrl: '/storeDeliveryManageApi/update',
+            form: {
+                pickupStatus: '',
+                takeoutStatus: '',
+                deliveryType: 1,
+                riderId: '',
+                riderName: '',
+                telNo: '',
+            }
+        }
+    },
+	computed: {
+		...mapState(['storeInfo'])
+	},
+    methods: {
+
+		loadData () {		
+			uni.showLoading({
+				title: '加载中'
+			});
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign('' + this.storeInfo.storeUserId + dateStr);
+			this.requst({
+				url: this.deliveryUrl,
+				data: {
+					timestamp: dateStr,
+					sign: sign,
+					storeId: this.storeInfo.storeId,
+					storeUserId: this.storeInfo.storeUserId
+				},
+				success: res => {
+					if (res.data.rtnBean.rtnCode == 0) {
+						this.form = res.data.obj;
+						console.log('this.form :>> ', this.form);
+					}
+					uni.hideLoading();
+				},
+				fail: (e) => {
+					console.log("接口调用失败:" + JSON.stringify(e));
+					uni.hideLoading();
+				}
+			})
+		},
+        handSettingClerk () {
+            uni.navigateTo({
+                url: `/pages/store/deliveryman`
+            });
+        },
+        deleteDeliveryman () {
+            this.form.deliverymanName = ''
+            this.form.deliverymanPhone = ''
+            this.form.deliverymanId = ''
+        },
+		changeTakeoutStatus (e) {
+			this.form.takeoutStatus = e.detail.value
+		},
+        handSave () {
+            console.log('save', this.form);
+			
+			if (this.form.takeoutStatus == null || this.form.takeoutStatus == undefined) {
+				uni.showToast({
+					title: '请选择是否开启外卖',
+					icon: 'none'
+				})
+				return
+			}
+			if (this.form.pickupStatus == 0 && this.form.takeoutStatus == 0 ) {
+				uni.showToast({
+					title: '外卖和自取不可以同时关闭',
+					icon: 'none'
+				})
+				return
+			}
+			uni.showLoading({
+				title: '加载中'
+			});
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign('' + this.storeInfo.storeUserId + dateStr);
+			this.requst({
+				url: this.updateUrl,
+				data: {
+					timestamp: dateStr,
+					sign: sign,
+					storeId: this.storeInfo.storeId,
+					storeUserId: this.storeInfo.storeUserId,
+					takeoutStatus: this.form.takeoutStatus ? 1 : 0,
+					deliveryType: this.form.deliveryType
+				},
+				success: res => {
+					uni.hideLoading();
+					if (res.data.rtnBean.rtnCode == 0) {
+						uni.showToast({
+							title: '保存成功',
+							icon: 'none'
+						})
+						this.form = {
+							pickupStatus: '',
+							takeoutStatus: '',
+							deliveryType: '',
+							riderId: '',
+							riderName: '',
+							telNo: ''
+						}
+						this.loadData()
+					}
+					
+				},
+				fail: (e) => {
+					console.log("接口调用失败:" + JSON.stringify(e));
+					uni.hideLoading();
+				}
+			})
+        },
+		takeawayTimeInput (e,val){
+			// 只能输入数字
+			const inputType = /[^\d]/g
+			this.$nextTick(function () {
+				this.form.takeawayTime = e.replace(inputType,'');
+			})
+		},
+		pickupTimeInput (e,val) {
+			// 只能输入数字
+			const inputType = /[^\d]/g
+			this.$nextTick(function () {
+				this.form.pickupTime = e.replace(inputType,'');
+			})
+		}
+    },
+    onShow () {
+    },
+    onLoad () {
+		this.loadData()
+	}
+}
+</script>
+<style lang="scss" scoped>
+.distribution-page {
+    height: 95vh;
+    background-color: #f5f5f5;
+}
+
+.scroll-content {
+    height: calc(100vh - 120rpx - env(safe-area-inset-bottom));
+    height: calc(100vh - 120rpx - constant(safe-area-inset-bottom));
+}
+.title {
+    font-size: 36rpx;
+    color: $title-color;
+    margin: 24rpx 0 16rpx;
+    padding: 0 20rpx;
+}
+.form-item {
+    background-color: #fff;
+    @include flexStart(flex-start);
+    padding: 20rpx 60rpx;
+    box-sizing: border-box;
+    .label {
+        margin-right: 20rpx;
+        margin-top: 20rpx;
+        font-size: $font-base;
+        color: $text-color;
+        width: 180rpx;
+    }
+    .label.flex-center {
+        margin-top: 6rpx;
+    }
+    .desc {
+        font-size: $font-sm;
+        color: $uni-color-button;
+    }
+    .flex-start {
+        @include flexStart;
+        .desc {
+            margin-left: 20rpx;
+            color: $text-color;
+        }
+    }
+}
+.form-item.pb0 {
+    padding-bottom: 0;
+}
+.form-item.flex-center {
+    @include flexStart(center);
+    .label {
+        margin-top: 0;
+    }
+    .btn {
+        font-size: $font-base;
+        color: #fff;
+        background-color: $theme-color;
+        @include flexCenter;
+        padding: 6rpx 20rpx;
+        border-radius: 10rpx;
+    }
+    .deliveryman {
+        height: 52rpx;
+        @include flexStart;
+        font-size: $font-base;
+        color: $text-color;
+    }
+    .del {
+        color: $uni-color-button;
+        margin-left: 20rpx;
+    }
+}
+/deep/ .u-radio {
+    margin-right: 50rpx;
+}
+.footer-btn {
+    width: 100vw;
+    margin-top: 40rpx;
+    .save {
+        margin: 0 10vw;
+        width: 80vw;
+        height: 80rpx;
+        border-radius: 40rpx;
+        background-color: $theme-color;
+        color: #fff;
+        @include flexCenter;
+    }
+}
+</style>

+ 98 - 0
pages/store/extensionCode.vue

@@ -0,0 +1,98 @@
+<template>
+    <view class="extension-code-page">
+        <view class="qrcodes">
+            <view class="item" v-for="item in qrcodes" :key="item.id" @click="handSelectItem(item)">
+                <image class="qrcode" :src="item.thumbnailUrl.length > 1 ? item.thumbnailUrl : ''" />
+                <view class="name">{{ item.name }}</view>
+            </view>
+        </view>
+    </view>
+</template>
+<script>
+export default {
+    data () {
+        return {
+			fileUrl: getApp().globalData.tuiguangmaUrl,
+            qrcodes: [
+                {
+                    id: 1,
+                    thumbnailUrl: getApp().globalData.tuiguangmaUrl + '000.png',
+                    url: getApp().globalData.tuiguangmaUrl + '00.jpg',
+                    name: '不发胖'
+                },
+                {
+                    id: 2,
+                    thumbnailUrl: getApp().globalData.tuiguangmaUrl + '011.png',
+                    url: getApp().globalData.tuiguangmaUrl + '11.jpg',
+                    name: '按时吃饭'
+                },
+                {
+                    id: 3,
+                    thumbnailUrl: getApp().globalData.tuiguangmaUrl + '022.png',
+                    url: getApp().globalData.tuiguangmaUrl + '22.jpg',
+                    name: '科技质感'
+                },
+                {
+                    id: 4,
+                    thumbnailUrl: getApp().globalData.tuiguangmaUrl + '033.png',
+                    url: getApp().globalData.tuiguangmaUrl + '33.jpg',
+                    name: '叮咚黄'
+                },
+                {
+                    id: 5,
+                    thumbnailUrl: getApp().globalData.tuiguangmaUrl + '044.png',
+                    url: getApp().globalData.tuiguangmaUrl + '44.jpg',
+                    name: '美味点心'
+                },
+                {
+                    id: 6,
+                    thumbnailUrl: getApp().globalData.tuiguangmaUrl + '055.png',
+                    url: getApp().globalData.tuiguangmaUrl + '11.jpg',
+                    name: '按时吃饭'
+                }
+            ]
+        }
+    },
+    methods: {
+        handSelectItem (item) {
+			let url = item.url || '';
+            uni.navigateTo({
+                url: '/pages/store/codeinfo?src=' + url + '&id='+item.id
+            });
+        }
+    },
+    onLoad () {
+    }
+}
+</script>
+<style lang="scss" scoped>
+.extension-code-page {
+    width: 100vw;
+    min-height: 100vh;
+}
+.qrcodes {
+    display: flex;
+    flex-wrap: wrap;
+    justify-content: flex-start;
+    align-items: flex-start;
+    .item {
+        width: 250rpx;
+        margin-bottom: 20rpx;
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+        justify-content: center;
+    }
+    .qrcode {
+        width: 220rpx;
+        height: 380rpx;
+        background-color: #D8E9FF;
+        margin-bottom: 10rpx;
+    }
+    .name {
+        font-size: $font-base;
+        color: $text-color;
+    }
+}
+
+</style>

+ 88 - 0
pages/store/index.vue

@@ -0,0 +1,88 @@
+<template>
+    <view class="store-page">
+        <view class="container">
+            <view class="item">
+                <view class="label">门店名称</view>
+                <view class="value">{{ storeInfo.storeName }}</view>
+            </view>
+            <view class="item">
+                <view class="label">联系电话</view>
+                <view class="value">{{ storeInfo.contactPhone }}</view>
+            </view>
+            <view class="item">
+                <view class="label">门店地址</view>
+                <view class="value">{{ storeInfo.provinceName != null ? storeInfo.provinceName : '' }}{{ storeInfo.cityName != null ? storeInfo.cityName : '' }}{{ storeInfo.address != null ? storeInfo.address : '' }}</view>
+            </view>
+            <view class="item">
+                <view class="label">店铺推广码</view>
+                <view class="value flex-end" @click="goToQrcodes">
+                    <image class="qrcode" src="../../static/imgs/mine/qrcode.png" />
+                    <u-icon name="arrow-right" size="24" color="#6c6c6c"></u-icon>
+                </view>
+            </view>
+        </view>
+    </view>
+</template>
+<script>
+export default {
+    data () {
+        return {
+            storeInfo: {
+            }
+        }
+    },
+    methods: {
+        goToQrcodes () {
+            uni.navigateTo({
+                url: '/pages/store/extensionCode'
+            })
+        }
+    },
+    onLoad (opt) {
+		console.log('opt :>> ', opt);
+		let str = decodeURIComponent(opt.data);
+		if (str != 'undefined'){
+			this.storeInfo = JSON.parse(str)
+		}
+		console.log('this.storeInfo :>> ', this.storeInfo);
+    }
+}
+</script>
+<style lang="scss" scoped>
+.store-page {
+    height: 100vh;
+    background-color: #f5f5f5;
+    border-top: 4rpx solid #ececec;
+}
+.container {
+    padding: 0 30rpx;
+    box-sizing: border-box;
+    background-color: #fff;
+}
+.item {
+    @include between;
+    min-height: 100rpx;
+    border-bottom: 4rpx solid #ececec;
+    box-sizing: border-box;
+    font-size: $font-lg;
+    padding: 20rpx 0;
+    .label {
+        width: 220rpx;
+        color: $desc-color;
+    }
+    .value {
+        width: calc(100% - 220rpx);
+        color: $text-color;
+    }
+    .qrcode {
+        width: 40rpx;
+        height: 40rpx;
+        margin: 0 20rpx;
+    }
+    .flex-end {
+        display: flex;
+        justify-content: flex-end;
+        align-items: center;
+    }
+}
+</style>

+ 69 - 0
pages/tools/bind.vue

@@ -0,0 +1,69 @@
+<template>
+    <view class="print-page">
+		<image class="pic" :src="QRCode" mode="widthFix" :show-menu-by-longpress="true"></image>
+		<view v-if="QRCode.length>0">
+			<span style="display: flex; justify-content: center;">长按识别二维码</span>
+		</view>
+    </view>
+</template>
+<script>
+import {mapState} from 'vuex';
+import {checkBox} from '../components/p-checkbox.vue'
+export default {
+	components: {
+		checkBox
+	},
+    data () {
+        return {
+			qrCodeUrl: '/storeUserLoginApi/getTempWeiXinQRCode',
+			QRCode: ''
+        }
+    },
+	computed: {
+		...mapState(['storeInfo'])
+	},
+    methods: {
+		loadData () {
+			uni.showLoading({
+				title: '加载中'
+			});
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign('' + this.storeInfo.storeUserId + dateStr);
+			this.requst({
+				url: this.qrCodeUrl,
+				data: {
+					timestamp: dateStr,
+					sign: sign,
+					storeUserId: this.storeInfo.storeUserId
+				},
+				success: res => {
+					if (res.data.rtnBean.rtnCode == 0) {
+						this.QRCode = res.data.obj
+					} else {
+						this.$api.msg(res.data.rtnBean.rtnMsg);
+					}
+					uni.hideLoading();
+				},
+				fail: (e) => {
+					console.log("接口调用失败:" + JSON.stringify(e));
+					uni.hideLoading();
+				},
+			})
+		}
+    },
+    onLoad () {
+		this.loadData()
+	}
+}
+</script>
+<style lang="scss" scoped>
+.print-page {
+    width: 100vw;
+    height: 100vh;
+    background-color: #fff;
+}
+.pic {
+	display: block;
+	margin: 0 auto;
+}
+</style>

+ 17 - 0
pages/tools/evaluation.vue

@@ -0,0 +1,17 @@
+<template>
+    <view class="evaluation-page">
+
+    </view>
+</template>
+<script>
+export default {
+    data () {
+        return {}
+    },
+    methods: {},
+    onLoad () {}
+}
+</script>
+<style lang="scss" scoped>
+
+</style>

+ 546 - 0
pages/tools/hexiao_coupon.vue

@@ -0,0 +1,546 @@
+<template>
+	<view class="index-page">
+		<view class="content-wrap color-white">
+		    <view class="scan-code" @click="saoma">
+		        <view class="label">扫码核销霸王餐券</view>
+		        <u-icon name="scan" size="24" color="#666"></u-icon>
+		    </view>
+			<!-- <swiper-item class="swiper-box1"> -->
+			    <scroll-view scroll-y class="scroll-order-list" @scrolltolower="scrolltolower">
+					<view class="order-list">
+					    <view class="item" v-for="item in couponList" :key="item.raffleRecordId">
+					        <view class="goods-wrap">
+					            <image class="goods-cover" :src="item.pictureUrl" />
+					            <view class="cont">
+									<view class="row">
+										<view class="label">霸王餐标题:{{item.title}}</view>
+									</view>
+									<view class="row">
+										<view class="label">奖品:{{item.goodsName}}</view>
+									</view>
+									<view class="row">
+										<view class="label">兑奖人:{{item.memberName ? item.memberName : ''}}</view>
+									</view>
+					                <view class="row create-time">
+					                    <view class="label">核销时间:</view>
+					                    <view class="value">{{ item.useDate }}</view>
+					                </view>
+					               <!-- 
+									<view class="row">
+										<view class="label" v-if="item.takeupCode">取餐码:{{item.takeupCode}}</view>
+									</view> -->
+					            </view>
+					        </view>
+					    </view>
+						
+					</view>
+					<view v-if="isShow" class="zanwu column">
+						<image src="../../static/empty2x.png" style="width: 280upx;height: auto" mode="widthFix">
+						</image>
+						暂无数据
+					</view>
+					<uni-load-more :status="loadingType_status" v-if="ismore"></uni-load-more>
+				</scroll-view>
+			<!-- </swiper-item> -->
+		</view>
+	</view>
+</template>
+
+<script>
+	import uniPopup from "@/components/uni-popup/uni-popup.vue"
+	import uniLoadMore from '@/components/uni-load-more/uni-load-more.vue';
+	import {mapState} from 'vuex';
+	export default {
+		components: {
+			uniPopup,
+			uniLoadMore
+		},
+		data() {
+			return {
+				listUrl: '/storeHeXiaoApi/getCouponList', 
+				checkUrl: '/storeHeXiaoApi/hxCheck', 
+				cancelurl: '/storeHeXiaoApi/surePassStoreGoods',
+				flag: false,
+				isDisable1: false,
+				page: 1,
+				couponList: [],
+				loadingType: 0,
+				isShow: false,
+				ismore: false,
+				loadingType_status: 'more',
+				errorMsg: ''
+			};
+		},
+		onLoad() {
+			
+		},
+		onShow() {
+			this.loadData(this.errorMsg);
+		},
+		computed: {
+			...mapState(['storeInfo'])
+		},
+		methods: {
+			navTo(url){
+				uni.navigateTo({
+					url:url
+				})
+			},
+			loadData(msg) {
+				uni.showLoading({
+					title: '加载中'
+				});
+				this.isShow = false
+				this.ismore = false;
+				var dateStr = (new Date()).getTime();
+				var sign = this.getSign('' + this.storeInfo.storeUserId + dateStr);
+				this.requst({
+					url: this.listUrl,
+					data: {
+						storeId: this.storeInfo.storeId,
+						storeUserId: this.storeInfo.storeUserId,
+						timestamp: dateStr,
+						sign: sign,
+						currentPage: 1
+					},
+					success: res => {
+						if (res.data.rtnBean.rtnCode == 0) {
+
+							if (res.data.list != null && res.data.list.length > 0) {
+								if (res.data.list != null && res.data.list.length > 0 && res.data.list
+									.length <= 20) {
+									this.couponList = res.data.list;
+									if (res.data.page.pageCount == 1) {
+										this.isShow = false
+										this.loadingType = 2;
+										this.loadingType_status = 'nomore';
+										this.ismore = true;
+									} else {
+										this.isShow = true
+										this.loadingType = 0;
+										this.loadingType_status = 'more';
+										this.ismore = true;
+									}
+								} else {
+									this.ismore = false;
+									this.isShow = true
+									this.loadingType = 2;
+								}
+							} else {
+								this.isShow = true
+								this.couponList = [];
+							}
+						} else {
+							this.isShow = true
+							this.orderList = [];
+						}
+						uni.stopPullDownRefresh()
+						uni.hideLoading();
+						if (msg != '' && msg != undefined && msg != null) {
+							uni.showToast({
+								icon: 'none',
+								title: msg,
+								duration: 1500
+							})
+						}
+						this.errorMsg = '';
+					},
+					fail: (e) => {
+						console.log("接口调用失败:" + JSON.stringify(e));
+						uni.stopPullDownRefresh()
+						uni.hideLoading();
+					},
+				})
+			},
+			scrolltolower() {
+				if (this.loadingType != 0) { // loadingType!=0;直接返回
+					return false
+				}
+				this.loadingType_status = 'loading';
+				this.page = this.page + 1;
+				this.loadingType = 1;
+				uni.showLoading({
+					title: '加载中'
+				});
+				var dateStr = (new Date()).getTime();
+				var sign = this.getSign('' + this.storeInfo.storeUserId + dateStr);
+				this.requst({
+					url: this.listUrl,
+					data: {
+						storeId: this.storeInfo.storeId,
+						storeUserId: this.storeInfo.storeUserId,
+						timestamp: dateStr,
+						sign: signUser,
+						currentPage: this.page
+					},
+					success: res => {
+						if (res.data.rtnBean.rtnCode == 0) {
+							if (res.data.list != "" && res.data.list != null && res.data.list.length > 0) {
+								
+								for (var i = 0; i < res.data.list.length; i++) {
+									this.couponList.push(res.data.list[i]);
+								}
+								
+								if (res.data.page.currentPage == res.data.page.pageCount) {
+									this.loadingType = 2;
+									this.loadingType_status = 'more'
+									this.ismore = false;
+								}else {
+									this.isShow = false
+									this.loadingType = 0;
+									this.loadingType_status = 'more';
+									this.ismore = true;
+								}
+							} else {
+								this.loadingType_status = 'nomore'
+								this.loadingType = 2;
+								this.ismore = false;
+							}
+						} else {
+							if (res.data.rtnBean.rtnMsg != '') {
+								this.$api.msg(res.data.rtnBean.rtnMsg);
+							}
+						}
+						uni.hideLoading();
+					},
+					fail: (e) => {
+						console.log("接口调用失败:" + JSON.stringify(e));
+						uni.hideLoading();
+					},
+				});
+			},
+			
+			saoma() {
+				uni.showLoading({
+					title: '加载中'
+				})
+				var that = this;
+				// 只允许从相机扫码
+				wx.scanCode({
+					onlyFromCamera: true,
+					scanType: 'qrCode',
+					success(res) {
+						uni.hideLoading()
+						that.isCouponCode(encodeURIComponent(res.result));
+					},fail(res) {
+						uni.hideLoading()
+					}
+				})
+			},
+			// 判断是不是核销码类型
+			isCouponCode(secreyKey) {
+				var dateStr = (new Date()).getTime();
+				var sign = this.getSign(decodeURIComponent(secreyKey) + this.storeInfo.storeUserId + dateStr);
+				let that = this
+				this.requst({
+					url: this.checkUrl,
+					data: {
+						secreyKey: decodeURIComponent(secreyKey),
+						storeId: this.storeInfo.storeId,
+						storeUserId: this.storeInfo.storeUserId,
+						timestamp: dateStr,
+						sign: sign
+					},
+					success: res => {
+						if (res.data.rtnBean.rtnCode == 0) {
+							uni.showModal({
+								title: '提示',
+								content: '是否确认核销?',
+								success: function (r) {
+									if (r.confirm) {
+										that.cancelCoupon(res.data.obj)
+									} else if (r.cancel) {
+										console.log('用户点击取消');
+									}
+								}
+							});
+						}else {
+							this.$api.msg(res.data.rtnBean.rtnMsg);
+							this.errorMsg = res.data.rtnBean.rtnMsg;
+							return;
+						}
+					},
+					fail: (e) => {
+						console.log("接口调用失败:" + JSON.stringify(e));
+					},
+				})
+			},
+
+			cancelCoupon (e) {
+				uni.showLoading({
+					title: '核销中'
+				})
+				var dateStr = (new Date()).getTime();
+				var sign = this.getSign('' + this.storeInfo.storeUserId + dateStr);
+				this.requst({
+					url: this.cancelurl,
+					data: {
+						timestamp: dateStr,
+						sign: sign,
+						id: e.raffleRecordId,
+						storeId: this.storeInfo.storeId,
+						storeUserId: this.storeInfo.storeUserId
+					},
+					success: res => {
+						
+						if (res.data.rtnBean.rtnCode == 0) {
+							uni.showToast({
+								title: '核销成功',
+								icon: 'none'
+							})
+							this.loadData()
+						} else {
+							if (res.data.rtnBean.rtnMsg != '') {
+								this.$api.msg(res.data.rtnBean.rtnMsg);
+								// return;
+							}
+						}
+						uni.hideLoading()
+					},
+					fail: (e) => {
+						uni.hideLoading()
+						console.log("接口调用失败:" + JSON.stringify(e));
+						this.handle = false;
+					},
+				});
+			},
+			onPullDownRefresh() {
+				this.loadData();
+			},
+		},
+	}
+</script>
+
+<style lang="scss" scoped>
+	.fs14 {
+	    font-size: $font-base;
+	}
+	.pd24 {
+	    padding: 0 24rpx;
+	    box-sizing: border-box;
+	}
+	.mlr24 {
+	    margin: 0 24rpx;
+	}
+	.ptb10 {
+	    padding: 10rpx 0;
+	    box-sizing: border-box;
+	}
+	.mt10 {
+	    margin-top: 10rpx;
+	}
+	.mt20 {
+	    margin-top: 20rpx;
+	}
+	.mb10 {
+	    margin-bottom: 10rpx;
+	}
+	.w100 {
+	    width: 100%;
+	}
+	.borbtm {
+	    border-bottom: 2rpx solid #ececec;
+	}
+	.text {
+	    color: $text-color;
+	}
+	.desc {
+	    color: $desc-color;
+	}
+	.index-page {
+	    width: 100vw;
+	    height: 100vh;
+	    background-color: #f5f5f5;
+	    ::-webkit-scrollbar {
+	        display: none;
+	    }
+	}
+	/deep/ .input-placeholder {
+		color: #666666
+	}
+	.tabs-wrap {
+	    padding-left: 5vw;
+	    box-sizing: border-box;
+	    background-color: #fff;
+		position: fixed;
+	    .tabs {
+	        margin-bottom: 20rpx;
+	    }
+	}
+	.content-wrap {
+	    height: calc(100vh - 80rpx);
+	    .take-food-wrap {
+	        background-color: #f5f5f5;
+	    }
+	
+	    .scan-code {
+	        @include between;
+	        height: 60rpx;
+	        margin: 0 20rpx 20rpx;
+	        padding: 0 20rpx;
+	        box-sizing: border-box;
+	        background-color: #f5f5f5;
+	        .label {
+	            font-size: $font-base;
+	            color: $icon-color;
+	        }
+	    }
+	    .item-date {
+	        height: 60rpx;
+	        background-color: #f5f5f5;
+	        @include flexStart;
+	        font-size: $font-base;
+	        padding: 0 20rpx;
+	        box-sizing: border-box;
+	    }
+	    .take-food-wrap {
+	        background-color: #fff;
+	    }
+	    .item-food-no {
+	        padding: 0 20rpx;
+	        box-sizing: border-box;
+	        border-bottom: 2rpx solid #ececec;
+	        height: 100rpx;
+	        @include between;
+	        font-size: $font-base;
+	        color: $title-color;
+	        font-weight: bold;
+	        .btn {
+	            font-weight: normal;
+	            width: 140rpx;
+	            height: 52rpx;
+	            background-color: $base-color;
+	            color: #fff;
+	            border-radius: 26rpx;
+	            @include flexCenter;
+	            font-size: $font-sm;
+	        }
+	    }
+	    .btm {
+	        font-size: $font-sm;
+	        color: #6c6c6c;
+	    }
+	}
+	.auto-taking-order {
+	    width: 100vw;
+	    height: 80rpx;
+	    @include between;
+	    padding: 0 20rpx;
+	    box-sizing: border-box;
+	    /deep/ .u-switch {
+	        transform: scale(.75) translateX(12rpx);
+	    }
+	}
+	.list-scroll-wrap {
+	    width: calc(100vw - 40rpx);
+	    margin-left: 20rpx;
+	    padding-bottom: 20rpx;
+	}
+	.color-white {
+	    background-color: #fff;
+	    padding-top: 20rpx;
+	    box-sizing: border-box;
+	}
+	.h70 {
+	    .item-food-no {
+	        height: 140rpx;
+	    }
+	    .top {
+	        margin-bottom: 10rpx;
+	    }
+	}
+	.swiper-wrap, .swiper-box1, .scroll-order-list {
+	    height: calc(100vh - 120rpx);
+	}
+	.order-list {
+	    padding: 20rpx;
+	    box-sizing: border-box;
+	    .item {
+	        background-color: #fff;
+	        border-radius: 20rpx;
+	        overflow: hidden;
+	        margin-bottom: 20rpx;
+	    }
+	    .store-wrap {
+	        border-bottom: 2rpx solid #ececec;
+	        @include between;
+	        height: 100rpx;
+	        padding: 0 20rpx;
+	        box-sizing: border-box;
+	    }
+	    .store-icon {
+	        min-width: 50rpx;
+	        width: 50rpx;
+	        height: 50rpx;
+	        border-radius: 10rpx;
+	        overflow: hidden;
+	        .img {
+	            width: 100%;
+	            height: 100%;
+	        }
+	    }
+	    .pay-state {
+	        width: 140rpx;
+	        min-width: 140rpx;
+	        color: $desc-color;
+	        text-align: right;
+	        font-size: $font-base;
+	    }
+	    .store-name {
+	        margin: 0 10rpx;
+	        @include textOverflow;
+	    }
+	    .goods-wrap {
+	        @include flexStart;
+	        padding: 20rpx 20rpx 0;
+	        box-sizing: border-box;
+	        margin-bottom: 20rpx;
+	    }
+	    .goods-cover {
+	        width: 96rpx;
+	        min-width: 96rpx;
+	        height: 96rpx;
+	        margin-right: 20rpx;
+	        border-radius: 10rpx;
+	        overflow: hidden;
+	    }
+	    .cont {
+	        width: calc(100% - 120rpx);
+	    }
+	    .row {
+	        @include flexStart;
+	        font-size: $font-base;
+	        color: $text-color;
+	    }
+	    .label {
+	        margin-right: 20rpx;
+	    }
+	    .btn-wrap {
+	        display: flex;
+	        justify-content: flex-end;
+	        align-items: center;
+	        margin: 0 20rpx 20rpx 0;
+	    }
+	    .btn {
+	        background-color: $theme-color;
+	        color: #fff;
+	        height: 60rpx;
+	        width: 180rpx;
+	        border-radius: 30rpx;
+			margin-left: 16rpx;
+	        @include flexCenter;
+	        font-size: $font-base;
+	    }
+		.btn1 {
+	        // background-color: $theme-color;
+	        color: black;
+	        height: 60rpx;
+	        width: 180rpx;
+			border: 1px solid #ccc;
+	        border-radius: 30rpx;
+			margin-left: 16rpx;
+	        @include flexCenter;
+	        font-size: $font-base;
+	    }
+	}
+</style>

+ 464 - 0
pages/tools/print.vue

@@ -0,0 +1,464 @@
+<template>
+    <view class="print-page">
+        <view class="print-list">
+            <view class="item" v-for="item in prints" :key="item.printerId">
+                <view class="row">
+                    <view class="name">
+                        <view class="text">{{ item.printerName }}</view>
+                        <view class="default" v-if="item.moren">默认</view>
+                        <view class="offline" v-if="item.status == 1">设备已离线</view>
+                        <view class="online" v-if="item.status == 2">在线</view>
+                        <view class="danger" v-if="item.status == 3">异常</view>
+						<view class="reload" @click="reload(item.printerId)">刷新</view>
+                    </view>
+                    <u-icon name="trash" size="26" color="#6c6c6c" @click="handItemRemove(item)"></u-icon>
+                </view>
+                <view class="row">
+                    <view class="no">打印机编号: {{ item.sn }}</view>
+                    <u-icon name="edit-pen" size="26" color="#6c6c6c" @click="handItemEdit(item)"></u-icon>
+                </view>
+            </view>
+        </view>
+        <view class="add-print" @click="addPrint">
+            添加小票打印机
+        </view>
+        <u-modal
+            :show="deleteVisible"
+            :title="modalTitle"
+            closeOnClickOverlay
+            showCancelButton
+            confirmColor="#d64d3b"
+            @confirm="handDelete"
+            @cancel="deleteVisible = false"
+            @close="deleteVisible = false">
+        </u-modal>
+        <u-modal
+            :show="formVisible"
+            :title="modalTitle"
+            closeOnClickOverlay
+            showCancelButton
+            @confirm="handAdd"
+            @cancel="cancelForm"
+            @close="cancelForm">
+            <view class="container">
+                <view class="item">
+                    <view class="label">名称</view>
+                    <view class="value">
+                        <u-input type="text" placeholder="请输入打印机名称" v-model="formItem.printerName"></u-input>
+                    </view>
+                </view>
+                <view class="item">
+                    <view class="label">编号</view>
+                    <view class="value">
+                        <u-input type="text" placeholder="请输入打印机编号" :disabled="flag" v-model="formItem.sn"></u-input>
+                    </view>
+                </view>
+                <view class="item">
+                    <view class="label">密钥</view>
+                    <view class="value">
+                        <u-input type="text" placeholder="请输入打印机密钥" :disabled="flag" v-model="formItem.printerKey"></u-input>
+                    </view>
+                </view>
+                <view class="item">
+                    <view class="label">打印联数</view>
+                    <view class="value">
+                        <u-input type="text" placeholder="请输入打印机联数" v-model="formItem.times"></u-input>
+                    </view>
+                </view>
+                <view class="item">
+                    <view class="label">是否默认</view>
+                    <view class="value">
+                        <!-- <u-switch v-model="formItem.moren" size="18"></u-switch> -->
+						<!-- <switch :checked="formItem.moren"   size="22" class="switch" style="transform:scale(0.7)"></switch> -->
+						<checkBox :isOpen="formItem.moren" @toggle="changMoren"></checkBox>
+                    </view>
+                </view>
+                <view class="desc">
+                    当有多台小票机时, 新订单只会在默认小票机上出票
+                </view>
+            </view>
+        </u-modal>
+    </view>
+</template>
+<script>
+import {mapState} from 'vuex';
+import {checkBox} from '../components/p-checkbox.vue'
+export default {
+	components: {
+		checkBox
+	},
+    data () {
+        return {
+			printerUrl: '/storePrintApi/getPrinterList',
+			reloadUrl: '/storePrintApi/reloadPrinter',
+			printerByIdUrl: '/storePrintApi/getPrinterById',
+			deleteUrl: '/storePrintApi/delete',
+			saveUrl: '/storePrintApi/updatePrinter',
+            modalTitle: '',
+            deleteVisible: false,
+            delItem: null,
+            formItem: {
+                printerId: '',
+				storeId: '',
+                moren: false,
+				sn: '',
+				printerKey: '',
+				printerName: '',
+				times: ''
+            },
+            formVisible: '',
+            prints: [],
+			flag: true
+        }
+    },
+	computed: {
+		...mapState(['storeInfo'])
+	},
+    methods: {
+		loadData () {
+			uni.showLoading({
+				title: '加载中'
+			});
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign('' + this.storeInfo.storeUserId + dateStr);
+			this.requst({
+				url: this.printerUrl,
+				data: {
+					timestamp: dateStr,
+					sign: sign,
+					storeId: this.storeInfo.storeId,
+					storeUserId: this.storeInfo.storeUserId
+				},
+				success: res => {
+					if (res.data.rtnBean.rtnCode == 0) {
+						this.prints = res.data.list
+					}
+					uni.stopPullDownRefresh();
+					uni.hideLoading();
+				},
+				fail: (e) => {
+					console.log("接口调用失败:" + JSON.stringify(e));
+					uni.stopPullDownRefresh()
+					uni.hideLoading();
+				},
+			})
+		},
+		getPrinter (id) {
+			uni.showLoading({
+				title: '加载中'
+			});
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign('' + this.storeInfo.storeUserId + dateStr);
+			this.requst({
+				url: this.printerByIdUrl,
+				data: {
+					timestamp: dateStr,
+					sign: sign,
+					storeId: this.storeInfo.storeId,
+					storeUserId: this.storeInfo.storeUserId,
+					id: id
+				},
+				success: res => {
+					if (res.data.rtnBean.rtnCode == 0) {
+						this.formItem = res.data.obj
+					}
+					uni.hideLoading();
+				},
+				fail: (e) => {
+					console.log("接口调用失败:" + JSON.stringify(e));
+					uni.hideLoading();
+				},
+			})
+		},
+		reload (printerId) {
+			uni.showLoading({
+				title: '加载中'
+			});
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign('' + this.storeInfo.storeUserId + dateStr);
+			this.requst({
+				url: this.reloadUrl,
+				data: {
+					timestamp: dateStr,
+					sign: sign,
+					storeId: this.storeInfo.storeId,
+					storeUserId: this.storeInfo.storeUserId,
+					printerId: printerId
+				},
+				success: res => {
+					if (res.data.rtnBean.rtnCode == 0) {
+						this.prints = res.data.list
+					}
+					uni.hideLoading();
+				},
+				fail: (e) => {
+					console.log("接口调用失败:" + JSON.stringify(e));
+					uni.hideLoading();
+				},
+			})
+		},
+        close () {
+            this.deleteVisible = false
+        },
+		cancelForm () {
+			this.formVisible = false
+			this.formItem = {
+				printerId: '',
+				storeId: '',
+				moren: false,
+				sn: '',
+				printerKey: '',
+				printerName: '',
+				times: ''
+			}
+		},
+        handItemRemove (item) {
+            this.modalTitle = `确定要删除 [${item.printerName}] 打印机吗?`
+            this.delItem = item
+            this.deleteVisible = true
+        },
+        handDelete () {
+            console.log('这里写删除接口: ', this.delItem);
+			uni.showLoading({
+				title: '删除中'
+			});
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign('' + this.storeInfo.storeUserId + dateStr);
+			this.requst({
+				url: this.deleteUrl,
+				data: {
+					timestamp: dateStr,
+					sign: sign,
+					storeId: this.storeInfo.storeId,
+					storeUserId: this.storeInfo.storeUserId,
+					id: this.delItem.printerId,
+					sn: this.delItem.sn
+				},
+				success: res => {
+					if (res.data.rtnBean.rtnCode == 0) {
+						uni.hideLoading();
+						uni.showToast({
+							title: '删除成功',
+							icon: 'none'
+						})
+						this.deleteVisible = false
+						this.delItem = {}
+						this.loadData()
+					} else {
+						uni.hideLoading();
+						this.$api.msg(res.data.rtnBean.rtnMsg);
+					}
+					
+				},
+				fail: (e) => {
+					console.log("接口调用失败:" + JSON.stringify(e));
+					uni.hideLoading();
+				}
+			})
+            this.delItem = null
+            this.modalTitle = ''
+            this.deleteVisible = false
+        },
+        handItemEdit (item) {
+			console.log('item :>> ', item);
+			this.getPrinter(item.printerId)
+            this.modalTitle = '添加/编辑打印机'
+            this.formVisible = true
+			this.flag = true
+        },
+        addPrint () {
+            this.modalTitle = '添加/编辑打印机'
+            this.formVisible = true
+			this.flag = false
+        },
+		
+        handAdd () {
+            console.log('这里写保存接口: ', this.formItem);
+			if (!this.formItem.printerName) {
+				uni.showToast({
+					title: '请输入打印机名称',
+					icon: 'none'
+				})
+				return
+			}
+			if (!this.formItem.sn) {
+				uni.showToast({
+					title: '请输入打印机编号',
+					icon: 'none'
+				})
+				return
+			}
+			if (!this.formItem.printerKey) {
+				uni.showToast({
+					title: '请输入打印机秘钥',
+					icon: 'none'
+				})
+				return
+			}
+			if (!this.formItem.times) {
+				uni.showToast({
+					title: '请输入打印机打印份数',
+					icon: 'none'
+				})
+				return
+			}
+			uni.showLoading({
+				title: '保存中'
+			});
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign('' + this.storeInfo.storeUserId + dateStr);
+			this.requst({
+				url: this.saveUrl,
+				data: {
+					timestamp: dateStr,
+					sign: sign,
+					storeId: this.storeInfo.storeId,
+					storeUserId: this.storeInfo.storeUserId,
+					printerId: this.formItem.printerId,
+					moren: this.formItem.moren ? 1 : 0,
+					sn: this.formItem.sn,
+					printerKey: this.formItem.printerKey,
+					printerName: this.formItem.printerName,
+					times: this.formItem.times,
+				},
+				success: res => {
+					if (res.data.rtnBean.rtnCode == 0) {
+						uni.hideLoading();
+						uni.showToast({
+							title: '添加成功',
+							icon: 'none'
+						})
+						this.formVisible = false
+						this.formItem = {
+							printerId: '',
+							storeId: '',
+							moren: false,
+							sn: '',
+							printerKey: '',
+							printerName: '',
+							times: ''
+						}
+						this.loadData()
+					} else {
+						uni.hideLoading();
+						this.$api.msg(res.data.rtnBean.rtnMsg);
+					}
+					
+				},
+				fail: (e) => {
+					console.log("接口调用失败:" + JSON.stringify(e));
+					uni.hideLoading();
+				}
+			})
+        },
+		changMoren (e) {
+			console.log('e :>> ', e);
+			this.formItem.moren = !this.formItem.moren
+		}
+    },
+	onPullDownRefresh() {
+		this.loadData();
+	},
+    onLoad () {
+		this.loadData()
+	}
+}
+</script>
+<style lang="scss" scoped>
+.print-page {
+    width: 100vw;
+    height: 100vh;
+    background-color: #fff;
+}
+.print-list {
+    height: calc(100vh - env(safe-area-inset-bottom) - 120rpx);
+    height: calc(100vh - constant(safe-area-inset-bottom) - 120rpx);
+    background-color: #f5f5f5;
+    padding: 20rpx;
+    box-sizing: border-box;
+    .item {
+        background-color: #fff;
+        margin-bottom: 20rpx;
+        border-radius: 10rpx;
+        padding: 20rpx;
+        display: flex;
+        flex-direction: column;
+        .row {
+            @include between;
+            color: $text-color;
+        }
+        .row:first-child {
+            margin-bottom: 20rpx;
+        }
+        .name {
+            font-size: 36rpx;
+            @include flexStart;
+        }
+        .no {
+            font-size: $font-base;
+        } 
+        .default, .offline, .online, .dange, .reload {
+            font-size: $font-sm;
+            padding: 4rpx 20rpx;
+            border-radius: 10rpx;
+            margin-left: 20rpx;
+        }
+        .default {
+            background-color: $theme-color;
+            color: #fff;
+        }
+        .offline {
+            border: 2rpx solid $uni-color-button;
+            color: $uni-color-button;
+        }
+		.online {
+            border: 2rpx solid $uni-color-success;
+            color: $uni-color-success;
+        }
+		.dange {
+            border: 2rpx solid $uni-color-button;
+            color: $uni-color-button;
+        }
+		.reload {
+            border: 2rpx solid $uni-color-primary;
+            color: $uni-color-primary;
+        }
+    }
+}
+.add-print {
+    position: fixed;
+    left: 10vw;
+    width: 80vw;
+    height: 80rpx;
+    border-radius: 40rpx;
+    @include flexCenter;
+    color: #fff;
+    background-color: $light-color;
+    bottom: calc(20rpx + env(safe-area-inset-bottom));
+    bottom: calc(20rpx + constant(safe-area-inset-bottom));
+}
+/deep/ .u-modal__content {
+    padding: 20rpx !important;
+    box-sizing: border-box !important;
+}
+.container {
+    width: 100%;
+    .name {
+        margin: 40rpx 0;
+    }
+    .item {
+        @include between;
+        margin-bottom: 20rpx;
+        .label {
+            width: 160rpx;
+        }
+        .value {
+            width: calc(100% - 160rpx);
+        }
+    }
+    .desc {
+        color: $uni-color-button;
+        font-size: $font-sm;
+    }
+}
+</style>

+ 285 - 0
pages/tools/privacyAngument.vue

@@ -0,0 +1,285 @@
+
+<template>
+    <view class="angument-page">
+        <view class="title">叮咚校送隐私政策</view>
+        <view class="version">更新日期:2020年8月11日</view>
+        <view class="publish">生效日期:2020年8月11日</view>
+        <view class="content-args">
+            叮咚校送(以下或称“我们”)非常注重保护用户(以下或
+            称“您”)的个人信息,我们深知个人信息对您的重要性,
+            并将按照法律法规要求和业界成熟的安全标准,采取相应
+            的安全保护措施来保护您的个人信息。
+            【特别提示】请您在使用我们提供的各项产品服务前,
+            仔细阅读并充分理解本《隐私政策》(重点内容我们已将
+            字体加粗请您特别关注)并作出相应选择。一旦您使用或
+            继续使用我们的产品服务时,即意味着您同意我们按照
+            本隐私政策收集、使用、保存、共享和保护您的相关信
+            息。<br/>
+            如对本隐私政策有任何疑问,您可以通过本隐私政策如
+            何联系我们中提供的方式与我们联系。<br/>
+            “叮咚校送”骑手的产品服务是遇见未来(大连)数字科技有限公司
+            的网络及技术服务提供者通过下述途径向您提供的产品
+            \服务:包括但不限于叮咚校送App以及相关微信开放平台
+            账号或小程序等。<br/>
+            本政策所称的叮咚校送及其关联方是指遇见未来(大连)数字科技有限公司
+			及现有和\或未来设立的相关关联公司的单称或
+            合称。本政策所称的代理商是指叮咚校送的平台合作伙
+            伴,被叮咚校送授权开发某一地域校园订餐配送市场,并
+            为学校食堂商家、配送员提供配套服务。<br/>
+            叮咚校送骑手所有的产品服务(包括叮咚校送提供的产
+            品\服务及未设独立隐私政策的叮咚校送关联方提供的产
+            品\服务)均适用本隐私政策。<br/>
+            请您注意,您应保证在使用叮咚校送骑手服务中所提供的
+            一切数据信息均为真实,且经过合法途径获得,如其中包
+            含第三方个人信息,请确认您已取得该信息所有方的充
+            分、合法的授权,一旦您在使用我们服务的过程中提供此
+            类信息,我们将认为该类信息均为您有权处理,不存在任
+            何违反法律、法规或侵犯任何第三方合法权利的事由/瑕
+            疵/场景。本政策不适用于您通过我们的产品服务而接入
+            的其他第三方产品服务(“其他第三方",包括但不限干您
+            的交易相对方、任何第三方网站以及第三方服务提供
+            者),具体规定请参照该第三方的隐私政策或类似声明。<br/>
+            一、我们如何收集和使用您的个人信息<br/>
+            我们会出于本政策所述的以下目的,收集和使用您的个人
+            信息:<br/>
+            (一)您向我们提供的信息<br/>
+            1、帮助您完成注册、登录、接单、配送所需要的信息
+            为完成注册\登录叮咚校送骑手账号,在线提供配送服
+            务,您需要提供账号信息(手机号码及验证码等)、网络
+            身份识别信息(如头像、姓名等)、个人实名身份信息
+            (如本人真实正面照片、身份证、健康证、实时体温、人
+            脸动态图像等)、工作地点(所在城市、校区、实时定位
+            等)。<br/>
+            您在使用叮咚校送骑手产品服务的过程中所提交的任何
+            文字、照片、视频等各种形式的信息,也可能会包含或者
+            关联到您的个人信息,我们将对于上述信息中涉及的个人
+            信息予以严格保护。<br/>
+            2、向您提供支付\结算\提现所需要的信息<br/>
+            您在叮咚校送骑手产品服务上使用支付、结算、提现功能
+            时,您需要提供微信号、叮咚校送及其关联方或者其他与
+            叮咚校送合作的第三方支付机构(如微信支付等支付通
+            道)所提供服务的支付账户信息。特别注意:叮咚校送
+            APP“我的钱包”中仅显示数据。您的实际结算方式应以您
+            与代理商签订的协议为准。<br/>
+            (二)我们在您使用服务过程中收集的信息<br/>
+            1、保障订单管理及运行安全所需要的信息<br/>
+            为了保障您能够正常使用在线接单功能并完成相应配送服
+            务,以及为了保障您的账号安全、交易安全以及系统运行
+            安全,在您使用我们的叮咚校送骑手产品服务过程中,
+            经您授权我们会获取您的设备信息,包括您使用的设备属
+            性、连接和状态信息,例如设备型号、唯一设备标识符
+            (如IMEl/androidID/IDFA/OPENUDID/GUID、SIM卡IMSI
+            信息等)、设备MAC地址、软件列表、电信运营商等。
+            为了向您提供基于位置的服务,便于您在线接单、对配送
+            订单进行管理,更好地完成配送服务、保障您的账号安全
+            与系统运行安全,我们会收集您的位置信息、设备所在位
+            置信息(如基于WiFi、GPS、蓝牙、基站、传感器信息来
+            判断您的位置)、日志信息、订单信息、网络信息、浏览
+            及搜索信息等。<br/>
+            2、为您提供客户服务所需要的信息<br/>
+            当您与我们联系时,我们可能会收集您的通信\通话记录
+            和内容或您留下的联系方式等信息,以便核实身份、与您
+            联系、帮助您解决问题,或记录相关问题的处理方案及结
+            果。<br/>
+            当您参与我们的推广活动和问卷调查过程中,我们可能会
+            收集您主动提供的相关信息等。<br/>
+            3、解决纠纷、判定责任所需要的信息<br/>
+            请您注意,在履行使用隐私号码保护订单时,您在接听和
+            拨打电话的通话过程中都可能会被录音,该录音信息的获
+            取与使用目的如下:<br/>
+            (1)经授权,当消费者、商家、骑手间出现服务纠纷
+            时,叮咚校送平台(简称“平台”)可调取并使用录音信息
+            作为解决纠纷、判定责任的参考依据。<br/>
+            (2)平台将切实保障通话双方的隐私安全,录音将上传
+            至叮咚校送服务器保存,各方无法自行下载、调取或播放
+            录音。<br/>
+            (3)除以下情形外,平台不会将录音提供给任何人(包
+            括通话主、被叫方):a.有权机关依法定程序调取;b.被
+            录音的一方持司法机关出具的法律文件依法调取。<br/>
+            (4)录音在保存7天后将自动永久删除,如遇差评、投
+            诉、尚未处理完毕的纠纷等,平台将适当延长录音保存期
+            限。<br/>
+            (5)受网络状态稳定性等限制,极端情况下,可能会出
+            现无法录音或部分录音无法听清等录音失败情形,如遇此
+            类情形,平台无需承担责任。<br/>
+            (三)我们间接收集到的您的个人信息<br/>
+            经您授权后我们会通过配送合作商 您选择的支付\结算
+            提现直接涉及的支付机构等收集与配送服务相关的身
+            份、配送、支付\结算等信息,或者将您的信息共享给上
+            述服务提供者,以验证身份并保证配送服务的顺利完成,
+            或者更好地预防诈骗、刷单等恶意行为。我们会依据法律
+            法规的要求以及与第三方的约定,经您授权后向叮咚校送
+            的关联方、合作伙伴等获得您的有关信息,并对其信息来
+            源的合法性进行确认后使用您的这些信息。<br/>
+            请注意,无法与任何特定个人直接建立联系的数据,不属
+            于个人信息。<br/>
+            (四)改进我们的产品服务<br/>
+            为改善我们的产品或服务、向您提供个性化的信息搜索及
+            交易服务,使您及时了解丰富的商品信息,提升服务体
+            验,我们会根据您的浏览及搜索记录、设备信息、位置信
+            息、订单信息,提取您的浏览、搜索偏好、行为习惯、位
+            置信息等特征,基于特征标签进行间接人群画像并展示、
+            推送相关信息,包括通过短信、电话、邮件等各种方式向
+            你推送营销活动信息、商品交易信息、促销信息等。如果
+            您不同意接收来自平台的推送信息,可以通过相应的退订
+            方式进行退订。<br/>
+            (五)为您提供安全保障<br/>
+            为提高您使用我们提供服务的安全性,更准确地预防钓鱼
+            网站欺诈和木马病毒,我们可能会使用或整合您的个人信
+            息以及我们的关联方、合作伙伴取得您授权或者依法共享
+            的信息,根据您的使用习惯和常用软件信息等来综合判断
+            您的账号及交易风险,包括验证身份,预防、发现、调查
+            可能存在的欺诈、网络病毒、网络攻击等安全风险以及违
+            反我们或关联方协议、政策或规则等行为,以保护您、其
+            他用户、我们或关联方的合法权益,并记录一些我们认为
+            有风险的链接(“URL”)。<br/>
+            (六)征得同意的例外<br/>
+            请您知悉,以下情形中,我们收集、使用个人信息无需征
+            得您的授权同意:<br/>
+            1、与国家安全、国防安全有关的;<br/>
+            2、与公共安全、公共卫生、重大公共利益有关的;<br/>
+            3、与犯罪侦查、起诉、审判和判决执行等有关的;<br/>
+            4、出于维护个人信息主体或其他个人的生命、财产等重
+            大合法权益但又很难得到本人同意的;<br/>
+            5 所收集的个人信息是个人信息主体自行向社会公众公
+            开的;<br/>
+            6 从合法公开披露的信息中收集的您的个人信息的,如
+            合法的新闻报道、政府信息公开等渠道;<br/>
+            7根据您的要求签订合同所必需的;<br/>
+            8、用于维护所提供的产品与/或服务的安全稳定运行所必
+            需的,例如发现、处置产品与/或服务的故障;<br/>
+            9、为合法的新闻报道所必需的;<br/>
+            10 学术研究机构基于公共利益开展统计或学术研究所必
+            要,且对外提供学术研究或描述的结果时,对结果中所包
+            含的个人信息进行去标识化处理的;<br/>
+            11、法律法规规定的其他情形。<br/>
+            二、我们如何共享、转让、公开披露您的个人信息<br/>
+            (一)共享<br/>
+            我们不会与平台以外的任何公司、组织和个人共享您的个
+            人信息,但以下情况除外:<br/>
+            1、基于法定情形下:根据法律法规的规定、诉讼争议解
+            决需要,或行政、司法等有权机关依法提出的要求;<br/>
+            2、与关联公司共享:为向用户提供一致化服务以及便于
+            用户进行统一管理,我们会基于必要性的前提下将您的个
+            人信息与我们的关联公司共享。<br/>
+            3、与合作伙伴分享:仅为实现本政策中声明的目的,我
+            们的某些服务将由业务合作伙伴提供,我们可能会向业务
+            合作伙伴共享为您提供服务所必要的订单信息、位置信
+            息、账户信息、支付信息等,以保障为您提供的服务顺利
+            完成(例如为了确保配送订单发布方能够与您达成配送服
+            务合作以保障订单的顺利完成,我们需要将您的配送订单
+            信息提供给配送订单发布方,又如为了方便你使用内嵌的
+            地图导航功能,需要将您的位置相关信息发送给地图服务
+            提供商用于判断您的位置)。我们的业务合作伙伴包括供
+            应商、第三方服务提供商等业务合作伙伴。<br/>
+            4、基于协议约定:依据您与我们签署的相关协议(包括在
+            线签署的电子协议及平台规则)或法律文件,有必要向第
+            三方共享时;<br/>
+            5、基于合理商业习惯:例如与第三方共享联合营销活动
+            中的中奖信息,以便第三方能及时向您发放奖品;或者在
+            叮咚校送骑手服务产品运营及技术服务提供方及关联公司
+            与其他公司进行资本市场活动(包括但不限于IPO,债券发
+            行)接受尽职调查时等;<br/>
+            6 基于学术研究;例如为科研机构开展统计或学术研究
+            之必要;<br/>
+            7、基于符合法律法规的社会公共利益等。<br/>
+            我们会通过应用程序接口(API)、软件工具开发包
+            (SDK)与第三方进行必要的数据共享,不同版本的第三
+            方SDK会有所不同,一般包括第三方账号登录类、分享
+            类、第三方支付类、地图导航类、厂商推送类、统计类、
+            语音识别类等,我们会对第三方进行严格的安全检测并约
+            定数据保护措施。<br/>
+            请您知悉,我们仅会出于合法、正当、必要、特定、明确
+            的目的共享您的个人信息,对我们仅为实现本政策中声明
+            的目的与之共享个人信息的公司、组织和个人,我们会与
+            其签署严格的信息保护和保密协定,要求他们遵守协议并
+            采取相关的安全措施来保护您的个人信息。<br/>
+            (二)转让<br/>
+            我们不会将您的个人信息转让给任何公司、组织和个人,
+            但以下情况除外:<br/>
+            1、在获取明确同意的情况下转让:获得您的明确同意
+            后,我们会向其他方转让您的个人信息;<br/>
+            2、在我们发生合并、收购或破产清算情形,或其他涉及
+            合并、收购或破产清算情形时,如涉及到个人信息转让,
+            我们会要求新的持有您个人信息的公司、组织继续受本政
+            策的约束,否则我们将要求该公司、组织和个人重新向您
+            征求授权同意。<br/>
+            (三)公开披露<br/>
+            我们不会公开披露您的个人信息,但以下情况除外:<br/>
+            1、基于您的主动选择或获得您的明确同意,我们可能会
+            公开您的相关个人信息;<br/>
+            2、基于配送订单服务的必要性及构建服务诚信环境,我
+            们可能会公开您的姓名、头像、联系方式、评价信息等;<br/>
+            3 如果我们确定您出现违反法律法规或严重违反叮咚校
+            送相关协议规则的情况,或为保护叮咚校送及其关联公司
+            用户或公众的人身财产安全免遭侵害以及维护良好的公平
+            交易环境,我们可能依据法律法规或叮咚校送相关协议规
+            则披露您的相关个人信息,例如若您要求配送不符合法律
+            规定的商品而严重违反叮咚校送配送规则时,我们可能会
+            公开披露您的主体信息与相关处罚情况。<br/>
+            4、基于法律的披露:在法律、法律程序、诉讼或政府主
+            管部门强制性要求的情况下,我们可能会公开披露您的个
+            人信息。<br/>
+            三 我们如何保护您个人信息的安全<br/>
+            为保障您的信息安全,我们努力采取各种符合业界标准的
+            物理、电子和管理方面的安全措施来保护您的个人信息,
+            防止您的个人信息遭到未经授权访问、公开披露、使用、
+            修改、损坏或丢失。我们会采取一切合理可行的措施,保
+            护您的个人信息,使用加密技术确保数据的保密性;使用
+            受信赖的保护机制防止数据遭到恶意攻击;部署访问控制
+            机制,确保只有授权人员才可访问个人信息;以及举办安
+            全和隐私保护培训课程,加强员工对于保护个人信息重要
+            性的认识。<br/>
+            在不幸发生个人信息安全事件后,我们将按照法律法规的
+            要求,及时向您告知:安全事件的基本情况和可能的影
+            响、我们已采取或将要采取的处置措施、您可自主防范和
+            降低风险的建议、对您的补救措施等。我们将及时将事件
+            相关情况以邮件、信函、电话、推送通知等方式告知您,
+            难以逐一告知个人信息主体时,我们会采取合理、有效的
+            方式发布公告。同时,我们还将按照监管部门要求,主动
+            上报个人信息安全事件的处置情况。<br/>
+            四、您的个人信息如何在全球范围转移<br/>
+            我们在中华人民共和国境内运营中收集和产生的个人信
+            息,存储在中国境内,以下情形除外:<br/>
+            1、法律法规有明确规定;<br/>
+            2、获得您的明确授权;<br/>
+            3 您通过互联网进行跨境交易等个人主动行为。<br/>
+            针对以上情形,我们会确保依据本隐私权政策对您的个人
+            信息提供足够的保护。<br/>
+            五、通知与修订<br/>
+            为给您提供更好的服务,我们的业务将不时变化,本隐私
+            政策也将随之调整。未经您明确同意,我们不会削减您依
+            据本隐私政策所应享有的权利。我们会通过在我们网站、
+            移动端上发出更新版本或以其他方式提醒您相关内容的更
+            新,也请您访问我们以便及时了解最新的隐私政策。在前
+            述情况下,若您继续使用我们的服务,即表示同意接受修
+            订后的本政策并受之约束。<br/>
+            六、如何联系我们<br/>
+            如果您对于我们的个人信息处理行为存在任何投诉举报需
+            求,您可以通过叮咚校送App内提供的联系方式或客服系
+            统与我们联系。
+        </view>
+    </view>
+</template>
+<script>
+export default {
+    data () {
+        return {}
+    },
+    methods: {},
+    onLoad () {}
+}
+</script>
+<style lang="scss" scoped>
+.angument-page {
+    font-size: $font-base;
+    padding: 20rpx 40rpx 20rpx 50rpx;
+    box-sizing: border-box;
+}
+.title {
+    color: #49BD86;
+}
+.title, .version, .publish {
+    margin-bottom: 10rpx;
+}
+</style>

+ 367 - 0
pages/tools/safety-archives.vue

@@ -0,0 +1,367 @@
+<template>
+    <view class="archives-page">
+        <scroll-view scroll-y class="scroll-content">
+			<view class="note mt">食品安全监督公示</view>
+			<view class="block-item bbline">
+				<view class="label">单位名称: </view>
+				<input class="value" maxlength="20" style="width: 100%;" v-model="info.storeInfoName"></input>
+			</view>
+			<view class="block-item bbline">
+				<view class="label">许可证号:</view>
+				<input class="value" maxlength="20" style="width: 100%;" v-model="info.licenseKey"></input>
+			</view>
+			<view class="block-item bbline">
+				<view class="label">经营地址:</view>
+				<input class="value" maxlength="20" style="width: 100%;" v-model="info.address"></input>
+			</view>
+			<view class="block-item bbline">
+				<view class="label">主体业态:</view>
+				<input class="value" maxlength="20" style="width: 100%;" v-model="info.businessFormat"></input>
+			</view>
+			<view class="block-item bbline">
+				<view class="label">经营范围:</view>
+				<input class="value" maxlength="20" style="width: 100%;" v-model="info.businessScope"></input>
+			</view>
+			<view class="block-item">
+				<view class="label">有效期:</view>
+			<view class="value" v-if="info.effectiveEndDate" @click="selectDate">
+				{{ info.effectiveEndDate|formatDate('yyyy-M-d') }}
+			</view>
+			<view class="value" v-else @click="selectDate">
+				请选择时间
+			</view>
+			
+			</view>
+			<view class="note">商家资质信息公示</view>
+			<view class="imgs-scroll">
+				<view class="imgs-list" :style="{ width: 3 * 348 + 40 + 'rpx' }">
+					<view class="item">
+						<!-- <u-image width="348rpx" height="348rpx" :src="info.licenseOneUrl"></u-image> -->
+						<view>营业执照</view>
+						<u-upload :fileList="fileList1" @afterRead="afterRead1" @delete="deletePic1" name="1"
+							:previewFullImage="true" :maxCount="1" :width="150" :height="150"></u-upload>
+					</view>
+					<view class="item">
+						<!-- <u-image width="348rpx" height="348rpx" :src="info.licenseTwoUrl"></u-image> -->
+						<view>经营许可证</view>
+						<u-upload :fileList="fileList2" @afterRead="afterRead2" @delete="deletePic2" name="2"
+							:previewFullImage="true" :maxCount="1" :width="150" :height="150"></u-upload>
+					</view>
+				</view>
+			</view>
+		</scroll-view>
+
+		<uni-calendar ref="calendar" class="uni-calendar--hook" :clear-date="true" :insert="false" @confirm="confirm" />
+
+		<view class="update-events">
+			<view class="btn" @click="save">保存</view>
+		</view>
+    </view>
+</template>
+<script>
+import {mapState} from 'vuex';
+export default {
+    data () {
+        return {
+			infoUrl: '/storeInfoManageApi/getStoreInfo',
+			uploadFileUrl: '/storeInfoManageApi/uploadUrl',
+			saveUrl: '/storeInfoManageApi/update',
+			storeId: '',
+			fileList1: [],
+			fileList2: [],
+			list: [],
+			info: {
+				storeInfoId: '',
+				licenseOneUrl: '',
+				licenseTwoUrl: '',
+				licenseKey: '',
+				storeInfoName: '',
+				address: '',
+				businessFormat: '',
+				businessScope: '',
+				effectiveEndDate: ''
+			},
+			file1: {},
+			file2: {},
+        }
+    },
+	computed: {
+		...mapState(['storeInfo'])
+	},
+    methods: {
+		getStoreInfo() {
+			uni.showLoading({
+				title: '加载中'
+			});
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign('' + this.storeInfo.storeUserId + dateStr);
+			this.requst({
+				url: this.infoUrl,
+				data: {
+					timestamp: dateStr,
+					sign: sign,
+					storeId: this.storeInfo.storeId,
+					storeUserId: this.storeInfo.storeUserId
+				},
+				success: res => {
+					if (res.data.rtnBean.rtnCode == 0) {
+						this.info = res.data.obj
+						let picUrl1 = this.info.licenseOneUrl;
+						if (picUrl1 !== null && picUrl1 !== '' && picUrl1 !== undefined){
+							this.fileList1.push({
+								url: picUrl1
+							})
+						}
+						
+						let picUrl2 = this.info.licenseTwoUrl;
+						if (picUrl2 !== null && picUrl2 !== '' && picUrl2 !== undefined){
+							this.fileList2.push({
+								url: picUrl2
+							})
+						}	
+					}
+					uni.hideLoading();
+				},
+				fail: (e) => {
+					console.log("接口调用失败:" + JSON.stringify(e));
+					uni.hideLoading();
+				},
+			})
+		},
+		// 删除图片
+		deletePic1(event) {
+			this.fileList1.splice(event.index, 1)
+			this.info.licenseOneUrl = ''
+		},
+		deletePic2(event) {
+			this.fileList2.splice(event.index, 1)
+			this.info.licenseTwoUrl = ''
+		},
+		// 新增图片
+		async afterRead1(event) {
+			// console.log('event :>> ', event);
+			this.file1 = event.file
+			let file = event.file
+			let fileListLen = this.fileList1.length
+			// lists.map((item) => {
+			this.fileList1.push({
+				...file,
+				status: 'loading',
+				// message: '上传中'
+			})
+			this.info.licenseOneUrl = this.file1.url
+			this.uploadFile(1,this.file1.url)
+			
+	
+		},
+		uploadFile(type,url) {
+			uni.showLoading({
+				title: '上传中'
+			});
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign('' + this.storeInfo.storeUserId + dateStr);
+			return new Promise((resolve, reject) => {
+				let a = uni.uploadFile({
+					url: this.storeInfo.code + this.uploadFileUrl,
+					filePath: url,
+					name: 'file',
+					formData: {
+						timestamp: dateStr,
+						sign: sign,
+						storeId: this.storeInfo.storeId,
+						storeUserId: this.storeInfo.storeUserId,
+						type: type
+					},
+					success: (res) => {
+						let data = JSON.parse(res.data)
+						if (data.rtnBean.rtnCode == 0) {
+							uni.hideLoading();
+							uni.showToast({
+								title: "上传成功",
+								duration: 3000,
+								icon: 'none',
+							});
+
+						} else {
+							uni.hideLoading();
+							if (data.rtnBean.rtnMsg != '') {
+								this.$api.msg(data.rtnBean.rtnMsg);
+							}
+						}
+						
+					},
+					fail: (res) => {
+						uni.hideLoading();
+					}
+				});
+			})
+		},
+		async afterRead2(event) {
+			console.log('event :>> ', event);
+			this.file2 = event.file
+			let file = event.file
+			let fileListLen = this.fileList2.length
+			// lists.map((item) => {
+			this.fileList2.push({
+				...file,
+				status: 'loading',
+				// message: '上传中'
+			})
+			this.info.licenseTwoUrl = this.file2.url
+			this.uploadFile(2,this.file2.url)
+		
+		},
+		selectDate() {
+		    this.$refs.calendar.open();
+		},
+		confirm(e) {
+			console.log('e :>> ', e);
+			if (e.fulldate != '' && e.fulldate != undefined && e.fulldate != null ) {
+				this.info.effectiveEndDate = e.fulldate
+			}
+		},
+		save () {
+			if (this.info.storeInfoName == null || this.info.storeInfoName == '') {
+				this.$api.msg("请输入单位名称");
+				return;
+			} else if (this.info.licenseKey == null || this.info.licenseKey == '') {
+				this.$api.msg("请输入许可证号");
+				return;
+			} else if (this.info.address == null || this.info.address == '') {
+				this.$api.msg("请输入经营地址");
+				return;
+			} else if (this.info.businessFormat == null || this.info.businessFormat == '') {
+				this.$api.msg("请输入主体业态");
+				return;
+			}else if (this.info.businessScope == null  || this.info.businessScope == '') {
+				this.$api.msg("请输入经营范围");
+				return;
+			} else if (this.info.effectiveEndDate == null || this.info.effectiveEndDate == '') {
+				this.$api.msg("请选择有效期");
+				return;
+			} else if (this.info.licenseOneUrl == null || this.info.licenseOneUrl == '') {
+				this.$api.msg("请上传营业执照");
+				return;
+			}else if (this.info.licenseTwoUrl == null || this.info.licenseTwoUrl == '') {
+				this.$api.msg("请上传经营许可证");
+				return;
+			}else{
+				uni.showLoading({
+					title: '保存中'
+				});
+				var dateStr = (new Date()).getTime();
+				var sign = this.getSign('' + this.storeInfo.storeUserId + dateStr);
+				
+				this.requst({
+					url: this.saveUrl,
+					data: {
+						timestamp: dateStr,
+						sign: sign,
+						storeId: this.storeInfo.storeId,
+						storeUserId: this.storeInfo.storeUserId,
+						storeInfoName: this.info.storeInfoName,
+						licenseKey: this.info.licenseKey,
+						address: this.info.address,
+						businessFormat: this.info.businessFormat,
+						businessScope: this.info.businessScope,
+						effectiveEndDate: this.info.effectiveEndDate,
+					},
+					success: res => {
+						if (res.data.rtnBean.rtnCode == 0) {
+							uni.showToast({
+								title: "保存成功",
+								duration: 3000,
+								icon: 'none',
+							});
+						} else {
+							if (res.data.rtnBean.rtnMsg != '') {
+								this.$api.msg(res.data.rtnBean.rtnMsg);
+								return;
+							}
+						}
+						uni.hideLoading();
+					},
+					fail: (e) => {
+						this.isDisable = false;
+						console.log("接口调用失败:" + JSON.stringify(e));
+						uni.hideLoading();
+					},
+				});
+					
+		
+				
+			}
+			// uni.navigateBack();
+		}
+	},
+    onLoad (options) {
+        this.getStoreInfo()
+    }
+}
+</script>
+<style lang="scss" scoped>
+.scroll-content {
+    height: calc(100vh - 120rpx - env(safe-area-inset-bottom));
+    height: calc(100vh - 120rpx - constant(safe-area-inset-bottom));
+}
+.note {
+    width: 100%;
+    height: 80upx;
+    @include flexStart;
+    background-color: $bg-color;
+    color: $other-color;
+    padding-left: 20upx;
+    box-sizing: border-box;
+    font-size: $font-base;
+
+    background-color: rgba(245, 245, 245, 1);
+    color: rgba(108, 108, 108, 1);
+}
+.block-item {
+    margin: 0 20upx;
+    padding: 24upx 0;
+    box-sizing: border-box;
+    @include flexStart(flex-start);
+    font-size: $font-sm;
+    .label {
+        width: 140upx;
+        min-width: 140upx;
+        color: $desc-color;
+    }
+    .value {
+        color: $title-color;
+    }
+}
+.imgs-scroll {
+    width: 100vw;
+    height: 348rpx;
+    padding: 0 20rpx;
+    box-sizing: border-box;
+    margin-top: 20rpx;
+}
+.imgs-list {
+    height: 348rpx;
+    overflow: hidden;
+    display: flex;
+    flex-wrap: nowrap;
+}
+.item {
+    width: 348rpx;
+    height: 348rpx;
+    margin-right: 20upx;
+}
+.update-events {
+    width: 100vw;
+    @include flexCenter;
+	margin-top: 40rpx;
+    .btn {
+        width: 80vw;
+        height: 80rpx;
+        border-radius: 40rpx;
+        background-color: $theme-color;
+        color: #fff;
+        @include flexCenter;
+    }
+}
+
+</style>

+ 295 - 0
pages/tools/setting.vue

@@ -0,0 +1,295 @@
+<template>
+    <view class="setting-page">
+        
+		<scroll-view scroll-y class="scroll-content">
+			<view class="cell">
+				<view class="label">自动接单</view>
+				<view class="value">
+					<!-- <u-switch v-model="autoTake" size="22" class="switch"></u-switch> -->
+					<switch :checked="autoTaking" @change="handAutoTakingChange" color="#007AFF" class="switch" style="transform:scale(0.7)"></switch>
+				</view>
+			</view>
+			<view class="cell">
+				<view class="label">食品安全档案</view>
+				<view class="value" @click="goToSafetyArchives">
+					<u-icon name="arrow-right" size="22" color="#6c6c6c"></u-icon>
+				</view>
+			</view>
+			<view class="cell">
+				<view class="label">修改密码</view>
+				<view class="value" @click="goToUpdatePwd">
+					<u-icon name="arrow-right" size="22" color="#6c6c6c"></u-icon>
+				</view>
+			</view>
+		</scroll-view>
+
+        <view class="footer-btn">
+            <view class="logout" @click="showModalDialog">
+                退出登录
+            </view>
+        </view>
+
+		<u-modal :show="formVisible" :title="modalTitle" closeOnClickOverlay showCancelButton @confirm="updatePwd"
+			@cancel="cancelUpdate" @close="cancelUpdate">
+			<view class="container">
+				<view class="name">
+					<u-input border v-model="formItem.oldPwd" password placeholder="请输入原密码"></u-input>
+				</view>
+				<view></view>
+				<view class="name">
+					<u-input border v-model="formItem.newPwd1" password placeholder="请输入新密码"></u-input>
+				</view>
+				<view></view>
+				<view class="name">
+					<u-input border v-model="formItem.newPwd2" password placeholder="请确认新密码"></u-input>
+				</view>
+			</view>
+		</u-modal>
+		<!-- <u-modal show-cancel-button :show="quitShow" @confirm="confirm" :confirm-color="lightColor" @close="closeMo" @cancel="closeMo"
+		  :content="'确定要退出登录么?'"></u-modal> -->
+    </view>
+</template>
+<script>
+import {mapState} from 'vuex';
+export default {
+    data () {
+        return {
+			updateUrl: '/storeOrderApi/updateStoreAutoGo',
+			autoGoUrl: '/storeOrderApi/getStoreAutoGo',
+			updatePwdUrl: '/storeUserLoginApi/updatePwd',
+            autoTaking: false,
+			quitShow: false,
+			formItem: {},
+			formVisible: false,
+			modalTitle: '修改密码'
+        }
+    },
+	computed: {
+		...mapState(['storeInfo'])
+	},
+    methods: {
+		getStoreAutoGo() {
+			uni.showLoading({
+				title: '加载中'
+			});
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign(dateStr);
+			this.requst({
+				url: this.autoGoUrl,
+				data: {
+					timestamp: dateStr,
+					sign: sign,
+					storeId: this.storeInfo.storeId
+				},
+				success: res => {
+					if (res.data.rtnBean.rtnCode == 0) {
+						uni.hideLoading();
+						this.autoTaking = res.data.obj.autoGo;
+					} else {
+						uni.hideLoading();
+						if (res.data.rtnBean.rtnMsg != '') {
+							this.$api.msg(res.data.rtnBean.rtnMsg);
+							return;
+						}
+					}
+					
+				},
+				fail: (e) => {
+					console.log("接口调用失败:" + JSON.stringify(e));
+					uni.hideLoading();
+				},
+			});
+		},
+		// 自动接单切换
+		handAutoTakingChange (e) {
+			uni.showLoading({
+				title: '加载中'
+			});
+			this.autoTaking = e.detail.value
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign(dateStr);
+			this.requst({
+				url: this.updateUrl,
+				data: {
+					timestamp: dateStr,
+					sign: sign,
+					storeId: this.storeInfo.storeId,
+					autoGo: this.autoTaking ? 1 : 0
+				},
+				success: res => {
+					uni.hideLoading();
+				},
+				fail: (e) => {
+					console.log("接口调用失败:" + JSON.stringify(e));
+					uni.hideLoading();
+				},
+			});
+		},
+		/**
+		 * 显示退出登录对话框
+		 */
+		showModalDialog() {
+			let that = this
+			uni.showModal({
+				title: '提示',
+				content: '是否确认退出?',
+				success: function (r) {
+					if (r.confirm) {
+						that.confirm()
+					} else if (r.cancel) {
+						console.log('用户点击取消');
+					}
+				}
+			});
+		    // this.quitShow = true;
+		},
+        goToSafetyArchives () {
+            uni.navigateTo({
+                url: '/pages/tools/safety-archives'
+            })
+        },
+		goToUpdatePwd () {
+           this.formVisible = true
+        },
+		closeMo () {
+			this.quitShow = false
+		},
+		
+		updatePwd() {
+			if (!this.formItem.oldPwd) {
+				uni.showToast({
+					title: '请输入原密码',
+					icon: 'none'
+				})
+				return
+			}
+			if (!this.formItem.newPwd1) {
+				uni.showToast({
+					title: '请输入新密码',
+					icon: 'none'
+				})
+				return
+			}
+			if (!this.formItem.newPwd2) {
+				uni.showToast({
+					title: '请确认新密码',
+					icon: 'none'
+				})
+				return
+			}
+			if (this.formItem.newPwd1 != this.formItem.newPwd2) {
+				uni.showToast({
+					title: '两次新密码不一致',
+					icon: 'none'
+				})
+				return
+			}
+			uni.showLoading({
+				title: '加载中'
+			})
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign('' + this.storeInfo.storeUserId + dateStr);
+			this.requst({
+				url: this.updatePwdUrl,
+				data: {
+					timestamp: dateStr,
+					sign: sign,
+					storeId: this.storeInfo.storeId,
+					storeUserId: this.storeInfo.storeUserId,
+					oldPwd: this.formItem.oldPwd,
+					newPwd: this.formItem.newPwd1
+				},
+				success: res => {
+					if (res.data.rtnBean.rtnCode == 0) {
+						this.formVisible = false
+						this.formItem = {
+							storeUserId: '',
+							userName: '',
+							tel: ''
+						}
+					} else {
+						if (res.data.rtnBean.rtnMsg != '') {
+							this.$api.msg(res.data.rtnBean.rtnMsg);
+							return;
+						}
+					}
+					uni.hideLoading()
+				},
+				fail: (e) => {
+					console.log("接口调用失败:" + JSON.stringify(e));
+					uni.hideLoading()
+				}
+			})
+		},
+		cancelUpdate () {
+			this.formVisible = false;
+			this.formItem = {}
+		},
+		
+		/**
+		 * 确认退出
+		 * 清除缓存重新登录
+		 */
+		confirm() {
+			// this.quitShow = false
+			// uni.removeStorageSync('riderInfo')
+			// console.log('111 :>> ', 111);
+			uni.clearStorage()
+			uni.reLaunch({ 
+				url: '/pages/login/login'
+			});
+		}
+    },
+    onLoad () {
+		this.getStoreAutoGo();
+	}
+}
+</script>
+<style lang="scss" scoped>
+.setting-page {
+    width: 100vw;
+    height: 100vh;
+    background-color: #f5f5f5;
+}
+
+.scroll-content {
+    height: calc(100vh - 120rpx - env(safe-area-inset-bottom));
+    height: calc(100vh - 120rpx - constant(safe-area-inset-bottom));
+}
+.cell {
+    width: 100%;
+    height: 100rpx;
+    background-color: #fff;
+    padding: 0 20rpx;
+    box-sizing: border-box;
+    @include between;
+    border-top: 2rpx solid #ececec;
+    .label {
+        width: 200rpx;
+    }
+    .value {
+        flex: 1;
+        @include flexEnd;
+    }
+}
+.footer-btn {
+    width: 100vw;
+	margin-top: 40rpx;
+    @include flexCenter;
+    .logout {
+        width: 80%;
+        height: 80rpx;
+        border-radius: 40rpx;
+        background-color: $theme-color;
+        color: #fff;
+        @include flexCenter;
+    }
+}
+.container {
+	.name {
+		margin: 40rpx 0;
+		border: 2rpx solid #ececec;
+		border-radius: 6rpx;
+	}
+}
+</style>

+ 227 - 0
pages/tools/settle-info.vue

@@ -0,0 +1,227 @@
+<template>
+    <view class="settle-info-page">
+        <view class="settle-date">
+            {{ order.date }}
+        </view>
+        <view class="curr-day">
+            <view class="item">
+                <view class="label">当日收入(元)</view>
+                <view class="value">{{moneyConverter(order.money)}}</view>
+            </view>
+            <view class="item">
+                <view class="label">完成单量</view>
+                <view class="value">{{order.orderNum}}</view>
+            </view>
+        </view>
+        <view class="order-info" v-for="item in orderList" :key="item.orderId">
+            <view class="order-top">
+                <view class="order-no">订单编号: {{ item.orderCode }}</view>
+                <view class="more" @click="goToOrderSearch(item.orderCode)">{{item.statusStr}} <u-icon name="arrow-right" size="20" color="#6c6c6c"></u-icon></view>
+            </view>
+            <view class="order-title">收入明细</view>
+            <view class="order-dis-price">
+                <view class="item">
+                    <view class="label">订单金额</view>
+                    <view class="price sr">¥{{moneyConverter(item.orderFee)}}</view>
+                </view>
+                <view class="item">
+                    <view class="label">扣除配送费</view>
+                    <view class="price">-¥{{item.status > -50 && item.status < 0 ? 0 : moneyConverter(item.deliveryFee + item.sortingFee + item.climbingFee)}}</view>
+                </view>
+                <view class="item">
+                    <view class="label">微信扣点</view>
+                    <view class="price">-¥{{item.status < 0 ? 0 : moneyConverter(calcPoints(item.wxPoint / 1000, item.orderFee))}}</view>
+                </view>
+                <view class="item">
+                    <view class="label">平台扣点</view>
+                    <view class="price">-¥{{ commissionPoint(item) }}</view>
+                </view>
+				<view class="item" v-if="item.status < 0">
+				    <view class="label">退款金额</view>
+				    <view class="price">-¥{{moneyConverter(item.refundFee)}}</view>
+				</view>
+            </view>
+            <view class="curr-order">
+                <view class="label">本单收入</view>
+                <view class="price">{{item.status == -50 ? '-' : ''}}¥{{incomeFormatter(item)}}</view>
+            </view>
+        </view>
+    </view>
+</template>
+<script>
+	import {mapState} from 'vuex';
+export default {
+    data () {
+        return {
+			listUrl: '/storeOrderApi/getOrderListForStoreSearch',
+            order: {
+                date: '',
+            },
+			orderList: []
+        }
+    },
+	computed: {
+		...mapState(['storeInfo'])
+	},
+    methods: {
+        goToOrderSearch (no) {
+            uni.navigateTo({
+                url: `/pages/search/index?no=${no}`
+            });
+        },
+		loadData() {
+			uni.showLoading({
+				title: '加载中'
+			});
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign(dateStr);
+			let that = this;
+			this.requst({
+				url: this.listUrl,
+				data: {
+					timestamp: dateStr,
+					sign: sign,
+					storeId: this.storeInfo.storeId,
+					dateStr: this.order.date,
+					type: 1
+				},
+				success: res => {
+					if (res.data.rtnBean.rtnCode == 0) {
+						uni.hideLoading();
+						this.orderList = res.data.list;
+					} else {
+						if (res.data.rtnBean.rtnMsg != '') {
+							uni.hideLoading();
+							this.$api.msg(res.data.rtnBean.rtnMsg);
+							return;
+						}
+					}
+					
+				},
+				fail: (e) => {
+					console.log("接口调用失败:" + JSON.stringify(e));
+					uni.hideLoading();
+				},
+			})
+			
+		},
+		calcPoints(rate, orderFee) {
+		    return (Number(orderFee) * Number(rate));
+		},
+		toFixed2(money) {
+			return Math.round(money * 100) / 100
+		},
+		commissionPoint(item) {
+			let point = item.status < 0 ? 0 : this.moneyConverter(this.calcPoints(Number(item.commissionRate) / Number(1000) , item.orderFee - item.deliveryFee - item.sortingFee - item.climbingFee));
+			return point > 0 ? point : 0;
+		},
+		incomeFormatter(item) {
+			if (item.status == -40 || item.status == -20 || item.status == -10 || item.status == -1) {
+				return 0;
+			} else if (item.status == -50) {
+				return this.moneyConverter(item.sortingFee + item.deliveryFee + item.climbingFee)
+			} else {
+				return this.toFixed2(this.moneyConverter(item.orderFee)
+				- this.moneyConverter(item.deliveryFee + item.sortingFee + item.climbingFee) 
+				- this.moneyConverter(this.calcPoints(item.wxPoint / 1000, item.orderFee)) 
+				- this.commissionPoint(item))
+			}
+		}
+    },
+    onLoad (opt) {
+        this.order.money = opt.money
+		this.order.orderNum = opt.orderNum
+        this.order.date = opt.time
+		this.loadData()
+    }
+}
+</script>
+<style lang="scss" scoped>
+.settle-info-page {
+    min-height: 100vh;
+    background-color: #f5f5f5;
+    border-top: 2rpx solid #f5f5f5;
+}
+.settle-date {
+    font-size: $font-base;
+    margin: 10rpx 0;
+    padding: 0 20rpx;
+    box-sizing: border-box;
+}
+.curr-day {
+    width: calc(100% - 40rpx);
+    margin: 0 20rpx 26rpx;
+    @include flexCenter;
+    background-color: #fff;
+    border-radius: 10rpx;
+    padding: 20rpx;
+    box-sizing: border-box;
+    height: 186rpx;
+    .item {
+        flex: 1;
+        text-align: center;
+    }
+    .label {
+        margin-bottom: 30rpx;
+        font-size: $font-base;
+        color: $text-color;
+    }
+    .value {
+        color: $uni-color-button;
+        font-size: 36rpx;
+        font-weight: bold;
+    }
+}
+.order-info {
+    width: calc(100vw - 40rpx);
+    margin: 0 20rpx;
+    background-color: #fff;
+    border-radius: 10rpx;
+    padding: 20rpx;
+    box-sizing: border-box;
+	margin-bottom: 20rpx;
+    .order-top {
+        @include between;
+        font-size: $font-base;
+        border-bottom: 2rpx solid #ececec;
+        padding-bottom: 20rpx;
+        box-sizing: border-box;
+    }
+    .more {
+        @include between(flex-end);
+    }
+    .order-title {
+        font-weight: bold;
+        color: #000;
+        font-size: $font-base;
+        margin: 20rpx 0;
+    }
+}
+.order-dis-price {
+    border-bottom: 2rpx solid #ececec;
+    margin-bottom: 20rpx;
+    .item {
+        @include between;
+        font-size: $font-base;
+        margin-bottom: 20rpx;
+    }
+    .label {
+        color: $desc-color;
+    }
+    .price.sr {
+        color: $uni-color-button;
+    }
+}
+.curr-order {
+    @include flexEnd;
+    font-size: 36rpx;
+    font-weight: bold;
+    .label {
+        margin-right: 10rpx;
+        color: $title-color;
+    }
+    .price {
+        color: $uni-color-button;
+    }
+}
+</style>

+ 168 - 0
pages/tools/settle-order.vue

@@ -0,0 +1,168 @@
+<template>
+    <view class="settle-page">
+        <view class="settle-info">
+            <view class="label">总收入</view>
+            <view class="not-settle-price">¥{{moneyConverter(form.unSettleMoney)}}</view>
+        </view>
+        <view class="settle-tabs" style="padding-top: 20upx;">
+			<view class="tabs-wrap" style="padding: 0 20upx;">
+			    <uni-datetime-picker v-model="range" type="daterange" @change="dateChange" />
+			</view>
+            <scroll-view class="settle-list" :scroll-y="true">
+                <view class="line"></view>
+                <view class="item" v-for="item in settleList" :key="item.accountDate" @click="handItemInfo(item)">
+                    <view class="label">{{ item.accountDate }}</view>
+                    <view class="value">
+                        <view class="price" :class="{ sr: item.actualIncom > 0 }">{{ item.actualIncom > 0 ? '+¥' : '-¥' }}{{ moneyConverter(Math.abs(item.actualIncom)) }}</view>
+                        <u-icon class="more" name="arrow-right" size="22" color="#6c6c6c"></u-icon>
+                    </view>
+                </view>
+            </scroll-view>
+        </view>
+    </view>
+</template>
+<script>
+import {mapState} from 'vuex';
+export default {
+    data () {
+        return {
+			listUrl: '/storeSettlementApi/getStoreStatementListForStore',
+            active: 0,
+            itemStyle: {
+                width: 'calc(50vw - 20rpx)',
+                height: '80rpx'
+            },
+			form: {
+				settledMoney: 0,
+				unSettleMoney: 0
+			},
+            settleList: [],
+			range: []
+        }
+    },
+	computed: {
+		...mapState(['storeInfo'])
+	},
+    methods: {
+        handItemInfo (item) {
+            uni.navigateTo({
+                url: `/pages/tools/settle-info?time=${item.accountDate}&money=${item.actualIncom}&orderNum=${item.orderNum}`
+            });
+        },
+		dateChange(dateRange) {
+			this.range = dateRange;
+			this.loadData()
+		},
+		activeChange(e) {
+			this.active = e.index
+			this.loadData()
+		},
+		loadData() {
+			uni.showLoading({
+				title: '加载中'
+			});
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign(dateStr);
+			
+			let that = this;
+			let startTime = "";
+			let endTime = "";
+			if (this.range.length > 0) {
+				startTime = this.range[0];
+				endTime = this.range[1];
+			}
+			this.requst({
+				url: this.listUrl,
+				data: {
+					timestamp: dateStr,
+					sign: sign,
+					storeId: this.storeInfo.storeId,
+					startTime: startTime,
+					endTime: endTime
+				},
+				success: res => {
+					if (res.data.rtnBean.rtnCode == 0) {
+						uni.hideLoading();
+						this.settleList = res.data.list;
+						this.form = res.data.obj;
+					} else {
+						uni.hideLoading();
+						if (res.data.rtnBean.rtnMsg != '') {
+							this.$api.msg(res.data.rtnBean.rtnMsg);
+							return;
+						}
+					}
+					
+				},
+				fail: (e) => {
+					console.log("接口调用失败:" + JSON.stringify(e));
+					uni.hideLoading();
+				},
+			})
+		}
+    },
+    onLoad () {
+		this.loadData()
+	}
+}
+</script>
+<style lang="scss" scoped>
+.settle-page {
+    min-height: 100vh;
+    background-color: #f5f5f5;
+}
+.settle-info {
+    background-color: $theme-color;
+    height: 324rpx;
+    color: #fff;
+    padding: 40rpx 40rpx 70rpx;
+    box-sizing: border-box;
+    border-radius: 0 0 20rpx 20rpx;
+    .not-settle-price {
+        font-weight: bold;
+        font-size: 56rpx;
+        text-align: center;
+        margin: 10rpx 0 42rpx;
+    }
+    .yjs {
+        font-size: $font-base;
+    }
+}
+.settle-tabs {
+    width: calc(100vw - 40rpx);
+    height: calc(100vh - 324rpx - constant(safe-area-inset-bottom));
+    height: calc(100vh - 324rpx - env(safe-area-inset-bottom));
+    margin-left: 20rpx;
+    border-radius: 10rpx;
+    position: relative;
+    top: -40rpx;
+    background-color: #fff;
+}
+.settle-list {
+    width: 100%;
+	height: calc(100vh - 444rpx - constant(safe-area-inset-bottom));
+	height: calc(100vh - 444rpx - env(safe-area-inset-bottom));
+    padding: 0 20rpx;
+    box-sizing: border-box;
+    margin-top: -2rpx;
+    .line {
+        height: 2rpx;
+        width: 100%;
+        background-color: #ececec;
+    }
+    .item {
+        border-bottom: 2rpx solid #ececec;
+        height: 100rpx;
+        @include between;
+    }
+    .value {
+        @include between(flex-end);
+    }
+    .price {
+        margin-right: 10rpx;
+    }
+    .price.sr {
+        color: $uni-color-button;
+    }
+}
+</style>

+ 173 - 0
pages/tools/settle.vue

@@ -0,0 +1,173 @@
+<template>
+    <view class="settle-page">
+        <view class="settle-info">
+            <view class="label">未结算金额</view>
+            <view class="not-settle-price">¥{{moneyConverter(form.unSettleMoney)}}</view>
+            <view class="label yjs">已结算金额: ¥{{moneyConverter(form.settledMoney)}}</view>
+        </view>
+        <view class="settle-tabs">
+            <view class="tabs-wrap">
+                <u-tabs
+                    class="tabs"
+                    :list="tabs"
+                    :activeStyle="{ color: '#1A78F6' }"
+                    :itemStyle="itemStyle"
+                    :is-scroll="false"
+					@change="activeChange"
+                    :current="active">
+                </u-tabs>
+            </view>
+            <scroll-view class="settle-list" :scroll-y="true">
+                <view class="line"></view>
+                <view class="item" v-for="item in settleList" :key="item.accountDate" @click="handItemInfo(item)">
+                    <view class="label">{{ item.accountDate }}</view>
+                    <view class="value">
+                        <view class="price" :class="{ sr: item.actualIncom > 0 }">{{ item.actualIncom > 0 ? '+¥' : '-¥' }}{{ moneyConverter(Math.abs(item.actualIncom)) }}</view>
+                        <u-icon class="more" name="arrow-right" size="22" color="#6c6c6c"></u-icon>
+                    </view>
+                </view>
+            </scroll-view>
+        </view>
+    </view>
+</template>
+<script>
+import {mapState} from 'vuex';
+export default {
+    data () {
+        return {
+			listUrl: '/storeSettlementApi/getStoreStatementListForStore',
+            active: 0,
+            itemStyle: {
+                width: 'calc(50vw - 20rpx)',
+                height: '80rpx'
+            },
+            tabs: [
+                {
+                    name: '未结算金额'
+                },
+                {
+                    name: '已结算金额'
+                }
+            ],
+			form: {
+				settledMoney: 0,
+				unSettleMoney: 0
+			},
+            settleList: []
+        }
+    },
+	computed: {
+		...mapState(['storeInfo'])
+	},
+    methods: {
+        handItemInfo (item) {
+            uni.navigateTo({
+                url: `/pages/tools/settle-info?time=${item.accountDate}&money=${item.actualIncom}&orderNum=${item.orderNum}`
+            });
+        },
+		activeChange(e) {
+			this.active = e.index
+			this.loadData()
+		},
+		loadData() {
+			uni.showLoading({
+				title: '加载中'
+			});
+			var dateStr = (new Date()).getTime();
+			var sign = this.getSign(dateStr);
+			
+			let that = this;
+			this.requst({
+				url: this.listUrl,
+				data: {
+					timestamp: dateStr,
+					sign: sign,
+					storeId: this.storeInfo.storeId,
+					status: this.active,
+				},
+				success: res => {
+					if (res.data.rtnBean.rtnCode == 0) {
+						uni.hideLoading();
+						this.settleList = res.data.list;
+						this.form = res.data.obj;
+					} else {
+						uni.hideLoading();
+						if (res.data.rtnBean.rtnMsg != '') {
+							this.$api.msg(res.data.rtnBean.rtnMsg);
+							return;
+						}
+					}
+					
+				},
+				fail: (e) => {
+					console.log("接口调用失败:" + JSON.stringify(e));
+					uni.hideLoading();
+				},
+			})
+		}
+    },
+    onLoad () {
+		this.loadData()
+	}
+}
+</script>
+<style lang="scss" scoped>
+.settle-page {
+    min-height: 100vh;
+    background-color: #f5f5f5;
+}
+.settle-info {
+    background-color: $theme-color;
+    height: 324rpx;
+    color: #fff;
+    padding: 40rpx 40rpx 70rpx;
+    box-sizing: border-box;
+    border-radius: 0 0 20rpx 20rpx;
+    .not-settle-price {
+        font-weight: bold;
+        font-size: 56rpx;
+        text-align: center;
+        margin: 10rpx 0 42rpx;
+    }
+    .yjs {
+        font-size: $font-base;
+    }
+}
+.settle-tabs {
+    width: calc(100vw - 40rpx);
+    height: calc(100vh - 324rpx - constant(safe-area-inset-bottom));
+    height: calc(100vh - 324rpx - env(safe-area-inset-bottom));
+    margin-left: 20rpx;
+    border-radius: 10rpx;
+    position: relative;
+    top: -40rpx;
+    background-color: #fff;
+}
+.settle-list {
+    width: 100%;
+	height: calc(100vh - 444rpx - constant(safe-area-inset-bottom));
+	height: calc(100vh - 444rpx - env(safe-area-inset-bottom));
+    padding: 0 20rpx;
+    box-sizing: border-box;
+    margin-top: 20rpx;
+    .line {
+        height: 2rpx;
+        width: 100%;
+        background-color: #ececec;
+    }
+    .item {
+        border-bottom: 2rpx solid #ececec;
+        height: 100rpx;
+        @include between;
+    }
+    .value {
+        @include between(flex-end);
+    }
+    .price {
+        margin-right: 10rpx;
+    }
+    .price.sr {
+        color: $uni-color-button;
+    }
+}
+</style>

+ 109 - 0
pages/tools/sn.vue

@@ -0,0 +1,109 @@
+<template>
+    <view class="sn-page">
+        <view class="scan-qr-code">
+            <view class="label">扫码兑换商品</view>
+            <u-icon name="scan" size="28" color="#6c6c6c"></u-icon>
+        </view>
+        <scroll-view scroll-y class="sn-list">
+            <view class="item">
+                <view class="row code">
+                    <view class="label">兑换编码</view>
+                    <view class="value">Q20230610103240002</view>
+                </view>
+                <view class="row">
+                    <view class="label">商品名称</view>
+                    <view class="value">猪肉白菜水饺</view>
+                </view>
+                <view class="row">
+                    <view class="label">会员姓名</view>
+                    <view class="value">吃不胖的王小二</view>
+                </view>
+                <view class="row">
+                    <view class="label">获得日期</view>
+                    <view class="value">2023-06-02 15:10:10</view>
+                </view>
+                <view class="row bb0">
+                    <view class="label">兑换日期</view>
+                    <view class="value">2023-06-13 10:22:38</view>
+                </view>
+            </view>
+            <view class="item">
+                <view class="row code">
+                    <view class="label">兑换编码</view>
+                    <view class="value">Q20230610103240001</view>
+                </view>
+                <view class="row">
+                    <view class="label">商品名称</view>
+                    <view class="value">芹菜水饺</view>
+                </view>
+                <view class="row">
+                    <view class="label">会员姓名</view>
+                    <view class="value">王小五</view>
+                </view>
+                <view class="row">
+                    <view class="label">获得日期</view>
+                    <view class="value">2023-06-02 15:10:10</view>
+                </view>
+                <view class="row bb0">
+                    <view class="label">兑换日期</view>
+                    <view class="value">2023-06-13 10:22:38</view>
+                </view>
+            </view>
+        </scroll-view>
+    </view>
+</template>
+<script>
+export default {
+    data () {
+        return {
+
+        }
+    },
+    methods: {
+
+    },
+    onLoad () {}
+}
+</script>
+<style lang="scss" scoped>
+.sn-page {
+    background-color: #f5f5f5;
+    border-top: 2rpx solid #f5f5f5;
+}
+.scan-qr-code {
+    @include between;
+    font-size: $font-base;
+    height: 80rpx;
+    padding: 0 20rpx;
+    box-sizing: border-box;
+}
+.sn-list {
+    height: calc(100vh - 82rpx - env(safe-area-inset-bottom));
+    height: calc(100vh - 82rpx - constant(safe-area-inset-bottom));
+    .item {
+        background-color: #fff;
+        border-bottom: 10rpx solid #f5f5f5;
+        padding: 20rpx;
+        box-sizing: border-box;
+        font-size: $font-base;
+    }
+    .row {
+        @include flexStart;
+        margin-bottom: 20rpx;
+    }
+    .label {
+        width: 200rpx;
+    }
+    .value {
+        width: calc(100% - 200rpx);
+    }
+    .code {
+        border-bottom: 2rpx solid #ececec;
+        padding-bottom: 18rpx;
+        box-sizing: border-box;
+    }
+    .bb0 {
+        margin-bottom: 0;
+    }
+}
+</style>

Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio