Browse Source

新建项目

lilei 4 năm trước cách đây
commit
a6db19541a
70 tập tin đã thay đổi với 6204 bổ sung0 xóa
  1. 4 0
      .gitignore
  2. 76 0
      App.vue
  3. 37 0
      api/coupon.js
  4. 8 0
      api/data.js
  5. 44 0
      api/login.js
  6. 61 0
      api/order.js
  7. 42 0
      api/store.js
  8. 96 0
      components/uni-icons/icons.js
  9. 10 0
      components/uni-icons/uni-icons.vue
  10. 224 0
      components/uni-nav-bar/uni-nav-bar.vue
  11. 22 0
      components/uni-popup/message.js
  12. 25 0
      components/uni-popup/popup.js
  13. 243 0
      components/uni-popup/uni-popup-dialog.vue
  14. 116 0
      components/uni-popup/uni-popup-message.vue
  15. 165 0
      components/uni-popup/uni-popup-share.vue
  16. 294 0
      components/uni-popup/uni-popup.vue
  17. 25 0
      components/uni-status-bar/uni-status-bar.vue
  18. 243 0
      components/uni-steps/uni-steps.vue
  19. 279 0
      components/uni-transition/uni-transition.vue
  20. 104 0
      libs/axios.js
  21. 15 0
      libs/tools.js
  22. 27 0
      main.js
  23. 85 0
      manifest.json
  24. 13 0
      package-lock.json
  25. 19 0
      package.json
  26. 93 0
      pages.json
  27. 430 0
      pages/agreement/agreement.vue
  28. 35 0
      pages/careMore/careMore.vue
  29. 82 0
      pages/coupon/chooseCoupon/chooseCoupon.vue
  30. 115 0
      pages/coupon/couponTpl.vue
  31. 178 0
      pages/coupon/getCoupon/getCoupon.vue
  32. 140 0
      pages/coupon/index/index.vue
  33. 665 0
      pages/getOrder/getOrder.vue
  34. 41 0
      pages/index/index.vue
  35. 153 0
      pages/login/login.vue
  36. 179 0
      pages/order/order.vue
  37. 179 0
      pages/order/orderDetail.vue
  38. 290 0
      pages/store/storeDetail.vue
  39. 216 0
      pages/store/storeList.vue
  40. 103 0
      pages/userCenter/index.vue
  41. 649 0
      pages/work/index/index.vue
  42. 85 0
      pages/work/success/success.vue
  43. BIN
      static/img/carmore.jpg
  44. BIN
      static/img/cztis.png
  45. BIN
      static/img/dky.jpg
  46. BIN
      static/img/end.png
  47. BIN
      static/img/index-call.png
  48. BIN
      static/img/index-discount.png
  49. BIN
      static/img/index-list.png
  50. BIN
      static/img/location.png
  51. BIN
      static/img/logo.png
  52. BIN
      static/img/mddef.jpg
  53. BIN
      static/img/noPic.jpg
  54. BIN
      static/img/person.png
  55. BIN
      static/img/reset.png
  56. BIN
      static/img/sbts.png
  57. BIN
      static/img/select.png
  58. BIN
      static/img/start.png
  59. BIN
      static/img/step-one.png
  60. BIN
      static/img/store-dky.png
  61. BIN
      static/img/store-zt.png
  62. BIN
      static/img/store.png
  63. BIN
      static/img/unselect.png
  64. BIN
      static/img/yyz.jpg
  65. BIN
      static/img/ztyy.jpg
  66. BIN
      static/img/zysx.png
  67. BIN
      static/img/zyts.png
  68. 27 0
      store/$u.mixin.js
  69. 190 0
      store/index.js
  70. 77 0
      uni.scss

+ 4 - 0
.gitignore

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

+ 76 - 0
App.vue

@@ -0,0 +1,76 @@
+<script>
+	const uat_URL = 'https://carwash.test.zyucgj.com/cw-wechat/' // 预发布
+	// const uat_URL = 'http://192.168.16.103:8103/cw-wechat/' // 本地
+	const pro_URL = 'https://carwash.zyucgj.com/cw-wechat/'  // 生产
+	const buildType = 0 // 打包环境对应类型,1 生产 0 预发布
+	const buildURL = buildType ? pro_URL : uat_URL // 打包后实际对应的url
+	const envText = ['预发布环境','生产环境']
+	const envTips = process.env.NODE_ENV == 'development' ? envText[0] : envText[buildType]
+	export default {
+		globalData: {  
+			baseUrl: process.env.NODE_ENV == 'development' ? uat_URL : buildURL,
+			token: '',
+			version: '', // 当前版本号
+			buildType: buildType,
+			envTips: envTips
+		},
+		mounted() {
+			// 非V3模式
+			if(this.$scope){
+				this.$scope.globalData.token = this.$store.state.vuex_token
+			}else{
+				// V3模式
+				getApp({allowDefault: true}).globalData.token = this.$store.state.vuex_token
+			}
+		},
+		onLaunch: function() {
+			 console.log('App onLaunch')
+		},
+		onShow: function() {
+			console.log('App Show')
+		},
+		onHide: function() {
+			console.log('App Hide')
+		},
+	}
+</script>
+
+<style lang="scss">
+	@import "uview-ui/index.scss";
+	/*每个页面公共css */
+	page {
+		min-height: 100%;
+		display: flex;
+		background: #fff;
+	}
+	
+	/* #ifdef MP-BAIDU */
+	page {
+		width: 100%;
+		height: 100%;
+		display: block;
+	}
+	/* #endif */
+	
+	/* #ifdef MP-ALIPAY */
+	page {
+		min-height: 100vh;
+	}
+	
+	/* #endif */
+	
+	/* 原生组件模式下需要注意组件外部样式 */
+	m-input {
+		width: 100%;
+		min-height: 100%;
+		display: flex;
+	}
+	
+	.content {
+		display: flex;
+		flex: 1;
+		flex-direction: column;
+		background-color: #fff;
+		padding: 20upx;
+	}
+</style>

+ 37 - 0
api/coupon.js

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

+ 8 - 0
api/data.js

@@ -0,0 +1,8 @@
+import axios from '@/libs/axios.js';
+// 根据字典code查询选项
+export const getLookUpDatas = (params) => {
+  return axios.request({
+    url: `lookup/findByLookup/${params.type}`,
+    method: 'get'
+  })
+}

+ 44 - 0
api/login.js

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

+ 61 - 0
api/order.js

@@ -0,0 +1,61 @@
+import axios from '@/libs/axios.js';
+
+// 获取网点下的服务项目
+export const getStoreItems = params => {
+  return axios.request({
+    url: `item/storeItems/?storeId=${params.storeId}`,
+    method: 'get',
+  })
+};
+
+// 下单 保存订单
+export const saveOrder = params => {
+  return axios.request({
+    url: `order/save`,
+    method: 'post',
+	data: params
+  })
+};
+
+// 下单 预支付
+export const signPay  = params => {
+  return axios.request({
+    url: `order/pay?orderId=${params.orderId}`,
+    method: 'get',
+  })
+};
+
+// 我的订单
+export const getOrderList = params => {
+	let url = `order/query/${params.pageNo}/${params.pageSize}`
+	delete params.pageNo
+	delete params.pageSize
+	return axios.request({
+		url: url,
+		method: 'post',
+		data: params
+	})
+};
+// 订单详情
+export const orderDetail = params => {
+  return axios.request({
+    url: `order/find/${params.id}`,
+    method: 'get',
+  })
+};
+
+// 查询当前是否有未完成的订单
+export const getUnFinishedOrder = params => {
+	return axios.request({
+	  url: `order/unFinishedOrderRunStatus`,
+	  method: 'get',
+	})
+}
+
+// 取消订单
+export const cancleOrder = params => {
+	return axios.request({
+	  url: `order/cancel/${params.id}`,
+	  method: 'get',
+	})
+}

+ 42 - 0
api/store.js

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

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

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

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 10 - 0
components/uni-icons/uni-icons.vue


+ 224 - 0
components/uni-nav-bar/uni-nav-bar.vue

@@ -0,0 +1,224 @@
+<template>
+	<view class="uni-navbar">
+		<view :class="{ 'uni-navbar--fixed': fixed, 'uni-navbar--shadow': shadow, 'uni-navbar--border': border }" :style="{ 'background-color': backgroundColor }"
+		 class="uni-navbar__content">
+			<uni-status-bar v-if="statusBar" />
+			<view :style="{ color: color,backgroundColor: backgroundColor }" class="uni-navbar__header uni-navbar__content_view">
+				<view @tap="onClickLeft" class="uni-navbar__header-btns uni-navbar__header-btns-left uni-navbar__content_view">
+					<view class="uni-navbar__content_view" v-if="leftIcon.length">
+						<uni-icons :color="color" :type="leftIcon" size="24" />
+					</view>
+					<view :class="{ 'uni-navbar-btn-icon-left': !leftIcon.length }" class="uni-navbar-btn-text uni-navbar__content_view"
+					 v-if="leftText.length">
+						<text :style="{ color: color, fontSize: '14px' }">{{ leftText }}</text>
+					</view>
+					<slot name="left" />
+				</view>
+				<view class="uni-navbar__header-container uni-navbar__content_view">
+					<view class="uni-navbar__header-container-inner uni-navbar__content_view" v-if="title.length">
+						<text class="uni-nav-bar-text" :style="{color: color }">{{ title }}</text>
+					</view>
+					<!-- 标题插槽 -->
+					<slot />
+				</view>
+				<view :class="title.length ? 'uni-navbar__header-btns-right' : ''" @tap="onClickRight" class="uni-navbar__header-btns uni-navbar__content_view">
+					<view class="uni-navbar__content_view" v-if="rightIcon.length">
+						<uni-icons :color="color" :type="rightIcon" size="24" />
+					</view>
+					<!-- 优先显示图标 -->
+					<view class="uni-navbar-btn-text uni-navbar__content_view" v-if="rightText.length && !rightIcon.length">
+						<text class="uni-nav-bar-right-text">{{ rightText }}</text>
+					</view>
+					<slot name="right" />
+				</view>
+			</view>
+		</view>
+		<view class="uni-navbar__placeholder" v-if="fixed">
+			<uni-status-bar v-if="statusBar" />
+			<view class="uni-navbar__placeholder-view" />
+		</view>
+	</view>
+</template>
+
+<script>
+	import uniStatusBar from "../uni-status-bar/uni-status-bar.vue";
+	import uniIcons from "../uni-icons/uni-icons.vue";
+
+	export default {
+		name: "UniNavBar",
+		components: {
+			uniStatusBar,
+			uniIcons
+		},
+		props: {
+			title: {
+				type: String,
+				default: ""
+			},
+			leftText: {
+				type: String,
+				default: ""
+			},
+			rightText: {
+				type: String,
+				default: ""
+			},
+			leftIcon: {
+				type: String,
+				default: ""
+			},
+			rightIcon: {
+				type: String,
+				default: ""
+			},
+			fixed: {
+				type: [Boolean, String],
+				default: false
+			},
+			color: {
+				type: String,
+				default: "#000000"
+			},
+			backgroundColor: {
+				type: String,
+				default: "#FFFFFF"
+			},
+			statusBar: {
+				type: [Boolean, String],
+				default: false
+			},
+			shadow: {
+				type: [String, Boolean],
+				default: false
+			},
+			border: {
+				type: [String, Boolean],
+				default: true
+			}
+		},
+        mounted() {
+          if(uni.report && this.title !== '') {
+              uni.report('title', this.title)
+          }
+        },
+		methods: {
+			onClickLeft() {
+				this.$emit("clickLeft");
+			},
+			onClickRight() {
+				this.$emit("clickRight");
+			}
+		}
+	};
+</script>
+
+<style lang="scss" scoped>
+	$nav-height: 44px;
+	.uni-nav-bar-text {
+		/* #ifdef APP-PLUS */
+		font-size: 34rpx;
+		/* #endif */
+		/* #ifndef APP-PLUS */
+		font-size: $uni-font-size-lg;
+		/* #endif */
+	}
+	.uni-nav-bar-right-text {
+		font-size: $uni-font-size-base;
+	}
+
+	.uni-navbar {
+		width: 750rpx;
+	}
+
+	.uni-navbar__content {
+		position: relative;
+		width: 750rpx;
+		background-color: $uni-bg-color;
+		overflow: hidden;
+	}
+
+	.uni-navbar__content_view {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		align-items: center;
+		flex-direction: row;
+		// background-color: #FFFFFF;
+	}
+
+	.uni-navbar__header {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		width: 750rpx;
+		height: $nav-height;
+		line-height: $nav-height;
+		font-size: 14px;
+		// background-color: #ffffff;
+	}
+
+	.uni-navbar__header-btns {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-wrap: nowrap;
+		width: 120rpx;
+		padding: 0 6px;
+		justify-content: center;
+		align-items: center;
+	}
+
+	.uni-navbar__header-btns-left {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		// width: 300rpx;
+		justify-content: flex-start;
+	}
+
+	.uni-navbar__header-btns-right {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		width: 150rpx;
+		padding-right: 30rpx;
+		justify-content: flex-end;
+	}
+
+	.uni-navbar__header-container {
+		flex: 1;
+	}
+
+	.uni-navbar__header-container-inner {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex: 1;
+		align-items: center;
+		justify-content: center;
+		font-size: $uni-font-size-base;
+	}
+
+
+	.uni-navbar__placeholder-view {
+		height: $nav-height;
+	}
+
+	.uni-navbar--fixed {
+		position: fixed;
+		z-index: 998;
+	}
+
+	.uni-navbar--shadow {
+		/* #ifndef APP-NVUE */
+		box-shadow: 0 1px 6px #ccc;
+		/* #endif */
+	}
+
+	.uni-navbar--border {
+		border-bottom-width: 1rpx;
+		border-bottom-style: solid;
+		border-bottom-color: $uni-border-color;
+	}
+</style>

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

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

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

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

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

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

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

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

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

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

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

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

+ 25 - 0
components/uni-status-bar/uni-status-bar.vue

@@ -0,0 +1,25 @@
+<template>
+	<view :style="{ height: statusBarHeight }" class="uni-status-bar">
+		<slot />
+	</view>
+</template>
+
+<script>
+	var statusBarHeight = uni.getSystemInfoSync().statusBarHeight + 'px'
+	export default {
+		name: 'UniStatusBar',
+		data() {
+			return {
+				statusBarHeight: statusBarHeight
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.uni-status-bar {
+		width: 750rpx;
+		height: 20px;
+		// height: var(--status-bar-height);
+	}
+</style>

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

@@ -0,0 +1,243 @@
+<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:index<=active?activeColor: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="checkbox-filled" size="14"></uni-icons>
+					</view>
+					<view :class="[direction==='column'?'uni-steps__column-circle':'uni-steps__row-circle']" v-else :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>
+	import uniIcons from '../uni-icons/uni-icons.vue'
+	export default {
+		name: 'UniSteps',
+		components: {
+			uniIcons
+		},
+		props: {
+			direction: {
+				// 排列方向 row column
+				type: String,
+				default: 'row'
+			},
+			activeColor: {
+				// 激活状态颜色
+				type: String,
+				default: '#1aad19'
+			},
+			deactiveColor: {
+				// 未激活状态颜色
+				type: String,
+				default: '#999999'
+			},
+			active: {
+				// 当前步骤
+				type: Number,
+				default: 0
+			},
+			options: {
+				type: Array,
+				default () {
+					return []
+				}
+			} // 数据
+		},
+		data() {
+			return {}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.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;
+	}
+
+	.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: $uni-font-size-base;
+		line-height: 16px;
+		text-align: center;
+	}
+
+	.uni-steps__column-title {
+		font-size: $uni-font-size-base;
+		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: $uni-font-size-sm;
+		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: $uni-text-color-grey;
+	}
+
+	.uni-steps__column-line {
+		width: 1px;
+		background-color: $uni-text-color-grey;
+	}
+
+	.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, -1px);
+	}
+
+	.uni-steps__row-circle {
+		width: 5px;
+		height: 5px;
+		border-radius: 100px;
+		background-color: $uni-text-color-grey;
+		margin: 0px 3px;
+	}
+
+	.uni-steps__column-circle {
+		width: 5px;
+		height: 5px;
+		border-radius: 100px;
+		background-color: $uni-text-color-grey;
+		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>

+ 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>

+ 104 - 0
libs/axios.js

@@ -0,0 +1,104 @@
+import { objToUrl } from '@/libs/tools.js';
+const baseUrl = getApp().globalData.baseUrl
+//带Token请求
+const request = (opts, hasToken) => {
+	const authorization = getApp().globalData.token
+	// console.log(authorization,'authorization')
+	//  hasToken是否传入token,为true则不传token
+	const header = {
+			'Content-Type': 'application/json;charset=UTF-8'
+		}
+	if(!hasToken){
+		header['X-AUTH-TOKEN'] = authorization
+	}	
+	//此token是登录成功后后台返回保存在storage中的
+	let DefaultOpts = {
+		url: baseUrl+opts.url,
+		data: opts.data,
+		method: opts.method,
+		header: opts.header ? opts.header : header
+	}
+	let promise = new Promise(function(resolve, reject) {
+		uni.request(DefaultOpts).then(
+			(res) => {
+				console.log(res,opts.url)
+				// 是否超时已跳转
+				let loginTimeOut = uni.getStorageSync('loginTimeOut');
+				if(res[0]&&res[0].errMsg.indexOf('request:fail')>=0){
+					resolve({
+						status: 6000,
+						message: "网络请求失败"
+					})
+				}
+				let ret = res[1]
+				if(res[1].data.status){
+					ret = res[1].data
+				}else if(res[1].status){
+					ret = res[1]
+				}
+				if(ret.status == '200'){
+					resolve(ret)
+				}else{
+					if(ret.status == '401'){  //  未登录或登录过期
+					    // 除过查询未完成订单的请求
+						if(opts.url.indexOf("order/unFinishedOrderRunStatus")<0){
+							uni.showToast({
+								icon:'none',
+								title: '未登录或登录已过期,请重新登录',
+								duration: 5000 
+							})
+							setTimeout(function() {
+							  const currentRoute = getRoutePath();
+							  const url = `/pages/login/login?lanuch=${currentRoute.lanuch}&path=` + encodeURIComponent(currentRoute.url);
+							  uni.redirectTo({ url });
+							}, 1000);
+						}
+					}
+					else{
+						if(ret.status == 500){
+							if(opts.url.indexOf("store/validateStoreAndDevice")<0&&opts.url.indexOf("couponReceives/findUsableCoupon")<0){
+								uni.showToast({
+									icon:'none',
+									title: ret.message,
+									mask: true,
+									duration: 3000 
+								})
+							}
+						}
+					}
+					resolve(ret)
+				}
+				resolve(ret)
+			}
+		).catch(
+			(response) => {
+				reject(response)
+			}
+		)
+	})
+	return promise
+}
+function getRoutePath() {
+  // eslint-disable-next-line no-undef
+  const pages = getCurrentPages(); // 获取加载的页面
+  const currentPage = pages[pages.length - 1]; // 获取当前页面的对象
+  let url = currentPage.route; // 当前页面url
+  const options = objToUrl(currentPage.options); // 如果要获取url中所带的参数可以查看options
+
+  const lanuch = [
+    'pages/index/index'
+  ].includes(url);
+  url = '/' + url;
+  if (options.length) {
+    url += '?' + options;
+  }
+  return {
+    url,
+    lanuch
+  };
+}
+
+export default {
+	baseUrl,
+	request
+}

+ 15 - 0
libs/tools.js

@@ -0,0 +1,15 @@
+export function objToUrl(obj) {
+  let uri = '';
+  let keys = Object.keys(obj);
+  keys.forEach(item => {
+    uri += '&' + item + '=' + obj[item];
+  });
+
+  uri = uri.substr(1);
+
+  return uri;
+}
+
+
+
+  

+ 27 - 0
main.js

@@ -0,0 +1,27 @@
+import Vue from 'vue'
+import App from './App'
+import store from '@/store'
+Vue.config.productionTip = false
+
+App.mpType = 'app'
+import uView from "uview-ui"
+Vue.use(uView)
+
+let vuexStore = require("@/store/$u.mixin.js")
+Vue.mixin(vuexStore)
+/*
+* 提示信息
+*/ 
+Vue.prototype.toashMsg = function (title) {
+	title = title == undefined ? "系统繁忙" : title;
+	uni.showToast({
+		title:title,
+		icon:'none',
+		duration: 2000
+	})
+}
+const app = new Vue({
+	store,
+    ...App
+})
+app.$mount()

+ 85 - 0
manifest.json

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

+ 13 - 0
package-lock.json

@@ -0,0 +1,13 @@
+{
+  "name": "qhsxKx-mini-html",
+  "version": "1.0.0",
+  "lockfileVersion": 1,
+  "requires": true,
+  "dependencies": {
+    "uview-ui": {
+      "version": "1.5.7",
+      "resolved": "https://registry.npmjs.org/uview-ui/-/uview-ui-1.5.7.tgz",
+      "integrity": "sha512-brNsxtXYNRFVISCehLXT+ZXmtaWh7gydrOokokYXvJUy5MkuZozMu+TdwA4mIo9y40FqKz6iO5gGWJuXyCSE5w=="
+    }
+  }
+}

+ 19 - 0
package.json

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

+ 93 - 0
pages.json

@@ -0,0 +1,93 @@
+{
+	"easycom": {
+		"^u-(.*)": "uview-ui/components/u-$1/u-$1.vue"
+	},
+	"pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
+		{
+			"path": "pages/index/index", // 首页
+			"style": {
+				"navigationBarTitleText": "",
+				"navigationStyle": "custom",
+				"enablePullDownRefresh": false
+			}
+		},
+		{
+			"path": "pages/login/login",
+			"style": {
+				"navigationBarTitleText": "授权登录"
+			}
+		},
+		{
+			"path": "pages/store/storeList",
+			"style": {
+				"navigationBarTitleText": "网点列表"
+			}
+		}, {
+			"path": "pages/store/storeDetail",
+			"style": {
+				"navigationBarTitleText": "网点详情"
+			}
+		}, {
+			"path": "pages/userCenter/index",
+			"style": {
+				"navigationBarTitleText": "个人中心"
+			}
+		}, {
+			"path": "pages/coupon/index/index",
+			"style": {
+				"navigationBarTitleText": "我的优惠卷"
+			}
+		}, {
+			"path": "pages/order/order",
+			"style": {
+				"navigationBarTitleText": "我的订单"
+			}
+		}, {
+			"path": "pages/order/orderDetail",
+			"style": {
+				"navigationBarTitleText": "订单详情"
+			}
+		}, {
+			"path": "pages/work/index/index",
+			"style": {
+				"navigationBarTitleText": "洗车服务中"
+			}
+		}, {
+			"path": "pages/getOrder/getOrder",
+			"style": {
+				"navigationBarTitleText": "准备洗车"
+			}
+		}, {
+			"path": "pages/work/success/success",
+			"style": {
+				"navigationBarTitleText": "归位成功"
+			}
+		}, {
+			"path": "pages/agreement/agreement",
+			"style": {
+				"navigationBarTitleText": "注意事项及服务协议"
+			}
+		}, {
+			"path": "pages/coupon/chooseCoupon/chooseCoupon",
+			"style": {
+				"navigationBarTitleText": "选择优惠卷"
+			}
+		}, {
+			"path": "pages/careMore/careMore",
+			"style": {
+				"navigationBarTitleText": "注意事项"
+			}
+		}, {
+			"path": "pages/coupon/getCoupon/getCoupon",
+			"style": {
+				"navigationBarTitleText": "领取优惠券"
+			}
+		}
+	],
+	"globalStyle": {
+		"navigationBarTextStyle": "black",
+		"navigationBarTitleText": "uni-app",
+		"navigationBarBackgroundColor": "#F8F8F8",
+		"backgroundColor": "#F8F8F8"
+	}
+}

+ 430 - 0
pages/agreement/agreement.vue

@@ -0,0 +1,430 @@
+<template>
+	<view class="content">
+		<view class="page-content">
+			<view class="page-care" v-if="!hideZysx">
+				<u-image width="750rpx" height="1200rpx" mode="aspectFit" src="/static/img/zysx.png"></u-image>
+			</view>
+			<view class="page-care agreement">
+				<view class="title">-服务协议-</view>
+				<view class="xy-cons">
+					<view class="MsoNormal">
+						欢迎您使用智能快洗!
+					</view>
+					<view class="MsoNormal">
+						<text>本《智能快洗平台用户服务协议》(以下简称</text>“本服务协议”)是您与智能快洗平台之间就登录注册智能快洗用户账号及使用智能快洗的各项服务等相关事宜所订立的协议。为更好的体验智能快洗的服务,您应当仔细阅读并遵守本服务协议下的全部内容,特别是涉及免除或者责任限制的条款,该类条款可能以黑体加粗或加下划线的形式提示您重点注意。除非您已阅读并接受本服务协议所有条款,否则您将不能注册智能快洗账号或使用智能快洗的服务,如您不同意本服务条款的任意内容,请勿注册或使用智能快洗的服务,并应立即停止注册程序。如您对本服务协议的内容(特别是涉及免除或者责任限制的条款)有任何疑义,可随时按照本服务协议中列明的联系方式与我们联系,我们将根据您的要求为您进一步解释和说明相关内容。如您点击“同意、继续服务”等并通过登录注册程序或其他任何方式使用或接受智能快洗的任何服务,即视为您已阅读并同意本服务协议,自愿接受本服务协议的所有内容的约束。请您在决定注册或使用服务前再次确认您已知悉并完全理解本服务协议的所有内容。
+					</view>
+					<view class="MsoNormal">
+						<text>如果您未满</text>18<text>周岁,请务必在家长或其他法定监护人的陪同下阅读本服务协议,并在进行注册、下单、支付等任何行为或使用智能快洗的其他任何服务前,应事先征得您的家长或其他法定监护人的同意。</text>
+					</view>
+					<view class="MsoNormal">
+						<text>本服务协议所述</text>“服务”指您使用智能快洗用户账号接受智能快洗平台所提供的服务。无论“服务”属于上述何种情形,本《智能快洗平台用户服务协议》都将予以适用。
+					</view>
+					<view class="MsoNormal">
+						一、协议的主体与范围
+					</view>
+					<view class="MsoNormal">
+						<text>【签约主体】</text> <text>视您所使用的具体服务而定,</text>“智能快洗”是指陕西山海高科信息技术有限公司旗下运营或提供您所使用的相关具体服务的相关主体的统称,您可在相关服务的具体服务条款或说明文件中查看上述主体的名称和信息。 “用户”是指使用智能快洗相关服务的使用人,在本服务协议中更多称呼为“您”。
+					</view>
+					<view class="MsoNormal">
+						【协议内容】本服务协议内容同时包括:
+					</view>
+					<view class="MsoNormal">
+						<text>(</text>1<text>)本《智能快洗平台用户服务协议》文本;</text>
+					</view>
+					<view class="MsoNormal">
+						<text>(</text>2<text>)《智能快洗平台隐私政策》:智能快洗非常重视您的个人信息和隐私的保护,将按照《智能快洗平台隐私政策》中所公布的政策在具体服务中进行隐私及个人信息的收集、存储、使用、披露等活动。您在注册智能快洗账号前,应仔细阅读《智能快洗平台隐私政策》;</text>
+					</view>
+					<view class="MsoNormal">
+						<text>(</text>3<text>)专项商品或服务规则。为更好地提升用户体验,智能快洗在若干具体专项商品或服务中特别制定了详细的规则,供各方遵守。</text>
+					</view>
+					<view class="MsoNormal">
+						<text>(</text>4<text>)第三方服务相关的用户授权协议。为方便您在使用智能快洗平台服务的同时,根据您自身需要,通过智能快洗平台进行跳转等方式,自愿选择接受独立第三方服务商及部分关联第三方服务商所提供的服务,您将在使用该等第三方服务前对相应的用户授权协议予以同意确认,以作为您和智能快洗平台之间就该等第三方服务的使用所涉及的相关事项的权利义务依据。</text>
+					</view>
+					<view class="MsoNormal">
+						上述各项内容之间如存在不一致之处,以时间上最新发布的内容为准,发布时间相同的,以本款【协议内容】中所包含的组成部分的序号倒序排列为准。
+					</view>
+					<view class="MsoNormal">
+						【协议遵守】您理解并同意:
+					</view>
+					<view class="MsoNormal">
+						<text>(</text>1<text>)上述【协议内容】中所述的协议、政策、条款与规则构成本服务协议不可分割的组成部分,共同适用于您所使用的智能快洗平台服务。</text>
+					</view>
+					<view class="MsoNormal">
+						<text>(</text>2<text>)根据国家法律法规变化、运营需要或为提升服务质量的目的,智能快洗将在必要的时候对上述各项协议、条款与规则不时地进行修改更新,并通过在网页或移动客户端上发出公告、站内信等合理、醒目的方式向您进行提前通知,上述修改更新内容将在相关更新说明中指定的日期开始实施,通常情况下不短于发布之日后八个自然日。</text>
+					</view>
+					<view class="MsoNormal">
+						<text>(</text>3<text>)您应当及时查阅并了解相关更新修改内容,如您不同意相关更新修改内容,可停止使用相关更新修改内容所涉及的服务,此情形下,变更事项对您不产生效力;如您在上述更新修改内容实施后继续使用所涉及的服务,将视为您已同意各项更新修改内容。</text>
+					</view>
+					<view class="MsoNormal">
+						<text>【适用平台】本服务协议适用于网页端、移动客户端(包括</text>IOS<text>、安卓及已有或未来将新增的任何其他移动客户端)等各类平台或媒介中智能快洗所提供的各项服务。您知悉并理解,为享受上述相关服务,您必须自行提供相关上网设备(如:个人电脑、手机、平板电脑或其他设备)并自行负担相关网络使用和商品服务支付所产生的有关费用。</text>
+					</view>
+					<view class="MsoNormal">
+						二、账号的注册
+					</view>
+					<view class="MsoNormal">
+						【注册资料】您应遵循诚实信用、合法善意的原则,向智能快洗提交相关注册资料,相关注册资料应当遵守法律法规、社会主义制度、国家利益、公民合法权益、公序良俗、信息真实等原则,不应提交任何违法或不良信息,相关资料如有变动,您应及时更新。如果因您所提供的注册资料不合法、不真实、不准确或未及时更新,从而导致相关法律责任或不利后果的,您将承担相应的法律责任及不利后果。
+					</view>
+					<view class="MsoNormal">
+						您已知悉,在智能快洗账号注册过程中,因安全性、内部管理等原因,部分初始注册资料可能将无法再次更改。因此,您应谨慎核查此部分注册资料,确保正确填写。
+					</view>
+					<view class="MsoNormal">
+						【民事行为能力】您知悉并承诺,您具有完全民事权利能力和行为能力或虽不具有完全民事权利能力和行为能力但已经过您的家长和其他法定监护人同意并由您的家长和其他法定监护人代理注册及使用智能快洗的各项服务。若您不具备前述相适应的民事行为能力即进行账号注册的,则您及您的家长和其他法定监护人应依照法律规定承担因此而导致的一切后果。
+					</view>
+					<view class="MsoNormal">
+						【真实身份】因国家法律法规、监管要求及特定门类的服务(如金融服务等)需要,您有可能需要在注册或使用该等服务的特定环节中填写真实的身份信息,并需通过相关验证流程后方可使用服务,如您填写的身份信息不完整、不真实或未通过验证,将导致您无法使用该服务、损害自身、他人利益或造成其他不利后果,该等后果将由您予以承担。
+					</view>
+					<view class="MsoNormal">
+						【注册信息保护】智能快洗将对您所提交的注册信息予以保护,不会将其披露给任何非关联的第三方,除非:
+					</view>
+					<view class="MsoNormal">
+						<text>(</text>1<text>)相关法律法规或国家机关要求;</text>
+					</view>
+					<view class="MsoNormal">
+						<text>(</text>2<text>)智能快洗发生相关合并、分立、收购或资产转移;</text>
+					</view>
+					<view class="MsoNormal">
+						<text>(</text>3<text>)为提供相关服务所必须的</text>
+					</view>
+					<view class="MsoNormal">
+						对于您所提交的注册信息中涉及个人信息的内容,智能快洗将严格按照《智能快洗平台隐私政策》的规定进行收集、处理和使用。
+					</view>
+					<view class="MsoNormal">
+						【账号数量】除因历史原因、业务整合等智能快洗所认可的特殊情况外,智能快洗原则上只允许您使用一个智能快洗用户账号。如有证据证明或智能快洗有理由相信您存在不当注册或不当使用多个智能快洗账号的情形,智能快洗可将相关账号信息进行合并或采取其他合理措施,如因您不当注册或不当使用给智能快洗及相关方造成损失的,您还应承担相应的赔偿责任。此外,出于相关业务需要,智能快洗也可对同一用户的多个账号或相关信息进行合并,如该等合并将对您的权益产生实质影响,智能快洗将在进行上述合并前事先征得您的同意。
+					</view>
+					<view class="MsoNormal">
+						三、账号的使用
+					</view>
+					<view class="MsoNormal">
+						【账号登录】您可以使用注册账号时填写登记的并获得系统审核通过的账号密码登录智能快洗洗车平台。为了您的使用便利,智能快洗未来可能将适时提供指纹识别、人脸识别等更为安全便捷的登录方式,智能快洗可根据相关法律法规、监管要求、用户体验、风险控制等因素对登录方式予以适时调整或增减,或在具体业务门类的用户服务条款等规定中予以进一步细化或调整。
+					</view>
+					<view class="MsoNormal">
+						【账号使用】您应对您账号项下的所有行为(包括但不限于在线签署任何协议,浏览、购买、支付、点评、上传、发布、输入任何内容)所产生的一切后果负责,通过您的账号所发生的上述各项行为将视为您本人的真实意思表示。
+					</view>
+					<view class="MsoNormal">
+						智能快洗提示您,您通过自身账号在接受智能快洗的各项服务中所上传、发送的任何内容都应具有合法来源,如相关内容涉及任何第三方的合法权益,应事先获得相应的许可。如智能快洗收到涉及您的相关举报或投诉,智能快洗可采取相关法律法规所要求或允许的方式,向相关争议方、相关部门提供包括账号在内的必要的信息,以便解决纠纷争议,保护正当权利人的合法权益。
+					</view>
+					<view class="MsoNormal">
+						【账号借用】为保证相关账号安全,未经智能快洗的书面同意,您不应将智能快洗平台上所注册的账号借给他人使用,否则您应当承担由此产生的全部责任,智能快洗平台保留拒绝提供相应服务、冻结或收回注册账号或终止本服务协议的权利,并可要求您对智能快洗所承受的损失予以赔偿。
+					</view>
+					<view class="MsoNormal">
+						【安全义务】如您发现账号存在安全问题,请您立即联系智能快洗予以调查处理,否则智能快洗对潜在损失的产生或扩大不承担任何责任。
+					</view>
+					<view class="MsoNormal">
+						智能快洗特别提示您,您应该妥善保管您的账号和密码,当您使用完毕智能快洗的服务后,您应安全退出。此外,您不应轻信借款、索要密码或其他涉及财产的网络信息。涉及财产操作的,请一定先核实对方身份,并请经常留意智能快洗有关防范诈骗犯罪的提示。您知悉并同意,如您在账号信息的保管上、相关上网设备的网络安全维护上存在任何过失导致账号丢失、泄露,您应对此所产生的任何后果负责,智能快洗对此不负任何责任,如智能快洗因此产生相关支出,智能快洗将有权向您予以追偿。
+					</view>
+					<view class="MsoNormal">
+						【限制冻结】您知悉并同意,在符合法律法规的规定,或经国家机关要求的前提下,智能快洗有权对您的注册账号进行限制或冻结,在该等情况下,您可能无法继续登陆或使用您的注册账号。
+					</view>
+					<view class="MsoNormal">
+						四、服务说明、用户行为规范与责任承担
+					</view>
+					<view class="MsoNormal">
+						【服务说明】您应在享受智能洗车服务的过程中应按照下列操作进行:
+					</view>
+					<view class="MsoNormal">
+						<text>(</text>1<text>)您每次使用都需严格按照要求把车停到洗车机器人指定区域,可以通过洗车机器人两侧辅助镜查看,确保车辆在黄线内,车头到达绿色区域,严禁在未按要求停好车的情况下启动智能快洗洗车机器人。</text>
+					</view>
+					<view class="MsoNormal">
+						<text>(</text>2<text>) 您应按语言提示缓慢驶入或驶离洗车机器人,保持小心安全驾驶。并按要求停车后回正方向盘、挂</text><text>P</text><text>档、拉手刹、收后视镜、关车窗、关雨刮、收天线等超长加装件,保证车辆在洗车服务中安全。</text>
+					</view>
+					<view class="MsoNormal">
+						<text>(</text>3<text>) 智能快洗洗车机器人仅能服务低于</text><text>2</text><text>米高的普通轿车,您不能驾驶超过高</text><text>2</text><text>米、宽</text><text>2</text><text>米、长</text><text>5.3</text><text>米的车辆和货车等特殊车辆,强行使用智能快洗洗车机器人。</text>
+					</view>
+					<view class="MsoNormal">
+						<text>(</text>4<text>)在智能快洗洗车机器人服务过程中,用户应始终保持车辆静止,不得移动车辆,严禁上下车或伸出窗外张望,也不能在洗车机内部走动。</text>
+					</view>
+					<view class="MsoNormal">
+						<text>(</text>5<text>) 车龄比较老的车,因为部件老化松动等情况,清洗有可能会损坏,不建议您使用洗车机服务。</text>
+					</view>
+					<view class="MsoNormal">
+						【用户义务】您知悉并承诺,在使用智能快洗所提供的服务的过程中,您应遵守相关法律法规,不应从事如下违反法律法规的规定,影响正常服务提供或损害他人合法利益的行为:
+					</view>
+					<view class="MsoNormal">
+						<text>(</text>1<text>)不应利用智能快洗平台或相关服务危害国家安全、破坏政治稳定、泄露国家秘密,不侵犯国家、社会、集体利益和第三方的合法权益,不从事违法犯罪活动,不设立用于实施诈骗,传授犯罪方法,制作或者销售违禁物品、管制物品等违法犯罪活动的网站、通讯群组,不利用网络发布涉及实施诈骗,制作或者销售违禁物品、管制物品以及其他违法犯罪活动的信息;</text>
+					</view>
+					<view class="MsoNormal">
+						<text>(</text>2<text>)不应制作、发布、复制、查阅和传播、存储、链接下列信息:反对宪法所确定的基本原则的;危害国家安全,泄露国家秘密,颠覆国家政权,推翻社会主义制度,煽动分裂国家,破坏国家统一的;损害国家荣誉和利益的;煽动民族仇恨、民族歧视,破坏民族团结的;破坏国家宗教政策,宣扬邪教和封建迷信的;煽动非法集会、结社、游行、示威、聚众扰乱社会秩序的;捏造、散布谣言,侵犯他人权利,扰乱经济、社会秩序,破坏社会稳定的;散布淫秽、色情、赌博、暴力、凶杀、恐怖或者教唆犯罪的;侮辱或者诽谤他人,侵害他人合法权益的;宣扬恐怖主义、极端主义的;违背当地风俗习惯的;含有法律、行政法规禁止的其他内容的;</text>
+					</view>
+					<view class="MsoNormal">
+						<text>(</text>3<text>)不应从事下列危害计算机信息网络安全的活动:对网络服务及相关软硬件设施进行破解、破坏、删除、修改或者增加的;对计算机信息网络中存储或者传输的数据和应用程序进行删除、修改或者增加的;使用软件或硬件等方式窃取他人口令、非法入侵他人计算机系统;故意制作、传播计算机病毒等破坏性程序的;其他危害计算机信息网络安全的活动;</text>
+					</view>
+					<view class="MsoNormal">
+						<text>(</text>4<text>)不应擅自复制和使用网络上未公开和未授权的文件。除相关著作权人明示同意,不应在网络中擅自破解、传播、下载或复制第三人享有著作权的软件或进行其他任何侵害他人知识产权的活动;</text>
+					</view>
+					<view class="MsoNormal">
+						<text>(</text>5<text>)不应私自传播广告信息或以“刷单”等不正当方式帮助他人提升评价或利用评价权利对其他用户、商户实施威胁、敲诈勒索;</text>
+					</view>
+					<view class="MsoNormal">
+						<text>(</text>6<text>)不应因对智能快洗相关服务的使用行为导致智能快洗卷入政治和公共事件;</text>
+					</view>
+					<view class="MsoNormal">
+						<text>(</text>7<text>)不应通过侵犯第三人合法权益、作弊、扰乱系统、实施网络攻击、恶意套现、刷信誉、批量注册、用机器注册智能快洗平台账户、用机器模拟客户端等手段进行交易或参加智能快洗或其所授权的第三方发布的任何活动;</text>
+					</view>
+					<view class="MsoNormal">
+						<text>(</text>8<text>)未经智能快洗事先书面许可,不应自行或授权、协助任何第三方非法抓取智能快洗所展示的任何内容(“非法抓取”是指采用未经智能快洗认可的程序或者非正常浏览等技术手段获取内容数据的行为)。</text>
+					</view>
+					<view class="MsoNormal">
+						【责任承担】如您在使用智能快洗服务过程中涉及违反服务说明或违反用户义务中的一项或多项,则需要对自己的行为承担法律责任。承担法律责任的形式包括但不限于:造成的损失由您自己承担,对受到侵害者(包括智能快洗)进行赔偿。特别的,若您违反用户义务导致智能快洗承担了因您的行为导致的行政处罚或侵权损害赔偿责任等损失(含诉讼费、律师费等相关法律程序费用)后,您还应立即给予智能快洗等额的赔偿。此外,根据具体违法违规情形,智能快洗有权作出独立判断,立即暂停或终止对您提供部分或全部服务,包括锁定、注销、删除帐号等措施。
+					</view>
+					<view class="MsoNormal">
+						您知悉并理解,如果智能快洗发现您的上述违法违规行为,依据相关法律法规的规定,智能快洗有权或有义务立即停止相关服务,删除或屏蔽相关违规信息、服务评价,并视情况进行调查取证,保存有关记录,或向国家有关机关举报。同时,智能快洗有权自主删除、屏蔽含有该内容的任何数据信息。智能快洗将根据国家相关法律法规要求,对依法删除、屏蔽的数据信息予以记录、保存。
+					</view>
+					<view class="MsoNormal">
+						【广告促销】您理解并同意:为向您提供更为细致、贴心的服务,在经过您的事先确认后,智能快洗或智能快洗授权、认可的第三方商家、广告商可能通过您注册时填写的手机号码或者电子邮箱向您发送您可能感兴趣的商品服务的广告宣传信息、促销优惠等商业性信息,其方式和范围可不经向您特别通知而变更;如果您不愿意接收此类信息,则您有权通过智能快洗平台提供的相应的退订方式进行退订。
+					</view>
+					<view class="MsoNormal">
+						您理解并同意,对上述的相关广告信息,您应审慎判断其真实性和可靠性,除法律法规明确规定外,您应对依该广告信息进行的交易负责。
+					</view>
+					<view class="MsoNormal">
+						【使用目的】除非在特定服务条款或规则中您与智能快洗另有约定或智能快洗另行予以书面同意,您将确保本服务协议下的服务仅为您个人非商业性质的使用。未经智能快洗书面同意,您不得使用未经智能快洗授权的任何插件、外挂或第三方工具对本服务协议下的服务进行干扰、破坏、修改或施加其他影响。
+					</view>
+					<view class="MsoNormal">
+						五、知识产权
+					</view>
+					<view class="MsoNormal">
+						【知识产权】您理解并知悉,除另有书面声明以外,以下信息和内容的知识产权(包括但不限于专利权、著作权、商标权及商业秘密)归智能快洗所有:
+					</view>
+					<view class="MsoNormal">
+						1. <text>在智能快洗相关服务中所提供的内容和信息(包括但不限于软件、技术、程序、网页、文字、图片、图像、地图、图标、音频、视频、图表、版面设计、电子文档、数据资料等);</text>
+					</view>
+					<view class="MsoNormal">
+						2. <text>智能快洗用于提供上述内容和信息的基础设施和平台(包括但不限于软件、网站、应用程序等);</text>
+					</view>
+					<view class="MsoNormal">
+						3. <text>在智能快洗提供相关服务中所使用的各项商标、商业形象、商业标识、技术诀窍、标语、文案等;</text>
+					</view>
+					<view class="MsoNormal">
+						4. <text>智能快洗平台服务的开发、运营、维护等过程中产生的所有数据和信息。</text>
+					</view>
+					<view class="MsoNormal">
+						智能快洗所拥有的上述权利及所提供服务中所包含的任何内容的知识产权均受到法律保护,未经智能快洗事先书面许可,您承诺不应且不应允许或协助任何人以任何形式(包括但不限于通过任何机器人、蜘蛛、截屏等程序或设备)进行使用、出租、出借、分发、展示、复制、修改、链接、转载、汇编、发表、出版、抓取、监视、引用或创造相关衍生作品。
+					</view>
+					<view class="MsoNormal">
+						【用户生成内容】您理解并同意,您在使用智能快洗提供的服务时上传、提交、存储或发布的内容(包括但不限于文字、图片、视频、音频、动画等)均由您原创或已获合法授权。您通过智能快洗上传、提交、存储或发布的任何内容的知识产权归属您或原始著作权人所有,您的上传、提交、存储或发布行为不会侵犯他人的知识产权或其他合法权益。
+					</view>
+					<view class="MsoNormal">
+						您知悉、理解并同意,您一旦接受本服务协议,即表明您主动将上述内容的非专属、可转让的财产性权利,如著作权(包括但不限于:复制权、发行权、出租权、展览权、表演权、放映权、广播权、信息网络传播权、摄制权、改编权、翻译权、汇编权以及应当由著作权人享有的其他可转让权利),在全世界范围内永久、免费、独家且不可撤销地授权给智能快洗及其关联公司,智能快洗及其关联公司可基于该等授权使用上述内容(包括但不限于用于商业用途)或向第三方自主进行任何必要的转授权。该等授权、转授权的使用场景包括但不限于当前或其他任何网站、应用程序、产品或移动终端设备等,且智能快洗及其关联公司或智能快洗及其关联公司所授权许可的第三方可通过对上述授权内容进行修改、复制、改编、翻译、汇编或制作,形成衍生产品。在不违反相关法律法规的强制性规定、尊重相关原始授权内容的知识产权的基础上,该等衍生产品的相关知识产权归智能快洗及其关联公司或智能快洗所授权许可的第三方所有。
+					</view>
+					<view class="MsoNormal">
+						您确认并同意授权智能快洗及其关联公司以自己的名义或委托专业第三方针对有关您上传、提交、存储或发布的内容(含衍生作品)的侵权行为进行独立自主的维权并获得全部赔偿。维权形式包括但不限于:监测侵权行为、发送维权函、提起诉讼或仲裁、调解、和解等。智能快洗及其关联公司有权对维权事宜做出独立决策并予以实施。
+					</view>
+					<view class="MsoNormal">
+						您同意,因您通过智能快洗上传、发布的任何内容导致的知识产权侵权问题,您将承担全部责任;如智能快洗及其关联公司,或智能快洗授权的其他服务提供方因第三方知识产权维权而产生损失,您将等额赔偿
+					</view>
+					<view class="MsoNormal">
+						&nbsp;
+					</view>
+					<view class="MsoNormal">
+						本服务协议已经构成《著作权法》第二十五条所规定的书面协议,其效力及于用户在智能快洗平台发布的任何受著作权法保护的作品内容,无论该内容形成于本服务协议签订前还是本服务协议签订后。智能快洗及智能快洗转授权、许可的相关方有权将您在智能快洗平台发表的产品使用体验、产品讨论或图片进行使用或者与其他人合作使用,使用范围包括但不限于网站、电子杂志、杂志、刊物、海报、公众号文章、微信小程序等。
+					</view>
+					<view class="MsoNormal">
+						【软件使用】在使用智能快洗相关客户端的过程中,您可能需要下载特定软件。为了改善用户体验、修复漏洞、保障安全性等考虑,智能快洗有权对软件进行更新,您应该将相关软件更新至最新版本,否则智能快洗将不保证您能正常使用相关软件。
+					</view>
+					<view class="MsoNormal">
+						如果您从未经智能快洗授权的第三方获取软件或与该软件名称相同的安装程序,智能快洗无法保证该软件能够正常使用,由此造成的相关损失,智能快洗不予负责。
+					</view>
+					<view class="MsoNormal">
+						【软件使用禁止性规定】除非智能快洗书面许可,您在使用智能快洗的软件时不得从事下列任一行为:
+					</view>
+					<view class="MsoNormal">
+						<text>(</text>1<text>)删除、编辑或遮挡软件及其副本上关于著作权、商标或其他权利标识或标记的信息;</text>
+					</view>
+					<view class="MsoNormal">
+						<text>(</text>2<text>)复制、发布、出售或出租软件或其任何部分;</text>
+					</view>
+					<view class="MsoNormal">
+						<text>(</text>3<text>)对软件进行反向工程、反向汇编、反向编译,或者以其他方式尝试挖掘、提取软件的源代码;</text>
+					</view>
+					<view class="MsoNormal">
+						<text>(</text>4<text>)其他对软件、软件所产生的数据所进行的任何增加、删减、变动的任何行为,包含制作、利用、授权各种第三方外挂、插件、系统进行上述行为。</text>
+					</view>
+					<view class="MsoNormal">
+						六、协议的终止
+					</view>
+					<view class="MsoNormal">
+						【协议终止情形】本服务协议于以下任一情形下终止:
+					</view>
+					<view class="MsoNormal">
+						<text>(</text>1<text>)智能快洗有权依据本服务协议约定,收回、注销用户账号,此情形下,本服务协议于账号收回、注销之日终止;</text>
+					</view>
+					<view class="MsoNormal">
+						<text>(</text>2<text>)在满足智能快洗平台公示的账号注销条件时,您通过网站自助服务或智能快洗的客服注销智能快洗的用户账号,本服务协议于账号注销之日终止。您理解并同意,您已认真阅读、认可《智能快洗隐私政策》,并已了解、同意相关用户注销流程及注销后的权利义务安排;</text>
+					</view>
+					<view class="MsoNormal">
+						<text>(</text>3<text>)智能快洗有权根据自身商业安排经过合理的提前通知终止全部智能快洗平台服务,本服务协议于智能快洗平台全部服务依法定程序及方式终止之日终止。</text>
+					</view>
+					<view class="MsoNormal">
+						本服务协议终止后,智能快洗将无法继续向您提供任何服务或履行任何其他义务,包括但不限于为您保留或向您披露其原智能快洗账号中的任何信息,向您或第三方转发任何未曾阅读或发送过的信息等。
+					</view>
+					<view class="MsoNormal">
+						<text>本服务协议的终止不影响第五条知识产权条款以及其他根据其内容应继续有效</text> <text>的条款的有效性,也不影响本服务协议终止前各方的相关权利和义务,包括但不</text> <text>限于守约方依据本服务协议向违约方追究相应的违约责任。</text>
+					</view>
+					<view class="MsoNormal">
+						七、通知与送达
+					</view>
+					<view class="MsoNormal">
+						您知悉并认可,智能快洗可视情况通过下列任意一种或几种方式向您通知重要信息:
+					</view>
+					<view class="MsoNormal">
+						<text>(</text>1<text>)向您注册时所提交的电子邮箱地址发送电子信息;</text>
+					</view>
+					<view class="MsoNormal">
+						<text>(</text>2<text>)向您注册时所提交的手机号码发送电子信息;</text>
+					</view>
+					<view class="MsoNormal">
+						<text>(</text>3<text>)向您提供的实际地址进行寄送纸质载体信息;</text>
+					</view>
+					<view class="MsoNormal">
+						<text>(</text>4<text>)在网站或客户端显著位置刊登电子信息;</text>
+					</view>
+					<view class="MsoNormal">
+						<text>(</text>5<text>)向智能快洗网站或相应客户端的账户的站内信或其他即时通信客户端发送电子信息。</text>
+					</view>
+					<view class="MsoNormal">
+						上述电子信息在发送成功或刊登完成后即视为送达。相关纸质载体的发送以相关邮寄凭证上的邮寄日期后的第五个自然日视为送达。
+					</view>
+					<view class="MsoNormal">
+						上述送达方式同样可适用于相关仲裁或司法程序(含起诉、审理、执行等各阶段)。
+					</view>
+					<view class="MsoNormal">
+						您应当保证所提供的联系方式是准确、有效的,并进行适时更新,如因提供的联系方式不准确或怠于更新等不可归责于智能快洗的原因,导致相关通知、文件、文书无法送达或及时送达,您将自行承担由此可能产生的法律后果。
+					</view>
+					<view class="MsoNormal">
+						八、不可抗力或其他免责事由
+					</view>
+					<view class="MsoNormal">
+						【不可抗力】您理解并同意,在使用本服务的过程中,可能会遇到不可抗力等风险因素,使本服务协议下的服务发生中断或终止。不可抗力是指不能预见、不能克服并不能避免且对一方或双方造成重大影响的客观事件,包括但不限于信息网络设备维护、信息网络连接故障、电脑、通讯或其他系统的故障、电力故障、罢工、劳动争议、暴乱、起义、骚乱、生产力或生产资料不足、火灾、洪水、风暴、爆炸、战争、政府行为、法律法规变动、司法行政机关的命令、其他不可抗力或第三方的不作为而造成的不能服务或延迟服务等行为。出现上述情况时,智能快洗将努力在第一时间与相关部门配合,及时进行修复,但是由此给您造成的损失,智能快洗在法律允许的范围内免责。
+					</view>
+					<view class="MsoNormal">
+						【其他免责事由】您理解并同意,在法律允许的范围内,智能快洗对以下事由所导致的服务中断或终止不承担责任:
+					</view>
+					<view class="MsoNormal">
+						<text>(</text>1<text>)受到计算机病毒、木马或其他恶意程序、黑客攻击的破坏;</text>
+					</view>
+					<view class="MsoNormal">
+						<text>(</text>2<text>)用户或智能快洗的电脑软件、系统、硬件和通信线路出现故障;</text>
+					</view>
+					<view class="MsoNormal">
+						<text>(</text>3<text>)用户操作不当;</text>
+					</view>
+					<view class="MsoNormal">
+						<text>(</text>4<text>)用户通过非智能快洗授权的方式使用本服务;</text>
+					</view>
+					<view class="MsoNormal">
+						<text>(</text>5<text>)其他智能快洗无法控制或合理预见的情形。</text>
+					</view>
+					<view class="MsoNormal">
+						【信息真实性】智能快洗提示您,在使用本服务的过程中,您可能会遇到网络信息或其他用户行为带来的风险,该等风险包含他人匿名或冒名的含有威胁、诽谤、令人反感、非法、误导、欺骗等任何信息或行为所造成任何心理、生理上的伤害或经济损失。请您仔细甄别,并在遇到上述相关不法行为时及时向智能快洗或有关机关举报或投诉。智能快洗将对相关内容开展日常检查并在收到举报或投诉时进行相应处理,但请您注意,智能快洗不对非智能快洗所发出或刊登的任何信息的真实性、适用性、合法性承担责任,也不对因第三方侵权行为给您造成的损害负责。
+					</view>
+					<view class="MsoNormal">
+						【责任限制】除非另有明确书面说明,在符合法律法规规定的情况下,智能快洗不对其网站和客户端上的信息、内容、材料、产品或服务做任何形式的明示或默示的声明或担保。
+					</view>
+					<view class="MsoNormal">
+						九、管辖、法律适用与争议解决
+					</view>
+					<view class="MsoNormal">
+						本服务协议的成立、生效、履行、解释与纠纷解决,适用中华人民共和国大陆地区法律法规,并且排除一切冲突法规定的适用。
+					</view>
+					<view class="MsoNormal">
+						如因某项具体服务中的产品或服务问题导致您与智能快洗间出现纠纷,您同意,该等纠纷将由适用于该项具体服务的服务条款或规则中所规定的争议解决地的有管辖权人民法院受理。就本服务协议而言,如您因本服务协议文本与智能快洗产生争议纠纷,您同意交由本服务协议签订地有管辖权人民法院受理。本服务协议签订地为上海市杨浦区。
+					</view>
+					<view class="MsoNormal">
+						十、其他
+					</view>
+					<view class="MsoNormal">
+						【关键词】本服务协议各条款前所列关键词仅供帮助理解条款主旨及快速定位查询条款所用,不能代替条款的任何内容,亦不作为解释条款的依据,智能快洗建议您仔细阅读各条款的具体表述,以维护您的合法权益。
+					</view>
+					<view class="MsoNormal">
+						【可分性】如果本服务协议中任何一条被视为废止、无效或因任何理由不可执行,该条应视为可分的且并不影响任何其余条款的有效性和可执行性。
+					</view>
+					<view class="MsoNormal">
+						再次感谢您的耐心阅读!
+					</view>
+					<view style="text-align: right;">
+						陕西山海高科信息技术有限公司
+					</view>
+					<view style="text-align: right;">
+						2020年8月
+					</view>
+				</view>
+			</view>
+		</view>
+		<view class="footer" @click="goBack" v-if="!hideZysx">
+			我已阅读并同意无人洗车《注意事项及服务协议》
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		components: {
+		},
+		data() {
+			return {
+				hideZysx: false
+			}
+		},
+		onLoad(option) {
+			// 从个人中心打开的
+			if(option.fromPage == 'usercenter'){
+				this.hideZysx = true
+				uni.setNavigationBarTitle({
+					title: "服务协议"
+				})
+			}
+		},
+		methods: {
+			// 点击已阅读返回上一页
+			goBack () {
+				// 出发已阅读事件
+				uni.$emit('read',{msg:'read'})
+				uni.navigateBack()
+			}
+			
+		}
+	}
+</script>
+
+<style lang="scss">
+	page{
+		width: 100%;
+		height: 100%;
+	}
+	.content{
+		padding: 0;
+		width: 100%;
+		height: 100%;
+		display: flex;
+		flex-direction: column;
+		.page-content{
+			flex: 1;
+			overflow-y: scroll;
+		}
+		.title{
+			font-size: 30rpx;
+			font-weight: 600;
+		}
+		.xy-cons{
+			padding: 30rpx;
+			line-height: 42rpx;
+			.MsoNormal{
+				text-indent: 52rpx;
+				margin-bottom: 14rpx;
+			}
+		}
+		.page-care{
+			width: 100%;
+			display: flex;
+			flex-direction: column;
+			align-items: center;
+			padding-top: 20rpx;
+		}
+		.footer{
+			width: 100%;
+			text-align: center;
+			line-height: 100rpx;
+			background-color: rgb(255,0,0);
+			color: #fff;
+		}
+	}
+</style>

+ 35 - 0
pages/careMore/careMore.vue

@@ -0,0 +1,35 @@
+<template>
+	<view class="care-content">
+		<view class="title">
+			以下情况不能清洗车辆
+		</view>
+		<view class="img">
+			<u-image width="80%" height="1600rpx" mode="widthFix" src="/static/img/carmore.jpg"></u-image>
+		</view>
+	</view>
+</template>
+
+<script>
+</script>
+
+<style lang="scss">
+	page {
+		height: 100%;
+	}
+	.care-content{
+		padding: 20rpx;
+		width: 100%;
+		height: 100%;
+		display: flex;
+		flex-direction: column;
+		.title{
+			font-size: 30rpx;
+			font-weight: 600;
+			margin-bottom: 20rpx;
+		}
+		.img{
+			flex: 1;
+			margin-left: 40rpx;
+		}
+	}
+</style>

+ 82 - 0
pages/coupon/chooseCoupon/chooseCoupon.vue

@@ -0,0 +1,82 @@
+<template>
+	<view class="coopon-pages">
+		<couponTpl @checkOk="checkOk" :list="couponList" :showCheck="true"></couponTpl>
+		<view class="fix-button" v-if="curCoupon">
+			<view>
+				<view>优惠:¥{{curCoupon.couponAmount}}</view>
+				<view @click="comfirn">确认</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import couponTpl from '@/pages/coupon/couponTpl.vue'
+	export default {
+		components: {
+			couponTpl
+		},
+		data() {
+			return {
+				curCoupon: null,
+				couponList:  []
+			};
+		},
+		onLoad(options) {
+			console.log(options,'options')
+			this.couponList = JSON.parse(decodeURIComponent(options.list)) 
+			this.curCoupon = JSON.parse(decodeURIComponent(options.curCoupon)) 
+		},
+		methods:{
+			checkOk(item){
+				this.couponList.map(list=>{
+					list.checked = (list.id === item.id && !list.checked) ? true : false
+				})
+				let has = this.couponList.find(item=> item.checked)
+				if (has) {
+					this.curCoupon = has
+				} else {
+					this.curCoupon = null
+				}
+				// 将用户选择优惠券传给下单页面
+				uni.$emit('couponChoose',{curCoupon:this.curCoupon})
+			},
+			// 确认按钮
+			comfirn(){
+				uni.navigateBack()
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	.coopon-pages{
+		width: 100%;
+		padding-bottom: 100rpx;
+		.fix-button{
+			width: 100%;
+			position: fixed;
+			bottom: 0;
+			> view{
+				display: flex;
+				align-items: center;
+				background: #333;
+				>view{
+					padding: 20rpx 30rpx;
+					color: #fff;
+					&:first-child{
+						width: 70%;
+						flex-grow: 1;
+					}
+					&:last-child{
+						background:#4CD964;
+						width: 30%;
+						color: #fff;
+						text-align: center;
+						font-size: 36rpx;
+					}
+				}
+			}
+		}
+	}
+</style>

+ 115 - 0
pages/coupon/couponTpl.vue

@@ -0,0 +1,115 @@
+<template>
+	<view class="coupon-list">
+		<view class="coupon-box" v-for="item in list" :key="item.id" @click="checked(item)">
+			<view class="coupon-head">
+				<view class="coupon-sel coupon-checked" v-if="item.checked"></view>
+				<view class="coupon-sel" v-else></view>
+				<view class="coupon-title">
+					<view class="coupon-title-1">{{item.couponTitle}}</view>
+				</view>
+				<view class="coupon-price">
+					¥<text>{{item.couponAmount}}</text>
+				</view>
+			</view>
+			<view class="coupon-desc">
+				<view><u-icon class="icon" size="30" name="clock"></u-icon>{{item.validEndTimeStr}}</view>
+				<view>
+					<u-icon class="icon" size="30" name="info-circle"></u-icon>
+					<text class="couponRuleDesc">{{item.couponRuleDesc}}</text>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default{
+		name: 'couponTpl',
+		props: {
+			showCheck: {
+				type: Boolean,
+				default: false
+			},
+			list:{
+				type: Array,
+				default: function(){
+					return []
+				}
+			}
+		},
+		methods: {
+			// 选中优惠卷
+			checked(item) {
+				if(this.showCheck){
+					this.$emit('checkOk',item)
+				}
+			},
+		},
+	}
+</script>
+
+<style lang="scss">
+	.coupon-list{
+		padding: 25rpx;
+	}
+	.coupon-box.diabled{
+		background: #999;
+	}
+	.coupon-box{
+		margin-bottom: 30rpx;
+		border-radius: 15rpx;
+		box-shadow: 2rpx 2rpx 10rpx #ddd;
+		background: #ff0000;
+		color: #FFFFFF;
+		.coupon-head{
+			display: flex;
+			align-items: center;
+			padding: 20rpx 30rpx;
+			position: relative;
+			border-bottom: 15rpx dotted #FFFFFF;
+			margin-bottom: -8rpx;
+			.coupon-sel{
+				position: absolute;
+				width: 56rpx;
+				height: 46rpx;
+				right: 0;
+				top: 0;
+			}
+			.coupon-checked{
+				background: url(/static/img/select.png) no-repeat center;
+				background-size: 100% 100%;
+			}
+			.coupon-title{
+				flex-grow: 1;
+				font-size: 32rpx;
+			}
+			.coupon-price{
+				padding: 0 15rpx;
+				color: #FFFFFF;
+				text{
+					font-size: 60rpx;
+				}
+			}
+		}
+		.coupon-desc{
+			padding: 10rpx 30rpx;
+			color: #666;
+			background: #FFFFFF;
+			border-bottom-left-radius:10rpx;
+			border-bottom-right-radius: 10rpx;
+			>view{
+				padding: 5rpx 0;
+				display: flex;
+				align-items: flex-start;
+			}
+			.couponRuleDesc{
+				width: 100%;
+				padding-right: 30rpx;
+				word-wrap: break-word;
+			}
+			.icon{
+				margin-right: 10rpx;
+			}
+		}
+	}
+</style>

+ 178 - 0
pages/coupon/getCoupon/getCoupon.vue

@@ -0,0 +1,178 @@
+<template>
+	<view class="coupon-list">
+		<view v-if="couponInfo" class="coupon-box">
+			<view class="coupon-head">
+				<view class="coupon-sel"></view>
+				<view class="coupon-title">
+					<view class="coupon-title-1">{{couponInfo.title}}</view>
+				</view>
+				<view class="coupon-price">
+					¥<text>{{couponInfo.subAmount}}</text>
+				</view>
+			</view>
+			<view class="coupon-desc">
+				<view><u-icon class="icon" size="30" name="clock"></u-icon> 有效期:{{couponInfo.validDateStr}}</view>
+				<view>
+					<u-icon class="icon" size="30" name="info-circle"></u-icon>
+					<text class="couponRuleDesc">{{couponInfo.ruleDesc}}</text>
+				</view>
+			</view>
+		</view>
+		<u-empty :text="noDataText" v-if="!couponInfo && status!='loading'" mode="coupon"></u-empty>
+		<u-button v-if="!isDisabled" class="handleSubmit" :disabled="isDisabled" shape="circle" @click="handleSubmit" type="error">确认领取</u-button>
+	</view>
+</template>
+
+<script>
+	import {couponData, receivesCoupon} from '@/api/coupon.js'
+	export default{
+		name: 'getCoupon',
+		data () {
+			return {
+				couponInfo: null, // 领取优惠券信息
+				isDisabled: true, // 是否可领取
+				status: 'loading',
+				noDataText: '暂无优惠券'
+			}
+		},
+		onLoad(options) {
+			console.log(options,'options')
+			// 存储当前扫码的券码
+			if (options.scene) {
+				let scene = decodeURIComponent(options.scene);
+				console.log(scene,'1111')
+				//&是我们定义的参数链接方式
+				let customerType = scene.split(',')[0];
+				let storeId = scene.split(',')[1];
+				let couponCode = scene.split(',')[2];
+				console.log(scene.split(','),'222')
+				//其他逻辑处理。。。。。
+				this.$store.state.vuex_couponInfo.customerType = customerType
+				this.$store.state.vuex_couponInfo.storeId = storeId
+				this.$store.state.vuex_couponInfo.couponCode = couponCode
+				// 获取要领取的优惠券
+				this.getCoupon(couponCode)
+			} else {
+				// 判断是否扫码过
+				if (this.$store.state.vuex_couponCode != '') {
+					let couponCode = this.$store.state.vuex_couponCode
+					this.getCoupon(couponCode)
+				} else {
+					// 返回首页
+					uni.reLaunch({
+						url: "/pages/index/index"
+					})
+				}
+			}
+		},
+		methods: {
+			// 获取要领取的优惠券
+			getCoupon (code) {
+				this.status = 'loading'
+				couponData({couponCode:code}).then(res=>{
+					this.status = 'onmore'
+					if (res.status == 200 ) {
+						this.couponInfo = res.data
+						this.isDisabled = res.data ? false : true
+					} else {
+						this.couponInfo = null
+						this.isDisabled = true
+						this.noDataText = res.message
+					}
+				})
+			},
+			// 确认领取优惠券
+			handleSubmit () {
+				let params= {
+					wechatCustomerType: this.$store.state.vuex_couponInfo.customerType,
+					storeId: this.$store.state.vuex_couponInfo.storeId,
+					code: this.$store.state.vuex_couponInfo.couponCode
+				}
+				receivesCoupon(params).then(res =>{
+					if (res.status == 200) {
+						this.toashMsg('领取成功')
+						setTimeout(()=>{
+							uni.reLaunch({
+								url: '/pages/index/index'
+							})
+						},500)
+					}
+				})
+			}
+			
+		},
+	}
+</script>
+
+<style lang="scss">
+	.coupon-list{
+		width: 100%;
+		height: 100%;
+		padding: 25rpx;
+		.u-size-default.data-v-3bf2dba7 {
+			margin-top: 100px;
+			width: 60%;
+		}
+	}
+	.coupon-box.diabled{
+		background: #999;
+	}
+	.coupon-box{
+		margin-bottom: 30rpx;
+		border-radius: 15rpx;
+		box-shadow: 2rpx 2rpx 10rpx #ddd;
+		background: #ff0000;
+		color: #FFFFFF;
+		.coupon-head{
+			display: flex;
+			align-items: center;
+			padding: 20rpx 30rpx;
+			position: relative;
+			border-bottom: 15rpx dotted #FFFFFF;
+			margin-bottom: -8rpx;
+			.coupon-sel{
+				position: absolute;
+				width: 56rpx;
+				height: 46rpx;
+				right: 0;
+				top: 0;
+			}
+			.coupon-checked{
+				background: url(/static/img/select.png) no-repeat center;
+				background-size: 100% 100%;
+			}
+			.coupon-title{
+				flex-grow: 1;
+				font-size: 32rpx;
+			}
+			.coupon-price{
+				padding: 0 15rpx;
+				color: #FFFFFF;
+				text{
+					font-size: 60rpx;
+				}
+			}
+		}
+		.coupon-desc{
+			padding: 10rpx 30rpx;
+			color: #666;
+			background: #FFFFFF;
+			border-bottom-left-radius:10rpx;
+			border-bottom-right-radius: 10rpx;
+			>view{
+				padding: 5rpx 0;
+				display: flex;
+				align-items: flex-start;
+			}
+			.couponRuleDesc{
+				width: 100%;
+				padding-right: 30rpx;
+				word-wrap: break-word;
+			}
+			.icon{
+				margin-right: 10rpx;
+			}
+		}
+		
+	}
+</style>

+ 140 - 0
pages/coupon/index/index.vue

@@ -0,0 +1,140 @@
+<template>
+	<view class="pages-content">
+		<view class="tabs">
+			<u-tabs-swiper ref="uTabs" active-color="red" :list="tabsList" bar-width="100" :current="current" @change="tabsChange" :is-scroll="false"
+			 swiperWidth="750"></u-tabs-swiper>
+		</view>
+		<swiper style="height: calc(100% - 50px);" :current="swiperCurrent" @transition="transition"  @change="swiperChange" @animationfinish="animationfinish">
+			<swiper-item class="swiper-item" v-for="(item, index) in tabsList" :key="index">
+				<scroll-view scroll-y style="height: 100%;width: 100%;" @scrolltolower="onreachBottom">
+					<couponTpl :list="index==current?couponList:[]"></couponTpl>
+					<u-empty :text="noDataText" img-width="120" v-if="couponList.length==0 && status!='loading'" mode="list"></u-empty>
+					<view style="padding: 20upx;">
+						<u-loadmore v-if="count>pageSize || status=='loading'" :status="status" />
+					</view>
+				</scroll-view>
+			</swiper-item>
+		</swiper>
+	</view>
+</template>
+
+<script>
+	import couponTpl from '@/pages/coupon/couponTpl.vue'
+	import {couponListFind} from '@/api/coupon.js'
+	export default {
+		components: {
+			couponTpl
+		},
+		data() {
+			return {
+				noDataText: '暂无数据',
+				status: 'loading',
+				tabsList: [{
+					name: '未使用',
+					status: 0
+				}, {
+					name: '已使用',
+					status: 1
+				}, {
+					name: '已过期',
+					status: -1
+				}],
+				couponList: [], // 优惠卷列表
+				// 因为内部的滑动机制限制,请将tabs组件和swiper组件的current用不同变量赋值
+				current: 0, // tabs组件的current值,表示当前活动的tab选项
+				swiperCurrent: 0, // swiper组件的current值,表示当前那个swiper-item是活动的
+				pageNo:1,
+				pageSize:10,
+				count:0
+			};
+		},
+		onLoad() {
+			this.pageInit()			
+		},
+		methods: {
+			pageInit () {
+				this.current = 0 // tabs组件的current值,表示当前活动的tab选项
+				this.swiperCurrent = 0
+				this.couponList = []
+				this.pageNo = 1
+				this.status = 'loading'
+				this.getList()
+			},
+			// tabs通知swiper切换
+			tabsChange(index) {
+				this.swiperCurrent = index;
+			},
+			// swiper-item左右移动,通知tabs的滑块跟随移动
+			transition(e) {
+				let dx = e.detail.dx;
+				this.$refs.uTabs.setDx(dx);
+			},
+			swiperChange(event){
+				console.log(event.detail)
+				this.couponList = []
+				this.status = 'loading'
+			},
+			// 由于swiper的内部机制问题,快速切换swiper不会触发dx的连续变化,需要在结束时重置状态
+			// swiper滑动结束,分别设置tabs和swiper的状态
+			animationfinish(e) {
+				let current = e.detail.current;
+				if (current != this.current) {
+					this.$refs.uTabs.setFinishCurrent(current);
+					this.swiperCurrent = current;
+					this.current = current;
+					this.getList()
+				}
+			},
+			// 获取优惠卷列表
+			getList(num){
+				if(num) {
+					this.pageNo = num
+				}
+				let item = this.tabsList[this.swiperCurrent]
+				let params = {
+					pageNo: this.pageNo,
+					pageSize: this.pageSize,
+					status: item.status
+				}
+				couponListFind(params).then(res=>{
+					this.status = 'loadmore'
+					if (res.status == 200) {
+						let list = res.data.list
+						this.couponList = this.couponList.concat(list)
+						this.count = res.data.count
+					} else {
+						this.couponList = []
+						this.count = 0
+					}
+				})
+			},
+			// scroll-view到底部加载更多
+			onreachBottom() {
+				if (this.couponList.length<this.count) {
+					this.pageNo++
+					this.getList()
+				} else {
+					this.status = "nomore"
+				}
+			}
+		}
+	};
+</script>
+
+<style lang="scss">
+	page{
+		height: 100%;
+	}
+	.pages-content{
+		width: 100%;
+		height: 100%;
+		display: flex;
+		flex-direction: column;
+		uni-swiper{
+			flex-grow: 1;
+		}
+		.u-empty{
+			height: auto!important;
+		}
+	}
+</style>

+ 665 - 0
pages/getOrder/getOrder.vue

@@ -0,0 +1,665 @@
+<template>
+	<view class="order-content">
+		<view class="step">
+			<uni-steps :options="[{title: '停车'}, {title: '检查'}, {title: '下单'}]" :active="stepIndex" active-color="rgb(255,0,0)"></uni-steps>
+		</view>
+		<view class="step-content">
+			<!-- 停车 -->
+			<view v-if="stepIndex===0" class="step-item step-one">
+				<text>请确认车辆是否停到指定位置</text>
+				<image src="/static/img/step-one.png" mode="aspectFit"></image>
+			</view>
+			<!-- 检查 -->
+			<view v-if="stepIndex===1" class="step-item step-two">
+				<view class="item top">
+					<text class="item-text">-请完成以下操作-</text>
+					<image src="/static/img/cztis.png" mode="aspectFit"></image>
+					<view class="care">
+						注意:洗车过程中禁止移动车辆和上下车
+					</view>
+				</view>
+				<view class="item bottom" @click="lookMore">
+					<view class="item-text">
+						<text>超高加装件不能清洗</text>
+						<view class="right">
+							<text>查看更多</text>
+							<u-icon name="arrow-right" size="28"></u-icon>
+						</view>
+					</view>
+					<image src="/static/img/zyts.png" ></image>
+				</view>
+			</view>
+			<!-- 下单 -->
+			<view v-if="stepIndex===2" class="step-item step-three">
+				<view class="choose">
+					<view class="title">
+						请选择服务模式
+					</view>
+					<view class="list">
+						<view :class="['item', serverIndex==index ?'avtive':'']" @click="chooseItem(index)" v-for="(item,index) in itemList"
+						 :key="index">
+							<text>{{item.itemName}}</text>
+							<text class="price">¥{{item.currentPrice}}</text>
+						</view>
+						<u-empty text="暂无服务项目" img-width="120" v-if="itemList.length==0 && status!='loading'" mode="list"></u-empty>
+					</view>
+				</view>
+				<view class="choose-content">
+					<view class="choose-item">
+						<view class="item" >
+							<text>优惠卷</text>
+							<view @click="intoChooseCoupon" v-if="couponList.length" class="right">
+								<u-icon name="gift" color="rgb(255,0,0)" size="32"></u-icon>
+								<text class="coopon">{{checkedCoupon?(checkedCoupon.couponTitle+'(¥-'+checkedCoupon.couponAmount+')'):''}}</text>
+								<u-icon name="arrow-right" color="rgb(255,0,0)" size="32"></u-icon>
+							</view>
+							<view v-else class="right">
+								<text>暂无可用优惠券</text>
+							</view>
+						</view>
+						<view class="item phone">
+							<text>手机号码</text>
+							<view class="right">
+								<text>{{userPhone}}</text>
+							</view>
+						</view>
+					</view>
+					<view class="read" @click="intoAgreement">
+						<u-radio-group v-model="isRead">
+							<u-radio shape="square" :disabled="isRead!='read'" name="read" active-color="red" icon-size="28" label-size="28">
+								点击阅读并同意
+								<text>无人洗车</text>
+								<text>《注意事项及服务协议》</text>
+							</u-radio>
+						</u-radio-group>
+					</view>
+				</view>
+			</view>
+		</view>
+		<view class="step-footer">
+			<button class="next-button" @click="nextStep" v-if="stepIndex!=2" type="warn">下一步</button>
+			<view v-else class="pay">
+				<text>合计:</text>
+				<text class="amount">¥{{finalAmount}}</text>
+				<view class="">
+					<button :disabled="disabledPay" @click="handleSubmit" type="warn">去付款</button>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import {couponEnable} from '@/api/coupon.js'
+	import uniSteps from '@/components/uni-steps/uni-steps.vue'
+	import {
+		getUserInfo
+	} from '@/api/login.js'
+	import {
+		getStoreItems,
+		saveOrder,
+		signPay
+	} from '@/api/order.js'
+	import {
+		getStoreStatus
+	} from '@/api/store.js'
+	export default {
+		components: {
+			uniSteps
+		},
+		data() {
+			return {
+				status: 'loadmore',
+				disabledPay: false,
+				userPhone: '',
+				stepIndex: 0, // 当前步奏下标
+				serverIndex: 0, // 选择服务下标
+				itemList: [], // 服务类型
+				isRead: '', // 是否同意协议
+				couponList: [], // 用户可用优惠券
+				checkedCoupon: null, // 用户当前所选优惠券
+				amount: 0, // 所选服务项目总金额
+				couponAmount: 0, // 优惠券优惠金额
+				finalAmount: 0, // 用户最终所需支付金额
+			}
+		},
+		onLoad(options) {
+			// 存储当前扫码的设备及网点id
+			if (options.scene) {
+				let scene = decodeURIComponent(options.scene);
+				//&是我们定义的参数链接方式
+				let storeId = scene.split(",")[0];
+				let bizId = scene.split(',')[1];
+				//其他逻辑处理。。。。。
+				this.$u.vuex('vuex_bizId',bizId)
+				this.$u.vuex('vuex_storeId',storeId)
+				// 判断当前门店是否已运营
+				this.getStoreStatus(storeId,bizId)
+			} else {
+				// 判断是否扫码过
+				if (this.$store.state.vuex_bizId != '') {
+					this.dataInit()
+				} else {
+					// 返回首页
+					uni.reLaunch({
+						url: "/pages/index/index"
+					})
+				}
+			}
+		},
+		onUnload() {
+			uni.removeStorageSync('stepIndex')
+			uni.$off('couponChoose', this.couponChoose)
+		},
+		onShow() {
+			const index = uni.getStorageSync('stepIndex')
+			this.stepIndex = index ? index : 0
+			console.log(this.stepIndex)
+		},
+		methods: {
+			// 查询基础数据
+			dataInit(){
+				// 监听点击已阅读事件
+				uni.$once('read', this.read)
+				// 监听用户选择的优惠券
+				uni.$on('couponChoose', this.couponChoose)
+				// 获取用户信息
+				this.getUserPhone()
+				// 获取服务项目
+				this.getServerList()
+			},
+			// 获取网点营业状态
+			getStoreStatus(storeId, bizId) {
+				uni.showLoading({
+					title: '加载中...',
+					mask: true
+				})
+				getStoreStatus({
+					storeId: storeId,
+					deviceNo: bizId
+				}).then(res => {
+					uni.hideLoading()
+					console.log(res,'---------')
+					if(res.status != 200 && res.status != 401) {
+						uni.showModal({
+						    title: '提示',
+						    content: res.message,
+							showCancel: false,
+						    success: function (res) {
+						        if (res.confirm) {
+									uni.reLaunch({
+										url:'/pages/index/index'
+									})
+						        }
+						    }
+						});
+					}else{
+						this.dataInit()
+					}
+				})
+			},
+			// 获取用户信息
+			getUserPhone() {
+				getUserInfo().then(res => {
+					console.log(res, 'rrrrrrr')
+					if (res.status == 200) {
+						this.userPhone = res.data.mobile
+					} else {
+					}
+				})
+			},
+			// 查看更多注意事项
+			lookMore() {
+				uni.navigateTo({
+					url: '/pages/careMore/careMore'
+				})
+			},
+			// 获取服务列表
+			getServerList(flag) {
+				this.status = 'loading'
+				let storeId = this.$store.state.vuex_storeId
+				getStoreItems({
+					storeId: storeId
+				}).then(res => {
+					this.status = 'loadmore'
+					if (res.status == 200) {
+						this.itemList = res.data
+						// 支付失败 重查服务项时不切换服务及重查优惠券
+						if (!flag) {
+							this.serverIndex = 0
+							// 获取用户可用优惠券
+							this.getCouponList()
+						}
+					} else {
+						this.itemList = []
+					}
+				})
+			},
+			// 选择服务项目
+			chooseItem(index) {
+				this.serverIndex = index
+				// 获取用户可用优惠券
+				this.getCouponList()
+			},
+			// 下一步
+			nextStep() {
+				this.stepIndex = this.stepIndex === 0 ? 1 : 2
+				uni.setStorageSync('stepIndex', this.stepIndex)
+			},
+			// 已阅读
+			read(e) {
+				this.isRead = e.msg
+			},
+			// 获取用户选择的优惠券
+			couponChoose (e) {
+				this.checkedCoupon = e.curCoupon
+				// 计算合计
+				this.getFinalAmount()
+			},
+			// 获取用户可用优惠券
+			getCouponList() {
+				let item = this.itemList[this.serverIndex]
+				let params = {
+					consumAmount: item.currentPrice, //服务金额
+					consumStoreId: this.$store.state.vuex_storeId, // 门店id
+					itemId: item.itemId  // 服务id
+				}
+				uni.showLoading({
+					title: '正在查询可用优惠券...',
+					mask: true
+				})
+				couponEnable(params).then(res=> {
+					uni.hideLoading()
+					if (res.status == 200) {
+						this.couponList = res.data
+						this.checkedCoupon = res.data.length ? this.findCouponMax() : null
+					} else {
+						this.couponList = []
+						this.checkedCoupon = null
+					}
+					// 计算合计
+					this.getFinalAmount()
+				})
+			},
+			// 查找优惠卷中优惠金额最大得
+			findCouponMax(){
+				// 比较数据中优惠金额最大的值 并返回该对象
+				let obj = this.couponList.reduce((p, v) => p.couponAmount < v.couponAmount ? v : p)
+				return obj
+			},
+			// 计算合计
+			getFinalAmount() {
+				this.amount = this.itemList[this.serverIndex].currentPrice
+				this.couponAmount = (this.checkedCoupon && this.checkedCoupon.couponAmount > this.amount) ? this.amount : (this.checkedCoupon ?this.checkedCoupon.couponAmount : 0)
+				let ret = this.amount - this.couponAmount
+				this.finalAmount = ret.toFixed(2)
+			},
+			// 选择优惠券
+			intoChooseCoupon() {
+				this.couponList.map((item)=> {
+					item.checked = (this.checkedCoupon && item.id == this.checkedCoupon.id) ? true : false
+				})
+				uni.navigateTo({
+					url: `/pages/coupon/chooseCoupon/chooseCoupon?list=${encodeURIComponent(JSON.stringify(this.couponList))}&curCoupon=${encodeURIComponent(JSON.stringify(this.checkedCoupon))}`
+				})
+			},
+			// 打开服务协议
+			intoAgreement() {
+				uni.navigateTo({
+					url: '/pages/agreement/agreement'
+				})
+			},
+			// 去付款 保存订单
+			handleSubmit() {
+				if (this.itemList.length == 0) {
+					this.toashMsg('请先选择服务')
+					return
+				}
+				if (this.isRead != 'read') {
+					this.toashMsg('请先阅读并同意无人洗车《注意事项及服务协议》!')
+					return
+				} 
+				uni.showLoading({
+					title: '请求中...',
+					mask: true
+				})
+				let item = this.itemList[this.serverIndex]
+				let storeId = this.$store.state.vuex_storeId
+				let deviceNo = this.$store.state.vuex_bizId
+				let params = {
+					"itemId": item.itemId, // 服务id
+					"itemCode": item.itemCode,
+					"storeId": storeId, // 门店id
+					"deviceNo": deviceNo, // 设备编号
+					"orderTime": this.$u.timeFormat(Date.now(), 'yyyy-mm-dd hh:MM:ss'),
+					"payableAmount": this.amount, // 应付金额 即优惠前金额
+					"paymentAmount": this.finalAmount, // 实收金额 即优惠后金额
+					"couponAmount": this.couponAmount, // 优惠金额
+				}
+				console.log(item,'itemitemitem')
+				this.$u.vuex('vuex_itemTime',item.duration)
+				if (this.checkedCoupon) {
+					params.couponReceivesList = [{id:this.checkedCoupon.id}]
+				}
+				this.disabledPay = true
+				saveOrder(params).then(res => {
+					if (res.status == 200) {
+						let data = res.data
+						// 保存成功后 请求预支付返回吊起微信支付所需参数
+						this.getPayData(data)
+						this.$u.vuex('vuex_itemCode',data.itemCode)
+						this.$u.vuex('vuex_orderNo',data.orderNo)
+					} else {
+						uni.hideLoading()
+						this.toashMsg(res.message)
+						this.disabledPay = false
+						setTimeout(()=>{
+							// 刷新服务项
+							this.getServerList(true)
+						},2000)
+					}
+				})
+			},
+			// 预支付 返回微信支付所需参数
+			getPayData(orderData) {
+				let _this = this
+				signPay({
+					orderId: orderData.id
+				}).then(res => {
+					console.log(res, '预支付')
+					uni.hideLoading()
+					if (res.status == 200) {
+						let data = res.data
+						// 0元订单直接支付成功,不用吊微信支付
+						if (data.tradeStatus && data.tradeStatus == "PAID") {
+							this.paySuccess()
+						} else {
+							uni.requestPayment({
+								provider: 'wxpay',
+								timeStamp: data.timeStamp,
+								nonceStr: data.nonceStr,
+								package: data.package,
+								signType: data.signType,
+								paySign: data.paySign,
+								success: function(res) {
+									console.log('success:' + JSON.stringify(res));
+									_this.paySuccess()
+								},
+								fail: function(err) {
+									console.log('fail:' + JSON.stringify(err));
+									_this.payFail(err)
+									_this.disabledPay = false
+								}
+							});
+						}
+					} else {
+						this.toashMsg(res.message)
+						this.disabledPay = false
+					}
+				})
+			},
+			// 付款成功后
+			paySuccess(){
+				this.$u.vuex('vuex_orderStatus','PAID')
+				this.$u.vuex('vuex_orderDjs',0)
+				this.$u.vuex('vuex_workStep',0)
+				this.$u.vuex('vuex_workStatus','linking')
+				 
+				// 付款成功后开始洗车
+				uni.showLoading({
+					title: '支付验证中...',
+					mask: true
+				})
+				setTimeout(()=>{
+					this.disabledPay = false
+					uni.hideLoading()
+					uni.redirectTo({
+						url: `/pages/work/index/index`
+					})
+				},3000)
+			},
+			// 付款失败或取消
+			payFail(err){
+				this.$u.vuex('vuex_orderStatus','FAIL')
+				let content = '支付失败,请重试'
+				if(err.errMsg.indexOf("cancel")>=0){
+					content = '支付取消'
+					this.toashMsg(content)
+					// 取消支付 跳到订单列表
+					setTimeout(()=>{
+						uni.redirectTo({
+							url: `/pages/order/order`
+						})
+					},500)
+				}
+				this.toashMsg(content)
+				
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	page {
+		height: 100%;
+	}
+
+	.order-content {
+		width: 100%;
+		height: 100%;
+		display: flex;
+		flex-direction: column;
+
+		.step {
+			padding: 40rpx 0;
+		}
+		.step-content {
+			flex: 1;
+			width: 100%;
+			display: flex;
+			flex-direction: column;
+			overflow-y: scroll;
+			.step-item {
+				width: 100%;
+				display: flex;
+				flex-direction: column;
+				align-items: center;
+				flex: 1;
+			}
+
+			// 第一步
+			.step-one {
+				text {
+					color: #999;
+					margin: 40rpx 0;
+				}
+			}
+
+			// 第二步
+			.step-two {
+				background-color: #F8F8F8;
+				justify-content: space-around;
+
+				.item {
+					background-color: #fff;
+					width: 92%;
+					height: 45%;
+					display: flex;
+					flex-direction: column;
+					align-items: center;
+					image {
+						width: 80%;
+						flex: 1;
+					}
+
+				}
+
+				.top {
+					text {
+						color: #df928e;
+						font-size: 24rpx;
+					}
+
+					.item-text {
+						padding-top: 20rpx;
+					}
+
+					.care {
+						padding: 10rpx 20rpx;
+						background-color: #ff805b;
+						border-radius: 30rpx;
+						color: #fff;
+						font-size: 24rpx;
+						margin-bottom: 20rpx;
+					}
+				}
+
+				.bottom {
+					text {
+						font-size: 24rpx;
+						color: #333;
+					}
+					.item-text {
+						width: 100%;
+						padding: 0 40rpx;
+						display: flex;
+						justify-content: space-between;
+						padding-top: 20rpx;
+					}
+					image {
+						margin-top: 20rpx;
+					}
+				}
+			}
+
+			// 第三步 下单
+			.step-three {
+				.choose {
+					width: 100%;
+					height: auto;
+					padding-bottom: 60rpx;
+					display: flex;
+					flex-direction: column;
+					align-items: center;
+					border-bottom: 1px solid #eee;
+
+					.title {
+						color: #666;
+					}
+
+					.list {
+						width: 100%;
+						display: flex;
+						justify-content: space-around;
+						padding: 0 20rpx;
+
+						.item {
+							width: 30%;
+							height: 140rpx;
+							display: flex;
+							flex-direction: column;
+							align-items: center;
+							justify-content: center;
+							border: 1px solid #eee;
+							border-radius: 15rpx;
+							margin-top: 30rpx;
+							color: #999;
+
+							.price {
+								font-size: 34rpx;
+								margin-top: 10rpx;
+							}
+						}
+
+						.avtive {
+							color: #333;
+							border-color: rgb(255, 0, 0);
+
+							.price {
+								color: rgb(255, 0, 0);
+							}
+						}
+					}
+
+				}
+
+				.choose-content {
+					flex: 1;
+					width: 100%;
+
+					.choose-item {
+						width: 100%;
+
+						.item {
+							padding: 0 30rpx;
+						}
+					}
+
+					.item {
+						width: 100%;
+						height: 100rpx;
+						display: flex;
+						align-items: center;
+						justify-content: space-between;
+						border-bottom: 1px solid #eee;
+
+						.coopon {
+							color: rgb(255, 0, 0);
+						}
+					}
+
+					.phone {
+						border-bottom: none;
+					}
+
+					.read {
+						width: 100%;
+						border-top: 1px solid #eee;
+						padding-top: 40rpx;
+						display: flex;
+						justify-content: center;
+
+						text {
+							color: #999;
+						}
+
+						text:last-child {
+							color: #ff9ba5;
+						}
+					}
+				}
+			}
+		}
+
+		.step-footer {
+			width: 100%;
+			background-color: #fff;
+
+			.next-button {
+				margin: 0;
+				border-radius: 0;
+				padding: 10rpx 0;
+			}
+
+			.pay {
+				width: 100%;
+				height: 100%;
+				padding: 10rpx;
+				border-top: 1px solid #eee;
+				display: flex;
+				align-items: center;
+				justify-content: flex-end;
+
+				.amount {
+					color: rgb(255, 0, 0);
+					font-size: 36rpx;
+					margin-right: 40rpx;
+				}
+
+				button {
+					width: 200rpx;
+				}
+			}
+		}
+	}
+</style>

+ 41 - 0
pages/index/index.vue

@@ -0,0 +1,41 @@
+<template>
+	<view class="content">
+		 sdfdf 
+	</view>
+</template>
+
+<script>
+	import {
+		checkLogin
+	} from '@/api/login.js'
+	import {
+		getMapStoreList
+	} from '@/api/store.js'
+	import {
+		getLookUpDatas
+	} from '@/api/data.js'
+	import {
+		getUnFinishedOrder
+	} from '@/api/order.js'
+	export default {
+		components: {
+		},
+		data() {
+			return {
+			}
+		},
+		onReady() {
+		},
+		onShow() {
+		},
+		onLoad() {
+		},
+		methods: { 
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.content { 
+	}
+</style>

+ 153 - 0
pages/login/login.vue

@@ -0,0 +1,153 @@
+<template>
+	<view class="content">
+		<image class="logo-img" mode="aspectFit" src="/static/img/logo.png"></image>
+		<u-button shape="circle" class="login-btn" type="success" plain :hair-line="false" open-type="getPhoneNumber" @getphonenumber="getPhoneNumber">
+			<view class="login-btn-content">
+			  <view class="wechat-img"></view>
+			  <view>微信登陆</view>
+			</view>
+		</u-button>
+		<view class="canseLogin" @click="gotoHome()">
+		  取消登录
+		</view>
+	</view>
+</template>
+
+<script>
+	import {
+		login
+	} from "@/api/login.js"
+	export default {
+		components: {},
+		data() {
+			return {
+				path: '/pages/index/index',
+				lanuch: true,
+				code: ''
+			}
+		},
+		onShow() {
+			
+		},
+		mounted() {},
+		onLoad(option) {
+			// 为解决首次登录异常问题 进入页面先调微信登录获取code
+			this.wxLogin()
+			const {
+				path,
+				lanuch
+			} = option;
+			this.lanuch = (lanuch + '') === 'true';
+			if (path) {
+				this.path = decodeURIComponent(path);
+			}
+		},
+		methods: {
+			getPhoneNumber(e) {
+				let _this = this
+				uni.showLoading({
+					mask: true,
+					title: '加载中'
+				});
+				if (!this.code) {
+					this.wxLogin()
+				} 
+				if (e.target.errMsg === 'getPhoneNumber:ok') {
+					setTimeout(()=>{
+						login({
+							code: _this.code,
+							encryptedData: e.target.encryptedData,
+							iv: e.target.iv
+						}).then(res => {
+							uni.hideLoading();
+							_this.code = ''
+							console.log(res,_this.path, 'login data')
+							if (res.status == '200') {
+								getApp().globalData.token = res.data
+								_this.$u.vuex('vuex_token',res.data)
+								if (_this.path === '/pages/index/index' || _this.lanuch) {
+									uni.reLaunch({
+										url: _this.path
+									});
+								} else {
+									uni.redirectTo({
+										url: _this.path
+									});
+								}
+							} else {
+								uni.showToast({
+									title: res.message,
+									icon: 'none',
+									duration: 2500
+								});
+							}
+						});
+					},300)
+				} else {
+					uni.showToast({
+						title: '授权失败',
+						icon: 'none',
+						duration: 2500
+					});
+				}
+			},
+			// 调微信登录 获取code
+			wxLogin () {
+				let _this = this
+				uni.login({
+					provider: 'weixin',
+					success(res) {
+						_this.code = res.code
+					}
+				})
+				console.log(this.code,'1111111111')
+			},
+			gotoHome () {
+			  wx.reLaunch({
+			    url: '/pages/index/index'
+			  })
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	.content {
+		width: 100%;
+		height: 100%;
+		padding: 0;
+		display: flex;
+		align-items: center;
+		padding-top: 35%;
+		.logo-img {
+			width: 400rpx;
+			height: 100rpx;
+		}
+	}
+	.wechat-img {
+	  width: 40px;
+	  height: 35px;
+	  margin-right: 10px;
+	  padding-top: 10px;
+	  background: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/PjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+PHN2ZyBjbGFzcz0iaWNvbiIgd2lkdGg9IjMwcHgiIGhlaWdodD0iMzBweCIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGZpbGw9IiMwOUJCMDciIGQ9Ik0xMDEwLjggNjI4YzAtMTQxLjItMTQxLjMtMjU2LjItMjk5LjktMjU2LjItMTY4IDAtMzAwLjMgMTE1LjEtMzAwLjMgMjU2LjIgMCAxNDEuNCAxMzIuMyAyNTYuMiAzMDAuMyAyNTYuMiAzNS4yIDAgNzAuNy04LjkgMTA2LTE3LjdsOTYuOCA1My0yNi42LTg4LjJjNzAuOS01My4yIDEyMy43LTEyMy43IDEyMy43LTIwMy4zek02MTggNTg4LjhjLTIyLjEgMC00MC0xNy45LTQwLTQwczE3LjktNDAgNDAtNDAgNDAgMTcuOSA0MCA0MGMwIDIyLTE3LjkgNDAtNDAgNDB6IG0xOTQuMy0wLjNjLTIyLjEgMC00MC0xNy45LTQwLTQwczE3LjktNDAgNDAtNDAgNDAgMTcuOSA0MCA0MC0xNy45IDQwLTQwIDQweiIgIC8+PHBhdGggZmlsbD0iIzA5QkIwNyIgZD0iTTM2Ni4zIDEwNi45Yy0xOTQuMSAwLTM1My4xIDEzMi4zLTM1My4xIDMwMC4zIDAgOTcgNTIuOSAxNzYuNiAxNDEuMyAyMzguNGwtMzUuMyAxMDYuMiAxMjMuNC02MS45YzQ0LjIgOC43IDc5LjYgMTcuNyAxMjMuNyAxNy43IDExLjEgMCAyMi4xLTAuNSAzMy0xLjQtNi45LTIzLjYtMTAuOS00OC4zLTEwLjktNzQgMC0xNTQuMyAxMzIuNS0yNzkuNSAzMDAuMi0yNzkuNSAxMS41IDAgMjIuOCAwLjggMzQgMi4xQzY5MiAyMTIuNiA1MzkuOSAxMDYuOSAzNjYuMyAxMDYuOXpNMjQ3LjcgMzQ5LjJjLTI2LjUgMC00OC0yMS41LTQ4LTQ4czIxLjUtNDggNDgtNDggNDggMjEuNSA0OCA0OC0yMS41IDQ4LTQ4IDQ4eiBtMjQ2LjYgMGMtMjYuNSAwLTQ4LTIxLjUtNDgtNDhzMjEuNS00OCA0OC00OCA0OCAyMS41IDQ4IDQ4LTIxLjUgNDgtNDggNDh6IiAgLz48L3N2Zz4K') no-repeat center  center;
+	}
+	
+	.login-btn {
+	  margin-top: 30px;
+	}
+	
+	.login-btn-content {
+	  width: 65vw;
+	  display: flex;
+	  justify-content: center;
+	  align-items: center;
+	}
+	.canseLogin{
+	  width: 65vw;
+	  padding : 20rpx 15rpx;
+	  text-align : center;
+	  font-size: 1em;
+	  margin-top: 1.8em;
+	  color: #666;
+	}
+</style>

+ 179 - 0
pages/order/order.vue

@@ -0,0 +1,179 @@
+<template>
+	<view class="order-pages">
+		<scroll-view scroll-y="true" class="scroll-Y" @scrolltolower="reachBottom">
+			<view class="order-list">
+				<view v-for="item in list" :key="item.id" class="order-list-item" @click="goView(item)">
+					<view>
+						<view>{{item.storeName}}</view>
+						<view :class="item.orderStatus">{{getOptionName(orderStatusList,item.orderStatus)}}</view>
+					</view>
+					<view>
+						<view class="order-carNo">{{item.customerMobile}}</view>
+						<u-icon name="arrow-right" color="#666" size="28"></u-icon>
+					</view>
+					<view>
+						<view class="order-price">
+							<!-- 显示用户实付金额,未支付显示0 -->
+							<text>¥{{item.payStatus=='PAID' ?item.paymentAmount:0}}</text>
+							<text>{{item.payType}}</text>
+						</view>
+						<view>{{item.orderTime}}</view>
+					</view>
+				</view>
+			</view>
+			<u-empty :text="noDataText" img-width="120" v-if="list.length==0 && status!='loading'" mode="list"></u-empty>
+			<view style="padding: 20upx;">
+				<u-loadmore v-if="count>pageSize || status=='loading'" :status="status" />
+			</view>
+		</scroll-view>
+	</view>
+</template>
+
+<script>
+	import {getOrderList, orderDetail} from '@/api/order.js'
+	export default {
+		data() {
+			return {
+				noDataText: '暂无数据',
+				status: 'loading',
+				list:[],
+				pageNo:1,
+				pageSize:10,
+				count:0,
+				orderStatusList: []
+			}
+		},
+		onShow() {
+			this.pageInit()
+		},
+		methods: {
+			pageInit () {
+				this.pageNo = 1
+				this.count = 0
+				this.list = []
+				this.orderStatusList = this.$store.state.vuex_payStatus
+				this.getList()
+			},
+			getOptionName (list,val) {
+			  let p = list.find((item) => {
+				  return item.code == val
+			  })
+			  return p ? p.dispName : '-'
+			},
+			// 获取订单列表
+			getList() {
+				this.status = "loading"
+				let payTypeList = this.$store.state.vuex_payType
+				let params = {
+					pageNo: this.pageNo,
+					pageSize: this.pageSize
+				}
+				getOrderList(params).then(res=>{
+					this.status = "loadmore"
+					if(res.status == 200) {
+						let list = res.data.list
+						list.map(item => {
+							item.payType = item.payType ? this.getOptionName(payTypeList,item.payType) : ''
+						})
+						this.list = this.list.concat(list)
+						this.count = res.data.count
+					} else {
+						this.list = []
+						this.count = 0
+						this.noDataText = res.message
+					}
+				})
+			},
+			// 查看详情
+			goView(item){
+				uni.navigateTo({
+					url:"/pages/order/orderDetail?id="+item.id
+				})
+			},
+			// 滚动到底部自动加载下页
+			reachBottom() {
+				if(this.list.length < this.count){
+					this.pageNo += 1
+					this.getList()
+				}else{
+					this.status = "nomore"
+				}
+			}
+		},
+		
+	}
+</script>
+
+<style lang="scss">
+	page{
+		height: 100%;
+	}
+	.order-pages{
+		width: 100%;
+		height: 100%;
+		.scroll-Y {
+			width: 100%;
+			height: 100%;
+			.order-list{
+				padding: 25rpx;
+				.order-list-item{
+					box-shadow: 1rpx 1rpx 5rpx #eee;
+					border: 1px solid #eee;
+					border-radius: 10rpx;
+					margin: 30rpx 0;
+					> view{
+						padding:20rpx 30rpx;
+						display: flex;
+						align-items: center;
+						border-bottom: 1px dashed #eee;
+						&:last-child{
+							border: 0;
+						}
+						>view{
+							&:first-child{
+								flex-grow: 1;
+							}
+							&:last-child{
+								padding-left: 15rpx;
+							}
+						}
+						.FINISHED{
+							color: #00aa00;
+						}
+						.UN_PAY{
+							color: #ff0000;
+						}
+						.CANCELED{
+							color: #7e7b88;
+						}
+						.PAID{
+							color: #00aaff;
+						}
+						.REFUNDING{
+							color: #00557f;
+						}
+						.REFUNDED{
+							color: #005500;
+						}
+						.order-carNo{
+							font-size: 32rpx;
+						}
+						.order-price{
+							text{
+								margin-right: 20rpx;
+								color: #666;
+								&:first-child{
+									color: red;
+									font-size: 32rpx;
+								}
+							}
+						}
+					}
+				}
+			}
+			.u-empty.data-v-6938e513{
+				height: 90%;
+			}
+		}
+	}
+</style>

+ 179 - 0
pages/order/orderDetail.vue

@@ -0,0 +1,179 @@
+<template>
+	<view class="order-pages">
+		<view class="order-info">
+			<u-cell-group>
+				<u-cell-item title="订单状态" :arrow="false">
+					<view slot="right-icon">
+						<text :class="orderInfo.orderStatus">{{getOptionName(orderStatusList,orderInfo?orderInfo.orderStatus:'')}}</text>
+					</view>
+				</u-cell-item>
+				<u-cell-item title="订单编号" :arrow="false" >
+					{{orderInfo.orderNo}}
+				</u-cell-item>
+				<u-cell-item title="创建时间" :arrow="false" >
+					{{orderInfo.orderTime}}
+				</u-cell-item>
+				<u-cell-item title="服务网点" :arrow="false">
+					{{orderInfo.storeName}}
+				</u-cell-item>
+				<u-cell-item title="服务项目" :arrow="false">
+					{{orderInfo.itemName}}
+				</u-cell-item>
+				<u-cell-item title="手机号" :arrow="false">
+					{{orderInfo.customerMobile}}
+				</u-cell-item>
+				<u-cell-item v-if="orderInfo.payType" title="支付方式" :arrow="false">
+					{{orderInfo.payType}}
+				</u-cell-item>
+				<u-cell-item title="应付金额" :arrow="false">
+					¥{{orderInfo.payableAmount}}
+				</u-cell-item>
+				<!-- 已取消订单优惠金额显示0 -->
+				<u-cell-item title="优惠金额" :arrow="false">
+					{{(orderInfo.couponAmount && orderInfo.orderStatus!='CANCELED')?orderInfo.couponAmount:0}}
+				</u-cell-item>
+				<u-cell-item v-if="orderInfo.orderCoupons && orderInfo.orderCoupons.length" title="优惠券" :arrow="false">
+					{{orderInfo.orderCoupons[0].couponTitle}}
+				</u-cell-item>
+				<u-cell-item title="实付金额" :arrow="false">
+					<text class="order-price">¥{{orderInfo.payStatus=='PAID' ? orderInfo.paymentAmount : 0}}</text>
+				</u-cell-item>
+			</u-cell-group>
+			<view class="order-photo">
+				<view v-if="orderInfo.beginImage">
+					<view>洗车前</view>
+					<u-image @click="showImage(orderInfo.beginImage)" width="100%" height="400rpx" mode="aspectFit" :src="orderInfo.beginImage"></u-image>
+				</view>
+				<view v-if="orderInfo.endImage">
+					<view>洗车后</view>
+					<u-image @click="showImage(orderInfo.endImage)" width="100%" height="400rpx" mode="aspectFit" :src="orderInfo.endImage"></u-image>
+				</view>
+			</view>
+		</view>
+		<view v-if="orderInfo.orderStatus=='UN_PAY'" class="footer">
+			<u-button @click="cancleOrder" type="error" plain>取消订单</u-button>
+		</view>
+	</view>
+</template>
+
+<script>
+	import {orderDetail, cancleOrder} from '@/api/order.js'
+	import {getLookUpDatas} from '@/api/data.js'
+	export default{
+		data() {
+			return {
+				orderInfo: null,
+				src: '',
+				orderStatusList: []
+			}
+		},
+		onLoad(options) {
+			if (options.id) {
+				console.log(options.id)
+				this.getOrderDetail(options.id)
+				this.orderStatusList = this.$store.state.vuex_payStatus
+			}
+		},
+		methods:{
+			getOptionName (list,val) {
+			  if(val){
+				  let p = list.find((item) => {
+				  				  return item.code == val
+				  })
+				  return p ? p.dispName : '-'
+			  }
+			  return '-'
+			},
+			// 获取订单详情
+			getOrderDetail (id) {
+				let payTypeList = this.$store.state.vuex_payType
+				orderDetail({id:id}).then(res =>{
+					if (res.status == 200) {
+						this.orderInfo = res.data
+						this.orderInfo.payType = this.orderInfo.payType ? this.getOptionName(payTypeList,this.orderInfo.payType) : ''
+					} else {
+					}
+				})
+			},
+			// 取消订单
+			cancleOrder (){
+				cancleOrder({id:this.orderInfo.id}).then(res=>{
+					if (res.status==200) {
+						this.toashMsg(res.message)
+						setTimeout(()=>{
+							uni.navigateBack()
+						},500)
+					}
+				})
+			},
+			// 图片预览
+			showImage (url) {
+				uni.previewImage({
+					urls: [url],
+					longPressActions: {
+						itemList: ['发送给朋友', '保存图片', '收藏'],
+						success: function(data) {
+							console.log('选中了第' + (data.tapIndex + 1) + '个按钮,第' + (data.index + 1) + '张图片');
+						},
+						fail: function(err) {
+							console.log(err.errMsg);
+						}
+					}
+				});
+			}
+		},
+	}
+</script>
+
+<style lang="scss">
+	page {
+		height: 100%;
+	}
+	.order-pages{
+		width: 100%;
+		height: 100%;
+		display: flex;
+		flex-direction: column;
+		.order-info{
+			width: 100%;
+			flex: 1;
+			overflow-y: scroll;
+			.u-cell{
+				padding:10rpx 32rpx;
+			}
+			.order-photo{
+				padding:0 30rpx;
+				>view{
+					text-align: center;
+					padding: 20rpx;
+				}
+			}
+			.FINISHED{
+				color: #00aa00;
+			}
+			.UN_PAY{
+				color: #ff0000;
+			}
+			.CANCELED{
+				color: #7e7b88;
+			}
+			.PAID{
+				color: #00aaff;
+			}
+			.REFUNDING{
+				color: #00557f;
+			}
+			.REFUNDED{
+				color: #005500;
+			}
+			.order-price{
+				font-size: 36rpx;
+				color: red;
+			}
+		}
+		.footer{
+			padding: 20rpx;
+		}
+		
+	}
+</style>

+ 290 - 0
pages/store/storeDetail.vue

@@ -0,0 +1,290 @@
+<template>
+	<view class="content">
+		<view class="page-content">
+			<view class="header">
+				<u-image width="160rpx" height="160rpx" :border-radius="10" :src="storeInfo?storeInfo.icon:''"></u-image>
+				<view class="store-info">
+					<view class="name">
+						{{storeInfo ?storeInfo.name : ''}}
+					</view>
+					<view class="address">
+						{{storeInfo?storeInfo.addrDetail:''}}
+					</view>
+					<view class="location">
+						<view class="left">
+							<uni-icons class="icon" size="16" type="paperplane"></uni-icons>
+							<text>{{storeInfo?storeInfo.distance:''}}km</text>
+						</view>
+						<view class="right">
+							<u-icon class="icon" size="32" name="clock"></u-icon>
+							<text>{{storeInfo?storeInfo.beginTime:''}}-{{storeInfo?storeInfo.endTime:''}} 营业</text>
+						</view>
+					</view>
+				</view>
+			</view>
+			<!-- 网点标签 -->
+			<view class="store-mark">
+				<text v-for="(item,index) in labelList" :key="index" class="mark-item">
+					{{item}}
+				</text>
+			</view>
+			<view class="store-server">
+				服务项目:
+				<text v-for="item in storeInfo.itemVOList" :key="item.id">{{item.itemName}}</text>
+			</view>
+			<!-- 路线指引 -->
+			<view class="store-rich store-line" v-if="storeInfo&&storeInfo.guidance">
+				<view class="line-title">
+					* 路线指引 *
+				</view>
+				<view class="line-content">
+					<u-parse :html="storeInfo&&storeInfo.guidance?storeInfo.guidance:''"></u-parse>
+				</view>
+			</view>
+			<!-- 温馨提示 -->
+			<view class="store-rich" v-if="storeInfo&&storeInfo.reminder">
+				<view class="line-title">
+					* 温馨提示 *
+				</view>
+				<view class="line-content">
+					<u-parse :html="storeInfo&&storeInfo.reminder?storeInfo.reminder:''"></u-parse>
+				</view>
+			</view>
+		</view>
+		<view class="footer">
+			<button type="warn" :loading="loading" plain @click="getWaitPhoto">排队情况</button>
+			<button type="warn" @click="openLocation">立刻导航</button>
+		</view>
+		<u-popup v-model="showPop" mode="center" @close="showPop=false" width="90%" height="60%" closeable>
+			<image mode="aspectFit" class="pop-img" :src="waitPhoto"></image>
+		</u-popup>
+	</view>
+</template>
+
+<script>
+	import {getStoreDetail, getWaitPhoto} from '@/api/store.js'
+	export default {
+		components: {
+		},
+		data() {
+			return {
+				// 用户拒绝授权位置信息时默认展示青海西宁位置地图
+				currentPosition: {
+					lat: '36.636193',
+					lng: '101.760521'
+				},
+				showPop: false, // 是否显示排队弹窗
+				storeInfo: null, // 网点详细信息
+				labelList: [], // 网点标签
+				waitPhoto: '',  // 排队照片
+				loading: false  // 是否显示排队按钮loading
+			}
+		},
+		onLoad(option) {
+			// 获取当前经纬度
+			let _this = this
+			this.pageInit()
+			uni.getLocation({
+				type: 'wgs84', // 默认wgs84
+				success: function(res) {
+					_this.currentPosition.lat = res.latitude;
+					_this.currentPosition.lng = res.longitude;
+					// 查询门店信息
+					if (option.id) {
+						_this.getStoreDetail(option.id)
+					}
+				},
+				fail: function(res) {
+					console.log(res)
+					// 查询门店信息
+					if (option.id) {
+						_this.getStoreDetail(option.id)
+					}
+				},
+			});
+			
+		},
+		methods: {
+			pageInit () {
+				this.storeInfo = null
+				this.labelList = []
+				this.loading = false
+			},
+			// 获取网点详情信息
+			getStoreDetail (id) {
+				uni.showLoading({
+					title: '加载中...'
+				})
+				let params = {
+					id: id,
+					lat: this.currentPosition.lat,
+					lng: this.currentPosition.lng
+				}
+				getStoreDetail(params).then(res=>{
+					uni.hideLoading()
+					if (res.status == 200) {
+						this.storeInfo = Object.assign({},res.data)
+						if (this.storeInfo.addrDetail.indexOf("省") > 0 && this.storeInfo.addrDetail.indexOf("市") > 0 && this.storeInfo.addrDetail.indexOf("区") >
+							0) {
+							this.storeInfo.addrDetail = this.storeInfo.addrDetail
+						} else {
+							if (this.storeInfo.addrDetail.indexOf("市") > 0 && this.storeInfo.addrDetail.indexOf("区") > 0) {
+								this.storeInfo.addrDetail = this.storeInfo.addrProvinceName + this.storeInfo.addrDetail
+							} else if (this.storeInfo.addrDetail.indexOf("区") > 0) {
+								this.storeInfo.addrDetail = this.storeInfo.addrProvinceName + this.storeInfo.addrCityName + this.storeInfo.addrDetail
+							} else {
+								this.storeInfo.addrDetail = this.storeInfo.addrProvinceName + this.storeInfo.addrCityName + this.storeInfo.addrDistrictName + this.storeInfo.addrDetail
+							}
+						}
+						this.getOptionName()
+						this.storeInfo.distance = this.storeInfo.distanct && this.storeInfo.distanct.distance ? Math.round(this.storeInfo.distanct.distance / 1000) : ''
+					} else {
+					}
+				})
+			},
+			// 匹配数据字典 获取相应的name
+			getOptionName () {
+				console.log(222)
+				let storeLabelList = this.$store.state.vuex_storeLabel  // 网点标签数据字典列表
+				let arr = []
+				this.storeInfo.labelList.map(item =>{
+					let p = storeLabelList.find((i) => {
+						return i.code == item
+					})
+					if (p) {
+						arr.push(p.dispName)
+					} 
+				})
+				this.labelList = arr
+			},
+			// 获取排队情况
+			getWaitPhoto () {
+				this.loading = true
+				let deviceNo = this.storeInfo.deviceVOList && this.storeInfo.deviceVOList.length? this.storeInfo.deviceVOList[0].deviceNo :''
+				getWaitPhoto({deviceNo:deviceNo}).then(res=>{
+					this.loading = false
+					if(res.status==200) {
+						this.waitPhoto = res.data ? res.data : '/static/img/noPic.jpg'
+						this.showPop=true
+					} 
+				})
+			},
+			// 打开导航位置信息界面
+			openLocation () {
+				let data = this.storeInfo
+				let _this = this
+				uni.getLocation({
+				    type: 'gcj02', //返回可以用于uni.openLocation的经纬度
+				    success: function (res) {
+				        const latitude = data.lat;
+				        const longitude = data.lng;
+						uni.openLocation({
+							latitude: latitude,
+							longitude: longitude,
+							name: data.name,
+							address: data.addrDetail,
+							success: function () {
+								console.log('success');
+							  }
+						})
+				    },
+					fail:function(){
+						_this.toashMsg('获取位置信息失败,请先授权开启定位功能!')
+					}
+				});
+			}
+			
+		}
+	}
+</script>
+
+<style lang="scss">
+	page{
+		height: 100%;
+	}
+	.content{
+		padding: 0 20rpx;
+		width: 100%;
+		height: 100%;
+		display: flex;
+		flex-direction: column;
+		.page-content{
+			flex: 1;
+			overflow-y: scroll;
+		}
+		.header {
+			padding: 20rpx 0;
+			display: flex;
+			.store-info {
+				display: flex;
+				flex: 1;
+				flex-direction: column;
+				justify-content: space-between;
+				padding: 0 20rpx;
+				.name {
+					font-size: 32rpx;
+					color: #000;
+				}
+				.address{
+					font-size: 26rpx;
+				}
+				.location {
+					display: flex;
+					align-items: center;
+					font-size: 24rpx;
+					.left {
+						margin-right: 20rpx;
+					}
+					.icon {
+						color: #999;
+						margin-right: 5rpx;
+					}
+				}
+			}
+		}
+		.store-mark{
+			padding: 20rpx 0;
+			width: 100%;
+			.mark-item{
+				padding: 10rpx 20rpx;
+				border: 1px solid #eee;
+				border-radius: 30rpx;
+				margin-right: 20rpx;
+				font-size: 24rpx;
+			}
+		}
+		.store-server{
+			padding: 20rpx 0;
+			width: 100%;
+			font-size: 24rpx;
+			border-bottom: 1px solid #eee;
+			text{
+				margin-right: 30rpx;
+			}
+		}
+		.store-rich{
+			padding: 20rpx 0;
+			width: 100%;
+			display: flex;
+			flex-direction: column;
+			align-items: center;
+			justify-content: center;
+		}
+		.store-line{
+			border-bottom: 1px solid #eee;
+		}
+		.footer{
+			width: 100%;
+			display: flex;
+			margin-bottom: 20rpx;
+			justify-content: space-between;
+			button{
+				width: 45%;
+			}
+		}
+		.pop-img{
+			width: 100%;
+			height: 100%;
+		}
+	}
+</style>

+ 216 - 0
pages/store/storeList.vue

@@ -0,0 +1,216 @@
+<template>
+	<view class="content">
+		<scroll-view scroll-y="true" class="scroll-Y">
+			<view class="list" @click="intoDetail(item)" v-for="item in storeList" :key="item.id">
+				<view class="list-imgs">
+					<u-image width="160rpx" height="160rpx" :border-radius="10" :src="item.icon?item.icon:'/static/img/mddef.jpg'"></u-image>
+				</view>
+				<view class="store-info">
+					<view class="name">
+						{{item.name}}
+					</view>
+					<view class="address">
+						{{item.addrDetail}}
+					</view>
+					<view class="location">
+						<view class="left">
+							<uni-icons class="icon" size="16" type="paperplane"></uni-icons>
+							<text>{{item.distance}} km</text>
+						</view>
+						<view class="right">
+							<u-icon class="icon" size="32" name="clock"></u-icon>
+							<text>{{item.beginTime}}-{{item.endTime}} 营业</text>
+						</view>
+					</view>
+				</view>
+				<view class="list-right">
+					<u-image width="110rpx" height="110rpx" mode="aspectFit" :src="getStoreStatus(item)"></u-image>
+					<u-icon size="30" color="#999" name="arrow-right"></u-icon>
+				</view>
+			</view>
+			<u-empty class="noData" :text="noDataText" img-width="120" v-if="storeList.length==0 && status!='loading'" mode="list"></u-empty>
+			<view style="padding: 20upx;">
+				<u-loadmore v-if="total>pageSize || status=='loading'" :status="status" />
+			</view>
+		</scroll-view>
+	</view>
+</template>
+
+<script>
+	import uniIcons from "@/components/uni-icons/uni-icons.vue"
+	import {getStoreList} from '@/api/store.js'
+	export default {
+		components: {
+			uniIcons
+		},
+		data() {
+			return {
+				noDataText: '暂无数据',
+				status: 'loading',
+				storeList: [],
+				// 用户拒绝授权位置信息时默认展示青海西宁位置地图
+				currentPosition: {
+					lat: '36.636193',
+					lng: '101.760521'
+				},
+				pageNo: 1,
+				pageSize: 10,
+				total: 0
+			}
+		},
+		onShow() {
+
+		},
+		onLoad() {
+			// 获取当前经纬度
+			let _this = this
+			uni.getLocation({
+				type: 'wgs84', // 默认wgs84
+				success: function(res) {
+					_this.currentPosition.lat = res.latitude;
+					_this.currentPosition.lng = res.longitude;
+					// 查询门店信息
+					_this.getStoresList()
+				},
+				fail: function(res) {
+					console.log(res)
+					// 查询门店信息
+					_this.getStoresList()
+				},
+			});
+		},
+
+		computed: {},
+		methods: {
+			getStoresList() {
+				this.status = "loading"
+				let lat = this.currentPosition.lat
+				let lng = this.currentPosition.lng
+				getStoreList({
+					lat: lat,
+					lng: lng
+				}).then(res => {
+					console.log(res)
+					uni.hideLoading()
+					this.status = "loadmore"
+					if (res.status == 200) {
+						let list = res.data
+						list.map(item => {
+							if (item.addrDetail.indexOf("省") > 0 && item.addrDetail.indexOf("市") > 0 && item.addrDetail.indexOf("区") >
+								0) {
+								item.addrDetail = item.addrDetail
+							} else {
+								if (item.addrDetail.indexOf("市") > 0 && item.addrDetail.indexOf("区") > 0) {
+									item.addrDetail = item.addrProvinceName + item.addrDetail
+								} else if (item.addrDetail.indexOf("区") > 0) {
+									item.addrDetail = item.addrProvinceName + item.addrCityName + item.addrDetail
+								} else {
+									item.addrDetail = item.addrProvinceName + item.addrCityName + item.addrDistrictName + item.addrDetail
+								}
+							}
+							item.distance = item.distanct && item.distanct.distance ? Math.round(item.distanct.distance / 1000) : ''
+						})
+						this.storeList = this.storeList.concat(list)
+					} else {
+						this.storeList = []
+						this.noDataText = res.message
+					}
+				})
+			},
+			// 获取网点营业状态 营业状态 OPEN :营业  CLOSED:暂停营业  TO_BE_OPENED:即将开业
+			getStoreStatus(item) {
+				let today = new Date().toLocaleDateString() // 获取今天日期
+				let beginTime = today + ' ' + item.beginTime
+				let endTime = today + ' ' + item.endTime
+				let beginTimeStamp = new Date(beginTime).getTime()  // 获取开始时间戳
+				let endTimeStamp = new Date(endTime).getTime()  // 获取结束时间戳
+				let nowTimeStamp = new Date().getTime() // 获取当前时间戳
+				// 结束时间小于开始时间时 为跨天 结束时间应加1天
+				endTimeStamp = endTimeStamp <=beginTimeStamp ? (endTimeStamp+24*60*60*1000) : endTimeStamp
+				switch (item.businessStatus) {
+					case 'CLOSED':
+						return '/static/img/ztyy.jpg'
+						break;
+					case 'OPEN':
+						// 根据当前时间判断目前是否在营业时间范围内
+						if (nowTimeStamp>=beginTimeStamp && nowTimeStamp<=endTimeStamp) {
+							return '/static/img/yyz.jpg'
+						} else {
+							return '/static/img/ztyy.jpg'
+						}
+						break;
+					case 'TO_BE_OPENED':
+						return '/static/img/dky.jpg'
+						break;
+					default:
+						break;
+				}
+			},
+			// 进入网点详情
+			intoDetail (item) {
+				uni.navigateTo({
+					url: `/pages/store/storeDetail?id=${item.id}`
+				})
+			},
+
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	page{
+		height: 100%;
+	}
+	.content {
+		padding: 0 20rpx;
+		width: 100%;
+		height: 100%;
+		.scroll-Y {
+			width: 100%;
+			height: 100%;
+			padding: 20rpx 0;
+			.list {
+				display: flex;
+				border-bottom: 1px solid #eee;
+				.store-info {
+					display: flex;
+					flex: 1;
+					flex-direction: column;
+					justify-content: space-between;
+					padding: 20rpx;
+					.name {
+						font-size: 32rpx;
+						color: #000;
+					}
+					.address{
+						font-size: 26rpx;
+						padding: 10rpx 0;
+					}
+					.location {
+						display: flex;
+						align-items: center;
+						font-size: 24rpx;
+						.left {
+							margin-right: 20rpx;
+						}
+						.icon {
+							color: #999;
+							margin-right: 5rpx;
+						}
+					}
+				}
+				.list-imgs{
+					padding: 20rpx 0;
+				}
+				.list-right {
+					display: flex;
+					justify-content: center;
+					align-items: center;
+				}
+			}
+			.u-empty.data-v-6938e513{
+				height: 90%;
+			}
+		}
+	}
+</style>

+ 103 - 0
pages/userCenter/index.vue

@@ -0,0 +1,103 @@
+<template>
+	<view class="user-content">
+		<!-- 头部 -->
+		<view class="user-head">
+			<view class="user-photo">
+				<open-data type="userAvatarUrl"></open-data>
+			</view>
+			<view class="user-info">
+				<open-data type="userNickName"></open-data>
+			</view>
+		</view>
+		<!-- 列表 -->
+		<view class="user-list">
+			<u-cell-group>
+				<u-cell-item icon="gift" index="0" @click="toPage" title="我的优惠卷"></u-cell-item>
+				<u-cell-item icon="order" index="1" @click="toPage" title="我的订单"></u-cell-item>
+				<u-cell-item icon="file-text" index="2" @click="toPage" title="服务协议"></u-cell-item>
+				<!-- <u-cell-item icon="info-circle" index="3" @click="toPage" title="关于我们"></u-cell-item> -->
+			</u-cell-group>
+		</view>
+		<!-- 退出 -->
+		<view class="user-btn">
+			<u-button type="error" @click="quitSys" shape="circle">退出登录</u-button>
+		</view>
+	</view>
+</template>
+
+<script>
+	import {logout} from '@/api/login.js'
+	export default {
+		data() {
+			return {
+				
+			};
+		},
+		onShow() {
+		},
+		onLoad() {
+		},
+		methods:{
+			// 打开对应的页面
+			toPage(index){
+				let pageUrl=['/pages/coupon/index/index','/pages/order/order','/pages/agreement/agreement?fromPage=usercenter']
+				uni.navigateTo({
+					url: pageUrl[index]
+				})
+			},
+			// 退出登录
+			quitSys(){
+				let _this = this
+				uni.showModal({
+				  title: '提示',
+				  content: '您确定要退出登录吗?',
+				  success(res) {
+				    if (res.confirm) {
+				      logout({}).then(res => {
+				       getApp().globalData.token = ''
+				       _this.$u.vuex('vuex_token','')
+					   // 关闭socket
+					   _this.$store.commit("$closeWebsocket")
+				        uni.reLaunch({
+				        	url: '/pages/index/index'
+				        });
+				      });
+				    }
+				  }
+				});
+			},
+			bindGetUserInfo(res){
+				console.log(res)
+			},
+		}
+	}
+</script>
+
+<style lang="scss">
+	.user-content{
+		width: 100%;
+		.user-head{
+			display: flex;
+			align-items: center;
+			padding: 35rpx;
+			.user-info{
+				flex-grow: 1;
+				padding: 0 20rpx;
+			}
+			.user-photo{
+				width: 100rpx;
+				height: 100rpx;
+				border-radius: 50%;
+				overflow: hidden;
+				margin-right: 30rpx;
+			}
+		}
+		
+		.user-list{
+			padding:0 20rpx;
+		}
+		.user-btn{
+			padding: 85rpx 100rpx;
+		}
+	}
+</style>

+ 649 - 0
pages/work/index/index.vue

@@ -0,0 +1,649 @@
+<template>
+	<view class="work-pages">
+		<u-no-network></u-no-network>
+		<!-- 工作控制按钮及进度 -->
+		<view class="work-cons flex-center" style="flex-direction: column;">
+			<!-- 操作按钮组件 -->
+			<view class="work-button-wrap">
+				<!-- 按钮 -->
+				<view class="work-button flex-center">
+					<!-- 扇形进度条 -->
+					<view class="circle_process">
+						<view class="wrapper right"><view class="circle rightcircle" :style="{ transform: `rotate(${rdeg}deg)` }"></view></view>
+						<view class="wrapper left"><view class="circle leftcircle" :style="{ transform: `rotate(${ldeg}deg)` }"></view></view>
+					</view>
+					<view class="cirle-one"></view>
+					<!-- 指针 -->
+					<view class="cirle-two flex-center" v-for="item in 60" :key="item">
+						<view class="sector" :style="{ transform: `rotate(${item * 6}deg)` }"></view>
+					</view>
+					<view class="cirle-three flex-center"></view>
+					<view class="cirle-free" :class="status == 'working' ? 'w-stop' : 'w-start'"></view>
+					<view class="cirle-free-1" :class="status == 'working' ? 'w-stop' : 'w-start'"></view>
+					<view class="cirle-free-2" :class="status == 'working' ? 'w-stop' : 'w-start'"></view>
+					<view class="cirle-sex flex-center" @tap="startWork">
+					 {{ status == 'working' ? '紧急停止' : '点击启动' }}
+					</view>
+				</view>
+			</view>
+			<!-- 状态提示 -->
+			<view class="work-button-tips">
+				<u-notice-bar type="error" border-radius="50" color="#fff" bg-color="#666" mode="vertical" :list="[curWorkStutesText,curWorkStutesText]"></u-notice-bar>
+			</view>
+		</view>
+		<!-- 说明提示 -->
+		<view class="work-tips">
+			<view>
+				<view class="work-dtime" v-show="status=='start'||status=='starting'">
+					距离订单强制完成还剩
+					<u-count-down
+						v-if="timestamp>0"
+						@end="endDtime"
+						separator="zh"
+						font-size="36"
+						separator-size="28"
+						color="#ff0000"
+						:timestamp="timestamp"
+						:show-days="false"
+						:show-hours="false"
+					></u-count-down>
+				</view>
+				<view class="work-remarks">
+					<view class="title">说明:</view>
+					<view v-if="status=='start'||status=='starting'">
+						支付完成后
+						<text class="red">请尽快启动洗车服务</text>
+						,否则订单将在5分钟后自动完成。
+					</view>
+					<view v-else>
+						<view>设备启动中请注意安全,如果设备工作中出现故障,请按紧急停止按钮或联系客服。</view>
+						<view @click="callPhone">如有疑问,请联系客服:<text>{{kfMobile}}</text> </view>
+					</view>
+				</view>
+			</view>
+		</view>
+
+		<!-- 正在启动,归位弹框提示 -->
+		<u-popup v-model="showStart" :mask-close-able="false" border-radius="10" mode="center" width="500rpx" height="400rpx">
+			<view style="padding: 30rpx;display: flex;justify-content: center;text-align: center;">
+				<!-- 启动中 -->
+				<view v-if="status == 'starting'">
+					<u-image width="216px" height="120px" src="/static/img/start.png"></u-image>
+					<view style="padding: 15rpx;">洗车机正在启动...</view>
+				</view>
+				<!-- 停止中 -->
+				<view v-if="status == 'stoping'">
+					<u-image width="216px" height="120px" src="/static/img/start.png"></u-image>
+					<view style="padding: 15rpx;">洗车机正在停止中...</view>
+				</view>
+				<!-- 归位中 -->
+				<view v-if="status == 'reseting'">
+					<u-image width="216px" height="120px" src="/static/img/reset.png"></u-image>
+					<view style="padding: 15rpx;">洗车机正在归位中...</view>
+				</view>
+			</view>
+		</u-popup>
+		<!-- 洗车完成 -->
+		<u-modal v-model="showTips" @confirm="confirmFun" :content="content"></u-modal>
+		<!-- 急停提示弹框 -->
+		<u-popup v-model="showStop" :mask-close-able="false" border-radius="10" mode="center" width="80%">
+			<view style="padding: 30rpx;text-align: center;">
+				<view>
+					<view style="font-weight: bold;">设备提示</view>
+					<view style="display: flex;justify-content: center;padding: 10rpx 0;">
+						<u-image width="165px" height="138px" src="/static/img/sbts.png"></u-image>
+					</view>
+					<u-divider>可能原因</u-divider>
+					<view style="padding: 15rpx;text-align: left;">
+						<view>1、洗车机紧急停止,本次服务结束</view>
+						<view>2、因设备故障无法继续为您服务,所支付金额将退至您的支付账户</view>
+						<view>3、如需再次服务,请退出车辆重新进入,再扫码支付洗车</view>
+						<view>4、如需帮助请联系客服</view>
+					</view>
+					<view style="display: flex;padding: 20rpx 0;justify-content: center;">
+						<u-button @click="callPhone" style="width: 45%;border-radius: 0;margin-right: 5%;">联系客服</u-button>
+						<u-button :disabled="disabledResetBtn" @click="sendCmdRabt('reset')" style="width: 45%;" type="error">洗车机归位</u-button>
+					</view>
+				</view>
+			</view>
+		</u-popup>
+	</view>
+</template>
+
+<script>
+export default {
+	data() {
+		return {
+			timestamp: 0, // 初始情况下5分钟后自动结束
+			rdeg: -135, // 进度条初始角度
+			ldeg: -135, /// 进度条初始角度
+			step: 0, // 进度条增值,共60步
+			status: 'linking', // 状态,linking 连接中,start 开始启动, starting 启动中,working 正在工作,stoping 停止中, reset 归位,reseting 归位中, end 洗车完成,error-end 异常结束
+			curWorkStutesText: '设备准备就绪', // 当前机器运行的进度提示
+			showTips: false, // 错误提示
+			content: '', // 消息内容
+			confirmFun: function(){}, // 消息确认弹框
+			disabledResetBtn: false, 
+			showStart: false ,// 启动弹框提示
+			showStop: false, // 急停提示
+			washCarType: '', // 洗车服务类型,不同的类型对应的时间不一样
+			washDuration: 7.5, // 机器运行步骤,共8步,等分60
+			orderNo: '',// 订单编号
+			kfMobile: ''
+		};
+	},
+	onShow() {
+		console.log(this.$store.state.vuex_workStatus,"onShow")
+		this.kfMobile = this.$store.state.vuex_kfMobile
+	},
+	onLoad(options) {
+		console.log("onLoad")
+		// 创建websocket
+		this.$store.commit('$webSocket')
+		uni.showLoading({
+			title: '正在连接...',
+			mask: true
+		})
+		
+		// 监听网络状态
+		uni.onNetworkStatusChange(function (res) {
+		    console.log(res.isConnected);
+		    console.log(res.networkType);
+			if(res.isConnected){
+				uni.showLoading({
+					title: '正在连接...',
+					mask: true
+				})
+			}
+		});
+		
+		// 第一次进入洗车页面
+		if(this.$store.state.vuex_orderDjs == 0){
+			// 记录当前的时间
+			this.$u.vuex('vuex_orderDjs',new Date().getTime())
+		}
+		 
+		// 获取用户及订单信息
+		this.washCarType = this.$store.state.vuex_itemCode
+		this.orderNo = this.$store.state.vuex_orderNo
+		 
+		// 监听消息
+		uni.$on('wsMessage', this.watchMessage)
+		
+		// 更新进度条
+		if(this.$store.state.vuex_workStatus == 'working'){
+			this.step = this.$store.state.vuex_workStep
+			this.loading()
+		}
+		// 如果是复位状态
+		if(this.$store.state.vuex_workStatus == 'rest'){
+			this.showReset()
+		}
+		if(this.$store.state.vuex_workStatus == 'starting'){
+			this.status = 'start'
+			this.$store.state.vuex_workStatus = 'start'
+			// 更新当前倒计时
+			this.updateDjs()
+		}
+	},
+	onUnload() {
+		// 关闭socket
+		this.$store.commit("$closeWebsocket")
+		uni.$off('wsMessage', this.watchMessage)
+	},
+	methods: {
+		// 提示弹框
+		showModal(content, fun){
+			this.showTips = true
+			this.content = content
+			this.confirmFun = fun || function(){
+				this.showTips=false
+			}
+		},
+		// 电话客服
+		callPhone () {
+			uni.makePhoneCall({
+			    phoneNumber: this.$store.state.vuex_kfMobile
+			})
+		},
+		// 更新当前倒计时
+		updateDjs(){
+			let od = this.$store.state.vuex_orderDjs
+			console.log(od,'od')
+			if(od!=0){
+				let d = new Date().getTime() - od
+				this.timestamp = 300 - d/1000
+				if(this.timestamp<=0){
+					this.endDtime()
+				}
+			}
+		},
+		// 倒计时结束时,自动完成洗车订单
+		endDtime() {
+			console.log('endDtime',this.status)
+			if(this.status == 'start'||this.status=='starting'){
+				this.washCarSuccess()
+			}
+		},
+		// 监听socket消息
+		watchMessage(e){
+			if(typeof e == 'string'){
+				// 连接成功后
+				if(e=='connected'){
+					console.log('connected---',this.$store.state.vuex_workStatus)
+					uni.hideLoading()
+					if(this.$store.state.vuex_workStatus == 'linking'){
+						this.status = 'start'
+						this.$u.vuex('vuex_workStatus','start')
+					}
+				}
+				// 重连连接打开时发送获取当前状态的命令
+				if(e=='connectOpen'){
+					console.log('connectOpen---')
+					this.sendCmdRabt("order_run_status")
+				}
+			}
+			if(typeof e == 'object' && e.type == 'xcz'){
+				console.log(e.data.level,e.data.msgCodeInfo,e.data.msgCodeSrc)
+				// 自定义消息,不是机器返回的
+				if(e.data.level == 'O'){
+					// 启动超时
+					if(e.data.msgCode == "PAUSE"){
+						this.startWashCarFail(e.data.msgCodeInfo)
+					}
+					// 停车位置不正确,
+					if(e.data.msgCode=="TCWZ_CW"){
+						this.startWashCarFail(e.data.msgCodeInfo)
+					}
+					// 重新进入页面且还未启动洗车时
+					if(e.data.msgCode == "PAID"){
+						// 更新当前倒计时
+						this.updateDjs()
+					}
+					// 订单异常
+					if(e.data.msgCode == "0"){
+						// 更新当前倒计时
+						this.devError(e.data.msgCodeInfo)
+					}
+					// 洗车完成
+					if(e.data.msgCode=="FINISHED"){
+						this.washCarSuccess()
+					}
+				}
+				// 进度消息
+				if(e.data.level == 'C'&&this.status!="rest"){
+					this.curWorkStutesText= e.data.msgCodeInfo
+					if(e.data.msgCodeInfo != '开始洗车'){
+						// 启动成功
+						this.startWashCarSuccess()
+					}
+					// 更新进度条
+					this.step = e.data.msgCodeSrc?Number(e.data.msgCodeSrc.split('-')[1]):0
+					this.$store.state.vuex_workStep = this.step
+					this.loading()
+					 // 洗车完成
+					 if(e.data.msgCode=="XCWC" && this.status != 'end'){
+						 this.washCarSuccess()
+					 }
+				}
+				// 错误消息
+				if(e.data.level == 'F'&&this.status!="rest"){
+					this.curWorkStutesText= e.data.msgCodeInfo
+					// 启动成功
+					this.startWashCarSuccess()
+					// 显示复位提示弹框
+					this.showReset()
+				}
+				// 急停
+				if(e.data.level == 'A'){
+					// 设备急停
+					if(e.data.msgCode=="JJTZ"){
+						this.devError(e.data.msgCodeInfo)
+					}
+					// 小程序紧急急停
+					else if(e.data.msgCode=="XCX_JJTZ"){
+						this.curWorkStutesText= e.data.msgCodeInfo
+						// 显示复位提示弹框
+						this.showReset()
+					}
+					else{
+						if(this.status == 'working'){
+							this.devError()
+						}else{
+							this.showModal(e.data.msgCodeInfo)
+						}
+					}
+				}
+				// 复位状态
+				if(e.data.level == 'D'){
+					// 开始复位中
+					if(e.data.msgCode=="FW_START"){
+						this.reseting()
+					}
+					// 复位成功
+					else if(e.data.msgCode=="FW_SUCC"){
+						this.resetRabtSuccess()
+					}
+					// 设备离线,设备异常,暂停营业或复位失败,设备使用中
+					else if(e.data.msgCode=="PAUSE" || e.data.msgCode == "FW_FAIL" || e.data.msgCode == 'ERROR' || e.data.msgCode == 'SB_SYZ'){
+						this.devError(e.data.msgCodeInfo)
+						this.disabledResetBtn = false
+					}
+				}
+			}
+		},
+		// 重置到进度条初始状态
+		resetLoadingStatus(){
+			this.rdeg= -135;
+			this.ldeg= -135;
+		},
+		// 洗车机进度条动画
+		loading() {
+			if(this.step==16){this.step = 8}
+			let second = this.step*this.washDuration
+			if (second <= 30) {
+				this.rdeg = -135 + 6 * second;
+				this.ldeg = -135;
+			} else {
+				this.rdeg = 45;
+				this.ldeg = -135 + 6 * (second - 30);
+			}
+		},
+		// 操作按钮
+		startWork() {
+			let _this = this
+			console.log(this.status)
+			// 启动
+			if (this.status == 'start') {
+				this.sendCmdRabt("start")
+				this.starting()
+			}
+			// 急停
+			if (this.status == 'working') {
+				uni.showModal({
+				    title: '提示',
+				    content: '确定紧急停止洗车机?',
+				    success: function (res) {
+				        if (res.confirm) {
+							_this.sendCmdRabt("stop")
+							_this.stoping()
+				        }
+				    }
+				});
+			}
+		},
+		// 启动中
+		starting(){
+			this.showStart = true;
+			this.status = 'starting';
+			this.$u.vuex('vuex_workStatus','starting')
+			this.curWorkStutesText='设备启动中';
+		},
+		// 启动成功
+		startWashCarSuccess(){
+			this.showStart = false;
+			this.status = 'working';
+			this.$u.vuex('vuex_workStatus','working')
+		},
+		// 启动超时或失败
+		startWashCarFail(msg){
+			this.showStart = false;
+			this.status = 'start';
+			this.$u.vuex('vuex_workStatus','start')
+			this.curWorkStutesText=msg
+			this.showModal(msg)
+		},
+		// 发送命令,start 启动,stop 急停,reset 归位
+		sendCmdRabt(cmdCode){
+			// 归位命令
+			let cmd = {
+				orderNo: this.orderNo, // 订单编号
+				operateType: cmdCode, //操作命令
+				washType: this.washCarType,// 洗车模式
+			}
+			this.$store.commit("$sendWebsocket",JSON.stringify(cmd))
+			
+			// 点击复位后
+			if(cmdCode == 'reset'){
+				this.disabledResetBtn = true
+			}
+		},
+		// 停止中
+		stoping(){
+			this.showStart = true;
+			this.status = 'stoping';
+			this.$u.vuex('vuex_workStatus','stoping')
+			this.curWorkStutesText='设备停止中';
+		},
+		// 停止成功后或有F类错误时,显示复位弹框
+		showReset(){
+			this.showStart = false;
+			this.showStop = true;
+			this.status = 'reset';
+			this.$u.vuex('vuex_workStatus','reset')
+			this.resetLoadingStatus();
+		},
+		// 归位中
+		reseting(){
+			this.status = 'reseting';
+			this.$u.vuex('vuex_workStatus','reseting')
+			this.showStart = true;
+			this.showStop = false;
+			this.disabledResetBtn = false
+		},
+		// 归位结束后
+		resetRabtSuccess(){
+			this.curWorkStutesText='设备已归位';
+			this.status = 'end';
+			this.$u.vuex('vuex_workStatus','end')
+			// 关闭socket
+			this.$store.commit("$closeWebsocket");
+			// 跳转到复位成功页面
+			uni.redirectTo({
+				url:"/pages/work/success/success?type=1"
+			})
+		},
+		// 洗车完成
+		washCarSuccess(){
+			this.status = 'end';
+			this.$u.vuex('vuex_workStatus','end')
+			this.curWorkStutesText='洗车已结束';
+			this.resetLoadingStatus();
+			// 显示弹框
+			this.showModal('洗车已结束',function(){
+				// 跳转到成功页面
+				uni.redirectTo({
+					url:"/pages/work/success/success?type=0"
+				})
+			})
+			// 关闭socket
+			this.$store.commit("$closeWebsocket")
+		},
+		// 设备异常
+		devError(msg){
+			this.showStart = false;
+			this.showStop = false;
+			this.status = "error-end";
+			this.$u.vuex('vuex_workStatus','error-end')
+			this.resetLoadingStatus();
+			// 显示弹框
+			this.showModal(msg || '设备异常,请联系客服',function () {
+					uni.navigateBack()
+		    })
+			// 关闭socket
+			this.$store.commit("$closeWebsocket");
+		},
+	}
+};
+</script>
+
+<style lang="scss">
+// 扇形圆环
+.circle_process {
+	position: absolute;
+	width: 390rpx;
+	height: 390rpx;
+}
+.circle_process .wrapper {
+	width: 195rpx;
+	height: 390rpx;
+	position: absolute;
+	top: 0;
+	overflow: hidden;
+}
+.circle_process .right {
+	right: 0;
+}
+.circle_process .left {
+	left: 0;
+}
+.circle_process .circle {
+	width: 390rpx;
+	height: 390rpx;
+	border: 20rpx solid transparent;
+	border-radius: 50%;
+	position: absolute;
+	top: 0;
+	transform: rotate(-135deg);
+}
+.circle_process .rightcircle {
+	border-top: 20rpx solid #4CD964;
+	border-right: 20rpx solid #4CD964;
+	right: 1px;
+}
+.circle_process .leftcircle {
+	border-bottom: 20rpx solid #4CD964;
+	border-left: 20rpx solid #4CD964;
+	left: 1px;
+}
+// 动画
+@keyframes mymove {
+	0% {
+		transform: scale(1); /*开始为原始大小*/
+		opacity: 1;
+	}
+	50% {
+		transform: scale(1.2); /*放大1.1倍*/
+		opacity: 0;
+	}
+}
+
+.work-pages {
+	width: 100%;
+	.flex-center {
+		display: flex;
+		align-items: center;
+		justify-content: center;
+	}
+	.work-cons {
+		height: 50vh;
+		background-image: linear-gradient(0deg, #666, #333);
+		border-bottom-left-radius: 500rpx 50rpx;
+		border-bottom-right-radius: 500rpx 50rpx;
+		position: relative;
+		// 按钮
+		.work-button-wrap {
+			width: 400rpx;
+			margin-top: -50rpx;
+			.work-button {
+				background: #666;
+				border-radius: 100%;
+				width: 390rpx;
+				height: 390rpx;
+				box-shadow:inset 0rpx 2rpx 10rpx #000;
+				.cirle-one {
+					background-image: linear-gradient(180deg, #666, #333 30%);
+					width: 370rpx;
+					height: 370rpx;
+					border-radius: 100%;
+					position: absolute;
+					box-shadow: 0rpx 2rpx 10rpx #333;
+				}
+				.cirle-two {
+					width: 370rpx;
+					height: 370rpx;
+					border-radius: 100%;
+					position: absolute;
+					.sector {
+						transform-origin: center center;
+						width: 350rpx;
+						height: 5rpx;
+						background: #777;
+					}
+				}
+				.cirle-three {
+					background: #444;
+					width: 280rpx;
+					height: 280rpx;
+					border-radius: 100%;
+					position: absolute;
+					box-shadow: inset 0rpx 3rpx 10rpx #000;
+				}
+				.cirle-free, .cirle-free-1, .cirle-free-2 {
+					width: 230rpx;
+					height: 230rpx;
+					border-radius: 100%;
+					position: absolute;
+					animation: mymove 5s infinite;
+				}
+				.cirle-free-1{
+					width: 210rpx;
+					height: 210rpx;
+					animation: mymove 4.5s infinite;
+				}
+				.cirle-free-2{
+					width: 190rpx;
+					height: 190rpx;
+					animation: mymove 4s infinite;
+				}
+				.cirle-sex {
+					background-image: linear-gradient(180deg, #888, #333 50%);
+					box-shadow: 0rpx 5rpx 10rpx #333;
+					width: 210rpx;
+					height: 210rpx;
+					border-radius: 100%;
+					position: absolute;
+					color: #ffffff;
+					font-size: 28rpx;
+					color: #eee;
+					&:active {
+						background-image: linear-gradient(-180deg, #666, #333 80%);
+					}
+				}
+				.w-start {
+					background: #ffc800;
+				}
+				.w-stop {
+					background: #ff0000;
+				}
+			}
+		}
+		.work-button-tips {
+			width: 80%;
+			color: #eee;
+			text-align: center;
+			padding-top: 30rpx;
+			.u-swiper-item{
+				justify-content: center;
+			}
+		}
+	}
+	.work-tips {
+		padding: 45rpx;
+		.work-dtime {
+			text-align: center;
+			font-weight: bold;
+			font-size: 34rpx;
+		}
+		.work-remarks {
+			padding-top: 50rpx;
+			.red {
+				color: red;
+			}
+			.title {
+				font-weight: bold;
+			}
+		}
+	}
+}
+</style>

+ 85 - 0
pages/work/success/success.vue

@@ -0,0 +1,85 @@
+<template>
+	<view class="s-pages">
+		<vew class="heads">
+			<view><u-icon name="checkmark-circle-fill" size="200" color="red"></u-icon></view>
+			<view v-if="type==1">洗车机归位成功</view>
+			<view v-if="type==0">洗车已完成</view>
+			<view class="s-butoon">
+				<u-button @click="toHome" type="error">重新扫码洗车</u-button>
+			</view>
+		</vew>
+		<view class="infos">
+			<view>操作说明:</view>
+			<view>【重新扫码洗车】需要再次支付洗车费用,如果遇到非个人原因导致洗车机中途归位,客服会在7个工作日内核实情况后,将已付费用按原支付渠道退款给付款账户。带来不便,敬请谅解。</view>
+			<view @click="callPhone">如有疑问,请联系客服:<text>{{kfMobile}}</text> </view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				type: 1,
+				kfMobile: ''
+			};
+		},
+		onLoad(opts) {
+			this.type = opts.type
+			this.kfMobile = this.$store.state.vuex_kfMobile
+		},
+		methods:{
+			// 电话客服
+			callPhone () {
+				uni.makePhoneCall({
+				    phoneNumber: this.$store.state.vuex_kfMobile
+				})
+			},
+			// 返回首页
+			toHome () {
+				uni.reLaunch({
+					url: "/pages/index/index"
+				})
+			},
+			// 再次洗车
+			toWork(){
+				uni.redirectTo({
+					url: "/pages/getOrder/getOrder"
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	.s-pages{
+		width: 100%;
+		.heads{
+			text-align: center;
+			display: flex;
+			justify-content: center;
+			flex-direction: column;
+			padding: 50rpx 50rpx 20rpx;
+			.s-butoon{
+				padding: 50rpx 0 0 0;
+				.u-btn{
+					margin-bottom: 35rpx;
+				}
+			}
+		}
+		.infos{
+			padding:0 50rpx;
+			> view{
+				padding: 10rpx 0;
+				text-indent: 24px;
+				line-height: 22px;
+				&:first-child{
+					text-indent: 0;
+				}
+				text{
+					color: red;
+				}
+			}
+		}
+	}
+</style>

BIN
static/img/carmore.jpg


BIN
static/img/cztis.png


BIN
static/img/dky.jpg


BIN
static/img/end.png


BIN
static/img/index-call.png


BIN
static/img/index-discount.png


BIN
static/img/index-list.png


BIN
static/img/location.png


BIN
static/img/logo.png


BIN
static/img/mddef.jpg


BIN
static/img/noPic.jpg


BIN
static/img/person.png


BIN
static/img/reset.png


BIN
static/img/sbts.png


BIN
static/img/select.png


BIN
static/img/start.png


BIN
static/img/step-one.png


BIN
static/img/store-dky.png


BIN
static/img/store-zt.png


BIN
static/img/store.png


BIN
static/img/unselect.png


BIN
static/img/yyz.jpg


BIN
static/img/ztyy.jpg


BIN
static/img/zysx.png


BIN
static/img/zyts.png


+ 27 - 0
store/$u.mixin.js

@@ -0,0 +1,27 @@
+import { mapState } from 'vuex'
+import store from "@/store"
+
+// 尝试将用户在根目录中的store/index.js的vuex的state变量,全部加载到全局变量中
+let $uStoreKey = [];
+try{
+	$uStoreKey = store.state ? Object.keys(store.state) : [];
+}catch(e){
+	
+}
+
+module.exports = {
+	created() {
+		// 将vuex方法挂在到$u中
+		// 使用方法为:如果要修改vuex的state中的user.name变量为"史诗" => this.$u.vuex('user.name', '史诗')
+		// 如果要修改vuex的state的version变量为1.0.1 => this.$u.vuex('version', '1.0.1')
+		this.$u.vuex = (name, value) => {
+			this.$store.commit('$uStore', {
+				name,value
+			})
+		}
+	},
+	computed: {
+		// 将vuex的state中的所有变量,解构到全局混入的mixin中
+		...mapState($uStoreKey)
+	}
+}

+ 190 - 0
store/index.js

@@ -0,0 +1,190 @@
+import Vue from 'vue'
+import Vuex from 'vuex'
+Vue.use(Vuex)
+
+let lifeData = {};
+
+try {
+	// 尝试获取本地是否存在lifeData变量,第一次启动APP时是不存在的
+	lifeData = uni.getStorageSync('lifeData');
+} catch (e) {
+
+}
+
+// 需要永久存储,且下次APP启动需要取出的,在state中的变量名
+let saveStateKeys = [
+	'vuex_user',
+	'vuex_token',
+	'vuex_userData',
+	'vuex_payType',
+	'vuex_payStatus',
+	'vuex_storeLabel',
+	'vuex_storeId',
+	'vuex_bizId',
+	'vuex_itemCode',
+	'vuex_itemTime',
+	'vuex_orderNo',
+	'vuex_orderStatus',
+	'vuex_workStatus',
+	'vuex_orderDjs',
+	'vuex_workStep',
+];
+
+// 保存变量到本地存储中
+const saveLifeData = function(key, value) {
+	// 判断变量名是否在需要存储的数组中
+	if (saveStateKeys.indexOf(key) != -1) {
+		// 获取本地存储的lifeData对象,将变量添加到对象中
+		let tmp = uni.getStorageSync('lifeData');
+		// 第一次打开APP,不存在lifeData变量,故放一个{}空对象
+		tmp = tmp ? tmp : {};
+		tmp[key] = value;
+		// 执行这一步后,所有需要存储的变量,都挂载在本地的lifeData对象中
+		uni.setStorageSync('lifeData', tmp);
+	}
+}
+
+// websocket 连接
+const SocketInit = function($store,state,wsUrl){
+	let token = getApp().globalData.token
+	// 开始连接
+	state.vuex_socket = uni.connectSocket({
+		header: {
+			'X-AUTH-TOKEN': token
+		},
+		url: wsUrl, // ws 请求地址
+		complete: () => {
+			console.log('连接complete回调!')
+		}
+	});
+	// 打开连接
+	state.vuex_socket.onOpen(function(e) {
+		console.log('连接成功!')
+		uni.$emit('wsMessage', 'connectOpen')
+	})
+	// 连接关闭
+	state.vuex_socket.onClose(function(e) {
+		console.log('ws关闭!')
+	})
+	// 连接错误
+	state.vuex_socket.onError(function(e) {
+		console.log('ws错误!', JSON.stringify(e))
+	})
+	// 接受消息
+	state.vuex_socket.onMessage(function(e) {
+		console.log(e,'onmessage')
+		if (e.data == 'pong' || e.data == 'connected') {
+			uni.$emit('wsMessage', e.data)
+		}else{
+			uni.$emit('wsMessage', JSON.parse(e.data))
+		}
+	})
+	// 发送心跳包
+	state.vuex_heartInterId = setInterval(function() {
+		// console.log(state.vuex_socket.readyState)
+		if (state.vuex_socket.readyState != 1) {
+			 // 停止心跳
+			 clearInterval(state.vuex_heartInterId)
+			 SocketInit($store,state,wsUrl)
+			 uni.$emit('wsMessage','reconnect')
+		} else {
+			$store.commit("$sendWebsocket", "ping")
+		}
+	}, 20000)
+}
+
+// store 对象
+const store = new Vuex.Store({
+	// 下面这些值仅为示例,使用过程中请删除
+	state: {
+		// 如果上面从本地获取的lifeData对象下有对应的属性,就赋值给state中对应的变量
+		// 加上vuex_前缀,是防止变量名冲突,也让人一目了然
+		vuex_user: lifeData.vuex_user ? lifeData.vuex_user : {
+			account: '',
+			password: ''
+		},
+		vuex_token: lifeData.vuex_token ? lifeData.vuex_token : '',
+		vuex_userData: lifeData.vuex_userData ? lifeData.vuex_userData : '',
+		vuex_payType: lifeData.vuex_payType ? lifeData.vuex_payType : [], // 支付方式数据字典
+		vuex_payStatus: lifeData.vuex_payStatus ? lifeData.vuex_payStatus : [], // 支付状态数据字典
+		vuex_storeLabel: lifeData.vuex_storeLabel ? lifeData.vuex_storeLabel : [], // 网点标签数据字典
+		vuex_workStatus: lifeData.vuex_workStatus ? lifeData.vuex_workStatus : 'linking',// 设备是否正在运行中
+		// 设备运行页面的计时器
+		vuex_orderDjs: lifeData.vuex_orderDjs ? lifeData.vuex_orderDjs : 0, // 订单完成倒计时
+		vuex_workStep: lifeData.vuex_workStep ? lifeData.vuex_workStep : 0, // 进度条步骤
+		// 订单信息
+		vuex_storeId: lifeData.vuex_storeId ? lifeData.vuex_storeId : '',// 网点id
+		vuex_bizId: lifeData.vuex_bizId ? lifeData.vuex_bizId : '',// 设备编号
+		vuex_itemCode: lifeData.vuex_itemCode ? lifeData.vuex_itemCode : '',// 服务类型
+		vuex_itemTime: lifeData.vuex_itemTime ? lifeData.vuex_itemTime : '',// 服务时长
+		vuex_orderNo: lifeData.vuex_orderNo ? lifeData.vuex_orderNo : '',// 订单编号
+		vuex_orderStatus: lifeData.vuex_orderStatus ? lifeData.vuex_orderStatus : '',// 订单状态
+		// 如果无需保存到本地永久存储,无需lifeData.xx方式
+		vuex_socket: null,
+		vuex_wsMessageData: '',
+		vuex_heartInterId: null,
+		// 当前网络连接状态
+		vuex_netStatus: '',
+		// 优惠券相关信息
+		vuex_couponInfo: {
+			storeId: '', // 网点id
+			customerType: '', // 客户类型
+			couponCode: '' // 优惠券码
+		},
+		vuex_kfMobile: '17792302576'
+	},
+	mutations: {
+		$uStore(state, payload) {
+			// 判断是否多层级调用,state中为对象存在的情况,诸如user.info.score = 1
+			let nameArr = payload.name.split('.');
+			let saveKey = '';
+			let len = nameArr.length;
+			if (nameArr.length >= 2) {
+				let obj = state[nameArr[0]];
+				for (let i = 1; i < len - 1; i++) {
+					obj = obj[nameArr[i]];
+				}
+				obj[nameArr[len - 1]] = payload.value;
+				saveKey = nameArr[0];
+			} else {
+				// 单层级变量,在state就是一个普通变量的情况
+				state[payload.name] = payload.value;
+				saveKey = payload.name;
+			}
+			// 保存变量到本地,见顶部函数定义
+			saveLifeData(saveKey, state[saveKey])
+		},
+		// 创建通知消息websocket
+		$webSocket(state, params) {
+			let _this = this
+			let url = getApp().globalData.baseUrl
+			let wsBaseUrl = url.indexOf("https:") >= 0 ? url.replace("https:", "wss:") : url.replace("http:", "ws:")
+			let wsUrl = wsBaseUrl + 'websocket/weixin/' + state.vuex_bizId
+			console.log(wsUrl,'wsUrl')
+			SocketInit(_this,state,wsUrl)
+		},
+		// 关闭websocket
+		$closeWebsocket(state) {
+			if (state.vuex_socket) {
+				state.vuex_socket.close({
+					complete: function(e) {
+						console.log('关闭websocket完成', e)
+						// 停止心跳
+						clearInterval(state.vuex_heartInterId)
+					}
+				})
+			}
+		},
+		// 发送消息
+		$sendWebsocket(state, msg) {
+			state.vuex_socket.send({
+				data: msg,
+				complete: function(e) {
+					console.log('ws发送完成', msg)
+				}
+			})
+		},
+	}
+})
+
+export default store

+ 77 - 0
uni.scss

@@ -0,0 +1,77 @@
+@import 'uview-ui/theme.scss';
+/**
+ * 这里是uni-app内置的常用样式变量
+ *
+ * uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量
+ * 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App
+ *
+ */
+
+/**
+ * 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能
+ *
+ * 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件
+ */
+
+/* 颜色变量 */
+
+/* 行为相关颜色 */
+$uni-color-primary: #007aff;
+$uni-color-success: #4cd964;
+$uni-color-warning: #f0ad4e;
+$uni-color-error: #dd524d;
+
+/* 文字基本颜色 */
+$uni-text-color:#333;//基本色
+$uni-text-color-inverse:#fff;//反色
+$uni-text-color-grey:#999;//辅助灰色,如加载更多的提示信息
+$uni-text-color-placeholder: #808080;
+$uni-text-color-disable:#c0c0c0;
+
+/* 背景颜色 */
+$uni-bg-color:#ffffff;
+$uni-bg-color-grey:#f8f8f8;
+$uni-bg-color-hover:#f1f1f1;//点击状态颜色
+$uni-bg-color-mask:rgba(0, 0, 0, 0.4);//遮罩颜色
+
+/* 边框颜色 */
+$uni-border-color:#c8c7cc;
+
+/* 尺寸变量 */
+
+/* 文字尺寸 */
+$uni-font-size-sm:24rpx;
+$uni-font-size-base:28rpx;
+$uni-font-size-lg:32rpx;
+
+/* 图片尺寸 */
+$uni-img-size-sm:40rpx;
+$uni-img-size-base:52rpx;
+$uni-img-size-lg:80rpx;
+
+/* Border Radius */
+$uni-border-radius-sm: 4rpx;
+$uni-border-radius-base: 6rpx;
+$uni-border-radius-lg: 12rpx;
+$uni-border-radius-circle: 50%;
+
+/* 水平间距 */
+$uni-spacing-row-sm: 10px;
+$uni-spacing-row-base: 20rpx;
+$uni-spacing-row-lg: 30rpx;
+
+/* 垂直间距 */
+$uni-spacing-col-sm: 8rpx;
+$uni-spacing-col-base: 16rpx;
+$uni-spacing-col-lg: 24rpx;
+
+/* 透明度 */
+$uni-opacity-disabled: 0.3; // 组件禁用态的透明度
+
+/* 文章场景相关 */
+$uni-color-title: #2C405A; // 文章标题颜色
+$uni-font-size-title:40rpx;
+$uni-color-subtitle: #555555; // 二级标题颜色
+$uni-font-size-subtitle:36rpx;
+$uni-color-paragraph: #3F536E; // 文章段落颜色
+$uni-font-size-paragraph:30rpx;

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác