Browse Source

初始化

bobo04052021@163.com 1 năm trước cách đây
mục cha
commit
d785d69543
100 tập tin đã thay đổi với 15240 bổ sung2 xóa
  1. 2 0
      .gitignore
  2. 279 0
      App.vue
  3. 201 0
      LICENSE
  4. 2 2
      README.md
  5. 43 0
      api/api.js
  6. 110 0
      common/js-sdk/socket/socket.js
  7. 438 0
      common/luch-request/core/Request.js
  8. 20 0
      common/luch-request/core/buildFullPath.js
  9. 71 0
      common/luch-request/helpers/buildURL.js
  10. 14 0
      common/luch-request/helpers/combineURLs.js
  11. 14 0
      common/luch-request/helpers/isAbsoluteURL.js
  12. 2 0
      common/luch-request/index.js
  13. 95 0
      common/luch-request/utils.js
  14. 99 0
      common/mixin/Mixin.js
  15. 31 0
      common/router/index.js
  16. 10 0
      common/router/modules/index.js
  17. 131 0
      common/router/modules/routes.js
  18. 18 0
      common/service/config.service.js
  19. 108 0
      common/service/service.js
  20. 101 0
      common/util/MinCache.js
  21. 73 0
      common/util/appUpdate.js
  22. 14 0
      common/util/constants.js
  23. 130 0
      common/util/tip.js
  24. 37 0
      common/util/vue-py.js
  25. 124 0
      common/util/work.js
  26. 83 0
      components/mescroll-uni/app-tabs.vue
  27. 55 0
      components/mescroll-uni/components/mescroll-down.css
  28. 47 0
      components/mescroll-uni/components/mescroll-down.vue
  29. 91 0
      components/mescroll-uni/components/mescroll-empty.vue
  30. 81 0
      components/mescroll-uni/components/mescroll-top.vue
  31. 46 0
      components/mescroll-uni/components/mescroll-up.css
  32. 39 0
      components/mescroll-uni/components/mescroll-up.vue
  33. 10 0
      components/mescroll-uni/mescroll-body.css
  34. 292 0
      components/mescroll-uni/mescroll-body.vue
  35. 60 0
      components/mescroll-uni/mescroll-mixins.js
  36. 34 0
      components/mescroll-uni/mescroll-uni-option.js
  37. 29 0
      components/mescroll-uni/mescroll-uni.css
  38. 862 0
      components/mescroll-uni/mescroll-uni.js
  39. 364 0
      components/mescroll-uni/mescroll-uni.vue
  40. 23 0
      components/mescroll-uni/mixins/mescroll-comp.js
  41. 48 0
      components/mescroll-uni/mixins/mescroll-more-item.js
  42. 56 0
      components/mescroll-uni/mixins/mescroll-more.js
  43. 158 0
      components/my-componets/appSelect.vue
  44. 93 0
      components/my-componets/my-date.vue
  45. 106 0
      components/my-componets/my-image-upload.vue
  46. 326 0
      components/my-componets/my-map.vue
  47. 79 0
      components/my-componets/my-nav.vue
  48. 46 0
      components/my-componets/my-page.vue
  49. 85 0
      components/my-componets/my-select.vue
  50. 546 0
      components/uni-calendar/calendar.js
  51. 179 0
      components/uni-calendar/uni-calendar-item.vue
  52. 512 0
      components/uni-calendar/uni-calendar.vue
  53. 352 0
      components/uni-calendar/util.js
  54. 54 0
      components/uni-collapse/uni-collapse.vue
  55. 29 0
      components/uni-popup/message.js
  56. 25 0
      components/uni-popup/popup.js
  57. 243 0
      components/uni-popup/uni-popup-dialog.vue
  58. 116 0
      components/uni-popup/uni-popup-message.vue
  59. 165 0
      components/uni-popup/uni-popup-share.vue
  60. 294 0
      components/uni-popup/uni-popup.vue
  61. 279 0
      components/uni-transition/uni-transition.vue
  62. 0 0
      components/w-picker/areadata/areadata.js
  63. 742 0
      components/w-picker/date-picker.vue
  64. 346 0
      components/w-picker/half-picker.vue
  65. 274 0
      components/w-picker/linkage-picker.vue
  66. 344 0
      components/w-picker/range-picker.vue
  67. 183 0
      components/w-picker/region-picker.vue
  68. 129 0
      components/w-picker/selector-picker.vue
  69. 250 0
      components/w-picker/shortterm-picker.vue
  70. 218 0
      components/w-picker/time-picker.vue
  71. 26 0
      components/w-picker/w-picker.css
  72. 340 0
      components/w-picker/w-picker.vue
  73. 86 0
      main.js
  74. 82 0
      manifest.json
  75. BIN
      myhjdc.keystore
  76. 44 0
      package-lock.json
  77. 5 0
      package.json
  78. 182 0
      pages.json
  79. 28 0
      pages/about/about.vue
  80. 251 0
      pages/addressbook/address-book.vue
  81. 122 0
      pages/addressbook/address-detail.vue
  82. 207 0
      pages/addressbook/level-address-book.vue
  83. 128 0
      pages/addressbook/member.vue
  84. 108 0
      pages/basics/avatar.vue
  85. 141 0
      pages/basics/background.vue
  86. 123 0
      pages/basics/button.vue
  87. 130 0
      pages/basics/design.vue
  88. 97 0
      pages/basics/home.vue
  89. 939 0
      pages/basics/icon.vue
  90. 207 0
      pages/basics/layout.vue
  91. 101 0
      pages/basics/loading.vue
  92. 153 0
      pages/basics/progress.vue
  93. 54 0
      pages/basics/shadow.vue
  94. 130 0
      pages/basics/tag.vue
  95. 169 0
      pages/basics/text.vue
  96. 51 0
      pages/common/exit.vue
  97. 48 0
      pages/common/success.vue
  98. 448 0
      pages/component/bar.vue
  99. 167 0
      pages/component/card.vue
  100. 113 0
      pages/component/chat.vue

+ 2 - 0
.gitignore

@@ -0,0 +1,2 @@
+/unpackage/
+/.idea/

+ 279 - 0
App.vue

@@ -0,0 +1,279 @@
+<script>
+	import Vue from 'vue'
+	import appUpdate from 'common/util/appUpdate.js'
+	export default {
+		onLaunch: function() {
+			uni.getSystemInfo({
+				success: function(e) {
+					
+					// #ifdef APP-PLUS
+					// 检测升级
+					appUpdate()
+					// #endif
+					// #ifndef MP
+					Vue.prototype.StatusBar = e.statusBarHeight;
+					if (e.platform == 'android') {
+						Vue.prototype.CustomBar = e.statusBarHeight + 50;
+					} else {
+						Vue.prototype.CustomBar = e.statusBarHeight + 45;
+					};
+					// #endif
+
+					// #ifdef MP-WEIXIN
+					Vue.prototype.StatusBar = e.statusBarHeight;
+					let custom = wx.getMenuButtonBoundingClientRect();
+					Vue.prototype.Custom = custom;
+					Vue.prototype.CustomBar = custom.bottom + custom.top - e.statusBarHeight;
+					// #endif		
+
+					// #ifdef MP-ALIPAY
+					Vue.prototype.StatusBar = e.statusBarHeight;
+					Vue.prototype.CustomBar = e.statusBarHeight + e.titleBarHeight;
+					// #endif
+					
+					// #ifdef APP-PLUS
+					//Vue.prototype.$api.listenTranMsg()
+			// 		var info = plus.push.getClientInfo();
+			
+			// 		/* 5+  push 消息推送 ps:使用:H5+的方式监听,实现推送*/
+			// 		plus.push.addEventListener("click", function(msg) {
+			// 			console.log("click:" + JSON.stringify(msg));
+			// 			console.log(msg.payload);
+			// 			console.log(JSON.stringify(msg));
+			// 			//这里可以写跳转业务代码
+			// 		}, false);
+			// 		// 监听在线消息事件    
+			// 		plus.push.addEventListener("receive", function(msg) {
+			// 			// plus.ui.alert(2);  
+			// 			//这里可以写跳转业务代码
+			// 			console.log("recevice:" + JSON.stringify(msg))
+			// 		}, false);
+					// #endif
+					
+					//Vue.prototype.$api.initLogin()
+					
+				}
+			})
+            Vue.prototype.NavBarColor='bg-gradual-blue'
+            Vue.prototype.Radio_Check_Size='scale(0.7)'
+			Vue.prototype.ColorList = [{
+					title: '嫣红',
+					name: 'red',
+					color: '#e54d42'
+				},
+				{
+					title: '桔橙',
+					name: 'orange',
+					color: '#f37b1d'
+				},
+				{
+					title: '明黄',
+					name: 'yellow',
+					color: '#fbbd08'
+				},
+				{
+					title: '橄榄',
+					name: 'olive',
+					color: '#8dc63f'
+				},
+				{
+					title: '森绿',
+					name: 'green',
+					color: '#39b54a'
+				},
+				{
+					title: '天青',
+					name: 'cyan',
+					color: '#1cbbb4'
+				},
+				{
+					title: '海蓝',
+					name: 'blue',
+					color: '#0081ff'
+				},
+				{
+					title: '姹紫',
+					name: 'purple',
+					color: '#6739b6'
+				},
+				{
+					title: '木槿',
+					name: 'mauve',
+					color: '#9c26b0'
+				},
+				{
+					title: '桃粉',
+					name: 'pink',
+					color: '#e03997'
+				},
+				{
+					title: '棕褐',
+					name: 'brown',
+					color: '#a5673f'
+				},
+				{
+					title: '玄灰',
+					name: 'grey',
+					color: '#8799a3'
+				},
+				{
+					title: '草灰',
+					name: 'gray',
+					color: '#aaaaaa'
+				},
+				{
+					title: '墨黑',
+					name: 'black',
+					color: '#333333'
+				},
+				{
+					title: '雅白',
+					name: 'white',
+					color: '#ffffff'
+				},
+			]
+
+		},
+		onShow: function() {
+			console.log('App Show')
+		},
+		onHide: function() {
+			console.log('App Hide')
+		}
+
+	}
+</script>
+
+<style>
+	@import "plugin/colorui/main.css";
+	@import "plugin/colorui/icon.css";
+    @import "plugin/colorui/animation.css";
+	.nav-list {
+		display: flex;
+		flex-wrap: wrap;
+		padding: 0px 40upx 0px;
+		justify-content: space-between;
+	}
+
+	.nav-li {
+		padding: 30upx;
+		border-radius: 12upx;
+		width: 45%;
+		margin: 0 2.5% 40upx;
+		background-image: url(https://cdn.nlark.com/yuque/0/2019/png/280374/1552996358352-assets/web-upload/cc3b1807-c684-4b83-8f80-80e5b8a6b975.png);
+		background-size: cover;
+		background-position: center;
+		position: relative;
+		z-index: 1;
+	}
+
+	.nav-li::after {
+		content: "";
+		position: absolute;
+		z-index: -1;
+		background-color: inherit;
+		width: 100%;
+		height: 100%;
+		left: 0;
+		bottom: -10%;
+		border-radius: 10upx;
+		opacity: 0.2;
+		transform: scale(0.9, 0.9);
+	}
+
+	.nav-li.cur {
+		color: #fff;
+		background: rgb(94, 185, 94);
+		box-shadow: 4upx 4upx 6upx rgba(94, 185, 94, 0.4);
+	}
+
+	.nav-title {
+		font-size: 32upx;
+		font-weight: 300;
+	}
+
+	.nav-title::first-letter {
+		font-size: 40upx;
+		margin-right: 4upx;
+	}
+
+	.nav-name {
+		font-size: 28upx;
+		text-transform: Capitalize;
+		margin-top: 20upx;
+		position: relative;
+	}
+
+	.nav-name::before {
+		content: "";
+		position: absolute;
+		display: block;
+		width: 40upx;
+		height: 6upx;
+		background: #fff;
+		bottom: 0;
+		right: 0;
+		opacity: 0.5;
+	}
+
+	.nav-name::after {
+		content: "";
+		position: absolute;
+		display: block;
+		width: 100upx;
+		height: 1px;
+		background: #fff;
+		bottom: 0;
+		right: 40upx;
+		opacity: 0.3;
+	}
+
+	.nav-name::first-letter {
+		font-weight: bold;
+		font-size: 36upx;
+		margin-right: 1px;
+	}
+
+	.nav-li text {
+		position: absolute;
+		right: 30upx;
+		top: 30upx;
+		font-size: 52upx;
+		width: 60upx;
+		height: 60upx;
+		text-align: center;
+		line-height: 60upx;
+	}
+
+	.text-light {
+		font-weight: 300;
+	}
+
+	@keyframes show {
+		0% {
+			transform: translateY(-50px);
+		}
+
+		60% {
+			transform: translateY(40upx);
+		}
+
+		100% {
+			transform: translateY(0px);
+		}
+	}
+
+	@-webkit-keyframes show {
+		0% {
+			transform: translateY(-50px);
+		}
+
+		60% {
+			transform: translateY(40upx);
+		}
+
+		100% {
+			transform: translateY(0px);
+		}
+	}
+</style>

+ 201 - 0
LICENSE

@@ -0,0 +1,201 @@
+    Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright (c) 2019 <a href="http://www.jeecg.com">Jeecg Boot</a> All rights reserved.
+ 
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.

+ 2 - 2
README.md

@@ -1,3 +1,3 @@
-# VentAnaly_6.0_APP
+VentAnaly_6.0_APP
 
-手机端
+手机端

+ 43 - 0
api/api.js

@@ -0,0 +1,43 @@
+import { http } from '@/common/service/service.js' 
+import configService from '@/common/service/config.service.js';
+const apiService = {
+	 
+	 /**
+	  * 登录
+	  */
+	login(params) {
+		return http.post('/sys/mLogin',params)	
+	},
+	/**
+	  * 手机号码登录
+	  */
+	phoneNoLogin(params) {
+		return http.post('/sys/phoneLogin',params);
+	},
+	/**
+	  * 退出
+	  */
+	logout(params) {
+		return http.post('/sys/logout',params);
+	},
+	// 按系统查询设备分类信息
+	getDeviceInfo(params){
+		return http.get('/ventanaly-device/safety/ventanalyDeviceInfo/DeviceKind/queryBySystem',params);
+	},
+	/**
+	 * 获取文件访问路径
+	 * @param avatar
+	 * @param subStr
+	 * @returns {*}
+	 */
+	getFileAccessHttpUrl(avatar,subStr){
+	    if(!subStr) subStr = 'http'
+	    if(avatar && avatar.startsWith(subStr)){
+	        return avatar;
+	    }else{
+	        return configService.staticDomainURL + "/" + avatar;
+	    }
+	}
+};
+
+export default apiService;

+ 110 - 0
common/js-sdk/socket/socket.js

@@ -0,0 +1,110 @@
+import configService from '@/common/service/config.service.js';
+import store from '@/store/index.js';
+class socket {
+	constructor(options) {
+		this.socketUrl = configService.apiUrl;
+		this.socketStart = false;
+		this.monitorSocketError();
+		this.monitorSocketClose();
+		this.socketReceive();
+	}
+	init(socket_type,callback) {
+		const _this = this;
+		if (configService.apiUrl) {
+			if(this.socketStart){
+				console.log('webSocket已经启动了');
+			}else{
+				let userid=store.state.userid?store.state.userid:store.getters.userid;
+				let url=this.socketUrl.replace("https://","wss://").replace("http://","ws://")+"/"+socket_type+"/"+userid+"_app";
+				console.log("启动this.socketUrl连接地址:",url);
+				
+				uni.connectSocket({
+					url: url,
+					method: 'GET'
+				});
+				uni.onSocketOpen((res) => {
+					this.socketStart = true;
+					callback && callback();
+					console.log('WebSocket连接已打开!');
+				});
+				/*setTimeout(() => {
+				   _this.getHeartbeat();
+				}, 5000);*/
+			}
+		}else{
+			console.log('config/baseUrl socketUrl为空');
+		}
+	}
+	//Socket给服务器发送消息
+	send(data, callback) {
+		const _this = this;
+		if (store.state.userid) {
+			data.userUid =store.state.userid;
+		}
+		console.log(data);
+		uni.sendSocketMessage({
+			data: JSON.stringify(data),
+			success: () => {
+				callback && callback(true);
+			},
+			fail: () => {
+				callback && callback(false);
+			}
+		});
+	}
+	//Socket接收服务器发送过来的消息
+	socketReceive() {
+		const _this = this;
+		uni.onSocketMessage(function(res) {
+			console.log("APP:----》收到服务器内容:",res);
+			let data = JSON.parse(res.data);
+			//console.log('收到服务器内容:', data);
+			_this.acceptMessage && _this.acceptMessage(data);
+		});
+	}
+	//关闭Socket
+	closeSocket() {
+		const _this = this;
+		uni.closeSocket();
+		_this.socketStart = false;
+	}
+	//监听Socket关闭
+	monitorSocketClose() {
+		const _this = this;
+		uni.onSocketClose(function(res) {
+			console.log('WebSocket 已关闭!');
+			_this.socketStart = false;
+			setTimeout(function() {
+				//_this.init();
+			}, 3000); 
+		});
+	}
+	//监听Socket错误
+	monitorSocketError() {
+		const _this = this;
+		uni.onSocketError(function(res) {
+			_this.socketStart = false;
+			console.log('WebSocket连接打开失败,请检查!');
+		});
+	}
+	//心跳
+	getHeartbeat() {
+		const _this = this;
+		this.send({
+			type: "心跳",
+			userUid: store.state.userid
+		}, (val) => {
+			setTimeout(() => {
+				if (val) {
+					//_this.getHeartbeat();
+				} else {
+					if(!_this.socketStart){
+						//_this.init();
+					}
+				}
+			}, 10000);
+		});
+	}
+};
+const mySocket = new socket();
+export default mySocket;

+ 438 - 0
common/luch-request/core/Request.js

@@ -0,0 +1,438 @@
+/**
+ * Request 2.0.1
+ * @Class Request
+ * @description luch-request 2.0.1 http请求插件
+ * @Author lu-ch
+ * @Date 2020-05-01
+ * @Email webwork.s@qq.com
+ * http://ext.dcloud.net.cn/plugin?id=392
+ * hbuilderx:2.6.15
+ */
+
+import buildURL from '../helpers/buildURL'
+import buildFullPath from './buildFullPath'
+import { isBoolean } from '../utils'
+
+export default class Request {
+  config = {
+    baseUrl: '',
+    header: {},
+    method: 'GET',
+    dataType: 'json',
+    // #ifndef MP-ALIPAY || APP-PLUS
+    responseType: 'text',
+    // #endif
+    custom: {},
+    // #ifdef MP-ALIPAY || MP-WEIXIN
+    timeout: 30000,
+    // #endif
+    // #ifdef APP-PLUS
+    sslVerify: true,
+    // #endif
+    // #ifdef H5
+    withCredentials: false
+    // #endif
+  }
+
+  /**
+   * @property {Function} request 请求拦截器
+   * @property {Function} response 响应拦截器
+   * @type {{request: Request.interceptor.request, response: Request.interceptor.response}}
+   */
+  interceptor = {
+    /**
+     * @param {Request~requestCallback} cb - 请求之前拦截,接收一个函数(config, cancel)=> {return config}。第一个参数为全局config,第二个参数为函数,调用则取消本次请求。
+     */
+    request: (cb) => {
+      if (cb) {
+        this.requestBeforeFun = cb
+      }
+    },
+    /**
+     * @param {Request~responseCallback} cb 响应拦截器,对响应数据做点什么
+     * @param {Request~responseErrCallback} ecb 响应拦截器,对响应错误做点什么
+     */
+    response: (cb, ecb) => {
+      if (cb) {
+        this.requestComFun = cb
+      }
+      if (ecb) {
+        this.requestComFail = ecb
+      }
+    }
+  }
+
+  requestBeforeFun = (config) => {
+    return config
+  }
+
+  requestComFun = (response) => {
+    return response
+  }
+
+  requestComFail = (response) => {
+    return response
+  }
+
+  /**
+   * 自定义验证器,如果返回true 则进入响应拦截器的响应成功函数(resolve),否则进入响应拦截器的响应错误函数(reject)
+   * @param { Number } statusCode - 请求响应体statusCode(只读)
+   * @return { Boolean } 如果为true,则 resolve, 否则 reject
+   */
+  validateStatus(statusCode) {
+    return statusCode === 200
+  }
+
+  /**
+   * @Function
+   * @param {Request~setConfigCallback} f - 设置全局默认配置
+   */
+  setConfig(f) {
+    this.config = f(this.config)
+  }
+
+  /**
+   * @Function
+   * @param {Object} options - 请求配置项
+   * @prop {String} options.url - 请求路径
+   * @prop {Object} options.data - 请求参数
+   * @prop {Object} [options.responseType = config.responseType] [text|arraybuffer] - 响应的数据类型
+   * @prop {Object} [options.dataType = config.dataType] - 如果设为 json,会尝试对返回的数据做一次 JSON.parse
+   * @prop {Object} [options.header = config.header] - 请求header
+   * @prop {Object} [options.method = config.method] - 请求方法
+   * @returns {Promise<unknown>}
+   */
+  async request(options = {}) {
+    return new Promise((resolve, reject) => {
+      options.baseUrl = this.config.baseUrl
+      options.dataType = options.dataType || this.config.dataType
+      // #ifndef MP-ALIPAY || APP-PLUS
+      options.responseType = options.responseType || this.config.responseType
+      // #endif
+      // #ifdef MP-ALIPAY || MP-WEIXIN
+      options.timeout = options.timeout || this.config.timeout
+      // #endif
+      // #ifdef H5
+      options.withCredentials = isBoolean(options.withCredentials) ? options.withCredentials : this.config.withCredentials
+      // #endif
+      options.url = options.url || ''
+      options.data = options.data || {}
+      options.params = options.params || {}
+      options.header = {...this.config.header, ...(options.header || {})}
+      options.method = options.method || this.config.method
+      options.custom =  {...this.config.custom,...(options.custom || {})}
+      // #ifdef APP-PLUS
+      options.sslVerify = options.sslVerify === undefined ? this.config.sslVerify : options.sslVerify
+      // #endif
+      options.getTask = options.getTask || this.config.getTask
+      let next = true
+      const cancel = (t = 'handle cancel', config = options) => {
+        const err = {
+          errMsg: t,
+          config: config
+        }
+        reject(err)
+        next = false
+      }
+
+      const handleRe =  {...this.requestBeforeFun(options, cancel)}
+      const _config = {...handleRe}
+      if (!next) return
+      const requestTask = uni.request({
+        url: buildURL(buildFullPath(_config.baseUrl, _config.url), _config.params),
+        data: _config.data,
+        header: _config.header,
+        method: _config.method,
+        // #ifdef MP-ALIPAY || MP-WEIXIN
+        timeout: _config.timeout,
+        // #endif
+        dataType: _config.dataType,
+        // #ifndef MP-ALIPAY || APP-PLUS
+        responseType: _config.responseType,
+        // #endif
+        // #ifdef APP-PLUS
+        sslVerify: _config.sslVerify,
+        // #endif
+        // #ifdef H5
+        withCredentials: _config.withCredentials,
+        // #endif
+        complete: (response) => {
+          response.config = handleRe
+          if (this.validateStatus(response.statusCode)) { // 成功
+            response = this.requestComFun(response)
+            resolve(response)
+          } else {
+            response = this.requestComFail(response)
+            reject(response)
+          }
+        }
+      })
+      if (handleRe.getTask) {
+        handleRe.getTask(requestTask, handleRe)
+      }
+    })
+  }
+
+  get(url, options = {}) {
+    return this.request({
+      url,
+      method: 'GET',
+      ...options
+    })
+  }
+
+  post(url, data, options = {}) {
+    return this.request({
+      url,
+      data,
+      method: 'POST',
+      ...options
+    })
+  }
+
+  // #ifndef MP-ALIPAY
+  put(url, data, options = {}) {
+    return this.request({
+      url,
+      data,
+      method: 'PUT',
+      ...options
+    })
+  }
+
+  // #endif
+
+  // #ifdef APP-PLUS || H5 || MP-WEIXIN || MP-BAIDU
+  delete(url, data, options = {}) {
+    return this.request({
+      url,
+      data,
+      method: 'DELETE',
+      ...options
+    })
+  }
+
+  // #endif
+
+  // #ifdef APP-PLUS || H5 || MP-WEIXIN
+  connect(url, data, options = {}) {
+    return this.request({
+      url,
+      data,
+      method: 'CONNECT',
+      ...options
+    })
+  }
+
+  // #endif
+
+  // #ifdef APP-PLUS || H5 || MP-WEIXIN || MP-BAIDU
+  head(url, data, options = {}) {
+    return this.request({
+      url,
+      data,
+      method: 'HEAD',
+      ...options
+    })
+  }
+
+  // #endif
+
+  // #ifdef APP-PLUS || H5 || MP-WEIXIN || MP-BAIDU
+  options(url, data, options = {}) {
+    return this.request({
+      url,
+      data,
+      method: 'OPTIONS',
+      ...options
+    })
+  }
+
+  // #endif
+
+  // #ifdef APP-PLUS || H5 || MP-WEIXIN
+  trace(url, data, options = {}) {
+    return this.request({
+      url,
+      data,
+      method: 'TRACE',
+      ...options
+    })
+  }
+
+  // #endif
+
+  upload(url, {
+    // #ifdef APP-PLUS || H5
+    files,
+    // #endif
+    // #ifdef MP-ALIPAY
+    fileType,
+    // #endif
+    filePath,
+    name,
+    // #ifdef H5
+    file,
+    // #endif
+    header = {},
+    formData = {},
+    custom = {},
+    params = {},
+    getTask
+  }) {
+    return new Promise((resolve, reject) => {
+      let next = true
+      const globalHeader = {...this.config.header}
+      delete globalHeader['content-type']
+      delete globalHeader['Content-Type']
+      const pubConfig = {
+        baseUrl: this.config.baseUrl,
+        url,
+        // #ifdef MP-ALIPAY
+        fileType,
+        // #endif
+        filePath,
+        method: 'UPLOAD',
+        name,
+        header: {...globalHeader, ...header},
+        formData,
+        params,
+        custom: {...this.config.custom, ...custom},
+        getTask: getTask || this.config.getTask
+      }
+      // #ifdef APP-PLUS || H5
+      if (files) {
+        pubConfig.files = files
+      }
+      // #endif
+      // #ifdef H5
+      if (file) {
+        pubConfig.file = file
+      }
+      // #endif
+      const cancel = (t = 'handle cancel', config = pubConfig) => {
+        const err = {
+          errMsg: t,
+          config: config
+        }
+        reject(err)
+        next = false
+      }
+
+      const handleRe = {...this.requestBeforeFun(pubConfig, cancel)}
+      const _config = {
+        url: buildURL(buildFullPath(handleRe.baseUrl, handleRe.url), handleRe.params),
+        // #ifdef MP-ALIPAY
+        fileType: handleRe.fileType,
+        // #endif
+        filePath: handleRe.filePath,
+        name: handleRe.name,
+        header: handleRe.header,
+        formData: handleRe.formData,
+        complete: (response) => {
+          response.config = handleRe
+          try {
+            // 对可能字符串不是json 的情况容错
+            if (typeof response.data === 'string') {
+              response.data = JSON.parse(response.data)
+            }
+            // eslint-disable-next-line no-empty
+          } catch (e) {
+          }
+          if (this.validateStatus(response.statusCode)) { // 成功
+            response = this.requestComFun(response)
+            resolve(response)
+          } else {
+            response = this.requestComFail(response)
+            reject(response)
+          }
+        }
+      }
+      // #ifdef APP-PLUS || H5
+      if (handleRe.files) {
+        _config.files = handleRe.files
+      }
+      // #endif
+      // #ifdef H5
+      if (handleRe.file) {
+        _config.file = handleRe.file
+      }
+      // #endif
+      if (!next) return
+      const requestTask = uni.uploadFile(_config)
+      if (handleRe.getTask) {
+        handleRe.getTask(requestTask, handleRe)
+      }
+    })
+  }
+
+  download(url, options = {}) {
+    return new Promise((resolve, reject) => {
+      let next = true
+      const pubConfig = {
+        baseUrl: this.config.baseUrl,
+        url,
+        method: 'DOWNLOAD',
+        header: {...this.config.header, ...(options.header || {})},
+        params: options.params || {},
+        custom: {...this.config.custom, ...(options.custom || {})},
+        getTask: options.getTask || this.config.getTask
+      }
+      const cancel = (t = 'handle cancel', config = pubConfig) => {
+        const err = {
+          errMsg: t,
+          config: config
+        }
+        reject(err)
+        next = false
+      }
+
+      const handleRe = {...this.requestBeforeFun(pubConfig, cancel)}
+      if (!next) return
+      const requestTask = uni.downloadFile({
+        url: buildURL(buildFullPath(handleRe.baseUrl, handleRe.url), handleRe.params),
+        header: handleRe.header,
+        complete: (response) => {
+          response.config = handleRe
+          if (this.validateStatus(response.statusCode)) { // 成功
+            response = this.requestComFun(response)
+            resolve(response)
+          } else {
+            response = this.requestComFail(response)
+            reject(response)
+          }
+        }
+      })
+      if (handleRe.getTask) {
+        handleRe.getTask(requestTask, handleRe)
+      }
+    })
+  }
+}
+
+
+/**
+ * setConfig回调
+ * @return {Object} - 返回操作后的config
+ * @callback Request~setConfigCallback
+ * @param {Object} config - 全局默认config
+ */
+/**
+ * 请求拦截器回调
+ * @return {Object} - 返回操作后的config
+ * @callback Request~requestCallback
+ * @param {Object} config - 全局config
+ * @param {Function} [cancel] - 取消请求钩子,调用会取消本次请求
+ */
+/**
+ * 响应拦截器回调
+ * @return {Object} - 返回操作后的response
+ * @callback Request~responseCallback
+ * @param {Object} response - 请求结果 response
+ */
+/**
+ * 响应错误拦截器回调
+ * @return {Object} - 返回操作后的response
+ * @callback Request~responseErrCallback
+ * @param {Object} response - 请求结果 response
+ */

+ 20 - 0
common/luch-request/core/buildFullPath.js

@@ -0,0 +1,20 @@
+'use strict'
+
+import isAbsoluteURL from '../helpers/isAbsoluteURL'
+import combineURLs from '../helpers/combineURLs'
+
+/**
+ * Creates a new URL by combining the baseURL with the requestedURL,
+ * only when the requestedURL is not already an absolute URL.
+ * If the requestURL is absolute, this function returns the requestedURL untouched.
+ *
+ * @param {string} baseURL The base URL
+ * @param {string} requestedURL Absolute or relative URL to combine
+ * @returns {string} The combined full path
+ */
+export default function buildFullPath(baseURL, requestedURL) {
+  if (baseURL && !isAbsoluteURL(requestedURL)) {
+    return combineURLs(baseURL, requestedURL)
+  }
+  return requestedURL
+}

+ 71 - 0
common/luch-request/helpers/buildURL.js

@@ -0,0 +1,71 @@
+'use strict'
+
+import * as utils from './../utils'
+
+function encode(val) {
+  return encodeURIComponent(val).
+    replace(/%40/gi, '@').
+    replace(/%3A/gi, ':').
+    replace(/%24/g, '$').
+    replace(/%2C/gi, ',').
+    replace(/%20/g, '+').
+    replace(/%5B/gi, '[').
+    replace(/%5D/gi, ']')
+}
+
+/**
+ * Build a URL by appending params to the end
+ *
+ * @param {string} url The base of the url (e.g., http://www.google.com)
+ * @param {object} [params] The params to be appended
+ * @returns {string} The formatted url
+ */
+// var baseURL = "http://http://182.92.126.35:9999";
+// var formattedURL = buildURL(baseURL, params);
+export default function buildURL(url, params) {
+  /*eslint no-param-reassign:0*/
+  if (!params) {
+    return url
+  }
+
+  var serializedParams
+  if (utils.isURLSearchParams(params)) {
+    serializedParams = params.toString()
+  } else {
+    var parts = []
+
+    utils.forEach(params, function serialize(val, key) {
+      if (val === null || typeof val === 'undefined') {
+        return
+      }
+
+      if (utils.isArray(val)) {
+        key = key + '[]'
+      } else {
+        val = [val]
+      }
+
+      utils.forEach(val, function parseValue(v) {
+        if (utils.isDate(v)) {
+          v = v.toISOString()
+        } else if (utils.isObject(v)) {
+          v = JSON.stringify(v)
+        }
+        parts.push(encode(key) + '=' + encode(v))
+      })
+    })
+
+    serializedParams = parts.join('&')
+  }
+
+  if (serializedParams) {
+    var hashmarkIndex = url.indexOf('#')
+    if (hashmarkIndex !== -1) {
+      url = url.slice(0, hashmarkIndex)
+    }
+
+    url += (url.indexOf('?') === -1 ? '?' : '&') + serializedParams
+  }
+
+  return url
+}

+ 14 - 0
common/luch-request/helpers/combineURLs.js

@@ -0,0 +1,14 @@
+'use strict'
+
+/**
+ * Creates a new URL by combining the specified URLs
+ *
+ * @param {string} baseURL The base URL
+ * @param {string} relativeURL The relative URL
+ * @returns {string} The combined URL
+ */
+export default function combineURLs(baseURL, relativeURL) {
+  return relativeURL
+    ? baseURL.replace(/\/+$/, '') + '/' + relativeURL.replace(/^\/+/, '')
+    : baseURL
+}

+ 14 - 0
common/luch-request/helpers/isAbsoluteURL.js

@@ -0,0 +1,14 @@
+'use strict'
+
+/**
+ * Determines whether the specified URL is absolute
+ *
+ * @param {string} url The URL to test
+ * @returns {boolean} True if the specified URL is absolute, otherwise false
+ */
+export default function isAbsoluteURL(url) {
+  // A URL is considered absolute if it begins with "<scheme>://" or "//" (protocol-relative URL).
+  // RFC 3986 defines scheme name as a sequence of characters beginning with a letter and followed
+  // by any combination of letters, digits, plus, period, or hyphen.
+  return /^([a-z][a-z\d+\-.]*:)?\/\//i.test(url)
+}

+ 2 - 0
common/luch-request/index.js

@@ -0,0 +1,2 @@
+import Request from './core/Request'
+export default Request

+ 95 - 0
common/luch-request/utils.js

@@ -0,0 +1,95 @@
+'use strict'
+
+// utils is a library of generic helper functions non-specific to axios
+
+var toString = Object.prototype.toString
+
+/**
+ * Determine if a value is an Array
+ *
+ * @param {Object} val The value to test
+ * @returns {boolean} True if value is an Array, otherwise false
+ */
+export function isArray (val) {
+  return toString.call(val) === '[object Array]'
+}
+
+
+/**
+ * Determine if a value is an Object
+ *
+ * @param {Object} val The value to test
+ * @returns {boolean} True if value is an Object, otherwise false
+ */
+export function isObject (val) {
+  return val !== null && typeof val === 'object'
+}
+
+/**
+ * Determine if a value is a Date
+ *
+ * @param {Object} val The value to test
+ * @returns {boolean} True if value is a Date, otherwise false
+ */
+export function isDate (val) {
+  return toString.call(val) === '[object Date]'
+}
+
+/**
+ * Determine if a value is a URLSearchParams object
+ *
+ * @param {Object} val The value to test
+ * @returns {boolean} True if value is a URLSearchParams object, otherwise false
+ */
+export function isURLSearchParams (val) {
+  return typeof URLSearchParams !== 'undefined' && val instanceof URLSearchParams
+}
+
+
+/**
+ * Iterate over an Array or an Object invoking a function for each item.
+ *
+ * If `obj` is an Array callback will be called passing
+ * the value, index, and complete array for each item.
+ *
+ * If 'obj' is an Object callback will be called passing
+ * the value, key, and complete object for each property.
+ *
+ * @param {Object|Array} obj The object to iterate
+ * @param {Function} fn The callback to invoke for each item
+ */
+export function forEach (obj, fn) {
+  // Don't bother if no value provided
+  if (obj === null || typeof obj === 'undefined') {
+    return
+  }
+
+  // Force an array if not already something iterable
+  if (typeof obj !== 'object') {
+    /*eslint no-param-reassign:0*/
+    obj = [obj]
+  }
+
+  if (isArray(obj)) {
+    // Iterate over array values
+    for (var i = 0, l = obj.length; i < l; i++) {
+      fn.call(null, obj[i], i, obj)
+    }
+  } else {
+    // Iterate over object keys
+    for (var key in obj) {
+      if (Object.prototype.hasOwnProperty.call(obj, key)) {
+        fn.call(null, obj[key], key, obj)
+      }
+    }
+  }
+}
+
+/**
+ * 是否为boolean 值
+ * @param val
+ * @returns {boolean}
+ */
+export function isBoolean(val) {
+  return typeof val === 'boolean'
+}

+ 99 - 0
common/mixin/Mixin.js

@@ -0,0 +1,99 @@
+/**
+ * 下了列表混入
+ * @type {{data(): *, methods: {upCallback(*): void, downCallback(): void, loadList(*): void}}}
+ */
+const ListMixin = {
+	data() {
+		return {
+			downOption:{
+				auto:false,//是否在初始化完毕之后自动执行下拉回调callback; 默认true
+			},
+			upOption:{
+				page:{
+				  num : 0 ,
+				  size : 8 ,
+				  time : null
+				}
+			},
+			queryParam:{
+				pageNo:1,
+				pageSize:8
+			},
+			list:[],
+			pageNo:1,
+			pageSize:8,
+		}
+	},
+	methods: {
+		/*下拉刷新的回调 */
+		downCallback(){
+		//加载列表数据
+		  this.loadList('down');
+		},
+		/*上拉加载的回调: 其中page.num:当前页 从1开始, page.size:每页数据条数,默认10 */
+		upCallback(page) {
+			let param = this.queryParam
+				param.pageNo= page.num,
+				param.pageSize= page.size
+			
+			if(page.num == 1){
+				this.list = [];
+			}
+		 console.log("upCallback==param::",param)
+		 this.$http.get(this.url,{params:param}).then(res=>{
+		 	 console.log("upCallback请求返回res",res)
+		 	 if(res.data.success){
+		 		let rec=res.data.result.records;
+				let hasNext=true;
+				if(!rec || rec.length<this.pageSize){
+				  console.log("加载完成!没有更多了")
+				  hasNext=false;
+				}
+				 console.log("hasNext",hasNext)
+				//方法四 (不推荐),会存在一个小问题:比如列表共有20条数据,每页加载10条,共2页.如果只根据当前页的数据个数判断,则需翻到第三页才会知道无更多数据.
+				this.mescroll.endSuccess(rec.length);
+				
+				//设置列表数据
+				this.list=this.list.concat(rec);
+				this.$forceUpdate();
+		 	  }else{
+		 		this.mescroll.endErr();
+		 	  }
+		  }).catch(()=>{
+		 		//加载失败, 结束
+		 		this.mescroll.endErr();
+		 })
+		},
+		loadList(flag){
+		    let param = this.queryParam
+				param.pageNo=this.pageNo,
+				param.pageSize=this.pageSize
+		    console.log("请求参数",param)
+		    this.$http.get(this.url,{params:param}).then(res=>{
+			if(res.data.success){
+				console.log("请求返回res.data",res.data) 
+				let rec=res.data.result.records
+				if(flag=='down'){
+					//下拉刷新成功的回调,隐藏下拉刷新的状态
+					this.mescroll.endSuccess();	
+				}
+				//添加新数据
+				this.list=rec;
+				/* if(!rec || rec.length<this.pageSize){
+				  console.log("加载完成!")
+				} */
+			  }else{
+				  console.log("请求返回else",res) 
+				this.mescroll.endErr();
+			  }
+		 }).catch((err)=>{
+			 console.log("请求返回err",err) 
+				//加载失败, 结束
+				this.mescroll.endErr();
+		}) 
+		},
+	}
+	
+}
+
+export default ListMixin;

+ 31 - 0
common/router/index.js

@@ -0,0 +1,31 @@
+import modules from './modules'
+import Vue from 'vue'
+import Router from '@/plugin/uni-simple-router/index.js'
+import {ACCESS_TOKEN} from '@/common/util/constants.js'
+
+Vue.use(Router)
+//初始化
+const router = new Router({
+	encodeURI:true,  
+    routes: [...modules]//路由表
+});
+
+const whiteList = ['/pages/login/login'] 
+//全局路由前置守卫
+router.beforeEach((to, from, next) => {
+	let token=uni.getStorageSync(ACCESS_TOKEN);
+	if(token){
+		 next()
+	}else{
+		if (whiteList.indexOf(to.path) !== -1) {
+		  next()
+		}else{
+		  next({ path: '/pages/login/login'})
+		}
+	} 
+})
+// 全局路由后置守卫
+router.afterEach((to, from) => {
+	console.log("afterEach")
+})
+export default router;

+ 10 - 0
common/router/modules/index.js

@@ -0,0 +1,10 @@
+const files = require.context('.', false, /\.js$/)
+const modules = []
+
+files.keys().forEach(key => {
+  if (key === './index.js') return
+  const item = files(key).default
+  modules.push(...item)
+})
+
+export default modules

+ 131 - 0
common/router/modules/routes.js

@@ -0,0 +1,131 @@
+const routes = [
+	{
+	 path: "/pages/login/login",
+	 name: 'login',
+		 meta: {
+			 title: '登录',
+		 },
+	},
+	{
+        //注意:path必须跟pages.json中的地址对应,最前面别忘了加'/'哦
+      path: '/pages/index/index',
+      name: 'index',
+        meta: {
+	        title: '主页',
+	    },
+    },
+	{
+	    //注意:path必须跟pages.json中的地址对应,最前面别忘了加'/'哦
+	  path: '/pages/home/home',
+	  //aliasPath:'/',  //对于h5端你必须在首页加上aliasPath并设置为/
+	  name: 'home',
+	    meta: {
+	        title: '首页',
+	    },
+	},
+	{
+	  path: '/pages/test1/test1',
+	  name: 'test1',
+	    meta: {
+	        title: '测试1',
+	    },
+	},
+    {
+	    path: '/pages/user/people',
+        name: 'people',
+        meta: {
+	        title: '个人中心',
+	    },
+	},
+	{
+	    path: '/pages/user/userdetail',
+	    name: 'userdetail',
+	    meta: {
+	        title: '个人详情',
+	    },
+	},
+	{
+	    path: '/pages/user/useredit',
+	    name: 'useredit',
+	    meta: {
+	        title: '个人编辑',
+	    },
+	},
+	{
+	    path: '/pages/user/userexit',
+	    name: 'userexit',
+	    meta: {
+	        title: '退出',
+	    },
+	},
+	{
+	    path: '/pages/user/location',
+	    name: 'location',
+	    meta: {
+	        title: '定位',
+	    },
+	},
+	{
+	    path: '/pages/common/exit',
+	    name: 'exit',
+	    meta: {
+	        title: '退出',
+	    },
+	},
+	{
+	    path: '/pages/common/success',
+	    name: 'success',
+	    meta: {
+	        title: 'success',
+	    },
+	},{
+	  path: '/pages/addressbook/address-book',
+	  name: 'addressBook',
+	    meta: {
+	        title: 'addressBook',
+	    },
+	},
+	{
+	  path: '/pages/addressbook/level-address-book',
+	  name: 'levelAddressBook',
+	    meta: {
+	        title: 'levelAddressBook',
+	    },
+	},
+	{
+	  path: '/pages/addressbook/member',
+	  name: 'member',
+	    meta: {
+	        title: 'member',
+	    },
+	},
+	{
+	  path: '/pages/addressbook/address-detail',
+	  name: 'addressDetail',
+	    meta: {
+	        title: 'addressDetail',
+	    },
+	},
+	{
+	    path: '/pages/annotation/annotationList',
+	    name: 'annotationList',
+	    meta: {
+	        title: '通知公告',
+	    },
+	},
+	{
+	    path: '/pages/annotation/annotationDetail',
+	    name: 'annotationDetail',
+	    meta: {
+	        title: '通知详情',
+	    },
+	},
+	{
+	    path: '/pages/common/helloWorld',
+	    name: 'helloWorld',
+	    meta: {
+	        title: 'helloWorld',
+	    },
+	},
+]
+export default routes

+ 18 - 0
common/service/config.service.js

@@ -0,0 +1,18 @@
+let BASE_URL = ''
+
+
+if (process.env.NODE_ENV == 'development') {
+	BASE_URL = 'http://182.92.126.35:9999' // 开发环境
+	// BASE_URL = 'http://boot.jeecg.org:8080/jeecg-boot' // 开发环境
+} else {
+	// BASE_URL = 'http://boot.jeecg.org:8080/jeecg-boot' // 生产环境
+	BASE_URL = 'http://182.92.126.35:9999' // 生产环境
+}
+let staticDomainURL = BASE_URL+ '/sys/common/static';
+
+const configService = {
+	apiUrl: BASE_URL,
+	staticDomainURL: staticDomainURL
+};
+
+export default configService

+ 108 - 0
common/service/service.js

@@ -0,0 +1,108 @@
+import Request from '@/common/luch-request/index.js'
+import {ACCESS_TOKEN} from '@/common/util/constants.js'
+import configService from './config.service.js'
+import tip from '@/common/util/tip.js';
+import store from '@/store/index.js';
+
+let apiUrl = configService.apiUrl;
+
+const getTokenStorage = () => {
+	let token = ''
+	try{
+		token = uni.getStorageSync(ACCESS_TOKEN)
+	}catch(e){
+		//TODO handle the exception
+		console.log("getTokenStorage",token)
+	}
+	return token
+}
+
+
+
+const http = new Request()
+http.setConfig((config) => { /* 设置全局配置 */
+  config.baseUrl = apiUrl /* 根域名不同 */
+  config.header = {
+    ...config.header
+  }
+  return config
+})
+
+/**
+ * 自定义验证器,如果返回true 则进入响应拦截器的响应成功函数(resolve),否则进入响应拦截器的响应错误函数(reject)
+ * @param { Number } statusCode - 请求响应体statusCode(只读)
+ * @return { Boolean } 如果为true,则 resolve, 否则 reject
+ */
+// 有默认,非必写
+http.validateStatus = (statusCode) => {
+  return statusCode === 200
+}
+
+http.interceptor.request((config, cancel) => { /* 请求之前拦截器 */
+  config.header = {
+    ...config.header,
+     'X-Access-Token':getTokenStorage()
+  }
+  /*
+  if (!token) { // 如果token不存在,调用cancel 会取消本次请求,但是该函数的catch() 仍会执行
+    cancel('token 不存在') // 接收一个参数,会传给catch((err) => {}) err.errMsg === 'token 不存在'
+  }
+  */
+  return config
+})
+
+// 必须使用异步函数,注意
+http.interceptor.response(async (response) => { /* 请求之后拦截器 */
+  // if (response.data.code !== 200) { // 服务端返回的状态码不等于200,则reject()
+  //   return Promise.reject(response)
+  // }
+  return response
+}, (response) => {
+	// 请求错误做点什么
+  console.log("请求错误做点什么",response);
+  if (response) {
+      let data = response.data
+      const token = uni.getStorageSync(ACCESS_TOKEN)
+      console.log("------异常响应------",token)
+      console.log("------异常响应------",data.status)
+      switch (data.status) {
+        case 403:
+          tip.error('拒绝访问');
+          break
+        case 500:
+          if(!token || data.message=="Token失效,请重新登录"){
+            let timeout=setTimeout(tip.alert('登录已过期'), 1000);
+            store.dispatch('Logout').then(() => {
+				clearTimeout(timeout)
+                window.location.reload()
+            }) 
+          }
+          break
+        case 404:
+          break
+        case 504:
+         break
+        case 401:
+          if (token) {
+           /* store.dispatch('Logout').then(() => {
+              setTimeout(() => {
+                window.location.reload()
+              }, 1500)
+            }) */
+          }
+          break
+        default:
+          tip.error({
+            duration: 0,
+            forbidClick: true,
+            message: data.message
+          });
+          break
+      }
+    }
+  return response
+})
+
+export {
+  http
+}

+ 101 - 0
common/util/MinCache.js

@@ -0,0 +1,101 @@
+let cacheMap =  new Map()
+let timeoutDefault = 1200
+ 
+function isTimeout (name) {
+  const data = cacheMap.get(name)
+  if (!data) return true
+  if (data.timeout === 0) return false 
+  const currentTime = Date.now()
+  const overTime = (currentTime - data.createTime) / 1000
+  if (overTime > data.timeout) {
+    cacheMap.delete(name)
+    if (name.startsWith('_')) {
+      try {
+        uni.removeStorageSync(name)
+      } catch (e) {
+        console.log(e)
+      }
+    }
+    return true
+  }
+  return false
+}
+ 
+class CacheCell {
+  constructor (data, timeout) {
+    this.data = data
+    this.timeout = timeout
+    this.createTime = Date.now()
+  }
+}
+ 
+class MinCache {
+  constructor (timeout) {
+    try {
+      const res = uni.getStorageInfoSync()
+      res.keys.forEach(name => {
+        try {
+          const value = uni.getStorageSync(name)
+          cacheMap.set(name, value)
+        } catch (e) {
+          console.log(e)
+        }
+      })
+    } catch (e) {
+      console.log(e)
+    }
+    timeoutDefault = timeout
+  }
+  set (name, data, timeout = timeoutDefault) {
+    const cachecell = new CacheCell(data, timeout)
+    let cache = null
+    if (name.startsWith('_')) {
+      try {
+        uni.setStorageSync(name, cachecell)
+        cache = cacheMap.set(name, cachecell)
+      } catch (e) {
+        console.log(e)
+      }
+    } else {
+      cache = cacheMap.set(name, cachecell)
+    }
+    return cache
+  }
+  get (name) {
+    return isTimeout(name) ? null : cacheMap.get(name).data
+  }
+  delete (name) {
+    let value = false
+    if (name.startsWith('_')) {
+      try {
+        uni.removeStorageSync(name)
+        value = cacheMap.delete(name)
+      } catch (e) {
+        console.log(e)
+      }
+    } else {
+      value = cacheMap.delete(name)
+    }
+    return value
+  }
+  has (name) {
+    return !isTimeout(name)
+  }
+  clear () {
+    let value = false
+    try {
+      uni.clearStorageSync()
+      cacheMap.clear()
+      value = true
+    } catch (e) {
+      console.log(e)
+    }
+    return value
+  }
+}
+ 
+MinCache.install = function (Vue, {timeout = 1200} = {}) {
+  Vue.prototype.$cache = new MinCache(timeout)
+}
+ 
+export default MinCache

+ 73 - 0
common/util/appUpdate.js

@@ -0,0 +1,73 @@
+//APP更新
+
+export default function appUpdate() {
+	uni.request({
+		url: 'http://app.jeecg.com/update.json', //检查更新的服务器地址
+		data: {
+			appid: plus.runtime.appid,
+			version: plus.runtime.version,
+			imei: plus.device.imei
+		},
+		success: (res) => {
+			plus.runtime.getProperty(plus.runtime.appid, function(wgtinfo) {
+				let client_version = wgtinfo.version
+				var flag_update = client_version.split(".").splice(0, 2).join(".") != res.data.version.split(".").splice(0, 2)
+					.join(".")
+				var flag_hot = (Number(client_version.split(".")[2]) < Number(res.data.version.split(".")[2])) & !flag_update
+				console.log(client_version)
+				console.log(flag_update)
+				console.log(flag_hot)
+
+				if (flag_update) {
+					// 提醒用户更新
+					uni.showModal({
+						title: '更新提示',
+						content: res.data.note,
+						success: (showResult) => {
+							if (showResult.confirm) {
+								plus.nativeUI.toast("正在准备环境,请稍后!");
+								console.log(res.data.url, )
+								var dtask = plus.downloader.createDownload(res.data.url, {
+									method: 'GET',
+									filename: '_doc/update/'
+								}, function(d, status) {
+									if (status == 200) {
+										var path = d.filename; //下载apk
+										plus.runtime.install(path); // 自动安装apk文件
+									} else {
+										plus.nativeUI.alert('版本更新失败:' + status);
+									}
+								});
+								dtask.start();
+							}
+						}
+					})
+				} else if (flag_hot) {
+					uni.downloadFile({
+						url: res.data.wgtUrl,
+						success: (downloadResult) => {
+							console.log(downloadResult.tempFilePath)
+							if (downloadResult.statusCode === 200) {
+								plus.nativeUI.toast(`正在热更新!${res.data.versionCode}`);
+								plus.runtime.install(downloadResult.tempFilePath, {
+									force: false
+								}, function() {
+									plus.nativeUI.toast("热更新成功");
+									plus.runtime.restart();
+								}, function(e) {
+									console.log(e)
+									plus.nativeUI.toast(`热更新失败:${e.message}`);
+								});
+							}
+						}
+					});
+				}
+
+			});
+
+
+
+
+		}
+	})
+}

+ 14 - 0
common/util/constants.js

@@ -0,0 +1,14 @@
+export const ACCESS_TOKEN = 'Access-Token'
+export const USER_NAME = 'login_username'
+export const USER_INFO = 'login_user_info'
+
+
+const STORAGE_OPTIONS = {
+  namespace: 'pro__', // key prefix
+    name: 'ls', // name variable Vue.[ls] or this.[$ls],
+    storage: 'local', // storage name session, local, memory
+}
+
+export default STORAGE_OPTIONS;
+
+

+ 130 - 0
common/util/tip.js

@@ -0,0 +1,130 @@
+/**
+ * 提示与加载工具类
+ */
+export default class Tips {
+  constructor() {
+    this.isLoading = false;
+  }
+  /**
+   * 弹出提示框
+   */
+
+  static success(title, duration = 1000) {
+    setTimeout(() => {
+      uni.showToast({
+        title: title,
+        icon: "success",
+        mask: true,
+        duration: duration
+      });
+    }, 300);
+    if (duration > 0) {
+      return new Promise((resolve, reject) => {
+        setTimeout(() => {
+          resolve();
+        }, duration);
+      });
+    }
+  }
+
+  /**
+   * 弹出确认窗口
+   */
+  static confirm(text,showCancel, payload = {}, title = "提示") {
+    return new Promise((resolve, reject) => {
+      uni.showModal({
+        title: title,
+        content: text,
+        showCancel: showCancel,
+        success: res => {
+          if (res.confirm) {
+            resolve(payload);
+          } else if (res.cancel) {
+            reject(payload);
+          }
+        },
+        fail: res => {
+          reject(payload);
+        }
+      });
+    });
+  }
+
+  static toast(title, onHide, icon = "none") {
+    setTimeout(() => {
+      uni.showToast({
+        title: title,
+        icon: icon,
+        mask: true,
+        duration:1000
+      });
+    }, 300);
+
+    // 隐藏结束回调
+    if (onHide) {
+      setTimeout(() => {
+        onHide();
+      }, 500);
+    }
+  }
+
+  /**
+   * 警告框
+   */
+  static alert(title) {
+    uni.showToast({
+      title: title,
+      image: "../../static/alert.png",
+      mask: true,
+      duration: 1500
+    });
+  }
+
+  /**
+   * 错误框
+   */
+
+  static error(title, onHide) {
+    uni.showToast({
+      title: title,
+      image: "../../static/error.png",
+      mask: true,
+      duration: 1500
+    });
+    // 隐藏结束回调
+    if (onHide) {
+      setTimeout(() => {
+        onHide();
+      }, 500);
+    }
+  }
+
+  /**
+   * 弹出加载提示
+   */
+  static loading(title = "加载中") {
+    if (Tips.isLoading) {
+      return;
+    }
+    Tips.isLoading = true;
+    uni.showLoading({
+      title: title,
+      mask: true
+    });
+  }
+
+  /**
+   * 加载完毕
+   */
+  static loaded() {
+    if (Tips.isLoading) {
+      Tips.isLoading = false;
+      uni.hideLoading();
+    }
+  }
+}
+
+/**
+ * 静态变量,是否加载中
+ */
+Tips.isLoading = false;

+ 37 - 0
common/util/vue-py.js

@@ -0,0 +1,37 @@
+import { pinyin } from './constants.js';
+export default {
+    chineseToPinYin: function (l1) {
+        var l2 = l1.length;
+        var I1 = '';
+        var reg = new RegExp('[a-zA-Z0-9]');
+        for (var i = 0; i < l2; i++) {
+            var val = l1.substr(i, 1);
+            var name = this.arraySearch(val, pinyin);
+            if (reg.test(val)) {
+                I1 += val;
+            } else if (name !== false) {
+                I1 += name;
+            }
+        }
+        I1 = I1.replace(/ /g, '-');
+        while (I1.indexOf('--') > 0) {
+            I1 = I1.replace('--', '-');
+        }
+        return I1;
+    },
+    arraySearch: function (l1, l2) {
+        for (var name in pinyin) {
+            if (pinyin[name].indexOf(l1) !== -1) {
+                return this.ucfirst(name);
+            }
+        }
+        return false;
+    },
+    ucfirst: function (l1) {
+        if (l1.length > 0) {
+            var first = l1.substr(0, 1).toUpperCase();
+            var spare = l1.substr(1, l1.length);
+            return first + spare;
+        }
+    }
+};

+ 124 - 0
common/util/work.js

@@ -0,0 +1,124 @@
+/**
+ * 常用服务
+ * useful server
+ */
+const  icon_prefix="/static/home/128/"
+
+export const us = {
+  data:[
+    {
+      title:"日报",
+      icon:icon_prefix+"richang.png",
+      description:"记录每天的工作经验和心得",
+      useCount:1000,
+	  page:'helloWorld'
+    },{
+      title:"周报",
+      icon:icon_prefix+"zhoubao.png",
+      description:"总结每周的工作情况和下周计划",
+      useCount:10000,
+	  page:'helloWorld'
+    },{
+      title:"考勤",
+      icon:icon_prefix+"kaoqin.png",
+      description:"工作考勤",
+      useCount:10000,
+	  page:'helloWorld'
+    },{
+      title:"日程",
+      icon:icon_prefix+"richeng.png",
+      description:"建立和查看个人工作安排",
+      useCount:10000,
+	  page:'helloWorld'
+    },{
+      title:"请假申请",
+      icon:icon_prefix+"qingjia1.png",
+      description:"请假申请",
+      useCount:10000,
+	  page:'helloWorld'
+    },{
+	  title:"出差申请",
+	  icon:icon_prefix+"chuchai.png",
+	  description:"出差申请",
+	  useCount:10000,
+	  page:'helloWorld'
+    },{
+	  title:"公文发文",
+	  icon:icon_prefix+"gongwen.png",
+	  description:"公文发文",
+	  useCount:10000,
+	  page:'helloWorld'
+    },{
+	  title:"通知公告",
+	  icon:icon_prefix+"tongzhi.png",
+	  description:"查看企业对员工下发的通知公告",
+	  useCount:10000,
+	  page:'annotationList'
+    },{
+	  title:"内部邮件",
+	  icon:icon_prefix+"youjian.png",
+	  description:"查看内部消息",
+	  useCount:10000,
+	  dot:false,
+	  page:'helloWorld'
+    },{
+	  title:"通讯录",
+	  icon:icon_prefix+"tongxun.png",
+	  description:"查看部门,组员",
+	  useCount:10000,
+	  page:'levelAddressBook'
+    }
+  ]
+}
+
+
+/**
+ * other server 其他服务
+ */
+export const os = {
+  data:[
+    {
+      title:"新闻中心",
+      icon:icon_prefix+"xinwen.png",
+      description:"新闻中心",
+      useCount:10000,
+	  page:'helloWorld'
+    },{
+      title:"投票中心",
+      icon:icon_prefix+"toupiao.png",
+      description:"投票中心",
+      useCount:10000,
+	  page:'helloWorld'
+    },{
+      title:"任务中心",
+      icon:icon_prefix+"renwu.png",
+      description:"任务中心",
+      useCount:10000,
+	  page:'helloWorld'
+    },{
+      title:"文档中心",
+      icon:icon_prefix+"wendang.png",
+      description:"文档中心",
+      useCount:10000,
+	  page:'helloWorld'
+    },{
+      title:"合同",
+      icon:icon_prefix+"hetong.png",
+      description:"合同",
+      useCount:10000,
+	  page:'helloWorld'
+    },{
+      title:"会议",
+      icon:icon_prefix+"huiyi.png",
+      description:"会议",
+      useCount:10000,
+	  page:'helloWorld'
+    },{
+      title:"客户关系",
+      icon:icon_prefix+"tongzhi.png",
+      description:"客户关系",
+      useCount:10000,
+	  page:'helloWorld'
+    }
+  ]
+}

+ 83 - 0
components/mescroll-uni/app-tabs.vue

@@ -0,0 +1,83 @@
+<template>
+	<view v-if="tabs && tabs.length" class="app-tabs" :class="{'tabs-fixed': fixed}">
+		<view class="tabs-item">
+			<view class="tab-item" v-for="(tab, i) in tabs" :class="{'active': value===i}" :key="i" @click="tabClick(i)">
+				{{getTabName(tab)}}
+			</view>
+		</view>
+		<view class="tabs-line" :style="{left:lineLift}"></view>
+	</view>
+</template>
+
+<script>
+	export default {
+		props:{
+			tabs: Array,
+			value: { // 当前显示的下标 (使用v-model语法糖: 1.props需为value; 2.需回调input事件)
+				type: [String, Number],
+				default(){
+					return 0
+				}
+			},
+			fixed: Boolean // 是否悬浮,默认false
+		},
+		computed: {
+			lineLift() {
+				return 100/this.tabs.length*(this.value + 1) - 100/(this.tabs.length*2) + '%'
+			}
+		},
+		methods: {
+			getTabName(tab){
+				return typeof tab === "object" ? tab.name : tab
+			},
+			tabClick(i){
+				if(this.value!=i){
+					this.$emit("input",i);
+					this.$emit("change",i);
+				}
+			}
+		}
+	}
+</script>
+
+<style>
+	.app-tabs{
+		position: relative;
+		height: 80rpx;
+		line-height: 80rpx;
+		font-size: 24rpx;
+		background-color: #fff;
+		border-bottom: 1rpx solid #eee;
+	}
+	.app-tabs .tabs-item{
+		display: flex;
+		text-align: center;
+		font-size: 28rpx;
+	}
+	.app-tabs .tabs-item .tab-item{
+		flex: 1;
+	}
+	.app-tabs .tabs-item .active{
+		color: red;
+	}
+	.app-tabs .tabs-line{
+		position: absolute;
+		bottom: 0;
+		width: 150rpx;
+		height: 4rpx;
+		transform: translateX(-50%);
+		border-radius: 4rpx;
+		transition: left .3s;
+		background: red;
+	}
+	
+	/*悬浮*/
+	.app-tabs.tabs-fixed{
+		z-index: 9999;
+		position: fixed;
+		top: var(--window-top);
+		left: 0;
+		width: 100%;
+	}
+</style>
+

+ 55 - 0
components/mescroll-uni/components/mescroll-down.css

@@ -0,0 +1,55 @@
+/* 下拉刷新区域 */
+.mescroll-downwarp {
+	position: absolute;
+	top: -100%;
+	left: 0;
+	width: 100%;
+	height: 100%;
+	text-align: center;
+}
+
+/* 下拉刷新--内容区,定位于区域底部 */
+.mescroll-downwarp .downwarp-content {
+	position: absolute;
+	left: 0;
+	bottom: 0;
+	width: 100%;
+	min-height: 60rpx;
+	padding: 20rpx 0;
+	text-align: center;
+}
+
+/* 下拉刷新--提示文本 */
+.mescroll-downwarp .downwarp-tip {
+	display: inline-block;
+	font-size: 28rpx;
+	vertical-align: middle;
+	margin-left: 16rpx;
+	/* color: gray; 已在style设置color,此处删去*/
+}
+
+/* 下拉刷新--旋转进度条 */
+.mescroll-downwarp .downwarp-progress {
+	display: inline-block;
+	width: 32rpx;
+	height: 32rpx;
+	border-radius: 50%;
+	border: 2rpx solid gray;
+	border-bottom-color: transparent !important; /*已在style设置border-color,此处需加 !important*/
+	vertical-align: middle;
+}
+
+/* 旋转动画 */
+.mescroll-downwarp .mescroll-rotate {
+	animation: mescrollDownRotate 0.6s linear infinite;
+}
+
+@keyframes mescrollDownRotate {
+	0% {
+		transform: rotate(0deg);
+	}
+
+	100% {
+		transform: rotate(360deg);
+	}
+}

+ 47 - 0
components/mescroll-uni/components/mescroll-down.vue

@@ -0,0 +1,47 @@
+<!-- 下拉刷新区域 -->
+<template>
+	<view v-if="mOption.use" class="mescroll-downwarp" :style="{'background-color':mescroll.optDown.bgColor,'color':mescroll.optDown.textColor}">
+		<view class="downwarp-content">
+			<view class="downwarp-progress" :class="{'mescroll-rotate': isDownLoading}" :style="{'border-color':mescroll.optDown.textColor, 'transform':downRotate}"></view>
+			<view class="downwarp-tip">{{downText}}</view>
+		</view>
+	</view>
+</template>
+
+<script>
+export default {
+	props: {
+		option: Object , // down的配置项
+		type: Number, // 下拉状态(inOffset:1, outOffset:2, showLoading:3, endDownScroll:4)
+		rate: Number // 下拉比率 (inOffset: rate<1; outOffset: rate>=1)
+	},
+	computed: {
+		// 支付宝小程序需写成计算属性,prop定义default仍报错
+		mOption(){
+			return this.option || {}
+		},
+		// 是否在加载中
+		isDownLoading(){
+			return this.type === 3
+		},
+		// 旋转的角度
+		downRotate(){
+			return 'rotate(' + 360 * this.rate + 'deg)'
+		},
+		// 文本提示
+		downText(){
+			switch (this.type){
+				case 1: return this.mOption.textInOffset;
+				case 2: return this.mOption.textOutOffset;
+				case 3: return this.mOption.textLoading;
+				case 4: return this.mOption.textLoading;
+				default: return this.mOption.textInOffset;
+			}
+		}
+	}
+};
+</script>
+
+<style>
+@import "./mescroll-down.css";
+</style>

+ 91 - 0
components/mescroll-uni/components/mescroll-empty.vue

@@ -0,0 +1,91 @@
+<!--空布局
+
+可作为独立的组件, 不使用mescroll的页面也能单独引入, 以便APP全局统一管理:
+import MescrollEmpty from '@/components/mescroll-uni/components/mescroll-empty.vue';
+<mescroll-empty v-if="isShowEmpty" :option="optEmpty" @emptyclick="emptyClick"></mescroll-empty>
+
+-->
+<template>
+	<view class="mescroll-empty" :class="{ 'empty-fixed': option.fixed }" :style="{ 'z-index': option.zIndex, top: option.top }">
+		<image v-if="icon" class="empty-icon" src="/static/nocontent-1.png" mode="widthFix" />
+		<view v-if="tip" class="empty-tip">{{ tip }}</view>
+		<view v-if="option.btnText" class="empty-btn" @click="emptyClick">{{ option.btnText }}</view>
+	</view>
+</template>
+
+<script>
+// 引入全局配置
+import GlobalOption from './../mescroll-uni-option.js';
+export default {
+	props: {
+		// empty的配置项: 默认为GlobalOption.up.empty
+		option: {
+			type: Object,
+			default() {
+				return {
+				};
+			}
+		}
+	},
+	// 使用computed获取配置,用于支持option的动态配置
+	computed: {
+		// 图标
+		icon() {
+			return this.option.icon == null ? GlobalOption.up.empty.icon : this.option.icon; // 此处不使用短路求值, 用于支持传空串不显示图标
+		},
+		// 文本提示
+		tip() {
+			return this.option.tip == null ? GlobalOption.up.empty.tip : this.option.tip; // 此处不使用短路求值, 用于支持传空串不显示文本提示
+		}
+	},
+	methods: {
+		// 点击按钮
+		emptyClick() {
+			this.$emit('emptyclick');
+		}
+	}
+};
+</script>
+
+<style>
+/* 无任何数据的空布局 */
+.mescroll-empty {
+	box-sizing: border-box;
+	width: 100%;
+	padding: 60% 50rpx;
+	text-align: center;
+}
+
+.mescroll-empty.empty-fixed {
+	z-index: 99;
+	position: absolute; /*transform会使fixed失效,最终会降级为absolute */
+	top: 100rpx;
+	left: 0;
+}
+
+.mescroll-empty .empty-icon {
+	width: 280rpx;
+	height: 280rpx;
+}
+
+.mescroll-empty .empty-tip {
+	font-size: 24rpx;
+	margin-top: -80rpx;
+	color: gray;
+}
+
+.mescroll-empty .empty-btn {
+	display: inline-block;
+	margin-top: 40rpx;
+	min-width: 200rpx;
+	padding: 18rpx;
+	font-size: 28rpx;
+	border: 1rpx solid #e04b28;
+	border-radius: 60rpx;
+	color: #e04b28;
+}
+
+.mescroll-empty .empty-btn:active {
+	opacity: 0.75;
+}
+</style>

+ 81 - 0
components/mescroll-uni/components/mescroll-top.vue

@@ -0,0 +1,81 @@
+<!-- 回到顶部的按钮 -->
+<template>
+	<image
+		v-if="mOption.src"
+		class="mescroll-totop"
+		:class="[value ? 'mescroll-totop-in' : 'mescroll-totop-out', {'mescroll-safe-bottom': mOption.safearea}]"
+		:style="{'z-index':mOption.zIndex, 'left': left, 'right': right, 'bottom':addUnit(mOption.bottom), 'width':addUnit(mOption.width), 'border-radius':addUnit(mOption.radius)}"
+		:src="mOption.src"
+		mode="widthFix"
+		@click="toTopClick"
+	/>
+</template>
+
+<script>
+export default {
+	props: {
+		// up.toTop的配置项
+		option: Object,
+		// 是否显示
+		value: false
+	},
+	computed: {
+		// 支付宝小程序需写成计算属性,prop定义default仍报错
+		mOption(){
+			return this.option || {}
+		},
+		// 优先显示左边
+		left(){
+			return this.mOption.left ? this.addUnit(this.mOption.left) : 'auto';
+		},
+		// 右边距离 (优先显示左边)
+		right() {
+			return this.mOption.left ? 'auto' : this.addUnit(this.mOption.right);
+		}
+	},
+	methods: {
+		addUnit(num){
+			if(!num) return 0;
+			if(typeof num === 'number') return num + 'rpx';
+			return num
+		},
+		toTopClick() {
+			this.$emit('input', false); // 使v-model生效
+			this.$emit('click'); // 派发点击事件
+		}
+	}
+};
+</script>
+
+<style>
+/* 回到顶部的按钮 */
+.mescroll-totop {
+	z-index: 9990;
+	position: fixed !important; /* 加上important避免编译到H5,在多mescroll中定位失效 */
+	right: 20rpx;
+	bottom: 120rpx;
+	width: 72rpx;
+	height: auto;
+	border-radius: 50%;
+	opacity: 0;
+	transition: opacity 0.5s; /* 过渡 */
+	margin-bottom: var(--window-bottom); /* css变量 */
+}
+
+ /* 适配 iPhoneX */
+.mescroll-safe-bottom{
+	margin-bottom: calc(var(--window-bottom) + constant(safe-area-inset-bottom)); /* window-bottom + 适配 iPhoneX */
+	margin-bottom: calc(var(--window-bottom) + env(safe-area-inset-bottom));
+}
+
+/* 显示 -- 淡入 */
+.mescroll-totop-in {
+	opacity: 1;
+}
+
+/* 隐藏 -- 淡出且不接收事件*/
+.mescroll-totop-out {
+	opacity: 0;
+	pointer-events: none;
+}
+</style>

+ 46 - 0
components/mescroll-uni/components/mescroll-up.css

@@ -0,0 +1,46 @@
+/* 上拉加载区域 */
+.mescroll-upwarp {
+	min-height: 60rpx;
+	padding: 30rpx 0;
+	text-align: center;
+	clear: both;
+}
+
+/*提示文本 */
+.mescroll-upwarp .upwarp-tip,
+.mescroll-upwarp .upwarp-nodata {
+	display: inline-block;
+	font-size: 28rpx;
+	vertical-align: middle;
+	/* color: gray; 已在style设置color,此处删去*/
+}
+
+.mescroll-upwarp .upwarp-tip {
+	margin-left: 16rpx;
+}
+
+/*旋转进度条 */
+.mescroll-upwarp .upwarp-progress {
+	display: inline-block;
+	width: 32rpx;
+	height: 32rpx;
+	border-radius: 50%;
+	border: 2rpx solid gray;
+	border-bottom-color: transparent !important; /*已在style设置border-color,此处需加 !important*/
+	vertical-align: middle;
+}
+
+/* 旋转动画 */
+.mescroll-upwarp .mescroll-rotate {
+	animation: mescrollUpRotate 0.6s linear infinite;
+}
+
+@keyframes mescrollUpRotate {
+	0% {
+		transform: rotate(0deg);
+	}
+
+	100% {
+		transform: rotate(360deg);
+	}
+}

+ 39 - 0
components/mescroll-uni/components/mescroll-up.vue

@@ -0,0 +1,39 @@
+<!-- 上拉加载区域 -->
+<template>
+	<view class="mescroll-upwarp" :style="{'background-color':mescroll.optUp.bgColor,'color':mescroll.optUp.textColor}">
+		<!-- 加载中 (此处不能用v-if,否则android小程序快速上拉可能会不断触发上拉回调) -->
+		<view v-show="isUpLoading">
+			<view class="upwarp-progress mescroll-rotate" :style="{'border-color':mescroll.optUp.textColor}"></view>
+			<view class="upwarp-tip">{{ mOption.textLoading }}</view>
+		</view>
+		<!-- 无数据 -->
+		<view v-if="isUpNoMore" class="upwarp-nodata">{{ mOption.textNoMore }}</view>
+	</view>
+</template>
+
+<script>
+export default {
+	props: {
+		option: Object, // up的配置项
+		type: Number // 上拉加载的状态:0(loading前),1(loading中),2(没有更多了)
+	},
+	computed: {
+		// 支付宝小程序需写成计算属性,prop定义default仍报错
+		mOption() {
+			return this.option || {};
+		},
+		// 加载中
+		isUpLoading() {
+			return this.type === 1;
+		},
+		// 没有更多了
+		isUpNoMore() {
+			return this.type === 2;
+		}
+	}
+};
+</script>
+
+<style>
+@import './mescroll-up.css';
+</style>

+ 10 - 0
components/mescroll-uni/mescroll-body.css

@@ -0,0 +1,10 @@
+page {
+	-webkit-overflow-scrolling: touch; /* 使iOS滚动流畅 */
+}
+
+.mescroll-body {
+	position: relative; /* 下拉刷新区域相对自身定位 */
+	height: auto; /* 不可固定高度,否则overflow: hidden, 可通过设置最小高度使列表不满屏仍可下拉*/
+	overflow: hidden; /* 遮住顶部下拉刷新区域 */
+	box-sizing: border-box; /* 避免设置padding出现双滚动条的问题 */
+}

+ 292 - 0
components/mescroll-uni/mescroll-body.vue

@@ -0,0 +1,292 @@
+<template>
+	<view class="mescroll-body" :style="{'minHeight':minHeight, 'padding-top': padTop, 'padding-bottom': padBottom, 'padding-bottom': padBottomConstant, 'padding-bottom': padBottomEnv }" @touchstart="touchstartEvent" @touchmove="touchmoveEvent" @touchend="touchendEvent" @touchcancel="touchendEvent" >
+		<view class="mescroll-body-content" :style="{ transform: translateY, transition: transition }">
+			<!-- 下拉加载区域 (支付宝小程序子组件传参给子子组件仍报单项数据流的异常,暂时不通过mescroll-down组件实现)-->
+			<!-- <mescroll-down :option="mescroll.optDown" :type="downLoadType" :rate="downRate"></mescroll-down> -->
+			<view v-if="mescroll.optDown.use" class="mescroll-downwarp" :style="{'background-color':mescroll.optDown.bgColor,'color':mescroll.optDown.textColor}">
+				<view class="downwarp-content">
+					<view class="downwarp-progress" :class="{'mescroll-rotate': isDownLoading}" :style="{'border-color':mescroll.optDown.textColor, 'transform': downRotate}"></view>
+					<view class="downwarp-tip">{{downText}}</view>
+				</view>
+			</view>
+	
+			<!-- 列表内容 -->
+			<slot></slot>
+
+			<!-- 空布局 -->
+			<mescroll-empty v-if="isShowEmpty" :option="mescroll.optUp.empty" @emptyclick="emptyClick"></mescroll-empty>
+
+			<!-- 上拉加载区域 (下拉刷新时不显示, 支付宝小程序子组件传参给子子组件仍报单项数据流的异常,暂时不通过mescroll-up组件实现)-->
+			<!-- <mescroll-up v-if="mescroll.optUp.use && !isDownLoading" :option="mescroll.optUp" :type="upLoadType"></mescroll-up> -->
+			<view v-if="mescroll.optUp.use && !isDownLoading" class="mescroll-upwarp" :style="{'background-color':mescroll.optUp.bgColor,'color':mescroll.optUp.textColor}">
+				<!-- 加载中 (此处不能用v-if,否则android小程序快速上拉可能会不断触发上拉回调) -->
+				<view v-show="upLoadType===1">
+					<view class="upwarp-progress mescroll-rotate" :style="{'border-color':mescroll.optUp.textColor}"></view>
+					<view class="upwarp-tip">{{ mescroll.optUp.textLoading }}</view>
+				</view>
+				<!-- 无数据 -->
+				<view v-if="upLoadType===2" class="upwarp-nodata">{{ mescroll.optUp.textNoMore }}</view>
+			</view>
+		</view>
+
+		<!-- 回到顶部按钮 (fixed元素需写在transform外面,防止降级为absolute)-->
+		<mescroll-top v-model="isShowToTop" :option="mescroll.optUp.toTop" @click="toTopClick"></mescroll-top>
+	</view>
+</template>
+
+<script>
+	// 引入mescroll-uni.js,处理核心逻辑
+	import MeScroll from './mescroll-uni.js';
+	// 引入全局配置
+	import GlobalOption from './mescroll-uni-option.js';
+	// 引入空布局组件
+	import MescrollEmpty from './components/mescroll-empty.vue';
+	// 引入回到顶部组件
+	import MescrollTop from './components/mescroll-top.vue';
+	
+	export default {
+		components: {
+			MescrollEmpty,
+			MescrollTop
+		},
+		data() {
+			return {
+				mescroll: {optDown:{},optUp:{}}, // mescroll实例
+				downHight: 0, //下拉刷新: 容器高度
+				downRate: 0, // 下拉比率(inOffset: rate<1; outOffset: rate>=1)
+				downLoadType: 4, // 下拉刷新状态 (inOffset:1, outOffset:2, showLoading:3, endDownScroll:4)
+				upLoadType: 0, // 上拉加载状态:0(loading前),1(loading中),2(没有更多了)
+				isShowEmpty: false, // 是否显示空布局
+				isShowToTop: false, // 是否显示回到顶部按钮
+				windowHeight: 0, // 可使用窗口的高度
+				statusBarHeight: 0, // 状态栏高度
+				isSafearea: false // 支持安全区
+			};
+		},
+		props: {
+			down: Object, // 下拉刷新的参数配置
+			up: Object, // 上拉加载的参数配置
+			top: [String, Number], // 下拉布局往下的偏移量 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
+			topbar: Boolean, // top的偏移量是否加上状态栏高度, 默认false (使用场景:取消原生导航栏时,配置此项可自动加上状态栏高度的偏移量)
+			bottom: [String, Number], // 上拉布局往上的偏移量 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
+			safearea: Boolean, // bottom的偏移量是否加上底部安全区的距离, 默认false (需要适配iPhoneX时使用)
+			height: [String, Number] // 指定mescroll最小高度,默认windowHeight,使列表不满屏仍可下拉
+		},
+		computed: {
+			// mescroll最小高度,默认windowHeight,使列表不满屏仍可下拉
+			minHeight(){
+				return this.toPx(this.height || '100%') + 'px'
+			},
+			// 下拉布局往下偏移的距离 (px)
+			numTop() {
+				return this.toPx(this.top) + (this.topbar ? this.statusBarHeight : 0);
+			},
+			padTop() {
+				return this.numTop + 'px';
+			},
+			// 上拉布局往上偏移 (px)
+			numBottom() {
+				return this.toPx(this.bottom);
+			},
+			padBottom() {
+				return this.numBottom + 'px';
+			},
+			padBottomConstant() {
+				return this.isSafearea ? 'calc(' + this.padBottom + ' + constant(safe-area-inset-bottom))' : this.padBottom;
+			},
+			padBottomEnv() {
+				return this.isSafearea ? 'calc(' + this.padBottom + ' + env(safe-area-inset-bottom))' : this.padBottom;
+			},
+			// 是否为重置下拉的状态
+			isDownReset() {
+				return this.downLoadType === 3 || this.downLoadType === 4;
+			},
+			// 过渡
+			transition() {
+				return this.isDownReset ? 'transform 300ms' : this.downTransition;
+			},
+			translateY() {
+				return this.downHight > 0 ? 'translateY(' + this.downHight + 'px)' : ''; // transform会使fixed失效,需注意把fixed元素写在mescroll之外
+			},
+			// 是否在加载中
+			isDownLoading(){
+				return this.downLoadType === 3
+			},
+			// 旋转的角度
+			downRotate(){
+				return 'rotate(' + 360 * this.downRate + 'deg)'
+			},
+			// 文本提示
+			downText(){
+				switch (this.downLoadType){
+					case 1: return this.mescroll.optDown.textInOffset;
+					case 2: return this.mescroll.optDown.textOutOffset;
+					case 3: return this.mescroll.optDown.textLoading;
+					case 4: return this.mescroll.optDown.textLoading;
+					default: return this.mescroll.optDown.textInOffset;
+				}
+			}
+		},
+		methods: {
+			//number,rpx,upx,px,% --> px的数值
+			toPx(num) {
+				if (typeof num === 'string') {
+					if (num.indexOf('px') !== -1) {
+						if (num.indexOf('rpx') !== -1) {
+							// "10rpx"
+							num = num.replace('rpx', '');
+						} else if (num.indexOf('upx') !== -1) {
+							// "10upx"
+							num = num.replace('upx', '');
+						} else {
+							// "10px"
+							return Number(num.replace('px', ''));
+						}
+					} else if (num.indexOf('%') !== -1) {
+						// 传百分比,则相对于windowHeight,传"10%"则等于windowHeight的10%
+						let rate = Number(num.replace('%', '')) / 100;
+						return this.windowHeight * rate;
+					}
+				}
+				return num ? uni.upx2px(Number(num)) : 0;
+			},
+			//注册列表touchstart事件,用于下拉刷新
+			touchstartEvent(e) {
+				this.mescroll.touchstartEvent(e);
+			},
+			//注册列表touchmove事件,用于下拉刷新
+			touchmoveEvent(e) {
+				this.mescroll.touchmoveEvent(e);
+			},
+			//注册列表touchend事件,用于下拉刷新
+			touchendEvent(e) {
+				this.mescroll.touchendEvent(e);
+			},
+			// 点击空布局的按钮回调
+			emptyClick() {
+				this.$emit('emptyclick', this.mescroll);
+			},
+			// 点击回到顶部的按钮回调
+			toTopClick() {
+				this.mescroll.scrollTo(0, this.mescroll.optUp.toTop.duration); // 执行回到顶部
+				this.$emit('topclick', this.mescroll); // 派发点击回到顶部按钮的回调
+			}
+		},
+		// 使用created初始化mescroll对象; 如果用mounted部分css样式编译到H5会失效
+		created() {
+			let vm = this;
+
+			let diyOption = {
+				// 下拉刷新的配置
+				down: {
+					inOffset(mescroll) {
+						vm.downLoadType = 1; // 下拉的距离进入offset范围内那一刻的回调 (自定义mescroll组件时,此行不可删)
+					},
+					outOffset(mescroll) {
+						vm.downLoadType = 2; // 下拉的距离大于offset那一刻的回调 (自定义mescroll组件时,此行不可删)
+					},
+					onMoving(mescroll, rate, downHight) {
+						// 下拉过程中的回调,滑动过程一直在执行;
+						vm.downHight = downHight; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
+						vm.downRate = rate; //下拉比率 (inOffset: rate<1; outOffset: rate>=1)
+					},
+					showLoading(mescroll, downHight) {
+						vm.downLoadType = 3; // 显示下拉刷新进度的回调 (自定义mescroll组件时,此行不可删)
+						vm.downHight = downHight; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
+					},
+					endDownScroll(mescroll) {
+						vm.downLoadType = 4; // 结束下拉 (自定义mescroll组件时,此行不可删)
+						vm.downHight = 0; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
+					},
+					// 派发下拉刷新的回调
+					callback: function(mescroll) {
+						vm.$emit('down', mescroll);
+					}
+				},
+				// 上拉加载的配置
+				up: {
+					// 显示加载中的回调
+					showLoading() {
+						vm.upLoadType = 1;
+					},
+					// 显示无更多数据的回调
+					showNoMore() {
+						vm.upLoadType = 2;
+					},
+					// 隐藏上拉加载的回调
+					hideUpScroll() {
+						vm.upLoadType = 0;
+					},
+					// 空布局
+					empty: {
+						onShow(isShow) {
+							// 显示隐藏的回调
+							vm.isShowEmpty = isShow;
+						}
+					},
+					// 回到顶部
+					toTop: {
+						onShow(isShow) {
+							// 显示隐藏的回调
+							vm.isShowToTop = isShow;
+						}
+					},
+					// 派发上拉加载的回调
+					callback: function(mescroll) {
+						vm.$emit('up', mescroll);
+					}
+				}
+			};
+
+			MeScroll.extend(diyOption, GlobalOption); // 混入全局的配置
+			let myOption = JSON.parse(
+				JSON.stringify({
+					down: vm.down,
+					up: vm.up
+				})
+			); // 深拷贝,避免对props的影响
+			MeScroll.extend(myOption, diyOption); // 混入具体界面的配置
+
+			// 初始化MeScroll对象
+			vm.mescroll = new MeScroll(myOption, true); // 传入true,标记body为滚动区域
+			// init回调mescroll对象
+			vm.$emit('init', vm.mescroll);
+
+			// 设置高度
+			const sys = uni.getSystemInfoSync();
+			if (sys.windowHeight) vm.windowHeight = sys.windowHeight;
+			if (sys.statusBarHeight) vm.statusBarHeight = sys.statusBarHeight;
+			// 使down的bottomOffset生效
+			vm.mescroll.setBodyHeight(sys.windowHeight);
+			// mescroll-body在Android小程序下拉会卡顿,无法像mescroll-uni那样通过设置"disableScroll":true解决,只能用动画过渡缓解
+			// #ifdef MP
+			if(sys.platform == "android") vm.downTransition = 'transform 200ms'
+			// #endif
+
+			// 因为使用的是page的scroll,这里需自定义scrollTo
+			vm.mescroll.resetScrollTo((y, t) => {
+				uni.pageScrollTo({
+					scrollTop: y,
+					duration: t
+				})
+			});
+
+			// 具体的界面如果不配置up.toTop.safearea,则取本vue的safearea值
+			if(sys.platform == "ios"){
+				vm.isSafearea = vm.safearea;
+				if (vm.up && vm.up.toTop && vm.up.toTop.safearea != null) {} else {
+					vm.mescroll.optUp.toTop.safearea = vm.safearea;
+				}
+			}else{
+				vm.isSafearea = false
+				vm.mescroll.optUp.toTop.safearea = false
+			}
+		}
+	};
+</script>
+
+<style>
+	@import "./mescroll-body.css";
+	@import "./components/mescroll-down.css";
+	@import './components/mescroll-up.css';
+</style>

+ 60 - 0
components/mescroll-uni/mescroll-mixins.js

@@ -0,0 +1,60 @@
+// mescroll-body 和 mescroll-uni 通用
+
+// import MescrollUni from "./mescroll-uni.vue";
+// import MescrollBody from "./mescroll-body.vue";
+
+const MescrollMixin = {
+	// components: { // 非H5端无法通过mixin注册组件, 只能在main.js中注册全局组件或具体界面中注册
+	// 	MescrollUni,
+	// 	MescrollBody
+	// },
+	data() {
+		return {
+			mescroll: null //mescroll实例对象
+		}
+	},
+	// 注册系统自带的下拉刷新 (配置down.native为true时生效, 还需在pages配置enablePullDownRefresh:true;详请参考mescroll-native的案例)
+	onPullDownRefresh(){
+		this.mescroll && this.mescroll.onPullDownRefresh();
+	},
+	// 注册列表滚动事件,用于判定在顶部可下拉刷新,在指定位置可显示隐藏回到顶部按钮 (此方法为页面生命周期,无法在子组件中触发, 仅在mescroll-body生效)
+	onPageScroll(e) {
+		this.mescroll && this.mescroll.onPageScroll(e);
+	},
+	// 注册滚动到底部的事件,用于上拉加载 (此方法为页面生命周期,无法在子组件中触发, 仅在mescroll-body生效)
+	onReachBottom() {
+		this.mescroll && this.mescroll.onReachBottom();
+	},
+	methods: {
+		// mescroll组件初始化的回调,可获取到mescroll对象
+		mescrollInit(mescroll) {
+			this.mescroll = mescroll;
+			this.mescrollInitByRef(); // 兼容字节跳动小程序
+		},
+		// 以ref的方式初始化mescroll对象 (兼容字节跳动小程序: http://www.mescroll.com/qa.html?v=20200107#q26)
+		mescrollInitByRef() {
+			if(!this.mescroll || !this.mescroll.resetUpScroll){
+				let mescrollRef = this.$refs.mescrollRef;
+				if(mescrollRef) this.mescroll = mescrollRef.mescroll
+			}
+		},
+		// 下拉刷新的回调
+		downCallback() {
+			// mixin默认resetUpScroll
+			this.mescroll.resetUpScroll()
+		},
+		// 上拉加载的回调
+		upCallback() {
+			// mixin默认延时500自动结束加载
+			setTimeout(()=>{
+				this.mescroll.endErr();
+			}, 500)
+		}
+	},
+	mounted() {
+		this.mescrollInitByRef(); // 兼容字节跳动小程序, 避免未设置@init或@init此时未能取到ref的情况
+	}
+	
+}
+
+export default MescrollMixin;

+ 34 - 0
components/mescroll-uni/mescroll-uni-option.js

@@ -0,0 +1,34 @@
+// 全局配置
+// mescroll-body 和 mescroll-uni 通用
+const GlobalOption = {
+	down: {
+		// 其他down的配置参数也可以写,这里只展示了常用的配置:
+		textInOffset: '下拉刷新', // 下拉的距离在offset范围内的提示文本
+		textOutOffset: '释放更新', // 下拉的距离大于offset范围的提示文本
+		textLoading: '加载中 ...', // 加载中的提示文本
+		offset: 80, // 在列表顶部,下拉大于80px,松手即可触发下拉刷新的回调
+		native: false // 是否使用系统自带的下拉刷新; 默认false; 仅在mescroll-body生效 (值为true时,还需在pages配置enablePullDownRefresh:true;详请参考mescroll-native的案例)
+	},
+	up: {
+		// 其他up的配置参数也可以写,这里只展示了常用的配置:
+		textLoading: '加载中 ...', // 加载中的提示文本
+		textNoMore: '-- END --', // 没有更多数据的提示文本
+		offset: 80, // 距底部多远时,触发upCallback
+		isBounce: false, // 默认禁止橡皮筋的回弹效果, 必读事项: http://www.mescroll.com/qa.html?v=190725#q25
+		toTop: {
+			// 回到顶部按钮,需配置src才显示
+			src: "http://www.mescroll.com/img/mescroll-totop.png?v=1", // 图片路径 (建议放入static目录, 如 /static/img/mescroll-totop.png )
+			offset: 1000, // 列表滚动多少距离才显示回到顶部按钮,默认1000px
+			right: 20, // 到右边的距离, 默认20 (支持"20rpx", "20px", "20%"格式的值, 纯数字则默认单位rpx)
+			bottom: 120, // 到底部的距离, 默认120 (支持"20rpx", "20px", "20%"格式的值, 纯数字则默认单位rpx)
+			width: 72 // 回到顶部图标的宽度, 默认72 (支持"20rpx", "20px", "20%"格式的值, 纯数字则默认单位rpx)
+		},
+		empty: {
+			use: true, // 是否显示空布局
+			icon: "http://www.mescroll.com/img/mescroll-empty.png?v=1", // 图标路径 (建议放入static目录, 如 /static/img/mescroll-empty.png )
+			tip: '~ 暂无相关数据 ~' // 提示
+		}
+	}
+}
+
+export default GlobalOption

+ 29 - 0
components/mescroll-uni/mescroll-uni.css

@@ -0,0 +1,29 @@
+page {
+	height: 100%;
+	box-sizing: border-box; /* 避免设置padding出现双滚动条的问题 */
+}
+
+.mescroll-uni-warp{
+	height: 100%;
+}
+
+.mescroll-uni {
+	position: relative;
+	width: 100%;
+	height: 100%;
+	min-height: 200rpx;
+	overflow-y: auto;
+	box-sizing: border-box; /* 避免设置padding出现双滚动条的问题 */
+}
+
+/* 定位的方式固定高度 */
+.mescroll-uni-fixed{
+	z-index: 1;
+	position: fixed;
+	top: 0;
+	left: 0;
+	right: 0;
+	bottom: 0;
+	width: auto; /* 使right生效 */
+	height: auto; /* 使bottom生效 */
+}

+ 862 - 0
components/mescroll-uni/mescroll-uni.js

@@ -0,0 +1,862 @@
+/* mescroll
+ * version 1.2.5
+ * 2020-03-15 wenju
+ * http://www.mescroll.com
+ */
+
+export default function MeScroll(options, isScrollBody) {
+	let me = this;
+	me.version = '1.2.5'; // mescroll版本号
+	me.options = options || {}; // 配置
+	me.isScrollBody = isScrollBody || false; // 滚动区域是否为原生页面滚动; 默认为scroll-view
+
+	me.isDownScrolling = false; // 是否在执行下拉刷新的回调
+	me.isUpScrolling = false; // 是否在执行上拉加载的回调
+	let hasDownCallback = me.options.down && me.options.down.callback; // 是否配置了down的callback
+
+	// 初始化下拉刷新
+	me.initDownScroll();
+	// 初始化上拉加载,则初始化
+	me.initUpScroll();
+
+	// 自动加载
+	setTimeout(function() { // 待主线程执行完毕再执行,避免new MeScroll未初始化,在回调获取不到mescroll的实例
+		// 自动触发下拉刷新 (只有配置了down的callback才自动触发下拉刷新)
+		if (me.optDown.use && me.optDown.auto && hasDownCallback) {
+			if (me.optDown.autoShowLoading) {
+				me.triggerDownScroll(); // 显示下拉进度,执行下拉回调
+			} else {
+				me.optDown.callback && me.optDown.callback(me); // 不显示下拉进度,直接执行下拉回调
+			}
+		}
+		// 自动触发上拉加载
+		setTimeout(function(){ // 延时确保先执行down的callback,再执行up的callback,因为部分小程序emit是异步,会导致isUpAutoLoad判断有误
+			me.optUp.use && me.optUp.auto && !me.isUpAutoLoad && me.triggerUpScroll();
+		},100)
+	}, 30); // 需让me.optDown.inited和me.optUp.inited先执行
+}
+
+/* 配置参数:下拉刷新 */
+MeScroll.prototype.extendDownScroll = function(optDown) {
+	// 下拉刷新的配置
+	MeScroll.extend(optDown, {
+		use: true, // 是否启用下拉刷新; 默认true
+		auto: true, // 是否在初始化完毕之后自动执行下拉刷新的回调; 默认true
+		native: false, // 是否使用系统自带的下拉刷新; 默认false; 仅mescroll-body生效 (值为true时,还需在pages配置enablePullDownRefresh:true;详请参考mescroll-native的案例)
+		autoShowLoading: false, // 如果设置auto=true(在初始化完毕之后自动执行下拉刷新的回调),那么是否显示下拉刷新的进度; 默认false
+		isLock: false, // 是否锁定下拉刷新,默认false;
+		offset: 80, // 在列表顶部,下拉大于80px,松手即可触发下拉刷新的回调
+		startTop: 100, // scroll-view滚动到顶部时,此时的scroll-top不一定为0, 此值用于控制最大的误差
+		fps: 80, // 下拉节流 (值越大每秒刷新频率越高)
+		inOffsetRate: 1, // 在列表顶部,下拉的距离小于offset时,改变下拉区域高度比例;值小于1且越接近0,高度变化越小,表现为越往下越难拉
+		outOffsetRate: 0.2, // 在列表顶部,下拉的距离大于offset时,改变下拉区域高度比例;值小于1且越接近0,高度变化越小,表现为越往下越难拉
+		bottomOffset: 20, // 当手指touchmove位置在距离body底部20px范围内的时候结束上拉刷新,避免Webview嵌套导致touchend事件不执行
+		minAngle: 45, // 向下滑动最少偏移的角度,取值区间  [0,90];默认45度,即向下滑动的角度大于45度则触发下拉;而小于45度,将不触发下拉,避免与左右滑动的轮播等组件冲突;
+		textInOffset: '下拉刷新', // 下拉的距离在offset范围内的提示文本
+		textOutOffset: '释放更新', // 下拉的距离大于offset范围的提示文本
+		textLoading: '加载中 ...', // 加载中的提示文本
+		bgColor: "transparent", // 背景颜色 (建议在pages.json中再设置一下backgroundColorTop)
+		textColor: "gray", // 文本颜色 (当bgColor配置了颜色,而textColor未配置时,则textColor会默认为白色)
+		inited: null, // 下拉刷新初始化完毕的回调
+		inOffset: null, // 下拉的距离进入offset范围内那一刻的回调
+		outOffset: null, // 下拉的距离大于offset那一刻的回调
+		onMoving: null, // 下拉过程中的回调,滑动过程一直在执行; rate下拉区域当前高度与指定距离的比值(inOffset: rate<1; outOffset: rate>=1); downHight当前下拉区域的高度
+		beforeLoading: null, // 准备触发下拉刷新的回调: 如果return true,将不触发showLoading和callback回调; 常用来完全自定义下拉刷新, 参考案例【淘宝 v6.8.0】
+		showLoading: null, // 显示下拉刷新进度的回调
+		afterLoading: null, // 准备结束下拉的回调. 返回结束下拉的延时执行时间,默认0ms; 常用于结束下拉之前再显示另外一小段动画,才去隐藏下拉刷新的场景, 参考案例【dotJump】
+		endDownScroll: null, // 结束下拉刷新的回调
+		callback: function(mescroll) {
+			// 下拉刷新的回调;默认重置上拉加载列表为第一页
+			mescroll.resetUpScroll();
+		}
+	})
+}
+
+/* 配置参数:上拉加载 */
+MeScroll.prototype.extendUpScroll = function(optUp) {
+	// 上拉加载的配置
+	MeScroll.extend(optUp, {
+		use: true, // 是否启用上拉加载; 默认true
+		auto: true, // 是否在初始化完毕之后自动执行上拉加载的回调; 默认true
+		isLock: false, // 是否锁定上拉加载,默认false;
+		isBoth: true, // 上拉加载时,如果滑动到列表顶部是否可以同时触发下拉刷新;默认true,两者可同时触发;
+		isBounce: false, // 默认禁止橡皮筋的回弹效果, 必读事项: http://www.mescroll.com/qa.html?v=190725#q25
+		callback: null, // 上拉加载的回调;function(page,mescroll){ }
+		page: {
+			num: 0, // 当前页码,默认0,回调之前会加1,即callback(page)会从1开始
+			size: 10, // 每页数据的数量
+			time: null // 加载第一页数据服务器返回的时间; 防止用户翻页时,后台新增了数据从而导致下一页数据重复;
+		},
+		noMoreSize: 5, // 如果列表已无数据,可设置列表的总数量要大于等于5条才显示无更多数据;避免列表数据过少(比如只有一条数据),显示无更多数据会不好看
+		offset: 80, // 距底部多远时,触发upCallback
+		textLoading: '加载中 ...', // 加载中的提示文本
+		textNoMore: '-- END --', // 没有更多数据的提示文本
+		bgColor: "transparent", // 背景颜色 (建议在pages.json中再设置一下backgroundColorBottom)
+		textColor: "gray", // 文本颜色 (当bgColor配置了颜色,而textColor未配置时,则textColor会默认为白色)
+		inited: null, // 初始化完毕的回调
+		showLoading: null, // 显示加载中的回调
+		showNoMore: null, // 显示无更多数据的回调
+		hideUpScroll: null, // 隐藏上拉加载的回调
+		errDistance: 60, // endErr的时候需往上滑动一段距离,使其往下滑动时再次触发onReachBottom,仅mescroll-body生效
+		toTop: {
+			// 回到顶部按钮,需配置src才显示
+			src: null, // 图片路径,默认null (绝对路径或网络图)
+			offset: 1000, // 列表滚动多少距离才显示回到顶部按钮,默认1000
+			duration: 300, // 回到顶部的动画时长,默认300ms (当值为0或300则使用系统自带回到顶部,更流畅; 其他值则通过step模拟,部分机型可能不够流畅,所以非特殊情况不建议修改此项)
+			btnClick: null, // 点击按钮的回调
+			onShow: null, // 是否显示的回调
+			zIndex: 9990, // fixed定位z-index值
+			left: null, // 到左边的距离, 默认null. 此项有值时,right不生效. (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx)
+			right: 20, // 到右边的距离, 默认20 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx)
+			bottom: 120, // 到底部的距离, 默认120 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx)
+			safearea: false, // bottom的偏移量是否加上底部安全区的距离, 默认false, 需要适配iPhoneX时使用 (具体的界面如果不配置此项,则取本vue的safearea值)
+			width: 72, // 回到顶部图标的宽度, 默认72 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx)
+			radius: "50%" // 圆角, 默认"50%" (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx)
+		},
+		empty: {
+			use: true, // 是否显示空布局
+			icon: null, // 图标路径
+			tip: '~ 暂无相关数据 ~', // 提示
+			btnText: '', // 按钮
+			btnClick: null, // 点击按钮的回调
+			onShow: null, // 是否显示的回调
+			fixed: false, // 是否使用fixed定位,默认false; 配置fixed为true,以下的top和zIndex才生效 (transform会使fixed失效,最终会降级为absolute)
+			top: "100rpx", // fixed定位的top值 (完整的单位值,如 "10%"; "100rpx")
+			zIndex: 99 // fixed定位z-index值
+		},
+		onScroll: false // 是否监听滚动事件
+	})
+}
+
+/* 配置参数 */
+MeScroll.extend = function(userOption, defaultOption) {
+	if (!userOption) return defaultOption;
+	for (let key in defaultOption) {
+		if (userOption[key] == null) {
+			let def = defaultOption[key];
+			if (def != null && typeof def === 'object') {
+				userOption[key] = MeScroll.extend({}, def); // 深度匹配
+			} else {
+				userOption[key] = def;
+			}
+		} else if (typeof userOption[key] === 'object') {
+			MeScroll.extend(userOption[key], defaultOption[key]); // 深度匹配
+		}
+	}
+	return userOption;
+}
+
+/* 简单判断是否配置了颜色 (非透明,非白色) */
+MeScroll.prototype.hasColor = function(color) {
+	if(!color) return false;
+	let c = color.toLowerCase();
+	return c != "#fff" && c != "#ffffff" && c != "transparent" && c != "white"
+}
+
+/* -------初始化下拉刷新------- */
+MeScroll.prototype.initDownScroll = function() {
+	let me = this;
+	// 配置参数
+	me.optDown = me.options.down || {};
+	if(!me.optDown.textColor && me.hasColor(me.optDown.bgColor)) me.optDown.textColor = "#fff"; // 当bgColor有值且textColor未设置,则textColor默认白色
+	me.extendDownScroll(me.optDown);
+	
+	// 如果是mescroll-body且配置了native,则禁止自定义的下拉刷新
+	if(me.isScrollBody && me.optDown.native){
+		me.optDown.use = false
+	}else{
+		me.optDown.native = false // 仅mescroll-body支持,mescroll-uni不支持
+	}
+	
+	me.downHight = 0; // 下拉区域的高度
+
+	// 在页面中加入下拉布局
+	if (me.optDown.use && me.optDown.inited) {
+		// 初始化完毕的回调
+		setTimeout(function() { // 待主线程执行完毕再执行,避免new MeScroll未初始化,在回调获取不到mescroll的实例
+			me.optDown.inited(me);
+		}, 0)
+	}
+}
+
+/* 列表touchstart事件 */
+MeScroll.prototype.touchstartEvent = function(e) {
+	if (!this.optDown.use) return;
+
+	this.startPoint = this.getPoint(e); // 记录起点
+	this.startTop = this.getScrollTop(); // 记录此时的滚动条位置
+	this.lastPoint = this.startPoint; // 重置上次move的点
+	this.maxTouchmoveY = this.getBodyHeight() - this.optDown.bottomOffset; // 手指触摸的最大范围(写在touchstart避免body获取高度为0的情况)
+	this.inTouchend = false; // 标记不是touchend
+}
+
+/* 列表touchmove事件 */
+MeScroll.prototype.touchmoveEvent = function(e) {
+	// #ifdef H5
+	window.isPreventDefault = false // 标记不需要阻止window事件
+	// #endif
+	
+	if (!this.optDown.use) return;
+	if (!this.startPoint) return;
+	let me = this;
+
+	// 节流
+	let t = new Date().getTime();
+	if (me.moveTime && t - me.moveTime < me.moveTimeDiff) { // 小于节流时间,则不处理
+		return;
+	} else {
+		me.moveTime = t
+		if(!me.moveTimeDiff) me.moveTimeDiff = 1000 / me.optDown.fps
+	}
+
+	let scrollTop = me.getScrollTop(); // 当前滚动条的距离
+	let curPoint = me.getPoint(e); // 当前点
+
+	let moveY = curPoint.y - me.startPoint.y; // 和起点比,移动的距离,大于0向下拉,小于0向上拉
+
+	// 向下拉 && 在顶部
+	// mescroll-body,直接判定在顶部即可
+	// scroll-view在滚动时不会触发touchmove,当触顶/底/左/右时,才会触发touchmove
+	// scroll-view滚动到顶部时,scrollTop不一定为0; 在iOS的APP中scrollTop可能为负数,不一定和startTop相等
+	if (moveY > 0 && (
+			(me.isScrollBody && scrollTop <= 0)
+			||
+			(!me.isScrollBody && (scrollTop <= 0 || (scrollTop <= me.optDown.startTop && scrollTop === me.startTop)) )
+		)) {
+		// 可下拉的条件
+		if (!me.inTouchend && !me.isDownScrolling && !me.optDown.isLock && (!me.isUpScrolling || (me.isUpScrolling &&
+				me.optUp.isBoth))) {
+
+			// 下拉的角度是否在配置的范围内
+			let angle = me.getAngle(me.lastPoint, curPoint); // 两点之间的角度,区间 [0,90]
+			if (angle < me.optDown.minAngle) return; // 如果小于配置的角度,则不往下执行下拉刷新
+
+			// 如果手指的位置超过配置的距离,则提前结束下拉,避免Webview嵌套导致touchend无法触发
+			if (me.maxTouchmoveY > 0 && curPoint.y >= me.maxTouchmoveY) {
+				me.inTouchend = true; // 标记执行touchend
+				me.touchendEvent(); // 提前触发touchend
+				return;
+			}
+			
+			// #ifdef H5
+			window.isPreventDefault = true // 标记阻止window事件
+			// #endif
+			me.preventDefault(e); // 阻止默认事件
+
+			let diff = curPoint.y - me.lastPoint.y; // 和上次比,移动的距离 (大于0向下,小于0向上)
+
+			// 下拉距离  < 指定距离
+			if (me.downHight < me.optDown.offset) {
+				if (me.movetype !== 1) {
+					me.movetype = 1; // 加入标记,保证只执行一次
+					me.optDown.inOffset && me.optDown.inOffset(me); // 进入指定距离范围内那一刻的回调,只执行一次
+					me.isMoveDown = true; // 标记下拉区域高度改变,在touchend重置回来
+				}
+				me.downHight += diff * me.optDown.inOffsetRate; // 越往下,高度变化越小
+
+				// 指定距离  <= 下拉距离
+			} else {
+				if (me.movetype !== 2) {
+					me.movetype = 2; // 加入标记,保证只执行一次
+					me.optDown.outOffset && me.optDown.outOffset(me); // 下拉超过指定距离那一刻的回调,只执行一次
+					me.isMoveDown = true; // 标记下拉区域高度改变,在touchend重置回来
+				}
+				if (diff > 0) { // 向下拉
+					me.downHight += Math.round(diff * me.optDown.outOffsetRate); // 越往下,高度变化越小
+				} else { // 向上收
+					me.downHight += diff; // 向上收回高度,则向上滑多少收多少高度
+				}
+			}
+
+			let rate = me.downHight / me.optDown.offset; // 下拉区域当前高度与指定距离的比值
+			me.optDown.onMoving && me.optDown.onMoving(me, rate, me.downHight); // 下拉过程中的回调,一直在执行
+		}
+	}
+
+	me.lastPoint = curPoint; // 记录本次移动的点
+}
+
+/* 列表touchend事件 */
+MeScroll.prototype.touchendEvent = function(e) {
+	if (!this.optDown.use) return;
+	// 如果下拉区域高度已改变,则需重置回来
+	if (this.isMoveDown) {
+		if (this.downHight >= this.optDown.offset) {
+			// 符合触发刷新的条件
+			this.triggerDownScroll();
+		} else {
+			// 不符合的话 则重置
+			this.downHight = 0;
+			this.optDown.endDownScroll && this.optDown.endDownScroll(this);
+		}
+		this.movetype = 0;
+		this.isMoveDown = false;
+	} else if (!this.isScrollBody && this.getScrollTop() === this.startTop) { // scroll-view到顶/左/右/底的滑动事件
+		let isScrollUp = this.getPoint(e).y - this.startPoint.y < 0; // 和起点比,移动的距离,大于0向下拉,小于0向上拉
+		// 上滑
+		if (isScrollUp) {
+			// 需检查滑动的角度
+			let angle = this.getAngle(this.getPoint(e), this.startPoint); // 两点之间的角度,区间 [0,90]
+			if (angle > 80) {
+				// 检查并触发上拉
+				this.triggerUpScroll(true);
+			}
+		}
+	}
+}
+
+/* 根据点击滑动事件获取第一个手指的坐标 */
+MeScroll.prototype.getPoint = function(e) {
+	if (!e) {
+		return {
+			x: 0,
+			y: 0
+		}
+	}
+	if (e.touches && e.touches[0]) {
+		return {
+			x: e.touches[0].pageX,
+			y: e.touches[0].pageY
+		}
+	} else if (e.changedTouches && e.changedTouches[0]) {
+		return {
+			x: e.changedTouches[0].pageX,
+			y: e.changedTouches[0].pageY
+		}
+	} else {
+		return {
+			x: e.clientX,
+			y: e.clientY
+		}
+	}
+}
+
+/* 计算两点之间的角度: 区间 [0,90]*/
+MeScroll.prototype.getAngle = function(p1, p2) {
+	let x = Math.abs(p1.x - p2.x);
+	let y = Math.abs(p1.y - p2.y);
+	let z = Math.sqrt(x * x + y * y);
+	let angle = 0;
+	if (z !== 0) {
+		angle = Math.asin(y / z) / Math.PI * 180;
+	}
+	return angle
+}
+
+/* 触发下拉刷新 */
+MeScroll.prototype.triggerDownScroll = function() {
+	if (this.optDown.beforeLoading && this.optDown.beforeLoading(this)) {
+		//return true则处于完全自定义状态
+	} else {
+		this.showDownScroll(); // 下拉刷新中...
+		this.optDown.callback && this.optDown.callback(this); // 执行回调,联网加载数据
+	}
+}
+
+/* 显示下拉进度布局 */
+MeScroll.prototype.showDownScroll = function() {
+	this.isDownScrolling = true; // 标记下拉中
+	if (this.optDown.native) {
+		uni.startPullDownRefresh(); // 系统自带的下拉刷新
+		this.optDown.showLoading && this.optDown.showLoading(this, 0); // 仍触发showLoading,因为上拉加载用到
+	} else{
+		this.downHight = this.optDown.offset; // 更新下拉区域高度
+		this.optDown.showLoading && this.optDown.showLoading(this, this.downHight); // 下拉刷新中...
+	}
+}
+
+/* 显示系统自带的下拉刷新时需要处理的业务 */
+MeScroll.prototype.onPullDownRefresh = function() {
+	this.isDownScrolling = true; // 标记下拉中
+	this.optDown.showLoading && this.optDown.showLoading(this, 0); // 仍触发showLoading,因为上拉加载用到
+	this.optDown.callback && this.optDown.callback(this); // 执行回调,联网加载数据
+}
+
+/* 结束下拉刷新 */
+MeScroll.prototype.endDownScroll = function() {
+	if (this.optDown.native) { // 结束原生下拉刷新
+		this.isDownScrolling = false;
+		this.optDown.endDownScroll && this.optDown.endDownScroll(this);
+		uni.stopPullDownRefresh();
+		return
+	}
+	let me = this;
+	// 结束下拉刷新的方法
+	let endScroll = function() {
+		me.downHight = 0;
+		me.isDownScrolling = false;
+		me.optDown.endDownScroll && me.optDown.endDownScroll(me);
+		!me.isScrollBody && me.setScrollHeight(0) // scroll-view重置滚动区域,使数据不满屏时仍可检查触发翻页
+	}
+	// 结束下拉刷新时的回调
+	let delay = 0;
+	if (me.optDown.afterLoading) delay = me.optDown.afterLoading(me); // 结束下拉刷新的延时,单位ms
+	if (typeof delay === 'number' && delay > 0) {
+		setTimeout(endScroll, delay);
+	} else {
+		endScroll();
+	}
+}
+
+/* 锁定下拉刷新:isLock=ture,null锁定;isLock=false解锁 */
+MeScroll.prototype.lockDownScroll = function(isLock) {
+	if (isLock == null) isLock = true;
+	this.optDown.isLock = isLock;
+}
+
+/* 锁定上拉加载:isLock=ture,null锁定;isLock=false解锁 */
+MeScroll.prototype.lockUpScroll = function(isLock) {
+	if (isLock == null) isLock = true;
+	this.optUp.isLock = isLock;
+}
+
+/* -------初始化上拉加载------- */
+MeScroll.prototype.initUpScroll = function() {
+	let me = this;
+	// 配置参数
+	me.optUp = me.options.up || {use: false}
+	if(!me.optUp.textColor && me.hasColor(me.optUp.bgColor)) me.optUp.textColor = "#fff"; // 当bgColor有值且textColor未设置,则textColor默认白色
+	me.extendUpScroll(me.optUp);
+
+	if (!me.optUp.isBounce) me.setBounce(false); // 不允许bounce时,需禁止window的touchmove事件
+
+	if (me.optUp.use === false) return; // 配置不使用上拉加载时,则不初始化上拉布局
+	me.optUp.hasNext = true; // 如果使用上拉,则默认有下一页
+	me.startNum = me.optUp.page.num + 1; // 记录page开始的页码
+
+	// 初始化完毕的回调
+	if (me.optUp.inited) {
+		setTimeout(function() { // 待主线程执行完毕再执行,避免new MeScroll未初始化,在回调获取不到mescroll的实例
+			me.optUp.inited(me);
+		}, 0)
+	}
+}
+
+/*滚动到底部的事件 (仅mescroll-body生效)*/
+MeScroll.prototype.onReachBottom = function() {
+	if (this.isScrollBody && !this.isUpScrolling) { // 只能支持下拉刷新的时候同时可以触发上拉加载,否则滚动到底部就需要上滑一点才能触发onReachBottom
+		if (!this.optUp.isLock && this.optUp.hasNext) {
+			this.triggerUpScroll();
+		}
+	}
+}
+
+/*列表滚动事件 (仅mescroll-body生效)*/
+MeScroll.prototype.onPageScroll = function(e) {
+	if (!this.isScrollBody) return;
+	
+	// 更新滚动条的位置 (主要用于判断下拉刷新时,滚动条是否在顶部)
+	this.setScrollTop(e.scrollTop);
+
+	// 顶部按钮的显示隐藏
+	if (e.scrollTop >= this.optUp.toTop.offset) {
+		this.showTopBtn();
+	} else {
+		this.hideTopBtn();
+	}
+}
+
+/*列表滚动事件*/
+MeScroll.prototype.scroll = function(e, onScroll) {
+	// 更新滚动条的位置
+	this.setScrollTop(e.scrollTop);
+	// 更新滚动内容高度
+	this.setScrollHeight(e.scrollHeight);
+
+	// 向上滑还是向下滑动
+	if (this.preScrollY == null) this.preScrollY = 0;
+	this.isScrollUp = e.scrollTop - this.preScrollY > 0;
+	this.preScrollY = e.scrollTop;
+
+	// 上滑 && 检查并触发上拉
+	this.isScrollUp && this.triggerUpScroll(true);
+
+	// 顶部按钮的显示隐藏
+	if (e.scrollTop >= this.optUp.toTop.offset) {
+		this.showTopBtn();
+	} else {
+		this.hideTopBtn();
+	}
+
+	// 滑动监听
+	this.optUp.onScroll && onScroll && onScroll()
+}
+
+/* 触发上拉加载 */
+MeScroll.prototype.triggerUpScroll = function(isCheck) {
+	if (!this.isUpScrolling && this.optUp.use && this.optUp.callback) {
+		// 是否校验在底部; 默认不校验
+		if (isCheck === true) {
+			let canUp = false;
+			// 还有下一页 && 没有锁定 && 不在下拉中
+			if (this.optUp.hasNext && !this.optUp.isLock && !this.isDownScrolling) {
+				if (this.getScrollBottom() <= this.optUp.offset) { // 到底部
+					canUp = true; // 标记可上拉
+				}
+			}
+			if (canUp === false) return;
+		}
+		this.showUpScroll(); // 上拉加载中...
+		this.optUp.page.num++; // 预先加一页,如果失败则减回
+		this.isUpAutoLoad = true; // 标记上拉已经自动执行过,避免初始化时多次触发上拉回调
+		this.num = this.optUp.page.num; // 把最新的页数赋值在mescroll上,避免对page的影响
+		this.size = this.optUp.page.size; // 把最新的页码赋值在mescroll上,避免对page的影响
+		this.time = this.optUp.page.time; // 把最新的页码赋值在mescroll上,避免对page的影响
+		this.optUp.callback(this); // 执行回调,联网加载数据
+	}
+}
+
+/* 显示上拉加载中 */
+MeScroll.prototype.showUpScroll = function() {
+	this.isUpScrolling = true; // 标记上拉加载中
+	this.optUp.showLoading && this.optUp.showLoading(this); // 回调
+}
+
+/* 显示上拉无更多数据 */
+MeScroll.prototype.showNoMore = function() {
+	this.optUp.hasNext = false; // 标记无更多数据
+	this.optUp.showNoMore && this.optUp.showNoMore(this); // 回调
+}
+
+/* 隐藏上拉区域**/
+MeScroll.prototype.hideUpScroll = function() {
+	this.optUp.hideUpScroll && this.optUp.hideUpScroll(this); // 回调
+}
+
+/* 结束上拉加载 */
+MeScroll.prototype.endUpScroll = function(isShowNoMore) {
+	if (isShowNoMore != null) { // isShowNoMore=null,不处理下拉状态,下拉刷新的时候调用
+		if (isShowNoMore) {
+			this.showNoMore(); // isShowNoMore=true,显示无更多数据
+		} else {
+			this.hideUpScroll(); // isShowNoMore=false,隐藏上拉加载
+		}
+	}
+	this.isUpScrolling = false; // 标记结束上拉加载
+}
+
+/* 重置上拉加载列表为第一页
+ *isShowLoading 是否显示进度布局;
+ * 1.默认null,不传参,则显示上拉加载的进度布局
+ * 2.传参true, 则显示下拉刷新的进度布局
+ * 3.传参false,则不显示上拉和下拉的进度 (常用于静默更新列表数据)
+ */
+MeScroll.prototype.resetUpScroll = function(isShowLoading) {
+	if (this.optUp && this.optUp.use) {
+		let page = this.optUp.page;
+		this.prePageNum = page.num; // 缓存重置前的页码,加载失败可退回
+		this.prePageTime = page.time; // 缓存重置前的时间,加载失败可退回
+		page.num = this.startNum; // 重置为第一页
+		page.time = null; // 重置时间为空
+		if (!this.isDownScrolling && isShowLoading !== false) { // 如果不是下拉刷新触发的resetUpScroll并且不配置列表静默更新,则显示进度;
+			if (isShowLoading == null) {
+				this.removeEmpty(); // 移除空布局
+				this.showUpScroll(); // 不传参,默认显示上拉加载的进度布局
+			} else {
+				this.showDownScroll(); // 传true,显示下拉刷新的进度布局,不清空列表
+			}
+		}
+		this.isUpAutoLoad = true; // 标记上拉已经自动执行过,避免初始化时多次触发上拉回调
+		this.num = page.num; // 把最新的页数赋值在mescroll上,避免对page的影响
+		this.size = page.size; // 把最新的页码赋值在mescroll上,避免对page的影响
+		this.time = page.time; // 把最新的页码赋值在mescroll上,避免对page的影响
+		this.optUp.callback && this.optUp.callback(this); // 执行上拉回调
+	}
+}
+
+/* 设置page.num的值 */
+MeScroll.prototype.setPageNum = function(num) {
+	this.optUp.page.num = num - 1;
+}
+
+/* 设置page.size的值 */
+MeScroll.prototype.setPageSize = function(size) {
+	this.optUp.page.size = size;
+}
+
+/* 联网回调成功,结束下拉刷新和上拉加载
+ * dataSize: 当前页的数据量(必传)
+ * totalPage: 总页数(必传)
+ * systime: 服务器时间 (可空)
+ */
+MeScroll.prototype.endByPage = function(dataSize, totalPage, systime) {
+	let hasNext;
+	if (this.optUp.use && totalPage != null) hasNext = this.optUp.page.num < totalPage; // 是否还有下一页
+	this.endSuccess(dataSize, hasNext, systime);
+}
+
+/* 联网回调成功,结束下拉刷新和上拉加载
+ * dataSize: 当前页的数据量(必传)
+ * totalSize: 列表所有数据总数量(必传)
+ * systime: 服务器时间 (可空)
+ */
+MeScroll.prototype.endBySize = function(dataSize, totalSize, systime) {
+	let hasNext;
+	if (this.optUp.use && totalSize != null) {
+		let loadSize = (this.optUp.page.num - 1) * this.optUp.page.size + dataSize; // 已加载的数据总数
+		hasNext = loadSize < totalSize; // 是否还有下一页
+	}
+	this.endSuccess(dataSize, hasNext, systime);
+}
+
+/* 联网回调成功,结束下拉刷新和上拉加载
+ * dataSize: 当前页的数据个数(不是所有页的数据总和),用于上拉加载判断是否还有下一页.如果不传,则会判断还有下一页
+ * hasNext: 是否还有下一页,布尔类型;用来解决这个小问题:比如列表共有20条数据,每页加载10条,共2页.如果只根据dataSize判断,则需翻到第三页才会知道无更多数据,如果传了hasNext,则翻到第二页即可显示无更多数据.
+ * systime: 服务器时间(可空);用来解决这个小问题:当准备翻下一页时,数据库新增了几条记录,此时翻下一页,前面的几条数据会和上一页的重复;这里传入了systime,那么upCallback的page.time就会有值,把page.time传给服务器,让后台过滤新加入的那几条记录
+ */
+MeScroll.prototype.endSuccess = function(dataSize, hasNext, systime) {
+	let me = this;
+	// 结束下拉刷新
+	if (me.isDownScrolling) me.endDownScroll();
+
+	// 结束上拉加载
+	if (me.optUp.use) {
+		let isShowNoMore; // 是否已无更多数据
+		if (dataSize != null) {
+			let pageNum = me.optUp.page.num; // 当前页码
+			let pageSize = me.optUp.page.size; // 每页长度
+			// 如果是第一页
+			if (pageNum === 1) {
+				if (systime) me.optUp.page.time = systime; // 设置加载列表数据第一页的时间
+			}
+			if (dataSize < pageSize || hasNext === false) {
+				// 返回的数据不满一页时,则说明已无更多数据
+				me.optUp.hasNext = false;
+				if (dataSize === 0 && pageNum === 1) {
+					// 如果第一页无任何数据且配置了空布局
+					isShowNoMore = false;
+					me.showEmpty();
+				} else {
+					// 总列表数少于配置的数量,则不显示无更多数据
+					let allDataSize = (pageNum - 1) * pageSize + dataSize;
+					if (allDataSize < me.optUp.noMoreSize) {
+						isShowNoMore = false;
+					} else {
+						isShowNoMore = true;
+					}
+					me.removeEmpty(); // 移除空布局
+				}
+			} else {
+				// 还有下一页
+				isShowNoMore = false;
+				me.optUp.hasNext = true;
+				me.removeEmpty(); // 移除空布局
+			}
+		}
+
+		// 隐藏上拉
+		me.endUpScroll(isShowNoMore);
+	}
+}
+
+/* 回调失败,结束下拉刷新和上拉加载 */
+MeScroll.prototype.endErr = function(errDistance) {
+	// 结束下拉,回调失败重置回原来的页码和时间
+	if (this.isDownScrolling) {
+		let page = this.optUp.page;
+		if (page && this.prePageNum) {
+			page.num = this.prePageNum;
+			page.time = this.prePageTime;
+		}
+		this.endDownScroll();
+	}
+	// 结束上拉,回调失败重置回原来的页码
+	if (this.isUpScrolling) {
+		this.optUp.page.num--;
+		this.endUpScroll(false);
+		// 如果是mescroll-body,则需往回滚一定距离
+		if(this.isScrollBody && errDistance !== 0){ // 不处理0
+			if(!errDistance) errDistance = this.optUp.errDistance; // 不传,则取默认
+			this.scrollTo(this.getScrollTop() - errDistance, 0) // 往上回滚的距离
+		}
+	}
+}
+
+/* 显示空布局 */
+MeScroll.prototype.showEmpty = function() {
+	this.optUp.empty.use && this.optUp.empty.onShow && this.optUp.empty.onShow(true)
+}
+
+/* 移除空布局 */
+MeScroll.prototype.removeEmpty = function() {
+	this.optUp.empty.use && this.optUp.empty.onShow && this.optUp.empty.onShow(false)
+}
+
+/* 显示回到顶部的按钮 */
+MeScroll.prototype.showTopBtn = function() {
+	if (!this.topBtnShow) {
+		this.topBtnShow = true;
+		this.optUp.toTop.onShow && this.optUp.toTop.onShow(true);
+	}
+}
+
+/* 隐藏回到顶部的按钮 */
+MeScroll.prototype.hideTopBtn = function() {
+	if (this.topBtnShow) {
+		this.topBtnShow = false;
+		this.optUp.toTop.onShow && this.optUp.toTop.onShow(false);
+	}
+}
+
+/* 获取滚动条的位置 */
+MeScroll.prototype.getScrollTop = function() {
+	return this.scrollTop || 0
+}
+
+/* 记录滚动条的位置 */
+MeScroll.prototype.setScrollTop = function(y) {
+	this.scrollTop = y;
+}
+
+/* 滚动到指定位置 */
+MeScroll.prototype.scrollTo = function(y, t) {
+	this.myScrollTo && this.myScrollTo(y, t) // scrollview需自定义回到顶部方法
+}
+
+/* 自定义scrollTo */
+MeScroll.prototype.resetScrollTo = function(myScrollTo) {
+	this.myScrollTo = myScrollTo
+}
+
+/* 滚动条到底部的距离 */
+MeScroll.prototype.getScrollBottom = function() {
+	return this.getScrollHeight() - this.getClientHeight() - this.getScrollTop()
+}
+
+/* 计步器
+ star: 开始值
+ end: 结束值
+ callback(step,timer): 回调step值,计步器timer,可自行通过window.clearInterval(timer)结束计步器;
+ t: 计步时长,传0则直接回调end值;不传则默认300ms
+ rate: 周期;不传则默认30ms计步一次
+ * */
+MeScroll.prototype.getStep = function(star, end, callback, t, rate) {
+	let diff = end - star; // 差值
+	if (t === 0 || diff === 0) {
+		callback && callback(end);
+		return;
+	}
+	t = t || 300; // 时长 300ms
+	rate = rate || 30; // 周期 30ms
+	let count = t / rate; // 次数
+	let step = diff / count; // 步长
+	let i = 0; // 计数
+	let timer = setInterval(function() {
+		if (i < count - 1) {
+			star += step;
+			callback && callback(star, timer);
+			i++;
+		} else {
+			callback && callback(end, timer); // 最后一次直接设置end,避免计算误差
+			clearInterval(timer);
+		}
+	}, rate);
+}
+
+/* 滚动容器的高度 */
+MeScroll.prototype.getClientHeight = function(isReal) {
+	let h = this.clientHeight || 0
+	if (h === 0 && isReal !== true) { // 未获取到容器的高度,可临时取body的高度 (可能会有误差)
+		h = this.getBodyHeight()
+	}
+	return h
+}
+MeScroll.prototype.setClientHeight = function(h) {
+	this.clientHeight = h;
+}
+
+/* 滚动内容的高度 */
+MeScroll.prototype.getScrollHeight = function() {
+	return this.scrollHeight || 0;
+}
+MeScroll.prototype.setScrollHeight = function(h) {
+	this.scrollHeight = h;
+}
+
+/* body的高度 */
+MeScroll.prototype.getBodyHeight = function() {
+	return this.bodyHeight || 0;
+}
+MeScroll.prototype.setBodyHeight = function(h) {
+	this.bodyHeight = h;
+}
+
+/* 阻止浏览器默认滚动事件 */
+MeScroll.prototype.preventDefault = function(e) {
+	// 小程序不支持e.preventDefault
+	// app的bounce只能通过配置pages.json的style.app-plus.bounce为"none"来禁止
+	// cancelable:是否可以被禁用; defaultPrevented:是否已经被禁用
+	if (e && e.cancelable && !e.defaultPrevented) e.preventDefault()
+}
+
+/* 是否允许下拉回弹(橡皮筋效果); true或null为允许; false禁止bounce */
+MeScroll.prototype.setBounce = function(isBounce) {
+	// #ifdef H5
+	if (isBounce === false) {
+		this.optUp.isBounce = false; // 禁止
+		// 标记当前页使用了mescroll (需延时,确保page已切换)
+		setTimeout(function() {
+			let uniPageDom = document.getElementsByTagName('uni-page')[0];
+			uniPageDom && uniPageDom.setAttribute('use_mescroll', true)
+		}, 30);
+		// 避免重复添加事件
+		if (window.isSetBounce) return;
+		window.isSetBounce = true;
+		// 需禁止window的touchmove事件才能有效的阻止bounce
+		window.bounceTouchmove = function(e) {
+			if(!window.isPreventDefault) return; // 根据标记判断是否阻止
+			
+			let el = e.target;
+			// 当前touch的元素及父元素是否要拦截touchmove事件
+			let isPrevent = true;
+			while (el !== document.body && el !== document) {
+				if (el.tagName === 'UNI-PAGE') { // 只扫描当前页
+					if (!el.getAttribute('use_mescroll')) {
+						isPrevent = false; // 如果当前页没有使用mescroll,则不阻止
+					}
+					break;
+				}
+				let cls = el.classList;
+				if (cls) {
+					if (cls.contains('mescroll-touch')) { // 采用scroll-view 此处不能过滤mescroll-uni,否则下拉仍然有回弹
+						isPrevent = false; // mescroll-touch无需拦截touchmove事件
+						break;
+					} else if (cls.contains('mescroll-touch-x') || cls.contains('mescroll-touch-y')) {
+						// 如果配置了水平或者垂直滑动
+						let curX = e.touches ? e.touches[0].pageX : e.clientX; // 当前第一个手指距离列表顶部的距离x
+						let curY = e.touches ? e.touches[0].pageY : e.clientY; // 当前第一个手指距离列表顶部的距离y
+						if (!this.preWinX) this.preWinX = curX; // 设置上次移动的距离x
+						if (!this.preWinY) this.preWinY = curY; // 设置上次移动的距离y
+						// 计算两点之间的角度
+						let x = Math.abs(this.preWinX - curX);
+						let y = Math.abs(this.preWinY - curY);
+						let z = Math.sqrt(x * x + y * y);
+						this.preWinX = curX; // 记录本次curX的值
+						this.preWinY = curY; // 记录本次curY的值
+						if (z !== 0) {
+							let angle = Math.asin(y / z) / Math.PI * 180; // 角度区间 [0,90]
+							if ((angle <= 45 && cls.contains('mescroll-touch-x')) || (angle > 45 && cls.contains('mescroll-touch-y'))) {
+								isPrevent = false; // 水平滑动或者垂直滑动,不拦截touchmove事件
+								break;
+							}
+						}
+					}
+				}
+				el = el.parentNode; // 继续检查其父元素
+			}
+			// 拦截touchmove事件:是否可以被禁用&&是否已经被禁用 (这里不使用me.preventDefault(e)的方法,因为某些情况下会报找不到方法的异常)
+			if (isPrevent && e.cancelable && !e.defaultPrevented && typeof e.preventDefault === "function") e.preventDefault();
+		}
+		window.addEventListener('touchmove', window.bounceTouchmove, {
+			passive: false
+		});
+	} else {
+		this.optUp.isBounce = true; // 允许
+		if (window.bounceTouchmove) {
+			window.removeEventListener('touchmove', window.bounceTouchmove);
+			window.bounceTouchmove = null;
+			window.isSetBounce = false;
+		}
+	}
+	// #endif
+}

+ 364 - 0
components/mescroll-uni/mescroll-uni.vue

@@ -0,0 +1,364 @@
+<template>
+	<view class="mescroll-uni-warp">
+		<scroll-view :id="viewId" class="mescroll-uni" :class="{'mescroll-uni-fixed':isFixed}" :style="{'height':scrollHeight,'padding-top':padTop,'padding-bottom':padBottom,'padding-bottom':padBottomConstant,'padding-bottom':padBottomEnv,'top':fixedTop,'bottom':fixedBottom,'bottom':fixedBottomConstant,'bottom':fixedBottomEnv}" :scroll-top="scrollTop" :scroll-into-view="scrollToViewId" :scroll-with-animation="scrollAnim" @scroll="scroll" @touchstart="touchstartEvent" @touchmove="touchmoveEvent" @touchend="touchendEvent" @touchcancel="touchendEvent" :scroll-y='isDownReset' :enable-back-to-top="true">
+			<view class="mescroll-uni-content" :style="{'transform': translateY, 'transition': transition}">
+				<!-- 下拉加载区域 (支付宝小程序子组件传参给子子组件仍报单项数据流的异常,暂时不通过mescroll-down组件实现)-->
+				<!-- <mescroll-down :option="mescroll.optDown" :type="downLoadType" :rate="downRate"></mescroll-down> -->
+				<view v-if="mescroll.optDown.use" class="mescroll-downwarp" :style="{'background-color':mescroll.optDown.bgColor,'color':mescroll.optDown.textColor}">
+					<view class="downwarp-content">
+						<view class="downwarp-progress" :class="{'mescroll-rotate': isDownLoading}" :style="{'border-color':mescroll.optDown.textColor, 'transform': downRotate}"></view>
+						<view class="downwarp-tip">{{downText}}</view>
+					</view>
+				</view>
+
+				<!-- 列表内容 -->
+				<slot></slot>
+
+				<!-- 空布局 -->
+				<mescroll-empty v-if="isShowEmpty" :option="mescroll.optUp.empty" @emptyclick="emptyClick"></mescroll-empty>
+
+				<!-- 上拉加载区域 (下拉刷新时不显示, 支付宝小程序子组件传参给子子组件仍报单项数据流的异常,暂时不通过mescroll-up组件实现)-->
+				<!-- <mescroll-up v-if="mescroll.optUp.use && !isDownLoading" :option="mescroll.optUp" :type="upLoadType"></mescroll-up> -->
+				<view v-if="mescroll.optUp.use && !isDownLoading" class="mescroll-upwarp" :style="{'background-color':mescroll.optUp.bgColor,'color':mescroll.optUp.textColor}">
+					<!-- 加载中 (此处不能用v-if,否则android小程序快速上拉可能会不断触发上拉回调) -->
+					<view v-show="upLoadType===1">
+						<view class="upwarp-progress mescroll-rotate" :style="{'border-color':mescroll.optUp.textColor}"></view>
+						<view class="upwarp-tip">{{ mescroll.optUp.textLoading }}</view>
+					</view>
+					<!-- 无数据 -->
+					<view v-if="upLoadType===2" class="upwarp-nodata">{{ mescroll.optUp.textNoMore }}</view>
+				</view>
+			</view>
+		</scroll-view>
+
+		<!-- 回到顶部按钮 (fixed元素,需写在scroll-view外面,防止滚动的时候抖动)-->
+		<mescroll-top v-model="isShowToTop" :option="mescroll.optUp.toTop" @click="toTopClick"></mescroll-top>
+	</view>
+</template>
+
+<script>
+	// 引入mescroll-uni.js,处理核心逻辑
+	import MeScroll from './mescroll-uni.js';
+	// 引入全局配置
+	import GlobalOption from './mescroll-uni-option.js';
+	// 引入空布局组件
+	import MescrollEmpty from './components/mescroll-empty.vue';
+	// 引入回到顶部组件
+	import MescrollTop from './components/mescroll-top.vue';
+
+	export default {
+		components: {
+			MescrollEmpty,
+			MescrollTop
+		},
+		data() {
+			return {
+				mescroll: {optDown:{},optUp:{}}, // mescroll实例
+				viewId: 'id_' + Math.random().toString(36).substr(2), // 随机生成mescroll的id(不能数字开头,否则找不到元素)
+				downHight: 0, //下拉刷新: 容器高度
+				downRate: 0, // 下拉比率(inOffset: rate<1; outOffset: rate>=1)
+				downLoadType: 4, // 下拉刷新状态 (inOffset:1, outOffset:2, showLoading:3, endDownScroll:4)
+				upLoadType: 0, // 上拉加载状态:0(loading前),1(loading中),2(没有更多了)
+				isShowEmpty: false, // 是否显示空布局
+				isShowToTop: false, // 是否显示回到顶部按钮
+				scrollTop: 0, // 滚动条的位置
+				scrollAnim: false, // 是否开启滚动动画
+				windowTop: 0, // 可使用窗口的顶部位置
+				windowBottom: 0, // 可使用窗口的底部位置
+				windowHeight: 0, // 可使用窗口的高度
+				statusBarHeight: 0, // 状态栏高度
+				isSafearea: false, // 支持安全区
+				scrollToViewId: '' // 滚动到指定view的id
+			}
+		},
+		props: {
+			down: Object, // 下拉刷新的参数配置
+			up: Object, // 上拉加载的参数配置
+			top: [String, Number], // 下拉布局往下的偏移量 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
+			topbar: Boolean, // top的偏移量是否加上状态栏高度, 默认false (使用场景:取消原生导航栏时,配置此项可自动加上状态栏高度的偏移量)
+			bottom: [String, Number], // 上拉布局往上的偏移量 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
+			safearea: Boolean, // bottom的偏移量是否加上底部安全区的距离, 默认false (需要适配iPhoneX时使用)
+			fixed: { // 是否通过fixed固定mescroll的高度, 默认true
+				type: Boolean,
+				default () {
+					return true
+				}
+			},
+			height: [String, Number] // 指定mescroll的高度, 此项有值,则不使用fixed. (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
+		},
+		computed: {
+			// 是否使用fixed定位 (当height有值,则不使用)
+			isFixed(){
+				return !this.height && this.fixed
+			},
+			// mescroll的高度
+			scrollHeight(){
+				if (this.isFixed) {
+					return "auto"
+				} else if(this.height){
+					return this.toPx(this.height) + 'px'
+				}else{
+					return "100%"
+				}
+			},
+			// 下拉布局往下偏移的距离 (px)
+			numTop() {
+				return this.toPx(this.top) + (this.topbar ? this.statusBarHeight : 0)
+			},
+			fixedTop() {
+				return this.isFixed ? (this.numTop + this.windowTop) + 'px' : 0
+			},
+			padTop() {
+				return !this.isFixed ? this.numTop + 'px' : 0
+			},
+			// 上拉布局往上偏移 (px)
+			numBottom() {
+				return this.toPx(this.bottom)
+			},
+			fixedBottom() {
+				return this.isFixed ? (this.numBottom + this.windowBottom) + 'px' : 0
+			},
+			fixedBottomConstant(){
+				return this.isSafearea ? "calc("+this.fixedBottom+" + constant(safe-area-inset-bottom))" : this.fixedBottom
+			},
+			fixedBottomEnv(){
+				return this.isSafearea ? "calc("+this.fixedBottom+" + env(safe-area-inset-bottom))" : this.fixedBottom
+			},
+			padBottom() {
+				return !this.isFixed ? this.numBottom + 'px' : 0
+			},
+			padBottomConstant(){
+				return this.isSafearea ? "calc("+this.padBottom+" + constant(safe-area-inset-bottom))" : this.padBottom
+			},
+			padBottomEnv(){
+				return this.isSafearea ? "calc("+this.padBottom+" + env(safe-area-inset-bottom))" : this.padBottom
+			},
+			// 是否为重置下拉的状态
+			isDownReset(){
+				return this.downLoadType===3 || this.downLoadType===4
+			},
+			// 过渡
+			transition() {
+				return this.isDownReset ? 'transform 300ms' : '';
+			},
+			translateY() {
+				return this.downHight > 0 ? 'translateY(' + this.downHight + 'px)' : ''; // transform会使fixed失效,需注意把fixed元素写在mescroll之外
+			},
+			// 是否在加载中
+			isDownLoading(){
+				return this.downLoadType === 3
+			},
+			// 旋转的角度
+			downRotate(){
+				return 'rotate(' + 360 * this.downRate + 'deg)'
+			},
+			// 文本提示
+			downText(){
+				switch (this.downLoadType){
+					case 1: return this.mescroll.optDown.textInOffset;
+					case 2: return this.mescroll.optDown.textOutOffset;
+					case 3: return this.mescroll.optDown.textLoading;
+					case 4: return this.mescroll.optDown.textLoading;
+					default: return this.mescroll.optDown.textInOffset;
+				}
+			}
+		},
+		methods: {
+			//number,rpx,upx,px,% --> px的数值
+			toPx(num){
+				if(typeof num === "string"){
+					if (num.indexOf('px') !== -1) {
+						if(num.indexOf('rpx') !== -1) { // "10rpx"
+							num = num.replace('rpx', '');
+						} else if(num.indexOf('upx') !== -1) { // "10upx"
+							num = num.replace('upx', '');
+						} else { // "10px"
+							return Number(num.replace('px', ''))
+						}
+					}else if (num.indexOf('%') !== -1){
+						// 传百分比,则相对于windowHeight,传"10%"则等于windowHeight的10%
+						let rate = Number(num.replace("%","")) / 100
+						return this.windowHeight * rate
+					}
+				}
+				return num ? uni.upx2px(Number(num)) : 0
+			},
+			//注册列表滚动事件,用于下拉刷新和上拉加载
+			scroll(e) {
+				this.mescroll.scroll(e.detail, () => {
+					this.$emit('scroll', this.mescroll) // 此时可直接通过 this.mescroll.scrollTop获取滚动条位置; this.mescroll.isScrollUp获取是否向上滑动
+				})
+			},
+			//注册列表touchstart事件,用于下拉刷新
+			touchstartEvent(e) {
+				this.mescroll.touchstartEvent(e);
+			},
+			//注册列表touchmove事件,用于下拉刷新
+			touchmoveEvent(e) {
+				this.mescroll.touchmoveEvent(e);
+			},
+			//注册列表touchend事件,用于下拉刷新
+			touchendEvent(e) {
+				this.mescroll.touchendEvent(e);
+			},
+			// 点击空布局的按钮回调
+			emptyClick() {
+				this.$emit('emptyclick', this.mescroll)
+			},
+			// 点击回到顶部的按钮回调
+			toTopClick() {
+				this.mescroll.scrollTo(0, this.mescroll.optUp.toTop.duration); // 执行回到顶部
+				this.$emit('topclick', this.mescroll); // 派发点击回到顶部按钮的回调
+			},
+			// 更新滚动区域的高度 (使内容不满屏和到底,都可继续翻页)
+			setClientHeight() {
+				if (this.mescroll.getClientHeight(true) === 0 && !this.isExec) {
+					this.isExec = true; // 避免多次获取
+					this.$nextTick(() => { // 确保dom已渲染
+						let view = uni.createSelectorQuery().in(this).select('#' + this.viewId);
+						view.boundingClientRect(data => {
+							this.isExec = false;
+							if (data) {
+								this.mescroll.setClientHeight(data.height);
+							} else if (this.clientNum != 3) { // 极少部分情况,可能dom还未渲染完毕,递归获取,最多重试3次
+								this.clientNum = this.clientNum == null ? 1 : this.clientNum + 1;
+								setTimeout(() => {
+									this.setClientHeight()
+								}, this.clientNum * 100)
+							}
+						}).exec();
+					})
+				}
+			}
+		},
+		// 使用created初始化mescroll对象; 如果用mounted部分css样式编译到H5会失效
+		created() {
+			let vm = this;
+
+			let diyOption = {
+				// 下拉刷新的配置
+				down: {
+					inOffset(mescroll) {
+						vm.downLoadType = 1; // 下拉的距离进入offset范围内那一刻的回调 (自定义mescroll组件时,此行不可删)
+					},
+					outOffset(mescroll) {
+						vm.downLoadType = 2; // 下拉的距离大于offset那一刻的回调 (自定义mescroll组件时,此行不可删)
+					},
+					onMoving(mescroll, rate, downHight) {
+						// 下拉过程中的回调,滑动过程一直在执行;
+						vm.downHight = downHight; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
+						vm.downRate = rate; //下拉比率 (inOffset: rate<1; outOffset: rate>=1)
+					},
+					showLoading(mescroll, downHight) {
+						vm.downLoadType = 3; // 显示下拉刷新进度的回调 (自定义mescroll组件时,此行不可删)
+						vm.downHight = downHight; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
+					},
+					endDownScroll(mescroll) {
+						vm.downLoadType = 4; // 结束下拉 (自定义mescroll组件时,此行不可删)
+						vm.downHight = 0; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
+					},
+					// 派发下拉刷新的回调
+					callback: function(mescroll) {
+						vm.$emit('down', mescroll)
+					}
+				},
+				// 上拉加载的配置
+				up: {
+					// 显示加载中的回调
+					showLoading() {
+						vm.upLoadType = 1;
+					},
+					// 显示无更多数据的回调
+					showNoMore() {
+						vm.upLoadType = 2;
+					},
+					// 隐藏上拉加载的回调
+					hideUpScroll() {
+						vm.upLoadType = 0;
+					},
+					// 空布局
+					empty: {
+						onShow(isShow) { // 显示隐藏的回调
+							vm.isShowEmpty = isShow;
+						}
+					},
+					// 回到顶部
+					toTop: {
+						onShow(isShow) { // 显示隐藏的回调
+							vm.isShowToTop = isShow;
+						}
+					},
+					// 派发上拉加载的回调
+					callback: function(mescroll) {
+						vm.$emit('up', mescroll);
+						// 更新容器的高度 (多mescroll的情况)
+						vm.setClientHeight()
+					}
+				}
+			}
+
+			MeScroll.extend(diyOption, GlobalOption); // 混入全局的配置
+			let myOption = JSON.parse(JSON.stringify({
+				'down': vm.down,
+				'up': vm.up
+			})) // 深拷贝,避免对props的影响
+			MeScroll.extend(myOption, diyOption); // 混入具体界面的配置
+
+			// 初始化MeScroll对象
+			vm.mescroll = new MeScroll(myOption);
+			vm.mescroll.viewId = vm.viewId; // 附带id
+			// init回调mescroll对象
+			vm.$emit('init', vm.mescroll);
+			
+			// 设置高度
+			const sys = uni.getSystemInfoSync();
+			if(sys.windowTop) vm.windowTop = sys.windowTop;
+			if(sys.windowBottom) vm.windowBottom = sys.windowBottom;
+			if(sys.windowHeight) vm.windowHeight = sys.windowHeight;
+			if(sys.statusBarHeight) vm.statusBarHeight = sys.statusBarHeight;
+			// 使down的bottomOffset生效
+			vm.mescroll.setBodyHeight(sys.windowHeight);
+
+			// 因为使用的是scrollview,这里需自定义scrollTo
+			vm.mescroll.resetScrollTo((y, t) => {
+				vm.scrollAnim = (t !== 0); // t为0,则不使用动画过渡
+				if(typeof y === 'string'){ // 第一个参数如果为字符串,则使用scroll-into-view
+					vm.scrollToViewId = y;
+					return;
+				}
+				let curY = vm.mescroll.getScrollTop()
+				if (t === 0 || t === 300) { // 当t使用默认配置的300时,则使用系统自带的动画过渡
+					vm.scrollTop = curY;
+					vm.$nextTick(function() {
+						vm.scrollTop = y
+					})
+				} else {
+					vm.mescroll.getStep(curY, y, step => { // 此写法可支持配置t
+						vm.scrollTop = step
+					}, t)
+				}
+			})
+			
+			// 具体的界面如果不配置up.toTop.safearea,则取本vue的safearea值
+			if(sys.platform == "ios"){
+				vm.isSafearea = vm.safearea;
+				if (vm.up && vm.up.toTop && vm.up.toTop.safearea != null) {} else {
+					vm.mescroll.optUp.toTop.safearea = vm.safearea;
+				}
+			}else{
+				vm.isSafearea = false
+				vm.mescroll.optUp.toTop.safearea = false
+			}
+		},
+		mounted() {
+			// 设置容器的高度
+			this.setClientHeight()
+		}
+	}
+</script>
+
+<style>
+	@import "./mescroll-uni.css";
+	@import "./components/mescroll-down.css";
+	@import './components/mescroll-up.css';
+</style>

+ 23 - 0
components/mescroll-uni/mixins/mescroll-comp.js

@@ -0,0 +1,23 @@
+/**
+ * mescroll-body写在子组件时,需通过mescroll的mixins补充子组件缺少的生命周期:
+ * 当一个页面只有一个mescroll-body写在子组件时, 则使用mescroll-comp.js (参考 mescroll-comp 案例)
+ * 当一个页面有多个mescroll-body写在子组件时, 则使用mescroll-more.js (参考 mescroll-more 案例)
+ */
+const MescrollCompMixin = {
+	// 因为子组件无onPageScroll和onReachBottom的页面生命周期,需在页面传递进到子组件
+	onPageScroll(e) {
+		let item = this.$refs["mescrollItem"];
+		if(item && item.mescroll) item.mescroll.onPageScroll(e);
+	},
+	onReachBottom() {
+		let item = this.$refs["mescrollItem"];
+		if(item && item.mescroll) item.mescroll.onReachBottom();
+	},
+	// 当down的native: true时, 还需传递此方法进到子组件
+	onPullDownRefresh(){
+		let item = this.$refs["mescrollItem"];
+		if(item && item.mescroll) item.mescroll.onPullDownRefresh();
+	}
+}
+
+export default MescrollCompMixin;

+ 48 - 0
components/mescroll-uni/mixins/mescroll-more-item.js

@@ -0,0 +1,48 @@
+/**
+ * mescroll-more-item的mixins, 仅在多个 mescroll-body 写在子组件时使用 (参考 mescroll-more 案例)
+ */
+const MescrollMoreItemMixin = {
+	props:{
+		i: Number, // 每个tab页的专属下标
+		index: { // 当前tab的下标
+			type: Number,
+			default(){
+				return 0
+			}
+		}
+	},
+	data() {
+		return {
+			downOption:{
+				auto:false // 不自动加载
+			},
+			upOption:{
+				auto:false // 不自动加载
+			},
+			isInit: false // 当前tab是否已初始化
+		}
+	},
+	watch:{
+		// 监听下标的变化
+		index(val){
+			if (this.i === val && !this.isInit) {
+				this.isInit = true; // 标记为true
+				this.mescroll && this.mescroll.triggerDownScroll();
+			}
+		}
+	},
+	methods: {
+		// mescroll组件初始化的回调,可获取到mescroll对象
+		mescrollInit(mescroll) {
+			this.mescroll = mescroll;
+			this.mescrollInitByRef && this.mescrollInitByRef(); // 兼容字节跳动小程序 (mescroll-mixins.js)
+			// 自动加载当前tab的数据
+			if(this.i === this.index){
+				this.isInit = true; // 标记为true
+				this.mescroll.triggerDownScroll();
+			}
+		},
+	}
+}
+
+export default MescrollMoreItemMixin;

+ 56 - 0
components/mescroll-uni/mixins/mescroll-more.js

@@ -0,0 +1,56 @@
+/**
+ * mescroll-body写在子组件时,需通过mescroll的mixins补充子组件缺少的生命周期:
+ * 当一个页面只有一个mescroll-body写在子组件时, 则使用mescroll-comp.js (参考 mescroll-comp 案例)
+ * 当一个页面有多个mescroll-body写在子组件时, 则使用mescroll-more.js (参考 mescroll-more 案例)
+ */
+const MescrollMoreMixin = {
+	data() {
+		return {
+			tabIndex: 0 // 当前tab下标
+		}
+	},
+	// 因为子组件无onPageScroll和onReachBottom的页面生命周期,需在页面传递进到子组件
+	onPageScroll(e) {
+		let mescroll = this.getMescroll(this.tabIndex);
+		mescroll && mescroll.onPageScroll(e);
+	},
+	onReachBottom() {
+		let mescroll = this.getMescroll(this.tabIndex);
+		mescroll && mescroll.onReachBottom();
+	},
+	// 当down的native: true时, 还需传递此方法进到子组件
+	onPullDownRefresh(){
+		let mescroll = this.getMescroll(this.tabIndex);
+		mescroll && mescroll.onPullDownRefresh();
+	},
+	methods:{
+		// 根据下标获取对应子组件的mescroll
+		getMescroll(i){
+			if(!this.mescrollItems) this.mescrollItems = [];
+			if(!this.mescrollItems[i]) {
+				// v-for中的refs
+				let vForItem = this.$refs["mescrollItem"];
+				if(vForItem){
+					this.mescrollItems[i] = vForItem[i]
+				}else{
+					// 普通的refs,不可重复
+					this.mescrollItems[i] = this.$refs["mescrollItem"+i];
+				}
+			}
+			let item = this.mescrollItems[i]
+			return item ? item.mescroll : null
+		},
+		// 切换tab,恢复滚动条位置
+		tabChange(i){
+			let mescroll = this.getMescroll(i);
+			if(mescroll){
+				// 延时(比$nextTick靠谱一些),确保元素已渲染
+				setTimeout(()=>{
+					mescroll.scrollTo(mescroll.getScrollTop(),0)
+				},30)
+			}
+		}
+	}
+}
+
+export default MescrollMoreMixin;

+ 158 - 0
components/my-componets/appSelect.vue

@@ -0,0 +1,158 @@
+<template>
+	<view class="cu-form-group" style="z-index:10">
+		<view class="flex align-center">
+			<view class="title">
+				<text class="text-red" v-if="required">*</text>
+				<text space="ensp">{{label}}</text>
+			</view>
+			<picker
+				 @change="pickerChange"
+				 :range="selections"
+				 :value="valueIndex"
+				 :disabled="disabled">
+				<input
+				   :placeholder="placeholder"
+				   name="input"
+				   v-model="selected"
+				   :disabled="true"
+				></input>
+			</picker>
+		</view>
+	</view>
+</template>
+
+<script>
+
+    export default {
+        name: "AppSecelt",
+		behaviors: ['uni://form-field'],
+        props:{
+            display:{
+                type:String,
+                default:'inline-block',
+                required:false
+            },
+            placeholder:{
+                type:String,
+                default:'请选择',
+                required:false
+            },
+            label:{
+                type:String,
+                default:'',
+                required:false
+            },
+            value:{
+                type:String,
+                required:false
+            },
+            border:{
+                type:Boolean,
+                default:false,
+                required:false
+            },
+            dict:{
+                type:Array,
+                default:()=>[],
+                required:true
+            },
+            name:{
+                type:String,
+                default:'',
+                required:false
+            },
+			required:{
+			    type:Boolean,
+				default:false,
+			    required:false
+			},
+			disabled:{
+			    type:Boolean,
+				default:false,
+			    required:false
+			},
+			space:{
+			    type:Boolean,
+				default:false,
+			    required:false
+			}
+        },
+        data(){
+            return {
+                show:false,
+                selected:'',
+                valueIndex:0,
+                selections:[]
+            }
+        },
+        watch:{
+            value:{
+                immediate:true,
+                handler(val){
+                  if(!val){
+                    this.selected = ''
+                    this.valueIndex = 0
+                }else{
+                  this.dict.map((item,index)=>{
+					 if(item.value == val){
+                      this.selected = item.text;
+                      this.valueIndex = index
+					}
+                  })
+                }
+              }
+            },
+            dict(){
+                this.initSelections();
+            }
+        },
+        created(){
+            this.initSelections();
+        },
+        methods:{
+            initSelections(){
+                let arr = [];
+                this.dict.map(item=>{
+                    arr.push(item.text)
+                });
+                this.selections = arr
+            },
+            pickerChange(e){
+				console.log("appselect::pickerChange",e.detail.value)
+                let backString = '';
+				let obj=this.dict[e.detail.value];
+				this.selected=obj.text;
+				backString=obj.value;
+				console.log("backString",backString)
+				console.log("this.selected",this.selected)
+				
+				this.$emit('input',backString);
+				// #ifndef MP-WEIXIN
+				this.$emit('change',backString);
+				// #endif
+            }
+        },
+        model: {
+            prop: 'value',
+			event:'change'
+        }
+    }
+</script>
+
+<style scoped>
+	.cu-form-group uni-picker::after {
+		font-family: cuIcon;
+		display: block;
+		content: "\e6a3";
+		position: absolute;
+		font-size: 14px;
+		color: #FFFFFF;
+		line-height: 42px;
+		width: 25px;
+		text-align: center;
+		top: 0;
+		bottom: 0;
+		right: -8px;
+		margin: auto;
+	}
+</style>

+ 93 - 0
components/my-componets/my-date.vue

@@ -0,0 +1,93 @@
+<template>
+		<view class="cu-form-group" @click="timechange">
+			<view class="title"><text class="text-red" v-if="required">*</text>{{label}}</view>
+			<input
+			  :placeholder="placeholder"
+			   name="input"
+			   v-model="selected"
+			   disabled="true"
+			></input>
+			<w-picker
+			    :visible.sync="visible"
+				ref="picker" 
+			    mode="date" 
+			    startYear="2020" 
+			    endYear="2100"
+				:value="value"
+			    :fields="fields"
+			    @confirm="onConfirm($event,'date')"
+			 ></w-picker>
+		</view>
+</template>
+
+<script>
+	export default {
+	        name: "AppSecelt",
+	        props:{
+	            placeholder:{
+	                type:String,
+	                default:'请选择',
+	                required:false
+	            },
+	            label:{
+	                type:String,
+	                default:'',
+	                required:false
+	            },
+				fields:{
+				    type:String,
+				    default:'second',
+				    required:false
+				},
+	            value:{
+	                type:String,
+	                required:false
+	            } ,
+				required:{
+	                type:Boolean,
+					default:false,
+	                required:false
+	            }
+	        },
+	        data(){
+	            return {
+	                visible:false,
+	                selected:''
+	            }
+	        },
+	        watch:{
+	            value:{
+	                immediate:true,
+	                handler(val){
+	                  if(!val){
+	                    this.selected = ''
+	                  }else{
+	                    this.selected = val;
+	                }
+	              }
+	            }
+	        },
+	        created(){
+				
+	        },
+	        methods:{
+				timechange(){
+					this.$refs.picker.show()
+				},
+	            onConfirm(e){
+					console.log("confirm",e)
+					let backString = e.value;
+					this.selected=e.value;
+	                this.$emit('input',backString);
+	            }
+	        },
+	        model: {
+	            prop: 'value',
+	            event: 'input'
+	        }
+	    }
+</script>
+	
+<style scoped>
+	
+</style>

+ 106 - 0
components/my-componets/my-image-upload.vue

@@ -0,0 +1,106 @@
+<template>
+	<view class="margin-top">
+		<view class="cu-bar bg-white ">
+			<view class="action">
+				{{label}}
+			</view>
+			<view class="action">
+				{{imgList.length}}/{{maxImg}}
+			</view>
+		</view>
+		<view class="cu-form-group">
+			<view class="grid col-4 grid-square flex-sub">
+				<view class="bg-img" v-for="(item,index) in imgList" :key="index" @tap="ViewImage" :data-url="imgList[index]">
+					<image :src="imgList[index]" mode="aspectFill"></image>
+					<view class="cu-tag bg-red" @tap.stop="DelImg" :data-index="index">
+						<text class='cuIcon-close'></text>
+					</view>
+				</view>
+				<view class="solids" @tap="ChooseImage" v-if="imgList.length<maxImg">
+					<text class='cuIcon-cameraadd'></text>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	
+	export default {
+		name: 'MyImageUpoad',
+		props: {
+			value: {type:String,default:''},
+			label:{type:String,default:'图片上传'},
+			maxImg: {
+				type: Number,
+				default: 3
+			},
+
+		},
+		mounted:function(){
+			if (this.value.split(',')!=''){
+				this.value.split(',').forEach(res=>{
+					this.imgList.push(baseurl+res)
+				})
+			}
+		},
+		data() {
+			return {
+				imgList: [],
+				pathlist:[],
+			}
+		},
+		methods: {
+			ChooseImage() {
+				uni.chooseImage({
+					count: this.maxImg, //默认9
+					sizeType: ['original', 'compressed'], //可以指定是原图还是压缩图,默认二者都有
+					sourceType: ['album','camera'], //从相册选择
+					success: (res) => {
+						uni.uploadFile({
+							url: `${baseurl}systemController/filedeal.do?isup=1`,
+							filePath: res.tempFilePaths[0],
+							name: 'file',
+							success: (uploadFileRes) => {
+								let path = JSON.parse(uploadFileRes.data).obj
+								this.pathlist.push(path);
+								this.$emit('input',this.pathlist.join(','))
+								if (this.imgList.length != 0) {
+									this.imgList = this.imgList.concat(res.tempFilePaths)
+								} else {
+									this.imgList = res.tempFilePaths
+								}
+							}
+						})
+
+					}
+				});
+			},
+			ViewImage(e) {
+				uni.previewImage({
+					urls: this.imgList,
+					current: e.currentTarget.dataset.url
+				});
+			},
+			DelImg(e) {
+				uni.showModal({
+					title: '提示',
+					content: '确认要删除吗',
+					cancelText: '取消',
+					confirmText: '确认',
+					success: res => {
+						if (res.confirm) {
+							this.pathlist.splice(e.currentTarget.dataset.index,1)
+							this.imgList.splice(e.currentTarget.dataset.index, 1)
+							this.$emit('input',this.pathlist.join(','))
+						}
+					}
+				})
+			},
+		}
+	}
+</script>
+
+<style>
+
+</style>

+ 326 - 0
components/my-componets/my-map.vue

@@ -0,0 +1,326 @@
+<template>
+	<view>
+		<map
+			style="width: 100%; height:250px;"
+			:latitude="latitude"
+			:longitude="longitude"
+			:markers="marker"
+			:scale="scale"
+			:circles="circles"
+		>
+		<!--  :circles="circles" -->
+		</map>
+	</view>
+</template>
+
+<script>
+	import { geoDistance } from '@/common/util/util.js'
+	import amap from "@/common/js-sdk/js-amap/amap-wx.js";
+	// #ifdef H5
+	import AMap  from "@/common/js-sdk/js-amap/amap-h5.js";
+	// #endif
+	
+	export default {
+		 props:{
+			compLatitude:{
+				type:Number,
+				default:40.009390,
+				required:false
+			},
+			compLongitude:{
+				type:Number,
+				default:116.374322,
+				required:false
+			}
+		},
+		data() {
+			return {
+				amapPlugin:null,
+				wxMapKey:"53324ee357405c4a65f35a1aa05ffaf2",
+				id:0,
+			    title: 'map',
+				distance:0,
+				address:"",
+		       latitude: this.compLatitude,  //纬度
+			    longitude: this.compLongitude,  //经度
+				scale:16,//地图缩放程度
+				tipText:'打卡范围',
+				bgColor:'#00c16f',
+			    marker: [],
+			   circles:[{//在地图上显示圆
+				    latitude: this.compLatitude,
+				    longitude: this.compLongitude,
+				    radius:80,//半径
+					  fillColor:"#ffffffAA",//填充颜色
+				    color:"#55aaffAA",//描边的颜色
+				    strokeWidth:1//描边的宽度
+				}],
+				resAmap:null
+			}
+		},
+	    created() {
+			// #ifdef MP-WEIXIN || APP-PLUS
+	    	this.amapPlugin = new amap.AMapWX({
+	    	    key: this.wxMapKey
+	    	});
+			// #endif
+			
+			// #ifdef H5
+			this.initAMap()
+			// #endif
+	    },
+		mounted() {
+			// #ifdef MP-WEIXIN
+			  this.getAuthorizeInfo();
+			// #endif
+			// #ifdef APP-PLUS
+			  this.getLocationInfoWx();
+			// #endif
+			// #ifdef H5
+			  //this.getLocationInfo()
+			// #endif
+		},
+		computed:{
+		  inCircle(){
+			return this.address && this.distance <= 80
+		  }
+		},
+		methods: {
+			allowed(){
+				return this.inCircle
+			},
+			getMyAddress(){
+				return this.address
+			},
+			refreshLocation(){
+				// #ifdef MP-WEIXIN
+				  this.getAuthorizeInfo();
+				// #endif
+				// #ifdef APP-PLUS
+				  this.getLocationInfoWx();
+				// #endif
+				// #ifdef H5
+				  this.initAMap()
+				// #endif
+			},
+			getAuthorizeInfo(){
+				//1. uniapp弹窗弹出获取授权(地理,个人微信信息等授权信息)弹窗
+				var _this=this;
+				uni.authorize({
+					scope: "scope.userLocation",
+					success() { //1.1 允许授权
+						_this.getLocationInfoWx();
+					},
+					fail(){    //1.2 拒绝授权
+						console.log("你拒绝了授权,无法获得周边信息")
+						_this.openConfirm();
+					}
+				})
+			},
+			getLocationInfoWx(){
+				var that=this;
+				this.amapPlugin.getRegeo({
+				    type: 'gcj02', //map 组件使用的经纬度是国测局坐标, type 为 gcj02
+				    success: function(res) {
+						console.log("success",res);
+						that.latitude = res[0].latitude;
+						that.longitude = res[0].longitude;
+						that.address = res[0].name + res[0].desc;
+						that.distance=geoDistance(that.longitude, that.latitude,that.compLongitude,that.compLatitude)
+						console.log("that.distance",that.distance);
+						let tipText=(that.distance>80?"未在":"已在")+"打卡范围内";
+						let bgColor=that.distance>80?"#ff0000":"#00c16f";
+						let marker={
+							   id:0,
+						     latitude:that.latitude,//纬度
+						     longitude:that.longitude,//经度
+						       iconPath: '/static/location.png',
+							   width:35,
+							   height:35,
+							   // #ifdef MP-WEIXIN
+							   label:{//为标记点旁边增加标签
+							     content:tipText,//文本
+						        color:'#ffffff',//文本颜色
+								  fontSize:14,//文字大小
+								   borderWidth:2,//边框宽度
+								   borderColor:bgColor,//边框颜色 
+								   bgColor:bgColor,//背景颜色
+								  borderRadius:2,//边框圆角  
+								  padding:5,//文本边缘留白
+								   textAlign:'center',//文本对齐方式
+								   x:0,//label的坐标,原点是 marker 对应的经纬度
+								   y:0,//label的坐标,原点是 marker 对应的经纬度 
+							    }, 
+							   // #endif
+							   // #ifdef APP-PLUS
+							   callout:{//自定义标记点上方的气泡窗口 点击有效
+							     content:tipText,//文本
+							     color:'#ffffff',//文字颜色
+							     fontSize:14,//文本大小
+							     //borderRadius:2,//边框圆角
+							     bgColor:bgColor,//背景颜色
+							     display:'ALWAYS',//常显
+								   textAlign:'center'
+							    },
+							   // #endif
+						    }
+							that.marker=[marker];
+				    },
+				    fail: (res) => {
+				      console.log(JSON.stringify(res));
+				    }
+				 });
+			},
+			getLocationInfo() {
+				var _this=this;
+				uni.showLoading({
+					title: '获取信息中',
+					mask:true
+				});
+				uni.getLocation({
+					//type: 'wgs84',
+					type:'gcj02',
+					success: function (res) {
+						console.log('当前位置的经度:' + res.longitude);
+						console.log('当前位置的纬度:' + res.latitude);
+					    _this.distance=geoDistance(res.longitude, res.latitude,_this.compLongitude,_this.compLatitude)
+						let tipText=(_this.distance>80?"未在":"已在")+"打卡范围内";
+						let bgColor=_this.distance>80?"#ff0000":"#00c16f";
+						_this.longitude=res.longitude
+						_this.latitude=res.latitude
+						let marker={
+					         latitude: res.latitude,//纬度
+					         longitude:res.longitude,//经度
+					         callout:{//自定义标记点上方的气泡窗口 点击有效
+					    	     content:tipText,//文本
+					    	     color:'#ffffff',//文字颜色
+					    	     fontSize:14,//文本大小
+					    	     borderRadius:2,//边框圆角
+					    	     bgColor:bgColor,//背景颜色
+					    	     display:'ALWAYS'//常显
+					           }
+					        }
+							_this.marker=[marker];
+					},
+					fail: function (res){
+						console.log('getLocation==> fail:' + res);
+						console.log(res);
+					}
+				});
+				uni.hideLoading();
+			},
+			 // 当用户第一次拒绝后再次请求授权
+			openConfirm(){
+				uni.showModal({
+					title: '请求授权当前位置',
+					content: '需要获取您的地理位置,请确认授权',
+					success: (res)=> {
+						if (res.confirm) {
+							uni.openSetting();// 打开地图权限设置
+						} else if (res.cancel) {
+							uni.showToast({
+								title: '你拒绝了授权,无法获得位置信息',
+								icon: 'none',
+								duration: 1000
+							})
+						}
+					}
+				});
+			},
+		  // 根据坐标返回地址(逆地理编码)
+	      /* async getAddress (points) {
+			  try {
+			  	this.resAmap = await AMap();
+			  	this.$nextTick(function() {
+			  		this.resAmap.plugin('AMap.Geocoder', () => {
+			  			var geocoder  = new this.resAmap.Geocoder({
+			  				radius: 1000,
+						});
+			  			geocoder.getAddress(points, (status, result) => {
+							if (status === 'complete' && result.regeocode) {
+							  this.address = result.regeocode.formattedAddress
+							}
+						})
+			  		});
+			  	})
+			  } catch (e) {
+			  	console.log(e)
+			  }
+			}, */
+			// #ifdef H5
+			async initAMap() {
+				try {
+					uni.showLoading({
+						title: '定位中...',
+						mask:true
+					});
+					this.resAmap = await AMap();
+					this.$nextTick(function() {
+						this.resAmap.plugin('AMap.Geolocation', () => {
+							var geolocation = new this.resAmap.Geolocation({
+								enableHighAccuracy: true, //是否使用高精度定位,默认:true
+								timeout: 10000, //超过10秒后停止定位,默认:5s
+								buttonPosition: 'RB', //定位按钮的停靠位置
+								// buttonOffset: new AMap.Pixel(10, 20),//定位按钮与设置的停靠位置的偏移量,默认:Pixel(10, 20)
+								zoomToAccuracy: true, //定位成功后是否自动调整地图视野到定位点
+					
+							});
+							geolocation.getCurrentPosition(function(status, result) {
+								if (status == 'complete') {
+									onComplete(result)
+								} else {
+									onError(result)
+								}
+							});
+					
+						});
+						
+						//解析定位结果
+						var _this = this;
+					
+						function onComplete(data) {
+							console.log("H5高德定位",data) 
+							console.log('当前位置的经度:' + data.position.lat);
+							console.log('当前位置的纬度:' + data.position.lng);
+							_this.distance=geoDistance(data.position.lng, data.position.lat,_this.compLongitude,_this.compLatitude)
+							let tipText=(_this.distance>80?"未在":"已在")+"打卡范围内";
+							let bgColor=_this.distance>80?"#ff0000":"#00c16f";
+							_this.longitude=data.position.lng
+							_this.latitude=data.position.lat
+							_this.address=data.formattedAddress
+							let marker={
+							     latitude: _this.latitude,//纬度
+							     longitude:_this.longitude,//经度
+							     callout:{//自定义标记点上方的气泡窗口 点击有效
+								     content:tipText,//文本
+								     color:'#ffffff',//文字颜色
+								     fontSize:14,//文本大小
+								     borderRadius:2,//边框圆角
+								     bgColor:bgColor,//背景颜色
+								     display:'ALWAYS'//常显
+							       }
+							    }
+								_this.marker=[marker];
+								uni.hideLoading();
+								_this.$tip.success("定位成功")
+						}
+					
+						function onError(data) {
+							console.log(data) // 定位失败的信息
+						}
+					
+					})
+				} catch (e) {
+					console.log(e)
+				   _this.$tip.alert("定位失败")
+			    }
+			}, 
+			// #endif
+			
+		}
+	}
+</script>
+
+<style>
+
+</style>

+ 79 - 0
components/my-componets/my-nav.vue

@@ -0,0 +1,79 @@
+<template>
+	<view>
+		<view class="cu-custom" :style="[{height:CustomBar + 'px',zIndex:zIndex}]">
+			<view class="cu-bar fixed" :style="style" :class="[bgImage!=''?'none-bg text-white bg-img':'',bgColor]">
+				<view class="action" @tap="BackPage" v-if="isBack">
+					<text class="cuIcon-back"></text>
+					<slot name="backText"></slot>
+				</view>
+				<view class="content" :style="[{top:StatusBar + 'px'}]">
+					<slot name="content"></slot>
+				</view>
+				<view class="action">
+					<slot name="right"></slot>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				StatusBar: this.StatusBar,
+				CustomBar: this.CustomBar
+			};
+		},
+		name: 'cu-custom',
+		computed: {
+			style() {
+				var StatusBar= this.StatusBar;
+				var CustomBar= this.CustomBar;
+				var bgImage = this.bgImage;
+				var style = `height:${CustomBar}px;padding-top:${StatusBar}px;`;
+				if (this.bgImage) {
+					style = `${style}background-image:url(${bgImage});`;
+				}
+				return style
+			}
+		},
+		props: {
+			bgColor: {
+				type: String,
+				default: ''
+			},
+			isBack: {
+				type: [Boolean, String],
+				default: false
+			},
+			bgImage: {
+				type: String,
+				default: ''
+			},
+			zIndex:{
+				type: String,
+				default: '10'
+			},
+			backRouterName:{
+				type: String,
+				default: ''
+			}
+		},
+		methods: {
+			BackPage() {
+				if(!this.backRouterName){
+					uni.navigateBack({
+						delta: 1
+					});	
+				}else{
+					this.$Router.replace({name:this.backRouterName})
+				}
+			}
+		}
+	}
+</script>
+
+<style>
+
+</style>

+ 46 - 0
components/my-componets/my-page.vue

@@ -0,0 +1,46 @@
+<template>
+	<view>
+		<scroll-view :scroll-y="modalName==null" class="page" :class="modalName!=null?'show':''">
+			<cu-custom bgColor="bg-gradual-pink" :isBack="true">
+				<block slot="backText">{{back}}</block>
+				<block slot="content">{{title}}</block>
+			</cu-custom>
+			<slot></slot>
+		</scroll-view>
+	</view>
+</template>
+
+<script>
+	export default {
+		name: 'MyPage',
+		props: {
+			title: {
+				type: String,
+				default: '标题'
+			},
+			back: {
+				type: String,
+				default: '返回'
+			},
+			modalName:  {
+				type: String,
+				default: null
+			},
+		},
+		watch: {
+
+		},
+		computed: {
+
+		},
+		data() {
+			return {
+				
+			}
+		},
+		methods: {}
+	}
+</script>
+
+<style>
+</style>

+ 85 - 0
components/my-componets/my-select.vue

@@ -0,0 +1,85 @@
+<template>
+		<view class="cu-form-group">
+			<view class="title">{{label}}</view>
+		<picker @change="PickerChange" :value="index" :range-key="rangeKey" :range="dictOptions">
+			<view class="picker">
+				{{index>-1?dictOptions[index][rangeKey]:'请选择'}}
+			</view>
+		</picker>
+		</view>
+</template>
+
+<script>
+	export default {
+		name: 'MySelect',
+		props: {
+			dictCode: String,
+			value: String,
+			label:String,
+			rangeKey:{type:String,default:'label'},
+			valueKey:{type:String,default:'value'},
+			searchUrl:String,
+
+		},
+		watch: {
+			dictCode: {
+				immediate: true,
+				handler() {
+					this.initDictData()
+				},
+			},
+		},
+		computed: {
+			
+		},
+		data() {
+			return {
+				dictOptions: [],
+				index: -1,
+			}
+		},
+		methods: {
+			initDictData() {
+				//根据字典Code, 初始化字典数组
+				if (this.searchUrl){
+					this.$http.get(this.searchUrl,{"code":this.dictCode}).then(res=>{
+						if(res.data.success){
+							this.dictOptions = res;
+							this.getIndex()
+						}
+					})
+				}else{
+					let code = this.dictCode;
+					this.$http.get(`/sys/dict/getDictItems/${code}`).then(res=>{
+						if(res.data.success){
+							this.dictOptions = res.data.result;
+							this.getIndex()
+						}
+					})
+				}
+			},
+			PickerChange(e) {
+				this.index=e.detail.value
+				if(this.index==-1){
+					this.index=0
+					this.$emit('input',this.dictOptions[0][this.valueKey])
+				}else{
+					this.$emit('input', this.dictOptions[this.index][this.valueKey]);
+				}
+			},
+			getIndex() {
+				for (var i = 0; i < this.dictOptions.length; i++) {
+					if (this.dictOptions[i].value == this.value) {
+						this.index = i
+						return
+					}
+				}
+				this.index=-1
+			},
+		}
+	}
+</script>
+
+<style>
+
+</style>

+ 546 - 0
components/uni-calendar/calendar.js

@@ -0,0 +1,546 @@
+/**
+* @1900-2100区间内的公历、农历互转
+* @charset UTF-8
+* @github  https://github.com/jjonline/calendar.js
+* @Author  Jea杨(JJonline@JJonline.Cn)
+* @Time    2014-7-21
+* @Time    2016-8-13 Fixed 2033hex、Attribution Annals
+* @Time    2016-9-25 Fixed lunar LeapMonth Param Bug
+* @Time    2017-7-24 Fixed use getTerm Func Param Error.use solar year,NOT lunar year
+* @Version 1.0.3
+* @公历转农历:calendar.solar2lunar(1987,11,01); //[you can ignore params of prefix 0]
+* @农历转公历:calendar.lunar2solar(1987,09,10); //[you can ignore params of prefix 0]
+*/
+/* eslint-disable */
+var calendar = {
+
+  /**
+      * 农历1900-2100的润大小信息表
+      * @Array Of Property
+      * @return Hex
+      */
+  lunarInfo: [0x04bd8, 0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0, 0x055d2, // 1900-1909
+    0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540, 0x0d6a0, 0x0ada2, 0x095b0, 0x14977, // 1910-1919
+    0x04970, 0x0a4b0, 0x0b4b5, 0x06a50, 0x06d40, 0x1ab54, 0x02b60, 0x09570, 0x052f2, 0x04970, // 1920-1929
+    0x06566, 0x0d4a0, 0x0ea50, 0x06e95, 0x05ad0, 0x02b60, 0x186e3, 0x092e0, 0x1c8d7, 0x0c950, // 1930-1939
+    0x0d4a0, 0x1d8a6, 0x0b550, 0x056a0, 0x1a5b4, 0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0b557, // 1940-1949
+    0x06ca0, 0x0b550, 0x15355, 0x04da0, 0x0a5b0, 0x14573, 0x052b0, 0x0a9a8, 0x0e950, 0x06aa0, // 1950-1959
+    0x0aea6, 0x0ab50, 0x04b60, 0x0aae4, 0x0a570, 0x05260, 0x0f263, 0x0d950, 0x05b57, 0x056a0, // 1960-1969
+    0x096d0, 0x04dd5, 0x04ad0, 0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b6a0, 0x195a6, // 1970-1979
+    0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50, 0x06d40, 0x0af46, 0x0ab60, 0x09570, // 1980-1989
+    0x04af5, 0x04970, 0x064b0, 0x074a3, 0x0ea50, 0x06b58, 0x05ac0, 0x0ab60, 0x096d5, 0x092e0, // 1990-1999
+    0x0c960, 0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0, 0x0abb7, 0x025d0, 0x092d0, 0x0cab5, // 2000-2009
+    0x0a950, 0x0b4a0, 0x0baa4, 0x0ad50, 0x055d9, 0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930, // 2010-2019
+    0x07954, 0x06aa0, 0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0, 0x0d260, 0x0ea65, 0x0d530, // 2020-2029
+    0x05aa0, 0x076a3, 0x096d0, 0x04afb, 0x04ad0, 0x0a4d0, 0x1d0b6, 0x0d250, 0x0d520, 0x0dd45, // 2030-2039
+    0x0b5a0, 0x056d0, 0x055b2, 0x049b0, 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0, // 2040-2049
+    /** Add By JJonline@JJonline.Cn**/
+    0x14b63, 0x09370, 0x049f8, 0x04970, 0x064b0, 0x168a6, 0x0ea50, 0x06b20, 0x1a6c4, 0x0aae0, // 2050-2059
+    0x0a2e0, 0x0d2e3, 0x0c960, 0x0d557, 0x0d4a0, 0x0da50, 0x05d55, 0x056a0, 0x0a6d0, 0x055d4, // 2060-2069
+    0x052d0, 0x0a9b8, 0x0a950, 0x0b4a0, 0x0b6a6, 0x0ad50, 0x055a0, 0x0aba4, 0x0a5b0, 0x052b0, // 2070-2079
+    0x0b273, 0x06930, 0x07337, 0x06aa0, 0x0ad50, 0x14b55, 0x04b60, 0x0a570, 0x054e4, 0x0d160, // 2080-2089
+    0x0e968, 0x0d520, 0x0daa0, 0x16aa6, 0x056d0, 0x04ae0, 0x0a9d4, 0x0a2d0, 0x0d150, 0x0f252, // 2090-2099
+    0x0d520], // 2100
+
+  /**
+      * 公历每个月份的天数普通表
+      * @Array Of Property
+      * @return Number
+      */
+  solarMonth: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
+
+  /**
+      * 天干地支之天干速查表
+      * @Array Of Property trans["甲","乙","丙","丁","戊","己","庚","辛","壬","癸"]
+      * @return Cn string
+      */
+  Gan: ['\u7532', '\u4e59', '\u4e19', '\u4e01', '\u620a', '\u5df1', '\u5e9a', '\u8f9b', '\u58ec', '\u7678'],
+
+  /**
+      * 天干地支之地支速查表
+      * @Array Of Property
+      * @trans["子","丑","寅","卯","辰","巳","午","未","申","酉","戌","亥"]
+      * @return Cn string
+      */
+  Zhi: ['\u5b50', '\u4e11', '\u5bc5', '\u536f', '\u8fb0', '\u5df3', '\u5348', '\u672a', '\u7533', '\u9149', '\u620c', '\u4ea5'],
+
+  /**
+      * 天干地支之地支速查表<=>生肖
+      * @Array Of Property
+      * @trans["鼠","牛","虎","兔","龙","蛇","马","羊","猴","鸡","狗","猪"]
+      * @return Cn string
+      */
+  Animals: ['\u9f20', '\u725b', '\u864e', '\u5154', '\u9f99', '\u86c7', '\u9a6c', '\u7f8a', '\u7334', '\u9e21', '\u72d7', '\u732a'],
+
+  /**
+      * 24节气速查表
+      * @Array Of Property
+      * @trans["小寒","大寒","立春","雨水","惊蛰","春分","清明","谷雨","立夏","小满","芒种","夏至","小暑","大暑","立秋","处暑","白露","秋分","寒露","霜降","立冬","小雪","大雪","冬至"]
+      * @return Cn string
+      */
+  solarTerm: ['\u5c0f\u5bd2', '\u5927\u5bd2', '\u7acb\u6625', '\u96e8\u6c34', '\u60ca\u86f0', '\u6625\u5206', '\u6e05\u660e', '\u8c37\u96e8', '\u7acb\u590f', '\u5c0f\u6ee1', '\u8292\u79cd', '\u590f\u81f3', '\u5c0f\u6691', '\u5927\u6691', '\u7acb\u79cb', '\u5904\u6691', '\u767d\u9732', '\u79cb\u5206', '\u5bd2\u9732', '\u971c\u964d', '\u7acb\u51ac', '\u5c0f\u96ea', '\u5927\u96ea', '\u51ac\u81f3'],
+
+  /**
+      * 1900-2100各年的24节气日期速查表
+      * @Array Of Property
+      * @return 0x string For splice
+      */
+  sTermInfo: ['9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf97c3598082c95f8c965cc920f',
+    '97bd0b06bdb0722c965ce1cfcc920f', 'b027097bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
+    '97bcf97c359801ec95f8c965cc920f', '97bd0b06bdb0722c965ce1cfcc920f', 'b027097bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f', '97bd0b06bdb0722c965ce1cfcc920f',
+    'b027097bd097c36b0b6fc9274c91aa', '9778397bd19801ec9210c965cc920e', '97b6b97bd19801ec95f8c965cc920f',
+    '97bd09801d98082c95f8e1cfcc920f', '97bd097bd097c36b0b6fc9210c8dc2', '9778397bd197c36c9210c9274c91aa',
+    '97b6b97bd19801ec95f8c965cc920e', '97bd09801d98082c95f8e1cfcc920f', '97bd097bd097c36b0b6fc9210c8dc2',
+    '9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec95f8c965cc920e', '97bcf97c3598082c95f8e1cfcc920f',
+    '97bd097bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec9210c965cc920e',
+    '97bcf97c3598082c95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c965cc920e', '97bcf97c3598082c95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722',
+    '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f',
+    '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
+    '97bcf97c359801ec95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f', '97bd097bd07f595b0b6fc920fb0722',
+    '9778397bd097c36b0b6fc9210c8dc2', '9778397bd19801ec9210c9274c920e', '97b6b97bd19801ec95f8c965cc920f',
+    '97bd07f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c920e',
+    '97b6b97bd19801ec95f8c965cc920f', '97bd07f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2',
+    '9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bd07f1487f595b0b0bc920fb0722',
+    '7f0e397bd097c36b0b6fc9210c8dc2', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
+    '97bcf7f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c965cc920e', '97bcf7f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722',
+    '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf7f1487f531b0b0bb0b6fb0722',
+    '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
+    '97bcf7f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c9274c920e', '97bcf7f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722',
+    '9778397bd097c36b0b6fc9210c91aa', '97b6b97bd197c36c9210c9274c920e', '97bcf7f0e47f531b0b0bb0b6fb0722',
+    '7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c920e',
+    '97b6b7f0e47f531b0723b0b6fb0722', '7f0e37f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2',
+    '9778397bd097c36b0b70c9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', '7f0e37f1487f595b0b0bb0b6fb0722',
+    '7f0e397bd097c35b0b6fc9210c8dc2', '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721',
+    '7f0e27f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722',
+    '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722',
+    '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721',
+    '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b7f0e47f531b0723b0787b0721', '7f0e27f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722',
+    '9778397bd097c36b0b6fc9210c91aa', '97b6b7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722',
+    '7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9210c8dc2', '977837f0e37f149b0723b0787b0721',
+    '7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f5307f595b0b0bc920fb0722', '7f0e397bd097c35b0b6fc9210c8dc2',
+    '977837f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e37f1487f595b0b0bb0b6fb0722',
+    '7f0e397bd097c35b0b6fc9210c8dc2', '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
+    '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '977837f0e37f14998082b0787b06bd',
+    '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722',
+    '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722',
+    '7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
+    '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14998082b0787b06bd',
+    '7f07e7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722',
+    '977837f0e37f14998082b0723b06bd', '7f07e7f0e37f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722',
+    '7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b0721',
+    '7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f1487f595b0b0bb0b6fb0722', '7f0e37f0e37f14898082b0723b02d5',
+    '7ec967f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f1487f531b0b0bb0b6fb0722',
+    '7f0e37f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
+    '7f0e37f1487f531b0b0bb0b6fb0722', '7f0e37f0e37f14898082b072297c35', '7ec967f0e37f14998082b0787b06bd',
+    '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e37f0e37f14898082b072297c35',
+    '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722',
+    '7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f149b0723b0787b0721',
+    '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14998082b0723b06bd',
+    '7f07e7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722', '7f0e37f0e366aa89801eb072297c35',
+    '7ec967f0e37f14998082b0723b06bd', '7f07e7f0e37f14998083b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722',
+    '7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14898082b0723b02d5', '7f07e7f0e37f14998082b0787b0721',
+    '7f07e7f0e47f531b0723b0b6fb0722', '7f0e36665b66aa89801e9808297c35', '665f67f0e37f14898082b0723b02d5',
+    '7ec967f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0722', '7f0e36665b66a449801e9808297c35',
+    '665f67f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
+    '7f0e36665b66a449801e9808297c35', '665f67f0e37f14898082b072297c35', '7ec967f0e37f14998082b0787b06bd',
+    '7f07e7f0e47f531b0723b0b6fb0721', '7f0e26665b66a449801e9808297c35', '665f67f0e37f1489801eb072297c35',
+    '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722'],
+
+  /**
+      * 数字转中文速查表
+      * @Array Of Property
+      * @trans ['日','一','二','三','四','五','六','七','八','九','十']
+      * @return Cn string
+      */
+  nStr1: ['\u65e5', '\u4e00', '\u4e8c', '\u4e09', '\u56db', '\u4e94', '\u516d', '\u4e03', '\u516b', '\u4e5d', '\u5341'],
+
+  /**
+      * 日期转农历称呼速查表
+      * @Array Of Property
+      * @trans ['初','十','廿','卅']
+      * @return Cn string
+      */
+  nStr2: ['\u521d', '\u5341', '\u5eff', '\u5345'],
+
+  /**
+      * 月份转农历称呼速查表
+      * @Array Of Property
+      * @trans ['正','一','二','三','四','五','六','七','八','九','十','冬','腊']
+      * @return Cn string
+      */
+  nStr3: ['\u6b63', '\u4e8c', '\u4e09', '\u56db', '\u4e94', '\u516d', '\u4e03', '\u516b', '\u4e5d', '\u5341', '\u51ac', '\u814a'],
+
+  /**
+      * 返回农历y年一整年的总天数
+      * @param lunar Year
+      * @return Number
+      * @eg:var count = calendar.lYearDays(1987) ;//count=387
+      */
+  lYearDays: function (y) {
+    var i; var sum = 348
+    for (i = 0x8000; i > 0x8; i >>= 1) { sum += (this.lunarInfo[y - 1900] & i) ? 1 : 0 }
+    return (sum + this.leapDays(y))
+  },
+
+  /**
+      * 返回农历y年闰月是哪个月;若y年没有闰月 则返回0
+      * @param lunar Year
+      * @return Number (0-12)
+      * @eg:var leapMonth = calendar.leapMonth(1987) ;//leapMonth=6
+      */
+  leapMonth: function (y) { // 闰字编码 \u95f0
+    return (this.lunarInfo[y - 1900] & 0xf)
+  },
+
+  /**
+      * 返回农历y年闰月的天数 若该年没有闰月则返回0
+      * @param lunar Year
+      * @return Number (0、29、30)
+      * @eg:var leapMonthDay = calendar.leapDays(1987) ;//leapMonthDay=29
+      */
+  leapDays: function (y) {
+    if (this.leapMonth(y)) {
+      return ((this.lunarInfo[y - 1900] & 0x10000) ? 30 : 29)
+    }
+    return (0)
+  },
+
+  /**
+      * 返回农历y年m月(非闰月)的总天数,计算m为闰月时的天数请使用leapDays方法
+      * @param lunar Year
+      * @return Number (-1、29、30)
+      * @eg:var MonthDay = calendar.monthDays(1987,9) ;//MonthDay=29
+      */
+  monthDays: function (y, m) {
+    if (m > 12 || m < 1) { return -1 }// 月份参数从1至12,参数错误返回-1
+    return ((this.lunarInfo[y - 1900] & (0x10000 >> m)) ? 30 : 29)
+  },
+
+  /**
+      * 返回公历(!)y年m月的天数
+      * @param solar Year
+      * @return Number (-1、28、29、30、31)
+      * @eg:var solarMonthDay = calendar.leapDays(1987) ;//solarMonthDay=30
+      */
+  solarDays: function (y, m) {
+    if (m > 12 || m < 1) { return -1 } // 若参数错误 返回-1
+    var ms = m - 1
+    if (ms == 1) { // 2月份的闰平规律测算后确认返回28或29
+      return (((y % 4 == 0) && (y % 100 != 0) || (y % 400 == 0)) ? 29 : 28)
+    } else {
+      return (this.solarMonth[ms])
+    }
+  },
+
+  /**
+     * 农历年份转换为干支纪年
+     * @param  lYear 农历年的年份数
+     * @return Cn string
+     */
+  toGanZhiYear: function (lYear) {
+    var ganKey = (lYear - 3) % 10
+    var zhiKey = (lYear - 3) % 12
+    if (ganKey == 0) ganKey = 10// 如果余数为0则为最后一个天干
+    if (zhiKey == 0) zhiKey = 12// 如果余数为0则为最后一个地支
+    return this.Gan[ganKey - 1] + this.Zhi[zhiKey - 1]
+  },
+
+  /**
+     * 公历月、日判断所属星座
+     * @param  cMonth [description]
+     * @param  cDay [description]
+     * @return Cn string
+     */
+  toAstro: function (cMonth, cDay) {
+    var s = '\u9b54\u7faf\u6c34\u74f6\u53cc\u9c7c\u767d\u7f8a\u91d1\u725b\u53cc\u5b50\u5de8\u87f9\u72ee\u5b50\u5904\u5973\u5929\u79e4\u5929\u874e\u5c04\u624b\u9b54\u7faf'
+    var arr = [20, 19, 21, 21, 21, 22, 23, 23, 23, 23, 22, 22]
+    return s.substr(cMonth * 2 - (cDay < arr[cMonth - 1] ? 2 : 0), 2) + '\u5ea7'// 座
+  },
+
+  /**
+      * 传入offset偏移量返回干支
+      * @param offset 相对甲子的偏移量
+      * @return Cn string
+      */
+  toGanZhi: function (offset) {
+    return this.Gan[offset % 10] + this.Zhi[offset % 12]
+  },
+
+  /**
+      * 传入公历(!)y年获得该年第n个节气的公历日期
+      * @param y公历年(1900-2100);n二十四节气中的第几个节气(1~24);从n=1(小寒)算起
+      * @return day Number
+      * @eg:var _24 = calendar.getTerm(1987,3) ;//_24=4;意即1987年2月4日立春
+      */
+  getTerm: function (y, n) {
+    if (y < 1900 || y > 2100) { return -1 }
+    if (n < 1 || n > 24) { return -1 }
+    var _table = this.sTermInfo[y - 1900]
+    var _info = [
+      parseInt('0x' + _table.substr(0, 5)).toString(),
+      parseInt('0x' + _table.substr(5, 5)).toString(),
+      parseInt('0x' + _table.substr(10, 5)).toString(),
+      parseInt('0x' + _table.substr(15, 5)).toString(),
+      parseInt('0x' + _table.substr(20, 5)).toString(),
+      parseInt('0x' + _table.substr(25, 5)).toString()
+    ]
+    var _calday = [
+      _info[0].substr(0, 1),
+      _info[0].substr(1, 2),
+      _info[0].substr(3, 1),
+      _info[0].substr(4, 2),
+
+      _info[1].substr(0, 1),
+      _info[1].substr(1, 2),
+      _info[1].substr(3, 1),
+      _info[1].substr(4, 2),
+
+      _info[2].substr(0, 1),
+      _info[2].substr(1, 2),
+      _info[2].substr(3, 1),
+      _info[2].substr(4, 2),
+
+      _info[3].substr(0, 1),
+      _info[3].substr(1, 2),
+      _info[3].substr(3, 1),
+      _info[3].substr(4, 2),
+
+      _info[4].substr(0, 1),
+      _info[4].substr(1, 2),
+      _info[4].substr(3, 1),
+      _info[4].substr(4, 2),
+
+      _info[5].substr(0, 1),
+      _info[5].substr(1, 2),
+      _info[5].substr(3, 1),
+      _info[5].substr(4, 2)
+    ]
+    return parseInt(_calday[n - 1])
+  },
+
+  /**
+      * 传入农历数字月份返回汉语通俗表示法
+      * @param lunar month
+      * @return Cn string
+      * @eg:var cnMonth = calendar.toChinaMonth(12) ;//cnMonth='腊月'
+      */
+  toChinaMonth: function (m) { // 月 => \u6708
+    if (m > 12 || m < 1) { return -1 } // 若参数错误 返回-1
+    var s = this.nStr3[m - 1]
+    s += '\u6708'// 加上月字
+    return s
+  },
+
+  /**
+      * 传入农历日期数字返回汉字表示法
+      * @param lunar day
+      * @return Cn string
+      * @eg:var cnDay = calendar.toChinaDay(21) ;//cnMonth='廿一'
+      */
+  toChinaDay: function (d) { // 日 => \u65e5
+    var s
+    switch (d) {
+      case 10:
+        s = '\u521d\u5341'; break
+      case 20:
+        s = '\u4e8c\u5341'; break
+        break
+      case 30:
+        s = '\u4e09\u5341'; break
+        break
+      default :
+        s = this.nStr2[Math.floor(d / 10)]
+        s += this.nStr1[d % 10]
+    }
+    return (s)
+  },
+
+  /**
+      * 年份转生肖[!仅能大致转换] => 精确划分生肖分界线是“立春”
+      * @param y year
+      * @return Cn string
+      * @eg:var animal = calendar.getAnimal(1987) ;//animal='兔'
+      */
+  getAnimal: function (y) {
+    return this.Animals[(y - 4) % 12]
+  },
+
+  /**
+      * 传入阳历年月日获得详细的公历、农历object信息 <=>JSON
+      * @param y  solar year
+      * @param m  solar month
+      * @param d  solar day
+      * @return JSON object
+      * @eg:console.log(calendar.solar2lunar(1987,11,01));
+      */
+  solar2lunar: function (y, m, d) { // 参数区间1900.1.31~2100.12.31
+    // 年份限定、上限
+    if (y < 1900 || y > 2100) {
+      return -1// undefined转换为数字变为NaN
+    }
+    // 公历传参最下限
+    if (y == 1900 && m == 1 && d < 31) {
+      return -1
+    }
+    // 未传参  获得当天
+    if (!y) {
+      var objDate = new Date()
+    } else {
+      var objDate = new Date(y, parseInt(m) - 1, d)
+    }
+    var i; var leap = 0; var temp = 0
+    // 修正ymd参数
+    var y = objDate.getFullYear()
+    var m = objDate.getMonth() + 1
+    var d = objDate.getDate()
+    var offset = (Date.UTC(objDate.getFullYear(), objDate.getMonth(), objDate.getDate()) - Date.UTC(1900, 0, 31)) / 86400000
+    for (i = 1900; i < 2101 && offset > 0; i++) {
+      temp = this.lYearDays(i)
+      offset -= temp
+    }
+    if (offset < 0) {
+      offset += temp; i--
+    }
+
+    // 是否今天
+    var isTodayObj = new Date()
+    var isToday = false
+    if (isTodayObj.getFullYear() == y && isTodayObj.getMonth() + 1 == m && isTodayObj.getDate() == d) {
+      isToday = true
+    }
+    // 星期几
+    var nWeek = objDate.getDay()
+    var cWeek = this.nStr1[nWeek]
+    // 数字表示周几顺应天朝周一开始的惯例
+    if (nWeek == 0) {
+      nWeek = 7
+    }
+    // 农历年
+    var year = i
+    var leap = this.leapMonth(i) // 闰哪个月
+    var isLeap = false
+
+    // 效验闰月
+    for (i = 1; i < 13 && offset > 0; i++) {
+      // 闰月
+      if (leap > 0 && i == (leap + 1) && isLeap == false) {
+        --i
+        isLeap = true; temp = this.leapDays(year) // 计算农历闰月天数
+      } else {
+        temp = this.monthDays(year, i)// 计算农历普通月天数
+      }
+      // 解除闰月
+      if (isLeap == true && i == (leap + 1)) { isLeap = false }
+      offset -= temp
+    }
+    // 闰月导致数组下标重叠取反
+    if (offset == 0 && leap > 0 && i == leap + 1) {
+      if (isLeap) {
+        isLeap = false
+      } else {
+        isLeap = true; --i
+      }
+    }
+    if (offset < 0) {
+      offset += temp; --i
+    }
+    // 农历月
+    var month = i
+    // 农历日
+    var day = offset + 1
+    // 天干地支处理
+    var sm = m - 1
+    var gzY = this.toGanZhiYear(year)
+
+    // 当月的两个节气
+    // bugfix-2017-7-24 11:03:38 use lunar Year Param `y` Not `year`
+    var firstNode = this.getTerm(y, (m * 2 - 1))// 返回当月「节」为几日开始
+    var secondNode = this.getTerm(y, (m * 2))// 返回当月「节」为几日开始
+
+    // 依据12节气修正干支月
+    var gzM = this.toGanZhi((y - 1900) * 12 + m + 11)
+    if (d >= firstNode) {
+      gzM = this.toGanZhi((y - 1900) * 12 + m + 12)
+    }
+
+    // 传入的日期的节气与否
+    var isTerm = false
+    var Term = null
+    if (firstNode == d) {
+      isTerm = true
+      Term = this.solarTerm[m * 2 - 2]
+    }
+    if (secondNode == d) {
+      isTerm = true
+      Term = this.solarTerm[m * 2 - 1]
+    }
+    // 日柱 当月一日与 1900/1/1 相差天数
+    var dayCyclical = Date.UTC(y, sm, 1, 0, 0, 0, 0) / 86400000 + 25567 + 10
+    var gzD = this.toGanZhi(dayCyclical + d - 1)
+    // 该日期所属的星座
+    var astro = this.toAstro(m, d)
+
+    return { 'lYear': year, 'lMonth': month, 'lDay': day, 'Animal': this.getAnimal(year), 'IMonthCn': (isLeap ? '\u95f0' : '') + this.toChinaMonth(month), 'IDayCn': this.toChinaDay(day), 'cYear': y, 'cMonth': m, 'cDay': d, 'gzYear': gzY, 'gzMonth': gzM, 'gzDay': gzD, 'isToday': isToday, 'isLeap': isLeap, 'nWeek': nWeek, 'ncWeek': '\u661f\u671f' + cWeek, 'isTerm': isTerm, 'Term': Term, 'astro': astro }
+  },
+
+  /**
+      * 传入农历年月日以及传入的月份是否闰月获得详细的公历、农历object信息 <=>JSON
+      * @param y  lunar year
+      * @param m  lunar month
+      * @param d  lunar day
+      * @param isLeapMonth  lunar month is leap or not.[如果是农历闰月第四个参数赋值true即可]
+      * @return JSON object
+      * @eg:console.log(calendar.lunar2solar(1987,9,10));
+      */
+  lunar2solar: function (y, m, d, isLeapMonth) { // 参数区间1900.1.31~2100.12.1
+    var isLeapMonth = !!isLeapMonth
+    var leapOffset = 0
+    var leapMonth = this.leapMonth(y)
+    var leapDay = this.leapDays(y)
+    if (isLeapMonth && (leapMonth != m)) { return -1 }// 传参要求计算该闰月公历 但该年得出的闰月与传参的月份并不同
+    if (y == 2100 && m == 12 && d > 1 || y == 1900 && m == 1 && d < 31) { return -1 }// 超出了最大极限值
+    var day = this.monthDays(y, m)
+    var _day = day
+    // bugFix 2016-9-25
+    // if month is leap, _day use leapDays method
+    if (isLeapMonth) {
+      _day = this.leapDays(y, m)
+    }
+    if (y < 1900 || y > 2100 || d > _day) { return -1 }// 参数合法性效验
+
+    // 计算农历的时间差
+    var offset = 0
+    for (var i = 1900; i < y; i++) {
+      offset += this.lYearDays(i)
+    }
+    var leap = 0; var isAdd = false
+    for (var i = 1; i < m; i++) {
+      leap = this.leapMonth(y)
+      if (!isAdd) { // 处理闰月
+        if (leap <= i && leap > 0) {
+          offset += this.leapDays(y); isAdd = true
+        }
+      }
+      offset += this.monthDays(y, i)
+    }
+    // 转换闰月农历 需补充该年闰月的前一个月的时差
+    if (isLeapMonth) { offset += day }
+    // 1900年农历正月一日的公历时间为1900年1月30日0时0分0秒(该时间也是本农历的最开始起始点)
+    var stmap = Date.UTC(1900, 1, 30, 0, 0, 0)
+    var calObj = new Date((offset + d - 31) * 86400000 + stmap)
+    var cY = calObj.getUTCFullYear()
+    var cM = calObj.getUTCMonth() + 1
+    var cD = calObj.getUTCDate()
+
+    return this.solar2lunar(cY, cM, cD)
+  }
+}
+
+export default calendar

+ 179 - 0
components/uni-calendar/uni-calendar-item.vue

@@ -0,0 +1,179 @@
+<template>
+	<view class="uni-calendar-item__weeks-box" :class="{
+		'uni-calendar-item--disable':weeks.disable,
+		'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay,
+		'uni-calendar-item--checked':(calendar.fullDate === weeks.fullDate && !weeks.isDay) ,
+		'uni-calendar-item--before-checked':weeks.beforeMultiple,
+		'uni-calendar-item--multiple': weeks.multiple,
+		'uni-calendar-item--after-checked':weeks.afterMultiple,
+		}"
+	 @click="choiceDate(weeks)">
+		<view class="uni-calendar-item__weeks-box-item">
+			<text v-if="selected&&weeks.extraInfo" class="uni-calendar-item__weeks-box-circle"></text>
+			<text class="uni-calendar-item__weeks-box-text" :class="{
+				'uni-calendar-item--isDay-text': weeks.isDay,
+				'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay,
+				'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay,
+				'uni-calendar-item--before-checked':weeks.beforeMultiple,
+				'uni-calendar-item--multiple': weeks.multiple,
+				'uni-calendar-item--after-checked':weeks.afterMultiple,
+				'uni-calendar-item--disable':weeks.disable,
+				}">{{weeks.date}}</text>
+			<text v-if="!lunar&&!weeks.extraInfo && weeks.isDay" class="uni-calendar-item__weeks-lunar-text" :class="{
+				'uni-calendar-item--isDay-text':weeks.isDay,
+				'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay,
+				'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay,
+				'uni-calendar-item--before-checked':weeks.beforeMultiple,
+				'uni-calendar-item--multiple': weeks.multiple,
+				'uni-calendar-item--after-checked':weeks.afterMultiple,
+				}">今天</text>
+			<text v-if="lunar&&!weeks.extraInfo" class="uni-calendar-item__weeks-lunar-text" :class="{
+				'uni-calendar-item--isDay-text':weeks.isDay,
+				'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay,
+				'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay,
+				'uni-calendar-item--before-checked':weeks.beforeMultiple,
+				'uni-calendar-item--multiple': weeks.multiple,
+				'uni-calendar-item--after-checked':weeks.afterMultiple,
+				'uni-calendar-item--disable':weeks.disable,
+				}">{{weeks.isDay?'今天': (weeks.lunar.IDayCn === '初一'?weeks.lunar.IMonthCn:weeks.lunar.IDayCn)}}</text>
+			<text v-if="weeks.extraInfo&&weeks.extraInfo.info" class="uni-calendar-item__weeks-lunar-text" :class="{
+				'uni-calendar-item--extra':weeks.extraInfo.info,
+				'uni-calendar-item--isDay-text':weeks.isDay,
+				'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay,
+				'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay,
+				'uni-calendar-item--before-checked':weeks.beforeMultiple,
+				'uni-calendar-item--multiple': weeks.multiple,
+				'uni-calendar-item--after-checked':weeks.afterMultiple,
+				'uni-calendar-item--disable':weeks.disable,
+				}">{{weeks.extraInfo.info}}</text>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		props: {
+			weeks: {
+				type: Object,
+				default () {
+					return {}
+				}
+			},
+			calendar: {
+				type: Object,
+				default: () => {
+					return {}
+				}
+			},
+			selected: {
+				type: Array,
+				default: () => {
+					return []
+				}
+			},
+			lunar: {
+				type: Boolean,
+				default: false
+			}
+		},
+		methods: {
+			choiceDate(weeks) {
+				this.$emit('change', weeks)
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.uni-calendar-item__weeks-box {
+		flex: 1;
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: column;
+		justify-content: center;
+		align-items: center;
+	}
+
+	.uni-calendar-item__weeks-box-text {
+		font-size: $uni-font-size-base;
+		color: $uni-text-color;
+	}
+
+	.uni-calendar-item__weeks-lunar-text {
+		font-size: $uni-font-size-sm;
+		color: $uni-text-color;
+	}
+
+	.uni-calendar-item__weeks-box-item {
+		position: relative;
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: column;
+		justify-content: center;
+		align-items: center;
+		width: 100rpx;
+		height: 100rpx;
+	}
+
+	/* .uni-calendar-item__weeks-box-circle {
+		position: absolute;
+		top: 5px;
+		right: 5px;
+		width: 8px;
+		height: 8px;
+		border-radius: 8px;
+		background-color: $uni-color-error;
+	} */
+	.uni-calendar-item__weeks-box-circle{
+	    position: absolute;
+	    top: 37px;
+	    right: 23px;
+	    width: 3px;
+	    height: 3px;
+	    border-radius: 3px;
+	    //background-color: $uni-color-primary;
+	    background-color: $uni-color-error;
+	}
+
+	.uni-calendar-item--disable {
+		background-color: rgba(249, 249, 249, $uni-opacity-disabled);
+		color: $uni-text-color-disable;
+	}
+
+	.uni-calendar-item--isDay-text {
+		color: $uni-color-primary;
+	}
+
+	.uni-calendar-item--isDay {
+		background-color: $uni-color-primary;
+		opacity: 0.8;
+		color: #fff;
+	}
+
+	.uni-calendar-item--extra {
+		color: $uni-color-error;
+		opacity: 0.8;
+	}
+
+	.uni-calendar-item--checked {
+		background-color: $uni-color-primary;
+		color: #fff;
+		opacity: 0.8;
+	}
+
+	.uni-calendar-item--multiple {
+		background-color: $uni-color-primary;
+		color: #fff;
+		opacity: 0.8;
+	}
+	.uni-calendar-item--before-checked {
+		background-color: #ff5a5f;
+		color: #fff;
+	}
+	.uni-calendar-item--after-checked {
+		background-color: #ff5a5f;
+		color: #fff;
+	}
+</style>

+ 512 - 0
components/uni-calendar/uni-calendar.vue

@@ -0,0 +1,512 @@
+<template>
+	<view class="uni-calendar">
+		<view v-if="!insert&&show" class="uni-calendar__mask" :class="{'uni-calendar--mask-show':aniMaskShow}" @click="clean"></view>
+		<view v-if="insert || show" class="uni-calendar__content" :class="{'uni-calendar--fixed':!insert,'uni-calendar--ani-show':aniMaskShow}">
+			<view v-if="!insert" class="uni-calendar__header uni-calendar--fixed-top">
+				<view class="uni-calendar__header-btn-box" @click="close">
+					<text class="uni-calendar__header-text uni-calendar--fixed-width">取消</text>
+				</view>
+				<view class="uni-calendar__header-btn-box" @click="confirm">
+					<text class="uni-calendar__header-text uni-calendar--fixed-width">确定</text>
+				</view>
+			</view>
+			<view class="uni-calendar__header">
+				<view class="uni-calendar__header-btn-box" @click.stop="pre">
+					<view class="uni-calendar__header-btn uni-calendar--left"></view>
+				</view>
+				<picker mode="date" :value="date" fields="month" @change="bindDateChange">
+					<text class="uni-calendar__header-text">{{ (nowDate.year||'') +'年'+( nowDate.month||'') +'月'}}</text>
+				</picker>
+				<view class="uni-calendar__header-btn-box" @click.stop="next">
+					<view class="uni-calendar__header-btn uni-calendar--right"></view>
+				</view>
+				<text class="uni-calendar__backtoday" @click="backtoday">回到今天</text>
+
+			</view>
+			<view class="uni-calendar__box">
+				<view v-if="showMonth" class="uni-calendar__box-bg">
+					<text class="uni-calendar__box-bg-text">{{nowDate.month}}</text>
+				</view>
+				<view class="uni-calendar__weeks">
+					<view class="uni-calendar__weeks-day">
+						<text class="uni-calendar__weeks-day-text">日</text>
+					</view>
+					<view class="uni-calendar__weeks-day">
+						<text class="uni-calendar__weeks-day-text">一</text>
+					</view>
+					<view class="uni-calendar__weeks-day">
+						<text class="uni-calendar__weeks-day-text">二</text>
+					</view>
+					<view class="uni-calendar__weeks-day">
+						<text class="uni-calendar__weeks-day-text">三</text>
+					</view>
+					<view class="uni-calendar__weeks-day">
+						<text class="uni-calendar__weeks-day-text">四</text>
+					</view>
+					<view class="uni-calendar__weeks-day">
+						<text class="uni-calendar__weeks-day-text">五</text>
+					</view>
+					<view class="uni-calendar__weeks-day">
+						<text class="uni-calendar__weeks-day-text">六</text>
+					</view>
+				</view>
+				<view class="uni-calendar__weeks" v-for="(item,weekIndex) in weeks" :key="weekIndex">
+					<view class="uni-calendar__weeks-item" v-for="(weeks,weeksIndex) in item" :key="weeksIndex">
+						<calendar-item :weeks="weeks" :calendar="calendar" :selected="selected" :lunar="lunar" @change="choiceDate"></calendar-item>
+					</view>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import Calendar from './util.js';
+	import calendarItem from './uni-calendar-item.vue'
+	/**
+	 * Calendar 日历
+	 * @description 日历组件可以查看日期,选择任意范围内的日期,打点操作。常用场景如:酒店日期预订、火车机票选择购买日期、上下班打卡等
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=56
+	 * @property {String} date 自定义当前时间,默认为今天
+	 * @property {Boolean} lunar 显示农历
+	 * @property {String} startDate 日期选择范围-开始日期
+	 * @property {String} endDate 日期选择范围-结束日期
+	 * @property {Boolean} range 范围选择
+	 * @property {Boolean} insert = [true|false] 插入模式,默认为false
+	 * 	@value true 弹窗模式
+	 * 	@value false 插入模式
+	 * @property {Boolean} clearDate = [true|false] 弹窗模式是否清空上次选择内容
+	 * @property {Array} selected 打点,期待格式[{date: '2019-06-27', info: '签到', data: { custom: '自定义信息', name: '自定义消息头',xxx:xxx... }}]
+	 * @property {Boolean} showMonth 是否选择月份为背景
+	 * @event {Function} change 日期改变,`insert :ture` 时生效
+	 * @event {Function} confirm 确认选择`insert :false` 时生效
+	 * @event {Function} monthSwitch 切换月份时触发
+	 * @example <uni-calendar :insert="true":lunar="true" :start-date="'2019-3-2'":end-date="'2019-5-20'"@change="change" />
+	 */
+	export default {
+		components: {
+			calendarItem
+		},
+		props: {
+			date: {
+				type: String,
+				default: ''
+			},
+			selected: {
+				type: Array,
+				default () {
+					return []
+				}
+			},
+			lunar: {
+				type: Boolean,
+				default: false
+			},
+			startDate: {
+				type: String,
+				default: ''
+			},
+			endDate: {
+				type: String,
+				default: ''
+			},
+			range: {
+				type: Boolean,
+				default: false
+			},
+			insert: {
+				type: Boolean,
+				default: true
+			},
+			showMonth: {
+				type: Boolean,
+				default: true
+			},
+			clearDate: {
+				type: Boolean,
+				default: true
+			}
+		},
+		data() {
+			return {
+				show: false,
+				weeks: [],
+				calendar: {},
+				nowDate: '',
+				aniMaskShow: false
+			}
+		},
+		watch: {
+			date(newVal) {
+				this.cale.setDate(newVal)
+				this.init(this.cale.selectDate.fullDate)
+			},
+			startDate(val){
+				this.cale.resetSatrtDate(val)
+			},
+			endDate(val){
+				this.cale.resetEndDate(val)
+			},
+			selected(newVal) {
+				this.cale.setSelectInfo(this.nowDate.fullDate, newVal)
+				this.weeks = this.cale.weeks
+			}
+		},
+		created() {
+			// 获取日历方法实例
+			this.cale = new Calendar({
+				// date: new Date(),
+				selected: this.selected,
+				startDate: this.startDate,
+				endDate: this.endDate,
+				range: this.range,
+			})
+			// 选中某一天
+			this.cale.setDate(this.date)
+			this.init(this.cale.selectDate.fullDate)
+			// this.setDay
+		},
+		methods: {
+			// 取消穿透
+			clean() {},
+			bindDateChange(e) {
+				const value = e.detail.value + '-1'
+				console.log(this.cale.getDate(value));
+				this.cale.setDate(value)
+				this.init(value)
+			},
+			/**
+			 * 初始化日期显示
+			 * @param {Object} date
+			 */
+			init(date) {
+				this.weeks = this.cale.weeks
+				this.nowDate = this.calendar = this.cale.getInfo(date)
+			    console.log("this.weeks",this.weeks)
+			},
+			/**
+			 * 打开日历弹窗
+			 */
+			open() {
+				// 弹窗模式并且清理数据
+				if (this.clearDate && !this.insert) {
+					this.cale.cleanMultipleStatus()
+					this.cale.setDate(this.date)
+					this.init(this.cale.selectDate.fullDate)
+				}
+				this.show = true
+				this.$nextTick(() => {
+					setTimeout(() => {
+						this.aniMaskShow = true
+					}, 50)
+				})
+			},
+			/**
+			 * 关闭日历弹窗
+			 */
+			close() {
+				this.aniMaskShow = false
+				this.$nextTick(() => {
+					setTimeout(() => {
+						this.show = false
+						this.$emit('close')
+					}, 300)
+				})
+			},
+			/**
+			 * 确认按钮
+			 */
+			confirm() {
+				this.setEmit('confirm')
+				this.close()
+			},
+			/**
+			 * 变化触发
+			 */
+			change() {
+				if (!this.insert) return
+				this.setEmit('change')
+			},
+			/**
+			 * 选择月份触发
+			 */
+			monthSwitch() {
+				let {
+					year,
+					month
+				} = this.nowDate
+				this.$emit('monthSwitch', {
+					year,
+					month: Number(month)
+				})
+			},
+			/**
+			 * 派发事件
+			 * @param {Object} name
+			 */
+			setEmit(name) {
+				let {
+					year,
+					month,
+					date,
+					fullDate,
+					lunar,
+					extraInfo
+				} = this.calendar
+				this.$emit(name, {
+					range: this.cale.multipleStatus,
+					year,
+					month,
+					date,
+					fulldate: fullDate,
+					lunar,
+					extraInfo: extraInfo || {}
+				})
+			},
+			/**
+			 * 选择天触发
+			 * @param {Object} weeks
+			 */
+			choiceDate(weeks) {
+				console.log(weeks)
+				if (weeks.disable){
+					//this.next()
+					return
+				} 
+				this.calendar = weeks
+				// 设置多选
+				this.cale.setMultiple(this.calendar.fullDate)
+				this.weeks = this.cale.weeks
+				this.change()
+			},
+			/**
+			 * 回到今天
+			 */
+			backtoday() {
+				console.log(this.cale.getDate(new Date()).fullDate);
+				let date = this.cale.getDate(new Date()).fullDate
+				this.cale.setDate(date)
+				this.init(date)
+				this.change()
+			},
+			/**
+			 * 上个月
+			 */
+			pre() {
+				const preDate = this.cale.getDate(this.nowDate.fullDate, -1, 'month').fullDate
+				this.setDate(preDate)
+				this.monthSwitch()
+				this.$emit("monthChange",preDate)
+            },
+			/**
+			 * 下个月
+			 */
+			next() {
+				const nextDate = this.cale.getDate(this.nowDate.fullDate, +1, 'month').fullDate
+				this.setDate(nextDate)
+				this.monthSwitch()
+				this.$emit("monthChange",nextDate)
+			},
+			/**
+			 * 设置日期
+			 * @param {Object} date
+			 */
+			setDate(date) {
+				this.cale.setDate(date)
+				this.weeks = this.cale.weeks
+				this.nowDate = this.cale.getInfo(date)
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.uni-calendar {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: column;
+	}
+
+	.uni-calendar__mask {
+		position: fixed;
+		bottom: 0;
+		top: 0;
+		left: 0;
+		right: 0;
+		background-color: $uni-bg-color-mask;
+		transition-property: opacity;
+		transition-duration: 0.3s;
+		opacity: 0;
+		/* #ifndef APP-NVUE */
+		z-index: 99;
+		/* #endif */
+	}
+
+	.uni-calendar--mask-show {
+		opacity: 1
+	}
+
+	.uni-calendar--fixed {
+		position: fixed;
+		bottom: 0;
+		left: 0;
+		right: 0;
+		transition-property: transform;
+		transition-duration: 0.3s;
+		transform: translateY(460px);
+		/* #ifndef APP-NVUE */
+		z-index: 99;
+		/* #endif */
+	}
+
+	.uni-calendar--ani-show {
+		transform: translateY(0);
+	}
+
+	.uni-calendar__content {
+		background-color: #fff;
+	}
+
+	.uni-calendar__header {
+		position: relative;
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		justify-content: center;
+		align-items: center;
+		height: 50px;
+		border-bottom-color: $uni-border-color-gray;
+		border-bottom-style: solid;
+		border-bottom-width: 1px;
+	}
+
+	.uni-calendar--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: 1px;
+	}
+
+	.uni-calendar--fixed-width {
+		width: 50px;
+		// padding: 0 15px;
+	}
+
+	.uni-calendar__backtoday {
+		position: absolute;
+		right: 0;
+		top: 25rpx;
+		padding: 0 5px;
+		padding-left: 10px;
+		height: 25px;
+		line-height: 25px;
+		font-size: 12px;
+		border-top-left-radius: 25px;
+		border-bottom-left-radius: 25px;
+		color: $uni-text-color;
+		background-color: $uni-bg-color-hover;
+	}
+
+	.uni-calendar__header-text {
+		text-align: center;
+		width: 100px;
+		font-size: $uni-font-size-xl;
+		color: $uni-text-color;
+	}
+
+	.uni-calendar__header-btn-box {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		align-items: center;
+		justify-content: center;
+		width: 50px;
+		height: 50px;
+	}
+
+	.uni-calendar__header-btn {
+		width: 10px;
+		height: 10px;
+		border-left-color: $uni-text-color-placeholder;
+		border-left-style: solid;
+		border-left-width: 2px;
+		border-top-color: $uni-color-subtitle;
+		border-top-style: solid;
+		border-top-width: 2px;
+	}
+
+	.uni-calendar--left {
+		transform: rotate(-45deg);
+	}
+
+	.uni-calendar--right {
+		transform: rotate(135deg);
+	}
+
+
+	.uni-calendar__weeks {
+		position: relative;
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+	}
+
+	.uni-calendar__weeks-item {
+		flex: 1;
+	}
+
+	.uni-calendar__weeks-day {
+		flex: 1;
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: column;
+		justify-content: center;
+		align-items: center;
+		height: 45px;
+		border-bottom-color: #F5F5F5;
+		border-bottom-style: solid;
+		border-bottom-width: 1px;
+	}
+
+	.uni-calendar__weeks-day-text {
+		font-size: 14px;
+	}
+
+	.uni-calendar__box {
+		position: relative;
+	}
+
+	.uni-calendar__box-bg {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		justify-content: center;
+		align-items: center;
+		position: absolute;
+		top: 0;
+		left: 0;
+		right: 0;
+		bottom: 0;
+	}
+
+	.uni-calendar__box-bg-text {
+		font-size: 200px;
+		font-weight: bold;
+		color: $uni-text-color-grey;
+		opacity: 0.1;
+		text-align: center;
+		/* #ifndef APP-NVUE */
+		line-height: 1;
+		/* #endif */
+	}
+	
+</style>

+ 352 - 0
components/uni-calendar/util.js

@@ -0,0 +1,352 @@
+import CALENDAR from './calendar.js'
+
+class Calendar {
+	constructor({
+		date,
+		selected,
+		startDate,
+		endDate,
+		range
+	} = {}) {
+		// 当前日期
+		this.date = this.getDate(new Date()) // 当前初入日期
+		// 打点信息
+		this.selected = selected || [];
+		// 范围开始
+		this.startDate = startDate
+		// 范围结束
+		this.endDate = endDate
+		this.range = range
+		// 多选状态
+		this.cleanMultipleStatus()
+		// 每周日期
+		this.weeks = {}
+		// this._getWeek(this.date.fullDate)
+	}
+	/**
+	 * 设置日期
+	 * @param {Object} date
+	 */
+	setDate(date) {
+		this.selectDate = this.getDate(date)
+		this._getWeek(this.selectDate.fullDate)
+	}
+
+	/**
+	 * 清理多选状态
+	 */
+	cleanMultipleStatus() {
+		this.multipleStatus = {
+			before: '',
+			after: '',
+			data: []
+		}
+	}
+
+	/**
+	 * 重置开始日期
+	 */
+	resetSatrtDate(startDate) {
+		// 范围开始
+		this.startDate = startDate
+
+	}
+
+	/**
+	 * 重置结束日期
+	 */
+	resetEndDate(endDate) {
+		// 范围结束
+		this.endDate = endDate
+	}
+
+	/**
+	 * 获取任意时间
+	 */
+	getDate(date, AddDayCount = 0, str = 'day') {
+		if (!date) {
+			date = new Date()
+		}
+		if (typeof date !== 'object') {
+			date = date.replace(/-/g, '/')
+		}
+		const dd = new Date(date)
+		switch (str) {
+			case 'day':
+				dd.setDate(dd.getDate() + AddDayCount) // 获取AddDayCount天后的日期
+				break
+			case 'month':
+				if (dd.getDate() === 31) {
+					dd.setDate(dd.getDate() + AddDayCount)
+				} else {
+					dd.setMonth(dd.getMonth() + AddDayCount) // 获取AddDayCount天后的日期
+				}
+				break
+			case 'year':
+				dd.setFullYear(dd.getFullYear() + AddDayCount) // 获取AddDayCount天后的日期
+				break
+		}
+		const y = dd.getFullYear()
+		const m = dd.getMonth() + 1 < 10 ? '0' + (dd.getMonth() + 1) : dd.getMonth() + 1 // 获取当前月份的日期,不足10补0
+		const d = dd.getDate() < 10 ? '0' + dd.getDate() : dd.getDate() // 获取当前几号,不足10补0
+		return {
+			fullDate: y + '-' + m + '-' + d,
+			year: y,
+			month: m,
+			date: d,
+			day: dd.getDay()
+		}
+	}
+
+
+	/**
+	 * 获取上月剩余天数
+	 */
+	_getLastMonthDays(firstDay, full) {
+		let dateArr = []
+		for (let i = firstDay; i > 0; i--) {
+			const beforeDate = new Date(full.year, full.month - 1, -i + 1).getDate()
+			dateArr.push({
+				date: beforeDate,
+				month: full.month - 1,
+				lunar: this.getlunar(full.year, full.month - 1, beforeDate),
+				disable: true
+			})
+		}
+		return dateArr
+	}
+	/**
+	 * 获取本月天数
+	 */
+	_currentMonthDys(dateData, full) {
+		let dateArr = []
+		let fullDate = this.date.fullDate
+		for (let i = 1; i <= dateData; i++) {
+			let isinfo = false
+			let nowDate = full.year + '-' + (full.month < 10 ?
+				full.month : full.month) + '-' + (i < 10 ?
+				'0' + i : i)
+			// 是否今天
+			let isDay = fullDate === nowDate
+			// 获取打点信息
+			let info = this.selected && this.selected.find((item) => {
+				if (this.dateEqual(nowDate, item.date)) {
+					return item
+				}
+			})
+
+			// 日期禁用
+			let disableBefore = true
+			let disableAfter = true
+			if (this.startDate) {
+				let dateCompBefore = this.dateCompare(this.startDate, fullDate)
+				disableBefore = this.dateCompare(dateCompBefore ? this.startDate : fullDate, nowDate)
+			}
+
+			if (this.endDate) {
+				let dateCompAfter = this.dateCompare(fullDate, this.endDate)
+				disableAfter = this.dateCompare(nowDate, dateCompAfter ? this.endDate : fullDate)
+			}
+			let multiples = this.multipleStatus.data
+			let checked = false
+			let multiplesStatus = -1
+			if (this.range) {
+				if (multiples) {
+					multiplesStatus = multiples.findIndex((item) => {
+						return this.dateEqual(item, nowDate)
+					})
+				}
+				if (multiplesStatus !== -1) {
+					checked = true
+				}
+			}
+			let data = {
+				fullDate: nowDate,
+				year: full.year,
+				date: i,
+				multiple: this.range ? checked : false,
+				beforeMultiple: this.dateEqual(this.multipleStatus.before, nowDate),
+				afterMultiple: this.dateEqual(this.multipleStatus.after, nowDate),
+				month: full.month,
+				lunar: this.getlunar(full.year, full.month, i),
+				disable: !disableBefore || !disableAfter,
+				isDay
+			}
+			if (info) {
+				data.extraInfo = info
+			}
+
+			dateArr.push(data)
+		}
+		return dateArr
+	}
+	/**
+	 * 获取下月天数
+	 */
+	_getNextMonthDays(surplus, full) {
+		let dateArr = []
+		for (let i = 1; i < surplus + 1; i++) {
+			dateArr.push({
+				date: i,
+				month: Number(full.month) + 1,
+				lunar: this.getlunar(full.year, Number(full.month) + 1, i),
+				disable: true
+			})
+		}
+		return dateArr
+	}
+
+	/**
+	 * 获取当前日期详情
+	 * @param {Object} date
+	 */
+	getInfo(date) {
+		if (!date) {
+			date = new Date()
+		}
+		const dateInfo = this.canlender.find(item => item.fullDate === this.getDate(date).fullDate)
+		return dateInfo
+	}
+
+	/**
+	 * 比较时间大小
+	 */
+	dateCompare(startDate, endDate) {
+		// 计算截止时间
+		startDate = new Date(startDate.replace('-', '/').replace('-', '/'))
+		// 计算详细项的截止时间
+		endDate = new Date(endDate.replace('-', '/').replace('-', '/'))
+		if (startDate <= endDate) {
+			return true
+		} else {
+			return false
+		}
+	}
+
+	/**
+	 * 比较时间是否相等
+	 */
+	dateEqual(before, after) {
+		// 计算截止时间
+		before = new Date(before.replace('-', '/').replace('-', '/'))
+		// 计算详细项的截止时间
+		after = new Date(after.replace('-', '/').replace('-', '/'))
+		if (before.getTime() - after.getTime() === 0) {
+			return true
+		} else {
+			return false
+		}
+	}
+
+
+	/**
+	 * 获取日期范围内所有日期
+	 * @param {Object} begin
+	 * @param {Object} end
+	 */
+	geDateAll(begin, end) {
+		var arr = []
+		var ab = begin.split('-')
+		var ae = end.split('-')
+		var db = new Date()
+		db.setFullYear(ab[0], ab[1] - 1, ab[2])
+		var de = new Date()
+		de.setFullYear(ae[0], ae[1] - 1, ae[2])
+		var unixDb = db.getTime() - 24 * 60 * 60 * 1000
+		var unixDe = de.getTime() - 24 * 60 * 60 * 1000
+		for (var k = unixDb; k <= unixDe;) {
+			k = k + 24 * 60 * 60 * 1000
+			arr.push(this.getDate(new Date(parseInt(k))).fullDate)
+		}
+		return arr
+	}
+	/**
+	 * 计算阴历日期显示
+	 */
+	getlunar(year, month, date) {
+		return CALENDAR.solar2lunar(year, month, date)
+	}
+	/**
+	 * 设置打点
+	 */
+	setSelectInfo(data, value) {
+		this.selected = value
+		this._getWeek(data)
+	}
+
+	/**
+	 *  获取多选状态
+	 */
+	setMultiple(fullDate) {
+		let {
+			before,
+			after
+		} = this.multipleStatus
+
+		if (!this.range) return
+		if (before && after) {
+			this.multipleStatus.before = ''
+			this.multipleStatus.after = ''
+			this.multipleStatus.data = []
+		} else {
+			if (!before) {
+				this.multipleStatus.before = fullDate
+			} else {
+				this.multipleStatus.after = fullDate
+				if (this.dateCompare(this.multipleStatus.before, this.multipleStatus.after)) {
+					this.multipleStatus.data = this.geDateAll(this.multipleStatus.before, this.multipleStatus.after);
+				} else {
+					this.multipleStatus.data = this.geDateAll(this.multipleStatus.after, this.multipleStatus.before);
+				}
+			}
+		}
+		this._getWeek(fullDate)
+	}
+
+	/**
+	 * 获取每周数据
+	 * @param {Object} dateData
+	 */
+	_getWeek(dateData) {
+		const {
+			fullDate,
+			year,
+			month,
+			date,
+			day
+		} = this.getDate(dateData)
+		let firstDay = new Date(year, month - 1, 1).getDay()
+		let currentDay = new Date(year, month, 0).getDate()
+		let dates = {
+			lastMonthDays: this._getLastMonthDays(firstDay, this.getDate(dateData)), // 上个月末尾几天
+			currentMonthDys: this._currentMonthDys(currentDay, this.getDate(dateData)), // 本月天数
+			nextMonthDays: [], // 下个月开始几天
+			weeks: []
+		}
+		let canlender = []
+		const surplus = 42 - (dates.lastMonthDays.length + dates.currentMonthDys.length)
+		dates.nextMonthDays = this._getNextMonthDays(surplus, this.getDate(dateData))
+		canlender = canlender.concat(dates.lastMonthDays, dates.currentMonthDys, dates.nextMonthDays)
+		let weeks = {}
+		// 拼接数组  上个月开始几天 + 本月天数+ 下个月开始几天
+		for (let i = 0; i < canlender.length; i++) {
+			if (i % 7 === 0) {
+				weeks[parseInt(i / 7)] = new Array(7)
+			}
+			weeks[parseInt(i / 7)][i % 7] = canlender[i]
+		}
+		this.canlender = canlender
+		this.weeks = weeks
+	}
+
+	//静态方法
+	// static init(date) {
+	// 	if (!this.instance) {
+	// 		this.instance = new Calendar(date);
+	// 	}
+	// 	return this.instance;
+	// }
+}
+
+
+export default Calendar

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

@@ -0,0 +1,54 @@
+<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" scoped>
+	@import '@/uni.scss';
+
+	.uni-collapse {
+		/* #ifndef APP-NVUE */
+		width: 100%;
+		display: flex;
+		/* #endif */
+		/* #ifdef APP-NVUE */
+		flex: 1;
+		/* #endif */
+		flex-direction: column;
+		background-color: $uni-bg-color;
+	}
+</style>

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

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

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

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

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

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

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

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

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

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

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

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

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

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
components/w-picker/areadata/areadata.js


+ 742 - 0
components/w-picker/date-picker.vue

@@ -0,0 +1,742 @@
+<template>
+	<view class="w-picker-view">
+		<picker-view v-if="fields=='year'" class="d-picker-view" :indicator-style="itemHeight" :value="pickVal" @change="handlerChange">
+			<picker-view-column>
+				<view class="w-picker-item" v-for="(item,index) in range.years" :key="index">{{item}}年</view>
+			</picker-view-column>
+		</picker-view>
+		<picker-view v-if="fields=='month'" class="d-picker-view" :indicator-style="itemHeight" :value="pickVal" @change="handlerChange">
+			<picker-view-column>
+				<view class="w-picker-item" v-for="(item,index) in range.years" :key="index">{{item}}年</view>
+			</picker-view-column>
+			<picker-view-column>
+				<view class="w-picker-item" v-for="(item,index) in range.months" :key="index">{{item}}月</view>
+			</picker-view-column>
+		</picker-view>
+		<picker-view v-if="fields=='day'" class="d-picker-view" :indicator-style="itemHeight" :value="pickVal" @change="handlerChange">
+			<picker-view-column>
+				<view class="w-picker-item" v-for="(item,index) in range.years" :key="index">{{item}}年</view>
+			</picker-view-column>
+			<picker-view-column>
+				<view class="w-picker-item" v-for="(item,index) in range.months" :key="index">{{item}}月</view>
+			</picker-view-column>
+			<picker-view-column>
+				<view class="w-picker-item" v-for="(item,index) in range.days" :key="index">{{item}}日</view>
+			</picker-view-column>
+		</picker-view>
+		<picker-view v-if="fields=='hour'" class="d-picker-view" :indicator-style="itemHeight" :value="pickVal" @change="handlerChange">
+			<picker-view-column>
+				<view class="w-picker-item" v-for="(item,index) in range.years" :key="index">{{item}}年</view>
+			</picker-view-column>
+			<picker-view-column>
+				<view class="w-picker-item" v-for="(item,index) in range.months" :key="index">{{item}}月</view>
+			</picker-view-column>
+			<picker-view-column>
+				<view class="w-picker-item" v-for="(item,index) in range.days" :key="index">{{item}}日</view>
+			</picker-view-column>
+			<picker-view-column>
+				<view class="w-picker-item" v-for="(item,index) in range.hours" :key="index">{{item}}时</view>
+			</picker-view-column>
+		</picker-view>
+		<picker-view v-if="fields=='minute'" class="d-picker-view" :indicator-style="itemHeight" :value="pickVal" @change="handlerChange">
+			<picker-view-column>
+				<view class="w-picker-item" v-for="(item,index) in range.years" :key="index">{{item}}年</view>
+			</picker-view-column>
+			<picker-view-column>
+				<view class="w-picker-item" v-for="(item,index) in range.months" :key="index">{{item}}月</view>
+			</picker-view-column>
+			<picker-view-column>
+				<view class="w-picker-item" v-for="(item,index) in range.days" :key="index">{{item}}日</view>
+			</picker-view-column>
+			<picker-view-column>
+				<view class="w-picker-item" v-for="(item,index) in range.hours" :key="index">{{item}}时</view>
+			</picker-view-column>
+			<picker-view-column>
+				<view class="w-picker-item" v-for="(item,index) in range.minutes" :key="index">{{item}}分</view>
+			</picker-view-column>
+		</picker-view>
+		<picker-view v-if="fields=='second'" class="d-picker-view" :indicator-style="itemHeight" :value="pickVal" @change="handlerChange">
+			<picker-view-column>
+				<view class="w-picker-item" v-for="(item,index) in range.years" :key="index">{{item}}年</view>
+			</picker-view-column>
+			<picker-view-column>
+				<view class="w-picker-item" v-for="(item,index) in range.months" :key="index">{{item}}月</view>
+			</picker-view-column>
+			<picker-view-column>
+				<view class="w-picker-item" v-for="(item,index) in range.days" :key="index">{{item}}日</view>
+			</picker-view-column>
+			<picker-view-column>
+				<view class="w-picker-item" v-for="(item,index) in range.hours" :key="index">{{item}}时</view>
+			</picker-view-column>
+			<picker-view-column>
+				<view class="w-picker-item" v-for="(item,index) in range.minutes" :key="index">{{item}}分</view>
+			</picker-view-column>
+			<picker-view-column>
+				<view class="w-picker-item" v-for="(item,index) in range.seconds" :key="index">{{item}}秒</view>
+			</picker-view-column>
+		</picker-view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				pickVal:[],
+				range:{
+					years:[],
+					months:[],
+					days:[],
+					hours:[],
+					minutes:[],
+					seconds:[]
+				},
+				checkObj:{}
+			};
+		},
+		props:{
+			itemHeight:{
+				type:String,
+				default:"44px"
+			},
+			startYear:{
+				type:[String,Number],
+				default:""
+			},
+			endYear:{
+				type:[String,Number],
+				default:""
+			},
+			value:{
+				type:[String,Array,Number],
+				default:""
+			},
+			current:{//是否默认选中当前日期
+				type:Boolean,
+				default:false
+			},
+			disabledAfter:{//是否禁用当前之后的日期
+				type:Boolean,
+				default:false
+			},
+			fields:{
+				type:String,
+				default:"day"
+			}
+		},
+		watch:{
+			fields(val){
+				this.initData();
+			},
+			value(val){
+				this.initData();
+			}
+		},
+		created() {
+			this.initData();
+		},
+		methods:{
+			formatNum(n){
+				return (Number(n)<10?'0'+Number(n):Number(n)+'');
+			},
+			checkValue(value){
+				let strReg,example
+				switch(this.fields){
+					case "year":
+						strReg=/^\d{4}$/;
+						example="2019";
+						break;
+					case "month":
+						strReg=/^\d{4}-\d{2}$/;
+						example="2019-02";
+						break;
+					case "day":
+						strReg=/^\d{4}-\d{2}-\d{2}$/;
+						example="2019-02-01";
+						break;
+					case "hour":
+						strReg=/^\d{4}-\d{2}-\d{2} \d{2}(:\d{2}){1,2}?$/;
+						example="2019-02-01 18:00:00或2019-02-01 18";
+						break;
+					case "minute":
+						strReg=/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}(:\d{2}){0,1}?$/;
+						example="2019-02-01 18:06:00或2019-02-01 18:06";
+						break;
+					case "second":
+						strReg=/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/;
+						example="2019-02-01 18:06:01";
+						break;
+				}
+				if(!strReg.test(value)){
+					console.log(new Error("请传入与mode、fields匹配的value值,例value="+example+""))
+				}
+				return strReg.test(value);
+			},
+			resetData(year,month,day,hour,minute){
+				let curDate=this.getCurrenDate();
+				let curFlag=this.current;
+				let curYear=curDate.curYear;
+				let curMonth=curDate.curMonth;
+				let curDay=curDate.curDay;
+				let curHour=curDate.curHour;
+				let curMinute=curDate.curMinute;
+				let curSecond=curDate.curSecond;
+				let months=[],days=[],hours=[],minutes=[],seconds=[];
+				let disabledAfter=this.disabledAfter;
+				let monthsLen=disabledAfter?(year*1<curYear?12:curMonth):12;
+				let totalDays=new Date(year,month,0).getDate();//计算当月有几天;
+				let daysLen=disabledAfter?((year*1<curYear||month*1<curMonth)?totalDays:curDay):totalDays;
+				let hoursLen=disabledAfter?((year*1<curYear||month*1<curMonth||day*1<curDay)?24:curHour+1):24;
+				let minutesLen=disabledAfter?((year*1<curYear||month*1<curMonth||day*1<curDay||hour*1<curHour)?60:curMinute+1):60;
+				let secondsLen=disabledAfter?((year*1<curYear||month*1<curMonth||day*1<curDay||hour*1<curHour||minute*1<curMinute)?60:curSecond+1):60;
+				for(let month=1;month<=monthsLen;month++){
+					months.push(this.formatNum(month));
+				};
+				for(let day=1;day<=daysLen;day++){
+					days.push(this.formatNum(day));
+				}
+				for(let hour=0;hour<hoursLen;hour++){
+					hours.push(this.formatNum(hour));
+				}
+				for(let minute=0;minute<minutesLen;minute++){
+					minutes.push(this.formatNum(minute));
+				}
+				for(let second=0;second<secondsLen;second++){
+					seconds.push(this.formatNum(second));
+				}
+				return{
+					months,
+					days,
+					hours,
+					minutes,
+					seconds
+				}
+			},
+			isLeapYear (Year) {
+				if (((Year % 4)==0) && ((Year % 100)!=0) || ((Year % 400)==0)) {
+					return true;
+				} else { 
+					return false; 
+				}
+			},
+			getData(dVal){
+				//用来处理初始化数据
+				let curFlag=this.current;
+				let disabledAfter=this.disabledAfter;
+				let fields=this.fields;
+				let curDate=this.getCurrenDate();
+				let curYear=curDate.curYear;
+				let curMonthdays=curDate.curMonthdays;
+				let curMonth=curDate.curMonth;
+				let curDay=curDate.curDay;
+				let curHour=curDate.curHour;
+				let curMinute=curDate.curMinute;
+				let curSecond=curDate.curSecond;
+				let defaultDate=this.getDefaultDate();
+				let startYear=this.getStartDate().getFullYear();
+				let endYear=this.getEndDate().getFullYear();
+				//颗粒度,禁用当前之后日期仅对year,month,day,hour生效;分钟秒禁用没有意义,
+				let years=[],months=[],days=[],hours=[],minutes=[],seconds=[];
+				let year=dVal[0]*1;
+				let month=dVal[1]*1;
+				let day=dVal[2]*1;
+				let hour=dVal[3]*1;
+				let minute=dVal[4]*1;
+				let monthsLen=disabledAfter?(year<curYear?12:curDate.curMonth):12;
+				let daysLen=disabledAfter?((year<curYear||month<curMonth)?defaultDate.defaultDays:curDay):(curFlag?curMonthdays:defaultDate.defaultDays);
+				let hoursLen=disabledAfter?((year<curYear||month<curMonth||day<curDay)?24:curHour+1):24;
+				let minutesLen=disabledAfter?((year<curYear||month<curMonth||day<curDay||hour<curHour)?60:curMinute+1):60;
+				let secondsLen=disabledAfter?((year<curYear||month<curMonth||day<curDay||hour<curHour||minute<curMinute)?60:curSecond+1):60;
+				for(let year=startYear;year<=(disabledAfter?curYear:endYear);year++){
+					years.push(year.toString())
+				}
+				for(let month=1;month<=monthsLen;month++){
+					months.push(this.formatNum(month));
+				}
+				for(let day=1;day<=daysLen;day++){
+					days.push(this.formatNum(day));
+				}
+				for(let hour=0;hour<hoursLen;hour++){
+					hours.push(this.formatNum(hour));
+				}
+				for(let minute=0;minute<minutesLen;minute++){
+					minutes.push(this.formatNum(minute));
+				}
+				// for(let second=0;second<(disabledAfter?curDate.curSecond+1:60);second++){
+				// 	seconds.push(this.formatNum(second));
+				// }
+				for(let second=0;second<60;second++){
+					seconds.push(this.formatNum(second));
+				}
+				return {
+					years,
+					months,
+					days,
+					hours,
+					minutes,
+					seconds
+				}
+			},
+			getCurrenDate(){
+				let curDate=new Date();
+				let curYear=curDate.getFullYear();
+				let curMonth=curDate.getMonth()+1;
+				let curMonthdays=new Date(curYear,curMonth,0).getDate();
+				let curDay=curDate.getDate();
+				let curHour=curDate.getHours();
+				let curMinute=curDate.getMinutes();
+				let curSecond=curDate.getSeconds();
+				return{
+					curDate,
+					curYear,
+					curMonth,
+					curMonthdays,
+					curDay,
+					curHour,
+					curMinute,
+					curSecond
+				}
+			},
+			getDefaultDate(){
+				let value=this.value;
+				let reg=/-/g;
+				let defaultDate=value?new Date(value.replace(reg,"/")):new Date();
+				let defaultYear=defaultDate.getFullYear();
+				let defaultMonth=defaultDate.getMonth()+1;
+				let defaultDay=defaultDate.getDate();
+				let defaultDays=new Date(defaultYear,defaultMonth,0).getDate()*1;
+				return{
+					defaultDate,
+					defaultYear,
+					defaultMonth,
+					defaultDay,
+					defaultDays
+				}
+			},
+			getStartDate(){
+				let start=this.startYear;
+				let startDate="";
+				let reg=/-/g;
+				if(start){
+					startDate=new Date(start+"/01/01");
+				}else{
+					startDate=new Date("1970/01/01");
+				}
+				return startDate;
+			},
+			getEndDate(){
+				let end=this.endYear;
+				let reg=/-/g;
+				let endDate="";
+				if(end){
+					endDate=new Date(end+"/12/01");
+				}else{
+					endDate=new Date();
+				}
+				return endDate;
+			},
+			getDval(){
+				let value=this.value;
+				let fields=this.fields;
+				let dVal=null;
+				let aDate=new Date();
+				let year=this.formatNum(aDate.getFullYear());
+				let month=this.formatNum(aDate.getMonth()+1);
+				let day=this.formatNum(aDate.getDate());
+				let hour=this.formatNum(aDate.getHours());
+				let minute=this.formatNum(aDate.getMinutes());
+				let second=this.formatNum(aDate.getSeconds());
+				if(value){
+					let flag=this.checkValue(value);
+					if(!flag){
+						dVal=[year,month,day,hour,minute,second]
+					}else{
+						switch(this.fields){
+							case "year":
+								dVal=value?[value]:[];	
+								break;
+							case "month":
+								dVal=value?value.split("-"):[];
+								break;
+							case "day":
+								dVal=value?value.split("-"):[];
+								break;
+							case "hour":
+								dVal=[...value.split(" ")[0].split("-"),...value.split(" ")[1].split(":")];
+								break;
+							case "minute":
+								dVal=value?[...value.split(" ")[0].split("-"),...value.split(" ")[1].split(":")]:[];
+								break;
+							case "second":
+								dVal=[...value.split(" ")[0].split("-"),...value.split(" ")[1].split(":")];
+								break;
+						}
+					}
+				}else{
+					dVal=[year,month,day,hour,minute,second]
+				}
+				return dVal;
+			},
+			initData(){
+				let startDate,endDate,startYear,endYear,startMonth,endMonth,startDay,endDay;
+				let years=[],months=[],days=[],hours=[],minutes=[],seconds=[];
+				let dVal=[],pickVal=[];
+				let value=this.value;
+				let reg=/-/g;
+				let range={};
+				let result="",full="",year,month,day,hour,minute,second,obj={};
+				let defaultDate=this.getDefaultDate();
+				let defaultYear=defaultDate.defaultYear;
+				let defaultMonth=defaultDate.defaultMonth;
+				let defaultDay=defaultDate.defaultDay;
+				let defaultDays=defaultDate.defaultDays;
+				let curFlag=this.current;
+				let disabledAfter=this.disabledAfter;
+				let curDate=this.getCurrenDate();
+				let curYear=curDate.curYear;
+				let curMonth=curDate.curMonth;
+				let curMonthdays=curDate.curMonthdays;
+				let curDay=curDate.curDay;
+				let curHour=curDate.curHour;
+				let curMinute=curDate.curMinute;
+				let curSecond=curDate.curSecond;
+				let dateData=[];
+				dVal=this.getDval();
+				
+				startDate=this.getStartDate();
+				endDate=this.getEndDate();
+				startYear=startDate.getFullYear();
+				startMonth=startDate.getMonth();
+				startDay=startDate.getDate();
+				endYear=endDate.getFullYear();
+				endMonth=endDate.getMonth();
+				endDay=endDate.getDate();
+				dateData=this.getData(dVal);
+				years=dateData.years;
+				months=dateData.months;
+				days=dateData.days;
+				hours=dateData.hours;
+				minutes=dateData.minutes;
+				seconds=dateData.seconds;
+				switch(this.fields){
+					case "year":
+						pickVal=disabledAfter?[
+							dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0
+						]:(curFlag?[
+							years.indexOf(curYear+'')
+						]:[
+							dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0
+						]);
+						range={years};
+						year=dVal[0]?dVal[0]:years[0];
+						result=full=`${year}`;
+						obj={
+							year
+						}
+						break;
+					case "month":
+						pickVal=disabledAfter?[
+							dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0,
+							dVal[1]&&months.indexOf(dVal[1])!=-1?months.indexOf(dVal[1]):0
+						]:(curFlag?[
+							years.indexOf(curYear+''),
+							months.indexOf(this.formatNum(curMonth))
+						]:[
+							dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0,
+							dVal[1]&&months.indexOf(dVal[1])!=-1?months.indexOf(dVal[1]):0
+						]);
+						range={years,months};
+						year=dVal[0]?dVal[0]:years[0];
+						month=dVal[1]?dVal[1]:months[0];
+						result=full=`${year+'-'+month}`;
+						obj={
+							year,
+							month
+						}
+						break;
+					case "day":
+						pickVal=disabledAfter?[
+							dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0,
+							dVal[1]&&months.indexOf(dVal[1])!=-1?months.indexOf(dVal[1]):0,
+							dVal[2]&&days.indexOf(dVal[2])!=-1?days.indexOf(dVal[2]):0
+						]:(curFlag?[
+							years.indexOf(curYear+''),
+							months.indexOf(this.formatNum(curMonth)),
+							days.indexOf(this.formatNum(curDay)),
+						]:[
+							dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0,
+							dVal[1]&&months.indexOf(dVal[1])!=-1?months.indexOf(dVal[1]):0,
+							dVal[2]&&days.indexOf(dVal[2])!=-1?days.indexOf(dVal[2]):0
+						]);
+						range={years,months,days};
+						year=dVal[0]?dVal[0]:years[0];
+						month=dVal[1]?dVal[1]:months[0];
+						day=dVal[2]?dVal[2]:days[0];
+						result=full=`${year+'-'+month+'-'+day}`;
+						obj={
+							year,
+							month,
+							day
+						}
+						break;
+					case "hour":
+						pickVal=disabledAfter?[
+							dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0,
+							dVal[1]&&months.indexOf(dVal[1])!=-1?months.indexOf(dVal[1]):0,
+							dVal[2]&&days.indexOf(dVal[2])!=-1?days.indexOf(dVal[2]):0,
+							dVal[3]&&hours.indexOf(dVal[3])!=-1?hours.indexOf(dVal[3]):0
+						]:(curFlag?[
+							years.indexOf(curYear+''),
+							months.indexOf(this.formatNum(curMonth)),
+							days.indexOf(this.formatNum(curDay)),
+							hours.indexOf(this.formatNum(curHour)),
+						]:[
+							dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0,
+							dVal[1]&&months.indexOf(dVal[1])!=-1?months.indexOf(dVal[1]):0,
+							dVal[2]&&days.indexOf(dVal[2])!=-1?days.indexOf(dVal[2]):0,
+							dVal[3]&&hours.indexOf(dVal[3])!=-1?hours.indexOf(dVal[3]):0
+						]);
+						range={years,months,days,hours};
+						year=dVal[0]?dVal[0]:years[0];
+						month=dVal[1]?dVal[1]:months[0];
+						day=dVal[2]?dVal[2]:days[0];
+						hour=dVal[3]?dVal[3]:hours[0];
+						result=`${year+'-'+month+'-'+day+' '+hour}`;
+						full=`${year+'-'+month+'-'+day+' '+hour+':00:00'}`;
+						obj={
+							year,
+							month,
+							day,
+							hour
+						}
+						break;
+					case "minute":
+						pickVal=disabledAfter?[
+							dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0,
+							dVal[1]&&months.indexOf(dVal[1])!=-1?months.indexOf(dVal[1]):0,
+							dVal[2]&&days.indexOf(dVal[2])!=-1?days.indexOf(dVal[2]):0,
+							dVal[3]&&hours.indexOf(dVal[3])!=-1?hours.indexOf(dVal[3]):0,
+							dVal[4]&&minutes.indexOf(dVal[4])!=-1?minutes.indexOf(dVal[4]):0
+						]:(curFlag?[
+							years.indexOf(curYear+''),
+							months.indexOf(this.formatNum(curMonth)),
+							days.indexOf(this.formatNum(curDay)),
+							hours.indexOf(this.formatNum(curHour)),
+							minutes.indexOf(this.formatNum(curMinute)),
+						]:[
+							dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0,
+							dVal[1]&&months.indexOf(dVal[1])!=-1?months.indexOf(dVal[1]):0,
+							dVal[2]&&days.indexOf(dVal[2])!=-1?days.indexOf(dVal[2]):0,
+							dVal[3]&&hours.indexOf(dVal[3])!=-1?hours.indexOf(dVal[3]):0,
+							dVal[4]&&minutes.indexOf(dVal[4])!=-1?minutes.indexOf(dVal[4]):0
+						]);
+						range={years,months,days,hours,minutes};
+						year=dVal[0]?dVal[0]:years[0];
+						month=dVal[1]?dVal[1]:months[0];
+						day=dVal[2]?dVal[2]:days[0];
+						hour=dVal[3]?dVal[3]:hours[0];
+						minute=dVal[4]?dVal[4]:minutes[0];
+						full=`${year+'-'+month+'-'+day+' '+hour+':'+minute+':00'}`;
+						result=`${year+'-'+month+'-'+day+' '+hour+':'+minute}`;
+						obj={
+							year,
+							month,
+							day,
+							hour,
+							minute
+						}
+						break;
+					case "second":
+						pickVal=disabledAfter?[
+							dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0,
+							dVal[1]&&months.indexOf(dVal[1])!=-1?months.indexOf(dVal[1]):0,
+							dVal[2]&&days.indexOf(dVal[2])!=-1?days.indexOf(dVal[2]):0,
+							dVal[3]&&hours.indexOf(dVal[3])!=-1?hours.indexOf(dVal[3]):0,
+							dVal[4]&&minutes.indexOf(dVal[4])!=-1?minutes.indexOf(dVal[4]):0,
+							dVal[5]&&seconds.indexOf(dVal[5])!=-1?seconds.indexOf(dVal[5]):0
+						]:(curFlag?[
+							years.indexOf(curYear+''),
+							months.indexOf(this.formatNum(curMonth)),
+							days.indexOf(this.formatNum(curDay)),
+							hours.indexOf(this.formatNum(curHour)),
+							minutes.indexOf(this.formatNum(curMinute)),
+							seconds.indexOf(this.formatNum(curSecond)),
+						]:[
+							dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0,
+							dVal[1]&&months.indexOf(dVal[1])!=-1?months.indexOf(dVal[1]):0,
+							dVal[2]&&days.indexOf(dVal[2])!=-1?days.indexOf(dVal[2]):0,
+							dVal[3]&&hours.indexOf(dVal[3])!=-1?hours.indexOf(dVal[3]):0,
+							dVal[4]&&minutes.indexOf(dVal[4])!=-1?minutes.indexOf(dVal[4]):0,
+							dVal[5]&&seconds.indexOf(dVal[5])!=-1?seconds.indexOf(dVal[5]):0
+						]);
+						range={years,months,days,hours,minutes,seconds};
+						year=dVal[0]?dVal[0]:years[0];
+						month=dVal[1]?dVal[1]:months[0];
+						day=dVal[2]?dVal[2]:days[0];
+						hour=dVal[3]?dVal[3]:hours[0];
+						minute=dVal[4]?dVal[4]:minutes[0];
+						second=dVal[5]?dVal[5]:seconds[0];
+						result=full=`${year+'-'+month+'-'+day+' '+hour+':'+minute+':'+second}`;
+						obj={
+							year,
+							month,
+							day,
+							hour,
+							minute,
+							second
+						}
+						break;
+					default:
+						range={years,months,days};
+						break;
+				}
+				this.range=range;
+				this.checkObj=obj;
+				this.$emit("change",{
+					result:result,
+					value:full,
+					obj:obj
+				});
+				this.$nextTick(()=>{
+					this.pickVal=pickVal;
+				})
+			},
+			handlerChange(e){
+				let arr=[...e.detail.value];
+				let data=this.range;
+				let year="",month="",day="",hour="",minute="",second="";
+				let result="",full="",obj={};
+				let months=null,days=null,hours=null,minutes=null,seconds=null;
+				let disabledAfter=this.disabledAfter;
+				let leapYear=false,resetData={};
+				year=(arr[0]||arr[0]==0)?data.years[arr[0]]||data.years[data.years.length-1]:"";
+				month=(arr[1]||arr[1]==0)?data.months[arr[1]]||data.months[data.months.length-1]:"";
+				day=(arr[2]||arr[2]==0)?data.days[arr[2]]||data.days[data.days.length-1]:"";
+				hour=(arr[3]||arr[3]==0)?data.hours[arr[3]]||data.hours[data.hours.length-1]:"";
+				minute=(arr[4]||arr[4]==0)?data.minutes[arr[4]]||data.minutes[data.minutes.length-1]:"";
+				second=(arr[5]||arr[5]==0)?data.seconds[arr[5]]||data.seconds[data.seconds.length-1]:"";
+				resetData=this.resetData(year,month,day,hour,minute);//重新拉取当前日期数据;
+				leapYear=this.isLeapYear(year);//判断是否为闰年;
+				switch(this.fields){
+					case "year":
+						result=full=`${year}`;
+						obj={
+							year
+						};
+						break;
+					case "month":
+						result=full=`${year+'-'+month}`;
+						if(this.disabledAfter)months=resetData.months;
+						if(months)this.range.months=months;
+						obj={
+							year,
+							month
+						}
+						break;
+					case "day":
+						result=full=`${year+'-'+month+'-'+day}`;
+						if(this.disabledAfter){
+							months=resetData.months;
+							days=resetData.days;
+						}else{
+							if(leapYear||(month!=this.checkObj.month)||month==2){
+								days=resetData.days;
+							}
+						}
+						if(months)this.range.months=months;
+						if(days)this.range.days=days;
+						obj={
+							year,
+							month,
+							day
+						}
+						break;
+					case "hour":
+						result=`${year+'-'+month+'-'+day+' '+hour}`;
+						full=`${year+'-'+month+'-'+day+' '+hour+':00:00'}`;
+						if(this.disabledAfter){
+							months=resetData.months;
+							days=resetData.days;
+							hours=resetData.hours;
+						}else{
+							if(leapYear||(month!=this.checkObj.month)||month==2){
+								days=resetData.days;
+							}
+						}
+						if(months)this.range.months=months;
+						if(days)this.range.days=days;
+						if(hours)this.range.hours=hours;
+						obj={
+							year,
+							month,
+							day,
+							hour
+						}
+						break;
+					case "minute":
+						full=`${year+'-'+month+'-'+day+' '+hour+':'+minute+':00'}`;
+						result=`${year+'-'+month+'-'+day+' '+hour+':'+minute}`;
+						if(this.disabledAfter){
+							months=resetData.months;
+							days=resetData.days;
+							hours=resetData.hours;
+							minutes=resetData.minutes;
+						}else{
+							if(leapYear||(month!=this.checkObj.month)||month==2){
+								days=resetData.days;
+							}
+						}
+						if(months)this.range.months=months;
+						if(days)this.range.days=days;
+						if(hours)this.range.hours=hours;
+						if(minutes)this.range.minutes=minutes;
+						obj={
+							year,
+							month,
+							day,
+							hour,
+							minute
+						};
+						break;
+					case "second":
+						result=full=`${year+'-'+month+'-'+day+' '+hour+':'+minute+':'+second}`;
+						if(this.disabledAfter){
+							months=resetData.months;
+							days=resetData.days;
+							hours=resetData.hours;
+							minutes=resetData.minutes;
+							//seconds=resetData.seconds;
+						}else{
+							if(leapYear||(month!=this.checkObj.month)||month==2){
+								days=resetData.days;
+							}
+						}
+						if(months)this.range.months=months;
+						if(days)this.range.days=days;
+						if(hours)this.range.hours=hours;
+						if(minutes)this.range.minutes=minutes;
+						//if(seconds)this.range.seconds=seconds;
+						obj={
+							year,
+							month,
+							day,
+							hour,
+							minute,
+							second
+						}
+						break;
+				}
+				this.checkObj=obj;
+				this.$emit("change",{
+					result:result,
+					value:full,
+					obj:obj
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	@import "./w-picker.css";
+</style>

+ 346 - 0
components/w-picker/half-picker.vue

@@ -0,0 +1,346 @@
+<template>
+	<view class="w-picker-view">
+		<picker-view class="d-picker-view" :indicator-style="itemHeight" :value="pickVal" @change="handlerChange">
+			<picker-view-column>
+				<view class="w-picker-item" v-for="(item,index) in range.years" :key="index">{{item}}年</view>
+			</picker-view-column>
+			<picker-view-column>
+				<view class="w-picker-item" v-for="(item,index) in range.months" :key="index">{{item}}月</view>
+			</picker-view-column>
+			<picker-view-column>
+				<view class="w-picker-item" v-for="(item,index) in range.days" :key="index">{{item}}日</view>
+			</picker-view-column>
+			<picker-view-column>
+				<view class="w-picker-item" v-for="(item,index) in range.sections" :key="index">{{item}}</view>
+			</picker-view-column>
+		</picker-view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				pickVal:[],
+				range:{},
+				checkObj:{}
+			};
+		},
+		props:{
+			itemHeight:{
+				type:String,
+				default:"44px"
+			},
+			startYear:{
+				type:String,
+				default:""
+			},
+			endYear:{
+				type:String,
+				default:""
+			},
+			value:{
+				type:[String,Array,Number],
+				default:""
+			},
+			current:{//是否默认选中当前日期
+				type:Boolean,
+				default:false
+			},
+			disabledAfter:{//是否禁用当前之后的日期
+				type:Boolean,
+				default:false
+			}
+		},
+		watch:{
+			value(val){
+				this.initData();
+			}
+		},
+		created() {
+			this.initData();
+		},
+		methods:{
+			formatNum(n){
+				return (Number(n)<10?'0'+Number(n):Number(n)+'');
+			},
+			checkValue(value){
+				let strReg=/^\d{4}-\d{2}-\d{2} [\u4e00-\u9fa5]{2}$/,example;
+				if(!strReg.test(value)){
+					console.log(new Error("请传入与mode、fields匹配的value值,例value="+example+""))
+				}
+				return strReg.test(value);
+			},
+			resetData(year,month,day){
+				let curDate=this.getCurrenDate();
+				let curFlag=this.current;
+				let curYear=curDate.curYear;
+				let curMonth=curDate.curMonth;
+				let curDay=curDate.curDay;
+				let curHour=curDate.curHour;
+				let months=[],days=[],sections=[];
+				let disabledAfter=this.disabledAfter;
+				let monthsLen=disabledAfter?(year*1<curYear?12:curMonth):12;
+				let totalDays=new Date(year,month,0).getDate();//计算当月有几天;
+				let daysLen=disabledAfter?((year*1<curYear||month*1<curMonth)?totalDays:curDay):totalDays;
+				let sectionFlag=disabledAfter?((year*1<curYear||month*1<curMonth||day*1<curDay)==true?false:true):(curHour>12==true?true:false);
+				sections=["上午","下午"];
+				for(let month=1;month<=monthsLen;month++){
+					months.push(this.formatNum(month));
+				};
+				for(let day=1;day<=daysLen;day++){
+					days.push(this.formatNum(day));
+				}
+				if(sectionFlag){
+					sections=["上午"];
+				}
+				return{
+					months,
+					days,
+					sections
+				}
+			},
+			getData(dVal){
+				//用来处理初始化数据
+				let curFlag=this.current;
+				let disabledAfter=this.disabledAfter;
+				let curDate=this.getCurrenDate();
+				let curYear=curDate.curYear;
+				let curMonthdays=curDate.curMonthdays;
+				let curMonth=curDate.curMonth;
+				let curDay=curDate.curDay;
+				let curHour=curDate.curHour;
+				let defaultDate=this.getDefaultDate();
+				let startYear=this.getStartDate().getFullYear();
+				let endYear=this.getEndDate().getFullYear();
+				let years=[],months=[],days=[],sections=[];
+				let year=dVal[0]*1;
+				let month=dVal[1]*1;
+				let day=dVal[2]*1;
+				let monthsLen=disabledAfter?(year<curYear?12:curDate.curMonth):12;
+				let daysLen=disabledAfter?((year<curYear||month<curMonth)?defaultDate.defaultDays:curDay):(curFlag?curMonthdays:defaultDate.defaultDays);
+				let sectionFlag=disabledAfter?((year*1<curYear||month*1<curMonth||day*1<curDay)==true?false:true):(curHour>12==true?true:false);
+				for(let year=startYear;year<=(disabledAfter?curYear:endYear);year++){
+					years.push(year.toString())
+				}
+				for(let month=1;month<=monthsLen;month++){
+					months.push(this.formatNum(month));
+				}
+				for(let day=1;day<=daysLen;day++){
+					days.push(this.formatNum(day));
+				}
+				if(sectionFlag){
+					sections=["下午"];
+				}else{
+					sections=["上午","下午"];
+				}
+				sections=["上午","下午"];
+				return {
+					years,
+					months,
+					days,
+					sections
+				}
+			},
+			getCurrenDate(){
+				let curDate=new Date();
+				let curYear=curDate.getFullYear();
+				let curMonth=curDate.getMonth()+1;
+				let curMonthdays=new Date(curYear,curMonth,0).getDate();
+				let curDay=curDate.getDate();
+				let curHour=curDate.getHours();
+				let curSection="上午";
+				if(curHour>=12){
+					curSection="下午";
+				}
+				return{
+					curDate,
+					curYear,
+					curMonth,
+					curMonthdays,
+					curDay,
+					curHour,
+					curSection
+				}
+			},
+			getDefaultDate(){
+				let value=this.value;
+				let reg=/-/g;
+				let defaultDate=value?new Date(value.split(" ")[0].replace(reg,"/")):new Date();
+				let defaultYear=defaultDate.getFullYear();
+				let defaultMonth=defaultDate.getMonth()+1;
+				let defaultDay=defaultDate.getDate();
+				let defaultDays=new Date(defaultYear,defaultMonth,0).getDate()*1;
+				return{
+					defaultDate,
+					defaultYear,
+					defaultMonth,
+					defaultDay,
+					defaultDays
+				}
+			},
+			getStartDate(){
+				let start=this.startYear;
+				let startDate="";
+				let reg=/-/g;
+				if(start){
+					startDate=new Date(start+"/01/01");
+				}else{
+					startDate=new Date("1970/01/01");
+				}
+				return startDate;
+			},
+			getEndDate(){
+				let end=this.endYear;
+				let reg=/-/g;
+				let endDate="";
+				if(end){
+					endDate=new Date(end+"/12/31");
+				}else{
+					endDate=new Date();
+				}
+				return endDate;
+			},
+			getDval(){
+				let value=this.value;
+				let dVal=null;
+				let aDate=new Date();
+				let year=this.formatNum(aDate.getFullYear());
+				let month=this.formatNum(aDate.getMonth()+1);
+				let day=this.formatNum(aDate.getDate());
+				let hour=aDate.getHours();
+				let section="上午";
+				if(hour>=12)section="下午";
+				if(value){
+					let flag=this.checkValue(value);
+					if(!flag){
+						dVal=[year,month,day,section]
+					}else{
+						let v=value.split(" ");
+						dVal=[...v[0].split("-"),v[1]];
+					}
+				}else{
+					dVal=[year,month,day,section]
+				}
+				return dVal;
+			},
+			initData(){
+				let startDate,endDate,startYear,endYear,startMonth,endMonth,startDay,endDay;
+				let years=[],months=[],days=[],sections=[];
+				let dVal=[],pickVal=[];
+				let value=this.value;
+				let reg=/-/g;
+				let range={};
+				let result="",full="",year,month,day,section,obj={};
+				let defaultDate=this.getDefaultDate();
+				let defaultYear=defaultDate.defaultYear;
+				let defaultMonth=defaultDate.defaultMonth;
+				let defaultDay=defaultDate.defaultDay;
+				let defaultDays=defaultDate.defaultDays;
+				let curFlag=this.current;
+				let disabledAfter=this.disabledAfter;
+				let curDate=this.getCurrenDate();
+				let curYear=curDate.curYear;
+				let curMonth=curDate.curMonth;
+				let curMonthdays=curDate.curMonthdays;
+				let curDay=curDate.curDay;
+				let curSection=curDate.curSection;
+				let dateData=[];
+				dVal=this.getDval();
+				startDate=this.getStartDate();
+				endDate=this.getEndDate();
+				startYear=startDate.getFullYear();
+				startMonth=startDate.getMonth();
+				startDay=startDate.getDate();
+				endYear=endDate.getFullYear();
+				endMonth=endDate.getMonth();
+				endDay=endDate.getDate();
+				dateData=this.getData(dVal);
+				years=dateData.years;
+				months=dateData.months;
+				days=dateData.days;
+				sections=dateData.sections;
+				pickVal=disabledAfter?[
+					dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0,
+					dVal[1]&&months.indexOf(dVal[1])!=-1?months.indexOf(dVal[1]):0,
+					dVal[2]&&days.indexOf(dVal[2])!=-1?days.indexOf(dVal[2]):0,
+					dVal[3]&&sections.indexOf(dVal[3])!=-1?sections.indexOf(dVal[3]):0
+				]:(curFlag?[
+					years.indexOf(curYear+''),
+					months.indexOf(this.formatNum(curMonth)),
+					days.indexOf(this.formatNum(curDay)),
+					sections.indexOf(curSection),
+				]:[
+					dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0,
+					dVal[1]&&months.indexOf(dVal[1])!=-1?months.indexOf(dVal[1]):0,
+					dVal[2]&&days.indexOf(dVal[2])!=-1?days.indexOf(dVal[2]):0,
+					dVal[3]&&sections.indexOf(dVal[3])!=-1?sections.indexOf(dVal[3]):0
+				]);
+				range={years,months,days,sections};
+				year=dVal[0]?dVal[0]:years[0];
+				month=dVal[1]?dVal[1]:months[0];
+				day=dVal[2]?dVal[2]:days[0];
+				section=dVal[3]?dVal[3]:sections[0];
+				result=full=`${year+'-'+month+'-'+day+' '+section}`;
+				obj={
+					year,
+					month,
+					day,
+					section
+				}
+				this.range=range;
+				this.checkObj=obj;
+				this.$nextTick(()=>{
+					this.pickVal=pickVal;
+				});
+				this.$emit("change",{
+					result:result,
+					value:full,
+					obj:obj
+				})
+			},
+			handlerChange(e){
+				let arr=[...e.detail.value];
+				let data=this.range;
+				let year="",month="",day="",section="";
+				let result="",full="",obj={};
+				let months=null,days=null,sections=null;
+				let disabledAfter=this.disabledAfter;
+				year=(arr[0]||arr[0]==0)?data.years[arr[0]]||data.years[data.years.length-1]:"";
+				month=(arr[1]||arr[1]==0)?data.months[arr[1]]||data.months[data.months.length-1]:"";
+				day=(arr[2]||arr[2]==0)?data.days[arr[2]]||data.days[data.days.length-1]:"";
+				section=(arr[3]||arr[3]==0)?data.sections[arr[3]]||data.sections[data.sections.length-1]:"";
+				result=full=`${year+'-'+month+'-'+day+' '+section}`;
+				let resetData=this.resetData(year,month,day);
+				if(this.disabledAfter){
+					months=resetData.months;
+					days=resetData.days;
+					sections=resetData.sections;
+				}else{
+					if(year%4==0||(month!=this.checkObj.month)){
+						days=resetData.days;
+					}
+				}
+				if(months)this.range.months=months;
+				if(days)this.range.days=days;
+				if(sections)this.range.sections=sections;
+				obj={
+					year,
+					month,
+					day,
+					section
+				}
+				this.checkObj=obj;
+				this.$emit("change",{
+					result:result,
+					value:full,
+					obj:obj
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	@import "./w-picker.css";
+</style>

+ 274 - 0
components/w-picker/linkage-picker.vue

@@ -0,0 +1,274 @@
+<template>
+	<view class="w-picker-view">
+		<picker-view class="d-picker-view" :indicator-style="itemHeight" :value="pickVal" @change="handlerChange">
+			<picker-view-column v-for="(group,gIndex) in range" :key="gIndex">
+				<view class="w-picker-item" v-for="(item,index) in group" :key="index">{{item[nodeKey]}}</view>
+			</picker-view-column>
+		</picker-view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				pickVal:[],
+				range:[],
+				checkObj:{}
+			};
+		},
+		props:{
+			itemHeight:{
+				type:String,
+				default:"44px"
+			},
+			value:{
+				type:[Array,String],
+				default:""
+			},
+			defaultType:{
+				type:String,
+				default:"label"
+			},
+			options:{
+				type:Array,
+				default(){
+					return []
+				}
+			},
+			defaultProps:{
+				type:Object,
+				default(){
+					return{
+						lable:"label",
+						value:"value",
+						children:"children"
+					}
+				}
+			},
+			level:{
+				//多级联动层级,表示几级联动
+				type:[Number,String],
+				default:2
+			}
+		},
+		computed:{
+			nodeKey(){
+				return this.defaultProps.label;
+			},
+			nodeVal(){
+				return this.defaultProps.value;
+			},
+			nodeChild(){
+				return this.defaultProps.children;
+			}
+		},
+		watch:{
+			value(val){
+				if(this.options.length!=0){
+					this.initData();
+				}
+			},
+			options(val){
+				this.initData();
+			}
+		},
+		created() {
+			if(this.options.length!=0){
+				this.initData();
+			}
+		},
+		methods:{
+			getData(){
+				//用来处理初始化数据
+				let options=this.options;
+				let col1={},col2={},col3={},col4={};
+				let arr1=options,arr2=[],arr3=[],arr4=[];
+				let col1Index=0,col2Index=0,col3Index=0,col4Index=0;
+				let a1="",a2="",a3="",a4="";
+				let dVal=[],obj={};
+				let value=this.value;
+				let data=[];
+				a1=value[0];
+				a2=value[1];
+				if(this.level>2){
+					a3=value[2];
+				}
+				if(this.level>3){
+					a4=value[3];
+				};
+				/*第1列*/
+				col1Index=arr1.findIndex((v)=>{
+					return v[this.defaultType]==a1
+				});
+				col1Index=value?(col1Index!=-1?col1Index:0):0;
+				col1=arr1[col1Index];
+				
+				/*第2列*/
+				arr2=arr1[col1Index][this.nodeChild];
+				col2Index=arr2.findIndex((v)=>{
+					return v[this.defaultType]==a2
+				});
+				col2Index=value?(col2Index!=-1?col2Index:0):0;
+				col2=arr2[col2Index];
+				
+				/*第3列*/
+				if(this.level>2){
+					arr3=arr2[col2Index][this.nodeChild];
+					col3Index=arr3.findIndex((v)=>{
+						return v[this.defaultType]==a3;
+					});
+					col3Index=value?(col3Index!=-1?col3Index:0):0;
+					col3=arr3[col3Index];
+				};
+				
+				
+				/*第4列*/
+				if(this.level>3){
+					arr4=arr3[col4Index][this.nodeChild];
+					col4Index=arr4.findIndex((v)=>{
+						return v[this.defaultType]==a4;
+					});
+					col4Index=value?(col4Index!=-1?col4Index:0):0;
+					col4=arr4[col4Index];
+				};
+				switch(this.level*1){
+					case 2:
+						dVal=[col1Index,col2Index];
+						obj={
+							col1,
+							col2
+						}
+						data=[arr1,arr2];
+						break;
+					case 3:
+						dVal=[col1Index,col2Index,col3Index];
+						obj={
+							col1,
+							col2,
+							col3
+						}
+						data=[arr1,arr2,arr3];
+						break;
+					case 4:
+						dVal=[col1Index,col2Index,col3Index,col4Index];
+						obj={
+							col1,
+							col2,
+							col3,
+							col4
+						}
+						data=[arr1,arr2,arr3,arr4];
+						break
+				}
+				return {
+					data,
+					dVal,
+					obj
+				}
+			},
+			initData(){
+				let dataData=this.getData();
+				let data=dataData.data;
+				let arr1=data[0];
+				let arr2=data[1];
+				let arr3=data[2]||[];
+				let arr4=data[3]||[];
+				let obj=dataData.obj;
+				let col1=obj.col1,col2=obj.col2,col3=obj.col3||{},col4=obj.col4||{};
+				let result="",value=[];
+				let range=[];
+				switch(this.level){
+					case 2:
+						value=[col1[this.nodeVal],col2[this.nodeVal]];
+						result=`${col1[this.nodeKey]+col2[this.nodeKey]}`;
+						range=[arr1,arr2];
+						break;
+					case 3:
+						value=[col1[this.nodeVal],col2[this.nodeVal],col3[this.nodeVal]];
+						result=`${col1[this.nodeKey]+col2[this.nodeKey]+col3[this.nodeKey]}`;
+						range=[arr1,arr2,arr3];
+						break;
+					case 4:
+						value=[col1[this.nodeVal],col2[this.nodeVal],col3[this.nodeVal],col4[this.nodeVal]];
+						result=`${col1[this.nodeKey]+col2[this.nodeKey]+col3[this.nodeKey]+col4[this.nodeKey]}`;
+						range=[arr1,arr2,arr3,arr4];
+						break;
+				}
+				this.range=range;
+				this.checkObj=obj;
+				this.$nextTick(()=>{
+					this.pickVal=dataData.dVal;
+				});
+				this.$emit("change",{
+					result:result,
+					value:value,
+					obj:obj
+				})
+			},
+			handlerChange(e){
+				let arr=[...e.detail.value];
+				let col1Index=arr[0],col2Index=arr[1],col3Index=arr[2]||0,col4Index=arr[3]||0;
+				let arr1=[],arr2=[],arr3=[],arr4=[];
+				let col1,col2,col3,col4,obj={};
+				let result="",value=[];
+				arr1=this.options;
+				arr2=(arr1[col1Index]&&arr1[col1Index][this.nodeChild])||arr1[arr1.length-1][this.nodeChild]||[];
+				col1=arr1[col1Index]||arr1[arr1.length-1]||{};
+				col2=arr2[col2Index]||arr2[arr2.length-1]||{};
+				if(this.level>2){
+					arr3=(arr2[col2Index]&&arr2[col2Index][this.nodeChild])||arr2[arr2.length-1][this.nodeChild];
+					col3=arr3[col3Index]||arr3[arr3.length-1]||{};
+				}
+				if(this.level>3){
+					arr4=(arr3[col3Index]&&arr3[col3Index][this.nodeChild])||arr3[arr3.length-1][this.nodeChild]||[];
+					col4=arr4[col4Index]||arr4[arr4.length-1]||{};
+				}
+				switch(this.level){
+					case 2:
+						obj={
+							col1,
+							col2
+						}
+						this.range=[arr1,arr2];
+						result=`${(col1[this.nodeKey]||'')+(col2[this.nodeKey]||'')}`;
+						value=[col1[this.nodeVal]||'',col2[this.nodeVal]||''];
+						break;
+					case 3:
+						obj={
+							col1,
+							col2,
+							col3
+						}
+						this.range=[arr1,arr2,arr3];
+						result=`${(col1[this.nodeKey]||'')+(col2[this.nodeKey]||'')+(col3[this.nodeKey]||'')}`;
+						value=[col1[this.nodeVal]||'',col2[this.nodeVal]||'',col3[this.nodeVal]||''];
+						break;
+					case 4:
+						obj={
+							col1,
+							col2,
+							col3,
+							col4
+						}
+						this.range=[arr1,arr2,arr3,arr4];
+						result=`${(col1[this.nodeKey]||'')+(col2[this.nodeKey]||'')+(col3[this.nodeKey]||'')+(col4[this.nodeKey]||'')}`;
+						value=[col1[this.nodeVal]||'',col2[this.nodeVal]||'',col3[this.nodeVal]||'',col4[this.nodeVal]||''];
+						break;
+				}
+				this.checkObj=obj;
+				this.pickVal=arr;
+				this.$emit("change",{
+					result:result,
+					value:value,
+					obj:obj
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	@import "./w-picker.css";	
+</style>
+

+ 344 - 0
components/w-picker/range-picker.vue

@@ -0,0 +1,344 @@
+<template>
+	<view class="w-picker-view">
+		<picker-view  class="d-picker-view" :indicator-style="itemHeight" :value="pickVal" @change="handlerChange">
+			<picker-view-column class="w-picker-flex2">
+				<view class="w-picker-item" v-for="(item,index) in range.fyears" :key="index">{{item}}年</view>
+			</picker-view-column>
+			<picker-view-column class="w-picker-flex2">
+				<view class="w-picker-item" v-for="(item,index) in range.fmonths" :key="index">{{item}}月</view>
+			</picker-view-column>
+			<picker-view-column class="w-picker-flex2">
+				<view class="w-picker-item" v-for="(item,index) in range.fdays" :key="index">{{item}}日</view>
+			</picker-view-column>
+			<picker-view-column class="w-picker-flex1">
+				<view class="w-picker-item">-</view>
+			</picker-view-column>
+			<picker-view-column class="w-picker-flex2">
+				<view class="w-picker-item" v-for="(item,index) in range.tyears" :key="index">{{item}}年</view>
+			</picker-view-column>
+			<picker-view-column class="w-picker-flex2">
+				<view class="w-picker-item" v-for="(item,index) in range.tmonths" :key="index">{{item}}月</view>
+			</picker-view-column>
+			<picker-view-column class="w-picker-flex2">
+				<view class="w-picker-item" v-for="(item,index) in range.tdays" :key="index">{{item}}日</view>
+			</picker-view-column>
+		</picker-view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				pickVal:[],
+				range:{},
+				checkObj:{}
+			};
+		},
+		props:{
+			itemHeight:{
+				type:String,
+				default:"44px"
+			},
+			value:{
+				type:[String,Array],
+				default(){
+					return []
+				}
+			},
+			current:{//是否默认选中当前日期
+				type:Boolean,
+				default:false
+			},
+			startYear:{
+				type:[String,Number],
+				default:1970
+			},
+			endYear:{
+				type:[String,Number],
+				default:new Date().getFullYear()
+			}
+		},
+		watch:{
+			value(val){
+				this.initData();
+			}
+		},
+		created() {
+			this.initData();
+		},
+		methods:{
+			formatNum(n){
+				return (Number(n)<10?'0'+Number(n):Number(n)+'');
+			},
+			checkValue(value){
+				let strReg=/^\d{4}-\d{2}-\d{2}$/,example="2020-04-03";
+				if(!strReg.test(value[0])||!strReg.test(value[1])){
+					console.log(new Error("请传入与mode匹配的value值,例["+example+","+example+"]"))
+				}
+				return strReg.test(value[0])&&strReg.test(value[1]);
+			},
+			resetToData(fmonth,fday,tyear,tmonth){
+				let range=this.range;
+				let tmonths=[],tdays=[];
+				let yearFlag=tyear!=range.tyears[0];
+				let monthFlag=tyear!=range.tyears[0]||tmonth!=range.tmonths[0];
+				let ttotal=new Date(tyear,tmonth,0).getDate();
+				for(let i=yearFlag?1:fmonth*1;i<=12;i++){
+					tmonths.push(this.formatNum(i))
+				}
+				for(let i=monthFlag?1:fday*1;i<=ttotal;i++){
+					tdays.push(this.formatNum(i))
+				}
+				return{
+					tmonths,
+					tdays
+				}
+			},
+			resetData(fyear,fmonth,fday,tyear,tmonth){
+				let fyears=[],fmonths=[],fdays=[],tyears=[],tmonths=[],tdays=[];
+				let startYear=this.startYear;
+				let endYear=this.endYear;
+				let ftotal=new Date(fyear,fmonth,0).getDate();
+				let ttotal=new Date(tyear,tmonth,0).getDate();
+				for(let i=startYear*1;i<=endYear;i++){
+					fyears.push(this.formatNum(i))
+				}
+				for(let i=1;i<=12;i++){
+					fmonths.push(this.formatNum(i))
+				}
+				for(let i=1;i<=ftotal;i++){
+					fdays.push(this.formatNum(i))
+				}
+				for(let i=fyear*1;i<=endYear;i++){
+					tyears.push(this.formatNum(i))
+				}
+				for(let i=fmonth*1;i<=12;i++){
+					tmonths.push(this.formatNum(i))
+				}
+				for(let i=fday*1;i<=ttotal;i++){
+					tdays.push(this.formatNum(i))
+				}
+				return {
+					fyears,
+					fmonths,
+					fdays,
+					tyears,
+					tmonths,
+					tdays
+				}
+			},
+			getData(dVal){
+				let start=this.startYear*1;
+				let end=this.endYear*1;
+				let value=dVal;
+				let flag=this.current;
+				let aToday=new Date();
+				let tYear,tMonth,tDay,tHours,tMinutes,tSeconds,pickVal=[];
+				let initstartDate=new Date(start.toString());
+				let endDate=new Date(end.toString());
+				if(start>end){
+					initstartDate=new Date(end.toString());
+					endDate=new Date(start.toString());
+				};
+				let startYear=initstartDate.getFullYear();
+				let startMonth=initstartDate.getMonth()+1;
+				let endYear=endDate.getFullYear();
+				let fyears=[],fmonths=[],fdays=[],tyears=[],tmonths=[],tdays=[],returnArr=[],startDVal=[],endDVal=[];
+				let curMonth=flag?value[1]*1:(startDVal[1]*1+1);
+				let curMonth1=flag?value[5][1]*1:(value[5]*1+1);
+				let totalDays=new Date(value[0],value[1],0).getDate();
+				let totalDays1=new Date(value[4],value[5],0).getDate();
+				for(let s=startYear;s<=endYear;s++){
+					fyears.push(this.formatNum(s));
+				};
+				for(let m=1;m<=12;m++){
+					fmonths.push(this.formatNum(m));
+				};
+				for(let d=1;d<=totalDays;d++){
+					fdays.push(this.formatNum(d));
+				};
+				for(let s=value[0]*1;s<=endYear;s++){
+					tyears.push(this.formatNum(s));
+				};
+				
+				if(value[4]*1>value[0]*1){
+					for(let m=1;m<=12;m++){
+						tmonths.push(this.formatNum(m));
+					};
+					for(let d=1;d<=totalDays1;d++){
+						tdays.push(this.formatNum(d));
+					};
+				}else{
+					for(let m=value[1]*1;m<=12;m++){
+						tmonths.push(this.formatNum(m));
+					};
+					for(let d=value[2]*1;d<=totalDays1;d++){
+						tdays.push(this.formatNum(d));
+					};
+				};
+				
+				pickVal=[
+					fyears.indexOf(value[0])==-1?0:fyears.indexOf(value[0]),
+					fmonths.indexOf(value[1])==-1?0:fmonths.indexOf(value[1]),
+					fdays.indexOf(value[2])==-1?0:fdays.indexOf(value[2]),
+					0,
+					tyears.indexOf(value[4])==-1?0:tyears.indexOf(value[4]),
+					tmonths.indexOf(value[5])==-1?0:tmonths.indexOf(value[5]),
+					tdays.indexOf(value[6])==-1?0:tdays.indexOf(value[6])
+				];
+				return {
+					fyears,
+					fmonths,
+					fdays,
+					tyears,
+					tmonths,
+					tdays,
+					pickVal
+				}
+			},
+			getDval(){
+				let value=this.value;
+				let fields=this.fields;
+				let dVal=null;
+				let aDate=new Date();
+				let fyear=this.formatNum(aDate.getFullYear());
+				let fmonth=this.formatNum(aDate.getMonth()+1);
+				let fday=this.formatNum(aDate.getDate());
+				let tyear=this.formatNum(aDate.getFullYear());
+				let tmonth=this.formatNum(aDate.getMonth()+1);
+				let tday=this.formatNum(aDate.getDate());
+				if(value&&value.length>0){
+					let flag=this.checkValue(value);
+					if(!flag){
+						dVal=[fyear,fmonth,fday,"-",tyear,tmonth,tday]
+					}else{
+						dVal=[...value[0].split("-"),"-",...value[1].split("-")];
+					}
+				}else{
+					dVal=[fyear,fmonth,fday,"-",tyear,tmonth,tday]
+				}
+				return dVal;
+			},
+			initData(){
+				let range=[],pickVal=[];
+				let result="",full="",obj={};
+				let dVal=this.getDval();
+				let dateData=this.getData(dVal);
+				let fyears=[],fmonths=[],fdays=[],tyears=[],tmonths=[],tdays=[];
+				let fyear,fmonth,fday,tyear,tmonth,tday;
+				pickVal=dateData.pickVal;
+				fyears=dateData.fyears;
+				fmonths=dateData.fmonths;
+				fdays=dateData.fdays;
+				tyears=dateData.tyears;
+				tmonths=dateData.tmonths;
+				tdays=dateData.tdays;
+				range={
+					fyears,
+					fmonths,
+					fdays,
+					tyears,
+					tmonths,
+					tdays,
+				}
+				fyear=range.fyears[pickVal[0]];
+				fmonth=range.fmonths[pickVal[1]];
+				fday=range.fdays[pickVal[2]];
+				tyear=range.tyears[pickVal[4]];
+				tmonth=range.tmonths[pickVal[5]];
+				tday=range.tdays[pickVal[6]];
+				obj={
+					fyear,
+					fmonth,
+					fday,
+					tyear,
+					tmonth,
+					tday
+				}
+				result=`${fyear+'-'+fmonth+'-'+fday+'至'+tyear+'-'+tmonth+'-'+tday}`;
+				this.range=range;
+				this.checkObj=obj;
+				this.$nextTick(()=>{
+					this.pickVal=pickVal;
+				});
+				this.$emit("change",{
+					result:result,
+					value:result.split("至"),
+					obj:obj
+				})
+			},
+			handlerChange(e){
+				let arr=[...e.detail.value];
+				let result="",full="",obj={};
+				let year="",month="",day="",hour="",minute="",second="",note=[],province,city,area;
+				let checkObj=this.checkObj;
+				let days=[],months=[],endYears=[],endMonths=[],endDays=[],startDays=[];
+				let mode=this.mode;
+				let col1,col2,col3,d,a,h,m;
+				let xDate=new Date().getTime();
+				let range=this.range;
+				let fyear=range.fyears[arr[0]]||range.fyears[range.fyears.length-1];
+				let fmonth=range.fmonths[arr[1]]||range.fmonths[range.fmonths.length-1];
+				let fday=range.fdays[arr[2]]||range.fdays[range.fdays.length-1];
+				let tyear=range.tyears[arr[4]]||range.tyears[range.tyears.length-1];
+				let tmonth=range.tmonths[arr[5]]||range.tmonths[range.tmonths.length-1];
+				let tday=range.tdays[arr[6]]||range.tdays[range.tdays.length-1];
+				let resetData=this.resetData(fyear,fmonth,fday,tyear,tmonth);
+				if(fyear!=checkObj.fyear||fmonth!=checkObj.fmonth||fday!=checkObj.fday){
+					arr[4]=0;
+					arr[5]=0;
+					arr[6]=0;
+					range.tyears=resetData.tyears;
+					range.tmonths=resetData.tmonths;
+					range.tdays=resetData.tdays;
+					tyear=range.tyears[0];
+					checkObj.tyears=range.tyears[0];
+					tmonth=range.tmonths[0];
+					checkObj.tmonths=range.tmonths[0];
+					tday=range.tdays[0];
+					checkObj.tdays=range.tdays[0];
+				}
+				if(fyear!=checkObj.fyear||fmonth!=checkObj.fmonth){
+					range.fdays=resetData.fdays;
+				};
+				if(tyear!=checkObj.tyear){
+					arr[5]=0;
+					arr[6]=0;
+					let toData=this.resetToData(fmonth,fday,tyear,tmonth);
+					range.tmonths=toData.tmonths;
+					range.tdays=toData.tdays;
+					tmonth=range.tmonths[0];
+					checkObj.tmonths=range.tmonths[0];
+					tday=range.tdays[0];
+					checkObj.tdays=range.tdays[0];
+				};
+				if(tmonth!=checkObj.tmonth){
+					arr[6]=0;
+					let toData=this.resetToData(fmonth,fday,tyear,tmonth);
+					range.tdays=toData.tdays;
+					tday=range.tdays[0];
+					checkObj.tdays=range.tdays[0];
+				};
+				result=`${fyear+'-'+fmonth+'-'+fday+'至'+tyear+'-'+tmonth+'-'+tday}`;
+				obj={
+					fyear,fmonth,fday,tyear,tmonth,tday
+				}
+				this.checkObj=obj;
+				this.$nextTick(()=>{
+					this.pickVal=arr;
+				})
+				this.$emit("change",{
+					result:result,
+					value:result.split("至"),
+					obj:obj
+				})
+				
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	@import "./w-picker.css";
+</style>

+ 183 - 0
components/w-picker/region-picker.vue

@@ -0,0 +1,183 @@
+<template>
+	<view class="w-picker-view">
+		<picker-view class="d-picker-view" :indicator-style="itemHeight" :value="pickVal" @change="handlerChange">
+			<picker-view-column>
+				<view class="w-picker-item" v-for="(item,index) in range.provinces" :key="index">{{item.label}}</view>
+			</picker-view-column>
+			<picker-view-column>
+				<view class="w-picker-item" v-for="(item,index) in range.citys" :key="index">{{item.label}}</view>
+			</picker-view-column>
+			<picker-view-column v-if="!hideArea">
+				<view class="w-picker-item" v-for="(item,index) in range.areas" :key="index">{{item.label}}</view>
+			</picker-view-column>
+		</picker-view>
+	</view>
+</template>
+
+<script>
+	import areaData from "./areadata/areadata.js"
+	export default {
+		data() {
+			return {
+				pickVal:[],
+				range:{
+					provinces:[],
+					citys:[],
+					areas:[]
+				},
+				checkObj:{}
+			};
+		},
+		props:{
+			itemHeight:{
+				type:String,
+				default:"44px"
+			},
+			value:{
+				type:[Array,String],
+				default:""
+			},
+			defaultType:{
+				type:String,
+				default:"label"
+			},
+			hideArea:{
+				type:Boolean,
+				default:false
+			}
+		},
+		watch:{
+			value(val){
+				this.initData();
+			}
+		},
+		created() {
+			this.initData();
+		},
+		methods:{
+			getData(){
+				//用来处理初始化数据
+				let provinces=areaData;
+				let dVal=[];
+				let value=this.value;
+				let a1=value[0];//默认值省
+				let a2=value[1];//默认值市
+				let a3=value[2];//默认值区、县
+				let province,city,area;
+				let provinceIndex=provinces.findIndex((v)=>{
+					return v[this.defaultType]==a1
+				});
+				provinceIndex=value?(provinceIndex!=-1?provinceIndex:0):0;
+				let citys=provinces[provinceIndex].children;
+				let cityIndex=citys.findIndex((v)=>{
+					return v[this.defaultType]==a2
+				});
+				cityIndex=value?(cityIndex!=-1?cityIndex:0):0;
+				let areas=citys[cityIndex].children;
+				let areaIndex=areas.findIndex((v)=>{
+					return v[this.defaultType]==a3;
+				});
+				areaIndex=value?(areaIndex!=-1?areaIndex:0):0;
+				dVal=this.hideArea?[provinceIndex,cityIndex]:[provinceIndex,cityIndex,areaIndex];
+				province=provinces[provinceIndex];
+				city=citys[cityIndex];
+				area=areas[areaIndex];
+				let obj=this.hideArea?{
+					province,
+					city
+				}:{
+					province,
+					city,
+					area
+				}
+				return this.hideArea?{
+					provinces,
+					citys,
+					dVal,
+					obj
+				}:{
+					provinces,
+					citys,
+					areas,
+					dVal,
+					obj
+				}
+			},
+			initData(){
+				let dataData=this.getData();
+				let provinces=dataData.provinces;
+				let citys=dataData.citys;
+				let areas=this.hideArea?[]:dataData.areas;
+				let obj=dataData.obj;
+				let province=obj.province,city=obj.city,area=this.hideArea?{}:obj.area;
+				let value=this.hideArea?[province.value,city.value]:[province.value,city.value,area.value];
+				let result=this.hideArea?`${province.label+city.label}`:`${province.label+city.label+area.label}`;
+				this.range=this.hideArea?{
+					provinces,
+					citys,
+				}:{
+					provinces,
+					citys,
+					areas
+				};
+				this.checkObj=obj;
+				this.$nextTick(()=>{
+					this.pickVal=dataData.dVal;
+				});
+				this.$emit("change",{
+					result:result,
+					value:value,
+					obj:obj
+				})
+			},
+			handlerChange(e){
+				let arr=[...e.detail.value];
+				let provinceIndex=arr[0],cityIndex=arr[1],areaIndex=this.hideArea?0:arr[2];
+				let provinces=areaData;
+				let citys=(provinces[provinceIndex]&&provinces[provinceIndex].children)||provinces[provinces.length-1].children||[];
+				let areas=this.hideArea?[]:((citys[cityIndex]&&citys[cityIndex].children)||citys[citys.length-1].children||[]);
+				let province=provinces[provinceIndex]||provinces[provinces.length-1],
+				city=citys[cityIndex]||[citys.length-1],
+				area=this.hideArea?{}:(areas[areaIndex]||[areas.length-1]);
+				let obj=this.hideArea?{
+					province,
+					city
+				}:{
+					province,
+					city,
+					area
+				}
+				if(this.checkObj.province.label!=province.label){
+					//当省更新的时候需要刷新市、区县的数据;
+					this.range.citys=citys;
+					if(!this.hideArea){
+						this.range.areas=areas;
+					}
+					
+				}
+				if(this.checkObj.city.label!=city.label){
+					//当市更新的时候需要刷新区县的数据;
+					if(!this.hideArea){
+						this.range.areas=areas;
+					}
+				}
+				this.checkObj=obj;
+				this.$nextTick(()=>{
+					this.pickVal=arr;
+				})
+				let result=this.hideArea?`${province.label+city.label}`:`${province.label+city.label+area.label}`;
+				let value=this.hideArea?[province.value,city.value]:[province.value,city.value,area.value];
+				this.$emit("change",{
+					result:result,
+					value:value,
+					obj:obj
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	@import "./w-picker.css";	
+</style>
+

+ 129 - 0
components/w-picker/selector-picker.vue

@@ -0,0 +1,129 @@
+<template>
+	<view class="w-picker-view">
+		<picker-view class="d-picker-view" :indicator-style="itemHeight" :value="pickVal" @change="handlerChange">
+			<picker-view-column>
+				<view class="w-picker-item" v-for="(item,index) in range" :key="index">{{item[nodeKey]}}</view>
+			</picker-view-column>
+		</picker-view>
+	</view>
+</template>
+
+<script>
+	export default {
+		props:{
+			itemHeight:{
+				type:String,
+				default:"44px"
+			},
+			options:{
+				type:[Array,Object],
+				default(){
+					return []
+				}
+			},
+			value:{
+				type:String,
+				default:""
+			},
+			defaultType:{
+				type:String,
+				default:"label"
+			},
+			defaultProps:{
+				type:Object,
+				default(){
+					return{
+						label:"label",
+						value:"value"
+					}
+				}
+			}
+		},
+		data() {
+			return {
+				pickVal:[]
+			};
+		},
+		computed:{
+			nodeKey(){
+				return this.defaultProps.label;
+			},
+			nodeValue(){
+				return this.defaultProps.value;
+			},
+			range(){
+				return this.options
+			}
+		},
+		watch:{
+			value(val){
+				if(this.options.length!=0){
+					this.initData();
+				}
+			},
+			options(val){
+				this.initData();
+			}
+		},
+		created() {
+			if(this.options.length!=0){
+				this.initData();
+			}
+		},
+		methods:{
+			initData(){
+				let dVal=this.value||"";
+				let data=this.range;
+				let pickVal=[0];
+				let cur=null;
+				let label="";
+				let value,idx;
+				if(this.defaultType==this.nodeValue){
+					value=data.find((v)=>v[this.nodeValue]==dVal);
+					idx=data.findIndex((v)=>v[this.nodeValue]==dVal);
+				}else{
+					value=data.find((v)=>v[this.nodeKey]==dVal);
+					idx=data.findIndex((v)=>v[this.nodeKey]==dVal);
+				}
+				pickVal=[idx!=-1?idx:0];
+				this.$nextTick(()=>{
+					this.pickVal=pickVal;
+				});
+				if(this.defaultType==this.nodeValue){
+					this.$emit("change",{
+						result:value?value[this.nodeKey]:data[0][this.nodeKey],
+						value:dVal||data[0][this.nodeKey],
+						obj:value?value:data[0]
+					})
+				}else{
+					this.$emit("change",{
+						result:dVal||data[0][this.nodeKey],
+						value:value?value[this.nodeValue]:data[0][this.nodeValue],
+						obj:value?value:data[0]
+					})
+				}
+				
+			},
+			handlerChange(e){
+				let arr=[...e.detail.value];
+				let pickVal=[arr[0]||0];
+				let data=this.range;
+				let cur=data[arr[0]];
+				let label="";
+				let value="";
+				this.$nextTick(()=>{
+					this.pickVal=pickVal;
+				});
+				this.$emit("change",{
+					result:cur[this.nodeKey],
+					value:cur[this.nodeValue],
+					obj:cur
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	@import "./w-picker.css";
+</style>

+ 250 - 0
components/w-picker/shortterm-picker.vue

@@ -0,0 +1,250 @@
+<template>
+	<view class="w-picker-view">
+		<picker-view class="d-picker-view" :indicator-style="itemHeight" :value="pickVal" @change="handlerChange">
+			<picker-view-column>
+				<view class="w-picker-item" v-for="(item,index) in range.dates" :key="index">{{item.label}}</view>
+			</picker-view-column>
+			<picker-view-column>
+				<view class="w-picker-item" v-for="(item,index) in range.hours" :key="index">{{item.label}}时</view>
+			</picker-view-column>
+			<picker-view-column>
+				<view class="w-picker-item" v-for="(item,index) in range.minutes" :key="index">{{item.label}}分</view>
+			</picker-view-column>
+		</picker-view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				pickVal:[],
+				range:{},
+				checkObj:{}
+			};
+		},
+		props:{
+			itemHeight:{
+				type:String,
+				default:"44px"
+			},
+			value:{
+				type:[String,Array,Number],
+				default:""
+			},
+			current:{//是否默认选中当前日期
+				type:Boolean,
+				default:false
+			},
+			expand:{
+				type:[Number,String],
+				default:30
+			}
+		},
+		watch:{
+			value(val){
+				this.initData();
+			}
+		},
+		created() {
+			this.initData();
+		},
+		methods:{
+			formatNum(n){
+				return (Number(n)<10?'0'+Number(n):Number(n)+'');
+			},
+			checkValue(value){
+				let strReg=/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}(:\d{2})?$/,example="2019-12-12 18:05:00或者2019-12-12 18:05";
+				if(!strReg.test(value)){
+					console.log(new Error("请传入与mode、fields匹配的value值,例value="+example+""))
+				}
+				return strReg.test(value);
+			},
+			resetData(year,month,day){
+				let curDate=this.getCurrenDate();
+				let curFlag=this.current;
+				let curYear=curDate.curYear;
+				let curMonth=curDate.curMonth;
+				let curDay=curDate.curDay;
+				let curHour=curDate.curHour;
+				let months=[],days=[],sections=[];
+				let disabledAfter=this.disabledAfter;
+				let monthsLen=disabledAfter?(year*1<curYear?12:curMonth):12;
+				let totalDays=new Date(year,month,0).getDate();//计算当月有几天;
+				for(let month=1;month<=monthsLen;month++){
+					months.push(this.formatNum(month));
+				};
+				for(let day=1;day<=daysLen;day++){
+					days.push(this.formatNum(day));
+				}
+				return{
+					months,
+					days,
+					sections
+				}
+			},
+			getData(dVal){
+				//用来处理初始化数据
+				let curFlag=this.current;
+				let disabledAfter=this.disabledAfter;
+				let dates=[],hours=[],minutes=[];
+				let curDate=new Date();
+				let curYear=curDate.getFullYear();
+				let curMonth=curDate.getMonth();
+				let curDay=curDate.getDate();
+				let aDate=new Date(curYear,curMonth,curDay);
+				for(let i=0;i<this.expand*1;i++){
+					aDate=new Date(curYear,curMonth,curDay+i);
+					let year=aDate.getFullYear();
+					let month=aDate.getMonth()+1;
+					let day=aDate.getDate();
+					let label=year+"-"+this.formatNum(month)+"-"+this.formatNum(day);
+					switch(i){
+						case 0:
+							label="今天";
+							break;
+						case 1:
+							label="明天";
+							break;
+						case 2:
+							label="后天";
+							break
+					}
+					dates.push({
+						label:label,
+						value:year+"-"+this.formatNum(month)+"-"+this.formatNum(day)
+					})
+				};
+				for(let i=0;i<24;i++){
+					hours.push({
+						label:this.formatNum(i),
+						value:this.formatNum(i)
+					})
+				}
+				for(let i=0;i<60;i++){
+					minutes.push({
+						label:this.formatNum(i),
+						value:this.formatNum(i)
+					})
+				}
+				return {
+					dates,
+					hours,
+					minutes
+				}
+			},
+			getDefaultDate(){
+				let value=this.value;
+				let reg=/-/g;
+				let defaultDate=value?new Date(value.replace(reg,"/")):new Date();
+				let defaultYear=defaultDate.getFullYear();
+				let defaultMonth=defaultDate.getMonth()+1;
+				let defaultDay=defaultDate.getDate();
+				let defaultDays=new Date(defaultYear,defaultMonth,0).getDate()*1;
+				return{
+					defaultDate,
+					defaultYear,
+					defaultMonth,
+					defaultDay,
+					defaultDays
+				}
+			},
+			getDval(){
+				let value=this.value;
+				let dVal=null;
+				let aDate=new Date();
+				let year=this.formatNum(aDate.getFullYear());
+				let month=this.formatNum(aDate.getMonth()+1);
+				let day=this.formatNum(aDate.getDate());
+				let date=this.formatNum(year)+"-"+this.formatNum(month)+"-"+this.formatNum(day);
+				let hour=aDate.getHours();
+				let minute=aDate.getMinutes();
+				if(value){
+					let flag=this.checkValue(value);
+					if(!flag){
+						dVal=[date,hour,minute]
+					}else{
+						let v=value.split(" ");
+						dVal=[v[0],...v[1].split(":")];
+					}
+				}else{
+					dVal=[date,hour,minute]
+				}
+				return dVal;
+			},
+			initData(){
+				let startDate,endDate,startYear,endYear,startMonth,endMonth,startDay,endDay;
+				let dates=[],hours=[],minutes=[];
+				let dVal=[],pickVal=[];
+				let value=this.value;
+				let reg=/-/g;
+				let range={};
+				let result="",full="",date,hour,minute,obj={};
+				let defaultDate=this.getDefaultDate();
+				let defaultYear=defaultDate.defaultYear;
+				let defaultMonth=defaultDate.defaultMonth;
+				let defaultDay=defaultDate.defaultDay;
+				let defaultDays=defaultDate.defaultDays;
+				let curFlag=this.current;
+				let disabledAfter=this.disabledAfter;
+				let dateData=[];
+				dVal=this.getDval();
+				dateData=this.getData(dVal);
+				dates=dateData.dates;
+				hours=dateData.hours;
+				minutes=dateData.minutes;
+				pickVal=[
+					dates.findIndex(n => n.value == dVal[0])!=-1?dates.findIndex(n => n.value == dVal[0]):0,
+					hours.findIndex(n => n.value == dVal[1])!=-1?hours.findIndex(n => n.value == dVal[1]):0,
+					minutes.findIndex(n => n.value == dVal[2])!=-1?minutes.findIndex(n => n.value == dVal[2]):0,
+				];
+				range={dates,hours,minutes};
+				date=dVal[0]?dVal[0]:dates[0].label;
+				hour=dVal[1]?dVal[1]:hours[0].label;
+				minute=dVal[2]?dVal[2]:minutes[0].label;
+				result=full=`${date+' '+hour+':'+minute}`;
+				obj={
+					date,
+					hour,
+					minute
+				}
+				this.range=range;
+				this.checkObj=obj;
+				this.$nextTick(()=>{
+					this.pickVal=pickVal;
+				});
+				this.$emit("change",{
+					result:result,
+					value:full,
+					obj:obj
+				})
+			},
+			handlerChange(e){
+				let arr=[...e.detail.value];
+				let data=this.range;
+				let date="",hour="",minute="";
+				let result="",full="",obj={};
+				let disabledAfter=this.disabledAfter;
+				date=(arr[0]||arr[0]==0)?data.dates[arr[0]]||data.dates[data.dates.length-1]:"";
+				hour=(arr[1]||arr[1]==0)?data.hours[arr[1]]||data.hours[data.hours.length-1]:"";
+				minute=(arr[2]||arr[2]==0)?data.minutes[arr[2]]||data.minutes[data.minutes.length-1]:"";
+				result=full=`${date.label+' '+hour.label+':'+minute.label+':00'}`;
+				obj={
+					date,
+					hour,
+					minute
+				}
+				this.checkObj=obj;
+				this.$emit("change",{
+					result:result,
+					value:full,
+					obj:obj
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	@import "./w-picker.css";
+</style>

+ 218 - 0
components/w-picker/time-picker.vue

@@ -0,0 +1,218 @@
+<template>
+	<view class="w-picker-view">
+		<picker-view class="d-picker-view" :indicator-style="itemHeight" :value="pickVal" @change="handlerChange">
+			<picker-view-column>
+				<view class="w-picker-item" v-for="(item,index) in range.hours" :key="index">{{item}}时</view>
+			</picker-view-column>
+			<picker-view-column>
+				<view class="w-picker-item" v-for="(item,index) in range.minutes" :key="index">{{item}}分</view>
+			</picker-view-column>
+			<picker-view-column v-if="second">
+				<view class="w-picker-item" v-for="(item,index) in range.seconds" :key="index">{{item}}秒</view>
+			</picker-view-column>
+		</picker-view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				pickVal:[],
+				range:{},
+				checkObj:{}
+			};
+		},
+		props:{
+			itemHeight:{
+				type:String,
+				default:"44px"
+			},
+			value:{
+				type:[String,Array,Number],
+				default:""
+			},
+			current:{//是否默认选中当前日期
+				type:Boolean,
+				default:false
+			},
+			second:{
+				type:Boolean,
+				default:true
+			}
+		},
+		watch:{
+			value(val){
+				this.initData();
+			}
+		},
+		created() {
+			this.initData();
+		},
+		methods:{
+			formatNum(n){
+				return (Number(n)<10?'0'+Number(n):Number(n)+'');
+			},
+			checkValue(value){
+				let strReg=/^\d{2}:\d{2}:\d{2}$/,example="18:00:05";
+				if(!strReg.test(value)){
+					console.log(new Error("请传入与mode、fields匹配的value值,例value="+example+""))
+				}
+				return strReg.test(value);
+			},
+			resetData(year,month,day,hour,minute){
+				let curDate=this.getCurrenDate();
+				let curFlag=this.current;
+				let curHour=curDate.curHour;
+				let curMinute=curDate.curMinute;
+				let curSecond=curDate.curSecond;
+				for(let hour=0;hour<24;hour++){
+					hours.push(this.formatNum(hour));
+				}
+				for(let minute=0;minute<60;minute++){
+					minutes.push(this.formatNum(minute));
+				}
+				for(let second=0;second<60;second++){
+					seconds.push(this.formatNum(second));
+				}
+				return{
+					hours,
+					minutes,
+					seconds
+				}
+			},
+			getData(curDate){
+				//用来处理初始化数据
+				let hours=[],minutes=[],seconds=[];
+				let curFlag=this.current;
+				let disabledAfter=this.disabledAfter;
+				let fields=this.fields;
+				let curHour=curDate.curHour;
+				let curMinute=curDate.curMinute;
+				let curSecond=curDate.curSecond;
+				for(let hour=0;hour<24;hour++){
+					hours.push(this.formatNum(hour));
+				}
+				for(let minute=0;minute<60;minute++){
+					minutes.push(this.formatNum(minute));
+				}
+				for(let second=0;second<60;second++){
+					seconds.push(this.formatNum(second));
+				}
+				return this.second?{
+					hours,
+					minutes,
+					seconds
+				}:{
+					hours,
+					minutes
+				}
+			},
+			getCurrenDate(){
+				let curDate=new Date();
+				let curHour=curDate.getHours();
+				let curMinute=curDate.getMinutes();
+				let curSecond=curDate.getSeconds();
+				return this.second?{
+					curHour,
+					curMinute,
+					curSecond
+				}:{
+					curHour,
+					curMinute,
+				}
+			},
+			getDval(){
+				let value=this.value;
+				let fields=this.fields;
+				let dVal=null;
+				let aDate=new Date();
+				let hour=this.formatNum(aDate.getHours());
+				let minute=this.formatNum(aDate.getMinutes());
+				let second=this.formatNum(aDate.getSeconds());
+				if(value){
+					let flag=this.checkValue(value);
+					if(!flag){
+						dVal=[hour,minute,second]
+					}else{
+						dVal=value?value.split(":"):[];
+					}
+				}else{
+					dVal=this.second?[hour,minute,second]:[hour,minute]
+				}
+				return dVal;
+			},
+			initData(){
+				let curDate=this.getCurrenDate();
+				let dateData=this.getData(curDate);
+				let pickVal=[],obj={},full="",result="",hour="",minute="",second="";
+				let dVal=this.getDval();
+				let curFlag=this.current;
+				let disabledAfter=this.disabledAfter;
+				let hours=dateData.hours;
+				let minutes=dateData.minutes;
+				let seconds=dateData.seconds;
+				let defaultArr=this.second?[
+					dVal[0]&&hours.indexOf(dVal[0])!=-1?hours.indexOf(dVal[0]):0,
+					dVal[1]&&minutes.indexOf(dVal[1])!=-1?minutes.indexOf(dVal[1]):0,
+					dVal[2]&&seconds.indexOf(dVal[2])!=-1?seconds.indexOf(dVal[2]):0
+				]:[
+					dVal[0]&&hours.indexOf(dVal[0])!=-1?hours.indexOf(dVal[0]):0,
+					dVal[1]&&minutes.indexOf(dVal[1])!=-1?minutes.indexOf(dVal[1]):0
+				];
+				pickVal=disabledAfter?defaultArr:(curFlag?(this.second?[
+					hours.indexOf(this.formatNum(curDate.curHour)),
+					minutes.indexOf(this.formatNum(curDate.curMinute)),
+					seconds.indexOf(this.formatNum(curDate.curSecond)),
+				]:[
+					hours.indexOf(this.formatNum(curDate.curHour)),
+					minutes.indexOf(this.formatNum(curDate.curMinute))
+				]):defaultArr);
+				this.range=dateData;
+				this.checkObj=obj;
+				hour=dVal[0]?dVal[0]:hours[0];
+				minute=dVal[1]?dVal[1]:minutes[0];
+				if(this.second)second=dVal[2]?dVal[0]:seconds[0];
+				result=this.second?`${hour+':'+minute+':'+second}`:`${hour+':'+minute}`;
+				full=this.second?`${hour+':'+minute+':'+second}`:`${hour+':'+minute+':00'}`;
+				this.$nextTick(()=>{
+					this.pickVal=pickVal;
+				});
+				this.$emit("change",{
+					result:result,
+					value:full,
+					obj:obj
+				})
+			},
+			handlerChange(e){
+				let arr=[...e.detail.value];
+				let data=this.range;
+				let hour="",minute="",second="",result="",full="",obj={};
+				hour=(arr[0]||arr[0]==0)?data.hours[arr[0]]||data.hours[data.hours.length-1]:"";
+				minute=(arr[1]||arr[1]==0)?data.minutes[arr[1]]||data.minutes[data.minutes.length-1]:"";
+				if(this.second)second=(arr[2]||arr[2]==0)?data.seconds[arr[2]]||data.seconds[data.seconds.length-1]:"";
+				obj=this.second?{
+					hour,
+					minute,
+					second
+				}:{
+					hour,
+					minute
+				};
+				this.checkObj=obj;
+				result=this.second?`${hour+':'+minute+':'+second}`:`${hour+':'+minute}`;
+				full=this.second?`${hour+':'+minute+':'+second}`:`${hour+':'+minute+':00'}`;
+				this.$emit("change",{
+					result:result,
+					value:full,
+					obj:obj
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	@import "./w-picker.css";	
+</style>
+

+ 26 - 0
components/w-picker/w-picker.css

@@ -0,0 +1,26 @@
+.w-picker-flex2{
+	flex:2;
+}
+.w-picker-flex1{
+	flex:1;
+}
+.w-picker-view {
+	width: 100%;
+	height: 476upx;
+	overflow: hidden;
+	background-color: rgba(255, 255, 255, 1);
+	z-index: 666;
+}
+.d-picker-view{
+	height: 100%;
+}
+
+.w-picker-item {
+  text-align: center;
+  width: 100%;
+  height: 88upx;
+  line-height: 88upx;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+  font-size: 30upx;
+}

+ 340 - 0
components/w-picker/w-picker.vue

@@ -0,0 +1,340 @@
+<template name="w-picker">
+	<view class="w-picker" :key="createKey" :data-key="createKey">
+		<view class="mask" :class="{'visible':visible}" @tap="onCancel" @touchmove.stop.prevent catchtouchmove="true"></view>
+		<view class="w-picker-cnt" :class="{'visible':visible}">
+			<view class="w-picker-header"  @touchmove.stop.prevent catchtouchmove="true">
+				<text @tap.stop.prevent="onCancel">取消</text>
+				<slot></slot>
+				<text :style="{'color':themeColor}" @tap.stop.prevent="pickerConfirm">确定</text>
+			</view>
+			<date-picker 
+				v-if="mode=='date'" 
+				class="w-picker-wrapper"
+				:startYear="startYear"
+				:endYear="endYear"
+				:value="value"
+				:fields="fields"
+				:item-height="itemHeight"
+				:current="current"
+				:disabled-after="disabledAfter"
+				@change="handlerChange"
+				@touchstart="touchStart" 
+				@touchend="touchEnd">
+			</date-picker>
+			
+			<range-picker
+				v-if="mode=='range'" 
+				class="w-picker-wrapper"
+				:startYear="startYear"
+				:endYear="endYear"
+				:value="value"
+				:item-height="itemHeight"
+				:current="current"
+				@change="handlerChange"
+				@touchstart="touchStart" 
+				@touchend="touchEnd">
+			</range-picker>
+			
+			<half-picker
+				v-if="mode=='half'" 
+				class="w-picker-wrapper"
+				:startYear="startYear"
+				:endYear="endYear"
+				:value="value"
+				:item-height="itemHeight"
+				:current="current"
+				:disabled-after="disabledAfter"
+				@change="handlerChange"
+				@touchstart="touchStart" 
+				@touchend="touchEnd">
+			</half-picker>
+			
+			<shortterm-picker
+				v-if="mode=='shortTerm'" 
+				class="w-picker-wrapper"
+				:startYear="startYear"
+				:endYear="endYear"
+				:value="value"
+				:item-height="itemHeight"
+				:current="current"
+				expand="60"
+				:disabled-after="disabledAfter"
+				@change="handlerChange"
+				@touchstart="touchStart" 
+				@touchend="touchEnd">
+			</shortterm-picker>
+			
+			<time-picker
+				v-if="mode=='time'"
+				class="w-picker-wrapper"
+				:value="value"
+				:item-height="itemHeight"
+				:current="current"
+				:disabled-after="disabledAfter"
+				:second="second"
+				@change="handlerChange"
+				@touchstart="touchStart" 
+				@touchend="touchEnd">
+			</time-picker>
+			
+			<selector-picker
+				v-if="mode=='selector'"
+				class="w-picker-wrapper"
+				:value="value"
+				:item-height="itemHeight"
+				:options="options"
+				:default-type="defaultType"
+				:default-props="defaultProps"
+				@change="handlerChange"
+				@touchstart="touchStart" 
+				@touchend="touchEnd">
+			</selector-picker>
+			
+			<region-picker
+				v-if="mode=='region'"
+				class="w-picker-wrapper"
+				:value="value"
+				:hide-area="hideArea"
+				:default-type="defaultType"
+				:item-height="itemHeight"
+				@change="handlerChange"
+				@touchstart="touchStart" 
+				@touchend="touchEnd">
+			</region-picker>
+			
+			<linkage-picker
+				v-if="mode=='linkage'"
+				class="w-picker-wrapper"
+				:value="value"
+				:options="options"
+				:level="level"
+				:default-type="defaultType"
+				:default-props="defaultProps"
+				:item-height="itemHeight"
+				@change="handlerChange"
+				@touchstart="touchStart" 
+				@touchend="touchEnd">
+			</linkage-picker>
+		</view>
+	</view>
+</template>
+
+<script>
+	import datePicker from "./date-picker.vue"
+	import rangePicker from "./range-picker.vue"
+	import halfPicker from "./half-picker.vue"
+	import shorttermPicker from "./shortterm-picker.vue"
+	import timePicker from "./time-picker.vue"
+	import selectorPicker from "./selector-picker.vue"
+	import regionPicker from "./region-picker.vue"
+	import linkagePicker from "./linkage-picker.vue"
+	export default {
+		name:"w-picker",
+		components:{
+			datePicker,
+			rangePicker,
+			halfPicker,
+			timePicker,
+			selectorPicker,
+			shorttermPicker,
+			regionPicker,
+			linkagePicker
+		},
+		props:{
+			mode:{
+				type:String,
+				default:"date"
+			},
+			value:{//默认值
+				type:[String,Array,Number],
+				default:""
+			},
+			current:{//是否默认显示当前时间,如果是,传的默认值将失效
+				type:Boolean,
+				default:false
+			},
+			themeColor:{//确认按钮主题颜色
+				type:String,
+				default:"#f5a200"
+			},
+			fields:{//日期颗粒度:year、month、day、hour、minute、second
+				type:String,
+				default:"date"
+			},
+			disabledAfter:{//是否禁用当前之后的日期
+				type:Boolean,
+				default:false
+			},
+			second:{//time-picker是否显示秒
+				type:Boolean,
+				default:true
+			},
+			options:{//selector,region数据源
+				type:[Array,Object],
+				default(){
+					return []
+				}
+			},
+			defaultProps:{//selector,linkagle字段转换配置
+				type:Object,
+				default(){
+					return{
+						label:"label",
+						value:"value",
+						children:"children"
+					}
+				}
+			},
+			defaultType:{
+				type:String,
+				default:"label"
+			},
+			hideArea:{//mode=region时,是否隐藏区县列
+				type:Boolean,
+				default:false
+			},
+			level:{
+				//多级联动层级,表示几级联动,区间2-4;
+				type:[Number,String],
+				default:2
+			},
+			timeout:{//是否开启点击延迟,当快速滚动 还没有滚动完毕点击关闭时得到的值是不准确的
+				type:Boolean,
+				default:false
+			},
+			expand:{//mode=shortterm 默认往后拓展天数
+				type:[Number,String],
+				default:30
+			},
+			startYear:{
+				type:[String,Number],
+				default:1970
+			},
+			endYear:{
+				type:[String,Number],
+				default:new Date().getFullYear()
+			},
+			visible:{
+				type:Boolean,
+				default:false
+			}
+		},
+		created() {
+			this.createKey=Math.random()*1000;
+		},
+		data() {
+			return {
+				itemHeight:`height: ${uni.upx2px(88)}px;`,
+				result:{},
+				confirmFlag:true
+			};
+		},
+		methods:{
+			touchStart(){
+				if(this.timeout){
+					this.confirmFlag=false;
+				}
+			},
+			touchEnd(){
+				if(this.timeout){
+					setTimeout(()=>{
+						this.confirmFlag=true;
+					},500)
+				}
+			},
+			handlerChange(res){
+				let _this=this;
+				this.result={...res};
+			},
+			show(){
+				this.$emit("update:visible",true);
+			},
+			hide(){
+				this.$emit("update:visible",false);
+			},
+			onCancel(res){
+				this.$emit("update:visible",false);
+				this.$emit("cancel");
+			},
+			pickerConfirm(){
+				if(!this.confirmFlag){
+					return;
+				};
+				this.$emit("confirm",this.result);
+				this.$emit("update:visible",false);
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	.w-picker-item {
+	  text-align: center;
+	  width: 100%;
+	  height: 88upx;
+	  line-height: 88upx;
+	  text-overflow: ellipsis;
+	  white-space: nowrap;
+	  font-size: 30upx;
+	}
+	.w-picker{
+		z-index: 888;
+		.mask {
+		  position: fixed;
+		  z-index: 1000;
+		  top: 0;
+		  right: 0;
+		  left: 0;
+		  bottom: 0;
+		  background: rgba(0, 0, 0, 0.6);
+		  visibility: hidden;
+		  opacity: 0;
+		  transition: all 0.3s ease;
+		}
+		.mask.visible{
+			visibility: visible;
+			opacity: 1;
+		}
+		.w-picker-cnt {
+		  position: fixed;
+		  bottom: 0;
+		  left: 0;
+		  width: 100%;
+		  transition: all 0.3s ease;
+		  transform: translateY(100%);
+		  z-index: 3000;
+		  background-color: #fff;
+		}
+		.w-picker-cnt.visible {
+		  transform: translateY(0);
+		}
+		.w-picker-header{
+		  display: flex;
+		  align-items: center;
+		  padding: 0 30upx;
+		  height: 88upx;
+		  background-color: #fff;
+		  position: relative;
+		  text-align: center;
+		  font-size: 32upx;
+		  justify-content: space-between;
+		  border-bottom: solid 1px #eee;
+		  .w-picker-btn{
+		  	font-size: 30upx;
+		  }
+		}
+		
+		.w-picker-hd:after {
+		  content: ' ';
+		  position: absolute;
+		  left: 0;
+		  bottom: 0;
+		  right: 0;
+		  height: 1px;
+		  border-bottom: 1px solid #e5e5e5;
+		  color: #e5e5e5;
+		  transform-origin: 0 100%;
+		  transform: scaleY(0.5);
+		}
+	}
+</style>

+ 86 - 0
main.js

@@ -0,0 +1,86 @@
+import Vue from 'vue'
+import App from './App'
+import store from './store'
+import MinCache from'./common/util/MinCache.js'
+import tip from'./common/util/tip.js'
+import configService from'./common/service/config.service.js'
+import router from './common/router'
+import {RouterMount} from './plugin/uni-simple-router/index.js'
+
+// 注册缓存器
+Vue.use(MinCache,{timeout: 6})
+
+// store
+Vue.prototype.$store=store;
+// tip
+Vue.prototype.$tip=tip;
+// config
+Vue.prototype.$config=configService;
+
+// request请求
+import { http } from '@/common/service/service.js' 
+Vue.prototype.$http = http
+
+import home from './pages/home/home.vue'
+Vue.component('home',home)
+
+import test1 from './pages/test1/test1.vue'
+Vue.component('test1',test1)
+
+
+import people from './pages/user/people.vue'
+Vue.component('people',people)
+
+// 自定义组件
+import mySelect from './components/my-componets/my-select.vue'
+Vue.component('mySelect',mySelect)
+
+import myImageUpload from './components/my-componets/my-image-upload.vue'
+Vue.component('myImageUpload',myImageUpload)
+
+
+import myPage from './components/my-componets/my-page.vue'
+Vue.component('myPage',myPage)
+
+
+
+import basics from './pages/basics/home.vue'
+Vue.component('basics',basics)
+
+import components from './pages/component/home.vue'
+Vue.component('components',components)
+
+import plugin from './pages/plugin/home.vue'
+Vue.component('plugin',plugin)
+
+import cuCustom from './plugin/colorui/components/cu-custom.vue'
+Vue.component('cu-custom',cuCustom)
+
+// import VConsole from './js_sdk/vconsole.min'
+
+//   var vConsole = new VConsole();
+
+Vue.config.productionTip = false
+
+App.mpType = 'app'
+
+const app = new Vue({
+	store,
+	MinCache,
+    ...App
+})
+//v1.3.5起 H5端 你应该去除原有的app.$mount();使用路由自带的渲染方式
+// #ifdef H5
+	RouterMount(app,'#app');
+// #endif
+
+// #ifndef H5
+	app.$mount(); //为了兼容小程序及app端必须这样写才有效果
+// #endif
+
+
+
+ 
+
+
+

+ 82 - 0
manifest.json

@@ -0,0 +1,82 @@
+{
+    "name" : "智能通风",
+    "appid" : "__UNI__8193760",
+    "description" : "",
+    "versionName" : "2.2.22",
+    "versionCode" : 233,
+    "transformPx" : false,
+    "app-plus" : {
+        /* 5+App特有相关 */
+        "modules" : {
+            "Maps" : {}
+        },
+        /* 模块配置 */
+        "distribute" : {
+            /* 应用发布信息 */
+            "android" : {
+                /* android打包配置 */
+                "permissions" : [
+                    "<uses-feature android:name=\"android.hardware.camera\"/>",
+                    "<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_COARSE_LOCATION\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.CALL_PHONE\"/>",
+                    "<uses-permission android:name=\"android.permission.CAMERA\"/>",
+                    "<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
+                    "<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
+                    "<uses-permission android:name=\"android.permission.INTERNET\"/>",
+                    "<uses-permission android:name=\"android.permission.MODIFY_AUDIO_SETTINGS\"/>",
+                    "<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
+                    "<uses-permission android:name=\"android.permission.READ_CONTACTS\"/>",
+                    "<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
+                    "<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.RECORD_AUDIO\"/>",
+                    "<uses-permission android:name=\"android.permission.VIBRATE\"/>",
+                    "<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
+                    "<uses-permission android:name=\"android.permission.WRITE_CONTACTS\"/>",
+                    "<uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\"/>",
+                    "<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
+                ]
+            },
+            "ios" : {
+                "dSYMs" : false
+            },
+            /* ios打包配置 */
+            "sdkConfigs" : {
+                "ad" : {},
+                "maps" : {
+                    "amap" : {
+                        "appkey_ios" : "87f4d2a4a0c42e0c86cf312c8b8154e8",
+                        "appkey_android" : "87f4d2a4a0c42e0c86cf312c8b8154e8"
+                    }
+                },
+                "push" : {
+                    "unipush" : {}
+                }
+            }
+        }
+    },
+    /* SDK配置 */
+    "quickapp" : {},
+    /* 快应用特有相关 */
+    "mp-weixin" : {
+        "appid" : "wx2ba5c5690b35d173",
+        "setting" : {
+            "urlCheck" : false,
+            "es6" : true
+        },
+        "permission" : {
+            "scope.userLocation" : {
+                "desc" : "有定位功能需要导航定位"
+            }
+        }
+    },
+    "h5" : {
+        "title" : "智能通风",
+        "domain" : "myhjdc.cn"
+    }
+}

BIN
myhjdc.keystore


+ 44 - 0
package-lock.json

@@ -0,0 +1,44 @@
+{
+  "requires": true,
+  "lockfileVersion": 1,
+  "dependencies": {
+    "decode-uri-component": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
+      "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU="
+    },
+    "prettier": {
+      "version": "1.12.1",
+      "resolved": "http://registry.npm.taobao.org/prettier/download/prettier-1.12.1.tgz",
+      "integrity": "sha1-wa0g6APndJ+vkFpAnSNn4Gu+cyU="
+    },
+    "query-string": {
+      "version": "6.12.1",
+      "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.12.1.tgz",
+      "integrity": "sha512-OHj+zzfRMyj3rmo/6G8a5Ifvw3AleL/EbcHMD27YA31Q+cO5lfmQxECkImuNVjcskLcvBRVHNAB3w6udMs1eAA==",
+      "requires": {
+        "decode-uri-component": "^0.2.0",
+        "split-on-first": "^1.0.0",
+        "strict-uri-encode": "^2.0.0"
+      }
+    },
+    "split-on-first": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz",
+      "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw=="
+    },
+    "strict-uri-encode": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz",
+      "integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY="
+    },
+    "uni-simple-router": {
+      "version": "1.5.5",
+      "resolved": "https://registry.npmjs.org/uni-simple-router/-/uni-simple-router-1.5.5.tgz",
+      "integrity": "sha512-VjBnwhvmWYHVNsj2zcPjYBwb9TqG7miR87qLBBLI4gHOnJVYmCyjZK/bj06f9slvTMbWXrze7LJ9/Hi/8DB0ag==",
+      "requires": {
+        "query-string": "^6.12.1"
+      }
+    }
+  }
+}

+ 5 - 0
package.json

@@ -0,0 +1,5 @@
+{
+  "dependencies": {
+    "vuex": "^3.6.2"
+  }
+}

+ 182 - 0
pages.json

@@ -0,0 +1,182 @@
+{
+	"pages": [
+		//pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
+		{
+			"path": "pages/index/index",
+			"style": {}
+		},
+		{
+			"path": "pages/basics/layout",
+			"style": {}
+		},
+		{
+			"path": "pages/basics/background",
+			"style": {}
+		},
+		{
+			"path": "pages/basics/text",
+			"style": {}
+		},
+		{
+			"path": "pages/basics/icon",
+			"style": {}
+		},
+		{
+			"path": "pages/basics/button",
+			"style": {}
+		},
+		{
+			"path": "pages/basics/design",
+			"style": {}
+		},
+		{
+			"path": "pages/basics/tag",
+			"style": {}
+		},
+		{
+			"path": "pages/basics/avatar",
+			"style": {}
+		},
+		{
+			"path": "pages/basics/progress",
+			"style": {}
+		},
+		{
+			"path": "pages/basics/shadow",
+			"style": {}
+		},
+		{
+			"path": "pages/basics/loading",
+			"style": {}
+		},
+		{
+			"path": "pages/component/bar",
+			"style": {}
+		},
+		{
+			"path": "pages/component/nav",
+			"style": {}
+		},
+		{
+			"path": "pages/component/list",
+			"style": {}
+		},
+		{
+			"path": "pages/component/card",
+			"style": {}
+		},
+		{
+			"path": "pages/component/form",
+			"style": {}
+		},
+		{
+			"path": "pages/component/timeline",
+			"style": {}
+		},
+		{
+			"path": "pages/component/chat",
+			"style": {}
+		},
+		{
+			"path": "pages/component/swiper",
+			"style": {}
+		},
+		{
+			"path": "pages/component/modal",
+			"style": {}
+		},
+		{
+			"path": "pages/component/steps",
+			"style": {}
+		}, 
+		{
+			"path": "pages/plugin/indexes",
+			"style": {}
+		},
+		{
+			"path": "pages/plugin/animation",
+			"style": {}
+		}, 
+		{
+			"path": "pages/plugin/drawer",
+			"style": {}
+		}, 
+		{
+			"path": "pages/plugin/verticalnav",
+			"style": {}
+		}, 
+		{
+			"path": "pages/home/home",
+			"style": {}
+		}, 
+		{
+			"path": "pages/login/login",
+			"style": {}
+		}, 
+		{
+			"path": "pages/user/userexit",
+			"style": {}
+		}, 
+		{
+			"path": "pages/user/userdetail",
+			"style": {}
+		},
+		{
+			"path": "pages/user/useredit",
+			"style": {}
+		},
+		{
+			"path": "pages/user/people",
+			"style": {}
+		},
+		{
+			"path": "pages/common/exit",
+			"style": {}
+		},
+		{
+			"path": "pages/common/success",
+			"style": {}
+		},
+		{
+			"path": "pages/addressbook/address-book",
+			"style": {}
+		},
+		{
+			"path": "pages/addressbook/level-address-book",
+			"style": {}
+		},
+		{
+			"path": "pages/addressbook/member",
+			"style": {}
+		},
+		{
+			"path": "pages/addressbook/address-detail",
+			"style": {}
+		},
+		{
+			"path": "pages/test1/test1",
+			"style": {}
+		}
+    ],
+	"globalStyle": {
+		"mp-alipay": {
+			/* 支付宝小程序特有相关 */
+			"transparentTitle": "always",
+			"allowsBounceVertical": "NO"
+		},
+		"navigationBarBackgroundColor": "#0081ff",
+		"navigationBarTitleText": "JEECG BOOT",
+		"navigationStyle": "custom",
+		"navigationBarTextStyle": "white"
+	},
+	"usingComponts": true,
+	"condition": { //模式配置,仅开发期间生效
+		"current": 0, //当前激活的模式(list 的索引项)
+		"list": [{
+			"name": "表单", //模式名称
+			"path": "pages/index/index", //启动页面
+			"query": "" //启动参数
+		}]
+	}
+
+}

+ 28 - 0
pages/about/about.vue

@@ -0,0 +1,28 @@
+<template>
+	<view>
+		<view>
+			<scroll-view>
+				<!-- 标题 -->
+				<!-- 常用服务 -->
+			</scroll-view>
+			<view class="cu-tabbar-height margin-top"></view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				
+			}
+		},
+		methods: {
+			
+		}
+	}
+</script>
+
+<style lang="scss">
+
+</style>

+ 251 - 0
pages/addressbook/address-book.vue

@@ -0,0 +1,251 @@
+<template>
+	
+	<view>
+		<cu-custom :bgColor="NavBarColor" :isBack="true" backRouterName="index">
+			<block slot="backText">返回</block>
+			<block slot="content">通讯录</block>
+		</cu-custom>
+		<view class="cu-bar bg-white search fixed" :style="[{top:CustomBar + 'px'}]">
+			<view class="search-form round">
+				<text class="cuIcon-search"></text>
+				<input type="text" v-model="keyword" placeholder="输入搜索的关键词" confirm-type="search" @confirm="searchUserByKey"></input>
+			</view>
+			<view class="action">
+				<button class="cu-btn bg-gradual-blue shadow-blur round" @tap="searchUserByKey">搜索</button>
+			</view>
+		</view>
+		<scroll-view scroll-y class="indexes margin-top-xl"  :scroll-into-view="'indexes-'+ listCurID" :style="[{top:'calc('+ CustomBar + 'px - 20px)',height:'calc(100vh - '+ CustomBar + 'px - 70px)'}]"
+		 :scroll-with-animation="true" :enable-back-to-top="true">
+			<block v-for="(item,index) in list" :key="index">
+				<view :class="'indexItem-' + item.name" :id="'indexes-' + item.name" :data-index="item.name">
+					<view class="padding">{{item.name}}</view>
+					<view class="cu-list menu-avatar no-padding" >
+						<view class="cu-item" v-for="(items,sub) in userList" :key="sub" v-if="items.szm==item.name" @tap="toAddressDetail(items)">
+						   <view class="cu-avatar round lg" v-if="items.avatar" :style="[{ backgroundImage:'url(' + items.avatar + ')' }]"></view>
+							<view class="cu-avatar round lg" v-else>{{item.name}}</view>
+							<view class="content">
+								<view class="text-grey">{{items.realname?items.realname:items.username}}</view>
+								<view class="text-gray text-sm">
+									{{items.orgCode}}
+								</view>
+							</view>
+						</view>
+					</view>
+				</view>
+			</block>
+		</scroll-view>
+		<view class="indexBar" :style="[{height:'calc(110vh - ' + CustomBar + 'px - 50px)'}]">
+			<view class="indexBar-box" @touchstart="tStart" @touchend="tEnd" @touchmove.stop="tMove">
+				<view class="indexBar-item" v-for="(item,index) in list" :key="index" :id="index" @touchstart="getCur" @touchend="setCur"> {{item.name}}</view>
+			</view>
+		</view>
+		<!--选择显示-->
+		<view v-show="!hidden" class="indexToast">
+			{{listCur}}
+		</view>
+	</view>
+</template>
+
+<script>
+	import vPinyin from '@/common/util/vue-py.js';
+	import {getFileAccessHttpUrl} from '@/api/api.js';
+	export default {
+		data() {
+			return {
+				StatusBar: this.StatusBar,
+				CustomBar: this.CustomBar,
+				hidden: true,
+				listCurID: '',
+				list: [],
+				userList: [],
+				listCur: '',
+				keyword: '',
+				queryUserByKeyWord:'/sys/user/appQueryUser'
+			};
+		},
+		onLoad() {
+			let list = [{}];
+			for (let i = 0; i < 26; i++) {
+				list[i] = {};
+				list[i].name = String.fromCharCode(65 + i);
+			}
+			this.list = list;
+			this.listCur = list[0];
+			this.loadInfo()
+		},
+		onReady() {
+			let that = this;
+			uni.createSelectorQuery().select('.indexBar-box').boundingClientRect(function(res) {
+				that.boxTop = res.top
+			}).exec();
+			uni.createSelectorQuery().select('.indexes').boundingClientRect(function(res) {
+				that.barTop = res.top
+			}).exec()
+		},
+		methods: {
+			backTap(){
+				this.$Router.replace({name:'index'})
+			},
+			toAddressDetail(item){
+				let parmas = {...item}
+				parmas.departName=parmas.orgCode
+				this.$Router.push({name: 'addressDetail',params:parmas})
+			},
+			searchUserByKey(){
+				this.loadInfo()
+			},
+			loadInfo(){
+				this.$http.get(this.queryUserByKeyWord,{params:{'keyword':this.keyword}}).then(res=>{
+			         if (res.data.success) {
+						console.log("res",res)
+					    let arr=res.data.result;
+						let szuArr=[];
+					    this.userList = arr.map(item => {
+					        let { id,realname,avatar,username,phone,email,post,orgCode} = item
+							let pinYin = username.toUpperCase();
+							if(realname){
+								//TODO 判断汉字的位置
+								if(/.*[\u4e00-\u9fa5]+.*$/.test(realname)){
+									pinYin=vPinyin.chineseToPinYin(realname);
+								}
+							}
+							if(avatar){
+								avatar=getFileAccessHttpUrl(avatar);
+							}
+					        let event = {
+					          id, realname ,avatar,username,phone,email,post,orgCode,
+							  szm:pinYin.substr(0,1)
+					        }
+							szuArr.push(event.szm)
+					        return event
+					      })
+						 
+						  this.list=this.list.filter(item=>szuArr.indexOf(item.name)!=-1)	
+					      //this.list.unshift({name:"#"})
+					}
+				}).catch(err => {
+					console.log(err);
+				});
+				
+			},
+			//获取文字信息
+			getCur(e) {
+				this.hidden = false;
+				this.listCur = this.list[e.target.id].name;
+			},
+			setCur(e) {
+				this.hidden = true;
+				this.listCur = this.listCur
+			},
+			//滑动选择Item
+			tMove(e) {
+				let y = e.touches[0].clientY,
+					offsettop = this.boxTop,
+					that = this;
+				//判断选择区域,只有在选择区才会生效
+				if (y > offsettop) {
+					let num = parseInt((y - offsettop) / 20);
+					this.listCur = that.list[num].name
+				};
+			},
+
+			//触发全部开始选择
+			tStart() {
+				this.hidden = false
+			},
+
+			//触发结束选择
+			tEnd() {
+				this.hidden = true;
+				this.listCurID = this.listCur
+			},
+			indexSelect(e) {
+				let that = this;
+				let barHeight = this.barHeight;
+				let list = this.list;
+				let scrollY = Math.ceil(list.length * e.detail.y / barHeight);
+				for (let i = 0; i < list.length; i++) {
+					if (scrollY < i + 1) {
+						that.listCur = list[i].name;
+						that.movableY = i * 20
+						return false
+					}
+				}
+			}
+		}
+	}
+</script>
+
+<style>
+	page {
+		padding-top: 100upx;
+	}
+
+	.indexes {
+		position: relative;
+	}
+
+	.indexBar {
+		position: fixed;
+		right: 0px;
+		bottom: 0px;
+		padding: 20upx 20upx 20upx 60upx;
+		display: flex;
+		align-items: center;
+	}
+
+	.indexBar .indexBar-box {
+		width: 40upx;
+		height: auto;
+		background: #fff;
+		display: flex;
+		flex-direction: column;
+		box-shadow: 0 0 20upx rgba(0, 0, 0, 0.1);
+		border-radius: 10upx;
+	}
+
+	.indexBar-item {
+		flex: 1;
+		width: 40upx;
+		height: 40upx;
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		font-size: 24upx;
+		color: #888;
+	}
+
+	movable-view.indexBar-item {
+		width: 40upx;
+		height: 40upx;
+		z-index: 9;
+		position: relative;
+	}
+
+	movable-view.indexBar-item::before {
+		content: "";
+		display: block;
+		position: absolute;
+		left: 0;
+		top: 10upx;
+		height: 20upx;
+		width: 4upx;
+		background-color: #f37b1d;
+	}
+
+	.indexToast {
+		position: fixed;
+		top: 0;
+		right: 80upx;
+		bottom: 0;
+		background: rgba(0, 0, 0, 0.5);
+		width: 100upx;
+		height: 100upx;
+		border-radius: 10upx;
+		margin: auto;
+		color: #fff;
+		line-height: 100upx;
+		text-align: center;
+		font-size: 48upx;
+	}
+</style>

+ 122 - 0
pages/addressbook/address-detail.vue

@@ -0,0 +1,122 @@
+<template>
+	<view>
+		<scroll-view :scroll-y="modalName==null" class="page">
+			<cu-custom :bgColor="NavBarColor" :isBack="true">
+				<block slot="backText">返回</block>
+				<block slot="content">个人信息</block>
+			</cu-custom>
+			<view class="cu-list menu-avatar margin-top">
+				<view class="cu-item">
+					<view class="cu-avatar round lg" v-if="infoList.avatar"  :style="[{backgroundImage:'url('+ infoList.avatar +')'}]"></view>
+					<view class="cu-avatar round lg" v-else :style="[{backgroundImage:'url('+ avatar +')'}]"></view>
+					<view class="content">
+						<view class="text-grey text-lg">{{infoList.realname?infoList.realname:infoList.username}}<text class="cuIcon-peoplefill margin-left-sm" :class="infoList.sex=='2'?'text-pink':'text-blue'"></text></view>
+						<view class="text-gray text-sm flex">
+							<view class="text-cut">
+								{{infoList.post}}
+							</view>
+						</view> 
+					</view>
+				</view>	
+			</view>
+			
+			<view class="cu-list menu margin-top">
+				<view class="cu-item animation-slide-bottom" :style="[{animationDelay: '0.2s'}]">
+					<view class="content">
+						<text class="text-grey">手机</text>
+					</view>
+					<view class="action">
+						<text class="text-grey">{{infoList.phone?infoList.phone:phone}}</text>
+					</view>
+				</view>
+				<view class="cu-item animation-slide-bottom" :style="[{animationDelay: '0.3s'}]">
+					<view class="content">
+						<text class="text-grey">邮箱</text>
+					</view>
+					<view class="action">
+						<text class="text-grey">{{infoList.email?infoList.email:email}}</text>
+					</view>
+				</view>
+				<view class="cu-item animation-slide-bottom" :style="[{animationDelay: '0.4s'}]">
+					<view class="content">
+						<text class="text-grey">部门</text>
+					</view>
+					<view class="action">
+						<text class="text-grey">{{infoList.departName?infoList.departName:company}}</text>
+					</view>
+				</view>
+			</view>
+			
+		</scroll-view>
+	</view>
+</template>
+
+<script>
+	import {phone,email,company} from "@/common/util/constants"
+	export default {
+		data() {
+			return {
+				phone,
+				email,
+				company,
+				NavBarColor:this.NavBarColor,
+				avatar:'/static/login4.png',
+				modalName: null,
+				infoList:{
+					realname:'',
+					post:'',
+					avatar:'',
+					phone:null,
+					email:'',
+					department:'',
+					title:''
+				},
+			};
+		},
+		onLoad() {
+			 this.infoList = this.$Route.query;
+			 console.log("this.infoList>>>",this.infoList)
+		},
+		methods: {
+			backRoute() {
+				//通讯录个人信息页面返回问题
+				if(this.infoList.page){
+					this.$Router.push({name:this.infoList.page})
+				}else{
+					let parmas={
+						title:this.$Route.query.departName,
+						orgCode:this.$Route.query.orgCode
+					}
+					this.$Router.push({name: 'member',params:parmas})
+				}
+			},
+		}
+	}
+</script>
+
+<style>
+	.page {
+		height: 100Vh;
+		width: 100vw;
+	}
+
+	.page.show {
+		overflow: hidden;
+	}
+
+	.switch-sex::after {
+		content: "\e716";
+	}
+
+	.switch-sex::before {
+		content: "\e7a9";
+	}
+
+	.switch-music::after {
+		content: "\e66a";
+	}
+
+	.switch-music::before {
+		content: "\e6db";
+	}
+</style>

+ 207 - 0
pages/addressbook/level-address-book.vue

@@ -0,0 +1,207 @@
+<template>
+
+	<view>
+		<view class="cu-bar fixed" :style="style" :class="[NavBarColor]">
+			<view class="cuIcon-back margin-left" @tap="backRoute()"><text class="margin-left-sm">返回</text></view>
+			<view class="content" :style="[{top:StatusBar + 'px'}]">
+				通讯录
+			</view>
+		</view>
+		<!-- 展示标题 -->
+		<view class="bg-gray text-gray padding" :style="[{marginTop:CustomBar+'px'}]">
+			{{ levelTitle }}
+		</view>
+		<!-- 展示部门和用户 -->
+		<view class="cu-list menu sm-border">
+			<view class="cu-item" v-for="(item, index) in comList" :key="item.key" @tap="goMember(item)">
+				<image class="line2-icon" src="/static/folder.png"></image>
+				<view class="content margin-left-sm">
+					<view class="text-grey">{{item.title}}</view>
+				</view>
+			</view>
+			<view class="cu-item" v-for="(item,index) in childrenUserList" :key="index" @click="goMemberInfo(item)">
+				<view class="cu-avatar round lx" :style="[{backgroundImage:'url('+ item.avatar +')'}]"></view>
+				<view class="content margin-left-sm">
+					<view class="text-grey">{{item.realname}}</view>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+import { getFileAccessHttpUrl } from "../../api/api.js"
+export default {
+	name: "level-address-book",
+	data(){
+		return{
+			CustomBar:this.CustomBar,
+			StatusBar:this.StatusBar,
+			NavBarColor:this.NavBarColor,
+			value:"",
+			derpartment:'通讯录',
+			comList:[],
+			childrenUserList:[],
+			metaList:[],
+			depart2Url:'/sys/sysDepart/queryTreeList',
+			queryTreeByKeyWord:'/sys/sysDepart/queryTreeByKeyWord',
+			queryUserByDid:'/sys/user/appQueryByDepartId',
+			// departUrl:'/sys/user/userDepartList',
+			level:0,
+			titleArray:[],
+			parentId:''
+		}
+	},
+	computed:{
+		levelTitle(){
+			if(this.titleArray && this.titleArray.length>0){
+				return  this.titleArray.join(' > ')
+			}
+			console.log("this.titleArray",this.titleArray)
+			return '企业通讯录'
+		},
+		style() {
+			var StatusBar= this.StatusBar;
+			var CustomBar= this.CustomBar;
+			var style = `height:${CustomBar}px;padding-top:${StatusBar}px;`;
+			return style
+		}
+	},
+	onLoad() {
+		console.log("this.CustomBar",this.CustomBar)
+		this.loadList()
+	},
+	methods: {
+		backRoute() {
+			console.log("this.level",this.level)
+			console.log("this.parentId",this.parentId)
+			if(this.level==0){
+				// 返回首页
+				this.$Router.replaceAll({name: 'index'})
+			}else{
+				//正常列表的回退 只要找parentId的parent的data
+				this.getCyclePnode(this.metaList,'');
+				//还需要设置 title
+				this.titleArray.pop();
+				this.level--;
+			}
+		},
+		onRefresh(){
+			this.comList=[]
+			this.childrenUserList=[]
+			this.titleArray=[]
+			this.loadList()
+		},
+		loadList() {
+			console.log("loadList==>$Route",this.$Route)
+			if(this.$Route.query.level){
+				let params=this.$Route.query
+				this.level=params.level;
+				this.comList=params.comList;
+				this.metaList=params.metaList;
+				this.childrenUserList=params.childrenUserList;
+				this.derpartment=params.derpartment;
+				this.parentId=params.parentId;
+				this.titleArray=params.titleArray;
+				return;
+			}
+
+			this.$http.get(this.queryTreeByKeyWord).then(res => {
+				console.log("部门通讯树::", res)
+				if (res.data.success) {
+					console.log("部门::", res.data.result.departList)
+					if(res.data.result.departList && res.data.result.departList.length >0){
+						for (let item of res.data.result.departList) {
+							console.log("item::",item);
+							this.comList.push(item)
+							this.metaList.push(item)
+						}
+						this.derpartment = this.comList[0].title
+					}
+				}
+			}).catch(e => {
+				console.log("queryTreeByKeyWord 请求错误", e)
+			})
+		},
+		getUser(departId,title){
+			this.$http.get(this.queryUserByDid,{params:{departId:departId}}).then(res => {
+			  this.childrenUserList=res.data.result;
+			  for(let item of this.childrenUserList){
+				  item.departName=title
+				  this.avatarHandler(item)
+			   }
+			})
+		},
+		avatarHandler(item){
+			let avatar = item.avatar
+			if(avatar){
+				let url = getFileAccessHttpUrl(avatar);
+				item.avatar = url
+			}else{
+				if(item.sex=='2'){
+					item.avatar = 'https://static.jeecg.com/upload/test/avatar_girl_1595818025488.png';
+				}else{
+					item.avatar = 'https://static.jeecg.com/upload/test/avatar_boy_1595818012577.png';
+				}
+			}
+
+		},
+		goMember(item){
+			let params = {...item}
+			if (params.children && params.children.length>0){
+				this.level++;
+				this.comList=[];
+				this.childrenUserList=[];
+				for (let item of params.children) {
+				  this.comList.push(item)
+				}
+				this.getUser(params.id,params.title)
+				this.titleArray.push(params.title);
+				this.parentId = params.id;
+				this.derpartment = params.title;
+			}else{
+				console.log("params==>toMember",params)
+				params.level=this.level;
+				params.comList=this.comList;
+				params.metaList=this.metaList;
+				params.childrenUserList=this.childrenUserList;
+				params.derpartment=this.derpartment;
+				params.parentId=this.parentId;
+				params.titleArray=this.titleArray;
+				this.$Router.push({name: 'member',params:params})
+			}
+		},
+		getCyclePnode(arr, pid){
+			for(let item of arr){
+				if(item.id == this.parentId){
+					this.comList = arr;
+					this.childrenUserList=[];
+					this.derpartment = this.comList[0].title
+					this.parentId = pid;
+					this.getUser(this.parentId,item.title)
+					break;
+				}
+				if(item.children && item.children.length>0){
+					this.getCyclePnode(item.children, item.id)
+				}
+
+			}
+		},
+		goMemberInfo: function (item){
+			console.log("item",item)
+			
+			let parmas = {...item}
+			console.log("parmas",parmas)
+			parmas.page='levelAddressBook'
+			this.$Router.push({name: 'addressDetail',params:parmas})
+		},
+	}
+}
+</script>
+
+<style>
+	  .line2-icon {
+		width: 25px;
+		height: 20px;
+	  }
+</style>

+ 128 - 0
pages/addressbook/member.vue

@@ -0,0 +1,128 @@
+<template>
+    <view>
+        <view class="cu-bar fixed" :class="[NavBarColor]" :style="style" >
+        	<view class="cuIcon-back margin-left" @tap="backRoute()"><text class="margin-left-sm">返回</text></view>
+        	<view class="content" :style="[{top:StatusBar + 'px'}]">
+        		{{ memberTitle }}
+        	</view>
+        </view>
+		<!-- 展示部门和用户 -->
+		<view class="cu-list menu-avatar sm-border" :style="[{marginTop:'calc('+CustomBar+ 'px + 10px)'}]">
+		    <view class="cu-item" v-for="(item, index) in memberList" :key="index" @click="goMemberInfo(item)">
+				<view class="cu-avatar round lg" :style="[{backgroundImage:'url('+ item.avatar +')'}]"></view>
+				<view class="content">
+					<view class="text-grey">{{item.realname}}</view>
+					<view class="text-grey">{{item.post}}</view>
+				</view>
+			</view>
+			<view class="text-center text-lg"><text>共{{ memberTotal}}人</text></view>
+		</view>
+    </view>
+</template>
+
+<script>
+	import { getFileAccessHttpUrl } from "../../api/api.js"
+    export default {
+        name: "member",
+        data(){
+            return{
+				CustomBar:this.CustomBar,
+				StatusBar:this.StatusBar,
+				NavBarColor:this.NavBarColor,
+				departUrl:'/sys/sysDepart/queryTreeList',
+                addressSourceUrl:'/sys/user/queryByOrgCodeForAddressList',
+                queryByOrgCodeKeyword:'/sys/user/queryByOrgCodeKeyword',
+                positionUrl:'/sys/position/list',
+                value:'',
+                memberTitle:'',
+                memberList:[],
+                userId:'',
+                orgCode:'',
+                ids:{},
+                memberTotal:0,
+            }
+        },
+        onLoad(){
+			console.log("this.$Router", this.$Route);
+            this.memberTitle = this.$Route.query.title
+            this.orgCode = this.$Route.query.orgCode
+            this.loadList()
+		},
+		computed:{
+			style() {
+				var StatusBar= this.StatusBar;
+				var CustomBar= this.CustomBar;
+				var style = `height:${CustomBar}px;padding-top:${StatusBar}px;`;
+				return style
+			}
+		},
+        methods: {
+            backRoute() {
+                this.$Router.push({name: 'levelAddressBook',params:this.$Route.query})
+            },
+            goMemberInfo(item){
+                let parmas = {...item}
+                parmas.orgCode= this.orgCode
+                this.$Router.push({name: 'addressDetail',params:parmas})
+            },
+            loadList() {
+                let param = {
+                    orgCode:this.orgCode,
+                    realname:this.value
+                }
+                this.$http.get(this.addressSourceUrl,{params:param}).then(res => {
+                    console.log("用户2", res)
+                    if (res.data.success) {
+                        let memberArr = res.data.result.records;
+                        //memberArr = memberArr.filter(item => item.departName == this.memberTitle)
+                        for (let item of memberArr) {
+							this.avatarHandler(item);
+                            this.memberList.push(item)
+                            // this.userId = item.userId
+                            this.memberTotal = memberArr.length
+                            if (this.memberTotal == undefined){
+                                this.memberTotal = 0
+                            }
+                        }
+                    }
+                }).catch(e => {
+                    console.log("请求错误", e)
+                })
+
+                this.$http.get(this.positionUrl).then(res=> {
+                   console.log("用户1",res)
+                    if (res.data.success) {
+                        let postArr = res.data.result.records
+                        for (let item of postArr ){
+                            for (let it of this.memberList){
+                                if (it.post == item.code){
+                                    it.post = item.name
+                                }
+                            }
+                        }
+                    }
+                }).catch(e=>{
+                    console.log("请求错误",e)
+                })
+            },
+			avatarHandler(item){
+				let avatar = item.avatar
+				if(avatar){
+					let url = getFileAccessHttpUrl(avatar);
+					item.avatar = url
+				}else{
+					if(item.sex=='2'){
+						item.avatar = 'static/avatar_girl.png';
+					}else{
+						item.avatar = 'static/avatar_boy.png';
+					}
+				}
+			
+			},
+        }
+    }
+</script>
+
+<style scoped>
+	
+</style>

+ 108 - 0
pages/basics/avatar.vue

@@ -0,0 +1,108 @@
+<template>
+	<view>
+		<cu-custom bgColor="bg-gradual-blue" :isBack="true"><block slot="backText">返回</block><block slot="content">头像</block></cu-custom>
+		<view class="cu-bar bg-white">
+			<view class="action">
+				<text class="cuIcon-title text-blue"></text>头像形状
+			</view>
+		</view>
+		<view class="padding">
+			<view class="cu-avatar round" style="background-image:url(https://ossweb-img.qq.com/images/lol/web201310/skin/big10001.jpg)"></view>
+			<view class="cu-avatar radius margin-left" style="background-image:url(https://ossweb-img.qq.com/images/lol/web201310/skin/big81005.jpg);"></view>
+		</view>
+		<view class="cu-bar bg-white margin-top">
+			<view class="action">
+				<text class="cuIcon-title text-blue"></text>头像尺寸
+			</view>
+		</view>
+		<view class="padding">
+			<view class="cu-avatar sm round margin-left" style="background-image:url(https://ossweb-img.qq.com/images/lol/web201310/skin/big10001.jpg)"></view>
+			<view class="cu-avatar round margin-left" style="background-image:url(https://ossweb-img.qq.com/images/lol/web201310/skin/big81005.jpg);"></view>
+			<view class="cu-avatar lg round margin-left" style="background-image:url(https://ossweb-img.qq.com/images/lol/web201310/skin/big25002.jpg);"></view>
+			<view class="cu-avatar xl round margin-left" style="background-image:url(https://ossweb-img.qq.com/images/lol/web201310/skin/big99008.jpg);"></view>
+		</view>
+		<view class="padding">
+			<view class="cu-avatar sm round margin-left bg-red"> A</view>
+			<view class="cu-avatar round margin-left bg-red">B</view>
+			<view class="cu-avatar lg round margin-left bg-red">C</view>
+			<view class="cu-avatar xl round margin-left bg-red">D</view>
+		</view>
+		<view class="padding">
+			<view class="cu-avatar sm round margin-left bg-red"> 蔚</view>
+			<view class="cu-avatar round margin-left bg-red">蓝</view>
+			<view class="cu-avatar lg round margin-left bg-red">
+				<text>wl</text>
+			</view>
+			<view class="cu-avatar xl round margin-left bg-red">
+				<text class="avatar-text">网络</text>
+			</view>
+		</view>
+		<view class="cu-bar bg-white margin-top">
+			<view class="action">
+				<text class="cuIcon-title text-blue"></text>内嵌文字(图标)
+			</view>
+		</view>
+		<view class="padding">
+			<view class="cu-avatar radius">
+				<text class="cuIcon-people"></text>
+			</view>
+			<view class="cu-avatar radius margin-left">
+				<text>港</text>
+			</view>
+		</view>
+
+		<view class="cu-bar bg-white margin-top">
+			<view class="action">
+				<text class="cuIcon-title text-blue"></text>头像颜色
+			</view>
+		</view>
+		<view class="padding-sm">
+			<view class="cu-avatar round lg margin-xs" :class="'bg-' + item.name" v-for="(item,index) in ColorList" :key="index">
+				<text class="avatar-text">{{item.name}}</text>
+			</view>
+		</view>
+		<view class="cu-bar bg-white margin-top">
+			<view class="action">
+				<text class="cuIcon-title text-blue"></text>头像组
+			</view>
+		</view>
+		<view class="padding">
+			<view class="cu-avatar-group">
+				<view class="cu-avatar round lg" v-for="(item,index) in avatar" :key="index" :style="[{ backgroundImage:'url(' + avatar[index] + ')' }]"></view>
+			</view>
+		</view>
+
+		<view class="cu-bar bg-white margin-top">
+			<view class="action">
+				<text class="cuIcon-title text-blue"></text>头像标签
+			</view>
+		</view>
+		<view class="padding">
+			<view class="cu-avatar round lg margin-left"  v-for="(item,index) in avatar" :key="index" :style="[{ backgroundImage:'url(' + avatar[index] + ')' }]">
+				<view class="cu-tag badge" :class="index%2==0?'cuIcon-female bg-pink':'cuIcon-male bg-blue'"></view>
+			</view>
+		</view>
+
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				ColorList: this.ColorList,
+				avatar: [
+					'https://ossweb-img.qq.com/images/lol/web201310/skin/big10001.jpg',
+					'https://ossweb-img.qq.com/images/lol/web201310/skin/big81005.jpg',
+					'https://ossweb-img.qq.com/images/lol/web201310/skin/big25002.jpg',
+					'https://ossweb-img.qq.com/images/lol/web201310/skin/big91012.jpg'
+				],
+
+			};
+		}
+	}
+</script>
+
+<style>
+
+</style>

+ 141 - 0
pages/basics/background.vue

@@ -0,0 +1,141 @@
+<template>
+	<view>
+		<cu-custom bgColor="bg-gradual-blue" :isBack="true">
+			<block slot="backText">返回</block>
+			<block slot="content">背景</block>
+		</cu-custom>
+		<view class="cu-bar bg-white solid-bottom">
+			<view class="action">
+				<text class='cuIcon-title text-blue'></text>深色背景
+			</view>
+		</view>
+		<view class="grid col-3 padding-sm">
+			<view class="padding-sm" v-for="(item,index) in ColorList" :key="index">
+				<view class="padding radius text-center shadow-blur" :class="'bg-' + item.name">
+					<view class="text-lg">{{item.title}}</view>
+					<view class="margin-top-sm text-Abc">{{item.name}}</view>
+				</view>
+			</view>
+		</view>
+		<view class="cu-bar bg-white solid-bottom">
+			<view class="action">
+				<text class="cuIcon-title text-blue"></text>淡色背景
+			</view>
+		</view>
+		<view class="grid col-3 bg-white padding-sm">
+			<view class="padding-sm" v-for="(item,index) in ColorList" :key="index" v-if="index<12">
+				<view class="padding radius text-center light" :class="'bg-' + item.name">
+					<view class="text-lg">{{item.title}}</view>
+					<view class="margin-top-sm text-Abc">{{item.name}}</view>
+				</view>
+			</view>
+		</view>
+		<view class="cu-bar bg-white solid-bottom margin-top">
+			<view class="action">
+				<text class="cuIcon-title text-blue"></text>渐变背景
+			</view>
+		</view>
+		<view class="grid col-2 padding-sm">
+			<view class="padding-sm">
+				<view class="bg-gradual-red padding radius text-center shadow-blur">
+					<view class="text-lg">魅红</view>
+					<view class="margin-top-sm text-Abc">#f43f3b - #ec008c</view>
+				</view>
+			</view>
+			<view class="padding-sm">
+				<view class="bg-gradual-orange padding radius text-center shadow-blur">
+					<view class="text-lg">鎏金</view>
+					<view class="margin-top-sm text-Abc">#ff9700 - #ed1c24</view>
+				</view>
+			</view>
+			<view class="padding-sm">
+				<view class="bg-gradual-green padding radius text-center shadow-blur">
+					<view class="text-lg">翠柳</view>
+					<view class="margin-top-sm text-Abc">#39b54a - #8dc63f</view>
+				</view>
+			</view>
+			<view class="padding-sm">
+				<view class="bg-gradual-blue padding radius text-center shadow-blur">
+					<view class="text-lg">靛青</view>
+					<view class="margin-top-sm text-Abc">#0081ff - #1cbbb4</view>
+				</view>
+			</view>
+			<view class="padding-sm">
+				<view class="bg-gradual-purple padding radius text-center shadow-blur">
+					<view class="text-lg">惑紫</view>
+					<view class="margin-top-sm text-Abc">#9000ff - #5e00ff</view>
+				</view>
+			</view>
+			<view class="padding-sm">
+				<view class="bg-gradual-pink padding radius text-center shadow-blur">
+					<view class="text-lg">霞彩</view>
+					<view class="margin-top-sm text-Abc">#ec008c - #6739b6</view>
+				</view>
+			</view>
+		</view>
+		<view class="cu-bar bg-white margin-top">
+			<view class="action">
+				<text class="cuIcon-title text-blue"></text>图片背景
+			</view>
+		</view>
+		<view class="bg-img bg-mask flex align-center" style="background-image: url('https://ossweb-img.qq.com/images/lol/web201310/skin/big10006.jpg');height: 414upx;">
+			<view class="padding-xl text-white">
+				<view class="padding-xs text-xxl text-bold">
+					钢铁之翼
+				</view>
+				<view class="padding-xs text-lg">
+					Only the guilty need fear me.
+				</view>
+			</view>
+		</view>
+<!-- 		<view class="cu-bar bg-white margin-top">
+			<view class="action">
+				<text class="cuIcon-title text-blue"></text>视频背景
+			</view>
+		</view>
+		<view class="bg-video bg-mask flex align-center" style="height: 422upx;">
+			<video src="https://yz.lol.qq.com/v1/assets/videos/aatrox-splashvideo.webm" autoplay loop muted :show-play-btn="false"
+			 :controls="false" objectFit="cover"></video>
+			<cover-view class="padding-xl text-white ">
+				<cover-view class="padding-xs  text-xxl text-bold">
+					暗裔剑魔
+				</cover-view>
+				<cover-view class="padding-xs">
+					我必须连同希望一起毁坏……
+				</cover-view>
+			</cover-view>
+		</view> -->
+		
+		<view class="cu-bar bg-white margin-top">
+			<view class="action">
+				<text class="cuIcon-title text-blue"></text>透明背景(文字层)
+			</view>
+		</view>
+		<view class="grid col-2">
+			<view class="bg-img padding-bottom-xl" style="background-image: url('https://ossweb-img.qq.com/images/lol/web201310/skin/big10007.jpg');height: 207upx;">
+				<view class="bg-shadeTop padding padding-bottom-xl">
+					上面开始
+				</view>
+			</view>
+			<view class="bg-img padding-top-xl flex align-end" style="background-image: url('https://ossweb-img.qq.com/images/lol/web201310/skin/big10001.jpg');height: 207upx;">
+				<view class="bg-shadeBottom padding padding-top-xl flex-sub">
+					下面开始
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				ColorList: this.ColorList,
+			};
+		}
+	}
+</script>
+
+<style>
+
+</style>

+ 123 - 0
pages/basics/button.vue

@@ -0,0 +1,123 @@
+<template>
+	<view>
+		<cu-custom bgColor="bg-gradual-blue" :isBack="true"><block slot="backText">返回</block><block slot="content">按钮</block></cu-custom>
+		<view class="cu-bar bg-white solid-bottom">
+			<view class="action">
+				<text class="cuIcon-title text-blue"></text>按钮形状
+			</view>
+			<view class="action">
+				<navigator class="action" url="design" hover-class="none">
+					<text class="cuIcon-skinfill"></text>
+					<text class="text-df">设计</text>
+				</navigator>
+			</view>
+		</view>
+		<view class="padding flex flex-wrap justify-between align-center bg-white">
+			<button class="cu-btn">默认</button>
+			<button class="cu-btn round">圆角</button>
+			<button class="cu-btn cuIcon">
+				<text class="cuIcon-emojifill"></text>
+			</button>
+		</view>
+		<view class="cu-bar margin-top bg-white solid-bottom">
+			<view class="action">
+				<text class="cuIcon-title text-blue"></text>按钮尺寸
+			</view>
+		</view>
+		<view class="padding flex flex-wrap justify-between align-center bg-white">
+			<button class="cu-btn round sm">小尺寸</button>
+			<button class="cu-btn round">默认</button>
+			<button class="cu-btn round lg">大尺寸</button>
+		</view>
+		<view class="cu-bar margin-top bg-white">
+			<view class="action">
+				<text class="cuIcon-title text-blue"></text>按钮颜色
+			</view>
+			<view class="action">
+				<text class="text-df margin-right-sm">阴影</text>
+				<switch @change="SetShadow" :class="shadow?'checked':''" color="#39B54A"></switch>
+			</view>
+		</view>
+		<view class="grid col-5 padding-sm">
+			<view class="margin-tb-sm text-center" v-for="(item,index) in ColorList" :key="index">
+				<button class="cu-btn round" :class="['bg-' + item.name , shadow?'shadow':'']">{{item.title}}</button>
+			</view>
+		</view>
+		<view class="cu-bar margin-top bg-white">
+			<view class="action">
+				<text class="cuIcon-title text-blue"></text>镂空按钮
+			</view>
+			<view class="action">
+				<radio-group @change="SetBorderSize">
+					<label class="margin-left-sm">
+						<radio class="blue radio" value="" checked></radio>
+						<text class="margin-left-sm">小</text>
+					</label>
+					<label class="margin-left-sm">
+						<radio class="blue radio" value="s"></radio>
+						<text class="margin-left-sm">大</text>
+					</label>
+				</radio-group>
+			</view>
+		</view>
+		<view class="grid col-5 padding-sm">
+			<view class="margin-tb-sm text-center" v-for="(item,index) in ColorList" :key="index" v-if="item.name!='white'">
+				<button class="cu-btn round" :class="[bordersize?'lines-' + item.name:'line-' + item.name, shadow?'shadow':'']">{{item.title}}</button>
+			</view>
+		</view>
+		<view class="cu-bar margin-top bg-white">
+			<view class="action">
+				<text class="cuIcon-title text-blue"></text>块状按钮
+			</view>
+		</view>
+		<view class="padding flex flex-direction">
+			<button class="cu-btn bg-grey lg">玄灰</button>
+			<button class="cu-btn bg-red margin-tb-sm lg">嫣红</button>
+		</view>
+		<view class="cu-bar margin-top bg-white">
+			<view class="action">
+				<text class="cuIcon-title text-blue"></text>无效状态
+			</view>
+		</view>
+		<view class="padding">
+			<button class="cu-btn block bg-blue margin-tb-sm lg" disabled type="">无效状态</button>
+			<button class="cu-btn block line-blue margin-tb-sm lg" disabled>无效状态</button>
+		</view>
+		<view class="cu-bar margin-top bg-white">
+			<view class="action">
+				<text class="cuIcon-title text-blue"></text>按钮加图标
+			</view>
+		</view>
+		<view class="padding-xl">
+			<button class="cu-btn block line-orange lg">
+				<text class="cuIcon-upload"></text> 图标</button>
+			<button class="cu-btn block bg-blue margin-tb-sm lg">
+				<text class="cuIcon-loading2 cuIconfont-spin"></text> 加载</button>
+			<button class="cu-btn block bg-black margin-tb-sm lg" loading> 原生加载</button>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				ColorList: this.ColorList,
+				shadow: false,
+				bordersize: ''
+			};
+		},
+		methods: {
+			SetShadow(e) {
+				this.shadow = e.detail.value
+			},
+			SetBorderSize(e) {
+				this.bordersize = e.detail.value
+			}
+		}
+	}
+</script>
+
+<style>
+
+</style>

+ 130 - 0
pages/basics/design.vue

@@ -0,0 +1,130 @@
+<template>
+	<view>
+		<cu-custom bgColor="bg-gradual-blue" :isBack="true"><block slot="backText">返回</block><block slot="content"> 按钮 / 设计</block></cu-custom>
+		<view class="padding-xl">
+			<view class="box bg-white text-center radius">
+				<button class="cu-btn" :class="[border?bordersize?'lines-' + color:'line-' + color:'bg-'+ color,round?'round':'',size,shadow?'shadow':'']">我是一个按钮</button>
+			</view>
+			<view class="padding text-center">
+				class="cu-btn <text v-if="color">{{' '}} {{border?bordersize?'lines-' + color:'line-' + color:'bg-'+ color}} {{round?'round':''}} {{size}} {{shadow?'shadow':''}}</text>"
+			</view>
+		</view>
+		<view class="cu-form-group margin-top" @tap="showModal" data-target="ColorModal">
+			<view class="title">选择颜色</view>
+			<view class="padding solid radius shadow-blur" :class="'bg-'+color"></view>
+		</view>
+		<view class="cu-form-group">
+			<view class="title">是否圆角</view>
+			<switch @change="SetRound" class="blue" :class="round?'checked':''"></switch>
+		</view>
+		<view class="cu-form-group">
+			<view class="title">选择尺寸</view>
+			<radio-group @change="SetSize">
+				<label class="margin-left-sm">
+					<radio class="blue radio" value="sm"></radio>
+					<text class="margin-left-sm"> 小</text>
+				</label>
+				<label class="margin-left-sm">
+					<radio class="blue radio" value="" checked></radio>
+					<text class="margin-left-sm"> 中</text>
+				</label>
+				<label class="margin-left-sm">
+					<radio class="blue radio" value="lg"></radio>
+					<text class="margin-left-sm"> 大</text>
+				</label>
+			</radio-group>
+		</view>
+		<view class="cu-form-group">
+			<view class="title">是否添加阴影</view>
+			<switch @change="SetShadow" :class="shadow?'checked':''"></switch>
+		</view>
+		<view class="cu-form-group">
+			<view class="title">是否镂空</view>
+			<switch @change="SetBorder" :class="border?'checked':''"></switch>
+		</view>
+		<view class="cu-form-group" v-if="border">
+			<view class="title">边框大小</view>
+			<radio-group @change="SetBorderSize">
+				<label class="margin-left-sm">
+					<radio class="blue radio" value="" checked></radio>
+					<text class="margin-left-sm"> 小</text>
+				</label>
+				<label class="margin-left-sm">
+					<radio class="blue radio" value="s"></radio>
+					<text class="margin-left-sm"> 大</text>
+				</label>
+			</radio-group>
+		</view>
+		<view class="cu-modal" :class="modalName=='ColorModal'?'show':''">
+			<view class="cu-dialog">
+				<view class="cu-bar justify-end solid-bottom">
+					<view class="content">选择颜色</view>
+					<view class="action" @tap="hideModal">
+						<text class="cuIcon-close text-red"></text>
+					</view>
+				</view>
+				<view class="grid col-5 padding">
+					<view class="padding-xs" v-for="(item,index) in ColorList" :key="index" @tap="SetColor" :data-color="item.name" v-if="item.name!='white'">
+						<view class="padding-tb radius" :class="'bg-' + item.name"> {{item.title}} </view>
+					</view>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				ColorList: this.ColorList,
+				modalName: '',
+				round: false,
+				size: '',
+				color: 'red',
+				shadow: false,
+				border: false,
+				bordersize: ''
+			};
+		},
+		methods: {
+			showModal(e) {
+				this.modalName = e.currentTarget.dataset.target
+			},
+			hideModal(e) {
+				this.modalName = null
+			},
+			SetRound(e) {
+				this.round = e.detail.value
+			},
+			SetSize(e) {
+				this.size = e.detail.value
+			},
+			SetColor(e) {
+				this.color = e.currentTarget.dataset.color;
+				this.modalName = null
+			},
+			SetShadow(e) {
+				this.shadow = e.detail.value
+			},
+			SetBorder(e) {
+				this.border = e.detail.value
+				if (!e.detail.value) {
+					this.bordersize = false
+				}
+			},
+			SetBorderSize(e) {
+				this.bordersize = e.detail.value
+			}
+		}
+	}
+</script>
+
+<style>
+	.box {
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		height: 100px;
+	}
+</style>

+ 97 - 0
pages/basics/home.vue

@@ -0,0 +1,97 @@
+<template name="basics">
+	<view>
+		<scroll-view scroll-y class="page">
+			<image src="/static/componentBg.png "
+			 mode="widthFix" class="response"></image>
+			<view class="nav-list">
+				<navigator hover-class="none" :url="'/pages/basics/' + item.name" class="nav-li" navigateTo :class="'bg-'+item.color"
+				 :style="[{animation: 'show ' + ((index+1)*0.2+1) + 's 1'}]" v-for="(item,index) in elements" :key="index">
+					<view class="nav-title">{{item.title}}</view>
+					<view class="nav-name">{{item.name}}</view>
+					<text :class="'cuIcon-' + item.cuIcon"></text>
+				</navigator>
+			</view>
+			<view class="cu-tabbar-height"></view>
+		</scroll-view>
+	</view>
+</template>
+
+<script>
+	export default {
+		name: "basics",
+		data() {
+			return {
+				elements: [{
+						title: '布局',
+						name: 'layout',
+						color: 'cyan',
+						cuIcon: 'newsfill'
+					},
+					{
+						title: '背景',
+						name: 'background',
+						color: 'blue',
+						cuIcon: 'colorlens'
+					},
+					{
+						title: '文本',
+						name: 'text',
+						color: 'purple',
+						cuIcon: 'font'
+					},
+					{
+						title: '图标 ',
+						name: 'icon',
+						color: 'mauve',
+						cuIcon: 'cuIcon'
+					},
+					{
+						title: '按钮',
+						name: 'button',
+						color: 'pink',
+						cuIcon: 'btn'
+					},
+					{
+						title: '标签',
+						name: 'tag',
+						color: 'brown',
+						cuIcon: 'tagfill'
+					},
+					{
+						title: '头像',
+						name: 'avatar',
+						color: 'red',
+						cuIcon: 'myfill'
+					},
+					{
+						title: '进度条',
+						name: 'progress',
+						color: 'orange',
+						cuIcon: 'icloading'
+					},
+					{
+						title: '边框阴影',
+						name: 'shadow',
+						color: 'olive',
+						cuIcon: 'copy'
+					},
+					{
+						title: '加载',
+						name: 'loading',
+						color: 'green',
+						cuIcon: 'loading2'
+					}
+				],
+			};
+		},
+		onShow() {
+			console.log("success")
+		}
+	}
+</script>
+
+<style>
+	.page {
+		height: 100vh;
+	}
+</style>

+ 939 - 0
pages/basics/icon.vue

@@ -0,0 +1,939 @@
+<template>
+	<view>
+		<cu-custom bgColor="bg-gradual-blue" :isBack="true"><block slot="backText">返回</block><block slot="content">图标</block></cu-custom>
+		<view class="cu-bar bg-white search fixed" :style="[{top:CustomBar + 'px'}]">
+			<view class="search-form round">
+				<text class="cuIcon-search"></text>
+				<input type="text" placeholder="搜索cuIcon" confirm-type="search" @input="searchIcon"></input>
+			</view>
+		</view>
+		<view class="cu-list grid col-3">
+			<view class="cu-item" v-for="(item,index) in cuIcon" :key="index" v-if="item.isShow">
+				<text class="lg text-gray" :class="'cuIcon-' + item.name"></text>
+				<text>{{item.name}}</text>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				CustomBar: this.CustomBar,
+				cuIcon: [{
+					name: 'appreciate',
+					isShow: true
+				}, {
+					name: 'check',
+					isShow: true
+				}, {
+					name: 'close',
+					isShow: true
+				}, {
+					name: 'edit',
+					isShow: true
+				}, {
+					name: 'emoji',
+					isShow: true
+				}, {
+					name: 'favorfill',
+					isShow: true
+				}, {
+					name: 'favor',
+					isShow: true
+				}, {
+					name: 'loading',
+					isShow: true
+				}, {
+					name: 'locationfill',
+					isShow: true
+				}, {
+					name: 'location',
+					isShow: true
+				}, {
+					name: 'phone',
+					isShow: true
+				}, {
+					name: 'roundcheckfill',
+					isShow: true
+				}, {
+					name: 'roundcheck',
+					isShow: true
+				}, {
+					name: 'roundclosefill',
+					isShow: true
+				}, {
+					name: 'roundclose',
+					isShow: true
+				}, {
+					name: 'roundrightfill',
+					isShow: true
+				}, {
+					name: 'roundright',
+					isShow: true
+				}, {
+					name: 'search',
+					isShow: true
+				}, {
+					name: 'taxi',
+					isShow: true
+				}, {
+					name: 'timefill',
+					isShow: true
+				}, {
+					name: 'time',
+					isShow: true
+				}, {
+					name: 'unfold',
+					isShow: true
+				}, {
+					name: 'warnfill',
+					isShow: true
+				}, {
+					name: 'warn',
+					isShow: true
+				}, {
+					name: 'camerafill',
+					isShow: true
+				}, {
+					name: 'camera',
+					isShow: true
+				}, {
+					name: 'commentfill',
+					isShow: true
+				}, {
+					name: 'comment',
+					isShow: true
+				}, {
+					name: 'likefill',
+					isShow: true
+				}, {
+					name: 'like',
+					isShow: true
+				}, {
+					name: 'notificationfill',
+					isShow: true
+				}, {
+					name: 'notification',
+					isShow: true
+				}, {
+					name: 'order',
+					isShow: true
+				}, {
+					name: 'samefill',
+					isShow: true
+				}, {
+					name: 'same',
+					isShow: true
+				}, {
+					name: 'deliver',
+					isShow: true
+				}, {
+					name: 'evaluate',
+					isShow: true
+				}, {
+					name: 'pay',
+					isShow: true
+				}, {
+					name: 'send',
+					isShow: true
+				}, {
+					name: 'shop',
+					isShow: true
+				}, {
+					name: 'ticket',
+					isShow: true
+				}, {
+					name: 'back',
+					isShow: true
+				}, {
+					name: 'cascades',
+					isShow: true
+				}, {
+					name: 'discover',
+					isShow: true
+				}, {
+					name: 'list',
+					isShow: true
+				}, {
+					name: 'more',
+					isShow: true
+				}, {
+					name: 'scan',
+					isShow: true
+				}, {
+					name: 'settings',
+					isShow: true
+				}, {
+					name: 'questionfill',
+					isShow: true
+				}, {
+					name: 'question',
+					isShow: true
+				}, {
+					name: 'shopfill',
+					isShow: true
+				}, {
+					name: 'form',
+					isShow: true
+				}, {
+					name: 'pic',
+					isShow: true
+				}, {
+					name: 'filter',
+					isShow: true
+				}, {
+					name: 'footprint',
+					isShow: true
+				}, {
+					name: 'top',
+					isShow: true
+				}, {
+					name: 'pulldown',
+					isShow: true
+				}, {
+					name: 'pullup',
+					isShow: true
+				}, {
+					name: 'right',
+					isShow: true
+				}, {
+					name: 'refresh',
+					isShow: true
+				}, {
+					name: 'moreandroid',
+					isShow: true
+				}, {
+					name: 'deletefill',
+					isShow: true
+				}, {
+					name: 'refund',
+					isShow: true
+				}, {
+					name: 'cart',
+					isShow: true
+				}, {
+					name: 'qrcode',
+					isShow: true
+				}, {
+					name: 'remind',
+					isShow: true
+				}, {
+					name: 'delete',
+					isShow: true
+				}, {
+					name: 'profile',
+					isShow: true
+				}, {
+					name: 'home',
+					isShow: true
+				}, {
+					name: 'cartfill',
+					isShow: true
+				}, {
+					name: 'discoverfill',
+					isShow: true
+				}, {
+					name: 'homefill',
+					isShow: true
+				}, {
+					name: 'message',
+					isShow: true
+				}, {
+					name: 'addressbook',
+					isShow: true
+				}, {
+					name: 'link',
+					isShow: true
+				}, {
+					name: 'lock',
+					isShow: true
+				}, {
+					name: 'unlock',
+					isShow: true
+				}, {
+					name: 'vip',
+					isShow: true
+				}, {
+					name: 'weibo',
+					isShow: true
+				}, {
+					name: 'activity',
+					isShow: true
+				}, {
+					name: 'friendaddfill',
+					isShow: true
+				}, {
+					name: 'friendadd',
+					isShow: true
+				}, {
+					name: 'friendfamous',
+					isShow: true
+				}, {
+					name: 'friend',
+					isShow: true
+				}, {
+					name: 'goods',
+					isShow: true
+				}, {
+					name: 'selection',
+					isShow: true
+				}, {
+					name: 'explore',
+					isShow: true
+				}, {
+					name: 'present',
+					isShow: true
+				}, {
+					name: 'squarecheckfill',
+					isShow: true
+				}, {
+					name: 'square',
+					isShow: true
+				}, {
+					name: 'squarecheck',
+					isShow: true
+				}, {
+					name: 'round',
+					isShow: true
+				}, {
+					name: 'roundaddfill',
+					isShow: true
+				}, {
+					name: 'roundadd',
+					isShow: true
+				}, {
+					name: 'add',
+					isShow: true
+				}, {
+					name: 'notificationforbidfill',
+					isShow: true
+				}, {
+					name: 'explorefill',
+					isShow: true
+				}, {
+					name: 'fold',
+					isShow: true
+				}, {
+					name: 'game',
+					isShow: true
+				}, {
+					name: 'redpacket',
+					isShow: true
+				}, {
+					name: 'selectionfill',
+					isShow: true
+				}, {
+					name: 'similar',
+					isShow: true
+				}, {
+					name: 'appreciatefill',
+					isShow: true
+				}, {
+					name: 'infofill',
+					isShow: true
+				}, {
+					name: 'info',
+					isShow: true
+				}, {
+					name: 'forwardfill',
+					isShow: true
+				}, {
+					name: 'forward',
+					isShow: true
+				}, {
+					name: 'rechargefill',
+					isShow: true
+				}, {
+					name: 'recharge',
+					isShow: true
+				}, {
+					name: 'vipcard',
+					isShow: true
+				}, {
+					name: 'voice',
+					isShow: true
+				}, {
+					name: 'voicefill',
+					isShow: true
+				}, {
+					name: 'friendfavor',
+					isShow: true
+				}, {
+					name: 'wifi',
+					isShow: true
+				}, {
+					name: 'share',
+					isShow: true
+				}, {
+					name: 'wefill',
+					isShow: true
+				}, {
+					name: 'we',
+					isShow: true
+				}, {
+					name: 'lightauto',
+					isShow: true
+				}, {
+					name: 'lightforbid',
+					isShow: true
+				}, {
+					name: 'lightfill',
+					isShow: true
+				}, {
+					name: 'camerarotate',
+					isShow: true
+				}, {
+					name: 'light',
+					isShow: true
+				}, {
+					name: 'barcode',
+					isShow: true
+				}, {
+					name: 'flashlightclose',
+					isShow: true
+				}, {
+					name: 'flashlightopen',
+					isShow: true
+				}, {
+					name: 'searchlist',
+					isShow: true
+				}, {
+					name: 'service',
+					isShow: true
+				}, {
+					name: 'sort',
+					isShow: true
+				}, {
+					name: 'down',
+					isShow: true
+				}, {
+					name: 'mobile',
+					isShow: true
+				}, {
+					name: 'mobilefill',
+					isShow: true
+				}, {
+					name: 'copy',
+					isShow: true
+				}, {
+					name: 'countdownfill',
+					isShow: true
+				}, {
+					name: 'countdown',
+					isShow: true
+				}, {
+					name: 'noticefill',
+					isShow: true
+				}, {
+					name: 'notice',
+					isShow: true
+				}, {
+					name: 'upstagefill',
+					isShow: true
+				}, {
+					name: 'upstage',
+					isShow: true
+				}, {
+					name: 'babyfill',
+					isShow: true
+				}, {
+					name: 'baby',
+					isShow: true
+				}, {
+					name: 'brandfill',
+					isShow: true
+				}, {
+					name: 'brand',
+					isShow: true
+				}, {
+					name: 'choicenessfill',
+					isShow: true
+				}, {
+					name: 'choiceness',
+					isShow: true
+				}, {
+					name: 'clothesfill',
+					isShow: true
+				}, {
+					name: 'clothes',
+					isShow: true
+				}, {
+					name: 'creativefill',
+					isShow: true
+				}, {
+					name: 'creative',
+					isShow: true
+				}, {
+					name: 'female',
+					isShow: true
+				}, {
+					name: 'keyboard',
+					isShow: true
+				}, {
+					name: 'male',
+					isShow: true
+				}, {
+					name: 'newfill',
+					isShow: true
+				}, {
+					name: 'new',
+					isShow: true
+				}, {
+					name: 'pullleft',
+					isShow: true
+				}, {
+					name: 'pullright',
+					isShow: true
+				}, {
+					name: 'rankfill',
+					isShow: true
+				}, {
+					name: 'rank',
+					isShow: true
+				}, {
+					name: 'bad',
+					isShow: true
+				}, {
+					name: 'cameraadd',
+					isShow: true
+				}, {
+					name: 'focus',
+					isShow: true
+				}, {
+					name: 'friendfill',
+					isShow: true
+				}, {
+					name: 'cameraaddfill',
+					isShow: true
+				}, {
+					name: 'apps',
+					isShow: true
+				}, {
+					name: 'paintfill',
+					isShow: true
+				}, {
+					name: 'paint',
+					isShow: true
+				}, {
+					name: 'picfill',
+					isShow: true
+				}, {
+					name: 'refresharrow',
+					isShow: true
+				}, {
+					name: 'colorlens',
+					isShow: true
+				}, {
+					name: 'markfill',
+					isShow: true
+				}, {
+					name: 'mark',
+					isShow: true
+				}, {
+					name: 'presentfill',
+					isShow: true
+				}, {
+					name: 'repeal',
+					isShow: true
+				}, {
+					name: 'album',
+					isShow: true
+				}, {
+					name: 'peoplefill',
+					isShow: true
+				}, {
+					name: 'people',
+					isShow: true
+				}, {
+					name: 'servicefill',
+					isShow: true
+				}, {
+					name: 'repair',
+					isShow: true
+				}, {
+					name: 'file',
+					isShow: true
+				}, {
+					name: 'repairfill',
+					isShow: true
+				}, {
+					name: 'taoxiaopu',
+					isShow: true
+				}, {
+					name: 'weixin',
+					isShow: true
+				}, {
+					name: 'attentionfill',
+					isShow: true
+				}, {
+					name: 'attention',
+					isShow: true
+				}, {
+					name: 'commandfill',
+					isShow: true
+				}, {
+					name: 'command',
+					isShow: true
+				}, {
+					name: 'communityfill',
+					isShow: true
+				}, {
+					name: 'community',
+					isShow: true
+				}, {
+					name: 'read',
+					isShow: true
+				}, {
+					name: 'calendar',
+					isShow: true
+				}, {
+					name: 'cut',
+					isShow: true
+				}, {
+					name: 'magic',
+					isShow: true
+				}, {
+					name: 'backwardfill',
+					isShow: true
+				}, {
+					name: 'playfill',
+					isShow: true
+				}, {
+					name: 'stop',
+					isShow: true
+				}, {
+					name: 'tagfill',
+					isShow: true
+				}, {
+					name: 'tag',
+					isShow: true
+				}, {
+					name: 'group',
+					isShow: true
+				}, {
+					name: 'all',
+					isShow: true
+				}, {
+					name: 'backdelete',
+					isShow: true
+				}, {
+					name: 'hotfill',
+					isShow: true
+				}, {
+					name: 'hot',
+					isShow: true
+				}, {
+					name: 'post',
+					isShow: true
+				}, {
+					name: 'radiobox',
+					isShow: true
+				}, {
+					name: 'rounddown',
+					isShow: true
+				}, {
+					name: 'upload',
+					isShow: true
+				}, {
+					name: 'writefill',
+					isShow: true
+				}, {
+					name: 'write',
+					isShow: true
+				}, {
+					name: 'radioboxfill',
+					isShow: true
+				}, {
+					name: 'punch',
+					isShow: true
+				}, {
+					name: 'shake',
+					isShow: true
+				}, {
+					name: 'move',
+					isShow: true
+				}, {
+					name: 'safe',
+					isShow: true
+				}, {
+					name: 'activityfill',
+					isShow: true
+				}, {
+					name: 'crownfill',
+					isShow: true
+				}, {
+					name: 'crown',
+					isShow: true
+				}, {
+					name: 'goodsfill',
+					isShow: true
+				}, {
+					name: 'messagefill',
+					isShow: true
+				}, {
+					name: 'profilefill',
+					isShow: true
+				}, {
+					name: 'sound',
+					isShow: true
+				}, {
+					name: 'sponsorfill',
+					isShow: true
+				}, {
+					name: 'sponsor',
+					isShow: true
+				}, {
+					name: 'upblock',
+					isShow: true
+				}, {
+					name: 'weblock',
+					isShow: true
+				}, {
+					name: 'weunblock',
+					isShow: true
+				}, {
+					name: 'my',
+					isShow: true
+				}, {
+					name: 'myfill',
+					isShow: true
+				}, {
+					name: 'emojifill',
+					isShow: true
+				}, {
+					name: 'emojiflashfill',
+					isShow: true
+				}, {
+					name: 'flashbuyfill',
+					isShow: true
+				}, {
+					name: 'text',
+					isShow: true
+				}, {
+					name: 'goodsfavor',
+					isShow: true
+				}, {
+					name: 'musicfill',
+					isShow: true
+				}, {
+					name: 'musicforbidfill',
+					isShow: true
+				}, {
+					name: 'card',
+					isShow: true
+				}, {
+					name: 'triangledownfill',
+					isShow: true
+				}, {
+					name: 'triangleupfill',
+					isShow: true
+				}, {
+					name: 'roundleftfill-copy',
+					isShow: true
+				}, {
+					name: 'font',
+					isShow: true
+				}, {
+					name: 'title',
+					isShow: true
+				}, {
+					name: 'recordfill',
+					isShow: true
+				}, {
+					name: 'record',
+					isShow: true
+				}, {
+					name: 'cardboardfill',
+					isShow: true
+				}, {
+					name: 'cardboard',
+					isShow: true
+				}, {
+					name: 'formfill',
+					isShow: true
+				}, {
+					name: 'coin',
+					isShow: true
+				}, {
+					name: 'cardboardforbid',
+					isShow: true
+				}, {
+					name: 'circlefill',
+					isShow: true
+				}, {
+					name: 'circle',
+					isShow: true
+				}, {
+					name: 'attentionforbid',
+					isShow: true
+				}, {
+					name: 'attentionforbidfill',
+					isShow: true
+				}, {
+					name: 'attentionfavorfill',
+					isShow: true
+				}, {
+					name: 'attentionfavor',
+					isShow: true
+				}, {
+					name: 'titles',
+					isShow: true
+				}, {
+					name: 'icloading',
+					isShow: true
+				}, {
+					name: 'full',
+					isShow: true
+				}, {
+					name: 'mail',
+					isShow: true
+				}, {
+					name: 'peoplelist',
+					isShow: true
+				}, {
+					name: 'goodsnewfill',
+					isShow: true
+				}, {
+					name: 'goodsnew',
+					isShow: true
+				}, {
+					name: 'medalfill',
+					isShow: true
+				}, {
+					name: 'medal',
+					isShow: true
+				}, {
+					name: 'newsfill',
+					isShow: true
+				}, {
+					name: 'newshotfill',
+					isShow: true
+				}, {
+					name: 'newshot',
+					isShow: true
+				}, {
+					name: 'news',
+					isShow: true
+				}, {
+					name: 'videofill',
+					isShow: true
+				}, {
+					name: 'video',
+					isShow: true
+				}, {
+					name: 'exit',
+					isShow: true
+				}, {
+					name: 'skinfill',
+					isShow: true
+				}, {
+					name: 'skin',
+					isShow: true
+				}, {
+					name: 'moneybagfill',
+					isShow: true
+				}, {
+					name: 'usefullfill',
+					isShow: true
+				}, {
+					name: 'usefull',
+					isShow: true
+				}, {
+					name: 'moneybag',
+					isShow: true
+				}, {
+					name: 'redpacket_fill',
+					isShow: true
+				}, {
+					name: 'subscription',
+					isShow: true
+				}, {
+					name: 'loading1',
+					isShow: true
+				}, {
+					name: 'github',
+					isShow: true
+				}, {
+					name: 'global',
+					isShow: true
+				}, {
+					name: 'settingsfill',
+					isShow: true
+				}, {
+					name: 'back_android',
+					isShow: true
+				}, {
+					name: 'expressman',
+					isShow: true
+				}, {
+					name: 'evaluate_fill',
+					isShow: true
+				}, {
+					name: 'group_fill',
+					isShow: true
+				}, {
+					name: 'play_forward_fill',
+					isShow: true
+				}, {
+					name: 'deliver_fill',
+					isShow: true
+				}, {
+					name: 'notice_forbid_fill',
+					isShow: true
+				}, {
+					name: 'fork',
+					isShow: true
+				}, {
+					name: 'pick',
+					isShow: true
+				}, {
+					name: 'wenzi',
+					isShow: true
+				}, {
+					name: 'ellipse',
+					isShow: true
+				}, {
+					name: 'qr_code',
+					isShow: true
+				}, {
+					name: 'dianhua',
+					isShow: true
+				}, {
+					name: 'cuIcon',
+					isShow: true
+				}, {
+					name: 'loading2',
+					isShow: true
+				}, {
+					name: 'btn',
+					isShow: true
+				}]
+
+			};
+		},
+		methods: {
+			searchIcon(e) {
+				let key = e.detail.value.toLowerCase();
+				let list = this.cuIcon;
+				for (let i = 0; i < list.length; i++) {
+					let a = key;
+					let b = list[i].name.toLowerCase();
+					if (b.search(a) != -1) {
+						list[i].isShow = true
+					} else {
+						list[i].isShow = false
+					}
+				}
+				this.cuIcon = list
+			}
+		}
+	}
+</script>
+
+<style>
+	page {
+		padding-top: 50px;
+	}
+</style>

+ 207 - 0
pages/basics/layout.vue

@@ -0,0 +1,207 @@
+<template>
+	<view>
+		<cu-custom bgColor="bg-gradual-blue" :isBack="true"><block slot="backText">返回</block><block slot="content">布局</block></cu-custom>
+		<scroll-view scroll-x class="bg-white nav text-center fixed" :style="[{top:CustomBar + 'px'}]">
+			<view class="cu-item" :class="index==TabCur?'text-blue cur':''" v-for="(item,index) in tabNav" :key="index" @tap="tabSelect"
+			 :data-id="index">
+				{{tabNav[index]}}
+			</view>
+		</scroll-view>
+		<block v-if="TabCur==0">
+			<view class="cu-bar bg-white solid-bottom margin-top">
+				<view class="action">
+					<text class="cuIcon-title text-blue"></text>固定尺寸
+				</view>
+			</view>
+			<view class="padding bg-white">
+				<view class="flex flex-wrap">
+					<view class="basis-xs bg-grey margin-xs padding-sm radius">xs(20%)</view>
+					<view class="basis-df"></view>
+					<view class="basis-sm bg-grey margin-xs padding-sm radius">sm(40%)</view>
+					<view class="basis-df"></view>
+					<view class="basis-df bg-grey margin-xs padding-sm radius">sub(50%)</view>
+					<view class="basis-lg bg-grey margin-xs padding-sm radius">lg(60%)</view>
+					<view class="basis-xl bg-grey margin-xs padding-sm radius">xl(80%)</view>
+				</view>
+			</view>
+			<view class="cu-bar bg-white  margin-top solid-bottom">
+				<view class="action">
+					<text class="cuIcon-title text-blue"></text>比例布局
+				</view>
+			</view>
+			<view class="padding bg-white">
+				<view class="flex">
+					<view class="flex-sub bg-grey padding-sm margin-xs radius">1</view>
+					<view class="flex-sub bg-grey padding-sm margin-xs radius">1</view>
+				</view>
+				<view class="flex  p-xs margin-bottom-sm mb-sm">
+					<view class="flex-sub bg-grey padding-sm margin-xs radius">1</view>
+					<view class="flex-twice bg-grey padding-sm margin-xs radius">2</view>
+				</view>
+				<view class="flex  p-xs margin-bottom-sm mb-sm">
+					<view class="flex-sub bg-grey padding-sm margin-xs radius">1</view>
+					<view class="flex-twice bg-grey padding-sm margin-xs radius">2</view>
+					<view class="flex-treble bg-grey padding-sm margin-xs radius">3</view>
+				</view>
+			</view>
+			<view class="cu-bar bg-white  margin-top solid-bottom">
+				<view class="action">
+					<text class="cuIcon-title text-blue"></text>水平对齐(justify)
+				</view>
+			</view>
+			<view class="bg-white">
+				<view class="flex solid-bottom padding justify-start">
+					<view class="bg-grey padding-sm margin-xs radius">start</view>
+					<view class="bg-grey padding-sm margin-xs radius">start</view>
+				</view>
+				<view class="flex solid-bottom padding justify-end">
+					<view class="bg-grey padding-sm margin-xs radius">end</view>
+					<view class="bg-grey padding-sm margin-xs radius">end</view>
+				</view>
+				<view class="flex solid-bottom padding justify-center">
+					<view class="bg-grey padding-sm margin-xs radius">center</view>
+					<view class="bg-grey padding-sm margin-xs radius">center</view>
+				</view>
+				<view class="flex solid-bottom padding justify-between">
+					<view class="bg-grey padding-sm margin-xs radius">between</view>
+					<view class="bg-grey padding-sm margin-xs radius">between</view>
+				</view>
+				<view class="flex solid-bottom padding justify-around">
+					<view class="bg-grey padding-sm margin-xs radius">around</view>
+					<view class="bg-grey padding-sm margin-xs radius">around</view>
+				</view>
+			</view>
+			<view class="cu-bar bg-white  margin-top solid-bottom">
+				<view class="action">
+					<text class="cuIcon-title text-blue"></text>垂直对齐(align)
+				</view>
+			</view>
+			<view class="bg-white">
+				<view class="flex solid-bottom padding align-start">
+					<view class="bg-grey padding-lg margin-xs radius">ColorUi</view>
+					<view class="bg-grey padding-sm margin-xs radius">start</view>
+				</view>
+				<view class="flex solid-bottom padding align-end">
+					<view class="bg-grey padding-lg margin-xs radius">ColorUi</view>
+					<view class="bg-grey padding-sm margin-xs radius">end</view>
+				</view>
+				<view class="flex solid-bottom padding align-center">
+					<view class="bg-grey padding-lg margin-xs radius">ColorUi</view>
+					<view class="bg-grey padding-sm margin-xs radius">center</view>
+				</view>
+			</view>
+		</block>
+		<block v-if="TabCur==1">
+			<view class="cu-bar bg-white  margin-top solid-bottom">
+				<view class="action">
+					<text class="cuIcon-title text-blue"></text>等分列
+				</view>
+				<view class="action"></view>
+			</view>
+			<view class="bg-white padding">
+				<view class="grid margin-bottom text-center" v-for="(item,index) in 5" :key="index" :class="'col-' + (index+1)">
+					<view class="padding" :class="indexs%2==0?'bg-cyan':'bg-blue'" v-for="(item,indexs) in (index+1)*2" :key="indexs">{{indexs+1}}</view>
+				</view>
+			</view>
+			<view class="cu-bar bg-white  margin-top solid-bottom">
+				<view class="action">
+					<text class="cuIcon-title text-blue"></text>等高
+				</view>
+				<view class="action"></view>
+			</view>
+			<view class="bg-white padding">
+				<view class="grid col-4 grid-square">
+					<view class="bg-img" v-for="(item,index) in avatar" :key="index" :style="[{ backgroundImage:'url(' + avatar[index] + ')' }]"></view>
+				</view>
+			</view>
+		</block>
+		<block v-if="TabCur==2">
+			<view class="cu-bar bg-white margin-top solid-bottom">
+				<view class="action">
+					<text class="cuIcon-title text-blue"></text>浮动
+				</view>
+			</view>
+			<view class="bg-white padding">
+				<view class=" cf padding-sm">
+					<view class="bg-grey radius fl padding-sm">ColorUi fl</view>
+					<view class="bg-grey radius fr padding-sm">ColorUi fr</view>
+				</view>
+			</view>
+			<view class="cu-bar bg-white  solid-bottom margin-top solid-bottom">
+				<view class="action">
+					<text class="cuIcon-title text-blue"></text>内外边距
+				</view>
+			</view>
+			<view class="bg-white">
+				<view class="padding bg-gray">{size}的尺寸有xs/sm/df/lg/xl</view>
+				<view class="flex flex-wrap padding solid-top">
+					<view class="basis-df padding-bottom-xs">外边距</view>
+					<view class="basis-df padding-bottom-xs">内边距</view>
+					<view class="basis-df">.margin-{size}</view>
+					<view class="basis-df">.padding-{size}</view>
+				</view>
+				<view class="flex flex-wrap padding solid-top">
+					<view class="basis-df padding-bottom-xs">水平方向外边距</view>
+					<view class="basis-df padding-bottom-xs">水平方向内边距</view>
+					<view class="basis-df">.margin-lr-{size}</view>
+					<view class="basis-df">.padding-lr-{size}</view>
+				</view>
+				<view class="flex flex-wrap padding solid-top">
+					<view class="basis-df padding-bottom-xs">垂直方向外边距</view>
+					<view class="basis-df padding-bottom-xs">垂直方向内边距</view>
+					<view class="basis-df">.margin-tb-{size}</view>
+					<view class="basis-df">.padding-tb-{size}</view>
+				</view>
+				<view class="flex flex-wrap padding solid-top">
+					<view class="basis-df padding-bottom-xs">上外边距</view>
+					<view class="basis-df padding-bottom-xs">上内边距</view>
+					<view class="basis-df">.margin-top-{size}</view>
+					<view class="basis-df">.padding-top-{size}</view>
+				</view>
+				<view class="flex flex-wrap padding solid-top">
+					<view class="basis-df padding-bottom-xs">右外边距</view>
+					<view class="basis-df padding-bottom-xs">右内边距</view>
+					<view class="basis-df">.margin-right-{size}</view>
+					<view class="basis-df">.padding-right-{size}</view>
+				</view>
+				<view class="flex flex-wrap padding solid-top">
+					<view class="basis-df padding-bottom-xs">下外边距</view>
+					<view class="basis-df padding-bottom-xs">下内边距</view>
+					<view class="basis-df">margin-bottom-{size}</view>
+					<view class="basis-df">.padding-bottom-{size}</view>
+				</view>
+				<view class="flex flex-wrap padding solid-top">
+					<view class="basis-df padding-bottom-xs">左外边距</view>
+					<view class="basis-df padding-bottom-xs">左内边距</view>
+					<view class="basis-df">.margin-left-{size}</view>
+					<view class="basis-df">.padding-left-{size}</view>
+				</view>
+			</view>
+		</block>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				CustomBar: this.CustomBar,
+				TabCur: 0,
+				avatar:['https://ossweb-img.qq.com/images/lol/web201310/skin/big10001.jpg','https://ossweb-img.qq.com/images/lol/web201310/skin/big81005.jpg','https://ossweb-img.qq.com/images/lol/web201310/skin/big25002.jpg','https://ossweb-img.qq.com/images/lol/web201310/skin/big99008.jpg'],
+				tabNav: ['Flex布局', 'Grid布局', '辅助布局']
+			};
+		},
+		methods: {
+			tabSelect(e) {
+				this.TabCur = e.currentTarget.dataset.id;
+				this.scrollLeft = (e.currentTarget.dataset.id - 1) * 60
+			}
+		}
+	}
+</script>
+
+<style>
+	page {
+		padding-top: 45px;
+	}
+</style>

+ 101 - 0
pages/basics/loading.vue

@@ -0,0 +1,101 @@
+<template>
+	<view>
+		<cu-custom bgColor="bg-gradual-blue" :isBack="true"><block slot="backText">返回</block><block slot="content">加载</block>
+			<block slot="right">
+				<view class="action">
+					<view class="cu-load load-cuIcon" :class="!isLoad?'loading':'over'"></view>
+				</view>
+			</block>
+		</cu-custom>
+		<view class="cu-bar bg-white">
+			<view class="action">
+				<text class="cuIcon-title text-blue"></text>背景
+			</view>
+		</view>
+		<view class="cu-load bg-blue" :class="!isLoad?'loading':'over'"></view>
+		<view class="cu-bar bg-white margin-top">
+			<view class="action">
+				<text class="cuIcon-title text-blue"></text>加载状态
+			</view>
+			<view class="action">
+				<switch @change="isLoading" :class="isLoad?'checked':''"></switch>
+			</view>
+		</view>
+		<view class="cu-load bg-grey" :class="!isLoad?'loading':'over'"></view>
+		<view class="cu-bar bg-white margin-top">
+			<view class="action">
+				<text class="cuIcon-title text-blue"></text>加载错误
+			</view>
+		</view>
+		<view class="cu-load bg-red erro"></view>
+
+		<view class="cu-bar bg-white margin-top">
+			<view class="action">
+				<text class="cuIcon-title text-blue"></text>弹框加载
+			</view>
+			<view class="action">
+				<button class="cu-btn bg-green shadow" @tap="LoadModal">
+					点我
+				</button>
+			</view>
+		</view>
+		<view class="cu-load load-modal" v-if="loadModal">
+			<!-- <view class="cuIcon-emojifill text-orange"></view> -->
+			<image src="/static/logo.png" mode="aspectFit"></image>
+			<view class="gray-text">加载中...</view>
+		</view>
+		<view class="cu-bar bg-white margin-top">
+			<view class="action">
+				<text class="cuIcon-title text-blue"></text>进度条加载
+			</view>
+			<view class="action">
+				<button class="cu-btn bg-green shadow" @tap="LoadProgress">
+					点我
+				</button>
+			</view>
+		</view>
+		<view class="load-progress" :class="loadProgress!=0?'show':'hide'" :style="[{top:CustomBar+'px'}]">
+			<view class="load-progress-bar bg-green" :style="[{transform: 'translate3d(-' + (100-loadProgress) + '%, 0px, 0px)'}]"></view>
+			<view class="load-progress-spinner text-green"></view>
+		</view>
+	</view>
+</template>
+
+
+<script>
+	export default {
+		data() {
+			return {
+				CustomBar: this.CustomBar,
+				isLoad:false,
+				loadModal: false,
+				loadProgress: 0
+			};
+		},
+		methods: {
+			isLoading(e) {
+				this.isLoad = e.detail.value;
+			},
+			LoadModal(e) {
+				this.loadModal = true;
+				setTimeout(() => {
+					this.loadModal = false;
+				}, 2000)
+			},
+			LoadProgress(e) {
+				this.loadProgress = this.loadProgress + 3;
+				if (this.loadProgress < 100) {
+					setTimeout(() => {
+						this.LoadProgress();
+					}, 100)
+				} else {
+					this.loadProgress = 0;
+				}
+			}
+		}
+	}
+</script>
+
+<style>
+
+</style>

+ 153 - 0
pages/basics/progress.vue

@@ -0,0 +1,153 @@
+<template>
+	<view>
+		<cu-custom bgColor="bg-gradual-blue" :isBack="true"><block slot="backText">返回</block><block slot="content">进度条</block></cu-custom>
+		<view class="cu-bar bg-white solid-bottom">
+			<view class="action">
+				<text class="cuIcon-title text-blue"></text>进度条形状
+			</view>
+		</view>
+		<view class="padding bg-white">
+			<view class="cu-progress">
+				<view class="bg-red" :style="[{ width:loading?'61.8%':''}]">61.8%</view>
+			</view>
+			<view class="cu-progress radius margin-top">
+				<view class="bg-red" :style="[{ width:loading?'61.8%':''}]">61.8%</view>
+			</view>
+			<view class="cu-progress round margin-top">
+				<view class="bg-red" :style="[{ width:loading?'61.8%':''}]">61.8%</view>
+			</view>
+		</view>
+		<view class="cu-bar bg-white solid-bottom margin-top">
+			<view class="action">
+				<text class="cuIcon-title text-blue"></text>进度条尺寸
+			</view>
+		</view>
+		<view class="padding bg-white">
+			<view class="cu-progress round">
+				<view class="bg-red" :style="[{ width:loading?'61.8%':''}]"></view>
+			</view>
+			<view class="cu-progress round margin-top sm">
+				<view class="bg-red" :style="[{ width:loading?'61.8%':''}]"></view>
+			</view>
+			<view class="cu-progress round margin-top xs">
+				<view class="bg-red" :style="[{ width:loading?'61.8%':''}]"></view>
+			</view>
+		</view>
+
+		<view class="cu-bar bg-white solid-bottom margin-top" @tap="showModal" data-target="ColorModal">
+			<view class="action">
+				<text class="cuIcon-title text-blue"></text>进度条颜色
+			</view>
+			<view class="action">
+				<view class="padding solid radius shadow-blur" :class="'bg-' + color"></view>
+			</view>
+		</view>
+		<view class="padding" :class="color=='white'?'bg-grey':'bg-white'">
+			<view class="cu-progress round">
+				<view :class="'bg-' + color" :style="[{ width:loading?'61.8%':''}]"></view>
+			</view>
+		</view>
+
+
+		<view class="cu-bar bg-white solid-bottom margin-top">
+			<view class="action">
+				<text class="cuIcon-title text-blue"></text>进度条条纹
+			</view>
+			<switch class="margin-right-sm" :class="active?'checked':''" @change="SetActive"></switch>
+		</view>
+		<view class="padding bg-white">
+			<view class="cu-progress round sm striped" :class="active?'active':''">
+				<view class="bg-green" :style="[{ width:loading?'60%':''}]"></view>
+			</view>
+			<view class="cu-progress round sm margin-top-sm striped" :class="active?'active':''">
+				<view class="bg-black" :style="[{ width:loading?'40%':''}]"></view>
+			</view>
+		</view>
+		<view class="cu-bar bg-white solid-bottom margin-top">
+			<view class="action">
+				<text class="cuIcon-title text-blue"></text>进度条比例
+			</view>
+		</view>
+		<view class="padding bg-white">
+			<view class="cu-progress radius striped active">
+				<view class="bg-red" :style="[{ width:loading?'30%':''}]">30%</view>
+				<view class="bg-olive" :style="[{ width:loading?'45%':''}]">45%</view>
+				<view class="bg-cyan" :style="[{ width:loading?'25%':''}]">25%</view>
+			</view>
+		</view>
+		<view class="cu-bar bg-white solid-bottom margin-top">
+			<view class="action">
+				<text class="cuIcon-title text-blue"></text>进度条布局
+			</view>
+		</view>
+		<view class="padding bg-white ">
+			<view class="flex">
+				<view class="cu-progress round">
+					<view class="bg-green" :style="[{ width:loading?'100%':''}]"></view>
+				</view>
+				<text class="cuIcon-roundcheckfill text-green margin-left-sm"></text>
+			</view>
+			<view class="flex margin-top">
+				<view class="cu-progress round">
+					<view class="bg-green" :style="[{ width:loading?'80%':''}]"></view>
+				</view>
+				<text class="margin-left">80%</text>
+			</view>
+		</view>
+
+		<view class="cu-modal" :class="modalName=='ColorModal'?'show':''">
+			<view class="cu-dialog">
+				<view class="cu-bar justify-end solid-bottom">
+					<view class="content">选择颜色</view>
+					<view class="action" @tap="hideModal">
+						<text class="cuIcon-close text-red"></text>
+					</view>
+				</view>
+				<view class="grid col-5 padding">
+					<view class="padding-xs" v-for="(item,index) in ColorList" :key="index" @tap="SetColor" :data-color="item.name" v-if="item.name!='gray' && item.name!='white'">
+						<view class="padding-tb radius" :class="'bg-' + item.name"> {{item.title}} </view>
+					</view>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				ColorList: this.ColorList,
+				color: 'red',
+				loading: false,
+				modalName: '',
+				active: false
+			};
+		},
+		onLoad: function() {
+			let that = this;
+			setTimeout(function() {
+				that.loading = true
+			}, 500)
+		},
+		methods: {
+			showModal(e) {
+				this.modalName = e.currentTarget.dataset.target
+			},
+			hideModal(e) {
+				this.modalName = null
+			},
+			SetColor(e) {
+				this.color = e.currentTarget.dataset.color;
+				this.modalName = null
+			},
+			SetActive(e) {
+				this.active = e.detail.value
+			},
+		}
+	}
+</script>
+
+<style>
+
+</style>

+ 54 - 0
pages/basics/shadow.vue

@@ -0,0 +1,54 @@
+<template>
+	<view>
+		<cu-custom bgColor="bg-gradual-blue" :isBack="true"><block slot="backText">返回</block><block slot="content">边框阴影</block></cu-custom>
+		<view class="cu-bar bg-white solid-bottom">
+			<view class="action">
+				<text class="cuIcon-title text-blue"></text>边框
+			</view>
+			<view class="action">
+				<switch class="sm" @change="SetSize" :class="size?'checked':''"></switch>
+			</view>
+		</view>
+		<view class="padding bg-white text-center">
+			<view class="padding" :class="size?'solids':'solid'">四周</view>
+			<view class="padding margin-top" :class="size?'solids-top':'solid-top'">上</view>
+			<view class="padding margin-top" :class="size?'solids-right':'solid-right'">右</view>
+			<view class="padding margin-top" :class="size?'solids-bottom':'solid-bottom'">下</view>
+			<view class="padding margin-top" :class="size?'solids-left':'solid-left'">左</view>
+		</view>
+		<view class="cu-bar bg-white margin-top">
+			<view class="action">
+				<text class="cuIcon-title text-blue"></text>阴影
+			</view>
+		</view>
+		<view class="padding text-center">
+			<view class="padding-xl radius shadow bg-white">默认阴影</view>
+			<view class="padding-xl radius shadow bg-blue margin-top">根据背景颜色而改变的阴影</view>
+			<view class="padding-xl radius shadow shadow-lg bg-white margin-top">长阴影</view>
+			<view class="padding-xl radius shadow shadow-lg bg-blue margin-top">长阴影</view>
+			<view class="padding-xl radius shadow-warp bg-white margin-top">翘边阴影</view>
+			<view class="padding-xl radius shadow-blur bg-red margin-top bg-img" style="background-image:url(https://ossweb-img.qq.com/images/lol/web201310/skin/big91005.jpg);">
+				<view>根据背景图而改变的阴影</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				size: false
+			};
+		},
+		methods: {
+			SetSize(e) {
+				this.size = e.detail.value
+			}
+		}
+	}
+</script>
+
+<style>
+
+</style>

+ 130 - 0
pages/basics/tag.vue

@@ -0,0 +1,130 @@
+<template>
+	<view>
+		<cu-custom bgColor="bg-gradual-blue" :isBack="true"><block slot="backText">返回</block><block slot="content">标签</block></cu-custom>
+		<view class="cu-bar bg-white solid-bottom">
+			<view class='action'>
+				<text class='cuIcon-title text-blue'></text>标签形状
+			</view>
+		</view>
+		<view class="padding bg-white solid-bottom">
+			<view class='cu-tag'>默认</view>
+			<view class='cu-tag round'>椭圆</view>
+			<view class='cu-tag radius'>圆角</view>
+		</view>
+
+		<view class="cu-bar bg-white margin-top">
+			<view class='action'>
+				<text class='cuIcon-title text-blue'></text>标签尺寸
+			</view>
+		</view>
+		<view class="padding bg-white">
+			<view class='cu-tag radius sm'>小尺寸</view>
+			<view class='cu-tag radius'>普通尺寸</view>
+		</view>
+		<view class="cu-bar bg-white margin-top">
+			<view class='action'>
+				<text class='cuIcon-title text-blue'></text>标签颜色
+			</view>
+		</view>
+		<view class='padding-sm flex flex-wrap'>
+			<view class="padding-xs" v-for="(item,index) in ColorList" :key="index" v-if="item.name!='gray'">
+				<view class='cu-tag' :class="'bg-' + item.name">{{item.title}}</view>
+			</view>
+			<view class="padding-xs" v-for="(item,index) in ColorList" :key="index" v-if="item.name!='gray' && item.name!='black' && item.name!='white'">
+				<view class='cu-tag light' :class="'bg-' + item.name">{{item.title}}</view>
+			</view>
+		</view>
+		<view class="cu-bar bg-white margin-top">
+			<view class='action'>
+				<text class='cuIcon-title text-blue'></text>镂空标签
+			</view>
+		</view>
+		<view class='padding-sm flex flex-wrap'>
+			<view class="padding-xs" v-for="(item,index) in ColorList" :key="index" v-if="item.name!='white'">
+				<view class='cu-tag' :class="'line-' + item.name">{{item.title}}</view>
+			</view>
+		</view>
+		<view class="cu-bar bg-white margin-top">
+			<view class='action'>
+				<text class='cuIcon-title text-blue'></text>胶囊样式
+			</view>
+		</view>
+		<view class="padding">
+			<view class="cu-capsule">
+				<view class='cu-tag bg-red'>
+					<text class='cuIcon-likefill'></text>
+				</view>
+				<view class="cu-tag line-red">
+					12
+				</view>
+			</view>
+			<view class="cu-capsule round">
+				<view class='cu-tag bg-blue '>
+					<text class='cuIcon-likefill'></text>
+				</view>
+				<view class="cu-tag line-blue">
+					23
+				</view>
+			</view>
+			<view class="cu-capsule round">
+				<view class='cu-tag bg-blue '>
+					说明
+				</view>
+				<view class="cu-tag line-blue">
+					123
+				</view>
+			</view>
+			<view class="cu-capsule radius">
+				<view class='cu-tag bg-grey '>
+					<text class='cuIcon-likefill'></text>
+				</view>
+				<view class="cu-tag line-grey">
+					23
+				</view>
+			</view>
+			<view class="cu-capsule radius">
+				<view class='cu-tag bg-brown sm'>
+					<text class='cuIcon-likefill'></text>
+				</view>
+				<view class="cu-tag line-brown sm">
+					23
+				</view>
+			</view>
+		</view>
+		<view class="cu-bar bg-white margin-top">
+			<view class='action'>
+				<text class='cuIcon-title text-blue'></text>数字标签
+			</view>
+		</view>
+		<view class="padding flex justify-between align-center">
+			<view class='cu-avatar xl radius'>
+				港
+				<view class="cu-tag badge">99+</view>
+			</view>
+			<view class='cu-avatar xl radius' style="background-image:url(https://ossweb-img.qq.com/images/lol/web201310/skin/big10001.jpg)">
+				<view class='cu-tag badge'>9</view>
+			</view>
+			<view class='cu-avatar xl radius'>
+				<view class='cu-tag badge'>99</view>
+				<text class='cuIcon-people'></text>
+			</view>
+			<view class='cu-avatar xl radius'>
+				<view class='cu-tag badge'>99+</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				ColorList: this.ColorList,
+			};
+		}
+	}
+</script>
+
+<style>
+
+</style>

+ 169 - 0
pages/basics/text.vue

@@ -0,0 +1,169 @@
+<template>
+	<view>
+		<cu-custom bgColor="bg-gradual-blue" :isBack="true"><block slot="backText">返回</block><block slot="content">文本</block></cu-custom>
+		<view class="cu-bar bg-white solid-bottom">
+			<view class="action">
+				<text class="cuIcon-title text-blue"></text>文字大小
+			</view>
+		</view>
+		<view class="bg-white padding-lr">
+			<view class="solids-bottom padding-xs flex align-center">
+				<view class="padding">60</view>
+				<view class="flex-sub text-center">
+					<view class="solid-bottom text-xsl padding">
+						<text class=" cuIcon-roundcheckfill text-green"></text>
+					</view>
+					<view class="padding">用于图标、数字等特大显示</view>
+				</view>
+			</view>
+			<view class="solids-bottom padding-xs flex align-center">
+				<view class="padding">40</view>
+				<view class="flex-sub text-center">
+					<view class="solid-bottom text-sl padding">
+						<text class=" cuIcon-roundcheckfill text-green"></text>
+					</view>
+					<view class="padding">用于图标、数字等较大显示</view>
+				</view>
+			</view>
+			<view class="solids-bottom padding-xs flex align-center">
+				<view class="padding">22</view>
+				<view class="flex-sub text-center">
+					<view class="solid-bottom text-xxl padding">
+						<text class="text-price text-red">80.00</text>
+					</view>
+					<view class="padding">用于金额数字等信息</view>
+				</view>
+			</view>
+			<view class="solids-bottom padding-xs flex align-center">
+				<view class="padding">18</view>
+				<view class="flex-sub text-center">
+					<view class="solid-bottom text-xl padding">
+						<text class="text-black text-bold">您的订单已提交成功!</text>
+					</view>
+					<view class="padding">页面大标题,用于结果页等单一信息页</view>
+				</view>
+			</view>
+			<view class="solids-bottom padding-xs flex align-center">
+				<view class="padding">16</view>
+				<view class="flex-sub text-center">
+					<view class="solid-bottom text-lg padding">
+						<text class="text-black">ColorUI组件库</text>
+					</view>
+					<view class="padding">页面小标题,首要层级显示内容</view>
+				</view>
+			</view>
+			<view class="solids-bottom padding-xs flex align-center">
+				<view class="padding">14</view>
+				<view class="flex-sub text-center">
+					<view class="solid-bottom text-df padding">专注视觉的小程序组件库</view>
+					<view class="padding">页面默认字号,用于摘要或阅读文本</view>
+				</view>
+			</view>
+			<view class="solids-bottom padding-xs flex align-center">
+				<view class="padding">12</view>
+				<view class="flex-sub text-center">
+					<view class="solid-bottom text-sm padding">
+						<text class="text-grey">衬衫的价格是9磅15便士</text>
+					</view>
+					<view class="padding">页面辅助信息,次级内容等</view>
+				</view>
+			</view>
+			<view class="padding-xs flex align-center">
+				<view class="padding">10</view>
+				<view class="flex-sub text-center">
+					<view class="solid-bottom text-xs padding">
+						<text class="text-gray">我于杀戮之中绽放 亦如黎明中的花朵</text>
+					</view>
+					<view class="padding">说明文本,标签文字等关注度低的文字</view>
+				</view>
+			</view>
+		</view>
+		<view class="cu-bar bg-white solid-bottom margin-top">
+			<view class="action">
+				<text class="cuIcon-title text-blue"></text>文字颜色
+			</view>
+		</view>
+		<view class="grid col-5 padding-sm">
+			<view class="padding-sm" v-for="(item,index) in ColorList" :key="index">
+				<view class="text-center" :class="'text-' + item.name">
+					{{item.title}}
+				</view>
+			</view>
+		</view>
+		<view class="cu-bar bg-white solid-bottom margin-top">
+			<view class="action">
+				<text class="cuIcon-title text-blue"></text>文字阴影
+			</view>
+		</view>
+		<view class="grid col-5 padding-sm">
+			<view class="padding-sm" v-for="(item,index) in ColorList" :key="index">
+				<view class="text-center text-shadow" :class="'text-' + item.name">
+					<view class="cuIcon-ellipse text-xxl"></view>
+				</view>
+			</view>
+		</view>
+		<view class="cu-bar bg-white solid-bottom margin-top">
+			<view class="action">
+				<text class="cuIcon-title text-blue"></text>文字截断
+			</view>
+		</view>
+		<view class="padding bg-white">
+			<view class="text-cut padding bg-grey radius" style="width:220px">我于杀戮之中绽放 ,亦如黎明中的花朵</view>
+		</view>
+		<view class="cu-bar bg-white solid-bottom margin-top">
+			<view class="action">
+				<text class="cuIcon-title text-blue"></text>文字对齐
+			</view>
+		</view>
+		<view class="padding bg-white">
+			<view class="text-left padding">我于杀戮之中绽放 ,亦如黎明中的花朵</view>
+			<view class="text-center padding">我于杀戮之中绽放 ,亦如黎明中的花朵</view>
+			<view class="text-right padding">我于杀戮之中绽放 ,亦如黎明中的花朵</view>
+		</view>
+		<view class="cu-bar bg-white solid-bottom margin-top">
+			<view class="action">
+				<text class="cuIcon-title text-blue"></text>特殊文字
+			</view>
+		</view>
+		<view class="padding text-center">
+			<view class="padding-lr bg-white">
+				<view class="solid-bottom padding">
+					<text class="text-price">80.00</text>
+				</view>
+				<view class="padding">价格文本,利用伪元素添加"¥"符号</view>
+			</view>
+			<view class="padding-lr bg-white margin-top">
+				<view class="solid-bottom padding">
+					<text class="text-Abc">color Ui</text>
+				</view>
+				<view class="padding">英文单词首字母大写</view>
+			</view>
+			<view class="padding-lr bg-white margin-top">
+				<view class="solid-bottom padding">
+					<text class="text-ABC">color Ui</text>
+				</view>
+				<view class="padding">全部字母大写</view>
+			</view>
+			<view class="padding-lr bg-white margin-top">
+				<view class="solid-bottom padding">
+					<text class="text-abc">color Ui</text>
+				</view>
+				<view class="padding">全部字母小写</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				ColorList: this.ColorList,
+			};
+		}
+	}
+</script>
+
+<style>
+
+</style>

+ 51 - 0
pages/common/exit.vue

@@ -0,0 +1,51 @@
+<template>
+	<view>
+		<scroll-view :scroll-y="modalName==null" class="page" :class="modalName!=null?'show':''">
+			<cu-custom bgColor="bg-gradual-pink" :isBack="false">
+				<!-- <block slot="backText">返回</block> -->
+				<block slot="content">退出页</block>
+			</cu-custom>
+			<view class="solids-bottom padding-xs flex align-center">
+				<view class="flex-sub text-center">
+					<view class="solid-bottom text-xsl padding">
+						<text class=" cuIcon-roundcheckfill text-green"></text>
+					</view>
+					<view class="padding">{{item.msg}}</view>
+				</view>
+			</view>
+			<view class="padding flex flex-direction">
+				<button class="cu-btn bg-green shadow-blur round lg" @tap="goback()">返回登录
+				</button>
+			</view>
+		</scroll-view>
+	</view>
+
+</template>
+
+<script>
+	import api from "@/api/api";
+	
+	export default {
+		data() {
+			return {
+				modalName: null,
+				item:{msg:'退出成功'},
+			}
+		},
+		onLoad: function (option) {
+			api.logout().then(res=>{
+				uni.clearStorageSync()
+			})
+		},
+		methods: {
+			goback(){
+				uni.navigateTo({
+					url:'/pages/login/login'
+				})
+			}
+		}
+	}
+</script>
+
+<style>
+</style>

+ 48 - 0
pages/common/success.vue

@@ -0,0 +1,48 @@
+<template>
+	<view>
+		<scroll-view :scroll-y="modalName==null" class="page" :class="modalName!=null?'show':''">
+			<cu-custom bgColor="bg-gradual-pink" :isBack="true">
+				<block slot="backText">返回</block>
+				<block slot="content">结果</block>
+			</cu-custom>
+			<view class="solids-bottom padding-xs flex align-center">
+				<view class="flex-sub text-center">
+					<view class="solid-bottom text-xsl padding">
+						<text class=" cuIcon-roundcheckfill text-green"></text>
+					</view>
+					<view class="padding">{{item.msg}}</view>
+				</view>
+			</view>
+			<view class="padding flex flex-direction">
+				<button class="cu-btn bg-green shadow-blur round lg" @tap="goback()">返回主页
+				</button>
+			</view>
+		</scroll-view>
+	</view>
+
+</template>
+
+<script>
+	
+	export default {
+		data() {
+			return {
+				modalName: null,
+				item:{},
+			}
+		},
+		onLoad: function (option) {
+		    this.item = JSON.parse(decodeURIComponent(option.item));
+		},
+		methods: {
+			goback(){
+				uni.navigateTo({
+					url:'/pages/index/index'
+				})
+			}
+		}
+	}
+</script>
+
+<style>
+</style>

+ 448 - 0
pages/component/bar.vue

@@ -0,0 +1,448 @@
+<template>
+	<view>
+		<cu-custom bgColor="bg-gradual-pink" :isBack="true"><block slot="backText">返回</block><block slot="content">操作条</block></cu-custom>
+		<view class="cu-bar bg-white margin-top">
+			<view class="action">
+				<text class="cuIcon-title text-green"></text>
+				<text>底部操作条</text>
+			</view>
+		</view>
+		<view class="box">
+			<view class="cu-bar tabbar bg-white">
+				<view class="action">
+					<view class="cuIcon-cu-image">
+						<image src="/static/tabbar/basics_cur.png"></image>
+					</view>
+					<view class="text-green">元素</view>
+				</view>
+				<view class="action">
+					<view class="cuIcon-cu-image">
+						<image src="/static/tabbar/component.png"></image>
+					</view>
+					<view class="text-gray">组件</view>
+				</view>
+				<view class="action">
+					<view class="cuIcon-cu-image">
+						<image src="/static/tabbar/plugin.png"></image>
+						<view class="cu-tag badge">99</view>
+					</view>
+					<view class="text-gray">扩展</view>
+				</view>
+				<view class="action">
+					<view class="cuIcon-cu-image">
+						<image src="/static/tabbar/about.png"></image>
+						<view class="cu-tag badge"></view>
+					</view>
+					<view class="text-gray">关于</view>
+				</view>
+			</view>
+			<view class="cu-bar tabbar margin-bottom-xl bg-black">
+				<view class="action text-orange">
+					<view class="cuIcon-homefill"></view> 首页
+				</view>
+				<view class="action text-gray">
+					<view class="cuIcon-similar"></view> 分类
+				</view>
+				<view class="action text-gray">
+					<view class="cuIcon-recharge"></view>
+					积分
+				</view>
+				<view class="action text-gray">
+					<view class="cuIcon-cart">
+						<view class="cu-tag badge">99</view>
+					</view>
+					购物车
+				</view>
+				<view class="action text-gray">
+					<view class="cuIcon-my">
+						<view class="cu-tag badge"></view>
+					</view>
+					我的
+				</view>
+			</view>
+			<view class="cu-bar tabbar margin-bottom-xl bg-white">
+				<view class="action text-green">
+					<view class="cuIcon-homefill"></view> 首页
+				</view>
+				<view class="action text-gray">
+					<view class="cuIcon-similar"></view> 分类
+				</view>
+				<view class="action text-gray add-action">
+					<button class="cu-btn cuIcon-add bg-green shadow"></button>
+					发布
+				</view>
+				<view class="action text-gray">
+					<view class="cuIcon-cart">
+						<view class="cu-tag badge">99</view>
+					</view>
+					购物车
+				</view>
+				<view class="action text-gray">
+					<view class="cuIcon-my">
+						<view class="cu-tag badge"></view>
+					</view>
+					我的
+				</view>
+			</view>
+			<view class="cu-bar tabbar bg-black">
+				<view class="action text-green">
+					<view class="cuIcon-homefill"></view> 首页
+				</view>
+				<view class="action text-gray">
+					<view class="cuIcon-similar"></view> 分类
+				</view>
+				<view class="action text-gray add-action">
+					<button class="cu-btn cuIcon-add bg-green shadow"></button>
+					发布
+				</view>
+				<view class="action text-gray">
+					<view class="cuIcon-cart">
+						<view class="cu-tag badge">99</view>
+					</view>
+					购物车
+				</view>
+				<view class="action text-gray">
+					<view class="cuIcon-my">
+						<view class="cu-tag badge"></view>
+					</view>
+					我的
+				</view>
+			</view>
+
+			<view class="cu-bar bg-white tabbar border shop">
+				<button class="action" open-type="contact">
+					<view class="cuIcon-service text-green">
+						<view class="cu-tag badge"></view>
+					</view>
+					客服
+				</button>
+				<view class="action text-orange">
+					<view class="cuIcon-favorfill"></view> 已收藏
+				</view>
+				<view class="action">
+					<view class="cuIcon-cart">
+						<view class="cu-tag badge">99</view>
+					</view>
+					购物车
+				</view>
+				<view class="bg-red submit">立即订购</view>
+			</view>
+
+			<view class="cu-bar bg-white tabbar border shop">
+				<button class="action" open-type="contact">
+					<view class="cuIcon-service text-green">
+						<view class="cu-tag badge"></view>
+					</view>
+					客服
+				</button>
+				<view class="action">
+					<view class="cuIcon-cart">
+						<view class="cu-tag badge">99</view>
+					</view>
+					购物车
+				</view>
+				<view class="bg-orange submit">加入购物车</view>
+				<view class="bg-red submit">立即订购</view>
+			</view>
+
+			<view class="cu-bar bg-white tabbar border shop">
+				<button class="action" open-type="contact">
+					<view class="cuIcon-service text-green">
+						<view class="cu-tag badge"></view>
+					</view>
+					客服
+				</button>
+				<view class="action">
+					<view class=" cuIcon-shop"></view> 店铺
+				</view>
+				<view class="action">
+					<view class="cuIcon-cart">
+						<view class="cu-tag badge">99</view>
+					</view>
+					购物车
+				</view>
+				<view class="btn-group">
+					<button class="cu-btn bg-red round shadow-blur">立即订购</button>
+				</view>
+			</view>
+			<view class="cu-bar bg-white tabbar border shop">
+				<button class="action" open-type="contact">
+					<view class="cuIcon-service text-green">
+						<view class="cu-tag badge"></view>
+					</view> 客服
+				</button>
+				<view class="action">
+					<view class="cuIcon-cart">
+						<view class="cu-tag badge">99</view>
+					</view>
+					购物车
+				</view>
+				<view class="btn-group">
+					<button class="cu-btn bg-orange round shadow-blur">加入购物车</button>
+					<button class="cu-btn bg-red round shadow-blur">立即订购</button>
+				</view>
+			</view>
+		</view>
+
+		<view class="cu-bar bg-white">
+			<view class="action">
+				<text class="cuIcon-title text-green"></text>
+				<text>标题操作条</text>
+			</view>
+		</view>
+		<view class="box" v-if="false">
+			<view class="cu-bar justify-center bg-white">
+				<view class="action border-title">
+					<text class="text-xl text-bold">关于我们</text>
+					<text class="bg-grey" style="width:2rem"></text>
+					<!-- 底部样式 last-child选择器-->
+				</view>
+			</view>
+			<view class="cu-bar justify-center bg-white">
+				<view class="action border-title">
+					<text class="text-xl text-bold text-blue">关于我们</text>
+					<text class="bg-gradual-blue" style="width:3rem"></text>
+				</view>
+			</view>
+			<view class="cu-bar justify-center bg-white">
+				<view class="action sub-title">
+					<text class="text-xl text-bold text-green">关于我们</text>
+					<text class="bg-green" style="width:2rem"></text>
+					<!-- last-child选择器-->
+				</view>
+			</view>
+			<view class="cu-bar justify-center bg-white">
+				<view class="action sub-title">
+					<text class="text-xl text-bold text-blue">关于我们</text>
+					<text class="text-ABC text-blue">about</text>
+					<!-- last-child选择器-->
+				</view>
+			</view>
+		</view>
+		<view class="box">
+			<view class="cu-bar bg-white">
+				<view class="action border-title">
+					<text class="text-xl text-bold">关于我们</text>
+					<text class="bg-grey" style="width:2rem"></text>
+					<!-- 底部样式 last-child选择器-->
+				</view>
+			</view>
+			<view class="cu-bar bg-white">
+				<view class="action border-title">
+					<text class="text-xl text-bold text-blue">关于我们</text>
+					<text class="bg-gradual-blue" style="width:3rem"></text>
+				</view>
+			</view>
+			<view class="cu-bar bg-white">
+				<view class="action sub-title">
+					<text class="text-xl text-bold text-green">关于我们</text>
+					<text class="bg-green"></text>
+					<!-- last-child选择器-->
+				</view>
+			</view>
+			<view class="cu-bar bg-white">
+				<view class="action sub-title">
+					<text class="text-xl text-bold text-blue">关于我们</text>
+					<text class="text-ABC text-blue">about</text>
+					<!-- last-child选择器-->
+				</view>
+			</view>
+			<view class="cu-bar bg-white">
+				<view class="action title-style-3">
+					<text class="text-xl text-bold">关于我们</text>
+					<text class="text-Abc text-gray self-end margin-left-sm">about</text>
+				</view>
+			</view>
+			<view class="cu-bar bg-white">
+				<view class="action">
+					<text class="cuIcon-title text-green"></text>
+					<text class="text-xl text-bold">关于我们</text>
+				</view>
+			</view>
+			<view class="cu-bar bg-white">
+				<view class="action">
+					<text class="cuIcon-titles text-green"></text>
+					<text class="text-xl text-bold">关于我们</text>
+				</view>
+			</view>
+		</view>
+
+		<view class="cu-bar bg-white">
+			<view class="action">
+				<text class="cuIcon-title text-green"></text>
+				<text>顶部操作条</text>
+			</view>
+		</view>
+		<view class="box">
+			<view class="cu-bar bg-white">
+				<view class="action">
+					<text class="cuIcon-back text-gray"></text> 返回
+				</view>
+				<view class="content text-bold">
+					操作条
+				</view>
+			</view>
+			<view class="cu-bar bg-white">
+				<view class="action">
+					<text class="cuIcon-homefill text-gray"></text> 首页
+				</view>
+				<view class="content text-bold">
+					鲜亮的高饱和色彩,专注视觉的小程序组件库
+				</view>
+				<view class="action">
+					<text class="cuIcon-cardboardfill text-grey"></text>
+					<text class="cuIcon-recordfill text-red"></text>
+				</view>
+			</view>
+			<view class="cu-bar bg-blue">
+				<view class="action">
+					<text class="cuIcon-close"></text> 关闭
+				</view>
+				<view class="content text-bold">
+					海蓝
+				</view>
+			</view>
+			<view class="cu-bar bg-black search">
+				<view class="cu-avatar round" style="background-image:url(https://ossweb-img.qq.com/images/lol/web201310/skin/big91012.jpg);"></view>
+				<view class="content">
+					ColorUI
+				</view>
+				<view class="action">
+					<text class="cuIcon-more"></text>
+				</view>
+			</view>
+		</view>
+
+
+		<view class="cu-bar bg-white">
+			<view class="action">
+				<text class="cuIcon-title text-green"></text>
+				<text>搜索操作条</text>
+			</view>
+		</view>
+		<view class="box">
+			<view class="cu-bar search bg-white">
+				<view class="search-form round">
+					<text class="cuIcon-search"></text>
+					<input @focus="InputFocus" @blur="InputBlur" :adjust-position="false" type="text" placeholder="搜索图片、文章、视频" confirm-type="search"></input>
+				</view>
+				<view class="action">
+					<button class="cu-btn bg-green shadow-blur round">搜索</button>
+				</view>
+			</view>
+			<view class="cu-bar search bg-white">
+				<view class="cu-avatar round" style="background-image:url(https://ossweb-img.qq.com/images/lol/web201310/skin/big11010.jpg"></view>
+				<view class="search-form round">
+					<text class="cuIcon-search"></text>
+					<input @focus="InputFocus" @blur="InputBlur" :adjust-position="false" type="text" placeholder="搜索图片、文章、视频" confirm-type="search"></input>
+				</view>
+				<view class="action">
+					<text>广州</text>
+					<text class="cuIcon-triangledownfill"></text>
+				</view>
+			</view>
+			<view class="cu-bar bg-red search">
+				<view class="cu-avatar round" style="background-image:url(https://ossweb-img.qq.com/images/lol/web201310/skin/big114004.jpg);"></view>
+				<view class="search-form radius">
+					<text class="cuIcon-search"></text>
+					<input @focus="InputFocus" @blur="InputBlur" :adjust-position="false" type="text" placeholder="搜索图片、文章、视频" confirm-type="search"></input>
+				</view>
+				<view class="action">
+					<text>广州</text>
+					<text class="cuIcon-triangledownfill"></text>
+				</view>
+			</view>
+			<view class="cu-bar bg-cyan search">
+				<view class="search-form radius">
+					<text class="cuIcon-search"></text>
+					<input @focus="InputFocus" @blur="InputBlur" :adjust-position="false" type="text" placeholder="搜索图片、文章、视频" confirm-type="search"></input>
+				</view>
+				<view class="action">
+					<text class="cuIcon-close"></text>
+					<text>取消</text>
+				</view>
+			</view>
+		</view>
+
+		<view class="cu-bar bg-white">
+			<view class="action">
+				<text class="cuIcon-title text-green"></text>
+				<text>操作条按钮组</text>
+			</view>
+		</view>
+
+		<view class="box">
+			<view class="cu-bar btn-group">
+				<button class="cu-btn bg-green shadow-blur round lg">保存</button>
+			</view>
+			<view class="cu-bar btn-group">
+				<button class="cu-btn bg-green shadow-blur">保存</button>
+				<button class="cu-btn text-green line-green shadow">上传</button>
+			</view>
+			<view class="cu-bar btn-group">
+				<button class="cu-btn bg-green shadow-blur round">保存</button>
+				<button class="cu-btn bg-blue shadow-blur round">提交</button>
+			</view>
+		</view>
+
+
+		<view class="cu-bar bg-white">
+			<view class="action">
+				<text class="cuIcon-title text-green"></text>
+				<text>输入操作条</text>
+			</view>
+		</view>
+		<view class="box">
+			<view class="cu-bar input">
+				<view class="action">
+					<text class="cuIcon-sound text-grey"></text>
+				</view>
+				<input @focus="InputFocus" @blur="InputBlur" :adjust-position="false" class="solid-bottom" :focus="false" maxlength="300" cursor-spacing="10"></input>
+				<view class="action">
+					<text class="cuIcon-emojifill text-grey"></text>
+				</view>
+				<button class="cu-btn bg-green shadow-blur">发送</button>
+			</view>
+
+			<view class="cu-bar input">
+				<view class="cu-avatar round" style="background-image:url(https://ossweb-img.qq.com/images/lol/web201310/skin/big91012.jpg);"></view>
+				<view class="action">
+					<text class="cuIcon-roundaddfill text-grey"></text>
+				</view>
+				<input @focus="InputFocus" @blur="InputBlur" :adjust-position="false" class="solid-bottom" maxlength="300" cursor-spacing="10"></input>
+				<view class="action">
+					<text class="cuIcon-emojifill text-grey"></text>
+				</view>
+				<button class="cu-btn bg-green shadow-blur">发送</button>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				InputBottom: 0
+			};
+		},
+		methods: {
+			InputFocus(e) {
+				this.InputBottom = e.detail.height
+			},
+			InputBlur(e) {
+				this.InputBottom = 0
+			}
+		}
+	}
+</script>
+
+<style>
+	.box {
+		margin: 20upx 0;
+	}
+
+	.box view.cu-bar {
+		margin-top: 20upx;
+	}
+</style>

+ 167 - 0
pages/component/card.vue

@@ -0,0 +1,167 @@
+<template>
+	<view>
+		<cu-custom bgColor="bg-gradual-pink" :isBack="true"><block slot="backText">返回</block><block slot="content">卡片</block></cu-custom>
+		<view class="cu-bar bg-white solid-bottom">
+			<view class="action">
+				<text class="cuIcon-titles text-orange"></text> 案例类卡片
+			</view>
+			<view class="action">
+				<switch :class="isCard?'checked':''" :checked="isCard?true:false" @change="IsCard"></switch>
+			</view>
+		</view>
+		<view class="cu-card case" :class="isCard?'no-card':''">
+			<view class="cu-item shadow">
+				<view class="image">
+					<image src="https://ossweb-img.qq.com/images/lol/web201310/skin/big10006.jpg"
+					 mode="widthFix"></image>
+					<view class="cu-tag bg-blue">史诗</view>
+					<view class="cu-bar bg-shadeBottom"> <text class="text-cut">我已天理为凭,踏入这片荒芜,不再受凡人的枷锁遏制。我已天理为凭,踏入这片荒芜,不再受凡人的枷锁遏制。</text></view>
+				</view>
+				<view class="cu-list menu-avatar">
+					<view class="cu-item">
+						<view class="cu-avatar round lg" style="background-image:url(https://ossweb-img.qq.com/images/lol/web201310/skin/big10006.jpg);"></view>
+						<view class="content flex-sub">
+							<view class="text-grey">正义天使 凯尔</view>
+							<view class="text-gray text-sm flex justify-between">
+								十天前
+								<view class="text-gray text-sm">
+									<text class="cuIcon-attentionfill margin-lr-xs"></text> 10
+									<text class="cuIcon-appreciatefill margin-lr-xs"></text> 20
+									<text class="cuIcon-messagefill margin-lr-xs"></text> 30
+								</view>
+							</view>
+						</view>
+					</view>
+				</view>
+			</view>
+		</view>
+		<view class="cu-bar bg-white solid-bottom" :class="isCard?'margin-top':''">
+			<view class="action">
+				<text class="cuIcon-titles text-orange "></text> 动态类卡片
+			</view>
+			<view class="action">
+				<switch :class="isCard?'checked':''" :checked="isCard?true:false" @change="IsCard"></switch>
+			</view>
+		</view>
+		<view class="cu-card dynamic" :class="isCard?'no-card':''">
+			<view class="cu-item shadow">
+				<view class="cu-list menu-avatar">
+					<view class="cu-item">
+						<view class="cu-avatar round lg" style="background-image:url(https://ossweb-img.qq.com/images/lol/web201310/skin/big10006.jpg);"></view>
+						<view class="content flex-sub">
+							<view>凯尔</view>
+							<view class="text-gray text-sm flex justify-between">
+								2019年12月3日
+							</view>
+						</view>
+					</view>
+				</view>
+				<view class="text-content">
+					折磨生出苦难,苦难又会加剧折磨,凡间这无穷的循环,将有我来终结!
+				</view>
+				<view class="grid flex-sub padding-lr" :class="isCard?'col-3 grid-square':'col-1'">
+					<view class="bg-img" :class="isCard?'':'only-img'" style="background-image:url(https://ossweb-img.qq.com/images/lol/web201310/skin/big10006.jpg);"
+					 v-for="(item,index) in isCard?9:1" :key="index">
+					</view>
+				</view>
+				<view class="text-gray text-sm text-right padding">
+					<text class="cuIcon-attentionfill margin-lr-xs"></text> 10
+					<text class="cuIcon-appreciatefill margin-lr-xs"></text> 20
+					<text class="cuIcon-messagefill margin-lr-xs"></text> 30
+				</view>
+
+				<view class="cu-list menu-avatar comment solids-top">
+					<view class="cu-item">
+						<view class="cu-avatar round" style="background-image:url(https://ossweb-img.qq.com/images/lol/img/champion/Morgana.png);"></view>
+						<view class="content">
+							<view class="text-grey">莫甘娜</view>
+							<view class="text-gray text-content text-df">
+								凯尔,你被自己的光芒变的盲目。
+							</view>
+							<view class="bg-grey padding-sm radius margin-top-sm  text-sm">
+								<view class="flex">
+									<view>凯尔:</view>
+									<view class="flex-sub">妹妹,你在帮他们给黑暗找借口吗?</view>
+								</view>
+							</view>
+							<view class="margin-top-sm flex justify-between">
+								<view class="text-gray text-df">2018年12月4日</view>
+								<view>
+									<text class="cuIcon-appreciatefill text-red"></text>
+									<text class="cuIcon-messagefill text-gray margin-left-sm"></text>
+								</view>
+							</view>
+						</view>
+					</view>
+
+					<view class="cu-item">
+						<view class="cu-avatar round" style="background-image:url(https://ossweb-img.qq.com/images/lol/web201310/skin/big10006.jpg);"></view>
+						<view class="content">
+							<view class="text-grey">凯尔</view>
+							<view class="text-gray text-content text-df">
+								妹妹,如果不是为了飞翔,我们要这翅膀有什么用?
+							</view>
+							<view class="bg-grey padding-sm radius margin-top-sm  text-sm">
+								<view class="flex">
+									<view>莫甘娜:</view>
+									<view class="flex-sub">如果不能立足于大地,要这双脚又有何用?</view>
+								</view>
+							</view>
+							<view class="margin-top-sm flex justify-between">
+								<view class="text-gray text-df">2018年12月4日</view>
+								<view>
+									<text class="cuIcon-appreciate text-gray"></text>
+									<text class="cuIcon-messagefill text-gray margin-left-sm"></text>
+								</view>
+							</view>
+						</view>
+					</view>
+				</view>
+			</view>
+		</view>
+
+		<view class="cu-bar bg-white solid-bottom margin-top">
+			<view class="action">
+				<text class="cuIcon-titles text-orange "></text> 文章类卡片
+			</view>
+			<view class="action">
+				<switch :class="isCard?'checked':''" :checked="isCard?true:false" @change="IsCard"></switch>
+			</view>
+		</view>
+		<view class="cu-card article" :class="isCard?'no-card':''">
+			<view class="cu-item shadow">
+				<view class="title"><view class="text-cut">无意者 烈火焚身;以正义的烈火拔出黑暗。我有自己的正义,见证至高的烈火吧。</view></view>
+				<view class="content">
+					<image src="https://ossweb-img.qq.com/images/lol/web201310/skin/big10006.jpg"
+					 mode="aspectFill"></image>
+					<view class="desc">
+						<view class="text-content"> 折磨生出苦难,苦难又会加剧折磨,凡间这无穷的循环,将有我来终结!真正的恩典因不完整而美丽,因情感而真诚,因脆弱而自由!</view>
+						<view>
+							<view class="cu-tag bg-red light sm round">正义天使</view>
+							<view class="cu-tag bg-green light sm round">史诗</view>
+						</view>
+					</view>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				isCard: false
+			};
+		},
+		methods: {
+			IsCard(e) {
+				this.isCard = e.detail.value
+			},
+		}
+	}
+</script>
+
+<style>
+
+</style>

+ 113 - 0
pages/component/chat.vue

@@ -0,0 +1,113 @@
+<template>
+	<view>
+		<cu-custom bgColor="bg-gradual-pink" :isBack="true"><block slot="backText">返回</block><block slot="content">聊天</block></cu-custom>
+		<view class="cu-chat">
+			<view class="cu-item self">
+				<view class="main">
+					<view class="content bg-green shadow">
+						<text>喵喵喵!喵喵喵!喵喵喵!喵喵!喵喵!!喵!喵喵喵!</text>
+					</view>
+				</view>
+				<view class="cu-avatar radius" style="background-image:url(https://ossweb-img.qq.com/images/lol/web201310/skin/big107000.jpg);"></view>
+				<view class="date">2018年3月23日 13:23</view>
+			</view>
+			<view class="cu-info round">对方撤回一条消息!</view>
+			<view class="cu-item">
+				<view class="cu-avatar radius" style="background-image:url(https://ossweb-img.qq.com/images/lol/web201310/skin/big143004.jpg);"></view>
+				<view class="main">
+					<view class="content shadow">
+						<text>喵喵喵!喵喵喵!</text>
+					</view>
+				</view>
+				<view class="date "> 13:23</view>
+			</view>
+			<view class="cu-info">
+				<text class="cuIcon-roundclosefill text-red "></text> 对方拒绝了你的消息
+			</view>
+			<view class="cu-info">
+				对方开启了好友验证,你还不是他(她)的好友。请先发送好友验证请求,对方验证通过后,才能聊天。
+				<text class="text-blue">发送好友验证</text>
+			</view>
+			<view class="cu-item self">
+				<view class="main">
+					<image src="https://ossweb-img.qq.com/images/lol/web201310/skin/big10006.jpg" class="radius" mode="widthFix"></image>
+				</view>
+				<view class="cu-avatar radius" style="background-image:url(https://ossweb-img.qq.com/images/lol/web201310/skin/big107000.jpg);"></view>
+				<view class="date"> 13:23</view>
+			</view>
+			<view class="cu-item self">
+				<view class="main">
+					<view class="action text-bold text-grey">
+						3"
+					</view>
+					<view class="content shadow">
+						<text class="cuIcon-sound text-xxl padding-right-xl"> </text>
+					</view>
+				</view>
+				<view class="cu-avatar radius" style="background-image:url(https://ossweb-img.qq.com/images/lol/web201310/skin/big107000.jpg);"></view>
+				<view class="date">13:23</view>
+			</view>
+			<view class="cu-item self">
+				<view class="main">
+					<view class="action">
+						<text class="cuIcon-locationfill text-orange text-xxl"></text>
+					</view>
+					<view class="content shadow">
+						喵星球,喵喵市
+					</view>
+				</view>
+				<view class="cu-avatar radius" style="background-image:url(https://ossweb-img.qq.com/images/lol/web201310/skin/big107000.jpg);"></view>
+				<view class="date">13:23</view>
+			</view>
+			<view class="cu-item">
+				<view class="cu-avatar radius" style="background-image:url(https://ossweb-img.qq.com/images/lol/web201310/skin/big143004.jpg);"></view>
+				<view class="main">
+					<view class="content shadow">
+						@#$^&**
+					</view>
+					<view class="action text-grey">
+						<text class="cuIcon-warnfill text-red text-xxl"></text> <text class="text-sm margin-left-sm">翻译错误</text>
+					</view>
+				</view>
+				<view class="date">13:23</view>
+			</view>
+		</view>
+
+		<view class="cu-bar foot input" :style="[{bottom:InputBottom+'px'}]">
+			<view class="action">
+				<text class="cuIcon-sound text-grey"></text>
+			</view>
+			<input class="solid-bottom" :adjust-position="false" :focus="false" maxlength="300" cursor-spacing="10"
+			 @focus="InputFocus" @blur="InputBlur"></input>
+			<view class="action">
+				<text class="cuIcon-emojifill text-grey"></text>
+			</view>
+			<button class="cu-btn bg-green shadow">发送</button>
+		</view>
+
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				InputBottom: 0
+			};
+		},
+		methods: {
+			InputFocus(e) {
+				this.InputBottom = e.detail.height
+			},
+			InputBlur(e) {
+				this.InputBottom = 0
+			}
+		}
+	}
+</script>
+
+<style>
+page{
+  padding-bottom: 100upx;
+}
+</style>

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