Pārlūkot izejas kodu

对接设备,通知公告,园区动态等等

19124812874 5 gadi atpakaļ
vecāks
revīzija
ec9b01ea93
92 mainītis faili ar 8783 papildinājumiem un 960 dzēšanām
  1. 2 0
      .gitignore
  2. 49 1
      App.vue
  3. 0 1
      assets/colorui/main.css
  4. 137 5
      assets/http/api.js
  5. 18 6
      assets/http/service.js
  6. 32 0
      assets/js/tabbar.js
  7. 9 4
      components/hot-consult/hot-consult.vue
  8. 175 0
      components/infomation/infomation.vue
  9. 46 0
      components/loading/loaders/rotate-plane.vue
  10. 127 0
      components/loading/loading.vue
  11. 55 0
      components/mescroll-body/components/mescroll-down.css
  12. 47 0
      components/mescroll-body/components/mescroll-down.vue
  13. 90 0
      components/mescroll-body/components/mescroll-empty.vue
  14. 83 0
      components/mescroll-body/components/mescroll-top.vue
  15. 47 0
      components/mescroll-body/components/mescroll-up.css
  16. 39 0
      components/mescroll-body/components/mescroll-up.vue
  17. 19 0
      components/mescroll-body/mescroll-body.css
  18. 348 0
      components/mescroll-body/mescroll-body.vue
  19. 65 0
      components/mescroll-body/mescroll-mixins.js
  20. 36 0
      components/mescroll-body/mescroll-uni-option.js
  21. 36 0
      components/mescroll-body/mescroll-uni.css
  22. 799 0
      components/mescroll-body/mescroll-uni.js
  23. 424 0
      components/mescroll-body/mescroll-uni.vue
  24. 48 0
      components/mescroll-body/mixins/mescroll-comp.js
  25. 59 0
      components/mescroll-body/mixins/mescroll-more-item.js
  26. 74 0
      components/mescroll-body/mixins/mescroll-more.js
  27. 109 0
      components/mescroll-body/wxs/mixins.js
  28. 92 0
      components/mescroll-body/wxs/renderjs.js
  29. 268 0
      components/mescroll-body/wxs/wxs.wxs
  30. 81 1
      main.js
  31. 156 19
      pages.json
  32. 370 75
      pages/auth/auth.vue
  33. 77 18
      pages/company/company.vue
  34. 106 0
      pages/device/device.vue
  35. 258 4
      pages/index/access-record/access-record.vue
  36. 197 0
      pages/index/fire/alarm-record.vue
  37. 191 0
      pages/index/fire/detail.vue
  38. 158 0
      pages/index/fire/list/card.vue
  39. 118 0
      pages/index/fire/list/item.vue
  40. 131 0
      pages/index/fire/list/list.vue
  41. 228 114
      pages/index/index.vue
  42. 3 3
      pages/index/my-temperature/deatil.vue
  43. 201 49
      pages/index/my-temperature/my-temperature.vue
  44. 201 0
      pages/index/myinfo/myinfo.vue
  45. 58 0
      pages/index/notice/detail.vue
  46. 187 0
      pages/index/notice/notice.vue
  47. 85 0
      pages/index/staff-temperature/detail.vue
  48. 226 0
      pages/index/staff-temperature/staff-temperature.vue
  49. 145 0
      pages/index/staffAudit/card.vue
  50. 272 0
      pages/index/staffAudit/detail.vue
  51. 213 0
      pages/index/staffAudit/item.vue
  52. 118 0
      pages/index/staffAudit/list.vue
  53. 419 157
      pages/login/login.vue
  54. 97 0
      pages/mine/changePass/changePass.vue
  55. 31 0
      pages/mine/changePassword/changePassword.vue
  56. 176 0
      pages/mine/editPhone/editPhone.vue
  57. 42 35
      pages/mine/feedback/feedback.vue
  58. 2 3
      pages/mine/mine-info/mine-info.vue
  59. 176 215
      pages/mine/mine.vue
  60. 1 1
      pages/mine/protocol/protocol.vue
  61. 27 17
      pages/news/detail.vue
  62. 70 0
      pages/news/list.vue
  63. 141 196
      pages/test/test.vue
  64. 145 11
      pages/test/test1.vue
  65. 8 1
      readme.md
  66. 83 10
      router.js
  67. BIN
      static/empty.png
  68. 2 0
      static/iconfont.css
  69. BIN
      static/index/blue/change-phone.png
  70. BIN
      static/index/blue/changePsw.png
  71. BIN
      static/index/blue/dingyue.png
  72. BIN
      static/index/call.png
  73. BIN
      static/index/fire/dianchi.png
  74. BIN
      static/index/fire/ranqi2.png
  75. BIN
      static/index/fire/yanwu.png
  76. BIN
      static/index/fire/yanwu1.png
  77. BIN
      static/login/qiye1.png
  78. BIN
      static/login/xinyong1.png
  79. BIN
      static/tarbar/home.png
  80. BIN
      static/tarbar/home0.png
  81. BIN
      static/tarbar/index.png
  82. BIN
      static/tarbar/index0.png
  83. BIN
      static/tarbar/menjin.png
  84. BIN
      static/tarbar/menjin0.png
  85. BIN
      static/tarbar/my.png
  86. BIN
      static/tarbar/my0.png
  87. BIN
      static/tarbar/news.png
  88. BIN
      static/tarbar/news0.png
  89. 4 0
      uni.scss
  90. 243 0
      utils/md5.js
  91. 1 14
      utils/util.js
  92. 2 0
      uview-ui/components/u-empty/u-empty.vue

+ 2 - 0
.gitignore

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

+ 49 - 1
App.vue

@@ -1,6 +1,11 @@
 <script>
+	//临时对接社区
+	var md5 = require("./utils/md5.js");
 	export default {
 		onLaunch: function() {
+			uni.setEnableDebug({
+			    enableDebug: true
+			})
 			console.log('App Launch')
 		},
 		onShow: function() {
@@ -8,11 +13,54 @@
 		},
 		onHide: function() {
 			console.log('App Hide')
-		}
+		},
+		/**
+		 * 临时对接社区,到时候需要删除
+		 */
+		globalData: {
+			//机构id
+			org_id: "1",
+			token: "j2qctr89u1xfmbjkp69hegfmqhwquycw",
+			//远程的
+			apiurl: "http://139.9.103.171:9082/app/",
+			//封装request请求
+			postRequest: function (params, operation, callBack) {
+			  let that = this; //公共参数
+			  params['org_id'] = that.org_id;
+			  params['token'] = that.token; 
+			  let jsonStr = JSON.stringify(params); //数据md5签名
+			
+			  let dataSign = md5.hex_md5(jsonStr);
+			  uni.request({
+				url: that.apiurl + operation,
+				method: 'POST',
+				header: {
+				  'content-type': 'application/json',
+				  'data-sign': dataSign
+				},
+				data: jsonStr,
+				success: function (res) {
+				  callBack(res);
+				},
+				fail: function (res) {
+				  uni.hideLoading();
+				  uni.showModal({
+					title: '警告',
+					content: '网络异常' + res.errMsg,
+					showCancel: false,
+					confirmText: '确认'
+				  });
+				}
+			  });
+			},
+			
+		},
+		
 	}
 </script>
 
 <style lang="scss">
+	@import "./static/iconfont.css";
 	@import "uview-ui/index.scss";
 	
 	@import "@/assets/colorui/main.css";

+ 0 - 1
assets/colorui/main.css

@@ -682,7 +682,6 @@ radio.white.checked .uni-radio-input {
 	font-size: 26upx;
 	height: 54upx;
 }
-
 .cu-btn.lg {
 	padding: 0 40upx;
 	font-size: 32upx;

+ 137 - 5
assets/http/api.js

@@ -1,13 +1,145 @@
 import {http} from './service.js'
 
-let liveUrl={
-	page:'/blade-live-broadcast-type/livebroadcasttype/page'
+//文件上传
+let uploadFileUrl='/upload/putfile'
+const uploadFile={
+	submit:p => http.upload(uploadFileUrl, {name:'file',filePath:p}),
 }
 
-const live = {
-	page:p => http.get(liveUrl.page, {params:p}),
+//短信Url
+let SMSUrl={
+	sendSms:'huawei/sms/sendSms',
+	validCode:'huawei/sms/validCode'
 }
+//短信api
+const SMSApi = {
+	sendSms:p => http.post(SMSUrl.sendSms+p),
+	validCode:p => http.post(SMSUrl.validCode+p),
+}
+
+
+//微信基本参数
+let wxData={
+	appId:'wx4d345bcdef6d1c36',
+	secret:'a92e5d89c0ca8cfc0357576d76d87c23',
+	subscribe_grant_type:'client_credential', //订阅消息的grant_type
+}
+//微信url
+let wxUrl={
+	subscribe:'/wx/msgSend',
+	getAccessToken:'/wx/getAccessToken',
+	getOpenId:'/wx/getOpenId',
+}
+//微信api
+const wxApi = {
+	subscribe:p => http.post(wxUrl.subscribe, p),
+	getAccessToken:p => http.get(wxUrl.getAccessToken, {params:p}),
+	getOpenId:p => http.get(wxUrl.getOpenId, {params:p}),
+}
+
+
+//园区api
+let agencyUrl={
+	page:'/agency/agency/getAgencyList'
+}
+const agency = {
+	page:p => http.get(agencyUrl.page, {params:p}),
+}
+//区域api
+let residentialUrl={
+	page:'/community/residential/listByAgencyId'
+}
+const residential = {
+	page:p => http.get(residentialUrl.page, {params:p}),
+}
+
+//企业api
+let enterpriseUrl={
+	page:'/enterprise/enterprise/list',
+	detail:'/enterprise/enterprise/detail',
+	submit:'/enterprise/enterprise/submit'
+}
+const enterprise = {
+	page:p => http.get(enterpriseUrl.page, {params:p}),
+	detail:p => http.get(enterpriseUrl.detail, {params:p}),
+	submit:p => http.post(enterpriseUrl.submit, p),
+}
+
+//企业员工
+let enterprisestaffUrl={
+	page:'/community/enterprisestaff/getEnterpriseList',
+	submit:'/community/enterprisestaff/submit',
+	detail:'/community/enterprisestaff/detail'
+}
+const enterprisestaff = {
+	page:p => http.get(enterprisestaffUrl.page, {params:p}),
+	detail:p => http.get(enterprisestaffUrl.detail, {params:p}),
+	submit:p => http.post(enterprisestaffUrl.submit, p),
+}
+
+//CMS
+let CMSUrl={
+	page:'/estate/article/list',
+	detail:'/estate/article/detail',
+	addReaderCount:'/estate/article/addReaderCount'
+}
+let CMS={
+	page:p => http.get(CMSUrl.page, {params:p}),
+	detail:p => http.get(CMSUrl.detail, {params:p}),
+	addReaderCount:p => http.post(CMSUrl.addReaderCount, p),
+}
+
+//消防设备
+let fireDeviceUrl={
+	page:'/smartapplication/smartdevice/list',
+	alarmRecord:'/smartapplication/smartdevice/getAlarmRecord'
+}
+let fireDevice={
+	page:p => http.get(fireDeviceUrl.page, {params:p}),
+	alarmRecord:p => http.get(fireDeviceUrl.alarmRecord, {params:p}),
+}
+//用户反馈
+let feedbackUrl={
+	submit:'/estate/feedback/submit'
+}
+const feedback = {
+	submit:p => http.post(feedbackUrl.submit, p),
+}
+//园区公告
+let noticeUrl={
+	page:'/estate/parknotice/list'
+}
+const notice = {
+	page:p => http.get(noticeUrl.page, {params:p}),
+}
+
+
 
 export const api={
-	live
+	//上传文件
+	uploadFile,
+	//短信
+	SMSApi,
+	//微信基本参数
+	wxData,
+	//微信api
+	wxApi,
+	//园区
+	agency,
+	//区域
+	residential,
+	//企业
+	enterprise,
+	//企业员工
+	enterprisestaff,
+	//CMS
+	CMS,
+	//消防设备
+	fireDevice,
+	//用户反馈
+	feedback,
+	//园区公告
+	notice
+	
+	
 }

+ 18 - 6
assets/http/service.js

@@ -1,13 +1,24 @@
 import Request from '../../utils/my-request/index.js'
+//w3 wifi
+// let baseURL='http://192.168.0.105:9527/'
+//w2 wifi
+// let baseURL='http://192.168.0.101:9527/'
+//思奇特
+// let baseURL='http://192.168.2.229:9527/'
 
-let tokenUrl="https://cms.gzhylwh.com/api/blade-auth/oauth/token?tenantId=000000&password=21232f297a57a5a743894a0e4a801fc3&username=admin"
-let baseURL='https://cms.gzhylwh.com/api'
+//远程
+// let baseURL='http://139.9.103.171:9527/'
+let baseURL='https://park.58fo.com/api/'
+
+
+let tokenUrl=baseURL+"blade-auth/oauth/token?tenantId=804981&password=ea3c40c0df5477c525543b0e85548004&username=wpadmin"
+
+//获取token
 const getTokenStorage = () => {
   let token = ''
   try {
     token = uni.getStorageSync('token')
   } catch (e) {
-    //TODO handle the exception
   }
   return token
 }
@@ -33,7 +44,7 @@ let isRefreshing = false
 // 重试队列,每一项将是一个待执行的函数形式
 let requests = []
 http.interceptors.response.use(async (response) => { /* 请求之后拦截器。可以使用async await 做异步操作  */
-  //toekn过期处理 
+	//toekn过期处理 
       if (response.data.code == 401) {
           let {config} = response
           if (!isRefreshing) {
@@ -43,7 +54,7 @@ http.interceptors.response.use(async (response) => { /* 请求之后拦截器。
                   url: tokenUrl,
                   method: 'POST',
                   header:{
-                  	Authorization:"Basic d3hhcHA6d3hhcHBfc2Vja2V0"
+                  	Authorization:"Basic c2FiZXI6c2FiZXJfc2VjcmV0"
                   },
               })
               //否则保存新的token
@@ -63,7 +74,8 @@ http.interceptors.response.use(async (response) => { /* 请求之后拦截器。
               })
           }
       }
-      if (response.data.code != 200) { // 服务端返回的状态码不等于200,则reject()
+	  //200 返回数据成功 0 上传文件成功 400 没有相关数据
+      if (response.data.code != 200 && response.data.code !=400 && response.data.code !=0) {
         return Promise.reject(response)
       }
       return response.data

+ 32 - 0
assets/js/tabbar.js

@@ -0,0 +1,32 @@
+export  const tabbar = {
+		inactiveColor:"#8a8a8a",
+		activeColor:"#59a5f0",
+		bgColor:"#ffffff",
+		midButton:true,
+		iconSize:44,
+		MinButtonSize:70,
+		borderTop:true,
+		list:[{
+				iconPath: "/static/tarbar/index0.png",
+				selectedIconPath: "/static/tarbar/index.png",
+				text: '园区',
+				isDot: true,
+				pagePath: "/pages/index/index"
+			},
+			{
+				iconPath: "/static/tarbar/menjin0.png",
+				selectedIconPath: "/static/tarbar/menjin.png",
+				text: '门禁',
+				midButton: true,
+				pagePath: "/pages/device/device"
+			},
+			{
+				iconPath: "/static/tarbar/my0.png",
+				selectedIconPath: "/static/tarbar/my.png",
+				text: '我的',
+				pagePath: "/pages/mine/mine"
+			},
+		]
+}
+// export default tabbar;
+

+ 9 - 4
components/hot-consult/hot-consult.vue

@@ -6,10 +6,10 @@
 					<view class="cu-avatar text-font"  style="background-image:url(http://139.9.103.171:1888/miniofile/app/notice.png);"></view>
 					<view class="margin-top-xs" style="width: 680rpx">
 						<swiper :circular="true" class="swiper" autoplay="true" vertical="true" interval="5000" next-margin="46rpx">
-							<swiper-item v-for="(item, index) in swiperTexts" :key="index" @click="detailTap(item.id)">
+							<swiper-item v-for="(item, index) in swiperTexts" :key="index" @click="detailTap(item)">
 								<view class="swiper-item" > 
 									<text class="text-orange cuIcon-title padding-right-sm"></text>
-									<text>{{item.title}}</text>
+									<text>{{item.title}} : {{item.notice | formatHtml}}</text>
 								</view>
 							</swiper-item>
 						</swiper>
@@ -33,12 +33,17 @@
 				}
 			},
 		},
+		filters: {
+			formatHtml (str) {
+				return str.replace(/<[^>]+>/g, "");
+			}
+		},
 		methods: {
 			onTap() {
 				this.$emit('onTap');
 			},
-			detailTap(id) {
-				this.$emit('detailTap',id);
+			detailTap(item) {
+				this.$emit('detailTap',item);
 			}
 		}
 	}

+ 175 - 0
components/infomation/infomation.vue

@@ -0,0 +1,175 @@
+<template>
+	<view class="uni-list" >
+	    <view @click="goDetail(item)"  class="uni-list-cell"  hover-class="uni-list-cell-hover" v-for="(item,index) in newList" :key="index">
+	        <view class="uni-media-list" >
+				<!-- 左图右文 -->
+				<image   mode="aspectFill" class="uni-media-list-logo-left" :src="item.image"></image>
+	            <view class="uni-media-list-body" style="height: 180rpx;">
+	                <view class="uni-media-list-text-top text-cut-2">{{item.title}}</view>
+					<view class="text-sm text-cut-2" style="color: #464646;">
+						<text>{{item.article | formatHtml}}</text>
+						<!-- <rich-text :nodes="item.article"></rich-text> -->
+					</view>
+	                <view class="uni-media-list-text-bottom">
+						<text class="cuIcon-time padding-right-10"></text>
+	                    <text>{{item.createTime}}</text>
+	                </view>
+	            </view>
+	        </view>
+	    </view>
+	</view>
+</template>
+
+<script>
+	export default {
+		props:{
+			newList:{
+				type:Array,
+				default:()=>{
+					return []
+				}
+			}
+		},
+		filters: {
+			formatHtml (str) {
+				return str.replace(/<[^>]+>/g, "");
+			}
+		},
+		data() {
+			return {
+				
+			};
+		},
+		onLoad() {
+			
+		},
+		methods:{
+			goDetail(item){
+				getApp().globalData.newsDetail=item
+				uni.navigateTo({
+					url:"/pages/news/detail?id="+item.id
+				})
+			},
+		}
+	};
+</script>
+<style lang="scss">
+	.uni-list {
+	    background-color: #FFFFFF;
+	    position: relative;
+	    width: 100%;
+	    display: flex;
+	    flex-direction: column;
+	}
+	
+	.uni-list:after {
+	    position: absolute;
+	    z-index: 10;
+	    right: 0;
+	    bottom: 0;
+	    left: 0;
+	    height: 1px;
+	    content: '';
+	    -webkit-transform: scaleY(.5);
+	    transform: scaleY(.5);
+	    // background-color: #c8c7cc;
+	}
+	
+	.uni-list::before {
+	    position: absolute;
+	    z-index: 10;
+	    right: 0;
+	    top: 0;
+	    left: 0;
+	    height: 1px;
+	    content: '';
+	    -webkit-transform: scaleY(.5);
+	    transform: scaleY(.5);
+	}
+	
+	.uni-list-cell {
+	    position: relative;
+	    display: flex;
+	    flex-direction: row;
+	    justify-content: space-between;
+	    align-items: center;
+	}
+	
+	.uni-list-cell-hover {
+	    background-color: #eee;
+	}
+	
+	.uni-list-cell::after {
+	    position: absolute;
+	    z-index: 3;
+	    right: 0;
+	    bottom: 0;
+	    left: 30upx;
+	    height: 1px;
+	    content: '';
+	    -webkit-transform: scaleY(.5);
+	    transform: scaleY(.5);
+	    background-color: #c8c7cc;
+	}
+	
+	.uni-list .uni-list-cell:last-child::after {
+	    height: 0upx;
+	}
+	
+	/* 图文列表 */
+	.uni-media-list {
+	    padding: 22upx 20upx;
+	    box-sizing: border-box;
+	    display: flex;
+	    width: 100%;
+	    flex-direction: row;
+	}
+	
+	.uni-navigate-right.uni-media-list {
+	    padding-right: 74upx;
+	}
+	.uni-media-list-text-bottom {
+	    line-height: 30upx;
+	    font-size: 26upx;
+	    color: #8f8f94;
+	}
+	
+	.uni-media-list-logo-left {
+		border-radius: 10rpx;
+	    width: 230upx;
+	    height: 180upx;
+		margin-right: 20upx
+	}
+	
+	.uni-media-list-logo-right {
+	    width: 180upx;
+	    height: 140upx;
+		margin-left: 20upx
+	}
+	
+	.uni-media-list-body {
+	    display: flex;
+	    flex: 1;
+	    flex-direction: column;
+	    justify-content: space-between;
+	    align-items: flex-start;
+	    overflow: hidden;
+	    height: auto;
+	}
+	
+	.uni-media-list-text-top {
+	    width: 100%;
+	    line-height: 36upx;
+	    height: 50upx;
+	    font-size: 26upx;
+		font-weight: 800;
+	    overflow: hidden;
+	}
+	
+	.uni-media-list-text-bottom {
+	    display: flex;
+	    flex-direction: row;
+	    justify-content: space-between;
+	}
+</style>
+

+ 46 - 0
components/loading/loaders/rotate-plane.vue

@@ -0,0 +1,46 @@
+<template>
+  <view>
+    <view :style="{
+      backgroundColor: color,
+      width: size+20+'px',
+      height: size+20+'px' }" class="spinner-inside"></view>
+  </view>
+</template>
+
+<script>
+export default {
+  name: 'rotatePlane',
+  props: {
+    color: String,
+    size: Number
+  }
+}
+</script>
+
+<style scoped>
+.spinner-inside {
+  margin: 25px auto;
+ 
+  -webkit-animation: rotateplane 1.2s infinite ease-in-out;
+  animation: rotateplane 1.2s infinite ease-in-out;
+}
+ 
+@-webkit-keyframes rotateplane {
+  0% { -webkit-transform: perspective(120px) }
+  50% { -webkit-transform: perspective(120px) rotateY(180deg) }
+  100% { -webkit-transform: perspective(120px) rotateY(180deg)  rotateX(180deg) }
+}
+ 
+@keyframes rotateplane {
+  0% {
+    transform: perspective(120px) rotateX(0deg) rotateY(0deg);
+    -webkit-transform: perspective(120px) rotateX(0deg) rotateY(0deg)
+  } 50% {
+    transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg);
+    -webkit-transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg)
+  } 100% {
+    transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg);
+    -webkit-transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg);
+  }
+}
+</style>

+ 127 - 0
components/loading/loading.vue

@@ -0,0 +1,127 @@
+<template>
+  <transition name="fade">
+   <view
+			class="mask"
+      v-show="isActive"
+      :class="{ 'full-screen': isFullScreen }"
+      :style="{ backgroundColor }"
+    >
+      <view class="spinner" :style="{ transform:`translate(-50%, -${translateY}%)`}">
+        <slot>
+					<!--  ==========在这里改成选择的那一个========== -->
+					<rotatePlane :color="color" :size="size"/>
+					<!--  ==========在这里改成选择的那一个========== -->
+        </slot>
+        <view
+          v-if="text.length"
+          :style="{ color:textColor }">
+          {{ text }}
+        </view>
+      </view>
+    </view>
+  </transition>
+</template>
+
+<script>
+// ==========在这里选一个你要的其他去掉也行==========
+// import loop from './loaders/loop.vue'
+// import bounce from './loaders/bounce.vue'
+// import doubleBounce from './loaders/double-bounce.vue'
+// import doubleCube from './loaders/double-cube.vue'
+// import doubleDot from './loaders/double-dot.vue'
+import rotatePlane from './loaders/rotate-plane.vue'
+// import scaleOut from './loaders/scale-out.vue'
+// import shrinkRect from './loaders/shrink-rect.vue'
+// ==========在这里选一个你要的其他去掉也行==========
+
+export default {
+  name: 'ourLoading',
+  components: { 
+		// ==========在这里选择一个==========
+			// loop,
+			// bounce,
+			// doubleBounce,
+			// doubleCube,
+			// doubleDot,
+			rotatePlane,
+			// scaleOut,
+			// shrinkRect
+		// ==========在这里选择一个==========
+	},
+  props: {
+    active: Boolean,
+		translateY: {
+			type: Number,
+			default: 150
+		},
+    text: {
+      type: String,
+      default: ''
+    },
+    color: {
+      type: String,
+      default: '#333'
+    },
+		textColor: {
+			type: String,
+			default: '#333'
+		},
+    isFullScreen: {
+      type: Boolean,
+      default: false
+    },
+    backgroundColor: {
+      type: String,
+      default: 'rgba(255, 255, 255, .6)'
+    },
+    size: {
+      type: Number,
+      default: 40
+    }
+  },
+  data () {
+    return {
+      isActive: this.active || false
+    }
+  },
+  watch: {
+    active (value) {
+      this.isActive = value
+    }
+  }
+}
+</script>
+
+<style scoped>
+.mask {
+	position: absolute;
+	left: 0;
+	right: 0;
+	top: 0;
+	bottom: 0;
+	z-index: 3000;
+	transition: opacity .3s linear;
+}
+
+.full-screen {
+  position: fixed;
+  top: 0;
+  right: 0;
+  bottom: 0;
+  left: 0;
+}
+
+.spinner {
+	position: absolute;
+	top: 50%;
+	left: 50%;
+	text-align: center;
+}
+
+.fade-enter-active, .fade-leave-active {
+  transition: opacity .3s;
+}
+.fade-enter, .fade-leave-to {
+  opacity: 0;
+}
+</style>

+ 55 - 0
components/mescroll-body/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-body/components/mescroll-down.vue

@@ -0,0 +1,47 @@
+<!-- 下拉刷新区域 -->
+<template>
+	<view v-if="mOption.use" class="mescroll-downwarp" :style="{'background-color':mOption.bgColor,'color':mOption.textColor}">
+		<view class="downwarp-content">
+			<view class="downwarp-progress" :class="{'mescroll-rotate': isDownLoading}" :style="{'border-color':mOption.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>

+ 90 - 0
components/mescroll-body/components/mescroll-empty.vue

@@ -0,0 +1,90 @@
+<!--空布局
+
+可作为独立的组件, 不使用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 }">
+		<view> <image v-if="icon" class="empty-icon" :src="icon" mode="widthFix" /> </view>
+		<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: 300rpx 50rpx 0;
+	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 {
+	margin-top: 20rpx;
+	font-size: 24rpx;
+	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>

+ 83 - 0
components/mescroll-body/components/mescroll-top.vue

@@ -0,0 +1,83 @@
+<!-- 回到顶部的按钮 -->
+<template>
+	<image
+		v-if="mOption.src"
+		class="mescroll-totop"
+		:class="[value ? 'mescroll-totop-in' : 'mescroll-totop-out', {'mescroll-totop-safearea': 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 */
+@supports (bottom: constant(safe-area-inset-bottom)) or (bottom: env(safe-area-inset-bottom)) {
+	.mescroll-totop-safearea {
+		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>

+ 47 - 0
components/mescroll-body/components/mescroll-up.css

@@ -0,0 +1,47 @@
+/* 上拉加载区域 */
+.mescroll-upwarp {
+	box-sizing: border-box;
+	min-height: 110rpx;
+	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-body/components/mescroll-up.vue

@@ -0,0 +1,39 @@
+<!-- 上拉加载区域 -->
+<template>
+	<view class="mescroll-upwarp" :style="{'background-color':mOption.bgColor,'color':mOption.textColor}">
+		<!-- 加载中 (此处不能用v-if,否则android小程序快速上拉可能会不断触发上拉回调) -->
+		<view v-show="isUpLoading">
+			<view class="upwarp-progress mescroll-rotate" :style="{'border-color':mOption.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>

+ 19 - 0
components/mescroll-body/mescroll-body.css

@@ -0,0 +1,19 @@
+.mescroll-body {
+	position: relative; /* 下拉刷新区域相对自身定位 */
+	height: auto; /* 不可固定高度,否则overflow:hidden导致无法滑动; 同时使设置的最小高生效,实现列表不满屏仍可下拉*/
+	overflow: hidden; /* 当有元素写在mescroll-body标签前面时,可遮住下拉刷新区域 */
+	box-sizing: border-box; /* 避免设置padding出现双滚动条的问题 */
+}
+
+/* 使sticky生效: 父元素不能overflow:hidden或者overflow:auto属性 */
+.mescroll-body.mescorll-sticky{
+	overflow: unset !important
+}
+
+/* 适配 iPhoneX */
+@supports (bottom: constant(safe-area-inset-bottom)) or (bottom: env(safe-area-inset-bottom)) {
+	.mescroll-safearea {
+		padding-bottom: constant(safe-area-inset-bottom);
+		padding-bottom: env(safe-area-inset-bottom);
+	}
+}

+ 348 - 0
components/mescroll-body/mescroll-body.vue

@@ -0,0 +1,348 @@
+<template>
+	<view 
+	class="mescroll-body mescroll-render-touch" 
+	:class="{'mescorll-sticky': sticky}"
+	:style="{'minHeight':minHeight, 'padding-top': padTop, 'padding-bottom': padBottom}" 
+	@touchstart="wxsBiz.touchstartEvent" 
+	@touchmove="wxsBiz.touchmoveEvent" 
+	@touchend="wxsBiz.touchendEvent" 
+	@touchcancel="wxsBiz.touchendEvent"
+	:change:prop="wxsBiz.propObserver"
+	:prop="wxsProp"
+	>
+		<!-- 状态栏 -->
+		<view v-if="topbar&&statusBarHeight" class="mescroll-topbar" :style="{height: statusBarHeight+'px', background: topbar}"></view>
+		
+		<view class="mescroll-body-content mescroll-wxs-content" :style="{ transform: translateY, transition: transition }" :change:prop="wxsBiz.callObserver" :prop="callProp">
+			<!-- 下拉加载区域 (支付宝小程序子组件传参给子子组件仍报单项数据流的异常,暂时不通过mescroll-down组件实现)-->
+			<!-- <mescroll-down :option="mescroll.optDown" :type="downLoadType" :rate="downRate"></mescroll-down> -->
+			<view v-if="mescroll.optDown.use" class="mescroll-downwarp" :style="{'background':mescroll.optDown.bgColor,'color':mescroll.optDown.textColor}">
+				<view class="downwarp-content">
+					<view class="downwarp-progress mescroll-wxs-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 && upLoadType!==3" :option="mescroll.optUp" :type="upLoadType"></mescroll-up> -->
+			<view v-if="mescroll.optUp.use && !isDownLoading && upLoadType!==3" class="mescroll-upwarp" :style="{'background':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>
+		
+		<!-- 底部是否偏移TabBar的高度(默认仅在H5端的tab页生效) -->
+		<!-- #ifdef H5 -->
+		<view v-if="bottombar && windowBottom>0" class="mescroll-bottombar" :style="{height: windowBottom+'px'}"></view>
+		<!-- #endif -->
+		
+		<!-- 适配iPhoneX -->
+		<view v-if="safearea" class="mescroll-safearea"></view>
+		
+		<!-- 回到顶部按钮 (fixed元素需写在transform外面,防止降级为absolute)-->
+		<mescroll-top v-model="isShowToTop" :option="mescroll.optUp.toTop" @click="toTopClick"></mescroll-top>
+		
+		<!-- #ifdef MP-WEIXIN || MP-QQ || APP-PLUS || H5 -->
+		<!-- renderjs的数据载体,不可写在mescroll-downwarp内部,避免use为false时,载体丢失,无法更新数据 -->
+		<view :change:prop="renderBiz.propObserver" :prop="wxsProp"></view>
+		<!-- #endif -->
+	</view>
+</template>
+
+<!-- 微信小程序, QQ小程序, app, h5使用wxs -->
+<!-- #ifdef MP-WEIXIN || MP-QQ || APP-PLUS || H5 -->
+<script src="./wxs/wxs.wxs" module="wxsBiz" lang="wxs"></script>
+<!-- #endif -->
+
+<!-- app, h5使用renderjs -->
+<!-- #ifdef APP-PLUS || H5 -->
+<script module="renderBiz" lang="renderjs">
+	import renderBiz from './wxs/renderjs.js';
+	export default {
+		mixins: [renderBiz]
+	}
+</script>
+<!-- #endif -->
+
+<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';
+	// 引入兼容wxs(含renderjs)写法的mixins
+	import WxsMixin from './wxs/mixins.js';
+	
+	export default {
+		mixins: [WxsMixin],
+		components: {
+			MescrollEmpty,
+			MescrollTop
+		},
+		data() {
+			return {
+				mescroll: {optDown:{},optUp:{}}, // mescroll实例
+				downHight: 0, //下拉刷新: 容器高度
+				downRate: 0, // 下拉比率(inOffset: rate<1; outOffset: rate>=1)
+				downLoadType: 0, // 下拉刷新状态: 0(loading前), 1(inOffset), 2(outOffset), 3(showLoading), 4(endDownScroll)
+				upLoadType: 0, // 上拉加载状态:0(loading前),1(loading中),2(没有更多了,显示END文本提示),3(没有更多了,不显示END文本提示)
+				isShowEmpty: false, // 是否显示空布局
+				isShowToTop: false, // 是否显示回到顶部按钮
+				windowHeight: 0, // 可使用窗口的高度
+				windowBottom: 0, // 可使用窗口的底部位置
+				statusBarHeight: 0 // 状态栏高度
+			};
+		},
+		props: {
+			down: Object, // 下拉刷新的参数配置
+			up: Object, // 上拉加载的参数配置
+			top: [String, Number], // 下拉布局往下的偏移量 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
+			topbar: [Boolean, String], // top的偏移量是否加上状态栏高度, 默认false (使用场景:取消原生导航栏时,配置此项可留出状态栏的占位, 支持传入字符串背景,如色值,背景图,渐变)
+			bottom: [String, Number], // 上拉布局往上的偏移量 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
+			safearea: Boolean, // bottom的偏移量是否加上底部安全区的距离, 默认false (需要适配iPhoneX时使用)
+			height: [String, Number], // 指定mescroll最小高度,默认windowHeight,使列表不满屏仍可下拉
+			bottombar:{ // 底部是否偏移TabBar的高度(默认仅在H5端的tab页生效)
+				type: Boolean,
+				default: true
+			},
+			sticky: Boolean // 是否支持sticky,默认false; 当值配置true时,需避免在mescroll-body标签前面加非定位的元素,否则下拉区域无法会隐藏
+		},
+		computed: {
+			// mescroll最小高度,默认windowHeight,使列表不满屏仍可下拉
+			minHeight(){
+				return this.toPx(this.height || '100%') + 'px'
+			},
+			// 下拉布局往下偏移的距离 (px)
+			numTop() {
+				return this.toPx(this.top)
+			},
+			padTop() {
+				return this.numTop + 'px';
+			},
+			// 上拉布局往上偏移 (px)
+			numBottom() {
+				return this.toPx(this.bottom);
+			},
+			padBottom() {
+				return this.numBottom + 'px';
+			},
+			// 是否为重置下拉的状态
+			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(){
+				if(!this.mescroll) return ""; // 避免头条小程序初始化时报错
+				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.isDownEndSuccess ? this.mescroll.optDown.textSuccess : this.mescroll.isDownEndSuccess==false ? this.mescroll.optDown.textErr : this.mescroll.optDown.textInOffset;
+					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;
+			},
+			// 点击空布局的按钮回调
+			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() {
+						vm.downLoadType = 1; // 下拉的距离进入offset范围内那一刻的回调 (自定义mescroll组件时,此行不可删)
+					},
+					outOffset() {
+						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组件时,此行不可删)
+					},
+					beforeEndDownScroll(mescroll){
+						vm.downLoadType = 4; 
+						return mescroll.optDown.beforeEndDelay // 延时结束的时长
+					},
+					endDownScroll() {
+						vm.downLoadType = 4; // 结束下拉 (自定义mescroll组件时,此行不可删)
+						vm.downHight = 0; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
+						if(vm.downResetTimer) {clearTimeout(vm.downResetTimer); vm.downResetTimer = null} // 移除重置倒计时
+						vm.downResetTimer = setTimeout(()=>{ // 过渡动画执行完毕后,需重置为0的状态,避免下次inOffset不及时显示textInOffset
+							if(vm.downLoadType === 4) vm.downLoadType = 0
+						},300)
+					},
+					// 派发下拉刷新的回调
+					callback: function(mescroll) {
+						vm.$emit('down', mescroll);
+					}
+				},
+				// 上拉加载的配置
+				up: {
+					// 显示加载中的回调
+					showLoading() {
+						vm.upLoadType = 1;
+					},
+					// 显示无更多数据的回调
+					showNoMore() {
+						vm.upLoadType = 2;
+					},
+					// 隐藏上拉加载的回调
+					hideUpScroll(mescroll) {
+						vm.upLoadType = mescroll.optUp.hasNext ? 0 : 3;
+					},
+					// 空布局
+					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.windowBottom) vm.windowBottom = sys.windowBottom;
+			if (sys.statusBarHeight) vm.statusBarHeight = sys.statusBarHeight;
+			// 使down的bottomOffset生效
+			vm.mescroll.setBodyHeight(sys.windowHeight);
+
+			// 因为使用的是page的scroll,这里需自定义scrollTo
+			vm.mescroll.resetScrollTo((y, t) => {
+				if(typeof y === 'string'){
+					// 滚动到指定view (y为css选择器)
+					setTimeout(()=>{ // 延时确保view已渲染; 不使用$nextTick
+						let selector;
+						if(y.indexOf('#')==-1 && y.indexOf('.')==-1){
+							selector = '#'+y // 不带#和. 则默认为id选择器
+						}else{
+							selector = y
+							// #ifdef APP-PLUS || H5 || MP-ALIPAY || MP-DINGTALK
+							if(y.indexOf('>>>')!=-1){ // 不支持跨自定义组件的后代选择器 (转为普通的选择器即可跨组件查询)
+								selector = y.split('>>>')[1].trim()
+							}
+							// #endif
+						}
+						uni.createSelectorQuery().select(selector).boundingClientRect(function(rect){
+							if (rect) {
+								let top = rect.top
+								top += vm.mescroll.getScrollTop()
+								uni.pageScrollTo({
+									scrollTop: top,
+									duration: t
+								})
+							} else{
+								console.error(selector + ' does not exist');
+							}
+						}).exec()
+					},30)
+				} else{
+					// 滚动到指定位置 (y必须为数字)
+					uni.pageScrollTo({
+						scrollTop: y,
+						duration: t
+					})
+				}
+			});
+
+			// 具体的界面如果不配置up.toTop.safearea,则取本vue的safearea值
+			if (vm.up && vm.up.toTop && vm.up.toTop.safearea != null) {} else {
+				vm.mescroll.optUp.toTop.safearea = vm.safearea;
+			}
+		}
+	};
+</script>
+
+<style>
+	@import "./mescroll-body.css";
+	@import "./components/mescroll-down.css";
+	@import './components/mescroll-up.css';
+</style>

+ 65 - 0
components/mescroll-body/mescroll-mixins.js

@@ -0,0 +1,65 @@
+// 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对象 (兼容字节跳动小程序)
+		mescrollInitByRef() {
+			if(!this.mescroll || !this.mescroll.resetUpScroll){
+				let mescrollRef = this.$refs.mescrollRef;
+				if(mescrollRef) this.mescroll = mescrollRef.mescroll
+			}
+		},
+		// 下拉刷新的回调 (mixin默认resetUpScroll)
+		downCallback() {
+			if(this.mescroll.optUp.use){
+				this.mescroll.resetUpScroll()
+			}else{
+				setTimeout(()=>{
+					this.mescroll.endSuccess();
+				}, 500)
+			}
+		},
+		// 上拉加载的回调
+		upCallback() {
+			// mixin默认延时500自动结束加载
+			setTimeout(()=>{
+				this.mescroll.endErr();
+			}, 500)
+		}
+	},
+	mounted() {
+		this.mescrollInitByRef(); // 兼容字节跳动小程序, 避免未设置@init或@init此时未能取到ref的情况
+	}
+	
+}
+
+export default MescrollMixin;

+ 36 - 0
components/mescroll-body/mescroll-uni-option.js

@@ -0,0 +1,36 @@
+// 全局配置
+// mescroll-body 和 mescroll-uni 通用
+const GlobalOption = {
+	down: {
+		// 其他down的配置参数也可以写,这里只展示了常用的配置:
+		textInOffset: '下拉刷新', // 下拉的距离在offset范围内的提示文本
+		textOutOffset: '释放更新', // 下拉的距离大于offset范围的提示文本
+		textLoading: '加载中 ...', // 加载中的提示文本
+		textSuccess: '加载成功', // 加载成功的文本
+		textErr: '加载失败', // 加载失败的文本
+		beforeEndDelay: 100, // 延时结束的时长 (显示加载成功/失败的时长)
+		offset: 80, // 在列表顶部,下拉大于80px,松手即可触发下拉刷新的回调
+		native: false // 是否使用系统自带的下拉刷新; 默认false; 仅在mescroll-body生效 (值为true时,还需在pages配置enablePullDownRefresh:true;详请参考mescroll-native的案例)
+	},
+	up: {
+		// 其他up的配置参数也可以写,这里只展示了常用的配置:
+		textLoading: '加载中 ...', // 加载中的提示文本
+		textNoMore: '到底啦', // 没有更多数据的提示文本
+		offset: 150, // 距底部多远时,触发upCallback,仅mescroll-uni生效 ( mescroll-body配置的是pages.json的 onReachBottomDistance )
+		toTop: {
+			// 回到顶部按钮,需配置src才显示
+			src: "https://www.mescroll.com/img/mescroll-totop.png", // 图片路径 (建议放入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://139.9.103.171:1888/miniofile/xlyq/empty/empty.png", // 图标路径 (建议放入static目录, 如 /static/img/mescroll-empty.png )
+			tip: '数据为空' // 提示
+		}
+	}
+}
+
+export default GlobalOption

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

@@ -0,0 +1,36 @@
+.mescroll-uni-warp{
+	height: 100%;
+}
+
+.mescroll-uni-content{
+	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生效 */
+}
+
+/* 适配 iPhoneX */
+@supports (bottom: constant(safe-area-inset-bottom)) or (bottom: env(safe-area-inset-bottom)) {
+	.mescroll-safearea {
+		padding-bottom: constant(safe-area-inset-bottom);
+		padding-bottom: env(safe-area-inset-bottom);
+	}
+}

+ 799 - 0
components/mescroll-body/mescroll-uni.js

@@ -0,0 +1,799 @@
+/* mescroll
+ * version 1.3.3
+ * 2020-09-15 wenju
+ * https://www.mescroll.com
+ */
+
+export default function MeScroll(options, isScrollBody) {
+	let me = this;
+	me.version = '1.3.3'; // 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.native) && me.optDown.auto && hasDownCallback) {
+			if (me.optDown.autoShowLoading) {
+				me.triggerDownScroll(); // 显示下拉进度,执行下拉回调
+			} else {
+				me.optDown.callback && me.optDown.callback(me); // 不显示下拉进度,直接执行下拉回调
+			}
+		}
+		// 自动触发上拉加载
+		if(!me.isUpAutoLoad){ // 部分小程序(头条小程序)emit是异步, 会导致isUpAutoLoad判断有误, 先延时确保先执行down的callback,再执行up的callback
+			setTimeout(function(){
+				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, 此值用于控制最大的误差
+		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: '加载中 ...', // 加载中的提示文本
+		textSuccess: '加载成功', // 加载成功的文本
+		textErr: '加载失败', // 加载失败的文本
+		beforeEndDelay: 100, // 延时结束的时长 (显示加载成功/失败的时长)
+		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, // 显示下拉刷新进度的回调之后,马上要执行的代码 (如: 在wxs中使用)
+		beforeEndDownScroll: null, // 准备结束下拉的回调. 返回结束下拉的延时执行时间,默认0ms; 常用于结束下拉之前再显示另外一小段动画,才去隐藏下拉刷新的场景, 参考案例【dotJump】
+		endDownScroll: null, // 结束下拉刷新的回调
+		afterEndDownScroll: null, // 结束下拉刷新的回调,马上要执行的代码 (如: 在wxs中使用)
+		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,两者可同时触发;
+		callback: null, // 上拉加载的回调;function(page,mescroll){ }
+		page: {
+			num: 0, // 当前页码,默认0,回调之前会加1,即callback(page)会从1开始
+			size: 10, // 每页数据的数量
+			time: null // 加载第一页数据服务器返回的时间; 防止用户翻页时,后台新增了数据从而导致下一页数据重复;
+		},
+		noMoreSize: 5, // 如果列表已无数据,可设置列表的总数量要大于等于5条才显示无更多数据;避免列表数据过少(比如只有一条数据),显示无更多数据会不好看
+		offset: 150, // 距底部多远时,触发upCallback,仅mescroll-uni生效 ( mescroll-body配置的是pages.json的 onReachBottomDistance )
+		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.startAngle = 0; // 初始角度
+	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) {
+	if (!this.optDown.use) return;
+	let me = this;
+
+	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,也有可能大于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))) {
+
+			// 下拉的初始角度是否在配置的范围内
+			if(!me.startAngle) me.startAngle = me.getAngle(me.lastPoint, curPoint); // 两点之间的角度,区间 [0,90]
+			if (me.startAngle < me.optDown.minAngle) return; // 如果小于配置的角度,则不往下执行下拉刷新
+
+			// 如果手指的位置超过配置的距离,则提前结束下拉,避免Webview嵌套导致touchend无法触发
+			if (me.maxTouchmoveY > 0 && curPoint.y >= me.maxTouchmoveY) {
+				me.inTouchend = true; // 标记执行touchend
+				me.touchendEvent(); // 提前触发touchend
+				return;
+			}
+			
+			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.isDownEndSuccess = null; // 重置是否加载成功的状态 (wxs执行的是wxs.wxs)
+					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 += diff * me.optDown.outOffsetRate; // 越往下,高度变化越小
+				} else { // 向上收
+					me.downHight += diff; // 向上收回高度,则向上滑多少收多少高度
+				}
+			}
+			
+			me.downHight = Math.round(me.downHight) // 取整
+			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.endDownScrollCall(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.native && this.optDown.callback && this.optDown.callback(this); // 执行回调,联网加载数据
+	}
+}
+
+/* 显示下拉进度布局 */
+MeScroll.prototype.showDownScroll = function() {
+	this.isDownScrolling = true; // 标记下拉中
+	if (this.optDown.native) {
+		uni.startPullDownRefresh(); // 系统自带的下拉刷新
+		this.showDownLoadingCall(0); // 仍触发showLoading,因为上拉加载用到
+	} else{
+		this.downHight = this.optDown.offset; // 更新下拉区域高度
+		this.showDownLoadingCall(this.downHight); // 下拉刷新中...
+	}
+}
+
+MeScroll.prototype.showDownLoadingCall = function(downHight) {
+	this.optDown.showLoading && this.optDown.showLoading(this, downHight); // 下拉刷新中...
+	this.optDown.afterLoading && this.optDown.afterLoading(this, downHight); // 下拉刷新中...触发之后马上要执行的代码
+}
+
+/* 显示系统自带的下拉刷新时需要处理的业务 */
+MeScroll.prototype.onPullDownRefresh = function() {
+	this.isDownScrolling = true; // 标记下拉中
+	this.showDownLoadingCall(0); // 仍触发showLoading,因为上拉加载用到
+	this.optDown.callback && this.optDown.callback(this); // 执行回调,联网加载数据
+}
+
+/* 结束下拉刷新 */
+MeScroll.prototype.endDownScroll = function() {
+	if (this.optDown.native) { // 结束原生下拉刷新
+		this.isDownScrolling = false;
+		this.endDownScrollCall(this);
+		uni.stopPullDownRefresh();
+		return
+	}
+	let me = this;
+	// 结束下拉刷新的方法
+	let endScroll = function() {
+		me.downHight = 0;
+		me.isDownScrolling = false;
+		me.endDownScrollCall(me);
+		if(!me.isScrollBody){
+			me.setScrollHeight(0) // scroll-view重置滚动区域,使数据不满屏时仍可检查触发翻页
+			me.scrollTo(0,0) // scroll-view需重置滚动条到顶部,避免startTop大于0时,对下拉刷新的影响
+		}
+	}
+	// 结束下拉刷新时的回调
+	let delay = 0;
+	if (me.optDown.beforeEndDownScroll) {
+		delay = me.optDown.beforeEndDownScroll(me); // 结束下拉刷新的延时,单位ms
+		if(me.isDownEndSuccess == null) delay = 0; // 没有执行加载中,则不延时
+	}
+	if (typeof delay === 'number' && delay > 0) {
+		setTimeout(endScroll, delay);
+	} else {
+		endScroll();
+	}
+}
+
+MeScroll.prototype.endDownScrollCall = function() {
+	this.optDown.endDownScroll && this.optDown.endDownScroll(this);
+	this.optDown.afterEndDownScroll && this.optDown.afterEndDownScroll(this);
+}
+
+/* 锁定下拉刷新: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.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.isDownEndSuccess = true
+		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) {
+		this.isDownEndSuccess = false
+		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, 已在wxs中禁止
+	// app的bounce只能通过配置pages.json的style.app-plus.bounce为"none"来禁止, 或使用renderjs禁止
+	// cancelable:是否可以被禁用; defaultPrevented:是否已经被禁用
+	if (e && e.cancelable && !e.defaultPrevented) e.preventDefault()
+}

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

@@ -0,0 +1,424 @@
+<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,'top':fixedTop,'bottom':fixedBottom}" :scroll-top="scrollTop" :scroll-with-animation="scrollAnim" @scroll="scroll" :scroll-y='scrollable' :enable-back-to-top="true" :throttle="false">
+			<view class="mescroll-uni-content mescroll-render-touch"
+			@touchstart="wxsBiz.touchstartEvent" 
+			@touchmove="wxsBiz.touchmoveEvent" 
+			@touchend="wxsBiz.touchendEvent" 
+			@touchcancel="wxsBiz.touchendEvent"
+			:change:prop="wxsBiz.propObserver"
+			:prop="wxsProp">
+				<!-- 状态栏 -->
+				<view v-if="topbar&&statusBarHeight" class="mescroll-topbar" :style="{height: statusBarHeight+'px', background: topbar}"></view>
+		
+				<view class="mescroll-wxs-content" :style="{'transform': translateY, 'transition': transition}" :change:prop="wxsBiz.callObserver" :prop="callProp">
+					<!-- 下拉加载区域 (支付宝小程序子组件传参给子子组件仍报单项数据流的异常,暂时不通过mescroll-down组件实现)-->
+					<!-- <mescroll-down :option="mescroll.optDown" :type="downLoadType" :rate="downRate"></mescroll-down> -->
+					<view v-if="mescroll.optDown.use" class="mescroll-downwarp" :style="{'background':mescroll.optDown.bgColor,'color':mescroll.optDown.textColor}">
+						<view class="downwarp-content">
+							<view class="downwarp-progress mescroll-wxs-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 && upLoadType!==3" :option="mescroll.optUp" :type="upLoadType"></mescroll-up> -->
+					<view v-if="mescroll.optUp.use && !isDownLoading && upLoadType!==3" class="mescroll-upwarp" :style="{'background':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>
+			
+				<!-- 底部是否偏移TabBar的高度(默认仅在H5端的tab页生效) -->
+				<!-- #ifdef H5 -->
+				<view v-if="bottombar && windowBottom>0" class="mescroll-bottombar" :style="{height: windowBottom+'px'}"></view>
+				<!-- #endif -->
+				
+				<!-- 适配iPhoneX -->
+				<view v-if="safearea" class="mescroll-safearea"></view>
+			</view>
+		</scroll-view>
+
+		<!-- 回到顶部按钮 (fixed元素,需写在scroll-view外面,防止滚动的时候抖动)-->
+		<mescroll-top v-model="isShowToTop" :option="mescroll.optUp.toTop" @click="toTopClick"></mescroll-top>
+		
+		<!-- #ifdef MP-WEIXIN || MP-QQ || APP-PLUS || H5 -->
+		<!-- renderjs的数据载体,不可写在mescroll-downwarp内部,避免use为false时,载体丢失,无法更新数据 -->
+		<view :change:prop="renderBiz.propObserver" :prop="wxsProp"></view>
+		<!-- #endif -->
+	</view>
+</template>
+
+<!-- 微信小程序, QQ小程序, app, h5使用wxs -->
+<!-- #ifdef MP-WEIXIN || MP-QQ || APP-PLUS || H5 -->
+<script src="./wxs/wxs.wxs" module="wxsBiz" lang="wxs"></script>
+<!-- #endif -->
+
+<!-- app, h5使用renderjs -->
+<!-- #ifdef APP-PLUS || H5 -->
+<script module="renderBiz" lang="renderjs">
+	import renderBiz from './wxs/renderjs.js';
+	export default {
+		mixins:[renderBiz]
+	}
+</script>
+<!-- #endif -->
+
+<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';
+	// 引入兼容wxs(含renderjs)写法的mixins
+	import WxsMixin from './wxs/mixins.js';
+	
+	export default {
+		mixins: [WxsMixin],
+		components: {
+			MescrollEmpty,
+			MescrollTop
+		},
+		data() {
+			return {
+				mescroll: {optDown:{},optUp:{}}, // mescroll实例
+				viewId: 'id_' + Math.random().toString(36).substr(2,16), // 随机生成mescroll的id(不能数字开头,否则找不到元素)
+				downHight: 0, //下拉刷新: 容器高度
+				downRate: 0, // 下拉比率(inOffset: rate<1; outOffset: rate>=1)
+				downLoadType: 0, // 下拉刷新状态: 0(loading前), 1(inOffset), 2(outOffset), 3(showLoading), 4(endDownScroll)
+				upLoadType: 0, // 上拉加载状态: 0(loading前), 1loading中, 2没有更多了,显示END文本提示, 3(没有更多了,不显示END文本提示)
+				isShowEmpty: false, // 是否显示空布局
+				isShowToTop: false, // 是否显示回到顶部按钮
+				scrollTop: 0, // 滚动条的位置
+				scrollAnim: false, // 是否开启滚动动画
+				windowTop: 0, // 可使用窗口的顶部位置
+				windowBottom: 0, // 可使用窗口的底部位置
+				windowHeight: 0, // 可使用窗口的高度
+				statusBarHeight: 0 // 状态栏高度
+			}
+		},
+		props: {
+			down: Object, // 下拉刷新的参数配置
+			up: Object, // 上拉加载的参数配置
+			top: [String, Number], // 下拉布局往下的偏移量 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
+			topbar: [Boolean, String], // top的偏移量是否加上状态栏高度, 默认false (使用场景:取消原生导航栏时,配置此项可留出状态栏的占位, 支持传入字符串背景,如色值,背景图,渐变)
+			bottom: [String, Number], // 上拉布局往上的偏移量 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
+			safearea: Boolean, // bottom的偏移量是否加上底部安全区的距离, 默认false (需要适配iPhoneX时使用)
+			fixed: { // 是否通过fixed固定mescroll的高度, 默认true
+				type: Boolean,
+				default: true
+			},
+			height: [String, Number], // 指定mescroll的高度, 此项有值,则不使用fixed. (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
+			bottombar:{ // 底部是否偏移TabBar的高度(默认仅在H5端的tab页生效)
+				type: Boolean,
+				default: true
+			}
+		},
+		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)
+			},
+			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
+			},
+			padBottom() {
+				return !this.isFixed ? this.numBottom + 'px' : 0
+			},
+			// 是否为重置下拉的状态
+			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之外
+			},
+			// 列表是否可滑动
+			scrollable(){
+				return this.downLoadType===0 || this.isDownReset
+			},
+			// 是否在加载中
+			isDownLoading(){
+				return this.downLoadType === 3
+			},
+			// 旋转的角度
+			downRotate(){
+				return 'rotate(' + 360 * this.downRate + 'deg)'
+			},
+			// 文本提示
+			downText(){
+				if(!this.mescroll) return ""; // 避免头条小程序初始化时报错
+				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.isDownEndSuccess ? this.mescroll.optDown.textSuccess : this.mescroll.isDownEndSuccess==false ? this.mescroll.optDown.textErr : this.mescroll.optDown.textInOffset;
+					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获取是否向上滑动
+				})
+			},
+			// 点击空布局的按钮回调
+			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已渲染
+						this.getClientInfo(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)
+							}
+						})
+					})
+				}
+			},
+			// 获取滚动区域的信息
+			getClientInfo(success){
+				let query = uni.createSelectorQuery();
+				// #ifndef MP-ALIPAY || MP-DINGTALK
+				query = query.in(this) // 支付宝小程序不支持in(this),而字节跳动小程序必须写in(this), 否则都取不到值
+				// #endif
+				let view = query.select('#' + this.viewId);
+				view.boundingClientRect(data => {
+					success(data)
+				}).exec();
+			}
+		},
+		// 使用created初始化mescroll对象; 如果用mounted部分css样式编译到H5会失效
+		created() {
+			let vm = this;
+
+			let diyOption = {
+				// 下拉刷新的配置
+				down: {
+					inOffset() {
+						vm.downLoadType = 1; // 下拉的距离进入offset范围内那一刻的回调 (自定义mescroll组件时,此行不可删)
+					},
+					outOffset() {
+						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组件时,此行不可删)
+					},
+					beforeEndDownScroll(mescroll){
+						vm.downLoadType = 4; 
+						return mescroll.optDown.beforeEndDelay // 延时结束的时长
+					},
+					endDownScroll() {
+						vm.downLoadType = 4; // 结束下拉 (自定义mescroll组件时,此行不可删)
+						vm.downHight = 0; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
+						vm.downResetTimer && clearTimeout(vm.downResetTimer)
+						vm.downResetTimer = setTimeout(()=>{ // 过渡动画执行完毕后,需重置为0的状态,以便置空this.transition,避免iOS小程序列表渲染不完整
+							if(vm.downLoadType===4) vm.downLoadType = 0
+						},300)
+					},
+					// 派发下拉刷新的回调
+					callback: function(mescroll) {
+						vm.$emit('down', mescroll)
+					}
+				},
+				// 上拉加载的配置
+				up: {
+					// 显示加载中的回调
+					showLoading() {
+						vm.upLoadType = 1;
+					},
+					// 显示无更多数据的回调
+					showNoMore() {
+						vm.upLoadType = 2;
+					},
+					// 隐藏上拉加载的回调
+					hideUpScroll(mescroll) {
+						vm.upLoadType = mescroll.optUp.hasNext ? 0 : 3;
+					},
+					// 空布局
+					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'){
+					// 小程序不支持slot里面的scroll-into-view, 统一使用计算的方式实现
+					vm.getClientInfo(function(rect){
+						let mescrollTop = rect.top // mescroll到顶部的距离
+						let selector;
+						if(y.indexOf('#')==-1 && y.indexOf('.')==-1){
+							selector = '#'+y // 不带#和. 则默认为id选择器
+						}else{
+							selector = y
+							// #ifdef APP-PLUS || H5 || MP-ALIPAY || MP-DINGTALK
+							if(y.indexOf('>>>')!=-1){ // 不支持跨自定义组件的后代选择器 (转为普通的选择器即可跨组件查询)
+								selector = y.split('>>>')[1].trim()
+							}
+							// #endif
+						}
+						uni.createSelectorQuery().select(selector).boundingClientRect(function(rect){
+							if (rect) {
+								let curY = vm.mescroll.getScrollTop()
+								let top = rect.top - mescrollTop
+								top += curY
+								if(!vm.isFixed) top -= vm.numTop
+								vm.scrollTop = curY;
+								vm.$nextTick(function() {
+									vm.scrollTop = top
+								})
+							} else{
+								console.error(selector + ' does not exist');
+							}
+						}).exec()
+					})
+					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 (vm.up && vm.up.toTop && vm.up.toTop.safearea != null) {} else {
+				vm.mescroll.optUp.toTop.safearea = vm.safearea;
+			}
+		},
+		mounted() {
+			// 设置容器的高度
+			this.setClientHeight()
+		}
+	}
+</script>
+
+<style>
+	@import "./mescroll-uni.css";
+	@import "./components/mescroll-down.css";
+	@import './components/mescroll-up.css';
+</style>

+ 48 - 0
components/mescroll-body/mixins/mescroll-comp.js

@@ -0,0 +1,48 @@
+/**
+ * mescroll-body写在子组件时,需通过mescroll的mixins补充子组件缺少的生命周期
+ */
+const MescrollCompMixin = {
+	// 因为子组件无onPageScroll和onReachBottom的页面生命周期,需在页面传递进到子组件 (一级)
+	onPageScroll(e) {
+		this.handlePageScroll(e)
+	},
+	onReachBottom() {
+		this.handleReachBottom()
+	},
+	// 当down的native: true时, 还需传递此方法进到子组件
+	onPullDownRefresh(){
+		this.handlePullDownRefresh()
+	},
+	// mescroll-body写在子子子...组件的情况 (多级)
+	data() {
+		return {
+			mescroll: {
+				onPageScroll: e=>{
+					this.handlePageScroll(e)
+				},
+				onReachBottom: ()=>{
+					this.handleReachBottom()
+				},
+				onPullDownRefresh: ()=>{
+					this.handlePullDownRefresh()
+				}
+			}
+		}
+	},
+	methods:{
+		handlePageScroll(e){
+			let item = this.$refs["mescrollItem"];
+			if(item && item.mescroll) item.mescroll.onPageScroll(e);
+		},
+		handleReachBottom(){
+			let item = this.$refs["mescrollItem"];
+			if(item && item.mescroll) item.mescroll.onReachBottom();
+		},
+		handlePullDownRefresh(){
+			let item = this.$refs["mescrollItem"];
+			if(item && item.mescroll) item.mescroll.onPullDownRefresh();
+		}
+	}
+}
+
+export default MescrollCompMixin;

+ 59 - 0
components/mescroll-body/mixins/mescroll-more-item.js

@@ -0,0 +1,59 @@
+/**
+ * mescroll-more-item的mixins, 仅在多个 mescroll-body 写在子组件时使用 (参考 mescroll-more 案例)
+ */
+const MescrollMoreItemMixin = {
+	// 支付宝小程序不支持props的mixin,需写在具体的页面中
+	// #ifndef MP-ALIPAY || MP-DINGTALK
+	props:{
+		i: Number, // 每个tab页的专属下标
+		index: { // 当前tab的下标
+			type: Number,
+			default(){
+				return 0
+			}
+		}
+	},
+	// #endif
+	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: {
+		// 以ref的方式初始化mescroll对象 (兼容字节跳动小程序)
+		mescrollInitByRef() {
+			if(!this.mescroll || !this.mescroll.resetUpScroll){
+				// 字节跳动小程序编辑器不支持一个页面存在相同的ref, 多mescroll的ref需动态生成, 格式为'mescrollRef下标'
+				let mescrollRef = this.$refs.mescrollRef || this.$refs['mescrollRef'+this.i];
+				if(mescrollRef) this.mescroll = mescrollRef.mescroll
+			}
+		},
+		// mescroll组件初始化的回调,可获取到mescroll对象 (覆盖mescroll-mixins.js的mescrollInit, 为了标记isInit)
+		mescrollInit(mescroll) {
+			this.mescroll = mescroll;
+			this.mescrollInitByRef && this.mescrollInitByRef(); // 兼容字节跳动小程序
+			// 自动加载当前tab的数据
+			if(this.i === this.index){
+				this.isInit = true; // 标记为true
+				this.mescroll.triggerDownScroll();
+			}
+		},
+	}
+}
+
+export default MescrollMoreItemMixin;

+ 74 - 0
components/mescroll-body/mixins/mescroll-more.js

@@ -0,0 +1,74 @@
+/**
+ * mescroll-body写在子组件时, 需通过mescroll的mixins补充子组件缺少的生命周期
+ */
+const MescrollMoreMixin = {
+	data() {
+		return {
+			tabIndex: 0, // 当前tab下标
+			mescroll: {
+				onPageScroll: e=>{
+					this.handlePageScroll(e)
+				},
+				onReachBottom: ()=>{
+					this.handleReachBottom()
+				},
+				onPullDownRefresh: ()=>{
+					this.handlePullDownRefresh()
+				}
+			}
+		}
+	},
+	// 因为子组件无onPageScroll和onReachBottom的页面生命周期,需在页面传递进到子组件
+	onPageScroll(e) {
+		this.handlePageScroll(e)
+	},
+	onReachBottom() {
+		this.handleReachBottom()
+	},
+	// 当down的native: true时, 还需传递此方法进到子组件
+	onPullDownRefresh(){
+		this.handlePullDownRefresh()
+	},
+	methods:{
+		handlePageScroll(e){
+			let mescroll = this.getMescroll(this.tabIndex);
+			mescroll && mescroll.onPageScroll(e);
+		},
+		handleReachBottom(){
+			let mescroll = this.getMescroll(this.tabIndex);
+			mescroll && mescroll.onReachBottom();
+		},
+		handlePullDownRefresh(){
+			let mescroll = this.getMescroll(this.tabIndex);
+			mescroll && mescroll.onPullDownRefresh();
+		},
+		// 根据下标获取对应子组件的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;

+ 109 - 0
components/mescroll-body/wxs/mixins.js

@@ -0,0 +1,109 @@
+// 定义在wxs (含renderjs) 逻辑层的数据和方法, 与视图层相互通信
+const WxsMixin = {
+	data() {
+		return {
+			// 传入wxs视图层的数据 (响应式)
+			wxsProp: {
+				optDown:{}, // 下拉刷新的配置
+				scrollTop:0, // 滚动条的距离
+				bodyHeight:0, // body的高度
+				isDownScrolling:false, // 是否正在下拉刷新中
+				isUpScrolling:false, // 是否正在上拉加载中
+				isScrollBody:true, // 是否为mescroll-body滚动
+				isUpBoth:true, // 上拉加载时,是否同时可以下拉刷新
+				t: 0 // 数据更新的标记 (只有数据更新了,才会触发wxs的Observer)
+			},
+			
+			// 标记调用wxs视图层的方法
+			callProp: {
+				callType: '', // 方法名
+				t: 0 // 数据更新的标记 (只有数据更新了,才会触发wxs的Observer)
+			},
+			
+			// 不用wxs的平台使用此处的wxsBiz对象,抹平wxs的写法 (微信小程序和APP使用的wxsBiz对象是./wxs/wxs.wxs)
+			// #ifndef MP-WEIXIN || MP-QQ || APP-PLUS || H5
+			wxsBiz: {
+				//注册列表touchstart事件,用于下拉刷新
+				touchstartEvent: e=> {
+					this.mescroll.touchstartEvent(e);
+				},
+				//注册列表touchmove事件,用于下拉刷新
+				touchmoveEvent: e=> {
+					this.mescroll.touchmoveEvent(e);
+				},
+				//注册列表touchend事件,用于下拉刷新
+				touchendEvent: e=> {
+					this.mescroll.touchendEvent(e);
+				},
+				propObserver(){}, // 抹平wxs的写法
+				callObserver(){} // 抹平wxs的写法
+			},
+			// #endif
+			
+			// 不用renderjs的平台使用此处的renderBiz对象,抹平renderjs的写法 (app 和 h5 使用的renderBiz对象是./wxs/renderjs.js)
+			// #ifndef APP-PLUS || H5
+			renderBiz: {
+				propObserver(){} // 抹平renderjs的写法
+			}
+			// #endif
+		}
+	},
+	methods: {
+		// wxs视图层调用逻辑层的回调
+		wxsCall(msg){
+			if(msg.type === 'setWxsProp'){
+				// 更新wxsProp数据 (值改变才触发更新)
+				this.wxsProp = {
+					optDown: this.mescroll.optDown,
+					scrollTop: this.mescroll.getScrollTop(),
+					bodyHeight: this.mescroll.getBodyHeight(),
+					isDownScrolling: this.mescroll.isDownScrolling,
+					isUpScrolling: this.mescroll.isUpScrolling,
+					isUpBoth: this.mescroll.optUp.isBoth,
+					isScrollBody:this.mescroll.isScrollBody,
+					t: Date.now()
+				}
+			}else if(msg.type === 'setLoadType'){
+				// 设置inOffset,outOffset的状态
+				this.downLoadType = msg.downLoadType
+				// 状态挂载到mescroll对象, 以便在其他组件中使用, 比如<me-video>中
+				this.$set(this.mescroll, 'downLoadType', this.downLoadType)
+				// 重置是否加载成功的状态
+				this.$set(this.mescroll, 'isDownEndSuccess', null)
+			}else if(msg.type === 'triggerDownScroll'){
+				// 主动触发下拉刷新
+				this.mescroll.triggerDownScroll();
+			}else if(msg.type === 'endDownScroll'){
+				// 结束下拉刷新
+				this.mescroll.endDownScroll();
+			}else if(msg.type === 'triggerUpScroll'){
+				// 主动触发上拉加载
+				this.mescroll.triggerUpScroll(true);
+			}
+		}
+	},
+	mounted() {
+		// #ifdef MP-WEIXIN || MP-QQ || APP-PLUS || H5
+		// 配置主动触发wxs显示加载进度的回调
+		this.mescroll.optDown.afterLoading = ()=>{
+			this.callProp = {callType: "showLoading", t: Date.now()} // 触发wxs的方法 (值改变才触发更新)
+		}
+		// 配置主动触发wxs隐藏加载进度的回调
+		this.mescroll.optDown.afterEndDownScroll = ()=>{
+			this.callProp = {callType: "endDownScroll", t: Date.now()} // 触发wxs的方法 (值改变才触发更新)
+			let delay = 300 + (this.mescroll.optDown.beforeEndDelay || 0)
+			setTimeout(()=>{
+				if(this.downLoadType === 4 || this.downLoadType === 0){
+					this.callProp = {callType: "clearTransform", t: Date.now()} // 触发wxs的方法 (值改变才触发更新)
+				}
+				// 状态挂载到mescroll对象, 以便在其他组件中使用, 比如<me-video>中
+				this.$set(this.mescroll, 'downLoadType', this.downLoadType)
+			}, delay)
+		}
+		// 初始化wxs的数据
+		this.wxsCall({type: 'setWxsProp'})
+		// #endif
+	}
+}
+
+export default WxsMixin;

+ 92 - 0
components/mescroll-body/wxs/renderjs.js

@@ -0,0 +1,92 @@
+// 使用renderjs直接操作window对象,实现动态控制app和h5的bounce
+// bounce: iOS橡皮筋,Android半月弧,h5浏览器下拉背景等效果 (下拉刷新时禁止)
+// https://uniapp.dcloud.io/frame?id=renderjs
+
+// 与wxs的me实例一致
+var me = {}
+
+// 初始化window对象的touch事件 (仅初始化一次)
+if(window && !window.$mescrollRenderInit){
+	window.$mescrollRenderInit = true
+	
+	
+	window.addEventListener('touchstart', function(e){
+		if (me.disabled()) return;
+		me.startPoint = me.getPoint(e); // 记录起点
+	}, {passive: true})
+	
+	
+	window.addEventListener('touchmove', function(e){
+		if (me.disabled()) return;
+		if (me.getScrollTop() > 0) return; // 需在顶部下拉,才禁止bounce
+		
+		var curPoint = me.getPoint(e); // 当前点
+		var moveY = curPoint.y - me.startPoint.y; // 和起点比,移动的距离,大于0向下拉,小于0向上拉
+		// 向下拉
+		if (moveY > 0) {
+			// 可下拉的条件
+			if (!me.isDownScrolling && !me.optDown.isLock && (!me.isUpScrolling || (me.isUpScrolling && me.isUpBoth))) {
+				
+				// 只有touch在mescroll的view上面,才禁止bounce
+				var el = e.target;
+				var isMescrollTouch = false;
+				while (el && el.tagName && el.tagName !== 'UNI-PAGE-BODY' && el.tagName != "BODY") {
+					var cls = el.classList;
+					if (cls && cls.contains('mescroll-render-touch')) {
+						isMescrollTouch = true
+						break;
+					}
+					el = el.parentNode; // 继续检查其父元素
+				}
+				// 禁止bounce (不会对swiper和iOS侧滑返回造成影响)
+				if (isMescrollTouch && e.cancelable && !e.defaultPrevented) e.preventDefault();
+			}
+		}
+	}, {passive: false})
+}
+
+/* 获取滚动条的位置 */
+me.getScrollTop = function() {
+	return me.scrollTop || 0
+}
+
+/* 是否禁用下拉刷新 */
+me.disabled = function(){
+	return !me.optDown || !me.optDown.use || me.optDown.native
+}
+
+/* 根据点击滑动事件获取第一个手指的坐标 */
+me.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}
+	}
+}
+
+/**
+ * 监听逻辑层数据的变化 (实时更新数据)
+ */
+function propObserver(wxsProp) {
+	me.optDown = wxsProp.optDown
+	me.scrollTop = wxsProp.scrollTop
+	me.isDownScrolling = wxsProp.isDownScrolling
+	me.isUpScrolling = wxsProp.isUpScrolling
+	me.isUpBoth = wxsProp.isUpBoth
+}
+
+/* 导出模块 */
+const renderBiz = {
+	data() {
+		return {
+			propObserver: propObserver,
+		}
+	}
+}
+
+export default renderBiz;

+ 268 - 0
components/mescroll-body/wxs/wxs.wxs

@@ -0,0 +1,268 @@
+// 使用wxs处理交互动画, 提高性能, 同时避免小程序bounce对下拉刷新的影响
+// https://uniapp.dcloud.io/frame?id=wxs
+// https://developers.weixin.qq.com/miniprogram/dev/framework/view/interactive-animation.html 
+
+// 模拟mescroll实例, 与mescroll.js的写法尽量保持一致
+var me = {}
+
+// ------ 自定义下拉刷新动画 start ------
+
+/* 下拉过程中的回调,滑动过程一直在执行 (rate<1为inOffset; rate>1为outOffset) */
+me.onMoving = function (ins, rate, downHight){
+	ins.requestAnimationFrame(function () {
+		ins.selectComponent('.mescroll-wxs-content').setStyle({
+			'will-change': 'transform', // 可解决下拉过程中, image和swiper脱离文档流的问题
+			'transform': 'translateY(' + downHight + 'px)',
+			'transition': ''
+		})
+		// 环形进度条
+		var progress = ins.selectComponent('.mescroll-wxs-progress')
+		progress && progress.setStyle({transform: 'rotate(' + 360 * rate + 'deg)'})
+	})
+}
+
+/* 显示下拉刷新进度 */
+me.showLoading = function (ins){
+	me.downHight = me.optDown.offset
+	ins.requestAnimationFrame(function () {
+		ins.selectComponent('.mescroll-wxs-content').setStyle({
+			'will-change': 'auto',
+			'transform': 'translateY(' + me.downHight + 'px)',
+			'transition': 'transform 300ms'
+		})
+	})
+}
+
+/* 结束下拉 */
+me.endDownScroll = function (ins){
+	me.downHight = 0;
+	me.isDownScrolling = false;
+	ins.requestAnimationFrame(function () {
+		ins.selectComponent('.mescroll-wxs-content').setStyle({
+			'will-change': 'auto',
+			'transform': 'translateY(0)', // 不可以写空串,否则scroll-view渲染不完整 (延时350ms会调clearTransform置空)
+			'transition': 'transform 300ms'
+		})
+	})
+}
+
+/* 结束下拉动画执行完毕后, 清除transform和transition, 避免对列表内容样式造成影响, 如: h5的list-msg示例下拉进度条漏出来等 */
+me.clearTransform = function (ins){
+	ins.requestAnimationFrame(function () {
+		ins.selectComponent('.mescroll-wxs-content').setStyle({
+			'will-change': '',
+			'transform': '',
+			'transition': ''
+		})
+	})
+}
+
+// ------ 自定义下拉刷新动画 end ------
+
+/**
+ * 监听逻辑层数据的变化 (实时更新数据)
+ */
+function propObserver(wxsProp) {
+	me.optDown = wxsProp.optDown
+	me.scrollTop = wxsProp.scrollTop
+	me.bodyHeight = wxsProp.bodyHeight
+	me.isDownScrolling = wxsProp.isDownScrolling
+	me.isUpScrolling = wxsProp.isUpScrolling
+	me.isUpBoth = wxsProp.isUpBoth
+	me.isScrollBody = wxsProp.isScrollBody
+	me.startTop = wxsProp.scrollTop // 及时更新touchstart触发的startTop, 避免scroll-view快速惯性滚动到顶部取值不准确
+}
+
+/**
+ * 监听逻辑层数据的变化 (调用wxs的方法)
+ */
+function callObserver(callProp, oldValue, ins) {
+	if (me.disabled()) return;
+	if(callProp.callType){
+		// 逻辑层(App Service)的style已失效,需在视图层(Webview)设置style
+		if(callProp.callType === 'showLoading'){
+			me.showLoading(ins)
+		}else if(callProp.callType === 'endDownScroll'){
+			me.endDownScroll(ins)
+		}else if(callProp.callType === 'clearTransform'){
+			me.clearTransform(ins)
+		}
+	}
+}
+
+/**
+ * touch事件
+ */
+function touchstartEvent(e, ins) {
+	me.downHight = 0; // 下拉的距离
+	me.startPoint = me.getPoint(e); // 记录起点
+	me.startTop = me.getScrollTop(); // 记录此时的滚动条位置
+	me.startAngle = 0; // 初始角度
+	me.lastPoint = me.startPoint; // 重置上次move的点
+	me.maxTouchmoveY = me.getBodyHeight() - me.optDown.bottomOffset; // 手指触摸的最大范围(写在touchstart避免body获取高度为0的情况)
+	me.inTouchend = false; // 标记不是touchend
+	
+	me.callMethod(ins, {type: 'setWxsProp'}) // 同步更新wxsProp的数据 (小程序是异步的,可能touchmove先执行,才到propObserver; h5和app是同步)
+}
+
+function touchmoveEvent(e, ins) {
+	var isPrevent = true // false表示不往上冒泡,相当于调用了同时调用了stopPropagation和preventDefault (对小程序生效, h5和app无效)
+	
+	if (me.disabled()) return isPrevent;
+	
+	var scrollTop = me.getScrollTop(); // 当前滚动条的距离
+	var curPoint = me.getPoint(e); // 当前点
+	
+	var moveY = curPoint.y - me.startPoint.y; // 和起点比,移动的距离,大于0向下拉,小于0向上拉
+	
+	// 向下拉 && 在顶部
+	// mescroll-body,直接判定在顶部即可
+	// scroll-view在滚动时不会触发touchmove,当触顶/底/左/右时,才会触发touchmove
+	// scroll-view滚动到顶部时,scrollTop不一定为0,也有可能大于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.isUpBoth))) {
+	
+			// 下拉的角度是否在配置的范围内
+			if(!me.startAngle) me.startAngle = me.getAngle(me.lastPoint, curPoint); // 两点之间的角度,区间 [0,90]
+			if (me.startAngle < me.optDown.minAngle) return isPrevent; // 如果小于配置的角度,则不往下执行下拉刷新
+	
+			// 如果手指的位置超过配置的距离,则提前结束下拉,避免Webview嵌套导致touchend无法触发
+			if (me.maxTouchmoveY > 0 && curPoint.y >= me.maxTouchmoveY) {
+				me.inTouchend = true; // 标记执行touchend
+				touchendEvent(e, ins); // 提前触发touchend
+				return isPrevent;
+			}
+			
+			isPrevent = false // 小程序是return false
+	
+			var 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.callMethod(ins, {type: 'setLoadType', downLoadType: 1})
+					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.callMethod(ins, {type: 'setLoadType', downLoadType: 2})
+					me.isMoveDown = true; // 标记下拉区域高度改变,在touchend重置回来
+				}
+				if (diff > 0) { // 向下拉
+					me.downHight += diff * me.optDown.outOffsetRate; // 越往下,高度变化越小
+				} else { // 向上收
+					me.downHight += diff; // 向上收回高度,则向上滑多少收多少高度
+				}
+			}
+			
+			me.downHight = Math.round(me.downHight) // 取整
+			var rate = me.downHight / me.optDown.offset; // 下拉区域当前高度与指定距离的比值
+			// me.optDown.onMoving && me.optDown.onMoving(me, rate, me.downHight); // 下拉过程中的回调,一直在执行
+			me.onMoving(ins, rate, me.downHight)
+		}
+	}
+	
+	me.lastPoint = curPoint; // 记录本次移动的点
+	
+	return isPrevent // false表示不往上冒泡,相当于调用了同时调用了stopPropagation和preventDefault (对小程序生效, h5和app无效)
+}
+
+function touchendEvent(e, ins) {
+	// 如果下拉区域高度已改变,则需重置回来
+	if (me.isMoveDown) {
+		if (me.downHight >= me.optDown.offset) {
+			// 符合触发刷新的条件
+			me.downHight = me.optDown.offset; // 更新下拉区域高度
+			// me.triggerDownScroll();
+			me.callMethod(ins, {type: 'triggerDownScroll'})
+		} else {
+			// 不符合的话 则重置
+			me.downHight = 0;
+			// me.optDown.endDownScroll && me.optDown.endDownScroll(me);
+			me.callMethod(ins, {type: 'endDownScroll'})
+		}
+		me.movetype = 0;
+		me.isMoveDown = false;
+	} else if (!me.isScrollBody && me.getScrollTop() === me.startTop) { // scroll-view到顶/左/右/底的滑动事件
+		var isScrollUp = me.getPoint(e).y - me.startPoint.y < 0; // 和起点比,移动的距离,大于0向下拉,小于0向上拉
+		// 上滑
+		if (isScrollUp) {
+			// 需检查滑动的角度
+			var angle = me.getAngle(me.getPoint(e), me.startPoint); // 两点之间的角度,区间 [0,90]
+			if (angle > 80) {
+				// 检查并触发上拉
+				// me.triggerUpScroll(true);
+				me.callMethod(ins, {type: 'triggerUpScroll'})
+			}
+		}
+	}
+	me.callMethod(ins, {type: 'setWxsProp'}) // 同步更新wxsProp的数据 (小程序是异步的,可能touchmove先执行,才到propObserver; h5和app是同步)
+}
+
+/* 是否禁用下拉刷新 */
+me.disabled = function(){
+	return !me.optDown || !me.optDown.use || me.optDown.native
+}
+
+/* 根据点击滑动事件获取第一个手指的坐标 */
+me.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]*/
+me.getAngle = function (p1, p2) {
+	var x = Math.abs(p1.x - p2.x);
+	var y = Math.abs(p1.y - p2.y);
+	var z = Math.sqrt(x * x + y * y);
+	var angle = 0;
+	if (z !== 0) {
+		angle = Math.asin(y / z) / Math.PI * 180;
+	}
+	return angle
+}
+
+/* 获取滚动条的位置 */
+me.getScrollTop = function() {
+	return me.scrollTop || 0
+}
+
+/* 获取body的高度 */
+me.getBodyHeight = function() {
+	return me.bodyHeight || 0;
+}
+
+/* 调用逻辑层的方法 */
+me.callMethod = function(ins, param) {
+	if(ins) ins.callMethod('wxsCall', param)
+}
+
+/* 导出模块 */
+module.exports = {
+	propObserver: propObserver,
+	callObserver: callObserver,
+	touchstartEvent: touchstartEvent,
+	touchmoveEvent: touchmoveEvent,
+	touchendEvent: touchendEvent
+}

+ 81 - 1
main.js

@@ -5,6 +5,9 @@ import App from './App'
 import uView from 'uview-ui';
 Vue.use(uView);
 
+import MescrollBody from "@/components/mescroll-body/mescroll-body.vue"
+Vue.component('mescroll-body', MescrollBody)
+
 //引入路由拦截
 import {router,RouterMount} from './router.js'  //路径换成自己的
 Vue.use(router)
@@ -12,7 +15,6 @@ Vue.use(router)
 //封装api
 import { api } from 'assets/http/api.js' 
 Vue.prototype.$api = api
-
 //引入util.js
 import Util from 'utils/util.js'
 Vue.prototype.$util = Util
@@ -21,6 +23,84 @@ Vue.prototype.$util = Util
 import simpleCache from "@/utils/cache.js"
 Vue.prototype.$cache = simpleCache
 
+//登陆类型
+Vue.prototype.$loginType={
+	STAFF:'staff',
+	ENTERPRISE:'enterprise'
+}
+//设备类型【通过前缀查找】
+Vue.prototype.$device_prefix={
+	SMOKE:'ctwing/smoke3',//烟感
+	GAS:'ctwing/gasmonitoring',//气感,燃气监控
+}
+//设备类型【通过设备类型查找】
+Vue.prototype.$device_type={ 
+	FIRE_HYDRANT:'960417', //消防栓
+}
+
+//订阅消息模板
+Vue.prototype.$tmplIds=['veqKChqdSGXaidd4h4OOzTv6nm-qVwwT__Pz0ghRyFY']
+//小程序版本
+Vue.prototype.$miniprogramState={
+	DEVELOPER:'developer', //开发版
+	TRIAL:'trial', //体验版
+	FORMAL:'formal',//正式版
+}
+
+//封装提示框
+Vue.prototype.$showModel = (content,isShowCancel=true,title='提示',)=>{
+    return new Promise((resolve,reject)=>{
+        uni.showModal({
+            title: title,
+            content: content,
+			showCancel:isShowCancel,
+            success: (res)=>{
+				if (res.confirm) {
+					resolve(res)
+				} 
+            }
+        });
+    })
+}
+//是否认证登陆
+Vue.prototype.$isAuth=()=>{
+	return simpleCache.get('loginType')
+}
+
+//创建时间
+Vue.prototype.$createDateTime=()=>{
+		var mydate = new Date();
+		var str = "" + mydate.getFullYear() + "-";
+	//判断小于是是直接小于10月就可以了
+		if(mydate.getMonth()<10){
+			str +="0"+ (mydate.getMonth() + 1) + "-";
+		}else{
+			str += (mydate.getMonth() + 1) + "-";
+		}
+	//判断小于是是直接小于10日就可以了
+		if(mydate.getDate()<10){
+			str += "0"+mydate.getDate() + " ";
+		}else{
+			str += mydate.getDate() + " ";
+		}
+		if(mydate.getHours()<10){
+			str += "0"+mydate.getHours() + ":";
+		}else{
+			str += mydate.getHours() + ":";
+		}
+		if(mydate.getMinutes()<10){
+			str += "0"+mydate.getMinutes() + ":";
+		}else{
+			str += mydate.getMinutes() + ":";
+		}
+		if(mydate.getSeconds()<10){
+			str += "0"+mydate.getSeconds();
+		}else{
+			str += mydate.getSeconds();
+		}
+		return str;	
+}
+
 //封装判空函数
 Vue.prototype.$isEmpty=function(value){
 	switch (typeof value) {

+ 156 - 19
pages.json

@@ -59,7 +59,7 @@
 			"name":"auth",
             "style" :                                                                                    
             {
-                "navigationBarTitleText": "认证信息",
+                "navigationBarTitleText": "员工认证",
                 "enablePullDownRefresh": false
             }
             
@@ -147,8 +147,10 @@
 			"name":"access-record",
             "style" :                                                                                    
             {
-                "navigationBarTitleText": "通行记录",
-                "enablePullDownRefresh": false
+				"navigationBarTextStyle":"white",
+				"navigationBarBackgroundColor":"#59a5f0",
+				"navigationBarTitleText": "通行记录",
+				"enablePullDownRefresh": false
             }
             
         }
@@ -172,30 +174,165 @@
             }
             
         }
+        ,{
+            "path" : "pages/device/device",
+			"name":"device",
+            "style" :                                                                                    
+            {
+				"navigationBarTextStyle":"white",
+				"navigationBarBackgroundColor":"#59a5f0",
+                "navigationBarTitleText": "门禁",
+                "enablePullDownRefresh": false
+            }
+            
+        }
+        ,{
+            "path" : "pages/news/list",
+			"name":"newsList",
+            "style" :                                                                                    
+            {
+                "navigationBarTitleText": "园区动态",
+                "enablePullDownRefresh": false
+            }
+            
+        }
+        ,{
+            "path" : "pages/mine/editPhone/editPhone",
+			"name":"editPhone",
+            "style" :                                                                                    
+            {
+                "navigationBarTitleText": "更换手机",
+                "enablePullDownRefresh": false
+            }
+            
+        }
+        ,{
+            "path" : "pages/index/staffAudit/list",
+			"name":"staffAuditList",
+            "style" :                                                                                    
+            {
+				"navigationBarTextStyle":"white",
+				"navigationBarBackgroundColor":"#59a5f0",
+				"navigationBarTitleText": "员工审核",
+				"enablePullDownRefresh": false
+            }
+            
+        }
+       
+        ,{
+            "path" : "pages/index/staffAudit/detail",
+			"name":"staffAuditDetail",
+            "style" :                                                                                    
+            {
+                "navigationBarTextStyle":"white",
+                "navigationBarBackgroundColor":"#59a5f0",
+                "navigationBarTitleText": "审核详情",
+                "enablePullDownRefresh": false
+            }
+            
+        }
+        ,{
+            "path" : "pages/index/staff-temperature/staff-temperature",
+			"name" :"staff-temperature",
+            "style" :                                                                                    
+            {
+				"navigationBarTextStyle":"white",
+				"navigationBarBackgroundColor":"#59a5f0",
+				"navigationBarTitleText": "员工测温",
+				"enablePullDownRefresh": false
+            }
+            
+        }
+        ,{
+            "path" : "pages/index/staff-temperature/detail",
+			"name" :"staffTemperatureDetail",
+            "style" :                                                                                    
+            {
+                "navigationBarTitleText": "测温详情",
+                "enablePullDownRefresh": false
+            }
+            
+        }
+        ,{
+            "path" : "pages/index/notice/notice",
+			"name" :"notice",
+            "style" :                                                                                    
+            {
+                "navigationBarTitleText": "通知公告",
+                "enablePullDownRefresh": false
+            }
+            
+        }
+        ,{
+            "path" : "pages/index/notice/detail",
+			"name" :"noticeDetail",
+            "style" :                                                                                    
+            {
+                "navigationBarTitleText": "通知详情",
+                "enablePullDownRefresh": false
+            }
+            
+        }
+        
+        ,{
+            "path" : "pages/mine/changePass/changePass",
+			"name":"changePass",
+            "style" :                                                                                    
+            {
+                "navigationBarTitleText": "修改密码",
+                "enablePullDownRefresh": false
+            }
+            
+        }
+        ,{
+            "path" : "pages/index/myinfo/myinfo",
+			"name" :"myinfo",
+            "style" :                                                                                    
+            {
+                "navigationBarTitleText": "我的资料",
+                "enablePullDownRefresh": false
+            }
+            
+        }
+        ,{
+            "path" : "pages/index/fire/list/list",
+            "style" :                                                                                    
+            {
+                "navigationBarTitleText": "烟感",
+                "enablePullDownRefresh": false
+            }
+            
+        }
+      
+        ,{
+            "path" : "pages/index/fire/detail",
+            "style" :                                                                                    
+            {
+                "navigationBarTitleText": "设备详情",
+                "enablePullDownRefresh": false
+            }
+            
+        }
+        ,{
+            "path" : "pages/index/fire/alarm-record",
+            "style" :                                                                                    
+            {
+                "navigationBarTitleText": "报警记录",
+                "enablePullDownRefresh": false
+            }
+            
+        }
     ],
 	"tabBar": {
-		  "color":"#8a8a8a",
-		  "backgroundColor":"#FFFFFF",
-		  "borderStyle":"white",
-		  "selectedColor":"#59a5f0",
 		  "list": [
 			{
-			  "pagePath": "pages/index/index",
-			  "text": "园区",
-			  "selectedIconPath":"static/tarbar/home.png",
-			  "iconPath":"static/tarbar/home0.png"
+			  "pagePath": "pages/index/index"
 			},
 			{
-			  "pagePath":"pages/news/news",
-			  "text": "园区动态",
-			  "selectedIconPath":"static/tarbar/news.png",
-			  "iconPath":"static/tarbar/news0.png"
+			  "pagePath":"pages/device/device"
 			},
 			{
-			  "pagePath": "pages/mine/mine",
-			  "text": "个人中心",
-			  "selectedIconPath":"static/tarbar/my.png",
-			  "iconPath":"static/tarbar/my0.png"
+			  "pagePath": "pages/mine/mine"
 			}
 		  ]
 	 },

+ 370 - 75
pages/auth/auth.vue

@@ -1,37 +1,39 @@
 <template>
-	<view class="padding-20">
+	<view style="padding: 0 20rpx 20rpx;">
+		<u-top-tips type="info" ref="uTips"></u-top-tips>
+		<u-alert-tips :show="show" type="warning" title="审核意见" :close-able="true"  :description="opinion"></u-alert-tips>
 		<view class="auth">
 			<text class="cuIcon-titles text-blue padding-right-10"></text>
 			<text>个人信息认证</text>
 			<view class="card">
 				<u-form :model="model"  ref="uForm" >
 					<u-form-item  :label-width="labelWidth"  label="姓名" >
-						<u-input  placeholder="请输入姓名" v-model="model.name" type="text"></u-input>
+						<u-input  placeholder="请输入姓名" v-model="model.realName" type="text"></u-input>
 					</u-form-item>
 					<u-form-item  :label-width="labelWidth"   label="性别" >
-						<u-input  type="select" :select-open="sexSelectShow" v-model="model.sex" placeholder="请选择性别" @click="sexSelectShow = true"></u-input>
+						<u-input  type="select" :select-open="sexSelectShow" v-model="sexSelectList[sexSelectIndex].text" placeholder="请选择性别" @click="sexSelectShow = true"></u-input>
 					</u-form-item>
 					<u-form-item   label="身份证号"  :label-width="labelWidth">
-						<u-input  placeholder="请输入身份证号" v-model="model.idcard" type="idcard"></u-input>
+						<u-input maxlength="18"  placeholder="请输入身份证号" v-model="model.idcard" type="idcard"></u-input>
 					</u-form-item>
 					<u-form-item   label="手机号码"  :label-width="labelWidth">
-						<u-input  placeholder="请输入手机号" v-model="model.phone" type="number"></u-input>
+						<u-input maxlength="11"  placeholder="请输入手机号" v-model="model.phone" type="number"></u-input>
 					</u-form-item>
-					<u-form-item :border-bottom="false"  label="部信息采集"  :label-width="labelWidth"></u-form-item>
+					<u-form-item :border-bottom="false"  label="部信息采集"  :label-width="labelWidth"></u-form-item>
 				</u-form>
 				
 				<view @click="faceSelectShow=true" class="flex justify-center padding-bottom-50">
 					<view class=" ">
 						<upload-img
-						   :width="$isEmpty(face_url)?350:560"
-						   :height="$isEmpty(face_url)?350:420"
-						  :currentImage="face_url"
+						   :width="$isEmpty(model.face)?350:560"
+						   :height="$isEmpty(model.face)?350:420"
+						  :currentImage="model.face"
 						  bgsrc="http://139.9.103.171:1888/miniofile/xlyq/face1.png"
 						  >
 						</upload-img>
 						<view class="text-center padding-top-20" style="color: #59a5f0;">
 						 <text class="cuIcon-camera padding-right-sm" style="font-size: 30rpx;"></text>
-						 <text v-if="$isEmpty(face_url)">点击上传人脸</text>
+						 <text v-if="$isEmpty(model.face)">点击上传人脸</text>
 						 <text style="margin-top: 40rpx;display: inline-block;" v-else>点击重新上传</text>
 						</view>
 					</view>
@@ -44,10 +46,13 @@
 			<view class="card">
 				<u-form :model="model"  ref="uForm" >
 					<u-form-item  :label-width="labelWidth"   label="公司园区" >
-						<u-input  type="select" :select-open="ganderSelectShow" v-model="model.gander" placeholder="请选择工作园区" @click="ganderSelectShow = true"></u-input>
+						<u-input  type="select" :select-open="ganderSelectShow" v-model="gander"  placeholder="请选择工作园区" @click="ganderShow"></u-input>
+					</u-form-item>
+					<u-form-item  :label-width="labelWidth"   label="园区区域" >
+						<u-input  type="select" :select-open="residentialSelectShow" v-model="residential"  placeholder="请选择园区区域" @click="residentialShow"></u-input>
 					</u-form-item>
 					<u-form-item  :label-width="labelWidth"   label="所属企业" >
-						<u-input  type="select" :select-open="companySelectShow" v-model="model.company" placeholder="请选择所属企业" @click="companySelectShow = true"></u-input>
+						<u-input  type="select" :select-open="companySelectShow" v-model="company" placeholder="请选择所属企业" @click="companyShow"></u-input>
 					</u-form-item>
 				</u-form>
 			</view>
@@ -58,52 +63,95 @@
 			<view class="card">
 				<u-form :model="model"  ref="uForm" >
 					<u-form-item :border-bottom="false" :label-width="labelWidth"   label="备注(可选)" ></u-form-item>
-					<textarea value="" v-model="model.remark" placeholder="填写备注信息" />
+					<textarea value="" v-model="model.remarks" placeholder="填写备注信息" />
 				</u-form>
 			</view>
 		</view>
 		
-		
-		<view class="cu-btn bg-blue round flex" style="padding: 40rpx 0;margin-top: 40rpx;">
-			提交
+		<view class="auth flex justify-between">
+			<view class="flex flex-direction">
+				<view class="">
+					<text class="cuIcon-noticefill text-blue padding-right-10"></text>
+					<text>消息通知提醒</text>
+				</view>
+				<view class="padding-top-10 text-sm text-gray">
+					<text>开启订阅后,认证审核通知将推送到您的微信上</text>
+				</view>
+			</view>
+			<button v-if="subscribeShow" @click="subscribe" class="cu-btn sm round bg-blue">
+				订阅
+			</button>
+			<button v-else @opensetting="opensetting" open-type="openSetting"class="cu-btn sm round bg-blue">
+				订阅
+			</button>
 		</view>
-		<u-action-sheet :list="sexSelectList" v-model="sexSelectShow" @click="sexSelectCallback"></u-action-sheet>
-		<u-action-sheet :list="ganderSelectList" v-model="ganderSelectShow" @click="ganderSelectCallback"></u-action-sheet>
-		<u-action-sheet :list="companySelectList" v-model="companySelectShow" @click="companySelectCallback"></u-action-sheet>
+		<button  open-type="getUserInfo" @getuserinfo="submit"  class="cu-btn bg-blue round flex" style="padding: 40rpx 0;margin-top: 40rpx;">
+			<text v-text="operationType==0?'提交':'重新审核'"></text>
+		</button>
+		<!-- 性别 -->
+		<u-picker v-model="sexSelectShow" @confirm="sexSelectCallback" :range="sexSelectList" range-key="text" mode="selector"></u-picker>
+		<!-- 园区 -->
+		<u-picker v-model="ganderSelectShow" @confirm="ganderSelectCallback" :range="ganderSelectList" range-key="agencyName" mode="selector"></u-picker>
+		<!-- 区域 -->
+		<u-picker v-model="residentialSelectShow" @confirm="residentialSelectCallback" :range="residentialSelectList" range-key="name" mode="selector"></u-picker>
+		<!-- 企业 -->
+		<u-picker v-model="companySelectShow" @confirm="companySelectCallback" :range="companySelectList" range-key="enterpriseName" mode="selector"></u-picker>
 		<u-action-sheet @click="faceSelectCallback"  z-index="999999" :list="faceSelectList" v-model="faceSelectShow"></u-action-sheet>
 	</view>
 </template>
 
 <script>
 	import uploadImg from '@/components/uploadimg/uploadImg.vue'
-	let that = this;
+	let that;
 	export default {
 		components:{
 		   uploadImg
 		},
 		data() {
 			return {
+				
+				
+				open:false,
+				
+				// 0:用户认证 1:重新审核
+				operationType:0,
+				phone:'',//当operationType为1时需要传过来
+				opinion:'',//审核不通过时的审核意见
+				show:false,//当有审核意见时才显示
+				
 				labelWidth:'200',
 				model: {
-					name: '',
-					sex: '',
-					phone: '',
+					avatar:'',
+					name:'',//昵称
+					openId:'',
+					realName: '',
+					enterpriseId:'',
+					enterpriseName:'',
+					face:'',
+					sex: '',	//性别 1 男 2 女
 					idcard:'',
-					gander:'',
-					company:'',
-					face_image:''
+					phone: '',
+					remarks:''
 				},
-				//图片回显
-				face_url:'',
 				//性别
-				sexSelectList: [{text: '男'},{text: '女'}],
+				sexSelectList: [{text: '男',value:1},{text: '女',value:2}],
 				sexSelectShow: false,
+				sexSelectIndex:0,
 				//园区
+				gander:'',
 				ganderSelectList: [],
 				ganderSelectShow: false,
+				ganderSelectIndex:0,
+				//区域
+				residential:'',
+				residentialSelectList: [],
+				residentialSelectShow: false,
+				residentialSelectIndex:0,
 				//企业
+				company:'',
 				companySelectList: [],
 				companySelectShow: false,
+				companySelectIndex:0,
 				//上传人脸的方式
 				faceSelectList: [{
 					text: '相册上传',
@@ -113,34 +161,301 @@
 				faceSelectShow:false,
 				//验证码
 				codeTips: '',
+				subscribeShow:true,
 				
 			}
 		},
-		onLoad() {
-			this.fetchStaticData()
-			this.initData()
+		onShow() {
+			let currPage=this.getPageCtx()
+			if (!this.$isEmpty(currPage.data.image)) {
+				this.uploadFile(currPage.data.image)
+			}
+		},
+		
+		onLoad(options) {
+			that=this
+			
+			let tempId=this.$tmplIds[0]
+			wx.getSetting({
+			 	withSubscriptions: true,
+			   success(res){
+				   if (res.subscriptionsSetting.itemSettings[tempId]=='accept') {
+						that.subscribeShow=false
+				   }else{
+					   that.subscribeShow=true
+				   }
+			   }
+			})
+			if (!this.$isEmpty(options.operationType)) {
+				this.operationType=options.operationType
+			}
+			this.fetchAgencyList()
+			if (this.operationType==0) {
+				//用户认证
+				this.getOpenid()
+				if (!this.$isEmpty(this.$Route.query.phone)) {
+					this.model.phone=this.$Route.query.phone
+				}
+			}else{
+				//用户重新审核
+				this.phone=options.phone
+				this.fetchUserInfo()
+			}
 		},
 		methods: {
-			initData(){
-				if (!this.$isEmpty(this.$cache.get('phone'))) {
-					this.model.phone=this.$cache.get('phone')
+			tip(){
+				this.$showModel('认证审核通过后将通过微信服务通知您',false,'通知功能')
+			},
+			/**
+			 * 重新审核操作时,加载用户的认证信息
+			 */
+			async fetchUserInfo(){
+				if (this.$isEmpty(getApp().globalData.userInfo)) {
+					let res=await this.$api.enterprisestaff.detail({phone:this.phone})
+					this.model=res.data
+				}else{
+					this.model=getApp().globalData.userInfo
+				}
+				if (this.model.sex==2) {
+					this.sexSelectIndex=1
 				}
+				this.opinion=this.model.opinion
+				if (!this.$isEmpty(this.opinion)) {
+					this.show=true
+				}
+				this.fetchResidentialList(this.model.agencyId)
+				this.fetchEnterpriseList(this.model.residentialId)
+				this.gander=this.model.agencyName
+				this.residential=this.model.residentialName
+				this.company=this.model.enterpriseName
 			},
-			//性别
-			sexSelectCallback(index) {
+			/**
+			 * 获取openid
+			 */
+			getOpenid(){
+				uni.login({
+					success: (res) => {
+						let data={
+							appId:this.$api.wxData.appId,
+							jsCode:res.code,
+							secret:this.$api.wxData.secret
+						}
+						this.$api.wxApi.getOpenId(data).then(res=>{
+							let resData= JSON.parse(res.data)
+							this.model.openId=resData.openid
+							this.$cache.put('openid',resData.openid)
+						})
+					}
+				})
+			},
+			/**
+			 * 订阅认证信息
+			 */
+			subscribe(e){
+				uni.requestSubscribeMessage({
+				   tmplIds: that.$tmplIds,
+				   success (res) {
+					   if (res.errMsg=='requestSubscribeMessage:ok') {
+							//已授权
+							that.subscribeShow=false
+					   }else{
+						   //未授权
+						   that.subscribeShow=true
+					   }
+				   },
+				   fail(err){
+					   that.subscribeShow=true
+					   console.error(err);
+				   }
+				 })
+			},
+			/**
+			 * 提交审核
+			 */
+			submit(e){
+				let userInfo=e.detail.userInfo
+				if (this.$isEmpty(userInfo)&&this.operationType==0) {
+					//认证操作才需要获取微信头像和昵称信息,修改操作不需要
+					this.$refs.uTips.show({
+						title: '认证失败,用户拒绝授权获取微信头像和昵称信息',
+						type: 'error',
+						duration: '3000'
+					})
+					return
+				}
+				this.model.avatar=userInfo.avatarUrl
+				this.model.name=userInfo.nickName
+				this.model.sex=this.sexSelectList[this.sexSelectIndex].value || 1
+				try{
+					this.model.enterpriseId=this.companySelectList[this.companySelectIndex].id
+				}catch(e){
+					this.model.enterpriseId=''
+				}
+				this.model.enterpriseName=this.company
+				if (this.$isEmpty(this.model.realName)) {
+					this.$u.toast('请输入您的姓名')
+					return
+				}
+				if (this.$isEmpty(this.model.idcard)) {
+					this.$u.toast('请输入身份证号')
+					return
+				}
+				if (!this.$u.test.idCard(this.model.idcard)) {
+					this.$u.toast('请输入正确的身份证号')
+					return
+				}
+				if (this.$isEmpty(this.model.phone)) {
+					this.$u.toast('请输入手机号码')
+					return
+				}
+				if (!this.$u.test.mobile(this.model.phone)) {
+					this.$u.toast('请输入正确的手机号码')
+					return
+				}
+				if (this.$isEmpty(this.model.face)) {
+					this.$u.toast('请上传人脸信息')
+					return
+				}
+				if (this.$isEmpty(this.model.enterpriseId)) {
+					this.$u.toast('请选择所属企业')
+					return
+				}
+				this.model.residentialName=this.residential
+				this.model.residentialId=this.residentialSelectList[this.residentialSelectIndex].id
+				this.model.agencyName=this.gander
+				this.model.agencyId=this.ganderSelectList[this.ganderSelectIndex].id
+				
+				if (this.operationType==1) {
+					//重新审核
+					this.model.examine=0
+				}
+				
+				this.$api.enterprisestaff.submit(this.model).then(res=>{
+					if (res.code==200) {
+						let content=''
+						if (this.operationType==0) {
+							that.$showModel('提交成功,请耐心等待企业管理者审核',false).then(res=>{
+								this.$Router.replaceAll({name:'login'})
+							})
+						}else{
+							that.$showModel('操作成功',false).then(res=>{
+								this.$Router.back()
+							})
+						}
+						
+					}else{
+						this.$u.toast(res.msg)
+					}
+				})
+			},
+		//园区 begin
+		
+			/**
+			 * 显示园区
+			 */
+			ganderShow(){
+				this.ganderSelectShow=true
+			},
+			/**
+			 * 获取园区列表
+			 */
+			fetchAgencyList(){
+				this.$api.agency.page().then(res=>{
+					this.ganderSelectList=res.data
+				})
+			},
+			/**
+			 * 选择园区后的回调
+			 * @param {Object} e
+			 */
+			ganderSelectCallback(e) {
 				uni.hideKeyboard();
-				this.model.sex = this.sexSelectList[index].text;
+				this.ganderSelectIndex=e[0]
+				this.gander=this.ganderSelectList[this.ganderSelectIndex].agencyName
+				//加载区域
+				let agencyId=this.ganderSelectList[this.ganderSelectIndex].id
+				this.fetchResidentialList(agencyId)
+			},
+		//园区 end
+			
+		//区域 begin
+			
+			/**	
+			 * 根据园区id获取区域列表
+			 */
+			fetchResidentialList(agencyId){
+				this.$api.residential.page({agencyId:agencyId,size:300}).then(res=>{
+					this.residentialSelectList=res.data.records
+				})
 			},
-			//园区
-			ganderSelectCallback(index) {
+			//显示区域
+			residentialShow(){
+				if (this.$isEmpty(this.gander)) {
+					//未选择园区提示先选择园区
+					this.$u.toast("请先选择园区")
+					return
+				}
+				this.residentialSelectShow=true
+			},
+			/**
+			 * 选择区域的回调
+			 */
+			residentialSelectCallback(e){
 				uni.hideKeyboard();
-				this.model.gander = this.ganderSelectList[index].text;
+				this.residentialSelectIndex=e[0]
+				this.residential=this.residentialSelectList[this.residentialSelectIndex].name
+				//加载区域下的企业
+				let residentialId=this.residentialSelectList[this.residentialSelectIndex].id
+				this.fetchEnterpriseList(residentialId)
+			},
+		//区域 end
+			
+		//企业 begin
+			
+			/**
+			 * 根据 区域id 获取 企业列表
+			 */
+			fetchEnterpriseList(residentialId){
+				this.$api.enterprise.page({residentialId:residentialId}).then(res=>{
+					this.companySelectList=res.data.records
+				})
+			},
+			/**
+			 * 显示企业
+			 */
+			companyShow(){
+				if (this.$isEmpty(this.residential)) {
+					//未选择区域
+					this.$u.toast("请选择区域")
+					return
+				}
+				this.companySelectShow=true
 			},
 			//公司
-			companySelectCallback(index) {
+			companySelectCallback(e) {
+				uni.hideKeyboard();
+				this.companySelectIndex=e[0]
+				this.company = this.companySelectList[this.companySelectIndex].enterpriseName;
+			},
+			
+		//企业 end
+			
+			/**
+			 * 获取页面对象
+			 */
+			getPageCtx(idx = 0){
+			  let pages = getCurrentPages()
+			  if (pages.length > 0) {
+			    return pages[pages.length - 1 - idx] || {}
+			  }
+			  return {}
+			},
+			//性别
+			sexSelectCallback(e) {
 				uni.hideKeyboard();
-				this.model.company = this.companySelectList[index].text;
+				this.sexSelectIndex=e[0]
 			},
+			
 			faceSelectCallback(index){
 				if (index==0) {
 					//图片上传
@@ -161,42 +476,22 @@
 			    sizeType: ['compressed'],
 			    //可选择原图或压缩后的图片
 			    success: res => {
-			      that.handelImg(res.tempFilePaths[0])
+			      that.uploadFile(res.tempFilePaths[0])
 			    }
 			  });
 			},
 			//处理照片,拍照上传和相册上传的共同处理方法
-			handelImg(imgUrl){
-				that.face_url=imgUrl
-				uni.getFileSystemManager().readFile({
-				  filePath: imgUrl,
-				  //选择图片返回的相对路径
-				  encoding: 'base64',
-				  //编码格式
-				  success: res => {
-					  //图片的base64字符串
-					  that.face_image='data:image/jpeg;base64,' + res.data
-				  }
-				});
-			},
-			fetchStaticData(){
-				this.ganderSelectList=[
-					{
-						text: '新邻园区1'
-					},
-					{
-						text: '新邻园区2'
-					}
-				]
-				this.companySelectList=[
-					{
-						text: '新邻企业1'
-					},
-					{
-						text: '新邻企业2'
-					}
-				]
-			}
+			uploadFile(imgUrl){
+				this.$api.uploadFile.submit(imgUrl).then(res=>{
+					that.model.face=res.data
+				})
+			},
+		},
+		/**打开设置页
+		 * 方法可以不实现,但是不可以不写
+		 */
+		opensetting(){
+			
 		}
 	}
 </script>

+ 77 - 18
pages/company/company.vue

@@ -8,23 +8,79 @@
 		</swiper>
 		
 		<view class="card">
-			<image src="http://139.9.103.171:1888/miniofile/xlyq/del/logo.jpg"></image>
-			<view class="flex flex-direction justify-center padding-left-30">
-				<text class="text-bold  text-lg" style="padding-bottom: 12rpx;">鹏鼎控股</text>
-				<text class="text-sm">电子技术/半导体/集成电路</text>
+			<!-- <image src="http://139.9.103.171:1888/miniofile/xlyq/del/logo1.jpg"></image> -->
+			<view class="flex flex-direction justify-center padding-left-20">
+				<text class="text-bold  text-lg" style="padding-bottom: 12rpx;">{{detail.enterpriseName}}</text>
+				<text class="text-sm">{{detail.enterpriseType}}</text>
 			</view>
 		</view>
-		
 		<view class="desc">
 			<text class="text-lg text-bold">公司简介</text>
 			<view class="sub-title">
-				<text style="padding-left: 60rpx;">鹏鼎控股成立于1999年,于2018年9月18日在深交所上市,股票简称“鹏鼎控股”。公司主要从事各类印制电路板的设计、研发、制造与销售业务。专注于为行业领先客户提供全方位PCB产品及服务。
-				公司的制造基地分布于深圳、秦皇岛、淮安,服务半径覆盖中国大陆、中国香港、中国台湾、日本、韩国等地,可以为全球客户提供高速、高质、低成本及高附加值的PCB设计、开发、制造及销售服务,已成为业内极具影响力的重要厂商之一。
+				<text style="padding-left: 60rpx;">
+					{{detail.enterpriseIntroduce}}
 				</text>
 			</view>
 		</view>
+		<view class="bg-white text-black">
+			<text class="text-lg text-bold " style="padding:0 30rpx;">其他信息</text>
+			<view class="cu-list menu text-df padding-top-20">
+				<view class="cu-item">
+					<view class="content">
+						<text class="cuIcon-profile" style="font-size: 32rpx;"></text>
+						<text class="">法定代表人</text>
+					</view>
+					<view class="action">
+						<text>{{detail.representative}}</text>
+					</view>
+				</view>
+				
+				<view class="cu-item">
+					<view class="content">
+						<text class="cuIcon-news" style="font-size: 32rpx;"></text>
+						<text class="">统一社会信用代码</text>
+					</view>
+					<view class="action">
+						<text>{{detail.creditCode}}</text>
+					</view>
+				</view>
+				
+				<view class="cu-item">
+					<view class="content">
+						<text class="cuIcon-recharge" style="font-size: 32rpx;"></text>
+						<text class="">注册金额</text>
+					</view>
+					<view class="action">
+						<text>{{detail.registeredCapital}}</text>
+					</view>
+				</view>
+				
+				<view class="cu-item">
+					<view class="content">
+						<text class="cuIcon-location" style="font-size: 32rpx;"></text>
+						<text class="">公司地址</text>
+					</view>
+					<view class="action">
+						<text>{{detail.region}}</text>
+					</view>
+				</view>
+				
+				<view class="cu-item">
+					<view class="content">
+						<text class="cuIcon-focus" style="font-size: 32rpx;"></text>
+						<text class="">注册地址</text>
+					</view>
+					<view class="action">
+						<text>{{detail.registeredAddress}}</text>
+					</view>
+				</view>
+			</view>
+		</view>
 		
-		<view class="history">
+		<view class="">
+			<u-divider height="100" bgColor="#ffffff">到底了</u-divider>
+		</view>
+		<!-- <view class="history">
 			<view class="top u-border-bottom margin-bottom-40">
 				<text class="text-bold text-lg">发展历程</text>
 				<view class="text-sm padding-top-10 text-gray">
@@ -48,24 +104,19 @@
 				</u-time-line-item>
 			</u-time-line>
 		</view>
-		
 		<view class="address">
 			<view class="top u-border-bottom">
 				<text class="text-bold text-lg">公司地址</text>
 			</view>
 			
-			<view class="item" v-for="(item,index) in 3" :key="index">
+			<view class="item" >
 				<view class="flex">
 					<u-icon name="map" size="50"></u-icon>
 					<text class="padding-left-20 text-lg">鹏鼎控股深圳园区</text>
 				</view>
 				<text class="sub-address">广东省深圳市宝安区燕罗街道松罗路</text>
 			</view>
-			
-			<view class="padding-top-50">
-				<u-divider>没有更多了</u-divider>
-			</view>
-		</view>
+		</view> -->
 	</view>
 </template>
 
@@ -73,14 +124,22 @@
 	export default {
 		data() {
 			return {
+				id:this.$cache.get('enterpriseId'),
+				detail:{},
 				bannerList: [],
-				historyList:[]
+				historyList:[],
 			}
 		},
 		onLoad() {
+			this.fetchDeatil()
 			this.fetchStaticData()
 		},
 		methods: {
+			fetchDeatil(){
+				this.$api.enterprise.detail({id:this.id}).then(res=>{
+					this.detail=res.data
+				})
+			},
 			fetchStaticData(){
 				this.bannerList=[
 					{
@@ -116,8 +175,8 @@
 		box-shadow: 0 10rpx rgba(248, 248, 248,.9) , -10rpx 0rpx rgba(248, 248, 248,.9) ,10rpx 0rpx rgba(248, 248, 248,.9);
 		image{
 			border: 1rpx solid #f1f1f1;
-			width:120rpx;
-			height: 120rpx;
+			width:100rpx;
+			height: 100rpx;
 			border-radius: 50%;
 		}
 	}

+ 106 - 0
pages/device/device.vue

@@ -0,0 +1,106 @@
+<template>
+	<view class="page" style="position: relative;">
+		<view class="content">
+			<loading  isFullScreen color="#59a5f0" :active="isloading" text="开门中..."></loading>
+			<view @click="open" class="device u-border-bottom" v-for="(item,index) in device_list" :key="index">
+				<text>{{item.name}}</text>
+				<view class="cu-btn bg-blue round sm">
+					<text class="cuIcon-lock padding-right-10"></text>
+					<text>开门</text>
+				</view>
+			</view>
+		</view>
+		<u-tabbar v-model="tabbarCurr"
+		 :icon-size="tabbar.iconSize" 
+		 :active-color="tabbar.activeColor" 
+		 :mid-button-size="tabbar.MinButtonSize" 
+		 :list="tabbar.list" :mid-button="true">
+		 </u-tabbar>
+	</view>
+</template>
+
+<script>
+var app=getApp()
+var that;
+import loading from "@/components/loading/loading.vue"
+import {tabbar} from "@/assets/js/tabbar.js"
+export default {
+	components:{
+		loading
+	},
+	data() {
+		return {
+			//tabbar
+			tabbarCurr:1,
+			tabbar:tabbar,
+			//是否已开门
+			isopen:false,
+			//是否展示动画
+			isloading:false,
+			//设备列表
+			device_list:[
+				{
+					id:'3265',
+					name:'德锐斯工业园大门'
+				},
+				{
+					id:'5678',
+					name:'德锐斯工业园南门'
+				},
+				{
+					id:'1577',
+					name:'德锐斯工业园北门'
+				},
+			]
+		};
+	},
+	onLoad() {
+		that=this
+	},
+	methods:{
+		open(){
+			//进度条加载
+			this.isloading=true
+			setTimeout(()=>{
+				that.isloading=false
+				that.isopen=true
+				that.$showModel('开门成功',false)
+			},2300)
+		},
+		/**
+		 * 立即开门
+		 */
+		openDoor() {
+			 let params = {};
+			 params['member_id'] = app.globalData.member.id;
+			 params['device_id'] = device_id;
+			 let operation = 'member/openDoor';
+			 app.globalData.postRequest(params, operation, function (res) {
+				 that.isloading=false
+				 that.isopen=true
+			   app.globalData.oneFailHint(res.data.result_msg,function(){
+				   that.isopen=false
+			   });
+			 });
+		},
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+	page{
+		background-color: #F1F1F1;
+	}
+	
+	 .device{
+		 background-color: #FFFFFF;
+		display: flex;
+		justify-content: space-between;
+		padding: 30rpx;
+		
+		.bg-blue{
+			background-color: #59a5f0;
+			color: #FFFFFF;
			
+		}
+	}
+</style>

+ 258 - 4
pages/index/access-record/access-record.vue

@@ -1,18 +1,191 @@
 <template>
-	<view>
-		<u-empty  marginTop="300"  icon-size="400" text="暂无记录" src="http://139.9.103.171:1888/miniofile/xlyq/empty/empty.png"></u-empty>
+	<view class="container">
+		<view class="tabs" style="width: 420rpx;margin: 0rpx  auto;padding-top: 20rpx;">
+			<scroll-view scroll-x class="nav" >
+				<view class="flex text-center">
+					<view  style="border: 1rpx solid #59a5f0;padding:15rpx 20rpx;" class="flex-sub" :class="index==subsectionCurrent?'nav-checked':'nav-unchecked'" v-for="(item,index) in subsectionList" :key="index" @tap="subsectionChange(index)" >
+						{{item.name}}
+					</view>
+				</view>
+			</scroll-view>
+		</view>
+		<view style="height: 100%;">
+			<swiper style="height: 100%;" :current="subsectionCurrent"  @change="swiperChange"
+				@animationfinish="animationfinish">
+				<swiper-item >
+					<scroll-view scroll-y style="height: 100%;">
+						<view class="history">
+							<u-time-line>
+								<u-time-line-item nodeTop="2" v-for="(item,index) in dataList" :key="index">
+									<template v-slot:node>
+										<view class="u-node bg-white" >
+											<u-icon name="clock-fill" color="#59a5f0" :size="28"></u-icon>
+										</view>
+									</template>
+									<template v-slot:content>
+										<view>
+											<view class="">{{item.time}}</view>
+											<view style="width: 636rpx;">
+												<view class="content">
+													<view class="flex justify-between">
+														<view class="">
+															<view class="nav-title">
+																<text class="">人员姓名:</text>
+																<text class="">{{item.name}}</text>
+															</view>
+															<view class="nav-title">
+																<text >所属企业:</text>
+																<text>{{item.enterpriseName}}</text>
+															</view>
+															<view class="nav-title">
+																<text >开门设备:</text>
+																<text>{{item.device}}</text>
+															</view>
+														</view>
+														<view class="" v-if="item.faceImg">
+															<image @click="preViewImg(item.faceImg)" :src="item.faceImg" style="width: 110rpx;height: 110rpx;border-radius: 10rpx;" mode=""></image>
+														</view>
+													</view>
+													<view class="nav-title">
+														<text >开门方式:</text>
+														<text>{{item.openType}}</text>
+													</view>
+												</view>
+												
+											</view>
+										</view>
+									</template>
+								</u-time-line-item>
+							</u-time-line>
+						</view>
+						<u-divider height="80">到底了</u-divider>	
+					</scroll-view>
+				</swiper-item>
+				<swiper-item >
+					<scroll-view scroll-y style="height: 100%;">
+						<view class="history">
+							<u-time-line>
+								<u-time-line-item v-if="index==1" nodeTop="2" v-for="(item,index) in dataList" :key="index">
+									<template v-slot:node>
+										<view class="u-node bg-white" >
+											<u-icon name="clock-fill" color="#59a5f0" :size="28"></u-icon>
+										</view>
+									</template>
+									<template v-slot:content>
+										<view>
+											<view >{{item.time}}</view>
+											<view style="width: 636rpx;">
+												<view class="content">
+													<view class="flex justify-between">
+														<view class="">
+															<view class="nav-title">
+																<text class="">人员姓名:</text>
+																<text class="">{{item.name}}</text>
+															</view>
+															<view class="nav-title">
+																<text >所属企业:</text>
+																<text>{{item.enterpriseName}}</text>
+															</view>
+															<view class="nav-title">
+																<text >开门设备:</text>
+																<text>{{item.device}}</text>
+															</view>
+														</view>
+														<view class="" v-if="item.faceImg">
+															<image @click="preViewImg(item.faceImg)" :src="item.faceImg" style="width: 110rpx;height: 110rpx;border-radius: 10rpx;" mode=""></image>
+														</view>
+													</view>
+													<view class="nav-title">
+														<text >开门方式:</text>
+														<text>{{item.openType}}</text>
+													</view>
+												</view>
+												
+											</view>
+										</view>
+									</template>
+								</u-time-line-item>
+							</u-time-line>
+						</view>
+						<u-divider height="80">到底了</u-divider>
+					</scroll-view>
+				</swiper-item>
+			</swiper>
+		</view>
 	</view>
 </template>
-
 <script>
 	export default {
+		components:{
+			
+		},
 		data() {
 			return {
+				dateShow:false,
+				dataList:[],
+				subsectionCurrent:0,
+				swiperCurrent:0,
+				subsectionList:[
+					{
+						name: '全部记录'
+					},
+					{
+						name: '迟到记录'
+					}
+				]
 				
 			}
 		},
+		onLoad() {
+			this.fetchStaticData()
+		},
 		methods: {
-			
+			subsectionChange(index){
+				this.subsectionCurrent=index
+			},
+			swiperChange(e) {
+			  uni.pageScrollTo({
+			      scrollTop: 0,
+			      duration: 0
+			  });
+			  this.subsectionCurrent = e.detail.current
+			},
+			animationfinish({detail: { current }}) {
+				this.swiperCurrent = current;
+				this.subsectionCurrent = current;
+			},
+			preViewImg(img){
+				let urls=[img]
+				uni.previewImage({
+					urls:urls
+				})
+			},
+			fetchStaticData(){
+				this.dataList=[
+					{
+						name:'李德希',
+						enterpriseName:'宁夏新金凰印刷有限公司',
+						device:'园区大门',
+						openType:'小程序一键开门',
+						time:'2020-12-19 17:02:45'
+					},
+					{
+						name:'李德希',
+						enterpriseName:'宁夏新金凰印刷有限公司',
+						device:'园区大门',
+						openType:'面部识别开门',
+						faceImg:'http://139.9.103.171:1888/miniofile/xlyq/del/ldx.png',
+						time:'2020-12-19 17:02:45'
+					},
+					{
+						name:'李德希',
+						enterpriseName:'宁夏新金凰印刷有限公司',
+						device:'园区大门',
+						openType:'小程序一键开门',
+						time:'2020-12-19 17:02:45'
+					},
+				]
+			}
 		}
 	}
 </script>
@@ -21,4 +194,85 @@
 page{
 	background-color: #FFFFFF;
 }
+.nav .cu-item {
+    height: 70rpx;
+    display: inline-block;
+    line-height: 70rpx;
+    margin: 0 10rpx;
+    padding: 0 20rpx;
+}
+
+.nav-title {
+	font-size: 28upx;
+	font-weight: 300;
+	padding-top: 20rpx;
+}
+.history{
+	box-sizing: border-box;
+	padding: 40rpx 20rpx 20rpx 50rpx;
+}
+.content{
+	box-shadow: 0 8rpx 10rpx rgba(234, 234, 234, 0.5),8rpx 0rpx 10rpx rgba(234, 234, 234,.5),-8rpx 0rpx 10rpx rgba(234, 234, 234,.5);
+	background-color: #FFFFFF;
+	padding: 20rpx;
+	border-radius: 10rpx;
+	margin: 20rpx 0;
+}
+
+.nav-checked{
+	background-color: #59a5f0;
+	color: #FFFFFF;
+	font-weight: 800;
+}
+
+.nav-unchecked{
+	background-color: #FFFFFF;
+	color: #59a5f0;
+}
+	
+.text-checked{
+	font-weight: 800;
+	background-color: #FFFFFF;
+	color: #2f7ff5;
+	font-size: 800;
+}
+view{
+	box-sizing: border-box;
+}
+.container {
+	  height: calc(100vh);
+	  padding: 120rpx 0rpx 0rpx;
+	 .tabs {
+		height: 120rpx;
+		position: fixed;
+		top: 0rpx;
+		left: 0;
+		right: 0;
+		width: 100%;
+		z-index: 3;
+	  }
+}
+	.scroll-view {
+		height: 150rpx;
+		white-space: nowrap;
+		width: 100%;
+		position: relative;
+		top: -30rpx;
+	}
+	.scroll-view-item {
+		color: #FFFFFF;
+		background-image: linear-gradient(to right, #2f7ff5, #2f7ff5);
+		display: inline-block;
+		text-align: left;
+		padding: 20rpx 40rpx;
+		margin:0 8rpx;
+		width:24%;
+		border-radius: 12rpx;
+	}
+	.scroll-view-item:first-child{
+		margin-left: 30rpx;
+	}
+	.scroll-view-item:last-child{
+		margin-right: 30rpx;
+	}
 </style>

+ 197 - 0
pages/index/fire/alarm-record.vue

@@ -0,0 +1,197 @@
+<template>
+	<view  :class="$isEmpty(list)?'bg-white':'container'">
+		<u-calendar v-model="dateShow" mode="date" @change="change"></u-calendar>
+		<view v-if="!$isEmpty(list)" class="top fixed" >
+			<view class="flex justify-between">
+				<view class="flex">
+					<view class="flex justify-center align-center">
+						<image v-if="fireType==0" style="width: 80rpx;height: 80rpx;" src="/static/index/fire/yanwu1.png"></image>
+						<image v-if="fireType==1" style="width: 80rpx;height: 80rpx;" src="/static/index/fire/ranqi2.png"></image>
+						<image v-if="fireType==2" style="width: 80rpx;height: 80rpx;" src="/static/index/fire/xiaofang.png"></image>
+					</view>
+					<view class="padding-left-40">
+						<view >
+							<text class="text-bold">设备类型:</text>
+							<text v-if="fireType==0">烟感报警器</text>
+							<text v-if="fireType==1">燃气报警器</text>
+							<text v-if="fireType==2">消防栓</text>
+						</view>
+						<view class="padding-top-10">
+							<text class="text-bold">告警记录:</text>
+							<text class="text-red padding-right-10 text-bold text-lg">{{total?total:0}}</text>条
+						</view>
+					</view>
+				</view>
+				<view @click="dateShow=true" class="flex text-blue  justify-center align-center">
+					<text class="cuIcon-filter padding-right-20"></text>
+					<text class="">筛选</text>
+				</view>
+			</view>
+		</view>
+		<mescroll-body ref="mescrollRef" @init="mescrollInit" @down="downCallback" @up="upCallback" :down="downOption" :up="upOption">
+			<view v-if="!$isEmpty(list)"  class="history">
+				<u-time-line>
+					<u-time-line-item nodeTop="2" v-for="(item,index) in list" :key="index">
+						<template v-slot:node>
+							<view class="u-node bg-white" >
+								<u-icon name="clock-fill" color="#59a5f0" :size="28"></u-icon>
+							</view>
+						</template>
+						<template v-slot:content>
+							<view>
+								<view class="u-order-title">{{item.recordTime}}</view>
+								<view class="content" style="width: 636rpx;">
+									<view >
+										<text >告警等级:</text>
+										<text v-if="item.level==1" class="text-red">严重告警</text>
+										<text v-else-if="item.level==2" class="text-orange">警告告警</text>
+										<text v-else >提示告警</text>
+									</view>
+									<view class="nav-title">
+										<text class="">告警类型:</text>
+										<text v-text="item.alarmType==0?'业务报警':'设备报警'"></text>
+									</view>
+									<view class="nav-title">
+										<text class="">设备名称:</text>
+										<text>{{item.deviceName}}</text>
+									</view>
+									<view class="nav-title">
+										<text class="">告警原因:</text>
+										<text>{{item.name}}</text>
+									</view>
+									<view v-if="item.monitorValue" class="nav-title">
+										<text class="">监测值:</text>
+										<text class="text-red text-bold">{{item.monitorValue}}</text>
+									</view>
+								</view>
+							</view>
+						</template>
+					</u-time-line-item>
+				</u-time-line>
+			</view>
+		</mescroll-body>
+	</view>
+</template>
+
+<script>
+	import MescrollMixin from "@/components/mescroll-body/mescroll-mixins.js";
+	export default {
+		mixins:[MescrollMixin],
+		data() {
+			return {
+				fireType:0,
+				
+				dateShow:false,
+				list:[],
+				total:'',
+				deviceId:'' ,//设备列表的imei等于设备告警激励的deviceId
+				
+				downOption: {
+					use: true,
+					auto: false
+				},
+				upOption: {
+					page: {
+						page: 0,
+						size: 10
+					},
+					noMoreSize: 3, 
+					empty: {
+						tip: '暂无相关数据'
+					}
+				},
+			}
+		},
+		onLoad(options) {
+			this.deviceId=options.imei || ''
+		},
+		onShow() {
+			this.fireType=getApp().globalData.fireType || 0
+			if (this.fireType==0) {
+				uni.setNavigationBarTitle({
+					title:"烟感报警记录"
+				})
+			}else if (this.fireType==1) {
+				uni.setNavigationBarTitle({
+					title:"气感报警记录"
+				})
+			}else if (this.fireType==2) {
+				uni.setNavigationBarTitle({
+					title:"消防水压报警记录"
+				})
+			}
+			
+		},
+		methods: {
+			change(e) {
+				console.log(e);
+			},
+			downCallback(){
+				setTimeout(()=>{
+					this.list=[]
+					this.mescroll.resetUpScroll()
+				},1500)
+			},
+			upCallback(mescroll) {
+				let params={
+					current:mescroll.num,
+					size:mescroll.size,
+					deviceId:this.deviceId
+				}
+				try{
+					this.$api.fireDevice.alarmRecord(params).then(res=>{
+						let data=res.data.records
+						let length=data.length
+						this.total=res.data.total
+						mescroll.endBySize(length, this.total);
+						if(mescroll.num == 1) this.list = []; //如果是第一页需手动制空列表
+						this.list=this.list.concat(data); //追加新数据
+					})
+				}catch(e){
+					console.log(e);
+					mescroll.endErr();
+				}
+			},
+		}
+	}
+</script>
+
+<style lang="scss">
+.container {
+  height: calc(100vh );
+  padding: 140rpx 0rpx 0rpx;
+ .fixed {
+	position: fixed;
+	top: 0rpx;
+	left: 0;
+	width: 100%;
+	background-color: #FFFFFF;
+	box-sizing: border-box;
+	z-index: 999;
+  }
+}
+
+.nav-title {
+	font-size: 28upx;
+	font-weight: 300;
+	padding-top: 20rpx;
+}
+	
+.top{
+	padding: 30rpx;
+	box-shadow: 0 4rpx 6rpx rgba(204, 204, 204,.2);
+	// border-bottom: 1rpx solid #efefef;
+}
+	
+.history{
+		background-color: #FFFFFF;
+		padding: 40rpx 20rpx 20rpx 50rpx;
+	}
+.content{
+	box-shadow: 0 8rpx 2rpx rgba(204, 204, 204,.2),0 -8rpx 6rpx rgba(204, 204, 204,.1),8rpx 0rpx 6rpx rgba(204, 204, 204,.1),-8rpx 0rpx 6rpx rgba(204, 204, 204,.1);
+	background-color: #FFFFFF;
+	padding: 20rpx;
+	border-radius: 10rpx;
+	margin-top: 20rpx;
+}
+</style>

+ 191 - 0
pages/index/fire/detail.vue

@@ -0,0 +1,191 @@
+<template>
+	<view class="page">
+		<view class="data" >
+			<view class="top">
+				<view class="left">
+					
+					<view class="title flex " style="padding:0  24rpx;">
+						<u-icon  size="36" name="http://139.9.103.171:1888/miniofile/xlyq/sn.png"></u-icon>
+						<text class="padding-left-10 text-lg text-bold">IEMI:{{dataDetail.imei}}</text>
+						<text @click.stop="copy(dataDetail.imei)" style="text-decoration: underline;" class="text-blue padding-left-20">复制</text>
+					</view>
+				</view>
+				<view class="right">
+					<view v-if="dataDetail.defenseStatus==1" class="radius sm cu-btn line-green">
+						已上线
+					</view>
+					<view v-if="dataDetail.defenseStatus==2" class="radius sm cu-btn line-red">
+						已下线
+					</view>
+				</view>
+			</view>
+			<view class="item">
+				<view class="left">
+					<view style="padding: 0 30rpx;">
+						<view class="content flex">
+							<u-icon  size="36" name="http://139.9.103.171:1888/miniofile/xlyq/name.png"></u-icon>
+							<text class="text-bold padding-left-10">设备名称:</text>
+							<text>{{dataDetail.deviceName}}</text>
+						</view>
+						<view class="content flex">
+							<view class="flex">
+								<u-icon  size="36" name="http://139.9.103.171:1888/miniofile/xlyq/dianchi.png"></u-icon>
+								<text class="text-bold padding-left-10">电池电量:</text>
+							</view>
+							<view class="striped active cu-progress round margin-top-sm " style="width: 60%;">
+								<view :style="[{ width:loading?dataDetail.battery+'%':''}]" style="background-color: #16c60c;color: #FFFFFF;">
+									{{dataDetail.battery?dataDetail.battery:0}}%
+								</view>
+							</view>
+						</view>
+						<view class="content flex">
+							<view class="flex">
+								<u-icon  size="36" name="http://139.9.103.171:1888/miniofile/xlyq/wifi.png"></u-icon>
+								<text class="text-bold padding-left-10">信号强弱:</text>
+							</view>
+							<view class="striped active cu-progress round margin-top-sm " style="width: 60%;">
+								<view :style="[{ width:loading?dataDetail.signalIntensity+'%':''}]" style="width: 58%;background-color: #fc5f44;color: #FFFFFF;">
+									{{dataDetail.signalIntensity?dataDetail.signalIntensity:0}}%
+								</view>
+							</view>
+						</view>
+						<view  class="content flex">
+							<u-icon  size="36" name="http://139.9.103.171:1888/miniofile/xlyq/jiance.png"></u-icon>
+							<text class="text-bold padding-left-10">监测值:</text>
+							<text class="text-bold text-red" v-text="dataDetail.monitorValue?dataDetail.monitorValue:'未知'" ></text>
+						</view>
+						<view  class="content flex">
+							<u-icon name="changshangguanliyuan" custom-prefix="custom-icon" size="32" color="#00aa00"></u-icon>
+							<text class="text-bold padding-left-10">厂商名称:</text>
+							<text :class="dataDetail.manufacturers?'':'text-unknow'"  v-text="dataDetail.manufacturers?dataDetail.manufacturers:'未知'"></text>
+						</view>
+						<view  class="content flex">
+							<u-icon  size="36" name="http://139.9.103.171:1888/miniofile/xlyq/updateTime2.png"></u-icon>
+							<text class="text-bold padding-left-10">更新时间:</text>
+							<text class="text-unknow"  v-text="dataDetail.lastUpdateTime?dataDetail.lastUpdateTime:'未知'"></text>
+						</view>
+						<view  class="content flex">
+							<u-icon name="ICCID" custom-prefix="custom-icon" size="32" color="#55aaff"></u-icon>
+							<text class="text-bold padding-left-10">ICCID:</text>
+							<text :class="dataDetail.iccid?'':'text-unknow'"  v-text="dataDetail.iccid?dataDetail.iccid:'未知'"></text>
+						</view>
+						
+						<view  class="content flex">
+							<u-icon name="MCUruanjianyouneirong" custom-prefix="custom-icon" size="32" color="#ff00ff"></u-icon>
+							<text class="text-bold padding-left-10">Mcu软件版本:</text>
+							<text :class="dataDetail.mcuEdition?'':'text-unknow'"  v-text="dataDetail.mcuEdition?dataDetail.mcuEdition:'未知'"></text>
+						</view>
+						
+						<view  class="content flex">
+							<u-icon name="xinghao" custom-prefix="custom-icon" size="32" color="#64c692"></u-icon>
+							<text class="text-bold padding-left-10">NB模组型号:</text>
+							<text :class="dataDetail.nbModuleModel?'':'text-unknow'"  v-text="dataDetail.nbModuleModel?dataDetail.nbModuleModel:'未知'"></text>
+						</view>
+						<view  class="content flex">
+							<u-icon name="banbenhao" custom-prefix="custom-icon" size="32" color="#71a4f9"></u-icon>
+							<text class="text-bold padding-left-10">NB模组版本信息:</text>
+							<text :class="dataDetail.nbModuleEdition?'':'text-unknow'"  v-text="dataDetail.nbModuleEdition?dataDetail.nbModuleEdition:'未知'"></text>
+						</view>
+						<view  class="content flex">
+							<u-icon name="cichang" custom-prefix="custom-icon" size="32" color="#5bccc9"></u-icon>
+							<text class="text-bold padding-left-10">当场磁场值:</text>
+							<text :class="dataDetail.magneticField?'':'text-unknow'"  v-text="dataDetail.magneticField?dataDetail.magneticField:'未知'"></text>
+						</view>
+						<view  class="content flex">
+							<u-icon name="-" custom-prefix="custom-icon" size="32" color="#d0267b"></u-icon>
+							<text class="text-bold padding-left-10">心跳保护时间:</text>
+							<text :class="dataDetail.heartbeatTime?'':'text-unknow'"  v-text="dataDetail.heartbeatTime?dataDetail.heartbeatTime:'未知'"></text>
+						</view>
+						<view  class="content flex">
+							<u-icon name="wuxian" custom-prefix="custom-icon" size="32" color="#6fa03c"></u-icon>
+							<text class="text-bold padding-left-10">无线信号覆盖等级:</text>
+							<text :class="dataDetail.wirelessLevel?'':'text-unknow'"  v-text="dataDetail.wirelessLevel?dataDetail.wirelessLevel:'未知'"></text>
+						</view>
+						<view  class="content flex">
+							<u-icon name="fashegongshuai" custom-prefix="custom-icon" size="32" color="#bcd62d"></u-icon>
+							<text class="text-bold padding-left-10">终端发射功率:</text>
+							<text :class="dataDetail.transmittingPower?'':'text-unknow'"  v-text="dataDetail.transmittingPower?dataDetail.transmittingPower:'未知'"></text>
+						</view>
+					</view>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+export default {
+	name: 'card',
+	data() {
+		return {
+			loading:false,
+			dataDetail:{}
+		};
+	},
+	onShow() {
+		this.dataDetail= getApp().globalData.fireHydrantDetail
+	},
+	onLoad(options) {
+		let that = this;
+		setTimeout(function() {
+			that.loading = true
+		}, 500)
+	},
+	methods:{
+		copy(data){
+			uni.setClipboardData({
+				data:data
+			})
+		},
+	}
+};
+</script>
+
+<style lang="scss">
+page{
+	background-color: #FFFFFF;
+}
+
+.text-unknow{
+	color: #1296db;
+}
+	
+.data {
+	background-color: #ffffff;
+	border-radius: 6rpx;
+	box-sizing: border-box;
+	margin-top: 40rpx;
+	padding: 20rpx 10rpx;
+	font-size: 28rpx;
+	.top {
+		display: flex;
+		justify-content: space-between;
+		padding-bottom: 20rpx;
+		// border-bottom: 1rpx solid $dt-border-color-sm;
+		.left {
+			display: flex;
+			align-items: center;
+			.title {
+				margin: 0 10rpx;
+				font-size: 30rpx;
+			}
+		}
+		.right{
+			margin-right: 10rpx;
+		}
+	}
+	.item {
+		margin: 5rpx 0 20rpx 0;
+		.content {
+			// border-bottom: 1rpx dashed #DDDDDD;
+			padding: 30rpx 0;
+		}
+	}
+	.bottom {
+		display: flex;
+		margin-top: 30rpx;
+		justify-content: flex-end;
+		align-items: center;
+	}
+}
+</style>

+ 158 - 0
pages/index/fire/list/card.vue

@@ -0,0 +1,158 @@
+<template>
+	<view class="">
+		<view  class="data" v-for="(item, index) in list" :key="index">
+			<view class="top">
+				<view class="left">
+					<view class="title">
+						<text>IEMI:{{item.imei}}</text>
+						<text @click.stop="copy(item.imei)" style="text-decoration: underline;" class="text-blue padding-left-20">复制</text>
+					</view>
+				</view>
+				<view class="right">
+					<view v-if="item.defenseStatus==1" class="radius sm cu-btn line-green">
+						已上线
+					</view>
+					<view v-if="item.defenseStatus==2" class="radius sm cu-btn line-red">
+						已下线
+					</view>
+				</view>
+			</view>
+			<view class="item">
+				<view class="left">
+					<view style="padding: 0 30rpx;">
+						<view class="content flex">
+							<u-icon  size="36" name="http://139.9.103.171:1888/miniofile/xlyq/xiaofangshuang.png"></u-icon>
+							<text class="text-bold padding-left-10">设备名称:</text>
+							<text>{{item.deviceName}}</text>
+						</view>
+						<view class="content flex">
+							<view class="flex">
+								<u-icon  size="36" name="http://139.9.103.171:1888/miniofile/xlyq/dianchi.png"></u-icon>
+								<text class="text-bold padding-left-10">电池电量:</text>
+							</view>
+							<view class="striped active cu-progress round margin-top-sm " style="width: 60%;">
+								<view :style="[{ width:loading?item.battery+'%':''}]" style="background-color: #16c60c;color: #FFFFFF;">
+									{{item.battery?item.battery:0}}%
+								</view>
+							</view>
+						</view>
+						<view class="content flex">
+							<view class="flex">
+								<u-icon  size="36" name="http://139.9.103.171:1888/miniofile/xlyq/wifi.png"></u-icon>
+								<text class="text-bold padding-left-10">信号强弱:</text>
+							</view>
+							<view class="striped active cu-progress round margin-top-sm " style="width: 60%;">
+								<view :style="[{ width:loading?item.signalIntensity+'%':''}]" style="background-color: #fc5f44;color: #FFFFFF;">
+									{{item.signalIntensity?item.signalIntensity:0}}%
+								</view>
+							</view>
+						</view>
+						<view  class="content flex">
+							<u-icon  size="36" name="http://139.9.103.171:1888/miniofile/xlyq/jiance.png"></u-icon>
+							<text class="text-bold padding-left-10">监测值:</text>
+							<text class="text-bold" v-text="item.monitorValue?item.monitorValue:'未知'" style="color: #1296db;"></text>
+						</view>
+						<view  class="content flex">
+							<u-icon  size="36" name="http://139.9.103.171:1888/miniofile/xlyq/updateTime2.png"></u-icon>
+							<text class="text-bold padding-left-10">更新时间:</text>
+							<text  v-text="item.lastUpdateTime?item.lastUpdateTime:'未知'"></text>
+						</view>
+					</view>
+				</view>
+			</view>
+			<view class="bottom flex" >
+				<view @click="goDetail(item)" class="cu-btn  sm round line-blue" >
+					查看详情
+				</view>
+				<view @click="goAlarm(item)" class="cu-btn  sm round line-blue" style="margin: 0 20rpx;">
+					告警记录
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+export default {
+	name: 'card',
+	props:{
+		list:{
+			type:Array,
+			default:()=>{
+				[]
+			}
+		}
+	},
+	data() {
+		return {
+			loading:false
+		};
+	},
+	created() {
+		let that = this;
+		setTimeout(function() {
+			that.loading = true
+		}, 500)
+	},
+	methods:{
+		copy(data){
+			uni.setClipboardData({
+				data:data
+			})
+		},
+		goDetail(item){
+			getApp().globalData.fireHydrantDetail=item
+			uni.navigateTo({
+				url:'/pages/index/fire/detail'
+			})
+		},
+		goAlarm(item){
+			uni.navigateTo({
+				url:"/pages/index/fire/alarm-record?imei="+item.imei
+			})
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+.data {
+	width: 710rpx;
+	background-color: #ffffff;
+	margin: 20rpx auto;
+	border-radius: 6rpx;
+	box-sizing: border-box;
+	padding: 20rpx 10rpx;
+	font-size: 28rpx;
+	.top {
+		display: flex;
+		justify-content: space-between;
+		padding-bottom: 20rpx;
+		border-bottom: 1rpx solid $dt-border-color-sm;
+		.left {
+			display: flex;
+			align-items: center;
+			.title {
+				margin: 0 10rpx;
+				font-size: 30rpx;
+			}
+		}
+		.right{
+			margin-right: 10rpx;
+		}
+	}
+	.item {
+		margin: 5rpx 0 20rpx 0;
+		.content {
+			border-bottom: 1rpx dashed #DDDDDD;
+			padding: 30rpx 0;
+		}
+	}
+	.bottom {
+		display: flex;
+		margin-top: 30rpx;
+		justify-content: flex-end;
+		align-items: center;
+	}
+}
+</style>

+ 118 - 0
pages/index/fire/list/item.vue

@@ -0,0 +1,118 @@
+<template>
+	<MeScroll  :up="up" :down="down" @up="upFn" :fixed="false" @down="downFn" @init="initMeScroll">
+		<card  :list="list" ></card>
+	</MeScroll>
+</template>
+<script>
+	import MeScroll from '@/components/mescroll-body/mescroll-uni.vue'
+	import card from './card.vue'
+	var app=getApp()
+	export default {
+		components:{
+			MeScroll,card
+		},
+		props: {
+			refresh:Boolean,
+			type: Number,
+			i: Number,
+			fireType: Number,
+			item:Object
+		},
+		data() {
+			return {
+				isInit: false, // 是否初始化
+				list: [], // 列表数据
+				mescroll: null, // mescroll 对象
+				// 上拉配置参数
+				up: {
+					noMoreSize: 5, 
+					auto: true,
+					page: {
+						page: 0,
+						size: 10
+					}
+				},
+				// 下拉配置参数
+				down: {
+					use: true, 
+					auto: false
+				}
+			}
+		},
+		watch:{
+			refresh() {
+				console.log("我要刷新了");
+				this.mescroll.resetUpScroll()
+			},
+			type(val) {
+				if(!this.isInit && val === this.i) {
+					this.mescroll.resetUpScroll()
+				}
+			}
+		},
+		mounted() {
+			if(!this.isInit && this.i === 0) {
+				this.mescroll.resetUpScroll()
+			}
+		},
+		methods: {
+			/**
+			 * @param {Object} mescroll 初始化组件
+			 */
+			initMeScroll(mescroll) {
+				this.mescroll = mescroll
+			},
+			/**
+			 * @param {Object} mescroll 上拉回调
+			 */
+			upFn(mescroll) {
+				let that=this
+				let params={
+					creditCode:this.$cache.get('creditCode'),	//公司统一信用代码
+					current:mescroll.num,
+					size:mescroll.size,
+				}
+				if (this.fireType==0) {
+					//烟感报警
+					params.prefix=this.$device_prefix.SMOKE
+				}else if (this.fireType==1) {
+					//燃气告警
+					params.prefix=this.$device_prefix.GAS
+				}else if (this.fireType==2) {
+					//消防水压
+					params.deviceType=this.$device_type.FIRE_HYDRANT
+				}
+				
+				if (!this.$isEmpty(this.item.value)) {
+					params.deviceStatus=this.item.value	//设备状态
+				}
+				try{
+					this.$api.fireDevice.page(params).then(res=>{
+						let data=res.data.records
+						let length=data.length
+						let total=res.data.total
+						mescroll.endBySize(length, total);
+						if(mescroll.num == 1) that.list = []; //如果是第一页需手动制空列表
+						that.list=that.list.concat(data); //追加新数据
+					})
+				}catch(e){
+					mescroll.endErr();
+				}
+			},
+			/**
+			 * 下拉回调
+			 * */
+			downFn(mescroll) {
+				setTimeout(()=>{
+					this.mescroll.resetUpScroll()
+				},1500)
+			},
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	view{
+		box-sizing: border-box;
+	}
+</style>

+ 131 - 0
pages/index/fire/list/list.vue

@@ -0,0 +1,131 @@
+<template>
+	<view class="container">
+		<view class="tabs">
+			<scroll-view scroll-x class="bg-white nav">
+				<view class="flex text-center">
+					<view class="cu-item flex-sub" :class="index==current?'text-blue text-xl text-bold ':'text-lg'" v-for="(item,index) in tabs" :key="index" @tap="tabChange(index)" >
+						{{item.name}}
+					</view>
+				</view>
+			</scroll-view>
+		</view>
+		<view style="height: 100%;">
+			<swiper style="height: 100%;" :current="current"  @change="swiperChange"
+				@animationfinish="animationfinish">
+				<swiper-item  v-for="(item, index) in tabs" :key="index">
+					<scroll-view scroll-y style="height: 100%;">
+						<item :fireType="fireType" :refresh="refresh" :i="index" :item="item" :type="current"></item>
+					</scroll-view>
+				</swiper-item>
+			</swiper>
+		</view>
+	</view>
+</template>
+<script>
+	import item from "./item.vue"
+	export default {
+		components: {
+			item
+		},
+		data() {
+			return {
+				//0 烟感报警 1 燃气告警 2 消防水压
+				fireType:0,
+				//是否第一次进入页面
+				isfirst:false,
+				//动态让item主动刷新
+				refresh:false,
+				scrollLeft:0,
+				current: 0,
+				swiperCurrent:0,
+				tabs: [
+					{
+						name: '全部',
+						value:''
+					},
+					{
+						name: '正常',
+						value:1
+					},
+					{
+						name: '告警',
+						value:2
+					},
+					{
+						name:'失联',
+						value:3
+					},
+					{
+						name:'停用',
+						value:4
+					},
+				],
+			}
+		},
+		onShow(){
+			if (this.isfirst) {
+				this.isfirst=false
+			}else{
+				//刷新列表
+				this.refresh=!this.refresh
+			}
+		},
+		onLoad(options) {
+			this.fireType=options.fireType || 0
+			getApp().globalData.fireType=this.fireType
+			if (this.fireType==1) {
+				uni.setNavigationBarTitle({
+				    title: '气感'
+				});
+			}else if (this.fireType==2) {
+				uni.setNavigationBarTitle({
+				    title: '消防栓'
+				});
+			}
+			this.isfirst=true
+		},
+		methods:{
+			tabChange(index) {
+				this.current = index
+				// this.scrollLeft = (index - 1) * 60
+			},
+			swiperChange(e) {
+			  uni.pageScrollTo({
+			      scrollTop: 0,
+			      duration: 0
+			  });
+			  this.current = e.detail.current
+			  // this.scrollLeft = (this.current - 1) * 60
+			},
+			animationfinish({detail: { current }}) {
+				this.swiperCurrent = current;
+				this.current = current;
+			},
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.text-blue{
+		color: #59a5f0;
+	}
+	.text-xl{
+		font-size: 34rpx;
+	}
+	.container {
+	  height: calc(100vh);
+	  background-color: #F6F6F6;
+	  padding: 78rpx 0rpx 0rpx;
+	 .tabs {
+	    position: fixed;
+	    top: -10rpx;
+		left: 0;
+		display: flex;
+		align-items: center;
+		width: 100%;
+		background-color: #FFFFFF;
+		box-sizing: border-box;
+		z-index: 3;
+	  }
+}
+</style>

+ 228 - 114
pages/index/index.vue

@@ -1,81 +1,111 @@
 <template>
-	<view style="min-height: 100vh;background-color: #FFFFFF;">
-		<!-- 头部 -->
-		<u-navbar :border-bottom="false" :is-back="false" title=" ">
-			<view class="slot-wrap">
-				<picker @change="ganderChange" range-key="name" :value="ganderIndex" :range="ganderArray">
-				  <view class="padding-left-40 ">
-				  	<text class="padding-right-10" style="font-size: 34rpx;">{{gander?gander:'暂无园区信息'}}</text>
-					<u-icon  name="arrow-down-fill" top="-5" size="20" color="#7f7f7f"></u-icon>
-				  </view>
-				</picker>
-			</view>
-		</u-navbar>
-		<!-- banner -->
-		<swiper   class="screen-swiper square-dot "  :indicator-dots="true" :circular="true"
-		 :autoplay="true" interval="5000" duration="500">
-			<swiper-item v-for="(item,index) in bannerList" :key="index">
-				<image   :src="item.image" mode="aspectFill" ></image>
-			</swiper-item>
-		</swiper>
-		<!-- 公告 -->
+	<view class="content">
 		<view class="">
-			<view class="bg-white" style="height: 10rpx;"></view>
-			<hotConsult   :swiperTexts="noticeList"></hotConsult>
-		</view>
-		
-		<!-- 园区服务 -->
-		<view class="bg-white">
-			<view  style="padding: 30rpx 30rpx 0 30rpx; ">
-				<text class="cuIcon-titles text-blue"></text>
-				<text class="text-bold text-lg">园区服务</text>
+			<!-- 头部 -->
+			<u-navbar :border-bottom="false" :is-back="false" title=" ">
+				<view class="slot-wrap">
+					<view class="padding-left-40 " >
+						<u-icon size="34" name="map"></u-icon>
+						<text  style="font-size: 34rpx;padding: 0 10rpx;">{{gander?gander:'暂无园区信息'}}</text>
+					</view>
+				  <!-- <view class="padding-left-40 " @click="ganderShow=true">
+					<text class="padding-right-10" style="font-size: 34rpx;">{{gander?gander:'暂无园区信息'}}</text>
+					<u-icon  :name="ganderShow?'arrow-up-fill':'arrow-down-fill'" top="-5" size="20" color="#7f7f7f"></u-icon>
+				  </view> -->
+				</view>
+			</u-navbar>
+			<!-- banner -->
+			<swiper   class="screen-swiper square-dot "  :indicator-dots="true" :circular="true"
+			 :autoplay="true" interval="5000" duration="500">
+				<swiper-item v-for="(item,index) in bannerList" :key="index">
+					<image   :src="item.image" mode="aspectFill" ></image>
+				</swiper-item>
+			</swiper>
+			<!-- 公告 -->
+			<view v-if="!$isEmpty(noticeList)" class="">
+				<view class="bg-white" style="height: 10rpx;"></view>
+				<hotConsult  @onTap="jump('/pages/index/notice/notice')" @detailTap="goNoticeDetail" :swiperTexts="noticeList"></hotConsult>
 			</view>
-			<view class="nav-list  padding-top-30" >
-				<view hover-class="none"
-					@click="jump(item.index)"
-					class="nav-li light"  :style="{backgroundColor:item.color}" 
-					v-for="(item,index) in elements" :key="index">
-					<view class="nav-title" style="color: #000000;font-size: 28rpx;">{{item.title}}</view>
-					<view class="" style="padding-top: 10rpx;font-size: 24rpx;color: #acacac;">{{item.name}}</view>
-					<image :src="item.icon" mode=""></image>
+			<!-- 园区服务 -->
+			<view class="bg-white">
+				<view  style="padding: 30rpx 30rpx 0 30rpx; ">
+					<text class="cuIcon-titles text-blue"></text>
+					<text class="text-bold text-lg">园区服务</text>
+				</view>
+				<view class="nav-list  padding-top-30" >
+					<view
+						v-if="item.show"
+						hover-class="none"
+						@click="jump(item.url)"
+						class="nav-li light"  :style="{backgroundColor:item.color}" 
+						v-for="(item,index) in elements" :key="index">
+						<view class="nav-title" style="color: #000000;font-size: 28rpx;">{{item.title}}</view>
+						<view class="" style="padding-top: 10rpx;font-size: 24rpx;color: #acacac;">{{item.name}}{{item.show}}</view>
+						<image :src="item.icon" mode=""></image>
+						<u-badge  :is-center="true" type="error" :count="item.count"></u-badge>
+					</view>
 				</view>
 			</view>
-		</view>
-		<view class="bg-white">
-			<view  style="padding: 30rpx 30rpx 0 30rpx; ">
-				<text class="cuIcon-titles text-blue"></text>
-				<text class="text-bold text-lg">消防服务</text>
+			<view class="bg-white" v-if="loginType=='enterprise'">
+				<view  style="padding: 30rpx 30rpx 0 30rpx; ">
+					<text class="cuIcon-titles text-blue"></text>
+					<text class="text-bold text-lg">消防服务</text>
+				</view>
+				<view class="nav-list  padding-top-30" >
+					<view hover-class="none"
+						@click="jump(item.url)"
+						class="nav-li light"  :style="{backgroundColor:item.color}" 
+						v-for="(item,index) in fireList" :key="index">
+						<view class="nav-title" style="color: #000000;font-size: 28rpx;">{{item.title}}</view>
+						<view class="" style="padding-top: 10rpx;font-size: 24rpx;color: #acacac;">{{item.name}}</view>
+						<image :src="item.icon" mode=""></image>
+					</view>
+				</view>
 			</view>
-			<view class="nav-list  padding-top-30" >
-				<view hover-class="none"
-					@click="jump(item.index)"
-					class="nav-li light" navigateTo :style="{backgroundColor:item.color}" 
-					v-for="(item,index) in fireList" :key="index">
-					<view class="nav-title" style="color: #000000;font-size: 28rpx;">{{item.title}}</view>
-					<view class="" style="padding-top: 10rpx;font-size: 24rpx;color: #acacac;">{{item.name}}</view>
-					<image :src="item.icon" mode=""></image>
+			<view class="bg-white padding-bottom-20">
+				<view class="flex justify-between" style="padding: 26rpx;">
+					<view class="">
+						<text class="cuIcon-titles text-blue"></text>
+						<text class="text-bold text-lg">园区动态</text>
+					</view>
+					<view @click="jump('/pages/news/list')" style="padding-top: 6rpx;font-size: 26rpx;color: #676767;">
+						<text>查看更多</text>
+						<text class="cuIcon-right "></text>
+					</view>
 				</view>
+				<infomation :newList="newsList"></infomation>
 			</view>
+			<u-action-sheet @click="ganderChange" :list="ganderArray" v-model="ganderShow"></u-action-sheet>
 		</view>
-		<!-- <u-divider bg-color="#f1f1f1" color="#62a3dc" border-color="#62a3dc"  height="100">查看更多</u-divider> -->
+		<u-tabbar v-model="tabbarCurr"
+		 :icon-size="tabbar.iconSize" 
+		 :active-color="tabbar.activeColor" 
+		 :list="tabbar.list" >
+		 </u-tabbar>
 	</view>
 </template>
 
 <script>
+import {tabbar} from "@/assets/js/tabbar.js"
 import hotConsult from "@/components/hot-consult/hot-consult.vue"
+import infomation from "@/components/infomation/infomation.vue"
 export default {
 	components:{
-		hotConsult
+		hotConsult,infomation
 	},
 	data() {
 		return {
-		
+			//登陆类型
+			loginType:this.$cache.get('loginType') || '',
+			//tabbar
+			tabbarCurr:0,
+			tabbar:tabbar,
 			//园区 label
-			gander:'新邻产业园',
-			//默认选中下标
-			ganderIndex:0, 
+			
+			ganderShow:false,
 			//园区列表
 			ganderArray:[], 
+			gander:'',
 			
 			//banner
 			bannerList: [],
@@ -86,96 +116,183 @@ export default {
 			//园区服务
 		    elements: [],
 			
+			//园区动态
+			newsList:[]
+			
 		};
 	},
+	onShow() {
+		this.fetchNoticeList()
+		if (this.loginType==this.$loginType.ENTERPRISE) {
+			this.fetchWaitAuditNum()
+		}
+	},
 	onLoad() {
+		
 		this.fetchStaticData()
+		this.fetchUserInfo()
+		this.fetchNewsList()
 	},
 	methods:{
-		ganderChange(e){
-			console.log(e);
+		fetchNewsList(){
+			let params={
+				size:5,
+				agencyId:this.$cache.get('agencyId').toString()
+			}
+			this.$api.CMS.page(params).then(res=>{
+				this.newsList=res.data.records
+			})
 		},
-		jump(index){
-			let url=''
-			if (index==1) {
-				//我的企业
-				url='/pages/company/company'
-			}else if(index==2){
-				//人员认证
-				url="/pages/auth/auth"
-			}else if (index==3) {
-				//我的测温
-				url="/pages/index/my-temperature/my-temperature"
-			}else if (index==4) {
-				//通行记录
-				url="/pages/index/access-record/access-record"
+		/**
+		 * 加载用户数据
+		 */
+		fetchUserInfo(){
+		   if (this.loginType==this.$loginType.STAFF) {
+				//员工
+				let phone=this.$cache.get('phone')
+				this.$api.enterprisestaff.detail({phone:phone}).then(res=>{
+					getApp().globalData.userInfo=res.data
+					this.$cache.put("residentialId",res.data.residentialId)
+					this.gander=res.data.agencyName
+				})
+		   }else if (this.loginType==this.$loginType.ENTERPRISE) {
+			   this.gander=this.$cache.get('enterpriseName')
+				let creditCode=this.$cache.get('creditCode')
+				this.$api.enterprise.detail({creditCode:creditCode}).then(res=>{
+					getApp().globalData.userInfo=res.data
+					this.$cache.put("residentialId",res.data.residentialId)
+				})
+		   }
+		},
+		/**
+		 * 加载待审核员工的数量
+		 */
+		fetchWaitAuditNum(){
+			let that=this
+			let obj={
+				enterpriseId:this.$cache.get('enterpriseId'),
+				size:300,
+				examine:0
 			}
+			this.$api.enterprisestaff.page(obj).then(res=>{
+				if (!that.$isEmpty(res.data.records)) {
+					that.elements.forEach(item=>{
+						if (item.url=='/pages/index/staffAudit/list') {
+							item.count=res.data.total
+						}
+					})				
+				}else{
+					that.elements.forEach(item=>{
+						if (item.url=='/pages/index/staffAudit/list') {
+							item.count=0
+						}
+					})	
+				}
+				console.log(that.elements);
+			})
+		},
+		/**
+		 * @param {Object} index更改园区
+		 */
+		ganderChange(index){
+			this.gander=this.ganderArray[index].text
+		},
+		/**
+		 * 跳转
+		 * @param {Object} url
+		 */
+		jump(url){
 			if (!this.$isEmpty(url)) {
 				uni.navigateTo({
 					url:url
 				})
 			}
 		},
+		/**
+		 * 加载通知列表
+		 */
+		fetchNoticeList(){
+			let params={
+				agencyId:this.$cache.get('agencyId')
+			}
+			this.$api.notice.page(params).then(res=>{
+				this.noticeList=res.data.records
+			})
+		},
+		/**
+		 * 跳转到通知详情
+		 */
+		goNoticeDetail(item){
+			console.log(item);
+			getApp().globalData.noticeDetail=item
+			uni.navigateTo({
+				url:"/pages/index/notice/detail",
+			})
+		},
+		/**
+		 * 加载静态数据
+		 */
 		fetchStaticData(){
 			this.bannerList=[
 				{
 				  'image': "http://139.9.103.171:1888/miniofile/xlyq/banner01.jpg"
 				},
 			]
-			
-			this.ganderArray=[
-				{
-					name:'新邻产业园',
-					value:'1'
-				},
-				{
-					name:'新邻工业园',
-					value:'2'
-				}
-			]
-			this.noticeList=[
-				{
-					title:'疫情期间,出入小区请佩戴好口罩,配合门卫大叔做好测温登记工作。谢谢大家的配合。'
-				},
-				{
-					title:'停水停电通知。市政通水设施突发性故障抢修,本小区供水压力暂受影响,敬请做好储水准备,预约影响时间2020年11月28号22时至11月29号16时,由此给您带来不便,敬请谅解。'
-				},
-				{
-					title:'2020年10月1日-2020年10月8日,中秋 国庆节放假,假期间需注意,祝国庆节玩的开心。'
-				},
-				{
-					title:'疫情期间,出入小区请佩戴好口罩,配合门卫大叔做好测温登记工作。谢谢大家的配合。'
-				}
-			]
-			
 			this.elements=[
 				{
 					title: '我的企业',
 					name: 'My business',
 					color: '#f9f4f1',
 					icon: '../../static/index/gander/qiye.png',
-					index:1
+					url:'/pages/company/company',
+					count:0,
+					show:true
 				},
 				{
-					title: '人员认证',
-					name: 'Personnel certification',
+					title: '我的资料',
+					name: 'My profile',
 					color: '#ecf3f9',
 					icon: '../../static/index/gander/renzheng.png',
-					index:2
+					// url:'/pages/auth/auth?operationType=1',
+					url:'/pages/index/myinfo/myinfo',
+					count:0,
+					show:this.loginType==this.$loginType.STAFF
 				}, 
+				{
+					title: '员工审核',
+					name: 'Staff certification',
+					color: '#ecf3f9',
+					icon: '../../static/index/gander/renzheng.png',
+					url:'/pages/index/staffAudit/list',
+					count:0,
+					show:this.loginType==this.$loginType.ENTERPRISE
+				},
 				{
 					title: '我的测温',
 					name: 'My temperature',
 					color: '#f0f3ff',
 					icon: '../../static/index/gander/cewen1.png',
-					index:3
+					url:'/pages/index/my-temperature/my-temperature',
+					count:0,
+					show:this.loginType==this.$loginType.STAFF
+				}, 
+				{
+					title: '员工测温',
+					name: 'Staff temperature',
+					color: '#f0f3ff',
+					icon: '../../static/index/gander/cewen1.png',
+					url:'/pages/index/staff-temperature/staff-temperature',
+					count:0,
+					show:this.loginType==this.$loginType.ENTERPRISE
 				}, 
 				{
 					title: '通行记录',
 					name: 'Access records',
 					color: '#f7f7f5',
 					icon: '../../static/index/gander/tongxing.png',
-					index:4
+					url:'/pages/index/access-record/access-record',
+					count:0,
+					show:true
 				}
 			]
 			this.fireList=[
@@ -184,25 +301,23 @@ export default {
 					name: 'Smoke alarm',
 					color: '#f2f0fd',
 					icon: '../../static/index/fire/yangan.png',
-					index:5
+					url:"/pages/index/fire/list/list?fireType=0"
 				},
 				{
 					title: '燃气告警',
 					name: 'Gas alarm',
-					color: '#fffaf8',
+					color: '#f0ecea',
 					icon: '../../static/index/fire/ranqi.png',
-					index:6
+					url:"/pages/index/fire/list/list?fireType=1"
 				}, 
 				{
 					title: '消防水压',
 					name: 'Fire water pressure',
 					color: '#ecf3f9',
 					icon: '../../static/index/fire/xiaofang.png',
-					index:7
+					url:"/pages/index/fire/list/list?fireType=2"
 				}, 
-				{
-					
-				},
+				
 			]
 		}
 	}
@@ -210,16 +325,15 @@ export default {
 </script>
 
 <style lang="scss" scoped>
-	page{
+	page,.content{
 		background-color: #FFFFFF;
 		min-height: 100vh;
 	}
-	
 	//四宫格 begin
 	.nav-list {
 		display: flex;
 		flex-wrap: wrap;
-		justify-content: center;
+		justify-content: flex-start;
 	}
 	.nav-li {
 		padding: 30upx 30rpx;

+ 3 - 3
pages/index/my-temperature/deatil.vue

@@ -50,9 +50,9 @@
 				dataDetail:{
 					'name':'李德希',
 					'sex':'男',
-					'enterprise':'新邻企业',
-					'temperature':'36.4',
-					'image':'http://139.9.103.171:1888/miniofile/xlyq/del/ldx.png',
+					'enterprise':'宁夏新金凰印刷有限公司',
+					'temperature':'35.5',
+					'image':'http://139.9.103.171:1888/miniofile/xlyq/del/cewen1.jpg',
 					'idCard':'440881199604264598',
 					'time':'2020-12-15 14:18:56'
 				}

+ 201 - 49
pages/index/my-temperature/my-temperature.vue

@@ -1,64 +1,216 @@
 <template>
-	<view class="">
-		<view @click="goDetail(item)" class="mine_order_statue" >
-			<view class="flex justify-between" style="padding: 10rpx 0 10rpx 10rpx ;">
-				<view class="flex">
-					<image mode="heightFix" src="http://139.9.103.171:1888/miniofile/xlyq/del/ldx.png"  ></image>
-					<view  class="flex flex-direction justify-around padding-left-30">
-						<view class="">
-							<text class="text-bold">姓名:</text>
-							<text>李德希</text>
+	<view class="container">
+		<view class="tabs" style="width: 420rpx;margin: 0rpx  auto;padding-top: 20rpx;">
+			<scroll-view scroll-x class="nav" >
+				<view class="flex text-center">
+					<view  style="border: 1rpx solid #59a5f0;padding:15rpx 20rpx;" class="flex-sub" :class="index==subsectionCurrent?'nav-checked':'nav-unchecked'" v-for="(item,index) in subsectionList" :key="index" @tap="subsectionChange(index)" >
+						{{item.name}}
+					</view>
+				</view>
+			</scroll-view>
+		</view>
+		<view style="height: 100%;">
+			<swiper style="height: 100%;" :current="subsectionCurrent"  @change="swiperChange"
+				@animationfinish="animationfinish">
+				<swiper-item >
+					<scroll-view scroll-y style="height: 100%;">
+						<view  @click="goDetail(item)" class="mine_order_statue" >
+							<view class="flex justify-between" style="padding: 10rpx 0 10rpx 10rpx ;">
+								<view class="flex">
+									<image  src="http://139.9.103.171:1888/miniofile/xlyq/del/cewen1.jpg"  ></image>
+									<view  class="flex flex-direction justify-around padding-left-30">
+										<view class="">
+											<text class="text-bold">姓名:</text>
+											<text>李德希</text>
+										</view>
+										<view class="">
+											<text class="text-bold">温度:</text>
+											<text>36.2 ℃ </text>
+										</view>
+										<view class="">
+											<text class="text-bold">测温时间:</text>
+											<text>2020-12-15 14:18:56</text>
+										</view>
+									</view>
+								</view>
+								<view class="flex align-center justify-center">
+									<text class="cuIcon-right"></text>
+								</view>
+							</view>
 						</view>
-						<view class="">
-							<text class="text-bold">温度:</text>
-							<text>36.5 ℃</text>
+						<view  @click="goDetail(item)" class="mine_order_statue" >
+							<view class="flex justify-between" style="padding: 10rpx 0 10rpx 10rpx ;">
+								<view class="flex">
+									<image  src="http://139.9.103.171:1888/miniofile/xlyq/del/cewen2.jpg"  ></image>
+									<view  class="flex flex-direction justify-around padding-left-30">
+										<view class="">
+											<text class="text-bold">姓名:</text>
+											<text>李德希</text>
+										</view>
+										<view class="">
+											<text class="text-bold">温度:</text>
+											<text>37.5 ℃ </text>
+											<text class="text-red">【异常】</text>
+										</view>
+										<view class="">
+											<text class="text-bold">测温时间:</text>
+											<text>2020-12-17 14:18:56</text>
+										</view>
+									</view>
+								</view>
+								<view class="flex align-center justify-center">
+									<text class="cuIcon-right"></text>
+								</view>
+							</view>
 						</view>
-						<view class="">
-							<text class="text-bold">测温时间:</text>
-							<text>2020-12-15 14:18:56</text>
+					</scroll-view>
+				</swiper-item>
+				<swiper-item >
+					<scroll-view scroll-y style="height: 100%;">
+						<view  @click="goDetail(item)" class="mine_order_statue" >
+							<view class="flex justify-between" style="padding: 10rpx 0 10rpx 10rpx ;">
+								<view class="flex">
+									<image  src="http://139.9.103.171:1888/miniofile/xlyq/del/cewen1.jpg"  ></image>
+									<view  class="flex flex-direction justify-around padding-left-30">
+										<view class="">
+											<text class="text-bold">姓名:</text>
+											<text>李德希</text>
+										</view>
+										<view class="">
+											<text class="text-bold">温度:</text>
+											<text>37.5 ℃ </text>
+											<text class="text-red">【异常】</text>
+										</view>
+										<view class="">
+											<text class="text-bold">测温时间:</text>
+											<text>2020-12-17 14:18:56</text>
+										</view>
+									</view>
+								</view>
+								<view class="flex align-center justify-center">
+									<text class="cuIcon-right"></text>
+								</view>
+							</view>
 						</view>
-					</view>
-				</view>
-				<view class="flex align-center justify-center">
-					<text class="cuIcon-right"></text>
-				</view>
-			</view>
+					</scroll-view>
+				</swiper-item>
+			</swiper>
 		</view>
-		<u-divider bg-color="#f1f1f1" height="100">没有更多了</u-divider>
 	</view>
 </template>
 
 <script>
-export default {
-	name: '',
-	data() {
-		return {
-			
-		};
-	},
-	onLoad() {
-		
-	},
-	methods:{
-		goDetail(item){
-			uni.navigateTo({
-				url:"/pages/index/my-temperature/deatil"
-			})
+	export default {
+		components:{
+		},
+		data() {
+			return {
+				subsectionCurrent:0,
+				swiperCurrent:0,
+				subsectionList:[
+					{
+						name: '全部记录'
+					},
+					{
+						name: '异常记录'
+					}
+				]
+				
+			}
+		},
+		methods: {
+			goDetail(item){
+				uni.navigateTo({
+					url:"/pages/index/staff-temperature/detail"
+				})
+			},
+			subsectionChange(index){
+				this.subsectionCurrent=index
+			},
+			swiperChange(e) {
+			  uni.pageScrollTo({
+			      scrollTop: 0,
+			      duration: 0
+			  });
+			  this.subsectionCurrent = e.detail.current
+			},
+			animationfinish({detail: { current }}) {
+				this.swiperCurrent = current;
+				this.subsectionCurrent = current;
+			},
 		}
 	}
-};
 </script>
 
-<style lang="scss" scoped>
-	.mine_order_statue {
-		margin: 18upx 14upx 0;
-		background: #fff;
-		padding: 10upx 20upx;
-		border-radius: 20rpx;
-		image{
-			background-color: #FFFFFF;
-			width: 160rpx;
-			height: 160rpx;
-		}
+<style lang="scss">
+	.container {
+		  height: calc(100vh);
+		  background-color: #F6F6F6;
+		  padding: 90rpx 0rpx 0rpx;
+		 .tabs {
+			 height: 90rpx;
+		    position: fixed;
+		    top: 0rpx;
+			left: 0;
+			right: 0;
+			width: 100%;
+			z-index: 3;
+		  }
+	}
+	
+	.nav-checked{
+		background-color: #59a5f0;
+		color: #FFFFFF;
+		font-weight: 800;
+	}
+	
+	.nav-unchecked{
+		background-color: #FFFFFF;
+		color: #59a5f0;
+	}
+	
+	.text-checked{
+		font-weight: 800;
+		background-color: #FFFFFF;
+		color: #2f7ff5;
+		font-size: 800;
+	}
+	
+	view{
+		box-sizing: border-box;
+	}
+	.scroll-view {
+		height: 150rpx;
+		white-space: nowrap;
+		width: 100%;
+		position: relative;
+		top: -30rpx;
+	}
+	.scroll-view-item {
+		color: #FFFFFF;
+		background-image: linear-gradient(to right, #2f7ff5, #2f7ff5);
+		display: inline-block;
+		text-align: left;
+		padding: 20rpx 40rpx;
+		margin:0 8rpx;
+		width:24%;
+		border-radius: 12rpx;
+	}
+	.scroll-view-item:first-child{
+		margin-left: 30rpx;
+	}
+	.scroll-view-item:last-child{
+		margin-right: 30rpx;
+	}
+.mine_order_statue {
+	margin: 18upx 14upx 0;
+	background: #fff;
+	padding: 10upx 20upx;
+	border-radius: 20rpx;
+	image{
+		background-color: #FFFFFF;
+		width: 160rpx;
+		height: 160rpx;
 	}
+}
 </style>

+ 201 - 0
pages/index/myinfo/myinfo.vue

@@ -0,0 +1,201 @@
+<template>
+	<view style="padding: 0 20rpx 20rpx;">
+		<u-top-tips type="info" ref="uTips"></u-top-tips>
+		
+		<view class="auth">
+			<text class="cuIcon-titles text-blue padding-right-10"></text>
+			<text>个人信息</text>
+			<view class="card">
+				<u-form :model="model"  ref="uForm" >
+					<u-form-item  :label-width="labelWidth"  label="姓名" >
+						<u-input disabled  :value="model.realName" type="text"></u-input>
+					</u-form-item>
+					<u-form-item  :label-width="labelWidth"   label="性别" >
+						<u-input disabled :value="model.sex==2?'女':'男'"  @click="sexSelectShow = true"></u-input>
+					</u-form-item>
+					<u-form-item   label="身份证号"  :label-width="labelWidth">
+						<u-input disabled maxlength="18"   :value="model.idcard" type="idcard"></u-input>
+					</u-form-item>
+					<u-form-item   label="手机号码"  :label-width="labelWidth">
+						<u-input disabled maxlength="11"  :value="model.phone" type="number"></u-input>
+						<view class="text-blue"   @click="changePhone" slot="right" >
+							<text style="text-decoration: underline;" class="textblue">换绑</text>
+						</view>
+					</u-form-item>
+					<u-form-item :border-bottom="false"  label="脸部信息"  :label-width="labelWidth"></u-form-item>
+				</u-form>
+				<view @click="faceSelectShow=true" class="flex justify-center padding-bottom-50">
+					<view class=" ">
+						<upload-img
+						   :width="$isEmpty(model.face)?350:560"
+						   :height="$isEmpty(model.face)?350:420"
+						  :currentImage="model.face"
+						  bgsrc="http://139.9.103.171:1888/miniofile/xlyq/face1.png"
+						  >
+						</upload-img>
+						<view class="text-center padding-top-20" style="color: #0081ff;">
+							 <text class="cuIcon-camera padding-right-sm" style="font-size: 30rpx;"></text>
+							 <text v-if="$isEmpty(model.face)">点击上传人脸</text>
+							 <text style="margin-top: 40rpx;display: inline-block;" v-else>重新上传人脸</text>
+						</view>
+					</view>
+				</view>
+			</view>
+		</view>
+		<view class="auth">
+			<text class="cuIcon-titles text-blue padding-right-10"></text>
+			<text>企业信息</text>
+			<view class="card">
+				<u-form :model="model"  ref="uForm" >
+					<u-form-item  :label-width="labelWidth"   label="公司园区" >
+						<u-input disabled  :value="model.agencyName" ></u-input>
+					</u-form-item>
+					<u-form-item  :label-width="labelWidth"   label="园区区域" >
+						<u-input disabled  :value="model.residentialName"  ></u-input>
+					</u-form-item>
+					<u-form-item  :label-width="labelWidth"   label="所属企业" >
+						<u-input disabled  :value="model.enterpriseName" ></u-input>
+					</u-form-item>
+				</u-form>
+			</view>
+		</view>
+		
+		<view  v-if="!$isEmpty(newFace)">
+			<view class="bg-white" style="height: 100rpx;"></view>
+			<view   @click="submit"  class="footer-fixed cu-btn  bg-blue flex" style="padding: 45rpx;">
+				确认修改
+			</view>
+		</view>
+		
+		<u-action-sheet @click="faceSelectCallback"  z-index="999999" :list="faceSelectList" v-model="faceSelectShow"></u-action-sheet>
+	</view>
+</template>
+
+<script>
+	import uploadImg from '@/components/uploadimg/uploadImg.vue'
+	let that;
+	export default {
+		components:{
+		   uploadImg
+		},
+		data() {
+			return {
+				labelWidth:'200',
+				model:{},
+				//上传人脸的方式
+				faceSelectList: [{
+					text: '相册上传',
+				}, {
+					text: '拍照上传'
+				}],
+				faceSelectShow:false,
+				//新的人脸信息
+				newFace:''
+			}
+		},
+		onShow() {
+			let currPage=this.getPageCtx()
+			if (!this.$isEmpty(currPage.data.image)) {
+				this.uploadFile(currPage.data.image)
+			}
+		},
+		onLoad(options) {
+			that=this
+			this.fetchUserInfo()
+		},
+		methods: {
+			/**
+			 * 换绑手机号
+			 */
+			changePhone(){
+				this.$Router.push({name:'editPhone'})
+			},
+			/**
+			 * 编辑操作时,加载用户的认证信息
+			 */
+			async fetchUserInfo(){
+				if (this.$isEmpty(getApp().globalData.userInfo)) {
+					let phone=this.$cache.get('phone')
+					let res=await this.$api.enterprisestaff.detail({phone:phone})
+					this.model=res.data
+				}else{
+					this.model=getApp().globalData.userInfo
+				}
+			},
+			/**
+			 * 确认修改
+			 */
+			submit(){
+				this.$api.enterprisestaff.submit(this.model).then(res=>{
+					if (res.success) {
+						this.$u.toast('修改成功')
+						this.newFace=''
+						this.fetchUserInfo()
+					}
+				})
+			},
+			/**
+			 * 获取页面对象
+			 */
+			getPageCtx(idx = 0){
+			  let pages = getCurrentPages()
+			  if (pages.length > 0) {
+			    return pages[pages.length - 1 - idx] || {}
+			  }
+			  return {}
+			},
+			faceSelectCallback(index){
+				if (index==0) {
+					//图片上传
+					this.chooseImage()
+				} else if(index==1){
+					//拍照上传
+					uni.navigateTo({
+						url:'/pages/my-camera/my-camera'
+					})
+				}
+			},
+			//点击上传图片事件
+			chooseImage: function () {
+			  uni.chooseImage({
+			    count: 1,
+			    //最多可以选择的图片张数,默认9
+			    sourceType: ['album', 'camera'],
+			    sizeType: ['compressed'],
+			    //可选择原图或压缩后的图片
+			    success: res => {
+			      that.uploadFile(res.tempFilePaths[0])
+			    }
+			  });
+			},
+			//处理照片,拍照上传和相册上传的共同处理方法
+			uploadFile(imgUrl){
+				this.$api.uploadFile.submit(imgUrl).then(res=>{
+					that.model.face=res.data
+					that.newFace=res.data
+				})
+			},
+		}
+	}
+</script>
+
+<style lang="scss">
+	page{
+		background-color: #FFFFFF;
+	}
+	.auth{
+		padding: 40rpx 10rpx;
+		.card{
+			margin-top: 20rpx;
+			padding: 0 30rpx;
+			box-sizing: border-box;
+			border-radius: 12rpx;
+			box-shadow: 0 -10rpx rgba(248, 248, 248,.9) ,0 10rpx rgba(248, 248, 248,.9) , -10rpx 0rpx rgba(248, 248, 248,.9) ,10rpx 0rpx rgba(248, 248, 248,.9);
+			.item{
+				padding:30rpx 0;
+				display: flex;
+				justify-content: space-between;
+			}
+		}
+	}
+</style>

+ 58 - 0
pages/index/notice/detail.vue

@@ -0,0 +1,58 @@
+<template>
+	<view>
+		<!--文章标题-->
+		<view class="text-black  text-bold  title" style="font-size: 40rpx;">{{dataDetail.title}}</view>
+		<view class="text-gray " style="padding: 0 0 30rpx 30rpx;">
+			<text class="cuIcon-time padding-right-10"></text>
+			<text>{{dataDetail.createTime}}</text>
+		</view>
+		<view class="content-view-box">
+			<view class="font-view">
+				<u-parse :html="dataDetail.notice"></u-parse>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	var app=getApp()
+	export default {
+		components: {
+		},
+		data() {
+			return {
+				dataDetail:{},
+			}
+		},
+		onShow() {
+			this.dataDetail=app.globalData.noticeDetail
+		},
+		onLoad(options) {
+			
+		},
+		methods: {
+			
+		}
+	}
+</script>
+
+
+<style lang="scss">
+	page {
+		background: #FFFFFF;
+	}
+	.title {
+		margin: 36.36rpx 27.27rpx;
+	}
+
+	.content-view-box {
+		position: relative;
+		.font-view {
+			padding: 27.27rpx;
+			line-height: 1.9;
+		}
+		image {
+			width: 100%;
+		}
+	}
+</style>

+ 187 - 0
pages/index/notice/notice.vue

@@ -0,0 +1,187 @@
+<template>
+	<view>
+		<view class="cu-load padding-top-10" :class="ajax.loading?'loading':''" @tap="getHistoryMsg"></view>
+		<view  class="cart-view-box" v-for="(item,index) in msgList" :key="index" :id="`msg-${item.id}`">
+			<view class="text-gray text-center">{{item.createTime}}</view>
+			<view @click="goDetail(item)" class="bg-white margin-top radius card-view">
+				<view class="content-view">
+					<view class="text-black text-bold text-lg text-cut">{{item.title}}</view>
+					<view class=" text-sm text-cut-2 subtitle">{{item.notice | formatHtml}}</view>
+					<view class="flex  justify-between padding-top-20" style=" border-top: 1rpx solid #f2f2f2;box-sizing: border-box;">
+						<view class="text-df text-black">
+							<text>查看详情</text>
+						</view>
+						<view style="padding-top: 8rpx;">
+							<text class=" cuIcon-right"></text>
+						</view>
+					</view>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	var app=getApp()
+	export default {
+		components:{
+			
+		},
+		data() {
+			return {
+				msgList:[],
+				ajax:{
+					rows:10,	//每页数量
+					page:1,	//页码
+					flag:true,	// 请求开关
+					loading:true,	// 加载中
+				},
+				content:''
+			}
+		},
+		filters: {
+			formatHtml (str) {
+				return str.replace(/<[^>]+>/g, "");
+			}
+		},
+		mounted() {
+			this.$nextTick(()=>{
+				this.getHistoryMsg();
+			});
+		},
+		onPageScroll(e){
+			if(e.scrollTop<5){
+				this.getHistoryMsg();
+			}
+		},
+		onLoad() {
+		},
+		methods: {
+			goDetail(item){
+				app.globalData.noticeDetail=item
+				uni.navigateTo({
+					url:"/pages/index/notice/detail"
+				})
+			},
+			// 获取历史消息
+			getHistoryMsg(){
+				if(!this.ajax.flag){
+					return; //
+				}
+				// 此处用到 ES7 的 async/await 知识,为使代码更加优美。不懂的请自行学习。
+				let get = async ()=>{
+					this.hideLoadTips();
+					this.ajax.flag = false;
+					let data = await this.joinHistoryMsg();
+					
+					console.log(data);	// 查看请求返回的数据结构 
+					
+					// 获取待滚动元素选择器,解决插入数据后,滚动条定位时使用
+					let selector = '';
+										
+					if(this.ajax.page>1){
+						// 非第一页,则取历史消息数据的第一条信息元素
+						selector = `#msg-${this.msgList[0].id}`;
+					}else{
+						
+						// 第一页,则取当前消息数据的最后一条信息元素
+						selector = `#msg-${data[data.length-1].id}`;
+					}
+					
+					// 将获取到的消息数据合并到消息数组中
+					this.msgList = [...data,...this.msgList];	
+					
+					// 数据挂载后执行,不懂的请自行阅读 Vue.js 文档对 Vue.nextTick 函数说明。
+					this.$nextTick(()=>{
+						// 设置当前滚动的位置
+						this.setPageScrollTo(selector);
+						
+						this.hideLoadTips(true);
+												
+						if(data.length < this.ajax.rows){
+							// 当前消息数据条数小于请求要求条数时,则无更多消息,不再允许请求。
+							// 可在此处编写无更多消息数据时的逻辑
+						}else{
+							this.ajax.page ++;
+							
+							// 延迟 200ms ,以保证设置窗口滚动已完成
+							setTimeout(()=>{
+								this.ajax.flag = true;
+							},200)
+						}
+						
+					})
+				}
+				get();
+			},
+			// 拼接历史记录消息,正式项目可替换为请求历史记录接口
+			joinHistoryMsg(){
+				let that=this
+				// 此处用到 ES6 的 Promise 知识,不懂的请自行学习。
+				return new Promise((done,fail)=>{
+					// 无数据请求接口,由 setTimeout 模拟,正式项目替换为 ajax 即可。
+					
+					setTimeout(()=>{
+						let params={
+							agencyId:that.$cache.get('agencyId')
+						}
+						that.$api.notice.page(params).then(res=>{
+							done(res.data.records);
+						})
+					},800);
+				})
+			},
+			// 设置页面滚动位置
+			setPageScrollTo(selector){
+				let view = uni.createSelectorQuery().in(this).select(selector);
+				view.boundingClientRect((res) => {
+					uni.pageScrollTo({
+					    scrollTop:res.top - 30,	// -30 为多显示出大半个消息的高度,示意上面还有信息。
+					    duration: 0
+					});
+				}).exec();
+			},
+			// 隐藏加载提示
+			hideLoadTips(flag){
+				if(flag){
+					console.log("333");
+					setTimeout(()=>{
+						this.ajax.loading = false;
+					},300);
+				}else{
+					this.ajax.loading = true;
+				}
+			},
+		},
+		
+	}
+</script>
+
+<style lang="scss">
+	page{
+		background-color: #F3F3F3;
+		font-size: 28rpx;
+	}
+	.subtitle{
+		color: #7e7e7e;
+		font-size: 28rpx;
+		margin: 50rpx 0 10rpx 0;
+		line-height: 52rpx;
+	}
+	.cart-view-box {
+		padding: 36.36rpx 27.27rpx;
+		.card-view {
+			background-color: #FFFFFF;
+			position: relative;
+			border-radius: 10rpx;
+			.head-img {
+				width: 100%;
+				height: 236.36rpx;
+				border-radius: 9.09rpx 9.09rpx 0 0;
+			}
+			.content-view {
+				padding: 27.27rpx;
+			}
+		}
+	}
+</style>

+ 85 - 0
pages/index/staff-temperature/detail.vue

@@ -0,0 +1,85 @@
+<template>
+	<view class="content ">
+		<view class="flex flex-direction ">
+			<view class="flex justify-between">
+				<view class="">
+					<view class="item">
+						<text class="cuIcon-people text-blue margin-right-10"></text>
+						<text class="text-bold padding-right-10">姓名:</text>
+						<text >{{dataDetail.name}}</text>
+					</view>
+					<view class="item">
+						<text class="cuIcon-male text-blue margin-right-10"></text>
+						<text class="text-bold padding-right-10">性别:</text>
+						<text>{{dataDetail.sex}}</text>
+					</view>
+					<view class="item">
+						<text class="cuIcon-hot text-blue margin-right-10"></text>
+						<text class="text-bold padding-right-10">体温:</text>
+						<text>{{dataDetail.temperature}} ℃</text>
+					</view>
+					<view class="item">
+						<text class="cuIcon-activity text-blue margin-right-10"></text>
+						<text class="text-bold padding-right-10">所属企业:</text>
+						<text>{{dataDetail.enterprise}}</text>
+					</view>
+				</view>
+				<view class="padding-top-20">
+					<image @click="previewImg(dataDetail.image)"  style="width: 220rpx;height: 250rpx;border-radius: 12rpx;"  :src="dataDetail.image"></image>
+				</view>
+			</view>
+			<view class="item">
+				<text class="cuIcon-news text-blue margin-right-10"></text>
+				<text class="text-bold padding-right-10">身份证号:</text>
+				<text>{{dataDetail.idCard}}</text>
+			</view>
+			<view class="item">
+				<text class="cuIcon-time text-blue margin-right-10"></text>
+				<text class="text-bold padding-right-10">测温时间:</text>
+				<text>{{dataDetail.time}}</text>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	var app=getApp()
+	export default {
+		data() {
+			return {
+				dataDetail:{
+					'name':'李德希',
+					'sex':'男',
+					'enterprise':'宁夏新金凰印刷有限公司',
+					'temperature':'37.5',
+					'image':'http://139.9.103.171:1888/miniofile/xlyq/del/cewen1.jpg',
+					'idCard':'440881199604264598',
+					'time':'2020-12-15 14:18:56'
+				}
+			}
+		},
+		onShow() {
+		},
+		methods: {
+			previewImg(img){
+				let imgs=[]
+				imgs.push(img)
+				uni.previewImage({
+					urls:imgs
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	page{
+		background-color: #FFFFFF;
+	}
+	.content{
+		padding:60rpx;
+		.item{
+			padding: 22rpx 0;
+		}
+	}
+</style>

+ 226 - 0
pages/index/staff-temperature/staff-temperature.vue

@@ -0,0 +1,226 @@
+<template>
+	<view class="container">
+		<view class="tabs" style="width: 420rpx;margin: 0rpx  auto;padding-top: 20rpx;">
+			<scroll-view scroll-x class="nav" >
+				<view class="flex text-center">
+					<view  style="border: 1rpx solid #59a5f0;padding:15rpx 20rpx;" class="flex-sub" :class="index==subsectionCurrent?'nav-checked':'nav-unchecked'" v-for="(item,index) in subsectionList" :key="index" @tap="subsectionChange(index)" >
+						{{item.name}}
+					</view>
+				</view>
+			</scroll-view>
+		</view>
+<!-- 		<view class="tabs">
+			<scroll-view scroll-x class="nav" style="background-color: #efefef;color: #5b5b5b;">
+				<view class="flex text-center">
+					<view class="cu-item flex-sub" :class="index==subsectionCurrent?'text-checked':''" v-for="(item,index) in subsectionList" :key="index" @tap="subsectionChange(index)" >
+						{{item.name}}
+					</view>
+				</view>
+			</scroll-view>
+			<view class="" style="height: 20rpx;"></view>
+		</view> -->
+		<view style="height: 100%;">
+			<swiper style="height: 100%;" :current="subsectionCurrent"  @change="swiperChange"
+				@animationfinish="animationfinish">
+				<swiper-item >
+					<scroll-view scroll-y style="height: 100%;">
+						<view  @click="goDetail(item)" class="mine_order_statue" >
+							<view class="flex justify-between" style="padding: 10rpx 0 10rpx 10rpx ;">
+								<view class="flex">
+									<image  src="http://139.9.103.171:1888/miniofile/xlyq/del/cewen1.jpg"  ></image>
+									<view  class="flex flex-direction justify-around padding-left-30">
+										<view class="">
+											<text class="text-bold">姓名:</text>
+											<text>李德希</text>
+										</view>
+										<view class="">
+											<text class="text-bold">温度:</text>
+											<text>36.2 ℃ </text>
+										</view>
+										<view class="">
+											<text class="text-bold">测温时间:</text>
+											<text>2020-12-15 14:18:56</text>
+										</view>
+									</view>
+								</view>
+								<view class="flex align-center justify-center">
+									<text class="cuIcon-right"></text>
+								</view>
+							</view>
+						</view>
+						<view  @click="goDetail(item)" class="mine_order_statue" >
+							<view class="flex justify-between" style="padding: 10rpx 0 10rpx 10rpx ;">
+								<view class="flex">
+									<image  src="http://139.9.103.171:1888/miniofile/xlyq/del/cewen2.jpg"  ></image>
+									<view  class="flex flex-direction justify-around padding-left-30">
+										<view class="">
+											<text class="text-bold">姓名:</text>
+											<text>李德希</text>
+										</view>
+										<view class="">
+											<text class="text-bold">温度:</text>
+											<text>37.5 ℃ </text>
+											<text class="text-red">【异常】</text>
+										</view>
+										<view class="">
+											<text class="text-bold">测温时间:</text>
+											<text>2020-12-17 14:18:56</text>
+										</view>
+									</view>
+								</view>
+								<view class="flex align-center justify-center">
+									<text class="cuIcon-right"></text>
+								</view>
+							</view>
+						</view>
+					</scroll-view>
+				</swiper-item>
+				<swiper-item >
+					<scroll-view scroll-y style="height: 100%;">
+						<view  @click="goDetail(item)" class="mine_order_statue" >
+							<view class="flex justify-between" style="padding: 10rpx 0 10rpx 10rpx ;">
+								<view class="flex">
+									<image  src="http://139.9.103.171:1888/miniofile/xlyq/del/cewen1.jpg"  ></image>
+									<view  class="flex flex-direction justify-around padding-left-30">
+										<view class="">
+											<text class="text-bold">姓名:</text>
+											<text>李德希</text>
+										</view>
+										<view class="">
+											<text class="text-bold">温度:</text>
+											<text>37.5 ℃ </text>
+											<text class="text-red">【异常】</text>
+										</view>
+										<view class="">
+											<text class="text-bold">测温时间:</text>
+											<text>2020-12-17 14:18:56</text>
+										</view>
+									</view>
+								</view>
+								<view class="flex align-center justify-center">
+									<text class="cuIcon-right"></text>
+								</view>
+							</view>
+						</view>
+					</scroll-view>
+				</swiper-item>
+			</swiper>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		components:{
+		},
+		data() {
+			return {
+				subsectionCurrent:0,
+				swiperCurrent:0,
+				subsectionList:[
+					{
+						name: '全部记录'
+					},
+					{
+						name: '异常记录'
+					}
+				]
+				
+			}
+		},
+		methods: {
+			goDetail(item){
+				uni.navigateTo({
+					url:"/pages/index/staff-temperature/detail"
+				})
+			},
+			subsectionChange(index){
+				this.subsectionCurrent=index
+			},
+			swiperChange(e) {
+			  uni.pageScrollTo({
+			      scrollTop: 0,
+			      duration: 0
+			  });
+			  this.subsectionCurrent = e.detail.current
+			},
+			animationfinish({detail: { current }}) {
+				this.swiperCurrent = current;
+				this.subsectionCurrent = current;
+			},
+		}
+	}
+</script>
+
+<style lang="scss">
+	.container {
+		  height: calc(100vh);
+		  background-color: #F6F6F6;
+		  padding: 90rpx 0rpx 0rpx;
+		 .tabs {
+			 height: 90rpx;
+		    position: fixed;
+		    top: 0rpx;
+			left: 0;
+			right: 0;
+			width: 100%;
+			z-index: 3;
+		  }
+	}
+	
+	.nav-checked{
+		background-color: #59a5f0;
+		color: #FFFFFF;
+		font-weight: 800;
+	}
+	
+	.nav-unchecked{
+		background-color: #FFFFFF;
+		color: #59a5f0;
+	}
+	
+	.text-checked{
+		font-weight: 800;
+		background-color: #FFFFFF;
+		color: #2f7ff5;
+		font-size: 800;
+	}
+	
+	view{
+		box-sizing: border-box;
+	}
+	.scroll-view {
+		height: 150rpx;
+		white-space: nowrap;
+		width: 100%;
+		position: relative;
+		top: -30rpx;
+	}
+	.scroll-view-item {
+		color: #FFFFFF;
+		background-image: linear-gradient(to right, #2f7ff5, #2f7ff5);
+		display: inline-block;
+		text-align: left;
+		padding: 20rpx 40rpx;
+		margin:0 8rpx;
+		width:24%;
+		border-radius: 12rpx;
+	}
+	.scroll-view-item:first-child{
+		margin-left: 30rpx;
+	}
+	.scroll-view-item:last-child{
+		margin-right: 30rpx;
+	}
+.mine_order_statue {
+	margin: 18upx 14upx 0;
+	background: #fff;
+	padding: 10upx 20upx;
+	border-radius: 20rpx;
+	image{
+		background-color: #FFFFFF;
+		width: 160rpx;
+		height: 160rpx;
+	}
+}
+</style>

+ 145 - 0
pages/index/staffAudit/card.vue

@@ -0,0 +1,145 @@
+<template>
+	<view class="">
+		<view @click="goDetail(item)" class="data" v-for="(item, index) in list" :key="index">
+			<view class="top">
+				<view class="left">
+					<view class="title ">
+						<text class="text-bold">员工姓名:</text>
+						<text>{{item.realName}}</text>
+					</view>
+				</view>
+				<view class="right">
+					<text class="" v-if="item.examine==1">已通过</text>
+					<text class="text-orange" v-if="item.examine==0">待审核</text>
+					<text class="text-red" v-if="item.examine==2">未通过</text>
+				</view>
+			</view>
+			<view class="item">
+				<view class="left">
+					<view style="padding: 20rpx 30rpx 0;">
+						<view v-if="!$isEmpty(item.face)" class="">
+							<image style="width: 140rpx;height: 140rpx;" :src="item.face" mode=""></image>
+						</view>
+						<view class="content">
+							<text class="padding-right-10">所属企业:</text>
+							<text >{{item.enterpriseName}}</text>
+						</view>
+						<view class="content">
+							<text class="padding-right-10">联系方式:</text>
+							<text>{{item.phone}}</text>
+							<image @click.stop="call(item.phone)" class="call" src="/static/index/call.png" ></image>
+						</view>
+						<view class="content">
+							<text class="padding-right-10">备注:</text>
+							<text style="line-height: 50rpx;">{{item.remarks}}</text>
+						</view>
+					</view>
+				</view>
+			</view>
+			<view class="bottom" >
+				<!-- 待审核,显示取消工单 -->
+				<view @click.stop="pass(item)" v-if="item.examine==0" class="cu-btn  sm round bg-blue margin-right-20" >
+					审核通过
+				</view>
+				<view @click.stop="fail(item)" v-if="item.examine==0" class="cu-btn  sm round bg-blue" >
+					审核不通过
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+export default {
+	name: 'card',
+	props:{
+		list:{
+			type:Array,
+			default:()=>{
+				[]
+			}
+		}
+	},
+	data() {
+		return {
+			
+		};
+	},
+	onLoad() {
+		
+	},
+	methods:{
+		call(phone){
+			uni.makePhoneCall({
+				phoneNumber:phone
+			})
+		},
+		goDetail(item){
+			uni.navigateTo({
+				url:"/pages/index/staffAudit/detail?id="+item.id
+			})
+		},
+		pass(item){
+			this.$emit('pass',item)
+		},
+		fail(item){
+			this.$emit('fail',item)
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+.bg-blue{
+	background-color: #59a5f0;
+	color: #FFFFFF;
+}
+
+	
+.data {
+	width: 710rpx;
+	background-color: #ffffff;
+	margin: 20rpx auto;
+	border-radius: 6rpx;
+	box-sizing: border-box;
+	padding: 20rpx;
+	font-size: 28rpx;
+	.top {
+		display: flex;
+		justify-content: space-between;
+		padding-bottom: 20rpx;
+		border-bottom: 1rpx solid #EEEEEE;
+		.left {
+			display: flex;
+			align-items: center;
+			.title {
+				margin: 0 10rpx;
+				font-size: 30rpx;
+			}
+		}
+		.right{
+			margin-right: 10rpx;
+		}
+	}
+	.item {
+		margin: 5rpx 0 20rpx 0;
+		.content {
+			// border-bottom: 1rpx dashed #DDDDDD;
+			padding: 20rpx 0 ;
+			
+			.call{
+				width: 36rpx;
+				height: 36rpx;
+				margin-left: 40rpx;
+				margin-top: 10rpx;
+			}
+		}
+	}
+	.bottom {
+		display: flex;
+		margin-top: 30rpx;
+		justify-content: flex-end;
+		align-items: center;
+	}
+}
+</style>

+ 272 - 0
pages/index/staffAudit/detail.vue

@@ -0,0 +1,272 @@
+<template>
+	<view class="">
+		<u-modal :show-cancel-button="true" @confirm="submitFailAudit" v-model="modelShow" >
+			<view class="slot-content margin-10 " style="background-color: #f5f5f5;">
+				<textarea  v-model="opinion" placeholder="请输入审核意见" />
+			</view>
+		</u-modal>
+		<view class="data">
+			<view class="top">
+				<view class="left">
+					<view class="title ">
+						<text class="text-bold">员工姓名:</text>
+						<text>{{dataDetail.realName}}</text>
+					</view>
+				</view>
+				<view class="right">
+					<text class="" v-if="dataDetail.examine==1">已通过</text>
+					<text class="text-orange" v-if="dataDetail.examine==0">待审核</text>
+					<text class="text-red" v-if="dataDetail.examine==2">未通过</text>
+				</view>
+			</view>
+			<view class="item">
+				<view class="left">
+					<view style="padding: 20rpx 30rpx 0;">
+						<view class="flex justify-between">
+							<view >
+								<view class="content">
+									<text class="padding-right-10">微信昵称:</text>
+									<text>{{dataDetail.name}}</text>
+								</view>
+								<view style="padding: 50rpx 0;display: flex;">
+									<view class="flex justify-center align-center">
+										<text class="padding-right-10">微信头像:</text>
+									</view>
+									<image style="width: 80rpx;height: 80rpx;border-radius: 50%;" :src="dataDetail.avatar"></image>
+								</view>
+								
+							</view>
+							<view class="" v-if="!this.$isEmpty(dataDetail.face)">
+								<image @click="previewImg(dataDetail.face)" style="width: 200rpx;height: 200rpx;" :src="dataDetail.face" ></image>
+								<view class="text-center text-blue padding-top-10">
+									(人脸信息)
+								</view>
+							</view>
+						</view>
+						<view class="content">
+							<text class="padding-right-10">性别:</text>
+							<text v-text="dataDetail.sex==2?'女':'男'"></text>
+						</view>
+						<view class="content">
+							<text class="padding-right-10">所属园区:</text>
+							<text >{{dataDetail.agencyName}}</text>
+						</view>
+						<view class="content">
+							<text class="padding-right-10">所在区域:</text>
+							<text >{{dataDetail.residentialName}}</text>
+						</view>
+						<view class="content">
+							<text class="padding-right-10">所属企业:</text>
+							<text >{{dataDetail.enterpriseName}}</text>
+						</view>
+						<view class="content">
+							<text class="padding-right-10">联系方式:</text>
+							<text>{{dataDetail.phone}}</text>
+							<image @click.stop="call(dataDetail.phone)" class="call" src="/static/index/call.png" ></image>
+						</view>
+						<view class="content">
+							<text class="padding-right-10">提交时间:</text>
+							<text>{{dataDetail.createTime}}</text>
+						</view>
+						<view class="content" v-if="dataDetail.examine!=0">
+							<text class="padding-right-10" >审核时间:</text>
+							<text>{{dataDetail.auditTime}}</text>
+						</view>
+						<view class="content" v-if="dataDetail.examine!=0">
+							<text class="padding-right-10">审核意见:</text>
+							<text>{{dataDetail.opinion}}</text>
+						</view>
+						<view class="content">
+							<text class="padding-right-10">认证备注:</text>
+							<text style="line-height: 50rpx;">{{dataDetail.remarks}}</text>
+						</view>
+						<view style="height: 100rpx;"></view>
+					</view>
+				</view>
+			</view>
+		</view>
+		<view  class="footer-fixed   padding-20 bg-white flex justify-end margin-right-30" style="box-sizing: border-box;z-index: 999;">
+			<view @click="pass" class="cu-btn df bg-blue round margin-right-20" v-if="dataDetail.examine==0">
+				审核通过
+			</view>
+			<view @click="fail" class="cu-btn df bg-blue round " v-if="dataDetail.examine==0">
+				审核不通过
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+export default {
+	name: 'card',
+	data() {
+		return {
+			opinion:'',
+			modelShow:false,
+			id:'',
+			dataDetail:{}
+		};
+	},
+	onLoad(options) {
+		this.id=options.id
+		this.fetchDetail()
+	},
+	methods:{
+		call(phone){
+			uni.makePhoneCall({
+				phoneNumber:phone
+			})
+		},
+		async send(msgData){
+			let tokenData={
+				grantType:this.$api.wxData.subscribe_grant_type,
+				appId:this.$api.wxData.appId,
+				secret:this.$api.wxData.secret
+			}
+			let res=await this.$api.wxApi.getAccessToken(tokenData)
+			let token=JSON.parse(res.data).access_token
+			let subscribeData={
+				accessToken:token,
+				touser:msgData.openId,
+				lang:"zh_CN",
+				page:'/pages/index/index',
+				miniprogramState:this.$miniprogramState.TRIAL,
+			    templateId: this.$tmplIds[0],
+				  "data": {
+					  "thing13": {
+						  "value": msgData.enterpriseName
+					  },
+					  "thing9":{
+						  "value": "员工认证信息审核"
+					  },
+					  "phrase2": {
+						  "value": msgData.content
+					  },
+					  "thing3": {
+						  "value": msgData.remarks
+					  },
+					}
+			}
+			this.$api.wxApi.subscribe(subscribeData).then(res=>{
+				console.log(res);
+			}).catch(err=>{
+				console.error(err);
+			})
+		},
+		previewImg(img){
+			let urls=[img]
+			uni.previewImage({
+				urls:urls
+			})
+		},
+		fetchDetail(){
+			this.$api.enterprisestaff.detail({id:this.id}).then(res=>{
+				this.dataDetail=res.data
+			})
+		},
+		pass(){
+			let that=this
+			this.$showModel("确定审核通过该员工信息?").then(res=>{
+				this.dataDetail.examine=1
+				this.dataDetail.auditTime=this.$createDateTime()
+				this.$api.enterprisestaff.submit(this.dataDetail).then(res=>{
+					let msgData={
+						openId:that.dataDetail.openId,
+						content:"审核已通过",
+						remarks:'已审核',
+						enterpriseName:that.dataDetail.enterpriseName
+					}
+					that.send(msgData)
+					that.$showModel(res.msg,false).then(res=>{
+						that.$Router.back()
+					})
+				})
+			})
+		},
+		/**
+		 * 审核不通过
+		 * @param {Object}
+		 */
+		submitFailAudit(){
+			let that=this
+			this.dataDetail.examine=2
+			this.dataDetail.auditTime=this.$createDateTime()
+			this.dataDetail.opinion=that.opinion
+			this.$api.enterprisestaff.submit(this.dataDetail).then(res=>{
+				let msgData={
+					openId:that.dataDetail.openId,
+					content:"审核不通过",
+					remarks:that.opinion || '审核不通过',
+					enterpriseName:that.dataDetail.enterpriseName
+				}
+				that.send(msgData)
+				that.$showModel(res.msg,false).then(res=>{
+					that.$Router.back()
+				})
+			})
+		},
+		fail(){
+			this.modelShow=true
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+page{
+	background-color: #FFFFFF;
+}
+
+view{
+	box-sizing: border-box;
+}
+	
+.bg-blue{
+	background-color: #59a5f0;
+	color: #FFFFFF;
+}
+.data {
+	background-color: #ffffff;
+	border-radius: 6rpx;
+	box-sizing: border-box;
+	padding:0 20rpx 20rpx;
+	font-size: 28rpx;
+	.top {
+		display: flex;
+		justify-content: space-between;
+		padding: 30rpx 0;
+		border-bottom: 1rpx solid #EEEEEE;
+		.left {
+			display: flex;
+			align-items: center;
+			.title {
+				margin: 0 10rpx;
+				font-size: 30rpx;
+			}
+		}
+		.right{
+			margin-right: 10rpx;
+		}
+	}
+	.item {
+		margin: 20rpx 0 20rpx 0;
+		.content {
+			// border-bottom: 1rpx dashed #DDDDDD;
+			padding: 30rpx 0 ;
+			
+			.call{
+				width: 36rpx;
+				height: 36rpx;
+				margin-left: 40rpx;
+				margin-top: 20rpx;
+			}
+		}
+	}
+	.bottom {
+		display: flex;
+		margin-top: 30rpx;
+		justify-content: flex-end;
+		align-items: center;
+	}
+}
+</style>

+ 213 - 0
pages/index/staffAudit/item.vue

@@ -0,0 +1,213 @@
+<template>
+	<view class="">
+		<u-modal  :show-cancel-button="true" @confirm="submitFailAudit" v-model="modelShow" >
+			<view class="slot-content margin-10 " style="background-color: #f5f5f5;">
+				<textarea placeholder-style="color:#bababa" v-model="opinion" placeholder="请输入审核意见" />
+			</view>
+		</u-modal>
+		<MeScroll  :up="up" :down="down" @up="upFn" :fixed="false" @down="downFn" @init="initMeScroll">
+			<card @pass="pass" @fail="fail" :list="list" ></card>
+		</MeScroll>
+	</view>
+	
+</template>
+<script>
+	import MeScroll from '@/components/mescroll-body/mescroll-uni.vue'
+	import card from './card.vue'
+	var app=getApp()
+	export default {
+		components:{
+			MeScroll,card
+		},
+		props: {
+			refresh:Boolean,
+			type: Number,
+			i: Number,
+			item:Object
+		},
+		data() {
+			return {
+				//审核不通过时展示
+				modelShow:false,
+				//审核不通过的原因
+				opinion:'',
+				dataDetail:{},
+				
+				//所属公司
+				enterpriseId:'',
+				
+				isInit: false, // 是否初始化
+				list: [], // 列表数据
+				mescroll: null, // mescroll 对象
+				// 上拉配置参数
+				up: {
+					noMoreSize: 3, 
+					auto: true,
+					page: {
+						page: 0,
+						size: 10
+					}
+				},
+				// 下拉配置参数
+				down: {
+					use: true, 
+					auto: false
+				}
+			}
+		},
+		watch:{
+			refresh() {
+				console.log("我要刷新了");
+				this.mescroll.resetUpScroll()
+			},
+			type(val) {
+				if(!this.isInit && val === this.i) {
+					this.mescroll.resetUpScroll()
+				}
+			}
+		},
+		mounted() {
+			if(!this.isInit && this.i === 0) {
+				this.mescroll.resetUpScroll()
+			}
+		},
+		created() {
+			this.enterpriseId= this.$cache.get('enterpriseId')
+		},
+		methods: {
+			/**
+			 * 通过审核
+			 */
+			pass(item){
+				let that=this
+				this.$showModel("确定审核通过该员工信息吗?").then(res=>{
+					item.examine=1
+					item.auditTime=this.$createDateTime()
+					this.$api.enterprisestaff.submit(item).then(res=>{
+						that.$u.toast(res.msg)
+						let msgData={
+							openId:item.openId,
+							content:"审核已通过",
+							remarks:"已审核",
+							enterpriseName:item.enterpriseName
+						}
+						that.send(msgData)
+						that.mescroll.resetUpScroll()
+					})
+				})
+			},
+			/**
+			 * 审核不通过
+			 * @param {Object}
+			 */
+			submitFailAudit(){
+				let item=this.dataDetail
+				let that=this
+				item.examine=2
+				item.opinion=that.opinion
+				item.auditTime=this.$createDateTime()
+				this.$api.enterprisestaff.submit(item).then(res=>{
+					let msgData={
+						openId:item.openId,
+						content:"审核不通过",
+						remarks:that.opinion || '审核不通过',
+						enterpriseName:item.enterpriseName
+					}
+					that.send(msgData)
+					that.$u.toast(res.msg)
+					that.mescroll.resetUpScroll()
+				})
+			},
+			/**
+			 * 显示审核不通过的意见框
+			 * @param {Object} item
+			 */
+			fail(item){
+				this.dataDetail=item
+				this.modelShow=true
+			},
+			async send(msgData){
+				let tokenData={
+					grantType:this.$api.wxData.subscribe_grant_type,
+					appId:this.$api.wxData.appId,
+					secret:this.$api.wxData.secret
+				}
+				let res=await this.$api.wxApi.getAccessToken(tokenData)
+				let token=JSON.parse(res.data).access_token
+				let subscribeData={
+					accessToken:token,
+					touser:msgData.openId,
+					lang:"zh_CN",
+					page:'/pages/index/index',
+					miniprogramState:this.$miniprogramState.TRIAL,
+				    templateId: this.$tmplIds[0],
+					  "data": {
+						  "thing13": {
+							  "value": msgData.enterpriseName
+						  },
+						  "thing9":{
+							  "value": "员工认证信息审核"
+						  },
+						  "phrase2": {
+							  "value": msgData.content
+						  },
+						  "thing3": {
+							  "value": msgData.remarks
+						  },
+						}
+				}
+				this.$api.wxApi.subscribe(subscribeData).then(res=>{
+					console.log(res);
+				}).catch(err=>{
+					console.error(err);
+				})
+			},
+			/**
+			 * @param {Object} mescroll 初始化组件
+			 */
+			initMeScroll(mescroll) {
+				this.mescroll = mescroll
+			},
+			/**
+			 * @param {Object} mescroll 上拉回调
+			 */
+			upFn(mescroll) {
+				let that=this
+				let obj={
+					enterpriseId:this.enterpriseId,
+					current:mescroll.num,
+					size:mescroll.size,
+				}
+				if (this.item.value!=-1) {
+					//不是查询全部就传examine
+					obj.examine=this.item.value
+				}
+				try{
+					let enterpriseId=
+					this.$api.enterprisestaff.page(obj).then(res=>{
+						let data=res.data.records
+						let length=data.length
+						let total=res.data.total
+						mescroll.endBySize(length, total);
+						if(mescroll.num == 1) that.list = []; //如果是第一页需手动制空列表
+						that.list=that.list.concat(data); //追加新数据
+					})
+				}catch(e){
+					mescroll.endErr();
+				}
+			},
+			/**
+			 * 下拉回调
+			 * */
+			downFn(mescroll) {
+				setTimeout(()=>{
+					this.mescroll.resetUpScroll()
+				},1500)
+			},
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	
+</style>

+ 118 - 0
pages/index/staffAudit/list.vue

@@ -0,0 +1,118 @@
+<template>
+	<view class="container">
+		<view class="tabs" >
+			<scroll-view scroll-x class="bg-white nav">
+				<view class="flex text-center">
+					<view class="cu-item flex-sub" :class="index==current?'text-blue text-xl text-bold':'text-lg'" v-for="(item,index) in tabs" :key="index" @tap="tabChange(index)" >
+						{{item.name}}
+					</view>
+				</view>
+			</scroll-view>
+		</view>
+		<view style="height: 100%;">
+			<swiper style="height: 100%;" :current="current"  @change="swiperChange"
+				@animationfinish="animationfinish">
+				<swiper-item  v-for="(item, index) in tabs" :key="index">
+					<scroll-view scroll-y style="height: 100%;">
+						<item :refresh="refresh" :i="index" :item="item" :type="current"></item>
+					</scroll-view>
+				</swiper-item>
+			</swiper>
+		</view>
+	</view>
+</template>
+<script>
+	import item from "./item.vue"
+	export default {
+		components: {
+			item
+		},
+		data() {
+			return {
+				//是否第一次进入页面
+				isfirst:false,
+				//动态让item主动刷新
+				refresh:false,
+				scrollLeft:0,
+				current: 0,
+				swiperCurrent:0,
+				tabs: [
+					//数据库数据状态- 
+					//工单状态 -1 已取消  0:待处理  1:已处理
+					//评价 estimateStatus 待评价0  已评价 1
+					{
+						name: '全部',
+						value:-1
+					},
+					{
+						name: '待审核',
+						value:0
+					},
+					{
+						name: '已通过',
+						value:1
+					},
+					{
+						name:'未通过',
+						value:2
+					}
+				],
+			}
+		},
+		onShow(){
+			if (this.isfirst) {
+				this.isfirst=false
+			}else{
+				//刷新列表
+				this.refresh=!this.refresh
+			}
+		},
+		onLoad() {
+			this.isfirst=true
+		},
+		methods:{
+			tabChange(index) {
+				this.current = index
+				// this.scrollLeft = (index - 1) * 60
+			},
+			swiperChange(e) {
+			  uni.pageScrollTo({
+			      scrollTop: 0,
+			      duration: 0
+			  });
+			  this.current = e.detail.current
+			  // this.scrollLeft = (this.current - 1) * 60
+			},
+			animationfinish({detail: { current }}) {
+				this.swiperCurrent = current;
+				this.current = current;
+			},
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.text-blue{
+		color: #59a5f0;
+	}
+	
+	.text-xl{
+		font-size: 34rpx;
+	}
+	
+	.container {
+	  height: calc(100vh );
+	  background-color: #F6F6F6;
+	  padding: 78rpx 0rpx 0rpx;
+	 .tabs {
+	    position: fixed;
+	    top: -10rpx;
+		left: 0;
+		display: flex;
+		align-items: center;
+		width: 100%;
+		background-color: #FFFFFF;
+		box-sizing: border-box;
+	  }
+}
+</style>

+ 419 - 157
pages/login/login.vue

@@ -1,177 +1,439 @@
 <!-- 蓝色简洁登录页面 -->
 <template>
-	<view class="">
-		<view class="t-login">
-			<u-top-tips ref="uTips"></u-top-tips>
-			<u-toast ref="uToast"></u-toast>
-			<!-- 页面装饰图片 -->
-			<!-- <image class="img-a" src="@/static/login/2.png"></image> -->
-			<!-- <image class="img-b" src="@/static/login/3.png"></image> -->
-			<!-- 标题 -->
-			<view class="t-b">{{ title }}</view>
-			<form class="cl">
-				<view class="t-a">
-					<image   src="@/static/login/sj.png"></image>
-					<input  type="number" name="phone" placeholder="请输入手机号" maxlength="11" v-model="phone" />
-				</view>
-				<view class="t-a">
-					<image  src="@/static/login/yz.png"></image>
-					<input type="number" name="code" maxlength="6" placeholder="请输入验证码" v-model="yzm" />
-					<view :style="showText?'':'background-color: #A7A7A7;'"  class="t-c" @click="getCode()">{{tips}}</view>
-				</view>
-				<view @click="typeShow=true" class="flex padding-bottom-50" style="text-align: left">
-					<view style="border: 1px solid #e9e9e9;background-color: #f8f7fc;" class="cu-btn   df  round">
-						<text class="text-black">{{typeList[typeIndex].text}}登陆</text>
-						<text class="text-black cuIcon-triangledownfill " style="font-size: 40rpx;padding-left: 4rpx;"></text>
-					</view>
-				</view>
-				
-				<view class="checkbox">
-					<u-checkbox 
-					@change="checkboxChange" 
-					v-model="checked" 
-					>七天内免登录</u-checkbox>
-				</view>
-				<button @tap="login()">登 录</button>
-			</form>
-			<view v-if="typeIndex==0">
-				<view class="t-f">
-					<u-divider color="#aaa" border-color="#aaa">微信快速登陆</u-divider>
-				</view>
-				<!-- <view class="t-f"><text>————— 微信快速登录 —————</text></view> -->
-				<view style="display: flex;justify-content: center;padding-top: 50rpx;">
-					<view  @tap="wxLogin()"><image style="width: 88rpx;height: 88rpx;" src="@/static/login/wx.png"></image></view>
-				</view>
-			</view>
-			<u-action-sheet @click="typeClick" :list="typeList" v-model="typeShow"></u-action-sheet>
-			<u-verification-code seconds="60" @end="end" @start="start" ref="uCode" @change="codeChange"></u-verification-code>
-		</view>
+	<view class="">
+		<view class="t-login">
+			<u-top-tips ref="uTips"></u-top-tips>
+			<u-toast ref="uToast"></u-toast>
+			<!-- 页面装饰图片 -->
+			<!-- <image class="img-a" src="@/static/login/2.png"></image> -->
+			<!-- <image class="img-b" src="@/static/login/3.png"></image> -->
+			<!-- 标题 -->
+			<view class="t-b">欢迎回来!</view>
+			<form class="cl">
+				<view class="t-a" v-if="typeIndex==0">
+					<image   src="@/static/login/sj.png"></image>
+					<input class="input"  type="number"  placeholder="请输入手机号" maxlength="11" v-model="phone" />
+				</view>
+				<view class="t-a" v-else>
+					<image  style="width: 50rpx;height: 54rpx;padding-bottom: 6rpx;" src="@/static/login/qiye1.png"></image>
+					<input class="input" disabled  type="text" @click="enterpriseShow = true"  placeholder="请选择企业" v-model="enterpriseName" />
+				</view>
+				<view class="t-a" v-if="typeIndex==0">
+					<image  src="@/static/login/yz.png"></image>
+					<input class="input" type="number"  maxlength="6" placeholder="请输入验证码" v-model="yzm" />
+					<view :style="showText?'':'background-color: #A7A7A7;'"  class="t-c" @click="getCode()">{{tips}}</view>
+				</view>
+				<view class="t-a" v-else>
+					<image  src="@/static/login/yz.png"></image>
+					<input class="input" type="password"  maxlength="6" placeholder="请输入登录密码" v-model="password" />
+				</view>
+				<view  class="flex padding-bottom-50" style="text-align: left">
+					<view @click="typeShow=true" style="border: 1px solid #e9e9e9;background-color: #f8f7fc;" class="cu-btn   df  round">
+						<text class="text-black">{{typeList[typeIndex].text}}</text>
+						<text class="text-black cuIcon-triangledownfill " style="font-size: 40rpx;padding-left: 4rpx;"></text>
+					</view>
+				</view>
+				<view class="checkbox">
+					<u-checkbox 
+					@change="checkboxChange" 
+					v-model="checked" 
+					>七天内免登录</u-checkbox>
+				</view>
+				<button class="button" @click="login()">登 录</button>
+			</form>
+			<view v-if="typeIndex==0">
+				<view class="t-f">
+					<u-divider color="#aaa" border-color="#aaa">微信快速登陆</u-divider>
+				</view>
+				<!-- <view class="t-f"><text>————— 微信快速登录 —————</text></view> -->
+				<view style="display: flex;justify-content: center;padding-top: 50rpx;">
+					<view  @click="wxLogin()"><image style="width: 88rpx;height: 88rpx;" src="@/static/login/wx.png"></image></view>
+				</view>
+			</view>
+			<u-popup  border-radius="60" height="60%"  mode="bottom" v-model="enterpriseShow">
+				<view class="fixed cu-bar search bg-white">
+					<view class="search-form round">
+						<text class="cuIcon-search"></text>
+						<input v-model="keyword"  :adjust-position="false" type="text" placeholder="请输入公司名称搜索" confirm-type="search"></input>
+					</view>
+					<view @click="fetchEnterpriseList" class="action">
+						<button class="cu-btn bg-blue shadow-blur round">搜索</button>
+					</view>
+				</view>
+				<scroll-view @scrolltolower="scrolltolower" style="padding-top: 110rpx;height: 100%;" :scroll-y="true" >
+					<view @click="selectEnterprise(item)" hover-class="hoverClass" class="text-center padding-30 solid-bottom"  v-for="(item,index) in enterpriseList" :key="index">
+						<text>{{item.enterpriseName}}</text>
+					</view>
+					<u-divider v-if="flag" height="80">没有更多了</u-divider>
+				</scroll-view>
+			</u-popup>
+			
+			<u-action-sheet @click="typeClick" :list="typeList" v-model="typeShow"></u-action-sheet>
+			<u-verification-code seconds="60" @end="end" @start="start" ref="uCode" @change="codeChange"></u-verification-code>
+		</view>
 	</view>
 </template>
-<script>
-let that
-export default {
+<script>
+let that;
+export default {
 	data() {
-		return {
-			typeList:[{text:'员工',value:'0'},{text:'企业',value:'1'}],
-			typeShow:false,
-			typeIndex:0,
+		return {
+			keyword:'',
+			enterpriseShow:false,
+			enterpriseList:[],
+			
 			checked:false,//七天免登录
-			title: '欢迎回来!', 
-			tips: '', 
+			
+			//登录类型
+			typeList:[{text:'员工登录',value:'0'},{text:'企业登录',value:'1'}],
+			typeShow:false,
+			typeIndex:0,
+			
+			//员工登录:手机号码+验证码登录
+			phone: '',
+			yzm: '' ,
+			
+			//发送验证码
+			tips: '',
 			showText:true,
-			phone: '', //手机号码
-			yzm: '' //验证码
+			
+			//企业登录:统一信用代码+密码登录
+			enterpriseName:'',
+			creditCode:'',
+			password:'',
+			
+			//微信快速登陆
+			openId:'',
+			
+			//分页
+			current:1,
+			flag:false,
 		};
 	},
-	onLoad() {
-		console.log(this.$Route.query.fullPath);
-		if (this.$cache.get('phone')) {
-			//免登录
-			uni.switchTab({
-				url:"/pages/index/index"
-			})
-		}
+	onLoad() {
+		that=this
+		this.getOpenid()
+		if (this.$isAuth()) {
+			//免登录
+			uni.switchTab({
+				url:"/pages/index/index"
+			})
+		}
 	},
-	methods: {
-		wxLogin(){
-			this.$refs.uTips.show({
-				title: '完成员工认证的用户才可以使用微信快速登陆',
-				type: 'primary',
-				duration: '2500'
-			})
-		},
-		typeClick(index){
-			this.typeIndex=index
-		},
-		getCode() {
-			if(this.$refs.uCode.canGetCode) {
-				// 模拟向后端请求验证码
-				uni.showLoading({
-					title: '正在获取验证码'
-				})
-				setTimeout(() => {
-					uni.hideLoading();
-					// 这里此提示会被this.start()方法中的提示覆盖
-					this.$u.toast('验证码已发送');
-					// 通知验证码组件内部开始倒计时
-					this.$refs.uCode.start();
-				}, 3000);
-			} else {
-				this.$u.toast('倒计时结束后再发送');
-			}
-		},
-		checkboxChange(e){
-			this.checked=e.value
-		},
-		codeChange(text) {
-			this.tips = text;
-		},
-		end() {
-			this.showText=true
-		},
-		start() {
-			this.showText=false
+	methods: {
+		scrolltolower(e){
+			 if(this.enterpriseList.length<this.current*10){
+				 this.flag=true
+			   return
+			}else{
+				this.current++
+				this.fetchEnterpriseList()
+			}
+			
+		},
+		/**
+		 * 获取openid
+		 */
+		getOpenid(){
+			let that=this
+			uni.login({
+				success: (res) => {
+					let data={
+						appId:this.$api.wxData.appId,
+						jsCode:res.code,
+						secret:this.$api.wxData.secret
+					}
+					this.$api.wxApi.getOpenId(data).then(res=>{
+						let resData= JSON.parse(res.data)
+						this.openId=resData.openid
+					})
+				}
+			})
+		},
+		/**
+		 * 加载企业列表
+		 */
+		fetchEnterpriseList(){
+			let params={
+				enterpriseName:this.keyword,
+				current:this.current
+			}
+			this.$api.enterprise.page(params).then(res=>{
+				this.pages=res.data.pages
+				this.enterpriseList = [...this.enterpriseList,...res.data.records]
+			})
+		},
+		/**
+		 * 选择公司的回调
+		 */
+		selectEnterprise(item){
+			this.$cache.put('enterpriseName',item.enterpriseName)
+			this.enterpriseShow=false
+			this.creditCode=item.creditCode
+			this.enterpriseName=item.enterpriseName
+		},
+		/**
+		 * 登陆类型
+		 * @param {Object} index
+		 */
+		typeClick(index){
+			this.typeIndex=index
+			if (this.typeIndex==1) {
+				//企业登录,加载企业数据
+				this.fetchEnterpriseList()
+			}
+		},
+		/**
+		 * 获取验证码
+		 */
+		async getCode() {
+			let that=this
+			
+			if (this.$isEmpty(this.phone)) {
+				this.$u.toast("请输入手机号")
+				return
+			}
+			if (!this.$u.test.mobile(this.phone)) {
+				this.$u.toast("请输入正确的手机号")
+				return
+			}
+			if(this.$refs.uCode.canGetCode) {
+				// 模拟向后端请求验证码
+				uni.showLoading({
+					title: '正在获取验证码'
+				})
+				setTimeout(async () => {
+					uni.hideLoading();
+					let params=`?phone=${this.phone}`
+					this.$api.SMSApi.sendSms(params).then(res=>{
+						if (res.data=='获取验证码成功') {
+							that.$u.toast('验证码已发送');
+							// 通知验证码组件内部开始倒计时
+							that.$refs.uCode.start();
+						}else{
+							that.$refs.uTips.show({
+								title: res.data,
+								type: 'primary',
+								duration: '3000'
+							})
+						}
+						// 通知验证码组件内部开始倒计时
+						that.$refs.uCode.start();
+					}).catch(err=>{
+						that.$u.toast('获取验证码失败');
+					})
+				}, 2300);
+			} else {
+				this.$u.toast('倒计时结束后再发送');
+			}
+		},
+		checkboxChange(e){
+			this.checked=e.value
+		},
+		codeChange(text) {
+			this.tips = text;
+		},
+		end() {
+			this.showText=true
+		},
+		start() {
+			this.showText=false
 		},
 		//当前登录按钮操作
-		login() {
-			var that = this;
-			if (!that.phone) {
+		async login() {
+			if (this.typeIndex==0) {
+				//员工登录
+				this.staffLogin()
+			}else if (this.typeIndex==1) {
+				//企业登录
+				this.enterpriseLogin()
+			}
+		},
+		/**
+		 * 登录成功的处理方式
+		 * 如果是从其他地方跳转回的登录页,则登录后跳转回原页面
+		 * 否则跳转到首页
+		 */
+		handelSuccessLogin(loginType){
+			//储存必要信息
+			if (that.checked) {
+				//七天内免登录
+				//存储登录类型,用于判断用户的登录类型和是否登录
+				that.$cache.put("loginType",loginType,1*24*60*60*7) 
+			}else{
+				//三十分内钟免登录
+				that.$cache.put("loginType",loginType,1*60*30)
+			}
+			//跳转
+			  uni.showLoading({
+				title: '登陆中...'
+			  })
+			  setTimeout(()=>{
+				uni.hideLoading()
+				if (that.$isEmpty(that.$Route.query.fullPath)) {
+					uni.switchTab({
+						url:"/pages/index/index"
+					})
+					return
+				}
+				if (that.$Route.query.fullPath.indexOf('login')) {
+					uni.switchTab({
+						url:"/pages/index/index"
+					})
+				}else{
+					uni.reLaunch({
+						url:that.$Route.query.fullPath
+					})
+				}
+				
+			  },1500)
+		},
+		//员工登录
+		async staffLogin(){
+			if (this.phone=='19124812874') {
+				//供微信审核人员审核的账号
+				uni.navigateTo({
+					url:"/pages/auth/auth?operationType=1&phone="+this.phone
+				})
+				return
+			}
+			if (!this.phone) {
 				uni.showToast({ title: '请输入手机号', icon: 'none' });
 				return;
 			}
-			if (!/^[1][3,4,5,7,8,9][0-9]{9}$/.test(that.phone)) {
+			if (!/^[1][3,4,5,7,8,9][0-9]{9}$/.test(this.phone)) {
 				uni.showToast({ title: '请输入正确手机号', icon: 'none' });
 				return;
 			}
-			if (!that.yzm) {
+			if (!this.yzm) {
 				uni.showToast({ title: '请输入验证码', icon: 'none' });
 				return;
-			}
-			if (that.checked) {
-				//七天内免登录
-				that.$cache.put("phone",that.phone,1*24*60*60*7) 
-			}else{
-				//三十分内钟免登录
-				that.$cache.put("phone",that.phone,1*60*30)
-			}
-			//存储登陆的用户类型
-			if (that.typeIndex==0) {
-				//员工登陆
-				that.$cache.put('loginType','employees')
-			}else if(that.typeIndex==1){
-				//企业登陆
-				that.$cache.put('loginType','enterprise')
-			}
-			uni.showLoading({
				title: '登陆中...'
			})
-			setTimeout(()=>{
-				uni.hideLoading()
-				if (that.$isEmpty(that.$Route.query.fullPath)) {
-					uni.switchTab({
-						url:"/pages/index/index"
-					})
-				}else{
-					uni.reLaunch({
-						url:that.$Route.query.fullPath
-					})
-				}
-				
-			},1500)
+			}
+			if (this.yzm.length!=6) {
+				this.$u.toast('请输入6位数的验证码')
+				return
+			}
+			let params={
+				phone:this.phone,
+				code:this.yzm
+			}
+			let smsRes=await this.$api.SMSApi.validCode(this.$u.queryParams(params))
+			if (smsRes.data!='success') {
+				this.$u.toast(smsRes.data)
+				return
+			}
+			this.userLogin()
 		},
+		//用户登陆
+		async userLogin(){
+			//通过手机号查找该员工信息
+			let staffRes=await this.$api.enterprisestaff.detail({phone:this.phone})
+			if (this.$isEmpty(staffRes.data)) {
+				let data={
+					phone:this.phone
+				}
+				//如果没有员工信息。新用户。跳转到注册认证界面
+				this.$Router.push({name:'auth',params:data})
+			}else{
+				let userInfo=staffRes.data
+				if (userInfo.examine==0) {
+					//有员工信息,但审核未通过,提示等待审核
+					this.$showModel('您的认证信息尚未审核,请联系企业负责人审核',false)
+				}else if(userInfo.examine==1){
+					this.$cache.put('agencyId',userInfo.agencyId)
+					getApp().globalData.userInfo=userInfo
+					//存储手机号,便于拉取用户信息
+					this.$cache.put('phone',userInfo.phone)
+					this.$cache.put('enterpriseId',userInfo.enterpriseId)
+					this.handelSuccessLogin('staff')
+				}else if (userInfo.examine==2) {
+					//审核不通过
+					this.$showModel('您的认证信息审核不通过,是否重新认证!').then(res=>{
+						uni.navigateTo({
+							url:"/pages/auth/auth?operationType=1&phone="+that.phone
+						})
+					})
+				}
+			}
+		},
+		/**
+		 * 微信快速登陆
+		 */
+		wxLogin(){
+			this.$api.enterprisestaff.page({openId:this.openId,examine:1}).then(res=>{
+				if (this.$isEmpty(res.data.records)) {
+					this.$refs.uTips.show({
+						title: '完成员工认证的用户才可以使用微信快速登陆',
+						type: 'primary',
+						duration: '2500'
+					})
+				}else{
+					let userInfo=res.data.records[0]
+					getApp().globalData.userInfo=userInfo
+					//存储手机号,便于拉取用户信息
+					this.$cache.put('phone',userInfo.phone)
+					this.$cache.put('agencyId',userInfo.agencyId)
+					this.$cache.put('enterpriseId',userInfo.enterpriseId)
+					this.handelSuccessLogin('staff')
+				}
+			})
+		},
+		/**
+		 * 企业登录
+		 */
+		async enterpriseLogin(){
+			if (this.$isEmpty(this.creditCode)) {
+				this.$u.toast('请选择企业')
+				return
+			}
+			if (this.$isEmpty(this.password)) {
+				this.$u.toast("请输入密码")
+				return
+			}
+			this.$api.enterprise.page({creditCode:this.creditCode,password:this.password}).then(res=>{
+				if (this.$isEmpty(res.data.records)) {
+					if (this.$isEmpty(res.data.data)) {
+						this.$u.toast('登录失败,密码错误')
+						return
+					}
+				}else{
+					//公司信息
+					let userInfo=res.data.records[0]
+					this.$api.agency.page({residentialId:userInfo.residentialId}).then(agencyRes=>{
+						this.$cache.put('agencyId',agencyRes.data[0].id)
+					})
+					getApp().globalData.userInfo=userInfo
+					//存储公司统一信用代码,便于拉取企业信息
+					this.$cache.put('creditCode',this.creditCode)
+					this.$cache.put('enterpriseId',userInfo.id)
+					this.handelSuccessLogin('enterprise')
+				}
+			})
+		}
 		
 	}
 };
 </script>
-<style lang="scss" >
-page{
-	background-color: #FFFFFF;
-}
-.checkbox{
-	padding-bottom: 50rpx;
-	padding-left: 20rpx;
-	margin-top: -10rpx;
-}
-
+<style lang="scss" >
+page{
+	background-color: #FFFFFF;
+}
+
+.hoverClass{
+	background-color: #eee;
+}
+.checkbox{
+	padding-bottom: 50rpx;
+	padding-left: 20rpx;
+	margin-top: -10rpx;
+}
+
+.cu-btn.lg {
+	padding: 0 50upx;
+	font-size: 30upx;
+	height: 70upx;
+}
+
 .img-a {
 	position: absolute;
 	width: 100%;
@@ -189,10 +451,10 @@ page{
 	width: 600rpx;
 	margin: -200rpx auto;
 	font-size: 28rpx;
-	color: #000;
+	color: #000;
 }
 
-.t-login button {
+.t-login .button {
 	font-size: 28rpx;
 	background: #5677fc;
 	color: #fff;
@@ -202,7 +464,7 @@ page{
 	box-shadow: 0 5px 7px 0 rgba(86, 119, 252, 0.2);
 }
 
-.t-login input {
+.t-login .input {
 	padding: 0 20rpx 0 120rpx;
 	height: 90rpx;
 	line-height: 90rpx;
@@ -214,11 +476,11 @@ page{
 }
 
 .t-login .t-a {
-	position: relative;
+	position: relative;
 }
 
-.t-login .t-a image {
-	width: 40rpx;
+.t-login .t-a image {
+	width: 40rpx;
 	height: 42rpx;
 	position: absolute;
 	left: 20rpx;
@@ -243,8 +505,8 @@ page{
 	border-radius: 50rpx;
 	height: 50rpx;
 	line-height: 50rpx;
-	padding: 0 25rpx;
-	z-index: 99999;
+	padding: 0 25rpx;
+	z-index: 1000;
 }
 
 .t-login .t-d {

+ 97 - 0
pages/mine/changePass/changePass.vue

@@ -0,0 +1,97 @@
+<template>
+	<view>
+		<view class="bg-white padding-left-40 padding-right-40">
+			<u-form   ref="uForm" >
+				<u-form-item   label="旧密码"  :label-width="labelWidth">
+					<u-input maxlength="12" placeholder="请输入旧密码"   v-model="password"  type="password"></u-input>
+				</u-form-item>
+				<u-form-item   label="新密码"  :label-width="labelWidth">
+					<u-input maxlength="12" placeholder="请输入新密码"   v-model="newPass"  type="password"></u-input>
+				</u-form-item>
+				<u-form-item   label="确认新密码"  :label-width="labelWidth">
+					<u-input maxlength="12" placeholder="再次输入新密码"   v-model="confirmPass"  type="password"></u-input>
+				</u-form-item>
+			</u-form>
+		</view>
+		<view @click="submit"  class="footer-fixed cu-btn  bg-blue flex" style="padding: 45rpx;">
+			确认修改
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				//企业信息
+				dataDetail:{},
+				
+				labelWidth:180,
+				password:'',
+				newPass:''
+			}
+		},
+		onShow() {
+			let dataDetail=getApp().globalData.userInfo
+			if (this.$isEmpty(dataDetail)) {
+				this.fetchUserInfo()
+			}else{
+				this.dataDetail=dataDetail
+			}
+		},
+		methods: {
+			/**
+			 * 加载企业信息
+			 * getApp().globalData.userInfo正常都会有值,一般不会来到这一步
+			 */
+			fetchUserInfo(){
+				let creditCode=this.$cache.get('creditCode')
+				this.$api.enterprise.detail({creditCode:creditCode}).then(res=>{
+					this.dataDetail=res.data
+				})
+			},
+			/**
+			 * 修改密码
+			 */
+			submit(){
+				if (this.$isEmpty(this.password)) {
+					this.$u.toast("请输入旧密码")
+					return
+				}
+				if (this.password!=this.dataDetail.password) {
+					this.$u.toast("旧密码错误")
+					return
+				}
+				if (this.$isEmpty(this.newPass)) {
+					this.$u.toast('请输入新密码')
+					return
+				}
+				if (this.newPass.length<6) {
+					this.$u.toast('新密码密码不能少于6位')
+					return
+				}
+				if (this.$isEmpty(this.confirmPass)) {
+					this.$u.toast('请再次输入新密码')
+					return
+				}
+				if (this.newPass!=this.confirmPass) {
+					this.$u.toast('两次密码输入不一致')
+					return
+				}
+				this.dataDetail.password=this.newPass
+				this.$api.enterprise.submit(this.dataDetail).then(res=>{
+					if (res.success) {
+						this.$showModel('修改成功,请重新登陆!',false).then(res=>{
+							this.$cache.clear()
+							this.$Router.push('login')
+						})
+					}
+				})
+			}
+		}
+	}
+</script>
+
+<style>
+
+</style>

+ 31 - 0
pages/mine/changePassword/changePassword.vue

@@ -0,0 +1,31 @@
+<template>
+	<view>
+		<view class="bg-white padding-left-40 padding-right-20">
+			<u-form   ref="uForm" >
+				<u-form-item   label="原密码"  :label-width="labelWidth">
+					<u-input   v-model="password"  type="text"></u-input>
+				</u-form-item>
+				<u-form-item   label="原密码"  :label-width="labelWidth">
+					<u-input   v-model="newPass"  type="text"></u-input>
+				</u-form-item>
+			</u-form>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				labelWidth:120,
+			}
+		},
+		methods: {
+			
+		}
+	}
+</script>
+
+<style>
+
+</style>

+ 176 - 0
pages/mine/editPhone/editPhone.vue

@@ -0,0 +1,176 @@
+<template>
+	<view class="page">
+		<view class="flex justify-center text-df padding-50">
+			完成短信验证进行换绑手机号
+		</view>
+		<view class="bg-white padding-left-40 padding-right-20">
+			<u-form   ref="uForm" >
+				<u-form-item   label="原手机号"  :label-width="labelWidth">
+					<u-input   v-model="phone" disabled type="number"></u-input>
+				</u-form-item>
+				<u-form-item  label="验证码"  :label-width="labelWidth">
+					<u-input  placeholder="请输入验证码" v-model="code" type="text"></u-input>
+					<view  :style="showText?'':'background-color: #A7A7A7;'"  @click="getCode" slot="right" class="cu-btn round sm bg-blue">
+						{{codeTips}}
+					</view>
+				</u-form-item>
+				<u-form-item   label="新手机号"  :label-width="labelWidth">
+					<u-input  placeholder="请输入新手机号" v-model="newPhone" type="number"></u-input>
+				</u-form-item>
+
+			</u-form>
+		</view>
+		<view @click="submit" class="cu-btn bg-blue round flex " style="padding: 40rpx;margin: 80rpx 20rpx;">
+			换绑手机号
+		</view>
+		<u-verification-code seconds="60" @end="end" @start="start" ref="uCode" @change="codeChange"></u-verification-code>
+	</view>
+</template>
+
+<script>
+export default {
+	name: '',
+	data() {
+		return {
+			userInfo:{},
+			phone:'',
+			newPhone:'',
+			labelWidth:200,
+			code:'',
+			codeTips:'',
+			showText:true,
+		};
+	},
+	onLoad() {
+		//从缓存中获取手机号
+		if (this.$isEmpty(this.$cache.get('phone'))) {
+			this.$showModel('系统错误',false).then(res=>{
+				this.$Router.back()
+				return
+			})
+		}else{
+			this.phone=this.$cache.get('phone')
+		}
+	},
+	onShow() {
+		//获取用户信息
+		if (this.$isEmpty(getApp().globalData.userInfo)) {
+			this.fetchUserInfo()
+		}else{
+			this.userInfo=getApp().globalData.userInfo
+		}
+	},
+	methods:{
+		/**
+		 * 获取用户信息
+		 */
+		fetchUserInfo(){
+			this.$api.enterprisestaff.detail({phone:this.phone}).then(res=>{
+				this.userInfo=res.data
+			})
+		},
+		/**
+		 * 验证码提示
+		 * @param {Object} text
+		 */
+		codeChange(text) {
+			this.codeTips = text;
+		},
+		end() {
+			this.showText=true
+		},
+		start() {
+			this.showText=false
+		},
+		/**
+		 * 确定修改
+		 */
+		async submit(){
+			let that=this
+			if (this.$isEmpty(this.newPhone)) {
+				this.$u.toast("请输入手机号")
+				return
+			}
+			if (!this.$u.test.mobile(this.newPhone)) {
+				this.$u.toast("请输入正确的手机号")
+				return
+			}
+			if (!this.code) {
+				uni.showToast({ title: '请输入验证码', icon: 'none' });
+				return;
+			}
+			if (this.code.length!=6) {
+				this.$u.toast('请输入6位数的验证码')
+				return
+			}
+			let params={
+				phone:this.phone,
+				code:this.code
+			}
+			let smsRes=await this.$api.SMSApi.validCode(this.$u.queryParams(params))
+			if (smsRes.data!='success') {
+				this.$u.toast(smsRes.data)
+				return
+			}
+			this.userInfo.phone=this.newPhone
+			this.$api.enterprisestaff.submit(this.userInfo).then(res=>{
+				if (res.success) {
+					getApp().globalData.userInfo=that.userInfo
+					that.$cache.put('phone',that.newPhone)
+					that.$showModel('修改成功',false).then(res=>{
+						that.$Router.back()
+					})
+				}
+			})
+		},
+		/**
+		 * 获取验证码
+		 */
+		async getCode() {
+			let that=this
+			if (this.$isEmpty(this.phone)) {
+				this.$showModel('系统异常',false).then(res=>{
+					this.$Router.back()
+				})
+				return
+			}
+			if(this.$refs.uCode.canGetCode) {
+				// 模拟向后端请求验证码
+				uni.showLoading({
+					title: '正在获取验证码'
+				})
+				setTimeout(async () => {
+					uni.hideLoading();
+					let params=`?phone=${this.phone}`
+					this.$api.SMSApi.sendSms(params).then(res=>{
+						if (res.data=='获取验证码成功') {
+							that.$u.toast('验证码已发送');
+						}else{
+							that.$refs.uTips.show({
+								title: res.data,
+								type: 'primary',
+								duration: '2500'
+							})
+						}
+						// 通知验证码组件内部开始倒计时
+						that.$refs.uCode.start();
+					}).catch(err=>{
+						that.$u.toast('获取验证码失败');
+						// 通知验证码组件内部开始倒计时
+						that.$refs.uCode.start();
+					})
+				}, 2300);
+			} else {
+				this.$u.toast('倒计时结束后再发送');
+			}
+		},
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+	.page{
+		background-color: #FFFFFF;
+		min-height: 100vh;
+	}
+</style>

+ 42 - 35
pages/mine/feedback/feedback.vue

@@ -7,11 +7,11 @@
 
 			<radio-group  class="block" @change="RadioChange">
 				<view class="radioBox">
-					<view class="radioItem" :class="data.type==item.type?'checkBorder':'defalutBorder'" v-for="(item,index) in typeList" :key="index">
+					<view class="radioItem" :class="data.feedbackType==item.feedbackType?'checkBorder':'defalutBorder'" v-for="(item,index) in typeList" :key="index">
 						<label class="flex" >
 							<view class="">
-								<radio  style="transform: scale(.7);"  class="round blue" :class="data.type===item.type?'checked':''" :checked="data.type==item.type?true:false"
-								 :value="item.type"></radio>
+								<radio  style="transform: scale(.7);"  class="round blue" :class="data.feedbackType===item.feedbackType?'checked':''" :checked="data.feedbackType==item.feedbackType?true:false"
+								 :value="item.feedbackType"></radio>
 							</view>
 							<view class="text-left padding-left-20 flex" style="flex-direction: column;justify-content: center;">
 								{{item.title}}
@@ -22,9 +22,9 @@
 			</radio-group>
 			<view class="area">
 				<view >
-					<textarea @input="input" :value="data.question"  maxlength="120"  placeholder="请描述您的问题,以便我们尽快为您处理"></textarea>
+					<textarea @input="input" :value="data.content"  maxlength="120"  placeholder="请描述您的问题,以便我们尽快为您处理"></textarea>
 					<view  class="text-right text-df text-gray padding-bottom-10">
-						{{data.question.length}} / 120
+						{{data.content.length}} / 120
 					</view>
 				</view>
 				<view class="imgArea">
@@ -32,7 +32,7 @@
 						<text class="">请提供相关问题的截图或照片(最多四张)</text>
 					</view>
 					<view class="cu-form-group" style="padding: 20rpx 0 0 0;">
-						<view class="grid col-3 grid-square flex-sub">
+						<view class="grid col-4 grid-square flex-sub">
 							<view class="bg-img" v-for="(item,index) in imgList" :key="index" @click="viewImage(index)" >
 							 <image :src="imgList[index]" mode="aspectFill"></image>
 								<view class="cu-tag bg-red" @click.stop="DelImg(index)" >
@@ -69,28 +69,29 @@
 				//图片回显
 				imgList:[],
 				data:{
-					member_id:'',
-					question:'',
-					image_base64:[],
-					type:"1",
+					name:'',
+					
+					content:'',
+					images:'',
+					feedbackType:"0",
 				},
 				//选择项列表
 				typeList:[
 					{
 						title:'功能异常:功能故障或不可用',
-						type:'1'
+						feedbackType:'0'
 					},
 					{
 						title:'产品建议:用的不爽,我有建议',
-						type:'2'
+						feedbackType:'1'
 					},
 					{
 						title:'安全密码:密码,故障等',
-						type:'3'
+						feedbackType:'2'
 					},
 					{
 						title:'其他问题',
-						type:'4'
+						feedbackType:'3'
 					},
 				],
 			}
@@ -98,38 +99,55 @@
 		methods: {
 			//选择反馈类型
 			RadioChange(e){
-				this.data.type=e.detail.value
+				this.data.feedbackType=e.detail.value
 			},
 			/**输入问题描述
 			 * @param {Object} e
 			 */
 			input(e){
-				this.data.question=e.detail.value
+				this.data.content=e.detail.value
 			},
 			sumit(){
-				if (this.$isEmpty(this.data.question)) {
+				this.data.agnecyId=this.$cache.get('agencyId')
+				if (this.$cache.get('loginType')==this.$loginType.STAFF) {
+					let userInfo=getApp().globalData.userInfo
+					this.data.openId=userInfo.openId
+					this.data.name=userInfo.realName
+					this.data.phone=userInfo.phone
+				}else if(this.$cache.get('loginType')==this.$loginType.ENTERPRISE){
+					this.data.name=this.$cache.get('enterpriseName')
+				}
+				this.data.images=this.imgList.join(',')
+				if (this.$isEmpty(this.data.content)) {
 					uni.showToast({
 						title:"问题描述不能为空",
 						icon:"none"
 					})
 					return
 				}
-				if (this.data.question.length<3) {
+				if (this.data.content.length<3) {
 					uni.showToast({
 						title:"问题描述不能少于三个字",
 						icon:"none"
 					})
 					return
 				}
-				if (this.$isEmpty(this.data.image_base64)) {
+				if (this.$isEmpty(this.data.images)) {
 					uni.showToast({
 						title:"请提供相关截图",
 						icon:"none"
 					})
 					return
 				}
-				this.data.member_id='19478'
-				console.log(this.data)
+				this.$api.feedback.submit(this.data).then(res=>{
+					if (res.success) {
+						this.$showModel('提交成功,感谢您的反馈!',false).then(res=>{
+							uni.navigateBack({
+								delta:1
+							})
+						})
+					}
+				})
 			},
 			/**
 			 * 上传问题截图
@@ -143,21 +161,10 @@
 					success: (res) => {
 						const tempFilePaths = res.tempFilePaths;
 						for (let index in tempFilePaths) {
-							//图片回显
-							that.imgList.push(tempFilePaths[index])
-							//图片转为base64的图片
-							uni.getFileSystemManager().readFile({
-							  filePath: tempFilePaths[index],
-							  //选择图片返回的相对路径
-							  encoding: 'base64',
-							  //编码格式
-							  success: res => {
-							    //成功的回调
-							    that.data.image_base64.push('data:image/jpeg;base64,' + res.data)
-							  }
-							});
+							that.$api.uploadFile.submit(tempFilePaths[index]).then(res=>{
+								that.imgList.push(res.data)
+							})
 						}
-					
 					}
 				});
 			},

+ 2 - 3
pages/mine/mine-info/mine-info.vue

@@ -73,6 +73,7 @@
 		},
 		methods: {
 			loginOut(){
+				let that=this
 				try {
 				     uni.clearStorageSync();
 					 uni.showModal({
@@ -80,9 +81,7 @@
 						confirmText:"确认",
 						content:"退出成功",
 						success() {
-							uni.reLaunch({
-								url:"../../login/login"
-							})
+							that.$Router.replaceAll({name:"login"})
 						}
 					 })
 				} catch (e) {

+ 176 - 215
pages/mine/mine.vue

@@ -1,38 +1,42 @@
 <template>
-<view class="">
-	<view class="pageBg">
-		<view class="mine_order_statue">
-			<view class="flex justify-between" style="padding: 20rpx;">
+<view >
+	<view >
+		<view v-if="loginType=='staff'" class="mine_order_statue">
+			<view   class="flex justify-between" style="padding:30rpx 20rpx;">
 				<view class="flex">
-					<image :src="userInfo.avatarUrl?userInfo.avatarUrl:'http://139.9.103.171:8888/group1/M00/00/01/iwlnq1_AYZqAb30pAAAQNvkj3Wk302.png'"  style="background-color: #FFFFFF;width: 110rpx;height: 110rpx;border-radius: 50%;" ></image>
+					<image :src="staffInfo.avatar?staffInfo.avatar:'http://139.9.103.171:8888/group1/M00/00/01/iwlnq1_AYZqAb30pAAAQNvkj3Wk302.png'"  style="background-color: #FFFFFF;width: 106rpx;height: 106rpx;border-radius: 10rpx;" ></image>
 					<view  class="flex flex-direction padding-left-30">
-						<text class="text-lg text-bold" v-text="userInfo.nickName?userInfo.nickName:'暂无昵称'"></text>
-						<button v-if="$isEmpty(userInfo)" open-type="getUserInfo" @getuserinfo="getUserInfo" class="cu-btn  bg-white text-blue margin-top-20 df" style="margin-left: -20rpx;">
-							点击获取微信昵称和头像
-						</button>
-						<view v-else class="padding-20" >
-							<text  class="margin-top-30 text-df">{{phone}}</text>
+						<text class="text-lg text-bold" v-text="staffInfo.realName"></text>
+						<view  class="text-df"  style="padding: 20rpx 0;" >
+							<text  class="margin-top-40 text-df">手机号:{{staffInfo.phone}}</text>
 						</view>
 					</view>
 				</view>
-				<view @click="goUserInfo" class="flex align-center justify-center">
-					<text class="cuIcon-right"></text>
-				</view>
 			</view>
 		</view>
-		
-		<view class="item_list">
-			<view class=" flex justify-between" style="padding: 40rpx 20rpx;">
+		<view class="item_list" v-if="loginType=='staff'">
+			<view class=" flex justify-between" style="padding: 34rpx 24rpx 34rpx 40rpx;">
 				<view class="text-bold">所属公司</view>
-				<view class="text-gray">
-					<text>暂无公司信息</text>
-					<text class="cuIcon-playfill  padding-left-20" style="font-size: 28rpx;"></text>
+				<view class="" @click="goEnterpriseDetail">
+					<text class="text-black" style="font-size: 28rpx;" >{{staffInfo.enterpriseName}}</text>
+					<text class="cuIcon-right text-gray padding-left-20" style="font-size: 24rpx;"></text>
+				</view>
+			</view>
+		</view>
+		<view class="item_list" style="margin-top: 0;" v-if="loginType=='enterprise'">
+			<view class="flex justify-between" style="padding: 50rpx 25rpx 60rpx 40rpx;">
+				<view class="text-bold">我的公司</view>
+				<view class="" @click="goEnterpriseDetail">
+					<text class="text-black" v-if="loginType=='enterprise'">{{enterpriseInfo.enterpriseName}}</text>
+					<text class="cuIcon-right text-gray padding-left-20" style="font-size: 24rpx;"></text>
 				</view>
 			</view>
 		</view>
 		
+		
+		
 		<view class="item_list" >
-			<view class="item" v-for="(item, index) in systemItemList" :key="index" @tap="tapToMenu(item.name)">
+			<view class="item" v-if="item.show"  v-for="(item, index) in systemItemList" :key="index" @tap="tapToMenu(item.name)">
 				<view class="item_content">
 					<image :src="item.itemIcon"></image>
 					<text>{{ item.itemName }}</text>
@@ -42,13 +46,24 @@
 				</view>
 			</view>
 		</view>
+		
+		<view @click="loginOut" class="cu-btn round flex bg-blue" style="padding: 40rpx;margin: 80rpx 15rpx 0;">
+			退出登陆
+		</view>
+		
 	</view>
+	<u-tabbar v-model="tabbarCurr"
+	 :icon-size="tabbar.iconSize" 
+	 :active-color="tabbar.activeColor" 
+	 :list="tabbar.list" :mid-button="false">
+	 </u-tabbar>
 </view>
 </template>
 
 <script>
 var app=getApp()
 import DtCustomBar from '@/components/dt_custom_bar.vue'
+import {tabbar} from "@/assets/js/tabbar.js"
 let that
 export default {
 	components: {
@@ -59,9 +74,39 @@ export default {
 			background: {
 				backgroundColor: '#f5f6f8',
 			},
-			phone:'',
-			userInfo:{},
-			systemItemList:[
+			//tabbar
+			tabbarCurr:2,
+			tabbar:tabbar,
+			
+			//登陆类型
+			loginType:'',
+			//员工信息
+			staffInfo:{},
+			//企业信息
+			enterpriseInfo:{},
+			
+			//功能列表
+			systemItemList:[],
+		};
+	},
+	onShow() {
+		this.apiGetUserInfo()
+	},
+	onLoad() {
+		that=this
+		
+		this.fetchStaticData()
+		if (this.loginType==this.$loginType.STAFF) {
+			this.fetchStaffInfo()
+		}else if (this.loginType==this.$loginType.ENTERPRISE) {
+			this.fetchEnterpriseInfo()
+		}
+	},
+	methods: {
+		fetchStaticData(){
+			this.loginType=this.$cache.get('loginType') || ''
+			
+			this.systemItemList=[
 				// {
 				// 	itemName: '物业报修',
 				// 	itemIcon: '/static/index/blue/wuye.png',
@@ -73,50 +118,120 @@ export default {
 				// 	index:10
 				// },
 				{
-					itemName: '我的消息',
-					itemIcon: '/static/index/blue/msg.png',
-					name:'msg'
+					itemName: '换绑手机号',
+					itemIcon: '/static/index/blue/change-phone.png',
+					name:'changePhone',
+					show:this.loginType==this.$loginType.STAFF
+				},
+				{
+					itemName: '修改密码',
+					itemIcon: '/static/index/blue/changePsw.png',
+					name:'changePsw',
+					show:this.loginType==this.$loginType.ENTERPRISE
 				},
 				{
 					itemName: '问题反馈',
 					itemIcon: '/static/index/blue/fankui.png',
-					name:'feedback'
+					name:'feedback',
+					show:true
 				},
 				{
 					itemName: '帮助中心',
 					itemIcon: '/static/index/blue/shuoming.png',
-					name:'help'
+					name:'help',
+					show:true
 				},
 				{
 					itemName: '用户协议',
 					itemIcon: '/static/index/blue/xieyi1.png',
-					name:'protocol'
+					name:'protocol',
+					show:true
 				}
-			],
-			memberId: '',
-			dataDetail: {},
-			orderStatusNum: {},
-			userDetail: {},
-			isLogin: false,
-		};
-	},
-	onShow() {
-		this.apiGetUserInfo()
-	},
-	onLoad() {
-		that=this
-	},
-	computed: {
+			]
+		},
 		
-	},
-	methods: {
+		loginOut(){
+			let that=this
+			try {
+			     uni.clearStorageSync();
+				 uni.showModal({
+				 	showCancel:false,
+					confirmText:"确认",
+					content:"退出成功",
+					success() {
+						getApp().globalData.userInfo=''
+						that.$Router.replaceAll({name:"login"})
+					}
+				 })
+			} catch (e) {
+				uni.showToast({
+					title:"退出失败",
+					icon:"none"
+				})
+			}
+		},
+		/**
+		 * 订阅认证信息
+		 */
+		subscribe(){
+			// 获取用户的当前设置,判断是否点击了“总是保持以上,不在询问”
+			uni.getSetting({
+			 	withSubscriptions: true,//是否获取用户订阅消息的订阅状态,默认false不返回
+			   success(res){
+				   if(res.authSetting['scope.address']){
+						 uni.openSetting({ // 打开设置页
+						   success(res) {
+								console.log(res.authSetting)
+							},
+							fail: (err) => {
+								console.log(err);
+							}
+						 });
+				    }
+				   else{// 用户没有点击“总是保持以上,不再询问”则每次都会调起订阅消息
+						 uni.requestSubscribeMessage({
+							   tmplIds: that.$tmplIds,
+							   success (res) {
+								 console.log(res)
+							   },
+							   fail(err){
+								   console.error(err);
+							   }
+						  })
+					 }
+			   }
+			})
+		},
+		fetchStaffInfo(){
+			let phone= this.$cache.get('phone')
+			this.$api.enterprisestaff.detail({phone:phone}).then(res=>{
+				this.staffInfo=res.data
+			})
+		},
+		fetchEnterpriseInfo(){
+			let creditCode=this.$cache.get('creditCode')
+			this.$api.enterprise.detail({creditCode:creditCode}).then(res=>{
+				this.enterpriseInfo=res.data
+			})
+		},
+		goEnterpriseDetail(){
+			uni.navigateTo({
+				url:"/pages/company/company"
+			})
+		},
 		//功能跳转
 		tapToMenu(name){
-			let url='/pages/mine/protocol/protocol'
+			let url=''
 			switch (name){
 				case 'feedback':
 					url="/pages/mine/feedback/feedback"
 					break;
+				case 'changePhone':
+					url="/pages/mine/editPhone/editPhone"
+					break;
+				case 'changePsw':
+					url="/pages/mine/changePass/changePass"
+					break;
 				default:
 					break;
 			}
@@ -124,6 +239,10 @@ export default {
 				uni.navigateTo({
 					url:url
 				})
+			}else{
+				uni.navigateTo({
+					url:"/pages/mine/protocol/protocol"
+				})
 			}
 		},
 		//获取微信昵称和头像
@@ -132,187 +251,29 @@ export default {
 			console.log(this.userInfo.avatarUrl)
 			this.$cache.put('userInfo',this.userInfo)
 		},
-		//从缓存中获取用户信息
+		//获取用户信息
 		apiGetUserInfo(){
 			this.phone=this.$cache.get('phone') || ''
-			this.userInfo= this.$cache.get('userInfo') || {}
+			this.userInfo= getApp().globalData.userInfo || {}
 		},
-		//跳转到用户信息
-		goUserInfo(){
-			uni.navigateTo({
-				url:"/pages/mine/mine-info/mine-info"
-			})
-		}
 	},
 
 };
 </script>
 <style lang="scss" scoped>
-page{
+.page{
 	min-height: 100vh;
-	background-color: #f5f6f8;
+	background-color: #FFFFFF;
+	// background-color: #f5f6f8;
 }
 button::after {
 	  border: none;
 	}
-.header {
-	// border-bottom-right-radius: 100rpx;
-	// border-bottom-left-radius: 100rpx;
-	//todo
-	height: 260upx;
-	background-repeat: no-repeat;
-	background-size: 100% 100%;
-	.member_head_img {
-		margin-right: 30upx;
-		image {
-			//todo
-			width: 110upx;
-			height:110upx;
-			border-radius: 59rpx;
-		}
-
-		.avatar-wrap {
-			display: inline-flex;
-			border: 0;
-			background-color: transparent;
-		}
-	}
-
-	.no_login {
-		display: flex;
-		flex-direction: column;
-		.no_login_btn {
-			border-radius: 25px;
-			//todo
-			font-size: 32upx;
-			color: #fff;
-			width: fit-content;
-			padding: 0 15upx;
-			height: 60upx;
-			line-height: 60upx;
-			background: transparent;
-		}
-	}
-
-	.member_info {
-		display: flex;
-		flex-direction: column;
-		.member_name_box {
-			display: flex;
-			flex-direction: row;
-			align-items: center;
-			.member_name { 
-				font-size: 40upx;
-				color: #fff;
-				font-weight: bold;
-			}
-			.member_level {
-				background: #ffc600;
-				color: #fff;
-				font-size: 22upx;
-				border-radius: 4upx;
-				padding: 5upx 10upx;
-				margin-left: 10upx;
-			}
-		}
-
-		.member_phone_box {
-			display: flex;
-			flex-direction: row;
-			align-items: center;
-			.member_phone { 
-				font-size: 24upx;
-				color: #FFFFFF;
-			}
-
-			image {
-				width: 19upx;
-				height: 19upx;
-				padding: 20upx;
-			}
-		}
-	}
-
-	.member_benefits {
-		background: #fff;
-		width: 180upx;
-		display: flex;
-		flex-direction: row;
-		align-items: center;
-		text-align: center;
-		border-radius: 27upx 0px 0px 27upx;
-		height: 54upx;
-		text-align: center;
-		position: absolute;
-		right: 0;
-
-		image {
-			width: 26upx;
-			height: 23upx;
-			padding: 0 10upx 0 30upx;
-		}
-
-		.member_benefits_text {
-			font-size: 24upx;
-			color: #2f7ff5;
-			letter-spacing: 2upx;
-		}
-	}
-
-	button::after {
-		border: none;
-	}
-}
-
-.coupon_point {
-	display: flex;
-	flex-direction: row;
-	align-items: center;
-	height: 110upx;
-	background: #fff;
-	justify-content: space-around;
-
-	.item {
-		display: flex;
-		flex-direction: column;
-		align-items: center;
-		width: 50%;
 
-		.item_box {
-			display: flex;
-			flex-direction: row;
-			align-items: center;
-			letter-spacing: 2upx;
-
-			.item_content {
-				color: #2f7ff5;
-				font-size: 34upx;
-			}
-
-			.item_unit {
-				color: #2f7ff5;
-				font-size: 22upx;
-				margin-top: 6upx;
-			}
-		}
-
-		.item_text {
-			color: #333333;
-			font-size: 22upx;
-		}
-	}
-
-	.line {
-		width: 1upx;
-		height: 50upx;
-		background: #e5e5e5;
-	}
-}
 
 .mine_order_statue {
 	//todo
-	box-shadow: 0 10rpx #e7f1fd ;
-	margin: 20upx 20upx 0;
+	// box-shadow: 0 10rpx #e7f1fd ;
 	background: #fff;
 	padding: 10upx 20upx;
 	border-radius: 20rpx;
@@ -322,9 +283,9 @@ button::after {
 	display: flex;
 	flex-direction: column;
 	background: #fff;
-	margin-top: 20upx;
-    margin-left: 20rpx;
-	margin-right: 20rpx;
+	margin-top: 15upx;
+    // margin-left: 20rpx;
+	// margin-right: 20rpx;
 	border-radius: 10rpx;
 	.item {
 		display: flex;

+ 1 - 1
pages/mine/protocol/protocol.vue

@@ -1,6 +1,6 @@
 <template>
 	<view>
-		<u-empty  marginTop="300"  icon-size="400" text="暂未开发" src="http://139.9.103.171:1888/miniofile/xlyq/empty/empty.png"></u-empty>
+		<u-empty  marginTop="300"   text="暂未开发" src="http://139.9.103.171:1888/miniofile/xlyq/empty/empty.png"></u-empty>
 	</view>
 </template>
 

+ 27 - 17
pages/news/detail.vue

@@ -2,13 +2,19 @@
 	<view>
 		<!--文章标题-->
 		<view class="title" >{{dataDetail.title}}</view>
-		<view class="text-gray " style="padding: 0 0 30rpx 30rpx;">
-			<text class="cuIcon-time padding-right-10"></text>
-			<text style="font-size: 28rpx;color: #999999;text-align: left;">{{dataDetail.time}}</text>
+		<view class="flex text-gray" style="padding: 0 0 30rpx 30rpx;">
+			<view >
+				<text class="cuIcon-time padding-right-10"></text>
+				<text style="font-size: 28rpx;color: #999999;text-align: left;">{{dataDetail.createTime}}</text>
+			</view>
+			<view class="margin-left-30">
+				<text class="cuIcon-attention  padding-right-10"></text>
+				<text style="font-size: 28rpx;color: #999999;text-align: left;">{{dataDetail.reader}}</text>
+			</view>
 		</view>
 		<view class="content-view-box">
 			<view class="font-view">
-				<u-parse :html="dataDetail.content"></u-parse>
+				<rich-text :nodes="dataDetail.article"></rich-text>
 			</view>
 		</view>
 		<u-divider height="80">到底了</u-divider>
@@ -22,27 +28,31 @@
 		},
 		data() {
 			return {
-				dataDetail:{
-					title:'银川经开区以技术创新推动行业发展',
-					time:'2020-11-22 12:14:56',
-					content:`原标题:银川经开区:以技术创新推动行业发展
-
-实时进行监控、全自动采集数据、提前发出设备故障报警提示……在银川经济技术开发区(以下简称“银川经开区”)宁夏希望信息产业股份有限公司(以下简称“希望信息产业”)的“智慧冷库云管理APP”上,这些基本功能一应俱全,只需要轻轻点击一下手机,就可以随时“操作”冷库设备,这帮助工业企业提供了减少能耗的安全运行方案,提高了工作效率和智能化水平,加快了企业实现转型升级的步伐。
-
-据悉,“智慧冷库云管理APP”利用“互联网、大数据、云计算”等新兴技术,来实现数据采集在线监控、历史查询、业务报表、监控调取、数据接口等基本功能,快速提升制冷能源系统管理效率,以实现对制冷能源系统在线化、实时化、移动化、智能化管理,改变传统人工管理带来的设备运行效率低、能源浪费严重、设备故障率高以及制冷品质不良等问题,以工业互联网赋能,提高行业经营运维管理。
-
-“通过技术创新,让数据采集能够跨越空间,冷库中数百种参数呈现在APP里,无论从管理层的手机还是到现场工程师的电脑,随时随地一眼看尽冷库、制冷系统状态。”希望信息产业相关负责人说,同时,公司还根据企业需求,提供定制化服务模块,服务领域覆盖全国商业冷链物流、工业冷冻、大中型空调以及区域冷热联供能源站。
-
-当前,随着新一轮科技革命和产业革命加速发展,银川经开区抢抓机遇,实施工业互联网创新发展战略,引进国内外工业互联网领军人才和创业创新团队,培育了一批数字车间、智能工厂、工业互联网平台、上云标杆企业、两化融合管理体系等试点企业,园区也正以大数据智能化为引领,点燃创新“引擎”,驱动经济加快转型升级,为打造“千亿级升级版”经开区储蓄更多能量。(记者闫茜)`
-				},
+				id:'',
+				dataDetail:{}
 			}
 		},
 		onShow() {
 			
 		},
 		onLoad(options) {
+			this.id=options.id || ''
+			this.read()
+			this.fetchDataDetail()
 		},
 		methods: {
+			fetchDataDetail(){
+				this.$api.CMS.detail({id:this.id}).then(res=>{
+					this.dataDetail=res.data
+				})
+			},
+			async read(){
+				let params={
+					id:this.id,
+					addCount:1
+				}
+				await this.$api.CMS.addReaderCount(params)
+			}
 			
 		}
 	}

+ 70 - 0
pages/news/list.vue

@@ -0,0 +1,70 @@
+<template>
+	<view>
+		 <mescroll-body ref="mescrollRef" @init="mescrollInit" @down="downCallback" @up="upCallback" :down="downOption" :up="upOption">
+			<infomation :newList="list"></infomation>
+		 </mescroll-body>
+	</view>
+</template>
+
+<script>
+	import MescrollMixin from "@/components/mescroll-body/mescroll-mixins.js";
+	import infomation from "@/components/infomation/infomation.vue"
+	export default {
+		mixins:[MescrollMixin],
+		components:{
+			infomation
+		},
+		data() {
+			return {
+				// 列表数据
+				list: [],
+				downOption: { 
+					use: true,
+					auto: false
+				},
+				upOption: {
+					page: {
+						page: 0,
+						size: 10
+					},
+					noMoreSize: 5, 
+					empty: {
+						tip: '暂无相关数据'
+					}
+				},
+			}
+		},
+		methods: {
+			downCallback(){
+				setTimeout(()=>{
+					this.mescroll.resetUpScroll()
+				},1500)
+			},
+			upCallback(mescroll) {
+				let params={
+					current:mescroll.num,
+					size:mescroll.size,
+					agencyId:this.$cache.get('agencyId')
+				}
+				try{
+					this.$api.CMS.page(params).then(res=>{
+						let data=res.data.records
+						let length=data.length
+						let total=res.data.total
+						mescroll.endBySize(length, total);
+						if(mescroll.num == 1) this.list = []; //如果是第一页需手动制空列表
+						this.list=this.list.concat(data); //追加新数据
+					})
+				}catch(e){
+					console.log(e);
+					mescroll.endErr();
+				}
+			},
+			
+		}
+	}
+</script>
+
+<style>
+
+</style>

+ 141 - 196
pages/test/test.vue

@@ -1,219 +1,164 @@
 <template>
-	<view class="padding-20">
-		<view class="auth">
-			<text class="cuIcon-titles text-blue padding-right-10"></text>
-			<text>个人信息认证</text>
-			<view class="card">
-				<u-form :model="model"  ref="uForm" >
-					<u-form-item  :label-width="labelWidth"  label="姓名" >
-						<u-input  placeholder="请输入姓名" v-model="model.name" type="text"></u-input>
-					</u-form-item>
-					<u-form-item  :label-width="labelWidth"   label="性别" >
-						<u-input  type="select" :select-open="sexSelectShow" v-model="model.sex" placeholder="请选择性别" @click="sexSelectShow = true"></u-input>
-					</u-form-item>
-					<u-form-item   label="身份证号"  :label-width="labelWidth">
-						<u-input  placeholder="请输入身份证号" v-model="model.idcard" type="idcard"></u-input>
-					</u-form-item>
-					<u-form-item   label="手机号码"  :label-width="labelWidth">
-						<u-input  placeholder="请输入手机号" v-model="model.phone" type="number"></u-input>
-					</u-form-item>
-					<u-form-item :border-bottom="false"  label="面部信息采集"  :label-width="labelWidth"></u-form-item>
-				</u-form>
-				
-				<view @click="" class="flex justify-center padding-bottom-50">
-					<view class=" ">
-						<upload-img
-						   :width="$isEmpty(face_url)?300:560"
-						   :height="$isEmpty(face_url)?300:420"
-						  :currentImage="face_url"
-						  bgsrc="http://139.9.103.171:1888/img/image/camera1.png"
-						  >
-						</upload-img>
-						<view class="text-center padding-top-20" style="color: #59a5f0;">
-						 <text class="cuIcon-camera padding-right-sm" style="font-size: 30rpx;"></text>
-						 <text v-if="$isEmpty(face_url)">点击上传人脸</text>
-						 <text style="margin-top: 40rpx;display: inline-block;" v-else>点击重新上传</text>
+	<view class="">
+		<view class="data" v-for="(item, index) in list" :key="index">
+			<view class="top">
+				<view class="left">
+					<view class="title ">
+						<text class="text-bold">IEMI:</text>
+						<text>{{item.imei}}</text>
+						<text @click="copy(item.imei)" style="text-decoration: underline;padding-left: 10rpx;color: #007AFF;">复制</text>
+					</view>
+				</view>
+				<view class="right">
+					<view v-if="item.defenseStatus==1" class="radius sm cu-btn line-green">
+						已上线
+					</view>
+					<view v-if="item.defenseStatus==2" class="radius sm cu-btn line-red">
+						已下线
+					</view>
+				</view>
+			</view>
+			<view class="item  padding-left-20">
+				<view class="content flex">
+					<u-icon  size="36" name="http://139.9.103.171:1888/miniofile/xlyq/xiaofangshuang.png"></u-icon>
+					<text class="text-bold padding-left-10">设备名称:</text>
+					<text>{{item.deviceName}}</text>
+				</view>
+				<view class="content flex">
+					<view class="flex">
+						<u-icon  size="36" name="http://139.9.103.171:1888/miniofile/xlyq/dianchi.png"></u-icon>
+						<text class="text-bold padding-left-10">电池电量:</text>
+					</view>
+					<view class="striped active cu-progress round margin-top-sm sm" style="width: 60%;">
+						<view style="width: 68%;background-color: #16c60c;color: #FFFFFF;">
+							{{item.battery?item.battery:0}}%
+						</view>
+					</view>
+				</view>
+				<view class="content flex">
+					<view class="flex">
+						<u-icon  size="36" name="http://139.9.103.171:1888/miniofile/xlyq/wifi.png"></u-icon>
+						<text class="text-bold padding-left-10">信号强弱:</text>
+					</view>
+					<view class="striped active cu-progress round margin-top-sm sm" style="width: 60%;">
+						<view style="width: 58%;background-color: #fc5f44;color: #FFFFFF;">
+							{{item.signalIntensity?item.signalIntensity:0}}%
 						</view>
 					</view>
 				</view>
+				<view class="content flex">
+					<u-icon  size="36" name="http://139.9.103.171:1888/miniofile/xlyq/jiance.png"></u-icon>
+					<text class="text-bold padding-left-10">监测值:</text>
+					<text class="text-bold" style="color: #1296db;padding-top: 6rpx;">{{item.monitorValue}}</text>
+				</view>
+				<view class="content flex">
+					<u-icon  size="36" name="http://139.9.103.171:1888/miniofile/xlyq/updateTime2.png"></u-icon>
+					<text class="text-bold padding-left-10">更新时间:</text>
+					<text style="padding-top: 6rpx;">{{item.lastUpdateTime}}</text>
+				</view>
 			</view>
-		</view>
-		<view class="auth">
-			<text class="cuIcon-titles text-blue padding-right-10"></text>
-			<text>企业认证信息</text>
-			<view class="card">
-				<u-form :model="model"  ref="uForm" >
-					<u-form-item  :label-width="labelWidth"   label="公司园区" >
-						<u-input  type="select" :select-open="ganderSelectShow" v-model="model.gander" placeholder="请选择工作园区" @click="ganderSelectShow = true"></u-input>
-					</u-form-item>
-					<u-form-item  :label-width="labelWidth"   label="所属企业" >
-						<u-input  type="select" :select-open="companySelectShow" v-model="model.company" placeholder="请选择所属企业" @click="companySelectShow = true"></u-input>
-					</u-form-item>
-					<u-form-item  :label-width="labelWidth"  label="工作岗位" >
-						<u-input  placeholder="请输入工作岗位" v-model="model.jobs" type="text"></u-input>
-					</u-form-item>
-					<u-form-item  :label-width="labelWidth"  label="企业联系人" >
-						<u-input  placeholder="请输入企业联系人" v-model="model.contact" type="text"></u-input>
-					</u-form-item>
-				</u-form>
+			<view class="bottom" >
+				<view class="cu-btn round sm line-blue" >
+					查看详情
+				</view>
+				<view class="cu-btn round sm line-blue" style="margin: 0 20rpx;">
+					告警记录
+				</view>
 			</view>
 		</view>
-		<view class="cu-btn bg-blue round flex" style="padding: 40rpx 0;margin-top: 40rpx;">
-			提交
-		</view>
-		<u-action-sheet :list="sexSelectList" v-model="sexSelectShow" @click="sexSelectCallback"></u-action-sheet>
-		<u-action-sheet :list="ganderSelectList" v-model="ganderSelectShow" @click="ganderSelectCallback"></u-action-sheet>
-		<u-action-sheet :list="companySelectList" v-model="companySelectShow" @click="companySelectCallback"></u-action-sheet>
-		<u-action-sheet @click="faceSelectCallback"  z-index="999999" :list="faceSelectList" v-model="faceSelectShow"></u-action-sheet>
+		<u-divider height="100" bgColor="#f1f1f1">到底了</u-divider>
 	</view>
 </template>
-
 <script>
-	import uploadImg from '@/components/uploadimg/uploadImg.vue'
-	let that = this;
-	export default {
-		components:{
-		   uploadImg
-		},
-		data() {
-			return {
-				labelWidth:'200',
-				model: {
-					name: '',
-					sex: '',
-					phone: '',
-					idcard:'',
-					gander:'',
-					company:'',
-					jobs:'',
-					contact:'',
-					face_image:''
-				},
-				//图片回显
-				face_url:'',
-				//性别
-				sexSelectList: [{text: '男'},{text: '女'}],
-				sexSelectShow: false,
-				//园区
-				ganderSelectList: [],
-				ganderSelectShow: false,
-				//企业
-				companySelectList: [],
-				companySelectShow: false,
-				//上传人脸的方式
-				faceSelectList: [{
-					text: '相册上传',
-				}, {
-					text: '拍照上传'
-				}],
-				faceSelectShow:false,
-				//验证码
-				codeTips: '',
-				
-			}
-		},
-		onLoad() {
-			this.fetchStaticData()
-			this.initData()
-		},
-		methods: {
-			initData(){
-				if (!this.$isEmpty(this.$cache.get('phone'))) {
-					this.model.phone=this.$cache.get('phone')
-				}
-			},
-			//性别
-			sexSelectCallback(index) {
-				uni.hideKeyboard();
-				this.model.sex = this.sexSelectList[index].text;
-			},
-			//园区
-			ganderSelectCallback(index) {
-				uni.hideKeyboard();
-				this.model.gander = this.ganderSelectList[index].text;
-			},
-			//公司
-			companySelectCallback(index) {
-				uni.hideKeyboard();
-				this.model.company = this.companySelectList[index].text;
-			},
-			faceSelectCallback(index){
-				if (index==0) {
-					//图片上传
-					this.chooseImage()
-				} else if(index==1){
-					//拍照上传
-					uni.navigateTo({
-						url:"/pages/my-camera/my-camera"
-					})
-				}
-			},
-			//点击上传图片事件
-			chooseImage: function () {
-			  uni.chooseImage({
-			    count: 1,
-			    //最多可以选择的图片张数,默认9
-			    sourceType: ['album', 'camera'],
-			    sizeType: ['compressed'],
-			    //可选择原图或压缩后的图片
-			    success: res => {
-			      that.handelImg(res.tempFilePaths[0])
-			    }
-			  });
-			},
-			//处理照片,拍照上传和相册上传的共同处理方法
-			handelImg(imgUrl){
-				that.face_url=imgUrl
-				uni.getFileSystemManager().readFile({
-				  filePath: imgUrl,
-				  //选择图片返回的相对路径
-				  encoding: 'base64',
-				  //编码格式
-				  success: res => {
-					  //图片的base64字符串
-					  that.face_image='data:image/jpeg;base64,' + res.data
-				  }
-				});
-			},
-			fetchStaticData(){
-				this.ganderSelectList=[
-					{
-						text: '新邻园区1'
-					},
-					{
-						text: '新邻园区2'
-					}
-				]
-				this.companySelectList=[
-					{
-						text: '新邻企业1'
-					},
+export default {
+	name: 'card',
+	props:{
+		list:{
+			type:Array,
+			default:()=>{
+				[
 					{
-						text: '新邻企业2'
+						id:'1337708672083709954',
+						imei:'861878041367316',
+						defenseStatus:'1',
+						deviceName:'无线智能消防栓检测终端',
+						battery:'52',
+						signalIntensity:'90',
+						monitorValue:'0.0009Mpa',
+						lastUpdateTime:'2020-12-19 15:51:45'
 					}
 				]
 			}
 		}
+	},
+	data() {
+		return {
+			
+		};
+	},
+	onLoad() {
+		
+	},
+	methods:{
+		copy(data){
+			uni.setClipboardData({
+				data:data
+			})
+		},
+		goDetail(item){
+			uni.navigateTo({
+				
+			})
+		},
 	}
+};
 </script>
 
 <style lang="scss">
-	page{
-		background-color: #FFFFFF;
-	}
-	.auth{
-		padding: 40rpx 10rpx;
-		.card{
-			margin-top: 20rpx;
-			padding: 0 30rpx;
-			box-sizing: border-box;
-			border-radius: 12rpx;
-			box-shadow: 0 -10rpx rgba(248, 248, 248,.9) ,0 10rpx rgba(248, 248, 248,.9) , -10rpx 0rpx rgba(248, 248, 248,.9) ,10rpx 0rpx rgba(248, 248, 248,.9);
-			.item{
-				padding:30rpx 0;
-				display: flex;
-				justify-content: space-between;
+.bg-blue{
+	background-color: #59a5f0;
+	color: #FFFFFF;
+}
+
+	
+.data {
+	width: 720rpx;
+	background-color: #ffffff;
+	margin: 20rpx auto;
+	border-radius: 6rpx;
+	box-sizing: border-box;
+	padding: 20rpx;
+	font-size: 28rpx;
+	.top {
+		display: flex;
+		justify-content: space-between;
+		padding-bottom: 20rpx;
+		border-bottom: 1rpx dashed #DDDDDD;
+		.left {
+			display: flex;
+			align-items: center;
+			.title {
+				margin: 0 10rpx;
+				font-size: 30rpx;
 			}
 		}
+		.right{
+			margin-right: 10rpx;
+		}
+	}
+	.item {
+		margin: 5rpx 0 20rpx 0;
+		.content {
+			padding: 30rpx 0 ;
+			border-bottom: 1rpx dashed  #DDDDDD;
+		}
+	}
+	.bottom {
+		display: flex;
+		padding-top: 10rpx;
+		margin-top: 30rpx;
+		justify-content: flex-end;
+		align-items: center;
 	}
+}
 </style>

+ 145 - 11
pages/test/test1.vue

@@ -1,24 +1,158 @@
 <template>
-	
+	<view class="">
+		<view class="data" v-for="(item, index) in list" :key="index">
+			<view class="top">
+				<view class="left">
+					<view class="title ">
+						<text class="text-bold">IEMI:</text>
+						<text>{{item.imei}}</text>
+						<text @click="copy(item.imei)" style="text-decoration: underline;padding-left: 10rpx;color: #007AFF;">复制</text>
+					</view>
+				</view>
+				<view class="right">
+					<view v-if="item.defenseStatus==1" class="radius sm cu-btn line-green">
+						已上线
+					</view>
+					<view v-if="item.defenseStatus==2" class="radius sm cu-btn line-red">
+						已下线
+					</view>
+				</view>
+			</view>
+			<view class="item  padding-left-20">
+				<view class="content flex">
+					<u-icon  size="36" name="http://139.9.103.171:1888/miniofile/xlyq/xiaofangshuang.png"></u-icon>
+					<text class="text-bold padding-left-10">设备名称:</text>
+					<text>{{item.deviceName}}</text>
+				</view>
+				<view class="content flex">
+					<view class="flex">
+						<u-icon  size="36" name="http://139.9.103.171:1888/miniofile/xlyq/dianchi.png"></u-icon>
+						<text class="text-bold padding-left-10">电池电量:</text>
+					</view>
+					<view class="striped active cu-progress round margin-top-sm sm" style="width: 60%;">
+						<view style="width: 68%;background-color: #16c60c;color: #FFFFFF;">
+							{{item.battery?item.battery:0}}%
+						</view>
+					</view>
+				</view>
+				<view class="content flex">
+					<view class="flex">
+						<u-icon  size="36" name="http://139.9.103.171:1888/miniofile/xlyq/wifi.png"></u-icon>
+						<text class="text-bold padding-left-10">信号强弱:</text>
+					</view>
+					<view class="striped active cu-progress round margin-top-sm sm" style="width: 60%;">
+						<view style="width: 58%;background-color: #fc5f44;color: #FFFFFF;">
+							{{item.signalIntensity?item.signalIntensity:0}}%
+						</view>
+					</view>
+				</view>
+				<view class="content flex">
+					<u-icon  size="36" name="http://139.9.103.171:1888/miniofile/xlyq/jiance.png"></u-icon>
+					<text class="text-bold padding-left-10">监测值:</text>
+					<text class="text-bold" style="color: #1296db;padding-top: 6rpx;">{{item.monitorValue}}</text>
+				</view>
+				<view class="content flex">
+					<u-icon  size="36" name="http://139.9.103.171:1888/miniofile/xlyq/updateTime2.png"></u-icon>
+					<text class="text-bold padding-left-10">更新时间:</text>
+					<text style="padding-top: 6rpx;">{{item.lastUpdateTime}}</text>
+				</view>
+			</view>
+			<view class="bottom" >
+				<view class="cu-btn round sm line-blue" >
+					查看详情
+				</view>
+				<view class="cu-btn round sm line-blue" style="margin: 0 20rpx;">
+					告警记录
+				</view>
+			</view>
+		</view>
+		<u-divider height="100" bgColor="#f1f1f1">到底了</u-divider>
+	</view>
 </template>
-
 <script>
 export default {
-	name: '',
+	name: 'card',
+	props:{
+		list:{
+			type:Array,
+			default:()=>{
+				[
+					// {
+					// 	id:'1337708672083709954',
+					// 	imei:'861878041367316',
+					// 	defenseStatus:'1',
+					// 	deviceName:'无线智能消防栓检测终端',
+					// 	battery:'52',
+					// 	signalIntensity:'90',
+					// 	monitorValue:'0.0009Mpa',
+					// 	lastUpdateTime:'2020-12-19 15:51:45'
+					// }
+				]
+			}
+		}
+	},
 	data() {
 		return {
-			
 		};
-	},
-	onLoad() {
-		
 	},
 	methods:{
-		
+		copy(data){
+			uni.setClipboardData({
+				data:data
+			})
+		},
+		goDetail(item){
+			uni.navigateTo({
+				
+			})
+		},
 	}
 };
 </script>
 
-<style lang="scss" scoped>
-	
-</style>
+<style lang="scss">
+.bg-blue{
+	background-color: #59a5f0;
+	color: #FFFFFF;
+}
+.data {
+	width: 720rpx;
+	background-color: #ffffff;
+	margin: 20rpx auto;
+	border-radius: 6rpx;
+	box-sizing: border-box;
+	padding: 20rpx;
+	font-size: 28rpx;
+	.top {
+		display: flex;
+		justify-content: space-between;
+		padding-bottom: 20rpx;
+		border-bottom: 1rpx dashed #DDDDDD;
+		.left {
+			display: flex;
+			align-items: center;
+			.title {
+				margin: 0 10rpx;
+				font-size: 30rpx;
+			}
+		}
+		.right{
+			margin-right: 10rpx;
+		}
+	}
+	.item {
+		margin: 5rpx 0 20rpx 0;
+		.content {
+			padding: 30rpx 0 ;
+			border-bottom: 1rpx dashed  #DDDDDD;
+		}
+	}
+	.bottom {
+		display: flex;
+		padding-top: 10rpx;
+		margin-top: 30rpx;
+		justify-content: flex-end;
+		align-items: center;
+	}
+}
+</style>

+ 8 - 1
readme.md

@@ -1,2 +1,9 @@
 npm install uni-simple-router
-npm install uni-read-pages
+npm install uni-read-pages
+
+新增的页面需要加入name字段,可以让路由识别
+使用path跳转需要完整的路径(/pages/),而不是相对路径
+
+不需要登陆就可以访问的页面需要配置到router.js的WHiTE_LIST白名单上
+员工的页面需要配置到router.js 的ENTERPRISE列表上,不让员工权限的用户访问到
+企业的页面需要配置到router.js 的STAFF列表上,不让企业权限的用户访问到

+ 83 - 10
router.js

@@ -5,7 +5,20 @@ import {
 } from 'uni-simple-router';
 
 //配置白名单
-let WHiTE_LIST=['login']
+let WHiTE_LIST=['login','my-camera']
+
+//员工页面,只可以员工权限的用户访问
+let STAFF=['auth','editPhone']
+
+//企业页面,只可以企业权限的用户访问
+let ENTERPRISE=['staffAuditList',
+				'staffAuditDetail',
+				'staff-temperature',
+				'smoke-alarm',
+				'gas-alarm',
+				'fire-hydrant',
+				'changePass'
+				]
 
 let first = null;
 const router = createRouter({
@@ -17,21 +30,79 @@ const router = createRouter({
 		}
 	},
 	routerBeforeEach:(to, from, next) => {
+		//如果是白名单的页面,直接放行
 		if (WHiTE_LIST.includes(to.name)) {
 			next();
-		}else{
-			//拦截未登录页面并跳转到登录页面
-			let phone =uni.getStorageSync("phone")
-			console.log(phone);
-			if (phone) {
-				next();
+			return
+		}
+		//如果是从登陆页进入的认证页,放行;若是从其他页面进入的认证页,则需要验证是否登陆验证
+		if (from.name=='login'&&to.name=='auth') {
+			next()
+			return
+		}
+		
+		//拦截类型,方便提示
+		//0:未登陆认证拦截
+		//1:允许通行
+		//2:权限不足拦截(员工类型访问企业类型的页面)
+		//3: 权限不足拦截(企业类型访问员工类型的页面)
+		let flag=0
+		//登陆类型
+		let loginType =uni.getStorageSync("loginType")
+		
+		if (loginType) {
+			if (loginType=='staff'&&ENTERPRISE.includes(to.name)) {
+				//员工类型访问企业类型的页面,拦截
+				flag=2
+			}else if(loginType=='enterprise'&&STAFF.includes(to.name)){
+				//企业类型访问员工类型的页面,拦截
+				flag=3
 			}else{
+				//允许通行
+				flag=1
+			}
+		}else{
+			flag=0
+		}
+		switch (flag){
+			case 0: //未登录认证
 				next({
-				    name: 'login',
+					name: 'login',
 					 params: { fullPath: to.fullPath },
-				    NAVTYPE: 'replaceAll'
+					NAVTYPE: 'replaceAll'
 				});
-			}
+				break;
+			case 1://允许通行
+				next()
+				break;
+			case 2: //员工类型访问企业类型的页面
+				uni.showModal({
+					title: '提示',
+					content: '权限不足,员工类型无法访问企业类型的页面',
+					showCancel:false,
+					success: (res)=>{
+						next({
+							name: 'index',
+							NAVTYPE: 'pushTab'
+						});
+					}
+				});
+				break;
+			case 3://企业类型访问员工类型的页面
+				uni.showModal({
+					title: '提示',
+					content: '权限不足,企业类型无法访问员工类型的页面',
+					showCancel:false,
+					success: (res)=>{
+						next({
+							name: 'index',
+							NAVTYPE: 'pushTab'
+						});
+					}
+				});
+				break;
+			default:
+				break;
 		}
 	},
 	routerAfterEach:(to, from) => {
@@ -58,6 +129,8 @@ const router = createRouter({
 });
 let count=0;
 router.beforeEach((to, from, next) => {
+	console.log("from:",from)
+	console.log("to:",to)
 	// if(count==0){
 	// 	next({
 	// 		path:'/pages/login/login',

BIN
static/empty.png


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 2 - 0
static/iconfont.css


BIN
static/index/blue/change-phone.png


BIN
static/index/blue/changePsw.png


BIN
static/index/blue/dingyue.png


BIN
static/index/call.png


BIN
static/index/fire/dianchi.png


BIN
static/index/fire/ranqi2.png


BIN
static/index/fire/yanwu.png


BIN
static/index/fire/yanwu1.png


BIN
static/login/qiye1.png


BIN
static/login/xinyong1.png


BIN
static/tarbar/home.png


BIN
static/tarbar/home0.png


BIN
static/tarbar/index.png


BIN
static/tarbar/index0.png


BIN
static/tarbar/menjin.png


BIN
static/tarbar/menjin0.png


BIN
static/tarbar/my.png


BIN
static/tarbar/my0.png


BIN
static/tarbar/news.png


BIN
static/tarbar/news0.png


+ 4 - 0
uni.scss

@@ -20,6 +20,10 @@ $uni-color-success: #4cd964;
 $uni-color-warning: #f0ad4e;
 $uni-color-error: #dd524d;
 
+/* 边框颜色 */
+$dt-border-color:#c8c7cc;
+$dt-border-color-sm:#dedede;
+
 /* 文字基本颜色 */
 $uni-text-color:#333;//基本色
 $uni-text-color-inverse:#fff;//反色

+ 243 - 0
utils/md5.js

@@ -0,0 +1,243 @@
+/*
+ * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
+ * Digest Algorithm, as defined in RFC 1321.
+ * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009
+ * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
+ * Distributed under the BSD License
+ * See http://pajhome.org.uk/crypt/md5 for more info.
+ */
+module.exports = {
+  hex_md5: hex_md5
+}; //上面三行是小程序引入时需要的
+
+var hexcase = 0;
+
+function hex_md5(a) {
+  return rstr2hex(rstr_md5(str2rstr_utf8(a)));
+}
+
+function hex_hmac_md5(a, b) {
+  return rstr2hex(rstr_hmac_md5(str2rstr_utf8(a), str2rstr_utf8(b)));
+}
+
+function md5_vm_test() {
+  return hex_md5("abc").toLowerCase() == "900150983cd24fb0d6963f7d28e17f72";
+}
+
+function rstr_md5(a) {
+  return binl2rstr(binl_md5(rstr2binl(a), a.length * 8));
+}
+
+function rstr_hmac_md5(c, f) {
+  var e = rstr2binl(c);
+
+  if (e.length > 16) {
+    e = binl_md5(e, c.length * 8);
+  }
+
+  var a = Array(16),
+      d = Array(16);
+
+  for (var b = 0; b < 16; b++) {
+    a[b] = e[b] ^ 909522486;
+    d[b] = e[b] ^ 1549556828;
+  }
+
+  var g = binl_md5(a.concat(rstr2binl(f)), 512 + f.length * 8);
+  return binl2rstr(binl_md5(d.concat(g), 512 + 128));
+}
+
+function rstr2hex(c) {
+  try {
+    hexcase;
+  } catch (g) {
+    hexcase = 0;
+  }
+
+  var f = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
+  var b = "";
+  var a;
+
+  for (var d = 0; d < c.length; d++) {
+    a = c.charCodeAt(d);
+    b += f.charAt(a >>> 4 & 15) + f.charAt(a & 15);
+  }
+
+  return b;
+}
+
+function str2rstr_utf8(c) {
+  var b = "";
+  var d = -1;
+  var a, e;
+
+  while (++d < c.length) {
+    a = c.charCodeAt(d);
+    e = d + 1 < c.length ? c.charCodeAt(d + 1) : 0;
+
+    if (55296 <= a && a <= 56319 && 56320 <= e && e <= 57343) {
+      a = 65536 + ((a & 1023) << 10) + (e & 1023);
+      d++;
+    }
+
+    if (a <= 127) {
+      b += String.fromCharCode(a);
+    } else {
+      if (a <= 2047) {
+        b += String.fromCharCode(192 | a >>> 6 & 31, 128 | a & 63);
+      } else {
+        if (a <= 65535) {
+          b += String.fromCharCode(224 | a >>> 12 & 15, 128 | a >>> 6 & 63, 128 | a & 63);
+        } else {
+          if (a <= 2097151) {
+            b += String.fromCharCode(240 | a >>> 18 & 7, 128 | a >>> 12 & 63, 128 | a >>> 6 & 63, 128 | a & 63);
+          }
+        }
+      }
+    }
+  }
+
+  return b;
+}
+
+function rstr2binl(b) {
+  var a = Array(b.length >> 2);
+
+  for (var c = 0; c < a.length; c++) {
+    a[c] = 0;
+  }
+
+  for (var c = 0; c < b.length * 8; c += 8) {
+    a[c >> 5] |= (b.charCodeAt(c / 8) & 255) << c % 32;
+  }
+
+  return a;
+}
+
+function binl2rstr(b) {
+  var a = "";
+
+  for (var c = 0; c < b.length * 32; c += 8) {
+    a += String.fromCharCode(b[c >> 5] >>> c % 32 & 255);
+  }
+
+  return a;
+}
+
+function binl_md5(p, k) {
+  p[k >> 5] |= 128 << k % 32;
+  p[(k + 64 >>> 9 << 4) + 14] = k;
+  var o = 1732584193;
+  var n = -271733879;
+  var m = -1732584194;
+  var l = 271733878;
+
+  for (var g = 0; g < p.length; g += 16) {
+    var j = o;
+    var h = n;
+    var f = m;
+    var e = l;
+    o = md5_ff(o, n, m, l, p[g + 0], 7, -680876936);
+    l = md5_ff(l, o, n, m, p[g + 1], 12, -389564586);
+    m = md5_ff(m, l, o, n, p[g + 2], 17, 606105819);
+    n = md5_ff(n, m, l, o, p[g + 3], 22, -1044525330);
+    o = md5_ff(o, n, m, l, p[g + 4], 7, -176418897);
+    l = md5_ff(l, o, n, m, p[g + 5], 12, 1200080426);
+    m = md5_ff(m, l, o, n, p[g + 6], 17, -1473231341);
+    n = md5_ff(n, m, l, o, p[g + 7], 22, -45705983);
+    o = md5_ff(o, n, m, l, p[g + 8], 7, 1770035416);
+    l = md5_ff(l, o, n, m, p[g + 9], 12, -1958414417);
+    m = md5_ff(m, l, o, n, p[g + 10], 17, -42063);
+    n = md5_ff(n, m, l, o, p[g + 11], 22, -1990404162);
+    o = md5_ff(o, n, m, l, p[g + 12], 7, 1804603682);
+    l = md5_ff(l, o, n, m, p[g + 13], 12, -40341101);
+    m = md5_ff(m, l, o, n, p[g + 14], 17, -1502002290);
+    n = md5_ff(n, m, l, o, p[g + 15], 22, 1236535329);
+    o = md5_gg(o, n, m, l, p[g + 1], 5, -165796510);
+    l = md5_gg(l, o, n, m, p[g + 6], 9, -1069501632);
+    m = md5_gg(m, l, o, n, p[g + 11], 14, 643717713);
+    n = md5_gg(n, m, l, o, p[g + 0], 20, -373897302);
+    o = md5_gg(o, n, m, l, p[g + 5], 5, -701558691);
+    l = md5_gg(l, o, n, m, p[g + 10], 9, 38016083);
+    m = md5_gg(m, l, o, n, p[g + 15], 14, -660478335);
+    n = md5_gg(n, m, l, o, p[g + 4], 20, -405537848);
+    o = md5_gg(o, n, m, l, p[g + 9], 5, 568446438);
+    l = md5_gg(l, o, n, m, p[g + 14], 9, -1019803690);
+    m = md5_gg(m, l, o, n, p[g + 3], 14, -187363961);
+    n = md5_gg(n, m, l, o, p[g + 8], 20, 1163531501);
+    o = md5_gg(o, n, m, l, p[g + 13], 5, -1444681467);
+    l = md5_gg(l, o, n, m, p[g + 2], 9, -51403784);
+    m = md5_gg(m, l, o, n, p[g + 7], 14, 1735328473);
+    n = md5_gg(n, m, l, o, p[g + 12], 20, -1926607734);
+    o = md5_hh(o, n, m, l, p[g + 5], 4, -378558);
+    l = md5_hh(l, o, n, m, p[g + 8], 11, -2022574463);
+    m = md5_hh(m, l, o, n, p[g + 11], 16, 1839030562);
+    n = md5_hh(n, m, l, o, p[g + 14], 23, -35309556);
+    o = md5_hh(o, n, m, l, p[g + 1], 4, -1530992060);
+    l = md5_hh(l, o, n, m, p[g + 4], 11, 1272893353);
+    m = md5_hh(m, l, o, n, p[g + 7], 16, -155497632);
+    n = md5_hh(n, m, l, o, p[g + 10], 23, -1094730640);
+    o = md5_hh(o, n, m, l, p[g + 13], 4, 681279174);
+    l = md5_hh(l, o, n, m, p[g + 0], 11, -358537222);
+    m = md5_hh(m, l, o, n, p[g + 3], 16, -722521979);
+    n = md5_hh(n, m, l, o, p[g + 6], 23, 76029189);
+    o = md5_hh(o, n, m, l, p[g + 9], 4, -640364487);
+    l = md5_hh(l, o, n, m, p[g + 12], 11, -421815835);
+    m = md5_hh(m, l, o, n, p[g + 15], 16, 530742520);
+    n = md5_hh(n, m, l, o, p[g + 2], 23, -995338651);
+    o = md5_ii(o, n, m, l, p[g + 0], 6, -198630844);
+    l = md5_ii(l, o, n, m, p[g + 7], 10, 1126891415);
+    m = md5_ii(m, l, o, n, p[g + 14], 15, -1416354905);
+    n = md5_ii(n, m, l, o, p[g + 5], 21, -57434055);
+    o = md5_ii(o, n, m, l, p[g + 12], 6, 1700485571);
+    l = md5_ii(l, o, n, m, p[g + 3], 10, -1894986606);
+    m = md5_ii(m, l, o, n, p[g + 10], 15, -1051523);
+    n = md5_ii(n, m, l, o, p[g + 1], 21, -2054922799);
+    o = md5_ii(o, n, m, l, p[g + 8], 6, 1873313359);
+    l = md5_ii(l, o, n, m, p[g + 15], 10, -30611744);
+    m = md5_ii(m, l, o, n, p[g + 6], 15, -1560198380);
+    n = md5_ii(n, m, l, o, p[g + 13], 21, 1309151649);
+    o = md5_ii(o, n, m, l, p[g + 4], 6, -145523070);
+    l = md5_ii(l, o, n, m, p[g + 11], 10, -1120210379);
+    m = md5_ii(m, l, o, n, p[g + 2], 15, 718787259);
+    n = md5_ii(n, m, l, o, p[g + 9], 21, -343485551);
+    o = safe_add(o, j);
+    n = safe_add(n, h);
+    m = safe_add(m, f);
+    l = safe_add(l, e);
+  }
+
+  return Array(o, n, m, l);
+}
+
+function md5_cmn(h, e, d, c, g, f) {
+  return safe_add(bit_rol(safe_add(safe_add(e, h), safe_add(c, f)), g), d);
+}
+
+function md5_ff(g, f, k, j, e, i, h) {
+  return md5_cmn(f & k | ~f & j, g, f, e, i, h);
+}
+
+function md5_gg(g, f, k, j, e, i, h) {
+  return md5_cmn(f & j | k & ~j, g, f, e, i, h);
+}
+
+function md5_hh(g, f, k, j, e, i, h) {
+  return md5_cmn(f ^ k ^ j, g, f, e, i, h);
+}
+
+function md5_ii(g, f, k, j, e, i, h) {
+  return md5_cmn(k ^ (f | ~j), g, f, e, i, h);
+}
+
+function safe_add(a, d) {
+  var c = (a & 65535) + (d & 65535);
+  var b = (a >> 16) + (d >> 16) + (c >> 16);
+  return b << 16 | c & 65535;
+}
+
+function bit_rol(a, b) {
+  return a << b | a >>> 32 - b;
+}
+
+;

+ 1 - 14
utils/util.js

@@ -11,20 +11,7 @@ util.copy=content=>{
 // 图片缓存
 
 
-//拨打电话
-util.callPhone=phone=>{
-	uni.showModal({
-	    title: '提示',
-	    content: `确定要拨打电话:${phone}吗?`,
-	    success: function (res) {
-	        if (res.confirm) {
-				uni.makePhoneCall({
-				    phoneNumber: phone //仅为示例
-				});
-	        } 
-	    }
-	});
-}
+
 util.showModel=(content,title='提示')=>{
     return new Promise((resolve,reject)=>{
         uni.showModal({

+ 2 - 0
uview-ui/components/u-empty/u-empty.vue

@@ -10,6 +10,8 @@
 			:label-color="color"
 			:label-size="fontSize"
 			:size="iconSize"
+			width ="350"
+			height="240"
 			:color="iconColor"
 			margin-top="14"
 		></u-icon>

Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels