空白格 vor 3 Jahren
Commit
ecef133128
100 geänderte Dateien mit 11382 neuen und 0 gelöschten Zeilen
  1. BIN
      .DS_Store
  2. 16 0
      .hbuilderx/launch.json
  3. 18 0
      App.vue
  4. 14 0
      index.html
  5. 25 0
      main.js
  6. 72 0
      manifest.json
  7. 151 0
      pages.json
  8. 88 0
      pages/index/chooseRoad/chooseRoad.vue
  9. 135 0
      pages/index/clockIn/clockIn.vue
  10. 192 0
      pages/index/deviceDetails/deviceDetails.vue
  11. 175 0
      pages/index/deviceList/deviceList.vue
  12. 154 0
      pages/index/gateDetails/gateDetails.vue
  13. 186 0
      pages/index/geomagDetails/geomagDetails.vue
  14. 84 0
      pages/index/index.scss
  15. 118 0
      pages/index/index.vue
  16. 186 0
      pages/index/lockDetails/lockDetails.vue
  17. 47 0
      pages/index/messageCenter/messageCenter.scss
  18. 57 0
      pages/index/messageCenter/messageCenter.vue
  19. 32 0
      pages/login/login.scss
  20. 106 0
      pages/login/login.vue
  21. 93 0
      pages/mine/changePassword/changePassword.vue
  22. 31 0
      pages/mine/mine.scss
  23. 85 0
      pages/mine/mine.vue
  24. 121 0
      pages/mine/problemReporting/problemReporting.vue
  25. 120 0
      pages/ownerInquiry/ownerInquiry.vue
  26. 165 0
      pages/ownerInquiry/queryResults/queryResults.vue
  27. 1 0
      static/icons/chengguan-wu-bujian-gonggongsheshi-daoluxinxixianshiping.svg
  28. BIN
      static/icons/clock-in-icon.png
  29. BIN
      static/icons/config-icon.png
  30. 1 0
      static/icons/daozhakongzhi.svg
  31. BIN
      static/icons/geomagnetism-icon.png
  32. 31 0
      static/icons/geomagnetism-icon.svg
  33. 1 0
      static/icons/has-car-icon.svg
  34. 1 0
      static/icons/has-ele-icon.svg
  35. BIN
      static/icons/logout-icon.png
  36. 1 0
      static/icons/no-car-icon.svg
  37. 1 0
      static/icons/no-ele-icon.svg
  38. BIN
      static/icons/parking-lock-icon.png
  39. 40 0
      static/icons/parking-lock-icon.svg
  40. BIN
      static/icons/password-icon.png
  41. BIN
      static/icons/question-icon.png
  42. 40 0
      static/icons/railing-gate-icon.svg
  43. BIN
      static/icons/railing-icon.png
  44. BIN
      static/icons/road-icon.png
  45. 1 0
      static/icons/shipinjiankongyemian.svg
  46. BIN
      static/logo.png
  47. BIN
      static/tabbar/index-selected.png
  48. BIN
      static/tabbar/index.png
  49. BIN
      static/tabbar/mine-selected.png
  50. BIN
      static/tabbar/mine.png
  51. BIN
      static/tabbar/search-selected.png
  52. BIN
      static/tabbar/search.png
  53. 78 0
      uni.scss
  54. 24 0
      uni_modules/z-paging/changelog.md
  55. 186 0
      uni_modules/z-paging/components/z-paging-empty-view/z-paging-empty-view.vue
  56. 88 0
      uni_modules/z-paging/components/z-paging-swiper-item/z-paging-swiper-item.vue
  57. 126 0
      uni_modules/z-paging/components/z-paging-swiper/z-paging-swiper.vue
  58. 157 0
      uni_modules/z-paging/components/z-paging/components/z-paging-load-more.vue
  59. 284 0
      uni_modules/z-paging/components/z-paging/components/z-paging-refresh.vue
  60. 1 0
      uni_modules/z-paging/components/z-paging/config/index.js
  61. 185 0
      uni_modules/z-paging/components/z-paging/css/z-paging-main.css
  62. 33 0
      uni_modules/z-paging/components/z-paging/css/z-paging-static.css
  63. 28 0
      uni_modules/z-paging/components/z-paging/js/z-paging-config.js
  64. 25 0
      uni_modules/z-paging/components/z-paging/js/z-paging-enum.js
  65. 153 0
      uni_modules/z-paging/components/z-paging/js/z-paging-i18n.js
  66. 3120 0
      uni_modules/z-paging/components/z-paging/js/z-paging-main.js
  67. 32 0
      uni_modules/z-paging/components/z-paging/js/z-paging-mixin.js
  68. 23 0
      uni_modules/z-paging/components/z-paging/js/z-paging-static.js
  69. 199 0
      uni_modules/z-paging/components/z-paging/js/z-paging-utils.js
  70. 54 0
      uni_modules/z-paging/components/z-paging/wxs/z-paging-renderjs.js
  71. 342 0
      uni_modules/z-paging/components/z-paging/wxs/z-paging-wxs.wxs
  72. 519 0
      uni_modules/z-paging/components/z-paging/z-paging.vue
  73. 84 0
      uni_modules/z-paging/package.json
  74. 46 0
      uni_modules/z-paging/readme.md
  75. 21 0
      uview-ui/LICENSE
  76. 105 0
      uview-ui/README.md
  77. 139 0
      uview-ui/changelog.md
  78. 78 0
      uview-ui/components/u--form/u--form.vue
  79. 40 0
      uview-ui/components/u--image/u--image.vue
  80. 72 0
      uview-ui/components/u--input/u--input.vue
  81. 43 0
      uview-ui/components/u--text/u--text.vue
  82. 47 0
      uview-ui/components/u--textarea/u--textarea.vue
  83. 54 0
      uview-ui/components/u-action-sheet/props.js
  84. 275 0
      uview-ui/components/u-action-sheet/u-action-sheet.vue
  85. 59 0
      uview-ui/components/u-album/props.js
  86. 259 0
      uview-ui/components/u-album/u-album.vue
  87. 44 0
      uview-ui/components/u-alert/props.js
  88. 243 0
      uview-ui/components/u-alert/u-alert.vue
  89. 52 0
      uview-ui/components/u-avatar-group/props.js
  90. 103 0
      uview-ui/components/u-avatar-group/u-avatar-group.vue
  91. 78 0
      uview-ui/components/u-avatar/props.js
  92. 163 0
      uview-ui/components/u-avatar/u-avatar.vue
  93. 54 0
      uview-ui/components/u-back-top/props.js
  94. 137 0
      uview-ui/components/u-back-top/u-back-top.vue
  95. 72 0
      uview-ui/components/u-badge/props.js
  96. 171 0
      uview-ui/components/u-badge/u-badge.vue
  97. 46 0
      uview-ui/components/u-button/nvue.scss
  98. 161 0
      uview-ui/components/u-button/props.js
  99. 490 0
      uview-ui/components/u-button/u-button.vue
  100. 0 0
      uview-ui/components/u-button/vue.scss

BIN
.DS_Store


+ 16 - 0
.hbuilderx/launch.json

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

+ 18 - 0
App.vue

@@ -0,0 +1,18 @@
+<script>
+	export default {
+		onLaunch: function() {
+			console.log('App Launch')
+		},
+		onShow: function() {
+			console.log('App Show')
+		},
+		onHide: function() {
+			console.log('App Hide')
+		}
+	}
+</script>
+
+<style lang="scss">
+	/*每个页面公共css */
+	@import "@/uview-ui/index.scss";
+</style>

+ 14 - 0
index.html

@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0" />
+    <title></title>
+    <!--preload-links-->
+    <!--app-context-->
+  </head>
+  <body>
+    <div id="app"><!--app-html--></div>
+    <script type="module" src="/main.js"></script>
+  </body>
+</html>

+ 25 - 0
main.js

@@ -0,0 +1,25 @@
+import App from './App'
+import uView from 'uview-ui'
+Vue.use(uView)
+
+// #ifndef VUE3
+import Vue from 'vue'
+Vue.config.productionTip = false
+App.mpType = 'app'
+const app = new Vue({
+	...App
+})
+app.$mount()
+// #endif
+
+// #ifdef VUE3
+import {
+	createSSRApp
+} from 'vue'
+export function createApp() {
+	const app = createSSRApp(App)
+	return {
+		app
+	}
+}
+// #endif

+ 72 - 0
manifest.json

@@ -0,0 +1,72 @@
+{
+    "name" : "停车巡检端",
+    "appid" : "__UNI__650D1C3",
+    "description" : "智慧停车巡检端",
+    "versionName" : "1.0.0",
+    "versionCode" : "100",
+    "transformPx" : false,
+    /* 5+App特有相关 */
+    "app-plus" : {
+        "usingComponents" : true,
+        "nvueStyleCompiler" : "uni-app",
+        "compilerVersion" : 3,
+        "splashscreen" : {
+            "alwaysShowBeforeRender" : true,
+            "waiting" : true,
+            "autoclose" : true,
+            "delay" : 0
+        },
+        /* 模块配置 */
+        "modules" : {},
+        /* 应用发布信息 */
+        "distribute" : {
+            /* android打包配置 */
+            "android" : {
+                "permissions" : [
+                    "<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
+                    "<uses-permission android:name=\"android.permission.VIBRATE\"/>",
+                    "<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
+                    "<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.CAMERA\"/>",
+                    "<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
+                    "<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
+                    "<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
+                    "<uses-feature android:name=\"android.hardware.camera\"/>",
+                    "<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
+                ]
+            },
+            /* ios打包配置 */
+            "ios" : {},
+            /* SDK配置 */
+            "sdkConfigs" : {}
+        }
+    },
+    /* 快应用特有相关 */
+    "quickapp" : {},
+    /* 小程序特有相关 */
+    "mp-weixin" : {
+        "appid" : "",
+        "setting" : {
+            "urlCheck" : false
+        },
+        "usingComponents" : true
+    },
+    "mp-alipay" : {
+        "usingComponents" : true
+    },
+    "mp-baidu" : {
+        "usingComponents" : true
+    },
+    "mp-toutiao" : {
+        "usingComponents" : true
+    },
+    "uniStatistics" : {
+        "enable" : false
+    },
+    "vueVersion" : "2"
+}

+ 151 - 0
pages.json

@@ -0,0 +1,151 @@
+{
+	"easycom": {
+		"^u-(.*)": "@/uview-ui/components/u-$1/u-$1.vue"
+	},
+	"pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
+		{
+			"path": "pages/index/index",
+			"style": {
+				"navigationBarTitleText": "停车巡检端",
+				"navigationStyle": "custom"
+			}
+		}, {
+			"path": "pages/login/login",
+			"style": {
+				"navigationBarTitleText": "登录",
+				"navigationStyle": "custom",
+				"enablePullDownRefresh": false
+			}
+
+		}, {
+			"path": "pages/ownerInquiry/ownerInquiry",
+			"style": {
+				"navigationBarTitleText": "车主查询",
+				"enablePullDownRefresh": false,
+				"navigationStyle": "custom"
+			}
+
+		}, {
+			"path": "pages/mine/mine",
+			"style": {
+				"navigationBarTitleText": "我的",
+				"enablePullDownRefresh": false,
+				"navigationStyle": "custom"
+			}
+
+		}, {
+			"path": "pages/index/messageCenter/messageCenter",
+			"style": {
+				"navigationBarTitleText": "消息中心",
+				"enablePullDownRefresh": false,
+				"navigationBarBackgroundColor": "#5290FF",
+				"navigationBarTextStyle": "white"
+			}
+
+		}, {
+			"path": "pages/mine/problemReporting/problemReporting",
+			"style": {
+				"navigationBarTitleText": "问题上报",
+				"enablePullDownRefresh": false
+			}
+
+		}, {
+			"path": "pages/mine/changePassword/changePassword",
+			"style": {
+				"navigationBarTitleText": "修改密码",
+				"enablePullDownRefresh": false
+			}
+
+		}, {
+			"path": "pages/ownerInquiry/queryResults/queryResults",
+			"style": {
+				"navigationBarTitleText": "查询结果",
+				"enablePullDownRefresh": false
+			}
+
+		}, {
+			"path": "pages/index/deviceList/deviceList",
+			"style": {
+				"navigationBarTitleText": "全部设备",
+				"enablePullDownRefresh": false,
+				"navigationBarBackgroundColor": "#5290FF",
+				"navigationBarTextStyle": "white"
+			}
+
+		}, {
+			"path": "pages/index/chooseRoad/chooseRoad",
+			"style": {
+				"navigationBarTitleText": "选择路段",
+				"enablePullDownRefresh": false,
+				"navigationBarBackgroundColor": "#5290FF",
+				"navigationBarTextStyle": "white"
+			}
+
+		}, {
+			"path": "pages/index/geomagDetails/geomagDetails",
+			"style": {
+				"navigationBarTitleText": "地磁",
+				"enablePullDownRefresh": false
+			}
+
+		}, {
+			"path": "pages/index/lockDetails/lockDetails",
+			"style": {
+				"navigationBarTitleText": "车位锁",
+				"enablePullDownRefresh": false
+			}
+
+		}, {
+			"path": "pages/index/gateDetails/gateDetails",
+			"style": {
+				"navigationBarTitleText": "栏杆道闸",
+				"enablePullDownRefresh": false
+			}
+
+		}, {
+			"path": "pages/index/clockIn/clockIn",
+			"style": {
+				"navigationBarTitleText": "打卡",
+				"enablePullDownRefresh": false,
+				"navigationBarBackgroundColor": "#5290FF",
+				"navigationBarTextStyle": "white"
+			}
+
+		}, {
+			"path": "pages/index/deviceDetails/deviceDetails",
+			"style": {
+				"navigationBarTitleText": "设备详情",
+				"enablePullDownRefresh": false
+			}
+
+		}
+	],
+	"globalStyle": {
+		"navigationBarTextStyle": "black",
+		"navigationBarTitleText": "停车巡检端",
+		"navigationBarBackgroundColor": "#F8F8F8",
+		"backgroundColor": "#F5F5F5"
+	},
+	"tabBar": {
+		"color": "#a7a7a7",
+		"selectedColor": "#2080FF",
+		"borderStyle": "black",
+		"backgroundColor": "#ffffff",
+		"list": [{
+			"pagePath": "pages/index/index",
+			"iconPath": "static/tabbar/index.png",
+			"selectedIconPath": "static/tabbar/index-selected.png",
+			"text": "首页"
+		}, {
+			"pagePath": "pages/ownerInquiry/ownerInquiry",
+			"iconPath": "static/tabbar/search.png",
+			"selectedIconPath": "static/tabbar/search-selected.png",
+			"text": "车主查询"
+		}, {
+			"pagePath": "pages/mine/mine",
+			"iconPath": "static/tabbar/mine.png",
+			"selectedIconPath": "static/tabbar/mine-selected.png",
+			"text": "我的"
+		}]
+	}
+}

+ 88 - 0
pages/index/chooseRoad/chooseRoad.vue

@@ -0,0 +1,88 @@
+<template>
+	<view class="road">
+		<view class="road-list">
+			<view class="road-list-item" v-for="(item, index) in roadList" :key="index" @click="jumpPage(type, { roadName: item.roadName })">
+				<view class="road-list-item-icon">
+					<u--image src="./../../../static/icons/road-icon.png" width="50rpx" height="60rpx"></u--image>
+				</view>
+				<view>{{ item.roadName }}</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				roadList: [
+					{
+						roadName: '可处路'
+					},
+					{
+						roadName: '党固路'
+					}
+				],
+				type: 1
+			}
+		},
+		onLoad(page) {
+			if (page.type) {
+				this.type = page.type
+				switch (Number(this.type)){
+					case 1:
+						this.roadList = [{roadName: '本杰路'}, { roadName: '丰林路' }]
+						break;
+					case 2:
+						this.roadList = [{roadName: '顺时路'}, { roadName: '党固路' }]
+						break;
+					case 3:
+						this.roadList = [{roadName: '税务大楼'}, { roadName: '政务大楼' }]
+						break;
+				}
+			}
+		},
+		methods: {
+			jumpPage(type, params) {
+				let url
+				switch (Number(type)){
+					case 1:
+						url = 'pages/index/geomagDetails/geomagDetails'
+						break;
+					case 2:
+						url = 'pages/index/lockDetails/lockDetails'
+						break;
+					case 3:
+						url = 'pages/index/gateDetails/gateDetails'
+						break;
+				}
+				uni.$u.route({
+					url,
+					params
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+.road {
+	&-list {
+		padding: 40rpx 20rpx;
+		display: flex;
+		flex-wrap: wrap;
+		&-item {
+			text-align: center;
+			color: #1e1e1e;
+			font-size: 26rpx;
+			width: 33%;
+			margin-bottom: 40rpx;
+			&-icon {
+				text-align: center;
+				width: 50rpx;
+				margin: 0 auto 20rpx;
+			}
+		}
+	}
+}
+</style>

+ 135 - 0
pages/index/clockIn/clockIn.vue

@@ -0,0 +1,135 @@
+<template>
+	<view class="clock">
+		<view class="clock-button">
+			<view class="clock-button-outer">
+				<view class="clock-button-inner">
+					<view class="tips">
+						<view>上班打卡</view>
+						<view>{{ currentTime }}</view>
+					</view>
+				</view>
+			</view>
+		</view>
+		<view class="clock-tips">
+			<view class="clock-tips-content">
+				<u-icon name="checkmark-circle-fill" color="#0E9CFF" size="15"></u-icon>
+				<view>已经进入考勤范围,潍坊软件园...<text>重新定位?</text></view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				currentTime: ''
+			}
+		},
+		onShow() {
+			this.currentDate();
+		},
+		onUnload() {
+			if (this.formatDate) {
+				clearInterval(this.formatDate); // 在Vue实例销毁前,清除时间定时器
+			}
+		},
+		methods: {
+			currentDate() {
+				setInterval(this.formatDate, 500);
+			},
+			formatDate() {
+				let date = new Date();
+				let year = date.getFullYear(); // 年
+				let month = date.getMonth() + 1; // 月
+				let day = date.getDate(); // 日
+				let week = date.getDay(); // 星期
+				let hour = date.getHours(); // 时
+				hour = hour < 10 ? "0" + hour : hour; // 如果只有一位,则前面补零
+				let minute = date.getMinutes(); // 分
+				minute = minute < 10 ? "0" + minute : minute; // 如果只有一位,则前面补零
+				let second = date.getSeconds(); // 秒
+				second = second < 10 ? "0" + second : second; // 如果只有一位,则前面补零
+				this.currentTime = `${hour}:${minute}:${second}`;
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.clock {
+		&-button {
+			position: relative;
+			animation-name: example;
+			animation-duration: 2s;
+			animation-fill-mode: forwards;
+
+			&-outer {
+				margin: 0 auto;
+				width: 270rpx;
+				height: 270rpx;
+				background-color: #98D4FE;
+				border-radius: 50%;
+				display: flex;
+				justify-content: center;
+				align-items: center;
+			}
+
+			@keyframes example {
+				from {
+					top: 0;
+				}
+
+				to {
+					top: 286rpx;
+				}
+			}
+
+			&-inner {
+				width: 220rpx;
+				height: 220rpx;
+				background-color: #0E9CFF;
+				border-radius: 50%;
+				display: flex;
+				align-items: center;
+				justify-content: center;
+				color: #fff;
+
+				.tips {
+					view {
+						color: #f2f2f2;
+						font-size: 34rpx;
+						text-align: center;
+
+						&:last-child {
+							color: #ddd;
+							font-size: 30rpx;
+							margin-top: 10rpx;
+						}
+					}
+				}
+			}
+		}
+
+		&-tips {
+			text-align: center;
+			margin-top: 320rpx;
+
+			&-content {
+				display: flex;
+				align-items: center;
+				justify-content: center;
+				color: #000;
+				font-size: 24rpx;
+
+				view {
+					margin-left: 5rpx;
+
+					text {
+						color: #F4993A;
+					}
+				}
+			}
+		}
+	}
+</style>

+ 192 - 0
pages/index/deviceDetails/deviceDetails.vue

@@ -0,0 +1,192 @@
+<template>
+	<view class="details">
+		<view class="details-list">
+			<view class="details-item" v-if="type == 1 || type == 2">
+				<view class="left">
+					<view>泊位号:</view>
+					<view>BJ001</view>
+				</view>
+				<view class="right">例行巡查</view>
+			</view>
+			<view class="details-item" v-if="type == 3">
+				<view class="left">
+					<view>停车场:</view>
+					<view>政务大楼停车场-入口处</view>
+				</view>
+				<view class="right">例行巡查</view>
+			</view>
+			<view class="details-item">
+				<view class="left">
+					<view>设备编号:</view>
+					<view>2021505862</view>
+				</view>
+			</view>
+			<view class="details-item" v-if="type == 1">
+				<view class="left">
+					<view>电量值:</view>
+					<view>9.9v</view>
+				</view>
+			</view>
+			<view class="details-item" v-if="type == 2 || type == 3">
+				<view class="left">
+					<view>设备状态:</view>
+					<view>降板</view>
+				</view>
+			</view>
+			<view class="details-item" v-if="type == 1">
+				<view class="left">
+					<view>信号值:</view>
+					<view>-90</view>
+				</view>
+			</view>
+			<view class="details-item" v-if="type == 2">
+				<view class="left">
+					<view>电量值:</view>
+					<view>9.9V</view>
+				</view>
+			</view>
+			<view class="details-item" v-if="type == 1">
+				<view class="left">
+					<view>车牌号:</view>
+					<view>贵A12345</view>
+				</view>
+			</view>
+			<view class="details-item" v-if="type == 2">
+				<view class="left">
+					<view>停车情况:</view>
+					<view>有车</view>
+				</view>
+			</view>
+			<view class="details-item" v-if="type == 1 || type == 2">
+				<view class="left">
+					<view>入场时间:</view>
+					<view>10月28日18:21:30</view>
+				</view>
+			</view>
+			<view class="details-item" v-if="type == 1 || type == 2">
+				<view class="left">
+					<view>停车时长:</view>
+					<view>1小时30分40秒</view>
+				</view>
+			</view>
+		</view>
+		<view class="details-recent">
+			<view class="details-recent-title">
+				<view></view>
+				<view>最近事件</view>
+				<view></view>
+			</view>
+			<view class="details-recent-list">
+				<view class="details-recent-list-header">
+					<view>时间</view>
+					<view>事件类型</view>
+				</view>
+				<view class="details-recent-list-content">
+					<view class="drlc-item" v-for="(item, index) in recentList" :key="index">
+						<view>{{ item.date }}</view>
+						<view>{{ item.event }}</view>
+					</view>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				type: 1,
+				recentList: [
+					{ date: '12月17日 15:15:30', event: '心跳事件' },
+					{ date: '12月17日 15:15:30', event: '心跳事件' },
+					{ date: '12月17日 15:15:30', event: '心跳事件' },
+					{ date: '12月17日 15:15:30', event: '心跳事件' },
+					{ date: '12月17日 15:15:30', event: '心跳事件' },
+					{ date: '12月17日 15:15:30', event: '心跳事件' },
+					{ date: '12月17日 15:15:30', event: '心跳事件' }
+				]
+			}
+		},
+		onLoad(page) {
+			this.type = page?.type ?? 1
+		},
+		methods: {
+			
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+.details {
+	padding: 60rpx 40rpx;
+	font-family: PingFangSC-regular;
+	&-item {
+		display: flex;
+		justify-content: space-between;
+		margin-bottom: 38rpx;
+		font-size: 26rpx;
+		color: #101010;
+		.left {
+			display: flex;
+			view {
+				&:first-child {
+					width: 160rpx;
+					text-align: right;
+				}
+			}
+		}
+		.right {
+			color: #5991E8;
+		}
+	}
+	&-recent {
+		&-title {
+			width: 100%;
+			display: flex;
+			justify-content: space-between;
+			align-items: center;
+			view {
+				color: #3D3C3C;
+				font-size: 26rpx;
+				&:first-child, &:last-child {
+					width: 35%;
+					border-top: dashed 1px #d6d6d6;
+				}
+			}
+		}
+		&-list {
+			margin-top: 52rpx;
+			&-header {
+				display: flex;
+				justify-content: space-between;
+				view {
+					color: #101010;
+					font-size: 26rpx;
+					&:first-child {
+						margin-left: 60rpx;
+					}
+					&:last-child {
+						margin-right: 60rpx;
+					}
+				}
+			}
+			&-content {
+				margin-top: 32rpx;
+				.drlc-item {
+					display: flex;
+					justify-content: space-between;
+					color: #a2a2a2;
+					font-size: 26rpx;
+					margin-bottom: 32rpx;
+					view {
+						&:last-child {
+							margin-right: 60rpx;
+						}
+					}
+				}
+			}
+		}
+	}
+}
+</style>

+ 175 - 0
pages/index/deviceList/deviceList.vue

@@ -0,0 +1,175 @@
+<template>
+	<view class="device">
+		<z-paging ref="paging" v-model="deviceList" @query="queryList">
+			<view class="device-header" slot="top">
+				<view class="device-header-left">
+					<u--input
+						v-model="deviceTypeValue"
+						suffixIcon="arrow-down"
+						:readonly="true"
+						@focus="deviceTypeShow = true"
+						fontSize="24rpx"
+					></u--input>
+					<u-picker :show="deviceTypeShow" :columns="deviceTypeList" keyName="label" @confirm="deviceTypeConfirm"></u-picker>
+				</view>
+				<view class="device-header-right">
+					<u--input
+						v-model="roadValue"
+						suffixIcon="arrow-down"
+						:readonly="true"
+						fontSize="24rpx"
+						@focus="roadShow = true"
+					></u--input>
+					<u-picker :show="roadShow" :columns="roadList" keyName="label" @confirm="roadConfirm"></u-picker>
+				</view>
+			</view>
+			<view class="device-list">
+				<view class="device-list-item" v-for="(item, index) in deviceList" :key="index">
+					<view class="device-list-item-left">
+						<view class="left-device">
+							<view>{{ item.deviceType }}</view>
+							<view>{{ item.road }}</view>
+						</view>
+						<view class="device-no">设备编号: {{ item.deviceNo }}</view>
+					</view>
+					<view class="device-list-item-right" :class="item.status === 0 ? 'success' : item.status === 1 ? 'error' : 'nomal'">{{ item.status | filterStatus }}</view>
+				</view>
+			</view>
+		</z-paging>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				deviceTypeValue: '全部',
+				roadValue: '全部',
+				deviceList: [],
+				deviceTypeShow: false,
+				deviceTypeList: [[
+					{ label: '全部', value: 0 },
+					{ label: '车位锁', value: 1 },
+					{ label: '地磁', value: 2 },
+					{ label: '栏杆道闸', value: 3 }
+				]],
+				roadShow: false,
+				roadList: [[
+					{ label: '全部', value: 0 },
+					{ label: '顺时路', value: 1 },
+					{ label: '党固路', value: 2 },
+					{ label: '丰林路', value: 3 },
+					{ label: '教育路', value: 4 },
+					{ label: '物资路', value: 5 },
+					{ label: '桂花苑路段', value: 6 },
+					{ label: '可处路', value: 7 }
+				]]
+			}
+		},
+		filters: {
+			filterStatus(val) {
+				let name
+				switch (val){
+					case 0:
+						name = '运行中'
+						break;
+					case 1:
+						name = '已离线'
+						break;
+					case 2:
+						name = '异常'
+						break;
+				}
+				return name
+			}
+		},
+		methods: {
+			queryList(pageNo, pageSize) {
+				this.$refs.paging.complete([{
+					deviceType: '车位锁',
+					road: '可处路-KC001',
+					deviceNo: '200602061258',
+					status: 0
+				}, {
+					deviceType: '地磁',
+					road: '可处路-KC002',
+					deviceNo: '200602061259',
+					status: 1
+				}, {
+					deviceType: '栏杆道闸',
+					road: '可处路-KC003',
+					deviceNo: '200602061269',
+					status: 2
+				}, {
+					deviceType: '地磁',
+					road: '可处路-KC005',
+					deviceNo: '200602061259',
+					status: 0
+				}])
+			},
+			deviceTypeConfirm(e) {
+				this.deviceTypeValue = e.value[0].label
+				this.deviceTypeShow = false
+			},
+			roadConfirm(e) {
+				this.roadValue = e.value[0].label
+				this.roadShow = false
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+.device {
+	font-family: 'PingFangSC-regular';
+	&-header {
+		display: flex;
+		justify-content: space-between;
+		padding: 24rpx;
+		&-left {
+			width: 30%;
+		}
+		&-right {
+			width: 30%;
+		}
+	}
+	&-list {
+		&-item {
+			border-bottom: solid 1px #bbb;
+			padding: 32rpx 24rpx;
+			display: flex;
+			justify-content: space-between;
+			align-items: center;
+			.left-device {
+				display: flex;
+				view {
+					font-size: 30rpx;
+					margin-bottom: 16rpx;
+					&:first-child {
+						color: #000;
+						font-weight: 600;
+						margin-right: 44rpx;
+						font-size: 30rpx;
+						width: 130rpx;
+					}
+				}
+			}
+			.device-no {
+				font-size: 28rpx;
+			}
+			&-right {
+				font-size: 28rpx;
+			}
+			.success {
+				color: #06C76E;
+			}
+			.error {
+				color: #FF3939;
+			}
+			.nomal {
+				color: #008CFF;
+			}
+		}
+	}
+}
+</style>

+ 154 - 0
pages/index/gateDetails/gateDetails.vue

@@ -0,0 +1,154 @@
+<template>
+	<view class="details">
+		<z-paging ref="paging" v-model="list" @query="queryList">
+			<view class="details-tabs" slot="top">
+				<u-tabs :list="tabList" @click="tabClick" lineWidth="40"></u-tabs>
+			</view>
+			<view class="details-total" slot="top">
+				<view>已停车位:{{ 30 }}个;空余车位:{{ 36 }}个</view>
+			</view>
+			<view class="details-list">
+				<view class="details-list-item" v-for="(item, index) in list" :key="index" @click="jumpPage('pages/index/deviceDetails/deviceDetails', { type: 3 })">
+					<view class="details-list-item-header">
+						<view>{{ item.roadNo }}</view>
+						<view>{{ item.signal }}</view>
+					</view>
+					<view class="details-list-item-img">
+						<u--image v-if="item.type === 1" src="../../../static/icons/daozhakongzhi.svg" width="174rpx" height="174rpx"></u--image>
+						<u--image v-else-if="item.type === 2" src="../../../static/icons/shipinjiankongyemian.svg" width="174rpx" height="174rpx"></u--image>
+						<u--image v-else-if="item.type === 3" src="../../../static/icons/chengguan-wu-bujian-gonggongsheshi-daoluxinxixianshiping.svg" width="174rpx" height="174rpx"></u--image>
+					</view>
+					<view class="details-list-item-bottom">
+						<view>设备号{{ item.deviceNo }}</view>
+					</view>
+				</view>
+			</view>
+		</z-paging>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				roadInfo: {
+					roadName: ''
+				},
+				tabList: [{
+						name: '全部',
+						value: ''
+					},
+					{
+						name: '异常',
+						value: 1
+					},
+					{
+						name: '离线',
+						value: 2
+					}
+				],
+				list: []
+			}
+		},
+		onLoad(page) {
+			if (page.roadName) {
+				this.roadInfo.roadName = page.roadName
+				uni.setNavigationBarTitle({
+					title: page.roadName
+				})
+			}
+		},
+		methods: {
+			queryList(pageNo, pageSize) {
+				this.$refs.paging.complete([{
+					roadNo: '入口道闸',
+					signal: '降板',
+					type: 1,
+					deviceNo: '202103051265',
+					electric: 9.9
+				}, {
+					roadNo: '入口车牌识别',
+					signal: '在线',
+					type: 2,
+					deviceNo: '202103051265',
+					electric: 10
+				}, {
+					roadNo: '入口显示屏',
+					signal: '在线',
+					type: 3,
+					deviceNo: '202103051265',
+					electric: 10
+				}])
+			},
+			tabClick(val) {
+				console.log(val)
+			},
+			getTimeLong(date) {
+				const nowDate = (new Date()).valueOf()
+				const curDate = (new Date(date)).valueOf()
+				const timeLen = (nowDate - curDate) / 1000
+				const day = parseInt(timeLen / (24 * 3600));
+				const hour = parseInt((timeLen - day * 24 * 3600) / 3600);
+				const minutes = parseInt((timeLen - day * 24 * 3600 - hour * 3600) / 60)
+				const second = parseInt(timeLen % 60)
+				return `${day}天${hour}时${minutes}分${second}秒`
+			},
+			jumpPage(url, params) {
+				uni.$u.route({
+					url, params
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	page {
+		min-height: calc(100vh - 88rpx);
+		background-color: #F6F6FF;
+	}
+
+	.details {
+		font-family: 'PingFangSC-regular';
+		&-tabs {
+			background-color: #fff;
+		}
+		&-total {
+			display: flex;
+			justify-content: flex-end;
+			margin-top: 32rpx;
+			font-size: 26rpx;
+			color: #000;
+			padding-right: 20rpx;
+		}
+		&-list {
+			padding: 40rpx 20rpx;
+			display: flex;
+			justify-content: space-between;
+			flex-wrap: wrap;
+
+			&-item {
+				width: calc(50% - 52rpx);
+				background-color: #fff;
+				border-radius: 16rpx;
+				padding: 20rpx;
+				&-header {
+					display: flex;
+					justify-content: space-between;
+					color: #101010;
+					font-size: 26rpx;
+				}
+				&-img {
+					width: 152rpx;
+					height: 152rpx;
+					margin: 16rpx auto 46rpx;
+				}
+				&-bottom {
+					text-align: center;
+					font-size: 24rpx;
+					color: #101010;
+				}
+			}
+		}
+	}
+</style>

+ 186 - 0
pages/index/geomagDetails/geomagDetails.vue

@@ -0,0 +1,186 @@
+<template>
+	<view class="details">
+		<z-paging ref="paging" v-model="list" @query="queryList">
+			<view class="details-tabs" slot="top">
+				<u-tabs :list="tabList" @click="tabClick" lineWidth="40"></u-tabs>
+			</view>
+			<view class="details-list">
+				<view class="details-list-item" v-for="(item, index) in list" :key="index" @click="jumpPage('pages/index/deviceDetails/deviceDetails', { type: 1 })">
+					<view class="details-list-item-header">
+						<view>{{ item.roadNo }}</view>
+						<view>{{ item.signal }}信号</view>
+					</view>
+					<view class="details-list-item-cardNo">
+						{{ item.cardNum }}
+					</view>
+					<view class="details-list-item-img">
+						<u--image v-if="item.cardNum" src="../../../static/icons/has-car-icon.svg" width="152rpx" height="152rpx"></u--image>
+						<u--image v-else src="../../../static/icons/no-car-icon.svg" width="152rpx" height="152rpx"></u--image>
+					</view>
+					<view class="details-list-item-time">
+						{{ item.inTime ? getTimeLong(item.inTime) : '' }}
+					</view>
+					<view class="details-list-item-bottom">
+						<view>{{ item.deviceNo }}</view>
+						<view>
+							<view>{{ item.electric }}V</view>
+							<view class="ele">
+								<u--image v-if="item.electric < 10" src="../../../static/icons/no-ele-icon.svg" width="48rpx" height="48rpx"></u--image>
+								<u--image v-else src="../../../static/icons/has-ele-icon.svg" width="48rpx" height="48rpx"></u--image>
+							</view>
+						</view>
+					</view>
+				</view>
+			</view>
+		</z-paging>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				roadInfo: {
+					roadName: ''
+				},
+				tabList: [{
+						name: '全部',
+						value: ''
+					},
+					{
+						name: '异常',
+						value: 1
+					},
+					{
+						name: '低电量',
+						value: 2
+					},
+					{
+						name: '低信号',
+						value: 3
+					}
+				],
+				list: []
+			}
+		},
+		onLoad(page) {
+			if (page.roadName) {
+				this.roadInfo.roadName = page.roadName
+				uni.setNavigationBarTitle({
+					title: page.roadName
+				})
+			}
+		},
+		methods: {
+			queryList(pageNo, pageSize) {
+				this.$refs.paging.complete([{
+					roadNo: 'KC001',
+					signal: '-90',
+					inTime: '2021-12-17 09:30:50',
+					cardNum: '贵A12345',
+					deviceNo: '202103051265',
+					electric: 9.9
+				}, {
+					roadNo: 'KC001',
+					signal: '-90',
+					inTime: '',
+					cardNum: '',
+					deviceNo: '202103051265',
+					electric: 10
+				}])
+			},
+			tabClick(val) {
+				console.log(val)
+			},
+			getTimeLong(date) {
+				const nowDate = (new Date()).valueOf()
+				const curDate = (new Date(date)).valueOf()
+				const timeLen = (nowDate - curDate) / 1000
+				const day = parseInt(timeLen / (24 * 3600));
+				const hour = parseInt((timeLen - day * 24 * 3600) / 3600);
+				const minutes = parseInt((timeLen - day * 24 * 3600 - hour * 3600) / 60)
+				const second = parseInt(timeLen % 60)
+				return `${day}天${hour}时${minutes}分${second}秒`
+			},
+			jumpPage(url, params) {
+				uni.$u.route({
+					url, params
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	page {
+		min-height: calc(100vh - 88rpx);
+		background-color: #F6F6FF;
+	}
+
+	.details {
+		font-family: 'PingFangSC-regular';
+		&-tabs {
+			background-color: #fff;
+		}
+
+		&-list {
+			padding: 40rpx 20rpx;
+			display: flex;
+			justify-content: space-between;
+			flex-wrap: wrap;
+
+			&-item {
+				width: calc(50% - 52rpx);
+				background-color: #fff;
+				border-radius: 16rpx;
+				padding: 20rpx;
+				&-header {
+					display: flex;
+					justify-content: space-between;
+					color: #101010;
+					font-size: 26rpx;
+				}
+				&-cardNo {
+					text-align: center;
+					color: #101010;
+					height: 40rpx;
+					line-height: 40rpx;
+					font-size: 26rpx;
+					margin-bottom: 10rpx;
+				}
+				&-img {
+					width: 152rpx;
+					height: 152rpx;
+					margin: 0 auto;
+				}
+				&-time {
+					text-align: center;
+					margin-top: 12rpx;
+					margin-bottom: 20rpx;
+					font-size: 26rpx;
+					color: #101010;
+					height: 40rpx;
+					overflow: hidden;
+					text-overflow: ellipsis;
+					white-space: nowrap;
+				}
+				&-bottom {
+					display: flex;
+					justify-content: space-between;
+					align-items: center;
+					font-size: 24rpx;
+					color: #101010;
+					view {
+						&:last-child {
+							display: flex;
+							align-items: center;
+						}
+					}
+					.ele {
+						transform: rotate(-90deg);
+					}
+				}
+			}
+		}
+	}
+</style>

+ 84 - 0
pages/index/index.scss

@@ -0,0 +1,84 @@
+page {
+	background-color: #f5f5f5;
+}
+.index {
+	font-family: 'PingFangSC-regular';
+	&-header {
+		height: 250rpx;
+		background-color: #5290FF;
+		padding: 30rpx;
+		display: flex;
+		justify-content: space-between;
+		&-left {
+			color: #fff;
+			font-size: 40rpx;
+			view {
+				&:last-child {
+					margin-top: 20rpx;
+					font-size: 28rpx;
+					font-family: 'PingFangSC-bold';
+				}
+			}
+		}
+		&-right {
+			margin-right: 30rpx;
+			height: 60rpx;
+		}
+	}
+	&-statistics {
+		padding: 0 30rpx;
+		&-list {
+			background-color: #fff;
+			border: solid 1px rgba(255, 0, 0, 0);
+			display: flex;
+			justify-content: space-between;
+			align-items: center;
+			padding: 44rpx 80rpx;
+			border-radius: 20rpx;
+			margin-top: -96rpx;
+			&-item {
+				text-align: center;
+				view {
+					color: #999898;
+					font-size: 26rpx;
+					&:first-child {
+						color: #101010;
+						font-size: 60rpx;
+						font-family: 'PingFangSC-bold';
+					}
+				}
+			}
+		}
+	}
+	&-device {
+		padding: 40rpx 30rpx;
+		background-color: #fff;
+		margin-top: 20rpx;
+		&-info {
+			&-title {
+				font-size: 36rpx;
+				color: #333;
+				font-family: 'PingFangSC-bold';
+				font-weight: 600;
+			}
+			&-menu {
+				display: flex;
+				.item {
+					width: 160rpx;
+					padding: 60rpx 0;
+					text-align: center;
+					.image {
+						display: inline-block;
+					}
+					view {
+						&:last-child {
+							font-size: 24rpx;
+							color: #101010;
+							margin-top: 6rpx;
+						}
+					}
+				}
+			}
+		}
+	}
+}

+ 118 - 0
pages/index/index.vue

@@ -0,0 +1,118 @@
+<template>
+	<view class="index">
+		<!-- 欢迎 -->
+		<view class="index-header">
+			<view class="index-header-left">
+				<view>欢迎登录!</view>
+				<view>张三</view>
+			</view>
+			<view class="index-header-right" @click="jumpPage('pages/index/messageCenter/messageCenter')">
+				<u-icon
+					name="bell"
+					color="#fff"
+					size="24"></u-icon>
+				<u-badge
+					type="error"
+					max="99"
+					:absolute="true"
+					:offset="[10, 14]"
+					bgColor="#FD0A0A"
+					:value="infoVal"></u-badge>
+			</view>
+		</view>
+		<!-- 数据汇总 -->
+		<view class="index-statistics">
+			<view class="index-statistics-list">
+				<view class="index-statistics-list-item" @click="jumpPage('pages/index/deviceList/deviceList')">
+					<view>36</view>
+					<view>设备总数</view>
+				</view>
+				<view class="index-statistics-list-item" @click="jumpPage('pages/index/deviceList/deviceList')">
+					<view>10</view>
+					<view>设备离线</view>
+				</view>
+				<view class="index-statistics-list-item" @click="jumpPage('pages/index/deviceList/deviceList')">
+					<view>5</view>
+					<view>设备异常</view>
+				</view>
+			</view>
+		</view>
+		<!-- 设备信息 -->
+		<view class="index-device">
+			<view class="index-device-info">
+				<view class="index-device-info-title">设备信息</view>
+				<view class="index-device-info-menu">
+					<view class="item" @click="jumpPage('pages/index/chooseRoad/chooseRoad', { type: 1 })">
+						<u--image
+							class="image"
+							src="../../static/icons/geomagnetism-icon.svg"
+							width="48rpx"
+							height="48rpx"
+						></u--image>
+						<view>地磁</view>
+					</view>
+					<view class="item" @click="jumpPage('pages/index/chooseRoad/chooseRoad', { type: 2 })">
+						<u--image
+							class="image"
+							src="../../static/icons/parking-lock-icon.svg"
+							width="48rpx"
+							height="48rpx"></u--image>
+						<view>车位锁</view>
+					</view>
+					<view class="item" @click="jumpPage('pages/index/chooseRoad/chooseRoad', { type: 3 })">
+						<u--image
+							class="image"
+							src="./../../static/icons/railing-gate-icon.svg"
+							width="48rpx"
+							height="48rpx"></u--image>
+						<view>栏杆道闸</view>
+					</view>
+				</view>
+			</view>
+		</view>
+		<!-- 考勤管理 -->
+		<view class="index-device">
+			<view class="index-device-info">
+				<view class="index-device-info-title">考勤管理</view>
+				<view class="index-device-info-menu">
+					<view class="item" @click="jumpPage('pages/index/clockIn/clockIn')">
+						<u--image
+							class="image"
+							src="./../../static/icons/clock-in-icon.png"
+							width="48rpx"
+							height="48rpx"></u--image>
+						<view>打卡</view>
+					</view>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				infoVal: 33
+			}
+		},
+		onLoad() {
+
+		},
+		methods: {
+			/**
+			 * @param { String } url
+			 * @param { Object } params
+			 */
+			jumpPage(url, params) {
+				uni.$u.route({
+					url, params
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	@import './index.scss';
+</style>

+ 186 - 0
pages/index/lockDetails/lockDetails.vue

@@ -0,0 +1,186 @@
+<template>
+	<view class="details">
+		<z-paging ref="paging" v-model="list" @query="queryList">
+			<view class="details-tabs" slot="top">
+				<u-tabs :list="tabList" @click="tabClick" lineWidth="40"></u-tabs>
+			</view>
+			<view class="details-list">
+				<view class="details-list-item" v-for="(item, index) in list" :key="index" @click="jumpPage('pages/index/deviceDetails/deviceDetails', { type: 2 })">
+					<view class="details-list-item-header">
+						<view>{{ item.roadNo }}</view>
+						<view>{{ item.deviceState }}</view>
+					</view>
+					<view class="details-list-item-cardNo">
+						{{ item.cardNum }}
+					</view>
+					<view class="details-list-item-img">
+						<u--image v-if="item.cardNum" src="../../../static/icons/has-car-icon.svg" width="152rpx" height="152rpx"></u--image>
+						<u--image v-else src="../../../static/icons/no-car-icon.svg" width="152rpx" height="152rpx"></u--image>
+					</view>
+					<view class="details-list-item-time">
+						{{ item.inTime ? getTimeLong(item.inTime) : '' }}
+					</view>
+					<view class="details-list-item-bottom">
+						<view>{{ item.deviceNo }}</view>
+						<view>
+							<view>{{ item.electric }}V</view>
+							<view class="ele">
+								<u--image v-if="item.electric < 10" src="../../../static/icons/no-ele-icon.svg" width="48rpx" height="48rpx"></u--image>
+								<u--image v-else src="../../../static/icons/has-ele-icon.svg" width="48rpx" height="48rpx"></u--image>
+							</view>
+						</view>
+					</view>
+				</view>
+			</view>
+		</z-paging>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				roadInfo: {
+					roadName: ''
+				},
+				tabList: [{
+						name: '全部',
+						value: ''
+					},
+					{
+						name: '异常',
+						value: 1
+					},
+					{
+						name: '低电量',
+						value: 2
+					},
+					{
+						name: '离线',
+						value: 3
+					}
+				],
+				list: []
+			}
+		},
+		onLoad(page) {
+			if (page.roadName) {
+				this.roadInfo.roadName = page.roadName
+				uni.setNavigationBarTitle({
+					title: page.roadName
+				})
+			}
+		},
+		methods: {
+			queryList(pageNo, pageSize) {
+				this.$refs.paging.complete([{
+					roadNo: 'KC001',
+					deviceState: '升板',
+					inTime: '2021-12-17 09:30:50',
+					cardNum: '贵A12345',
+					deviceNo: '202103051265',
+					electric: 9.9
+				}, {
+					roadNo: 'KC001',
+					deviceState: '降板',
+					inTime: '',
+					cardNum: '',
+					deviceNo: '202103051265',
+					electric: 10
+				}])
+			},
+			tabClick(val) {
+				console.log(val)
+			},
+			getTimeLong(date) {
+				const nowDate = (new Date()).valueOf()
+				const curDate = (new Date(date)).valueOf()
+				const timeLen = (nowDate - curDate) / 1000
+				const day = parseInt(timeLen / (24 * 3600));
+				const hour = parseInt((timeLen - day * 24 * 3600) / 3600);
+				const minutes = parseInt((timeLen - day * 24 * 3600 - hour * 3600) / 60)
+				const second = parseInt(timeLen % 60)
+				return `${day}天${hour}时${minutes}分${second}秒`
+			},
+			jumpPage(url, params) {
+				uni.$u.route({
+					url, params
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	page {
+		min-height: calc(100vh - 88rpx);
+		background-color: #F6F6FF;
+	}
+
+	.details {
+		font-family: 'PingFangSC-regular';
+		&-tabs {
+			background-color: #fff;
+		}
+
+		&-list {
+			padding: 40rpx 20rpx;
+			display: flex;
+			justify-content: space-between;
+			flex-wrap: wrap;
+
+			&-item {
+				width: calc(50% - 52rpx);
+				background-color: #fff;
+				border-radius: 16rpx;
+				padding: 20rpx;
+				&-header {
+					display: flex;
+					justify-content: space-between;
+					color: #101010;
+					font-size: 26rpx;
+				}
+				&-cardNo {
+					text-align: center;
+					color: #101010;
+					height: 40rpx;
+					line-height: 40rpx;
+					font-size: 26rpx;
+					margin-bottom: 10rpx;
+				}
+				&-img {
+					width: 152rpx;
+					height: 152rpx;
+					margin: 0 auto;
+				}
+				&-time {
+					text-align: center;
+					margin-top: 12rpx;
+					margin-bottom: 20rpx;
+					font-size: 26rpx;
+					color: #101010;
+					height: 40rpx;
+					overflow: hidden;
+					text-overflow: ellipsis;
+					white-space: nowrap;
+				}
+				&-bottom {
+					display: flex;
+					justify-content: space-between;
+					align-items: center;
+					font-size: 24rpx;
+					color: #101010;
+					view {
+						&:last-child {
+							display: flex;
+							align-items: center;
+						}
+					}
+					.ele {
+						transform: rotate(-90deg);
+					}
+				}
+			}
+		}
+	}
+</style>

+ 47 - 0
pages/index/messageCenter/messageCenter.scss

@@ -0,0 +1,47 @@
+page {
+	background-color: #f6f6ff;
+	min-height: calc(100vh - 88rpx);
+}
+.message {
+	font-family: 'PingFangSC-regular';
+	&-list {
+		padding-top: 40rpx;
+		&-item {
+			padding: 0 40rpx;
+			&-date {
+				color: #cfcfd6;
+				font-size: 26rpx;
+				text-align: center;
+				margin-top: 20rpx;
+			}
+			&-content {
+				background-color: #fff;
+				border-radius: 16rpx;
+				padding: 26rpx 0 20rpx;
+				margin-top: 20rpx;
+				.type {
+					color: #101010;
+					font-size: 34rpx;
+					font-weight: 600;
+					margin-bottom: 14rpx;
+					padding: 0 26rpx;
+				}
+				.info {
+					font-size: 28rpx;
+					color: #666;
+					line-height: 45rpx;
+					padding: 0 26rpx;
+					margin-bottom: 40rpx;
+				}
+				.jump {
+					display: flex;
+					justify-content: space-between;
+					padding: 20rpx 26rpx 0;
+					border-top: solid 1px #dfdfdf;
+					font-size: 28rpx;
+					color: #509DF4;
+				}
+			}
+		}
+	}
+}

+ 57 - 0
pages/index/messageCenter/messageCenter.vue

@@ -0,0 +1,57 @@
+<template>
+	<view class="message">
+		<z-paging ref="paging" v-model="messageList" @query="queryList">
+			<view class="message-list">
+				<view class="message-list-item" v-for="(item, index) in messageList" :key="index">
+					<view class="message-list-item-date">{{ item.date }}</view>
+					<view class="message-list-item-content">
+						<view class="type">{{ item.type }}</view>
+						<view class="info">
+							<text>{{ item.content }}</text>
+						</view>
+						<view class="jump">
+							<view>查看详情</view>
+							<view>
+								<u-icon name="arrow-right" color="#101010" size="16"></u-icon>
+							</view>
+						</view>
+					</view>
+				</view>
+			</view>
+		</z-paging>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				messageList: []
+			}
+		},
+		methods: {
+			queryList(pageNo, pageSize) {
+				this.$refs.paging.complete([{
+						date: '2021年04月22日 21:02:03',
+						type: '故障消息',
+						content: '欠费车贵AR366T于11月29日 12:00:03驶入可处路,欠费金额:¥500.00,请前往!'
+					},
+					{
+						date: '2021年04月21日 12:20:03',
+						type: '预警消息',
+						content: '可处路-KC002的信号过低,请及时处理!\n设备号:2022112542'
+					},
+					{
+						date: '2021年04月20日 14:05:49',
+						type: '应急求助',
+						content: '可处路-KC003有一个车主求紧急求助:求助内容:车车位锁卡住车子了'
+					}
+				]);
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	@import './messageCenter.scss';
+</style>

+ 32 - 0
pages/login/login.scss

@@ -0,0 +1,32 @@
+.login {
+	&-title {
+		margin-bottom: 226rpx;
+		padding: 246rpx 150rpx 0;
+		font-family: 'PingFangSC-bold';
+		color: #50A0FA;
+		font-size: 72rpx;
+		text-align: center;
+		font-weight: bold;
+	}
+	&-form {
+		padding: 0 56rpx;
+		&-title {
+			font-size: 40rpx;
+			font-family: PingFangSC-bold;
+			font-weight: bold;
+			margin-bottom: 50rpx;
+		}
+		&-box {
+			&-button {
+				margin-top: 80rpx;
+			}
+			&-forget {
+				width: 100%;
+				text-align: right;
+				font-size: 28rpx;
+				color: #999;
+				font-family: PingFangSC-regular;
+			}
+		}
+	}
+}

+ 106 - 0
pages/login/login.vue

@@ -0,0 +1,106 @@
+<!-- 登录 -->
+<template>
+	<view class="login">
+		<view class="login-title">智慧停车<br />设备巡检系统</view>
+		<view class="login-form">
+			<view class="login-form-title">账号密码登录</view>
+			<view class="login-form-box">
+				<u--form
+					:model="loginForm"
+					:rules="loginRules"
+					ref="loginForm">
+					<u-form-item prop="phoneNumber">
+						<u--input
+							v-model="loginForm.phoneNumber"
+							border="surround"
+							placeholder="请输入手机号码"
+							shape="square"
+							fontSize="30rpx"
+							maxlength="11"
+							type="number"
+							color="#B7B7B7"></u--input>
+					</u-form-item>
+					<u-form-item prop="password">
+						<u--input
+							v-model="loginForm.password"
+							border="surround"
+							:password="true"
+							placeholder="请输入密码"
+							shape="square"
+							fontSize="30rpx"
+							color="#B7B7B7">
+						</u--input>
+					</u-form-item>
+					<u-form-item>
+						<u-button
+							class="login-form-box-button"
+							type="primary"
+							text="登录"
+							@click="handleLogin"></u-button>
+					</u-form-item>
+					<u-form-item>
+						<view class="login-form-box-forget">忘记密码?</view>
+					</u-form-item>
+				</u--form>
+			</view>
+		</view>
+		<u-notify ref="uNotify" message="Hi uView"></u-notify>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				loginForm: {
+					phoneNumber: '18084288856',
+					password: '123456'
+				},
+				loginRules: {
+					phoneNumber: [
+						{
+							required: true, 
+							message: '请输入手机号码',
+							trigger: ['blur']
+						},
+						{
+							validator: (rule, value, callback) => {
+								return uni.$u.test.mobile(value);
+							},
+							message: '手机号码不正确',
+							trigger: ['blur']
+						}
+					],
+					password: {
+						type: 'string',
+						required: true,
+						message: '请输入密码',
+						trigger: ['blur']
+					}
+				}
+			}
+		},
+		methods: {
+			handleLogin() {
+				this.$refs.loginForm.validate().then(res => {
+					uni.$u.route({
+						url: 'pages/index/index',
+						type: 'switchTab'
+					})
+				}).catch(error => {
+					this.$refs.uNotify.show({
+						top: -1,
+						type: 'warning',
+						message: '请输入手机号/密码',
+						duration: 1000 * 3,
+						fontSize: 20
+					})
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	@import './login.scss';
+</style>

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

@@ -0,0 +1,93 @@
+<template>
+	<view class="password">
+		<u--form labelPosition="left" :model="changeForm" :rules="rules" ref="changeForm">
+			<u-form-item label="" prop="newPass" borderBottom labelWidth="0" :style="{'marginBottom': '30rpx'}">
+				<u--input v-model="changeForm.newPass" border="none" prefixIcon="lock"
+					prefixIconStyle="font-size: 40rpx;color: #638EE3" placeholder="输入新密码"
+					placeholderStyle="font-size: 24rpx" password></u--input>
+			</u-form-item>
+			<u-form-item label="" prop="confirmPass" borderBottom labelWidth="0">
+				<u--input v-model="changeForm.confirmPass" border="none" prefixIcon="lock"
+					prefixIconStyle="font-size: 40rpx;color: #638EE3" placeholder="确认新密码"
+					placeholderStyle="font-size: 24rpx" password></u--input>
+			</u-form-item>
+			<u-form-item label="" labelWidth="0">
+				<view class="confirm-button">
+					<u-button type="primary" text="确认" color="#638EE3" :customStyle="{borderRadius: '20rpx'}"
+						@click="submitChangeForm" :loading="loading"></u-button>
+				</view>
+			</u-form-item>
+		</u--form>
+		<u-toast ref="uToast"></u-toast>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				changeForm: {
+					newPass: '',
+					confirmPass: ''
+				},
+				rules: {
+					newPass: {
+						type: 'string',
+						required: true,
+						message: '输入新密码',
+						trigger: ['blur', 'change']
+					},
+					confirmPass: {
+						type: 'string',
+						required: true,
+						message: '输入确认密码',
+						trigger: ['blur', 'change']
+					}
+				},
+				loading: false
+			}
+		},
+		methods: {
+			submitChangeForm() {
+				this.$refs.changeForm.validate().then(res => {
+					console.log(this.changeForm)
+					if (this.changeForm.newPass === this.changeForm.confirmPass) {
+						this.loading = true
+						this.$refs.uToast.show({
+							type: 'success',
+							title: '提示',
+							message: "修改成功",
+							iconUrl: 'https://cdn.uviewui.com/uview/demo/toast/success.png'
+						})
+						setTimeout(() => {
+							uni.$u.route({
+								url: 'pages/mine/mine',
+								type: 'switchTab'
+							})
+						}, 2000)
+					} else {
+						this.$refs.uToast.show({
+							type: 'warning',
+							title: '提示',
+							message: "两次密码不一致",
+							iconUrl: 'https://cdn.uviewui.com/uview/demo/toast/warning.png'
+						})
+					}
+				}).catch(error => {
+
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.password {
+		padding: 100rpx 68rpx;
+
+		.confirm-button {
+			width: 100%;
+			margin-top: 200rpx;
+		}
+	}
+</style>

+ 31 - 0
pages/mine/mine.scss

@@ -0,0 +1,31 @@
+.mine {
+	&-header {
+		height: 234rpx;
+		background-color: #5290FF;
+		display: flex;
+		align-items: flex-end;
+		padding: 0 34rpx 36rpx;
+		&-avatar {
+			margin-right: 42rpx;
+		}
+		&-user {
+			font-size: 40rpx;
+			color: #fff;
+			view {
+				&:last-child {
+					font-size: 28rpx;
+					margin-bottom: 14rpx;
+				}
+			}
+		}
+	}
+	&-cell {
+		margin-top: 32rpx;
+		.cell-title {
+			font-size: 28rpx;
+			font-family: PingFangSC-regular;
+			color: #101010;
+			margin-left: 10rpx;
+		}
+	}
+}

+ 85 - 0
pages/mine/mine.vue

@@ -0,0 +1,85 @@
+<template>
+	<view class="mine">
+		<view class="mine-header">
+			<view class="mine-header-avatar">
+				<u-avatar :src="userInfo.avatar" size="60"></u-avatar>
+			</view>
+			<view class="mine-header-user">
+				<view>张三丰</view>
+				<view>巡检员</view>
+			</view>
+		</view>
+		<view class="mine-cell">
+			<u-cell-group :border="false">
+				<u-cell icon="setting-fill" title="" :border="false" :isLink="true"
+					:rightIconStyle="{color: '#638EE3', fontSize: '30rpx', fontWeight: 600}"
+					:customStyle="{ marginBottom: '10rpx' }" @click="jumpPage('pages/mine/problemReporting/problemReporting')">
+					<view slot="icon">
+						<u--image src="./../../static/icons/question-icon.png" width="44rpx" height="44rpx"></u--image>
+					</view>
+					<view class="cell-title" slot="title">
+						问题上报
+					</view>
+				</u-cell>
+				<u-cell icon="integral-fill" title="" :border="false" :isLink="true"
+					:rightIconStyle="{color: '#638EE3', fontSize: '30rpx', fontWeight: 600}"
+					:customStyle="{ marginBottom: '10rpx' }">
+					<view slot="icon">
+						<u--image src="./../../static/icons/config-icon.png" width="44rpx" height="44rpx"></u--image>
+					</view>
+					<view class="cell-title" slot="title">
+						配置管理
+					</view>
+				</u-cell>
+				<u-cell icon="integral-fill" title="" :border="false" :isLink="true"
+					:rightIconStyle="{color: '#638EE3', fontSize: '30rpx', fontWeight: 600}"
+					:customStyle="{ marginBottom: '10rpx' }" @click="jumpPage('pages/mine/changePassword/changePassword')">
+					<view slot="icon">
+						<u--image src="./../../static/icons/password-icon.png" width="44rpx" height="44rpx"></u--image>
+					</view>
+					<view class="cell-title" slot="title">
+						修改密码
+					</view>
+				</u-cell>
+				<u-cell icon="integral-fill" title="" :border="false" :isLink="true"
+					:rightIconStyle="{color: '#638EE3', fontSize: '30rpx', fontWeight: 600}"
+					:customStyle="{ marginBottom: '10rpx' }" @click="logout">
+					<view slot="icon">
+						<u--image src="./../../static/icons/logout-icon.png" width="44rpx" height="44rpx"></u--image>
+					</view>
+					<view class="cell-title" slot="title">
+						退出账号
+					</view>
+				</u-cell>
+			</u-cell-group>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				userInfo: {
+					avatar: 'https://cdn.uviewui.com/uview/album/1.jpg'
+				}
+			}
+		},
+		methods: {
+			logout() {
+				uni.$u.route({
+					url: 'pages/login/login'
+				})
+			},
+			jumpPage(url, params) {
+				uni.$u.route({
+					url, params
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	@import './mine.scss';
+</style>

+ 121 - 0
pages/mine/problemReporting/problemReporting.vue

@@ -0,0 +1,121 @@
+<template>
+	<view class="problem">
+		<view class="problem-form">
+			<u--form labelPosition="left" :model="reportForm" :rules="rules" ref="reportForm">
+				<u-form-item label="问题描述:" prop="content" labelWidth="80">
+					<u--textarea v-model="reportForm.content" placeholder="请详细描述问题内容" count maxlength="300" height="80">
+					</u--textarea>
+				</u-form-item>
+				<u-form-item label="上传图片:" labelWidth="80">
+					<u-upload :fileList="fileList1" @afterRead="afterRead" @delete="deletePic" name="1" multiple
+						:maxCount="10" uploadIcon="plus" uploadIconColor="rgba(0, 0, 0, 0.45)"
+						uploadText="上传照片" width="104" height="104" bgColor="#fff"></u-upload>
+				</u-form-item>
+				<u-form-item label="" labelWidth="20">
+					<u-button type="primary" text="提交" :loading="loading" @click="submitForm"></u-button>
+				</u-form-item>
+			</u--form>
+		</view>
+		<u-toast ref="uToast"></u-toast>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				reportForm: {
+					content: '',
+					fileList: []
+				},
+				rules: {
+					content: [
+						{
+							type: 'string',
+							required: true,
+							message: '请详细描述问题内容',
+							trigger: ['blur']
+						}
+					]
+				},
+				fileList1: [],
+				loading: false
+			}
+		},
+		methods: {
+			// 删除图片
+			deletePic(event) {
+				this[`fileList${event.name}`].splice(event.index, 1)
+			},
+			// 新增图片
+			async afterRead(event) {
+				// 当设置 mutiple 为 true 时, file 为数组格式,否则为对象格式
+				let lists = [].concat(event.file)
+				let fileListLen = this[`fileList${event.name}`].length
+				lists.map((item) => {
+					this[`fileList${event.name}`].push({
+						...item,
+						status: 'uploading',
+						message: '上传中'
+					})
+				})
+				for (let i = 0; i < lists.length; i++) {
+					const result = await this.uploadFilePromise(lists[i].thumb)
+					let item = this[`fileList${event.name}`][fileListLen]
+					this[`fileList${event.name}`].splice(fileListLen, 1, Object.assign(item, {
+						status: 'success',
+						message: '',
+						url: result
+					}))
+					fileListLen++
+					this.reportForm.fileList = this.fileList1.map(item => {
+						return item.url.url
+					})
+				}
+			},
+			uploadFilePromise(url) {
+				return new Promise((resolve, reject) => {
+					let a = uni.uploadFile({
+						url: 'https://wx.hw.hongweisoft.com/veterans/file/upload/single/minio',
+						filePath: url,
+						name: 'file',
+						formData: {
+							user: 'test'
+						},
+						success: (res) => {
+							setTimeout(() => {
+								resolve(JSON.parse(res.data)?.data)
+							}, 1000)
+						}
+					});
+				})
+			},
+			submitForm() {
+				this.$refs.reportForm.validate().then(res => {
+					console.log(this.reportForm)
+					this.loading = true
+					this.$refs.uToast.show({
+						type: 'success',
+						title: '提示',
+						message: "提交成功",
+						iconUrl: 'https://cdn.uviewui.com/uview/demo/toast/success.png'
+					})
+					setTimeout(() => {
+						uni.$u.route({
+							url: 'pages/mine/mine',
+							type: 'switchTab'
+						})
+					}, 2000)
+				}).catch(error => {
+					
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+.problem {
+	padding: 20rpx 40rpx;
+}
+</style>

+ 120 - 0
pages/ownerInquiry/ownerInquiry.vue

@@ -0,0 +1,120 @@
+<template>
+	<view class="inquiry">
+		<view class="inquiry-input" @click="keyboardShow = true">
+			<u-code-input v-model="cardNum" :maxlength="8" size="25" :disabledKeyboard="true"></u-code-input>
+			<u-keyboard ref="uKeyboard" mode="car" :show="keyboardShow" @change="keyboardChange"
+				@confirm="keyboardConfirm" @backspace="keyboardBackspace"></u-keyboard>
+			<u-picker :show="colorShow" :columns="colorList" keyName="text" @confirm="colorConfirm"></u-picker>
+		</view>
+		<view class="inquiry-button">
+			<u-button type="primary" text="查询" @click="searchRecords"></u-button>
+		</view>
+		<view class="inquiry-tips">通过输入车牌号查询车主的停车记录</view>
+		<u-toast ref="uToast"></u-toast>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				cardNum: '',
+				colorCode: '',
+				keyboardShow: false,
+				colorList: [
+					[{
+						text: '蓝色',
+						colorCode: 0
+					}, {
+						text: '黄色',
+						colorCode: 1
+					}, {
+						text: '黑色',
+						colorCode: 2
+					}, {
+						text: '白色',
+						colorCode: 3
+					}, {
+						text: '绿色',
+						colorCode: 4
+					}, {
+						text: '其他',
+						colorCode: 99
+					}]
+				],
+				colorShow: false
+			}
+		},
+		methods: {
+			keyboardChange(val) {
+				if (this.cardNum.length < 7) {
+					this.cardNum += val
+				}
+				console.log(this.cardNum)
+			},
+			keyboardConfirm(val) {
+				this.keyboardShow = false
+				this.colorShow = true
+			},
+			keyboardBackspace() {
+				if (this.cardNum.length) this.cardNum = this.cardNum.substr(0, this.cardNum.length - 1);
+			},
+			colorConfirm(val) {
+				this.colorCode = val.value[0].colorCode
+				this.colorShow = false
+			},
+			searchRecords() {
+				if (this.cardNum.length === 7 && (this.colorCode || this.colorCode == 0)) {
+					uni.$u.route({
+						url: 'pages/ownerInquiry/queryResults/queryResults'
+					})
+				} else if (this.cardNum.length < 7 && this.colorCode){
+					this.$refs.uToast.show({
+						type: 'warning',
+						title: '提示',
+						message: "请选择正确的车牌号",
+						iconUrl: 'https://cdn.uviewui.com/uview/demo/toast/warning.png'
+					})
+				} else if (this.cardNum.length === 7 && this.colorCode === '') {
+					this.$refs.uToast.show({
+						type: 'warning',
+						title: '提示',
+						message: "请选择车牌颜色",
+						iconUrl: 'https://cdn.uviewui.com/uview/demo/toast/warning.png'
+					})
+				} else {
+					this.$refs.uToast.show({
+						type: 'warning',
+						title: '提示',
+						message: "请选择车牌号和车牌颜色",
+						iconUrl: 'https://cdn.uviewui.com/uview/demo/toast/warning.png'
+					})
+				}
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.inquiry {
+		padding: 0 40rpx;
+
+		&-input {
+			padding-top: 420rpx;
+			text-align: center;
+			width: calc(100% - 40rpx);
+			margin: 0 auto;
+		}
+
+		&-button {
+			margin-top: 100rpx;
+		}
+
+		&-tips {
+			color: #bbb;
+			font-size: 28rpx;
+			margin-top: 100rpx;
+			text-align: center;
+		}
+	}
+</style>

+ 165 - 0
pages/ownerInquiry/queryResults/queryResults.vue

@@ -0,0 +1,165 @@
+<template>
+	<view class="results">
+		<z-paging ref="paging" v-model="resultList" @query="queryList">
+			<view class="results-list">
+				<view class="results-list-item" v-for="(item, index) in resultList" :key="index">
+					<view class="results-list-item-title">
+						<view class="left">
+							<view>{{ item.cardNo }}</view>
+							<view>{{ item.roadName }}</view>
+						</view>
+						<view class="right">{{ item.status === 0 ? '已完成': '' }}</view>
+					</view>
+					<view class="results-list-item-content">
+						<view class="item">订单编号:{{ item.orderNo }}</view>
+						<view class="item" v-if="item.type === 1">开始计费:{{ item.startDate }}</view>
+						<view class="item" v-else>入场时间:{{ item.startDate }}</view>
+						<view class="item" v-if="item.type === 1">结束计费:{{ item.endDate }}</view>
+						<view class="item" v-else>出场时间:{{ item.endDate }}</view>
+						<view class="item">免费时长:{{ item.freeDuration }}</view>
+						<view class="item">计费时长:{{ item.calcDuration }}</view>
+						<view class="item">累计停车时长:{{ item.totalDuration }}</view>
+						<view class="item">应缴金额:<text>{{ item.shouldPay || 0 }}元</text></view>
+						<view class="item">实缴金额:<text>{{ item.reallyPay || 0 }}元</text></view>
+						<view class="item">泊位号:{{ item.parkNo }}</view>
+						<view class="item" v-if="item.type === 1">车位锁设备号:{{ item.lockNo }}</view>
+					</view>
+					<view class="results-list-item-bottom">
+						<view>收费标准</view>
+						<view>
+							<u-icon name="arrow-right" color="#969799" size="18"></u-icon>
+						</view>
+					</view>
+				</view>
+			</view>
+		</z-paging>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				resultList: []
+			}
+		},
+		methods: {
+			queryList(pageNo, pageSize) {
+				this.$refs.paging.complete([{
+					cardNo: '贵AR366Y',
+					roadName: '园丁路',
+					status: 0,
+					orderNo: '617947319799648256',
+					startDate: '2021-11-09 15:08:2',
+					endDate: '2021-11-10 09:23:43',
+					freeDuration: '0天0时15分0秒',
+					calcDuration: '0天17时59分16秒',
+					totalDuration: '0天18时15分16秒',
+					shouldPay: '0.24',
+					reallyPay: '0.01',
+					parkNo: 'DJ001',
+					lockNo: '2015030411',
+					type: 1
+				}, {
+					cardNo: '贵AR366Y',
+					roadName: '园丁路',
+					status: 0,
+					orderNo: '617947319799648256',
+					startDate: '2021-11-09 15:08:2',
+					endDate: '2021-11-10 09:23:43',
+					freeDuration: '0天0时15分0秒',
+					calcDuration: '0天17时59分16秒',
+					totalDuration: '0天18时15分16秒',
+					shouldPay: '0.24',
+					reallyPay: '0.01',
+					parkNo: 'DJ001',
+					lockNo: '2015030411',
+					type: 2
+				}])
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	page {
+		background-color: #F6F6FF;
+		min-height: calc(100vh - 88rpx);
+	}
+
+	.results {
+		&-list {
+			padding: 30rpx 40rpx;
+
+			&-item {
+				background-color: #fff;
+				border-radius: 14rpx;
+				margin-bottom: 20rpx;
+
+				&-title {
+					display: flex;
+					justify-content: space-between;
+					align-items: center;
+					padding: 24rpx 40rpx;
+					border-bottom: 1px solid #dfdfdf;
+					margin-bottom: 30rpx;
+
+					.left {
+						view {
+							&:first-child {
+								font-size: 32rpx;
+								font-weight: 600;
+								color: #3a3a3a;
+								line-height: 50rpx;
+								letter-spacing: 1px;
+							}
+
+							&:last-child {
+								color: #858585;
+								font-size: 26rpx;
+								line-height: 40rpx;
+							}
+						}
+					}
+
+					.right {
+						padding: 0 14rpx;
+						line-height: 50rpx;
+						border-radius: 4rpx;
+						border: 1px solid #bdbdbd;
+						color: #858585;
+					}
+				}
+
+				&-content {
+					padding: 0 40rpx 24rpx;
+					border-bottom: 1px solid #dfdfdf;
+
+					.item {
+						margin-bottom: 8rpx;
+						font-size: 26rpx;
+						font-weight: 400;
+						color: #595959;
+						line-height: 40rpx;
+						letter-spacing: 1px;
+						text {
+							color: #fa6400;
+						}
+					}
+				}
+
+				&-bottom {
+					display: flex;
+					justify-content: space-between;
+					align-items: center;
+					padding: 26rpx 32rpx;
+					font-size: 28rpx;
+					line-height: 54rpx;
+					color: #606266;
+					background-color: #fff;
+					text-align: left;
+				}
+			}
+		}
+	}
+</style>

Datei-Diff unterdrückt, da er zu groß ist
+ 1 - 0
static/icons/chengguan-wu-bujian-gonggongsheshi-daoluxinxixianshiping.svg


BIN
static/icons/clock-in-icon.png


BIN
static/icons/config-icon.png


Datei-Diff unterdrückt, da er zu groß ist
+ 1 - 0
static/icons/daozhakongzhi.svg


BIN
static/icons/geomagnetism-icon.png


Datei-Diff unterdrückt, da er zu groß ist
+ 31 - 0
static/icons/geomagnetism-icon.svg


Datei-Diff unterdrückt, da er zu groß ist
+ 1 - 0
static/icons/has-car-icon.svg


Datei-Diff unterdrückt, da er zu groß ist
+ 1 - 0
static/icons/has-ele-icon.svg


BIN
static/icons/logout-icon.png


Datei-Diff unterdrückt, da er zu groß ist
+ 1 - 0
static/icons/no-car-icon.svg


Datei-Diff unterdrückt, da er zu groß ist
+ 1 - 0
static/icons/no-ele-icon.svg


BIN
static/icons/parking-lock-icon.png


Datei-Diff unterdrückt, da er zu groß ist
+ 40 - 0
static/icons/parking-lock-icon.svg


BIN
static/icons/password-icon.png


BIN
static/icons/question-icon.png


Datei-Diff unterdrückt, da er zu groß ist
+ 40 - 0
static/icons/railing-gate-icon.svg


BIN
static/icons/railing-icon.png


BIN
static/icons/road-icon.png


Datei-Diff unterdrückt, da er zu groß ist
+ 1 - 0
static/icons/shipinjiankongyemian.svg


BIN
static/logo.png


BIN
static/tabbar/index-selected.png


BIN
static/tabbar/index.png


BIN
static/tabbar/mine-selected.png


BIN
static/tabbar/mine.png


BIN
static/tabbar/search-selected.png


BIN
static/tabbar/search.png


+ 78 - 0
uni.scss

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

+ 24 - 0
uni_modules/z-paging/changelog.md

@@ -0,0 +1,24 @@
+## 2.0.9(2021-11-04)
+1.新增`loadingFullFixed`,支持设置loading铺满屏幕。  
+2.新增`minDelay`,支持设置触发@query后最小延迟处理的时间。  
+3.新增`autoHideEmptyViewWhenPull`,支持控制用户下拉刷新时是否自动隐藏空数据图。  
+4.修复在main.js中进行z-paging的全局配置无效的问题。  
+5.修复在微信小程序中使用自动显示下拉刷新view时,请求时间很短时刷新状态无法结束的问题。  
+6.修复在nvue中使用滑动切换选项卡可能出现的无法下拉刷新的问题。  
+7.修复自动显示下拉刷新view时,上次刷新时间不会更新的问题。  
+8.在z-paging中使用popup等组件时,未能盖住全屏的问题及z-paging内view position:fixed失效的问题可以通过更新最新版HbuilderX解决。
+## 2.0.8(2021-10-14)
+1.修复`ReferenceError: getPrivateLanguage is not defined`报错的问题  
+2.修复在nvue中使用聊天记录模式时,手动调用`scrollToTop`或`scrollToBottom`时递归调用的问题。  
+3.修复使用`u-grid`时,内部item元素过多时。`z-paging`自定义的下拉刷新view与默认下拉刷新view同时展示的问题。
+## 2.0.7(2021-10-08)
+1.修复在一些平台中,底部加载更多会被遮挡的问题。  
+2.修复在nvue中`safe-area-inset-bottom`为true时,可能出现的顶部异常偏移的问题。  
+3.修复在HbuilderX 3.2.8+中,下拉刷新时@onRefresh被触发多次的问题。  
+4.修复在iOS中滚动到顶部view,在某些情况下因bounce的影响闪一下又消失的问题。  
+5.修复在使用页面滚动时,滚动到顶部view未能正常显示的问题。  
+6.修复在nvue中,使用聊天记录模式,数据未满一页时,数组被颠倒的问题。  
+7.修复在nvue中,使用页面滚动时,`scrollToTop`无效的问题。  
+8.修复在nvue中,使用聊天记录模式时,`scrollToBottom`和`scrollToTop`效果颠倒的问题。  
+9.修复在安卓 nvue中,导航栏与z-paging间出现的白色分割线的问题。  
+10.修复在HbuilderX 3.2.9+中,vue下拉刷新加载中时有一段空白间隙的问题。

+ 186 - 0
uni_modules/z-paging/components/z-paging-empty-view/z-paging-empty-view.vue

@@ -0,0 +1,186 @@
+<!-- z-paging -->
+<!-- github地址:https://github.com/SmileZXLee/uni-z-paging -->
+<!-- dcloud地址:https://ext.dcloud.net.cn/plugin?id=3935 -->
+<!-- 反馈QQ群:790460711 -->
+
+<!-- 空数据占位view,此组件支持easycom规范,可以在项目中直接引用 -->
+<template>
+	<view :class="{'zp-container':true,'zp-container-fixed':emptyViewFixed}" :style="[finalEmptyViewStyle]">
+		<view :class="{'zp-main':true,'zp-main-fixed':emptyViewFixed}">
+			<image v-if="!emptyViewImg.length" class="zp-main-image" :style="[emptyViewImgStyle]" :src="emptyImg"></image>
+			<image v-else class="zp-main-image" mode="aspectFit" :style="[emptyViewImgStyle]" :src="emptyViewImg"></image>
+			<text class="zp-main-title" :style="[emptyViewTitleStyle]">{{emptyViewText}}</text>
+			<text v-if="showEmptyViewReload" class="zp-main-error-btn" :style="[emptyViewReloadStyle]"
+				@click="reloadClick">{{emptyViewReloadText}}</text>
+		</view>
+	</view>
+</template>
+
+<script>
+	import zStatic from '../z-paging/js/z-paging-static'
+	export default {
+		data() {
+			return {
+				base64Empty: zStatic.base64Empty,
+				base64Error: zStatic.base64Error
+			};
+		},
+		props: {
+			//空数据描述文字
+			emptyViewText: {
+				type: String,
+				default: function() {
+					return '没有数据哦~'
+				}
+			},
+			//空数据图片
+			emptyViewImg: {
+				type: String,
+				default: function() {
+					return ''
+				}
+			},
+			//是否显示空数据图重新加载按钮
+			showEmptyViewReload: {
+				type: Boolean,
+				default: function() {
+					return false
+				}
+			},
+			//空数据点击重新加载文字
+			emptyViewReloadText: {
+				type: String,
+				default: function() {
+					return '重新加载'
+				}
+			},
+			//是否是加载失败
+			isLoadFailed: {
+				type: Boolean,
+				default: function() {
+					return false
+				}
+			},
+			//空数据图样式
+			emptyViewStyle: {
+				type: Object,
+				default: function() {
+					return {}
+				}
+			},
+			//空数据图img样式
+			emptyViewImgStyle: {
+				type: Object,
+				default: function() {
+					return {}
+				}
+			},
+			//空数据图描述文字样式
+			emptyViewTitleStyle: {
+				type: Object,
+				default: function() {
+					return {}
+				}
+			},
+			//空数据图重新加载按钮样式
+			emptyViewReloadStyle: {
+				type: Object,
+				default: function() {
+					return {}
+				}
+			},
+			//空数据图z-index
+			emptyViewZIndex: {
+				type: Number,
+				default: function() {
+					return 9
+				}
+			},
+			//空数据图片是否使用fixed布局并铺满z-paging
+			emptyViewFixed: {
+				type: Boolean,
+				default: function() {
+					return true
+				}
+			}
+		},
+		computed: {
+			emptyImg() {
+				if (this.isLoadFailed) {
+					return this.base64Error;
+				} else {
+					return this.base64Empty;
+				}
+			},
+			finalEmptyViewStyle(){
+				this.emptyViewStyle['z-index'] = this.emptyViewZIndex;
+				return this.emptyViewStyle;
+			}
+		},
+		methods: {
+			reloadClick() {
+				this.$emit('reload');
+			}
+		}
+	}
+</script>
+
+<style scoped>
+	.zp-container{
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		align-items: center;
+		justify-content: center;
+	}
+	.zp-container-fixed {
+		/* #ifndef APP-NVUE */
+		position: absolute;
+		top: 0;
+		left: 0;
+		width: 100%;
+		height: 100%;
+		/* #endif */
+		/* #ifdef APP-NVUE */
+		flex: 1;
+		/* #endif */
+	}
+
+	.zp-main{
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: column;
+		align-items: center;
+	}
+	
+	.zp-main-fixed {
+		/* #ifndef APP-NVUE */
+		margin-top: -150rpx;
+		/* #endif */
+		/* #ifdef APP-NVUE */
+		margin-top: -100rpx;
+		/* #endif */
+	}
+
+	.zp-main-image {
+		width: 200rpx;
+		height: 200rpx;
+	}
+
+	.zp-main-title {
+		font-size: 26rpx;
+		color: #aaaaaa;
+		text-align: center;
+		margin-top: 10rpx;
+	}
+
+	.zp-main-error-btn {
+		font-size: 26rpx;
+		padding: 8rpx 24rpx;
+		border: solid 1px #dddddd;
+		border-radius: 6rpx;
+		color: #aaaaaa;
+		margin-top: 50rpx;
+	}
+</style>

+ 88 - 0
uni_modules/z-paging/components/z-paging-swiper-item/z-paging-swiper-item.vue

@@ -0,0 +1,88 @@
+<!-- z-paging -->
+<!-- github地址:https://github.com/SmileZXLee/uni-z-paging -->
+<!-- dcloud地址:https://ext.dcloud.net.cn/plugin?id=3935 -->
+<!-- 反馈QQ群:790460711 -->
+
+<!-- 滑动切换选项卡swiper-item,此组件支持easycom规范,可以在项目中直接引用 -->
+<template>
+	<view class="zp-swiper-item-container">
+		<z-paging ref="paging" :fixed="false" @query="_queryList" @listChange="_updateList" :mounted-auto-call-reload="false"
+			style="height: 100%;">
+			<slot></slot>
+		</z-paging>
+	</view>
+</template>
+
+<script>
+	import zPaging from '../z-paging/z-paging'
+	export default {
+		name: "z-paging-swiper-item",
+		components: {
+			zPaging
+		},
+		data() {
+			return {
+				firstLoaded: false
+			}
+		},
+		props: {
+			//当前组件的index,也就是当前组件是swiper中的第几个
+			tabIndex: {
+				type: Number,
+				default: function() {
+					return 0
+				}
+			},
+			//当前swiper切换到第几个index
+			currentIndex: {
+				type: Number,
+				default: function() {
+					return 0
+				}
+			},
+		},
+		watch: {
+			currentIndex: {
+				handler(newVal, oldVal) {
+					if (newVal === this.tabIndex) {
+						//懒加载,当滑动到当前的item时,才去加载
+						if (!this.firstLoaded) {
+							this.$nextTick(()=>{
+								setTimeout(() => {
+									this.$refs.paging.reload();
+								}, 5);
+							})
+						}
+					}
+				},
+				immediate: true
+			}
+		},
+		methods: {
+			reload(data) {
+				this.$refs.paging.reload(data);
+			},
+			complete(data) {
+				this.firstLoaded = true;
+				this.$refs.paging.complete(data);
+			},
+			_queryList(pageNo, pageSize) {
+				this.$emit('query', pageNo, pageSize);
+			},
+			_updateList(list) {
+				this.$emit('updateList', list);
+			}
+		}
+	}
+</script>
+
+<style scoped>
+	.zp-swiper-item-container {
+		/* #ifndef APP-NVUE */
+		height: 100%;
+		/* #endif */
+		/* #ifdef APP-NVUE */
+		flex: 1;
+		/* #endif */
+	}
+</style>

+ 126 - 0
uni_modules/z-paging/components/z-paging-swiper/z-paging-swiper.vue

@@ -0,0 +1,126 @@
+<!-- z-paging -->
+<!-- github地址:https://github.com/SmileZXLee/uni-z-paging -->
+<!-- dcloud地址:https://ext.dcloud.net.cn/plugin?id=3935 -->
+<!-- 反馈QQ群:790460711 -->
+
+<!-- 滑动切换选项卡swiper,此组件支持easycom规范,可以在项目中直接引用 -->
+<template>
+	<view :class="fixed?'zp-swiper-container zp-swiper-container-fixed':'zp-swiper-container'" :style="[swiperStyle]">
+		<slot v-if="$slots.top" name="top"></slot>
+		<view class="zp-swiper-super">
+			<view class="zp-swiper">
+				<slot/></slot>
+			</view>
+		</view>
+		<slot v-if="$slots.bottom" name="bottom"></slot>
+	</view>
+</template>
+
+<script>
+	export default {
+		name: "z-paging-swiper",
+		data() {
+			return {
+				systemInfo: null
+			};
+		},
+		props: {
+			//是否使用fixed布局,默认为是
+			fixed: {
+				type: Boolean,
+				default: true
+			},
+			//是否开启底部安全区域适配
+			safeAreaInsetBottom: {
+				type: Boolean,
+				default: false
+			}
+		},
+		mounted() {
+			this.$nextTick(() => {
+				this.systemInfo = uni.getSystemInfoSync();
+			})
+		},
+		computed: {
+			swiperStyle() {
+				if (!this.systemInfo) {
+					return {};
+				}
+				let swiperStyle = {};
+				const windowTop = this.systemInfo.windowTop;
+				const windowBottom = this.systemInfo.windowBottom;
+				if (this.fixed) {
+					if (windowTop && windowTop !== undefined) {
+						swiperStyle.top = windowTop + 'px';
+					}
+					let bottom = 0;
+					if (windowBottom && windowBottom !== undefined) {
+						bottom = windowBottom;
+					}
+					if (this.safeAreaInsetBottom) {
+						bottom += this.safeAreaBottom;
+					}
+					swiperStyle.bottom = bottom + 'px';
+				}
+				return swiperStyle;
+			},
+			safeAreaBottom() {
+				if (!this.systemInfo) {
+					return 0;
+				}
+				let safeAreaBottom = 0;
+				// #ifdef MP-WEIXIN
+				safeAreaBottom = this.systemInfo.screenHeight - this.systemInfo.safeArea.bottom;
+				// #endif
+				// #ifdef APP-PLUS || H5
+				safeAreaBottom = this.systemInfo.safeAreaInsets.bottom || 0;
+				// #endif
+				return Math.abs(safeAreaBottom);
+			}
+		}
+	}
+</script>
+
+<style scoped>
+	.zp-swiper-container {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: column;
+		flex: 1;
+	}
+
+	.zp-swiper-container-fixed {
+		position: fixed;
+		/* #ifndef APP-NVUE */
+		height: auto;
+		width: auto;
+		/* #endif */
+		top: 0;
+		left: 0;
+		bottom: 0;
+		right: 0;
+	}
+
+	.zp-swiper-super {
+		flex: 1;
+		position: relative;
+	}
+
+	.zp-swiper {
+		/* #ifndef APP-NVUE */
+		height: 100%;
+		width: 100%;
+		position: absolute;
+		top: 0;
+		left: 0;
+		/* #endif */
+		/* #ifdef APP-NVUE */
+		flex: 1;
+		/* #endif */
+	}
+	
+	.zp-swiper-item {
+		height: 100%;
+	}
+</style>

+ 157 - 0
uni_modules/z-paging/components/z-paging/components/z-paging-load-more.vue

@@ -0,0 +1,157 @@
+<!-- [z-paging]上拉加载更多view -->
+
+<template>
+	<view class="zp-l-container" :style="[zConfig.customStyle]">
+		<text
+			:class="zConfig.defaultThemeStyle==='white'?'zp-l-line zp-l-line-white':'zp-l-line zp-l-line-black'"
+			:style="[zConfig.noMoreLineCustomStyle]"
+			v-if="zConfig.showNoMoreLine&&zConfig.status===2"></text>
+		<!-- #ifndef APP-NVUE -->
+		<image v-if="zConfig.status===1&&zConfig.loadingIconCustomImage.length"
+			:src="zConfig.loadingIconCustomImage" :class="{'zp-l-line-loading-custom-image':true,'zp-l-line-loading-custom-image-animated':zConfig.loadingAnimated}">
+		</image>
+		<image
+			v-if="zConfig.status===1&&zConfig.loadingIconType==='flower'&&!zConfig.loadingIconCustomImage.length"
+			class="zp-line-loading-image" :style="[zConfig.iconCustomStyle]"
+			:src="zConfig.defaultThemeStyle==='white'?base64FlowerWhite:base64Flower">
+		</image>
+		<!-- #endif -->
+		<!-- #ifdef APP-NVUE -->
+		<view>
+			<loading-indicator v-if="zConfig.status===1"
+				:style="[{color:zConfig.defaultThemeStyle==='white'?'white':'#777777'}]" :animating="true"
+				class="zp-line-loading-image">
+			</loading-indicator>
+		</view>
+		<!-- #endif -->
+		<text
+			v-if="zConfig.status===1&&zConfig.loadingIconType==='circle'&&!zConfig.loadingIconCustomImage.length"
+			:class="zConfig.defaultThemeStyle==='white'?'zp-l-line-loading-view zp-l-line-loading-view-white':'zp-l-line-loading-view zp-l-line-loading-view-black'"
+			:style="[zConfig.iconCustomStyle]"></text>
+		<text
+			:class="zConfig.defaultThemeStyle==='white'?'zp-l-text zp-l-text-white':'zp-l-text zp-l-text-black'">{{ownLoadingMoreText}}</text>
+		<text
+			:class="zConfig.defaultThemeStyle==='white'?'zp-l-line zp-l-line-white':'zp-l-line zp-l-line-black'"
+			:style="[zConfig.noMoreLineCustomStyle]"
+			v-if="zConfig.showNoMoreLine&&zConfig.status===2"></text>
+	</view>
+</template>
+<script>
+	import zStatic from '../js/z-paging-static'
+	export default {
+		name: 'z-paging-load-more',
+		data() {
+			return {
+				base64Arrow: zStatic.base64Arrow,
+				base64Flower: zStatic.base64Flower,
+				base64FlowerWhite: zStatic.base64FlowerWhite,
+			};
+		},
+		props: ['zConfig'],
+		computed: {
+			ownLoadingMoreText() {
+				const loadingMoreText = this.loadingStatusTextMap[this.zConfig.status];
+				return loadingMoreText;
+			},
+			loadingStatusTextMap() {
+				return {
+					0: this.zConfig.defaultText,
+					1: this.zConfig.loadingText,
+					2: this.zConfig.noMoreText,
+					3: this.zConfig.failText,
+				}
+			}
+		}
+	}
+</script>
+
+<style scoped>
+	@import "../css/z-paging-static.css";
+
+	.zp-l-container {
+		height: 80rpx;
+		font-size: 27rpx;
+		/* #ifndef APP-NVUE */
+		clear: both;
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		align-items: center;
+		justify-content: center;
+	}
+
+	.zp-l-line-loading-custom-image {
+		color: #a4a4a4;
+		margin-right: 8rpx;
+		width: 28rpx;
+		height: 28rpx;
+	}
+	
+	.zp-l-line-loading-custom-image-animated{
+		/* #ifndef APP-NVUE */
+		animation: loading-circle 1s linear infinite;
+		/* #endif */
+	}
+
+	.zp-l-line-loading-view {
+		margin-right: 8rpx;
+		width: 22rpx;
+		height: 23rpx;
+		border: 3rpx solid #dddddd;
+		border-radius: 50%;
+		/* #ifndef APP-NVUE */
+		animation: loading-circle 1s linear infinite;
+		/* #endif */
+	}
+
+	.zp-l-line-loading-view-black {
+		border-color: #c8c8c8;
+		border-top-color: #444444;
+	}
+
+	.zp-l-line-loading-view-white {
+		border-color: #aaaaaa;
+		border-top-color: #ffffff;
+	}
+
+	.zp-l-text {
+		/* #ifdef APP-NVUE */
+		font-size: 30rpx;
+		margin: 0rpx 10rpx;
+		/* #endif */
+	}
+
+	.zp-l-text-black {
+		color: #a4a4a4;
+	}
+
+	.zp-l-text-white {
+		color: #efefef;
+	}
+
+	.zp-l-line {
+		height: 1px;
+		width: 100rpx;
+		margin: 0rpx 10rpx;
+	}
+
+	.zp-l-line-black {
+		background-color: #eeeeee;
+	}
+
+	.zp-l-line-white {
+		background-color: #efefef;
+	}
+
+	@keyframes loading-circle {
+		0% {
+			-webkit-transform: rotate(0deg);
+			transform: rotate(0deg);
+		}
+
+		100% {
+			-webkit-transform: rotate(360deg);
+			transform: rotate(360deg);
+		}
+	}
+</style>

+ 284 - 0
uni_modules/z-paging/components/z-paging/components/z-paging-refresh.vue

@@ -0,0 +1,284 @@
+<!-- [z-paging]下拉刷新view -->
+
+<template>
+	<view style="height: 100%;">
+		<view
+			:class="['zp-r-container',{'zp-r-container-padding':showUpdateTime}]" style="height: 100%;">
+			<view class="zp-r-left">
+				<image v-if="status!==2" :class="refresherLeftImageClass"
+					:style="[{width: showUpdateTime?'36rpx':'30rpx',height: showUpdateTime?'36rpx':'30rpx','margin-right': showUpdateTime?'20rpx':'9rpx'},imgStyle]"
+					:src="defaultThemeStyle==='white'?(status===3?base64SuccessWhite:base64ArrowWhite):(status===3?base64Success:base64Arrow)">
+				</image>
+				<!-- #ifndef APP-NVUE -->
+				<image v-else class="zp-line-loading-image zp-r-left-image"
+					:style="[{width: showUpdateTime?'36rpx':'30rpx',height: showUpdateTime?'36rpx':'30rpx','margin-right': showUpdateTime?'20rpx':'9rpx'},imgStyle]"
+					:src="defaultThemeStyle==='white'?base64FlowerWhite:base64Flower">
+				</image>
+				<!-- #endif -->
+				<!-- #ifdef APP-NVUE -->
+				<view v-else :style="[{'margin-right':showUpdateTime?'18rpx':'12rpx'}]">
+					<loading-indicator
+						:class="systemInfo.platform==='ios'?'zp-loading-image-ios':'zp-loading-image-android'"
+						:style="[{color:defaultThemeStyle==='white'?'white':'#777777'},imgStyle]" :animating="true">
+					</loading-indicator>
+				</view>
+				<!-- #endif -->
+			</view>
+			<view class="zp-r-right">
+				<text class="zp-r-right-text"
+					:style="[refresherRightTextStyle,titleStyle]">{{refresherStatusTextMap[status]||defaultText}}
+				</text>
+				<text class="zp-r-right-text zp-r-right-time-text"
+					:style="[refresherRightTextStyle,updateTimeStyle]"
+					v-if="showUpdateTime&&refresherTimeText.length">{{refresherTimeText}}
+				</text>
+			</view>
+		</view>
+	</view>
+</template>
+<script>
+	const systemInfo = uni.getSystemInfoSync();
+	import zStatic from '../js/z-paging-static'
+	import {
+		getRefesrherFormatTimeByKey
+	} from '../js/z-paging-utils'
+	export default {
+		name: 'z-paging-refresh',
+		data() {
+			return {
+				systemInfo: systemInfo,
+				base64Arrow: zStatic.base64Arrow,
+				base64ArrowWhite: zStatic.base64ArrowWhite,
+				base64Flower: zStatic.base64Flower,
+				base64FlowerWhite: zStatic.base64FlowerWhite,
+				base64Success: zStatic.base64Success,
+				base64SuccessWhite: zStatic.base64SuccessWhite,
+				refresherTimeText: '',
+				leftImageLoaded: false
+			};
+		},
+		props: {
+			'status': {
+				default: 0
+			},
+			'defaultThemeStyle': {},
+			'defaultText': {},
+			'pullingText': {},
+			'refreshingText': {},
+			'completeText': {},
+			'showUpdateTime': {
+				default: false
+			},
+			'updateTimeKey': {},
+			'imgStyle': {
+				default: {}
+			},
+			'titleStyle': {
+				default: {}
+			},
+			'updateTimeStyle': {
+				default: {}
+			},
+		},
+		computed: {
+			refresherStatusTextMap() {
+				this.updateTime(this.updateTimeKey);
+				return {
+					0: this.defaultText,
+					1: this.pullingText,
+					2: this.refreshingText,
+					3: this.completeText
+				};
+			},
+			refresherLeftImageClass() {
+				if(this.status === 3){
+					return 'zp-r-left-image-no-transform .zp-r-left-image-pre-size';
+				}
+				let refresherLeftImageClass = 'zp-r-left-image ';
+				if (this.status === 0) {
+					if (this.leftImageLoaded) {
+						refresherLeftImageClass += 'zp-r-arrow-down';
+					} else {
+						this.leftImageLoaded = true;
+						refresherLeftImageClass += 'zp-r-arrow-down-no-duration';
+					}
+				} else {
+					refresherLeftImageClass += 'zp-r-arrow-top';
+				}
+				return refresherLeftImageClass + ' zp-r-left-image-pre-size';
+			},
+			refresherRightTextStyle() {
+				let refresherRightTextStyle = {};
+				let color = '#555555';
+				if (this.defaultThemeStyle === 'white') {
+					color = '#efefef';
+				}
+				// #ifdef APP-NVUE
+				if (this.showUpdateTime) {
+					refresherRightTextStyle = {
+						'height': '40rpx',
+						'line-height': '40rpx'
+					};
+				} else {
+					refresherRightTextStyle = {
+						'height': '80rpx',
+						'line-height': '80rpx'
+					};
+				}
+				// #endif
+				refresherRightTextStyle['color'] = color;
+				return refresherRightTextStyle;
+			}
+		},
+		methods: {
+			updateTime(updateTimeKey) {
+				if (!updateTimeKey) {
+					updateTimeKey = this.updateTimeKey;
+				}
+				if (this.showUpdateTime) {
+					this.refresherTimeText = getRefesrherFormatTimeByKey(updateTimeKey);
+				}
+			}
+		}
+	}
+</script>
+
+<style scoped>
+	@import "../css/z-paging-static.css";
+
+	.zp-r-container {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		justify-content: center;
+		align-items: center;
+	}
+
+	.zp-r-container-padding {
+		/* #ifdef APP-NVUE */
+		padding: 15rpx 0rpx;
+		/* #endif */
+	}
+
+	.zp-r-left {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		align-items: center;
+		overflow: hidden;
+	}
+
+	.zp-r-left-image {
+		/* #ifndef APP-NVUE */
+		transform: rotate(180deg);
+		margin-top: 2rpx;
+		/* #endif */
+		/* #ifdef APP-NVUE */
+		transition-duration: .2s;
+		transition-property: transform;
+		color: #666666;
+		/* #endif */
+	}
+	
+	.zp-r-left-image-no-transform {
+		/* #ifndef APP-NVUE */
+		margin-top: 2rpx;
+		/* #endif */
+		/* #ifdef APP-NVUE */
+		transition-duration: .2s;
+		transition-property: transform;
+		color: #666666;
+		/* #endif */
+	}
+	
+	.zp-r-left-image-pre-size{
+		/* #ifndef APP-NVUE */
+		width: 30rpx;
+		width: 30rpx;
+		overflow: hidden;
+		/* #endif */
+	}
+
+	.zp-r-arrow-top {
+		/* #ifndef APP-NVUE */
+		animation: refresher-arrow-top .2s linear;
+		-webkit-animation: refresher-arrow-top .2s linear;
+		animation-fill-mode: forwards;
+		-webkit-animation-fill-mode: forwards;
+		/* #endif */
+		/* #ifdef APP-NVUE */
+		transform: rotate(0deg);
+		/* #endif */
+	}
+
+	.zp-r-arrow-down {
+		/* #ifndef APP-NVUE */
+		animation: refresher-arrow-down .2s linear;
+		-webkit-animation: refresher-arrow-down .2s linear;
+		animation-fill-mode: forwards;
+		-webkit-animation-fill-mode: forwards;
+		/* #endif */
+		/* #ifdef APP-NVUE */
+		transform: rotate(180deg);
+		/* #endif */
+	}
+
+	.zp-r-arrow-down-no-duration {
+		/* #ifndef APP-NVUE */
+		animation: refresher-arrow-down 0s linear;
+		-webkit-animation: refresher-arrow-down 0s linear;
+		animation-fill-mode: forwards;
+		-webkit-animation-fill-mode: forwards;
+		/* #endif */
+		/* #ifdef APP-NVUE */
+		transform: rotate(180deg);
+		/* #endif */
+	}
+
+	.zp-r-right {
+		font-size: 27rpx;
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: column;
+		align-items: center;
+		justify-content: center;
+	}
+
+	.zp-r-right-text {
+		/* #ifdef APP-NVUE */
+		font-size: 28rpx;
+		/* #endif */
+	}
+
+	.zp-r-right-time-text {
+		margin-top: 10rpx;
+		font-size: 24rpx;
+	}
+
+	@keyframes refresher-arrow-top {
+		0% {
+			-webkit-transform: rotate(180deg);
+			transform: rotate(180deg);
+		}
+
+		100% {
+			-webkit-transform: rotate(0deg);
+			transform: rotate(0deg);
+		}
+	}
+
+	@keyframes refresher-arrow-down {
+		0% {
+			-webkit-transform: rotate(0deg);
+			transform: rotate(0deg);
+		}
+
+		100% {
+			-webkit-transform: rotate(180deg);
+			transform: rotate(180deg);
+		}
+	}
+</style>

+ 1 - 0
uni_modules/z-paging/components/z-paging/config/index.js

@@ -0,0 +1 @@
+// z-paging全局配置文件,注意避免更新时此文件被覆盖,若被覆盖,可在此文件中右键->点击本地历史记录,找回覆盖前的配置

+ 185 - 0
uni_modules/z-paging/components/z-paging/css/z-paging-main.css

@@ -0,0 +1,185 @@
+/* [z-paging]公共css*/
+
+.z-paging-content {
+	position: relative;
+	/* #ifndef APP-NVUE */
+	display: flex;
+	width: 100%;
+	height: 100%;
+	/* #endif */
+	flex-direction: column;
+}
+
+.z-paging-content-fixed, .zp-loading-fixed {
+	position: fixed;
+	/* #ifndef APP-NVUE */
+	height: auto;
+	width: auto;
+	/* #endif */
+	top: 0;
+	left: 0;
+	bottom: 0;
+	right: 0;
+}
+
+.zp-page-scroll-top,
+.zp-page-scroll-bottom {
+	/* #ifndef APP-NVUE */
+	width: auto;
+	/* #endif */
+	position: fixed;
+	left: 0;
+	right: 0;
+	z-index: 999;
+}
+
+.zp-scroll-view-super {
+	flex: 1;
+	position: relative;
+}
+
+.zp-custom-refresher-container {
+	overflow: hidden;
+}
+
+.zp-scroll-view {
+	height: 100%;
+	width: 100%;
+}
+
+.zp-scroll-view-absolute {
+	position: absolute;
+	top: 0;
+	left: 0;
+}
+
+/* #ifndef APP-NVUE */
+.zp-scroll-view-hide-scrollbar ::-webkit-scrollbar {
+	display: none;
+	-webkit-appearance: none;
+	width: 0 !important;
+	height: 0 !important;
+	background: transparent;
+}
+
+/* #endif */
+
+.zp-paging-touch-view {
+	width: 100%;
+	height: 100%;
+	position: relative;
+}
+
+.zp-fixed-bac-view {
+	position: absolute;
+	width: 100%;
+	top: 0;
+	left: 0;
+	height: 200px;
+}
+
+.zp-paging-main {
+	height: 100%;
+	/* #ifndef APP-NVUE */
+	display: flex;
+	/* #endif */
+	flex-direction: column;
+}
+
+.zp-paging-container {
+	flex: 1;
+	position: relative;
+	/* #ifndef APP-NVUE */
+	display: flex;
+	/* #endif */
+	flex-direction: column;
+}
+
+.zp-chat-record-loading-container {
+	/* #ifndef APP-NVUE */
+	display: flex;
+	width: 100%;
+	/* #endif */
+	/* #ifdef APP-NVUE */
+	width: 750rpx;
+	/* #endif */
+	align-items: center;
+	justify-content: center;
+	height: 60rpx;
+	font-size: 26rpx;
+}
+
+.zp-chat-record-loading-custom-image {
+	width: 35rpx;
+	height: 35rpx;
+	/* #ifndef APP-NVUE */
+	animation: loading-flower 1s linear infinite;
+	/* #endif */
+}
+
+.zp-custom-refresher-container {
+	/* #ifndef APP-NVUE */
+	display: flex;
+	/* #endif */
+	flex-direction: row;
+	justify-content: center;
+	align-items: center;
+}
+
+.zp-back-to-top {
+	width: 76rpx;
+	height: 76rpx;
+	z-index: 999;
+	position: absolute;
+	bottom: 0rpx;
+	right: 25rpx;
+	transition-duration: .3s;
+	transition-property: opacity;
+}
+
+.zp-back-to-top-show {
+	opacity: 1;
+}
+
+.zp-back-to-top-hide {
+	opacity: 0;
+}
+
+.zp-back-to-top-img {
+	/* #ifndef APP-NVUE */
+	width: 100%;
+	height: 100%;
+	/* #endif */
+	/* #ifdef APP-NVUE */
+	flex: 1;
+	/* #endif */
+	z-index: 999;
+}
+
+.zp-empty-view {
+	/* #ifdef APP-NVUE */
+	height: 100%;
+	/* #endif */
+	flex: 1;
+}
+
+.zp-empty-view-center {
+	/* #ifndef APP-NVUE */
+	display: flex;
+	/* #endif */
+	flex-direction: column;
+	align-items: center;
+	justify-content: center;
+}
+
+.zp-loading-fixed{
+	z-index: 9999;
+}
+
+.zp-n-refresh-container {
+	/* #ifndef APP-NVUE */
+	display: flex;
+	/* #endif */
+	justify-content: center;
+	width: 750rpx;
+}

+ 33 - 0
uni_modules/z-paging/components/z-paging/css/z-paging-static.css

@@ -0,0 +1,33 @@
+/* [z-paging]公用的静态css资源 */
+
+.zp-line-loading-image {
+	margin-right: 8rpx;
+	width: 28rpx;
+	height: 28rpx;
+	/* #ifndef APP-NVUE */
+	animation: loading-flower 1s steps(12) infinite;
+	/* #endif */
+	color: #666666;
+}
+
+.zp-loading-image-ios{
+	width: 20px;
+	height: 20px;
+}
+
+.zp-loading-image-android{
+	width: 32rpx;
+	height: 32rpx;
+}
+
+@keyframes loading-flower {
+	0% {
+		-webkit-transform: rotate(0deg);
+		transform: rotate(0deg);
+	}
+
+	to {
+		-webkit-transform: rotate(1turn);
+		transform: rotate(1turn);
+	}
+}

+ 28 - 0
uni_modules/z-paging/components/z-paging/js/z-paging-config.js

@@ -0,0 +1,28 @@
+// [z-paging]处理main.js中的配置信息工具
+
+let config = null;
+let getedStorage = false;
+const storageKey = 'Z-PAGING-CONFIG-STORAGE-KEY'
+
+function setConfig(value) {
+	try {
+		uni.setStorageSync(storageKey, value);
+	} catch {}
+}
+
+function getConfig() {
+	try {
+		if (getedStorage) {
+			return config;
+		}
+		config = uni.getStorageSync(storageKey);
+		getedStorage = true;
+	} catch {
+		return null;
+	}
+}
+
+module.exports = {
+	setConfig,
+	getConfig
+};

+ 25 - 0
uni_modules/z-paging/components/z-paging/js/z-paging-enum.js

@@ -0,0 +1,25 @@
+// [z-paging]枚举
+
+const Enum = {
+	//当前加载类型 0-下拉刷新 1-上拉加载更多
+	LoadingType: {
+		Refresher: 0,
+		LoadingMore: 1
+	},
+	//下拉刷新状态 0-默认状态 1.松手立即刷新 2.刷新中 3.刷新结束
+	RefresherStatus: {
+		Default: 0,
+		PullToRefresh: 1,
+		Loading: 2,
+		Complete: 3
+	},
+	//底部加载更多状态 0-默认状态 1.加载中 2.没有更多数据 3.加载失败
+	LoadingMoreStatus: {
+		Default: 0,
+		Loading: 1,
+		NoMore: 2,
+		Fail: 3
+	}
+}
+
+module.exports = Enum;

+ 153 - 0
uni_modules/z-paging/components/z-paging/js/z-paging-i18n.js

@@ -0,0 +1,153 @@
+// z-paging国际化(支持中文、中文繁体和英文)
+
+const i18nUpdateKey = 'z-paging-i18n-update';
+
+const refresherDefaultText = {
+	'en': 'Pull down to refresh',
+	'zh-cn': '继续下拉刷新',
+	'zh-hant-cn': '繼續下拉重繪',
+}
+const refresherPullingText = {
+	'en': 'Release to refresh',
+	'zh-cn': '松开立即刷新',
+	'zh-hant-cn': '鬆開立即重繪',
+}
+const refresherRefreshingText = {
+	'en': 'Refreshing...',
+	'zh-cn': '正在刷新...',
+	'zh-hant-cn': '正在重繪...',
+}
+const refresherCompleteText = {
+	'en': 'Refresh succeeded',
+	'zh-cn': '刷新成功',
+	'zh-hant-cn': '重繪成功',
+}
+
+const loadingMoreDefaultText = {
+	'en': 'Click to load more',
+	'zh-cn': '点击加载更多',
+	'zh-hant-cn': '點擊加載更多',
+}
+const loadingMoreLoadingText = {
+	'en': 'Loading...',
+	'zh-cn': '正在加载...',
+	'zh-hant-cn': '正在加載...',
+}
+const loadingMoreNoMoreText = {
+	'en': 'No more data',
+	'zh-cn': '没有更多了',
+	'zh-hant-cn': '沒有更多了',
+}
+const loadingMoreFailText = {
+	'en': 'Load failed,click to reload',
+	'zh-cn': '加载失败,点击重新加载',
+	'zh-hant-cn': '加載失敗,點擊重新加載',
+}
+
+const emptyViewText = {
+	'en': 'No data',
+	'zh-cn': '没有数据哦~',
+	'zh-hant-cn': '沒有數據哦~',
+}
+
+const emptyViewReloadText = {
+	'en': 'Reload',
+	'zh-cn': '重新加载',
+	'zh-hant-cn': '重新加載',
+}
+
+const emptyViewErrorText = {
+	'en': 'Sorry,load failed',
+	'zh-cn': '很抱歉,加载失败',
+	'zh-hant-cn': '很抱歉,加載失敗',
+}
+
+const refresherUpdateTimeText = {
+	'en': 'Last update: ',
+	'zh-cn': '最后更新:',
+	'zh-hant-cn': '最後更新:',
+}
+
+const refresherUpdateTimeNoneText = {
+	'en': 'None',
+	'zh-cn': '无',
+	'zh-hant-cn': '無',
+}
+
+const refresherUpdateTimeTodayText = {
+	'en': 'Today',
+	'zh-cn': '今天',
+	'zh-hant-cn': '今天',
+}
+
+const refresherUpdateTimeYesterdayText = {
+	'en': 'Yesterday',
+	'zh-cn': '昨天',
+	'zh-hant-cn': '昨天',
+}
+
+// 获取当前语言,格式为:zh-cn、zh-hant-cn、en。followSystemLanguage:获取的结果是否是在不跟随系统语言下获取到的
+function getLanguage(followSystemLanguage = true) {
+	return _getPrivateLanguage(false, followSystemLanguage);
+}
+
+// 获取当前语言,格式为:简体中文、繁體中文、English。followSystemLanguage:获取的结果是否是在不跟随系统语言下获取到的
+function getLanguageName(followSystemLanguage = true) {
+	const language = getLanguage(followSystemLanguage);
+	const languageNameMap = {
+		'zh-cn': '简体中文',
+		'zh-hant-cn': '繁體中文',
+		'en': 'English'
+	};
+	return languageNameMap[language];
+}
+
+//设置当前语言,格式为:zh-cn、zh-hant-cn、en
+function setLanguage(myLanguage) {
+	uni.setStorageSync(i18nUpdateKey, myLanguage);
+	uni.$emit(i18nUpdateKey, myLanguage);
+}
+
+// 插件内部使用,请勿直接调用
+function _getPrivateLanguage(myLanguage, followSystemLanguage = true) {
+	let systemLanguage = '';
+	if (followSystemLanguage) {
+		systemLanguage = uni.getSystemInfoSync().language;
+	}
+	let language = myLanguage || uni.getStorageSync(i18nUpdateKey) || systemLanguage;
+	language = language.toLowerCase();
+	var reg = new RegExp('_', '');
+	language = language.replace(reg, '-');
+	if (language.indexOf('zh') !== -1) {
+		if (language === 'zh' || language === 'zh-cn' || language.indexOf('zh-hans') !== -1) {
+			return 'zh-cn';
+		}
+		return 'zh-hant-cn';
+	}
+	if (language.indexOf('en') !== -1) {
+		return 'en';
+	}
+	return 'zh-cn';
+}
+
+module.exports = {
+	refresherDefaultText,
+	refresherPullingText,
+	refresherRefreshingText,
+	refresherCompleteText,
+	refresherUpdateTimeText,
+	refresherUpdateTimeNoneText,
+	refresherUpdateTimeTodayText,
+	refresherUpdateTimeYesterdayText,
+	loadingMoreDefaultText,
+	loadingMoreLoadingText,
+	loadingMoreNoMoreText,
+	loadingMoreFailText,
+	emptyViewText,
+	emptyViewReloadText,
+	emptyViewErrorText,
+	getLanguage,
+	getLanguageName,
+	setLanguage,
+	_getPrivateLanguage,
+}

Datei-Diff unterdrückt, da er zu groß ist
+ 3120 - 0
uni_modules/z-paging/components/z-paging/js/z-paging-main.js


+ 32 - 0
uni_modules/z-paging/components/z-paging/js/z-paging-mixin.js

@@ -0,0 +1,32 @@
+// [z-paging]使用页面滚动时引入此mixin,用于监听和处理onPullDownRefresh等页面生命周期方法
+
+const ZPagingMixin = {
+	onPullDownRefresh() {
+		if (this.isPagingRefNotFound()) {
+			return;
+		}
+		this.$refs.paging.reload();
+	},
+	onPageScroll(e) {
+		if (this.isPagingRefNotFound()) {
+			return;
+		}
+		this.$refs.paging.updatePageScrollTop(e.scrollTop);
+		if (e.scrollTop < 10) {
+			this.$refs.paging.doChatRecordLoadMore();
+		}
+	},
+	onReachBottom() {
+		if (this.isPagingRefNotFound()) {
+			return;
+		}
+		this.$refs.paging.doLoadMore();
+	},
+	methods: {
+		isPagingRefNotFound() {
+			return !this.$refs.paging || this.$refs.paging === undefined;
+		}
+	}
+}
+
+export default ZPagingMixin;

Datei-Diff unterdrückt, da er zu groß ist
+ 23 - 0
uni_modules/z-paging/components/z-paging/js/z-paging-static.js


+ 199 - 0
uni_modules/z-paging/components/z-paging/js/z-paging-utils.js

@@ -0,0 +1,199 @@
+// [z-paging]工具类
+
+import zI18n from './z-paging-i18n'
+
+const storageKey = 'Z-PAGING-REFRESHER-TIME-STORAGE-KEY'
+
+//判断两个数组是否相等
+function arrayIsEqual(arr1, arr2) {
+	if (arr1 === arr2) {
+		return true;
+	}
+	if (arr1.length !== arr2.length) {
+		return false;
+	}
+	for (let i = 0; i < arr1.length; i++) {
+		if (arr1[i] !== arr2[i]) {
+			return false;
+		}
+	}
+	return true;
+}
+
+//获取最终的touch位置
+function getCommonTouch(e) {
+	let touch = null;
+	if (e.touches && e.touches.length) {
+		touch = e.touches[0];
+	} else if (e.changedTouches && e.changedTouches.length) {
+		touch = e.changedTouches[0];
+	} else if (e.datail && e.datail !== {}) {
+		touch = e.datail;
+	} else {
+		return {
+			touchX: 0,
+			touchY: 0
+		}
+	}
+	return {
+		touchX: touch.clientX,
+		touchY: touch.clientY
+	};
+}
+
+//判断当前手势是否在z-paging内触发
+function getTouchFromZPaging(target) {
+	if (target && target.tagName && target.tagName !== 'BODY' && target.tagName !== 'UNI-PAGE-BODY') {
+		var classList = target.classList;
+		if (classList && classList.contains('zp-paging-touch-view')) {
+			return true;
+		} else {
+			return getTouchFromZPaging(target.parentNode);
+		}
+	} else {
+		return false;
+	}
+}
+
+//获取z-paging所在的parent
+function getParent(parent) {
+	if (!parent) {
+		return null;
+	}
+	if (parent.$refs.paging) {
+		return parent;
+	}
+	return getParent(parent.$parent);
+}
+
+//打印错误信息
+function consoleErr(err) {
+	console.error(`[z-paging]${err}`);
+}
+
+//打印警告信息
+function consoleWarn(warn) {
+	console.warn(`[z-paging]${warn}`);
+}
+
+//设置下拉刷新时间
+function setRefesrherTime(time, key) {
+	try {
+		let datas = getRefesrherTime();
+		if (!datas) {
+			datas = {};
+		}
+		datas[key] = time;
+		uni.setStorageSync(storageKey, datas);
+	} catch {}
+}
+
+//获取下拉刷新时间
+function getRefesrherTime() {
+	try {
+		const datas = uni.getStorageSync(storageKey);
+		return datas;
+	} catch {
+		return null;
+	}
+}
+
+//通过下拉刷新标识key获取下拉刷新时间
+function getRefesrherTimeByKey(key) {
+	const datas = getRefesrherTime();
+	if (datas) {
+		const data = datas[key];
+		if (data) {
+			return data;
+		}
+	}
+	return null;
+}
+
+//通过下拉刷新标识key获取下拉刷新时间(格式化之后)
+function getRefesrherFormatTimeByKey(key) {
+	const time = getRefesrherTimeByKey(key);
+	let timeText = zI18n['refresherUpdateTimeNoneText'][zI18n.getLanguage()];
+	if (time) {
+		timeText = _timeFormat(time);
+	}
+	return `${zI18n['refresherUpdateTimeText'][zI18n.getLanguage()]}${timeText}`;
+}
+
+//将文本的px或者rpx转为px的值
+function convertTextToPx(text) {
+	const dataType = Object.prototype.toString.call(text);
+	if (dataType === '[object Number]') {
+		return text;
+	}
+	let isRpx = false;
+	if (text.indexOf('rpx') !== -1 || text.indexOf('upx') !== -1) {
+		text = text.replace('rpx', '').replace('upx', '');
+		isRpx = true;
+	} else if (text.indexOf('px') !== -1) {
+		text = text.replace('px', '');
+	}
+	if (!isNaN(text)) {
+		if (isRpx) {
+			return Number(uni.upx2px(text));
+		}
+		return Number(text);
+	}
+	return 0;
+}
+
+//------------------ 私有方法 ------------------------
+function _timeFormat(time) {
+	const date = new Date(time);
+	const currentDate = new Date();
+	const dateDay = new Date(time).setHours(0, 0, 0, 0);
+	const currentDateDay = new Date().setHours(0, 0, 0, 0);
+	const disTime = dateDay - currentDateDay;
+	let dayStr = '';
+	const timeStr = _dateTimeFormat(date);
+	if (disTime === 0) {
+		dayStr = zI18n['refresherUpdateTimeTodayText'][zI18n.getLanguage()];
+	} else if (disTime === -86400000) {
+		dayStr = zI18n['refresherUpdateTimeYesterdayText'][zI18n.getLanguage()];
+	} else {
+		dayStr = _dateDayFormat(date, date.getFullYear() !== currentDate.getFullYear());
+	}
+	return `${dayStr} ${timeStr}`;
+}
+
+function _dateDayFormat(date, showYear = true) {
+	const year = date.getFullYear();
+	const month = date.getMonth() + 1;
+	const day = date.getDate();
+	if (showYear) {
+		return `${year}-${_fullZeroToTwo(month)}-${_fullZeroToTwo(day)}`;
+	} else {
+		return `${_fullZeroToTwo(month)}-${_fullZeroToTwo(day)}`;
+	}
+}
+
+function _dateTimeFormat(date) {
+	const hour = date.getHours();
+	const minute = date.getMinutes();
+	return `${_fullZeroToTwo(hour)}:${_fullZeroToTwo(minute)}`;
+}
+
+function _fullZeroToTwo(str) {
+	str = str.toString();
+	if (str.length === 1) {
+		return '0' + str;
+	}
+	return str;
+}
+
+module.exports = {
+	setRefesrherTime,
+	getRefesrherFormatTimeByKey,
+	arrayIsEqual,
+	getCommonTouch,
+	getTouchFromZPaging,
+	getParent,
+	convertTextToPx,
+	consoleErr,
+	consoleWarn
+};

+ 54 - 0
uni_modules/z-paging/components/z-paging/wxs/z-paging-renderjs.js

@@ -0,0 +1,54 @@
+// [z-paging]使用renderjs在app-vue和h5中对touchmove事件冒泡进行处理
+
+import zUtils from '../js/z-paging-utils'
+var data = {
+	renderScrollTop: 0,
+	renderUsePageScroll: false,
+	renderIsIos: uni.getSystemInfoSync().platform === 'ios',
+	startY: 0,
+	isTouchFromZPaging: false
+}
+
+export default {
+	mounted() {
+		this._handleTouch();
+	},
+	methods: {
+		//接收逻辑层发送的数据
+		renderPropScrollTopChange(newVal, oldVal, ownerVm, vm) {
+			data.renderScrollTop = newVal;
+		},
+		renderPropUsePageScrollChange(newVal, oldVal, ownerVm, vm) {
+			if(newVal !== -1){
+				data.renderUsePageScroll = newVal;
+			}
+		},
+		//拦截处理touch事件
+		_handleTouch() {
+			if (window && !window.$zPagingRenderJsInited) {
+				window.$zPagingRenderJsInited = true;
+				window.addEventListener('touchstart', this._handleTouchstart, {
+					passive: true
+				})
+				window.addEventListener('touchmove', this._handleTouchmove, {
+					passive: false
+				})
+			}
+		},
+		_handleTouchstart(e) {
+			const touch = zUtils.getCommonTouch(e);
+			data.startY = touch.touchY;
+			data.isTouchFromZPaging = zUtils.getTouchFromZPaging(e.target);
+		},
+		_handleTouchmove(e) {
+			const touch = zUtils.getCommonTouch(e);
+			var moveY = touch.touchY - data.startY;
+			if ((data.isTouchFromZPaging && data.renderScrollTop < 1 && moveY > 0) || (data.isTouchFromZPaging && data.renderIsIos && !data.renderUsePageScroll && moveY < 0)) {
+				if (e.cancelable && !e.defaultPrevented) {
+					e.preventDefault();
+				}
+			}
+		},
+
+	}
+};

+ 342 - 0
uni_modules/z-paging/components/z-paging/wxs/z-paging-wxs.wxs

@@ -0,0 +1,342 @@
+// [z-paging]微信小程序、QQ小程序、app-vue、h5上使用wxs实现自定义下拉刷新,降低逻辑层与视图层的通信折损,提升性能
+
+var currentMoveDistance = 0;
+
+function propObserver(newValue, oldValue, ownerInstance, instance) {
+	var state = ownerInstance.getState();
+	state.currentInstance = instance;
+	var dataset = instance.getDataset();
+	var loading = dataset.loading == true;
+	if (newValue.indexOf('end') != -1) {
+		_setTransform('translateY(0px)', instance, false)
+		state.moveDistance = 0;
+		state.oldMoveDistance = 0;
+		currentMoveDistance = 0;
+	} else if (newValue.indexOf('begin') != -1) {
+		var refresherThreshold = instance.getDataset().refresherthreshold
+		_setTransformValue(refresherThreshold, instance, state, false);
+	}
+}
+
+function touchstart(e, ownerInstance) {
+	var instance = ownerInstance.getState().currentInstance;
+	var state = instance.getState();
+	var dataset = instance.getDataset();
+	var isTouchEnded = state.isTouchEnded;
+	if (_getRefresherTouchDisabled(e, instance, 0)) {
+		return;
+	}
+	state.oldMoveDistance = 0;
+	var touch = _getCommonTouch(e);
+	var loading = _getIsTrue(dataset.loading);
+	state.startY = touch.touchY;
+	state.lastRefresherTouchmove = touch;
+	if (!loading && isTouchEnded) {
+		state.isTouchmoving = false;
+	}
+	state.isTouchEnded = false;
+	ownerInstance.callMethod('_handleRefresherTouchstart', touch);
+}
+
+function touchmove(e, ownerInstance) {
+	var touch = _getCommonTouch(e);
+	var instance = ownerInstance.getState().currentInstance;
+	var dataset = instance.getDataset();
+	var refresherThreshold = dataset.refresherthreshold;
+	var state = instance.getState();
+	if (_getRefresherTouchDisabled(e, instance, 1)) {
+		_handleTouchMovePullingDown(state, ownerInstance, false);
+		return true;
+	}
+	if (!_getAngleIsInRange(e, touch, state, dataset)) {
+		_handleTouchMovePullingDown(state, ownerInstance, false);
+		return true;
+	}
+	var moveDistanceObj = _getMoveDistance(e, instance);
+	var moveDistance = moveDistanceObj.currentMoveDistance;
+	var prevent = moveDistanceObj.isDown;
+	if (moveDistance < 0) {
+		_setTransformValue(0, instance, state, false);
+		_handleTouchMovePullingDown(state, ownerInstance, false);
+		return true;
+	}
+	if (prevent && !state.disabledBounce) {
+		ownerInstance.callMethod('_handleScrollViewDisableBounce', {
+			bounce: false
+		});
+		state.disabledBounce = true;
+		_handleTouchMovePullingDown(state, ownerInstance, prevent);
+		return !prevent;
+	}
+	_setTransformValue(moveDistance, instance, state, false);
+	var oldRefresherStatus = state.refresherStatus;
+	var dataset = instance.getDataset();
+	var oldIsTouchmoving = _getIsTrue(dataset.oldistouchmoving);
+	var isTouchmoving = state.isTouchmoving;
+	if (moveDistance >= refresherThreshold) {
+		state.refresherStatus = 1;
+	} else {
+		state.refresherStatus = 0;
+	}
+	if (!isTouchmoving) {
+		state.isTouchmoving = true;
+		isTouchmoving = true;
+	}
+	if (state.isTouchEnded) {
+		state.isTouchEnded = false;
+	}
+	if (oldRefresherStatus == undefined || oldRefresherStatus != state.refresherStatus || oldIsTouchmoving !=
+		isTouchmoving) {
+		ownerInstance.callMethod('_handleRefresherTouchmove', moveDistance, touch);
+	}
+	_handleTouchMovePullingDown(state, ownerInstance, prevent);
+	return !prevent;
+}
+
+function touchend(e, ownerInstance) {
+	var touch = _getCommonTouch(e);
+	var instance = ownerInstance.getState().currentInstance;
+	var dataset = instance.getDataset();
+	var state = instance.getState();
+	if (_getRefresherTouchDisabled(e, instance, 2)) {
+		return;
+	}
+	state.refresherReachMaxAngle = true;
+	state.hitReachMaxAngleCount = 0;
+	state.disabledBounce = false;
+	state.fixedIsTopHitCount = 0;
+	//ownerInstance.callMethod('_handleScrollViewDisableBounce', {bounce:true});
+	var isTouchmoving = state.isTouchmoving;
+	if (!isTouchmoving) {
+		return;
+	}
+	var oldRefresherStatus = state.refresherStatus;
+	var oldMoveDistance = state.moveDistance;
+	var refresherThreshold = instance.getDataset().refresherthreshold
+	var moveDistance = _getMoveDistance(e, instance).currentMoveDistance;
+	if (!(moveDistance >= refresherThreshold && oldRefresherStatus === 1)) {
+		state.isTouchmoving = false;
+	}
+	ownerInstance.callMethod('_handleRefresherTouchend', moveDistance);
+	state.isTouchEnded = true;
+	if (oldMoveDistance < refresherThreshold) {
+		return;
+	}
+	var animate = false;
+	if (moveDistance >= refresherThreshold) {
+		moveDistance = refresherThreshold;
+		var isIos13 = _getIsTrue(dataset.isios13);
+		if (isIos13) {
+			animate = true;
+		}
+	}
+	_setTransformValue(moveDistance, instance, state, animate);
+}
+
+// #ifdef H5
+function isPC() {
+	var userAgentInfo = navigator.userAgent;
+	var Agents = ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"];
+	var flag = true;
+	for (var v = 0; v < Agents.length - 1; v++) {
+		if (userAgentInfo.indexOf(Agents[v]) > 0) {
+			flag = false;
+			break;
+		}
+	}
+	return flag;
+}
+
+var movable = false;
+
+function mousedown(e, ins) {
+	if (!isPC()) return;
+	touchstart(e, ins);
+	movable = true;
+}
+
+function mousemove(e, ins) {
+	if (!isPC()) return;
+	if (!movable) return;
+	touchmove(e, ins);
+}
+
+function mouseup(e, ins) {
+	if (!isPC()) return;
+	touchend(e, ins);
+	movable = false;
+}
+
+function mouseleave(e, ins) {
+	if (!isPC()) return;
+	movable = false;
+}
+// #endif
+
+
+function _setTransformValue(value, instance, state, animate) {
+	value = value || 0;
+	if (state.moveDistance == value) {
+		return;
+	}
+	state.moveDistance = value;
+	_setTransform('translateY(' + value + 'px)', instance, animate);
+}
+
+function _setTransform(transform, instance, animate) {
+	if (transform == 'translateY(0px)') {
+		transform = 'none';
+	}
+	instance.requestAnimationFrame(function() {
+		if (animate) {
+			instance.setStyle({
+				'transform': transform,
+				'transition': 'transform .1s linear',
+			})
+		} else {
+			instance.setStyle({
+				'transform': transform
+			})
+		}
+	})
+}
+
+function _getMoveDistance(e, instance) {
+	var state = instance.getState();
+	var refresherThreshold = instance.getDataset().refresherthreshold;
+	var refresherOutRate = instance.getDataset().refresheroutrate;
+	refresherThreshold = parseFloat(refresherThreshold);
+	refresherOutRate = parseFloat(refresherOutRate);
+	var touch = _getCommonTouch(e);
+	var moveDistance = touch.touchY - state.startY;
+	var oldMoveDistance = state.oldMoveDistance || 0;
+	state.oldMoveDistance = moveDistance;
+	var diffDis = moveDistance - oldMoveDistance;
+	if (diffDis > 0) {
+		diffDis = diffDis * 0.85;
+		if (currentMoveDistance > refresherThreshold) {
+			diffDis = diffDis * (1 - refresherOutRate);
+		}
+	}
+	currentMoveDistance += diffDis;
+	if (currentMoveDistance < 0) {
+		currentMoveDistance = 0;
+	}
+	return {
+		currentMoveDistance: currentMoveDistance,
+		isDown: diffDis > 0
+	};
+}
+
+function _getCommonTouch(e) {
+	var touch = null;
+	if (e.touches && e.touches.length) {
+		touch = e.touches[0];
+	} else if (e.changedTouches && e.changedTouches.length) {
+		touch = e.changedTouches[0];
+	} else if (e.datail && e.datail !== {}) {
+		touch = e.datail;
+	} else {
+		touch = e;
+	}
+	return {
+		touchX: touch.clientX,
+		touchY: touch.clientY
+	};
+}
+
+function _getRefresherTouchDisabled(e, instance, processTag) {
+	var dataset = instance.getDataset();
+	var state = instance.getState();
+	var loading = _getIsTrue(dataset.loading);
+	var useChatRecordMode = _getIsTrue(dataset.usechatrecordmode);
+	var refresherEnabled = _getIsTrue(dataset.refresherenabled);
+	var useCustomRefresher = _getIsTrue(dataset.usecustomrefresher);
+	var usePageScroll = _getIsTrue(dataset.usepagescroll);
+	var pageScrollTop = parseFloat(dataset.pagescrolltop);
+	var scrollTop = parseFloat(dataset.scrolltop);
+	var finalScrollTop = usePageScroll ? pageScrollTop : scrollTop;
+	var fixedIsTop = false;
+	var isIos = _getIsTrue(dataset.isios);
+	if (!isIos && finalScrollTop == (state.startScrollTop || 0) && finalScrollTop <= 105) {
+		fixedIsTop = true;
+	}
+	var fixedIsTopHitCount = state.fixedIsTopHitCount || 0;
+	if (fixedIsTop) {
+		fixedIsTopHitCount++;
+		if (fixedIsTopHitCount <= 3) {
+			fixedIsTop = false;
+		}
+		state.fixedIsTopHitCount = fixedIsTopHitCount;
+	} else {
+		state.fixedIsTopHitCount = 0;
+	}
+	if (!isIos && processTag === 0) {
+		state.startScrollTop = finalScrollTop || 0;
+	}
+	if (!isIos && processTag === 2) {
+		fixedIsTop = true;
+	}
+	var res = loading || useChatRecordMode || !refresherEnabled || !useCustomRefresher || ((
+		usePageScroll && useCustomRefresher && pageScrollTop > 5) && !fixedIsTop) || ((
+		!usePageScroll && useCustomRefresher && scrollTop > 5) && !fixedIsTop);
+	return res;
+}
+
+function _getAngleIsInRange(e, touch, state, dataset) {
+	var refresherMaxAngle = dataset.refreshermaxangle;
+	var refresherAecc = _getIsTrue(dataset.refresheraecc);
+	var lastRefresherTouchmove = state.lastRefresherTouchmove;
+	var refresherReachMaxAngle = state.refresherReachMaxAngle;
+	var moveDistance = state.oldMoveDistance;
+	if (!lastRefresherTouchmove) {
+		return true;
+	}
+	if (refresherMaxAngle >= 0 && refresherMaxAngle <= 90 && lastRefresherTouchmove) {
+		if ((!moveDistance || moveDistance < 1) && !refresherAecc && refresherReachMaxAngle != null && !
+			refresherReachMaxAngle) {
+			return false;
+		}
+		var x = Math.abs(touch.touchX - lastRefresherTouchmove.touchX);
+		var y = Math.abs(touch.touchY - lastRefresherTouchmove.touchY);
+		var z = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
+		if ((x || y) && x > 1) {
+			var angle = Math.asin(y / z) / Math.PI * 180;
+			if (angle < refresherMaxAngle) {
+				var hitReachMaxAngleCount = state.hitReachMaxAngleCount || 0;
+				state.hitReachMaxAngleCount = ++hitReachMaxAngleCount;
+				if (state.hitReachMaxAngleCount > 2) {
+					state.lastRefresherTouchmove = touch;
+					state.refresherReachMaxAngle = false;
+				}
+				return false;
+			}
+		}
+	}
+	state.lastRefresherTouchmove = touch;
+	return true;
+}
+
+function _handleTouchMovePullingDown(state, instance, onPullingDown) {
+	var oldOnPullingDown = state.onPullingDown || false;
+	if (oldOnPullingDown != onPullingDown) {
+		instance.callMethod('_handleWxsOnPullingDown', onPullingDown);
+	}
+	state.onPullingDown = onPullingDown;
+}
+
+function _getIsTrue(value) {
+	value = (typeof(value) === 'string' ? JSON.parse(value) : value) || false;
+	return value == true || value == 'true';
+}
+
+module.exports = {
+	touchstart: touchstart,
+	touchmove: touchmove,
+	touchend: touchend,
+	mousedown: mousedown,
+	mousemove: mousemove,
+	mouseup: mouseup,
+	mouseleave: mouseleave,
+	propObserver: propObserver
+}

+ 519 - 0
uni_modules/z-paging/components/z-paging/z-paging.vue

@@ -0,0 +1,519 @@
+ <!--                        _             
+  ____     _ __   __ _  __ _(_)_ __   __ _ 
+ |_  /____| '_ \ / _` |/ _` | | '_ \ / _` |
+  / /_____| |_) | (_| | (_| | | | | | (_| |
+ /___|    | .__/ \__,_|\__, |_|_| |_|\__, |
+          |_|          |___/         |___/ 
+V2.0.9
+by ZXLee 2021-11-04
+-- >
+<!-- API文档地址:http://z-paging.com -->
+<!-- github地址:https://github.com/SmileZXLee/uni-z-paging -->
+<!-- dcloud地址:https://ext.dcloud.net.cn/plugin?id=3935 -->
+<!-- 反馈QQ群:790460711 -->
+
+<template name="z-paging">
+	<!-- #ifndef APP-NVUE -->
+	<view :class="!usePageScroll&&fixed?'z-paging-content z-paging-content-fixed':'z-paging-content'"
+		:style="[finalPagingStyle]">
+		<!-- 顶部固定的slot -->
+		<slot v-if="!usePageScroll&&$slots.top" name="top"></slot>
+		<view class="zp-page-scroll-top" v-else-if="usePageScroll&&$slots.top" :style="[{'top':`${windowTop}px`,'z-index':topZIndex}]">
+			<slot name="top"></slot>
+		</view>
+		<view :class="{'zp-scroll-view-super':!usePageScroll}" :style="[finalScrollViewStyle]">
+			<scroll-view
+				:class="{'zp-scroll-view':true,'zp-scroll-view-absolute':!usePageScroll,'zp-scroll-view-hide-scrollbar':!showScrollbar}"
+				:scroll-top="scrollTop" :scroll-x="scrollX"
+				:scroll-y="scrollable&&!usePageScroll&&scrollEnable&&refresherStatus!==3" :enable-back-to-top="finalEnableBackToTop"
+				:show-scrollbar="showScrollbar" :scroll-with-animation="finalScrollWithAnimation"
+				:scroll-into-view="scrollIntoView" :lower-threshold="finalLowerThreshold" :upper-threshold="5"
+				:refresher-enabled="finalRefresherEnabled&&!useCustomRefresher" :refresher-threshold="finalRefresherThreshold"
+				:refresher-default-style="finalRefresherDefaultStyle" :refresher-background="refresherBackground"
+				:refresher-triggered="finalRefresherTriggered" @scroll="_scroll" @scrolltolower="_onLoadingMore('toBottom')"
+				@scrolltoupper="_scrollToUpper" @refresherrestore="_onRestore" @refresherrefresh="_onRefresh(true)"  
+				>	
+				<view class="zp-paging-touch-view"
+				<!-- #ifndef APP-VUE || MP-WEIXIN || MP-QQ  || H5 -->
+				@touchstart="_refresherTouchstart" @touchmove="_refresherTouchmove" @touchend="_refresherTouchend" @touchcancel="_refresherTouchend"
+				<!-- #endif -->
+				<!-- #ifdef APP-VUE || MP-WEIXIN || MP-QQ || H5 -->
+				@touchstart="pagingWxs.touchstart" @touchmove="pagingWxs.touchmove" @touchend="pagingWxs.touchend" @touchcancel="pagingWxs.touchend"
+				@mousedown="pagingWxs.mousedown" @mousemove="pagingWxs.mousemove" @mouseup="pagingWxs.mouseup" @mouseleave="pagingWxs.mouseleave"
+				<!-- #endif -->
+				>	
+					<view v-if="finalRefresherFixedBacHeight>0" class="zp-fixed-bac-view" :style="[{'background': refresherFixedBackground,'height': `${finalRefresherFixedBacHeight}px`}]"></view>
+					<view class="zp-paging-main" :style="[scrollViewInStyle,{'transform': finalRefresherTransform,'transition': refresherTransition}]"
+					<!-- #ifdef APP-VUE || MP-WEIXIN || MP-QQ || H5 -->
+					:change:prop="pagingWxs.propObserver" :prop="wxsPropType"
+					:data-refresherThreshold="finalRefresherThreshold" :data-isIos="isIos" :data-isIos13="isIos13"
+					:data-loading="loading||isRefresherInComplete" :data-useChatRecordMode="useChatRecordMode" 
+					:data-refresherEnabled="refresherEnabled" :data-useCustomRefresher="useCustomRefresher" :data-pageScrollTop="wxsPageScrollTop"
+					:data-scrollTop="wxsScrollTop" :data-refresherMaxAngle="refresherMaxAngle" 
+					:data-refresherAecc="refresherAngleEnableChangeContinued" :data-usePageScroll="usePageScroll"
+					:data-oldIsTouchmoving="isTouchmoving" :data-refresherOutRate="finalRefresherOutRate"
+					<!-- #endif -->
+					<!-- #ifdef APP-VUE || H5 -->
+					:change:renderPropScrollTop="pagingRenderjs.renderPropScrollTopChange" :renderPropScrollTop="renderPropScrollTop"
+					:change:renderPropUsePageScroll="pagingRenderjs.renderPropUsePageScrollChange" :renderPropUsePageScroll="renderPropUsePageScroll"
+					<!-- #endif -->
+					>	
+						<view v-if="showRefresher" class="zp-custom-refresher-view"
+							:style="[{'margin-top': `-${finalRefresherThreshold}px`,'background': refresherBackground}]">
+							<view class="zp-custom-refresher-container" :style="[{'height': `${finalRefresherThreshold}px`,'background': refresherBackground}]">
+								<!-- 下拉刷新view -->
+								<view class="zp-custom-refresher-slot-view">
+									<slot
+									<!-- #ifndef MP-QQ -->
+									:refresherStatus="refresherStatus"
+									<!-- #endif -->
+									name="refresher" />
+								</view>
+								<z-paging-refresh ref="refresh" v-if="!showCustomRefresher" :style="[{'height': `${finalRefresherThreshold}px`}]" :status="refresherStatus"
+									:defaultThemeStyle="finalRefresherThemeStyle" :defaultText="finalRefresherDefaultText"
+									:pullingText="finalRefresherPullingText" :refreshingText="finalRefresherRefreshingText" :completeText="finalRefresherCompleteText"
+									:showUpdateTime="showRefresherUpdateTime" :updateTimeKey="refresherUpdateTimeKey"
+									:imgStyle="refresherImgStyle" :titleStyle="refresherTitleStyle" :updateTimeStyle="refresherUpdateTimeStyle"></z-paging-refresh>
+							</view>
+						</view>
+						<view class="zp-paging-container">
+							<slot v-if="useChatRecordMode&&$slots.chatLoading&&loadingStatus!==2&&realTotalData.length"
+								name="chatLoading" />
+							<view v-else-if="useChatRecordMode&&loadingStatus!==2&&realTotalData.length"
+								class="zp-chat-record-loading-container">
+								<text v-if="loadingStatus!==1" @click="_scrollToUpper()"
+									:class="defaultThemeStyle==='white'?'zp-loading-more-text zp-loading-more-text-white':'zp-loading-more-text zp-loading-more-text-black'">{{chatRecordLoadingMoreText}}</text>
+								<image v-else :src="base64Flower" class="zp-chat-record-loading-custom-image">
+								</image>
+							</view>
+							<!-- 全屏Loading -->
+							<slot v-if="$slots.loading&&showLoading&&!loadingFullFixed" name="loading" />
+							<!-- 主体内容 -->
+							<view class="zp-paging-container-content" :style="[finalPagingContentStyle]">
+								<slot />
+								<!-- 上拉加载更多view -->
+								<!-- #ifndef MP-ALIPAY -->
+								<slot v-if="_shouldShowLoadingMore('loadingMoreDefault')" name="loadingMoreDefault" />
+								<slot v-else-if="_shouldShowLoadingMore('loadingMoreLoading')" name="loadingMoreLoading" />
+								<slot v-else-if="_shouldShowLoadingMore('loadingMoreNoMore')" name="loadingMoreNoMore" />
+								<slot v-else-if="_shouldShowLoadingMore('loadingMoreFail')" name="loadingMoreFail" />
+								<z-paging-load-more @click.native="_onLoadingMore('click')"
+									v-else-if="_shouldShowLoadingMore('loadingMoreCustom')" :zConfig="zPagingLoadMoreConfig">
+								</z-paging-load-more>
+								<!-- #endif -->
+								<!-- #ifdef MP-ALIPAY -->
+								<slot v-if="loadingStatus===0&&$slots.loadingMoreDefault&&showLoadingMore&&loadingMoreEnabled&&!useChatRecordMode"
+									name="loadingMoreDefault" />
+								<slot v-else-if="loadingStatus===1&&$slots.loadingMoreLoading&&showLoadingMore&&loadingMoreEnabled"
+									name="loadingMoreLoading" />
+								<slot v-else-if="loadingStatus===2&&$slots.loadingMoreNoMore&&showLoadingMore&&showLoadingMoreNoMoreView&&loadingMoreEnabled&&!useChatRecordMode"
+									name="loadingMoreNoMore" />
+								<slot v-else-if="loadingStatus===3&&$slots.loadingMoreFail&&showLoadingMore&&loadingMoreEnabled&&!useChatRecordMode"
+									name="loadingMoreFail" />
+								<z-paging-load-more @click.native="_onLoadingMore('click')" v-else-if="showLoadingMore&&showDefaultLoadingMoreText&&!(loadingStatus===2&&!showLoadingMoreNoMoreView)&&loadingMoreEnabled&&!useChatRecordMode" :zConfig="zPagingLoadMoreConfig">
+								</z-paging-load-more>
+								<!-- #endif -->
+							</view>
+							<!-- 空数据图 -->
+							<view :class="{'zp-empty-view':true,'zp-empty-view-center':emptyViewCenter}" :style="[{emptyViewSuperStyle}]" v-if="showEmpty">
+								<slot v-if="$slots.empty" name="empty" />
+								<z-paging-empty-view v-else :emptyViewImg="finalEmptyViewImg" :emptyViewText="finalEmptyViewText" :showEmptyViewReload="finalShowEmptyViewReload" 
+								:emptyViewReloadText="finalEmptyViewReloadText" :isLoadFailed="isLoadFailed" :emptyViewStyle="emptyViewStyle" :emptyViewTitleStyle="emptyViewTitleStyle" 
+								:emptyViewImgStyle="emptyViewImgStyle" :emptyViewReloadStyle="emptyViewReloadStyle" :emptyViewZIndex="emptyViewZIndex" :emptyViewFixed="emptyViewFixed" 
+								@reload="_emptyViewReload">
+								</z-paging-empty-view>
+							</view>
+						</view>
+					</view>
+				</view>
+			</scroll-view>
+		</view>
+		<!-- 底部固定的slot -->
+		<slot v-if="!usePageScroll&&$slots.bottom" name="bottom"></slot>
+		<view class="zp-page-scroll-bottom" v-else-if="usePageScroll&&$slots.bottom" :style="[{'bottom': `${windowBottom}px`}]">
+			<slot name="bottom"></slot>
+		</view>
+		<!-- 点击返回顶部view -->
+		<view v-if="showBackToTopClass" :class="backToTopClass" :style="[finalBackToTopStyle]" @click.stop="_backToTopClick">
+			<slot v-if="$slots.backToTop" name="backToTop" />
+			<image v-else class="zp-back-to-top-img" :src="backToTopImg.length?backToTopImg:base64BackToTop"></image>
+		</view>
+		<!-- 全屏Loading(铺满z-paging并固定) -->
+		<view v-if="$slots.loading&&showLoading&&loadingFullFixed" class="zp-loading-fixed">
+			<slot name="loading" />
+		</view>
+	</view>
+	<!-- #endif -->
+	<!-- #ifdef APP-NVUE -->
+	<view :is="finalNvueSuperListIs" :style="[finalPagingStyle]" :class="{'z-paging-content-fixed':fixed&&!usePageScroll}" :scrollable="false">
+		<!-- 顶部固定的slot -->
+		<view ref="zp-page-scroll-top" :is="nViewIs" class="zp-page-scroll-top" v-if="$slots.top" :style="[{'top':`${windowTop}px`,'z-index':topZIndex}]">
+			<slot name="top"></slot>
+		</view>
+		<view ref="n-list" :id="nvueListId" :style="[{'flex': 1},scrollViewStyle,useChatRecordMode ? {transform: nIsFirstPageAndNoMore?'rotate(0deg)':'rotate(180deg)'}:{}]" :is="finalNvueListIs" alwaysScrollableVertical="true"
+			:fixFreezing="nFixFreezing" :show-scrollbar="showScrollbar&&!useChatRecordMode" :loadmoreoffset="finalLowerThreshold" :enable-back-to-top="enableBackToTop"
+			:scrollable="scrollable&&scrollEnable&&refresherStatus!==3" :bounce="nvueBounce" :column-count="nWaterfallColumnCount" :column-width="nWaterfallColumnWidth"
+			:column-gap="nWaterfallColumnGap" :left-gap="nWaterfallLeftGap" :right-gap="nWaterfallRightGap"
+			@loadmore="_nOnLoadmore" @scroll="_nOnScroll">
+			<refresh class="zp-n-refresh" :style="[nvueRefresherStyle]" v-if="finalNvueRefresherEnabled" :display="nRefresherLoading?'show':'hide'" @refresh="_nOnRrefresh"
+				@pullingdown="_nOnPullingdown">
+				<view ref="zp-n-refresh-container" class="zp-n-refresh-container">
+					<!-- 下拉刷新view -->
+					<slot v-if="zScopedSlots.refresher" :refresherStatus="refresherStatus" name="refresher" />
+					<z-paging-refresh ref="refresh" v-else :status="refresherStatus" :defaultThemeStyle="finalRefresherThemeStyle"
+						:defaultText="finalRefresherDefaultText" :pullingText="finalRefresherPullingText" :refreshingText="finalRefresherRefreshingText" :completeText="finalRefresherCompleteText"
+						:showUpdateTime="showRefresherUpdateTime" :updateTimeKey="refresherUpdateTimeKey"
+						:imgStyle="refresherImgStyle" :titleStyle="refresherTitleStyle" :updateTimeStyle="refresherUpdateTimeStyle"></z-paging-refresh>
+				</view>
+			</refresh>
+			<view ref="zp-n-list-top-tag" class="zp-n-list-top-tag" :style="[{height:finalNvueRefresherEnabled&&isIos?'0px':'1px'}]" :is="nViewIs"></view>
+			<view v-if="nShowRefresherReveal" ref="zp-n-list-refresher-reveal" :style="[{transform:`translateY(-${nShowRefresherRevealHeight}px)`,height:'0px'}]" :is="nViewIs">
+				<slot v-if="zScopedSlots.refresher" :refresherStatus="refresherStatus" name="refresher" />
+				<z-paging-refresh ref="refresh" v-else :status="refresherStatus" :defaultThemeStyle="finalRefresherThemeStyle"
+					:defaultText="finalRefresherDefaultText" :pullingText="finalRefresherPullingText" :refreshingText="finalRefresherRefreshingText" :completeText="finalRefresherCompleteText" 
+					:showUpdateTime="showRefresherUpdateTime" :updateTimeKey="refresherUpdateTimeKey"
+					:imgStyle="refresherImgStyle" :titleStyle="refresherTitleStyle" :updateTimeStyle="refresherUpdateTimeStyle"></z-paging-refresh>
+			</view>
+			<slot />
+			<!-- 空数据图 -->
+			<view class="z-paging-empty-view" :class="{'z-paging-content-fixed':usePageScroll}" style="flex: 1;" :style="[scrollViewStyle,emptyViewSuperStyle,useChatRecordMode ? {transform: nIsFirstPageAndNoMore?'rotate(0deg)':'rotate(180deg)'}:{}]" v-if="showEmpty" :is="nViewIs">
+				<view :class="{'zp-empty-view':true,'zp-empty-view-center':emptyViewCenter}">
+					<slot v-if="$slots.empty" name="empty" />
+					<z-paging-empty-view v-else :emptyViewImg="finalEmptyViewImg" :emptyViewText="finalEmptyViewText" :showEmptyViewReload="finalShowEmptyViewReload" 
+					:emptyViewReloadText="finalEmptyViewReloadText" :isLoadFailed="isLoadFailed" :emptyViewStyle="emptyViewStyle" :emptyViewTitleStyle="emptyViewTitleStyle" 
+					:emptyViewImgStyle="emptyViewImgStyle" :emptyViewReloadStyle="emptyViewReloadStyle" :emptyViewZIndex="emptyViewZIndex" :emptyViewFixed="emptyViewFixed" 
+					@reload="_emptyViewReload">
+					</z-paging-empty-view>
+				</view>
+			</view>
+			<view v-if="!hideNvueBottomTag" ref="zp-n-list-bottom-tag" class="zp-n-list-bottom-tag" is="header"></view>
+			<!-- 全屏Loading -->
+			<view :class="{'z-paging-content-fixed':usePageScroll}" style="flex: 1;" :style="[scrollViewStyle,useChatRecordMode ? {transform: nIsFirstPageAndNoMore?'rotate(0deg)':'rotate(180deg)'}:{}]" v-if="$slots.loading&&showLoading&&!loadingFullFixed" :is="nViewIs">
+				<slot name="loading" />
+			</view>
+			<!-- 上拉加载更多view -->
+			<view :is="nViewIs" v-if="!refresherOnly&&loadingMoreEnabled">
+				<view v-if="useChatRecordMode">
+					<view v-if="loadingStatus!==2&&realTotalData.length">
+						<slot v-if="$slots.chatLoading"
+							name="chatLoading" />
+						<view v-else class="zp-chat-record-loading-container">
+							<text v-if="loadingStatus!==1" @click="_scrollToUpper()"
+								:class="defaultThemeStyle==='white'?'zp-loading-more-text zp-loading-more-text-white':'zp-loading-more-text zp-loading-more-text-black'">{{chatRecordLoadingMoreText}}</text>
+							<view>
+								<loading-indicator v-if="loadingStatus===1" :animating="true"
+									class="zp-line-loading-image">
+								</loading-indicator>
+							</view>
+						</view>
+					</view>
+				</view>
+				<view :style="nLoadingMoreFixedHeight?{height:loadingMoreCustomStyle&&loadingMoreCustomStyle.height?loadingMoreCustomStyle.height:'80rpx'}:{}">
+					<slot v-if="_shouldShowLoadingMore('loadingMoreDefault')" name="loadingMoreDefault" />
+					<slot v-else-if="_shouldShowLoadingMore('loadingMoreLoading')" name="loadingMoreLoading" />
+					<slot v-else-if="_shouldShowLoadingMore('loadingMoreNoMore')" name="loadingMoreNoMore" />
+					<slot v-else-if="_shouldShowLoadingMore('loadingMoreFail')" name="loadingMoreFail" />
+					<z-paging-load-more @click.native="_onLoadingMore('click')"
+						v-else-if="_shouldShowLoadingMore('loadingMoreCustom')" :zConfig="zPagingLoadMoreConfig">
+					</z-paging-load-more>
+				</view>
+			</view>
+		</view>
+		<!-- 底部固定的slot -->
+		<slot name="bottom"></slot>
+		<!-- 点击返回顶部view -->
+		<view v-if="showBackToTopClass" :class="backToTopClass" :style="[finalBackToTopStyle]" @click.stop="_backToTopClick">
+			<slot v-if="$slots.backToTop" name="backToTop" />
+			<image v-else class="zp-back-to-top-img" :src="backToTopImg.length?backToTopImg:base64BackToTop"></image>
+		</view>
+		<!-- 全屏Loading(铺满z-paging并固定) -->
+		<view v-if="$slots.loading&&showLoading&&loadingFullFixed" class="zp-loading-fixed">
+			<slot name="loading" />
+		</view>
+	</view>
+	<!-- #endif -->
+</template>
+<!-- #ifdef APP-VUE || MP-WEIXIN || MP-QQ || H5 -->
+<script
+    src="./wxs/z-paging-wxs.wxs"
+    module="pagingWxs"
+    lang="wxs"
+></script>
+<!-- #endif -->
+<script module="pagingRenderjs" lang="renderjs">
+	import pagingRenderjs from './wxs/z-paging-renderjs.js';
+	/**
+	 * z-paging 分页组件
+	 * @description 【uni-app自动分页器】超简单,低耦合!仅需两步轻松完成完整分页逻辑(下拉刷新、上拉加载更多),分页全自动处理。支持自定义加载更多的文字或整个view,自定义下拉刷新样式,自动管理空数据view等。
+	 * @tutorial http://z-paging.com
+	 * @property {Number|String} default-page-no 自定义pageNo,默认为1
+	 * @property {Number|String} default-page-size 自定义pageSize,默认为10
+	 * @property {Number|Object} data-key 为保证数据一致,设置当前tab切换时的标识key,并在complete中传递相同key,若二者不一致,则complete将不会生效
+	 * @property {String} autowire-list-name 【极简写法】自动注入的list名,可自动修改父view(包含ref="paging")中对应name的list值(z-paging标签必须设置`ref="paging"`)
+	 * @property {String} autowire-query-name 【极简写法】自动注入的query名,可自动调用父view(包含ref="paging")中的query方法(z-paging标签必须设置`ref="paging"`)
+	 * @property {Number|String} delay 调用complete后延迟处理的时间,单位为毫秒
+	 * @property {Number|String} min-delay 触发@query后最小延迟处理的时间,单位为毫秒,默认0毫秒,优先级低于delay(假设设置为300毫秒,若分页请求时间小于300毫秒,则在调用complete后延迟[300毫秒-请求时长];若请求时长大于300毫秒,则不延迟),当show-refresher-when-reload为true或reload(true)时,其最小值为400
+	 * @property {String} language i18n国际化设置语言,支持简体中文(zh-cn)、繁体中文(zh-hant-cn)和英文(en)
+	 * @property {Boolean} follow-system-language i18n国际化默认是否跟随系统语言,默认为是
+	 * @property {Object} paging-style 设置z-paging的style,部分平台(如微信小程序)无法直接修改组件的style,可使用此属性代替
+	 * @property {String} height z-paging的高度,优先级低于pagingStyle中设置的height,传字符串,如100px、100rpx、100%
+	 * @property {String} width z-paging的宽度,优先级低于pagingStyle中设置的width,传字符串,如100px、100rpx、100%
+	 * @property {String} bg-color z-paging的背景色,优先级低于pagingStyle中设置的background-color。传字符串,如"#ffffff"
+	 * @property {Object} paging-content-style 设置z-paging的容器(插槽的父view)的style
+	 * @property {Boolean} auto-height z-paging是否自动高度,若自动高度则会自动铺满屏幕,默认为否
+	 * @property {Number|String} auto-height-addition z-paging是否自动高度时,附加的高度,注意添加单位px或rpx,默认为px,若需要减少高度,请传负数
+	 * @property {String} default-theme-style loading(下拉刷新、上拉加载更多)的主题样式,支持black,white,默认black
+	 * @property {String} refresher-theme-style 下拉刷新的主题样式,支持black,white,默认black
+	 * @property {Object} refresher-img-style 自定义下拉刷新中左侧图标的样式
+	 * @property {Object} refresher-title-style 自定义下拉刷新中右侧状态描述文字的样式
+	 * @property {Object} refresher-update-time-style 自定义下拉刷新中右侧最后更新时间文字的样式(show-refresher-update-time为true时有效)
+	 * @property {String} loading-more-theme-style 底部加载更多的主题样式,支持black,white,默认black
+	 * @property {Boolean} refresher-only 是否只使用下拉刷新,设置为true后将关闭mounted自动请求数据、关闭滚动到底部加载更多,强制隐藏空数据图。默认为否
+	 * @property {Number|String} refresher-complete-delay 自定义下拉刷新结束以后延迟回弹的时间,单位为毫秒,默认为0
+	 * @property {Number|String} refresher-complete-duration 自定义下拉刷新结束回弹动画时间,单位为毫秒,默认为300毫秒(refresherEndBounceEnabled为false时,refresherCompleteDuration为设定值的1/3),nvue无效
+	 * @property {Boolean} use-page-scroll 使用页面滚动,默认为否,当设置为是时则使用页面的滚动而非此组件内部的scroll-view的滚动,使用页面滚动时z-paging无需设置确定的高度且对于长列表展示性能更高,但配置会略微繁琐
+	 * @property {Boolean} fixed z-paging是否使用fixed布局,若使用fixed布局,则z-paging的父view无需固定高度,z-paging高度默认为100%,默认为否(当使用内置scroll-view滚动时有效)
+	 * @property {Boolean} safe-area-inset-bottom 是否开启底部安全区域适配,默认为否
+	 * @property {Boolean} scrollable 是否可以滚动,使用内置scroll-view和nvue时有效,默认为是
+	 * @property {Boolean} auto [z-paging]mounted后是否自动调用reload方法(mounted后自动调用接口),默认为是
+	 * @property {Boolean} auto-scroll-to-top-when-reload reload时是否自动滚动到顶部,默认为是
+	 * @property {Boolean} auto-clean-list-when-reload reload时是否立即自动清空原list,默认为是,若立即自动清空,则在reload之后、请求回调之前页面是空白的
+	 * @property {Boolean} show-refresher-when-reload 调用reload方法时是否自动显示下拉刷新view,默认为否
+	 * @property {Boolean} show-loading-more-when-reload 调用reload方法时自动显示加载更多view,且为加载中状态(仅初始设置有效,不可动态修改)
+	 * @property {Boolean} refresher-update-time-key 如果需要区别不同页面的最后更新时间,请为不同页面的z-paging的`refresher-update-time-key`设置不同的字符串
+	 * @property {Boolean} use-custom-refresher 是否使用自定义的下拉刷新,默认为是,即使用z-paging的下拉刷新。设置为false即代表使用uni scroll-view自带的下拉刷新,h5、App、微信小程序以外的平台不支持uni scroll-view自带的下拉刷新
+	 * @property {Number|String} refresher-fps 自定义下拉刷新下拉帧率,默认为40,过高可能会出现抖动问题
+	 * @property {Number|String} refresher-max-angle 自定义下拉刷新允许触发的最大下拉角度,默认为40度,当下拉角度小于设定值时,自定义下拉刷新动画不会被触发
+	 * @property {Boolean} refresher-angle-enable-change-continued 自定义下拉刷新的角度由未达到最大角度变到达到最大角度时,是否继续下拉刷新手势,默认为否
+	 * @property {String|Object} refresher-default-text 自定义下拉刷新默认状态下的文字
+	 * @property {String|Object} refresher-pulling-text 自定义下拉刷新松手立即刷新状态下的文字
+	 * @property {String|Object} refresher-refreshing-text 自定义下拉刷新刷新中状态下的文字
+	 * @property {String|Object} refresher-complete-text 自定义下拉刷新刷新结束状态下的文字
+	 * @property {Boolean} refresher-end-bounce-enabled 是否开启自定义下拉刷新刷新结束回弹效果,默认为是
+	 * @property {Object} loading-more-custom-style 自定义底部加载更多样式
+	 * @property {Object} loading-more-loading-icon-custom-style 自定义底部加载更多加载中动画样式
+	 * @property {String} loading-more-loading-icon-type 自定义底部加载更多加载中动画图标类型,可选circle或flower,默认为circle
+	 * @property {String} loading-more-loading-icon-custom-image 自定义底部加载更多加载中动画图标图片
+	 * @property {Boolean} loading-more-loading-animated 底部加载更多加载中view是否展示旋转动画(loading-more-loading-icon-custom-image有值时有效,nvue无效)
+	 * @property {Boolean} loading-more-enabled 是否启用加载更多数据(含滑动到底部加载更多数据和点击加载更多数据),默认为是
+	 * @property {Boolean} to-bottom-loading-more-enabled 是否启用滑动到底部加载更多数据
+	 * @property {String|Object} loading-more-default-text 滑动到底部"默认"文字,默认为【点击加载更多】
+	 * @property {String|Object} loading-more-loading-text 滑动到底部"加载中"文字,默认为【正在加载...】
+	 * @property {String|Object} loading-more-no-more-text 滑动到底部"没有更多"文字,默认为【没有更多了】
+	 * @property {String|Object} loading-more-fail-text 滑动到底部"加载失败"文字,默认为【加载失败,点击重新加载】
+	 * @property {Boolean} hide-loading-more-when-no-more-and-inside-of-paging 当没有更多数据且分页内容未超出z-paging时是否隐藏没有更多数据的view,默认为是
+	 * @property {Boolean} hide-loading-more-when-no-more-and-inside-of-paging 当没有更多数据且分页内容未超出z-paging时是否隐藏没有更多数据的view,默认为是
+	 * @property {Boolean} inside-more 当分页未满一屏时,是否自动加载更多(nvue无效),默认为否
+	 * @property {Boolean} show-default-loading-more-text 是否显示默认的加载更多text,默认为是
+	 * @property {Boolean} show-loading-more-no-more-line 是否显示没有更多数据的分割线,默认为是
+	 * @property {Object} loading-more-no-more-line-custom-style 自定义底部没有更多数据的分割线样式
+	 * @property {Boolean} hide-empty-view 是否强制隐藏空数据图,默认为否
+	 * @property {String|Object} empty-view-text 空数据图描述文字,默认为“没有数据哦~”
+	 * @property {Boolean} show-empty-view-reload 是否显示空数据图重新加载按钮(无数据时),默认为否
+	 * @property {Boolean} show-empty-view-reload-when-error 加载失败时是否显示空数据图重新加载按钮,默认为是
+	 * @property {String} empty-view-img 空数据图图片,默认使用z-paging内置的图片
+	 * @property {String|Object} empty-view-reload-text 空数据图点击重新加载文字
+	 * @property {String|Object} empty-view-error-text 空数据图“加载失败”描述文字
+	 * @property {String} empty-view-error-img 空数据图“加载失败”图片,默认使用z-paging内置的图片(建议使用绝对路径)
+	 * @property {Object} empty-view-style 空数据图样式
+     * @property {Object} empty-view-super-style 空数据图容器样式
+	 * @property {Object} empty-view-img-style 空数据图img样式
+	 * @property {Object} empty-view-title-style 空数据图描述文字样式
+	 * @property {Object} empty-view-reload-style 空数据图重新加载按钮样式
+	 * @property {Boolean} empty-view-fixed 空数据图片是否铺满z-paging,默认为是。若设置为否,则为填充满z-paging的剩余部分
+	 * @property {Boolean} empty-view-center 空数据图片是否垂直居中,默认为是。emptyViewFixed为false时有效
+	 * @property {Boolean} auto-hide-empty-view-when-loading 加载中时是否自动隐藏空数据图,默认为是
+	 * @property {Boolean} auto-hide-empty-view-when-pull 用户下拉列表触发下拉刷新加载中时是否自动隐藏空数据图,默认为是
+	 * @property {Boolean} auto-hide-loading-after-first-loaded 第一次加载后是否自动隐藏loading slot,默认为是
+	 * @property {Boolean} loading-full-fixed loading slot是否铺满屏幕并固定,默认为否
+	 * @property {Boolean} auto-show-back-to-top 自动显示点击返回顶部按钮,默认为否
+	 * @property {Number|String} back-to-top-threshold 点击返回顶部按钮显示/隐藏的阈值(滚动距离),单位为px,默认为400rpx
+	 * @property {String} back-to-top-img 点击返回顶部按钮的自定义图片地址,默认使用z-paging内置的图片
+	 * @property {Boolean} back-to-top-with-animate 点击返回顶部按钮返回到顶部时是否展示过渡动画,默认为是
+	 * @property {Number|String} back-to-top-bottom 点击返回顶部按钮与底部的距离,注意添加单位px或rpx,默认为160rpx
+	 * @property {Object} back-to-top-style 点击返回顶部按钮的自定义样式
+	 * @property {Boolean} show-scrollbar 控制是否出现滚动条,默认为是
+	 * @property {Boolean} scroll-x 是否允许横向滚动,默认为否
+	 * @property {Boolean} scroll-to-top-bounce-enabled iOS设备上滚动到顶部时是否允许回弹效果,默认为否。关闭回弹效果后可使滚动到顶部与下拉刷新更连贯,但是有吸顶view时滚动到顶部时可能出现抖动。
+	 * @property {Boolean} scroll-with-animation 控制是否出现滚动条,默认为否
+	 * @property {String} scroll-into-view 值应为某子元素id(id不能以数字开头)。设置哪个方向可滚动,则在哪个方向滚动到该元素
+	 * @property {Number|String} lower-threshold 距底部/右边多远时(单位px),触发 scrolltolower 事件,默认为100rpx
+	 * @property {Boolean} enable-back-to-top iOS点击顶部状态栏、安卓双击标题栏时,滚动条返回顶部,只支持竖向,默认为是
+	 * @property {Boolean} refresher-enabled 是否开启自定义下拉刷新,默认为是
+	 * @property {Number|String} refresher-threshold 设置自定义下拉刷新阈值,默认为80rpx
+	 * @property {String} refresher-default-style 设置自定义下拉刷新默认样式,支持设置 black,white,none,none 表示不使用默认样式,默认为black
+	 * @property {String} refresher-background 设置自定义下拉刷新区域背景颜色
+	 * @property {Boolean} show-refresher-update-time 是否显示上次下拉刷新更新时间,默认为否
+	 * @property {String} refresher-update-time-key 上次下拉刷新更新时间的key,用于区别不同的上次更新时间
+	 * @property {Number|String} local-paging-loading-time 本地分页时上拉加载更多延迟时间,单位为毫秒,默认200毫秒
+	 * @property {Boolean} use-chat-record-mode 使用聊天记录模式,默认为否
+	 * @property {Number} top-z-index slot="top"的view的z-index,仅使用页面滚动时有效,默认为99
+	 * @property {Number} super-content-z-index z-paging内容容器父view的z-index,默认为1
+	 * @property {Number} content-z-index z-paging内容容器部分的z-index,默认为10
+	 * @property {Number} empty-view-z-index 空数据view的z-index,默认为9
+	 * @property {Boolean} auto-full-height 使用页面滚动时,是否在不满屏时自动填充满屏幕,默认为是
+	 * @property {Boolean} concat 自动拼接complete中传过来的数组(使用聊天记录模式时无效),默认为是
+	 * @property {String} nvue-list-is nvue中修改列表类型,可选值有list、waterfall和scroller,默认为list
+	 * @property {Object} nvue-waterfall-config nvue waterfall配置,仅在nvue中且nvueListIs=waterfall时有效,配置参数详情参见:https://uniapp.dcloud.io/component/waterfall
+	 * @property {Boolean} nvue-bounce nvue 控制是否回弹效果,iOS不支持动态修改(若禁用回弹效果,下拉刷新将失效),默认为是
+	 * @property {Boolean} nvue-fast-scroll nvue中通过代码滚动到顶部/底部时,是否加快动画效果(无滚动动画时无效),默认为否
+	 * @property {String} nvue-list-id nvue中list的id
+	 * @property {Object} nvue-refresher-style nvue中refresh组件的样式
+	 * @property {Boolean} hide-nvue-bottom-tag 是否隐藏nvue列表底部的tagView,此view用于标识滚动到底部位置,若隐藏则滚动到底部功能将失效,在nvue中实现吸顶+swiper功能时需将最外层z-paging的此属性设置为true。默认为否
+	 * @property {Boolean} show-console-error 是否将错误信息打印至控制台,默认为是
+	 * @event {Function} query 下拉刷新或滚动到底部时会自动触发此方法。z-paging加载时也会触发(若要禁止,请设置:auto="false")。pageNo和pageSize会自动计算好,直接传给服务器即可。
+	 * @event {Function} refresherStatusChange 自定义下拉刷新状态改变(use-custom-refresher为true时生效)【注:通过`:refresher-status.sync`绑定当前data中的指定变量亦可】
+	 * @event {Function} loadingStatusChange 上拉加载更多状态改变
+	 * @event {Function} refresherTouchstart 自定义下拉刷新下拉开始(use-custom-refresher为true时生效)【注:当需要更细致定制自定义下拉刷新时使用,如果只需监听下拉刷新各个状态改变,使用`refresherStatusChange`即可】
+	 * @event {Function} refresherTouchmove 自定义下拉刷新下拉中开始(use-custom-refresher为true时生效)【注:当需要更细致定制自定义下拉刷新时使用,如果只需监听下拉刷新各个状态改变,使用`refresherStatusChange`即可】
+	 * @event {Function} refresherTouchend 自定义下拉刷新下拉结束(use-custom-refresher为true时生效)【注:当需要更细致定制自定义下拉刷新时使用,如果只需监听下拉刷新各个状态改变,使用`refresherStatusChange`即可】
+	 * @event {Function} onRefresh 自定义下拉刷新被触发
+	 * @event {Function} onRestore 自定义下拉刷新被复位
+	 * @event {Function} scroll `z-paging`内置的scroll-view滚动时触发
+	 * @event {Function} scrollTopChange scrollTop改变时触发,使用点击返回顶部时需要获取scrollTop时可使用此事件【注:通过`:scroll-top.sync`绑定当前data中的指定变量亦可】(@scrolltoupper触发时,也会自动触发此方法,且scrollTop=0)
+	 * @event {Function} scrolltolower `z-paging`内置的scroll-view滚动底部时触发
+	 * @event {Function} scrolltoupper `z-paging`内置的scroll-view滚动顶部时触发
+	 * @event {Function} listChange 分页渲染的数组改变时触发
+	 * @event {Function} emptyViewReload 点击了空数据图中的重新加载按钮
+	 * @example <z-paging ref="paging" v-model="dataList" @query="queryList"></z-paging>
+	 */
+	export default {
+		name:"z-paging",
+		// #ifdef APP-VUE || H5
+		mixins: [pagingRenderjs],
+		// #endif
+		// 以下代码是为了欺骗编译器使props的代码提示功能生效,不会被编译到项目中
+		// #ifdef QUICKAPP-WEBVIEW-UNION
+		props: {
+			defaultPageNo: {type: [Number, String]},
+			defaultPageSize: {type: [Number, String]},
+			dataKey: {type: [Number, Object]},
+			autowireListName: {type: String},
+			autowireQueryName: {type: String},
+			delay: {type: [Number, String]},
+			minDelay: {type: [Number, String]},
+			language: {type: String},
+			followSystemLanguage: {type: Boolean},
+			pagingStyle: {type: Object},
+			height: {type: String},
+			width: {type: String},
+			bgColor: {type: String},
+			pagingContentStyle: {type: Object},
+			autoHeight: {type: Boolean},
+			autoHeightAddition: {type: [Number, String]},
+			defaultThemeStyle: {type: String},
+			refresherThemeStyle: {type: String},
+			refresherImgStyle: {type: Object},
+			refresherTitleStyle: {type: Object},
+			refresherUpdateTimeStyle: {type: Object},
+			loadingMoreThemeStyle: {type: String},
+			refresherOnly: {type: Boolean},
+			refresherCompleteDelay: {type: [Number, String]},
+			refresherCompleteDuration: {type: [Number, String]},
+			usePageScroll: {type: Boolean},
+			fixed: {type: Boolean},
+			safeAreaInsetBottom: {type: Boolean},
+			scrollable: {type: Boolean},
+			mountedAutoCallReload: {type: Boolean},
+			auto: {type: Boolean},
+			autoScrollToTopWhenReload: {type: Boolean},
+			autoCleanListWhenReload: {type: Boolean},
+			showRefresherWhenReload: {type: Boolean},
+			showLoadingMoreWhenReload: {type: Boolean},
+			useCustomRefresher: {type: Boolean},
+			refresherFps: {type: [Number, String]},
+			refresherMaxAngle: {type: [Number, String]},
+			refresherAngleEnableChangeContinued: {type: Boolean},
+			refresherDefaultText: {type: [String, Object]},
+			refresherPullingText: {type: [String, Object]},
+			refresherRefreshingText: {type: [String, Object]},
+			refresherCompleteText: {type: [String, Object]},
+			refresherEndBounceEnabled: {type: Boolean},
+			loadingMoreCustomStyle: {type: Object},
+			loadingMoreLoadingIconCustomStyle: {type: Object},
+			loadingMoreLoadingIconType: {type: String},
+			loadingMoreLoadingIconCustomImage: {type: String},
+			loadingMoreLoadingAnimated: {type: Boolean},
+			loadingMoreEnabled: {type: Boolean},
+			toBottomLoadingMoreEnabled: {type: Boolean},
+			loadingMoreDefaultText: {type: [String, Object]},
+			loadingMoreLoadingText: {type: [String, Object]},
+			loadingMoreNoMoreText: {type: [String, Object]},
+			loadingMoreFailText: {type: [String, Object]},
+			hideLoadingMoreWhenNoMoreAndInsideOfPaging: {type: Boolean},
+			hideLoadingMoreWhenNoMoreByLimit: {type: Number},
+			insideMore: {type: Boolean},
+			showDefaultLoadingMoreText: {type: Boolean},
+			showLoadingMoreNoMoreView: {type: Boolean},
+			showLoadingMoreNoMoreLine: {type: Boolean},
+			loadingMoreNoMoreLineCustomStyle: {type: Object},
+			hideEmptyView: {type: Boolean},
+			emptyViewText: {type: [String, Object]},
+			showEmptyViewReload: {type: Boolean},
+			showEmptyViewReloadWhenError: {type: Boolean},
+			emptyViewReloadText: {type: [String, Object]},
+			emptyViewImg: {type: String},
+			emptyViewErrorText: {type: [String, Object]},
+			emptyViewErrorImg: {type: String},
+			emptyViewStyle: {type: Object},
+            emptyViewSuperStyle: {type: Object},
+			emptyViewImgStyle: {type: Object},
+			emptyViewTitleStyle: {type: Object},
+			emptyViewReloadStyle: {type: Object},
+			emptyViewFixed: {type: Boolean},
+			emptyViewCenter: {type: Boolean},
+			autoHideEmptyViewWhenLoading: {type: Boolean},
+			autoHideEmptyViewWhenPull: {type: Boolean},
+			autoHideLoadingAfterFirstLoaded: {type: Boolean},
+			loadingFullFixed: {type: Boolean},
+			autoShowBackToTop: {type: Boolean},
+			backToTopThreshold: {type: [Number, String]},
+			backToTopImg: {type: String},
+			backToTopWithAnimate: {type: Boolean},
+			backToTopBottom: {type: [Number, String]},
+			backToTopStyle: {type: Object},
+			showScrollbar: {type: Boolean},
+			scrollX: {type: Boolean},
+			scrollToTopBounceEnabled: {type: Boolean},
+			scrollToBottomBounceEnabled: {type: Boolean},
+			scrollWithAnimation: {type: Boolean},
+			scrollIntoView: {type: String},
+			lowerThreshold: {type: [Number, String]},
+			enableBackToTop: {type: Boolean},
+			refresherEnabled: {type: Boolean},
+			refresherThreshold: {type: [Number, String]},
+			refresherDefaultStyle: {type: String},
+			refresherBackground: {type: String},
+			refresherFixedBackground: {type: String},
+			refresherFixedBacHeight: {type: [Number, String]},
+			refresherOutRate: {type: Number},
+			showRefresherUpdateTime: {type: Boolean},
+			refresherUpdateTimeKey: {type: String},
+			localPagingLoadingTime: {type: [Number, String]},
+			useChatRecordMode: {type: Boolean},
+			topZIndex: {type: Number},
+			superContentZIndex: {type: Number},
+			contentZIndex: {type: Number},
+			emptyViewZIndex: {type: Number},
+			autoFullHeight: {type: Boolean},
+			concat: {type: Boolean},
+			nvueListIs: {type: String},
+			nvueWaterfallConfig: {type: Object},
+			nvueBounce: {type: Boolean},
+			nvueFastScroll: {type: Boolean},
+			nvueListId: {type: String},
+			nvueRefresherStyle: {type: Object},
+			hideNvueBottomTag: {type: Boolean},
+			showConsoleError: {type: Boolean},
+			value: {type: Array}
+		}
+		// #endif
+	}
+</script>
+<script
+    src="./js/z-paging-main.js"></script>
+	
+<style scoped>
+	@import "./css/z-paging-main.css";
+	@import "./css/z-paging-static.css";
+</style>

+ 84 - 0
uni_modules/z-paging/package.json

@@ -0,0 +1,84 @@
+{
+  "id": "z-paging",
+  "displayName": "【z-paging下拉刷新、上拉加载更多】超简单、低耦合!仅需两步轻松完成完整分页逻辑",
+  "version": "2.0.9",
+  "description": "【支持nvue,使用wxs+renderjs实现】全平台兼容,支持自定义下拉刷新、上拉加载更多,支持自动管理空数据图、点击返回顶部,支持聊天分页、本地分页,支持展示最后更新时间,支持国际化等等",
+  "keywords": [
+    "下拉刷新",
+    "上拉加载",
+    "分页器",
+    "nvue",
+    "wxs"
+],
+  "repository": "https://github.com/SmileZXLee/uni-z-paging",
+  "engines": {
+    "HBuilderX": "^3.0.7"
+  },
+  "dcloudext": {
+    "category": [
+        "前端组件",
+        "通用组件"
+    ],
+    "sale": {
+      "regular": {
+        "price": "0.00"
+      },
+      "sourcecode": {
+        "price": "0.00"
+      }
+    },
+    "contact": {
+      "qq": "393727164"
+    },
+    "declaration": {
+      "ads": "无",
+      "data": "无",
+      "permissions": "无"
+    },
+    "npmurl": ""
+  },
+  "uni_modules": {
+    "dependencies": [],
+    "encrypt": [],
+    "platforms": {
+      "cloud": {
+        "tcb": "y",
+        "aliyun": "y"
+      },
+      "client": {
+        "App": {
+          "app-vue": "y",
+          "app-nvue": "y"
+        },
+        "H5-mobile": {
+          "Safari": "y",
+          "Android Browser": "y",
+          "微信浏览器(Android)": "y",
+          "QQ浏览器(Android)": "y"
+        },
+        "H5-pc": {
+          "Chrome": "y",
+          "IE": "y",
+          "Edge": "y",
+          "Firefox": "y",
+          "Safari": "y"
+        },
+        "小程序": {
+          "微信": "y",
+          "阿里": "y",
+          "百度": "y",
+          "字节跳动": "y",
+          "QQ": "y"
+        },
+        "快应用": {
+          "华为": "u",
+          "联盟": "u"
+        },
+        "Vue": {
+            "vue2": "y",
+            "vue3": "u"
+        }
+      }
+    }
+  }
+}

+ 46 - 0
uni_modules/z-paging/readme.md

@@ -0,0 +1,46 @@
+# z-paging
+
+
+***  
+
+## 【注意】由V1.9.0起,fixed属性默认值为true,z-paging默认会铺满屏幕。老项目更新请注意,使用侧滑滚动切换选项卡或需要局部使用z-paging请设置:fixed="false"。如果您希望fixed属性默认为false,请参考文档:z-paging.com,将fixed默认值设置为false。
+*** 
+
+### API文档地址:[https://z-paging.com](http://z-paging.com)
+### 备用API文档地址:[https://www.zxlee.cn/z-paging/index.html](https://www.zxlee.cn/z-paging/index.html) 
+
+***  
+### 功能&特点
+* 【配置简单】仅需两步(绑定网络请求方法、绑定分页结果数组)轻松完成完整下拉刷新,上拉加载更多功能。
+* 【低耦合,低侵入】分页自动管理。在page中无需处理任何分页相关逻辑,无需在data中定义任何分页相关变量,全由z-paging内部处理。
+* 【超灵活,支持各种类型自定义】支持自定义下拉刷新,自定义上拉加载更多,自带自定义下拉刷新效果,及其他数十种自定义属性。
+* 【功能丰富】支持国际化,支持自定义且自动管理空数据图,支持主题模式切换,支持本地分页,支持聊天分页模式,支持展示最后更新时间,支持吸顶效果,支持内部scroll-view滚动与页面滚动,支持一键滚动到顶部等诸多功能。
+* 【多平台兼容,细致,流畅】支持nvue,支持h5、app及各家小程序;在app-vue、h5、微信小程序、QQ小程序上使用wxs实现下拉刷新,大幅提升性能。多处细节优化,给您精致流畅的体验。
+
+*** 
+### 反馈qq群(点击加群):[790460711](https://jq.qq.com/?_wv=1027&k=vU2fKZZH)
+ 
+*** 
+#### 关于自动引入组件
+
+> `z-paging` 支持[easycom组件规范](https://uniapp.dcloud.io/component/README?id=easycom组件规范),无需引用和注册组件即可直接使用,在正在运行的项目中导入`z-paging`可能会提示:`Unknown custom element:<z-paging> - did you register the component corrently?... `,此时需要重新运行项目即可。
+
+### 预览
+
+***
+
+|                 自定义下拉刷新效果+分页演示                  |                      吸顶效果+分页演示                       |
+| :----------------------------------------------------------: | :----------------------------------------------------------: |
+| ![](http://www.zxlee.cn/github/uni-z-paging/uni-z-paging.gif) | ![](http://www.zxlee.cn/github/uni-z-paging/uni-z-paging2.gif) |
+
+|                   滑动切换选项卡+分页演示                    |                    聊天记录模式+分页演示                     |
+| :----------------------------------------------------------: | :----------------------------------------------------------: |
+| ![](http://www.zxlee.cn/github/uni-z-paging/z-paging-demo3.gif) | ![](http://www.zxlee.cn/github/uni-z-paging/z-paging-demo4.gif) |
+
+### 在线demo体验地址:
+
+* [http://www.zxlee.cn/github/uni-z-paging/demo/index.html](http://www.zxlee.cn/github/uni-z-paging/demo/index.html)
+
+| 扫码体验                                                     |
+| ------------------------------------------------------------ |
+| ![](http://www.zxlee.cn/github/uni-z-paging/z-paging-demo.png) |

+ 21 - 0
uview-ui/LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2020 www.uviewui.com
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 105 - 0
uview-ui/README.md

@@ -0,0 +1,105 @@
+<p align="center">
+    <img alt="logo" src="https://uviewui.com/common/logo.png" width="120" height="120" style="margin-bottom: 10px;">
+</p>
+<h3 align="center" style="margin: 30px 0 30px;font-weight: bold;font-size:40px;">uView</h3>
+<h3 align="center">多平台快速开发的UI框架</h3>
+
+
+## 说明
+
+uView UI,是[uni-app](https://uniapp.dcloud.io/)生态优秀的UI框架,全面的组件和便捷的工具会让您信手拈来,如鱼得水
+
+## 特性
+
+- 兼容安卓,iOS,微信小程序,H5,QQ小程序,百度小程序,支付宝小程序,头条小程序
+- 60+精选组件,功能丰富,多端兼容,让您快速集成,开箱即用
+- 众多贴心的JS利器,让您飞镖在手,召之即来,百步穿杨
+- 众多的常用页面和布局,让您专注逻辑,事半功倍
+- 详尽的文档支持,现代化的演示效果
+- 按需引入,精简打包体积
+
+
+## 安装
+
+```bash
+# npm方式安装
+npm i uview-ui
+```
+
+## 快速上手
+
+1. `main.js`引入uView库
+```js
+// main.js
+import uView from 'uview-ui';
+Vue.use(uView);
+```
+
+2. `App.vue`引入基础样式(注意style标签需声明scss属性支持)
+```css
+/* App.vue */
+<style lang="scss">
+@import "uview-ui/index.scss";
+</style>
+```
+
+3. `uni.scss`引入全局scss变量文件
+```css
+/* uni.scss */
+@import "uview-ui/theme.scss";
+```
+
+4. `pages.json`配置easycom规则(按需引入)
+
+```js
+// pages.json
+{
+	"easycom": {
+		// npm安装的方式不需要前面的"@/",下载安装的方式需要"@/"
+		// npm安装方式
+		"^u-(.*)": "uview-ui/components/u-$1/u-$1.vue"
+		// 下载安装方式
+		// "^u-(.*)": "@/uview-ui/components/u-$1/u-$1.vue"
+	},
+	// 此为本身已有的内容
+	"pages": [
+		// ......
+	]
+}
+```
+
+请通过[快速上手](https://www.uviewui.com/components/quickstart.html)了解更详细的内容 
+
+## 使用方法
+配置easycom规则后,自动按需引入,无需`import`组件,直接引用即可。
+
+```html
+<template>
+	<u-button text="按钮"></u-button>
+</template>
+```
+
+请通过[快速上手](https://www.uviewui.com/components/quickstart.html)了解更详细的内容 
+
+## 链接
+
+- [官方文档](https://www.uviewui.com/)
+- [更新日志](https://www.www.uviewui.com/components/changelog.html)
+- [升级指南](https://www.uviewui.com/components/changelog.html)
+- [关于我们](https://www.uviewui.com/cooperation/about.html)
+
+## 预览
+
+您可以通过**微信**扫码,查看最佳的演示效果。
+<br>
+<br>
+<img src="https://uviewui.com/common/weixin_mini_qrcode.png" width="220" height="220" >
+
+## 捐赠uView的研发
+
+uView文档和源码全部开源免费,如果您认为uView帮到了您的开发工作,您可以捐赠uView的研发工作,捐赠无门槛,哪怕是一杯可乐也好(相信这比打赏主播更有意义)。
+
+<img src="https://uviewui.com/common/alipay.png" width="220" ><img style="margin-left: 100px;" src="https://uviewui.com/common/wechat.png" width="220" >
+
+## 版权信息
+uView遵循[MIT](https://en.wikipedia.org/wiki/MIT_License)开源协议,意味着您无需支付任何费用,也无需授权,即可将uView应用到您的产品中。

+ 139 - 0
uview-ui/changelog.md

@@ -0,0 +1,139 @@
+## 2.0.13(2021-12-14)
+## [点击加群交流反馈:364463526](https://jq.qq.com/?_chanwv=1027&k=mCxS3TGY)
+
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 修复配置默认单位为rpx可能会导致自定义导航栏高度异常的问题
+## 2.0.12(2021-12-14)
+## [点击加群交流反馈:364463526](https://jq.qq.com/?_chanwv=1027&k=mCxS3TGY)
+
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 修复tabs组件在vue环境下划线消失的问题
+2. 修复upload组件在安卓小程序无法选择视频的问题
+3. 添加uni.$u.config.unit配置,用于配置参数默认单位,详见:[默认单位配置](https://www.uviewui.com/components/setting.html#%E9%BB%98%E8%AE%A4%E5%8D%95%E4%BD%8D%E9%85%8D%E7%BD%AE)
+4. 修复textarea组件在没绑定v-model时,字符统计不生效问题
+5. 修复nvue下控制是否出现滚动条失效问题
+## 2.0.11(2021-12-13)
+## [点击加群交流反馈:364463526](https://jq.qq.com/?_chanwv=1027&k=mCxS3TGY)
+
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. text组件align参数无效的问题
+2. subsection组件添加keyName参数
+3. upload组件无法判断[Object file]类型的问题
+4. 处理notify层级过低问题
+5. codeInput组件添加disabledDot参数
+6. 处理actionSheet组件round参数无效的问题
+7. calendar组件添加round参数用于控制圆角值
+8. 处理swipeAction组件在vue环境下默认被打开的问题
+9. button组件的throttleTime节流参数无效的问题
+10. 解决u-notify手动关闭方法close()无效的问题
+11. input组件readonly不生效问题
+12. tag组件type参数为info不生效问题
+## 2.0.10(2021-12-08)
+## [点击加群交流反馈:364463526](https://jq.qq.com/?_chanwv=1027&k=mCxS3TGY)
+
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 修复button sendMessagePath属性不生效
+2. 修复DatetimePicker选择器title无效
+3. 修复u-toast设置loading=true不生效
+4. 修复u-text金额模式传0报错
+5. 修复u-toast组件的icon属性配置不生效
+6. button的icon在特殊场景下的颜色优化
+7. IndexList优化,增加#
+## 2.0.9(2021-12-01)
+## [点击加群交流反馈:232041042](https://jq.qq.com/?_wv=1027&k=KnbeceDU)
+
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 优化swiper的height支持100%值(仅vue有效),修复嵌入视频时click事件无法触发的问题
+2. 优化tabs组件对list值为空的判断,或者动态变化list时重新计算相关尺寸的问题
+3. 优化datetime-picker组件逻辑,让其后续打开的默认值为上一次的选中值,需要通过v-model绑定值才有效
+4. 修复upload内嵌在其他组件中,选择图片可能不会换行的问题
+## 2.0.8(2021-12-01)
+## [点击加群交流反馈:232041042](https://jq.qq.com/?_wv=1027&k=KnbeceDU)
+
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 修复toast的position参数无效问题
+2. 处理input在ios nvue上无法获得焦点的问题
+3. avatar-group组件添加extraValue参数,让剩余展示数量可手动控制
+4. tabs组件添加keyName参数用于配置从对象中读取的键名
+5. 处理text组件名字脱敏默认配置无效的问题
+6. 处理picker组件item文本太长换行问题
+## 2.0.7(2021-11-30)
+## [点击加群交流反馈:232041042](https://jq.qq.com/?_wv=1027&k=KnbeceDU)
+
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 修复radio和checkbox动态改变v-model无效的问题。
+2. 优化form规则validator在微信小程序用法
+3. 修复backtop组件mode参数在微信小程序无效的问题
+4. 处理Album的previewFullImage属性无效的问题
+5. 处理u-datetime-picker组件mode='time'在选择改变时间时,控制台报错的问题
+## 2.0.6(2021-11-27)
+## [点击加群交流反馈:232041042](https://jq.qq.com/?_wv=1027&k=KnbeceDU)
+
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 处理tag组件在vue下边框无效的问题。
+2. 处理popup组件圆角参数可能无效的问题。
+3. 处理tabs组件lineColor参数可能无效的问题。
+4. propgress组件在值很小时,显示异常的问题。
+## 2.0.5(2021-11-25)
+## [点击加群交流反馈:232041042](https://jq.qq.com/?_wv=1027&k=KnbeceDU)
+
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. calendar在vue下显示异常问题。 
+2. form组件labelPosition和errorType参数无效的问题
+3. input组件inputAlign无效的问题
+4. 其他一些修复
+## 2.0.4(2021-11-23)
+## [点击加群交流反馈:232041042](https://jq.qq.com/?_wv=1027&k=KnbeceDU)
+
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+0. input组件缺失@confirm事件,以及subfix和prefix无效问题
+1. component.scss文件样式在vue下干扰全局布局问题
+2. 修复subsection在vue环境下表现异常的问题
+3. tag组件的bgColor等参数无效的问题
+4. upload组件不换行的问题
+5. 其他的一些修复处理
+## 2.0.3(2021-11-16)
+## [点击加群交流反馈:1129077272](https://jq.qq.com/?_wv=1027&k=KnbeceDU)
+
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. uView2.0已实现全面兼容nvue
+2. uView2.0对1.x进行了架构重构,细节和性能都有极大提升
+3. 目前uView2.0为公测阶段,相关细节可能会有变动
+4. 我们写了一份与1.x的对比指南,详见[对比1.x](https://www.uviewui.com/components/diff1.x.html)
+5. 处理modal的confirm回调事件拼写错误问题
+6. 处理input组件@input事件参数错误问题
+7. 其他一些修复
+## 2.0.2(2021-11-16)
+## [点击加群交流反馈:1129077272](https://jq.qq.com/?_wv=1027&k=KnbeceDU)
+
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. uView2.0已实现全面兼容nvue
+2. uView2.0对1.x进行了架构重构,细节和性能都有极大提升
+3. 目前uView2.0为公测阶段,相关细节可能会有变动
+4. 我们写了一份与1.x的对比指南,详见[对比1.x](https://www.uviewui.com/components/diff1.x.html)
+5. 修复input组件formatter参数缺失问题
+6. 优化loading-icon组件的scss写法问题,防止不兼容新版本scss
+## 2.0.0(2020-11-15)
+## [点击加群交流反馈:1129077272](https://jq.qq.com/?_wv=1027&k=KnbeceDU)
+
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. uView2.0已实现全面兼容nvue
+2. uView2.0对1.x进行了架构重构,细节和性能都有极大提升
+3. 目前uView2.0为公测阶段,相关细节可能会有变动
+4. 我们写了一份与1.x的对比指南,详见[对比1.x](https://www.uviewui.com/components/diff1.x.html)
+5. 修复input组件formatter参数缺失问题
+
+

+ 78 - 0
uview-ui/components/u--form/u--form.vue

@@ -0,0 +1,78 @@
+<template>
+	<uvForm
+		ref="uForm"
+		:model="model"
+		:rules="rules"
+		:errorType="errorType"
+		:borderBottom="borderBottom"
+		:labelPosition="labelPosition"
+		:labelWidth="labelWidth"
+		:labelAlign="labelAlign"
+		:labelStyle="labelStyle"
+		:customStyle="customStyle"
+	>
+		<slot />
+	</uvForm>
+</template>
+
+<script>
+	/**
+	 * 此组件存在的理由是,在nvue下,u-form被uni-app官方占用了,u-form在nvue中相当于form组件
+	 * 所以在nvue下,取名为u--form,内部其实还是u-form.vue,只不过做一层中转
+	 */
+	import uvForm from '../u-form/u-form.vue';
+	import props from '../u-form/props.js'
+	export default {
+		// #ifdef MP-WEIXIN
+		name: 'u-form',
+		// #endif
+		// #ifndef MP-WEIXIN
+		name: 'u--form',
+		// #endif
+		mixins: [uni.$u.mpMixin, props, uni.$u.mixin],
+		components: {
+			uvForm
+		},
+		created() {
+			this.children = []
+		},
+		methods: {
+			// 手动设置校验的规则,如果规则中有函数的话,微信小程序中会过滤掉,所以只能手动调用设置规则
+			setRules(rules) {
+				this.$refs.uForm.setRules(rules)
+			},
+			validate() {
+				/**
+				 * 在微信小程序中,通过this.$parent拿到的父组件是u--form,而不是其内嵌的u-form
+				 * 导致在u-form组件中,拿不到对应的children数组,从而校验无效,所以这里每次调用u-form组件中的
+				 * 对应方法的时候,在小程序中都先将u--form的children赋值给u-form中的children
+				 */
+				// #ifdef MP-WEIXIN
+				this.setMpData()
+				// #endif
+				return this.$refs.uForm.validate()
+			},
+			validateField(value, callback) {
+				// #ifdef MP-WEIXIN
+				this.setMpData()
+				// #endif
+				return this.$refs.uForm.validateField(value, callback)
+			},
+			resetFields() {
+				// #ifdef MP-WEIXIN
+				this.setMpData()
+				// #endif
+				return this.$refs.uForm.resetFields()
+			},
+			clearValidate(props) {
+				// #ifdef MP-WEIXIN
+				this.setMpData()
+				// #endif
+				return this.$refs.uForm.clearValidate(props)
+			},
+			setMpData() {
+				this.$refs.uForm.children = this.children
+			}
+		},
+	}
+</script>

+ 40 - 0
uview-ui/components/u--image/u--image.vue

@@ -0,0 +1,40 @@
+<template>
+	<uvImage 
+		:src="src"
+		:mode="mode"
+		:width="width"
+		:height="height"
+		:shape="shape"
+		:radius="radius"
+		:lazyLoad="lazyLoad"
+		:showMenuByLongpress="showMenuByLongpress"
+		:loadingIcon="loadingIcon"
+		:errorIcon="errorIcon"
+		:showLoading="showLoading"
+		:showError="showError"
+		:fade="fade"
+		:webp="webp"
+		:duration="duration"
+		:bgColor="bgColor"
+		:customStyle="customStyle"
+		@click="$emit('click')"
+		@error="$emit('error')"
+		@load="$emit('load')"
+	></uvImage>
+</template>
+
+<script>
+	/**
+	 * 此组件存在的理由是,在nvue下,u-image被uni-app官方占用了,u-image在nvue中相当于image组件
+	 * 所以在nvue下,取名为u--image,内部其实还是u-iamge.vue,只不过做一层中转
+	 */
+	import uvImage from '../u-image/u-image.vue';
+	import props from '../u-image/props.js';
+	export default {
+		name: 'u--image',
+		mixins: [uni.$u.mpMixin, props, uni.$u.mixin],
+		components: {
+			uvImage
+		},
+	}
+</script>

+ 72 - 0
uview-ui/components/u--input/u--input.vue

@@ -0,0 +1,72 @@
+<template>
+	<uvInput 
+		:value="value"
+		:type="type"
+		:fixed="fixed"
+		:disabled="disabled"
+		:disabledColor="disabledColor"
+		:clearable="clearable"
+		:password="password"
+		:maxlength="maxlength"
+		:placeholder="placeholder"
+		:placeholderClass="placeholderClass"
+		:placeholderStyle="placeholderStyle"
+		:showWordLimit="showWordLimit"
+		:confirmType="confirmType"
+		:confirmHold="confirmHold"
+		:holdKeyboard="holdKeyboard"
+		:focus="focus"
+		:autoBlur="autoBlur"
+		:disableDefaultPadding="disableDefaultPadding"
+		:cursor="cursor"
+		:cursorSpacing="cursorSpacing"
+		:selectionStart="selectionStart"
+		:selectionEnd="selectionEnd"
+		:adjustPosition="adjustPosition"
+		:inputAlign="inputAlign"
+		:fontSize="fontSize"
+		:color="color"
+		:prefixIcon="prefixIcon"
+		:suffixIcon="suffixIcon"
+		:suffixIconStyle="suffixIconStyle"
+		:prefixIconStyle="prefixIconStyle"
+		:border="border"
+		:readonly="readonly"
+		:shape="shape"
+		:customStyle="customStyle"
+		:formatter="formatter"
+		@focus="$emit('focus')"
+		@blur="$emit('blur')"
+		@keyboardheightchange="$emit('keyboardheightchange')"
+		@change="e => $emit('change', e)"
+		@input="e => $emit('input', e)"
+		@confirm="e => $emit('confirm', e)"
+		@clear="$emit('clear')"
+		@click="$emit('click')"
+	>
+		<!-- #ifdef MP -->
+		<slot name="prefix"></slot>
+		<slot name="suffix"></slot>
+		<!-- #endif -->
+		<!-- #ifndef MP -->
+		<slot name="prefix" slot="prefix"></slot>
+		<slot name="suffix" slot="suffix"></slot>
+		<!-- #endif -->
+	</uvInput>
+</template>
+
+<script>
+	/**
+	 * 此组件存在的理由是,在nvue下,u-input被uni-app官方占用了,u-input在nvue中相当于input组件
+	 * 所以在nvue下,取名为u--input,内部其实还是u-input.vue,只不过做一层中转
+	 */
+	import uvInput from '../u-input/u-input.vue';
+	import props from '../u-input/props.js'
+	export default {
+		name: 'u--input',
+		mixins: [uni.$u.mpMixin, props, uni.$u.mixin],
+		components: {
+			uvInput
+		},
+	}
+</script>

+ 43 - 0
uview-ui/components/u--text/u--text.vue

@@ -0,0 +1,43 @@
+<template>
+    <uvText
+        :type="type"
+        :show="show"
+        :text="text"
+        :prefixIcon="prefixIcon"
+        :suffixIcon="suffixIcon"
+        :mode="mode"
+        :href="href"
+        :format="format"
+        :call="call"
+        :openType="openType"
+        :bold="bold"
+        :block="block"
+        :lines="lines"
+        :color="color"
+        :size="size"
+        :iconStyle="iconStyle"
+        :margin="margin"
+        :lineHeight="lineHeight"
+        :align="align"
+        :wordWrap="wordWrap"
+        :customStyle="customStyle"
+        @click="$emit('click')"
+    ></uvText>
+</template>
+
+<script>
+/**
+ * 此组件存在的理由是,在nvue下,u-text被uni-app官方占用了,u-text在nvue中相当于input组件
+ * 所以在nvue下,取名为u--input,内部其实还是u-text.vue,只不过做一层中转
+ * 不使用v-bind="$attrs",而是分开独立写传参,是因为微信小程序不支持此写法
+ */
+import uvText from "../u-text/u-text.vue";
+import props from "../u-text/props.js";
+export default {
+    name: "u--text",
+    mixins: [uni.$u.mpMixin, props, uni.$u.mixin],
+    components: {
+        uvText,
+    },
+};
+</script>

+ 47 - 0
uview-ui/components/u--textarea/u--textarea.vue

@@ -0,0 +1,47 @@
+<template>
+	<uvTextarea
+		:value="value"
+		:placeholder="placeholder"
+		:height="height"
+		:confirmType="confirmType"
+		:disabled="disabled"
+		:count="count"
+		:focus="focus"
+		:autoHeight="autoHeight"
+		:fixed="fixed"
+		:cursorSpacing="cursorSpacing"
+		:cursor="cursor"
+		:showConfirmBar="showConfirmBar"
+		:selectionStart="selectionStart"
+		:selectionEnd="selectionEnd"
+		:adjustPosition="adjustPosition"
+		:disableDefaultPadding="disableDefaultPadding"
+		:holdKeyboard="holdKeyboard"
+		:maxlength="maxlength"
+		:border="border"
+		:customStyle="customStyle"
+		:formatter="formatter"
+		@focus="e => $emit('focus')"
+		@blur="e => $emit('blur')"
+		@linechange="e => $emit('linechange', e)"
+		@confirm="e => $emit('confirm')"
+		@input="e => $emit('input', e)"
+		@keyboardheightchange="e => $emit('keyboardheightchange')"
+	></uvTextarea>
+</template>
+
+<script>
+	/**
+	 * 此组件存在的理由是,在nvue下,u--textarea被uni-app官方占用了,u-textarea在nvue中相当于textarea组件
+	 * 所以在nvue下,取名为u--textarea,内部其实还是u-textarea.vue,只不过做一层中转
+	 */
+	import uvTextarea from '../u-textarea/u-textarea.vue';
+	import props from '../u-textarea/props.js'
+	export default {
+		name: 'u--textarea',
+		mixins: [uni.$u.mpMixin, props, uni.$u.mixin],
+		components: {
+			uvTextarea
+		},
+	}
+</script>

+ 54 - 0
uview-ui/components/u-action-sheet/props.js

@@ -0,0 +1,54 @@
+export default {
+    props: {
+        // 操作菜单是否展示 (默认false)
+        show: {
+            type: Boolean,
+            default: uni.$u.props.actionSheet.show
+        },
+        // 标题
+        title: {
+            type: String,
+            default: uni.$u.props.actionSheet.title
+        },
+        // 选项上方的描述信息
+        description: {
+            type: String,
+            default: uni.$u.props.actionSheet.description
+        },
+        // 数据
+        actions: {
+            type: Array,
+            default: uni.$u.props.actionSheet.actions
+        },
+        // 取消按钮的文字,不为空时显示按钮
+        cancelText: {
+            type: String,
+            default: uni.$u.props.actionSheet.cancelText
+        },
+        // 点击某个菜单项时是否关闭弹窗
+        closeOnClickAction: {
+            type: Boolean,
+            default: uni.$u.props.actionSheet.closeOnClickAction
+        },
+        // 处理底部安全区(默认true)
+        safeAreaInsetBottom: {
+            type: Boolean,
+            default: uni.$u.props.actionSheet.safeAreaInsetBottom
+        },
+        // 小程序的打开方式
+        openType: {
+            type: String,
+            default: uni.$u.props.actionSheet.openType
+        },
+        // 点击遮罩是否允许关闭 (默认true)
+        closeOnClickOverlay: {
+            type: Boolean,
+            default: uni.$u.props.actionSheet.closeOnClickOverlay
+        },
+        // 圆角值
+        round: {
+            type: [Boolean, String, Number],
+            default: uni.$u.props.actionSheet.round
+        }
+    }
+}

+ 275 - 0
uview-ui/components/u-action-sheet/u-action-sheet.vue

@@ -0,0 +1,275 @@
+
+<template>
+	<u-popup
+	    :show="show"
+	    mode="bottom"
+	    @close="close"
+	    :closeOnClickOverlay="closeOnClickOverlay"
+	    :safeAreaInsetBottom="safeAreaInsetBottom"
+	    :round="round"
+	>
+		<view class="u-action-sheet">
+			<view
+			    class="u-action-sheet__header"
+			    v-if="title"
+			>
+				<text class="u-action-sheet__header__title u-line-1">{{title}}</text>
+				<view
+				    class="u-action-sheet__header__icon-wrap"
+				    @tap.stop="close"
+				>
+					<u-icon
+					    name="close"
+					    size="17"
+					    color="#c8c9cc"
+					    bold
+					></u-icon>
+				</view>
+			</view>
+			<text
+			    class="u-action-sheet__description"
+				:style="[{
+					marginTop: `${title && description ? 0 : '18px'}`
+				}]"
+			    v-if="description"
+			>{{description}}</text>
+			<slot>
+				<u-line v-if="description"></u-line>
+				<view class="u-action-sheet__item-wrap">
+					<template v-for="(item, index) in actions">
+						<!-- #ifdef MP -->
+						<button
+						    :key="index"
+						    class="u-reset-button"
+						    :openType="item.openType"
+						    @getuserinfo="onGetUserInfo"
+						    @contact="onContact"
+						    @getphonenumber="onGetPhoneNumber"
+						    @error="onError"
+						    @launchapp="onLaunchApp"
+						    @opensetting="onOpenSetting"
+						    :lang="lang"
+						    :session-from="sessionFrom"
+						    :send-message-title="sendMessageTitle"
+						    :send-message-path="sendMessagePath"
+						    :send-message-img="sendMessageImg"
+						    :show-message-card="showMessageCard"
+						    :app-parameter="appParameter"
+						    @tap="selectHandler(index)"
+						    :hover-class="!item.disabled && !item.loading ? 'u-action-sheet--hover' : ''"
+						>
+							<!-- #endif -->
+							<view
+							    class="u-action-sheet__item-wrap__item"
+							    @tap.stop="selectHandler(index)"
+							    :hover-class="!item.disabled && !item.loading ? 'u-action-sheet--hover' : ''"
+							    :hover-stay-time="150"
+							>
+								<template v-if="!item.loading">
+									<text
+									    class="u-action-sheet__item-wrap__item__name"
+									    :style="[itemStyle(index)]"
+									>{{ item.name }}</text>
+									<text
+									    v-if="item.subname"
+									    class="u-action-sheet__item-wrap__item__subname"
+									>{{ item.subname }}</text>
+								</template>
+								<u-loading-icon
+								    v-else
+								    custom-class="van-action-sheet__loading"
+								    size="18"
+								    mode="circle"
+								/>
+							</view>
+							<!-- #ifdef MP -->
+						</button>
+						<!-- #endif -->
+						<u-line v-if="index !== actions.length - 1"></u-line>
+					</template>
+				</view>
+			</slot>
+			<u-gap
+			    bgColor="#eaeaec"
+			    height="6"
+			    v-if="cancelText"
+			></u-gap>
+			<view hover-class="u-action-sheet--hover">
+				<text
+				    @touchmove.stop.prevent
+				    :hover-stay-time="150"
+				    v-if="cancelText"
+				    class="u-action-sheet__cancel-text"
+				    @tap="close"
+				>{{cancelText}}</text>
+			</view>
+		</view>
+	</u-popup>
+</template>
+
+<script>
+	import openType from '../../libs/mixin/openType'
+	import button from '../../libs/mixin/button'
+	import props from './props.js';
+	/**
+	 * ActionSheet 操作菜单
+	 * @description 本组件用于从底部弹出一个操作菜单,供用户选择并返回结果。本组件功能类似于uni的uni.showActionSheetAPI,配置更加灵活,所有平台都表现一致。
+	 * @tutorial https://www.uviewui.com/components/actionSheet.html
+	 * 
+	 * @property {Boolean}			show				操作菜单是否展示 (默认 false )
+	 * @property {String}			title				操作菜单标题
+	 * @property {String}			description			选项上方的描述信息
+	 * @property {Array<Object>}	actions				按钮的文字数组,见官方文档示例
+	 * @property {String}			cancelText			取消按钮的提示文字,不为空时显示按钮
+	 * @property {Boolean}			closeOnClickAction	点击某个菜单项时是否关闭弹窗 (默认 true )
+	 * @property {Boolean}			safeAreaInsetBottom	处理底部安全区 (默认 true )
+	 * @property {String}			openType			小程序的打开方式 (contact | launchApp | getUserInfo | openSetting |getPhoneNumber |error )
+	 * @property {Boolean}			closeOnClickOverlay	点击遮罩是否允许关闭  (默认 true )
+	 * @property {Number|String}	round				圆角值,默认无圆角  (默认 0 )
+	 * @property {String}			lang				指定返回用户信息的语言,zh_CN 简体中文,zh_TW 繁体中文,en 英文
+	 * @property {String}			sessionFrom			会话来源,openType="contact"时有效
+	 * @property {String}			sendMessageTitle	会话内消息卡片标题,openType="contact"时有效
+	 * @property {String}			sendMessagePath		会话内消息卡片点击跳转小程序路径,openType="contact"时有效
+	 * @property {String}			sendMessageImg		会话内消息卡片图片,openType="contact"时有效
+	 * @property {Boolean}			showMessageCard		是否显示会话内消息卡片,设置此参数为 true,用户进入客服会话会在右下角显示"可能要发送的小程序"提示,用户点击后可以快速发送小程序消息,openType="contact"时有效 (默认 false )
+	 * @property {String}			appParameter		打开 APP 时,向 APP 传递的参数,openType=launchApp 时有效
+	 * 
+	 * @event {Function} select			点击ActionSheet列表项时触发 
+	 * @event {Function} close			点击取消按钮时触发
+	 * @event {Function} getuserinfo	用户点击该按钮时,会返回获取到的用户信息,回调的 detail 数据与 wx.getUserInfo 返回的一致,openType="getUserInfo"时有效
+	 * @event {Function} contact		客服消息回调,openType="contact"时有效
+	 * @event {Function} getphonenumber	获取用户手机号回调,openType="getPhoneNumber"时有效
+	 * @event {Function} error			当使用开放能力时,发生错误的回调,openType="error"时有效
+	 * @event {Function} launchapp		打开 APP 成功的回调,openType="launchApp"时有效
+	 * @event {Function} opensetting	在打开授权设置页后回调,openType="openSetting"时有效
+	 * @example <u-action-sheet :actions="list" :title="title" :show="show"></u-action-sheet>
+	 */
+	export default {
+		name: "u-action-sheet",
+		// 一些props参数和methods方法,通过mixin混入,因为其他文件也会用到
+		mixins: [openType, button, uni.$u.mixin, props],
+		data() {
+			return {
+
+			}
+		},
+		computed: {
+			// 操作项目的样式
+			itemStyle() {
+				return (index) => {
+					let style = {};
+					if (this.actions[index].color) style.color = this.actions[index].color
+					if (this.actions[index].fontSize) style.fontSize = uni.$u.addUnit(this.actions[index].fontSize)
+					// 选项被禁用的样式
+					if (this.actions[index].disabled) style.color = '#c0c4cc'
+					return style;
+				}
+			},
+		},
+		methods: {
+			close() {
+				// 允许点击遮罩关闭时,才发出close事件
+				if(this.closeOnClickOverlay) {
+					this.$emit('close')
+				}
+			},
+			selectHandler(index) {
+				const item = this.actions[index]
+				if (item && !item.disabled && !item.loading) {
+					this.$emit('select', item)
+					if (this.closeOnClickAction) {
+						this.$emit('close')
+					}
+				}
+			},
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	@import "../../libs/css/components.scss";
+	$u-action-sheet-reset-button-width:100% !default;
+	$u-action-sheet-title-font-size: 16px !default;
+	$u-action-sheet-title-padding: 12px 30px !default;
+	$u-action-sheet-title-color: $u-main-color !default;
+	$u-action-sheet-header-icon-wrap-right:15px !default;
+	$u-action-sheet-header-icon-wrap-top:15px !default;
+	$u-action-sheet-description-font-size:13px !default;
+	$u-action-sheet-description-color:14px !default;
+	$u-action-sheet-description-margin: 18px 15px !default;
+	$u-action-sheet-item-wrap-item-padding:15px !default;
+	$u-action-sheet-item-wrap-name-font-size:16px !default;
+	$u-action-sheet-item-wrap-subname-font-size:13px !default;
+	$u-action-sheet-item-wrap-subname-color: #c0c4cc !default;
+	$u-action-sheet-item-wrap-subname-margin-top:10px !default;
+	$u-action-sheet-cancel-text-font-size:16px !default;
+	$u-action-sheet-cancel-text-color:$u-content-color !default;
+	$u-action-sheet-cancel-text-font-size:15px !default;
+	$u-action-sheet-cancel-text-hover-background-color:rgb(242, 243, 245) !default;
+
+	.u-reset-button {
+		width: $u-action-sheet-reset-button-width;
+	}
+
+	.u-action-sheet {
+		text-align: center;
+		&__header {
+			position: relative;
+			padding: $u-action-sheet-title-padding;
+			&__title {
+				font-size: $u-action-sheet-title-font-size;
+				color: $u-action-sheet-title-color;
+				font-weight: bold;
+				text-align: center;
+			}
+
+			&__icon-wrap {
+				position: absolute;
+				right: $u-action-sheet-header-icon-wrap-right;
+				top: $u-action-sheet-header-icon-wrap-top;
+			}
+		}
+
+		&__description {
+			font-size: $u-action-sheet-description-font-size;
+			color: $u-tips-color;
+			margin: $u-action-sheet-description-margin;
+			text-align: center;
+		}
+
+		&__item-wrap {
+
+			&__item {
+				padding: $u-action-sheet-item-wrap-item-padding;
+				@include flex;
+				align-items: center;
+				justify-content: center;
+				flex-direction: column;
+
+				&__name {
+					font-size: $u-action-sheet-item-wrap-name-font-size;
+					color: $u-main-color;
+					text-align: center;
+				}
+
+				&__subname {
+					font-size: $u-action-sheet-item-wrap-subname-font-size;
+					color: $u-action-sheet-item-wrap-subname-color;
+					margin-top: $u-action-sheet-item-wrap-subname-margin-top;
+					text-align: center;
+				}
+			}
+		}
+
+		&__cancel-text {
+			font-size: $u-action-sheet-cancel-text-font-size;
+			color: $u-action-sheet-cancel-text-color;
+			text-align: center;
+			padding: $u-action-sheet-cancel-text-font-size;
+		}
+
+		&--hover {
+			background-color: $u-action-sheet-cancel-text-hover-background-color;
+		}
+	}
+</style>

+ 59 - 0
uview-ui/components/u-album/props.js

@@ -0,0 +1,59 @@
+export default {
+    props: {
+        // 图片地址,Array<String>|Array<Object>形式
+        urls: {
+            type: Array,
+            default: uni.$u.props.album.urls
+        },
+        // 指定从数组的对象元素中读取哪个属性作为图片地址
+        keyName: {
+            type: String,
+            default: uni.$u.props.album.keyName
+        },
+        // 单图时,图片长边的长度
+        singleSize: {
+            type: [String, Number],
+            default: uni.$u.props.album.singleSize
+        },
+        // 多图时,图片边长
+        multipleSize: {
+            type: [String, Number],
+            default: uni.$u.props.album.multipleSize
+        },
+        // 多图时,图片水平和垂直之间的间隔
+        space: {
+            type: [String, Number],
+            default: uni.$u.props.album.space
+        },
+        // 单图时,图片缩放裁剪的模式
+        singleMode: {
+            type: String,
+            default: uni.$u.props.album.singleMode
+        },
+        // 多图时,图片缩放裁剪的模式
+        multipleMode: {
+            type: String,
+            default: uni.$u.props.album.multipleMode
+        },
+        // 最多展示的图片数量,超出时最后一个位置将会显示剩余图片数量
+        maxCount: {
+            type: [String, Number],
+            default: uni.$u.props.album.maxCount
+        },
+        // 是否可以预览图片
+        previewFullImage: {
+            type: Boolean,
+            default: uni.$u.props.album.previewFullImage
+        },
+        // 每行展示图片数量,如设置,singleSize和multipleSize将会无效
+        rowCount: {
+            type: [String, Number],
+            default: uni.$u.props.album.rowCount
+        },
+        // 超出maxCount时是否显示查看更多的提示
+        showMore: {
+            type: Boolean,
+            default: uni.$u.props.album.showMore
+        }
+    }
+}

+ 259 - 0
uview-ui/components/u-album/u-album.vue

@@ -0,0 +1,259 @@
+<template>
+    <view class="u-album">
+        <view
+            class="u-album__row"
+            ref="u-album__row"
+            v-for="(arr, index) in showUrls"
+            :forComputedUse="albumWidth"
+            :key="index"
+        >
+            <view
+                class="u-album__row__wrapper"
+                v-for="(item, index1) in arr"
+                :key="index1"
+                :style="[imageStyle(index + 1, index1 + 1)]"
+                @tap="previewFullImage ? onPreviewTap(getSrc(item)) : ''"
+            >
+                <image
+                    :src="getSrc(item)"
+                    :mode="
+                        urls.length === 1
+                            ? imageHeight > 0
+                                ? singleMode
+                                : 'widthFix'
+                            : multipleMode
+                    "
+                    :style="[
+                        {
+                            width: imageWidth,
+                            height: imageHeight
+                        }
+                    ]"
+                ></image>
+                <view
+                    v-if="
+                        showMore &&
+                        urls.length > rowCount * showUrls.length &&
+                        index === showUrls.length - 1 &&
+                        index1 === showUrls[showUrls.length - 1].length - 1
+                    "
+                    class="u-album__row__wrapper__text"
+                >
+                    <u--text
+                        :text="`+${urls.length - maxCount}`"
+                        color="#fff"
+                        :size="multipleSize * 0.3"
+                        align="center"
+                        customStyle="justify-content: center"
+                    ></u--text>
+                </view>
+            </view>
+        </view>
+    </view>
+</template>
+
+<script>
+import props from './props.js'
+// #ifdef APP-NVUE
+// 由于weex为阿里的KPI业绩考核的产物,所以不支持百分比单位,这里需要通过dom查询组件的宽度
+const dom = uni.requireNativePlugin('dom')
+// #endif
+
+/**
+ * Album 相册
+ * @description 本组件提供一个类似相册的功能,让开发者开发起来更加得心应手。减少重复的模板代码
+ * @tutorial https://www.uviewui.com/components/album.html
+ *
+ * @property {Array}           urls             图片地址列表 Array<String>|Array<Object>形式
+ * @property {String}          keyName          指定从数组的对象元素中读取哪个属性作为图片地址
+ * @property {String | Number} singleSize       单图时,图片长边的长度  (默认 180 )
+ * @property {String | Number} multipleSize     多图时,图片边长 (默认 70 )
+ * @property {String | Number} space            多图时,图片水平和垂直之间的间隔 (默认 6 )
+ * @property {String}          singleMode       单图时,图片缩放裁剪的模式 (默认 'scaleToFill' )
+ * @property {String}          multipleMode     多图时,图片缩放裁剪的模式 (默认 'aspectFill' )
+ * @property {String | Number} maxCount         取消按钮的提示文字 (默认 9 )
+ * @property {Boolean}         previewFullImage 是否可以预览图片 (默认 true )
+ * @property {String | Number} rowCount         每行展示图片数量,如设置,singleSize和multipleSize将会无效	(默认 3 )
+ * @property {Boolean}         showMore         超出maxCount时是否显示查看更多的提示 (默认 true )
+ *
+ * @event    {Function}        albumWidth       某些特殊的情况下,需要让文字与相册的宽度相等,这里事件的形式对外发送  (回调参数 width )
+ * @example <u-album :urls="urls2" @albumWidth="width => albumWidth = width" multipleSize="68" ></u-album>
+ */
+export default {
+    name: 'u-album',
+    mixins: [uni.$u.mpMixin, uni.$u.mixin, props],
+    data() {
+        return {
+            // 单图的宽度
+            singleWidth: 0,
+            // 单图的高度
+            singleHeight: 0,
+            // 单图时,如果无法获取图片的尺寸信息,让图片宽度默认为容器的一定百分比
+            singlePercent: 0.6
+        }
+    },
+    watch: {
+        urls: {
+            immediate: true,
+            handler(newVal) {
+                if (newVal.length === 1) {
+                    this.getImageRect()
+                }
+            }
+        }
+    },
+    computed: {
+        imageStyle() {
+            return (index1, index2) => {
+                const { space, rowCount, multipleSize, urls } = this,
+                    { addUnit, addStyle } = uni.$u,
+                    rowLen = this.showUrls.length,
+                    allLen = this.urls.length
+                const style = {
+                    marginRight: addUnit(space),
+                    marginBottom: addUnit(space)
+                }
+                // 如果为最后一行,则每个图片都无需下边框
+                if (index1 === rowLen) style.marginBottom = 0
+                // 每行的最右边一张和总长度的最后一张无需右边框
+                if (
+                    index2 === rowCount ||
+                    (index1 === rowLen &&
+                        index2 === this.showUrls[index1 - 1].length)
+                )
+                    style.marginRight = 0
+                return style
+            }
+        },
+        // 将数组划分为二维数组
+        showUrls() {
+            const arr = []
+            this.urls.map((item, index) => {
+                // 限制最大展示数量
+                if (index + 1 <= this.maxCount) {
+                    // 计算该元素为第几个素组内
+                    const itemIndex = Math.floor(index / this.rowCount)
+                    // 判断对应的索引是否存在
+                    if (!arr[itemIndex]) {
+                        arr[itemIndex] = []
+                    }
+                    arr[itemIndex].push(item)
+                }
+            })
+            return arr
+        },
+        imageWidth() {
+            return this.$u.addUnit(
+                this.urls.length === 1 ? this.singleWidth : this.multipleSize
+            )
+        },
+        imageHeight() {
+            return this.$u.addUnit(
+                this.urls.length === 1 ? this.singleHeight : this.multipleSize
+            )
+        },
+        // 此变量无实际用途,仅仅是为了利用computed特性,让其在urls长度等变化时,重新计算图片的宽度
+        // 因为用户在某些特殊的情况下,需要让文字与相册的宽度相等,所以这里事件的形式对外发送
+        albumWidth() {
+            let width = 0
+            if (this.urls.length === 1) {
+                width = this.singleWidth
+            } else {
+                width =
+                    this.showUrls[0].length * this.multipleSize +
+                    this.space * (this.showUrls[0].length - 1)
+            }
+            this.$emit('albumWidth', width)
+            return width
+        }
+    },
+    methods: {
+        // 预览图片
+        onPreviewTap(url) {
+            const urls = this.urls.map((item) => {
+                return this.getSrc(item)
+            })
+            uni.previewImage({
+                current: url,
+                urls
+            })
+        },
+        // 获取图片的路径
+        getSrc(item) {
+            return uni.$u.test.object(item)
+                ? (this.keyName && item[this.keyName]) || item.src
+                : item
+        },
+        // 单图时,获取图片的尺寸
+        // 在小程序中,需要将网络图片的的域名添加到小程序的download域名才可能获取尺寸
+        // 在没有添加的情况下,让单图宽度默认为盒子的一定宽度(singlePercent)
+        getImageRect() {
+            const src = this.getSrc(this.urls[0])
+            uni.getImageInfo({
+                src,
+                success: (res) => {
+                    // 判断图片横向还是竖向展示方式
+                    const isHorizotal = res.width >= res.height
+                    this.singleWidth = isHorizotal
+                        ? this.singleSize
+                        : (res.width / res.height) * this.singleSize
+                    this.singleHeight = !isHorizotal
+                        ? this.singleSize
+                        : (res.height / res.width) * this.singleWidth
+                },
+                fail: () => {
+                    this.getComponentWidth()
+                }
+            })
+        },
+        // 获取组件的宽度
+        async getComponentWidth() {
+            // 延时一定时间,以获取dom尺寸
+            await uni.$u.sleep(30)
+            // #ifndef APP-NVUE
+            this.$uGetRect('.u-album__row').then((size) => {
+                this.singleWidth = size.width * this.singlePercent
+            })
+            // #endif
+
+            // #ifdef APP-NVUE
+            // 这里ref="u-album__row"所在的标签为通过for循环出来,导致this.$refs['u-album__row']是一个数组
+            const ref = this.$refs['u-album__row'][0]
+            ref &&
+                dom.getComponentRect(ref, (res) => {
+                    this.singleWidth = res.size.width * this.singlePercent
+                })
+            // #endif
+        }
+    }
+}
+</script>
+
+<style lang="scss" scoped>
+@import '../../libs/css/components.scss';
+
+.u-album {
+    @include flex(column);
+
+    &__row {
+        @include flex(row);
+        flex-wrap: wrap;
+
+        &__wrapper {
+            position: relative;
+
+            &__text {
+                position: absolute;
+                top: 0;
+                left: 0;
+                right: 0;
+                bottom: 0;
+                background-color: rgba(0, 0, 0, 0.3);
+                @include flex(row);
+                justify-content: center;
+                align-items: center;
+            }
+        }
+    }
+}
+</style>

+ 44 - 0
uview-ui/components/u-alert/props.js

@@ -0,0 +1,44 @@
+export default {
+    props: {
+        // 显示文字
+        title: {
+            type: String,
+            default: uni.$u.props.alert.title
+        },
+        // 主题,success/warning/info/error
+        type: {
+            type: String,
+            default: uni.$u.props.alert.type
+        },
+        // 辅助性文字
+        description: {
+            type: String,
+            default: uni.$u.props.alert.description
+        },
+        // 是否可关闭
+        closable: {
+            type: Boolean,
+            default: uni.$u.props.alert.closable
+        },
+        // 是否显示图标
+        showIcon: {
+            type: Boolean,
+            default: uni.$u.props.alert.showIcon
+        },
+        // 浅或深色调,light-浅色,dark-深色
+        effect: {
+            type: String,
+            default: uni.$u.props.alert.effect
+        },
+        // 文字是否居中
+        center: {
+            type: Boolean,
+            default: uni.$u.props.alert.center
+        },
+        // 字体大小
+        fontSize: {
+            type: [String, Number],
+            default: uni.$u.props.alert.fontSize
+        }
+    }
+}

+ 243 - 0
uview-ui/components/u-alert/u-alert.vue

@@ -0,0 +1,243 @@
+<template>
+	<u-transition
+	    mode="fade"
+	    :show="show"
+	>
+		<view
+		    class="u-alert"
+		    :class="[`u-alert--${type}--${effect}`]"
+		    @tap.stop="clickHandler"
+		    :style="[$u.addStyle(customStyle)]"
+		>
+			<view
+			    class="u-alert__icon"
+			    v-if="showIcon"
+			>
+				<u-icon
+				    :name="iconName"
+				    size="18"
+				    :color="iconColor"
+				></u-icon>
+			</view>
+			<view
+			    class="u-alert__content"
+			    :style="[{
+					paddingRight: closable ? '20px' : 0
+				}]"
+			>
+				<text
+				    class="u-alert__content__title"
+				    v-if="title"
+					:style="[{
+						fontSize: $u.addUnit(fontSize),
+						textAlign: center ? 'center' : 'left'
+					}]"
+				    :class="[effect === 'dark' ? 'u-alert__text--dark' : `u-alert__text--${type}--light`]"
+				>{{ title }}</text>
+				<text
+				    class="u-alert__content__desc"
+					v-if="description"
+					:style="[{
+						fontSize: $u.addUnit(fontSize),
+						textAlign: center ? 'center' : 'left'
+					}]"
+				    :class="[effect === 'dark' ? 'u-alert__text--dark' : `u-alert__text--${type}--light`]"
+				>{{ description }}</text>
+			</view>
+			<view
+			    class="u-alert__close"
+			    v-if="closable"
+			    @tap.stop="closeHandler"
+			>
+				<u-icon
+				    name="close"
+				    :color="iconColor"
+				    size="15"
+				></u-icon>
+			</view>
+		</view>
+	</u-transition>
+</template>
+
+<script>
+	import props from './props.js';
+	/**
+	 * Alert  警告提示
+	 * @description 警告提示,展现需要关注的信息。
+	 * @tutorial https://www.uviewui.com/components/alertTips.html
+	 * 
+	 * @property {String}			title       显示的文字 
+	 * @property {String}			type        使用预设的颜色  (默认 'warning' )
+	 * @property {String}			description 辅助性文字,颜色比title浅一点,字号也小一点,可选  
+	 * @property {Boolean}			closable    关闭按钮(默认为叉号icon图标)  (默认 false )
+	 * @property {Boolean}			showIcon    是否显示左边的辅助图标   ( 默认 false )
+	 * @property {String}			effect      多图时,图片缩放裁剪的模式  (默认 'light' )
+	 * @property {Boolean}			center		文字是否居中  (默认 false )
+	 * @property {String | Number}	fontSize    字体大小  (默认 14 )
+	 * @property {Object}			customStyle	定义需要用到的外部样式
+	 * @event    {Function}        click       点击组件时触发
+	 * @example  <u-alert :title="title"  type = "warning" :closable="closable" :description = "description"></u-alert>
+	 */
+	export default {
+		name: 'u-alert',
+		mixins: [uni.$u.mpMixin, uni.$u.mixin, props],
+		data() {
+			return {
+				show: true
+			}
+		},
+		computed: {
+			iconColor() {
+				return this.effect === 'light' ? this.type : '#fff'
+			},
+			// 不同主题对应不同的图标
+			iconName() {
+				switch (this.type) {
+					case 'success':
+						return 'checkmark-circle-fill';
+						break;
+					case 'error':
+						return 'close-circle-fill';
+						break;
+					case 'warning':
+						return 'error-circle-fill';
+						break;
+					case 'info':
+						return 'info-circle-fill';
+						break;
+					case 'primary':
+						return 'more-circle-fill';
+						break;
+					default: 
+						return 'error-circle-fill';
+				}
+			}
+		},
+		methods: {
+			// 点击内容
+			clickHandler() {
+				this.$emit('click')
+			},
+			// 点击关闭按钮
+			closeHandler() {
+				this.show = false
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	@import "../../libs/css/components.scss";
+
+	.u-alert {
+		position: relative;
+		background-color: $u-primary;
+		padding: 8px 10px;
+		@include flex(row);
+		align-items: center;
+		border-top-left-radius: 4px;
+		border-top-right-radius: 4px;
+		border-bottom-left-radius: 4px;
+		border-bottom-right-radius: 4px;
+
+		&--primary--dark {
+			background-color: $u-primary;
+		}
+
+		&--primary--light {
+			background-color: #ecf5ff;
+		}
+
+		&--error--dark {
+			background-color: $u-error;
+		}
+
+		&--error--light {
+			background-color: #FEF0F0;
+		}
+
+		&--success--dark {
+			background-color: $u-success;
+		}
+
+		&--success--light {
+			background-color: #f5fff0;
+		}
+
+		&--warning--dark {
+			background-color: $u-warning;
+		}
+
+		&--warning--light {
+			background-color: #FDF6EC;
+		}
+
+		&--info--dark {
+			background-color: $u-info;
+		}
+
+		&--info--light {
+			background-color: #f4f4f5;
+		}
+
+		&__icon {
+			margin-right: 5px;
+		}
+
+		&__content {
+			@include flex(column);
+			flex: 1;
+
+			&__title {
+				color: $u-main-color;
+				font-size: 14px;
+				font-weight: bold;
+				color: #fff;
+				margin-bottom: 2px;
+			}
+
+			&__desc {
+				color: $u-main-color;
+				font-size: 14px;
+				flex-wrap: wrap;
+				color: #fff;
+			}
+		}
+
+		&__title--dark,
+		&__desc--dark {
+			color: #FFFFFF;
+		}
+
+		&__text--primary--light,
+		&__text--primary--light {
+			color: $u-primary;
+		}
+
+		&__text--success--light,
+		&__text--success--light {
+			color: $u-success;
+		}
+
+		&__text--warning--light,
+		&__text--warning--light {
+			color: $u-warning;
+		}
+
+		&__text--error--light,
+		&__text--error--light {
+			color: $u-error;
+		}
+
+		&__text--info--light,
+		&__text--info--light {
+			color: $u-info;
+		}
+
+		&__close {
+			position: absolute;
+			top: 11px;
+			right: 10px;
+		}
+	}
+</style>

+ 52 - 0
uview-ui/components/u-avatar-group/props.js

@@ -0,0 +1,52 @@
+export default {
+    props: {
+        // 头像图片组
+        urls: {
+            type: Array,
+            default: uni.$u.props.avatarGroup.urls
+        },
+        // 最多展示的头像数量
+        maxCount: {
+            type: [String, Number],
+            default: uni.$u.props.avatarGroup.maxCount
+        },
+        // 头像形状
+        shape: {
+            type: String,
+            default: uni.$u.props.avatarGroup.shape
+        },
+        // 图片裁剪模式
+        mode: {
+            type: String,
+            default: uni.$u.props.avatarGroup.mode
+        },
+        // 超出maxCount时是否显示查看更多的提示
+        showMore: {
+            type: Boolean,
+            default: uni.$u.props.avatarGroup.showMore
+        },
+        // 头像大小
+        size: {
+            type: [String, Number],
+            default: uni.$u.props.avatarGroup.size
+        },
+        // 指定从数组的对象元素中读取哪个属性作为图片地址
+        keyName: {
+            type: String,
+            default: uni.$u.props.avatarGroup.keyName
+        },
+		// 头像之间的遮挡比例
+        gap: {
+            type: [String, Number],
+            validator(value) {
+                return value >= 0 && value <= 1
+            },
+            default: uni.$u.props.avatarGroup.gap
+        },
+		// 需额外显示的值
+		extraValue: {
+			type: [Number, String],
+			default: uni.$u.props.avatarGroup.extraValue
+		}
+    }
+}

+ 103 - 0
uview-ui/components/u-avatar-group/u-avatar-group.vue

@@ -0,0 +1,103 @@
+<template>
+	<view class="u-avatar-group">
+		<view
+		    class="u-avatar-group__item"
+		    v-for="(item, index) in showUrl"
+		    :key="index"
+		    :style="{
+				marginLeft: index === 0 ? 0 : $u.addUnit(-size * gap)
+			}"
+		>
+			<u-avatar
+			    :size="size"
+			    :shape="shape"
+			    :mode="mode"
+			    :src="$u.test.object(item) ? keyName && item[keyName] || item.url : item"
+			></u-avatar>
+			<view
+			    class="u-avatar-group__item__show-more"
+			    v-if="showMore && index === showUrl.length - 1 && (urls.length > maxCount || extraValue > 0)"
+				@tap="clickHandler"
+			>
+				<u--text
+				    color="#ffffff"
+				    :size="size * 0.4"
+				    :text="`+${extraValue || urls.length - showUrl.length}`"
+					align="center"
+					customStyle="justify-content: center"
+				></u--text>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import props from './props.js';
+	/**
+	 * AvatarGroup  头像组
+	 * @description 本组件一般用于展示头像的地方,如个人中心,或者评论列表页的用户头像展示等场所。
+	 * @tutorial https://www.uviewui.com/components/avatar.html
+	 * 
+	 * @property {Array}           urls     头像图片组 (默认 [] )
+	 * @property {String | Number} maxCount 最多展示的头像数量 ( 默认 5 )
+	 * @property {String}          shape    头像形状( 'circle' (默认) | 'square' )
+	 * @property {String}          mode     图片裁剪模式(默认 'scaleToFill' )
+	 * @property {Boolean}         showMore 超出maxCount时是否显示查看更多的提示 (默认 true )
+	 * @property {String | Number} size      头像大小 (默认 40 )
+	 * @property {String}          keyName  指定从数组的对象元素中读取哪个属性作为图片地址 
+	 * @property {String | Number} gap      头像之间的遮挡比例(0.4代表遮挡40%)  (默认 0.5 )
+	 * @property {String | Number} extraValue  需额外显示的值
+	 * @event    {Function}        showMore 头像组更多点击
+	 * @example  <u-avatar-group:urls="urls" size="35" gap="0.4" ></u-avatar-group:urls=>
+	 */
+	export default {
+		name: 'u-avatar-group',
+		mixins: [uni.$u.mpMixin, uni.$u.mixin, props],
+		data() {
+			return {
+
+			}
+		},
+		computed: {
+			showUrl() {
+				return this.urls.slice(0, this.maxCount)
+			}
+		},
+		methods: {
+			clickHandler() {
+				this.$emit('showMore')
+			}
+		},
+	}
+</script>
+
+<style lang="scss" scoped>
+	@import "../../libs/css/components.scss";
+
+	.u-avatar-group {
+		@include flex;
+
+		&__item {
+			margin-left: -10px;
+			position: relative;
+
+			&--no-indent {
+				// 如果你想质疑作者不会使用:first-child,说明你太年轻,因为nvue不支持
+				margin-left: 0;
+			}
+
+			&__show-more {
+				position: absolute;
+				top: 0;
+				bottom: 0;
+				left: 0;
+				right: 0;
+				background-color: rgba(0, 0, 0, 0.3);
+				@include flex;
+				align-items: center;
+				justify-content: center;
+				border-radius: 100px;
+			}
+		}
+	}
+</style>

+ 78 - 0
uview-ui/components/u-avatar/props.js

@@ -0,0 +1,78 @@
+export default {
+    props: {
+        // 头像图片路径(不能为相对路径)
+        src: {
+            type: String,
+            default: uni.$u.props.avatar.src
+        },
+        // 头像形状,circle-圆形,square-方形
+        shape: {
+            type: String,
+            default: uni.$u.props.avatar.shape
+        },
+        // 头像尺寸
+        size: {
+            type: [String, Number],
+            default: uni.$u.props.avatar.size
+        },
+        // 裁剪模式
+        mode: {
+            type: String,
+            default: uni.$u.props.avatar.mode
+        },
+        // 显示的文字
+        text: {
+            type: String,
+            default: uni.$u.props.avatar.text
+        },
+        // 背景色
+        bgColor: {
+            type: String,
+            default: uni.$u.props.avatar.bgColor
+        },
+        // 文字颜色
+        color: {
+            type: String,
+            default: uni.$u.props.avatar.color
+        },
+        // 文字大小
+        fontSize: {
+            type: [String, Number],
+            default: uni.$u.props.avatar.fontSize
+        },
+        // 显示的图标
+        icon: {
+            type: String,
+            default: uni.$u.props.avatar.icon
+        },
+        // 显示小程序头像,只对百度,微信,QQ小程序有效
+        mpAvatar: {
+            type: Boolean,
+            default: uni.$u.props.avatar.mpAvatar
+        },
+        // 是否使用随机背景色
+        randomBgColor: {
+            type: Boolean,
+            default: uni.$u.props.avatar.randomBgColor
+        },
+        // 加载失败的默认头像(组件有内置默认图片)
+        defaultUrl: {
+            type: String,
+            default: uni.$u.props.avatar.defaultUrl
+        },
+        // 如果配置了randomBgColor为true,且配置了此值,则从默认的背景色数组中取出对应索引的颜色值,取值0-19之间
+        colorIndex: {
+            type: [String, Number],
+            // 校验参数规则,索引在0-19之间
+            validator(n) {
+                return uni.$u.test.range(n, [0, 19]) || n === ''
+            },
+            default: uni.$u.props.avatar.colorIndex
+        },
+        // 组件标识符
+        name: {
+            type: String,
+            default: uni.$u.props.avatar.name
+        }
+    }
+}

Datei-Diff unterdrückt, da er zu groß ist
+ 163 - 0
uview-ui/components/u-avatar/u-avatar.vue


+ 54 - 0
uview-ui/components/u-back-top/props.js

@@ -0,0 +1,54 @@
+export default {
+    props: {
+        // 返回顶部的形状,circle-圆形,square-方形
+        mode: {
+            type: String,
+            default: uni.$u.props.backtop.mode
+        },
+        // 自定义图标
+        icon: {
+            type: String,
+            default: uni.$u.props.backtop.icon
+        },
+        // 提示文字
+        text: {
+            type: String,
+            default: uni.$u.props.backtop.text
+        },
+        // 返回顶部滚动时间
+        duration: {
+            type: [String, Number],
+            default: uni.$u.props.backtop.duration
+        },
+        // 滚动距离
+        scrollTop: {
+            type: [String, Number],
+            default: uni.$u.props.backtop.scrollTop
+        },
+        // 距离顶部多少距离显示,单位px
+        top: {
+            type: [String, Number],
+            default: uni.$u.props.backtop.top
+        },
+        // 返回顶部按钮到底部的距离,单位px
+        bottom: {
+            type: [String, Number],
+            default: uni.$u.props.backtop.bottom
+        },
+        // 返回顶部按钮到右边的距离,单位px
+        right: {
+            type: [String, Number],
+            default: uni.$u.props.backtop.right
+        },
+        // 层级
+        zIndex: {
+            type: [String, Number],
+            default: uni.$u.props.backtop.zIndex
+        },
+        // 图标的样式,对象形式
+        iconStyle: {
+            type: Object,
+            default: uni.$u.props.backtop.iconStyle
+        }
+    }
+}

+ 137 - 0
uview-ui/components/u-back-top/u-back-top.vue

@@ -0,0 +1,137 @@
+<template>
+	<u-transition
+	    mode="fade"
+	    :customStyle="backTopStyle"
+	    :show="show"
+	>
+		<view
+		    class="u-back-top"
+			:style="[contentStyle]"
+		    v-if="!$slots.default && !$slots.$default"
+			@click="backToTop"
+		>
+			<u-icon
+			    :name="icon"
+			    :custom-style="iconStyle"
+			></u-icon>
+			<text
+			    v-if="text"
+			    class="u-back-top__text"
+			>{{text}}</text>
+		</view>
+		<slot v-else />
+	</u-transition>
+</template>
+
+<script>
+	import props from './props.js';
+	// #ifdef APP-NVUE
+	const dom = weex.requireModule('dom')
+	// #endif
+	/**
+	 * backTop 返回顶部
+	 * @description 本组件一个用于长页面,滑动一定距离后,出现返回顶部按钮,方便快速返回顶部的场景。
+	 * @tutorial https://uviewui.com/components/backTop.html
+	 * 
+	 * @property {String}			mode  		返回顶部的形状,circle-圆形,square-方形 (默认 'circle' )
+	 * @property {String} 			icon 		自定义图标 (默认 'arrow-upward' ) 见官方文档示例
+	 * @property {String} 			text 		提示文字 
+	 * @property {String | Number}  duration	返回顶部滚动时间 (默认 100)
+	 * @property {String | Number}  scrollTop	滚动距离 (默认 0 )
+	 * @property {String | Number}  top  		距离顶部多少距离显示,单位px (默认 400 )
+	 * @property {String | Number}  bottom  	返回顶部按钮到底部的距离,单位px (默认 100 )
+	 * @property {String | Number}  right  		返回顶部按钮到右边的距离,单位px (默认 20 )
+	 * @property {String | Number}  zIndex 		层级   (默认 9 )
+	 * @property {Object<Object>}  	iconStyle 	图标的样式,对象形式   (默认 {color: '#909399',fontSize: '19px'})
+	 * @property {Object}			customStyle	定义需要用到的外部样式
+	 * 
+	 * @example <u-back-top :scrollTop="scrollTop"></u-back-top>
+	 */
+	export default {
+		name: 'u-back-top',
+		mixins: [uni.$u.mpMixin, uni.$u.mixin,props],
+		computed: {
+			backTopStyle() {
+				// 动画组件样式
+				const style = {
+					bottom: uni.$u.addUnit(this.bottom),
+					right: uni.$u.addUnit(this.right),
+					width: '40px',
+					height: '40px',
+					position: 'fixed',
+					zIndex: 10,
+				}
+				return style
+			},
+			show() {
+				let top
+				// 如果是rpx,转为px
+				if (/rpx$/.test(this.top)) {
+					top = uni.rpx2px(parseInt(this.top))
+				} else {
+					// 如果px,通过parseInt获取其数值部分
+					top = parseInt(this.top)
+				}
+				return this.scrollTop > top
+			},
+			contentStyle() {
+				const style = {}
+				let radius = 0
+				// 是否圆形
+				if(this.mode === 'circle') {
+					radius = '100px'
+				} else {
+					radius = '4px'
+				}
+				// 为了兼容安卓nvue,只能这么分开写
+				style.borderTopLeftRadius = radius
+				style.borderTopRightRadius = radius
+				style.borderBottomLeftRadius = radius
+				style.borderBottomRightRadius = radius
+				return uni.$u.deepMerge(style, uni.$u.addStyle(this.customStyle))
+			}
+		},
+		methods: {
+			backToTop() {
+				// #ifdef APP-NVUE
+				if (!this.$parent.$refs['u-back-top']) {
+					uni.$u.error(`nvue页面需要给页面最外层元素设置"ref='u-back-top'`)
+				}
+				dom.scrollToElement(this.$parent.$refs['u-back-top'], {
+					offset: 0
+				})
+				// #endif
+				
+				// #ifndef APP-NVUE
+				uni.pageScrollTo({
+					scrollTop: 0,
+					duration: this.duration
+				});
+				// #endif
+				this.$emit('click')
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	@import '../../libs/css/components.scss';
+     $u-back-top-flex:1 !default;
+     $u-back-top-height:100% !default;
+     $u-back-top-background-color:#E1E1E1 !default;
+     $u-back-top-tips-font-size:12px !default;
+	.u-back-top {
+		@include flex;
+		flex-direction: column;
+		align-items: center;
+		flex:$u-back-top-flex;
+		height: $u-back-top-height;
+		justify-content: center;
+		background-color: $u-back-top-background-color;
+
+		&__tips {
+			font-size:$u-back-top-tips-font-size;
+			transform: scale(0.8);
+		}
+	}
+</style>

+ 72 - 0
uview-ui/components/u-badge/props.js

@@ -0,0 +1,72 @@
+export default {
+    props: {
+        // 是否显示圆点
+        isDot: {
+            type: Boolean,
+            default: uni.$u.props.badge.isDot
+        },
+        // 显示的内容
+        value: {
+            type: [Number, String],
+            default: uni.$u.props.badge.value
+        },
+        // 是否显示
+        show: {
+            type: Boolean,
+            default: uni.$u.props.badge.show
+        },
+        // 最大值,超过最大值会显示 '{max}+'
+        max: {
+            type: [Number, String],
+            default: uni.$u.props.badge.max
+        },
+        // 主题类型,error|warning|success|primary
+        type: {
+            type: String,
+            default: uni.$u.props.badge.type
+        },
+        // 当数值为 0 时,是否展示 Badge
+        showZero: {
+            type: Boolean,
+            default: uni.$u.props.badge.showZero
+        },
+        // 背景颜色,优先级比type高,如设置,type参数会失效
+        bgColor: {
+            type: [String, null],
+            default: uni.$u.props.badge.bgColor
+        },
+        // 字体颜色
+        color: {
+            type: [String, null],
+            default: uni.$u.props.badge.color
+        },
+        // 徽标形状,circle-四角均为圆角,horn-左下角为直角
+        shape: {
+            type: String,
+            default: uni.$u.props.badge.shape
+        },
+        // 设置数字的显示方式,overflow|ellipsis|limit
+        // overflow会根据max字段判断,超出显示`${max}+`
+        // ellipsis会根据max判断,超出显示`${max}...`
+        // limit会依据1000作为判断条件,超出1000,显示`${value/1000}K`,比如2.2k、3.34w,最多保留2位小数
+        numberType: {
+            type: String,
+            default: uni.$u.props.badge.numberType
+        },
+        // 设置badge的位置偏移,格式为 [x, y],也即设置的为top和right的值,absolute为true时有效
+        offset: {
+            type: Array,
+            default: uni.$u.props.badge.offset
+        },
+        // 是否反转背景和字体颜色
+        inverted: {
+            type: Boolean,
+            default: uni.$u.props.badge.inverted
+        },
+        // 是否绝对定位
+        absolute: {
+            type: Boolean,
+            default: uni.$u.props.badge.absolute
+        }
+    }
+}

+ 171 - 0
uview-ui/components/u-badge/u-badge.vue

@@ -0,0 +1,171 @@
+<template>
+	<text
+		v-if="show && ((Number(value) === 0 ? showZero : true) || isDot)"
+		:class="[isDot ? 'u-badge--dot' : 'u-badge--not-dot', inverted && 'u-badge--inverted', shape === 'horn' && 'u-badge--horn', `u-badge--${type}${inverted ? '--inverted' : ''}`]"
+		:style="[$u.addStyle(customStyle), badgeStyle]"
+		class="u-badge"
+	>{{ isDot ? '' :showValue }}</text>
+</template>
+
+<script>
+	import props from './props.js';
+	/**
+	 * badge 徽标数
+	 * @description 该组件一般用于图标右上角显示未读的消息数量,提示用户点击,有圆点和圆包含文字两种形式。
+	 * @tutorial https://uviewui.com/components/badge.html
+	 * 
+	 * @property {Boolean} 			isDot 		是否显示圆点 (默认 false )
+	 * @property {String | Number} 	value 		显示的内容
+	 * @property {Boolean} 			show 		是否显示 (默认 true )
+	 * @property {String | Number} 	max 		最大值,超过最大值会显示 '{max}+'  (默认999)
+	 * @property {String} 			type 		主题类型,error|warning|success|primary (默认 'error' )
+	 * @property {Boolean} 			showZero	当数值为 0 时,是否展示 Badge (默认 false )
+	 * @property {String} 			bgColor 	背景颜色,优先级比type高,如设置,type参数会失效
+	 * @property {String} 			color 		字体颜色 (默认 '#ffffff' )
+	 * @property {String} 			shape 		徽标形状,circle-四角均为圆角,horn-左下角为直角 (默认 'circle' )
+	 * @property {String} 			numberType	设置数字的显示方式,overflow|ellipsis|limit  (默认 'overflow' )
+	 * @property {Array}} 			offset		设置badge的位置偏移,格式为 [x, y],也即设置的为top和right的值,absolute为true时有效
+	 * @property {Boolean} 			inverted	是否反转背景和字体颜色(默认 false )
+	 * @property {Boolean} 			absolute	是否绝对定位(默认 false )
+	 * @property {Object}			customStyle	定义需要用到的外部样式
+	 * @example <u-badge :type="type" :count="count"></u-badge>
+	 */
+	export default {
+		name: 'u-badge',
+		mixins: [uni.$u.mpMixin, props, uni.$u.mixin],
+		computed: {
+			// 是否将badge中心与父组件右上角重合
+			boxStyle() {
+				let style = {};
+				return style;
+			},
+			// 整个组件的样式
+			badgeStyle() {
+				const style = {}
+				if(this.color) {
+					style.color = this.color
+				}
+				if (this.bgColor && !this.inverted) {
+					style.backgroundColor = this.bgColor
+				}
+				if (this.absolute) {
+					style.position = 'absolute'
+					// 如果有设置offset参数
+					if(this.offset.length) {
+						// top和right分为为offset的第一个和第二个值,如果没有第二个值,则right等于top
+						const top = this.offset[0]
+						const right = this.offset[1] || top
+						style.top = uni.$u.addUnit(top)
+						style.right = uni.$u.addUnit(right)
+					}
+				}
+				return style
+			},
+			showValue() {
+				switch (this.numberType) {
+					case "overflow":
+						return Number(this.value) > Number(this.max) ? this.max + "+" : this.value
+						break;
+					case "ellipsis":
+						return Number(this.value) > Number(this.max) ? "..." : this.value
+						break;
+					case "limit":
+						return Number(this.value) > 999 ? Number(this.value) >= 9999 ?
+							Math.floor(this.value / 1e4 * 100) / 100 + "w" : Math.floor(this.value /
+								1e3 * 100) / 100 + "k" : this.value
+						break;
+					default:
+						return Number(this.value)
+				}
+			},
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	@import "../../libs/css/components.scss";
+
+	$u-badge-primary: $u-primary !default;
+	$u-badge-error: $u-error !default;
+	$u-badge-success: $u-success !default;
+	$u-badge-info: $u-info !default;
+	$u-badge-warning: $u-warning !default;
+	$u-badge-dot-radius: 100px !default;
+	$u-badge-dot-size: 8px !default;
+	$u-badge-dot-right: 4px !default;
+	$u-badge-dot-top: 0 !default;
+	$u-badge-text-font-size: 11px !default;
+	$u-badge-text-right: 10px !default;
+	$u-badge-text-padding: 2px 5px !default;
+	$u-badge-text-align: center !default;
+	$u-badge-text-color: #FFFFFF !default;
+
+	.u-badge {
+		border-top-right-radius: $u-badge-dot-radius;
+		border-top-left-radius: $u-badge-dot-radius;
+		border-bottom-left-radius: $u-badge-dot-radius;
+		border-bottom-right-radius: $u-badge-dot-radius;
+		@include flex;
+		line-height: $u-badge-text-font-size;
+		text-align: $u-badge-text-align;
+		font-size: $u-badge-text-font-size;
+		color: $u-badge-text-color;
+
+		&--dot {
+			height: $u-badge-dot-size;
+			width: $u-badge-dot-size;
+		}
+		
+		&--inverted {
+			font-size: 13px;
+		}
+		
+		&--not-dot {
+			padding: $u-badge-text-padding;
+		}
+
+		&--horn {
+			border-bottom-left-radius: 0;
+		}
+
+		&--primary {
+			background-color: $u-badge-primary;
+		}
+		
+		&--primary--inverted {
+			color: $u-badge-primary;
+		}
+
+		&--error {
+			background-color: $u-badge-error;
+		}
+		
+		&--error--inverted {
+			color: $u-badge-error;
+		}
+
+		&--success {
+			background-color: $u-badge-success;
+		}
+		
+		&--success--inverted {
+			color: $u-badge-success;
+		}
+
+		&--info {
+			background-color: $u-badge-info;
+		}
+		
+		&--info--inverted {
+			color: $u-badge-info;
+		}
+
+		&--warning {
+			background-color: $u-badge-warning;
+		}
+		
+		&--warning--inverted {
+			color: $u-badge-warning;
+		}
+	}
+</style>

+ 46 - 0
uview-ui/components/u-button/nvue.scss

@@ -0,0 +1,46 @@
+$u-button-active-opacity:0.75 !default;
+$u-button-loading-text-margin-left:4px !default;
+$u-button-text-color: #FFFFFF !default;
+$u-button-text-plain-error-color:$u-error !default;
+$u-button-text-plain-warning-color:$u-warning !default;
+$u-button-text-plain-success-color:$u-success !default;
+$u-button-text-plain-info-color:$u-info !default;
+$u-button-text-plain-primary-color:$u-primary !default;
+.u-button {
+	&--active {
+		opacity: $u-button-active-opacity;
+	}
+	
+	&--active--plain {
+		background-color: rgb(217, 217, 217);
+	}
+	
+	&__loading-text {
+		margin-left:$u-button-loading-text-margin-left;
+	}
+	
+	&__text,
+	&__loading-text {
+		color:$u-button-text-color;
+	}
+	
+	&__text--plain--error {
+		color:$u-button-text-plain-error-color;
+	}
+	
+	&__text--plain--warning {
+		color:$u-button-text-plain-warning-color;
+	}
+	
+	&__text--plain--success{
+		color:$u-button-text-plain-success-color;
+	}
+	
+	&__text--plain--info {
+		color:$u-button-text-plain-info-color;
+	}
+	
+	&__text--plain--primary {
+		color:$u-button-text-plain-primary-color;
+	}
+}

+ 161 - 0
uview-ui/components/u-button/props.js

@@ -0,0 +1,161 @@
+/*
+ * @Author       : LQ
+ * @Description  :
+ * @version      : 1.0
+ * @Date         : 2021-08-16 10:04:04
+ * @LastAuthor   : LQ
+ * @lastTime     : 2021-08-16 10:04:24
+ * @FilePath     : /u-view2.0/uview-ui/components/u-button/props.js
+ */
+export default {
+    props: {
+        // 是否细边框
+        hairline: {
+            type: Boolean,
+            default: uni.$u.props.button.hairline
+        },
+        // 按钮的预置样式,info,primary,error,warning,success
+        type: {
+            type: String,
+            default: uni.$u.props.button.type
+        },
+        // 按钮尺寸,large,normal,small,mini
+        size: {
+            type: String,
+            default: uni.$u.props.button.size
+        },
+        // 按钮形状,circle(两边为半圆),square(带圆角)
+        shape: {
+            type: String,
+            default: uni.$u.props.button.shape
+        },
+        // 按钮是否镂空
+        plain: {
+            type: Boolean,
+            default: uni.$u.props.button.plain
+        },
+        // 是否禁止状态
+        disabled: {
+            type: Boolean,
+            default: uni.$u.props.button.disabled
+        },
+        // 是否加载中
+        loading: {
+            type: Boolean,
+            default: uni.$u.props.button.loading
+        },
+        // 加载中提示文字
+        loadingText: {
+            type: [String, Number],
+            default: uni.$u.props.button.loadingText
+        },
+        // 加载状态图标类型
+        loadingMode: {
+            type: String,
+            default: uni.$u.props.button.loadingMode
+        },
+        // 加载图标大小
+        loadingSize: {
+            type: [String, Number],
+            default: uni.$u.props.button.loadingSize
+        },
+        // 开放能力,具体请看uniapp稳定关于button组件部分说明
+        // https://uniapp.dcloud.io/component/button
+        openType: {
+            type: String,
+            default: uni.$u.props.button.openType
+        },
+        // 用于 <form> 组件,点击分别会触发 <form> 组件的 submit/reset 事件
+        // 取值为submit(提交表单),reset(重置表单)
+        formType: {
+            type: String,
+            default: uni.$u.props.button.formType
+        },
+        // 打开 APP 时,向 APP 传递的参数,open-type=launchApp时有效
+        // 只微信小程序、QQ小程序有效
+        appParameter: {
+            type: String,
+            default: uni.$u.props.button.appParameter
+        },
+        // 指定是否阻止本节点的祖先节点出现点击态,微信小程序有效
+        hoverStopPropagation: {
+            type: Boolean,
+            default: uni.$u.props.button.hoverStopPropagation
+        },
+        // 指定返回用户信息的语言,zh_CN 简体中文,zh_TW 繁体中文,en 英文。只微信小程序有效
+        lang: {
+            type: String,
+            default: uni.$u.props.button.lang
+        },
+        // 会话来源,open-type="contact"时有效。只微信小程序有效
+        sessionFrom: {
+            type: String,
+            default: uni.$u.props.button.sessionFrom
+        },
+        // 会话内消息卡片标题,open-type="contact"时有效
+        // 默认当前标题,只微信小程序有效
+        sendMessageTitle: {
+            type: String,
+            default: uni.$u.props.button.sendMessageTitle
+        },
+        // 会话内消息卡片点击跳转小程序路径,open-type="contact"时有效
+        // 默认当前分享路径,只微信小程序有效
+        sendMessagePath: {
+            type: String,
+            default: uni.$u.props.button.sendMessagePath
+        },
+        // 会话内消息卡片图片,open-type="contact"时有效
+        // 默认当前页面截图,只微信小程序有效
+        sendMessageImg: {
+            type: String,
+            default: uni.$u.props.button.sendMessageImg
+        },
+        // 是否显示会话内消息卡片,设置此参数为 true,用户进入客服会话会在右下角显示"可能要发送的小程序"提示,
+        // 用户点击后可以快速发送小程序消息,open-type="contact"时有效
+        showMessageCard: {
+            type: Boolean,
+            default: uni.$u.props.button.showMessageCard
+        },
+        // 额外传参参数,用于小程序的data-xxx属性,通过target.dataset.name获取
+        dataName: {
+            type: String,
+            default: uni.$u.props.button.dataName
+        },
+        // 节流,一定时间内只能触发一次
+        throttleTime: {
+            type: [String, Number],
+            default: uni.$u.props.button.throttleTime
+        },
+        // 按住后多久出现点击态,单位毫秒
+        hoverStartTime: {
+            type: [String, Number],
+            default: uni.$u.props.button.hoverStartTime
+        },
+        // 手指松开后点击态保留时间,单位毫秒
+        hoverStayTime: {
+            type: [String, Number],
+            default: uni.$u.props.button.hoverStayTime
+        },
+        // 按钮文字,之所以通过props传入,是因为slot传入的话
+        // nvue中无法控制文字的样式
+        text: {
+            type: [String, Number],
+            default: uni.$u.props.button.text
+        },
+        // 按钮图标
+        icon: {
+            type: String,
+            default: uni.$u.props.button.icon
+        },
+        // 按钮图标
+        iconColor: {
+            type: String,
+            default: uni.$u.props.button.icon
+        },
+        // 按钮颜色,支持传入linear-gradient渐变色
+        color: {
+            type: String,
+            default: uni.$u.props.button.color
+        }
+    }
+}

+ 490 - 0
uview-ui/components/u-button/u-button.vue

@@ -0,0 +1,490 @@
+<template>
+    <!-- #ifndef APP-NVUE -->
+    <button
+        :hover-start-time="Number(hoverStartTime)"
+        :hover-stay-time="Number(hoverStayTime)"
+        :form-type="formType"
+        :open-type="openType"
+        :app-parameter="appParameter"
+        :hover-stop-propagation="hoverStopPropagation"
+        :send-message-title="sendMessageTitle"
+        :send-message-path="sendMessagePath"
+        :lang="lang"
+        :data-name="dataName"
+        :session-from="sessionFrom"
+        :send-message-img="sendMessageImg"
+        :show-message-card="showMessageCard"
+        @getphonenumber="getphonenumber"
+        @getuserinfo="getuserinfo"
+        @error="error"
+        @opensetting="opensetting"
+        @launchapp="launchapp"
+        :hover-class="!disabled && !loading ? 'u-button--active' : ''"
+        class="u-button u-reset-button"
+        :style="[baseColor, $u.addStyle(customStyle)]"
+        @tap="clickHandler"
+        :class="bemClass"
+    >
+        <template v-if="loading">
+            <u-loading-icon
+                :mode="loadingMode"
+                :size="textSize * 1.15"
+                :color="loadingColor"
+            ></u-loading-icon>
+            <text
+                class="u-button__loading-text"
+                :style="[{ fontSize: textSize + 'px' }]"
+                >{{ loadingText || text }}</text
+            >
+        </template>
+        <template v-else>
+            <u-icon
+                v-if="icon"
+                :name="icon"
+                :color="iconColorCom"
+                :size="textSize * 1.35"
+                :customStyle="{ marginRight: '2px' }"
+            ></u-icon>
+            <slot>
+                <text
+                    class="u-button__text"
+                    :style="[{ fontSize: textSize + 'px' }]"
+                    >{{ text }}</text
+                >
+            </slot>
+        </template>
+    </button>
+    <!-- #endif -->
+
+    <!-- #ifdef APP-NVUE -->
+    <view
+        :hover-start-time="Number(hoverStartTime)"
+        :hover-stay-time="Number(hoverStayTime)"
+        class="u-button"
+        :hover-class="
+            !disabled && !loading && !color && (plain || type === 'info')
+                ? 'u-button--active--plain'
+                : !disabled && !loading && !plain
+                ? 'u-button--active'
+                : ''
+        "
+        @tap="clickHandler"
+        :class="bemClass"
+        :style="[baseColor, $u.addStyle(customStyle)]"
+    >
+        <template v-if="loading">
+            <u-loading-icon
+                :mode="loadingMode"
+                :size="textSize * 1.15"
+                :color="loadingColor"
+            ></u-loading-icon>
+            <text
+                class="u-button__loading-text"
+                :style="[nvueTextStyle]"
+                :class="[plain && `u-button__text--plain--${type}`]"
+                >{{ loadingText || text }}</text
+            >
+        </template>
+        <template v-else>
+            <u-icon
+                v-if="icon"
+                :name="icon"
+                :color="iconColorCom"
+                :size="textSize * 1.35"
+            ></u-icon>
+            <text
+                class="u-button__text"
+                :style="[
+                    {
+                        marginLeft: icon ? '2px' : 0,
+                    },
+                    nvueTextStyle,
+                ]"
+                :class="[plain && `u-button__text--plain--${type}`]"
+                >{{ text }}</text
+            >
+        </template>
+    </view>
+    <!-- #endif -->
+</template>
+
+<script>
+import button from "../../libs/mixin/button.js";
+import openType from "../../libs/mixin/openType.js";
+import props from "./props.js";
+/**
+ * button 按钮
+ * @description Button 按钮
+ * @tutorial https://www.uviewui.com/components/button.html
+ *
+ * @property {Boolean}			hairline				是否显示按钮的细边框 (默认 true )
+ * @property {String}			type					按钮的预置样式,info,primary,error,warning,success (默认 'info' )
+ * @property {String}			size					按钮尺寸,large,normal,mini (默认 normal)
+ * @property {String}			shape					按钮形状,circle(两边为半圆),square(带圆角) (默认 'square' )
+ * @property {Boolean}			plain					按钮是否镂空,背景色透明 (默认 false)
+ * @property {Boolean}			disabled				是否禁用 (默认 false)
+ * @property {Boolean}			loading					按钮名称前是否带 loading 图标(App-nvue 平台,在 ios 上为雪花,Android上为圆圈) (默认 false)
+ * @property {String | Number}	loadingText				加载中提示文字
+ * @property {String}			loadingMode				加载状态图标类型 (默认 'spinner' )
+ * @property {String | Number}	loadingSize				加载图标大小 (默认 15 )
+ * @property {String}			openType				开放能力,具体请看uniapp稳定关于button组件部分说明
+ * @property {String}			formType				用于 <form> 组件,点击分别会触发 <form> 组件的 submit/reset 事件
+ * @property {String}			appParameter			打开 APP 时,向 APP 传递的参数,open-type=launchApp时有效 (注:只微信小程序、QQ小程序有效)
+ * @property {Boolean}			hoverStopPropagation	指定是否阻止本节点的祖先节点出现点击态,微信小程序有效(默认 true )
+ * @property {String}			lang					指定返回用户信息的语言,zh_CN 简体中文,zh_TW 繁体中文,en 英文(默认 en )
+ * @property {String}			sessionFrom				会话来源,openType="contact"时有效
+ * @property {String}			sendMessageTitle		会话内消息卡片标题,openType="contact"时有效
+ * @property {String}			sendMessagePath			会话内消息卡片点击跳转小程序路径,openType="contact"时有效
+ * @property {String}			sendMessageImg			会话内消息卡片图片,openType="contact"时有效
+ * @property {Boolean}			showMessageCard			是否显示会话内消息卡片,设置此参数为 true,用户进入客服会话会在右下角显示"可能要发送的小程序"提示,用户点击后可以快速发送小程序消息,openType="contact"时有效(默认false)
+ * @property {String}			dataName				额外传参参数,用于小程序的data-xxx属性,通过target.dataset.name获取
+ * @property {String | Number}	throttleTime			节流,一定时间内只能触发一次 (默认 0 )
+ * @property {String | Number}	hoverStartTime			按住后多久出现点击态,单位毫秒 (默认 0 )
+ * @property {String | Number}	hoverStayTime			手指松开后点击态保留时间,单位毫秒 (默认 200 )
+ * @property {String | Number}	text					按钮文字,之所以通过props传入,是因为slot传入的话(注:nvue中无法控制文字的样式)
+ * @property {String}			icon					按钮图标
+ * @property {String}			iconColor				按钮图标颜色
+ * @property {String}			color					按钮颜色,支持传入linear-gradient渐变色
+ * @property {Object}			customStyle				定义需要用到的外部样式
+ *
+ * @event {Function}	click			非禁止并且非加载中,才能点击
+ * @event {Function}	getphonenumber	open-type="getPhoneNumber"时有效
+ * @event {Function}	getuserinfo		用户点击该按钮时,会返回获取到的用户信息,从返回参数的detail中获取到的值同uni.getUserInfo
+ * @event {Function}	error			当使用开放能力时,发生错误的回调
+ * @event {Function}	opensetting		在打开授权设置页并关闭后回调
+ * @event {Function}	launchapp		打开 APP 成功的回调
+ * @example <u-button>月落</u-button>
+ */
+export default {
+    name: "u-button",
+    // #ifdef MP
+    mixins: [uni.$u.mpMixin, uni.$u.mixin, button, openType, props],
+    // #endif
+    // #ifndef MP
+    mixins: [uni.$u.mpMixin, uni.$u.mixin, props],
+    // #endif
+    data() {
+        return {};
+    },
+    computed: {
+        // 生成bem风格的类名
+        bemClass() {
+            // this.bem为一个computed变量,在mixin中
+            if (!this.color) {
+                return this.bem(
+                    "button",
+                    ["type", "shape", "size"],
+                    ["disabled", "plain", "hairline"]
+                );
+            } else {
+                // 由于nvue的原因,在有color参数时,不需要传入type,否则会生成type相关的类型,影响最终的样式
+                return this.bem(
+                    "button",
+                    ["shape", "size"],
+                    ["disabled", "plain", "hairline"]
+                );
+            }
+        },
+        loadingColor() {
+            if (this.plain) {
+                // 如果有设置color值,则用color值,否则使用type主题颜色
+                return this.color
+                    ? this.color
+                    : this.$u.config.color[`u-${this.type}`];
+            }
+            if (this.type === "info") {
+                return "#c9c9c9";
+            }
+            return "rgb(200, 200, 200)";
+        },
+        iconColorCom() {
+            // 如果是镂空状态,设置了color就用color值,否则使用主题颜色,
+            // u-icon的color能接受一个主题颜色的值
+			if (this.iconColor) return this.iconColor;
+			if (this.plain) {
+                return this.color ? this.color : this.type;
+            } else {
+                return this.type === "info" ? "#000000" : "#ffffff";
+            }
+        },
+        baseColor() {
+            let style = {};
+            if (this.color) {
+                // 针对自定义了color颜色的情况,镂空状态下,就是用自定义的颜色
+                style.color = this.plain ? this.color : "white";
+                if (!this.plain) {
+                    // 非镂空,背景色使用自定义的颜色
+                    style["background-color"] = this.color;
+                }
+                if (this.color.indexOf("gradient") !== -1) {
+                    // 如果自定义的颜色为渐变色,不显示边框,以及通过backgroundImage设置渐变色
+                    // weex文档说明可以写borderWidth的形式,为什么这里需要分开写?
+                    // 因为weex是阿里巴巴为了部门业绩考核而做的你懂的东西,所以需要这么写才有效
+                    style.borderTopWidth = 0;
+                    style.borderRightWidth = 0;
+                    style.borderBottomWidth = 0;
+                    style.borderLeftWidth = 0;
+                    if (!this.plain) {
+                        style.backgroundImage = this.color;
+                    }
+                } else {
+                    // 非渐变色,则设置边框相关的属性
+                    style.borderColor = this.color;
+                    style.borderWidth = "1px";
+                    style.borderStyle = "solid";
+                }
+            }
+            return style;
+        },
+        // nvue版本按钮的字体不会继承父组件的颜色,需要对每一个text组件进行单独的设置
+        nvueTextStyle() {
+            let style = {};
+            // 针对自定义了color颜色的情况,镂空状态下,就是用自定义的颜色
+            if (this.type === "info") {
+                style.color = "#323233";
+            }
+            if (this.color) {
+                style.color = this.plain ? this.color : "white";
+            }
+            style.fontSize = this.textSize + "px";
+            return style;
+        },
+        // 字体大小
+        textSize() {
+            let fontSize = 14,
+                { size } = this;
+            if (size === "large") fontSize = 16;
+            if (size === "normal") fontSize = 14;
+            if (size === "small") fontSize = 12;
+            if (size === "mini") fontSize = 10;
+            return fontSize;
+        },
+    },
+    methods: {
+        clickHandler() {
+            // 非禁止并且非加载中,才能点击
+            if (!this.disabled && !this.loading) {
+				// 进行节流控制,每this.throttle毫秒内,只在开始处执行
+				uni.$u.throttle(() => {
+					this.$emit("click");
+				}, this.throttleTime);
+            }
+        },
+        // 下面为对接uniapp官方按钮开放能力事件回调的对接
+        getphonenumber(res) {
+            this.$emit("getphonenumber", res);
+        },
+        getuserinfo(res) {
+            this.$emit("getuserinfo", res);
+        },
+        error(res) {
+            this.$emit("error", res);
+        },
+        opensetting(res) {
+            this.$emit("opensetting", res);
+        },
+        launchapp(res) {
+            this.$emit("launchapp", res);
+        },
+    },
+};
+</script>
+
+<style lang="scss" scoped>
+@import "../../libs/css/components.scss";
+
+/* #ifndef APP-NVUE */
+@import "./vue.scss";
+/* #endif */
+
+/* #ifdef APP-NVUE */
+@import "./nvue.scss";
+/* #endif */
+
+$u-button-u-button-height: 40px !default;
+$u-button-text-font-size: 15px !default;
+$u-button-loading-text-font-size: 15px !default;
+$u-button-loading-text-margin-left: 4px !default;
+$u-button-large-width: 100% !default;
+$u-button-large-height: 50px !default;
+$u-button-normal-padding: 0 12px !default;
+$u-button-large-padding: 0 15px !default;
+$u-button-normal-font-size: 14px !default;
+$u-button-small-min-width: 60px !default;
+$u-button-small-height: 30px !default;
+$u-button-small-padding: 0px 8px !default;
+$u-button-mini-padding: 0px 8px !default;
+$u-button-small-font-size: 12px !default;
+$u-button-mini-height: 22px !default;
+$u-button-mini-font-size: 10px !default;
+$u-button-mini-min-width: 50px !default;
+$u-button-disabled-opacity: 0.5 !default;
+$u-button-info-color: #323233 !default;
+$u-button-info-background-color: #fff !default;
+$u-button-info-border-color: #ebedf0 !default;
+$u-button-info-border-width: 1px !default;
+$u-button-info-border-style: solid !default;
+$u-button-success-color: #fff !default;
+$u-button-success-background-color: $u-success !default;
+$u-button-success-border-color: $u-button-success-background-color !default;
+$u-button-success-border-width: 1px !default;
+$u-button-success-border-style: solid !default;
+$u-button-primary-color: #fff !default;
+$u-button-primary-background-color: $u-primary !default;
+$u-button-primary-border-color: $u-button-primary-background-color !default;
+$u-button-primary-border-width: 1px !default;
+$u-button-primary-border-style: solid !default;
+$u-button-error-color: #fff !default;
+$u-button-error-background-color: $u-error !default;
+$u-button-error-border-color: $u-button-error-background-color !default;
+$u-button-error-border-width: 1px !default;
+$u-button-error-border-style: solid !default;
+$u-button-warning-color: #fff !default;
+$u-button-warning-background-color: $u-warning !default;
+$u-button-warning-border-color: $u-button-warning-background-color !default;
+$u-button-warning-border-width: 1px !default;
+$u-button-warning-border-style: solid !default;
+$u-button-block-width: 100% !default;
+$u-button-circle-border-top-right-radius: 100px !default;
+$u-button-circle-border-top-left-radius: 100px !default;
+$u-button-circle-border-bottom-left-radius: 100px !default;
+$u-button-circle-border-bottom-right-radius: 100px !default;
+$u-button-square-border-top-right-radius: 3px !default;
+$u-button-square-border-top-left-radius: 3px !default;
+$u-button-square-border-bottom-left-radius: 3px !default;
+$u-button-square-border-bottom-right-radius: 3px !default;
+$u-button-icon-min-width: 1em !default;
+$u-button-plain-background-color: #fff !default;
+$u-button-hairline-border-width: 0.5px !default;
+
+.u-button {
+    height: $u-button-u-button-height;
+    position: relative;
+    align-items: center;
+    justify-content: center;
+    @include flex;
+    /* #ifndef APP-NVUE */
+    box-sizing: border-box;
+    /* #endif */
+    flex-direction: row;
+
+    &__text {
+        font-size: $u-button-text-font-size;
+    }
+
+    &__loading-text {
+        font-size: $u-button-loading-text-font-size;
+        margin-left: $u-button-loading-text-margin-left;
+    }
+
+    &--large {
+        /* #ifndef APP-NVUE */
+        width: $u-button-large-width;
+        /* #endif */
+        height: $u-button-large-height;
+        padding: $u-button-large-padding;
+    }
+
+    &--normal {
+        padding: $u-button-normal-padding;
+        font-size: $u-button-normal-font-size;
+    }
+
+    &--small {
+        /* #ifndef APP-NVUE */
+        min-width: $u-button-small-min-width;
+        /* #endif */
+        height: $u-button-small-height;
+        padding: $u-button-small-padding;
+        font-size: $u-button-small-font-size;
+    }
+
+    &--mini {
+        height: $u-button-mini-height;
+        font-size: $u-button-mini-font-size;
+        /* #ifndef APP-NVUE */
+        min-width: $u-button-mini-min-width;
+        /* #endif */
+        padding: $u-button-mini-padding;
+    }
+
+    &--disabled {
+        opacity: $u-button-disabled-opacity;
+    }
+
+    &--info {
+        color: $u-button-info-color;
+        background-color: $u-button-info-background-color;
+        border-color: $u-button-info-border-color;
+        border-width: $u-button-info-border-width;
+        border-style: $u-button-info-border-style;
+    }
+
+    &--success {
+        color: $u-button-success-color;
+        background-color: $u-button-success-background-color;
+        border-color: $u-button-success-border-color;
+        border-width: $u-button-success-border-width;
+        border-style: $u-button-success-border-style;
+    }
+
+    &--primary {
+        color: $u-button-primary-color;
+        background-color: $u-button-primary-background-color;
+        border-color: $u-button-primary-border-color;
+        border-width: $u-button-primary-border-width;
+        border-style: $u-button-primary-border-style;
+    }
+
+    &--error {
+        color: $u-button-error-color;
+        background-color: $u-button-error-background-color;
+        border-color: $u-button-error-border-color;
+        border-width: $u-button-error-border-width;
+        border-style: $u-button-error-border-style;
+    }
+
+    &--warning {
+        color: $u-button-warning-color;
+        background-color: $u-button-warning-background-color;
+        border-color: $u-button-warning-border-color;
+        border-width: $u-button-warning-border-width;
+        border-style: $u-button-warning-border-style;
+    }
+
+    &--block {
+        @include flex;
+        width: $u-button-block-width;
+    }
+
+    &--circle {
+        border-top-right-radius: $u-button-circle-border-top-right-radius;
+        border-top-left-radius: $u-button-circle-border-top-left-radius;
+        border-bottom-left-radius: $u-button-circle-border-bottom-left-radius;
+        border-bottom-right-radius: $u-button-circle-border-bottom-right-radius;
+    }
+
+    &--square {
+        border-bottom-left-radius: $u-button-square-border-top-right-radius;
+        border-bottom-right-radius: $u-button-square-border-top-left-radius;
+        border-top-left-radius: $u-button-square-border-bottom-left-radius;
+        border-top-right-radius: $u-button-square-border-bottom-right-radius;
+    }
+
+    &__icon {
+        /* #ifndef APP-NVUE */
+        min-width: $u-button-icon-min-width;
+        line-height: inherit !important;
+        vertical-align: top;
+        /* #endif */
+    }
+
+    &--plain {
+        background-color: $u-button-plain-background-color;
+    }
+
+    &--hairline {
+        border-width: $u-button-hairline-border-width !important;
+    }
+}
+</style>

+ 0 - 0
uview-ui/components/u-button/vue.scss


Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden.