Browse Source

初始代码

lilei 3 years ago
parent
commit
c4101c7457
79 changed files with 9276 additions and 0 deletions
  1. 4 0
      .gitignore
  2. 16 0
      .hbuilderx/launch.json
  3. 169 0
      App.vue
  4. 50 0
      api/data.js
  5. 68 0
      api/login.js
  6. 70 0
      api/request.js
  7. 1450 0
      common/uni.css
  8. 371 0
      components/LicensePlatNum.vue
  9. 443 0
      components/address.vue
  10. 176 0
      components/ar-circle-progress/ar-circle-progress.vue
  11. 41 0
      components/carNo.vue
  12. 115 0
      components/icon-items-list/icon-items-list.vue
  13. 377 0
      components/m-icon/m-icon.css
  14. 41 0
      components/m-icon/m-icon.vue
  15. 128 0
      components/m-input.vue
  16. 80 0
      components/scrollBox.vue
  17. 80 0
      components/tony-scroll/tony-scroll.vue
  18. 101 0
      components/uni-badge/uni-badge.vue
  19. 223 0
      components/uni-collapse-item/uni-collapse-item.vue
  20. 72 0
      components/uni-collapse/uni-collapse.vue
  21. 39 0
      components/uni-icons/uni-icons.vue
  22. 243 0
      components/uni-list-item/uni-list-item.vue
  23. 45 0
      components/uni-list/uni-list.vue
  24. 222 0
      components/uni-nav-bar/uni-nav-bar.vue
  25. 122 0
      components/uni-popup/image-txt-popup.vue
  26. 22 0
      components/uni-popup/message.js
  27. 96 0
      components/uni-popup/popup-con.vue
  28. 25 0
      components/uni-popup/popup.js
  29. 268 0
      components/uni-popup/uni-popup-dialog.vue
  30. 116 0
      components/uni-popup/uni-popup-message.vue
  31. 165 0
      components/uni-popup/uni-popup-share.vue
  32. 295 0
      components/uni-popup/uni-popup.vue
  33. 153 0
      components/uni-progress/uni-progress.vue
  34. 144 0
      components/uni-rate/uni-rate.vue
  35. 26 0
      components/uni-status-bar/uni-status-bar.vue
  36. 192 0
      components/uni-swiper-dot/uni-swiper-dot.vue
  37. 279 0
      components/uni-transition/uni-transition.vue
  38. 20 0
      index.html
  39. 82 0
      libs/axios.js
  40. 30 0
      libs/tools.js
  41. 5 0
      libs/validate.js
  42. 24 0
      main.js
  43. 75 0
      manifest.json
  44. 18 0
      package-lock.json
  45. 15 0
      package.json
  46. 105 0
      pages.json
  47. 54 0
      pages/about.vue
  48. 40 0
      pages/h5Page/index.vue
  49. 184 0
      pages/index/index.vue
  50. 197 0
      pages/login/login.vue
  51. 243 0
      pages/login/phoneLogin.vue
  52. 230 0
      pages/personCenter/personCenter.vue
  53. 176 0
      pages/personData/personData.vue
  54. 223 0
      pages/scan-frame/scan-frame.vue
  55. 22 0
      pages/user/user.vue
  56. 53 0
      pages/vinInput/confirmVin.vue
  57. 42 0
      pages/vinInput/vinInput.vue
  58. 75 0
      pages/xieyi/index.vue
  59. 26 0
      service.js
  60. BIN
      static/def_personal_avatar.png
  61. 3 0
      static/iconfont/iconfont.css
  62. BIN
      static/logo.png
  63. BIN
      static/scan-frame/album.png
  64. BIN
      static/scan-frame/beat.png
  65. BIN
      static/scan-frame/cameraBg.png
  66. BIN
      static/scan-frame/inputs.png
  67. BIN
      static/tab/tab_home_normal.png
  68. BIN
      static/tab/tab_home_pressed.png
  69. BIN
      static/tab/tab_marketing_normal.png
  70. BIN
      static/tab/tab_marketing_pressed.png
  71. BIN
      static/tab/tab_personal_normal.png
  72. BIN
      static/tab/tab_personal_pressed.png
  73. 208 0
      store/index.js
  74. 108 0
      uni.scss
  75. 13 0
      uni_modules/xt-verify-code/changelog.md
  76. 293 0
      uni_modules/xt-verify-code/components/xt-verify-code/xt-verify-code.vue
  77. 82 0
      uni_modules/xt-verify-code/package.json
  78. 20 0
      uni_modules/xt-verify-code/readme.md
  79. 83 0
      utils/index.js

+ 4 - 0
.gitignore

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

+ 16 - 0
.hbuilderx/launch.json

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

+ 169 - 0
App.vue

@@ -0,0 +1,169 @@
+<script>
+	export default {
+		globalData: {
+			// baseUrl: 'http://192.168.16.155:7003/clzwx/', // 本地
+			baseUrl: 'https://wx.test.chelingzhu.com/clzwx/', // 预发布
+			// baseUrl: 'https://wx.chelingzhu.com/clzwx/' // 生产
+		},
+		onLaunch: function() {
+			console.log('App Launch');
+		},
+		onShow: function() {
+			console.log('App Show');
+		},
+		onHide: function() {
+			console.log('App Hide');
+		}
+	}
+</script>
+
+<style lang="scss">
+	@import "uview-ui/index.scss";
+	@import "/static/iconfont/iconfont.css";
+	/*每个页面公共css */
+	page {
+		min-height: 100%;
+		display: flex;
+		background: #fff;
+	}
+
+	/* #ifdef MP-BAIDU */
+	page {
+		width: 100%;
+		height: 100%;
+		display: block;
+	}
+
+	swan-template {
+		width: 100%;
+		min-height: 100%;
+		display: flex;
+	}
+
+	/* 原生组件模式下需要注意组件外部样式 */
+	custom-component {
+		width: 100%;
+		min-height: 100%;
+		display: flex;
+	}
+
+	/* #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;
+	}
+	// 带圆角的区域块
+	.cont-item{
+		background: #ffffff;
+		margin: 20upx 32upx;
+		padding: 0 32rpx;
+		border-radius: 24upx;
+		box-shadow: 1px 1px 3px #EEEEEE;
+		font-size: 28upx;
+	}
+	/* 显示一行省略号 */
+	.ellipsis-one{
+		white-space: nowrap;
+		text-overflow: ellipsis;
+		overflow: hidden;
+		word-break: break-all;
+	}
+	/* 显示两行,省略号 */
+	.ellipsis-two{
+		overflow: hidden;
+		text-overflow: ellipsis;
+		display: -webkit-box;
+		-webkit-line-clamp: 2;
+		line-clamp: 2;
+		-webkit-box-orient: vertical;
+	}
+	/* felx布局 */
+	.flex{
+		display: -webkit-flex; /* Chrome 21+, Safari 6.1+, iOS Safari 7+, Opera 15/16 */  
+		display: -moz-flex; /* Firefox 18+ */  
+		display: -ms-flexbox; /* IE 10 */  
+		display: flex; /* Chrome 29+, Firefox 22+, IE 11+, Opera 12.1/17/18, Android 4.4+ */
+	}
+	/*伸缩流方向*/
+	.flex_column{
+	    -webkit-box-orient: vertical;
+	    -ms-flex-direction: column;
+	    -webkit-flex-direction: column;
+	    flex-direction: column;
+	}
+	/*主轴对齐*/
+	.justify_center{
+	    -webkit-box-pack: center;
+	    -ms-flex-pack: center;
+	    -webkit-justify-content: center;
+	    justify-content: center;
+	}
+	.justify_end{
+	    -webkit-box-pack: end;
+	    -ms-flex-pack: end;
+	    -webkit-justify-content: flex-end;
+	    justify-content: flex-end;
+	}
+	.justify_between{
+	    -webkit-box-pack: justify;
+	    -ms-flex-pack: justify;
+	    -webkit-justify-content: space-between;
+	    justify-content: space-between;
+	}
+	/*侧轴对齐*/
+	.align_start{
+	    -webkit-box-align: start;
+	    -ms-flex-align: start;
+	    -webkit-align-items: flex-start;
+	    align-items: flex-start;
+	}
+	.align_end{
+	    -webkit-box-align: end;
+	    -ms-flex-align: end;
+	    -webkit-align-items: flex-end;
+	    align-items: flex-end;
+	}
+	.align_center{
+	    -webkit-box-align: center;
+	    -ms-flex-align: center;
+	    -webkit-align-items: center;
+	    align-items: center;
+	}
+	.align_baseline{
+	    -webkit-box-align: baseline;
+	    -ms-flex-align: baseline;
+	    -webkit-align-items: baseline;
+	    align-items: baseline;
+	}
+	/*伸缩性*/
+	.flex_auto{
+	    -webkit-box-flex: 1;
+	    -ms-flex: auto;
+	    -webkit-flex: auto;
+	    flex: auto;
+	}
+	.flex_1{
+	    -webkit-box-flex: 1;
+	    -ms-flex: 1;
+	    -webkit-flex: 1;
+	    flex: 1;    
+	}
+</style>

+ 50 - 0
api/data.js

@@ -0,0 +1,50 @@
+import request from './request';
+
+//查询省
+export const  getProvince = () => {
+  return request({
+    url: `area/PROVINCE`,
+    method: 'get'
+  })
+}
+//查询市
+export const  getCityByPro = params => {
+  return request({
+    url: `area/CITY/${params.id}`,
+    method: 'get'
+  })
+}
+//查询区
+export const  getDistrictByCity = params => {
+  return request({
+    url: `area/DISTRICT/${params.id}`,
+    method: 'get'
+  })
+}
+// 根据字典code查询选项
+export const getLookUpDatas = (params) => {
+  return request({
+    url: `lookup/findByLookup/${params.type}`,
+    method: 'get',
+	data:{}
+  }).then(res=>res.data)
+}
+
+
+// 查询用户头像和昵称是否已存在
+export const findWxUserInfo = (params) => {
+  return request({
+    url: `wxuser/findWxUserInfo`,
+    method: 'post',
+	data:params
+  }).then(res=>res.data)
+}
+
+// 更新用户头像和昵称信息
+export const wxUserUpdate = (params) => {
+  return request({
+    url: `wxuser/update`,
+    method: 'post',
+	data:params
+  }).then(res=>res.data)
+}

+ 68 - 0
api/login.js

@@ -0,0 +1,68 @@
+import request from './request';
+
+// 微信授权登录
+export const wxlogin = params => {
+  return request({
+    url: 'login',
+    data: params,
+    method: 'post'
+  }).then(res => res.data);
+};
+
+// 获取图片验证码
+export const getCaptcha = randomCode => {
+  return request({
+    url: `captcha/create/${randomCode}`,
+    method: 'get',
+    responseType: 'text',
+  }).then(res=>res.data);
+}
+
+export const code2Session = params => {
+  return request({
+    url: 'code2Session',
+    data: params,
+    method: 'get'
+  }).then(res => res.data);
+};
+export const login = params => {
+  return request({
+    url: `loginVerify`,
+    data: params,
+    method: 'post',
+  }).then(res => res.data);
+};
+export const logout = params => {
+  return request({
+    url: `logout`,
+    data: params,
+    method: 'get'
+  }).then(res => res.data);
+};
+// 验证手机
+export const sendLoginVerifyCode = params => {
+  return request({
+    url: `sendLoginVerifyCode`,
+    method: 'post',
+	data:params,
+  }).then(res => res.data);
+};
+// 验证是否登录过
+export const checkLogin = () => {
+  let token = uni.getStorageSync('token')
+  console.log(token,'----------获取token')
+  if (token&&token!='') {
+    return request({
+      url: `checkLogin`,
+      method: 'get'
+    }).then(res => res.data);
+  } else {
+    return new Promise(resolve => {
+      resolve({
+        status: '9001',
+        message: '系统未登录'
+      });
+    });
+  }
+};
+

+ 70 - 0
api/request.js

@@ -0,0 +1,70 @@
+import { objToUrl } from '@/utils/index';
+function request(options) {
+  return new Promise((resolve, reject) => {
+    let _obj = {
+      url: '',
+      data: {},
+      method: 'GET',
+      header: {
+        'Content-Type': 'application/json;charset=UTF-8'
+      }
+    };
+	const baseUrl = getApp().globalData.baseUrl
+    Object.assign(_obj, options);
+    _obj.header['X-AUTH-TOKEN'] = uni.getStorageSync('token');
+    _obj.header['X-HEADER-APPID'] = 'WuyoKG1AcDZzLwxfhOAC+E16SeG3+mDqVYmsa/e5HRE=';
+    _obj.url = baseUrl + _obj.url;
+    _obj.method = _obj.method.toUpperCase();
+    _obj.success = function(res) {
+      if (_obj.url.indexOf('checkLogin') > -1) {
+        resolve(res);
+      } else if (res.data.status === '900010') {
+        setTimeout(function() {
+          wx.showToast({
+            title: '登录已过期,请重新登录',
+            icon: 'none',
+            duration: 1000
+          });
+        }, 10);
+		setTimeout(function() {
+		  const currentRoute = getRoutePath();
+		  mpvue.removeStorage({
+		    key: 'token'
+		  });
+		  const url = `/pages/login/login?lanuch=${currentRoute.lanuch}&path=` + encodeURIComponent(currentRoute.url);
+		  mpvue.reLaunch({ url });
+		}, 1000);
+      } else {
+        resolve(res);
+      }
+    };
+    _obj.fail = function(err) {
+      reject(err);
+    };
+    _obj.complete = function() {};
+    uni.request(_obj);
+  });
+}
+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/personCenter/personCenter',
+    'pages/myPackage/myPackage',
+    'pagesMe/personData/personData',
+    'pages/packageDetail/packageDetail',
+	'pages/chooseCar/chooseCar'
+  ].includes(url);
+  url = '/' + url;
+  if (options.length) {
+    url += '?' + options;
+  }
+  return {
+    url,
+    lanuch
+  };
+}
+export default request;

+ 1450 - 0
common/uni.css

@@ -0,0 +1,1450 @@
+@font-face {
+	font-family: uniicons;
+	font-weight: normal;
+	font-style: normal;
+	src: url('https://img-cdn-qiniu.dcloud.net.cn/fonts/uni.ttf') format('truetype');
+}
+
+
+
+
+/*通用 */
+
+.uni-flex {
+	display: flex;
+	flex-direction: row;
+}
+
+.uni-flex-item {
+	flex: 1;
+}
+
+.uni-flex-item2 {
+	flex: 2;
+}
+.uni-flex-item3 {
+	flex: 3;
+}
+.uni-flex-item4 {
+	flex: 4;
+}
+.uni-flex-item5 {
+	flex: 5;
+}
+.uni-flex-item6 {
+	flex: 6;
+}
+.uni-flex-item7 {
+	flex: 7;
+}
+.uni-flex-item8 {
+	flex: 8;
+}
+.uni-flex-item9 {
+	flex: 9;
+}
+.uni-row {
+	flex-direction: row;
+}
+.uni-column {
+	flex-direction: column;
+}
+.uni-link{
+	color:#576B95;
+	font-size:26upx;
+}
+.uni-center{
+	text-align:center;
+}
+.uni-inline-item{
+	display: flex;
+	flex-direction: row;
+	align-items:center;
+}
+.uni-inline-item text{
+	margin-right: 20upx;
+}
+.uni-inline-item text:last-child{
+	margin-right: 0upx;
+	margin-left: 20upx;
+}
+
+/* page */
+.uni-page-head{
+	padding:35upx; 
+	text-align: center;
+}
+.uni-page-head-title {
+	display: inline-block;
+	padding: 0 40upx;
+	font-size: 30upx;
+	height: 88upx;
+	line-height: 88upx;
+	color: #BEBEBE;
+	box-sizing: border-box;
+	border-bottom: 2upx solid #D8D8D8;
+}
+.uni-page-body {
+	width: 100%;
+	flex-grow: 1;
+	overflow-x: hidden;
+}
+.uni-padding-wrap{
+	width:690upx;
+	padding:0 30upx;
+}
+.uni-word {
+	text-align: center;
+	padding:200upx 100upx;
+}
+.uni-title {
+	font-size:30upx;
+	font-weight:500;
+	padding:20upx 0;
+	line-height:1.5;
+}
+.uni-text{
+	font-size:28upx;
+}
+.uni-title text{
+	font-size:24upx; 
+	color:#888;
+}
+
+.uni-text-gray{
+	color: #ccc;
+}
+.uni-text-small {
+	font-size:24upx;
+}
+.uni-common-mb{
+	margin-bottom:30upx;
+}
+.uni-common-pb{
+	padding-bottom:30upx;
+}
+.uni-common-pl{
+	padding-left:30upx;
+}
+.uni-common-mt{
+	margin-top:30upx;
+}
+/* 背景色 */
+.uni-bg-red{
+	background:#F76260; color:#FFF;
+}
+.uni-bg-green{
+	background:#09BB07; color:#FFF;
+}
+.uni-bg-blue{
+	background:#007AFF; color:#FFF;
+}
+/* 标题 */
+.uni-h1 {font-size: 80upx; font-weight:700;}
+.uni-h2 {font-size: 60upx; font-weight:700;}
+.uni-h3 {font-size: 48upx; font-weight:700;}
+.uni-h4 {font-size: 36upx; font-weight:700;}
+.uni-h5 {font-size: 28upx; color: #8f8f94;}
+.uni-h6 {font-size: 24upx; color: #8f8f94;}
+.uni-bold{font-weight:bold;}
+
+/* 文本溢出隐藏 */
+.uni-ellipsis {overflow: hidden; white-space: nowrap; text-overflow: ellipsis;}
+
+/* 竖向百分百按钮 */
+.uni-btn-v{
+	padding:10upx 0;
+}
+.uni-btn-v button{margin:20upx 0;}
+
+/* 表单 */
+.uni-form-item{
+	display:flex;
+	width:100%;
+	padding:10upx 0;
+}
+.uni-form-item .title{
+	padding:10upx 25upx;
+}
+.uni-label {
+	width: 210upx;
+	word-wrap: break-word;
+	word-break: break-all;
+	text-indent:20upx;
+}
+.uni-input {
+	height: 50upx;
+	padding: 15upx 25upx;
+	line-height:50upx;
+	font-size:28upx;
+	background:#FFF;
+	flex: 1;
+}
+radio-group, checkbox-group{
+	width:100%;
+}
+radio-group label, checkbox-group label{
+	padding-right:20upx;
+}
+.uni-form-item .with-fun{
+	display:flex; 
+	flex-wrap:nowrap; 
+	background:#FFFFFF;
+}
+.uni-form-item .with-fun .uni-icon{
+	width:40px; 
+	height:80upx; 
+	line-height:80upx; 
+	flex-shrink:0;
+}
+
+/* loadmore */
+.uni-loadmore{
+	height:80upx;
+	line-height:80upx;
+	text-align:center;
+	padding-bottom:30upx;
+}
+/*数字角标*/
+.uni-badge,
+.uni-badge-default {
+	font-family: 'Helvetica Neue', Helvetica, sans-serif;
+	font-size: 12px;
+	line-height: 1;
+	display: inline-block;
+	padding: 3px 6px;
+	color: #333;
+	border-radius: 100px;
+	background-color: rgba(0, 0, 0, .15);
+}
+.uni-badge.uni-badge-inverted {
+	padding: 0 5px 0 0;
+	color: #929292;
+	background-color: transparent
+}
+.uni-badge-primary {
+	color: #fff;
+	background-color: #007aff
+}
+.uni-badge-blue.uni-badge-inverted,
+.uni-badge-primary.uni-badge-inverted {
+	color: #007aff;
+	background-color: transparent
+}
+.uni-badge-green,
+.uni-badge-success {
+	color: #fff;
+	background-color: #4cd964;
+}
+.uni-badge-green.uni-badge-inverted,
+.uni-badge-success.uni-badge-inverted {
+	color: #4cd964;
+	background-color: transparent
+}
+.uni-badge-warning,
+.uni-badge-yellow {
+	color: #fff;
+	background-color: #f0ad4e
+}
+.uni-badge-warning.uni-badge-inverted,
+.uni-badge-yellow.uni-badge-inverted {
+	color: #f0ad4e;
+	background-color: transparent
+}
+.uni-badge-danger,
+.uni-badge-red {
+	color: #fff;
+	background-color: #dd524d
+}
+.uni-badge-danger.uni-badge-inverted,
+.uni-badge-red.uni-badge-inverted {
+	color: #dd524d;
+	background-color: transparent
+}
+.uni-badge-purple,
+.uni-badge-royal {
+	color: #fff;
+	background-color: #8a6de9
+}
+.uni-badge-purple.uni-badge-inverted,
+.uni-badge-royal.uni-badge-inverted {
+	color: #8a6de9;
+	background-color: transparent
+}
+
+/*折叠面板 */
+.uni-collapse-content {
+	height: 0;
+	width: 100%;
+	overflow: hidden;
+}
+.uni-collapse-content.uni-active {
+	height: auto;
+}
+
+/*卡片视图 */
+.uni-card {
+	background: #fff;
+	border-radius: 8upx;
+	margin:20upx 0;
+	position: relative;
+	box-shadow: 0 2upx 4upx rgba(0, 0, 0, .3);
+}
+.uni-card-content {
+	font-size: 30upx;
+}
+.uni-card-content.image-view{
+    width: 100%;
+    margin: 0;
+}
+.uni-card-content-inner {
+	position: relative;
+	padding: 30upx;
+}
+.uni-card-footer,
+.uni-card-header {
+	position: relative;
+	display: flex;
+	min-height: 50upx;
+	padding: 20upx 30upx;
+	justify-content: space-between;
+	align-items: center;
+}
+.uni-card-header {
+	font-size: 36upx;
+}
+.uni-card-footer {
+	color: #6d6d72;
+}
+.uni-card-footer:before,
+.uni-card-header:after {
+	position: absolute;
+	top: 0;
+	right: 0;
+	left: 0;
+	height: 2upx;
+	content: '';
+	-webkit-transform: scaleY(.5);
+	transform: scaleY(.5);
+	background-color: #c8c7cc;
+}
+.uni-card-header:after {
+	top: auto;
+	bottom: 0;
+}
+.uni-card-media {
+	justify-content: flex-start;
+}
+.uni-card-media-logo {
+	height: 84upx;
+	width: 84upx;
+	margin-right: 20upx;
+}
+.uni-card-media-body {
+	height: 84upx;
+	display: flex;
+	flex-direction: column;
+	justify-content: space-between;
+	align-items: flex-start;
+}
+.uni-card-media-text-top {
+	line-height: 36upx;
+	font-size: 34upx;
+}
+.uni-card-media-text-bottom {
+	line-height: 30upx;
+	font-size: 28upx;
+	color: #8f8f94;
+}
+.uni-card-link {
+	color: #007AFF;
+}
+
+/* 列表 */
+.uni-list {
+	background-color: #FFFFFF;
+	position: relative;
+	width: 100%;
+	display: flex;
+	flex-direction: column;
+}
+.uni-list:after {
+	position: absolute;
+	z-index: 10;
+	right: 0;
+	bottom: 0;
+	left: 0;
+	height: 1upx;
+	content: '';
+	-webkit-transform: scaleY(.5);
+	transform: scaleY(.5);
+	background-color: #c8c7cc;
+}
+.uni-list:before {
+	position: absolute;
+	z-index: 10;
+	right: 0;
+	top: 0;
+	left: 0;
+	height: 1upx;
+	content: '';
+	-webkit-transform: scaleY(.5);
+	transform: scaleY(.5);
+	background-color: #c8c7cc;
+}
+.uni-list-cell {
+	position: relative;
+	display: flex;
+	flex-direction: row;
+	justify-content: space-between;
+	align-items: center;
+}
+.uni-list-cell-hover {
+	background-color: #eee;
+}
+.uni-list-cell-pd {
+	padding: 22upx 30upx;
+}
+.uni-list-cell-left {
+	font-size:28upx;
+	padding: 0 30upx;
+}
+.uni-list-cell-db,
+.uni-list-cell-right {
+	flex: 1;
+}
+.uni-list-cell:after {
+	position: absolute;
+  z-index: 3;
+	right: 0;
+	bottom: 0;
+	left: 30upx;
+	height: 1upx;
+	content: '';
+	-webkit-transform: scaleY(.5);
+	transform: scaleY(.5);
+	background-color: #c8c7cc;
+}
+.uni-list .uni-list-cell:last-child:after {
+	height: 0upx;
+}
+.uni-list-cell-last.uni-list-cell:after {
+	height: 0upx;
+}
+.uni-list-cell-divider {
+	position: relative;
+	display: flex;
+	color: #999;
+	background-color: #f7f7f7;
+	padding:15upx 20upx;
+}
+.uni-list-cell-divider:before {
+	position: absolute;
+	right: 0;
+	top: 0;
+	left: 0upx;
+	height: 1upx;
+	content: '';
+	-webkit-transform: scaleY(.5);
+	transform: scaleY(.5);
+	background-color: #c8c7cc;
+}
+.uni-list-cell-divider:after {
+	position: absolute;
+	right: 0;
+	bottom: 0;
+	left: 0upx;
+	height: 1upx;
+	content: '';
+	-webkit-transform: scaleY(.5);
+	transform: scaleY(.5);
+	background-color: #c8c7cc;
+}
+.uni-list-cell-navigate {
+	font-size:30upx;
+	padding: 22upx 30upx;
+	line-height: 48upx;
+	position: relative;
+	display: flex;
+	box-sizing: border-box;
+	width: 100%;
+	flex: 1;
+	justify-content: space-between;
+	align-items: center;
+}
+.uni-list-cell-navigate {
+	padding-right: 36upx;
+}
+.uni-navigate-badge {
+	padding-right: 50upx;
+}
+.uni-list-cell-navigate.uni-navigate-right:after {
+	font-family: uniicons;
+	content: '\e583';
+	position: absolute;
+	right: 24upx;
+	top: 50%;
+	color: #bbb;
+	-webkit-transform: translateY(-50%);
+	transform: translateY(-50%);
+}
+.uni-list-cell-navigate.uni-navigate-bottom:after {
+	font-family: uniicons;
+	content: '\e581';
+	position: absolute;
+	right: 24upx;
+	top: 50%;
+	color: #bbb;
+	-webkit-transform: translateY(-50%);
+	transform: translateY(-50%);
+}
+.uni-list-cell-navigate.uni-navigate-bottom.uni-active:after {
+	font-family: uniicons;
+	content: '\e580';
+	position: absolute;
+	right: 24upx;
+	top: 50%;
+	color: #bbb;
+	-webkit-transform: translateY(-50%);
+	transform: translateY(-50%);
+}
+.uni-collapse.uni-list-cell {
+	flex-direction: column;
+}
+.uni-list-cell-navigate.uni-active {
+	background: #eee;
+}
+.uni-list.uni-collapse {
+	box-sizing: border-box;
+	height: 0;
+	overflow: hidden;
+}
+.uni-collapse .uni-list-cell {
+	padding-left: 20upx;
+}
+.uni-collapse .uni-list-cell:after {
+	left: 52upx;
+}
+.uni-list.uni-active {
+	height: auto;
+}
+
+/* 三行列表 */
+.uni-triplex-row {
+	display: flex;
+	flex: 1;
+	width: 100%;
+	box-sizing: border-box;
+	flex-direction: row;
+	padding: 22upx 30upx;
+}
+.uni-triplex-right,
+.uni-triplex-left {
+	display: flex;
+	flex-direction: column;
+}
+.uni-triplex-left {
+	width: 84%;
+}
+.uni-triplex-left .uni-title{
+	padding:8upx 0;
+}
+.uni-triplex-left .uni-text, .uni-triplex-left .uni-text-small{color:#999999;}
+.uni-triplex-right {
+	width: 16%;
+	text-align: right;
+}
+
+/* 图文列表 */
+.uni-media-list {
+	padding: 22upx 30upx;
+	box-sizing: border-box;
+	display: flex;
+	width: 100%;
+	flex-direction: row;
+}
+.uni-navigate-right.uni-media-list {
+	padding-right: 74upx;
+}
+.uni-pull-right {
+	flex-direction: row-reverse;
+}
+.uni-pull-right>.uni-media-list-logo {
+	margin-right: 0upx;
+	margin-left: 20upx;
+}
+.uni-media-list-logo {
+	height: 84upx;
+	width: 84upx;
+	margin-right: 20upx;
+}
+.uni-media-list-logo image {
+	height: 100%;
+	width: 100%;
+}
+.uni-media-list-body {
+	height: 84upx;
+	display: flex;
+	flex: 1;
+	flex-direction: column;
+	justify-content: space-between;
+	align-items: flex-start;
+	overflow: hidden;
+}
+.uni-media-list-text-top {
+	width: 100%;
+	line-height: 36upx;
+	font-size: 30upx;
+}
+.uni-media-list-text-bottom {
+	width: 100%;
+	line-height: 30upx;
+	font-size: 26upx;
+	color: #8f8f94;
+}
+
+/* 九宫格 */
+.uni-grid-9 {
+	background: #f2f2f2;
+	width: 750upx;
+	display: flex;
+	flex-direction: row;
+	flex-wrap: wrap;
+	border-top: 2upx solid #eee;
+}
+.uni-grid-9-item {
+	width: 250upx;
+	height: 200upx;
+	display: flex;
+	flex-direction: column;
+	align-items: center;
+	justify-content: center;
+	border-bottom: 2upx solid;
+	border-right: 2upx solid;
+	border-color: #eee;
+	box-sizing: border-box;
+}
+.no-border-right {
+	border-right: none;
+}
+.uni-grid-9-image {
+	width: 100upx;
+	height: 100upx;
+}
+.uni-grid-9-text {
+	width: 250upx;
+	line-height: 4upx;
+	height: 40upx;
+	text-align: center;
+	font-size: 30upx;
+}
+.uni-grid-9-item-hover {
+	background: rgba(0, 0, 0, 0.1);
+}
+
+/* 上传 */
+.uni-uploader {
+	flex: 1;
+	flex-direction: column;
+}
+.uni-uploader-head {
+	display: flex;
+	flex-direction: row;
+	justify-content: space-between;
+}
+.uni-uploader-info {
+	color: #B2B2B2;
+}
+.uni-uploader-body {
+	margin-top: 16upx;
+}
+.uni-uploader__files {
+	display: flex;
+	flex-direction: row;
+	flex-wrap: wrap;
+}
+.uni-uploader__file {
+	margin: 10upx;
+	width: 210upx;
+	height: 210upx;
+}
+.uni-uploader__img {
+	display: block;
+	width: 210upx;
+	height: 210upx;
+}
+.uni-uploader__input-box {
+	position: relative;
+	margin:10upx;
+	width: 208upx;
+	height: 208upx;
+	border: 2upx solid #D9D9D9;
+}
+.uni-uploader__input-box:before,
+.uni-uploader__input-box:after {
+	content: " ";
+	position: absolute;
+	top: 50%;
+	left: 50%;
+	-webkit-transform: translate(-50%, -50%);
+	transform: translate(-50%, -50%);
+	background-color: #D9D9D9;
+}
+.uni-uploader__input-box:before {
+	width: 4upx;
+	height: 79upx;
+}
+.uni-uploader__input-box:after {
+	width: 79upx;
+	height: 4upx;
+}
+.uni-uploader__input-box:active {
+	border-color: #999999;
+}
+.uni-uploader__input-box:active:before,
+.uni-uploader__input-box:active:after {
+	background-color: #999999;
+}
+.uni-uploader__input {
+	position: absolute;
+	z-index: 1;
+	top: 0;
+	left: 0;
+	width: 100%;
+	height: 100%;
+	opacity: 0;
+}
+
+/*问题反馈*/
+.feedback-title {
+	display: flex;
+	flex-direction: row;
+	justify-content: space-between;
+	align-items: center;
+	padding: 20upx;
+	color: #8f8f94;
+	font-size: 28upx;
+}
+.feedback-star-view.feedback-title {
+	justify-content: flex-start;
+	margin: 0;
+}
+.feedback-quick {
+	position: relative;
+	padding-right: 40upx;
+}
+.feedback-quick:after {
+	font-family: uniicons;
+	font-size: 40upx;
+	content: '\e581';
+	position: absolute;
+	right: 0;
+	top: 50%;
+	color: #bbb;
+	-webkit-transform: translateY(-50%);
+	transform: translateY(-50%);
+}
+.feedback-body {
+	background: #fff;
+}
+.feedback-textare {
+	height: 200upx;
+	font-size: 34upx;
+	line-height: 50upx;
+	width: 100%;
+	box-sizing: border-box;
+	padding: 20upx 30upx 0;
+}
+.feedback-input {
+	font-size: 34upx;
+	height: 50upx;
+	min-height: 50upx;
+	padding: 15upx 20upx;
+	line-height: 50upx;
+}
+.feedback-uploader {
+	padding: 22upx 20upx;
+}
+.feedback-star {
+	font-family: uniicons;
+	font-size: 40upx;
+	margin-left: 6upx;
+}
+.feedback-star-view {
+	margin-left: 20upx;
+}
+.feedback-star:after {
+	content: '\e408';
+}
+.feedback-star.active {
+	color: #FFB400;
+}
+.feedback-star.active:after {
+	content: '\e438';
+}
+.feedback-submit {
+	background: #007AFF;
+	color: #FFFFFF;
+	margin: 20upx;
+}
+
+/* input group */
+.uni-input-group {
+	position: relative;
+	padding: 0;
+	border: 0;
+	background-color: #fff;
+}
+
+.uni-input-group:before {
+	position: absolute;
+	top: 0;
+	right: 0;
+	left: 0;
+	height: 2upx;
+	content: '';
+	transform: scaleY(.5);
+	background-color: #c8c7cc;
+}
+
+.uni-input-group:after {
+	position: absolute;
+	right: 0;
+	bottom: 0;
+	left: 0;
+	height: 2upx;
+	content: '';
+	transform: scaleY(.5);
+	background-color: #c8c7cc;
+}
+
+.uni-input-row {
+	position: relative;
+	display: flex;
+	flex-direction: row;
+	font-size:28upx;
+	padding: 22upx 30upx;
+	justify-content: space-between;
+}
+
+.uni-input-group .uni-input-row:after {
+	position: absolute;
+	right: 0;
+	bottom: 0;
+	left: 30upx;
+	height: 2upx;
+	content: '';
+	transform: scaleY(.5);
+	background-color: #c8c7cc;
+}
+
+.uni-input-row label {
+	line-height: 70upx;
+}
+
+/* textarea */
+.uni-textarea{
+	width:100%;
+	background:#FFF;
+}
+.uni-textarea textarea{
+	width:96%;
+	padding:18upx 2%;
+	line-height:1.6;
+	font-size:28upx;
+	height:150upx;
+}
+
+/* tab bar */
+.uni-tab-bar {
+	display: flex;
+	flex: 1;
+	flex-direction: column;
+	overflow: hidden;
+	height: 100%;
+}
+
+.uni-tab-bar .list {
+	width: 750upx;
+	height: 100%;
+}
+
+.uni-swiper-tab {
+	width: 100%;
+	white-space: nowrap;
+	line-height: 100upx;
+	height: 100upx;
+	border-bottom: 1px solid #c8c7cc;
+}
+
+.swiper-tab-list {
+	font-size: 30upx;
+	width: 150upx;
+	display: inline-block;
+	text-align: center;
+	color: #555;
+}
+
+.uni-tab-bar .active {
+	color: #007AFF;
+}
+
+.uni-tab-bar .swiper-box {
+	flex: 1;
+	width: 100%;
+	height: calc(100% - 100upx);
+}
+
+.uni-tab-bar-loading{
+	padding:20upx 0;
+}
+
+/* steps */
+.uni-steps{padding:20upx 30upx; flex-grow: 1; display:flex; flex-wrap:wrap;}
+.uni-steps view{display:flex; flex-wrap:wrap; float:none;}
+.uni-steps .step{width:31.3%; margin:0 1%; flex-wrap:nowrap;}
+.uni-steps .step-circle{width:50upx; height:50upx; border-radius:50upx; background:#F1F1F3; justify-content:center; line-height:50upx; flex-shrink:0; margin-right:15upx; color:#666; font-size:28upx;}
+.uni-steps .step-content{width:100%; height:22upx; border-bottom:1px solid #F1F2F3;}
+.uni-steps .step-title{line-height:50upx; height:50upx; background:#FFFFFF; width:auto; overflow:hidden; padding-right:8upx;}
+.uni-steps .current .step-circle{background:#00B26A; color:#FFFFFF;}
+.uni-steps .current .step-content{border-color:#00B26A;}
+.uni-steps .current .step-title{color:#00B26A;}
+
+/* comment */
+.uni-comment{padding:5rpx 0; display: flex; flex-grow:1; flex-direction: column;}
+.uni-comment-list{flex-wrap:nowrap; padding:10rpx 0; margin:10rpx 0; width:100%; display: flex;}
+.uni-comment-face{width:70upx; height:70upx; border-radius:100%; margin-right:20upx; flex-shrink:0; overflow:hidden;}
+.uni-comment-face image{width:100%; border-radius:100%;}
+.uni-comment-body{width:100%;}
+.uni-comment-top{line-height:1.5em; justify-content:space-between;}
+.uni-comment-top text{color:#0A98D5; font-size:24upx;}
+.uni-comment-date{line-height:38upx; flex-direction:row; justify-content:space-between; display:flex !important; flex-grow:1;}
+.uni-comment-date view{color:#666666; font-size:24upx; line-height:38upx;}
+.uni-comment-content{line-height:1.6em; font-size:28upx; padding:8rpx 0;}
+.uni-comment-replay-btn{background:#FFF; font-size:24upx; line-height:28upx; padding:5rpx 20upx; border-radius:30upx; color:#333 !important; margin:0 10upx;}
+
+/* swiper msg */
+.uni-swiper-msg{width:100%; padding:12rpx 0; flex-wrap:nowrap; display:flex;}
+.uni-swiper-msg-icon{width:50upx; margin-right:20upx;}
+.uni-swiper-msg-icon image{width:100%; flex-shrink:0;}
+.uni-swiper-msg swiper{width:100%; height:50upx;}
+.uni-swiper-msg swiper-item{line-height:50upx;}
+
+/* product */
+.uni-product-list {
+    display: flex;
+    width: 100%;
+    flex-wrap: wrap;
+    flex-direction: row;
+}
+
+.uni-product {
+    padding: 20upx;
+    display: flex;
+    flex-direction: column;
+}
+
+.image-view {
+    height: 330upx;
+    width: 330upx;
+	margin:12upx 0;
+}
+
+.uni-product-image {
+    height: 330upx;
+    width: 330upx;
+}
+
+.uni-product-title {
+    word-break: break-all;
+    display: -webkit-box;
+    overflow: hidden;
+	line-height:1.5;
+    text-overflow: ellipsis;
+    -webkit-box-orient: vertical;
+    -webkit-line-clamp: 2;
+}
+
+.uni-product-price {
+	margin-top:10upx;
+    font-size: 28upx;
+	line-height:1.5;
+    position: relative;
+}
+
+.uni-product-price-original {
+    color: #e80080;
+}
+
+.uni-product-price-favour {
+    color: #888888;
+    text-decoration: line-through;
+    margin-left: 10upx;
+}
+
+.uni-product-tip {
+    position: absolute;
+    right: 10upx;
+    background-color: #ff3333;
+    color: #ffffff;
+    padding: 0 10upx;
+    border-radius: 5upx;
+}
+
+/* timeline */
+.uni-timeline {
+		margin: 35upx 0;
+		display: flex;
+		flex-direction: column;
+		position: relative;
+	}
+
+
+	.uni-timeline-item {
+		display: flex;
+		flex-direction: row;
+		position: relative;
+		padding-bottom: 20upx;
+		box-sizing: border-box;
+		overflow: hidden;
+
+	}
+
+	.uni-timeline-item .uni-timeline-item-keynode {
+		width: 160upx;
+		flex-shrink: 0;
+		box-sizing: border-box;
+		padding-right: 20upx;
+		text-align: right;
+		line-height: 65upx;
+	}
+
+	.uni-timeline-item .uni-timeline-item-divider {
+		flex-shrink: 0;
+		position: relative;
+		width: 30upx;
+		height: 30upx;
+		top: 15upx;
+		border-radius: 50%;
+		background-color: #bbb;
+	}
+
+
+
+	.uni-timeline-item-divider::before,
+	.uni-timeline-item-divider::after {
+		position: absolute;
+		left: 15upx;
+		width: 1upx;
+		height: 100vh;
+		content: '';
+		background: inherit;
+	}
+
+	.uni-timeline-item-divider::before {
+		bottom: 100%;
+	}
+
+	.uni-timeline-item-divider::after {
+		top: 100%;
+	}
+
+
+	.uni-timeline-last-item .uni-timeline-item-divider:after {
+		display: none;
+	}
+
+	.uni-timeline-first-item .uni-timeline-item-divider:before {
+		display: none;
+	}
+
+	.uni-timeline-item .uni-timeline-item-content {
+		padding-left: 20upx;
+	}
+	
+	.uni-timeline-last-item .bottom-border::after{
+		display: none;
+	}
+	
+	.uni-timeline-item-content .datetime{
+		color: #CCCCCC;
+	}
+	
+	/* 自定义节点颜色 */
+	.uni-timeline-last-item .uni-timeline-item-divider{
+		background-color: #1AAD19;
+	}
+  
+  
+/* uni-icon */
+
+.uni-icon {
+	font-family: uniicons;
+	font-size: 24px;
+	font-weight: normal;
+	font-style: normal;
+	line-height: 1;
+	display: inline-block;
+	text-decoration: none;
+	-webkit-font-smoothing: antialiased;
+}
+
+.uni-icon.uni-active {
+	color: #007aff;
+}
+
+.uni-icon-contact:before {
+	content: '\e100';
+}
+
+.uni-icon-person:before {
+	content: '\e101';
+}
+
+.uni-icon-personadd:before {
+	content: '\e102';
+}
+
+.uni-icon-contact-filled:before {
+	content: '\e130';
+}
+
+.uni-icon-person-filled:before {
+	content: '\e131';
+}
+
+.uni-icon-personadd-filled:before {
+	content: '\e132';
+}
+
+.uni-icon-phone:before {
+	content: '\e200';
+}
+
+.uni-icon-email:before {
+	content: '\e201';
+}
+
+.uni-icon-chatbubble:before {
+	content: '\e202';
+}
+
+.uni-icon-chatboxes:before {
+	content: '\e203';
+}
+
+.uni-icon-phone-filled:before {
+	content: '\e230';
+}
+
+.uni-icon-email-filled:before {
+	content: '\e231';
+}
+
+.uni-icon-chatbubble-filled:before {
+	content: '\e232';
+}
+
+.uni-icon-chatboxes-filled:before {
+	content: '\e233';
+}
+
+.uni-icon-weibo:before {
+	content: '\e260';
+}
+
+.uni-icon-weixin:before {
+	content: '\e261';
+}
+
+.uni-icon-pengyouquan:before {
+	content: '\e262';
+}
+
+.uni-icon-chat:before {
+	content: '\e263';
+}
+
+.uni-icon-qq:before {
+	content: '\e264';
+}
+
+.uni-icon-videocam:before {
+	content: '\e300';
+}
+
+.uni-icon-camera:before {
+	content: '\e301';
+}
+
+.uni-icon-mic:before {
+	content: '\e302';
+}
+
+.uni-icon-location:before {
+	content: '\e303';
+}
+
+.uni-icon-mic-filled:before,
+.uni-icon-speech:before {
+	content: '\e332';
+}
+
+.uni-icon-location-filled:before {
+	content: '\e333';
+}
+
+.uni-icon-micoff:before {
+	content: '\e360';
+}
+
+.uni-icon-image:before {
+	content: '\e363';
+}
+
+.uni-icon-map:before {
+	content: '\e364';
+}
+
+.uni-icon-compose:before {
+	content: '\e400';
+}
+
+.uni-icon-trash:before {
+	content: '\e401';
+}
+
+.uni-icon-upload:before {
+	content: '\e402';
+}
+
+.uni-icon-download:before {
+	content: '\e403';
+}
+
+.uni-icon-close:before {
+	content: '\e404';
+}
+
+.uni-icon-redo:before {
+	content: '\e405';
+}
+
+.uni-icon-undo:before {
+	content: '\e406';
+}
+
+.uni-icon-refresh:before {
+	content: '\e407';
+}
+
+.uni-icon-star:before {
+	content: '\e408';
+}
+
+.uni-icon-plus:before {
+	content: '\e409';
+}
+
+.uni-icon-minus:before {
+	content: '\e410';
+}
+
+.uni-icon-circle:before,
+.uni-icon-checkbox:before {
+	content: '\e411';
+}
+
+.uni-icon-close-filled:before,
+.uni-icon-clear:before {
+	content: '\e434';
+}
+
+.uni-icon-refresh-filled:before {
+	content: '\e437';
+}
+
+.uni-icon-star-filled:before {
+	content: '\e438';
+}
+
+.uni-icon-plus-filled:before {
+	content: '\e439';
+}
+
+.uni-icon-minus-filled:before {
+	content: '\e440';
+}
+
+.uni-icon-circle-filled:before {
+	content: '\e441';
+}
+
+.uni-icon-checkbox-filled:before {
+	content: '\e442';
+}
+
+.uni-icon-closeempty:before {
+	content: '\e460';
+}
+
+.uni-icon-refreshempty:before {
+	content: '\e461';
+}
+
+.uni-icon-reload:before {
+	content: '\e462';
+}
+
+.uni-icon-starhalf:before {
+	content: '\e463';
+}
+
+.uni-icon-spinner:before {
+	content: '\e464';
+}
+
+.uni-icon-spinner-cycle:before {
+	content: '\e465';
+}
+
+.uni-icon-search:before {
+	content: '\e466';
+}
+
+.uni-icon-plusempty:before {
+	content: '\e468';
+}
+
+.uni-icon-forward:before {
+	content: '\e470';
+}
+
+.uni-icon-back:before,
+.uni-icon-left-nav:before {
+	content: '\e471';
+}
+
+.uni-icon-checkmarkempty:before {
+	content: '\e472';
+}
+
+.uni-icon-home:before {
+	content: '\e500';
+}
+
+.uni-icon-navigate:before {
+	content: '\e501';
+}
+
+.uni-icon-gear:before {
+	content: '\e502';
+}
+
+.uni-icon-paperplane:before {
+	content: '\e503';
+}
+
+.uni-icon-info:before {
+	content: '\e504';
+}
+
+.uni-icon-help:before {
+	content: '\e505';
+}
+
+.uni-icon-locked:before {
+	content: '\e506';
+}
+
+.uni-icon-more:before {
+	content: '\e507';
+}
+
+.uni-icon-flag:before {
+	content: '\e508';
+}
+
+.uni-icon-home-filled:before {
+	content: '\e530';
+}
+
+.uni-icon-gear-filled:before {
+	content: '\e532';
+}
+
+.uni-icon-info-filled:before {
+	content: '\e534';
+}
+
+.uni-icon-help-filled:before {
+	content: '\e535';
+}
+
+.uni-icon-more-filled:before {
+	content: '\e537';
+}
+
+.uni-icon-settings:before {
+	content: '\e560';
+}
+
+.uni-icon-list:before {
+	content: '\e562';
+}
+
+.uni-icon-bars:before {
+	content: '\e563';
+}
+
+.uni-icon-loop:before {
+	content: '\e565';
+}
+
+.uni-icon-paperclip:before {
+	content: '\e567';
+}
+
+.uni-icon-eye:before {
+	content: '\e568';
+}
+
+.uni-icon-arrowup:before {
+	content: '\e580';
+}
+
+.uni-icon-arrowdown:before {
+	content: '\e581';
+}
+
+.uni-icon-arrowleft:before {
+	content: '\e582';
+}
+
+.uni-icon-arrowright:before {
+	content: '\e583';
+}
+
+.uni-icon-arrowthinup:before {
+	content: '\e584';
+}
+
+.uni-icon-arrowthindown:before {
+	content: '\e585';
+}
+
+.uni-icon-arrowthinleft:before {
+	content: '\e586';
+}
+
+.uni-icon-arrowthinright:before {
+	content: '\e587';
+}
+
+.uni-icon-pulldown:before {
+	content: '\e588';
+}
+
+.uni-icon-scan:before {
+    content: "\e612";
+}

+ 371 - 0
components/LicensePlatNum.vue

@@ -0,0 +1,371 @@
+<template>
+  <div class="license-plate-panel">
+    <div class="input-area">
+      <div v-for="(num, index) in 8" :key="num" @click="selectHandle(index)" :class="{ active: selected === index }" class="input-text">
+        {{ licensePlate[index] }}
+      </div>
+    </div>
+    <div class="keyboard-panel">
+      <div class="tool-bar">
+        <div @click="cancelHandle">取消</div>
+        <div @click="okHandle" class="btn-ok" :class="{ right: isRight }">
+          确定
+        </div>
+      </div>
+      <div class="text-panel">
+        <div class="row" v-if="selected !== 0">
+          <div class="text-key" v-for="text in carNum" :key="text" @click="numHandle(text)" :class="{'disabled': numDisable}">
+            {{ text }}
+          </div>
+        </div>
+        <div class="row" v-for="(keys, row) in carTxt" :key="row" v-if="selected === 0">
+          <div v-for="text in keys" :key="text" @click="keyHandle(text)" class="text-key" :class="{'selected': licensePlate[selected] === text}">
+            {{ text }}
+          </div>
+          <template v-if="row === carTxt.length - 1">
+            <div style="flex: 1"></div>
+            <div class="text-key del-key" @click="deleteKey()">
+              删除
+            </div>
+          </template>
+        </div>
+
+        <div class="row" v-for="(keys2, row2) in carLetter" :key="row2" v-if="selected !== 0">
+          <div v-for="text in keys2" :key="text" @click="keyHandle(text)"
+          :class="{'text-key': true,
+          'disabled': false ,
+          'selected': licensePlate[selected] === text
+          }">
+            {{ text }}
+          </div>
+          <template v-if="row2 === carLetter.length - 1">
+            <div style="flex: 1"></div>
+            <div class="text-key del-key" @click="deleteKey()">
+              删除
+            </div>
+          </template>
+        </div>
+
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'LicensePlatNum',
+  props: {
+    code: {
+      type: [String,Object],
+      default: ''
+    }
+  },
+  watch: {
+	  code: {
+		  handler(newVal,oldVal) {
+			   console.log(newVal,'nnnnnnnn')
+			   console.log('nnnnnnnn1111111')
+			  const codes = newVal.split('');
+			  for (let i = 0; i < 8; i++) {
+			    this.licensePlate[i] = codes[i] || '';
+			  }
+			  let index = codes.length - 1;
+			  if (index < 0) {
+			    index = 0;
+			  }
+			  this.selectHandle(index);
+		  },
+		  immediate: true,
+		  deep: true
+	  }
+  },
+  data() {
+    return {
+      licensePlate: {
+        0: '',
+        1: '',
+        2: '',
+        3: '',
+        4: '',
+        5: '',
+        6: '',
+        7: ''
+      },
+      selected: 0,
+      keyRows: [],
+      keyLast: [],
+      carTxt: [
+		['陕', '京', '沪', '粤', '津', '冀', '豫', '云', '辽', '黑'],
+		['皖', '鲁', '苏', '浙', '赣', '鄂', '桂', '甘', '晋', '湘'],
+		['蒙', '吉', '闽', '贵', '渝', '川', '青', '琼', '宁', '藏'],
+		[ '新', '使', '领']
+      ],
+      numDisable: true,
+      disableList: { 
+        // 1: ['Z']
+      },
+      carNum: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0'],
+      carLetter: [
+        ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K'],
+        ['L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V'],
+        ['W', 'X', 'Y', 'Z'],
+		['挂', '学', '警', '港', '澳']
+      ]
+    };
+  },
+  computed: {
+    isRight() {
+      // const val = Object.values(this.licensePlate).join('');
+      // return /^(([京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领][A-Z](([0-9]{5}[DF])|([DF]([A-HJ-NP-Z0-9])[0-9]{4})))|([京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领][A-Z][A-HJ-NP-Z0-9]{4}[A-HJ-NP-Z0-9挂学警港澳使领]))$/.test(
+      //   val
+      // );
+	  return true
+    }
+  },
+  mounted() {
+    const codes = this.code.split('');
+    for (let i = 0; i < 8; i++) {
+      this.licensePlate[i] = codes[i] || '';
+    }
+    let index = codes.length - 1;
+    if (index < 0) {
+      index = 0;
+    }
+    this.selectHandle(index);
+  },
+  methods: {
+    getStyle(item) {
+      return 'disable';
+      // return {
+      //   'text-key-last': this.selected === 0,
+      //   'disable': !this.isEnable(item),
+      //   'selected': this.licensePlate[this.selected] === item
+      // };
+    },
+    getNumStyle(item) {
+      return {
+        disable: this.numDisable,
+        selected: this.licensePlate[this.selected] === item
+      };
+    },
+    isEnable(item) {
+      const _dis = this.disableList[this.selected];
+		console.log(item,_dis,'-------')
+      if (_dis) {
+        console.log(_dis.indexOf(item) !== -1, item);
+        return _dis.indexOf(item) !== -1;
+      }
+      return false;
+    },
+    selectHandle(item) {
+      this.selected = item;
+	  console.log(item,this.selected, '-------index------')
+      if (item === 0) {
+        // this.keyRows = this.carTxt;
+      } else {
+        // this.keyRows = this.carLetter;
+        this.numDisable = item === 1;
+      }
+    },
+    keyHandle(text) {
+		console.log(text,'ttttttt')
+      if (!this.isEnable(text)) {
+        this.licensePlate[this.selected] = text;
+        const next = this.selected + 1;
+        if (next <= 7) {
+          this.selectHandle(next);
+        }
+      }
+    },
+    numHandle(text) {
+      if (!this.numDisable) {
+        this.licensePlate[this.selected] = text;
+        const next = this.selected + 1;
+        if (next <= 7) {
+          this.selectHandle(next);
+        }
+      }
+    },
+    deleteKey() {
+      if (this.licensePlate[this.selected] !== '') {
+        this.licensePlate[this.selected] = '';
+      } else if (this.selected !== 0) {
+        this.selectHandle(this.selected - 1);
+        this.licensePlate[this.selected] = '';
+      }
+    },
+    cancelHandle() {
+      this.claer();
+      this.$emit('cancel');
+    },
+    claer() {
+      this.licensePlate = {
+        0: '',
+        1: '',
+        2: '',
+        3: '',
+        4: '',
+        5: '',
+        6: '',
+        7: ''
+      };
+      this.selected = 0;
+    },
+    okHandle() {
+      if (this.isRight) {
+        const val = Object.values(this.licensePlate).join('');
+        this.claer();
+        this.$emit('ok', val);
+      }
+    }
+  }
+};
+</script>
+
+<style lang="less">
+.license-plate-panel {
+  position: absolute;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  z-index: 10000;
+  background-color: #fff;
+  border-top: 1px solid #ddd;
+
+  .input-area {
+    display: flex;
+    padding: 5vh 0;
+    box-sizing: border-box;
+    justify-content: center;
+
+    .input-text {
+      width: 10vw;
+      height: 10vw;
+      line-height: 10vw;
+      box-sizing: border-box;
+      border-top: 1px solid #dcdee2;
+      border-right: 1px solid #dcdee2;
+      border-bottom: 1px solid #dcdee2;
+      text-align: center;
+      font-size: 32upx;
+      &.active {
+        border: 1px solid #2db7f5 !important;
+      }
+
+      &:first-child {
+        overflow: hidden;
+        position: relative;
+
+        &::after {
+          content: '';
+          position: absolute;
+          bottom: -5px;
+          right: -5px;
+          width: 10px;
+          height: 10px;
+          background-color: #dcdee2;
+          transform: rotate(45deg);
+        }
+
+        &.active::after {
+          background-color: #2db7f5;
+        }
+      }
+
+      &:last-child {
+        margin-left: 5px;
+        border: 1px dashed #dcdee2;
+
+        &.active {
+          border: 1px dashed #2db7f5 !important;
+        }
+      }
+
+      &:nth-child(2) {
+        margin-right: 25px;
+        position: relative;
+
+        &::after {
+          content: '';
+          position: absolute;
+          border-radius: 50%;
+          top: 50%;
+          right: -15px;
+          transform: translateY(-50%);
+          width: 5px;
+          height: 5px;
+          background-color: #000;
+        }
+      }
+
+      &:first-child, &:nth-child(3) {
+        border-left: 1px solid #dcdee2;
+      }
+    }
+  }
+
+  .keyboard-panel {
+    position: relative;
+    bottom: 0;
+    border-top: 1px solid #eef0f4;
+    background: #eeeeee;
+    box-sizing: border-box;
+    width: 100%;
+
+    .tool-bar {
+      display: flex;
+      font-size: 32upx;
+      justify-content: space-between;
+      background-color: #fff;
+      padding: 10px 15px;
+    }
+
+    .text-panel {
+      padding: 0 1vw 5px 1vw;
+    }
+
+    .row {
+      display: flex;
+      margin: 1vw 0;
+    }
+
+    .text-key {
+      display: inline-block;
+      text-align: center;
+      font-size: 32upx;
+      width: 8.8vw;
+      margin: 0 0.5vw;
+      padding: 10px 0;
+      box-shadow: 0 1px 2px #dddddd;
+      border-radius: 4px;
+      background-color: #ffffff;
+      text-align: center;
+      flex-shrink: 0;
+      flex-grow: 0;
+    }
+
+    .text-key.disabled {
+      background-color: #f1f1f1;
+      color: #bebebe;
+    }
+
+    .text-key.selected {
+      background-color: #2db7f5;
+      color: #fff;
+    }
+
+    .del-key {
+      width: 19vw;
+	  color: red;
+    }
+
+    .btn-ok {
+      color: #c5c8ce;
+
+      &.right {
+        color: #00aaff;
+      }
+    }
+  }
+}
+</style>

+ 443 - 0
components/address.vue

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

+ 176 - 0
components/ar-circle-progress/ar-circle-progress.vue

@@ -0,0 +1,176 @@
+<template>
+	<view
+		class="circle-progress"
+		:style="{
+			width: widthPx + 'px',
+			height: widthPx + 'px',
+			backgroundColor: bgColor
+		}"
+	>
+		<!-- 有的不支持canvas-id属性,必须用id属性 -->
+		<canvas
+			class="canvas-bg"
+			:canvas-id="canvasId"
+			:id="canvasId"
+			:style="{
+				width: widthPx + 'px',
+				height: widthPx + 'px'
+			}"
+		></canvas>
+		<canvas
+			class="canvas"
+			:canvas-id="elId"
+			:id="elId"
+			:style="{
+				width: widthPx + 'px',
+				height: widthPx + 'px'
+			}"
+		></canvas>
+		<slot></slot>
+	</view>
+</template>
+
+<script>
+export default {
+	name: 'circle-progress',
+	props: {
+		// 圆环进度百分比值
+		percent: {
+			type: Number,
+			default: 0,
+			// 值在0到100之间
+			validator: val => {
+				return val >= 0 && val <= 100;
+			}
+		},
+		// 圆环底色(灰色的圆环)
+		inactiveColor: {
+			type: String,
+			default: '#ececec'
+		},
+		// 圆环激活部分的颜色
+		activeColor: {
+			type: String,
+			default: '#009dff'
+		},
+		// 圆环线条的宽度,单位rpx
+		borderWidth: {
+			type: [Number, String],
+			default: 14
+		},
+		// 整个圆形的宽度,单位rpx
+		width: {
+			type: [Number, String],
+			default: 200
+		},
+		// 整个圆环执行一圈的时间,单位ms
+		duration: {
+			type: [Number, String],
+			default: 1500
+		},
+		// 圆环进度区域的背景色
+		bgColor: {
+			type: String,
+			default: '#ffffff'
+		}
+	},
+	data() {
+		return {
+			canvasId: 'canvasId', //一个页面多个圆形进度
+			elId: 'elId',
+			widthPx: uni.upx2px(this.width), // 转成px后的整个组件的背景宽度
+			borderWidthPx: uni.upx2px(this.borderWidth), // 转成px后的圆环的宽度
+			startAngle: -Math.PI / 2, // canvas画圆的起始角度,默认为3点钟方向,定位到12点钟方向
+			progressContext: null, // 活动圆的canvas上下文
+			newPercent: 0, // 当动态修改进度值的时候,保存进度值的变化前后值,用于比较用
+			oldPercent: 0 // 当动态修改进度值的时候,保存进度值的变化前后值,用于比较用
+		};
+	},
+	watch: {
+		percent(nVal, oVal = 0) {
+			if (nVal > 100) nVal = 100;
+			if (nVal < 0) oVal = 0;
+			this.newPercent = nVal;
+			this.oldPercent = oVal;
+			setTimeout(() => {
+				this.drawCircleByProgress(oVal);
+			}, 50);
+		}
+	},
+	created() {
+		// 赋值,用于加载后第一个画圆使用
+		this.newPercent = this.percent;
+		this.oldPercent = 0;
+	},
+
+	mounted() {
+    this.drawProgressBg();
+    this.drawCircleByProgress(this.oldPercent);
+	},
+	methods: {
+    //一个页面多个progress时ID需不同
+    randomId(){
+      return 'progressId'+parseInt(Math.random()*1000000)
+    },
+		drawProgressBg() {
+			let ctx = uni.createCanvasContext(this.canvasId, this);
+			ctx.setLineWidth(this.borderWidthPx); // 设置圆环宽度
+			ctx.setStrokeStyle(this.inactiveColor); // 线条颜色
+			ctx.beginPath(); // 开始描绘路径
+			let radius = this.widthPx / 2;
+			ctx.arc(radius, radius, radius - this.borderWidthPx, 0, 2 * Math.PI, false);
+			ctx.stroke(); // 对路径进行描绘
+			ctx.draw();
+		},
+		drawCircleByProgress(progress) {
+			let ctx = this.progressContext;
+			if (!ctx) {
+				ctx = uni.createCanvasContext(this.elId, this);
+				this.progressContext = ctx;
+			}
+			// 表示进度的两端为圆形
+			ctx.setLineCap('round');
+			// 设置线条的宽度和颜色
+			ctx.setLineWidth(this.borderWidthPx);
+			ctx.setStrokeStyle(this.activeColor);
+			// 计算过渡时间
+			let time = Math.floor(this.duration / 100);
+			let endAngle = ((2 * Math.PI) / 100) * progress + this.startAngle;
+			ctx.beginPath();
+			// 半径为整个canvas宽度的一半
+			let radius = this.widthPx / 2;
+			ctx.arc(radius, radius, radius - this.borderWidthPx, this.startAngle, endAngle, false);
+			ctx.stroke();
+			ctx.draw();
+			// 增大了百分比
+			if (this.newPercent > this.oldPercent) {
+				progress=progress+10;
+				if (progress > this.newPercent) return;
+			} else {
+				// 减少百分比
+				progress=progress-10;
+				if (progress < this.newPercent) return;
+			}
+			setTimeout(() => {
+				// 定时器,为了让进度条有动画效果
+				this.drawCircleByProgress(progress);
+			}, time);
+		}
+	}
+};
+</script>
+
+<style scoped>
+  .circle-progress {
+    position: relative;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+  }
+  .canvas-bg {
+    position: absolute;
+  }
+  .canvas {
+    position: absolute;
+  }
+</style>

+ 41 - 0
components/carNo.vue

@@ -0,0 +1,41 @@
+<template>
+	<view class="carNo flex align_center">
+		<view class="prv">{{prv}}</view>
+		<view class="other">{{other}}</view>
+	</view>
+</template>
+
+<script>
+	export default{
+		props: {
+			val: {
+				type: String,
+				default: ''
+			},
+		},
+		computed: {
+			prv() {
+				return this.val.substr(0,1) 
+			},
+			other(){
+				return this.val.substr(1,this.val.length)
+			}
+		},
+	}
+</script>
+
+<style lang="scss">
+	.carNo{
+		border: 1px solid #424D5E;
+		border-radius: 4rpx 0 0 4rpx;
+		overflow: hidden;
+		font-size: 24rpx;
+		.prv{
+			background-color: #424D5E;
+			color: #FFFFFF;
+		}
+		>view{
+			padding: 2rpx 10rpx;
+		}
+	}
+</style>

+ 115 - 0
components/icon-items-list/icon-items-list.vue

@@ -0,0 +1,115 @@
+<template>
+	<div class="icon-items-list" :class="'icon-items-list-'+type">
+		<div class="icon-items-icons" v-for="(item,index) in list" :key="item.id" :style="{width:itemWidth}" @click="toPage(item,index)">
+			<div class="icon-items-imgse" >
+				<image class="imgs" :class="item.auth?'':'uni-img-gray'" :fade-show='true' :src="item.icon"></image>
+				<u-badge class="tag" :absolute="false" type="error"  v-if="item.nums" :count="item.nums"></u-badge>
+			</div>
+			<div class="icon-items-text">{{item.name.slice(0,14)}}</div>
+		</div>
+		<div class="nodata" v-if="list.length==0">
+			<u-empty :text="noDataText" mode="list"></u-empty>
+		</div>
+	</div>
+</template>
+
+<script>
+	export default {
+		name: "iconItemsList",
+		props:{
+			// 套餐服务项 或配件id
+			list:{
+				type: Array,
+				default: function(){
+					return []
+				}
+			},
+			itemWidth: {
+				type: String,
+				default: '25%'
+			},
+			type: {
+				type: String,
+				default: 'icon'
+			},
+			noDataText: {
+				type: String,
+				default: '暂无数据'
+			}
+		},
+		data() {
+			return {
+				datas: this.list
+			}
+		},
+		methods: {
+			toPage(item,index){
+				if(!item.auth){
+					// uni.showToast({icon: 'none',title: '您还未登录'})
+					uni.navigateTo({
+						url: '/pages/login/login'
+					})
+					return
+				}
+				if(item.url&&item.url!=''){
+					if(item.target == 'page'){
+						uni.navigateTo({
+							url: item.url
+						})
+					}
+					if(item.target == 'tabPage'){
+						uni.switchTab({
+							url: item.url
+						})
+					}
+					if(item.target == 'fun'){
+						this.$emit('callback',item.url)
+					}
+				}
+			}
+		}
+	}
+</script>
+
+<style lang="less">
+	.icon-items-list{
+		align-items: center;
+		display: flex;
+		flex-wrap: wrap;
+		.nodata{
+			text-align: center;
+			font-size: 24upx;
+			width: 100%;
+			color: #999999;
+		}
+	}
+	.icon-items-list-icon{
+		.icon-items-imgse{
+			position: relative;
+			width: 100upx;
+			height: 100upx;
+			margin-bottom: 6upx;
+			.imgs{
+				width: 100upx;
+				height: 100upx;
+			}
+			.tag{
+				position: absolute;
+				top: -5upx;
+				right: -5upx;
+			}
+		}
+		.icon-items-text{
+			font-size: 28upx;
+			color: #666666;
+		}
+	}
+	.icon-items-icons{
+		text-align: center;
+		margin: 20upx 0;
+		display: flex;
+		align-items: center;
+		flex-direction: column;
+		justify-content: center;
+	}
+</style>

+ 377 - 0
components/m-icon/m-icon.css

@@ -0,0 +1,377 @@
+@font-face {
+	font-family: uniicons;
+	font-weight: normal;
+	font-style: normal;
+	src: url('https://img-cdn-qiniu.dcloud.net.cn/fonts/uni.ttf?t=1536565627510') format('truetype');
+}
+
+.m-icon {
+	font-family: uniicons;
+	font-size: 48upx;
+	font-weight: normal;
+	font-style: normal;
+	line-height: 1;
+	display: inline-block;
+	text-decoration: none;
+	-webkit-font-smoothing: antialiased;
+}
+
+.m-icon.uni-active {
+	color: #007aff;
+}
+
+.m-icon-contact:before {
+	content: '\e100';
+}
+
+.m-icon-person:before {
+	content: '\e101';
+}
+
+.m-icon-personadd:before {
+	content: '\e102';
+}
+
+.m-icon-contact-filled:before {
+	content: '\e130';
+}
+
+.m-icon-person-filled:before {
+	content: '\e131';
+}
+
+.m-icon-personadd-filled:before {
+	content: '\e132';
+}
+
+.m-icon-phone:before {
+	content: '\e200';
+}
+
+.m-icon-email:before {
+	content: '\e201';
+}
+
+.m-icon-chatbubble:before {
+	content: '\e202';
+}
+
+.m-icon-chatboxes:before {
+	content: '\e203';
+}
+
+.m-icon-phone-filled:before {
+	content: '\e230';
+}
+
+.m-icon-email-filled:before {
+	content: '\e231';
+}
+
+.m-icon-chatbubble-filled:before {
+	content: '\e232';
+}
+
+.m-icon-chatboxes-filled:before {
+	content: '\e233';
+}
+
+.m-icon-weibo:before {
+	content: '\e260';
+}
+
+.m-icon-weixin:before {
+	content: '\e261';
+}
+
+.m-icon-pengyouquan:before {
+	content: '\e262';
+}
+
+.m-icon-chat:before {
+	content: '\e263';
+}
+
+.m-icon-qq:before {
+	content: '\e264';
+}
+
+.m-icon-videocam:before {
+	content: '\e300';
+}
+
+.m-icon-camera:before {
+	content: '\e301';
+}
+
+.m-icon-mic:before {
+	content: '\e302';
+}
+
+.m-icon-location:before {
+	content: '\e303';
+}
+
+.m-icon-mic-filled:before,
+.m-icon-speech:before {
+	content: '\e332';
+}
+
+.m-icon-location-filled:before {
+	content: '\e333';
+}
+
+.m-icon-micoff:before {
+	content: '\e360';
+}
+
+.m-icon-image:before {
+	content: '\e363';
+}
+
+.m-icon-map:before {
+	content: '\e364';
+}
+
+.m-icon-compose:before {
+	content: '\e400';
+}
+
+.m-icon-trash:before {
+	content: '\e401';
+}
+
+.m-icon-upload:before {
+	content: '\e402';
+}
+
+.m-icon-download:before {
+	content: '\e403';
+}
+
+.m-icon-close:before {
+	content: '\e404';
+}
+
+.m-icon-redo:before {
+	content: '\e405';
+}
+
+.m-icon-undo:before {
+	content: '\e406';
+}
+
+.m-icon-refresh:before {
+	content: '\e407';
+}
+
+.m-icon-star:before {
+	content: '\e408';
+}
+
+.m-icon-plus:before {
+	content: '\e409';
+}
+
+.m-icon-minus:before {
+	content: '\e410';
+}
+
+.m-icon-circle:before,
+.m-icon-checkbox:before {
+	content: '\e411';
+}
+
+.m-icon-close-filled:before,
+.m-icon-clear:before {
+	content: '\e434';
+}
+
+.m-icon-refresh-filled:before {
+	content: '\e437';
+}
+
+.m-icon-star-filled:before {
+	content: '\e438';
+}
+
+.m-icon-plus-filled:before {
+	content: '\e439';
+}
+
+.m-icon-minus-filled:before {
+	content: '\e440';
+}
+
+.m-icon-circle-filled:before {
+	content: '\e441';
+}
+
+.m-icon-checkbox-filled:before {
+	content: '\e442';
+}
+
+.m-icon-closeempty:before {
+	content: '\e460';
+}
+
+.m-icon-refreshempty:before {
+	content: '\e461';
+}
+
+.m-icon-reload:before {
+	content: '\e462';
+}
+
+.m-icon-starhalf:before {
+	content: '\e463';
+}
+
+.m-icon-spinner:before {
+	content: '\e464';
+}
+
+.m-icon-spinner-cycle:before {
+	content: '\e465';
+}
+
+.m-icon-search:before {
+	content: '\e466';
+}
+
+.m-icon-plusempty:before {
+	content: '\e468';
+}
+
+.m-icon-forward:before {
+	content: '\e470';
+}
+
+.m-icon-back:before,
+.m-icon-left-nav:before {
+	content: '\e471';
+}
+
+.m-icon-checkmarkempty:before {
+	content: '\e472';
+}
+
+.m-icon-home:before {
+	content: '\e500';
+}
+
+.m-icon-navigate:before {
+	content: '\e501';
+}
+
+.m-icon-gear:before {
+	content: '\e502';
+}
+
+.m-icon-paperplane:before {
+	content: '\e503';
+}
+
+.m-icon-info:before {
+	content: '\e504';
+}
+
+.m-icon-help:before {
+	content: '\e505';
+}
+
+.m-icon-locked:before {
+	content: '\e506';
+}
+
+.m-icon-more:before {
+	content: '\e507';
+}
+
+.m-icon-flag:before {
+	content: '\e508';
+}
+
+.m-icon-home-filled:before {
+	content: '\e530';
+}
+
+.m-icon-gear-filled:before {
+	content: '\e532';
+}
+
+.m-icon-info-filled:before {
+	content: '\e534';
+}
+
+.m-icon-help-filled:before {
+	content: '\e535';
+}
+
+.m-icon-more-filled:before {
+	content: '\e537';
+}
+
+.m-icon-settings:before {
+	content: '\e560';
+}
+
+.m-icon-list:before {
+	content: '\e562';
+}
+
+.m-icon-bars:before {
+	content: '\e563';
+}
+
+.m-icon-loop:before {
+	content: '\e565';
+}
+
+.m-icon-paperclip:before {
+	content: '\e567';
+}
+
+.m-icon-eye:before {
+	content: '\e568';
+}
+
+.m-icon-arrowup:before {
+	content: '\e580';
+}
+
+.m-icon-arrowdown:before {
+	content: '\e581';
+}
+
+.m-icon-arrowleft:before {
+	content: '\e582';
+}
+
+.m-icon-arrowright:before {
+	content: '\e583';
+}
+
+.m-icon-arrowthinup:before {
+	content: '\e584';
+}
+
+.m-icon-arrowthindown:before {
+	content: '\e585';
+}
+
+.m-icon-arrowthinleft:before {
+	content: '\e586';
+}
+
+.m-icon-arrowthinright:before {
+	content: '\e587';
+}
+
+.m-icon-pulldown:before {
+	content: '\e588';
+}
+
+.m-icon-scan:before {
+    content: "\e612";
+}

+ 41 - 0
components/m-icon/m-icon.vue

@@ -0,0 +1,41 @@
+<template>
+	<view class="m-icon" :class="['m-icon-'+type]" :style="{color:color,'font-size':fontSize}" @click="onClick()"></view>
+</template>
+
+<script>
+	export default {
+		props: {
+			/**
+			 * 图标类型
+			 */
+			type: String,
+			/**
+			 * 图标颜色
+			 */
+			color: String,
+			/**
+			 * 图标大小
+			 */
+			size: {
+				type: [Number, String],
+				default: 24
+			}
+		},
+		computed: {
+			fontSize() {
+				var size = Number(this.size)
+				size = isNaN(size) ? 24 : size
+				return `${size}px`
+			}
+		},
+		methods: {
+			onClick() {
+				this.$emit('click')
+			}
+		}
+	}
+</script>
+
+<style>
+	@import "./m-icon.css";
+</style>

+ 128 - 0
components/m-input.vue

@@ -0,0 +1,128 @@
+<template>
+	<view class="m-input-view">
+		<input :focus="focus_" :type="inputType" :value="value" @input="onInput" class="m-input-input" :placeholder="placeholder"
+		 :password="type==='password'&&!showPassword" @focus="onFocus" @blur="onBlur" />
+		<!-- 优先显示密码可见按钮 -->
+		<view v-if="clearable_&&!displayable_&&value.length" class="m-input-icon">
+			<m-icon color="#666666" type="clear" size="20" @click="clear"></m-icon>
+		</view>
+		<view v-if="displayable_" class="m-input-icon">
+			<m-icon :color="showPassword?'#666666':'#cccccc'" type="eye" size="20" @click="display"></m-icon>
+		</view>
+	</view>
+</template>
+
+<script>
+	import mIcon from './m-icon/m-icon.vue'
+
+	export default {
+		components: {
+			mIcon
+		},
+		props: {
+			/**
+			 * 输入类型
+			 */
+			type: String,
+			/**
+			 * 值
+			 */
+			value: String,
+			/**
+			 * 占位符
+			 */
+			placeholder: String,
+			/**
+			 * 是否显示清除按钮
+			 */
+			clearable: {
+				type: [Boolean, String],
+				default: false
+			},
+			/**
+			 * 是否显示密码可见按钮
+			 */
+			displayable: {
+				type: [Boolean, String],
+				default: false
+			},
+			/**
+			 * 自动获取焦点
+			 */
+			focus: {
+				type: [Boolean, String],
+				default: false
+			}
+		},
+		model: {
+			prop: 'value',
+			event: 'input'
+		},
+		data() {
+			return {
+				/**
+				 * 显示密码明文
+				 */
+				showPassword: false,
+				/**
+				 * 是否获取焦点
+				 */
+				isFocus: false
+			}
+		},
+		computed: {
+			inputType() {
+				const type = this.type
+				return type === 'password' ? 'text' : type
+			},
+			clearable_() {
+				return String(this.clearable) !== 'false'
+			},
+			displayable_() {
+				return String(this.displayable) !== 'false'
+			},
+			focus_() {
+				return String(this.focus) !== 'false'
+			}
+		},
+		methods: {
+			clear() {
+				this.$emit('input', '')
+			},
+			display() {
+				this.showPassword = !this.showPassword
+			},
+			onFocus() {
+				this.isFocus = true
+			},
+			onBlur() {
+				this.$nextTick(() => {
+					this.isFocus = false
+				})
+			},
+			onInput(e) {
+				this.$emit('input', e.target.value)
+			}
+		}
+	}
+</script>
+
+<style>
+	.m-input-view {
+		display: flex;
+		flex-direction: row;
+		align-items: center;
+		width: 100%;
+		flex: 1;
+		padding: 0 10rpx;
+	}
+
+	.m-input-input {
+		flex: 1;
+		width: 100%;
+	}
+
+	.m-input-icon {
+		width: 20px;
+	}
+</style>

+ 80 - 0
components/scrollBox.vue

@@ -0,0 +1,80 @@
+<template>
+	<view class="list uni-flex uni-column">
+		<view class="wrap-item">
+			<view class="lis uni-flex uni-column" :animation="animationData">
+				<view class="uni-flex uni-column item" v-for="(item, index) in list" :key="index">
+					<view class="swiper-item item_title uni-flex list_item">
+						<view class="time uni-flex-item justify-align-center uni-flex">{{ item.userName }}刚刚扫描了一个VIN</view>
+					</view>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+export default {
+	props: {
+		list: {
+			type: Array
+		}
+	},
+	data() {
+		return {
+			scrollHeight: 0, //向上滚动距离
+			height: 0, //.lis高度(滚动框高度)
+			animationData: {} //动画对象
+		};
+	},
+	components: {},
+	mounted() {
+		this.prizeScroll();
+	},
+	methods: {
+		getHeight(Class) {
+			let query = uni.createSelectorQuery().in(this);
+			query
+				.selectAll(Class)
+				.boundingClientRect(data => {
+					this.height = data[0].height;
+				})
+				.exec();
+		},
+		prizeScroll() {
+			let speed = 60;
+			let animation = uni.createAnimation({
+				duration: this.getHeight('.lis') / speed,
+				timingFunction: 'linear',
+				delay: 0
+			});
+			this.animation = animation;
+			setInterval(() => {
+				if (this.scrollHeight >= this.height) {
+					animation.translateY(0).step();
+					this.scrollHeight = 0;
+					this.animationData = animation.export();
+				} else {
+					this.scrollHeight = this.scrollHeight + 1;
+					animation.translateY(-this.scrollHeight).step();
+					this.animationData = animation.export();
+				}
+			}, speed);
+		}
+	}
+};
+</script>
+
+<style lang="less">
+	.list {
+		width: 50%;
+		height: 2.6em;
+		overflow: hidden;
+		.item{
+			background: rgba(229, 237, 240, 0.3);
+			border-radius: 5em;
+			padding: 0.2em 1em;
+			font-size: 0.8em;
+			margin:0.3em 0;
+		}
+	}
+</style>

+ 80 - 0
components/tony-scroll/tony-scroll.vue

@@ -0,0 +1,80 @@
+<template>
+	<view class="home">
+		<view class="list uni-flex uni-column">
+			<view class="wrap-item">
+				<view class="lis uni-flex uni-column" :animation="animationData">
+					<view class="uni-flex uni-column" v-for="(item, index) in list" :key="index">
+						<view class="swiper-item item_title uni-flex list_item">
+							<view class="time uni-flex-item justify-align-center uni-flex">{{ item.time }}</view>
+							<view class="phone uni-flex-item justify-align-center uni-flex">{{ item.phone }}</view>
+							<view class="prizes uni-flex-item justify-align-center uni-flex">{{ item.prize }}</view>
+						</view>
+					</view>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+export default {
+	props: {
+		list: {
+			type: Array
+		}
+	},
+	data() {
+		return {
+			scrollHeight: 0, //向上滚动距离
+			height: 0, //.lis高度(滚动框高度)
+			animationData: {} //动画对象
+		};
+	},
+	components: {},
+	mounted() {
+		console.log("11")
+		this.prizeScroll();
+	},
+	methods: {
+		getHeight(Class) {
+			let query = uni.createSelectorQuery().in(this);
+			query
+				.selectAll(Class)
+				.boundingClientRect(data => {
+					this.height = data[0].height;
+				})
+				.exec();
+		},
+		prizeScroll() {
+			let speed = 50;
+			let animation = uni.createAnimation({
+				duration: this.getHeight('.lis') / speed,
+				timingFunction: 'linear',
+				delay: 0
+			});
+			this.animation = animation;
+			setInterval(() => {
+				if (this.scrollHeight >= this.height) {
+					animation.translateY(0).step();
+					this.scrollHeight = 0;
+					this.animationData = animation.export();
+				} else {
+					this.scrollHeight = this.scrollHeight + 1;
+					animation.translateY(-this.scrollHeight).step();
+					this.animationData = animation.export();
+				}
+			}, speed);
+		}
+	}
+};
+</script>
+
+<style>
+	page{
+		width: 100%;
+	}
+	.home,.list {
+		width: 750upx;
+		padding-top: 30upx;
+	}
+</style>

+ 101 - 0
components/uni-badge/uni-badge.vue

@@ -0,0 +1,101 @@
+<template>
+  <text v-if="text" :class="inverted ? 'uni-badge-' + type + ' uni-badge--' + size + ' uni-badge-inverted' : 'uni-badge-' + type + ' uni-badge--' + size"
+    class="uni-badge" @click="onClick()">{{ text }}</text>
+</template>
+
+<script>
+  export default {
+    name: 'UniBadge',
+    props: {
+      type: {
+        type: String,
+        default: 'default'
+      },
+      inverted: {
+        type: Boolean,
+        default: false
+      },
+      text: {
+        type: String,
+        default: ''
+      },
+      size: { // small.normal
+        type: String,
+        default: 'normal'
+      }
+    },
+    methods: {
+      onClick() {
+        this.$emit('click')
+      }
+    }
+  }
+</script>
+
+<style lang="scss">
+  $bage-size:12px;
+  $bage-small:scale(0.8);
+
+  .uni-badge {
+    font-family: 'Helvetica Neue', Helvetica, sans-serif;
+    box-sizing: border-box;
+    font-size: $bage-size;
+    line-height: 1;
+    display: inline-block;
+    padding: 3px 6px;
+    color: $uni-text-color;
+    border-radius: 100px;
+    background-color: $uni-bg-color-hover;
+
+    &.uni-badge-inverted {
+      padding: 0 5px 0 0;
+      color: $uni-text-color-grey;
+      background-color: transparent;
+    }
+
+    &-primary {
+      color: $uni-text-color-inverse;
+      background-color: $uni-color-primary;
+
+      &.uni-badge-inverted {
+        color: $uni-color-primary;
+        background-color: transparent
+      }
+    }
+
+    &-success {
+      color: $uni-text-color-inverse;
+      background-color: $uni-color-success;
+
+      &.uni-badge-inverted {
+        color: $uni-color-success;
+        background-color: transparent
+      }
+    }
+
+    &-warning {
+      color: $uni-text-color-inverse;
+      background-color: $uni-color-warning;
+
+      &.uni-badge-inverted {
+        color: $uni-color-warning;
+        background-color: transparent
+      }
+    }
+
+    &-error {
+      color: $uni-text-color-inverse;
+      background-color: $uni-color-error;
+
+      &.uni-badge-inverted {
+        color: $uni-color-error;
+        background-color: transparent
+      }
+    }
+
+    &--small {
+      transform: $bage-small;
+      transform-origin: center center;
+    }
+  }
+</style>

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

@@ -0,0 +1,223 @@
+<template>
+  <view
+    :class="['uni-collapse-cell', { 'uni-collapse-cell--disabled': disabled, 'uni-collapse-cell--open': isOpen }]"
+    :hover-class="disabled ? '' : 'uni-collapse-cell--hover'">
+    <view
+      class="uni-collapse-cell__title header"
+      @click="onClick">
+      <view
+        v-if="thumb"
+        class="uni-collapse-cell__title-extra"><image
+          :src="thumb"
+          class="uni-collapse-cell__title-img" /></view>
+      <view class="uni-collapse-cell__title-inner">
+        <view class="uni-collapse-cell__title-text">{{ title }}</view>
+      </view>
+      <view
+        :class="{ 'uni-active': isOpen, 'uni-collapse-cell--animation': showAnimation === true }"
+        class="uni-collapse-cell__title-arrow">
+        <uni-icons
+          color="#bbb"
+          size="20"
+          type="arrowdown" />
+      </view>
+    </view>
+    <view
+      :style="{ height: isOpen ? 'auto' : '0px' }"
+      class="uni-collapse-cell__content">
+      <view :class="{ 'uni-collapse-cell--animation': showAnimation === true }" :style="{ transform: isOpen ? 'translateY(0px)' : 'translateY(-50%)','-webkit-transform' : isOpen ? 'translateY(0px)' : 'translateY(-50%)' }">
+        <slot />
+      </view>
+    </view>
+  </view>
+</template>
+
+<script>
+import uniIcons from '../uni-icons/uni-icons.vue'
+export default {
+  name: 'UniCollapseItem',
+  components: {
+    uniIcons
+  },
+  props: {
+    title: {
+      // 列表标题
+      type: String,
+      default: ''
+    },
+    name: {
+      // 唯一标识符
+      type: [Number, String],
+      default: 0
+    },
+    disabled: {
+      // 是否禁用
+      type: [Boolean, String],
+      default: false
+    },
+    showAnimation: {
+      // 是否显示动画
+      type: Boolean,
+      default: false
+    },
+    open: {
+      // 是否展开
+      type: [Boolean, String],
+      default: false
+    },
+    thumb: {
+      // 缩略图
+      type: String,
+      default: ''
+    }
+  },
+  data () {
+    return {
+      isOpen: false
+    }
+  },
+  watch: {
+    open (val) {
+      this.isOpen = val
+    }
+  },
+  inject: ['collapse'],
+  created () {
+    this.isOpen = this.open
+    this.nameSync = this.name ? this.name : this.collapse.childrens.length
+    this.collapse.childrens.push(this)
+    if (String(this.collapse.accordion) === 'true') {
+      if (this.isOpen) {
+        let lastEl = this.collapse.childrens[this.collapse.childrens.length - 2]
+        if (lastEl) {
+          this.collapse.childrens[this.collapse.childrens.length - 2].isOpen = false
+        }
+      }
+    }
+  },
+  methods: {
+    onClick () {
+      if (this.disabled) {
+        return
+      }
+      if (String(this.collapse.accordion) === 'true') {
+        this.collapse.childrens.forEach(vm => {
+          if (vm === this) {
+            return
+          }
+          vm.isOpen = false
+        })
+      }
+      this.isOpen = !this.isOpen
+      this.collapse.onChange && this.collapse.onChange()
+			this.$forceUpdate()
+    }
+  }
+}
+</script>
+
+<style lang="scss">
+@mixin collapse-hover {
+	background-color: #f5f5f5;
+}
+
+@mixin collapse-disabled {
+	opacity: 0.3;
+}
+
+$collapse-title-pd: $uni-spacing-col-lg $uni-spacing-row-lg;
+
+.uni-collapse-cell {
+	position: relative;
+	&--hover {
+		@include collapse-hover;
+	}
+
+	&--open {
+		@include collapse-hover;
+	}
+
+	&--disabled {
+		@include collapse-disabled;
+	}
+
+	&--animation {
+		transition: all 0.3s;
+	}
+
+	&:after {
+		position: absolute;
+		z-index: 3;
+		right: 0;
+		bottom: 0;
+		left: 0px;
+		height: 1px;
+		content: '';
+		-webkit-transform: scaleY(0.5);
+		transform: scaleY(0.5);
+		background-color: $uni-border-color;
+	}
+
+	&__title {
+		padding: $collapse-title-pd;
+		width: 100%;
+		box-sizing: border-box;
+		flex: 1;
+		position: relative;
+		display: flex;
+		flex-direction: row;
+		justify-content: space-between;
+		align-items: center;
+
+		&-extra {
+			margin-right: 18upx;
+			display: flex;
+			flex-direction: row;
+			justify-content: center;
+			align-items: center;
+		}
+
+		&-img {
+			height: $uni-img-size-base;
+			width: $uni-img-size-base;
+		}
+
+		&-arrow {
+			width: 20px;
+			height: 20px;
+			transform: rotate(0deg);
+			transform-origin: center center;
+
+			&.uni-active {
+				transform: rotate(180deg);
+			}
+		}
+
+		&-inner {
+			flex: 1;
+			overflow: hidden;
+			display: flex;
+			flex-direction: column;
+		}
+
+		&-text {
+			font-size: $uni-font-size-lg;
+			text-overflow: ellipsis;
+			white-space: nowrap;
+			color: inherit;
+			line-height: 1.5;
+			overflow: hidden;
+		}
+	}
+
+	&__content {
+		position: relative;
+		width: 100%;
+		overflow: hidden;
+
+		.view {
+			font-size: $uni-font-size-base;
+		}
+	}
+}
+</style>

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

@@ -0,0 +1,72 @@
+<template>
+  <view class="uni-collapse"><slot /></view>
+</template>
+<script>
+export default {
+  name: 'UniCollapse',
+  props: {
+    accordion: {
+      // 是否开启手风琴效果
+      type: [Boolean, String],
+      default: false
+    }
+  },
+  data () {
+    return {}
+  },
+  provide () {
+    return {
+      collapse: this
+    }
+  },
+  created () {
+    this.childrens = []
+  },
+  methods: {
+    onChange () {
+      let activeItem = []
+      this.childrens.forEach((vm, index) => {
+        if (vm.isOpen) {
+          activeItem.push(vm.nameSync)
+        }
+      })
+      this.$emit('change', activeItem)
+    }
+  }
+}
+</script>
+<style lang="scss">
+.uni-collapse {
+	background-color: $uni-bg-color;
+	position: relative;
+	width: 100%;
+	display: flex;
+	flex-direction: column;
+
+	&:after {
+		position: absolute;
+		z-index: 10;
+		right: 0;
+		bottom: 0;
+		left: 0;
+		height: 0px;
+		content: '';
+		-webkit-transform: scaleY(0.5);
+		transform: scaleY(0.5);
+		background-color: $uni-border-color;
+	}
+
+	&:before {
+		position: absolute;
+		z-index: 10;
+		right: 0;
+		top: 0;
+		left: 0;
+		height: 0px;
+		content: '';
+		-webkit-transform: scaleY(0.5);
+		transform: scaleY(0.5);
+		background-color: $uni-border-color;
+	}
+}
+</style>

File diff suppressed because it is too large
+ 39 - 0
components/uni-icons/uni-icons.vue


+ 243 - 0
components/uni-list-item/uni-list-item.vue

@@ -0,0 +1,243 @@
+<template>
+  <view
+    :class="disabled ? 'uni-list-item--disabled' : ''"
+    :hover-class="disabled || showSwitch ? '' : 'uni-list-item--hover'"
+    class="uni-list-item"
+    @click="onClick">
+    <view class="uni-list-item__container">
+      <view
+        v-if="thumb"
+        class="uni-list-item__icon"><image
+          :src="thumb"
+          class="uni-list-item__icon-img" /></view>
+      <view
+        v-else-if="showExtraIcon"
+        class="uni-list-item__icon">
+        <uni-icons
+          :color="extraIcon.color"
+          :size="extraIcon.size"
+          :type="extraIcon.type"
+          class="uni-icon-wrapper" />
+      </view>
+      <view class="uni-list-item__content">
+        <view class="uni-list-item__content-title">{{ title }}</view>
+        <view
+          v-if="note"
+          class="uni-list-item__content-note">{{ note }}</view>
+      </view>
+      <view
+        v-if="showBadge || showArrow || showSwitch"
+        class="uni-list-item__extra">
+        <uni-badge
+          v-if="showBadge"
+          :type="badgeType"
+          :text="badgeText" />
+        <switch
+          v-if="showSwitch"
+          :disabled="disabled"
+          :checked="switchChecked"
+          @change="onSwitchChange" />
+        <uni-icons
+          v-if="showArrow"
+          :size="20"
+          class="uni-icon-wrapper"
+          color="#bbb"
+          type="arrowright" />
+      </view>
+    </view>
+  </view>
+</template>
+
+<script>
+import uniIcons from '../uni-icons/uni-icons.vue'
+import uniBadge from '../uni-badge/uni-badge.vue'
+export default {
+  name: 'UniListItem',
+  components: {
+    uniIcons,
+    uniBadge
+  },
+  props: {
+    title: {
+      type: String,
+      default: ''
+    }, // 列表标题
+    note: {
+      type: String,
+      default: ''
+    }, // 列表描述
+    disabled: {
+      // 是否禁用
+      type: [Boolean, String],
+      default: false
+    },
+    showArrow: {
+      // 是否显示箭头
+      type: [Boolean, String],
+      default: true
+    },
+    showBadge: {
+      // 是否显示数字角标
+      type: [Boolean, String],
+      default: false
+    },
+    showSwitch: {
+      // 是否显示Switch
+      type: [Boolean, String],
+      default: false
+    },
+    switchChecked: {
+      // Switch是否被选中
+      type: [Boolean, String],
+      default: false
+    },
+    badgeText: {
+      // badge内容
+      type: String,
+      default: ''
+    },
+    badgeType: {
+      // badge类型
+      type: String,
+      default: 'success'
+    },
+    thumb: {
+      // 缩略图
+      type: String,
+      default: ''
+    },
+    showExtraIcon: {
+      // 是否显示扩展图标
+      type: [Boolean, String],
+      default: false
+    },
+    extraIcon: {
+      type: Object,
+      default () {
+        return {
+          type: 'contact',
+          color: '#000000',
+          size: 20
+        }
+      }
+    }
+  },
+  data () {
+    return {}
+  },
+  methods: {
+    onClick () {
+      this.$emit('click')
+    },
+    onSwitchChange (e) {
+      this.$emit('switchChange', e.detail)
+    }
+  }
+}
+</script>
+
+<style lang="scss">
+@mixin list-hover {
+	background-color: $uni-bg-color-hover;
+}
+
+@mixin list-disabled {
+	opacity: 0.3;
+}
+
+$list-item-pd: $uni-spacing-col-lg $uni-spacing-row-lg;
+
+.uni-list-item {
+	font-size: $uni-font-size-lg;
+	position: relative;
+	display: flex;
+	flex-direction: column;
+	justify-content: space-between;
+	align-items: center;
+
+	&--disabled {
+		@include list-disabled;
+	}
+
+	&--hover {
+		@include list-hover;
+	}
+
+	&__container {
+		padding: $list-item-pd;
+		width: 100%;
+		box-sizing: border-box;
+		flex: 1;
+		position: relative;
+		display: flex;
+		flex-direction: row;
+		justify-content: space-between;
+		align-items: center;
+
+		&:after {
+			position: absolute;
+			z-index: 3;
+			right: 0;
+			bottom: 0;
+			left: 30upx;
+			height: 1px;
+			content: '';
+			-webkit-transform: scaleY(0.5);
+			transform: scaleY(0.5);
+			background-color: $uni-border-color;
+		}
+	}
+
+	&__content {
+		flex: 1;
+		overflow: hidden;
+		display: flex;
+		flex-direction: column;
+		color: #3b4144;
+		&-title {
+			font-size: $uni-font-size-lg;
+			text-overflow: ellipsis;
+			white-space: nowrap;
+			color: inherit;
+			line-height: 1.5;
+			overflow: hidden;
+		}
+
+		&-note {
+			margin-top: 6upx;
+			color: $uni-text-color-grey;
+			font-size: $uni-font-size-base;
+			white-space: normal;
+			display: -webkit-box;
+			-webkit-box-orient: vertical;
+			-webkit-line-clamp: 2;
+			overflow: hidden;
+		}
+	}
+
+	&__extra {
+		width: 25%;
+		display: flex;
+		flex-direction: row;
+		justify-content: flex-end;
+		align-items: center;
+	}
+
+	&__icon {
+		margin-right: 18upx;
+		display: flex;
+		flex-direction: row;
+		justify-content: center;
+		align-items: center;
+
+		&-img {
+			height: $uni-img-size-base;
+			width: $uni-img-size-base;
+		}
+	}
+}
+
+.uni-list > .uni-list-item:last-child .uni-list-item-container:after {
+	height: 0px;
+}
+</style>

+ 45 - 0
components/uni-list/uni-list.vue

@@ -0,0 +1,45 @@
+<template>
+  <view class="uni-list">
+    <slot/>
+  </view>
+</template>
+<script>
+export default {
+  name: 'UniList'
+}
+</script>
+<style lang="scss">
+	.uni-list {
+		background-color: $uni-bg-color;
+		position: relative;
+		width: 100%;
+		display: flex;
+		flex-direction: column;
+
+		&:after {
+			position: absolute;
+			z-index: 10;
+			right: 0;
+			bottom: 0;
+			left: 0;
+			height: 1px;
+			content: '';
+			-webkit-transform: scaleY(0.5);
+			transform: scaleY(0.5);
+			background-color: $uni-border-color;
+		}
+
+		&:before {
+			position: absolute;
+			z-index: 10;
+			right: 0;
+			top: 0;
+			left: 0;
+			height: 1px;
+			content: '';
+			-webkit-transform: scaleY(0.5);
+			transform: scaleY(0.5);
+			background-color: $uni-border-color;
+		}
+	}
+</style>

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

@@ -0,0 +1,222 @@
+<template>
+
+  <view class="uni-navbar">
+    <view
+      :class="{'uni-navbar--fixed': fixed,'uni-navbar--shadow':border,'uni-navbar--border':border}"
+      :style="{'background-color':backgroundColor}"
+      class="uni-navbar__content">
+      <uni-status-bar v-if="statusBar"/>
+      <view
+        :style="{color:color}"
+        class="uni-navbar__header uni-navbar__content_view">
+        <view
+          class="uni-navbar__header-btns uni-navbar__content_view"
+          @tap="onClickLeft">
+          <view
+            v-if="leftIcon.length"
+            class="uni-navbar__content_view">
+            <uni-icons
+              :type="leftIcon"
+              :color="color"
+              size="24"/>
+          </view>
+          <view
+            v-if="leftText.length"
+            :class="{'uni-navbar-btn-icon-left':!leftIcon.length}"
+            class="uni-navbar-btn-text uni-navbar__content_view">{{ leftText }}</view>
+          <slot name="left"/>
+        </view>
+        <view class="uni-navbar__header-container uni-navbar__content_view">
+          <view
+            v-if="title.length"
+            class="uni-navbar__header-container-inner uni-navbar__content_view">{{ title }}</view>
+          <!-- 标题插槽 -->
+          <slot/>
+        </view>
+        <view
+          :class="title.length?'uni-navbar__header-btns-right':''"
+          class="uni-navbar__header-btns uni-navbar__content_view"
+          @tap="onClickRight">
+          <view
+            v-if="rightIcon.length"
+            class="uni-navbar__content_view">
+            <uni-icons
+              :type="rightIcon"
+              :color="color"
+              size="24"/>
+          </view>
+          <!-- 优先显示图标 -->
+          <view
+            v-if="rightText.length&&!rightIcon.length"
+            class="uni-navbar-btn-text uni-navbar__content_view">{{ rightText }}</view>
+          <slot name="right"/>
+        </view>
+      </view>
+    </view>
+    <view
+      v-if="fixed"
+      class="uni-navbar__placeholder">
+      <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: true
+    },
+    border: {
+      type: [String, Boolean],
+      default: true
+    }
+  },
+  methods: {
+    onClickLeft () {
+      this.$emit('click-left')
+    },
+    onClickRight () {
+      this.$emit('click-right')
+    }
+  }
+}
+</script>
+
+<style lang="scss">
+	$nav-height:44px;
+
+	.uni-navbar {
+		&__content {
+			display: block;
+			position: relative;
+			width: 100%;
+			background-color: $uni-bg-color;
+			overflow: hidden;
+
+			.uni-navbar__content_view {
+				// line-height: $nav-height;
+				display: flex;
+				align-items: center;
+			}
+		}
+
+		&__header {
+			display: flex;
+			flex-direction: row;
+			width: 100%;
+			height: $nav-height;
+			line-height: $nav-height;
+			font-size: 16px;
+
+			&-btns {
+				display: inline-flex;
+				flex-wrap: nowrap;
+				flex-shrink: 0;
+				width: 120upx;
+				padding: 0 12upx;
+
+				&:first-child {
+					padding-left: 0;
+				}
+
+				&:last-child {
+					width: 60upx;
+				}
+
+        &-right:last-child{
+          width: 120rpx;
+          text-align: right;
+          flex-direction: row-reverse;
+        }
+			}
+
+			&-container {
+				width: 100%;
+				margin: 0 10upx;
+
+				&-inner {
+					width: 100%;
+					display: flex;
+					justify-content: center;
+					font-size: 30upx;
+					// padding-right: 60upx;
+				}
+			}
+		}
+
+		&__placeholder {
+			&-view {
+				height: $nav-height;
+			}
+		}
+
+		&--fixed {
+			position: fixed;
+			z-index: 998;
+		}
+
+		&--shadow {
+			box-shadow: 0 1px 6px #ccc;
+		}
+
+		&--border:after {
+			position: absolute;
+			z-index: 3;
+			bottom: 0;
+			left: 0;
+			right: 0;
+			height: 1px;
+			content: '';
+			-webkit-transform: scaleY(.5);
+			transform: scaleY(.5);
+			background-color: $uni-border-color;
+		}
+	}
+</style>

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

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

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

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

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

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

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

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

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

@@ -0,0 +1,268 @@
+<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 v-if="showCancle" class="uni-dialog-button" @click="close">
+				<text class="uni-dialog-button-text">{{cancleText}}</text>
+			</view>
+			<view class="uni-dialog-button uni-border-left" @click="onOk">
+				<text class="uni-dialog-button-text uni-button-color">{{confirmText}}</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
+			},
+			/**
+			 * 取消按钮文字
+			 */
+			cancleText: {
+				type: String,
+				default: '取消'
+			},
+			/**
+			 * 确定按钮文字
+			 */
+			confirmText: {
+				type: String,
+				default: '确定'
+			},
+			/**
+			 * 是否显示取消按钮
+			 */
+			showCancle: {
+				type: Boolean,
+				default: true
+			}
+		},
+		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;
+		text-align: center;
+	}
+
+	.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: #FFD100;
+	}
+
+	.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;
+	}
+	.uni-popup__modal {
+		color: #000000;
+	}
+</style>

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

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

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

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

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

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

+ 153 - 0
components/uni-progress/uni-progress.vue

@@ -0,0 +1,153 @@
+<template>
+	<view class="progress_box">
+		<canvas class="progress_bg" canvas-id="cpbg"></canvas>
+		<canvas class="progress_bar" canvas-id="cpbar"></canvas>
+		<canvas class="progress_line" canvas-id="cpline"></canvas>
+		<view class="progress_txt">
+			<view class="progress_info">{{ progress_txt }}<text>分</text></view>
+		</view>
+	</view>
+</template>
+
+<script>
+export default {
+	props: {
+		progress_txt: {
+			type: Number,
+			default: 0
+		}
+	},
+	mounted: function() {
+		this.drawProgressbg();
+		this.drawCircle(this.progress_txt); //参数为1-100
+		this.drawLine();
+	},
+	methods: {
+		drawProgressbg: function() {
+			// 自定义组件实例 this ,表示在这个自定义组件下查找拥有 canvas-id 的 <canvas/>
+			var ctx = uni.createCanvasContext('cpbg', this);
+			ctx.setLineWidth(12); // 设置圆环的宽度
+			ctx.setStrokeStyle('#fdee1b'); // 设置圆环的颜色
+			// ctx.setLineCap('round'); // 设置圆环端点的形状
+			ctx.setLineCap('square'); // 设置圆环端点的形状
+			ctx.beginPath(); //开始一个新的路径
+			ctx.arc(55, 55, 45, 0 * Math.PI, 2 * Math.PI, false);
+			//设置一个原点(100,100),半径为100的圆的路径到当前路径
+			ctx.stroke(); //对当前路径进行描边
+			ctx.draw();
+		},
+		drawCircle: function(step) {
+			var ctx = uni.createCanvasContext('cpbar', this);
+			// 进度条的渐变(中心x坐标-半径-边宽,中心Y坐标,中心x坐标+半径+边宽,中心Y坐标)
+			var gradient = ctx.createLinearGradient(0, 0, 65, 0);
+
+			let increase = 0.05;
+			let end = (step / 100) * 2 * Math.PI - Math.PI / 2; // 最后的角度
+			let current = -Math.PI / 2; // 起始角度
+			let timer = setInterval(() => {
+				gradient.addColorStop('0', '#ff9549');
+				gradient.addColorStop('1.0', '#ff5500');
+				ctx.setLineWidth(12);
+				ctx.setStrokeStyle(gradient);
+				ctx.setLineCap('square');
+				ctx.beginPath();
+				// 参数step 为绘制的百分比
+				if (current < end) {
+					current = current + increase;
+				}
+				if (current >= end) {
+					current = end;
+					clearInterval(timer);
+				}
+				ctx.arc(55, 55, 45, -Math.PI / 2, current, false);
+				ctx.stroke();
+				ctx.draw();
+			}, 30);
+		},
+		// 画刻度
+		drawLine() {
+			var context = uni.createCanvasContext("cpline",this);
+			var r = 45;
+			var x0 = 55;
+			var y0 = 55;
+			var x;
+			var y;
+			var lineWidth = 12;
+
+			for (let i = 0; i < 60; i++) {
+			  context.beginPath();
+			  context.setLineWidth(lineWidth);
+			  context.setStrokeStyle("#fdee1b");
+                
+			  x = x0 - r * Math.sin(((6 * (i + 1) - 3) * Math.PI) / 180);
+			  y = y0 - r * Math.cos(((6 * (i + 1) - 3) * Math.PI) / 180);
+
+			  // console.log('x0:' + x0 + '   y0:' + y0 + '    x:' + x + '    y:' + y);
+			  context.moveTo(x, y);
+			  context.arc(
+			    x0,
+			    y0,
+			    r,
+			    ((270 - 6 * (i + 1) + 3) * Math.PI) / 180,
+			    ((270 - 6 * i) * Math.PI) / 180,
+			    false
+			  );
+			  context.stroke();
+			  context.closePath();
+			}
+			context.stroke();
+			context.draw();
+		}
+	}
+};
+</script>
+
+<style>
+.progress_box {
+	position: relative;
+	width: 100%;
+	height: 200upx;
+	display: flex;
+	align-items: center;
+	justify-content: center;
+	text-align: center;
+}
+.progress_bg {
+	position: absolute;
+	width: 110px;
+	height: 110px;
+}
+.progress_bar {
+	position: absolute;
+	width: 110px;
+	height: 110px;
+}
+.progress_line {
+	position: absolute;
+	width: 110px;
+	height: 110px;
+}
+.progress_txt {
+	position: absolute;
+	font-size: 28upx;
+	color: #999999;
+}
+.progress_info {
+	padding-left: 16upx;
+	letter-spacing: 2upx;
+	font-size: 50upx;
+	color: #333333;
+	display: flex;
+	align-items: center;
+	justify-content: center;
+}
+.progress_info text{
+		font-size: 24upx;
+	}
+.progress_dot {
+	width: 16upx;
+	height: 16upx;
+	border-radius: 50%;
+	background-color: #fb9126;
+}
+</style>

+ 144 - 0
components/uni-rate/uni-rate.vue

@@ -0,0 +1,144 @@
+<template>
+  <view class="uni-rate">
+    <view
+      v-for="(star, index) in stars"
+      :key="index"
+      :style="{ marginLeft: margin + 'px' }"
+      class="uni-rate-icon"
+      @click="_onClick(index)"
+    >
+      <uni-icons
+        :size="size"
+        :color="color"
+        :type="isFill ? 'star-filled' : 'star'"
+      />
+      <view
+        :style="{ width: star.activeWitch }"
+        class="uni-rate-icon-on">
+        <uni-icons
+          :size="size"
+          :color="activeColor"
+          type="star-filled"/>
+      </view>
+    </view>
+  </view>
+</template>
+
+<script>
+import uniIcons from '../uni-icons/uni-icons.vue'
+export default {
+  name: 'UniRate',
+  components: {
+    uniIcons
+  },
+  props: {
+    isFill: {
+      // 星星的类型,是否镂空
+      type: [Boolean, String],
+      default: true
+    },
+    color: {
+      // 星星的颜色
+      type: String,
+      default: '#ececec'
+    },
+    activeColor: {
+      // 星星选中状态颜色
+      type: String,
+      default: '#ffca3e'
+    },
+    size: {
+      // 星星的大小
+      type: [Number, String],
+      default: 24
+    },
+    value: {
+      // 当前评分
+      type: [Number, String],
+      default: 0
+    },
+    max: {
+      // 最大评分
+      type: [Number, String],
+      default: 5
+    },
+    margin: {
+      // 星星的间距
+      type: [Number, String],
+      default: 0
+    },
+    disabled: {
+      // 是否可点击
+      type: [Boolean, String],
+      default: false
+    }
+  },
+  data () {
+    return {
+      valueSync: ''
+    }
+  },
+  computed: {
+    stars () {
+      const value = Number(this.valueSync) ? Number(this.valueSync) : 0
+      const starList = []
+      const floorValue = Math.floor(value)
+      const ceilValue = Math.ceil(value)
+      for (let i = 0; i < this.max; i++) {
+        if (floorValue > i) {
+          starList.push({
+            activeWitch: '100%'
+          })
+        } else if (ceilValue - 1 === i) {
+          starList.push({
+            activeWitch: (value - floorValue) * 100 + '%'
+          })
+        } else {
+          starList.push({
+            activeWitch: '0'
+          })
+        }
+      }
+      return starList
+    }
+  },
+  created () {
+    this.valueSync = this.value
+  },
+  methods: {
+    _onClick (index) {
+      if (this.disabled) {
+        return
+      }
+      this.valueSync = index + 1
+      this.$emit('change', {
+        value: this.valueSync
+      })
+    }
+  }
+}
+</script>
+
+<style lang="scss">
+.uni-rate {
+	line-height: 0;
+	font-size: 0;
+	display: flex;
+	flex-direction: row;
+
+	&-icon {
+		position: relative;
+		line-height: 0;
+		font-size: 0;
+		display: inline-block;
+
+		&-on {
+			line-height: 1;
+			position: absolute;
+			top: 0;
+			left: 0;
+			overflow: hidden;
+		}
+	}
+}
+</style>

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

@@ -0,0 +1,26 @@
+<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>
+.uni-status-bar {
+	display: block;
+	width: 100%;
+	height: 20px;
+	height: var(--status-bar-height);
+}
+</style>

+ 192 - 0
components/uni-swiper-dot/uni-swiper-dot.vue

@@ -0,0 +1,192 @@
+<template>
+  <view class="uni-swiper__warp">
+    <slot/>
+    <view
+      v-if="mode === 'default'"
+      :style="{'bottom':dots.bottom + 'px'}"
+      class="uni-swiper__dots-box">
+      <view
+        v-for="(item,index) in info"
+        :style="{
+        'width': (index === current? dots.width*2:dots.width ) + 'px','height':dots.width/3 +'px' ,'background-color':index !== current?dots.backgroundColor:dots.selectedBackgroundColor,'border-radius':'0px'}"
+        :key="index"
+        class="uni-swiper__dots-item uni-swiper__dots-bar"/>
+    </view>
+    <view
+      v-if="mode === 'dot'"
+      :style="{'bottom':dots.bottom + 'px'}"
+      class="uni-swiper__dots-box">
+      <view
+        v-for="(item,index) in info"
+        :style="{
+        'width': dots.width + 'px','height':dots.height +'px' ,'background-color':index !== current?dots.backgroundColor:dots.selectedBackgroundColor,'border':index !==current ? dots.border:dots.selectedBorder}"
+        :key="index"
+        class="uni-swiper__dots-item"/>
+    </view>
+		<view v-if="mode === 'round'" :style="{'bottom':dots.bottom + 'px'}" class="uni-swiper__dots-box">
+			<view v-for="(item,index) in info" :class="[index === current&&'uni-swiper__dots-long']" :style="{
+		    'width':(index === current? dots.width*3:dots.width ) + 'px','height':dots.height +'px' ,'background-color':index !== current?dots.backgroundColor:dots.selectedBackgroundColor,'border':index !==current ? dots.border:dots.selectedBorder}" :key="index" class="uni-swiper__dots-item " />
+		</view>
+    <view
+      v-if="mode === 'nav'"
+      :style="{'background-color':dotsStyles.backgroundColor}"
+      class="uni-swiper__dots-box uni-swiper__dots-nav">
+      <view
+        :style="{'color':dotsStyles.color}"
+        class="uni-swiper__dots-nav-item">{{ (current+1)+"/"+info.length }}
+        {{ info[current][field] }}</view>
+    </view>
+    <view
+      v-if="mode === 'indexes'"
+      :style="{'bottom':dots.bottom + 'px'}"
+      class="uni-swiper__dots-box">
+      <view
+        v-for="(item,index) in info"
+        :style="{
+        'width':dots.width + 'px','height':dots.height +'px' ,'color':index === current?dots.selectedColor:dots.color,'background-color':index !== current?dots.backgroundColor:dots.selectedBackgroundColor,'border':index !==current ? dots.border:dots.selectedBorder}"
+        :key="index"
+        class="uni-swiper__dots-item uni-swiper__dots-indexes">{{ index+1 }}</view>
+    </view>
+  </view>
+</template>
+
+<script>
+export default {
+  name: 'UniSwiperDot',
+  props: {
+    info: {
+      type: Array,
+      default () {
+        return []
+      }
+    },
+    current: {
+      type: Number,
+      default: 0
+    },
+    dotsStyles: {
+      type: Object,
+      default () {
+        return {}
+      }
+    },
+    // 类型 :default(默认) indexes long nav
+    mode: {
+      type: String,
+      default: 'default'
+    },
+    // 只在 nav 模式下生效,变量名称
+    field: {
+      type: String,
+      default: ''
+    }
+  },
+  data () {
+    return {
+      dots: {
+        width: 8,
+        height: 8,
+        bottom: 10,
+        color: '#fff',
+        backgroundColor: 'rgba(0, 0, 0, .3)',
+        border: '1px rgba(0, 0, 0, .3) solid',
+        selectedBackgroundColor: '#333',
+        selectedBorder: '1px rgba(0, 0, 0, .9) solid'
+      }
+    }
+  },
+  watch: {
+    dotsStyles (newVal) {
+      this.dots = Object.assign(this.dots, this.dotsStyles)
+    },
+    mode (newVal) {
+      if (newVal === 'indexes') {
+        this.dots.width = 20
+        this.dots.height = 20
+      } else {
+        this.dots.width = 8
+        this.dots.height = 8
+      }
+    }
+
+  },
+  created () {
+    if (this.mode === 'indexes') {
+      this.dots.width = 20
+      this.dots.height = 20
+    }
+    this.dots = Object.assign(this.dots, this.dotsStyles)
+  }
+}
+</script>
+
+<style>
+	.uni-swiper__warp {
+		position: relative;
+		width: 100%;
+		box-sizing: border-box;
+		overflow: hidden;
+	}
+
+	.uni-swiper__dots-box {
+		position: absolute;
+		bottom: 20rpx;
+		display: flex;
+		justify-content: center;
+		align-items: center;
+		box-sizing: box-sizing;
+		width: 100%;
+	}
+
+	.uni-swiper__dots-item {
+		flex-shrink: 0;
+		width: 16rpx;
+		border-radius: 50%;
+		margin-left: 12rpx;
+		background: rgba(0, 0, 0, .3);
+		transition: all 0.2s linear;
+	}
+
+	.uni-swiper__dots-item:first-child {
+		margin: 0;
+	}
+
+	.uni-swiper__dots-default {
+		border-radius: 50%;
+	}
+
+	.uni-swiper__dots-long {
+		border-radius: 100rpx;
+	}
+
+  .uni-swiper__dots-bar {
+    border-radius: 100rpx;
+  }
+
+	.uni-swiper__dots-nav {
+		bottom: 0;
+		height: 80rpx;
+		justify-content: flex-start;
+		background: rgba(0, 0, 0, 0.2);
+		box-sizing: box-sizing;
+		overflow: hidden;
+	}
+
+	.uni-swiper__dots-nav-item {
+		overflow: hidden;
+		text-overflow: ellipsis;
+		white-space: nowrap;
+		font-size: 28rpx;
+		color: #fff;
+		box-sizing: box-sizing;
+		margin: 0 30rpx;
+	}
+
+	.uni-swiper__dots-indexes {
+		display: flex;
+		justify-content: center;
+		align-items: center;
+		color: #fff;
+		font-size: 24rpx;
+	}
+</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>

+ 20 - 0
index.html

@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <script>
+      var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
+        CSS.supports('top: constant(a)'))
+      document.write(
+        '<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
+        (coverSupport ? ', viewport-fit=cover' : '') + '" />')
+    </script>
+    <title></title>
+    <!--preload-links-->
+    <!--app-context-->
+  </head>
+  <body>
+    <div id="app"><!--app-html--></div>
+    <script type="module" src="/main.js"></script>
+  </body>
+</html>

+ 82 - 0
libs/axios.js

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

+ 30 - 0
libs/tools.js

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

+ 5 - 0
libs/validate.js

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

+ 24 - 0
main.js

@@ -0,0 +1,24 @@
+import Vue from 'vue'
+import App from './App'
+
+import store from './store'
+import uView from "uview-ui"
+Vue.use(uView)
+Vue.config.productionTip = false
+
+Vue.prototype.$store = store
+
+App.mpType = 'app'
+/*
+* 打电话
+*/ 
+Vue.prototype.callPhone = function (phone) {
+	uni.makePhoneCall({
+		phoneNumber: phone
+	});
+} 
+const app = new Vue({
+    store,
+    ...App
+})
+app.$mount()

+ 75 - 0
manifest.json

@@ -0,0 +1,75 @@
+{
+    "name" : "digitalShelf-mini-html",
+    "appid" : "__UNI__D000D19",
+    "description" : "数字货架小程序",
+    "versionName" : "1.0.0",
+    "versionCode" : "100",
+    "transformPx" : false,
+    /* 5+App特有相关 */
+    "app-plus" : {
+        "usingComponents" : true,
+        "nvueStyleCompiler" : "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.VIBRATE\"/>",
+                    "<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
+                    "<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.CAMERA\"/>",
+                    "<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
+                    "<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
+                    "<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
+                    "<uses-feature android:name=\"android.hardware.camera\"/>",
+                    "<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
+                ]
+            },
+            /* ios打包配置 */
+            "ios" : {},
+            /* SDK配置 */
+            "sdkConfigs" : {}
+        }
+    },
+    /* 快应用特有相关 */
+    "quickapp" : {},
+    /* 小程序特有相关 */
+    "mp-weixin" : {
+        "appid" : "wxb1ef46a0ae34db32",
+        "setting" : {
+            "urlCheck" : false,
+            "es6" : true,
+            "postcss" : true,
+            "minified" : true
+        },
+        "usingComponents" : true
+    },
+    "mp-alipay" : {
+        "usingComponents" : true
+    },
+    "mp-baidu" : {
+        "usingComponents" : true
+    },
+    "mp-toutiao" : {
+        "usingComponents" : true
+    },
+    "uniStatistics" : {
+        "enable" : false
+    },
+    "vueVersion" : "2"
+}

+ 18 - 0
package-lock.json

@@ -0,0 +1,18 @@
+{
+  "name": "digitalshelf-mini-html",
+  "version": "1.0.0",
+  "lockfileVersion": 1,
+  "requires": true,
+  "dependencies": {
+    "moment": {
+      "version": "2.29.3",
+      "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.3.tgz",
+      "integrity": "sha512-c6YRvhEo//6T2Jz/vVtYzqBzwvPT95JBQ+smCytzf7c50oMZRsR/a4w88aD34I+/QVSfnoAnSBFPJHItlOMJVw=="
+    },
+    "uview-ui": {
+      "version": "1.8.6",
+      "resolved": "https://registry.npmjs.org/uview-ui/-/uview-ui-1.8.6.tgz",
+      "integrity": "sha512-R93WU6htCP2Fpw668ABpF7Y8UqIx66pUKGAwBYLDkbnIldyKWkjtfJzqjFm22tF41ty6FVB+82L+gIL775c5LQ=="
+    }
+  }
+}

+ 15 - 0
package.json

@@ -0,0 +1,15 @@
+{
+  "name": "digitalshelf-mini-html",
+  "version": "1.0.0",
+  "description": "",
+  "main": "main.js",
+  "dependencies": {
+    "moment": "^2.29.1",
+    "uview-ui": "^1.8.4"
+  },
+  "scripts": {
+    "test": "echo \"Error: no test specified\" && exit 1"
+  },
+  "author": "",
+  "license": "ISC"
+}

+ 105 - 0
pages.json

@@ -0,0 +1,105 @@
+{
+	"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"
+			}
+		},
+		{
+			"path": "pages/personCenter/personCenter",
+			"style": {
+				"navigationBarTitleText": "我的",
+				"navigationBarBackgroundColor": "#86defa"
+			}
+		},
+		{
+			"path": "pages/login/login",
+			"style": {
+				"navigationBarTitleText": "登录"
+			}
+		}, {
+			"path": "pages/login/phoneLogin",
+			"style": {
+				"navigationBarTitleText": "手机号登录"
+			}
+		},
+		{
+			"path": "pages/about",
+			"style": {
+				"navigationBarTitleText": "关于我们"
+			}
+		},
+		{
+			"path": "pages/xieyi/index",
+			"style": {
+				"navigationBarTitleText": "用户协议"
+			}
+		},
+		{
+			"path": "pages/h5Page/index",
+			"style": {
+				"navigationBarTitleText": "车领主"
+			}
+		},
+		{
+			"path": "pages/personData/personData",
+			"style": {
+				"navigationBarTitleText": "个人信息"
+			}
+		},
+		{
+			"path": "pages/scan-frame/scan-frame",
+			"style": {
+				"navigationBarTitleText": "扫描VIN"
+			}
+		}
+	    ,{
+            "path" : "pages/vinInput/vinInput",
+            "style" :                                                                                    
+            {
+                "navigationBarTitleText": "手动输入VIN",
+                "enablePullDownRefresh": false
+            }
+            
+        }
+        ,{
+            "path" : "pages/vinInput/confirmVin",
+            "style" :                                                                                    
+            {
+                "navigationBarTitleText": "确认VIN",
+                "enablePullDownRefresh": false
+            }
+            
+        }
+    ],
+	"globalStyle": {
+		"navigationBarTextStyle": "black",
+		"navigationBarTitleText": "uni-app",
+		"navigationBarBackgroundColor": "#F8F8F8",
+		"backgroundColor": "#F8F8F8"
+	},
+	"tabBar": {
+		"color": "#7A7E83",
+		"selectedColor": "#191919",
+		"backgroundColor": "#ffffff",
+		"fontSize": "14px",
+		"list": [{
+				"pagePath": "pages/index/index",
+				"iconPath": "static/tab/tab_home_normal.png",
+				"selectedIconPath": "static/tab/tab_home_pressed.png",
+				"text": "首页"
+			},
+			{
+				"pagePath": "pages/personCenter/personCenter",
+				"iconPath": "static/tab/tab_personal_normal.png",
+				"selectedIconPath": "static/tab/tab_personal_pressed.png",
+				"text": "我的"
+			}
+		]
+	}
+}

+ 54 - 0
pages/about.vue

@@ -0,0 +1,54 @@
+<template>
+	<view class="aboutUs-content">
+		<view class="aboutUs-content-info">
+			<u-image src="/static/log.png"  width="144" height="144" class="logo"></u-image>
+			<view class="content-title">关于修配一码通</view>
+			<view class="content-version">Version 1.0.0</view>
+		</view>
+		<view>陕西山海高科信息技术有限公司专注于汽车服务行业,是一家以先进云端互联网技术驱动的公司,致力于通过saas云门店管理系统为汽车综修厂、社区店以及大量的中小门店赋能。以解决汽车维修、美容、保养等业务中发生的一系列问题。</view>
+		<view>修配一码通设计了一套完善的降本增效方案,在引流获客、员工提成、营销活动、财务报表、数据分析等方面获得客户一致认可。</view>
+		<view>陕西山海高科信息技术有限公司将通过持续的科技成果转化为推动汽后市场发展提供动力,帮助更多服务终端、供应链企业、主机厂商、银行以及保险等汽车后市场参与方转型升级。</view>
+	</view>
+</template>
+
+<script>
+</script>
+
+<style lang="less">
+	.aboutUs-content{
+		width: 100%;
+		height: 100vh;
+		padding: 0 60upx;
+		> view{
+			margin-bottom: 30upx;
+			line-height: 50upx;
+		}
+		.aboutUs-content-info{
+			text-align: center;
+			.logo{
+				display: inline-block;
+				margin: 100upx auto 60upx;
+				// text-align: center;
+			}
+			.content-title{
+				font-size: 38upx;
+				// text-align: center;
+			}
+			.content-version{
+				// text-align: center;
+				font-size: 32upx;
+				color:#666E75;
+				margin: 10upx 0 80upx;
+			}
+		}
+		.content-text{
+			color:#666E75;
+			// font-size: 28upx;
+			margin-bottom: 50upx;
+			text-indent:2em
+		}
+		// .last-text{
+		// 	margin-bottom: 0;
+		// }
+	}
+</style>

+ 40 - 0
pages/h5Page/index.vue

@@ -0,0 +1,40 @@
+<template>
+   <web-view :src="src" bindload='pageLoad'></web-view>
+</template>
+
+<script>
+export default {
+  name: 'h5Page',
+  props: {
+    src: {
+      type: [String]
+    }
+  },
+  methods: {
+    pageLoad (){
+       uni.hideLoading()
+    }
+  },
+  onHide() {
+  },
+  onUnload() {
+  },
+  onShow() {
+  },
+  onLoad(options) {
+     uni.showLoading({
+       title: '加载中',
+     })
+     console.log(options.src)
+     this.src = options.src
+
+     setTimeout(function() {
+       uni.hideLoading()
+     }, 5000);
+  }
+};
+</script>
+
+<style lang="stylus">
+
+</style>

+ 184 - 0
pages/index/index.vue

@@ -0,0 +1,184 @@
+<template>
+	<view class="content">
+		<view class="header-box">
+			<view class="status_bar"></view>
+			<view class="title"><text>管配件,就用</text>修配一码通</view>
+		</view>
+		<view class="body-box">
+			<!-- 滚动区域 -->
+			<view class="scroll-list">
+				<scrollBox :list="list"></scrollBox>
+			</view>
+			<!-- 保证金 -->
+			<view class="notices flex align_center">
+				<view><u-icon name="volume" color="#f2a557" :size="28" :margin-right='15'></u-icon> 您有一笔保证金待支付 ¥2,000.00</view> <text>立即支付>></text>
+			</view>
+			<!-- 名片 -->
+			<view class="userCard flex align_center justify_between">
+				<view class="userCard-info flex align_center justify_between">
+					<view>
+						<u-image v-if="!hasLogin" src="/static/def_personal_avatar.png" width="90" height="90"></u-image>
+						<open-data v-else type="userAvatarUrl" class="user-photo"></open-data>
+					</view>
+					<view>
+						<view>王杰</view>
+						<view>宇博极速汽车维修养护中心</view>
+					</view>
+				</view>
+				<view>
+					<u-icon name="arrow-right"></u-icon>
+				</view>
+			</view>
+			<!-- 扫描按钮 -->
+			<view class="scanButton flex align_center" @click="openCamera">
+				<u-icon name="scan" size="40"></u-icon><text class="ml10">扫描VIN</text>
+			</view>
+			<!-- 扫描记录 -->
+			<u-cell-group :border="false">
+				<u-cell-item title="扫描记录" :title-style="{fontSize:'1.1em'}" value="查看全部">
+					<u-icon slot="icon" size="38" color="#00aaff" style="margin-right: 0.3em;" name="order"></u-icon>
+				</u-cell-item>
+			</u-cell-group>
+		</view>
+		<!-- 扫描记录 -->
+		<view class="list-box">
+			<u-cell-group :border="false">
+				<u-cell-item v-for="item in 10" title="LBVNU79049SB81489" :title-style="{fontSize:'1em'}" value="宝马 54646d">
+					<text slot="icon"></text>
+				</u-cell-item>
+			</u-cell-group>
+			<view class="des">仅展示最近10条记录,点击“全部”查看更多</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import {
+	    mapState,
+	    mapMutations
+	} from 'vuex'
+	import scrollBox from '@/components/scrollBox.vue'
+	export default {
+		components: {
+			scrollBox
+		},
+		data() {
+			return {
+				list:[
+				{
+					userName: '一部'
+				},
+				{
+					userName: '夏利我'
+				},
+				{
+					userName: '离散'
+				},
+				{
+					userName: '网络'
+				},
+				{
+					userName: '撒打发'
+				},
+				{
+					userName: 'ipho23'
+				}
+			]
+			}
+		},
+		computed: {
+			...mapState(['hasLogin'])
+		},
+		onLoad() {
+
+		},
+		methods: {
+			openCamera(){
+				uni.navigateTo({
+					url: "/pages/scan-frame/scan-frame"
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="less">
+	.content {
+		margin: 0;
+		padding: 0;
+		height: 100vh;
+		display: flex;
+		flex-direction: column;
+		background-image: linear-gradient(#86defa 0%,#cdeff9 25%,#ffffff 40%);
+		.header-box{
+			padding: 0.3em 1em;
+			.status_bar {
+				height: var(--status-bar-height);
+				width: 100%;
+			 }
+			 .title{
+				 padding-top: 0.4em;
+				 font-size: 1.5em;
+				 color: #000;
+				 text{
+					 color: #0077fd;
+				 }
+			 }
+		}
+		.body-box{
+			flex-grow: 1;
+			padding: 0.5em 1em;
+			.notices{
+				padding: 0.5em 0 1em;
+				view{
+					color: #f2a557;
+				}
+				text{
+					color: #0077fd;
+					margin-left: 1em;
+				}
+			}
+			.userCard{
+				padding:0.8em;
+				background: #fff;
+				border-radius: 1em;
+				box-shadow: 0.2em 0.3em 0.8em #cdeff9;
+				.userCard-info{
+					> view{
+						&:last-child{
+							margin-left: 0.5em;
+							>view{
+								padding: 0.2em;
+							}
+						}
+					}
+				}
+			}
+			.scanButton{
+				margin: 0.6em 0;
+				padding: 0.5em;
+				background: #1283d4;
+				color: #fff;
+				font-size: 1.5em;
+				border-radius: 5em;
+				justify-content: center;
+				&:active{
+					opacity: 0.8;
+				}
+			}
+			.ml10{
+				margin-left: 0.2em;
+			}
+		}
+		.list-box{
+			padding: 0 1em;
+			overflow: auto;
+			.des{
+				text-align: center;
+				padding: 1em 0;
+				color: #999;
+				font-size: 0.8em;
+			}
+		}
+	}
+</style>

+ 197 - 0
pages/login/login.vue

@@ -0,0 +1,197 @@
+<template>
+	<view class="content">
+		<view class="content-body">
+			<view class="login-logo">
+				<u-image src="/static/logo.png"  width="160" height="160" class="logo"></u-image>
+			</view>
+		    <view class="login-btns">
+				<u-button shape="circle" :custom-style="wxButton" class="login-btn" type="success" :hair-line="false" open-type="getPhoneNumber" @getphonenumber="getPhoneNumber">
+					微信一键登录
+				</u-button>
+				<u-button shape="circle" :custom-style="phoneButton" class="login-btn phoneBtn" @click="phoneLogin" :hoverClass='none'>
+					手机号登录/注册
+				</u-button>
+		    </view>
+		</view>
+		<view class="xieyi" @click="gotoXieyi()">
+			首次登录会自动创建账号且代表同意 <text>《用户协议》</text>及<text>《隐私政策》</text>
+		</view>
+	</view>
+    
+</template>
+
+<script>
+    import service from '../../service.js';
+	import {wxlogin} from '../../api/login.js'
+    import {
+        mapState,
+        mapMutations
+    } from 'vuex'
+     
+    export default {
+        components: {
+        },
+        data() {
+            return {
+				path: '/pages/index/index',
+				lanuch: false,
+				mobile: '',
+				code: '',
+				codeText: '获取验证码',
+				getCodeing: false,
+				totalTime: 60,
+                positionTop: 0,
+				hasFocus: false,
+				hasFocus1: false,
+				wxButton:{
+					background: '#1283d4'
+				},
+				phoneButton:{
+					background: '#ffffff'
+				}
+            }
+        },
+        computed: mapState(['forcedLogin']),
+		mounted() {
+			//微信平台登录
+			//#ifdef MP-WEIXIN
+			 this.$store.dispatch('wxLogin','weixin')
+			//#endif
+		},
+		onLoad(option) {
+			// 为解决首次登录异常问题 进入页面先调微信登录获取code
+			this.wxLogin()
+			const {
+				path,
+				lanuch,
+				q
+			} = option;
+			this.lanuch = (lanuch + '') === 'true';
+			if (path) {
+				this.path = decodeURIComponent(path+"?q="+q);
+			}
+		},
+        methods: {
+			...mapMutations(['login']),
+			// 微信授权登录
+			getPhoneNumber(e) {
+				let _this = this
+				uni.showLoading({
+					mask: true,
+					title: '加载中'
+				});
+				if (!this.code) {
+					this.wxLogin()
+				} 
+				console.log(this.code, e)
+				if (e.target.errMsg === 'getPhoneNumber:ok') {
+					setTimeout(()=>{
+						const params={
+							code: _this.code,
+							encryptedData: e.target.encryptedData,
+							iv: e.target.iv,
+						}
+						wxlogin(params).then(res => {
+							uni.hideLoading();
+							_this.code = ''
+							console.log(res,_this.path, 'login data')
+							if (res.status == '200') {
+								this.toMain(res.data)
+								// _this.$u.vuex('vuex_token',res.data)
+								uni.$emit("getUserInfo")
+								if (_this.path === '/pages/index/index' || _this.lanuch) {
+									uni.reLaunch({
+										url: _this.path
+									});
+								} else {
+									uni.redirectTo({
+										url: _this.path
+									});
+								}
+							} else {
+								uni.showToast({
+									title: res.message,
+									icon: 'none',
+									duration: 2500
+								});
+							}
+						});
+					},300)
+				} else {
+					uni.showToast({
+						title: '授权失败',
+						icon: 'none',
+						duration: 2500
+					});
+				}
+			},
+			// 调微信登录 获取code
+			wxLogin () {
+				let _this = this
+				uni.login({
+					provider: 'weixin',
+					success(res) {
+						console.log(res.code)
+						_this.code = res.code
+					}
+				})
+			},
+			toMain(data) {
+			    this.login({token:data});
+			    uni.reLaunch({
+			        url: '/pages/index/index',
+			    });
+			},
+			// 手机号登录
+			phoneLogin(){
+				wx.navigateTo({
+					url: '/pages/login/phoneLogin'
+				})
+			},
+            initPosition() {
+                /**
+                 * 使用 absolute 定位,并且设置 bottom 值进行定位。软键盘弹出时,底部会因为窗口变化而被顶上来。
+                 * 反向使用 top 进行定位,可以避免此问题。
+                 */
+                this.positionTop = uni.getSystemInfoSync().windowHeight - 100;
+            },
+			gotoXieyi(url){
+				uni.navigateTo({
+					url: '/pagesMe/xieyi/index?id=0'
+				})
+			}
+        },
+        onReady() {
+            this.initPosition();
+        }
+    }
+</script>
+
+<style lang="less">
+	.content{
+		width: 100%;
+		height: 100vh;
+		padding:0 125upx;
+		display: flex;
+		flex-direction: column;
+		text-align: center;
+		.content-body{
+			flex: 1;
+			.logo{
+				display: inline-block;
+				margin:250upx 0 120upx;
+			}
+			.phoneBtn{
+				display: block;
+				margin: 30rpx 0 350upx;
+			}
+		}
+		.xieyi{
+			font-size: 24upx;
+			padding-bottom: 60upx;
+			text{
+				color:#1283d4
+			}
+		}
+	}
+</style>

+ 243 - 0
pages/login/phoneLogin.vue

@@ -0,0 +1,243 @@
+<template>
+	<view class="phone-login">
+		<view class="login-content">
+			<view class="login-logo">
+				<u-image src="/static/logo.png" width="160" height="160" class="logo"></u-image>
+			</view>
+			<u-form ref="uForm">
+				<u-form-item>
+					<u-input v-model.trim="mobile" placeholder="请输入手机号" type="number" maxlength='11' />
+				</u-form-item>
+				<u-form-item>
+					<u-input placeholder="请输入验证码" maxlength='4' type="number" v-model.trim="code" />
+					<u-button slot="right" class="yzmBtn" :custom-style="yzmBtn" shape="circle" :hair-line="false" size="mini"
+						@click="checkMobile" :disabled="isDisabled">{{codeText}}</u-button>
+				</u-form-item>
+				<u-form-item class="form-btn" :border-bottom="false">
+					<u-button :custom-style="submitBtn" type="warring" form-type="submit" shape="circle" :hair-line="false"
+						@click="bindLogin">登录</u-button>
+				</u-form-item>
+			</u-form>
+		</view>
+
+		<view class="xieyi" @click="gotoXieyi()">
+			首次登录会自动创建账号且代表同意 <text>《用户协议》</text>及<text>《隐私政策》</text>
+		</view>
+		<!-- 图文验证码 -->
+		<uni-popup @change="changeModal" ref="imageTxtPopup" type="center">
+			<popup-con title="图文验证码" :popBtn="popBtn" :changeImg="changeImg" @captchaImg="captchaVerify"></popup-con>
+		</uni-popup>
+	</view>
+</template>
+
+<script>
+	import service from '../../service.js';
+	import {
+		code2Session,
+		login,
+		sendLoginVerifyCode
+	} from '../../api/login.js'
+	import {
+		mapState,
+		mapMutations
+	} from 'vuex'
+	import uniPopup from '@/components/uni-popup/uni-popup.vue'
+	import popupCon from '@/components/uni-popup/popup-con.vue'
+	import {
+		isvalidPhone
+	} from '@/libs/validate.js'
+	export default {
+		components: {
+			uniPopup,
+			popupCon
+		},
+		data() {
+			return {
+				mobile: '', // 手机号码
+				code: '', // 短信验证码
+				popBtn: [], //  name为操作按钮名称,color为操作按钮颜色值
+				changeImg: false, //  是否重新更换图形验证码
+				randomCode: '', //  图片验证码随机码
+				codeText: '获取验证码',
+				getCodeing: false,
+				totalTime: 60,
+				yzmBtn: {
+					background: "#EFEFEF"
+				},
+				submitBtn: {
+					background: "#1283d4",
+					marginTop: '15px',
+					color: '#ffffff'
+				},
+				isDisabled: false // 倒计时按钮是否禁用
+			}
+		},
+		computed: mapState(['forcedLogin']),
+		mounted() {
+			//微信平台登录
+			//#ifdef MP-WEIXIN
+			  this.$store.dispatch('wxLogin', 'weixin')
+			//#endif
+		},
+		methods: {
+			...mapMutations(['login']),
+			// 获取验证码
+			getCode() {
+				let sid = null
+				sid = setInterval(() => {
+					if (this.totalTime > 0) {
+						this.totalTime = this.totalTime - 1
+						this.codeText = '已发送' + this.totalTime + 's'
+						if (this.totalTime < 60) {
+							this.isDisabled = true
+						}
+					} else {
+						this.isDisabled = false
+						clearInterval(sid)
+						this.getCodeing = false
+						this.codeText = '获取验证码'
+						this.totalTime = 60
+					}
+				}, 1000)
+			},
+			// 验证手机
+			checkMobile() {
+				if (!this.getCodeing) {
+					if (this.mobile) {
+						if (!isvalidPhone(this.mobile)) {
+							uni.showToast({
+								title: '请输入正确的手机号码',
+								icon: 'none'
+							})
+							return false
+						} else {
+							this.getCodeing = true
+							this.retsetCode()
+							//  图文验证码
+							this.$refs.imageTxtPopup.open()
+						}
+					}
+				}
+			},
+			changeModal(val) {
+				if (val.show == false) {
+					this.getCodeing = false
+				}
+			},
+			//  验证图片验证码
+			captchaVerify(code, nowRandomCode) {
+				const _this = this
+				let obj = {}
+				obj.mobile = this.mobile
+				obj.random = nowRandomCode
+				obj.code = code
+				obj.openid = this.$store.state.openid
+				//  发送短信验证码
+				sendLoginVerifyCode(obj).then(res => {
+					console.log(JSON.stringify(res.data))
+					if (res.status == 200) { //  验证码输入正确
+						_this.randomCode = nowRandomCode //  图片验证码随机码
+						//  关闭   图形验证码 弹框
+						_this.$refs.imageTxtPopup.close()
+						//  开启倒计时
+						_this.getCode()
+						uni.showToast({
+							icon: 'none',
+							title: '验证码发送成功'
+						})
+					} else { //  验证码输入错误
+						_this.retsetCode()
+						uni.showToast({
+							icon: 'none',
+							title: res.message
+						})
+					}
+				})
+			},
+			// 重新触发获取图片验证码
+			retsetCode() {
+				const _this = this
+				_this.code = ''
+				_this.randomCode = ''
+				//  切换验证码重新校验
+				_this.changeImg = false
+				_this.$nextTick(function() {
+					_this.changeImg = true
+				})
+			},
+			// 登录
+			bindLogin() {
+				let mobile = this.mobile
+				let openid = this.$store.state.openid
+				let code = this.code
+				let random = this.randomCode
+				let data = {
+					'mobile': mobile,
+					'code': code,
+					'openid': openid,
+					'random': random
+				}
+				login(data).then(res => {
+					console.log(res)
+					if (res.status == 200) {
+						this.toMain(res.data)
+					} else {
+						uni.showToast({
+							title: res.message,
+							icon: 'none'
+						})
+					}
+				})
+			},
+			toMain(data) {
+				this.login(data);
+				uni.reLaunch({
+					url: '/pages/index/index',
+				});
+			},
+			// 用户协议
+			gotoXieyi(url) {
+				uni.navigateTo({
+					url: '/pagesMe/xieyi/index?id=0'
+				})
+			},
+		},
+		watch: {
+			mobile(val) {
+				this.mobile = val
+			}
+		}
+	}
+</script>
+<style lang="scss">
+	.phone-login {
+		display: flex;
+		flex-direction: column;
+		width: 100%;
+		height: 100vh;
+		padding: 0 125upx;
+
+		.login-content {
+			flex: 1;
+
+			.login-logo {
+				text-align: center;
+
+				.logo {
+					display: inline-block;
+					margin: 200upx 0 100upx;
+				}
+			}
+		}
+
+		.xieyi {
+			font-size: 24upx;
+			text-align: center;
+			padding-bottom: 60upx;
+
+			text {
+				color: #1283d4
+			}
+		}
+	}
+</style>

+ 230 - 0
pages/personCenter/personCenter.vue

@@ -0,0 +1,230 @@
+<template>
+    <view class="content">
+        <view class="header">
+        	<view @click="toLoginPage" class="user-head">
+				<view>
+					<u-image v-if="!hasLogin" src="/static/def_personal_avatar.png" width="120" height="120"></u-image>
+					<open-data v-else type="userAvatarUrl" class="user-photo"></open-data>
+				</view>
+				<view class="user-info">
+					<open-data class="user-info-item" v-if="hasLogin" type="userNickName"></open-data>
+					<view class="user-info-item" v-if="hasLogin">{{userInfo.mobile}}</view>
+					<view v-if="!hasLogin"  class="user-info-item">
+						请点击登录/注册
+					</view>
+				</view>
+				<view>
+					<u-icon class="back-img" name="icon_more_little" color="#424D5E" size="32" custom-prefix="custom-icon"></u-icon>
+				</view>
+        	</view>
+        </view>
+		<view class="list-container">
+			<view class="list-item flex align_center justify_between" @click="callPhone">
+				<view class="flex align_center">
+					<u-icon class="icon" name="personal_icon_serve" custom-prefix="custom-icon" size="48" color="#515151"></u-icon>
+					<text class="container-item">联系客服</text>
+				</view>
+				<u-icon name="icon_more_little" color="#9DA8B5" size="32" custom-prefix="custom-icon"></u-icon>
+			</view>
+			<navigator url="/pages/xieyi/index" class="list-item margin-bot flex align_center justify_between">
+				<view class="flex align_center">
+					<u-icon class="icon" name="personal_icon_agreement" custom-prefix="custom-icon" size="48" color="#515151"></u-icon>
+					<text class="container-item">用户协议</text>
+				</view>
+				<u-icon name="icon_more_little" color="#9DA8B5" size="32" custom-prefix="custom-icon"></u-icon>
+			</navigator>
+			<navigator url="/pages/about" class="list-item margin-bot flex align_center justify_between">
+				<view class="flex align_center">
+					<u-icon class="icon" name="personal_icon_about" custom-prefix="custom-icon" size="48" color="#515151"></u-icon>
+					<text class="container-item">关于我们</text>
+				</view>
+				<u-icon name="icon_more_little" color="#9DA8B5" size="32" custom-prefix="custom-icon"></u-icon>
+			</navigator>
+		</view>
+    </view>
+</template>
+
+<script>
+    import service from '../../service.js';
+	import mIcon from '../../components/m-icon/m-icon.vue'
+    import {
+        mapState,
+        mapMutations
+    } from 'vuex'
+     
+    export default {
+        components: {
+			mIcon
+        },
+        data() {
+            return {
+				userInfo:'',
+            }
+        },
+		onLoad() {
+			let UserInfo = uni.getStorageSync('userInfo');
+			this.userInfo = UserInfo;
+			// 开启分享
+			uni.showShareMenu({
+				withShareTicket: true,
+				menus: ['shareAppMessage', 'shareTimeline']
+			})
+		},
+		onShow() {
+			//检测是否登录
+			if(this.hasLogin) {
+			}else{
+				this.serverCount=0
+				this.count = 0
+				this.pacrkingCount = 0
+			}
+		},
+		computed: {
+			...mapState(['hasLogin'])
+		},
+        methods: {
+			// 联系客服
+			callPhone(){
+			  uni.makePhoneCall({
+				phoneNumber: '400-1616-312'
+			  });
+			},
+			toLoginPage(){
+			  let url = this.hasLogin ? '/pages/personData/personData' : '/pages/login/login'
+				uni.navigateTo({
+					url: url
+				})
+			}
+        },
+		
+    }
+</script>
+
+<style lang="scss">
+	.content{
+		padding: 0;
+		display: flex;
+		flex-direction: column;
+		align-items: center;
+		background: rgb(245,245,245);
+	}
+	// 顶部
+	.header{
+		height: auto;
+		width: 100%;
+		background-image: linear-gradient(#86defa,#ffffff);
+		padding: 0rpx 32rpx;
+		.user-head{
+			display: flex;
+			align-items: center;
+			width: 100%;
+			height: 200rpx;
+			.user-info{
+				flex-grow: 1;
+				padding: 0 20rpx;
+				color: #FFFFFF;
+				.user-info-item{
+					margin: 10rpx 0;
+					color: #666666;
+					font-size: 32upx;
+				}
+				:first-child{
+					color: #000000;
+					font-size: 48upx;
+				}
+			}
+			.user-photo{
+				display: block;
+				width: 120rpx;
+				height: 120rpx;
+				border-radius: 50%;
+				overflow: hidden;
+			}
+		}
+		.check{
+			height: 126rpx;
+			background-color: #29467D;
+			border-radius: 20rpx 20rpx 0rpx 0rpx;
+			color: #FFE196;
+			padding: 0rpx 24rpx;
+			font-size: 32rpx;
+			.padding-9{
+				padding: 0 18rpx;
+			}
+			.check-item{
+				font-size: 24rpx;
+				color: #CCB57C;
+				margin-top: 6rpx;
+			}
+			.color{
+				color: #CCB57C;
+			}
+		}
+	}
+	.tabs-cont{
+		width: 100%;
+		background-color: #fff;
+		padding: 28rpx 0rpx;
+		justify-content: space-around;
+		margin-bottom: 20rpx;
+		text{
+			font-size: 26rpx;
+			color: #222222;
+			margin-top: 10rpx;
+		}
+		.cart-img{
+			position: relative;
+		}
+	}
+	.list-container{
+		width: 100%;
+		.list-item{
+			padding: 32rpx 36rpx;
+			background-color: #fff;
+			.container-item{
+				font-size: 34rpx;
+				color: #191919;
+				margin-left: 30rpx;
+			}
+		}
+		.margin-bot{
+			margin-bottom: 20rpx;
+		}
+		// .user-list{
+		// 	border-radius: 15upx;
+		// 	margin: 20upx;
+		// 	overflow: hidden;
+		// 	box-shadow: 0upx 3upx 6upx #eee;
+		// 	.quit{
+		// 		margin-top: 20upx;
+		// 	}
+		// }
+		// .border-bottom{
+		// 	border-bottom: 1px solid rgb(228,228,228);
+		// }
+		// .list-item{
+		// 	width: 100%;
+		// 	height:110rpx ;
+		// 	padding:0 30rpx;
+		// 	box-sizing:border-box;
+		// 	display: flex;
+		// 	justify-content: space-between;
+		// 	align-items:center;
+		// 	.list-item-left{
+		// 		display:flex;
+		// 		font-size: 36rpx;					
+		// 		color: rgb(40,40,40);				
+		// 		.icon-box{
+		// 			width:40rpx;
+		// 			margin-right: 20rpx;
+					
+		// 		}
+				
+		// 	}
+		// 	.back-img{
+		// 		width: 16rpx;
+		// 		height: 22rpx;
+		// 	}
+		// }
+	}
+</style>

+ 176 - 0
pages/personData/personData.vue

@@ -0,0 +1,176 @@
+<template>
+    <view class="content">
+       
+		<view class="list-container">
+			<view class="list-item margin-bot">
+				<view class="list-item-left">
+			 		<text>头像</text>
+				</view>
+                <view class="list-item-right">
+					<open-data class="user-photo" type="userAvatarUrl"></open-data>
+					<!-- <u-icon class="back-img" name="arrow-right" color="#9DA8B5"></u-icon> -->
+                </view>
+			</view>
+			<view class="list-item border-bottom">
+				<view class="list-item-left">
+					<text>名称</text>
+				</view>
+			    <view class="list-item-right">
+					<open-data type="userNickName"></open-data>
+					<!-- <u-icon class="back-img" name="arrow-right" color="#9DA8B5"></u-icon> -->
+			    </view>
+			</view>
+			
+			<view class="list-item margin-bot">
+				<view class="list-item-left">
+					<text>性别</text>
+				</view>
+			    <view class="list-item-right">
+			    	<open-data type="userGender" lang="zh_CN"></open-data>
+					<!-- <u-icon class="back-img" name="arrow-right" color="#9DA8B5"></u-icon> -->
+			    </view>
+			</view>
+			
+			<view class="list-item ">
+				<view class="list-item-left">
+					<text>联系电话</text>
+				</view>
+			    <view class="list-item-right">
+			    	<text>{{userInfo.mobile}}</text>
+					<!-- <u-icon class="back-img" name="arrow-right" color="#9DA8B5"></u-icon> -->
+			    </view>
+			</view>
+		</view>
+		<view class="bottom-cont" @click="loginOut">
+			退出登录
+		</view>
+		<uni-popup ref="openModal" type="center">
+			<uni-popup-dialog content="确定退出登录吗?" @confirm="onOk" :title="title"></uni-popup-dialog>
+		</uni-popup>
+    </view>
+</template>
+
+<script>
+	import uniPopup from '@/components/uni-popup/uni-popup.vue'
+	import uniPopupDialog from '@/components/uni-popup/uni-popup-dialog.vue'
+    import service from '../../service.js';
+	import mIcon from '../../components/m-icon/m-icon.vue'
+    import {
+        mapState,
+        mapMutations
+    } from 'vuex'
+    export default {
+        components: {
+			mIcon,uniPopup,uniPopupDialog
+        },
+        data() {
+            return {
+				userInfo:'',
+				title:'',
+            }
+        },
+		onShow() {
+			//检测是否登录
+			this.$store.dispatch('checkLogin')
+		},
+		computed: {
+			...mapState(['hasLogin'])
+		},
+        methods: {
+          loginOut(){
+			  this.$refs.openModal.open()
+			  // this.$store.dispatch('userLogout');
+			  // setTimeout(function(){
+				 //  uni.redirectTo({
+				 //      url: '/pages/login/login'
+				 //  });
+			  // },300)
+		  },
+		  onOk(){
+			this.$refs.openModal.close()
+			this.$store.dispatch('userLogout');
+			setTimeout(function(){
+				  uni.redirectTo({
+					  url: '/pages/login/login'
+				  });
+			},300)
+		  }
+        },
+		onLoad() {
+			let UserInfo = uni.getStorageSync('userInfo');
+			this.userInfo = UserInfo;
+			console.log(UserInfo,"UserInfo")
+			// 开启分享
+			uni.showShareMenu({
+				withShareTicket: true,
+				menus: ['shareAppMessage', 'shareTimeline']
+			})
+		},
+        onReady() {
+            
+        }
+    }
+</script>
+
+<style lang="less" scoped>
+	.content{
+		padding: 0;
+		background: rgb(245,245,245);
+	}
+	
+	.list-container{
+		width: 100%;
+		box-sizing:border-box;
+		.border-bottom{
+			padding-left: 32rpx;
+			border-bottom: 1px solid #E5E5E5;
+		}
+		.margin-bot{
+			margin-bottom: 20rpx;
+		}
+		.list-item{
+			width: 100%;
+			background-color: #fff;
+			display: flex;
+			padding: 30rpx 32rpx;
+			justify-content: space-between;
+			align-items:center;
+			// border-bottom: 1px solid #E5E5E5;
+			.user-photo{
+				display: block;
+				width: 100rpx;
+				height: 100rpx;
+				border-radius: 50%;
+				overflow: hidden;
+			}
+			.list-item-left{
+				font-size: 36rpx;
+				color: #191919;
+			}
+			.list-item-right{
+				font-size: 36rpx;
+				color: #666666;
+				display: flex;
+				align-items: center;
+				.back-img{
+					width: 18rpx;
+					height: 34rpx;
+					margin-left: 20rpx;
+				}
+			}
+			
+		}
+	}
+	.bottom-cont{
+		position: fixed;
+		bottom: 0;
+		width: 100%;
+		background-color: #fff;
+		height: 110rpx;
+		font-size: 36rpx;
+		color: #999999;
+		text-align: center;
+		line-height: 110rpx;
+	}
+		
+</style>

+ 223 - 0
pages/scan-frame/scan-frame.vue

@@ -0,0 +1,223 @@
+<template>
+  <view class="cameraBg">
+	<canvas style="width: 100%; height: 100vh;position: absolute;left: 100000px;" canvas-id="canvas" id="canvas"></canvas>
+    <camera device-position="back" flash="auto" style="width: 100%; height: 100vh">
+      <cover-image src="@/static/scan-frame/cameraBg.png" class="scan-img"> </cover-image>
+      <cover-view class="scanBtn" v-if="scanShow">
+        <cover-view class="beat" @click="scan">
+          <cover-image class="beatImg" src="@/static/scan-frame/album.png"></cover-image>
+          <cover-view> 相册 </cover-view>
+        </cover-view>
+        <cover-view class="album" @click="takePhoto">
+          <cover-image class="albumImg" src="@/static/scan-frame/beat.png"></cover-image>
+          <cover-view> 拍照 </cover-view>
+        </cover-view>
+		<cover-view class="inputs" @click="inputs">
+		  <cover-image class="beatImg" src="@/static/scan-frame/inputs.png"></cover-image>
+		  <cover-view> 手动输入 </cover-view>
+		</cover-view>
+      </cover-view>
+    </camera>
+  </view>
+</template>
+
+<script>
+ 
+export default {
+  data() {
+    return {
+      scanShow: true,
+	  sysinfo: null,
+	  imageInfo: null
+    }
+  },
+  onReady() {
+  	const sysinfo = uni.getSystemInfoSync()
+	this.sysinfo = sysinfo
+	console.log(sysinfo)
+  },
+  methods: {
+	// 手动输入
+	inputs(){
+		uni.redirectTo({
+			url:"/pages/vinInput/vinInput"
+		})
+	},
+    // 相册
+    scan() {
+      // 选择图片
+      uni.chooseImage({
+        count: 1,
+        sizeType: ['original', 'compressed'],
+        sourceType: ['album'],
+        success: (res) => {
+          this.compress(res.tempFilePaths[0],false)
+        }
+      })
+    },
+	// 裁剪图片
+	cutImg(fileUrl){
+		const vm = this
+		const ctx = uni.createCanvasContext('canvas')
+		ctx.drawImage(fileUrl,0,0,this.sysinfo.windowWidth,this.sysinfo.windowHeight)
+		ctx.draw(true,()=>{
+			const cutW = 650
+			const cutH = 160
+			const cutT = 400
+			const cutL = 50
+			const canvasW=cutW/750*this.sysinfo.windowWidth; 
+			const canvasH=cutH/1624*this.sysinfo.windowHeight; 
+			const canvasL =cutL/750*this.sysinfo.windowWidth; 
+			const canvasT= cutT/1624*this.sysinfo.windowHeight; 
+			uni.canvasToTempFilePath({
+			  x: canvasL,
+			  y: canvasT,
+			  width: canvasW,
+			  height: canvasH,
+			  canvasId: 'canvas',
+			  success: function(res) {
+			    // 在H5平台下,tempFilePath 为 base64
+				vm.camera64(res.tempFilePath)
+			  } 
+			})
+		})
+	},
+    // 启动图片压缩
+    compress(tempFilePaths,isCut) {
+      const vm = this
+      vm.scanShow = false
+      uni.showLoading({
+        title: '智能识别中...'
+      })
+      uni.compressImage({
+        src: tempFilePaths,
+        quality: 100,
+        success: (imageRes) => {
+          // 获取类型
+          uni.getImageInfo({
+            src: imageRes.tempFilePath,
+            success(imageInfo) {
+			  console.log(imageInfo)
+			  vm.imageInfo = imageInfo
+			  if(isCut){
+				  // 裁剪图片
+				  vm.cutImg(imageRes.tempFilePath)
+			  }else{
+				  vm.camera64(imageRes.tempFilePath)
+			  }
+            }
+          })
+        }
+      })
+    },
+    // 拿到图片开始进行识别
+    camera64(filePath) {
+		console.log(filePath)
+		uni.getFileSystemManager().readFile({
+		  filePath: filePath,
+		  encoding: 'base64',
+		  success: (base) => {
+			// 返回base64格式
+			const base64Str = 'data:image/' + this.imageInfo.type + ';base64,' + base.data
+			// 拿到base64,不需要base64  就把上层的转换去掉
+			this.scanShow = true
+			uni.hideLoading()
+			uni.showToast({
+				title: '已识别到图片,看console',
+				duration: 2000
+			});
+			console.log(base64Str,'base64Str图片')
+			// 此处为后端接口 传base64图片 进行ocr识别
+		  },
+		  fail: (err) => {
+			console.log(err)
+		  }
+		})
+    },
+    // 拍照
+    takePhoto() {
+      const ctx = uni.createCameraContext()
+      ctx.takePhoto({
+        quality: 'high',
+        success: (res) => {
+          this.compress(res.tempImagePath,true)
+        }
+      })
+    },
+    error(e) {
+      console.log(e.detail)
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.cameraBg {
+  width: 100%;
+  height: 100vh;
+  position: fixed;
+  .scan-img {
+    width: 100%;
+    height: 100vh;
+    z-index: 1;
+  }
+  .scanBtn {
+    width: 100%;
+    z-index: 99999;
+    position: fixed;
+    bottom: 100rpx;
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+    align-items: center;
+    .beat {
+      position: absolute;
+      bottom: 0rpx;
+      left: 100rpx;
+      display: flex;
+      flex-direction: column;
+      justify-content: center;
+      align-items: center;
+      font-size: 24rpx;
+      font-weight: 400;
+      color: #ffffff;
+      .beatImg {
+        width: 88rpx;
+        height: 88rpx;
+        margin-bottom: 30rpx;
+      }
+    }
+	.inputs{
+		position: absolute;
+		bottom: 0rpx;
+		right: 100rpx;
+		display: flex;
+		flex-direction: column;
+		justify-content: center;
+		align-items: center;
+		font-size: 24rpx;
+		font-weight: 400;
+		color: #ffffff;
+		.beatImg {
+		  width: 88rpx;
+		  height: 88rpx;
+		  margin-bottom: 30rpx;
+		}
+	}
+    .album {
+      display: flex;
+      flex-direction: column;
+      justify-content: center;
+      align-items: center;
+      font-size: 24rpx;
+      font-weight: 400;
+      color: #ffffff;
+      .albumImg {
+        width: 120rpx;
+        height: 120rpx;
+        margin-bottom: 30rpx;
+      }
+    }
+  }
+}
+</style>

+ 22 - 0
pages/user/user.vue

@@ -0,0 +1,22 @@
+<template>
+	<view class="user">
+		会员
+	</view>
+</template>
+
+<script>
+export default {
+	data() {
+		return {
+		};
+	},
+	components: {
+	},
+	onLoad() {},
+	methods: {
+	}
+};
+</script>
+
+<style>
+</style>

+ 53 - 0
pages/vinInput/confirmVin.vue

@@ -0,0 +1,53 @@
+<template>
+	<view style="width: 100%;">
+		<view class="confirm-text">
+			<view>请核对VIN识别是否正确?</view>
+			<view>如果有误,请修改</view>
+		</view>
+		<view class="vin-img">
+			<u-image src="vinImg"></u-image>
+		</view>
+		<xt-verify-code v-model="verifyCode" inputType='text' size="17" @confirm="confirm"></xt-verify-code>
+		<view class="confirm-button">
+			<u-button @click="ok" type="primary" shape="circle">确认输入</u-button>
+		</view>
+		<view class="share-list">
+			
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				verifyCode: '',
+				vinImg: ''
+			}
+		},
+		methods: {
+			confirm(code){
+				console.log(code)
+				this.verifyCode = code
+			},
+			ok(){
+				if(this.verifyCode==''){
+					uni.showToast({
+						title: "请输入VIN码",
+						icon:"none"
+					})
+				}else{
+					uni.navigateTo({
+						url: "/pages/vinInput/confirmVin"
+					})
+				}
+			}
+		}
+	}
+</script>
+
+<style lang="less">
+.confirm-button{
+	padding: 50rpx;
+}
+</style>

+ 42 - 0
pages/vinInput/vinInput.vue

@@ -0,0 +1,42 @@
+<template>
+	<view style="width: 100%;">
+		<xt-verify-code v-model="verifyCode" inputType='text' size="17" @confirm="confirm"></xt-verify-code>
+		<view class="confirm-button">
+			<u-button @click="ok" type="primary" shape="circle">确认输入</u-button>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				verifyCode: ''
+			}
+		},
+		methods: {
+			confirm(code){
+				console.log(code)
+				this.verifyCode = code
+			},
+			ok(){
+				if(this.verifyCode==''){
+					uni.showToast({
+						title: "请输入VIN码",
+						icon:"none"
+					})
+				}else{
+					uni.redirectTo({
+						url: "/pages/vinInput/confirmVin"
+					})
+				}
+			}
+		}
+	}
+</script>
+
+<style lang="less">
+.confirm-button{
+	padding: 50rpx;
+}
+</style>

+ 75 - 0
pages/xieyi/index.vue

@@ -0,0 +1,75 @@
+<template>
+	<view class="article">
+		<view>修配一码通系统尊重并保护所有使用服务用户的个人隐私权。为了给您提供更准确、更有个性化的服务,本应用会按照本隐私权政策的规定使用和披露您的个人信息。但本应用将以高度的勤勉、审慎义务对待这些信息。除本隐私权政策另有规定外,在未征得您事先许可的情况下,本应用不会将这些信息对外披露或向第三方提供。</view>
+		<view>本应用会不时更新本隐私权政策。 您在同意本应用服务使用协议之时,即视为您已经同意本隐私权政策全部内容。本隐私权政策属于本应用服务使用协议不可分割的一部分。</view>
+		<view>
+			<p>适用范围</p>
+			<p>(a) 在您注册本应用帐号时,您根据本应用要求提供的个人注册信息;</p>
+			<p>(b) 在您使用本应用网络服务,或访问本应用平台网页时,本应用自动接收并记录的您的浏览器和计算机上的信息,包括但不限于您的IP地址、浏览器的类型、使用的语言、访问日期和时间、软硬件特征信息及您需求的网页记录等数据;</p>
+			<p>(c) 本应用通过合法途径从商业伙伴处取得的用户个人数据。您了解并同意,以下信息不适用本隐私权政策:(a) 您在使用本应用平台提供的搜索服务时输入的关键字信息;</p>
+			<p>(b) 本应用收集到的您在本应用发布的有关信息数据,包括但不限于参与活动、成交信息及评价详情;</p>
+			<p>(c) 违反法律规定或违反本应用规则行为及本应用已对您采取的措施。</p>
+		</view>
+		<view>
+			<p>信息使用</p>
+			<p>(a) 本应用不会向任何无关第三方提供、出售、出租、分享或交易您的个人信息,除非事先得到您的许可,或该第三方和本应用(含本应用关联公司)单独或共同为您提供服务,且在该服务结束后,其将被禁止访问包括其以前能够访问的所有这些资料。</p>
+			<p>(b) 本应用亦不允许任何第三方以任何手段收集、编辑、出售或者无偿传播您的个人信息。任何本应用平台用户如从事上述活动,一经发现,本应用有权立即终止与该用户的服务协议。</p>
+			<p>(c) 为服务用户的目的,本应用可能通过使用您的个人信息,向您提供您感兴趣的信息,包括但不限于向您发出产品和服务信息,或者与本应用合作伙伴共享信息以便他们向您发送有关其产品和服务的信息(后者需要您的事先同意)。</p>
+		</view>
+		<view>
+			<p>信息披露在如下情况下,本应用将依据您的个人意愿或法律的规定全部或部分的披露您的个人信息:</p>
+			<p>(a) 经您事先同意,向第三方披露;</p>
+			<p>(b) 为提供您所要求的产品和服务,而必须和第三方分享您的个人信息;</p>
+			<p>(c) 根据法律的有关规定,或者行政或司法机构的要求,向第三方或者行政、司法机构披露;</p>
+			<p>(d) 如您出现违反中国有关法律、法规或者本应用服务协议或相关规则的情况,需要向第三方披露;</p>
+			<p>(e) 如您是适格的知识产权投诉人并已提起投诉,应被投诉人要求,向被投诉人披露,以便双方处理可能的权利纠纷;</p>
+			<p>(f) 在本应用平台上创建的某一交易中,如交易任何一方履行或部分履行了交易义务并提出信息披露请求的,本应用有权决定向该用户提供其交易对方的联络方式等必要信息,以促成交易的完成或纠纷的解决。</p>
+			<p>(g) 其它本应用根据法律、法规或者网站政策认为合适的披露。</p>
+		</view>
+		<view><p>信息存储和交换本应用收集的有关您的信息和资料将保存在本应用及(或)其关联公司的服务器上,这些信息和资料可能传送至您所在国家、地区或本应用收集信息和资料所在地的境外并在境外被访问、存储和展示。</p></view>
+		<view>
+			<p>Cookie的使用</p>
+			<p>(a) 在您未拒绝接受cookies的情况下,本应用会在您的计算机上设定或取用cookies ,以便您能登录或使用依赖于cookies的本应用平台服务或功能。本应用使用cookies可为您提供更加周到的个性化服务,包括推广服务。</p>
+			<p>(b) 您有权选择接受或拒绝接受cookies。您可以通过修改浏览器设置的方式拒绝接受cookies。但如果您选择拒绝接受cookies,则您可能无法登录或使用依赖于cookies的本应用网络服务或功能。</p>
+			<p>(c) 通过本应用所设cookies所取得的有关信息,将适用本政策。</p>
+		</view>
+		<view>
+			<p>信息安全</p>
+			<p>(a) 本应用帐号均有安全保护功能,请妥善保管您的用户名及密码信息。本应用将通过对用户密码进行加密等安全措施确保您的信息不丢失,不被滥用和变造。尽管有前述安全措施,但同时也请您注意在信息网络上不存在“完善的安全措施”。</p>
+			<p>(b) 在使用本软件网络服务进行网上交易时,您不可避免的要向交易对方或潜在的交易对方披露自己的个人信息,如联络方式或者邮政地址。请您妥善保护自己的个人信息,仅在必要的情形下向他人提供。如您发现自己的个人信息泄密,请您立即联络本软件客服,以便本软件采取相应措施。</p>
+		</view>
+		<view>
+			<p>本隐私政策的更改</p>
+			<p>(a) 如果决定更改隐私政策,我们会在本政策中、本公司网站中以及我们认为适当的位置发布这些更改,以便您了解我们如何收集、使用您的个人信息,哪些人可以访问这些信息,以及在什么情况下我们会透露这些信息。</p>
+			<p>(b) 本公司保留随时修改本政策的权利,因此请经常查看。如对本政策作出重大更改,本公司会通过网站通知的形式告知。</p>
+		</view>
+    </view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				theme: ''
+			}
+		},
+		onLoad() {
+		}
+	}
+</script>
+
+<style lang="scss">
+	.article{
+		 padding: 35upx 40upx;
+		 line-height: 46upx;
+		 >view{
+			 margin: 30upx 0;
+			 p{
+				 margin: 20upx 0;
+				 &:first-child{
+					 font-weight: bold;
+				 }
+			 }
+		 }
+	}
+</style>

+ 26 - 0
service.js

@@ -0,0 +1,26 @@
+// 管理账号信息
+const USERS_KEY = 'USERS_KEY';
+const STATE_KEY = 'STATE_KEY';
+
+const getUsers = function () {
+    let ret = '';
+    ret = uni.getStorageSync(USERS_KEY);
+    if (!ret) {
+        ret = '[]';
+    }
+    return JSON.parse(ret);
+}
+
+const addUser = function (userInfo) {
+    let users = getUsers();
+    users.push({
+        account: userInfo.account,
+        password: userInfo.password
+    });
+    uni.setStorageSync(USERS_KEY, JSON.stringify(users));
+}
+
+export default {
+    getUsers,
+    addUser
+}

BIN
static/def_personal_avatar.png


File diff suppressed because it is too large
+ 3 - 0
static/iconfont/iconfont.css


BIN
static/logo.png


BIN
static/scan-frame/album.png


BIN
static/scan-frame/beat.png


BIN
static/scan-frame/cameraBg.png


BIN
static/scan-frame/inputs.png


BIN
static/tab/tab_home_normal.png


BIN
static/tab/tab_home_pressed.png


BIN
static/tab/tab_marketing_normal.png


BIN
static/tab/tab_marketing_pressed.png


BIN
static/tab/tab_personal_normal.png


BIN
static/tab/tab_personal_pressed.png


+ 208 - 0
store/index.js

@@ -0,0 +1,208 @@
+import Vue from 'vue'
+import Vuex from 'vuex'
+Vue.use(Vuex)
+import {code2Session, logout, checkLogin} from '@/api/login.js'
+import {getLookUpDatas} from '@/api/data.js'
+let lifeData = {};
+
+try{
+	// 尝试获取本地是否存在lifeData变量,第一次启动APP时是不存在的
+	lifeData = uni.getStorageSync('lifeData');
+}catch(e){
+	
+}
+
+// 需要永久存储,且下次APP启动需要取出的,在state中的变量名
+let saveStateKeys = ['vuex_carBrandList'];
+
+// 保存变量到本地存储中
+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);
+	}
+}
+const store = new Vuex.Store({
+    state: {
+        /**
+         * 是否需要强制登录
+         */
+        forcedLogin: false,
+        hasLogin: false,
+		openid: "",
+		isOpenLocation: false, // 用户是否开启授权开启定位
+		orderStatusList: [], // 服务订单状态数据字典数据
+		// 如果上面从本地获取的lifeData对象下有对应的属性,就赋值给state中对应的变量
+		// 加上vuex_前缀,是防止变量名冲突,也让人一目了然
+		vuex_carBrandList: lifeData.vuex_carBrandList ? lifeData.vuex_carBrandList : [] ,// 车型品牌数据
+		vuex_carType: {}, // 选择的车辆品牌
+		// 检测单详情数据
+		vuex_checkOrderData:null,
+		deviceNo: ""
+    },
+	getters:{
+		getOpeid(state){
+			return state.openid
+		}
+	},
+    mutations: {
+        login(state, userInfo) {
+			console.log(userInfo,'--token')
+            state.hasLogin = true;
+			uni.setStorageSync('token', userInfo.token);
+			uni.setStorageSync('userInfo', userInfo.user);
+        },
+        logout(state) {
+            state.hasLogin = false;
+			uni.removeStorageSync('token')
+			uni.removeStorageSync('userInfo')
+        },
+		openId(state,id) {
+			state.openid = id
+		},
+		checkOrder(state,data) {
+			state.vuex_checkOrderData = data
+		},
+		$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])
+		},
+		isOpenLocation(state,isOpenLocation){
+			state.isOpenLocation = isOpenLocation
+		},
+		orderStatusList(state,orderStatusList){
+			state.orderStatusList = orderStatusList
+		},
+    },
+	actions: {
+		wxLogin(context,provider){
+			uni.login({
+			  provider: provider,
+			  success(result) {
+				console.log(result)
+				if (result.code) {
+				  code2Session({
+					code: result.code
+				  }).then(res => {
+					if (res.status === '200') {
+					  uni.setStorageSync('openid', res.data);
+					  context.commit('openId',res.data)
+					};
+				  });
+				}
+			  }
+			});
+		},
+		// 登出
+		userLogout(context,data){
+			logout().then(res => {
+				console.log(res,'out')
+				if(res.status == 200){
+					context.commit('logout')
+				}
+			})
+		},
+		// 版本检测
+		checekAppUpdate(){
+			const updateManager = uni.getUpdateManager();
+			updateManager.onCheckForUpdate(function (res) {
+			  // 请求完新版本信息的回调
+			  console.log(res.hasUpdate);
+			});
+			updateManager.onUpdateReady(function (res) {
+			  uni.showModal({
+			    title: '更新提示',
+			    content: '新版本已经准备好,是否重启应用?',
+			    success(res) {
+			      if (res.confirm) {
+			        // 新的版本已经下载好,调用 applyUpdate 应用新版本并重启
+			        updateManager.applyUpdate();
+			      }
+			    }
+			  });
+			
+			});
+			updateManager.onUpdateFailed(function (res) {
+			  // 新的版本下载失败
+			});
+		},
+		// 检测是否登录
+		checkLogin(context,data){
+			checkLogin().then(res => {
+				if(res.status == '200'){
+					context.commit('login',res.data)
+				}else{
+					context.commit('logout')
+				}
+			})
+		},
+		// 检测是否开启定位
+		checkIsOpenLocation(context,data) {
+			uni.authorize({
+			    scope: 'scope.userLocation',
+			    success:()=> {
+					// 判断手机设置是否开启对微信的定位服务
+					const systemInfo = uni.getSystemInfoSync()
+					if(systemInfo.locationAuthorized) {
+						console.log('授权定位开启----------')
+						context.commit('isOpenLocation', true)
+					} else {
+						console.log('授权定位未开启----------')
+						context.commit('isOpenLocation', false)
+					}
+			    },
+				fail() {
+					uni.getSetting({
+					   success(res) {
+					      console.log(res.authSetting,'gggggggggg')
+						  if(!res.authSetting['scope.userLocation'] || !res.authSetting['scope.userLocationBackground']){
+							context.commit('isOpenLocation', false)
+						  } else {
+							context.commit('isOpenLocation', true)
+						  }
+					   },
+					   fail() {
+							context.commit('isOpenLocation', false)
+					   }
+					})
+					
+				}
+			})
+		},
+		// 获取订单状态数据字典
+		getOrderStatusList(context,data) {
+			getLookUpDatas({type:'ORDER_ON_CALL_STATUS'}).then(res =>{
+				context.commit('orderStatusList', res.data || [])
+			})
+		},
+		// 获取检测单详情数据
+		getCheckOrderData(context,data){
+			console.log(data,'==========data============index.js')
+			context.commit('checkOrder', data)
+		}
+	}
+})
+
+export default store

+ 108 - 0
uni.scss

@@ -0,0 +1,108 @@
+@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;
+
+/* 显示一行省略号 */
+.ellipsis-one{
+	white-space: nowrap;
+	text-overflow: ellipsis;
+	overflow: hidden;
+	word-break: break-all;
+}
+/* 显示两行,省略号 */
+.ellipsis-two{
+	overflow: hidden;
+	text-overflow: ellipsis;
+	display: -webkit-box;
+	-webkit-line-clamp: 2;
+	line-clamp: 2;
+	-webkit-box-orient: vertical;
+}
+// 必填*
+.u-required{
+	position: relative;
+}
+.u-required:before{
+	content: '*';
+	position: absolute;
+	left: -8px;
+	top: 10upx;
+	font-size: 14px;
+	color: #fa3534;
+	height: 9px;
+	line-height: 1;
+}

+ 13 - 0
uni_modules/xt-verify-code/changelog.md

@@ -0,0 +1,13 @@
+## 1.0.4(2021-12-27)
+## 修改文档
+## 1.0.3(2021-10-18)
+修改说明文档
+## 1.0.2(2021-10-18)
+修改size 传入值,可以传入1~6 的值
+
+## 1.0.1(2021-09-02)
+- type 属性参数修改 box、middle、bottom 三种
+- 新增middle模式
+## 1.0.0(2021-08-31)
+- 验证码输入
+- 提供两种显示模式

+ 293 - 0
uni_modules/xt-verify-code/components/xt-verify-code/xt-verify-code.vue

@@ -0,0 +1,293 @@
+<template>
+	<view class="xt__verify-code">
+		<!-- 输入框 -->
+		<input
+			id="xt__input"
+			:value="code"
+			class="xt__input"
+			:focus="isFocus"
+			:password="isPassword"
+			:type="inputType"
+			:maxlength="itemSize"
+			@input="input"
+			@focus="inputFocus"
+			@blur="inputBlur"
+		/>
+
+		<!-- 光标 -->
+		<view
+			id="xt__cursor"
+			v-if="cursorVisible && type !== 'middle'"
+			class="xt__cursor"
+			:style="{ left: codeCursorLeft[code.length] + 'px', height: cursorHeight + 'px', backgroundColor: cursorColor }"
+		></view>
+
+		<!-- 输入框 - 组 -->
+		<view id="xt__input-ground" class="xt__input-ground">
+			<template v-for="(item, index) in itemSize">
+				<view
+					:key="index"
+					:style="{ borderColor: code.length === index && cursorVisible ? boxActiveColor : boxNormalColor }"
+					:class="['xt__box', `xt__box-${type + ''}`, `xt__box::after`]"
+				>
+					<view :style="{ borderColor: boxActiveColor }" class="xt__middle-line" v-if="type === 'middle' && !code[index]"></view>
+					<text class="xt__code-text">{{ code[index] | codeFormat(isPassword) }}</text>
+				</view>
+			</template>
+		</view>
+	</view>
+</template>
+<script>
+/**
+ * @description 输入验证码组件
+ * @property {string} type = [box|middle|bottom] - 显示类型 默认:box -eg:bottom
+ * @property {string} inputType = [text|number] - 输入框类型 默认:number -eg:number
+ * @property {number} size = [1|2|3|4|5|6] - 支持的验证码数量 默认:6 -eg:6
+ * @property {boolean} isFocus - 是否立即聚焦 默认:true
+ * @property {boolean} isPassword - 是否以密码形式显示 默认false -eg:false
+ * @property {string} cursorColor - 光标颜色 默认:#cccccc
+ * @property {string} boxNormalColor - 光标未聚焦到的框的颜色 默认:#cccccc
+ * @property {string} boxActiveColor - 光标聚焦到的框的颜色 默认:#000000
+ * @event {Function(data)} confirm - 输入完成
+ */
+export default {
+	name: 'xt-verify-code',
+	props: {
+		value: {
+			type: String,
+			default: () => ''
+		},
+		type: {
+			type: String,
+			default: () => 'box'
+		},
+		inputType: {
+			type: String,
+			default: () => 'number'
+		},
+		size: {
+			type: Number,
+			default: () => 6
+		},
+		isFocus: {
+			type: Boolean,
+			default: () => true
+		},
+		isPassword: {
+			type: Boolean,
+			default: () => false
+		},
+		cursorColor: {
+			type: String,
+			default: () => '#cccccc'
+		},
+		boxNormalColor: {
+			type: String,
+			default: () => '#cccccc'
+		},
+		boxActiveColor: {
+			type: String,
+			default: () => '#000000'
+		}
+	},
+	model: {
+		prop: 'value',
+		event: 'input'
+	},
+	data() {
+		return {
+			cursorVisible: false,
+			cursorHeight: 35,
+			code: '', // 输入的验证码
+			codeCursorLeft: [], // 向左移动的距离数组,
+			itemSize: 6
+		};
+	},
+	created() {
+		this.cursorVisible = this.isFocus;
+		this.itemSize = this.size
+		// this.validatorSize();
+	},
+	mounted() {
+		this.init();
+	},
+	methods: {
+		/**
+		 *
+		 */
+		validatorSize() {
+			if (this.size <= 6 && this.size > 0) {
+				this.itemSize = Math.floor(this.size);
+			} else {
+				this.itemSize = 6;
+			}
+		},
+		/**
+		 * @description 初始化
+		 */
+		init() {
+			this.getCodeCursorLeft();
+			this.setCursorHeight();
+		},
+		/**
+		 * @description 获取元素节点
+		 * @param {string} elm - 节点的id、class 相当于 document.querySelect的参数 -eg: #id
+		 * @param {string} type = [single|array] - 单个元素获取多个元素 默认是单个元素
+		 * @param {Function} callback - 回调函数
+		 */
+		getElement(elm, type = 'single', callback) {
+			uni
+				.createSelectorQuery()
+				.in(this)
+				[type === 'array' ? 'selectAll' : 'select'](elm)
+				.boundingClientRect()
+				.exec(data => {
+					callback(data[0]);
+				});
+		},
+		/**
+		 * @description 计算光标的高度
+		 */
+		setCursorHeight() {
+			this.getElement('.xt__box', 'single', boxElm => {
+				this.cursorHeight = boxElm.height * 0.6;
+			});
+		},
+		/**
+		 * @description 获取光标在每一个box的left位置
+		 */
+		getCodeCursorLeft() {
+			// 获取父级框的位置信息
+			this.getElement('#xt__input-ground', 'single', parentElm => {
+				const parentLeft = parentElm.left;
+				// 获取各个box信息
+				this.getElement('.xt__box', 'array', elms => {
+					this.codeCursorLeft = [];
+					elms.forEach(elm => {
+						this.codeCursorLeft.push(elm.left - parentLeft + elm.width / 2);
+					});
+				});
+			});
+		},
+
+		// 输入框输入变化的回调
+		input(e) {
+			const value = e.detail.value;
+			this.cursorVisible = value.length !== this.itemSize;
+			this.$emit('input', value);
+			this.inputSuccess(value);
+		},
+
+		// 输入完成回调
+		inputSuccess(value) {
+			if (value.length === this.itemSize) {
+				this.$emit('confirm', value);
+			}
+		},
+		// 输入聚焦
+		inputFocus() {
+			this.cursorVisible = this.code.length !== this.itemSize;
+		},
+		// 输入失去焦点
+		inputBlur() {
+			this.cursorVisible = false;
+		}
+	},
+	watch: {
+		value(val) {
+			this.code = val;
+		}
+	},
+	filters: {
+		codeFormat(val, isPassword) {
+			let value = '';
+			if (val) {
+				value = isPassword ? '*' : val;
+			}
+			return value;
+		}
+	}
+};
+</script>
+<style lang="scss" scoped>
+
+.xt__verify-code {
+	position: relative;
+	width: 100%;
+	box-sizing: border-box;
+	padding: 50rpx 20rpx;
+	.xt__input {
+		height: 100%;
+		width: 200%;
+		position: absolute;
+		left: -100%;
+		z-index: 1;
+	}
+	.xt__cursor {
+		position: absolute;
+		top: 50%;
+		transform: translateY(-50%);
+		display: inline-block;
+		width: 2px;
+		animation-name: cursor;
+		animation-duration: 0.8s;
+		animation-iteration-count: infinite;
+	}
+
+	.xt__input-ground {
+		display: flex;
+		justify-content: space-between;
+		align-items: center;
+		width: 100%;
+		box-sizing: border-box;
+		.xt__box {
+			position: relative;
+			display: inline-block;
+			width: 40rpx;
+			height: 50rpx;
+			&-bottom {
+				border-bottom-width: 2rpx;
+				border-bottom-style: solid;
+			}
+
+			&-box {
+				border-width: 2rpx;
+				border-style: solid;
+				border-radius: 6rpx;
+			}
+
+			&-middle {
+				border: none;
+			}
+
+			.xt__middle-line {
+				position: absolute;
+				top: 50%;
+				left: 50%;
+				width: 50%;
+				transform: translate(-50%, -50%);
+				border-bottom-width: 2px;
+				border-bottom-style: solid;
+			}
+
+			.xt__code-text {
+				position: absolute;
+				top: 50%;
+				left: 50%;
+				font-size: 40rpx;
+				transform: translate(-50%, -50%);
+			}
+		}
+	}
+}
+
+@keyframes cursor {
+	0% {
+		opacity: 1;
+	}
+
+	100% {
+		opacity: 0;
+	}
+}
+</style>

+ 82 - 0
uni_modules/xt-verify-code/package.json

@@ -0,0 +1,82 @@
+{
+	"id": "xt-verify-code",
+	"displayName": "xt-verify-code验证码输入",
+	"version": "1.0.4",
+	"description": "xt-verify-code验证码输入组件,提供三种显示模式",
+	"keywords": [
+        "xt-verify-code",
+        "verify",
+        "验证码"
+    ],
+	"repository": "https://github.com/xiaodaweb/xt-ui/tree/main/uni_modules/xt-verify-code",
+	"engines": {
+		"HBuilderX": "^3.1.0"
+	},
+	"dcloudext": {
+		"category": [
+			"前端组件",
+			"通用组件"
+		],
+		"sale": {
+			"regular": {
+				"price": "0.00"
+			},
+			"sourcecode": {
+				"price": "0.00"
+			}
+		},
+		"contact": {
+			"qq": "1010161292"
+		},
+		"declaration": {
+			"ads": "无",
+			"data": "无",
+			"permissions": "无"
+		},
+		"npmurl": ""
+	},
+	"uni_modules": {
+		"dependencies": [],
+		"encrypt": [],
+		"platforms": {
+			"cloud": {
+				"tcb": "y",
+				"aliyun": "y"
+			},
+			"client": {
+				"Vue": {
+					"vue2": "y",
+					"vue3": "u"
+				},
+				"App": {
+					"app-vue": "u",
+					"app-nvue": "u"
+				},
+				"H5-mobile": {
+					"Safari": "u",
+					"Android Browser": "y",
+					"微信浏览器(Android)": "y",
+					"QQ浏览器(Android)": "y"
+				},
+				"H5-pc": {
+					"Chrome": "y",
+					"IE": "u",
+					"Edge": "y",
+					"Firefox": "y",
+					"Safari": "y"
+				},
+				"小程序": {
+					"微信": "y",
+					"阿里": "y",
+					"百度": "y",
+					"字节跳动": "y",
+					"QQ": "y"
+				},
+				"快应用": {
+					"华为": "u",
+					"联盟": "u"
+				}
+			}
+		}
+	}
+}

+ 20 - 0
uni_modules/xt-verify-code/readme.md

@@ -0,0 +1,20 @@
+# 验证码输入框
+## 使用方法
+```vue
+<xt-verify-code v-model="verifyCode" @confirm="confirm"></xt-verify-code>
+```
+## 插件使用了scss,安装本插件之前,请选择安装scss
+
+### 参数
+|属性						|类型		|说明																								|默认值	|
+|--							|--			|--																									|--			|
+|value(v-model)	|string	|值(可双向绑定)																			|''			|
+|type						|string	|验证输入框类型可选值: box、middle、bottom					|box		|
+|inputType			|string	|输入类型 可选值:text、number											|number	|
+|size						|number	|验证码长度 可选值: 1、2、3、4、5、6								|6			|
+|isFocus				|boolean|是否一开始聚焦																			|true		|
+|isPassword			|boolean|是否使用密码方式显示																|false	|
+|cursorColor		|string	|光标颜色																						|#cccccc|
+|boxNormalColor	|string	|显示框未选中的颜色																	|#cccccc|
+|boxActiveColor	|string	|显示框选中的颜色如果type='middle' 则是线的默认样式	|#000000|
+|@confirm				|event	|输入完成的回调事件  {(code)=>{}}										|				|

+ 83 - 0
utils/index.js

@@ -0,0 +1,83 @@
+function formatNumber(n) {
+  const str = n.toString();
+  return str[1] ? str : `0${str}`;
+}
+
+export function objToUrl(obj) {
+  let uri = '';
+  let keys = Object.keys(obj);
+  keys.forEach(item => {
+    uri += '&' + item + '=' + obj[item];
+  });
+
+  uri = uri.substr(1);
+
+  return uri;
+}
+
+export function formatTime(date) {
+  date = new Date(date.substr(0, 19));
+  let year1 = date.getFullYear();
+  let month1 = date.getMonth() + 1;
+  let day1 = date.getDate();
+  let hour1 = date.getHours();
+  let minute1 = date.getMinutes();
+  let second1 = date.getSeconds();
+
+  let [year, month, day, hour, minute, second] = [year1, month1, day1, hour1, minute1, second1].map(formatNumber);
+
+  return {
+    year, month, day, hour, minute, second
+  };
+}
+
+// 计算经纬度距离
+export function getDistance(lat1, lng1, currentPosition) {
+  const lat2 = currentPosition.lat;
+  const lng2 = currentPosition.lng;
+  if (lat1 && lng1 && lat2 && lng2) {
+	const radLat1 = lat1 * Math.PI / 180.0;
+	const radLat2 = lat2 * Math.PI / 180.0;
+	const a = radLat1 - radLat2;
+	const b = lng1 * Math.PI / 180.0 - lng2 * Math.PI / 180.0;
+	let s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) +
+	  Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2)));
+	s = s * 6378.137;// EARTH_RADIUS;
+	s = Math.round(s * 10000) / 10000;
+	return s;
+  }
+  return null;
+}
+// 打开位置
+export function openLocation(item) {
+  uni.openLocation({
+	latitude: item.lat - 0,
+	longitude: item.lng - 0,
+	name: item.name,
+	address: item.addrProvinceName + item.addrCityName + item.addrDistrictName + item.addrDetail
+  });
+}
+// 评分
+export function getStarNum(level) {
+  console.log(level,'level')
+  if(level){
+	  const star = level.toString();
+	  return star.indexOf('.') > -1 ? star : star + '.0';
+  }
+  return 0
+}
+// webview 打开新页面
+export function openWebView(item){
+	if (item.url) {
+		// 外链
+		if(item.url.indexOf("http")>=0){
+			uni.navigateTo({
+				url: '/pages/h5Page/index?src='+item.url
+			})
+		}else{
+			uni.navigateTo({
+				url: '/' + item.url
+			})
+		}
+	}
+}

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