ソースを参照

feat:新增第三方信息模块

443166679@qq.com 1 年間 前
コミット
1f5b0a6e88
91 ファイル変更6321 行追加826 行削除
  1. 9 9
      components/en-utils/en-button/en-button.vue
  2. 59 0
      components/en-utils/en-data-picker/en-data-picker.vue
  3. 27 23
      components/en-utils/en-image/en-image.vue
  4. 3 4
      components/en-utils/en-nav/en-nav.vue
  5. 82 0
      components/en-utils/en-radio/en-radio.vue
  6. 90 0
      components/en-utils/en-upload/en-upload.vue
  7. 3 0
      main.js
  8. 24 22
      page_subpack/personal_details/personal_details.vue
  9. 132 116
      page_subpack/system_setting/system_setting.vue
  10. 0 1
      page_task/apply/apply.vue
  11. 107 65
      page_task/identity/identity.vue
  12. 2 6
      page_task/identity_upload/identity_upload.vue
  13. 410 395
      page_task/information/information.vue
  14. 14 8
      page_task/module/wh-input.vue
  15. 109 106
      page_task/task_details/module/record.vue
  16. 8 0
      page_task/tripartite_info/components/guarantee_company.vue
  17. 156 0
      page_task/tripartite_info/components/guarantee_user.vue
  18. 357 0
      page_task/tripartite_info/tripartite_info.vue
  19. 54 55
      pages.json
  20. 1 1
      static/css/common.css
  21. BIN
      static/img/login/eye-filled.png
  22. BIN
      static/img/login/eye.png
  23. BIN
      static/img/statistics/Group 1783.png
  24. BIN
      static/img/statistics/Group 1786.png
  25. BIN
      static/img/statistics/Group 2112.png
  26. BIN
      static/img/statistics/Group 2113.png
  27. BIN
      static/img/task/task-three.png
  28. 8 7
      uni_modules/uni-data-picker/components/uni-data-picker/uni-data-picker.vue
  29. 12 8
      uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-pickerview.vue
  30. 7 0
      uni_modules/uv-action-sheet/changelog.md
  31. 50 0
      uni_modules/uv-action-sheet/components/uv-action-sheet/props.js
  32. 280 0
      uni_modules/uv-action-sheet/components/uv-action-sheet/uv-action-sheet.vue
  33. 92 0
      uni_modules/uv-action-sheet/package.json
  34. 13 0
      uni_modules/uv-action-sheet/readme.md
  35. 5 0
      uni_modules/uv-gap/changelog.md
  36. 25 0
      uni_modules/uv-gap/components/uv-gap/props.js
  37. 36 0
      uni_modules/uv-gap/components/uv-gap/uv-gap.vue
  38. 87 0
      uni_modules/uv-gap/package.json
  39. 12 0
      uni_modules/uv-gap/readme.md
  40. 31 0
      uni_modules/uv-icon/changelog.md
  41. 160 0
      uni_modules/uv-icon/components/uv-icon/icons.js
  42. 90 0
      uni_modules/uv-icon/components/uv-icon/props.js
  43. 226 0
      uni_modules/uv-icon/components/uv-icon/uv-icon.vue
  44. BIN
      uni_modules/uv-icon/components/uv-icon/uvicons.ttf
  45. 83 0
      uni_modules/uv-icon/package.json
  46. 15 0
      uni_modules/uv-icon/readme.md
  47. 5 0
      uni_modules/uv-line/changelog.md
  48. 34 0
      uni_modules/uv-line/components/uv-line/props.js
  49. 60 0
      uni_modules/uv-line/components/uv-line/uv-line.vue
  50. 87 0
      uni_modules/uv-line/package.json
  51. 11 0
      uni_modules/uv-line/readme.md
  52. 9 0
      uni_modules/uv-loading-icon/changelog.md
  53. 67 0
      uni_modules/uv-loading-icon/components/uv-loading-icon/props.js
  54. 347 0
      uni_modules/uv-loading-icon/components/uv-loading-icon/uv-loading-icon.vue
  55. 87 0
      uni_modules/uv-loading-icon/package.json
  56. 19 0
      uni_modules/uv-loading-icon/readme.md
  57. 9 0
      uni_modules/uv-overlay/changelog.md
  58. 25 0
      uni_modules/uv-overlay/components/uv-overlay/props.js
  59. 85 0
      uni_modules/uv-overlay/components/uv-overlay/uv-overlay.vue
  60. 88 0
      uni_modules/uv-overlay/package.json
  61. 11 0
      uni_modules/uv-overlay/readme.md
  62. 33 0
      uni_modules/uv-picker/changelog.md
  63. 95 0
      uni_modules/uv-picker/components/uv-picker/props.js
  64. 330 0
      uni_modules/uv-picker/components/uv-picker/uv-picker.vue
  65. 90 0
      uni_modules/uv-picker/package.json
  66. 21 0
      uni_modules/uv-picker/readme.md
  67. 18 0
      uni_modules/uv-popup/changelog.md
  68. 45 0
      uni_modules/uv-popup/components/uv-popup/keypress.js
  69. 539 0
      uni_modules/uv-popup/components/uv-popup/uv-popup.vue
  70. 92 0
      uni_modules/uv-popup/package.json
  71. 21 0
      uni_modules/uv-popup/readme.md
  72. 11 0
      uni_modules/uv-safe-bottom/changelog.md
  73. 67 0
      uni_modules/uv-safe-bottom/components/uv-safe-bottom/uv-safe-bottom.vue
  74. 87 0
      uni_modules/uv-safe-bottom/package.json
  75. 11 0
      uni_modules/uv-safe-bottom/readme.md
  76. 7 0
      uni_modules/uv-status-bar/changelog.md
  77. 8 0
      uni_modules/uv-status-bar/components/uv-status-bar/props.js
  78. 54 0
      uni_modules/uv-status-bar/components/uv-status-bar/uv-status-bar.vue
  79. 87 0
      uni_modules/uv-status-bar/package.json
  80. 10 0
      uni_modules/uv-status-bar/readme.md
  81. 2 0
      uni_modules/uv-toolbar/changelog.md
  82. 40 0
      uni_modules/uv-toolbar/components/uv-toolbar/props.js
  83. 109 0
      uni_modules/uv-toolbar/components/uv-toolbar/uv-toolbar.vue
  84. 87 0
      uni_modules/uv-toolbar/package.json
  85. 31 0
      uni_modules/uv-toolbar/readme.md
  86. 19 0
      uni_modules/uv-transition/changelog.md
  87. 131 0
      uni_modules/uv-transition/components/uv-transition/createAnimation.js
  88. 31 0
      uni_modules/uv-transition/components/uv-transition/props.js
  89. 320 0
      uni_modules/uv-transition/components/uv-transition/uv-transition.vue
  90. 87 0
      uni_modules/uv-transition/package.json
  91. 15 0
      uni_modules/uv-transition/readme.md

+ 9 - 9
components/en-utils/en-button/en-button.vue

@@ -29,10 +29,10 @@
 				type: String,
 				default: '提交'
 			},
-      centreText: {
-        type: String,
-        default: '取消'
-      },
+			centreText: {
+				type: String,
+				default: '取消'
+			},
 			leftText: {
 				type: String,
 				default: '取消'
@@ -71,15 +71,15 @@
 		},
 		methods: {
 			onSubmit() {
-        console.log('----------------------')
+				console.log('----------------------')
 				this.$emit('onSubmit')
 			},
 			onLeftSubmit() {
 				this.$emit('onLeftSubmit')
 			},
-      onCentreSubmit(){
-        this.$emit('onCentreSubmit')
-      }
+			onCentreSubmit() {
+				this.$emit('onCentreSubmit')
+			}
 		}
 	}
 </script>
@@ -104,7 +104,7 @@
 	.fixed-button {
 		position: fixed;
 		bottom: 0;
-		z-index: 999999;
+		z-index: 4;
 		background: #fff;
 		width: calc(100% - 60rpx);
 		padding-bottom: calc(20rpx + env(safe-area-inset-bottom));

+ 59 - 0
components/en-utils/en-data-picker/en-data-picker.vue

@@ -0,0 +1,59 @@
+<template>
+	<view class="">
+		<uni-data-picker style="z-index: 10;" :popup-title="title" :localdata="localData" v-model="dataValue"
+			@change="onChange">
+			<slot></slot>
+		</uni-data-picker>
+	</view>
+</template>
+
+<script>
+	import {
+		getRegion
+	} from "@/api/common";
+
+	export default {
+		props: {
+			title: {
+				type: String,
+				default: '请选择所在城市'
+			},
+		},
+		data() {
+			return {
+				localData: [],
+			}
+		},
+		created() {
+			this.onGetRegion()
+		},
+		methods: {
+			onGetRegion() {
+				let localData = uni.getStorageSync('sysCityData')
+				if (!localData) {
+					getRegion({
+						type: 1
+					}).then((res) => {
+						if (res.code === 1) {
+							this.localData = res.data;
+							uni.setStorageSync('sysCityData', JSON.stringify(this.localData))
+						} else {
+							tools.error(res.msg);
+						}
+					})
+				} else {
+					this.localData = JSON.parse(localData)
+				}
+			},
+			onChange(e) {
+				const address = e.detail.value.map(item => {
+					return item.text
+				})
+				this.$emit('onChange', address.join("/"))
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+</style>

+ 27 - 23
components/en-utils/en-image/en-image.vue

@@ -1,6 +1,6 @@
 <template>
 	<view>
-		<view class="row">
+		<view class="row image-box">
 			<image class="image-item r-10 m-r20 m-b20" :style="[{width},{height}]" :src="item" mode="aspectFill"
 				v-for="(item,index) in imgList" @click="onPreviewImage(item)"></image>
 		</view>
@@ -25,34 +25,34 @@
 			img: {
 				default: []
 			},
-      previewImage:{
-        default: []
-      }
+			previewImage: {
+				default: []
+			}
 		},
 		data() {
 			return {
-        imgList:[]
-      }
+				imgList: []
+			}
+		},
+		watch: {
+			'img': function() {
+				this.setImgList()
+			}
 		},
-    watch:{
-      'img':function () {
-        this.setImgList()
-      }
-    },
-    mounted() {
-      this.setImgList()
-    },
-    methods: {
-      setImgList(){
-        if(typeof this.img ==="string"){
-          this.imgList=[this.img]
-        }else {
-          this.imgList=this.img
-        }
-      },
+		mounted() {
+			this.setImgList()
+		},
+		methods: {
+			setImgList() {
+				if (typeof this.img === "string") {
+					this.imgList = [this.img]
+				} else {
+					this.imgList = this.img
+				}
+			},
 			onPreviewImage(index) {
 				uni.previewImage({
-					urls: this.previewImage?this.previewImage:this.imgList,
+					urls: this.previewImage ? this.previewImage : this.imgList,
 					current: index
 				});
 			}
@@ -61,6 +61,10 @@
 </script>
 
 <style lang="scss" scoped>
+	.image-box {
+		flex-wrap: wrap;
+	}
+
 	.image-item {
 		display: block;
 	}

+ 3 - 4
components/en-utils/en-nav/en-nav.vue

@@ -16,10 +16,9 @@
 		<!-- 带背景插槽 start-->
 		<slot></slot>
 		<!-- 带背景插槽 end-->
-		<view class="sys-list-background-color header" style="z-index: 2;" :style="{opacity:is_fixed?navOpacity:1}">
-			<image class="" style="width: 100%;"
-				:style="[{height:is_fixed?`${$tools.topHeight()+navsHeight}px` :`${$tools.topHeight()+fixedHeight}px`}]"
-				:src="bgList[genre].nav_path" mode="">
+		<view class="sys-list-background-color header" style="z-index: 2;"
+			:style="[{height:is_fixed?`${$tools.topHeight()+navsHeight}px` :`${$tools.topHeight()+fixedHeight}px`},{opacity:is_fixed?navOpacity:1}]">
+			<image class="" style="width: 100%;height: 100%;" :src="bgList[genre].nav_path" mode="">
 			</image>
 		</view>
 	</view>

+ 82 - 0
components/en-utils/en-radio/en-radio.vue

@@ -0,0 +1,82 @@
+<template>
+	<view class="row-justify-sb center p-tb30 bor-bottom-1 size-28">
+		<view class="wh-text"><text>{{name}}</text></view>
+		<view class="row-c">
+			<view class="row-c radiu-item m-l20" :class="{'active-radiu':radioValue === item.value}"
+				v-for="(item,index) in list" :key="index" @click="onSelect(item.value)">
+				<image class="wh-30 m-r20"
+					:src="radioValue == item.value?'/static/img/information/correct.png':'/static/img/information/error.png'"
+					mode="aspectFill"></image>
+				<text>{{item.text}}</text>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		props: {
+			name: {
+				type: String,
+				default: ''
+			},
+			value: {
+				type: Number,
+				default: 0
+			},
+			list: {
+				type: Array,
+				default: () => []
+			}
+		},
+		watch: {
+			value: {
+				handler(value) {
+					this.radioValue = value
+				},
+				immediate: true
+			},
+		},
+		data() {
+			return {
+				radioValue: 0
+			}
+		},
+		methods: {
+			onSelect(value) {
+				this.radioValue = value
+				this.$emit('onSelect', value)
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.wh-text {
+		width: 110rpx;
+		text-align: justify;
+		text-align-last: justify;
+		vertical-align: top;
+		height: 38rpx;
+	}
+
+	.wh-text:after {
+		content: '';
+		width: 110rpx;
+		height: 0;
+		display: inline-block;
+		overflow: hidden;
+	}
+
+	.radiu-item {
+		width: 140rpx;
+		padding: 6rpx 16rpx;
+		border-radius: 100rpx;
+		border: 1rpx solid #CCCCCC;
+	}
+
+	.active-radiu {
+		color: #0FB160;
+		border: 1rpx solid #0FB160;
+	}
+</style>

+ 90 - 0
components/en-utils/en-upload/en-upload.vue

@@ -0,0 +1,90 @@
+<template>
+	<view class="">
+		<view class="upload-image">
+			<image class="wh-110 m-t20 m-r16" :style="[{width},{height}]" src="/static/img/logo.png" mode="aspectFill"
+				v-for="(item,index) in 3" @click="onPreviewImage(index)">
+			</image>
+			<view class="add-image column-c justify-center m-t20" @click="addUpload()" v-if="is_add">
+				<image class="wh-45" src="/static/img/task-details/Mask oup.png" mode=""></image>
+				<text class="size-24 text-color-666">{{text}}</text>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		props: {
+			text: {
+				type: String,
+				default: '图片'
+			},
+			is_add: {
+				type: Boolean,
+				default: true
+			},
+			width: {
+				type: String,
+				default: '110rpx'
+			},
+			height: {
+				type: String,
+				default: '110rpx'
+			},
+			fileList: {
+				type: Array,
+				default: () => []
+			}
+		},
+		data() {
+			return {
+				fileData: []
+			}
+		},
+		methods: {
+			addUpload() {
+				uni.chooseImage({
+					count: 6,
+					sizeType: ['original', 'compressed'], //可以指定是原图还是压缩图,默认二者都有
+					success: function(res) {
+						const tempFilePaths = res.tempFilePaths
+						console.log(tempFilePaths)
+						// tempFilePaths.forEach(item => {
+						// 	uni.uploadFile({
+						// 		url: 'https://www.example.com/upload', //仅为示例,非真实的接口地址
+						// 		filePath: tempFilePaths[0],
+						// 		name: 'file',
+						// 		formData: {
+						// 			'user': 'test'
+						// 		},
+						// 		success: (uploadFileRes) => {
+						// 			console.log(uploadFileRes.data);
+						// 		}
+						// 	})
+						// })
+					}
+				});
+			},
+			onPreviewImage(index) {
+				uni.previewImage({
+					urls: this.fileList,
+					current: index
+				});
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.upload-image {
+		display: flex;
+		flex-wrap: wrap;
+	}
+
+	.add-image {
+		width: 110rpx;
+		height: 110rpx;
+		border-radius: 10rpx;
+		background: #F4F4F4;
+	}
+</style>

+ 3 - 0
main.js

@@ -23,6 +23,8 @@ import EnButton from './components/en-utils/en-button/en-button.vue'
 import Enblank from './components/en-utils/en-blank/en-blank.vue'
 // scroll-view组件
 import EnScroll from './components/en-scroll/en-scroll.vue'
+// 单选
+import EnRadio from './components/en-utils/en-radio/en-radio.vue'
 
 Vue.component('Search', Search)
 Vue.component('Nav', Nav)
@@ -30,6 +32,7 @@ Vue.component('Tab', Tab)
 Vue.component('EnButton', EnButton)
 Vue.component('Enblank', Enblank)
 Vue.component('EnScroll', EnScroll)
+Vue.component('EnRadio', EnRadio)
 
 Vue.prototype.$tools = tools
 

+ 24 - 22
page_subpack/personal_details/personal_details.vue

@@ -1,5 +1,5 @@
 <template>
-	<view class="total-page page-box size-28">
+	<view class="total-page page-box size-28 task-bg">
 		<Nav :type="1" :genre="2" :title="title" :is_fixed="true">
 			<view class="m-30 m-b20">
 				<view class="row-justify-sb sys-background-fff  center p-30 bor-bottom-1"
@@ -51,7 +51,9 @@
 
 <script>
 	import EnInput from "@/components/en-from/en-input/index.vue";
-  import {getMemberInfo} from "@/api/user";
+	import {
+		getMemberInfo
+	} from "@/api/user";
 
 	export default {
 		components: {
@@ -62,33 +64,33 @@
 				id: 0,
 				title: '',
 				phoneShake: false,
-        memberData:{
-          'head_img':"",
-          'name':"",
-          'nickname':"",
-          'phone':"",
-          'position_name':"",
-        }
+				memberData: {
+					'head_img': "",
+					'name': "",
+					'nickname': "",
+					'phone': "",
+					'position_name': "",
+				}
 			}
 		},
 		onLoad(options) {
 			this.id = options.id
 			this.title = options.title
-      this.getMemberInfo()
-      uni.$on('updateMemberInfo',()=>{
-        this.getMemberInfo()
-      })
+			this.getMemberInfo()
+			uni.$on('updateMemberInfo', () => {
+				this.getMemberInfo()
+			})
 		},
 		methods: {
-      getMemberInfo() {
-        getMemberInfo({
-          'type': 2
-        }).then((res) => {
-          if (res.code === 1) {
-            this.memberData = res.data;
-          }
-        })
-      },
+			getMemberInfo() {
+				getMemberInfo({
+					'type': 2
+				}).then((res) => {
+					if (res.code === 1) {
+						this.memberData = res.data;
+					}
+				})
+			},
 			onSubmit() {
 				this.phoneShake = true
 				setTimeout(() => {

+ 132 - 116
page_subpack/system_setting/system_setting.vue

@@ -1,22 +1,26 @@
 <template>
-	<view class="total-page page-box">
+	<view class="total-page page-box task-bg">
 		<Nav :type="1" :genre="2" :title="title" :is_fixed="true">
 			<view class="sys-background-fff m-30 r-30 p-lr30">
 				<view class="from-animation animate__animated animate__fadeIn" v-if="id === 2">
 					<view class="p-tb30 bor-bottom-1 row-c" :class="{'apply-shake':phoneShake}">
 						<text class="size-28 sys-weight-400 input-text">原密码</text>
-						<en-input focus type="password" class="m-l30" placeholder="请输入原密码" v-model="passData.old_password"
-							maxlength="11"></en-input>
+						<en-input focus type="password" class="m-l30" placeholder="请输入原密码"
+							v-model="passData.old_password" maxlength="11"></en-input>
 					</view>
 					<view class="p-tb30 row-c">
 						<text class="size-28 sys-weight-400 input-text">新密码</text>
-						<en-input type="password" class="m-l30" placeholder="请输入新密码"
+						<en-input :type="is_password?'text':'password'" class="m-l30" placeholder="请输入新密码"
 							v-model="passData.password"></en-input>
+						<image class="eye-class wh-45" :src="`/static/img/login/${is_password?'eye-filled':'eye'}.png`"
+							mode="aspectFill" @click="is_password = !is_password"></image>
 					</view>
 					<view class="p-tb30 row-c">
 						<text class="size-28 sys-weight-400 input-text">确认密码</text>
-						<en-input type="password" class="m-l30" placeholder="请确认密码"
+						<en-input :type="is_pass_two?'text':'password'" class="m-l30" placeholder="请确认密码"
 							v-model="passData.pass_two"></en-input>
+						<image class="eye-class wh-45" :src="`/static/img/login/${is_pass_two?'eye-filled':'eye'}.png`"
+							mode="aspectFill" @click="is_pass_two = !is_pass_two"></image>
 					</view>
 				</view>
 				<view class="from-animation animate__animated animate__fadeIn" v-if="id === 3">
@@ -27,11 +31,12 @@
 					</view>
 					<view class="p-tb30 row-c">
 						<text class="size-28 sys-weight-400 p-r30">验证码</text>
-						<en-input type="password" class="flex" placeholder="验证码"
-							v-model="memberData.code"></en-input>
-						<view class="text-color-dominant sys-size-28 sys-weight-400" @click="getVerifiedCode" v-if="timeNum<=0">发送验证码
+						<en-input type="password" class="flex" placeholder="验证码" v-model="memberData.code"></en-input>
+						<view class="text-color-dominant sys-size-28 sys-weight-400" @click="getVerifiedCode"
+							v-if="timeNum<=0">发送验证码
+						</view>
+						<view class="login-send text-color-dominant sys-size-28 sys-weight-400" v-else>{{timeNum}} s
 						</view>
-						 <view class="login-send text-color-dominant sys-size-28 sys-weight-400" v-else>{{timeNum}} s</view>
 					</view>
 				</view>
 				<view class="p-tb30 row-c" :class="{'apply-shake':phoneShake}" v-if="id === 4">
@@ -52,9 +57,16 @@
 
 <script>
 	import EnInput from "@/components/en-from/en-input/index.vue";
-  import {getMemberInfo, setPassword, updateMemberInfo, updatePhone} from "@/api/user";
-  import tools from "@/service/tools";
-  import {commonSend} from "@/api/common";
+	import {
+		getMemberInfo,
+		setPassword,
+		updateMemberInfo,
+		updatePhone
+	} from "@/api/user";
+	import tools from "@/service/tools";
+	import {
+		commonSend
+	} from "@/api/common";
 
 	export default {
 		components: {
@@ -63,54 +75,56 @@
 		data() {
 			return {
 				id: 0,
-        timeNum: 0,
+				timeNum: 0,
 				title: '',
 				placeholder: '',
 				phoneShake: false,
-        memberData:{
-          'head_img':"",
-          'name':"",
-          'nickname':"",
-          'phone':"",
-          'position_name':"",
-          code:'',
-        },
-        passData:{
-          old_password:'',
-          password:'',
-          pass_two:'',
-        },
+				is_password: false,
+				is_pass_two: false,
+				memberData: {
+					'head_img': "",
+					'name': "",
+					'nickname': "",
+					'phone': "",
+					'position_name': "",
+					code: '',
+				},
+				passData: {
+					old_password: '',
+					password: '',
+					pass_two: '',
+				},
 			}
 		},
 		onLoad(options) {
-			this.id = options.id*1
+			this.id = options.id * 1
 			this.title = options.title
-      this.getMemberInfo()
+			this.getMemberInfo()
 		},
 		methods: {
-      getMemberInfo() {
-        getMemberInfo({
-          'type': 2
-        }).then((res) => {
-          if (res.code === 1) {
-            this.memberData = res.data;
-          }
-        })
-      },
-      updateMemberInfo(){
+			getMemberInfo() {
+				getMemberInfo({
+					'type': 2
+				}).then((res) => {
+					if (res.code === 1) {
+						this.memberData = res.data;
+					}
+				})
+			},
+			updateMemberInfo() {
 
-        updateMemberInfo(this.memberData).then((res)=>{
-          if(res.code===1){
-            tools.success('设置成功')
-            uni.$emit('updateMemberInfo')
-            setTimeout(()=>{
-             tools.leftClick()
-            },1500)
-          }else {
-            tools.error(res.msg)
-          }
-        })
-      },
+				updateMemberInfo(this.memberData).then((res) => {
+					if (res.code === 1) {
+						tools.success('设置成功')
+						uni.$emit('updateMemberInfo')
+						setTimeout(() => {
+							tools.leftClick()
+						}, 1500)
+					} else {
+						tools.error(res.msg)
+					}
+				})
+			},
 			onSubmit() {
 				// this.phoneShake = true
 				// setTimeout(() => {
@@ -118,74 +132,74 @@
 				// 	this.passwordShake = false
 				// 	this.codedShake = false
 				// }, 500)
-        if(this.id>=4){
-          this.updateMemberInfo()
-        }else if(this.id===3){
-          this.updatePhone()
-        }else {
-          this.setPassword()
-        }
+				if (this.id >= 4) {
+					this.updateMemberInfo()
+				} else if (this.id === 3) {
+					this.updatePhone()
+				} else {
+					this.setPassword()
+				}
+			},
+			setPassword() {
+				if (this.passData.password !== this.passData.pass_two) {
+					tools.error('两次密码不一致')
+					return
+				}
+				setPassword(this.passData).then((res) => {
+					if (res.code === 1) {
+						tools.success(res.msg)
+						setTimeout(() => {
+							this.leftClick();
+						}, 1500)
+					} else {
+						tools.error(res.msg)
+					}
+				})
+			},
+			updatePhone() {
+				updatePhone(this.memberData).then((res) => {
+					if (res.code === 1) {
+						tools.success(res.msg)
+						uni.$emit('updateMemberInfo')
+						setTimeout(() => {
+							this.leftClick();
+						}, 1500)
+					} else {
+						tools.error(res.msg)
+					}
+				})
 			},
-      setPassword(){
-        if(this.passData.password!==this.passData.pass_two){
-          tools.error('两次密码不一致')
-          return
-        }
-        setPassword(this.passData).then((res)=>{
-          if(res.code===1){
-            tools.success(res.msg)
-            setTimeout(()=>{
-              this.leftClick();
-            },1500)
-          }else {
-            tools.error(res.msg)
-          }
-        })
-      },
-      updatePhone(){
-        updatePhone(this.memberData).then((res)=>{
-          if(res.code===1){
-            tools.success(res.msg)
-            uni.$emit('updateMemberInfo')
-            setTimeout(()=>{
-              this.leftClick();
-            },1500)
-          }else {
-            tools.error(res.msg)
-          }
-        })
-      },
 			getVerifiedCode() {
-        if (this.timeNum > 0) {
-          return;
-        }
-        if (this.memberData.phone === '') {
-          tools.error("请输入手机号码")
-          return;
-        }
-        let regPhone = /^(?:(?:\+|00)86)?1\d{10}$/;
-        if (!regPhone.test(this.memberData.phone)) {
-          tools.error("手机号码格式错误")
-          return;
-        }
-        commonSend({
-          'phone': this.memberData.phone,
-          'send_type': 'retrieve'
-        }).then((res) => {
-          if (res.code === 1) {
-            tools.success(res.msg);
-            this.timeNum = 60;
-            this.timer = setInterval(() => {
-              this.timeNum--;
-              if (this.timeNum <= 0) {
-                clearInterval(this.timer);
-              }
-            }, 1000);
-          } else {
-            tools.error(res.msg);
-          }
-        })
-      }
+				if (this.timeNum > 0) {
+					return;
+				}
+				if (this.memberData.phone === '') {
+					tools.error("请输入手机号码")
+					return;
+				}
+				let regPhone = /^(?:(?:\+|00)86)?1\d{10}$/;
+				if (!regPhone.test(this.memberData.phone)) {
+					tools.error("手机号码格式错误")
+					return;
+				}
+				commonSend({
+					'phone': this.memberData.phone,
+					'send_type': 'retrieve'
+				}).then((res) => {
+					if (res.code === 1) {
+						tools.success(res.msg);
+						this.timeNum = 60;
+						this.timer = setInterval(() => {
+							this.timeNum--;
+							if (this.timeNum <= 0) {
+								clearInterval(this.timer);
+							}
+						}, 1000);
+					} else {
+						tools.error(res.msg);
+					}
+				})
+			}
 		}
 	}
 </script>
@@ -233,4 +247,6 @@
 		line-height: 96rpx;
 		text-align: center;
 	}
+
+	.eye-class {}
 </style>

+ 0 - 1
page_task/apply/apply.vue

@@ -37,7 +37,6 @@
 							</view>
 							<text>万元</text>
 						</view>
-
 						<view class="row-justify-sb center p-tb30 bor-bottom-1 size-28">
 							<view class="wh-text"><text></text>是否涉农</view>
 							<view class="row-c">

+ 107 - 65
page_task/identity/identity.vue

@@ -1,6 +1,7 @@
 <template>
 	<view class="total-page page-env-160 page-box task-bg">
-		<Nav :title="title" :genre="1" :fixedHeight="fixedHeight">
+		<Nav :title="title" :genre="4" is_fixed :opacity="scrollTop" :fixedHeight="fixedHeight"
+			:navsHeight="fixedHeight" :bgHeight="fixedHeight">
 			<!-- 步鄹条start -->
 			<view class="task-tabs" :style="{top:`${$tools.topHeight()}px`}">
 				<view class="row-justify-sb m-lr50 m-tb20">
@@ -28,8 +29,7 @@
 			</view>
 			<!-- 步鄹条end -->
 		</Nav>
-		<view class="sys-list-background-color attestation_content page-env-160"
-			:style="[{top:`${$tools.topHeight() + fixedHeight}px`}]">
+		<view class=" page-env-20" :style="[{top:`${$tools.topHeight() + fixedHeight}px`}]">
 			<!-- 身份认证start -->
 			<view class="m-t20 m-lr30 text-color-12" v-if="step === 1">
 				<view class="sys-background-fff r-20 p-lr30 p-t30">
@@ -44,17 +44,22 @@
 							hover-class="is-hover" @click="onAuthentication">去认证</button>
 					</view>
 					<view class="p-t20">
-						<WhInput :value="formData.name" :name="'姓名'" :placeholder="'待自动录入'"></WhInput>
-						<WhInput :value="formData.gender" :name="'性别'" :placeholder="'待自动录入'"></WhInput>
-						<WhInput :value="formData.id_number" :is_border="false" :name="'身份证号'" :placeholder="'待自动录入'">
+						<WhInput :value="formData.name" name="姓名" placeholder="待自动录入" disabled></WhInput>
+						<WhInput :value="formData.gender" name="性别" placeholder="待自动录入" disabled></WhInput>
+						<WhInput :value="formData.id_number" :is_border="false" :name="'身份证号'" placeholder="待自动录入"
+							disabled>
 						</WhInput>
 					</view>
 				</view>
-				<view class="sys-background-fff r-20 m-t20 p-lr30 p-t30">
+				<view class="sys-background-fff r-20 m-t20 p-lr30">
 					<WhInput :name="'联系方式'"></WhInput>
-					<WhInput :name="'常居城市'" is_select></WhInput>
+					<EnDataPicker title="请选择常居城市" @onChange="onChange">
+						<WhInput :value="formData.firm_address" name="常居城市" disabled is_select></WhInput>
+					</EnDataPicker>
 					<WhInput :name="'详细地址'"></WhInput>
-					<WhInput :is_border="false" :name="'婚姻状态'" is_select></WhInput>
+					<view @click="onMarriage">
+						<WhInput :is_border="false" name="婚姻状态" disabled is_select></WhInput>
+					</view>
 				</view>
 			</view>
 			<!-- 身份认证end -->
@@ -63,10 +68,12 @@
 			<view class="m-t20 m-lr30 text-color-12" v-if="step === 2">
 				<view class="sys-background-fff r-20 m-t20 p-lr30 p-t30">
 					<view class="size-30 sys-weight-600 p-b30">教育职业背景</view>
-					<WhInput :name="'联系方式'"></WhInput>
-					<WhInput :name="'常居城市'" is_select></WhInput>
-					<WhInput :name="'详细地址'"></WhInput>
-					<WhInput :is_border="false" :name="'婚姻状态'" is_select></WhInput>
+					<WhInput name="学历" disabled is_select></WhInput>
+					<EnRadio :value="formData.is_farming" name="雇佣类型" :list="farming" @onSelect="onFarming">
+					</EnRadio>
+					<WhInput name="企业名称"></WhInput>
+					<WhInput name="企业城市" disabled is_select></WhInput>
+					<WhInput :is_border="false" name="详细地址"></WhInput>
 				</view>
 			</view>
 			<!-- 教育职业背景end -->
@@ -75,82 +82,103 @@
 			<view class="m-t20 m-lr30 text-color-12" v-if="step === 3 || step === 4">
 				<view class="sys-background-fff r-20 m-t20 p-lr30 p-t30">
 					<view class="size-30 sys-weight-600 p-b30">社保信息</view>
-					<WhInput :name="'联系方式'"></WhInput>
-					<WhInput :name="'常居城市'" is_select></WhInput>
-					<WhInput :name="'详细地址'"></WhInput>
-					<WhInput :is_border="false" :name="'婚姻状态'" is_select></WhInput>
+					<WhInput name="缴纳时间" rightText="月" placeholder="请输入社保连续缴纳时间"></WhInput>
+					<WhInput name="公积金" rightText="元" placeholder="请输入公积金缴纳基数"></WhInput>
 				</view>
 			</view>
 			<!-- 社保信息end -->
 		</view>
 		<!-- <EnButton :text="'完成'" @onSubmit="onSubmit" v-if="step === 3 || step === 4"></EnButton> -->
-		<EnButton v-if="step === 3 || step === 4" is_both :leftText="'上一步'" :rightText="'保存'" @onLeftSubmit="onStep(0)"  @onSubmit="onSubmit" />
-		<EnButton v-else is_both :leftText="'保存'" :rightText="'下一步'" @onLeftSubmit="onLeftSubmit" @onSubmit="onStep(1)" >	</EnButton>
-
+		<EnButton v-if="step === 3 || step === 4" is_both :leftText="'上一步'" :rightText="'保存'" @onLeftSubmit="onStep(0)"
+			@onSubmit="onSubmit" />
+		<EnButton v-else is_both :leftText="'保存'" :rightText="'下一步'" @onLeftSubmit="onLeftSubmit" @onSubmit="onStep(1)">
+		</EnButton>
+		<uv-action-sheet ref="actionSheet" round="10" :actions="list" title="请选择婚姻状态" @select="onSelect">
+		</uv-action-sheet>
 	</view>
 </template>
 <!-- correct -->
 <script>
 	import TaskImgTab from "@/common/task/task_ima_tab.vue"
 	import WhInput from "@/page_task/module/wh-input.vue"
+	import EnDataPicker from "@/components/en-utils/en-data-picker/en-data-picker.vue"
 
 	export default {
 		components: {
 			TaskImgTab,
-			WhInput
+			WhInput,
+			EnDataPicker
 		},
 		data() {
 			return {
+				list: [{
+					name: '单身',
+					value: 1
+				}, {
+					name: '已婚',
+					value: 2
+				}],
+				farming: [{
+					text: '受薪',
+					value: 1
+				}, {
+					text: '自雇',
+					value: 2
+				}],
 				title: '',
 				step: 1,
+				scrollTop: 0,
 				fixedHeight: 75,
 				stepTwo: ['two', 'double', 'correct', 'correct'],
 				stepThree: ['three', 'three', 'double', 'correct'],
 				value: '',
 				formData: {
-          "name": "",
-          "birthday": "",
-          "sex": 1,
-          "id_number": "",
-          "identity_one": "",
-          "identity_two": "",
-          "phone": "",
-          "marriage_type": 1,
-          "education_type": '',
-          "education_name": '',
-          "employ_type": 1,
-          "firm_name": "",
-          "firm_address": "",
-          "resident_city": "",
-          "resident_address": "",
-          "mate_name": "",
-          "mate_phone": "",
-          "mate_id_number": "",
-          "mate_firm": "",
-          "m_code": "",
-          "code": "",
-          "m_identity_one": "",
-          "m_identity_two": "",
-          "firm_city": "",
-          "social_num": "",
-          "reserved_money": "",
-          "marriage_img": [],
-          "credit_img": []
+					"name": "",
+					"birthday": "",
+					"sex": 1,
+					"id_number": "",
+					"identity_one": "",
+					"identity_two": "",
+					"phone": "",
+					"marriage_type": 1,
+					"education_type": '',
+					"education_name": '',
+					"employ_type": 1,
+					"firm_name": "",
+					"firm_address": "",
+					"resident_city": "",
+					"resident_address": "",
+					"mate_name": "",
+					"mate_phone": "",
+					"mate_id_number": "",
+					"mate_firm": "",
+					"m_code": "",
+					"code": "",
+					"m_identity_one": "",
+					"m_identity_two": "",
+					"firm_city": "",
+					"social_num": "",
+					"reserved_money": "",
+					"marriage_img": [],
+					"credit_img": []
 				}
 			}
 		},
+		onPageScroll(res) {
+			this.scrollTop = res.scrollTop / 120
+		},
 		onLoad(options) {
 			this.title = options.title
-      let clientInfoData=uni.getStorageSync('clientInfoData')
-      let clientInfoKey=uni.getStorageSync('clientInfoKey')
-      console.log(clientInfoData,clientInfoKey)
+			let clientInfoData = uni.getStorageSync('clientInfoData')
+			let clientInfoKey = uni.getStorageSync('clientInfoKey')
+			console.log(clientInfoData, clientInfoKey)
 		},
 		methods: {
-      setNum(step){
-        if(this.step!==step){
-          this.step=step
-        }
-      },
+			setNum(step) {
+				if (this.step !== step) {
+					this.step = step
+				}
+			},
 			//  身份认证
 			onAuthentication(item) {
 				uni.navigateTo({
@@ -165,18 +193,32 @@
 			},
 			// 下一步
 			onStep(type) {
-        if(type===1){
-          if (this.step === 3) return
-          this.step++
-        }else {
-          if (this.step === 1) return
-          this.step--
-        }
+				if (type === 1) {
+					if (this.step === 3) return
+					this.step++
+				} else {
+					if (this.step === 1) return
+					this.step--
+				}
 
 			},
 			// 完成
 			onSubmit() {
 				this.step = 4
+			},
+			onChange(address) {
+				console.log(address);
+				this.formData.firm_address = address
+			},
+			onMarriage() {
+				console.log(11111);
+				this.$refs.actionSheet.open();
+			},
+			onSelect(e) {
+				console.log(e);
+			},
+			onFarming(e) {
+				this.formData.is_farming = e
 			}
 		}
 	}
@@ -213,4 +255,4 @@
 	button::after {
 		border: none;
 	}
-</style>
+</style>

+ 2 - 6
page_task/identity_upload/identity_upload.vue

@@ -1,6 +1,6 @@
 <template>
-	<view class="total-page page-env-160 page-box">
-		<Nav :title="'身份证上传'" :is_fixed="true" :opacity="scrollTop">
+	<view class="total-page page-env-160 page-box task-bg">
+		<Nav :title="'身份证上传'" :genre="2" is_fixed>
 			<view>
 				<view class="sys-background-fff p-30 m-30 r-30">
 					<text class="size-30 sys-weight-600">身份证上传认证照片</text>
@@ -52,7 +52,6 @@
 		components: {},
 		data() {
 			return {
-				scrollTop: 0,
 				hintList: [{
 					name: '标准照片',
 					image: 'hint-one',
@@ -72,9 +71,6 @@
 				}, ]
 			}
 		},
-		onPageScroll(res) {
-			this.scrollTop = res.scrollTop / 120
-		},
 		onLoad() {},
 		methods: {
 			// 完成

+ 410 - 395
page_task/information/information.vue

@@ -1,10 +1,11 @@
 <template>
 	<view class="total-page page-box task-bg">
-		<Nav :title="productInfo.product_name" :genre="1" :fixedHeight="105"></Nav>
+		<Nav :title="productInfo.product_name" :genre="1" is_fixed></Nav>
 		<view class="task-tabs" :style="{top:`${$tools.topHeight()}px`}">
 			<view class="m-t20">
 				<view class="creation_item row-justify-sb center sys-background-fff m-lr30 m-b20 p-30 r-20"
-					v-for="(item,index) in informationList"  :key="index" v-show="item.is_show" @click="onAuthentication(item)">
+					v-for="(item,index) in informationList" :key="index" v-show="item.is_show"
+					@click="onAuthentication(item)">
 					<view class="row-c">
 						<image class="wh-80 m-r20 r-100" :src="item.icon" mode="aspectFill"></image>
 						<view>
@@ -13,9 +14,8 @@
 						</view>
 					</view>
 					<view class="">
-						<button class="button-background info_button sys-weight-500 text-color-fff size-26 r-100">待上传</button>
-					<!-- 	<button class="info_button sys-bg-CBCBCB sys-weight-500 text-color-fff size-26 r-100"
-							v-else>已认证</button> -->
+						<button
+							class="button-background info_button sys-weight-500 text-color-fff size-26 r-100">待上传</button>
 					</view>
 				</view>
 			</view>
@@ -27,16 +27,19 @@
 
 <script>
 	import TaskImgTab from "@/common/task/task_ima_tab.vue"
-  import tools from "@/service/tools";
-  import {
-    addDraft,
-    addTask, delBusinessCache,
-    getBusinessCache,
-    getClientInfo,
-    getDraftItem, getTaskDetail,
-    getTaskTypeInfo,
-    setBusinessCache, verifyBusinessModuleKey
-  } from "@/api/task";
+	import tools from "@/service/tools";
+	import {
+		addDraft,
+		addTask,
+		delBusinessCache,
+		getBusinessCache,
+		getClientInfo,
+		getDraftItem,
+		getTaskDetail,
+		getTaskTypeInfo,
+		setBusinessCache,
+		verifyBusinessModuleKey
+	} from "@/api/task";
 
 	export default {
 		components: {
@@ -53,7 +56,7 @@
 					type: 1,
 					is_finish: false,
 					is_show: false,
-          type_keys:[]
+					type_keys: []
 				}, {
 					name: '资产信息',
 					explain: '房产、车辆等资产',
@@ -61,8 +64,8 @@
 					path: '/page_task/property/property',
 					type: 2,
 					is_finish: false,
-          is_show: false,
-          type_keys:[]
+					is_show: false,
+					type_keys: []
 				}, {
 					name: '申请信息',
 					explain: '贷款额度信息',
@@ -70,412 +73,424 @@
 					path: '/page_task/apply/apply',
 					type: 3,
 					is_finish: false,
-          is_show: false,
-          type_keys:[]
+					is_show: false,
+					type_keys: []
 				}, {
-          name: '第三方信息',
-          explain: '第三方企业/人员相关信息',
-          icon: '/static/img/task/task-apply.png',
-          path: '/page_task/apply/apply',
-          type: 4,
-          is_finish: false,
-          is_show: false,
-          type_keys:[]
-        },{
 					name: '联系人信息',
 					explain: '其他联系人',
 					icon: '/static/img/task/task-contacts.png',
 					type: 5,
 					is_finish: false,
-          is_show: false,
-          type_keys:[]
+					is_show: false,
+					type_keys: []
+				}, {
+					name: '第三方信息',
+					explain: '第三方企业/人员相关信息',
+					icon: '/static/img/task/task-three.png',
+					path: '/page_task/tripartite_info/tripartite_info',
+					type: 4,
+					is_finish: false,
+					is_show: false,
+					type_keys: []
 				}, {
 					name: '征信信息',
 					explain: '银行征信信息',
 					icon: '/static/img/task/task-credit.png',
 					type: 6,
 					is_finish: false,
-          is_show: false,
-          type_keys:[]
+					is_show: false,
+					type_keys: []
 				}, ],
-        stepsNum: 0,
-        stepsKey: 0,
-        productInfo: {
-          id: 1,
-          product_name: "",
-          product_types: '',
-          field_ids: [],
-          type_data: [],
-        },
-        taskData: {
-          'productId': '',
-          "clientInfo": {
-            "name": "",
-            "birthday": "",
-            "sex": 1,
-            "id_number": "",
-            "identity_one": "",
-            "identity_two": "",
-            "phone": "",
-            "marriage_type": 1,
-            "education_type": '',
-            "education_name": '',
-            "employ_type": 1,
-            "firm_name": "",
-            "firm_address": "",
-            "resident_city": "",
-            "resident_address": "",
-            "mate_name": "",
-            "mate_phone": "",
-            "mate_id_number": "",
-            "mate_firm": "",
-            "m_code": "",
-            "code": "",
-            "m_identity_one": "",
-            "m_identity_two": "",
-            "firm_city": "",
-            "social_num": "",
-            "reserved_money": "",
-            "marriage_img": [],
-            "credit_img": []
-          },
-          "property": [],
-          "applies": {
-            "is_farming": '1',
-            "loan_form": '',
-            "loan_type": '',
-            "loan_industry": [],
-            "enterprise_type": "",
-            "use_date": "",
-            "pay_status": '1',
-            "quota": "",
-            "apply_data":[]
-          },
-          "tripartite": [],
-          "linkman": [],
-          "credit": {
-            'credit_img': [],
-            'deal_data': [],
-            'overdue_data': [],
-            'query_data': [],
-          },
-          "post_loan": {
-            'bank_id':'',
-            'loans_money':'',
-            'interest_rate':'',
-            'repayment_money':'',
-            'refund_num':'',
-            'repayment_type':'',
-            'repayment_date':'',
-          },
-        },
-        isCache: true,
-        isNew: true
+				stepsNum: 0,
+				stepsKey: 0,
+				productInfo: {
+					id: 1,
+					product_name: "",
+					product_types: '',
+					field_ids: [],
+					type_data: [],
+				},
+				taskData: {
+					'productId': '',
+					"clientInfo": {
+						"name": "",
+						"birthday": "",
+						"sex": 1,
+						"id_number": "",
+						"identity_one": "",
+						"identity_two": "",
+						"phone": "",
+						"marriage_type": 1,
+						"education_type": '',
+						"education_name": '',
+						"employ_type": 1,
+						"firm_name": "",
+						"firm_address": "",
+						"resident_city": "",
+						"resident_address": "",
+						"mate_name": "",
+						"mate_phone": "",
+						"mate_id_number": "",
+						"mate_firm": "",
+						"m_code": "",
+						"code": "",
+						"m_identity_one": "",
+						"m_identity_two": "",
+						"firm_city": "",
+						"social_num": "",
+						"reserved_money": "",
+						"marriage_img": [],
+						"credit_img": []
+					},
+					"property": [],
+					"applies": {
+						"is_farming": '1',
+						"loan_form": '',
+						"loan_type": '',
+						"loan_industry": [],
+						"enterprise_type": "",
+						"use_date": "",
+						"pay_status": '1',
+						"quota": "",
+						"apply_data": []
+					},
+					"tripartite": [],
+					"linkman": [],
+					"credit": {
+						'credit_img': [],
+						'deal_data': [],
+						'overdue_data': [],
+						'query_data': [],
+					},
+					"post_loan": {
+						'bank_id': '',
+						'loans_money': '',
+						'interest_rate': '',
+						'repayment_money': '',
+						'refund_num': '',
+						'repayment_type': '',
+						'repayment_date': '',
+					},
+				},
+				isCache: true,
+				isNew: true
 			}
 		},
-    watch:{
-      'productInfo.product_types':function () {
-        let productTypes=this.productInfo.product_types
-        if(typeof productTypes==='string'){
-          productTypes=productTypes.split(',')
-        }
-        this.informationList.forEach(item=>{
-          item.is_show = productTypes.indexOf(item.type + '') >= 0;
-          this.productInfo.type_data.forEach(val=>{
-            if(val.type===item.type+''){
-              item.type_keys=val.type_keys
-            }
-          })
-        })
+		watch: {
+			'productInfo.product_types': function() {
+				let productTypes = this.productInfo.product_types
+				if (typeof productTypes === 'string') {
+					productTypes = productTypes.split(',')
+				}
+				this.informationList.forEach(item => {
+					item.is_show = productTypes.indexOf(item.type + '') >= 0;
+					this.productInfo.type_data.forEach(val => {
+						if (val.type === item.type + '') {
+							item.type_keys = val.type_keys
+						}
+					})
+				})
 
-        
-      }
-    },
+
+			}
+		},
 		onLoad(data) {
-      if (data.draftId) {
-        this.getDraftItem(data.draftId)
-        this.isNew = false
-      } else if (data.businessId) {
-        this.getTaskDetail(data.businessId)
-        this.isNew = false
-      } else {
-        if (data.productId === undefined) {
-          tools.leftClick()
-        } else {
-          this.taskData.productId = data.productId
-          this.getTaskTypeInfo(data.productId)
-        }
-      }
+			if (data.draftId) {
+				this.getDraftItem(data.draftId)
+				this.isNew = false
+			} else if (data.businessId) {
+				this.getTaskDetail(data.businessId)
+				this.isNew = false
+			} else {
+				if (data.productId === undefined) {
+					tools.leftClick()
+				} else {
+					this.taskData.productId = data.productId
+					this.getTaskTypeInfo(data.productId)
+				}
+			}
 		},
 		methods: {
-      addTask() {
-        console.log('addTask----start')
-        // let isEnd=false;
-        // this.productInfo.type_data.forEach((typeItem,typeKey)=>{
-        //   if(!typeItem.is_end && !isEnd && typeKey!==this.stepsNum){
-        //     isEnd=true
-        //     tools.error('完善'+typeItem.type_name)
-        //   }
-        // })
-        // if(isEnd){
-        //   return;
-        // }
-        if (this.isAjax) {
-          return ;
-        }
-        this.isAjax = true;
-        addTask(this.taskData).then((res) => {
-          if (res.code === 1) {
-            tools.success(res.msg)
-            // uni.$emit('goToTaskList', 0)
-            setTimeout(() => {
-              uni.redirectTo({
-                url: 'pages-task/task-info/task-info?id=' + res.data.id
-              });
-            }, 1500)
-          } else {
-            this.isAjax = false;
-            tools.error(res.msg)
-          }
-        })
-      },
-      addDraft() {
-        addDraft(this.taskData).then((res) => {
-          if (res.code === 1) {
-            tools.success(res.msg)
-            delBusinessCache({'productId': this.productInfo.id}).then((res) => {
-              uni.$emit('setNewDraft')
-              setTimeout(() => {
-                tools.leftClick()
-              }, 1000)
-            })
-          } else {
-            tools.error(res.msg)
-          }
-        })
-      },
+			addTask() {
+				console.log('addTask----start')
+				// let isEnd=false;
+				// this.productInfo.type_data.forEach((typeItem,typeKey)=>{
+				//   if(!typeItem.is_end && !isEnd && typeKey!==this.stepsNum){
+				//     isEnd=true
+				//     tools.error('完善'+typeItem.type_name)
+				//   }
+				// })
+				// if(isEnd){
+				//   return;
+				// }
+				if (this.isAjax) {
+					return;
+				}
+				this.isAjax = true;
+				addTask(this.taskData).then((res) => {
+					if (res.code === 1) {
+						tools.success(res.msg)
+						// uni.$emit('goToTaskList', 0)
+						setTimeout(() => {
+							uni.redirectTo({
+								url: 'pages-task/task-info/task-info?id=' + res.data.id
+							});
+						}, 1500)
+					} else {
+						this.isAjax = false;
+						tools.error(res.msg)
+					}
+				})
+			},
+			addDraft() {
+				addDraft(this.taskData).then((res) => {
+					if (res.code === 1) {
+						tools.success(res.msg)
+						delBusinessCache({
+							'productId': this.productInfo.id
+						}).then((res) => {
+							uni.$emit('setNewDraft')
+							setTimeout(() => {
+								tools.leftClick()
+							}, 1000)
+						})
+					} else {
+						tools.error(res.msg)
+					}
+				})
+			},
 
-      verifyBusinessModuleKey(verifyKey){
-        let typeRow=this.productInfo.type_data[verifyKey];
-        let data={}
-        if(typeRow.type==='1'){
-          data=this.taskData.clientInfo
-        }else if(typeRow.type==='2'){
-          data=this.taskData.property
-        }else if(typeRow.type==='3'){
-          data=this.taskData.applies
-        }else if(typeRow.type==='4'){
-          data=this.taskData.tripartite
-        }else if(typeRow.type==='5'){
-          data=this.taskData.linkman
-        }else if(typeRow.type==='6'){
-          data=this.taskData.credit
-        }else if(typeRow.type==='7'){
-          data=this.taskData.post_loan
-        }
-        console.log(data)
-        verifyBusinessModuleKey({'productId':this.productInfo.id,'data':data,'type':typeRow.type}).then((res)=>{
-          this.productInfo.type_data[verifyKey].is_end = res.code === 1;
-        })
-      },
-      getClientInfo() {
-        //获取客户原始数据
-        if (this.taskData.clientInfo.id_number === '') {
-          return;
-        }
-        getClientInfo({
-          'id_number': this.taskData.clientInfo.id_number,
-          'productId': this.taskData.productId
-        }).then((res) => {
-          if (res.code === 1) {
-            this.isNew = false
-            if (res.data.business) {
-              res.data.business.id = ''
-              this.taskData.clientInfo = res.data.business
-            }
-            if (res.data.propertyList) {
-              this.taskData.property = res.data.propertyList
-              if( this.taskData.property){
-                this.taskData.property.forEach((item) => {
-                  item.id = ''
-                })
-              }
-            }
+			verifyBusinessModuleKey(verifyKey) {
+				let typeRow = this.productInfo.type_data[verifyKey];
+				let data = {}
+				if (typeRow.type === '1') {
+					data = this.taskData.clientInfo
+				} else if (typeRow.type === '2') {
+					data = this.taskData.property
+				} else if (typeRow.type === '3') {
+					data = this.taskData.applies
+				} else if (typeRow.type === '4') {
+					data = this.taskData.tripartite
+				} else if (typeRow.type === '5') {
+					data = this.taskData.linkman
+				} else if (typeRow.type === '6') {
+					data = this.taskData.credit
+				} else if (typeRow.type === '7') {
+					data = this.taskData.post_loan
+				}
+				console.log(data)
+				verifyBusinessModuleKey({
+					'productId': this.productInfo.id,
+					'data': data,
+					'type': typeRow.type
+				}).then((res) => {
+					this.productInfo.type_data[verifyKey].is_end = res.code === 1;
+				})
+			},
+			getClientInfo() {
+				//获取客户原始数据
+				if (this.taskData.clientInfo.id_number === '') {
+					return;
+				}
+				getClientInfo({
+					'id_number': this.taskData.clientInfo.id_number,
+					'productId': this.taskData.productId
+				}).then((res) => {
+					if (res.code === 1) {
+						this.isNew = false
+						if (res.data.business) {
+							res.data.business.id = ''
+							this.taskData.clientInfo = res.data.business
+						}
+						if (res.data.propertyList) {
+							this.taskData.property = res.data.propertyList
+							if (this.taskData.property) {
+								this.taskData.property.forEach((item) => {
+									item.id = ''
+								})
+							}
+						}
 
-            if (res.data.applyFor) {
-              res.data.applyFor.id = ''
-              this.taskData.applies = res.data.applyFor
-              if( this.taskData.applies.apply_data){
-                this.taskData.applies.apply_data.forEach((item) => {
-                  item.id = ''
-                })
-              }
-            }
-            if (res.data.tripartite) {
-              this.taskData.tripartite = res.data.tripartite
-              this.taskData.tripartite.forEach((item) => {
-                item.id = ''
-              })
-            }
-            if (res.data.linkman) {
-              res.data.linkman.forEach((item) => {
-                item.id = ''
-              })
-              this.taskData.linkman = res.data.linkman
-            }
-            if (res.data.credit) {
-              this.taskData.credit = res.data.credit
-              this.taskData.credit.deal_data.forEach((item)=>{
-                item.id=''
-              })
-              this.taskData.credit.overdue_data.forEach((item)=>{
-                item.id=''
-              })
-              this.taskData.credit.query_data.forEach((item)=>{
-                item.id=''
-              })
-            }
-            if (res.data.postLoan) {
-              this.taskData.post_loan = res.data.postLoan
-            }
-            console.log('-----------this.taskData-------------',this.taskData)
-          }
-        })
-      },
-      setBusinessCache() {
-        //设置缓存
-        if (this.isCache) {
-          return false
-        }
-        this.isCache = true
-        setBusinessCache(this.taskData).then((res) => {
-          setTimeout(() => {
-            this.isCache = false;
-          }, 2000)
-        })
-      },
-      getBusinessCache(productId) {
-        //获取缓存
-        getBusinessCache({'productId': productId}).then((res) => {
-          if (res.code === 1) {
-            if (res.data.productId !== 0) {
-              this.isNew = false
-              this.taskData = res.data
-            }
-          }
+						if (res.data.applyFor) {
+							res.data.applyFor.id = ''
+							this.taskData.applies = res.data.applyFor
+							if (this.taskData.applies.apply_data) {
+								this.taskData.applies.apply_data.forEach((item) => {
+									item.id = ''
+								})
+							}
+						}
+						if (res.data.tripartite) {
+							this.taskData.tripartite = res.data.tripartite
+							this.taskData.tripartite.forEach((item) => {
+								item.id = ''
+							})
+						}
+						if (res.data.linkman) {
+							res.data.linkman.forEach((item) => {
+								item.id = ''
+							})
+							this.taskData.linkman = res.data.linkman
+						}
+						if (res.data.credit) {
+							this.taskData.credit = res.data.credit
+							this.taskData.credit.deal_data.forEach((item) => {
+								item.id = ''
+							})
+							this.taskData.credit.overdue_data.forEach((item) => {
+								item.id = ''
+							})
+							this.taskData.credit.query_data.forEach((item) => {
+								item.id = ''
+							})
+						}
+						if (res.data.postLoan) {
+							this.taskData.post_loan = res.data.postLoan
+						}
+						console.log('-----------this.taskData-------------', this.taskData)
+					}
+				})
+			},
+			setBusinessCache() {
+				//设置缓存
+				if (this.isCache) {
+					return false
+				}
+				this.isCache = true
+				setBusinessCache(this.taskData).then((res) => {
+					setTimeout(() => {
+						this.isCache = false;
+					}, 2000)
+				})
+			},
+			getBusinessCache(productId) {
+				//获取缓存
+				getBusinessCache({
+					'productId': productId
+				}).then((res) => {
+					if (res.code === 1) {
+						if (res.data.productId !== 0) {
+							this.isNew = false
+							this.taskData = res.data
+						}
+					}
 
-        })
-      },
+				})
+			},
 
-      getTaskTypeInfo(id) {
-        getTaskTypeInfo({
-          'id': id
-        }).then((res) => {
-          if (res.code === 1) {
-            this.productInfo = res.data;
-            if(this.isNew)this.getBusinessCache(id)
-          } else {
-            tools.error(res.msg)
-            setTimeout(() => {
-              this.closePage();
-            }, 1000)
-          }
-        })
-      },
-      getDraftItem(draftId) {
-        getDraftItem({'draftId': draftId}).then((res) => {
-          if (res.code === 1) {
-            this.productInfo = res.data.productInfo
-            this.taskData = res.data.taskData
-            this.getTaskTypeInfo(this.taskData.productId)
-          } else {
-            this.closePage();
-          }
-        })
-      },
-      async getTaskDetail(businessId) {
-        const res = await getTaskDetail({
-          id: businessId
-        })
-        if (res.code === 1) {
-          if (res.data.product) {
-            this.productInfo = res.data.product
-            this.taskData.productId = res.data.product.id
-          }
-          if (res.data.business) {
-            this.taskData.clientInfo = res.data.business
-          }
-          if (res.data.propertyList) {
-            this.taskData.property = res.data.propertyList
-          }
-          if (res.data.applyFor) {
-            this.taskData.applies = res.data.applyFor
-          }
-          if (res.data.tripartite) {
-            this.taskData.tripartite = res.data.tripartite
-          }
-          if (res.data.linkman) {
-            this.taskData.linkman = res.data.linkman
-            console.log('this.taskData.linkman:',this.taskData.linkman)
-          }
-          if (res.data.credit) {
-            this.taskData.credit = res.data.credit
-          }
-          if (res.data.postLoan) {
-            this.taskData.post_loan = res.data.postLoan
-          }
-          this.getTaskTypeInfo(this.taskData.productId)
-        } else {
-          this.closePage();
-        }
-      },
-      delBusinessCache() {
-        uni.showModal({
-          title: '警告',
-          content: '是否退出当前任务,并清除缓存!',
-          success: (res) => {
-            if (res.confirm) {
-              delBusinessCache({'productId': this.productInfo.id}).then((res) => {
-                tools.leftClick()
-              })
-              uni.removeStorageSync('creditImg');
-            } else {
-              tools.leftClick()
-            }
-          }
-        });
+			getTaskTypeInfo(id) {
+				getTaskTypeInfo({
+					'id': id
+				}).then((res) => {
+					if (res.code === 1) {
+						this.productInfo = res.data;
+						if (this.isNew) this.getBusinessCache(id)
+					} else {
+						tools.error(res.msg)
+						setTimeout(() => {
+							this.closePage();
+						}, 1000)
+					}
+				})
+			},
+			getDraftItem(draftId) {
+				getDraftItem({
+					'draftId': draftId
+				}).then((res) => {
+					if (res.code === 1) {
+						this.productInfo = res.data.productInfo
+						this.taskData = res.data.taskData
+						this.getTaskTypeInfo(this.taskData.productId)
+					} else {
+						this.closePage();
+					}
+				})
+			},
+			async getTaskDetail(businessId) {
+				const res = await getTaskDetail({
+					id: businessId
+				})
+				if (res.code === 1) {
+					if (res.data.product) {
+						this.productInfo = res.data.product
+						this.taskData.productId = res.data.product.id
+					}
+					if (res.data.business) {
+						this.taskData.clientInfo = res.data.business
+					}
+					if (res.data.propertyList) {
+						this.taskData.property = res.data.propertyList
+					}
+					if (res.data.applyFor) {
+						this.taskData.applies = res.data.applyFor
+					}
+					if (res.data.tripartite) {
+						this.taskData.tripartite = res.data.tripartite
+					}
+					if (res.data.linkman) {
+						this.taskData.linkman = res.data.linkman
+						console.log('this.taskData.linkman:', this.taskData.linkman)
+					}
+					if (res.data.credit) {
+						this.taskData.credit = res.data.credit
+					}
+					if (res.data.postLoan) {
+						this.taskData.post_loan = res.data.postLoan
+					}
+					this.getTaskTypeInfo(this.taskData.productId)
+				} else {
+					this.closePage();
+				}
+			},
+			delBusinessCache() {
+				uni.showModal({
+					title: '警告',
+					content: '是否退出当前任务,并清除缓存!',
+					success: (res) => {
+						if (res.confirm) {
+							delBusinessCache({
+								'productId': this.productInfo.id
+							}).then((res) => {
+								tools.leftClick()
+							})
+							uni.removeStorageSync('creditImg');
+						} else {
+							tools.leftClick()
+						}
+					}
+				});
 
-      },
+			},
 			onAuthentication(item) {
-        if(item.type===1){
-          uni.setStorageSync('clientInfoKey',item.type_keys)
-          uni.setStorageSync('clientInfoData',this.taskData.clientInfo)
-        }else if(item.type===2){
-          uni.setStorageSync('propertyKey',item.type_keys)
-          uni.setStorageSync('propertyData',this.taskData.property)
-        }else if(item.type===3){
-          uni.setStorageSync('appliesKey',item.type_keys)
-          uni.setStorageSync('appliesData',this.taskData.applies)
-        }else if(item.type===4){
-          uni.setStorageSync('tripartiteKey',item.type_keys)
-          uni.setStorageSync('tripartiteData',this.taskData.tripartite)
-        }else if(item.type===5){
-          uni.setStorageSync('linkmanKey',item.type_keys)
-          uni.setStorageSync('linkmanData',this.taskData.linkman)
-        }else if(item.type===6){
-          uni.setStorageSync('creditKey',item.type_keys)
-          uni.setStorageSync('creditData',this.taskData.credit)
-        }else if(item.type===7){
-          uni.setStorageSync('postLoanKey',item.type_keys)
-          uni.setStorageSync('postLoanData',this.taskData.post_loan)
-        }
+				if (item.type === 1) {
+					uni.setStorageSync('clientInfoKey', item.type_keys)
+					uni.setStorageSync('clientInfoData', this.taskData.clientInfo)
+				} else if (item.type === 2) {
+					uni.setStorageSync('propertyKey', item.type_keys)
+					uni.setStorageSync('propertyData', this.taskData.property)
+				} else if (item.type === 3) {
+					uni.setStorageSync('appliesKey', item.type_keys)
+					uni.setStorageSync('appliesData', this.taskData.applies)
+				} else if (item.type === 4) {
+					uni.setStorageSync('tripartiteKey', item.type_keys)
+					uni.setStorageSync('tripartiteData', this.taskData.tripartite)
+				} else if (item.type === 5) {
+					uni.setStorageSync('linkmanKey', item.type_keys)
+					uni.setStorageSync('linkmanData', this.taskData.linkman)
+				} else if (item.type === 6) {
+					uni.setStorageSync('creditKey', item.type_keys)
+					uni.setStorageSync('creditData', this.taskData.credit)
+				} else if (item.type === 7) {
+					uni.setStorageSync('postLoanKey', item.type_keys)
+					uni.setStorageSync('postLoanData', this.taskData.post_loan)
+				}
 				uni.navigateTo({
 					url: `${item.path}?title=${item.name}`
 				})
 			},
-      closePage(){
-        tools.leftClick()
-      },
+			closePage() {
+				tools.leftClick()
+			},
 			onCeationTask() {},
 			onLeftSubmit() {
 				console.log('预览');

+ 14 - 8
page_task/module/wh-input.vue

@@ -1,18 +1,19 @@
 <template>
 	<view class="wh-box">
 		<view class="row p-tb30" style="line-height: 30rpx;height: 30rpx;" :class="{'bor-bottom-1':is_border}">
-			<view class="wh-text size-28">{{name}}<text></text> </view>
+			<view class="wh-text size-28">{{name}}<text></text></view>
 			<view class="row-c flex">
-				<input class="wh-input sys-size-28 text-color-12 m-l40"
-					placeholder-class="sys-size-28 text-color-999 sys-weight-400" :maxlength="maxlength" :type="type"
-					:focus="focus" :placeholder="is_select ? '请选择' : placeholder?placeholder:`请输入${name}`"
-					:disabled="disabled" v-model="inputValue" @blur="nameInput" @input="nameInput"></input>
+				<input class="wh-input sys-size-28 text-color-12 m-l40" placeholder-class="sys-size-28 text-color-999"
+					:maxlength="maxlength" :type="type" :focus="focus"
+					:placeholder="is_select ? '请选择' : placeholder?placeholder:`请输入${name}`" :disabled="disabled"
+					v-model="inputValue" @blur="nameInput" @input="nameInput"></input>
 				<view class="" style="margin-top: 4rpx;">
 					<uni-icons type="forward" size="18" color="#D8D8D8" v-if="is_select"></uni-icons>
 				</view>
-				<!-- 	<image class="m-l10" style="width: 16rpx;height: 24rpx;" src="/static/img/information/right-arrows.png"
-					mode="aspectFill" v-if="is_select">
-				</image> -->
+			</view>
+			<text class="sys-size-28 m-l20" v-if="rightText">{{rightText}}</text>
+			<view class="m-l20">
+				<slot></slot>
 			</view>
 		</view>
 		<view class="size-24 text-color-E21 p-b20 p-t10" v-if="is_requis">{{name}}为必填</view>
@@ -39,6 +40,10 @@
 				type: String,
 				default: '姓名'
 			},
+			rightText: {
+				type: String,
+				default: ''
+			},
 			type: {
 				type: String,
 				default: 'text'
@@ -116,6 +121,7 @@
 		text-align-last: justify;
 		vertical-align: top;
 		height: 0;
+		padding-top: 2rpx;
 	}
 
 	.wh-text:after {

+ 109 - 106
page_task/task_details/module/record.vue

@@ -22,39 +22,40 @@
 					<view class="image-box">
 						<image class="reply-img wh-60 m-t20 r-10 m-r20" :src="msg_img.url" mode="aspectFill"
 							v-for="(msg_img,index) in msg.msg_img" :key="index"></image>
-<!--            <en-image :img="msg.msg_img" :width="130" height="130"></en-image>-->
+						<!--            <en-image :img="msg.msg_img" :width="130" height="130"></en-image>-->
 					</view>
 					<!-- 文件类型 -->
-					<view class="sys-from-background-color p-20 r-20 m-t20"  v-if="msg.file_list.length>0">
-						<view class="file-item row-c m-b20"  @click.stop="openFile(file)" v-for="(file,fileIndex) in msg.file_list" :key="index">
+					<view class="sys-from-background-color p-20 r-20 m-t20" v-if="msg.file_list.length>0">
+						<view class="file-item row-c m-b20" @click.stop="openFile(file)"
+							v-for="(file,fileIndex) in msg.file_list" :key="index">
 							<image class="wh-45 m-r20" src="/static/img/task-details/icon-pdf.png" mode="aspectFill">
 							</image>
 							<text class="size-24 color-111827">{{ file.name }}</text>
 						</view>
 					</view>
 					<!-- 1对1回复 -->
-					<view class="sys-from-background-color p-20 m-t20 r-20"  v-if="msg.reply_list.length>0">
+					<view class="sys-from-background-color p-20 m-t20 r-20" v-if="msg.reply_list.length>0">
 						<view class="" v-for="replyItem in msg.reply_list">
 							<text class="color-00A775">{{ replyItem.name }}</text><text
-								class="m-lr10 text-color-666">回复</text><text class="color-00A775">{{ replyItem.ru_name }}:</text>
+								class="m-lr10 text-color-666">回复</text><text
+								class="color-00A775">{{ replyItem.ru_name }}:</text>
 							<view class="">
 								<view class="m-t16">{{ replyItem.msg }}</view>
 								<view class="image-box" v-if="replyItem.msg_img.length>0">
-									<image class="reply-img wh-60 m-t20 r-10 m-r20" :src="r_img.url"
-										mode="aspectFill" v-for="(r_img,index) in replyItem.msg_img" :key="index"></image>
+									<image class="reply-img wh-60 m-t20 r-10 m-r20" :src="r_img.url" mode="aspectFill"
+										v-for="(r_img,index) in replyItem.msg_img" :key="index"></image>
 								</view>
 							</view>
 						</view>
 					</view>
 				</view>
 			</view>
-      <view class="row m-t20 ">
-        <view class="empty-box"></view>
-        <view class="size-22 text-color-999">{{ msg.created_at }}</view>
-      </view>
-
+			<view class="row m-t20 ">
+				<view class="empty-box"></view>
+				<view class="size-22 text-color-999 m-l16">{{ msg.created_at }}</view>
+			</view>
 		</view>
-    <en-blank v-if="msgList.length<=0"></en-blank>
+		<en-blank v-if="msgList.length<=0"></en-blank>
 		<uni-popup background-color="#fff" ref="popup" type="bottom" borderRadius="10px 10px 0 0"
 			@touchmove.stop.prevent="moveHandle">
 			<view class="page-env-160">
@@ -71,114 +72,116 @@
 
 <script>
 	import SendChat from "./send_chat.vue"
-  import {getMsgList, setMsg} from "@/api/task";
-  import EnImage from "@/components/en-utils/en-image/en-image.vue";
-  import tools from "@/service/tools";
-  import EnBlank from "@/components/en-utils/en-blank/en-blank.vue";
+	import {
+		getMsgList,
+		setMsg
+	} from "@/api/task";
+	import EnImage from "@/components/en-utils/en-image/en-image.vue";
+	import tools from "@/service/tools";
+	import EnBlank from "@/components/en-utils/en-blank/en-blank.vue";
 
 	export default {
 		components: {
-      EnBlank,
-      EnImage,
+			EnBlank,
+			EnImage,
 			SendChat
 		},
-    props: {
-      businessId:{
-        default:0
-      }
-    },
-    watch:{
-      'businessId':function () {
+		props: {
+			businessId: {
+				default: 0
+			}
+		},
+		watch: {
+			'businessId': function() {
 
-      }
-    },
+			}
+		},
 		data() {
 			return {
-        msgList: [],
-        replyId:'',
-        msgData: {
-          business_id: '',
-          msg: '',
-          reply_id: 0,
-          msg_img: [],
-          file_list: [],
-        },
-        isAjax:false
-      }
+				msgList: [],
+				replyId: '',
+				msgData: {
+					business_id: '',
+					msg: '',
+					reply_id: 0,
+					msg_img: [],
+					file_list: [],
+				},
+				isAjax: false
+			}
 		},
 		methods: {
-      startData(){
-        this.getMsgList()
-      },
-      setMsg() {
-        if (this.isAjax) {
-          return;
-        }
-        // this.isAjax=true;
-        this.msgData.business_id = this.businessId
-        if (this.replyId) {
-          this.msgData.reply_id = this.replyId;
-        }
-        setMsg(this.msgData).then((res) => {
-          if (res.code === 1) {
-            tools.success(res.msg)
-            this.msgData.reply_id=''
-            this.msgData.msg=''
-            this.msgData.msg_img=[]
-            this.getMsgList()
-            this.$refs.popup.close()
-          } else {
-            tools.error(res.msg)
-            this.isAjax = false;
-          }
+			startData() {
+				this.getMsgList()
+			},
+			setMsg() {
+				if (this.isAjax) {
+					return;
+				}
+				// this.isAjax=true;
+				this.msgData.business_id = this.businessId
+				if (this.replyId) {
+					this.msgData.reply_id = this.replyId;
+				}
+				setMsg(this.msgData).then((res) => {
+					if (res.code === 1) {
+						tools.success(res.msg)
+						this.msgData.reply_id = ''
+						this.msgData.msg = ''
+						this.msgData.msg_img = []
+						this.getMsgList()
+						this.$refs.popup.close()
+					} else {
+						tools.error(res.msg)
+						this.isAjax = false;
+					}
 
-        })
-      },
-      openFile(file) {
-        if (!file.url) {
-          tools.error('下载地址不存在')
-          return
-        }
-        tools.showLoading()
-        uni.downloadFile({
-          url: file.url, //仅为示例,并非真实的资源
-          success: (dRes) => {
-            tools.hideLoading()
-            if (dRes.statusCode === 200) {
-              tools.success('下载成功')
-              uni.saveFile({
-                tempFilePath: dRes.tempFilePath,
-                success: (res) => {
-                  uni.openDocument({
-                    filePath: res.savedFilePath,
-                    showMenu: true,
-                    success: function (res) {
-                    }
-                  });
-                }
-              });
-            }
-          }
-        });
-      },
-      getMsgList() {
-        if(this.businessId<=0){
-          return
-        }
-        getMsgList({
-          'business_id': this.businessId
-        }).then((res) => {
-          if (res.code === 1) {
-            this.msgList = res.data;
-            // this.setImgList()
-          }
-        })
-      },
+				})
+			},
+			openFile(file) {
+				if (!file.url) {
+					tools.error('下载地址不存在')
+					return
+				}
+				tools.showLoading()
+				uni.downloadFile({
+					url: file.url, //仅为示例,并非真实的资源
+					success: (dRes) => {
+						tools.hideLoading()
+						if (dRes.statusCode === 200) {
+							tools.success('下载成功')
+							uni.saveFile({
+								tempFilePath: dRes.tempFilePath,
+								success: (res) => {
+									uni.openDocument({
+										filePath: res.savedFilePath,
+										showMenu: true,
+										success: function(res) {}
+									});
+								}
+							});
+						}
+					}
+				});
+			},
+			getMsgList() {
+				if (this.businessId <= 0) {
+					return
+				}
+				getMsgList({
+					'business_id': this.businessId
+				}).then((res) => {
+					if (res.code === 1) {
+						this.msgList = res.data;
+						// this.setImgList()
+					}
+				})
+			},
 			moveHandle() {
 				return false
 			},
 			onSendChat(replyId) {
-        this.replyId=replyId
+				this.replyId = replyId
 				this.$refs.popup.open('bottom')
 			},
 			onClose() {

+ 8 - 0
page_task/tripartite_info/components/guarantee_company.vue

@@ -0,0 +1,8 @@
+<template>
+</template>
+
+<script>
+</script>
+
+<style>
+</style>

+ 156 - 0
page_task/tripartite_info/components/guarantee_user.vue

@@ -0,0 +1,156 @@
+<template>
+	<view class="">
+		<view class="sys-from-background-color p-20 r-20 size-26" v-if="item.is_edit">
+			<view class="">
+				<view class="row-justify-sb center">
+					<text class="size-28 sys-weight-600">担保人信息({{index+1}})</text>
+					<view class="row-c sys-background-fff r-100 p-lr30 p-tb10">
+						<image class="wh-30" src="/static/img/information/edit.png" mode="aspectFill"
+							@click="onEditInfo(item)"></image>
+						<view class="title-line m-lr20"></view>
+						<image class="wh-30" src="/static/img/information/delete.png" mode="aspectFill"
+							@click="onDeleteInfo(index)">
+						</image>
+					</view>
+				</view>
+				<view class="row-c p-tb30">
+					<view class="text-color-666"><text></text>担保类型:</view>
+					<text>个人</text>
+				</view>
+				<view class="row-c">
+					<view class="text-color-666"><text></text>担保人信息:</view>
+					<text>张三</text>
+				</view>
+			</view>
+			<view class="row-c p-tb30">
+				<view class="text-color-666"><text></text>绑定资产:</view>
+				<text>绑定资产</text>
+			</view>
+			<view class="">
+				<text class="size-28 sys-weight-600">还款账户信息</text>
+				<view class="row-c p-tb30">
+					<view class="text-color-666"><text></text>还款金额:</view>
+					<text>100万</text>
+				</view>
+				<view class="row-c ">
+					<view class="text-color-666"><text></text>银行卡号:</view>
+					<text>4576476465498790878</text>
+				</view>
+				<view class="row-c p-tb30">
+					<view class="text-color-666"><text></text>所属银行:</view>
+					<text>中国银行</text>
+				</view>
+			</view>
+			<view class="">
+				<text class="size-28 sys-weight-600">放款账户信息</text>
+				<view class="row-c p-tb30">
+					<view class="text-color-666"><text></text>批复金额:</view>
+					<text>100万</text>
+				</view>
+				<view class="row-c ">
+					<view class="text-color-666"><text></text>银行卡号:</view>
+					<text>4576476465498790878</text>
+				</view>
+				<view class="row-c p-t30">
+					<view class="text-color-666"><text></text>所属银行:</view>
+					<text>中国银行</text>
+				</view>
+			</view>
+		</view>
+		<view v-else>
+			<view class="">
+				<text class="size-30 sys-weight-600'">抵押信息{{index+1}}</text>
+				<view class="row-justify-sb center p-tb30 bor-bottom-1 size-28">
+					<view class="wh-text"><text></text>申请类型</view>
+					<view class="row-c">
+						<view class="row-c radiu-item m-l20"
+							:class="{'active-radiu':formData.is_materials === item.value}"
+							v-for="(item,index) in impawn" :key="index" @click="onSetMaterials(item.value)">
+							<image class="wh-30 m-r20"
+								:src="formData.is_materials === item.value?'/static/img/information/correct.png':'/static/img/information/error.png'"
+								mode="aspectFill"></image>
+							<text>{{item.text}}</text>
+						</view>
+					</view>
+				</view>
+			</view>
+			<view class="m-t30">
+				<text class="size-30 sys-weight-600'">绑定资产信息</text>
+				<WhInput :name="'绑定资产'" disabled is_select></WhInput>
+			</view>
+			<view class="m-t30">
+				<text class="size-30 sys-weight-600'">还款账户信息</text>
+				<view class="row-justify-sb center p-tb30 bor-bottom-1 size-28">
+					<view class="wh-text"><text></text>还款金额</view>
+					<view class="row-c flex">
+						<input class="wh-input sys-size-28 text-color-12 m-lr20 flex"
+							placeholder-class="sys-size-28 text-color-999 sys-weight-400" placeholder="请输入"
+							v-model="inputValue"></input>
+					</view>
+					<text>万元</text>
+				</view>
+				<view class="row-justify-sb center p-tb30 bor-bottom-1 size-28">
+					<view class="wh-text"><text></text>银行卡</view>
+					<view class="row-c flex">
+						<input class="wh-input sys-size-28 text-color-12 m-lr20 flex"
+							placeholder-class="sys-size-28 text-color-999 sys-weight-400" placeholder="请输入银行账号"
+							v-model="inputValue"></input>
+					</view>
+					<uni-icons type="camera" size="30"></uni-icons>
+				</view>
+				<WhInput :is_border="false" :name="'所属银行'" disabled is_select></WhInput>
+			</view>
+			<view class="m-t30">
+				<text class="size-30 sys-weight-600'">放款账户信息</text>
+				<view class="row-justify-sb center p-tb30 bor-bottom-1 size-28">
+					<view class="wh-text"><text></text>还款金额</view>
+					<view class="row-c flex">
+						<input class="wh-input sys-size-28 text-color-12 m-lr20 flex"
+							placeholder-class="sys-size-28 text-color-999 sys-weight-400" placeholder="请输入"
+							v-model="inputValue"></input>
+					</view>
+					<text>万元</text>
+				</view>
+				<view class="row-justify-sb center p-tb30 bor-bottom-1 size-28">
+					<view class="wh-text"><text></text>银行卡</view>
+					<view class="row-c flex">
+						<input class="wh-input sys-size-28 text-color-12 m-lr20 flex"
+							placeholder-class="sys-size-28 text-color-999 sys-weight-400" placeholder="请输入银行账号"
+							v-model="inputValue"></input>
+					</view>
+					<uni-icons type="camera" size="30"></uni-icons>
+				</view>
+				<WhInput :is_border="false" :name="'所属银行'" disabled is_select></WhInput>
+			</view>
+			<view class="row-justify-sb">
+				<view class="flex"></view>
+				<view class="row-c m-tb20">
+					<button class="size-26 r-10 apply-button-delete m-r20" hover-class="is-hover"
+						@click="onDeleteInfo(index)">删除</button>
+					<button class="size-26 r-10 button-color apply-button-save" hover-class="is-hover"
+						@click="onSaveInfo(item)">保存</button>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		props: {
+			item: {
+				type: Object,
+				default: () => {}
+			}
+		},
+		data() {
+			return {}
+		},
+		methods: {
+
+		}
+	}
+</script>
+
+<style>
+</style>

+ 357 - 0
page_task/tripartite_info/tripartite_info.vue

@@ -0,0 +1,357 @@
+<template>
+	<view class="total-page page-env-160 page-box task-bg">
+		<Nav :title="title" is_fixed :genre="4" :opacity="scrollTop"></Nav>
+		<view id="apply-info" class="m-t20 m-lr30 text-color-12">
+			<view class="sys-background-fff r-20 p-20 m-b20" v-for="(item,index) in tripartiteList" :key="index">
+				<!-- 个人担保start -->
+				<view class="" v-if="item.data.type == 1">
+					<!-- 个人担保信息start -->
+					<view class="sys-from-background-color p-20 r-20 size-26" v-if="!item.is_edit">
+						<view class="">
+							<view class="row-justify-sb center">
+								<text class="size-28 sys-weight-600">担保人信息({{index+1}})</text>
+								<view class="row-c sys-background-fff r-100 p-lr30 p-tb10">
+									<image class="wh-30" src="/static/img/information/edit.png" mode="aspectFill"
+										@click="onEditInfo(item)"></image>
+									<view class="title-line m-lr20"></view>
+									<image class="wh-30" src="/static/img/information/delete.png" mode="aspectFill"
+										@click="onDeleteInfo(index)">
+									</image>
+								</view>
+							</view>
+							<view class="row-c p-tb30">
+								<view class="text-color-666"><text></text>担保类型:</view>
+								<text>个人</text>
+							</view>
+							<view class="row-c">
+								<view class="text-color-666"><text></text>担保人信息:</view>
+								<text>张三</text>
+							</view>
+							<view class="p-t30">
+								<view class="wh-text size-28 text-color-666 m-b20">证件照片</view>
+								<EnImage :img="['/static/img/logo.png','/static/img/logo.png']"></EnImage>
+							</view>
+						</view>
+						<view class="m-t30" v-for="(value,key) in item.data.bank_card" :key="key">
+							<text class="size-28 sys-weight-600">银行信息({{key+1}})</text>
+							<view class="row-c p-tb30">
+								<view class="text-color-666"><text></text>客户姓名:</view>
+								<text>张三</text>
+							</view>
+							<view class="row-c">
+								<view class="text-color-666"><text></text>所属银行:</view>
+								<text>中国银行</text>
+							</view>
+							<view class="row-c p-t30">
+								<view class="text-color-666"><text></text>交通银行:</view>
+								<text>4576476465498790878</text>
+							</view>
+						</view>
+					</view>
+					<!-- 个人担保信息end -->
+
+					<!-- 编辑个人担保start -->
+					<view v-else>
+						<view class="">
+							<text class="size-30 sys-weight-600'">担保人信息({{index+1}})</text>
+							<EnRadio :value="item.data.type" name="担保类型" :list="guaranteeList" @onSelect="onGuarantee">
+							</EnRadio>
+						</view>
+						<view class="sys-bg-BFD2CC row-justify-sb sys-weight-500 center r-20 p-30 m-t30">
+							<view class="row-c flex">
+								<image class="wh-45 m-r20" src="/static/img/task/sf_icon.png" mode=""></image>
+								<text class="size-26">身份证认证</text>
+							</view>
+							<button class="attestation-button button-background button-color size-26 r-30"
+								type="default" hover-class="is-hover" @click="onAuthentication">去认证</button>
+						</view>
+						<WhInput name="姓名" disabled placeholder="待自动录入"></WhInput>
+						<WhInput name="身份证号" disabled placeholder="待自动录入"></WhInput>
+						<view class="m-t30" v-for="(value,key) in item.data.bank_card" :key="key">
+							<view class="row-justify-sb center">
+								<text class="size-30 sys-weight-600'">银行卡信息({{key+1}})</text>
+								<image class="wh-30" src="/static/img/information/delete.png" mode="aspectFill"
+									@click="onDeleteBank(index)">
+								</image>
+							</view>
+							<WhInput name="账户姓名" placeholder="请输入批复金额"></WhInput>
+							<WhInput name="银行卡" placeholder="请输入银行账户">
+								<uni-icons type="camera" size="28" color="#999999"></uni-icons>
+							</WhInput>
+							<WhInput name="所属银行" disabled is_select></WhInput>
+						</view>
+						<view class="row-justify-sb">
+							<button class="size-26 r-10 button-color still-button m-t30" hover-class="is-hover"
+								@click="onAddBank">+ 添加还款信息</button>
+							<view class="flex"></view>
+						</view>
+						<view class="row-justify-sb">
+							<view class="flex"></view>
+							<view class="row-c m-tb20">
+								<button class="size-26 r-10 apply-button-delete m-r20" hover-class="is-hover"
+									@click="onDeleteInfo(index)">删除</button>
+								<button class="size-26 r-10 button-color apply-button-save" hover-class="is-hover"
+									@click="onSaveInfo(item)">保存</button>
+							</view>
+						</view>
+					</view>
+					<!-- 编辑个人担保end -->
+				</view>
+				<!-- 个人担保end -->
+
+				<!-- 公司担保start -->
+				<view class="" v-if="item.data.type == 2">
+					<!-- 企业信息start -->
+					<view class="sys-from-background-color p-20 r-20 size-26" v-if="!item.is_edit">
+						<view class="">
+							<view class="row-justify-sb center">
+								<text class="size-28 sys-weight-600'">企业担保信息({{index+1}})</text>
+								<view class="row-c sys-background-fff r-100 p-lr30 p-tb10">
+									<image class="wh-30" src="/static/img/information/edit.png" mode="aspectFill"
+										@click="onEditInfo(item)"></image>
+									<view class="title-line m-lr20"></view>
+									<image class="wh-30" src="/static/img/information/delete.png" mode="aspectFill"
+										@click="onDeleteInfo(index)">
+									</image>
+								</view>
+							</view>
+							<view class="row-c p-tb30">
+								<view class="text-color-666"><text></text>担保类型:</view>
+								<text>企业</text>
+							</view>
+							<view class="row-c">
+								<view class="text-color-666"><text></text>企业名称:</view>
+								<text>成都九吨科技</text>
+							</view>
+							<view class="row-c p-tb30">
+								<view class="text-color-666"><text></text>营业执照编号:</view>
+								<text>YX4857i345</text>
+							</view>
+							<view class="row-c">
+								<view class="text-color-666"><text></text>证件照片:</view>
+								<text>YX4857i345</text>
+							</view>
+						</view>
+						<view class="m-t30" v-for="(value,key) in item.data.bank_card" :key="key">
+							<text class="size-28 sys-weight-600">银行信息({{key+1}})</text>
+							<view class="row-c p-tb30">
+								<view class="text-color-666"><text></text>客户姓名:</view>
+								<text>张三</text>
+							</view>
+							<view class="row-c">
+								<view class="text-color-666"><text></text>所属银行:</view>
+								<text>中国银行</text>
+							</view>
+							<view class="row-c p-t30">
+								<view class="text-color-666"><text></text>交通银行:</view>
+								<text>4576476465498790878</text>
+							</view>
+						</view>
+					</view>
+					<!-- 企业信息end -->
+
+					<!-- 编辑企业信息start -->
+					<view v-else>
+						<view class="">
+							<text class="size-30 sys-weight-600'">企业担保信息({{index+1}})</text>
+							<EnRadio :value="item.data.type" name="担保类型" :list="guaranteeList" @onSelect="onGuarantee">
+							</EnRadio>
+							<WhInput name="企业名称" placeholder="请输入企业名称"></WhInput>
+							<WhInput name="营业执照" placeholder="请输入营业执照编号"></WhInput>
+							<view class="p-tb30 bor-bottom-1">
+								<view class="wh-text size-28">营业执照图片</view>
+								<EnUpload></EnUpload>
+							</view>
+						</view>
+						<view class="m-t30" v-for="(value,key) in item.data.bank_card" :key="key">
+							<view class="row-justify-sb center">
+								<text class="size-30 sys-weight-600'">银行卡信息({{key+1}})</text>
+								<image class="wh-30" src="/static/img/information/delete.png" mode="aspectFill"
+									@click="onDeleteBank(index)">
+								</image>
+							</view>
+							<WhInput name="账户姓名" placeholder="请输入批复金额"></WhInput>
+							<WhInput name="银行卡" placeholder="请输入银行账户">
+								<uni-icons type="camera" size="28" color="#999999"></uni-icons>
+							</WhInput>
+							<WhInput name="所属银行" disabled is_select></WhInput>
+						</view>
+						<view class="row-justify-sb">
+							<button class="size-26 r-10 button-color still-button m-t30" hover-class="is-hover"
+								@click="onAddBank">+ 添加还款信息</button>
+							<view class="flex"></view>
+						</view>
+						<view class="row-justify-sb">
+							<view class="flex"></view>
+							<view class="row-c m-tb20">
+								<button class="size-26 r-10 apply-button-delete m-r20" hover-class="is-hover"
+									@click="onDeleteInfo(index)">删除</button>
+								<button class="size-26 r-10 button-color apply-button-save" hover-class="is-hover"
+									@click="onSaveInfo(item)">保存</button>
+							</view>
+						</view>
+					</view>
+					<!-- 编辑企业信息end -->
+				</view>
+				<!-- 公司担保start -->
+			</view>
+			<button class="size-26 r-10 button-color apply-button m-t30" hover-class="is-hover"
+				@click="onAddInfo">+添加第三方信息</button>
+		</view>
+		<EnButton is_both leftText="保存" rightText="提交" @onLeftSubmit="onLeftSubmit" @onSubmit="onSubmit">
+		</EnButton>
+	</view>
+</template>
+
+<script>
+	import WhInput from "@/page_task/module/wh-input.vue"
+	import EnUpload from "@/components/en-utils/en-upload/en-upload.vue"
+	import EnImage from "@/components/en-utils/en-image/en-image.vue"
+
+	export default {
+		components: {
+			WhInput,
+			EnUpload,
+			EnImage
+		},
+		data() {
+			return {
+				title: '',
+				scrollTop: 0,
+				guaranteeList: [{
+					text: '个人',
+					value: 1
+				}, {
+					text: '企业',
+					value: 2
+				}],
+				tripartiteList: [{
+					is_edit: false,
+					data: {
+						type: 1,
+						bank_card: [1]
+					}
+				}, {
+					is_edit: true,
+					data: {
+						type: 1,
+						bank_card: [1]
+					}
+				}, {
+					is_edit: false,
+					data: {
+						type: 2,
+						bank_card: [1]
+					}
+				}, {
+					is_edit: true,
+					data: {
+						type: 2,
+						bank_card: [1]
+					}
+				}]
+			}
+		},
+		onPageScroll(res) {
+			this.scrollTop = res.scrollTop / 120
+		},
+		onLoad(options) {
+			this.title = options.title
+		},
+		methods: {
+			//  身份认证
+			onAuthentication(item) {
+				uni.navigateTo({
+					url: "/page_task/identity_upload/identity_upload"
+				})
+			},
+			// 保存
+			onLeftSubmit() {
+				this.tripartiteList.map(item => {
+					item.is_edit = true
+				})
+			},
+			// 提交
+			onSubmit() {},
+			// 添加信息
+			onAddInfo() {
+				this.tripartiteList.push({
+					is_edit: true,
+					data: {
+						type: 1
+					}
+				})
+			},
+			// 添加银行卡信息
+			onAddBank() {
+
+			},
+			// 编辑单个信息
+			onEditInfo(item) {
+				item.is_edit = !item.is_edit
+			},
+			// 删除信息
+			onDeleteInfo(index) {
+				uni.showModal({
+					title: '提示',
+					content: '确定删除?',
+					success: (res) => {
+						if (res.confirm) {
+							this.tripartiteList.splice(index, 1)
+						}
+					}
+				})
+
+			},
+			// 删除银行卡
+			onDeleteBank(index) {
+				console.log(index);
+			},
+			// 保存单个信息
+			onSaveInfo(item) {
+				item.is_edit = !item.is_edit
+			},
+			onGuarantee(e) {
+				console.log(e);
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.attestation-button {
+		width: 156rpx;
+		height: 54rpx;
+		line-height: 54rpx;
+		background: #FFFFFF;
+		border: 1rpx solid #0FB160;
+	}
+
+	.still-button {
+		width: 260rpx;
+		background: #FFFFFF;
+		border-radius: 10rpx;
+		border: 1rpx solid #0FB160;
+	}
+
+	.apply-button {
+		height: 80rpx;
+		line-height: 80rpx;
+		background: #FFFFFF;
+		border: 2rpx solid #0FB160;
+		margin-bottom: 100rpx;
+	}
+
+	.apply-button-save {
+		width: 140rpx;
+		background: #FFFFFF;
+		border: 2rpx solid #0FB160;
+	}
+
+	.apply-button-delete {
+		color: #E83F37;
+		width: 140rpx;
+		background: #FFFFFF;
+		border: 2rpx solid #E83F37;
+	}
+</style>

+ 54 - 55
pages.json

@@ -87,67 +87,66 @@
 	}, {
 		"root": "page_task",
 		"pages": [{
-			"path": "gain_task/gain_task",
-			"style": {
-				"navigationBarTitleText": ""
-			}
-		},
-		{
-			"path" : "task_details/task_details",
-			"style" : 
+				"path": "gain_task/gain_task",
+				"style": {
+					"navigationBarTitleText": ""
+				}
+			},
 			{
-				"navigationBarTitleText" : ""
-			}
-		},
-		{
-			"path" : "creation_task/creation_task",
-			"style" : 
+				"path": "task_details/task_details",
+				"style": {
+					"navigationBarTitleText": ""
+				}
+			},
 			{
-				"navigationBarTitleText" : ""
-			}
-		},
-		{
-			"path" : "information/information",
-			"style" : 
+				"path": "creation_task/creation_task",
+				"style": {
+					"navigationBarTitleText": ""
+				}
+			},
 			{
-				"navigationBarTitleText" : ""
-			}
-		},
-		{
-			"path" : "identity/identity",
-			"style" : 
+				"path": "information/information",
+				"style": {
+					"navigationBarTitleText": ""
+				}
+			},
 			{
-				"navigationBarTitleText" : ""
-			}
-		},
-		{
-			"path" : "identity_upload/identity_upload",
-			"style" : 
+				"path": "identity/identity",
+				"style": {
+					"navigationBarTitleText": ""
+				}
+			},
 			{
-				"navigationBarTitleText" : ""
-			}
-		},
-		{
-			"path" : "property/property",
-			"style" : 
+				"path": "identity_upload/identity_upload",
+				"style": {
+					"navigationBarTitleText": ""
+				}
+			},
 			{
-				"navigationBarTitleText" : ""
-			}
-		},
-		{
-			"path" : "apply/apply",
-			"style" : 
+				"path": "property/property",
+				"style": {
+					"navigationBarTitleText": ""
+				}
+			},
 			{
-				"navigationBarTitleText" : ""
-			}
-		},
-		{
-			"path" : "task_operate/task_operate",
-			"style" : 
+				"path": "apply/apply",
+				"style": {
+					"navigationBarTitleText": ""
+				}
+			},
 			{
-				"navigationBarTitleText" : ""
+				"path": "task_operate/task_operate",
+				"style": {
+					"navigationBarTitleText": ""
+				}
+			},
+			{
+				"path": "tripartite_info/tripartite_info",
+				"style": {
+					"navigationBarTitleText": ""
+				}
 			}
-		}]
+		]
 	}],
 	"globalStyle": {
 		"navigationBarTextStyle": "black",
@@ -160,9 +159,9 @@
 	"tabBar": {
 		"custom": true,
 		"list": [{
-			"pagePath": "pages/index/index",
-			"text": ""
-		},
+				"pagePath": "pages/index/index",
+				"text": ""
+			},
 			{
 				"pagePath": "pages/task/task",
 				"text": ""

+ 1 - 1
static/css/common.css

@@ -53,7 +53,7 @@
 	position: fixed;
 	width: 100%;
 	top: 84px;
-	z-index: 9999;
+	z-index: 4;
 }
 
 .scroll_content {

BIN
static/img/login/eye-filled.png


BIN
static/img/login/eye.png


BIN
static/img/statistics/Group 1783.png


BIN
static/img/statistics/Group 1786.png


BIN
static/img/statistics/Group 2112.png


BIN
static/img/statistics/Group 2113.png


BIN
static/img/task/task-three.png


+ 8 - 7
uni_modules/uni-data-picker/components/uni-data-picker/uni-data-picker.vue

@@ -16,8 +16,7 @@
 						</view>
 					</scroll-view>
 					<text v-else class="selected-area placeholder">{{placeholder}}</text>
-					<view v-if="clearIcon && !readonly && inputSelected.length" class="icon-clear"
-						@click.stop="clear">
+					<view v-if="clearIcon && !readonly && inputSelected.length" class="icon-clear" @click.stop="clear">
 						<uni-icons type="clear" color="#c0c4cc" size="24"></uni-icons>
 					</view>
 					<view class="arrow-area" v-if="(!clearIcon || !inputSelected.length) && !readonly ">
@@ -294,7 +293,7 @@
 	}
 </script>
 
-<style >
+<style>
 	.uni-data-tree {
 		flex: 1;
 		position: relative;
@@ -362,7 +361,7 @@
 		white-space: nowrap;
 		/* #endif */
 	}
-	
+
 	.text-color {
 		color: #333;
 	}
@@ -451,7 +450,8 @@
 	}
 
 	.dialog-title {
-		/* font-weight: bold; */
+		font-weight: bold;
+		font-size: 16px;
 		line-height: 44px;
 	}
 
@@ -485,7 +485,7 @@
 		flex: 1;
 		overflow: hidden;
 	}
-	
+
 	.icon-clear {
 		display: flex;
 		align-items: center;
@@ -550,5 +550,6 @@
 		border-top-width: 0;
 		border-bottom-color: #fff;
 	}
+
 	/* #endif */
-	</style>
+</style>

+ 12 - 8
uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-pickerview.vue

@@ -5,14 +5,14 @@
 				<template v-for="(item,index) in selected">
 					<view class="selected-item"
 						:class="{'selected-item-active':index==selectedIndex, 'selected-item-text-overflow': ellipsis}"
-						 v-if="item.text" @click="handleSelect(index)">
+						v-if="item.text" @click="handleSelect(index)">
 						<text class="">{{item.text}}</text>
 					</view>
 				</template>
 			</view>
 		</scroll-view>
 		<view class="tab-c">
-			<template v-for="(child, i) in dataList" >
+			<template v-for="(child, i) in dataList">
 				<scroll-view class="list" :key="i" v-if="i==selectedIndex" :scroll-y="true">
 					<view class="item" :class="{'is-disabled': !!item.disable}" v-for="(item, j) in child"
 						@click="handleNodeClick(item, i, j)">
@@ -177,7 +177,7 @@
 		}
 	}
 </script>
-<style >
+<style>
 	.uni-data-pickerview {
 		flex: 1;
 		/* #ifndef APP-NVUE */
@@ -253,11 +253,11 @@
 	}
 
 	.selected-item-text-overflow {
-		width: 168px;
+		/* width: 168px; */
 		/* fix nvue */
 		overflow: hidden;
 		/* #ifndef APP-NVUE */
-		width: 6em;
+		/* width: 6em; */
 		white-space: nowrap;
 		text-overflow: ellipsis;
 		-o-text-overflow: ellipsis;
@@ -265,7 +265,7 @@
 	}
 
 	.selected-item-active {
-		border-bottom: 2px solid #007aff;
+		border-bottom: 2px solid #0FB160;
 	}
 
 	.selected-item-text {
@@ -296,6 +296,10 @@
 		justify-content: space-between;
 	}
 
+	.item:last-child {
+		padding-bottom: env(safe-area-inset-bottom);
+	}
+
 	.is-disabled {
 		opacity: .5;
 	}
@@ -319,7 +323,7 @@
 
 	.check {
 		margin-right: 5px;
-		border: 2px solid #007aff;
+		border: 2px solid #0FB160;
 		border-left: 0;
 		border-top: 0;
 		height: 12px;
@@ -330,4 +334,4 @@
 		/* #endif */
 		transform: rotate(45deg);
 	}
-</style>
+</style>

+ 7 - 0
uni_modules/uv-action-sheet/changelog.md

@@ -0,0 +1,7 @@
+## 1.0.2(2023-07-02)
+uv-action-sheet  由于弹出层uv-popup的修改,打开和关闭方法更改,详情参考文档:https://www.uvui.cn/components/actionSheet.html
+## 1.0.1(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.0(2023-05-10)
+uv-action-sheet 底部操作菜单

+ 50 - 0
uni_modules/uv-action-sheet/components/uv-action-sheet/props.js

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

+ 280 - 0
uni_modules/uv-action-sheet/components/uv-action-sheet/uv-action-sheet.vue

@@ -0,0 +1,280 @@
+
+<template>
+	<uv-popup
+	  ref="popup"
+	  mode="bottom"
+	  :safeAreaInsetBottom="safeAreaInsetBottom"
+	  :round="round"
+		:close-on-click-overlay="closeOnClickOverlay"
+		@change="popupChange"
+	>
+		<view class="uv-action-sheet">
+			<view
+			  class="uv-action-sheet__header"
+			  v-if="title"
+			>
+				<text class="uv-action-sheet__header__title uv-line-1">{{title}}</text>
+				<view
+				  class="uv-action-sheet__header__icon-wrap"
+				  @tap.stop="cancel"
+				>
+					<uv-icon
+					  name="close"
+					  size="17"
+					  color="#c8c9cc"
+					  bold
+					></uv-icon>
+				</view>
+			</view>
+			<text
+			  class="uv-action-sheet__description"
+				:style="[{
+					marginTop: `${title && description ? 0 : '18px'}`
+				}]"
+			  v-if="description"
+			>{{description}}</text>
+			<slot>
+				<uv-line v-if="description"></uv-line>
+				<view class="uv-action-sheet__item-wrap">
+					<view v-for="(item, index) in actions" :key="index">
+						<!-- #ifdef MP -->
+						<button
+						  class="uv-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 ? 'uv-action-sheet--hover' : ''"
+						>
+							<!-- #endif -->
+							<view
+							  class="uv-action-sheet__item-wrap__item"
+							  @tap.stop="selectHandler(index)"
+							  :hover-class="!item.disabled && !item.loading ? 'uv-action-sheet--hover' : ''"
+							  :hover-stay-time="150"
+							>
+								<template v-if="!item.loading">
+									<text
+									  class="uv-action-sheet__item-wrap__item__name"
+									  :style="[itemStyle(index)]"
+									>{{ item.name }}</text>
+									<text
+									  v-if="item.subname"
+									  class="uv-action-sheet__item-wrap__item__subname"
+									>{{ item.subname }}</text>
+								</template>
+								<uv-loading-icon
+								  v-else
+								  custom-class="van-action-sheet__loading"
+								  size="18"
+								  mode="circle"
+								/>
+							</view>
+							<!-- #ifdef MP -->
+						</button>
+						<!-- #endif -->
+						<uv-line v-if="index !== actions.length - 1"></uv-line>
+					</view>
+				</view>
+			</slot>
+			<uv-gap
+			  bgColor="#eaeaec"
+			  height="6"
+			  v-if="cancelText"
+			></uv-gap>
+			<view hover-class="uv-action-sheet--hover">
+				<text
+				  @touchmove.stop.prevent
+				  :hover-stay-time="150"
+				  v-if="cancelText"
+				  class="uv-action-sheet__cancel-text"
+				  @tap="cancel"
+				>{{cancelText}}</text>
+			</view>
+		</view>
+	</uv-popup>
+</template>
+
+<script>
+	import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+	import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+	import button from '@/uni_modules/uv-ui-tools/libs/mixin/button.js'
+	import openType from '@/uni_modules/uv-ui-tools/libs/mixin/openType.js'
+	import props from './props.js';
+	/**
+	 * ActionSheet 操作菜单
+	 * @description 本组件用于从底部弹出一个操作菜单,供用户选择并返回结果。本组件功能类似于uni的uni.showActionSheetAPI,配置更加灵活,所有平台都表现一致。
+	 * @tutorial https://www.uvui.cn/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 {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 <uv-action-sheet ref="actionSheet" :actions="list" :title="title" ></uv-action-sheet>
+	 */
+	export default {
+		name: "uv-action-sheet",
+		mixins: [openType, button, mpMixin , mixin, props],
+		emits: ['close', 'select'],
+		computed: {
+			// 操作项目的样式
+			itemStyle() {
+				return (index) => {
+					let style = {};
+					if (this.actions[index].color) style.color = this.actions[index].color
+					if (this.actions[index].fontSize) style.fontSize = this.$uv.addUnit(this.actions[index].fontSize)
+					// 选项被禁用的样式
+					if (this.actions[index].disabled) style.color = '#c0c4cc'
+					return style;
+				}
+			},
+		},
+		methods: {
+			open() {
+				this.$refs.popup.open();
+			},
+			close() {
+				this.$refs.popup.close();
+			},
+			popupChange(e) {
+				if(!e.show) this.$emit('close');
+			},
+			// 点击取消按钮
+			cancel() {
+				this.close();
+			},
+			selectHandler(index) {
+				const item = this.actions[index]
+				if (item && !item.disabled && !item.loading) {
+					this.$emit('select', item)
+					if (this.closeOnClickAction) {
+						this.close();
+					}
+				}
+			},
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	$show-lines: 1;
+	$show-reset-button: 1;
+	@import '@/uni_modules/uv-ui-tools/libs/css/variable.scss';
+	@import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+	@import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
+	$uv-action-sheet-reset-button-width:100% !default;
+	$uv-action-sheet-title-font-size: 16px !default;
+	$uv-action-sheet-title-padding: 12px 30px !default;
+	$uv-action-sheet-title-color: $uv-main-color !default;
+	$uv-action-sheet-header-icon-wrap-right:15px !default;
+	$uv-action-sheet-header-icon-wrap-top:15px !default;
+	$uv-action-sheet-description-font-size:13px !default;
+	$uv-action-sheet-description-color:14px !default;
+	$uv-action-sheet-description-margin: 18px 15px !default;
+	$uv-action-sheet-item-wrap-item-padding:15px !default;
+	$uv-action-sheet-item-wrap-name-font-size:16px !default;
+	$uv-action-sheet-item-wrap-subname-font-size:13px !default;
+	$uv-action-sheet-item-wrap-subname-color: #c0c4cc !default;
+	$uv-action-sheet-item-wrap-subname-margin-top:10px !default;
+	$uv-action-sheet-cancel-text-font-size:16px !default;
+	$uv-action-sheet-cancel-text-color:$uv-content-color !default;
+	$uv-action-sheet-cancel-text-font-size:15px !default;
+	$uv-action-sheet-cancel-text-hover-background-color:rgb(242, 243, 245) !default;
+
+	.uv-reset-button {
+		width: $uv-action-sheet-reset-button-width;
+	}
+
+	.uv-action-sheet {
+		text-align: center;
+		&__header {
+			position: relative;
+			padding: $uv-action-sheet-title-padding;
+			&__title {
+				font-size: $uv-action-sheet-title-font-size;
+				color: $uv-action-sheet-title-color;
+				font-weight: bold;
+				text-align: center;
+			}
+
+			&__icon-wrap {
+				position: absolute;
+				right: $uv-action-sheet-header-icon-wrap-right;
+				top: $uv-action-sheet-header-icon-wrap-top;
+			}
+		}
+
+		&__description {
+			font-size: $uv-action-sheet-description-font-size;
+			color: $uv-tips-color;
+			margin: $uv-action-sheet-description-margin;
+			text-align: center;
+		}
+
+		&__item-wrap {
+
+			&__item {
+				padding: $uv-action-sheet-item-wrap-item-padding;
+				@include flex;
+				align-items: center;
+				justify-content: center;
+				flex-direction: column;
+
+				&__name {
+					font-size: $uv-action-sheet-item-wrap-name-font-size;
+					color: $uv-main-color;
+					text-align: center;
+				}
+
+				&__subname {
+					font-size: $uv-action-sheet-item-wrap-subname-font-size;
+					color: $uv-action-sheet-item-wrap-subname-color;
+					margin-top: $uv-action-sheet-item-wrap-subname-margin-top;
+					text-align: center;
+				}
+			}
+		}
+
+		&__cancel-text {
+			font-size: $uv-action-sheet-cancel-text-font-size;
+			color: $uv-action-sheet-cancel-text-color;
+			text-align: center;
+			padding: $uv-action-sheet-cancel-text-font-size;
+		}
+
+		&--hover {
+			background-color: $uv-action-sheet-cancel-text-hover-background-color;
+		}
+	}
+</style>

+ 92 - 0
uni_modules/uv-action-sheet/package.json

@@ -0,0 +1,92 @@
+{
+  "id": "uv-action-sheet",
+  "displayName": "uv-action-sheet 底部操作菜单 全面兼容小程序、nvue、vue2、vue3等多端",
+  "version": "1.0.2",
+  "description": "该组件用于从底部弹出一个操作菜单,供用户选择并返回结果。本组件功能类似于uni的uni.showActionSheet API,配置更加灵活,所有平台都表现一致。",
+  "keywords": [
+    "action-sheet",
+    "uvui",
+    "uv-ui",
+    "操作菜单",
+    "菜单选择"
+],
+  "repository": "",
+  "engines": {
+    "HBuilderX": "^3.1.0"
+  },
+  "dcloudext": {
+    "type": "component-vue",
+    "sale": {
+      "regular": {
+        "price": "0.00"
+      },
+      "sourcecode": {
+        "price": "0.00"
+      }
+    },
+    "contact": {
+      "qq": ""
+    },
+    "declaration": {
+      "ads": "无",
+      "data": "插件不采集任何数据",
+      "permissions": "无"
+    },
+    "npmurl": ""
+  },
+  "uni_modules": {
+    "dependencies": [
+			"uv-ui-tools",
+			"uv-popup",
+			"uv-icon",
+			"uv-line",
+			"uv-loading-icon",
+			"uv-gap"
+		],
+    "encrypt": [],
+    "platforms": {
+      "cloud": {
+        "tcb": "y",
+        "aliyun": "y"
+      },
+      "client": {
+        "Vue": {
+          "vue2": "y",
+          "vue3": "y"
+        },
+        "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",
+          "飞书": "u",
+          "京东": "u"
+        },
+        "快应用": {
+          "华为": "u",
+          "联盟": "u"
+        }
+      }
+    }
+  }
+}

+ 13 - 0
uni_modules/uv-action-sheet/readme.md

@@ -0,0 +1,13 @@
+## ActionSheet 操作菜单
+
+> **组件名:uv-action-sheet**
+
+本组件用于从底部弹出一个操作菜单,供用户选择并返回结果。
+
+本组件功能类似于uni的uni.showActionSheet API,配置更加灵活,所有平台都表现一致。
+
+### <a href="https://www.uvui.cn/components/actionSheet.html" target="_blank">查看文档</a>
+
+### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+#### 如使用过程中有任何问题,或者您对uv-ui有一些好的建议,欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a>、<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>

+ 5 - 0
uni_modules/uv-gap/changelog.md

@@ -0,0 +1,5 @@
+## 1.0.1(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.0(2023-05-10)
+1. 新增间隔槽组件

+ 25 - 0
uni_modules/uv-gap/components/uv-gap/props.js

@@ -0,0 +1,25 @@
+export default {
+	props: {
+		// 背景颜色(默认transparent)
+		bgColor: {
+			type: String,
+			default: 'transparent'
+		},
+		// 分割槽高度,单位px(默认20)
+		height: {
+			type: [String, Number],
+			default: 20
+		},
+		// 与上一个组件的距离
+		marginTop: {
+			type: [String, Number],
+			default: 0
+		},
+		// 与下一个组件的距离
+		marginBottom: {
+			type: [String, Number],
+			default: 0
+		},
+		...uni.$uv?.props?.gap
+	}
+}

+ 36 - 0
uni_modules/uv-gap/components/uv-gap/uv-gap.vue

@@ -0,0 +1,36 @@
+<template>
+	<view class="uv-gap" :style="[gapStyle]"></view>
+</template>
+
+<script>
+	import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+	import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+	import props from './props.js';
+	/**
+	 * gap 间隔槽
+	 * @description 该组件一般用于内容块之间的用一个灰色块隔开的场景,方便用户风格统一,减少工作量
+	 * @tutorial https://www.uvui.cn/components/gap.html
+	 * @property {String}			bgColor			背景颜色 (默认 'transparent' )
+	 * @property {String | Number}	height			分割槽高度,单位px (默认 20 )
+	 * @property {String | Number}	marginTop		与前一个组件的距离,单位px( 默认 0 )
+	 * @property {String | Number}	marginBottom	与后一个组件的距离,单位px (默认 0 )
+	 * @property {Object}			customStyle		定义需要用到的外部样式
+	 * 
+	 * @example <uv-gap height="80" bg-color="#bbb"></uv-gap>
+	 */
+	export default {
+		name: "uv-gap",
+		mixins: [mpMixin, mixin,props],
+		computed: {
+			gapStyle() {
+				const style = {
+					backgroundColor: this.bgColor,
+					height: this.$uv.addUnit(this.height),
+					marginTop: this.$uv.addUnit(this.marginTop),
+					marginBottom: this.$uv.addUnit(this.marginBottom),
+				}
+				return this.$uv.deepMerge(style, this.$uv.addStyle(this.customStyle))
+			}
+		}
+	};
+</script>

+ 87 - 0
uni_modules/uv-gap/package.json

@@ -0,0 +1,87 @@
+{
+  "id": "uv-gap",
+  "displayName": "uv-gap 间隔槽 全面兼容小程序、nvue、vue2、vue3等多端",
+  "version": "1.0.1",
+  "description": "该组件一般用于内容块之间的用一个灰色块隔开的场景,方便用户风格统一,减少工作量。",
+  "keywords": [
+    "gap",
+    "uvui",
+    "uv-ui",
+    "间隔槽",
+    "内容块"
+],
+  "repository": "",
+  "engines": {
+    "HBuilderX": "^3.1.0"
+  },
+  "dcloudext": {
+    "type": "component-vue",
+    "sale": {
+      "regular": {
+        "price": "0.00"
+      },
+      "sourcecode": {
+        "price": "0.00"
+      }
+    },
+    "contact": {
+      "qq": ""
+    },
+    "declaration": {
+    	"ads": "无",
+    	"data": "插件不采集任何数据",
+    	"permissions": "无"
+    },
+    "npmurl": ""
+  },
+  "uni_modules": {
+    "dependencies": [
+			"uv-ui-tools"
+		],
+    "encrypt": [],
+    "platforms": {
+			"cloud": {
+				"tcb": "y",
+				"aliyun": "y"
+			},
+			"client": {
+				"Vue": {
+					"vue2": "y",
+					"vue3": "y"
+				},
+				"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",
+					"飞书": "u",
+					"京东": "u"
+				},
+				"快应用": {
+					"华为": "u",
+					"联盟": "u"
+				}
+			}
+		}
+  }
+}

+ 12 - 0
uni_modules/uv-gap/readme.md

@@ -0,0 +1,12 @@
+## Gap 间隔槽
+
+> **组件名:uv-gap**
+
+该组件一般用于内容块之间的用一个灰色块隔开的场景,方便用户风格统一,减少工作量。
+
+### <a href="https://www.uvui.cn/components/gap.html" target="_blank">查看文档</a>
+
+### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+#### 如使用过程中有任何问题,或者您对uv-ui有一些好的建议,欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a>、<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
+

+ 31 - 0
uni_modules/uv-icon/changelog.md

@@ -0,0 +1,31 @@
+## 1.0.13(2023-12-06)
+1. 优化
+## 1.0.12(2023-12-06)
+1. 阻止事件冒泡处理
+## 1.0.11(2023-10-29)
+1. imgMode默认值改成aspectFit
+## 1.0.10(2023-08-13)
+1. 优化nvue,方便自定义图标
+## 1.0.9(2023-07-28)
+1. 修改几个对应错误图标的BUG
+## 1.0.8(2023-07-24)
+1. 优化 支持base64图片
+## 1.0.7(2023-07-17)
+1. 修复  uv-icon 恢复uv-empty相关的图标
+## 1.0.6(2023-07-13)
+1. 修复icon设置name属性对应图标错误的BUG
+## 1.0.5(2023-07-04)
+1. 更新图标,删除一些不常用的图标
+2. 删除base64,修改成ttf文件引入读取图标
+3. 自定义图标文档说明:https://www.uvui.cn/guide/customIcon.html
+## 1.0.4(2023-07-03)
+1. 修复主题颜色在APP不生效的BUG
+## 1.0.3(2023-05-24)
+1. 将线上ttf字体包替换成base64,避免加载时或者网络差时候显示白色方块
+## 1.0.2(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.1(2023-05-10)
+1. 修复小程序中异常显示
+## 1.0.0(2023-05-04)
+新发版

+ 160 - 0
uni_modules/uv-icon/components/uv-icon/icons.js

@@ -0,0 +1,160 @@
+export default {
+	'uvicon-level': 'e68f',
+	'uvicon-checkbox-mark': 'e659',
+	'uvicon-folder': 'e694',
+	'uvicon-movie': 'e67c',
+	'uvicon-star-fill': 'e61e',
+	'uvicon-star': 'e618',
+	'uvicon-phone-fill': 'e6ac',
+	'uvicon-phone': 'e6ba',
+	'uvicon-apple-fill': 'e635',
+	'uvicon-backspace': 'e64d',
+	'uvicon-attach': 'e640',
+	'uvicon-empty-data': 'e671',
+	'uvicon-empty-address': 'e68a',
+	'uvicon-empty-favor': 'e662',
+	'uvicon-empty-car': 'e657',
+	'uvicon-empty-order': 'e66b',
+	'uvicon-empty-list': 'e672',
+	'uvicon-empty-search': 'e677',
+	'uvicon-empty-permission': 'e67d',
+	'uvicon-empty-news': 'e67e',
+	'uvicon-empty-history': 'e685',
+	'uvicon-empty-coupon': 'e69b',
+	'uvicon-empty-page': 'e60e',
+	'uvicon-empty-wifi-off': 'e6cc',
+	'uvicon-reload': 'e627',
+	'uvicon-order': 'e695',
+	'uvicon-server-man': 'e601',
+	'uvicon-search': 'e632',
+	'uvicon-more-dot-fill': 'e66f',
+	'uvicon-scan': 'e631',
+	'uvicon-map': 'e665',
+	'uvicon-map-fill': 'e6a8',
+	'uvicon-tags': 'e621',
+	'uvicon-tags-fill': 'e613',
+	'uvicon-eye': 'e664',
+	'uvicon-eye-fill': 'e697',
+	'uvicon-eye-off': 'e69c',
+	'uvicon-eye-off-outline': 'e688',
+	'uvicon-mic': 'e66d',
+	'uvicon-mic-off': 'e691',
+	'uvicon-calendar': 'e65c',
+	'uvicon-trash': 'e623',
+	'uvicon-trash-fill': 'e6ce',
+	'uvicon-play-left': 'e6bf',
+	'uvicon-play-right': 'e6b3',
+	'uvicon-minus': 'e614',
+	'uvicon-plus': 'e625',
+	'uvicon-info-circle': 'e69f',
+	'uvicon-info-circle-fill': 'e6a7',
+	'uvicon-question-circle': 'e622',
+	'uvicon-question-circle-fill': 'e6bc',
+	'uvicon-close': 'e65a',
+	'uvicon-checkmark': 'e64a',
+	'uvicon-checkmark-circle': 'e643',
+	'uvicon-checkmark-circle-fill': 'e668',
+	'uvicon-setting': 'e602',
+	'uvicon-setting-fill': 'e6d0',
+	'uvicon-heart': 'e6a2',
+	'uvicon-heart-fill': 'e68b',
+	'uvicon-camera': 'e642',
+	'uvicon-camera-fill': 'e650',
+	'uvicon-more-circle': 'e69e',
+	'uvicon-more-circle-fill': 'e684',
+	'uvicon-chat': 'e656',
+	'uvicon-chat-fill': 'e63f',
+	'uvicon-bag': 'e647',
+	'uvicon-error-circle': 'e66e',
+	'uvicon-error-circle-fill': 'e655',
+	'uvicon-close-circle': 'e64e',
+	'uvicon-close-circle-fill': 'e666',
+	'uvicon-share': 'e629',
+	'uvicon-share-fill': 'e6bb',
+	'uvicon-share-square': 'e6c4',
+	'uvicon-shopping-cart': 'e6cb',
+	'uvicon-shopping-cart-fill': 'e630',
+	'uvicon-bell': 'e651',
+	'uvicon-bell-fill': 'e604',
+	'uvicon-list': 'e690',
+	'uvicon-list-dot': 'e6a9',
+	'uvicon-zhifubao-circle-fill': 'e617',
+	'uvicon-weixin-circle-fill': 'e6cd',
+	'uvicon-weixin-fill': 'e620',
+	'uvicon-qq-fill': 'e608',
+	'uvicon-qq-circle-fill': 'e6b9',
+	'uvicon-moments-circel-fill': 'e6c2',
+	'uvicon-moments': 'e6a0',
+	'uvicon-car': 'e64f',
+	'uvicon-car-fill': 'e648',
+	'uvicon-warning-fill': 'e6c7',
+	'uvicon-warning': 'e6c1',
+	'uvicon-clock-fill': 'e64b',
+	'uvicon-clock': 'e66c',
+	'uvicon-edit-pen': 'e65d',
+	'uvicon-edit-pen-fill': 'e679',
+	'uvicon-email': 'e673',
+	'uvicon-email-fill': 'e683',
+	'uvicon-minus-circle': 'e6a5',
+	'uvicon-plus-circle': 'e603',
+	'uvicon-plus-circle-fill': 'e611',
+	'uvicon-file-text': 'e687',
+	'uvicon-file-text-fill': 'e67f',
+	'uvicon-pushpin': 'e6d1',
+	'uvicon-pushpin-fill': 'e6b6',
+	'uvicon-grid': 'e68c',
+	'uvicon-grid-fill': 'e698',
+	'uvicon-play-circle': 'e6af',
+	'uvicon-play-circle-fill': 'e62a',
+	'uvicon-pause-circle-fill': 'e60c',
+	'uvicon-pause': 'e61c',
+	'uvicon-pause-circle': 'e696',
+	'uvicon-gift-fill': 'e6b0',
+	'uvicon-gift': 'e680',
+	'uvicon-kefu-ermai': 'e660',
+	'uvicon-server-fill': 'e610',
+	'uvicon-coupon-fill': 'e64c',
+	'uvicon-coupon': 'e65f',
+	'uvicon-integral': 'e693',
+	'uvicon-integral-fill': 'e6b1',
+	'uvicon-home-fill': 'e68e',
+	'uvicon-home': 'e67b',
+	'uvicon-account': 'e63a',
+	'uvicon-account-fill': 'e653',
+	'uvicon-thumb-down-fill': 'e628',
+	'uvicon-thumb-down': 'e60a',
+	'uvicon-thumb-up': 'e612',
+	'uvicon-thumb-up-fill': 'e62c',
+	'uvicon-lock-fill': 'e6a6',
+	'uvicon-lock-open': 'e68d',
+	'uvicon-lock-opened-fill': 'e6a1',
+	'uvicon-lock': 'e69d',
+	'uvicon-red-packet': 'e6c3',
+	'uvicon-photo-fill': 'e6b4',
+	'uvicon-photo': 'e60d',
+	'uvicon-volume-off-fill': 'e6c8',
+	'uvicon-volume-off': 'e6bd',
+	'uvicon-volume-fill': 'e624',
+	'uvicon-volume': 'e605',
+	'uvicon-download': 'e670',
+	'uvicon-arrow-up-fill': 'e636',
+	'uvicon-arrow-down-fill': 'e638',
+	'uvicon-play-left-fill': 'e6ae',
+	'uvicon-play-right-fill': 'e6ad',
+	'uvicon-arrow-downward': 'e634',
+	'uvicon-arrow-leftward': 'e63b',
+	'uvicon-arrow-rightward': 'e644',
+	'uvicon-arrow-upward': 'e641',
+	'uvicon-arrow-down': 'e63e',
+	'uvicon-arrow-right': 'e63c',
+	'uvicon-arrow-left': 'e646',
+	'uvicon-arrow-up': 'e633',
+	'uvicon-skip-back-left': 'e6c5',
+	'uvicon-skip-forward-right': 'e61f',
+	'uvicon-arrow-left-double': 'e637',
+	'uvicon-man': 'e675',
+	'uvicon-woman': 'e626',
+	'uvicon-en': 'e6b8',
+	'uvicon-twitte': 'e607',
+	'uvicon-twitter-circle-fill': 'e6cf'
+}

+ 90 - 0
uni_modules/uv-icon/components/uv-icon/props.js

@@ -0,0 +1,90 @@
+export default {
+	props: {
+		// 图标类名
+		name: {
+			type: String,
+			default: ''
+		},
+		// 图标颜色,可接受主题色
+		color: {
+			type: String,
+			default: '#606266'
+		},
+		// 字体大小,单位px
+		size: {
+			type: [String, Number],
+			default: '16px'
+		},
+		// 是否显示粗体
+		bold: {
+			type: Boolean,
+			default: false
+		},
+		// 点击图标的时候传递事件出去的index(用于区分点击了哪一个)
+		index: {
+			type: [String, Number],
+			default: null
+		},
+		// 触摸图标时的类名
+		hoverClass: {
+			type: String,
+			default: ''
+		},
+		// 自定义扩展前缀,方便用户扩展自己的图标库
+		customPrefix: {
+			type: String,
+			default: 'uvicon'
+		},
+		// 图标右边或者下面的文字
+		label: {
+			type: [String, Number],
+			default: ''
+		},
+		// label的位置,只能右边或者下边
+		labelPos: {
+			type: String,
+			default: 'right'
+		},
+		// label的大小
+		labelSize: {
+			type: [String, Number],
+			default: '15px'
+		},
+		// label的颜色
+		labelColor: {
+			type: String,
+			default: '#606266'
+		},
+		// label与图标的距离
+		space: {
+			type: [String, Number],
+			default: '3px'
+		},
+		// 图片的mode
+		imgMode: {
+			type: String,
+			default: 'aspectFit'
+		},
+		// 用于显示图片小图标时,图片的宽度
+		width: {
+			type: [String, Number],
+			default: ''
+		},
+		// 用于显示图片小图标时,图片的高度
+		height: {
+			type: [String, Number],
+			default: ''
+		},
+		// 用于解决某些情况下,让图标垂直居中的用途
+		top: {
+			type: [String, Number],
+			default: 0
+		},
+		// 是否阻止事件传播
+		stop: {
+			type: Boolean,
+			default: false
+		},
+		...uni.$uv?.props?.icon
+	}
+}

+ 226 - 0
uni_modules/uv-icon/components/uv-icon/uv-icon.vue

@@ -0,0 +1,226 @@
+<template>
+	<view
+	  class="uv-icon"
+	  @tap="clickHandler"
+	  :class="['uv-icon--' + labelPos]"
+	>
+		<image
+		  class="uv-icon__img"
+		  v-if="isImg"
+		  :src="name"
+		  :mode="imgMode"
+		  :style="[imgStyle, $uv.addStyle(customStyle)]"
+		></image>
+		<text
+		  v-else
+		  class="uv-icon__icon"
+		  :class="uClasses"
+		  :style="[iconStyle, $uv.addStyle(customStyle)]"
+		  :hover-class="hoverClass"
+		>{{icon}}</text>
+		<!-- 这里进行空字符串判断,如果仅仅是v-if="label",可能会出现传递0的时候,结果也无法显示 -->
+		<text
+		  v-if="label !== ''" 
+		  class="uv-icon__label"
+		  :style="{
+			color: labelColor,
+			fontSize: $uv.addUnit(labelSize),
+			marginLeft: labelPos == 'right' ? $uv.addUnit(space) : 0,
+			marginTop: labelPos == 'bottom' ? $uv.addUnit(space) : 0,
+			marginRight: labelPos == 'left' ? $uv.addUnit(space) : 0,
+			marginBottom: labelPos == 'top' ? $uv.addUnit(space) : 0
+		}"
+		>{{ label }}</text>
+	</view>
+</template>
+
+<script>
+	import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+	import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+	// #ifdef APP-NVUE
+	// nvue通过weex的dom模块引入字体,相关文档地址如下:
+	// https://weex.apache.org/zh/docs/modules/dom.html#addrule
+	import iconUrl from './uvicons.ttf';
+	const domModule = weex.requireModule('dom')
+	domModule.addRule('fontFace', {
+		'fontFamily': "uvicon-iconfont",
+		'src': "url('" + iconUrl + "')"
+	})
+	// #endif
+	// 引入图标名称,已经对应的unicode
+	import icons from './icons';
+	import props from './props.js';
+	/**
+	 * icon 图标
+	 * @description 基于字体的图标集,包含了大多数常见场景的图标。
+	 * @tutorial https://www.uvui.cn/components/icon.html
+	 * @property {String}			name			图标名称,见示例图标集
+	 * @property {String}			color			图标颜色,可接受主题色 (默认 color['uv-content-color'] )
+	 * @property {String | Number}	size			图标字体大小,单位px (默认 '16px' )
+	 * @property {Boolean}			bold			是否显示粗体 (默认 false )
+	 * @property {String | Number}	index			点击图标的时候传递事件出去的index(用于区分点击了哪一个)
+	 * @property {String}			hoverClass		图标按下去的样式类,用法同uni的view组件的hoverClass参数,详情见官网
+	 * @property {String}			customPrefix	自定义扩展前缀,方便用户扩展自己的图标库 (默认 'uicon' )
+	 * @property {String | Number}	label			图标右侧的label文字
+	 * @property {String}			labelPos		label相对于图标的位置,只能right或bottom (默认 'right' )
+	 * @property {String | Number}	labelSize		label字体大小,单位px (默认 '15px' )
+	 * @property {String}			labelColor		图标右侧的label文字颜色 ( 默认 color['uv-content-color'] )
+	 * @property {String | Number}	space			label与图标的距离,单位px (默认 '3px' )
+	 * @property {String}			imgMode			图片的mode
+	 * @property {String | Number}	width			显示图片小图标时的宽度
+	 * @property {String | Number}	height			显示图片小图标时的高度
+	 * @property {String | Number}	top				图标在垂直方向上的定位 用于解决某些情况下,让图标垂直居中的用途  (默认 0 )
+	 * @property {Boolean}			stop			是否阻止事件传播 (默认 false )
+	 * @property {Object}			customStyle		icon的样式,对象形式
+	 * @event {Function} click 点击图标时触发
+	 * @event {Function} touchstart 事件触摸时触发
+	 * @example <uv-icon name="photo" color="#2979ff" size="28"></uv-icon>
+	 */
+	export default {
+		name: 'uv-icon',
+		emits: ['click'],
+		mixins: [mpMixin, mixin, props],
+		data() {
+			return {
+				colorType: [
+					'primary',
+					'success',
+					'info',
+					'error',
+					'warning'
+				]
+			}
+		},
+		computed: {
+			uClasses() {
+				let classes = []
+				classes.push(this.customPrefix)
+				classes.push(this.customPrefix + '-' + this.name)
+				// 主题色,通过类配置
+				if (this.color && this.colorType.includes(this.color)) classes.push('uv-icon__icon--' + this.color)
+				// 阿里,头条,百度小程序通过数组绑定类名时,无法直接使用[a, b, c]的形式,否则无法识别
+				// 故需将其拆成一个字符串的形式,通过空格隔开各个类名
+				//#ifdef MP-ALIPAY || MP-TOUTIAO || MP-BAIDU
+				classes = classes.join(' ')
+				//#endif
+				return classes
+			},
+			iconStyle() {
+				let style = {}
+				style = {
+					fontSize: this.$uv.addUnit(this.size),
+					lineHeight: this.$uv.addUnit(this.size),
+					fontWeight: this.bold ? 'bold' : 'normal',
+					// 某些特殊情况需要设置一个到顶部的距离,才能更好的垂直居中
+					top: this.$uv.addUnit(this.top)
+				}
+				// 非主题色值时,才当作颜色值
+				if (this.color && !this.colorType.includes(this.color)) style.color = this.color
+				return style
+			},
+			// 判断传入的name属性,是否图片路径,只要带有"/"均认为是图片形式
+			isImg() {
+				const isBase64 = this.name.indexOf('data:') > -1 && this.name.indexOf('base64') > -1;
+				return this.name.indexOf('/') !== -1 || isBase64;
+			},
+			imgStyle() {
+				let style = {}
+				// 如果设置width和height属性,则优先使用,否则使用size属性
+				style.width = this.width ? this.$uv.addUnit(this.width) : this.$uv.addUnit(this.size)
+				style.height = this.height ? this.$uv.addUnit(this.height) : this.$uv.addUnit(this.size)
+				return style
+			},
+			// 通过图标名,查找对应的图标
+			icon() {
+				// 如果内置的图标中找不到对应的图标,就直接返回name值,因为用户可能传入的是unicode代码
+				const code = icons['uvicon-' + this.name];
+				// #ifdef APP-NVUE
+				if(!code) {
+					return code ? unescape(`%u${code}`) : ['uvicon'].indexOf(this.customPrefix) > -1 ? unescape(`%u${this.name}`) : '';
+				}
+				// #endif
+				return code ? unescape(`%u${code}`) : ['uvicon'].indexOf(this.customPrefix) > -1 ? this.name : '';
+			}
+		},
+		methods: {
+			clickHandler(e) {
+				this.$emit('click', this.index)
+				// 是否阻止事件冒泡
+				this.stop && this.preventEvent(e)
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	@import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+	@import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
+	// 变量定义
+	$uv-icon-primary: $uv-primary !default;
+	$uv-icon-success: $uv-success !default;
+	$uv-icon-info: $uv-info !default;
+	$uv-icon-warning: $uv-warning !default;
+	$uv-icon-error: $uv-error !default;
+	$uv-icon-label-line-height: 1 !default;
+	/* #ifndef APP-NVUE */
+	// 非nvue下加载字体
+	@font-face {
+		font-family: 'uvicon-iconfont';
+		src: url('./uvicons.ttf') format('truetype');
+	}
+	/* #endif */
+	.uv-icon {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		align-items: center;
+		&--left {
+			flex-direction: row-reverse;
+			align-items: center;
+		}
+		&--right {
+			flex-direction: row;
+			align-items: center;
+		}
+		&--top {
+			flex-direction: column-reverse;
+			justify-content: center;
+		}
+		&--bottom {
+			flex-direction: column;
+			justify-content: center;
+		}
+		&__icon {
+			font-family: uvicon-iconfont;
+			position: relative;
+			@include flex;
+			align-items: center;
+			&--primary {
+				color: $uv-icon-primary;
+			}
+			&--success {
+				color: $uv-icon-success;
+			}
+			&--error {
+				color: $uv-icon-error;
+			}
+			&--warning {
+				color: $uv-icon-warning;
+			}
+			&--info {
+				color: $uv-icon-info;
+			}
+		}
+		&__img {
+			/* #ifndef APP-NVUE */
+			height: auto;
+			will-change: transform;
+			/* #endif */
+		}
+		&__label {
+			/* #ifndef APP-NVUE */
+			line-height: $uv-icon-label-line-height;
+			/* #endif */
+		}
+	}
+</style>

BIN
uni_modules/uv-icon/components/uv-icon/uvicons.ttf


+ 83 - 0
uni_modules/uv-icon/package.json

@@ -0,0 +1,83 @@
+{
+  "id": "uv-icon",
+  "displayName": "uv-icon 图标 全面兼容vue3+2、app、h5、小程序等多端",
+  "version": "1.0.13",
+  "description": "基于字体的图标集,包含了大多数常见场景的图标,支持自定义,支持自定义图片图标等。可自定义颜色、大小。",
+  "keywords": [
+    "uv-ui,uvui,uv-icon,icon,图标,字体图标"
+],
+  "repository": "",
+  "engines": {
+    "HBuilderX": "^3.1.0"
+  },
+  "dcloudext": {
+    "type": "component-vue",
+    "sale": {
+      "regular": {
+        "price": "0.00"
+      },
+      "sourcecode": {
+        "price": "0.00"
+      }
+    },
+    "contact": {
+      "qq": ""
+    },
+    "declaration": {
+      "ads": "无",
+      "data": "插件不采集任何数据",
+      "permissions": "无"
+    },
+    "npmurl": ""
+  },
+  "uni_modules": {
+    "dependencies": [
+			"uv-ui-tools"
+		],
+    "encrypt": [],
+    "platforms": {
+      "cloud": {
+        "tcb": "y",
+        "aliyun": "y"
+      },
+      "client": {
+        "Vue": {
+          "vue2": "y",
+          "vue3": "y"
+        },
+        "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",
+          "飞书": "u",
+          "京东": "u"
+        },
+        "快应用": {
+          "华为": "u",
+          "联盟": "u"
+        }
+      }
+    }
+  }
+}

+ 15 - 0
uni_modules/uv-icon/readme.md

@@ -0,0 +1,15 @@
+## uv-icon 图标库
+
+> **组件名:uv-icon**
+
+基于字体的图标集,包含了大多数常见场景的图标,支持自定义,支持自定义图片图标等。
+
+# <a href="https://www.uvui.cn/components/icon.html" target="_blank">查看文档</a>
+
+## [下载完整示例项目](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+### [更多插件,请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+![image](https://mp-a667b617-c5f1-4a2d-9a54-683a67cff588.cdn.bspapp.com/uv-ui/banner.png)
+
+#### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>

+ 5 - 0
uni_modules/uv-line/changelog.md

@@ -0,0 +1,5 @@
+## 1.0.1(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.0(2023-05-10)
+1. 新增线条组件

+ 34 - 0
uni_modules/uv-line/components/uv-line/props.js

@@ -0,0 +1,34 @@
+export default {
+	props: {
+		color: {
+			type: String,
+			default: '#d6d7d9'
+		},
+		// 长度,竖向时表现为高度,横向时表现为长度,可以为百分比,带px单位的值等
+		length: {
+			type: [String, Number],
+			default: '100%'
+		},
+		// 线条方向,col-竖向,row-横向
+		direction: {
+			type: String,
+			default: 'row'
+		},
+		// 是否显示细边框
+		hairline: {
+			type: Boolean,
+			default: true
+		},
+		// 线条与上下左右元素的间距,字符串形式,如"30px"、"20px 30px"
+		margin: {
+			type: [String, Number],
+			default: 0
+		},
+		// 是否虚线,true-虚线,false-实线
+		dashed: {
+			type: Boolean,
+			default: false
+		},
+		...uni.$uv?.props?.line
+	}
+}

+ 60 - 0
uni_modules/uv-line/components/uv-line/uv-line.vue

@@ -0,0 +1,60 @@
+<template>
+	<view
+	    class="uv-line"
+	    :style="[lineStyle]"
+	>
+	</view>
+</template>
+
+<script>
+	import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+	import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+	import props from './props.js';
+	/**
+	 * line 线条
+	 * @description 此组件一般用于显示一根线条,用于分隔内容块,有横向和竖向两种模式,且能设置0.5px线条,使用也很简单
+	 * @tutorial https://www.uvui.cn/components/line.html
+	 * @property {String}			color		线条的颜色 ( 默认 '#d6d7d9' )
+	 * @property {String | Number}	length		长度,竖向时表现为高度,横向时表现为长度,可以为百分比,带px单位的值等 ( 默认 '100%' )
+	 * @property {String}			direction	线条的方向,row-横向,col-竖向 (默认 'row' )
+	 * @property {Boolean}			hairline	是否显示细线条 (默认 true )
+	 * @property {String | Number}	margin		线条与上下左右元素的间距,字符串形式,如"30px"  (默认 0 )
+	 * @property {Boolean}			dashed		是否虚线,true-虚线,false-实线 (默认 false )
+	 * @property {Object}			customStyle	定义需要用到的外部样式
+	 * @example <uv-line color="red"></uv-line>
+	 */
+	export default {
+		name: 'uv-line',
+		mixins: [mpMixin, mixin, props],
+		computed: {
+			lineStyle() {
+				const style = {}
+				style.margin = this.margin
+				// 如果是水平线条,边框高度为1px,再通过transform缩小一半,就是0.5px了
+				if (this.direction === 'row') {
+					// 此处采用兼容分开写,兼容nvue的写法
+					style.borderBottomWidth = '1px'
+					style.borderBottomStyle = this.dashed ? 'dashed' : 'solid'
+					style.width = this.$uv.addUnit(this.length)
+					if (this.hairline) style.transform = 'scaleY(0.5)'
+				} else {
+					// 如果是竖向线条,边框宽度为1px,再通过transform缩小一半,就是0.5px了
+					style.borderLeftWidth = '1px'
+					style.borderLeftStyle = this.dashed ? 'dashed' : 'solid'
+					style.height = this.$uv.addUnit(this.length)
+					if (this.hairline) style.transform = 'scaleX(0.5)'
+				}
+				style.borderColor = this.color
+				return this.$uv.deepMerge(style, this.$uv.addStyle(this.customStyle))
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.uv-line {
+		/* #ifndef APP-NVUE */
+		vertical-align: middle;
+		/* #endif */
+	}
+</style>

+ 87 - 0
uni_modules/uv-line/package.json

@@ -0,0 +1,87 @@
+{
+  "id": "uv-line",
+  "displayName": "uv-line 线条  全面兼容小程序、nvue、vue2、vue3等多端",
+  "version": "1.0.1",
+  "description": "uv-line 此组件一般用于显示一根线条,用于分隔内容块,有横向和竖向两种模式,且能设置0.5px线条,使用也很简单。",
+  "keywords": [
+    "uv-line",
+    "uvui",
+    "uv-ui",
+    "line",
+    "线条"
+],
+  "repository": "",
+  "engines": {
+    "HBuilderX": "^3.1.0"
+  },
+  "dcloudext": {
+    "type": "component-vue",
+    "sale": {
+      "regular": {
+        "price": "0.00"
+      },
+      "sourcecode": {
+        "price": "0.00"
+      }
+    },
+    "contact": {
+      "qq": ""
+    },
+    "declaration": {
+    	"ads": "无",
+    	"data": "插件不采集任何数据",
+    	"permissions": "无"
+    },
+    "npmurl": ""
+  },
+  "uni_modules": {
+    "dependencies": [
+			"uv-ui-tools"
+		],
+    "encrypt": [],
+    "platforms": {
+			"cloud": {
+				"tcb": "y",
+				"aliyun": "y"
+			},
+			"client": {
+				"Vue": {
+					"vue2": "y",
+					"vue3": "y"
+				},
+				"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",
+					"飞书": "u",
+					"京东": "u"
+				},
+				"快应用": {
+					"华为": "u",
+					"联盟": "u"
+				}
+			}
+		}
+  }
+}

+ 11 - 0
uni_modules/uv-line/readme.md

@@ -0,0 +1,11 @@
+## Line 线条
+
+> **组件名:uv-line**
+
+此组件一般用于显示一根线条,用于分隔内容块,有横向和竖向两种模式,且能设置0.5px线条,使用也很简单。
+
+### <a href="https://www.uvui.cn/components/line.html" target="_blank">查看文档</a>
+
+### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+#### 如使用过程中有任何问题,或者您对uv-ui有一些好的建议,欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a>、<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>

+ 9 - 0
uni_modules/uv-loading-icon/changelog.md

@@ -0,0 +1,9 @@
+## 1.0.3(2023-08-14)
+1. 新增参数textStyle,自定义文本样式
+## 1.0.2(2023-06-27)
+优化
+## 1.0.1(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.0(2023-05-10)
+1. 新增uv-loading-icon组件

+ 67 - 0
uni_modules/uv-loading-icon/components/uv-loading-icon/props.js

@@ -0,0 +1,67 @@
+export default {
+	props: {
+		// 是否显示组件
+		show: {
+			type: Boolean,
+			default: true
+		},
+		// 颜色
+		color: {
+			type: String,
+			default: '#909193'
+		},
+		// 提示文字颜色
+		textColor: {
+			type: String,
+			default: '#909193'
+		},
+		// 文字和图标是否垂直排列
+		vertical: {
+			type: Boolean,
+			default: false
+		},
+		// 模式选择,circle-圆形,spinner-花朵形,semicircle-半圆形
+		mode: {
+			type: String,
+			default: 'spinner'
+		},
+		// 图标大小,单位默认px
+		size: {
+			type: [String, Number],
+			default: 24
+		},
+		// 文字大小
+		textSize: {
+			type: [String, Number],
+			default: 15
+		},
+		// 文字样式
+		textStyle: {
+			type: Object,
+			default () {
+				return {}
+			}
+		},
+		// 文字内容
+		text: {
+			type: [String, Number],
+			default: ''
+		},
+		// 动画模式 https://www.runoob.com/cssref/css3-pr-animation-timing-function.html
+		timingFunction: {
+			type: String,
+			default: 'linear'
+		},
+		// 动画执行周期时间
+		duration: {
+			type: [String, Number],
+			default: 1200
+		},
+		// mode=circle时的暗边颜色
+		inactiveColor: {
+			type: String,
+			default: ''
+		},
+		...uni.$uv?.props?.loadingIcon
+	}
+}

+ 347 - 0
uni_modules/uv-loading-icon/components/uv-loading-icon/uv-loading-icon.vue

@@ -0,0 +1,347 @@
+<template>
+	<view
+		class="uv-loading-icon"
+		:style="[$uv.addStyle(customStyle)]"
+		:class="[vertical && 'uv-loading-icon--vertical']"
+		v-if="show"
+	>
+		<view
+			v-if="!webviewHide"
+			class="uv-loading-icon__spinner"
+			:class="[`uv-loading-icon__spinner--${mode}`]"
+			ref="ani"
+			:style="{
+				color: color,
+				width: $uv.addUnit(size),
+				height: $uv.addUnit(size),
+				borderTopColor: color,
+				borderBottomColor: otherBorderColor,
+				borderLeftColor: otherBorderColor,
+				borderRightColor: otherBorderColor,
+				'animation-duration': `${duration}ms`,
+				'animation-timing-function': mode === 'semicircle' || mode === 'circle' ? timingFunction : ''
+			}"
+		>
+			<block v-if="mode === 'spinner'">
+				<!-- #ifndef APP-NVUE -->
+				<view
+					v-for="(item, index) in array12"
+					:key="index"
+					class="uv-loading-icon__dot"
+				>
+				</view>
+				<!-- #endif -->
+				<!-- #ifdef APP-NVUE -->
+				<!-- 此组件内部图标部分无法设置宽高,即使通过width和height配置了也无效 -->
+				<loading-indicator
+					v-if="!webviewHide"
+					class="uv-loading-indicator"
+					:animating="true"
+					:style="{
+						color: color,
+						width: $uv.addUnit(size),
+						height: $uv.addUnit(size)
+					}"
+				/>
+				<!-- #endif -->
+			</block>
+		</view>
+		<text
+			v-if="text"
+			class="uv-loading-icon__text"
+			:style="[{
+				fontSize: $uv.addUnit(textSize),
+				color: textColor,
+			},$uv.addStyle(textStyle)]"
+		>{{text}}</text>
+	</view>
+</template>
+
+<script>
+	import { colorGradient } from '@/uni_modules/uv-ui-tools/libs/function/colorGradient.js'
+	import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+	import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+	import props from './props.js';
+	// #ifdef APP-NVUE
+	const animation = weex.requireModule('animation');
+	// #endif
+	/**
+	 * loading 加载动画
+	 * @description 警此组件为一个小动画,目前用在uvui的loadmore加载更多和switch开关等组件的正在加载状态场景。
+	 * @tutorial https://www.uvui.cn/components/loading.html
+	 * @property {Boolean}			show			是否显示组件  (默认 true)
+	 * @property {String}			color			动画活动区域的颜色,只对 mode = flower 模式有效(默认#909193)
+	 * @property {String}			textColor		提示文本的颜色(默认#909193)
+	 * @property {Boolean}			vertical		文字和图标是否垂直排列 (默认 false )
+	 * @property {String}			mode			模式选择,见官网说明(默认 'circle' )
+	 * @property {String | Number}	size			加载图标的大小,单位px (默认 24 )
+	 * @property {String | Number}	textSize		文字大小(默认 15 )
+	 * @property {String | Number}	text			文字内容 
+	 * @property {Object}	textStyle 文字样式
+	 * @property {String}			timingFunction	动画模式 (默认 'ease-in-out' )
+	 * @property {String | Number}	duration		动画执行周期时间(默认 1200)
+	 * @property {String}			inactiveColor	mode=circle时的暗边颜色 
+	 * @property {Object}			customStyle		定义需要用到的外部样式
+	 * @example <uv-loading mode="circle"></uv-loading>
+	 */
+	export default {
+		name: 'uv-loading-icon',
+		mixins: [mpMixin, mixin, props],
+		data() {
+			return {
+				// Array.form可以通过一个伪数组对象创建指定长度的数组
+				// https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/from
+				array12: Array.from({
+					length: 12
+				}),
+				// 这里需要设置默认值为360,否则在安卓nvue上,会延迟一个duration周期后才执行
+				// 在iOS nvue上,则会一开始默认执行两个周期的动画
+				aniAngel: 360, // 动画旋转角度
+				webviewHide: false, // 监听webview的状态,如果隐藏了页面,则停止动画,以免性能消耗
+				loading: false, // 是否运行中,针对nvue使用
+			}
+		},
+		computed: {
+			// 当为circle类型时,给其另外三边设置一个更轻一些的颜色
+			// 之所以需要这么做的原因是,比如父组件传了color为红色,那么需要另外的三个边为浅红色
+			// 而不能是固定的某一个其他颜色(因为这个固定的颜色可能浅蓝,导致效果没有那么细腻良好)
+			otherBorderColor() {
+				const lightColor = colorGradient(this.color, '#ffffff', 100)[80]
+				if (this.mode === 'circle') {
+					return this.inactiveColor ? this.inactiveColor : lightColor
+				} else {
+					return 'transparent'
+				}
+			}
+		},
+		watch: {
+			show(n) {
+				// nvue中,show为true,且为非loading状态,就重新执行动画模块
+				// #ifdef APP-NVUE
+				if (n && !this.loading) {
+					setTimeout(() => {
+						this.startAnimate()
+					}, 30)
+				}
+				// #endif
+			}
+		},
+		mounted() {
+			this.init()
+		},
+		methods: {
+			init() {
+				setTimeout(() => {
+					// #ifdef APP-NVUE
+					this.show && this.nvueAnimate()
+					// #endif
+					// #ifdef APP-PLUS 
+					this.show && this.addEventListenerToWebview()
+					// #endif
+				}, 20)
+			},
+			// 监听webview的显示与隐藏
+			addEventListenerToWebview() {
+				// webview的堆栈
+				const pages = getCurrentPages()
+				// 当前页面
+				const page = pages[pages.length - 1]
+				// 当前页面的webview实例
+				const currentWebview = page.$getAppWebview()
+				// 监听webview的显示与隐藏,从而停止或者开始动画(为了性能)
+				currentWebview.addEventListener('hide', () => {
+					this.webviewHide = true
+				})
+				currentWebview.addEventListener('show', () => {
+					this.webviewHide = false
+				})
+			},
+			// #ifdef APP-NVUE
+			nvueAnimate() {
+				// nvue下,非spinner类型时才需要旋转,因为nvue的spinner类型,使用了weex的
+				// loading-indicator组件,自带旋转功能
+				this.mode !== 'spinner' && this.startAnimate()
+			},
+			// 执行nvue的animate模块动画
+			startAnimate() {
+				this.loading = true
+				const ani = this.$refs.ani
+				if (!ani) return
+				animation.transition(ani, {
+					// 进行角度旋转
+					styles: {
+						transform: `rotate(${this.aniAngel}deg)`,
+						transformOrigin: 'center center'
+					},
+					duration: this.duration,
+					timingFunction: this.timingFunction,
+					// delay: 10
+				}, () => {
+					// 每次增加360deg,为了让其重新旋转一周
+					this.aniAngel += 360
+					// 动画结束后,继续循环执行动画,需要同时判断webviewHide变量
+					// nvue安卓,页面隐藏后依然会继续执行startAnimate方法
+					this.show && !this.webviewHide ? this.startAnimate() : this.loading = false
+				})
+			}
+			// #endif
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	@import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+	@import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
+	$uv-loading-icon-color: #c8c9cc !default;
+	$uv-loading-icon-text-margin-left:4px !default;
+	$uv-loading-icon-text-color:$uv-content-color !default;
+	$uv-loading-icon-text-font-size:14px !default;
+	$uv-loading-icon-text-line-height:20px !default;
+	$uv-loading-width:30px !default;
+	$uv-loading-height:30px !default;
+	$uv-loading-max-width:100% !default;
+	$uv-loading-max-height:100% !default;
+	$uv-loading-semicircle-border-width: 2px !default;
+	$uv-loading-semicircle-border-color:transparent !default;
+	$uv-loading-semicircle-border-top-right-radius: 100px !default;
+	$uv-loading-semicircle-border-top-left-radius: 100px !default;
+	$uv-loading-semicircle-border-bottom-left-radius: 100px !default;
+	$uv-loading-semicircle-border-bottom-right-radiu: 100px !default;
+	$uv-loading-semicircle-border-style: solid !default;
+	$uv-loading-circle-border-top-right-radius: 100px !default;
+	$uv-loading-circle-border-top-left-radius: 100px !default;
+	$uv-loading-circle-border-bottom-left-radius: 100px !default;
+	$uv-loading-circle-border-bottom-right-radiu: 100px !default;
+	$uv-loading-circle-border-width:2px !default;
+	$uv-loading-circle-border-top-color:#e5e5e5 !default;
+	$uv-loading-circle-border-right-color:$uv-loading-circle-border-top-color !default;
+	$uv-loading-circle-border-bottom-color:$uv-loading-circle-border-top-color !default;
+	$uv-loading-circle-border-left-color:$uv-loading-circle-border-top-color !default;
+	$uv-loading-circle-border-style:solid !default;
+	$uv-loading-icon-host-font-size:0px !default;
+	$uv-loading-icon-host-line-height:1 !default;
+	$uv-loading-icon-vertical-margin:6px 0 0 !default;
+	$uv-loading-icon-dot-top:0 !default;
+	$uv-loading-icon-dot-left:0 !default;
+	$uv-loading-icon-dot-width:100% !default;
+	$uv-loading-icon-dot-height:100% !default;
+	$uv-loading-icon-dot-before-width:2px !default;
+	$uv-loading-icon-dot-before-height:25% !default;
+	$uv-loading-icon-dot-before-margin:0 auto !default;
+	$uv-loading-icon-dot-before-background-color:currentColor !default;
+	$uv-loading-icon-dot-before-border-radius:40% !default;
+
+	.uv-loading-icon {
+		/* #ifndef APP-NVUE */
+		// display: inline-flex;
+		/* #endif */
+		flex-direction: row;
+		align-items: center;
+		justify-content: center;
+		color: $uv-loading-icon-color;
+
+		&__text {
+			margin-left: $uv-loading-icon-text-margin-left;
+			color: $uv-loading-icon-text-color;
+			font-size: $uv-loading-icon-text-font-size;
+			line-height: $uv-loading-icon-text-line-height;
+		}
+
+		&__spinner {
+			width: $uv-loading-width;
+			height: $uv-loading-height;
+			position: relative;
+			/* #ifndef APP-NVUE */
+			box-sizing: border-box;
+			max-width: $uv-loading-max-width;
+			max-height: $uv-loading-max-height;
+			animation: uv-rotate 1s linear infinite;
+			/* #endif */
+		}
+
+		&__spinner--semicircle {
+			border-width: $uv-loading-semicircle-border-width;
+			border-color: $uv-loading-semicircle-border-color;
+			border-top-right-radius: $uv-loading-semicircle-border-top-right-radius;
+			border-top-left-radius: $uv-loading-semicircle-border-top-left-radius;
+			border-bottom-left-radius: $uv-loading-semicircle-border-bottom-left-radius;
+			border-bottom-right-radius: $uv-loading-semicircle-border-bottom-right-radiu;
+			border-style: $uv-loading-semicircle-border-style;
+		}
+
+		&__spinner--circle {
+			border-top-right-radius: $uv-loading-circle-border-top-right-radius;
+			border-top-left-radius: $uv-loading-circle-border-top-left-radius;
+			border-bottom-left-radius: $uv-loading-circle-border-bottom-left-radius;
+			border-bottom-right-radius: $uv-loading-circle-border-bottom-right-radiu;
+			border-width: $uv-loading-circle-border-width;
+			border-top-color: $uv-loading-circle-border-top-color;
+			border-right-color: $uv-loading-circle-border-right-color;
+			border-bottom-color: $uv-loading-circle-border-bottom-color;
+			border-left-color: $uv-loading-circle-border-left-color;
+			border-style: $uv-loading-circle-border-style;
+		}
+
+		&--vertical {
+			flex-direction: column
+		}
+	}
+
+	/* #ifndef APP-NVUE */
+	:host {
+		font-size: $uv-loading-icon-host-font-size;
+		line-height: $uv-loading-icon-host-line-height;
+	}
+
+	.uv-loading-icon {
+		&__spinner--spinner {
+			animation-timing-function: steps(12)
+		}
+
+		&__text:empty {
+			display: none
+		}
+
+		&--vertical &__text {
+			margin: $uv-loading-icon-vertical-margin;
+			color: $uv-content-color;
+		}
+
+		&__dot {
+			position: absolute;
+			top: $uv-loading-icon-dot-top;
+			left: $uv-loading-icon-dot-left;
+			width: $uv-loading-icon-dot-width;
+			height: $uv-loading-icon-dot-height;
+
+			&:before {
+				display: block;
+				width: $uv-loading-icon-dot-before-width;
+				height: $uv-loading-icon-dot-before-height;
+				margin: $uv-loading-icon-dot-before-margin;
+				background-color: $uv-loading-icon-dot-before-background-color;
+				border-radius: $uv-loading-icon-dot-before-border-radius;
+				content: " "
+			}
+		}
+	}
+
+	@for $i from 1 through 12 {
+		.uv-loading-icon__dot:nth-of-type(#{$i}) {
+			transform: rotate($i * 30deg);
+			opacity: 1 - 0.0625 * ($i - 1);
+		}
+	}
+
+	@keyframes uv-rotate {
+		0% {
+			transform: rotate(0deg)
+		}
+
+		to {
+			transform: rotate(1turn)
+		}
+	}
+
+	/* #endif */
+</style>

+ 87 - 0
uni_modules/uv-loading-icon/package.json

@@ -0,0 +1,87 @@
+{
+  "id": "uv-loading-icon",
+  "displayName": "uv-loading-icon 加载动画 全面兼容vue3+2、app、h5、小程序等多端",
+  "version": "1.0.3",
+  "description": "此组件为一个小动画,目前用在uv-ui的uv-load-more加载更多等组件,还可以运用在项目中正在加载状态场景。",
+  "keywords": [
+    "uv-loading-icon",
+    "uvui",
+    "uv-ui",
+    "loading",
+    "加载动画"
+],
+  "repository": "",
+  "engines": {
+    "HBuilderX": "^3.1.0"
+  },
+  "dcloudext": {
+    "type": "component-vue",
+    "sale": {
+      "regular": {
+        "price": "0.00"
+      },
+      "sourcecode": {
+        "price": "0.00"
+      }
+    },
+    "contact": {
+      "qq": ""
+    },
+    "declaration": {
+    	"ads": "无",
+    	"data": "插件不采集任何数据",
+    	"permissions": "无"
+    },
+    "npmurl": ""
+  },
+  "uni_modules": {
+    "dependencies": [
+			"uv-ui-tools"
+		],
+    "encrypt": [],
+    "platforms": {
+			"cloud": {
+				"tcb": "y",
+				"aliyun": "y"
+			},
+			"client": {
+				"Vue": {
+					"vue2": "y",
+					"vue3": "y"
+				},
+				"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",
+					"飞书": "u",
+					"京东": "u"
+				},
+				"快应用": {
+					"华为": "u",
+					"联盟": "u"
+				}
+			}
+		}
+  }
+}

+ 19 - 0
uni_modules/uv-loading-icon/readme.md

@@ -0,0 +1,19 @@
+## LoadingIcon 加载动画
+
+> **组件名:uv-loading-icon**
+
+此组件为一个小动画,目前用在 `uv-ui` 的 `uv-load-more` 加载更多等组件,还可以运用在项目中正在加载状态场景。
+
+# <a href="https://www.uvui.cn/components/loadingIcon.html" target="_blank">查看文档</a>
+
+## [下载完整示例项目](https://ext.dcloud.net.cn/plugin?name=uv-ui) <small>(请不要 下载插件ZIP)</small>
+
+### [更多插件,请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+<a href="https://ext.dcloud.net.cn/plugin?name=uv-ui" target="_blank">
+
+![image](https://mp-a667b617-c5f1-4a2d-9a54-683a67cff588.cdn.bspapp.com/uv-ui/banner.png)
+
+</a>
+
+#### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>

+ 9 - 0
uni_modules/uv-overlay/changelog.md

@@ -0,0 +1,9 @@
+## 1.0.3(2023-07-02)
+uv-overlay  由于弹出层uv-transition的修改,组件内部做了相应的修改,参数不变。
+## 1.0.2(2023-06-29)
+1. 优化,H5端禁止穿透滚动
+## 1.0.1(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.0(2023-05-10)
+1. 新增uv-overlay组件

+ 25 - 0
uni_modules/uv-overlay/components/uv-overlay/props.js

@@ -0,0 +1,25 @@
+export default {
+	props: {
+		// 是否显示遮罩
+		show: {
+			type: Boolean,
+			default: false
+		},
+		// 层级z-index
+		zIndex: {
+			type: [String, Number],
+			default: 10070
+		},
+		// 遮罩的过渡时间,单位为ms
+		duration: {
+			type: [String, Number],
+			default: 300
+		},
+		// 不透明度值,当做rgba的第四个参数
+		opacity: {
+			type: [String, Number],
+			default: 0.5
+		},
+		...uni.$uv?.props?.overlay
+	}
+}

+ 85 - 0
uni_modules/uv-overlay/components/uv-overlay/uv-overlay.vue

@@ -0,0 +1,85 @@
+<template>
+	<uv-transition
+	  :show="show"
+		mode="fade"
+	  custom-class="uv-overlay"
+	  :duration="duration"
+	  :custom-style="overlayStyle"
+	  @click="clickHandler"
+		@touchmove.stop.prevent="clear"
+	>
+		<slot />
+	</uv-transition>
+</template>
+
+<script>
+	import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+	import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+	import props from './props.js';
+
+	/**
+	 * overlay 遮罩
+	 * @description 创建一个遮罩层,用于强调特定的页面元素,并阻止用户对遮罩下层的内容进行操作,一般用于弹窗场景
+	 * @tutorial https://www.uvui.cn/components/overlay.html
+	 * @property {Boolean}			show		是否显示遮罩(默认 false )
+	 * @property {String | Number}	zIndex		zIndex 层级(默认 10070 )
+	 * @property {String | Number}	duration	动画时长,单位毫秒(默认 300 )
+	 * @property {String | Number}	opacity		不透明度值,当做rgba的第四个参数 (默认 0.5 )
+	 * @property {Object}			customStyle	定义需要用到的外部样式
+	 * @event {Function} click 点击遮罩发送事件
+	 * @example <uv-overlay :show="show" @click="show = false"></uv-overlay>
+	 */
+	export default {
+		name: "uv-overlay",
+		emits: ['click'],
+		mixins: [mpMixin, mixin, props],
+		watch: {
+			show(newVal){
+				// #ifdef H5
+				if(newVal){
+					document.querySelector('body').style.overflow = 'hidden';
+				}else{
+					document.querySelector('body').style.overflow = '';
+				}
+				// #endif
+			}
+		},
+		computed: {
+			overlayStyle() {
+				const style = {
+					position: 'fixed',
+					top: 0,
+					left: 0,
+					right: 0,
+					zIndex: this.zIndex,
+					bottom: 0,
+					'background-color': `rgba(0, 0, 0, ${this.opacity})`
+				}
+				return this.$uv.deepMerge(style, this.$uv.addStyle(this.customStyle))
+			}
+		},
+		methods: {
+			clickHandler() {
+				this.$emit('click')
+			},
+			clear() {}
+		}
+	}
+</script>
+<style lang="scss" scoped>
+/* #ifndef APP-NVUE */
+$uv-overlay-top:0 !default;
+$uv-overlay-left:0 !default;
+$uv-overlay-width:100% !default;
+$uv-overlay-height:100% !default;
+$uv-overlay-background-color:rgba(0, 0, 0, .7) !default;
+.uv-overlay {
+	position: fixed;
+	top:$uv-overlay-top;
+	left:$uv-overlay-left;
+	width: $uv-overlay-width;
+	height:$uv-overlay-height;
+	background-color:$uv-overlay-background-color;
+}
+/* #endif */
+</style>

+ 88 - 0
uni_modules/uv-overlay/package.json

@@ -0,0 +1,88 @@
+{
+  "id": "uv-overlay",
+  "displayName": "uv-overlay 遮罩层  全面兼容小程序、nvue、vue2、vue3等多端",
+  "version": "1.0.3",
+  "description": "uv-overlay 创建一个遮罩层,用于强调特定的页面元素,并阻止用户对遮罩下层的内容进行操作,一般用于弹窗场景,uv-popup、uv-toast、uv-tooltip等组件就是用了该组件。",
+  "keywords": [
+    "uv-overlay",
+    "uvui",
+    "uv-ui",
+    "overlay",
+    "遮罩层"
+],
+  "repository": "",
+  "engines": {
+    "HBuilderX": "^3.1.0"
+  },
+  "dcloudext": {
+    "type": "component-vue",
+    "sale": {
+      "regular": {
+        "price": "0.00"
+      },
+      "sourcecode": {
+        "price": "0.00"
+      }
+    },
+    "contact": {
+      "qq": ""
+    },
+    "declaration": {
+    	"ads": "无",
+    	"data": "插件不采集任何数据",
+    	"permissions": "无"
+    },
+    "npmurl": ""
+  },
+  "uni_modules": {
+    "dependencies": [
+			"uv-ui-tools",
+			"uv-transition"
+		],
+    "encrypt": [],
+    "platforms": {
+			"cloud": {
+				"tcb": "y",
+				"aliyun": "y"
+			},
+			"client": {
+				"Vue": {
+					"vue2": "y",
+					"vue3": "y"
+				},
+				"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",
+					"飞书": "u",
+					"京东": "u"
+				},
+				"快应用": {
+					"华为": "u",
+					"联盟": "u"
+				}
+			}
+		}
+  }
+}

+ 11 - 0
uni_modules/uv-overlay/readme.md

@@ -0,0 +1,11 @@
+## Overlay 遮罩层
+
+> **组件名:uv-overlay**
+
+创建一个遮罩层,用于强调特定的页面元素,并阻止用户对遮罩下层的内容进行操作,一般用于弹窗场景,uv-popup、uv-toast、uv-tooltip等组件就是用了该组件。
+
+### <a href="https://www.uvui.cn/components/overlay.html" target="_blank">查看文档</a>
+
+### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+#### 如使用过程中有任何问题,或者您对uv-ui有一些好的建议,欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a>、<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>

+ 33 - 0
uni_modules/uv-picker/changelog.md

@@ -0,0 +1,33 @@
+## 1.0.14(2023-12-29)
+1. 修复上个版本引出的BUG
+## 1.0.13(2023-12-26)
+1. 修复抖音小程序滚到底不触发change的BUG
+## 1.0.12(2023-11-20)
+1. 修复issues反馈的问题uv-picker在组合式API的自定义组件中,columns动态赋值无法显示选项:https://gitee.com/climblee/uv-ui/issues/I8H0GQ
+## 1.0.11(2023-10-11)
+1. 将immediate-change默认值改为true,该值在于change回调的及时性,微信小程序生效
+## 1.0.10(2023-08-25)
+1. 增加round属性设置弹窗圆角,默认为0
+## 1.0.9(2023-08-24)
+1. 修复cli项目不返回值的问题
+## 1.0.8(2023-08-04)
+1. 优化
+## 1.0.7(2023-08-02)
+1. 改组件中删除uv-toolbar组件,请单独下载uv-toolbar组件
+## 1.0.6(2023-07-02)
+uv-picker  由于弹出层uv-popup的修改,打开和关闭方法更改,详情参考文档:https://www.uvui.cn/components/picker.html
+## 1.0.5(2023-06-26)
+1. 增加color参数
+2. 增加activeColor参数
+## 1.0.4(2023-06-15)
+1. 修改支付宝报错的BUG
+## 1.0.3(2023-06-12)
+1. setColumnValues的使用统一化,避免某些平台报错
+2. 取消change回调回传的组件实例,直接统一通过ref的方式调取setColumnValues方法
+## 1.0.2(2023-05-23)
+1. uv-toolbar组件新增下边框属性 
+## 1.0.1(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.0(2023-05-10)
+uv-picker 选择器

+ 95 - 0
uni_modules/uv-picker/components/uv-picker/props.js

@@ -0,0 +1,95 @@
+export default {
+	props: {
+		// 是否展示顶部的操作栏
+		showToolbar: {
+			type: Boolean,
+			default: true
+		},
+		// 顶部标题
+		title: {
+			type: String,
+			default: ''
+		},
+		// 弹窗圆角
+		round: {
+			type: [String, Number],
+			default: 0
+		},
+		// 对象数组,设置每一列的数据
+		columns: {
+			type: Array,
+			default: () => []
+		},
+		// 是否显示加载中状态
+		loading: {
+			type: Boolean,
+			default: false
+		},
+		// 各列中,单个选项的高度
+		itemHeight: {
+			type: [String, Number],
+			default: 44
+		},
+		// 取消按钮的文字
+		cancelText: {
+			type: String,
+			default: '取消'
+		},
+		// 确认按钮的文字
+		confirmText: {
+			type: String,
+			default: '确定'
+		},
+		// 取消按钮的颜色
+		cancelColor: {
+			type: String,
+			default: '#909193'
+		},
+		// 确认按钮的颜色
+		confirmColor: {
+			type: String,
+			default: '#3c9cff'
+		},
+		// 文字颜色
+		color: {
+			type: String,
+			default: ''
+		},
+		// 选中文字的颜色
+		activeColor: {
+			type: String,
+			default: ''
+		},
+		// 每列中可见选项的数量
+		visibleItemCount: {
+			type: [String, Number],
+			default: 5
+		},
+		// 选项对象中,需要展示的属性键名
+		keyName: {
+			type: String,
+			default: 'text'
+		},
+		// 是否允许点击遮罩关闭选择器
+		closeOnClickOverlay: {
+			type: Boolean,
+			default: true
+		},
+		// 是否允许点击确认关闭选择器
+		closeOnClickConfirm: {
+			type: Boolean,
+			default: true
+		},
+		// 各列的默认索引
+		defaultIndex: {
+			type: Array,
+			default: () => [],
+		},
+		// 是否在手指松开时立即触发 change 事件。若不开启则会在滚动动画结束后触发 change 事件,只在微信2.21.1及以上有效
+		immediateChange: {
+			type: Boolean,
+			default: true
+		},
+		...uni.$uv?.props?.picker
+	}
+}

+ 330 - 0
uni_modules/uv-picker/components/uv-picker/uv-picker.vue

@@ -0,0 +1,330 @@
+<template>
+	<uv-popup
+		ref="pickerPopup"
+		mode="bottom"
+		:round="round"
+		:close-on-click-overlay="closeOnClickOverlay"
+		@change="popupChange"
+	>
+		<view class="uv-picker">
+			<uv-toolbar
+				v-if="showToolbar"
+				:cancelColor="cancelColor"
+				:confirmColor="confirmColor"
+				:cancelText="cancelText"
+				:confirmText="confirmText"
+				:title="title"
+				@cancel="cancel"
+				@confirm="confirm"
+			></uv-toolbar>
+			<!-- #ifdef MP-TOUTIAO -->
+			<picker-view
+				class="uv-picker__view"
+				:indicatorStyle="`height: ${$uv.addUnit(itemHeight)}`"
+				:value="innerIndex"
+				:immediateChange="immediateChange"
+				:style="{
+					height: `${$uv.addUnit(visibleItemCount * itemHeight)}`
+				}"
+				@pickend="changeHandler"
+			>
+			<!-- #endif -->
+			<!-- #ifndef MP-TOUTIAO -->
+			<picker-view
+				class="uv-picker__view"
+				:indicatorStyle="`height: ${$uv.addUnit(itemHeight)}`"
+				:value="innerIndex"
+				:immediateChange="immediateChange"
+				:style="{
+					height: `${$uv.addUnit(visibleItemCount * itemHeight)}`
+				}"
+				@change="changeHandler"
+			>
+			<!-- #endif -->
+			<!-- @pickend在这里为了解决抖音等滚到底不触发change兼容性问题 -->
+				<picker-view-column
+					v-for="(item, index) in innerColumns"
+					:key="index"
+					class="uv-picker__view__column"
+				>
+					<text
+						v-if="$uv.test.array(item)"
+						class="uv-picker__view__column__item uv-line-1"
+						v-for="(item1, index1) in item"
+						:key="index1"
+						:style="[{
+								height: $uv.addUnit(itemHeight),
+								lineHeight: $uv.addUnit(itemHeight),
+								fontWeight: index1 === innerIndex[index] ? 'bold' : 'normal'
+							},textStyle(index,index1)]"
+					>{{ getItemText(item1) }}</text>
+				</picker-view-column>
+			</picker-view>
+			<view
+				v-if="loading"
+				class="uv-picker--loading"
+			>
+				<uv-loading-icon mode="circle"></uv-loading-icon>
+			</view>
+		</view>
+	</uv-popup>
+</template>
+
+<script>
+/**
+ * uv-picker
+ * @description 选择器
+ * @property {Boolean}			showToolbar			是否显示顶部的操作栏(默认 true )
+ * @property {String}			title				顶部标题
+ * @property {Array}			columns				对象数组,设置每一列的数据
+ * @property {Boolean}			loading				是否显示加载中状态(默认 false )
+ * @property {String | Number}	itemHeight			各列中,单个选项的高度(默认 44 )
+ * @property {String}			cancelText			取消按钮的文字(默认 '取消' )
+ * @property {String}			confirmText			确认按钮的文字(默认 '确定' )
+ * @property {String}			cancelColor			取消按钮的颜色(默认 '#909193' )
+ * @property {String}			confirmColor		确认按钮的颜色(默认 '#3c9cff' )
+ * @property {String}			color		文字颜色(默认 '' )
+ * @property {String}			activeColor		选中文字的颜色(默认 '' )
+ * @property {String | Number}	visibleItemCount	每列中可见选项的数量(默认 5 )
+ * @property {String}			keyName				选项对象中,需要展示的属性键名(默认 'text' )
+ * @property {Boolean}			closeOnClickOverlay	是否允许点击遮罩关闭选择器(默认 false )
+ * @property {Array}			defaultIndex		各列的默认索引
+ * @property {Boolean}			immediateChange		是否在手指松开时立即触发change事件(默认 false )
+ * @event {Function} close		关闭选择器时触发
+ * @event {Function} cancel		点击取消按钮触发
+ * @event {Function} change		当选择值变化时触发
+ * @event {Function} confirm	点击确定按钮,返回当前选择的值
+ */
+import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+import props from './props.js';
+export default {
+	name: 'uv-picker',
+	emits: ['confirm','cancel','close','change'],
+	mixins: [mpMixin, mixin, props],
+	computed: {
+		// 为了解决支付宝不生效
+		textStyle(){
+			return (index,index1) => {
+				const style = {};
+				// #ifndef APP-NVUE 
+				style.display = 'block';
+				// #endif
+				if(this.color) {
+					style.color = this.color;
+				}
+				if(this.activeColor && index1 === this.innerIndex[index]) {
+					style.color = this.activeColor;
+				}
+				return style;
+			}
+		}
+	},
+	data() {
+		return {
+			// 上一次选择的列索引
+			lastIndex: [],
+			// 索引值 ,对应picker-view的value
+			innerIndex: [],
+			// 各列的值
+			innerColumns: [],
+			// 上一次的变化列索引
+			columnIndex: 0,
+		}
+	},
+	watch: {
+		// 监听默认索引的变化,重新设置对应的值
+		defaultIndex: {
+			immediate: true,
+			handler(n) {
+				this.setIndexs(n, true)
+			}
+		},
+		// 监听columns参数的变化
+		columns: {
+			deep: true,
+			immediate: true,
+			handler(n) {
+				this.setColumns(n)
+			}
+		},
+	},
+	methods: {
+		open() {
+			this.$refs.pickerPopup.open();
+		},
+		close() {
+			this.$refs.pickerPopup.close();
+		},
+		popupChange(e) {
+			if(!e.show) this.$emit('close');
+		},
+		// 获取item需要显示的文字,判别为对象还是文本
+		getItemText(item) {
+			if (this.$uv.test.object(item)) {
+				return item[this.keyName]
+			} else {
+				return item
+			}
+		},
+		// 点击工具栏的取消按钮
+		cancel() {
+			this.$emit('cancel');
+			this.close();
+		},
+		// 点击工具栏的确定按钮
+		confirm() {
+			// 在这里使用deepClone拷贝后,vue3会自动转换成原始对象,这样处理是因为cli项目可能出现不返回值的情况
+			this.$emit('confirm', this.$uv.deepClone({
+				indexs: this.innerIndex,
+				value: this.innerColumns.map((item, index) => item[this.innerIndex[index]]),
+				values: this.innerColumns
+			}));
+			if(this.closeOnClickConfirm) {
+				this.close();
+			}
+		},
+		// 选择器某一列的数据发生变化时触发
+		changeHandler(e) {
+			const {
+				value
+			} = e.detail
+			let index = 0,
+				columnIndex = 0
+			// 通过对比前后两次的列索引,得出当前变化的是哪一列
+			for (let i = 0; i < value.length; i++) {
+				let item = value[i]
+				if (item !== (this.lastIndex[i] || 0)) { // 把undefined转为合法假值0
+					// 设置columnIndex为当前变化列的索引
+					columnIndex = i
+					// index则为变化列中的变化项的索引
+					index = item
+					break // 终止循环,即使少一次循环,也是性能的提升
+				}
+			}
+			this.columnIndex = columnIndex
+			const values = this.innerColumns
+			// 将当前的各项变化索引,设置为"上一次"的索引变化值
+			this.setLastIndex(value)
+			this.setIndexs(value)
+
+			this.$emit('change', {
+				value: this.innerColumns.map((item, index) => item[value[index]]),
+				index,
+				indexs: value,
+				// values为当前变化列的数组内容
+				values,
+				columnIndex
+			})
+		},
+		// 设置index索引,此方法可被外部调用设置
+		setIndexs(index, setLastIndex) {
+			this.innerIndex = this.$uv.deepClone(index)
+			if (setLastIndex) {
+				this.setLastIndex(index)
+			}
+		},
+		// 记录上一次的各列索引位置
+		setLastIndex(index) {
+			// 当能进入此方法,意味着当前设置的各列默认索引,即为“上一次”的选中值,需要记录,是因为changeHandler中
+			// 需要拿前后的变化值进行对比,得出当前发生改变的是哪一列
+			this.lastIndex = this.$uv.deepClone(index)
+		},
+		// 设置对应列选项的所有值
+		setColumnValues(columnIndex, values) {
+			// 替换innerColumns数组中columnIndex索引的值为values,使用的是数组的splice方法
+			this.innerColumns.splice(columnIndex, 1, values)
+			// 拷贝一份原有的innerIndex做临时变量,将大于当前变化列的所有的列的默认索引设置为0
+			let tmpIndex = this.$uv.deepClone(this.innerIndex)
+			for (let i = 0; i < this.innerColumns.length; i++) {
+				if (i > this.columnIndex) {
+					tmpIndex[i] = 0
+				}
+			}
+			// 一次性赋值,不能单个修改,否则无效
+			this.setIndexs(tmpIndex)
+		},
+		// 获取对应列的所有选项
+		getColumnValues(columnIndex) {
+			// 进行同步阻塞,因为外部得到change事件之后,可能需要执行setColumnValues更新列的值
+			// 索引如果在外部change的回调中调用getColumnValues的话,可能无法得到变更后的列值,这里进行一定延时,保证值的准确性
+			(async () => {
+				await this.$uv.sleep()
+			})()
+			return this.innerColumns[columnIndex]
+		},
+		// 设置整体各列的columns的值
+		setColumns(columns) {
+			this.innerColumns = this.$uv.deepClone(columns)
+			// 如果在设置各列数据时,没有被设置默认的各列索引defaultIndex,那么用0去填充它,数组长度为列的数量
+			if (this.innerIndex.length === 0) {
+				this.innerIndex = new Array(columns.length).fill(0)
+			}
+		},
+		// 获取各列选中值对应的索引
+		getIndexs() {
+			return this.innerIndex
+		},
+		// 获取各列选中的值
+		getValues() {
+			// 进行同步阻塞,因为外部得到change事件之后,可能需要执行setColumnValues更新列的值
+			// 索引如果在外部change的回调中调用getValues的话,可能无法得到变更后的列值,这里进行一定延时,保证值的准确性
+			(async () => {
+				await this.$uv.sleep()
+			})()
+			return this.innerColumns.map((item, index) => item[this.innerIndex[index]])
+		}
+	},
+}
+</script>
+
+<style lang="scss" scoped>
+	$show-lines: 1;
+	@import '@/uni_modules/uv-ui-tools/libs/css/variable.scss';
+	@import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+	@import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
+	.uv-picker {
+		position: relative;
+
+		&__view {
+
+			&__column {
+				@include flex;
+				flex: 1;
+				justify-content: center;
+
+				&__item {
+					@include flex;
+					justify-content: center;
+					align-items: center;
+					font-size: 16px;
+					text-align: center;
+					/* #ifndef APP-NVUE */
+					display: block;
+					/* #endif */
+					color: $uv-main-color;
+
+					&--disabled {
+						/* #ifndef APP-NVUE */
+						cursor: not-allowed;
+						/* #endif */
+						opacity: 0.35;
+					}
+				}
+			}
+		}
+
+		&--loading {
+			position: absolute;
+			top: 0;
+			right: 0;
+			left: 0;
+			bottom: 0;
+			@include flex;
+			justify-content: center;
+			align-items: center;
+			background-color: rgba(255, 255, 255, 0.87);
+			z-index: 1000;
+		}
+	}
+</style>

+ 90 - 0
uni_modules/uv-picker/package.json

@@ -0,0 +1,90 @@
+{
+  "id": "uv-picker",
+  "displayName": "uv-picker 选择器 全面兼容vue3+2、app、h5、小程序等多端",
+  "version": "1.0.14",
+  "description": "uv-picker 此选择器用于单列,多列,多列联动的选择场景...",
+  "keywords": [
+    "uv-picker",
+    "uvui",
+    "uv-ui",
+    "picker",
+    "联动选择"
+],
+  "repository": "",
+  "engines": {
+    "HBuilderX": "^3.1.0"
+  },
+  "dcloudext": {
+    "type": "component-vue",
+    "sale": {
+      "regular": {
+        "price": "0.00"
+      },
+      "sourcecode": {
+        "price": "0.00"
+      }
+    },
+    "contact": {
+      "qq": ""
+    },
+    "declaration": {
+    	"ads": "无",
+    	"data": "插件不采集任何数据",
+    	"permissions": "无"
+    },
+    "npmurl": ""
+  },
+  "uni_modules": {
+    "dependencies": [
+			"uv-ui-tools",
+			"uv-popup",
+			"uv-loading-icon",
+			"uv-toolbar"
+		],
+    "encrypt": [],
+    "platforms": {
+			"cloud": {
+				"tcb": "y",
+				"aliyun": "y"
+			},
+			"client": {
+				"Vue": {
+					"vue2": "y",
+					"vue3": "y"
+				},
+				"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",
+					"飞书": "u",
+					"京东": "u"
+				},
+				"快应用": {
+					"华为": "u",
+					"联盟": "u"
+				}
+			}
+		}
+  }
+}

+ 21 - 0
uni_modules/uv-picker/readme.md

@@ -0,0 +1,21 @@
+## Picker 选择器
+
+> **组件名:uv-picker**
+
+此选择器用于单列,多列,多列联动的选择场景。
+
+`uv-datetime-picker`等组件也用到了该组件,功能完善,需要特别注意的是`columns`参数的形式是数组嵌套。
+
+# <a href="https://www.uvui.cn/components/picker.html" target="_blank">查看文档</a>
+
+## [下载完整示例项目](https://ext.dcloud.net.cn/plugin?name=uv-ui) <small>(请不要 下载插件ZIP)</small>
+
+### [更多插件,请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+<a href="https://ext.dcloud.net.cn/plugin?name=uv-ui" target="_blank">
+
+![image](https://mp-a667b617-c5f1-4a2d-9a54-683a67cff588.cdn.bspapp.com/uv-ui/banner.png)
+
+</a>
+
+#### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>

+ 18 - 0
uni_modules/uv-popup/changelog.md

@@ -0,0 +1,18 @@
+## 1.0.7(2023-11-20)
+修复issues问题:https://gitee.com/climblee/uv-ui/issues/I8HDLO
+## 1.0.6(2023-10-13)
+1. 优化vue,内容有背景色,设置圆角被遮挡的情况
+## 1.0.5(2023-09-10)
+1. 修复H5默认层级过高的问题
+2. 修复全局设置prop无效的问题
+## 1.0.4(2023-08-08)
+1. 修复修改zIndex不生效的BUG
+## 1.0.3(2023-07-02)
+uv-popup  弹出层,代码重构优化,性能翻倍,小程序体验性能更加,避免卡顿。打开和关闭方法更改,详情参考文档:https://www.uvui.cn/components/popup.html
+## 1.0.2(2023-06-11)
+1. 修复zIndex层级问题
+## 1.0.1(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.0(2023-05-10)
+1. 新增uv-popup组件

+ 45 - 0
uni_modules/uv-popup/components/uv-popup/keypress.js

@@ -0,0 +1,45 @@
+// #ifdef H5
+export default {
+  name: 'Keypress',
+  props: {
+    disable: {
+      type: Boolean,
+      default: false
+    }
+  },
+  mounted () {
+    const keyNames = {
+      esc: ['Esc', 'Escape'],
+      tab: 'Tab',
+      enter: 'Enter',
+      space: [' ', 'Spacebar'],
+      up: ['Up', 'ArrowUp'],
+      left: ['Left', 'ArrowLeft'],
+      right: ['Right', 'ArrowRight'],
+      down: ['Down', 'ArrowDown'],
+      delete: ['Backspace', 'Delete', 'Del']
+    }
+    const listener = ($event) => {
+      if (this.disable) {
+        return
+      }
+      const keyName = Object.keys(keyNames).find(key => {
+        const keyName = $event.key
+        const value = keyNames[key]
+        return value === keyName || (Array.isArray(value) && value.includes(keyName))
+      })
+      if (keyName) {
+        // 避免和其他按键事件冲突
+        setTimeout(() => {
+          this.$emit(keyName, {})
+        }, 0)
+      }
+    }
+    document.addEventListener('keyup', listener)
+    // this.$once('hook:beforeDestroy', () => {
+    //   document.removeEventListener('keyup', listener)
+    // })
+  },
+	render: () => {}
+}
+// #endif

+ 539 - 0
uni_modules/uv-popup/components/uv-popup/uv-popup.vue

@@ -0,0 +1,539 @@
+<template>
+	<view 
+		v-if="showPopup" 
+		class="uv-popup" 
+		:class="[popupClass, isDesktop ? 'fixforpc-z-index' : '']"
+		:style="[{zIndex: zIndex}]"
+	>
+		<view @touchstart="touchstart">
+			<!-- 遮罩层 -->
+			<uv-overlay
+				key="1"
+				v-if="maskShow && overlay"
+				:show="showTrans"
+				:duration="duration"
+				:custom-style="overlayStyle"
+				:opacity="overlayOpacity"
+			  :zIndex="zIndex"
+				@click="onTap"
+			></uv-overlay>
+			<uv-transition 
+				key="2" 
+				:mode="ani" 
+				name="content" 
+				:custom-style="transitionStyle" 
+				:duration="duration"
+				:show="showTrans" 
+				@click="onTap"
+			>
+				<view 
+					class="uv-popup__content" 
+					:style="[contentStyle]" 
+					:class="[popupClass]" 
+					@click="clear"
+				>
+					<uv-status-bar v-if="safeAreaInsetTop"></uv-status-bar>
+					<slot />
+					<uv-safe-bottom v-if="safeAreaInsetBottom"></uv-safe-bottom>
+					<view
+						v-if="closeable"
+						@tap.stop="close"
+						class="uv-popup__content__close"
+						:class="['uv-popup__content__close--' + closeIconPos]"
+						hover-class="uv-popup__content__close--hover"
+						hover-stay-time="150"
+					>
+						<uv-icon
+							name="close"
+							color="#909399"
+							size="18"
+							bold
+						></uv-icon>
+					</view>
+				</view>
+			</uv-transition>
+		</view>
+		<!-- #ifdef H5 -->
+		<keypress v-if="maskShow" @esc="onTap" />
+		<!-- #endif -->
+	</view>
+</template>
+
+<script>
+	// #ifdef H5
+	import keypress from './keypress.js'
+	// #endif
+	import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+	import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+	/**
+	* PopUp 弹出层
+	* @description 弹出层组件,为了解决遮罩弹层的问题
+	* @tutorial https://www.uvui.cn/components/popup.html
+	* @property {String} mode = [top|center|bottom|left|right] 弹出方式
+	* 	@value top 顶部弹出
+	* 	@value center 中间弹出
+	* 	@value bottom 底部弹出
+	* 	@value left		左侧弹出
+	* 	@value right  右侧弹出
+	* @property {Number} duration 动画时长,默认300
+	* @property {Boolean} overlay 是否显示遮罩,默认true
+	* @property {Boolean} overlayOpacity 遮罩透明度,默认0.5 
+	* @property {Object} overlayStyle 遮罩自定义样式
+	* @property {Boolean} closeOnClickOverlay = [true|false] 蒙版点击是否关闭弹窗,默认true
+	* @property {Number | String} zIndex 弹出层的层级
+	* @property {Boolean} safeAreaInsetTop 是否留出顶部安全区(状态栏高度),默认false
+	* @property {Boolean} safeAreaInsetBottom 是否为留出底部安全区适配,默认true
+	* @property {Boolean} closeable 是否显示关闭图标,默认false
+	* @property {Boolean} closeIconPos 自定义关闭图标位置,`top-left`-左上角,`top-right`-右上角,`bottom-left`-左下角,`bottom-right`-右下角,默认top-right
+	* @property {String}  bgColor 主窗口背景色
+	* @property {String}  maskBackgroundColor 蒙版颜色
+	* @property {Boolean} customStyle 自定义样式
+	* @event {Function} change 打开关闭弹窗触发,e={show: false}
+	* @event {Function} maskClick 点击遮罩触发
+	*/
+	export default {
+		name: 'uv-popup',
+		components: {
+			// #ifdef H5
+			keypress
+			// #endif
+		},
+		mixins: [mpMixin, mixin],
+		emits: ['change', 'maskClick'],
+		props: {
+			// 弹出层类型,可选值,top: 顶部弹出层;bottom:底部弹出层;center:全屏弹出层
+			// message: 消息提示 ; dialog : 对话框
+			mode: {
+				type: String,
+				default: 'center'
+			},
+			// 动画时长,单位ms
+			duration: {
+				type: [String, Number],
+				default: 300
+			},
+			// 层级
+			zIndex: {
+				type: [String, Number],
+				// #ifdef H5
+				default: 997
+				// #endif
+				// #ifndef H5
+				default: 10075
+				// #endif
+			},
+			bgColor: {
+				type: String,
+				default: '#ffffff'
+			},
+			safeArea: {
+				type: Boolean,
+				default: true
+			},
+			// 是否显示遮罩
+			overlay: {
+				type: Boolean,
+				default: true
+			},
+			// 点击遮罩是否关闭弹窗
+			closeOnClickOverlay: {
+				type: Boolean,
+				default: true
+			},
+			// 遮罩的透明度,0-1之间
+			overlayOpacity: {
+				type: [Number, String],
+				default: 0.4
+			},
+			// 自定义遮罩的样式
+			overlayStyle: {
+				type: [Object, String],
+				default: ''
+			},
+			// 是否为iPhoneX留出底部安全距离
+			safeAreaInsetBottom: {
+				type: Boolean,
+				default: true
+			},
+			// 是否留出顶部安全距离(状态栏高度)
+			safeAreaInsetTop: {
+				type: Boolean,
+				default: false
+			},
+			// 是否显示关闭图标
+			closeable: {
+				type: Boolean,
+				default: false
+			},
+			// 自定义关闭图标位置,top-left为左上角,top-right为右上角,bottom-left为左下角,bottom-right为右下角
+			closeIconPos: {
+				type: String,
+				default: 'top-right'
+			},
+			// mode=center,也即中部弹出时,是否使用缩放模式
+			zoom: {
+				type: Boolean,
+				default: true
+			},
+			round: {
+				type: [Number, String],
+				default: 0
+			},
+			...uni.$uv?.props?.popup
+		},
+		watch: {
+			/**
+			 * 监听type类型
+			 */
+			type: {
+				handler: function(type) {
+					if (!this.config[type]) return
+					this[this.config[type]](true)
+				},
+				immediate: true
+			},
+			isDesktop: {
+				handler: function(newVal) {
+					if (!this.config[newVal]) return
+					this[this.config[this.mode]](true)
+				},
+				immediate: true
+			},
+			// H5 下禁止底部滚动
+			showPopup(show) {
+				// #ifdef H5
+				// fix by mehaotian 处理 h5 滚动穿透的问题
+				document.getElementsByTagName('body')[0].style.overflow = show ? 'hidden' : 'visible'
+				// #endif
+			}
+		},
+		data() {
+			return {
+				ani: [],
+				showPopup: false,
+				showTrans: false,
+				popupWidth: 0,
+				popupHeight: 0,
+				config: {
+					top: 'top',
+					bottom: 'bottom',
+					center: 'center',
+					left: 'left',
+					right: 'right',
+					message: 'top',
+					dialog: 'center',
+					share: 'bottom'
+				},
+				transitionStyle: {
+					position: 'fixed',
+					left: 0,
+					right: 0
+				},
+				maskShow: true,
+				mkclick: true,
+				popupClass: this.isDesktop ? 'fixforpc-top' : 'top',
+				direction: ''
+			}
+		},
+		computed: {
+			isDesktop() {
+				return this.popupWidth >= 500 && this.popupHeight >= 500
+			},
+			bg() {
+				if (this.bgColor === '' || this.bgColor === 'none' || this.$uv.getPx(this.round)>0) {
+					return 'transparent'
+				}
+				return this.bgColor
+			},
+			contentStyle() {
+				const style = {};
+				if (this.bgColor) {
+					style.backgroundColor = this.bg
+				}
+				if(this.round) {
+					const value = this.$uv.addUnit(this.round)
+					const mode = this.direction?this.direction:this.mode
+					style.backgroundColor = this.bgColor
+					if(mode === 'top') {
+						style.borderBottomLeftRadius = value
+						style.borderBottomRightRadius = value
+					} else if(mode === 'bottom') {
+						style.borderTopLeftRadius = value
+						style.borderTopRightRadius = value
+					} else if(mode === 'center') {
+						style.borderRadius = value
+					} 
+				}
+				return this.$uv.deepMerge(style, this.$uv.addStyle(this.customStyle))
+			}
+		},
+		// #ifndef VUE3
+		// TODO vue2
+		destroyed() {
+			this.setH5Visible()
+		},
+		// #endif
+		// #ifdef VUE3
+		// TODO vue3
+		unmounted() {
+			this.setH5Visible()
+		},
+		// #endif
+		created() {
+			// TODO 处理 message 组件生命周期异常的问题
+			this.messageChild = null
+			// TODO 解决头条冒泡的问题
+			this.clearPropagation = false
+		},
+		methods: {
+			setH5Visible() {
+				// #ifdef H5
+				// fix by mehaotian 处理 h5 滚动穿透的问题
+				document.getElementsByTagName('body')[0].style.overflow = 'visible'
+				// #endif
+			},
+			/**
+			 * 公用方法,不显示遮罩层
+			 */
+			closeMask() {
+				this.maskShow = false
+			},
+			// TODO nvue 取消冒泡
+			clear(e) {
+				// #ifndef APP-NVUE
+				e.stopPropagation()
+				// #endif
+				this.clearPropagation = true
+			},
+
+			open(direction) {
+				// fix by mehaotian 处理快速打开关闭的情况
+				if (this.showPopup) {
+					return
+				}
+				let innerType = ['top', 'center', 'bottom', 'left', 'right', 'message', 'dialog', 'share']
+				if (!(direction && innerType.indexOf(direction) !== -1)) {
+					direction = this.mode
+				}else {
+					this.direction = direction;
+				}
+				if (!this.config[direction]) {
+					return this.$uv.error(`缺少类型:${direction}`);
+				}
+				this[this.config[direction]]()
+				this.$emit('change', {
+					show: true,
+					type: direction
+				})
+			},
+			close(type) {
+				this.showTrans = false
+				this.$emit('change', {
+					show: false,
+					type: this.mode
+				})
+				clearTimeout(this.timer)
+				// // 自定义关闭事件
+				this.timer = setTimeout(() => {
+					this.showPopup = false
+				}, 300)
+			},
+			// TODO 处理冒泡事件,头条的冒泡事件有问题 ,先这样兼容
+			touchstart() {
+				this.clearPropagation = false
+			},
+			onTap() {
+				if (this.clearPropagation) {
+					// fix by mehaotian 兼容 nvue
+					this.clearPropagation = false
+					return
+				}
+				this.$emit('maskClick')
+				if (!this.closeOnClickOverlay) return
+				this.close()
+			},
+			/**
+			 * 顶部弹出样式处理
+			 */
+			top(type) {
+				this.popupClass = this.isDesktop ? 'fixforpc-top' : 'top'
+				this.ani = ['slide-top']
+				this.transitionStyle = {
+					position: 'fixed',
+					zIndex: this.zIndex,
+					left: 0,
+					right: 0,
+					backgroundColor: this.bg
+				}
+				// TODO 兼容 type 属性 ,后续会废弃
+				if (type) return
+				this.showPopup = true
+				this.showTrans = true
+				this.$nextTick(() => {
+					if (this.messageChild && this.mode === 'message') {
+						this.messageChild.timerClose()
+					}
+				})
+			},
+			/**
+			 * 底部弹出样式处理
+			 */
+			bottom(type) {
+				this.popupClass = 'bottom'
+				this.ani = ['slide-bottom']
+				this.transitionStyle = {
+					position: 'fixed',
+					zIndex: this.zIndex,
+					left: 0,
+					right: 0,
+					bottom: 0,
+					backgroundColor: this.bg
+				}
+				// TODO 兼容 type 属性 ,后续会废弃
+				if (type) return
+				this.showPopup = true
+				this.showTrans = true
+			},
+			/**
+			 * 中间弹出样式处理
+			 */
+			center(type) {
+				this.popupClass = 'center'
+				this.ani = this.zoom?['zoom-in', 'fade']:['fade'];
+				this.transitionStyle = {
+					position: 'fixed',
+					zIndex: this.zIndex,
+					/* #ifndef APP-NVUE */
+					display: 'flex',
+					flexDirection: 'column',
+					/* #endif */
+					bottom: 0,
+					left: 0,
+					right: 0,
+					top: 0,
+					justifyContent: 'center',
+					alignItems: 'center'
+				}
+				// TODO 兼容 type 属性 ,后续会废弃
+				if (type) return
+				this.showPopup = true
+				this.showTrans = true
+			},
+			left(type) {
+				this.popupClass = 'left'
+				this.ani = ['slide-left']
+				this.transitionStyle = {
+					position: 'fixed',
+					zIndex: this.zIndex,
+					left: 0,
+					bottom: 0,
+					top: 0,
+					backgroundColor: this.bg,
+					/* #ifndef APP-NVUE */
+					display: 'flex',
+					flexDirection: 'column'
+					/* #endif */
+				}
+				// TODO 兼容 type 属性 ,后续会废弃
+				if (type) return
+				this.showPopup = true
+				this.showTrans = true
+			},
+			right(type) {
+				this.popupClass = 'right'
+				this.ani = ['slide-right']
+				this.transitionStyle = {
+					position: 'fixed',
+					zIndex: this.zIndex,
+					bottom: 0,
+					right: 0,
+					top: 0,
+					backgroundColor: this.bg,
+					/* #ifndef APP-NVUE */
+					display: 'flex',
+					flexDirection: 'column'
+					/* #endif */
+				}
+				// TODO 兼容 type 属性 ,后续会废弃
+				if (type) return
+				this.showPopup = true
+				this.showTrans = true
+			}
+		}
+	}
+</script>
+<style lang="scss" scoped>
+	.uv-popup {
+		position: fixed;
+		/* #ifndef APP-NVUE */
+		z-index: 99;
+
+		/* #endif */
+		&.top,
+		&.left,
+		&.right {
+			/* #ifdef H5 */
+			top: var(--window-top);
+			/* #endif */
+			/* #ifndef H5 */
+			top: 0;
+			/* #endif */
+		}
+
+		.uv-popup__content {
+			/* #ifndef APP-NVUE */
+			display: block;
+			overflow: hidden;
+			/* #endif */
+			position: relative;
+
+			&.left,
+			&.right {
+				/* #ifdef H5 */
+				padding-top: var(--window-top);
+				/* #endif */
+				/* #ifndef H5 */
+				padding-top: 0;
+				/* #endif */
+				flex: 1;
+			}
+			&__close {
+				position: absolute;
+
+				&--hover {
+					opacity: 0.4;
+				}
+			}
+			
+			&__close--top-left {
+				top: 15px;
+				left: 15px;
+			}
+			
+			&__close--top-right {
+				top: 15px;
+				right: 15px;
+			}
+			
+			&__close--bottom-left {
+				bottom: 15px;
+				left: 15px;
+			}
+			
+			&__close--bottom-right {
+				right: 15px;
+				bottom: 15px;
+			}
+		}
+	}
+
+	.fixforpc-z-index {
+		/* #ifndef APP-NVUE */
+		z-index: 999;
+		/* #endif */
+	}
+
+	.fixforpc-top {
+		top: 0;
+	}
+</style>

+ 92 - 0
uni_modules/uv-popup/package.json

@@ -0,0 +1,92 @@
+{
+  "id": "uv-popup",
+  "displayName": "uv-popup 弹出层 全面兼容vue3+2、app、h5、小程序等多端",
+  "version": "1.0.7",
+  "description": "uv-popup 弹出层容器,用于展示弹窗、信息提示等内容,支持上、下、左、右和中部弹出。组件只提供容器,内部内容由用户自定义。",
+  "keywords": [
+    "uv-popup",
+    "uvui",
+    "uv-ui",
+    "popup",
+    "弹出层"
+],
+  "repository": "",
+  "engines": {
+    "HBuilderX": "^3.1.0"
+  },
+  "dcloudext": {
+    "type": "component-vue",
+    "sale": {
+      "regular": {
+        "price": "0.00"
+      },
+      "sourcecode": {
+        "price": "0.00"
+      }
+    },
+    "contact": {
+      "qq": ""
+    },
+    "declaration": {
+    	"ads": "无",
+    	"data": "插件不采集任何数据",
+    	"permissions": "无"
+    },
+    "npmurl": ""
+  },
+  "uni_modules": {
+    "dependencies": [
+			"uv-ui-tools",
+			"uv-overlay",
+			"uv-transition",
+			"uv-icon",
+			"uv-status-bar",
+			"uv-safe-bottom"
+		],
+    "encrypt": [],
+    "platforms": {
+			"cloud": {
+				"tcb": "y",
+				"aliyun": "y"
+			},
+			"client": {
+				"Vue": {
+					"vue2": "y",
+					"vue3": "y"
+				},
+				"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",
+					"飞书": "u",
+					"京东": "u"
+				},
+				"快应用": {
+					"华为": "u",
+					"联盟": "u"
+				}
+			}
+		}
+  }
+}

+ 21 - 0
uni_modules/uv-popup/readme.md

@@ -0,0 +1,21 @@
+## Popup 弹出层
+
+> **组件名:uv-popup**
+
+弹出层容器,用于展示弹窗、信息提示等内容,支持上、下、左、右和中部弹出。组件只提供容器,内部内容由用户自定义。
+
+该组件已经放弃原来`uview2.x`的写法,参照了官方`uni-popup`的写法进行重构。在小程序端的性能大大提升,打开和关闭避免延迟,调用方法与之前相比也有所差异,具体请查看文档。
+
+# <a href="https://www.uvui.cn/components/popup.html" target="_blank">查看文档</a>
+
+## [下载完整示例项目](https://ext.dcloud.net.cn/plugin?name=uv-ui) <small>(请不要 下载插件ZIP)</small>
+
+### [更多插件,请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+<a href="https://ext.dcloud.net.cn/plugin?name=uv-ui" target="_blank">
+
+![image](https://mp-a667b617-c5f1-4a2d-9a54-683a67cff588.cdn.bspapp.com/uv-ui/banner.png)
+
+</a>
+
+#### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>

+ 11 - 0
uni_modules/uv-safe-bottom/changelog.md

@@ -0,0 +1,11 @@
+## 1.0.4(2023-09-14)
+1. 飞书小程序支持
+## 1.0.3(2023-08-14)
+1. 修复百度报错的BUG
+## 1.0.2(2023-07-02)
+uv-safe-bottom 修复,在百度程序,抖音小程序不生效的BUG
+## 1.0.1(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.0(2023-05-10)
+uv-safe-bottom 底部安全区组件

+ 67 - 0
uni_modules/uv-safe-bottom/components/uv-safe-bottom/uv-safe-bottom.vue

@@ -0,0 +1,67 @@
+<template>
+	<view
+		class="uv-safe-bottom"
+		:style="[style]"
+		:class="[!isNvue && 'uv-safe-area-inset-bottom']"
+	>
+	</view>
+</template>
+
+<script>
+	import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+	import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+	/**
+	 * SafeBottom 底部安全区
+	 * @description 这个适配,主要是针对IPhone X等一些底部带指示条的机型,指示条的操作区域与页面底部存在重合,容易导致用户误操作,因此我们需要针对这些机型进行底部安全区适配。
+	 * @tutorial https://www.uvui.cn/components/safeAreaInset.html
+	 * @property {type}		prop_name
+	 * @property {Object}	customStyle	定义需要用到的外部样式
+	 *
+	 * @event {Function()}
+	 * @example <uv-status-bar></uv-status-bar>
+	 */
+	export default {
+		name: "uv-safe-bottom",
+		mixins: [mpMixin, mixin],
+		data() {
+			return {
+				safeAreaBottomHeight: 0,
+				isNvue: false,
+			};
+		},
+		computed: {
+			style() {
+				const style = {};
+				// #ifdef APP-NVUE || MP-TOUTIAO || MP-LARK
+				// nvue下,高度使用js计算填充
+				style.height = this.$uv.addUnit(this.$uv.sys()?.safeAreaInsets?.bottom, 'px');
+				// #endif
+				return this.$uv.deepMerge(style, this.$uv.addStyle(this.customStyle));
+			},
+		},
+		mounted() {
+			// #ifdef APP-NVUE
+			// 标识为是否nvue
+			this.isNvue = true;
+			// #endif
+		},
+	};
+</script>
+
+<style lang="scss" scoped>
+	.uv-safe-bottom {
+		/* #ifndef APP-NVUE */
+		width: 100%;
+		/* #endif */
+	}
+	/* #ifndef APP-NVUE */
+	// 历遍生成4个方向的底部安全区
+	@each $d in top, right, bottom, left {
+		.uv-safe-area-inset-#{$d} {
+			padding-#{$d}: 0;
+			padding-#{$d}: constant(safe-area-inset-#{$d});  
+			padding-#{$d}: env(safe-area-inset-#{$d});  
+		}
+	}
+	/* #endif */
+</style>

+ 87 - 0
uni_modules/uv-safe-bottom/package.json

@@ -0,0 +1,87 @@
+{
+  "id": "uv-safe-bottom",
+  "displayName": "uv-safe-bottom 底部安全区  全面兼容小程序、nvue、vue2、vue3等多端",
+  "version": "1.0.4",
+  "description": "这个适配,主要是针对IPhone X等一些底部带指示条的机型,指示条的操作区域与页面底部存在重合,容易导致用户误操作,因此我们需要针对这些机型进行底部安全区适配。",
+  "keywords": [
+    "uv-safe-bottom",
+    "uvui",
+    "uv-ui",
+    "bottom",
+    "底部安全区"
+],
+  "repository": "",
+  "engines": {
+    "HBuilderX": "^3.1.0"
+  },
+  "dcloudext": {
+    "type": "component-vue",
+    "sale": {
+      "regular": {
+        "price": "0.00"
+      },
+      "sourcecode": {
+        "price": "0.00"
+      }
+    },
+    "contact": {
+      "qq": ""
+    },
+    "declaration": {
+    	"ads": "无",
+    	"data": "插件不采集任何数据",
+    	"permissions": "无"
+    },
+    "npmurl": ""
+  },
+  "uni_modules": {
+    "dependencies": [
+			"uv-ui-tools"
+		],
+    "encrypt": [],
+    "platforms": {
+			"cloud": {
+				"tcb": "y",
+				"aliyun": "y"
+			},
+			"client": {
+				"Vue": {
+					"vue2": "y",
+					"vue3": "y"
+				},
+				"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",
+					"飞书": "u",
+					"京东": "u"
+				},
+				"快应用": {
+					"华为": "u",
+					"联盟": "u"
+				}
+			}
+		}
+  }
+}

+ 11 - 0
uni_modules/uv-safe-bottom/readme.md

@@ -0,0 +1,11 @@
+## SafeBottom 底部安全区 
+
+> **组件名:uv-safe-bottom**
+
+这个适配,主要是针对IPhone X等一些底部带指示条的机型,指示条的操作区域与页面底部存在重合,容易导致用户误操作,因此我们需要针对这些机型进行底部安全区适配。
+
+### <a href="https://www.uvui.cn/guide/safeAreaInset.html" target="_blank">查看文档</a>
+
+### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+#### 如使用过程中有任何问题,或者您对uv-ui有一些好的建议,欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a>、<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>

+ 7 - 0
uni_modules/uv-status-bar/changelog.md

@@ -0,0 +1,7 @@
+## 1.0.2(2023-06-05)
+1. 兼容渐变背景色
+## 1.0.1(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.0(2023-05-10)
+1. 新增uv-status-bar组件

+ 8 - 0
uni_modules/uv-status-bar/components/uv-status-bar/props.js

@@ -0,0 +1,8 @@
+export default {
+    props: {
+        bgColor: {
+            type: String,
+            default: 'transparent'
+        }
+    }
+}

+ 54 - 0
uni_modules/uv-status-bar/components/uv-status-bar/uv-status-bar.vue

@@ -0,0 +1,54 @@
+<template>
+	<view
+	    :style="[style]"
+	    class="uv-status-bar"
+	>
+		<slot />
+	</view>
+</template>
+
+<script>
+	import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+	import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+	import props from './props.js';
+	/**
+	 * StatbusBar 状态栏占位
+	 * @description 本组件主要用于状态填充,比如在自定导航栏的时候,它会自动适配一个恰当的状态栏高度。
+	 * @tutorial https://www.uvui.cn/components/statusBar.html
+	 * @property {String}			bgColor			背景色 (默认 'transparent' )
+	 * @property {String | Object}	customStyle		自定义样式 
+	 * @example <uv-status-bar></uv-status-bar>
+	 */
+	export default {
+		name: 'uv-status-bar',
+		mixins: [mpMixin, mixin, props],
+		data() {
+			return {
+			}
+		},
+		computed: {
+			style() {
+				const style = {}
+				// 状态栏高度,由于某些安卓和微信开发工具无法识别css的顶部状态栏变量,所以使用js获取的方式
+				style.height = this.$uv.addUnit(this.$uv.sys().statusBarHeight, 'px')
+				if(this.bgColor){
+					if (this.bgColor.indexOf("gradient") > -1) {// 渐变色
+						style.backgroundImage = this.bgColor;
+					}else{
+						style.background = this.bgColor;
+					}
+				}
+				return this.$uv.deepMerge(style, this.$uv.addStyle(this.customStyle))
+			}
+		},
+	}
+</script>
+
+<style lang="scss" scoped>
+	.uv-status-bar {
+		// nvue会默认100%,如果nvue下,显式写100%的话,会导致宽度不为100%而异常
+		/* #ifndef APP-NVUE */
+		width: 100%;
+		/* #endif */
+	}
+</style>

+ 87 - 0
uni_modules/uv-status-bar/package.json

@@ -0,0 +1,87 @@
+{
+  "id": "uv-status-bar",
+  "displayName": "uv-status-bar 状态栏占位",
+  "version": "1.0.2",
+  "description": "状态栏占位组件主要用于状态填充,比如在自定导航栏的时候,它会自动适配一个恰当的状态栏高度。",
+  "keywords": [
+    "uv-status-bar",
+    "uvui",
+    "uv-ui",
+    "status-bar",
+    "状态栏"
+],
+  "repository": "",
+  "engines": {
+    "HBuilderX": "^3.1.0"
+  },
+  "dcloudext": {
+    "type": "component-vue",
+    "sale": {
+      "regular": {
+        "price": "0.00"
+      },
+      "sourcecode": {
+        "price": "0.00"
+      }
+    },
+    "contact": {
+      "qq": ""
+    },
+    "declaration": {
+    	"ads": "无",
+    	"data": "插件不采集任何数据",
+    	"permissions": "无"
+    },
+    "npmurl": ""
+  },
+  "uni_modules": {
+    "dependencies": [
+			"uv-ui-tools"
+		],
+    "encrypt": [],
+    "platforms": {
+			"cloud": {
+				"tcb": "y",
+				"aliyun": "y"
+			},
+			"client": {
+				"Vue": {
+					"vue2": "y",
+					"vue3": "y"
+				},
+				"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",
+					"飞书": "u",
+					"京东": "u"
+				},
+				"快应用": {
+					"华为": "u",
+					"联盟": "u"
+				}
+			}
+		}
+  }
+}

+ 10 - 0
uni_modules/uv-status-bar/readme.md

@@ -0,0 +1,10 @@
+## StatbusBar 状态栏占位
+
+> **组件名:uv-status-bar**
+
+本组件主要用于状态填充,比如在自定导航栏的时候,它会自动适配一个恰当的状态栏高度。
+
+### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+#### 如使用过程中有任何问题,或者您对uv-ui有一些好的建议,欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a>、<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
+

+ 2 - 0
uni_modules/uv-toolbar/changelog.md

@@ -0,0 +1,2 @@
+## 1.0.0(2023-08-02)
+1. 新增工具条组件

+ 40 - 0
uni_modules/uv-toolbar/components/uv-toolbar/props.js

@@ -0,0 +1,40 @@
+export default {
+	props: {
+		// 是否展示工具条
+		show: {
+			type: Boolean,
+			default: true
+		},
+		// 是否显示下边框
+		showBorder: {
+			type: Boolean,
+			default: false
+		},
+		// 取消按钮的文字
+		cancelText: {
+			type: String,
+			default: '取消'
+		},
+		// 确认按钮的文字
+		confirmText: {
+			type: String,
+			default: '确认'
+		},
+		// 取消按钮的颜色
+		cancelColor: {
+			type: String,
+			default: '#909193'
+		},
+		// 确认按钮的颜色
+		confirmColor: {
+			type: String,
+			default: '#3c9cff'
+		},
+		// 标题文字
+		title: {
+			type: String,
+			default: ''
+		},
+		...uni.$uv?.props?.toolbar
+	}
+}

+ 109 - 0
uni_modules/uv-toolbar/components/uv-toolbar/uv-toolbar.vue

@@ -0,0 +1,109 @@
+<template>
+	<view
+		:class="['uv-toolbar',{'uv-border-bottom':showBorder}]"
+		@touchmove.stop.prevent="noop"
+		v-if="show"
+	>
+		<view
+			class="uv-toolbar__cancel__wrapper"
+			hover-class="uv-hover-class"
+		>
+			<text
+				class="uv-toolbar__wrapper__cancel"
+				@tap="cancel"
+				:style="{
+					color: cancelColor
+				}"
+			>{{ cancelText }}</text>
+		</view>
+		<text
+			class="uv-toolbar__title uv-line-1"
+			v-if="title"
+		>{{ title }}</text>
+		<view
+			class="uv-toolbar__confirm__wrapper"
+			hover-class="uv-hover-class"
+		>
+			<text
+				class="uv-toolbar__wrapper__confirm"
+				@tap="confirm"
+				:style="{
+				color: confirmColor
+			}"
+			>{{ confirmText }}</text>
+		</view>
+	</view>
+</template>
+<script>
+	import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+	import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+	import props from './props.js';
+	/**
+	 * Toolbar 工具条
+	 * @description 
+	 * @tutorial https://www.uvui.cn/components/toolbar.html
+	 * @property {Boolean}	show			是否展示工具条(默认 true )
+	 * @property {Boolean}	showBorder			是否展示工具条下方边框(默认 false )
+	 * @property {String}	cancelText		取消按钮的文字(默认 '取消' )
+	 * @property {String}	confirmText		确认按钮的文字(默认 '确认' )
+	 * @property {String}	cancelColor		取消按钮的颜色(默认 '#909193' )
+	 * @property {String}	confirmColor	确认按钮的颜色(默认 '#3c9cff' )
+	 * @property {String}	title	标题文字
+	 * @event {Function} 
+	 * @example 
+	 */
+	export default {
+		name: 'uv-toolbar',
+		emits: ['confirm', 'cancel'],
+		mixins: [mpMixin, mixin, props],
+		methods: {
+			// 点击取消按钮
+			cancel() {
+				this.$emit('cancel')
+			},
+			// 点击确定按钮
+			confirm() {
+				this.$emit('confirm')
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	$show-lines: 1;
+	$show-hover: 1;
+	$show-border: 1;
+	$show-border-bottom: 1;
+	@import '@/uni_modules/uv-ui-tools/libs/css/variable.scss';
+	@import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+	$uv-tips-color: #909193 !default;
+	$uv-main-color: #303133 !default;
+	$uv-primary: #3c9cff !default;
+	.uv-toolbar {
+		height: 42px;
+		@include flex;
+		justify-content: space-between;
+		align-items: center;
+		&__wrapper {
+			&__cancel {
+				color: $uv-tips-color;
+				font-size: 15px;
+				padding: 0 15px;
+			}
+		}
+		&__title {
+			color: $uv-main-color;
+			padding: 0 60rpx;
+			font-size: 16px;
+			flex: 1;
+			text-align: center;
+		}
+		&__wrapper {
+			&__confirm {
+				color: $uv-primary;
+				font-size: 15px;
+				padding: 0 15px;
+			}
+		}
+	}
+</style>

+ 87 - 0
uni_modules/uv-toolbar/package.json

@@ -0,0 +1,87 @@
+{
+  "id": "uv-toolbar",
+  "displayName": "uv-toolbar 工具条",
+  "version": "1.0.0",
+  "description": "该组价是仅用于uv-ui中一个公共小工具,提供一个取消和确定的样式,可以设置标题,主要用于弹窗顶部的选择确定工具条",
+  "keywords": [
+    "uv-toolbar",
+    "uvui",
+    "uv-ui",
+    "工具条",
+    "工具"
+],
+  "repository": "",
+  "engines": {
+    "HBuilderX": "^3.1.0"
+  },
+  "dcloudext": {
+    "type": "component-vue",
+    "sale": {
+      "regular": {
+        "price": "0.00"
+      },
+      "sourcecode": {
+        "price": "0.00"
+      }
+    },
+    "contact": {
+      "qq": ""
+    },
+    "declaration": {
+    	"ads": "无",
+    	"data": "插件不采集任何数据",
+    	"permissions": "无"
+    },
+    "npmurl": ""
+  },
+  "uni_modules": {
+    "dependencies": [
+			"uv-ui-tools"
+		],
+    "encrypt": [],
+    "platforms": {
+			"cloud": {
+				"tcb": "y",
+				"aliyun": "y"
+			},
+			"client": {
+				"Vue": {
+					"vue2": "y",
+					"vue3": "y"
+				},
+				"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",
+					"飞书": "u",
+					"京东": "u"
+				},
+				"快应用": {
+					"华为": "u",
+					"联盟": "u"
+				}
+			}
+		}
+  }
+}

+ 31 - 0
uni_modules/uv-toolbar/readme.md

@@ -0,0 +1,31 @@
+## Toolbar 工具条
+
+> **组件名:uv-toolbar**
+
+该组价是仅用于uv-ui中一个公共小工具,提供一个取消和确定的样式,可以设置标题,主要用于弹窗顶部的选择确定工具条。
+
+### 基本使用
+
+```vue
+<uv-toolbar title="标题文字"></uv-toolbar>
+```
+
+### Toolbar Props
+
+| 属性名 | 类型 | 默认值 | 说明 |
+|:-|:-|:-|:-|
+| show | Boolean | true | 是否展示工具条 |
+| showBorder | Boolean | false | 是否显示下边框 |
+| cancelText | String | '取消' | 取消按钮的文字 |
+| confirmText | String | '确定' | 确定按钮的文字 |
+| cancelColor | String | '#909193' | 取消按钮的颜色 |
+| confirmColor | String | '#3c9cff' | 确认按钮的颜色 |
+| title | String | - | 标题文字 |
+
+## [下载完整示例项目](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+### [更多插件,请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+![image](https://mp-a667b617-c5f1-4a2d-9a54-683a67cff588.cdn.bspapp.com/uv-ui/banner.png)
+
+#### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>

+ 19 - 0
uni_modules/uv-transition/changelog.md

@@ -0,0 +1,19 @@
+## 1.0.8(2023-10-18)
+1. 修复在APP上不能正常显示的BUG
+## 1.0.7(2023-10-12)
+1. 修复部分情况,修改某属性自动关闭的BUG
+## 1.0.6(2023-07-24)
+1. 优化  nvue模式下增加cellChild参数,是否在list中cell节点下,nvue中cell下建议设置成true
+## 1.0.5(2023-07-02)
+修改VUE3模式下可能存在的BUG
+## 1.0.4(2023-07-02)
+uv-transition  动画组件,代码重构优化,性能更加友好,增加自定义动画功能。详情参考文档:https://www.uvui.cn/components/transition.html
+## 1.0.3(2023-06-12)
+1. 恢复this.$nextTick的使用,经过测试百度等平台无问题
+## 1.0.2(2023-05-23)
+1. 百度小程序等平台不支持this.$nextick,修改成延时
+## 1.0.1(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.0(2023-05-10)
+1. 新增动画组件

+ 131 - 0
uni_modules/uv-transition/components/uv-transition/createAnimation.js

@@ -0,0 +1,131 @@
+// const defaultOption = {
+// 	duration: 300,
+// 	timingFunction: 'linear',
+// 	delay: 0,
+// 	transformOrigin: '50% 50% 0'
+// }
+// #ifdef APP-NVUE
+const nvueAnimation = uni.requireNativePlugin('animation')
+// #endif
+class MPAnimation {
+	constructor(options, _this) {
+		this.options = options
+		// 在iOS10+QQ小程序平台下,传给原生的对象一定是个普通对象而不是Proxy对象,否则会报parameter should be Object instead of ProxyObject的错误
+		this.animation = uni.createAnimation({
+			...options
+		})
+		this.currentStepAnimates = {}
+		this.next = 0
+		this.$ = _this
+
+	}
+
+	_nvuePushAnimates(type, args) {
+		let aniObj = this.currentStepAnimates[this.next]
+		let styles = {}
+		if (!aniObj) {
+			styles = {
+				styles: {},
+				config: {}
+			}
+		} else {
+			styles = aniObj
+		}
+		if (animateTypes1.includes(type)) {
+			if (!styles.styles.transform) {
+				styles.styles.transform = ''
+			}
+			let unit = ''
+			if(type === 'rotate'){
+				unit = 'deg'
+			}
+			styles.styles.transform += `${type}(${args+unit}) `
+		} else {
+			styles.styles[type] = `${args}`
+		}
+		this.currentStepAnimates[this.next] = styles
+	}
+	_animateRun(styles = {}, config = {}) {
+		let ref = this.$.$refs['ani'].ref
+		if (!ref) return
+		return new Promise((resolve, reject) => {
+			nvueAnimation.transition(ref, {
+				styles,
+				...config
+			}, res => {
+				resolve()
+			})
+		})
+	}
+
+	_nvueNextAnimate(animates, step = 0, fn) {
+		let obj = animates[step]
+		if (obj) {
+			let {
+				styles,
+				config
+			} = obj
+			this._animateRun(styles, config).then(() => {
+				step += 1
+				this._nvueNextAnimate(animates, step, fn)
+			})
+		} else {
+			this.currentStepAnimates = {}
+			typeof fn === 'function' && fn()
+			this.isEnd = true
+		}
+	}
+
+	step(config = {}) {
+		// #ifndef APP-NVUE
+		this.animation.step(config)
+		// #endif
+		// #ifdef APP-NVUE
+		this.currentStepAnimates[this.next].config = Object.assign({}, this.options, config)
+		this.currentStepAnimates[this.next].styles.transformOrigin = this.currentStepAnimates[this.next].config.transformOrigin
+		this.next++
+		// #endif
+		return this
+	}
+
+	run(fn) {
+		// #ifndef APP-NVUE
+		this.$.animationData = this.animation.export()
+		this.$.timer = setTimeout(() => {
+			typeof fn === 'function' && fn()
+		}, this.$.durationTime)
+		// #endif
+		// #ifdef APP-NVUE
+		this.isEnd = false
+		let ref = this.$.$refs['ani'] && this.$.$refs['ani'].ref
+		if(!ref) return
+		this._nvueNextAnimate(this.currentStepAnimates, 0, fn)
+		this.next = 0
+		// #endif
+	}
+}
+
+
+const animateTypes1 = ['matrix', 'matrix3d', 'rotate', 'rotate3d', 'rotateX', 'rotateY', 'rotateZ', 'scale', 'scale3d',
+	'scaleX', 'scaleY', 'scaleZ', 'skew', 'skewX', 'skewY', 'translate', 'translate3d', 'translateX', 'translateY',
+	'translateZ'
+]
+const animateTypes2 = ['opacity', 'backgroundColor']
+const animateTypes3 = ['width', 'height', 'left', 'right', 'top', 'bottom']
+animateTypes1.concat(animateTypes2, animateTypes3).forEach(type => {
+	MPAnimation.prototype[type] = function(...args) {
+		// #ifndef APP-NVUE
+		this.animation[type](...args)
+		// #endif
+		// #ifdef APP-NVUE
+		this._nvuePushAnimates(type, args)
+		// #endif
+		return this
+	}
+})
+
+export function createAnimation(option, _this) {
+	if(!_this) return
+	clearTimeout(_this.timer)
+	return new MPAnimation(option, _this)
+}

+ 31 - 0
uni_modules/uv-transition/components/uv-transition/props.js

@@ -0,0 +1,31 @@
+export default {
+	props: {
+		// 是否展示组件
+		show: {
+			type: Boolean,
+			default: false
+		},
+		// 使用的动画模式
+		mode: {
+			type: [Array, String, null],
+			default() {
+				return 'fade'
+			}
+		},
+		// 动画的执行时间,单位ms
+		duration: {
+			type: [String, Number],
+			default: 300
+		},
+		// 使用的动画过渡函数
+		timingFunction: {
+			type: String,
+			default: 'ease-out'
+		},
+		customClass: {
+			type: String,
+			default: ''
+		},
+		...uni.$uv?.props?.transition
+	}
+}

+ 320 - 0
uni_modules/uv-transition/components/uv-transition/uv-transition.vue

@@ -0,0 +1,320 @@
+<template>
+  <!-- #ifndef APP-NVUE -->
+  <view 
+		v-if="isShow" 
+		ref="ani" 
+		:animation="animationData" 
+		:class="customClass" 
+		:style="transformStyles" 
+		@click="onClick">
+		<slot></slot>
+	</view>
+  <!-- #endif -->
+  <!-- #ifdef APP-NVUE -->
+  <view 
+		v-if="isShow" 
+		ref="ani" 
+		:animation="animationData" 
+		:class="customClass" 
+		:style="transformStyles" 
+		@click="onClick">
+		<slot></slot>
+	</view>
+  <!-- #endif -->
+</template>
+<script>
+	import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+	import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+	import { createAnimation } from './createAnimation'
+	/**
+	* transition  动画组件
+	* @description
+	* @tutorial
+	* @property {Boolean}	show	控制组件显示或关闭 (默认 false )
+	* @property {Array | String	}	mode	内置过渡动画类型 (默认 'fade' )
+	* @value fade 渐隐渐出过渡
+	* @value slide-top 由上至下过渡
+	* @value slide-bottom 由下至上过渡
+	* @value slide-left 由左至右过渡
+	* @value slide-right 由右至左过渡
+	* @value zoom-in 由小到大过渡
+	* @value zoom-out 由大到小过渡
+	* @property {String | Number}	duration	动画的执行时间,单位ms (默认 300 )
+	* @property {String} timingFunction	使用的动画过渡函数 (默认 'ease-out' )
+	* @property {Object} customStyle	自定义样式
+	* @property {String} customClass	自定义类名
+	* @event {Function} click 点击组件触发	
+	* @event {Function} change	过渡动画结束时触发	
+	* @example 
+	*/
+	export default {
+		name: 'uv-transition',
+		mixins: [mpMixin,mixin],
+		emits:['click','change'],
+		props: {
+			// 是否展示组件
+			show: {
+				type: Boolean,
+				default: false
+			},
+			// 使用的动画模式
+			mode: {
+				type: [Array, String, null],
+				default() {
+					return 'fade'
+				}
+			},
+			// 动画的执行时间,单位ms
+			duration: {
+				type: [String, Number],
+				default: 300
+			},
+			// 使用的动画过渡函数
+			timingFunction: {
+				type: String,
+				default: 'ease-out'
+			},
+			customClass: {
+				type: String,
+				default: ''
+			},
+			// nvue模式下 是否直接显示,在uv-list等cell下面使用就需要设置
+			cellChild: {
+				type: Boolean,
+				default: false
+			}
+		},
+		data(){
+			return {
+				isShow: false,
+				transform: '',
+				opacity: 1,
+				animationData: {},
+				durationTime: 300,
+				config: {}
+			}
+		},
+		watch: {
+			show: {
+				handler(newVal) {
+					if (newVal) {
+						this.open();
+					} else {
+						// 避免上来就执行 close,导致动画错乱
+						if (this.isShow) {
+							this.close();
+						}
+					}
+				},
+				immediate: true
+			}
+		},
+		computed: {
+			// 初始化动画条件
+			transformStyles() {
+				const style = {
+					transform: this.transform,
+					opacity: this.opacity,
+					...this.$uv.addStyle(this.customStyle),
+					'transition-duration': `${this.duration / 1000}s`
+				};
+				return this.$uv.addStyle(style,'string');
+			}
+		},
+		created() {
+			// 动画默认配置
+			this.config = {
+				duration: this.duration,
+				timingFunction: this.timingFunction,
+				transformOrigin: '50% 50%',
+				delay: 0
+			};
+			this.durationTime = this.duration;
+		},
+		methods: {
+			/**
+			 *  ref 触发 初始化动画
+			 */
+			init(obj = {}) {
+				if (obj.duration) {
+					this.durationTime = obj.duration;
+				}
+				this.animation = createAnimation(Object.assign(this.config, obj),this);
+			},
+			/**
+			 * 点击组件触发回调
+			 */
+			onClick() {
+				this.$emit('click', {
+					detail: this.isShow
+				})
+			},
+			/**
+			 * ref 触发 动画分组
+			 * @param {Object} obj
+			 */
+			step(obj, config = {}) {
+				if (!this.animation) return;
+				for (let i in obj) {
+					try {
+						if(typeof obj[i] === 'object'){
+							this.animation[i](...obj[i]);
+						}else{
+							this.animation[i](obj[i]);
+						}
+					} catch (e) {
+						console.error(`方法 ${i} 不存在`);
+					}
+				}
+				this.animation.step(config);
+				return this;
+			},
+			/**
+			 *  ref 触发 执行动画
+			 */
+			run(fn) {
+				if (!this.animation) return;
+				this.animation.run(fn);
+			},
+			// 开始过度动画
+			open() {
+				clearTimeout(this.timer);
+				this.transform = '';
+				this.isShow = true;
+				let { opacity, transform } = this.styleInit(false);
+				if (typeof opacity !== 'undefined') {
+					this.opacity = opacity;
+				}
+				this.transform = transform;
+				// 确保动态样式已经生效后,执行动画,如果不加 nextTick ,会导致 wx 动画执行异常
+				this.$nextTick(() => {
+					// TODO 定时器保证动画完全执行,目前有些问题,后面会取消定时器
+					this.timer = setTimeout(() => {
+						this.animation = createAnimation(this.config, this);
+						this.tranfromInit(false).step();
+						// #ifdef APP-NVUE
+						if(this.cellChild) {
+							this.opacity = 1;
+						} else{
+							this.animation.run();
+						}
+						// #endif
+						// #ifndef APP-NVUE
+						this.animation.run();
+						// #endif
+						// #ifdef VUE3
+						// #ifdef H5
+						this.opacity = 1;
+						// #endif
+						// #endif
+						this.$emit('change', {
+							detail: this.isShow
+						})
+						// #ifdef H5
+						// #ifdef VUE3
+						this.transform = '';
+						// #endif
+						// #endif
+					}, 20);
+				})
+			},
+			// 关闭过渡动画
+			close(type) {
+				if (!this.animation) return;
+				this.tranfromInit(true)
+					.step()
+					.run(() => {
+						this.isShow = false;
+						this.animationData = null;
+						this.animation = null;
+						let { opacity, transform } = this.styleInit(false);
+						this.opacity = opacity || 1;
+						this.transform = transform;
+						this.$emit('change', {
+							detail: this.isShow
+						});
+					})
+			},
+			// 处理动画开始前的默认样式
+			styleInit(type) {
+				let styles = {
+					transform: ''
+				};
+				let buildStyle = (type, mode) => {
+					if (mode === 'fade') {
+						styles.opacity = this.animationType(type)[mode];
+					} else {
+						styles.transform += this.animationType(type)[mode] + ' ';
+					}
+				}
+				if (typeof this.mode === 'string') {
+					buildStyle(type, this.mode);
+				} else {
+					this.mode.forEach(mode => {
+						buildStyle(type, mode)
+					})
+				}
+				return styles
+			},
+			// 处理内置组合动画
+			tranfromInit(type) {
+				let buildTranfrom = (type, mode) => {
+					let aniNum = null;
+					if (mode === 'fade') {
+						aniNum = type ? 0 : 1;
+					} else {
+						aniNum = type ? '-100%' : '0';
+						if (mode === 'zoom-in') {
+							aniNum = type ? 0.8 : 1
+						}
+						if (mode === 'zoom-out') {
+							aniNum = type ? 1.2 : 1
+						}
+						if (mode === 'slide-right') {
+							aniNum = type ? '100%' : '0'
+						}
+						if (mode === 'slide-bottom') {
+							aniNum = type ? '100%' : '0'
+						}
+					}
+					this.animation[this.animationMode()[mode]](aniNum)
+				}
+				if (typeof this.mode === 'string') {
+					buildTranfrom(type, this.mode)
+				} else {
+					this.mode.forEach(mode => {
+						buildTranfrom(type, mode)
+					})
+				}
+				return this.animation;
+			},
+			animationType(type) {
+				return {
+					fade: type ? 1 : 0,
+					'slide-top': `translateY(${type ? '0' : '-100%'})`,
+					'slide-right': `translateX(${type ? '0' : '100%'})`,
+					'slide-bottom': `translateY(${type ? '0' : '100%'})`,
+					'slide-left': `translateX(${type ? '0' : '-100%'})`,
+					'zoom-in': `scaleX(${type ? 1 : 0.8}) scaleY(${type ? 1 : 0.8})`,
+					'zoom-out': `scaleX(${type ? 1 : 1.2}) scaleY(${type ? 1 : 1.2})`
+				}
+			},
+			// 内置动画类型与实际动画对应字典
+			animationMode() {
+				return {
+					fade: 'opacity',
+					'slide-top': 'translateY',
+					'slide-right': 'translateX',
+					'slide-bottom': 'translateY',
+					'slide-left': 'translateX',
+					'zoom-in': 'scale',
+					'zoom-out': 'scale'
+				}
+			},
+			// 驼峰转中横线
+			toLine(name) {
+				return name.replace(/([A-Z])/g, '-$1').toLowerCase()
+			}
+		}
+	}
+</script>

+ 87 - 0
uni_modules/uv-transition/package.json

@@ -0,0 +1,87 @@
+{
+  "id": "uv-transition",
+  "displayName": "uv-transition 动画 全面兼容vue3+2、app、h5、小程序等多端",
+  "version": "1.0.8",
+  "description": "transition 该组件用于组件的动画过渡效果。",
+  "keywords": [
+    "uv-transition",
+    "uvui",
+    "uv-ui",
+    "transition",
+    "动画"
+],
+  "repository": "",
+  "engines": {
+    "HBuilderX": "^3.1.0"
+  },
+  "dcloudext": {
+    "type": "component-vue",
+    "sale": {
+      "regular": {
+        "price": "0.00"
+      },
+      "sourcecode": {
+        "price": "0.00"
+      }
+    },
+    "contact": {
+      "qq": ""
+    },
+    "declaration": {
+    	"ads": "无",
+    	"data": "插件不采集任何数据",
+    	"permissions": "无"
+    },
+    "npmurl": ""
+  },
+  "uni_modules": {
+    "dependencies": [
+			"uv-ui-tools"
+		],
+    "encrypt": [],
+    "platforms": {
+			"cloud": {
+				"tcb": "y",
+				"aliyun": "y"
+			},
+			"client": {
+				"Vue": {
+					"vue2": "y",
+					"vue3": "y"
+				},
+				"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",
+					"飞书": "u",
+					"京东": "u"
+				},
+				"快应用": {
+					"华为": "u",
+					"联盟": "u"
+				}
+			}
+		}
+  }
+}

+ 15 - 0
uni_modules/uv-transition/readme.md

@@ -0,0 +1,15 @@
+## Transition 动画
+
+> **组件名:uv-transition**
+
+该组件用于组件的动画过渡效果,支持自定义动画,开箱即用。
+
+# <a href="https://www.uvui.cn/components/transition.html" target="_blank">查看文档</a>
+
+## [下载完整示例项目](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+### [更多插件,请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+![image](https://mp-a667b617-c5f1-4a2d-9a54-683a67cff588.cdn.bspapp.com/uv-ui/banner.png)
+
+#### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>