Jelajahi Sumber

Merge branch 'master' of http://git.wealfavor.cn/root/wh-new-xcx

# Conflicts:
#	page_task/credit/credit.vue
USER-20230908AJ\Administrator 1 tahun lalu
induk
melakukan
dfd7248140
39 mengubah file dengan 2811 tambahan dan 754 penghapusan
  1. 11 10
      common/chart/circle_chart.vue
  2. 6 3
      common/chart/peak_chart.vue
  3. 43 11
      common/task/loan-item.vue
  4. 70 67
      common/task/task_ima_tab.vue
  5. TEMPAT SAMPAH
      components/.DS_Store
  6. 7 5
      components/en-utils/en-nav/en-nav.vue
  7. 11 11
      components/en-utils/en-select/en-select.vue
  8. TEMPAT SAMPAH
      components/sn-swiper/.DS_Store
  9. 251 0
      components/sn-swiper/esc-swiper-item/index.vue
  10. 21 0
      components/sn-swiper/esc-swiper/helper.js
  11. 198 0
      components/sn-swiper/esc-swiper/index.vue
  12. 303 0
      components/sn-swiper/esc-swiper/mixins/base.mixin.js
  13. 91 0
      components/sn-swiper/esc-swiper/mixins/bindingx.js
  14. 143 0
      components/sn-swiper/esc-swiper/mixins/index.wxs
  15. 62 0
      components/sn-swiper/esc-swiper/mixins/mpother.js
  16. 7 0
      components/sn-swiper/esc-swiper/mixins/mpwxs.js
  17. 1 1
      components/static/css/en-common.css
  18. 15 0
      components/utils/style.js
  19. 155 155
      page_task/credit/credit.vue
  20. 6 1
      page_task/task_details/module/apply.vue
  21. 15 7
      page_task/task_details/module/identity.vue
  22. 17 9
      page_task/task_details/module/property.vue
  23. 13 6
      page_task/task_details/module/third_party.vue
  24. 150 26
      page_task/task_details/task_details.vue
  25. 14 0
      pages.json
  26. 1 1
      pages/index/index.vue
  27. 18 20
      pages/index/module/index_column.vue
  28. 71 0
      pages/loan/components/column-item.vue
  29. 44 2
      pages/loan/loan.vue
  30. 49 0
      pages/loan/module/perfect_rate.vue
  31. 155 0
      pages/loan/module/rate_compare.vue
  32. 452 406
      pages/login/index.vue
  33. 8 8
      pages/task/task.vue
  34. 4 0
      static/css/common.css
  35. 3 5
      uni_modules/qiun-data-charts/components/qiun-error/qiun-error.vue
  36. 33 0
      uni_modules/uni-badge/changelog.md
  37. 268 0
      uni_modules/uni-badge/components/uni-badge/uni-badge.vue
  38. 85 0
      uni_modules/uni-badge/package.json
  39. 10 0
      uni_modules/uni-badge/readme.md

+ 11 - 10
common/chart/circle_chart.vue

@@ -1,7 +1,7 @@
 <template>
 	<view class="charts-box" :style="[{width},{height},]">
-		<qiun-data-charts :canvas2d='isCanvas2d' :canvasId='canvasId' type="arcbar" :opts="opts"
-			:chartData="chartData" />
+		<qiun-data-charts :canvas2d='isCanvas2d' :canvasId='canvasId' type="arcbar" :errorMessage="errorMessage"
+			:errorReload="false" :opts="opts" :chartData="chartData" />
 
 	</view>
 </template>
@@ -23,13 +23,14 @@
 			},
 			bgColor: {
 				type: String,
-				default: ()=>{
-          return '#0FB160'
-        }
+				default: () => {
+					return '#0FB160'
+				}
 			},
 		},
 		data() {
 			return {
+				errorMessage: '',
 				isCanvas2d: process.uniEnv.isCanvas2d,
 				chartData: {},
 				//这里的 opts 是图表类型 type="arcbar" 的全部配置参数,您可以将此配置复制到 config-ucharts.js 文件中下标为 ['arcbar'] 的节点中来覆盖全局默认参数。实际应用过程中 opts 只需传入与全局默认参数中不一致的【某一个属性】即可实现同类型的图表显示不同的样式,达到页面简洁的需求。
@@ -110,14 +111,14 @@
 							data: data
 						}]
 					};
-					this.chartData = JSON.parse(JSON.stringify(res));
+					// this.chartData = JSON.parse(JSON.stringify(res));
+					this.chartData = {};
+					this.errorMessage = '暂无数据'
 				}, 100)
-
-			}
-
+			},
 		}
 	};
 </script>
 
 <style scoped>
-</style>
+</style>

+ 6 - 3
common/chart/peak_chart.vue

@@ -1,7 +1,7 @@
 <template>
 	<view class="charts-box">
 		<qiun-data-charts :canvas2d='isCanvas2d' :canvasId='canvasId' type="mount" :ontouch="true" :opts="opts"
-			:chartData="chartData" />
+			:chartData="chartData" :errorMessage="errorMessage" :errorReload="false" />
 	</view>
 </template>
 
@@ -17,6 +17,7 @@
 			return {
 				isCanvas2d: process.uniEnv.isCanvas2d,
 				chartData: {},
+				errorMessage: '',
 				//这里的 opts 是图表类型 type="mount" 的全部配置参数,您可以将此配置复制到 config-ucharts.js 文件中下标为 ['mount'] 的节点中来覆盖全局默认参数。实际应用过程中 opts 只需传入与全局默认参数中不一致的【某一个属性】即可实现同类型的图表显示不同的样式,达到页面简洁的需求。
 				opts: {
 					timing: "easeIn",
@@ -184,9 +185,11 @@
 							data: data
 						}]
 					};
-					this.chartData = JSON.parse(JSON.stringify(res));
+					// this.chartData = JSON.parse(JSON.stringify(res));
+					this.chartData = {};
+					this.errorMessage = '暂无数据'
 				}, 100);
-			}
+			},
 		}
 	};
 </script>

+ 43 - 11
common/task/loan-item.vue

@@ -1,16 +1,19 @@
 <template>
 	<view class="p-t20">
 		<view class="task-body m-lr20  animate__animated animate__fadeIn" v-if="taskList.length>0">
-			<view class="row-c page-box-bg-fff m-b20 r-30 box-shadow-197" v-for="(item,index) in taskList" :key="index">
+			<view class="row-c page-box-bg-fff m-b20 r-30 box-shadow-197 loan-item" v-for="(item,index) in taskList"
+				:key="index">
 				<view class="main_string" :style="{background:lineColor[item.product_id%3]}"></view>
-				<view class="row-c flex p-30">
+				<view class="row-c flex p-30 p-t60">
 					<image class="wh-80" :src="item.product_icon" mode=""></image>
 					<view class="flex m-l20">
 						<view class="row-justify-sb center flex">
 							<text class="text-color-333 sys-weight-600">{{item.product_name}}</text>
 							<view class="row-c line-40" @click.stop="makCall(item.phone)">
 								<text class="size-26 text-color-333">{{item.name}}</text>
-								<image class="wh-30 m-l16"	src="https://wealfavor-1257406827.cos.ap-beijing.myqcloud.com/new-xcx/task/task-phone.png" mode=""></image>
+								<image class="wh-30 m-l16"
+									src="https://wealfavor-1257406827.cos.ap-beijing.myqcloud.com/new-xcx/task/task-phone.png"
+									mode=""></image>
 							</view>
 						</view>
 						<view
@@ -18,10 +21,12 @@
 							style="line-height: 40rpx;">
 							<text class="size-28 text-color-E21">{{(item.loans_money/10000).toFixed(2)}}万</text>
 							<view class="">
-								<text>{{item.refund_num-item.residue_num}}/{{item.refund_num}}</text><text class="sys-weight-400">期</text>
+								<text>{{item.refund_num-item.residue_num}}/{{item.refund_num}}</text><text
+									class="sys-weight-400">期</text>
 							</view>
 							<view class="" v-if="item.status === 1">
-								<text>{{(item.repayment_money*1).toFixed(2)}}</text><text class="sys-weight-400">元</text>
+								<text>{{(item.repayment_money*1).toFixed(2)}}</text><text
+									class="sys-weight-400">元</text>
 							</view>
 							<text class="color-FF730E">{{(item.interest_rate*1).toFixed(2)}}%</text>
 						</view>
@@ -30,13 +35,16 @@
 							<view class="row-c">
 								<button
 									class="button-backgroun en_buttond left_button sys-weight-500 button_color-008FD6 size-26 r-100 m-r20"
-									type="default" hover-class="is-hover">核算</button>
+									type="default" hover-class="is-hover" @click="onCalculate">核算</button>
 								<button
 									class="button-background en_button left_button right_button sys-weight-500 button_color-008FD6 size-26 r-100"
 									type="default" hover-class="is-hover">客户分类</button>
 							</view>
 						</view>
 					</view>
+					<view class="accomplish-text" :class="{'unfinished':!is_accomplish}">
+						已完成
+					</view>
 				</view>
 			</view>
 			<view class="placeholder sys-list-background-color" v-if="is_bottom && taskList.length"></view>
@@ -90,14 +98,17 @@
 			return {
 				leftImg: ['task-house', 'task-business', 'task-repayment'],
 				iconList: ['task-audit', 'task-do', 'task-stay'],
-				lineColor: [ '#EF8F27','#DE5847', '#0FB160'],
+				lineColor: ['#EF8F27', '#DE5847', '#0FB160'],
 				isAjax: false
 			}
 		},
 		methods: {
-      makCall(phone){
-        tools.makingCall(phone)
-      }
+			onCalculate() {
+				this.$emit('onCalculate')
+			},
+			makCall(phone) {
+				tools.makingCall(phone)
+			}
 		}
 	}
 </script>
@@ -136,7 +147,28 @@
 		background: red;
 	}
 
+	.loan-item {
+		position: relative;
+	}
+
+	.accomplish-text {
+		font-size: 24rpx;
+		color: #00994D;
+		background-color: #e7f7ef;
+		padding: 6rpx 30rpx;
+		line-height: 30rpx;
+		border-radius: 0 30rpx 0 30rpx;
+		position: absolute;
+		right: 0;
+		top: 0;
+	}
+
+	.unfinished {
+		color: #F64646;
+		background-color: #feeded;
+	}
+
 	button::after {
 		border: none;
 	}
-</style>
+</style>

+ 70 - 67
common/task/task_ima_tab.vue

@@ -1,10 +1,10 @@
 <template>
-  <view class="m-lr20 r-20">
-    <k-tabs-swiper class="r-20" v-model="current" :tabs="navList" :field="'name'"   bgColor="#fff" color="#444444"
-                   activeColor="#10B261" fontSize="28rpx" :bold="true" :scroll="true" height="100rpx" lineHeight="10rpx"
-                   @change="changeTab" paddingItem="0 50rpx">
-    </k-tabs-swiper>
-  </view>
+	<view class="m-lr20 r-20">
+		<k-tabs-swiper class="r-20" v-model="current" :tabs="navList" :field="'name'" bgColor="#fff" color="#444444"
+			activeColor="#10B261" fontSize="28rpx" :bold="true" :scroll="true" height="100rpx" lineHeight="10rpx"
+			@change="changeTab" paddingItem="0 50rpx">
+		</k-tabs-swiper>
+	</view>
 </template>
 
 <script>
@@ -14,80 +14,83 @@
 
 	export default {
 		name: 'task-ima-tab',
-    props:{
-      listType:{
-        default:0
-      },
-      numType:{
-        default:0
-      }
-    },
+		props: {
+			listType: {
+				default: 0
+			},
+			numType: {
+				default: 0
+			}
+		},
 		data() {
 			return {
 				current: 0,
 
 				navList: [{
-          id: 0,
-          name: '全部',
-          product_name: '全部',
-          icon: 'https://wealfavor-1257406827.cos.ap-beijing.myqcloud.com/new-xcx/task/task-all.png',
-          num:0
+					id: 0,
+					name: '全部',
+					product_name: '全部',
+					icon: 'https://wealfavor-1257406827.cos.ap-beijing.myqcloud.com/new-xcx/task/task-all.png',
+					num: 0
 				}],
-        typeList:[
-          {
-            id: 2,
-            name: '垫资还款',
-            icon: 'https://wealfavor-1257406827.cos.ap-beijing.myqcloud.com/new-xcx/task/task-refund.png'
-          },
-          {
-            id: 1,
-            name: '房抵业务',
-            icon: 'https://wealfavor-1257406827.cos.ap-beijing.myqcloud.com/new-xcx/task/task-loan.png'
-          },
-          {
-            id: 3,
-            name: '消费金融',
-            icon: 'https://wealfavor-1257406827.cos.ap-beijing.myqcloud.com/new-xcx/task/task-financial.png'
-          },
-          {
-            id: 4,
-            name: '其他服务',
-            icon: 'https://wealfavor-1257406827.cos.ap-beijing.myqcloud.com/new-xcx/task/task-else.png'
-          }
-        ]
+				typeList: [{
+						id: 2,
+						name: '垫资还款',
+						icon: 'https://wealfavor-1257406827.cos.ap-beijing.myqcloud.com/new-xcx/task/task-refund.png'
+					},
+					{
+						id: 1,
+						name: '房抵业务',
+						icon: 'https://wealfavor-1257406827.cos.ap-beijing.myqcloud.com/new-xcx/task/task-loan.png'
+					},
+					{
+						id: 3,
+						name: '消费金融',
+						icon: 'https://wealfavor-1257406827.cos.ap-beijing.myqcloud.com/new-xcx/task/task-financial.png'
+					},
+					{
+						id: 4,
+						name: '其他服务',
+						icon: 'https://wealfavor-1257406827.cos.ap-beijing.myqcloud.com/new-xcx/task/task-else.png'
+					}
+				]
 			}
 		},
-    watch:{
+		watch: {
 
-    },
+		},
 		mounted() {
-      if(this.listType===1){
-        this.navList=this.typeList
-      }else {
-        this.getTaskType()
-      }
+			if (this.listType === 1) {
+				this.navList = this.typeList
+			} else {
+				this.getTaskType()
+			}
 
 		},
 		methods: {
-      getTaskType() {
-        getTaskType({'numType':this.numType,'listType':this.listType}).then(res => {
-          if (res.code === 1) {
-            res.data.forEach(item=>{
-              this.navList[0].num+=item.num
-              if(this.numType>0){
-                item.name=item.product_name+'('+item.num+')'
-                this.navList[0].name=this.navList[0].product_name+'('+ this.navList[0].num+')'
-              }else {
-                item.name=item.product_name
-              }
-              this.navList.push(item)
-            })
-          }
-        })
-      },
+			getTaskType() {
+				getTaskType({
+					'numType': this.numType,
+					'listType': this.listType
+				}).then(res => {
+					if (res.code === 1) {
+						res.data.forEach(item => {
+							this.navList[0].num += item.num
+							if (this.numType > 0) {
+								item.name = item.product_name + '(' + item.num + ')'
+								this.navList[0].name = this.navList[0].product_name + '(' + this.navList[0]
+									.num + ')'
+							} else {
+								item.name = item.product_name
+							}
+							this.navList.push(item)
+						})
+					}
+				})
+			},
 
-      changeTab(current) {
-        this.$emit('setProductId', this.navList[current].id)
+			changeTab(current) {
+				this.$emit('setProductId', this.navList[current].id)
 			},
 		}
 	}
@@ -95,4 +98,4 @@
 
 <style lang="scss" scoped>
 
-</style>
+</style>

TEMPAT SAMPAH
components/.DS_Store


+ 7 - 5
components/en-utils/en-nav/en-nav.vue

@@ -2,11 +2,13 @@
 	<view class="top-nav-bg" :style="{'background-image':`url(${bgList[genre].bg_path})`}">
 		<view class="header" style="z-index: 3;" :style="[{color},{height},{paddingTop},{justifyContent:justify}]">
 			<view class="left" :style="[{color},{paddingTop}]" v-if="back" @click="onBack">
-				<image class="back" src="https://wealfavor-1257406827.cos.ap-beijing.myqcloud.com/new-xcx/tabBar/nav-back.png"></image>
+				<image class="back"
+					src="https://wealfavor-1257406827.cos.ap-beijing.myqcloud.com/new-xcx/tabBar/nav-back.png"></image>
 			</view>
 			<view class="title row center" :style="[{marginLeft:justify == 'left'?'30rpx':0},{fontSize}]">
-				<image class="wh-36 m-r20" src="https://wealfavor-1257406827.cos.ap-beijing.myqcloud.com/new-xcx/statistics/stat-calendar.png" mode="aspectFill"
-					v-if="is_icons">
+				<image class="wh-36 m-r20"
+					src="https://wealfavor-1257406827.cos.ap-beijing.myqcloud.com/new-xcx/statistics/stat-calendar.png"
+					mode="aspectFill" v-if="is_icons">
 				</image>
 				<text>{{title}}</text>
 			</view>
@@ -53,7 +55,7 @@
 					nav_path: 'https://wealfavor-1257406827.cos.ap-beijing.myqcloud.com/new-xcx/statistics/statistics-bg2.png'
 				}, {
 					bg_path: '',
-					nav_path: 'https://wealfavor-1257406827.cos.ap-beijing.myqcloud.com/new-xcx/task/task-nav-bg.png'
+					nav_path: 'https://wealfavor-1257406827.cos.ap-beijing.myqcloud.com/new-xcx/task/jb-bj.jpg'
 				}],
 			}
 		},
@@ -193,4 +195,4 @@
 	.nav-bg2 {
 		background-image: url("https://wealfavor-1257406827.cos.ap-beijing.myqcloud.com/new-xcx/index/index-bg.png?imageMogr2/quality/20");
 	}
-</style>
+</style>

+ 11 - 11
components/en-utils/en-select/en-select.vue

@@ -1,5 +1,5 @@
 <template>
-	<view class="select sys-background-fff ">
+	<view class="select sys-background-fff page-env-20">
 		<view class="title p-30 row-justify-sb">
 			<view class="icon"></view>
 			<text class="size-30 sys-weight-600">{{title}}</text>
@@ -16,7 +16,7 @@
 				</view>
 			</scroll-view>
 		</view>
-		<EnButton :is_fixed="true" text="确认"  @onSubmit="setAffirm(true)" ></EnButton>
+		<EnButton :is_fixed="true" text="确认" @onSubmit="setAffirm(true)"></EnButton>
 	</view>
 </template>
 
@@ -51,7 +51,7 @@
 		data() {
 			return {
 				current: -1,
-        newValue:0
+				newValue: 0
 			};
 		},
 		watch: {
@@ -62,14 +62,14 @@
 			}
 		},
 		methods: {
-      setAffirm(type){
-        this.$emit('input',this.newValue)
-        this.$emit('setAffirm',type)
-      },
+			setAffirm(type) {
+				this.$emit('input', this.newValue)
+				this.$emit('setAffirm', type)
+			},
 			onSelect(index) {
-        this.current=index
-        this.newValue=this.localData[index][this.itemKey]
-        this.$emit('input',this.newValue)
+				this.current = index
+				this.newValue = this.localData[index][this.itemKey]
+				this.$emit('input', this.newValue)
 			}
 		}
 	}
@@ -78,7 +78,7 @@
 <style lang="scss">
 	.select {
 		height: 800rpx;
-    border-radius: 20rpx 20rpx 0 0;
+		border-radius: 20rpx 20rpx 0 0;
 	}
 
 	.icon {

TEMPAT SAMPAH
components/sn-swiper/.DS_Store


+ 251 - 0
components/sn-swiper/esc-swiper-item/index.vue

@@ -0,0 +1,251 @@
+<template>
+	<view class="swiper-item" ref="swiper_item" :style="itemStyle">
+		<view class="item-cntent" bubble="true" @click.stop="onClick"><slot></slot></view>
+	</view>
+</template>
+
+<script>
+import { getStyleStr } from '../../utils/style.js';
+// #ifdef APP-NVUE
+const animation = uni.requireNativePlugin('animation');
+// #endif
+/**
+ * esc-swiper-item
+ * @description esc-swiper-item (不支持使用class)
+ * @property {Number} index 索引(必填)
+ * @property {Boolean} clickAny 可以点击任意项
+ * @event {Function} click 点击事件
+ */
+export default {
+	name: 'esc-swiper-item',
+	inject: ['config'],
+	props: {
+		index: {
+			type: Number,
+			default: 0
+		},
+		clickAny: {
+			type: Boolean,
+			default: false
+		}
+	},
+	data() {
+		return {
+			isAnimated: false,
+			timingFunction: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',
+			duration: 0,
+			current: 0,
+			position: 0,
+			mScale: 0,
+			canClick: true
+		};
+	},
+	created() {},
+	computed: {
+		size() {
+			return this.config.size;
+		},
+		width() {
+			return this.config.width;
+		},
+		height() {
+			return this.config.height;
+		},
+		itemWidth() {
+			return this.config.itemWidth;
+		},
+		itemHeight() {
+			return this.config.itemHeight;
+		},
+		space() {
+			return this.config.space;
+		},
+		itemStyle() {
+			if (this.index == this.size - 1) {
+				return this.rightSpaceStyle();
+			} else if (this.index == this.size - 2) {
+				return this.centerSpaceStyle();
+			} else {
+				return this.leftSpaceStyle();
+			}
+		},
+		scale() {
+			if (!this.config.is3D) {
+				return 1;
+			}
+			if (this.myCurrent == this.current) {
+				return this.mScale || this.config.scale;
+			} else {
+				return this.mScale || 1;
+			}
+		},
+		// 当前swiper-item所属current索引
+		myCurrent() {
+			if (!this.config.isCircular) {
+				return this.index;
+			}
+			const p = this.index;
+			const plus = this.config.plus;
+			const actSize = this.size - plus * 2;
+			let current = 0;
+			if (p < plus) {
+				current = p + (actSize - plus);
+			} else if (p >= this.size - plus) {
+				current = p - (actSize + plus);
+			} else {
+				current = p - plus;
+			}
+			return current;
+		}
+	},
+	methods: {
+		itemSize() {
+			let style = {
+				width: this.itemWidth + 'rpx',
+				height: (this.itemHeight || this.height) + 'rpx'
+			};
+
+			if (this.config.is3D) {
+				// #ifndef APP-NVUE
+				if (this.isAnimated) {
+					style.transition = 'transform ' + this.duration + 'ms ' + this.timingFunction;
+				} else {
+					style.transition = '';
+				}
+				style.transform = 'scale(' + this.scale + ')';
+				// #endif
+				// #ifdef APP-NVUE
+				const isIOS = uni.getSystemInfoSync().platform == 'ios';
+				if (isIOS) {
+					style.transform = 'scale(' + this.scale + ')';
+				} else {
+					if (!this.isAnimated) style.transform = 'scale(' + this.scale + ')';
+				}
+				// #endif
+			}
+
+			return style;
+		},
+		leftSpaceStyle() {
+			return getStyleStr({
+				...this.itemSize(),
+				marginLeft: this.space + 'rpx'
+			});
+		},
+		centerSpaceStyle() {
+			return getStyleStr({
+				...this.itemSize(),
+				marginLeft: this.space + 'rpx',
+				marginRight: this.space + 'rpx'
+			});
+		},
+		rightSpaceStyle() {
+			return getStyleStr({
+				...this.itemSize(),
+				marginRight: this.space + 'rpx'
+			});
+		},
+		onClick(e) {
+			if (!this.canClick) {
+				return;
+			}
+			// #ifdef APP-NVUE
+			e.stopPropagation();
+			// #endif
+			// 点击任意项
+			if (this.clickAny) {
+				this.$emit('click', e);
+				return;
+			}
+			// 只能点击当前项
+			if (this.myCurrent == this.current) {
+				this.$emit('click', e);
+			}
+		},
+		restoreScale(duration) {
+			if (!this.config.is3D) {
+				return;
+			}
+			// #ifndef APP-NVUE
+			this.duration = duration;
+			this.isAnimated = true;
+			this.mScale = 0;
+			setTimeout(() => {
+				this.duration = 0;
+				this.isAnimated = false;
+			}, duration);
+			// #endif
+			// #ifdef APP-NVUE
+			this.isAnimated = true;
+			this.mScale = 0;
+			animation.transition(
+				this.$refs['swiper_item'].ref,
+				{
+					styles: {
+						transform: 'scale(' + this.scale + ')'
+					},
+					duration, //ms
+					timingFunction: this.timingFunction,
+					needLayout: false,
+					delay: 0 //ms
+				},
+				() => {
+					this.isAnimated = false;
+				}
+			);
+			// #endif
+		},
+		restoreToScale(scale, duration) {
+			if (!this.config.is3D) {
+				return;
+			}
+			// #ifndef APP-NVUE
+			this.duration = duration;
+			this.isAnimated = true;
+			this.mScale = scale;
+			setTimeout(() => {
+				this.duration = 0;
+				this.isAnimated = false;
+				this.mScale = 0;
+			}, duration);
+			// #endif
+			// #ifdef APP-NVUE
+			this.isAnimated = true;
+			animation.transition(
+				this.$refs['swiper_item'].ref,
+				{
+					styles: {
+						transform: 'scale(' + scale + ')'
+					},
+					duration, //ms
+					timingFunction: this.timingFunction,
+					needLayout: false,
+					delay: 0 //ms
+				},
+				() => {
+					this.isAnimated = false;
+					this.mScale = 0;
+				}
+			);
+			// #endif
+		}
+	}
+};
+</script>
+
+<style scoped>
+.swiper-item {
+	position: relative;
+}
+
+.item-cntent {
+	position: absolute;
+	top: 0;
+	right: 0;
+	bottom: 0;
+	left: 0;
+	/* #ifndef APP-NVUE */
+	display: flex;
+	/* #endif */
+}
+</style>

+ 21 - 0
components/sn-swiper/esc-swiper/helper.js

@@ -0,0 +1,21 @@
+/**
+ * getSwiperList
+ * @description 获取Swiper数据
+ * @param {Array} list 原数据
+ * @param {Object} options 配置
+ * @param {Boolean} options.circular 是否循环
+ * @param {Number} options.plus 左右追加个数(开启循环必填,至少为2)
+ * @return {Array}
+ */
+export function getSwiperList(list, options = {
+	circular: true,
+	plus: 2
+}) {
+	if (!options.circular) {
+		return list
+	}
+	const plus = options.plus || 2
+	const leftPlusList = [...list].reverse().slice(0, plus).reverse();
+	const rightPlusList = [...list].slice(0, plus);
+	return [].concat(leftPlusList).concat(list).concat(rightPlusList);
+}

+ 198 - 0
components/sn-swiper/esc-swiper/index.vue

@@ -0,0 +1,198 @@
+<template>
+	<!-- #ifdef APP-VUE || H5 -->
+	<view class="box" :style="boxStyle">
+		<view
+			ref="container"
+			class="container"
+			:change:prop="swipe.changeData"
+			:prop="wxsData"
+			@touchstart.stop="swipe.touchstart"
+			@touchmove.stop="swipe.touchmove"
+			@touchend.stop="swipe.touchend"
+			:style="containerStyle"
+		>
+			<slot></slot>
+		</view>
+	</view>
+	<!-- #endif -->
+	<!-- #ifdef MP-WEIXIN -->
+	<view class="box" :style="boxStyle">
+		<view
+			ref="container"
+			class="container"
+			:change:prop="swipe.changeData"
+			:prop="wxsData"
+			@touchstart="swipe.touchstart"
+			@touchmove="swipe.touchmove"
+			@touchend="swipe.touchend"
+			:style="containerStyle"
+		>
+			<slot></slot>
+		</view>
+	</view>
+	<!-- #endif -->
+	<!-- #ifdef APP-NVUE -->
+	<view class="box" :style="boxStyle">
+		<view ref="container" class="container" @horizontalpan="touchstart" :style="containerStyle"><slot></slot></view>
+	</view>
+	<!-- #endif -->
+	<!-- 其他平台使用 js ,长列表性能可能会有影响-->
+	<!-- #ifndef APP-PLUS || MP-WEIXIN || H5 -->
+	<view class="box" @touchstart="touchstart" @touchmove="touchmove" @touchend="touchend" :style="boxStyle">
+		<view ref="container" class="container" :style="containerStyle"><slot></slot></view>
+	</view>
+	<!-- #endif -->
+</template>
+<script src="./mixins/index.wxs" module="swipe" lang="wxs"></script>
+<script>
+import SwiperMixin from './mixins/base.mixin.js';
+// #ifdef APP-VUE || MP-WEIXIN || H5
+import MpMixin from './mixins/mpwxs.js';
+// #endif
+// #ifdef APP-NVUE
+import BindingxMixin from './mixins/bindingx.js';
+// #endif
+// #ifndef APP-PLUS || MP-WEIXIN || H5
+import OtherMixin from './mixins/mpother.js';
+// #endif
+
+const mixins = [
+	// #ifdef APP-VUE || MP-WEIXIN || H5
+	MpMixin,
+	// #endif
+	// #ifdef APP-NVUE
+	BindingxMixin,
+	// #endif
+	// #ifndef APP-PLUS || MP-WEIXIN || H5
+	OtherMixin,
+	// #endif
+	SwiperMixin
+];
+
+/**
+ * esc-swiper
+ * @description 自定义swiper (不支持使用class)
+ * @property {String} mode = [normal|3d]  模式
+ * @property {Number} scale 3D模式选中项的scale
+ * @property {Number} width 宽
+ * @property {Number} height 高
+ * @property {Number} itemWidth 项宽
+ * @property {Number} itemHeight 项高
+ * @property {Number} space 间距
+ * @property {Number} plus 左右追加个数(开启循环必填,至少为2)
+ * @property {Number} current 选中项索引
+ * @property {Boolean} autoplay 自动轮播
+ * @property {Boolean} circular 是否循环,如果开启,至少需要3项
+ * @property {Boolean} bounce 阻尼效果 
+ * @event {Function} change 索引变化
+ */
+export default {
+	name: 'esc-swiper',
+	mixins,
+	props: {
+		mode: {
+			type: String,
+			default: 'normal'
+		},
+		scale: Number,
+		width: Number,
+		height: Number,
+		itemWidth: {
+			type: Number,
+			default: 0
+		},
+		itemHeight: {
+			type: Number,
+			default: 0
+		},
+		space: {
+			type: Number,
+			default: 0
+		},
+		plus: {
+			type: Number,
+			default: 0
+		},
+		autoplay: {
+			type: Boolean,
+			default: false
+		},
+		current: {
+			type: Number,
+			default: 0
+		},
+		interval: {
+			type: Number,
+			default: 5000
+		},
+		duration: {
+			type: Number,
+			default: 500
+		},
+		circular: {
+			type: Boolean,
+			default: false
+		},
+		bounce: {
+			type: Boolean,
+			default: true
+		},
+		size: {
+			type: Number,
+			default: 0
+		}
+	},
+	provide() {
+		return {
+			config: {
+				is3D: this.is3D,
+				isCircular: this.circular,
+				scale: this.scale,
+				size: this._size,
+				width: this.width,
+				height: this.height,
+				itemWidth: this.itemWidth,
+				itemHeight: this.itemHeight,
+				space: this.space,
+				plus: this.plus
+			}
+		};
+	},
+	mounted() {
+		if (this.autoplay) {
+			this.autoplayInterval = setInterval(() => {
+				this.next();
+			}, this.interval);
+		}
+	},
+	watch: {
+		autoplay(newV) {
+			if (!newV) {
+				clearInterval(this.autoplayInterval);
+			} else {
+				this.autoplayInterval = setInterval(() => {
+					this.next();
+				}, this.interval);
+			}
+		}
+	},
+	methods: {}
+};
+</script>
+
+<style scoped>
+.box {
+	position: relative;
+	overflow: hidden;
+}
+
+.container {
+	position: absolute;
+	top: 0;
+	/* #ifndef APP-NVUE */
+	display: flex;
+	/* #endif */
+	flex-direction: row;
+	align-items: center;
+}
+</style>

+ 303 - 0
components/sn-swiper/esc-swiper/mixins/base.mixin.js

@@ -0,0 +1,303 @@
+import {
+	getStyleStr
+} from '../../../utils/style.js';
+// #ifdef APP-NVUE
+const animation = uni.requireNativePlugin('animation')
+// #endif
+export default {
+	data() {
+		return {
+			timingFunction: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',
+			isAnimated: false,
+			isScrolling: false,
+			customDuration: 0,
+			left: 0,
+			mCurrent: this.current
+		};
+	},
+	created() {},
+	mounted() {
+		// #ifdef MP
+		this.swiperViews = this.$children
+		// #endif
+		// #ifdef APP-PLUS || H5
+		this.swiperViews = this.$slots.default.map(it => it.child)
+		// #endif
+		this._setLeft();
+		this.mCurrent = this.current
+		this._notifyCurrentForItems(this.current, this.position)
+	},
+	watch: {
+		mCurrent() {
+			let current = this.mCurrent
+			if (this.circular) {
+				if (this.position == 1) {
+					current = this.actualSize - (this.plus - 1)
+					// console.log('最前了', current)
+				} else if (this.position == this._size - 2) {
+					current = this.plus - 2;
+					// console.log('最后了', current)
+				}
+				if (current < 0) {
+					current = this.position + 1
+				}
+				current %= this.actualSize
+			}
+			// console.log('position', this.position, current)
+			this.$emit('update:current', current)
+			this.$emit('change', current)
+			this._notifyCurrentForItems(current, this.position)
+		}
+	},
+	computed: {
+		is3D() {
+			return this.mode == '3d'
+		},
+		position() {
+			return this.circular ? (this.mCurrent + this.plus) : this.mCurrent
+		},
+		manualDuration() {
+			if (this.customDuration > 0)
+				return this.customDuration
+			return this.isAnimated ? this.duration : 0
+		},
+		boxStyle() {
+			return getStyleStr({
+				width: this.width + 'rpx',
+				height: this.height + 'rpx'
+			});
+		},
+		containerStyle() {
+			const style = {
+				height: this.height + 'rpx'
+			};
+			// #ifdef APP-NVUE
+			// FIXME: 理论isAnimated=false应该不设置transform,但是ios有个奇怪的问题,top不为0导致布局错位
+			const isIOS = uni.getSystemInfoSync().platform == 'ios'
+			if (isIOS) {
+				style.transform = 'translate(' + uni.upx2px(this.left) + 'px' + ',0px)'
+			} else {
+				if (this.isAnimated == false) {
+					style.transform = 'translate(' + uni.upx2px(this.left) + 'px' + ',0px)'
+				}
+			}
+			// #endif
+			// #ifndef APP-NVUE
+			style.left = this.left + 'rpx'
+			style.transition = 'left ' + this.manualDuration + 'ms ' + this.timingFunction
+			// #endif
+			return getStyleStr(style);
+		},
+		_size() {
+			return (this.$slots && this.$slots.default && this.$slots.default.length) || this.size;
+		},
+		// plus * 2
+		plusSize() {
+			return this.circular ? this.plus * 2 : 0;
+		},
+		// 真实长度
+		actualSize() {
+			return this._size - this.plusSize;
+		}
+	},
+	methods: {
+		prev() {
+			if (this.isAnimated) return;
+			if (this.isScrolling) return;
+			if (this.mCurrent == 0 && this.circular == false) return
+			this.mCurrent--;
+			this._run()
+		},
+		next() {
+			if (this.isAnimated) return;
+			if (this.isScrolling) return;
+			if (this.circular == true) {
+				this.mCurrent++;
+				if (this.mCurrent == this._size) {
+					this.mCurrent = 0;
+				}
+			} else {
+				if (this.mCurrent == this._size - 1) return
+				this.mCurrent++;
+			}
+			this._run()
+		},
+		moveTo(e) {
+			if (this.isAnimated) return
+			const {
+				deltaX,
+				left
+			} = e
+			this.isScrolling = true
+			if (!this.circular) {
+				if (
+					// 第一项,不能向右滑(上一项)
+					(deltaX > 0 && this.mCurrent == 0) ||
+					// 最后一项,不能向左滑(下一项)
+					(deltaX < 0 && this.mCurrent == this._size - 1)
+				) {
+					if (!this.bounce) return
+					// 添加阻尼滑动
+					const _left = this._left || this.wxsData.left
+					this.left = _left + (deltaX * (1 - Math.abs(deltaX) * 3 / (this.width * 5)))
+					this._set3DScale(deltaX)
+					return
+				}
+			}
+			this.left = left
+			this._set3DScale(deltaX)
+		},
+		moveEnd(e) {
+			const {
+				velocity,
+				deltaX,
+				deltaY
+			} = e
+			this.isScrolling = false
+
+			if (!this.circular) {
+				// 第一项,不能向右滑(上一项)
+				if (deltaX > 0 && this.mCurrent == 0) {
+					this._restoreStartTouch()
+					return
+				}
+				// 最后一项,不能向左滑(下一项)
+				if (deltaX < 0 && this.mCurrent == this._size - 1) {
+					this._restoreStartTouch()
+					return
+				}
+			}
+
+			const isTurnPage = Math.abs(deltaX) > this.itemWidth / 2
+			if (isTurnPage || velocity > 0.2) {
+				if (deltaX < 0) {
+					this.customDuration = 350
+					this.next()
+				} else if (deltaX > 0) {
+					this.customDuration = 350
+					this.prev()
+				}
+			} else {
+				this._restoreStartTouch()
+			}
+		},
+		_set3DScale(deltaX) {
+			if (this.is3D) {
+				const min = Math.min
+				const maxScale = Math.abs(this.scale - 1)
+				const mScale = deltaX * maxScale / this.width
+				const mRealScale = min(this.scale, this.scale - Math.abs(mScale))
+				this.swiperViews[this.position].mScale = mRealScale < 1 ? 1 : mRealScale
+				if (this.position - 1 > -1) {
+					this.swiperViews[this.position - 1].mScale = mScale > 0 ? min(this.scale, 1 + mScale) : min(1, 1 + mScale)
+				}
+				if (this.position + 1 < this._size) {
+					this.swiperViews[this.position + 1].mScale = mScale > 0 ? min(1, 1 - mScale) : min(this.scale, 1 - mScale)
+				}
+			}
+		},
+		_restoreStartTouch() {
+			const self = this
+			this.customDuration = 300
+			// #ifdef APP-VUE || MP-WEIXIN || H5
+			this.left = this.wxsData.left
+			// #endif
+			// #ifndef APP-PLUS || MP-WEIXIN || H5
+			this.left = this._left
+			// #endif
+			this._run(false)
+			if (this.is3D) {
+				this.swiperViews[this.position].restoreScale(this.manualDuration)
+				if (this.position - 1 > -1) {
+					this.swiperViews[this.position - 1].restoreScale(this.manualDuration)
+				}
+				if (this.position + 1 < this._size) {
+					this.swiperViews[this.position + 1].restoreScale(this.manualDuration)
+				}
+			}
+		},
+		_notifyCurrentForItems(current, position) {
+			this.swiperViews && this.swiperViews.forEach(it => {
+				it.current = current
+				it.position = position
+			})
+		},
+		_run(isSet = true) {
+			this.isAnimated = true;
+			if (isSet)
+				this._setLeft();
+			const self = this;
+			if (this.is3D) {
+				this.swiperViews[this.position].restoreToScale(this.scale, this.manualDuration)
+				if (this.position - 1 > -1) {
+					this.swiperViews[this.position - 1].restoreToScale(1, this.manualDuration)
+				}
+				if (this.position + 1 < this._size) {
+					this.swiperViews[this.position + 1].restoreToScale(1, this.manualDuration)
+				}
+			}
+			// #ifdef APP-NVUE
+			animation.transition(this.$refs.container, {
+				styles: {
+					transform: 'translate(' + uni.upx2px(this.left) + 'px' + ',0px)',
+				},
+				duration: this.manualDuration, //ms
+				timingFunction: this.timingFunction,
+				needLayout: false,
+				delay: 0 //ms
+			}, function() {
+				self._reset();
+			})
+			// #endif
+			// #ifndef APP-NVUE
+			setTimeout(() => {
+				this._reset();
+			}, this.manualDuration);
+			// #endif
+		},
+		_setLeft() {
+			if (this.circular == true) {
+				const s1 = (this.width - this.itemWidth - this.space * 2) / 2;
+				let left = (this.plus + this.mCurrent) * (this.space + this.itemWidth) - s1;
+				this.left = -left;
+			} else {
+				this.left = -(this.itemWidth + this.space) * this.mCurrent
+			}
+			// #ifdef APP-VUE || MP-WEIXIN || H5
+			this.wxsData = {
+				left: this.left,
+				bounce: this.bounce
+			}
+			// #endif
+		},
+		_reset() {
+			this.isScrolling = false
+			this.isAnimated = false
+			this.customDuration = 0
+			if (this.circular == true) {
+				if (this.position == 1) {
+					this.mCurrent = this.actualSize - (this.plus - 1);
+					this._setLeft();
+					this._restoreScale()
+				}
+				// -2:原数组索引-1 + plus数组索引-1
+				if (this.position == this._size - 2) {
+					this.mCurrent = this.plus - 2;
+					this._setLeft();
+					this._restoreScale()
+				}
+			}
+		},
+		_restoreScale() {
+			if (this.is3D) {
+				this.swiperViews[this.position].restoreToScale(this.scale, 0)
+				if (this.position - 1 > -1) {
+					this.swiperViews[this.position - 1].restoreToScale(1, 0)
+				}
+				if (this.position + 1 < this._size) {
+					this.swiperViews[this.position + 1].restoreToScale(1, 0)
+				}
+			}
+		}
+	}
+}

+ 91 - 0
components/sn-swiper/esc-swiper/mixins/bindingx.js

@@ -0,0 +1,91 @@
+const BindingX = uni.requireNativePlugin('bindingx');
+const animation = uni.requireNativePlugin('animation');
+export default {
+	mounted() {
+		this.container = this.getEl(this.$refs['container'])
+	},
+	methods: {
+		_notifyTouchChangeForItems(isTouchEnd) {
+			this.swiperViews && this.swiperViews.forEach(it => {
+				it.canClick = isTouchEnd
+			})
+		},
+		touchstart(e) {
+			if (this.isAnimated) return;
+			if (this.stop) return
+			this.isScrolling = true
+			this._notifyTouchChangeForItems(false)
+			this.stop = true
+			this.startTime = new Date().getTime()
+			const props = [{
+				element: this.container,
+				property: 'transform.translateX',
+				expression: uni.upx2px(this.left) + '+x'
+			}]
+			if (this.is3D) {
+				const deltaScale = `${this.scale}/${this.width}*x`
+				const currentScale = `${this.scale}-abs(x)/${this.width}*1`
+				const realScale = `min(${currentScale},${this.scale})`
+				const currentCardExp = `${realScale} < 1 ? 1 : ${realScale}`;
+				const leftCardExp = `${deltaScale} > 0 ? min(${this.scale},(1 + ${deltaScale})) : min(1,(1 + ${deltaScale}))`;
+				const rightCardExp = `${deltaScale} > 0 ? min(1,(1 - ${deltaScale})) : min(${this.scale},(1 - ${deltaScale}))`;
+				props.push({
+					element: this.getSwipteItemEl(this.position),
+					property: 'transform.scale',
+					expression: currentCardExp
+				})
+				if (this.position - 1 > -1) {
+					props.push({
+						element: this.getSwipteItemEl(this.position - 1),
+						property: 'transform.scale',
+						expression: leftCardExp
+					})
+				}
+				if (this.position + 1 > this._size) {
+					props.push({
+						element: this.getSwipteItemEl(this.position + 1),
+						property: 'transform.scale',
+						expression: rightCardExp
+					})
+				}
+			}
+			this.eventpan = BindingX.bind({
+				anchor: this.container,
+				eventType: 'pan',
+				props
+			}, (e) => {
+				if (e.state === 'end' ||
+					e.state === 'exit') {
+					setTimeout(() => {
+						this._notifyTouchChangeForItems(true)
+					}, 10)
+					const timing = new Date().getTime() - this.startTime
+					const velocity = Math.abs(e.deltaX / timing)
+					this.stop = false
+					this.isScrolling = false
+					if (this.eventpan && this.eventpan.token) {
+						BindingX.unbind({
+							token: this.eventpan.token,
+							eventType: 'pan'
+						})
+					}
+					this.moveEnd({
+						velocity,
+						deltaX: e.deltaX,
+						deltaY: e.deltaY
+					})
+				}
+			})
+		},
+		getSwipteItemEl(idx) {
+			return this.swiperViews[idx].$el.ref
+		},
+		/**
+		 * 获取ref
+		 * @param {Object} el
+		 */
+		getEl(el) {
+			return el.ref
+		}
+	}
+}

+ 143 - 0
components/sn-swiper/esc-swiper/mixins/index.wxs

@@ -0,0 +1,143 @@
+var MIN_DISTANCE = 10;
+
+function changeData(newValue, oldValue, ownerInstance, instance) {
+	var state = instance.getState();
+	if (newValue.left != undefined) {
+		state.left = newValue.left
+	}
+	if (newValue.bounce != undefined) {
+		state.bounce = newValue.bounce
+	}
+	// console.log('changeData', JSON.stringify(newValue))
+}
+
+/**
+ * 开始触摸操作
+ * @param {Object} e
+ * @param {Object} ins
+ */
+function touchstart(e, ins) {
+	var instance = e.instance;
+	// 计算滑动开始位置
+	stopTouchStart(e, ins)
+}
+
+/**
+ * 开始滑动操作
+ * @param {Object} e
+ * @param {Object} ownerInstance
+ */
+function touchmove(e, ownerInstance) {
+	var instance = e.instance;
+	// 是否可以滑动页面
+	stopTouchMove(e);
+	if (e.preventDefault) {
+		// 阻止页面滚动
+		e.preventDefault()
+	}
+	// var state = instance.getState();
+	 // && state.bounce
+	move(instance, ownerInstance)
+	return false
+}
+
+/**
+ * 结束触摸操作
+ * @param {Object} e
+ * @param {Object} ownerInstance
+ */
+function touchend(e, ownerInstance) {
+	var instance = e.instance;
+	var state = instance.getState()
+	ownerInstance.callMethod('moveEnd', {
+		velocity: Math.abs(state.deltaX / state.timing),
+		direction: state.direction,
+		deltaX: state.deltaX,
+		deltaY: state.deltaY
+	})
+}
+
+/**
+ * 设置移动距离
+ * @param {Object} instance
+ * @param {Object} ownerInstance
+ */
+function move(instance, ownerInstance) {
+	var state = instance.getState()
+	var value = state.deltaX || 0
+	var state = instance.getState()
+	if (state.direction == 'horizontal') {
+		// instance.requestAnimationFrame(function() {
+		// 	instance.setStyle({
+		// 		transform: 'translateX(' + value + 'px)',
+		// 		'-webkit-transform': 'translateX(' + value + 'px)'
+		// 	})
+		// })
+		ownerInstance.callMethod('moveTo', {
+			deltaX: value,
+			deltaY: state.deltaY || 0,
+			left: state.left + value
+		})
+	}
+
+}
+
+/**
+ * 滑动中,是否禁止打开
+ * @param {Object} event
+ */
+function stopTouchMove(event) {
+	var instance = event.instance;
+	var state = instance.getState();
+	var touch = event.touches[0];
+	state.timing = getDate().getTime() - state.startTime;
+	state.deltaX = touch.clientX - state.startX;
+	state.deltaY = touch.clientY - state.startY;
+	state.offsetX = Math.abs(state.deltaX);
+	state.offsetY = Math.abs(state.deltaY);
+	state.direction = state.direction || getDirection(state.offsetX, state.offsetY);
+}
+
+/**
+ * 设置滑动开始位置
+ * @param {Object} event
+ */
+function stopTouchStart(event) {
+	var instance = event.instance;
+	var state = instance.getState();
+	resetTouchStatus(instance);
+	var touch = event.touches[0];
+	state.startTime = getDate().getTime();
+	state.startX = touch.clientX;
+	state.startY = touch.clientY;
+}
+
+function getDirection(x, y) {
+	if (x > y && x > MIN_DISTANCE) {
+		return 'horizontal';
+	}
+	if (y > x && y > MIN_DISTANCE) {
+		return 'vertical';
+	}
+	return '';
+}
+
+/**
+ * 重置滑动状态
+ * @param {Object} event
+ */
+function resetTouchStatus(instance) {
+	var state = instance.getState();
+	state.direction = '';
+	state.deltaX = 0;
+	state.deltaY = 0;
+	state.offsetX = 0;
+	state.offsetY = 0;
+}
+
+module.exports = {
+	changeData: changeData,
+	touchstart: touchstart,
+	touchmove: touchmove,
+	touchend: touchend
+}

+ 62 - 0
components/sn-swiper/esc-swiper/mixins/mpother.js

@@ -0,0 +1,62 @@
+const MIN_DISTANCE = 10;
+export default {
+	methods: {
+		touchstart(e) {
+			this._left = this.left
+			this.stopTouchStart(e)
+		},
+		touchmove(e) {
+			// 是否可以滑动页面
+			this.stopTouchMove(e);
+			if (this.direction == 'horizontal') {
+				this.moveTo({
+					deltaX: this.deltaX,
+					deltaY: this.deltaY || 0,
+					left: this._left + this.deltaX
+				})
+			}
+			// FIXME: 冒泡
+			return false
+		},
+		touchend() {
+			this.moveEnd({
+				velocity: Math.abs(this.deltaX / this.timing),
+				direction: this.direction,
+				deltaX: this.deltaX,
+				deltaY: this.deltaY,
+			})
+		},
+		stopTouchStart(event) {
+			this.resetTouchStatus();
+			const touch = event.touches[0];
+			this.startTime = new Date().getTime();
+			this.startX = touch.clientX;
+			this.startY = touch.clientY;
+		},
+		stopTouchMove(event) {
+			const touch = event.touches[0];
+			this.timing = new Date().getTime() - this.startTime;
+			this.deltaX = touch.clientX - this.startX;
+			this.deltaY = touch.clientY - this.startY;
+			this.offsetX = Math.abs(this.deltaX);
+			this.offsetY = Math.abs(this.deltaY);
+			this.direction = this.direction || this.getDirection(this.offsetX, this.offsetY);
+		},
+		getDirection(x, y) {
+			if (x > y && x > MIN_DISTANCE) {
+				return 'horizontal';
+			}
+			if (y > x && y > MIN_DISTANCE) {
+				return 'vertical';
+			}
+			return '';
+		},
+		resetTouchStatus() {
+			this.direction = '';
+			this.deltaX = 0;
+			this.deltaY = 0;
+			this.offsetX = 0;
+			this.offsetY = 0;
+		}
+	}
+}

+ 7 - 0
components/sn-swiper/esc-swiper/mixins/mpwxs.js

@@ -0,0 +1,7 @@
+export default {
+	data() {
+		return {
+			wxsData: {}
+		}
+	}
+}

+ 1 - 1
components/static/css/en-common.css

@@ -17,7 +17,7 @@
 .box {
 	/* background-color: #ffffff; */
 	border-bottom: 2rpx solid #F0F0F0;
-	padding: 32rpx 0 30rpx 0;
+	padding: 30rpx 0;
 	font-size: 32rpx;
 }
 

+ 15 - 0
components/utils/style.js

@@ -0,0 +1,15 @@
+const toLine = (name) => {
+	return name.replace(/([A-Z])/g, '-$1').toLowerCase();
+}
+/**
+ * style对象转化为style字符串
+ * @return {string}
+ */
+export const getStyleStr = (styleObject) => {
+	let transfrom = '';
+	for (let i in styleObject) {
+		let line = toLine(i);
+		transfrom += line + ':' + styleObject[i] + ';';
+	}
+	return transfrom
+}

+ 155 - 155
page_task/credit/credit.vue

@@ -1,163 +1,163 @@
 <template>
-  <view class="text-color-333" style="position: relative;left: -15rpx;">
-    <view class="property content p-30 sys-background-fff r-30">
-      <view  class=" size-26">
-        <!-- 房产信息 -->
-        <view  class="">
-          <view class="row-c">
-            <view class="dot"></view>
-            <text class="sys-weight-600 m-l10">征信信息</text>
-          </view>
-          <view class="property-box-one r-20 m-t30 p-20">
-
-            <view class="sys-from-background-color m-t20 p-20 r-20">
-              <view class="m-b20">征信照片</view>
-              <view class="row-c">
-                <!--									<image class="picture m-r20 r-10" :src="property.data.certificate_img"-->
-                <!--										mode="aspectFill">-->
-                <!--									</image>-->
-                <en-image @onShowImg="onPreviewImage" height="160rpx" :img="creditData.credit_img"></en-image>
-              </view>
-            </view>
-          </view>
-        </view>
-
-
-
-      </view>
-
-    </view>
-  </view>
+	<view class="text-color-333" style="position: relative;left: -15rpx;">
+		<view class="property content p-30 sys-background-fff r-30" :style="{height}">
+			<view class=" size-26">
+				<!-- 房产信息 -->
+				<view class="">
+					<view class="row-c">
+						<view class="dot"></view>
+						<text class="sys-weight-600 m-l10">征信信息</text>
+					</view>
+					<view class="property-box-one r-20 m-t30 p-20">
+
+						<view class="sys-from-background-color m-t20 p-20 r-20">
+							<view class="m-b20">征信照片</view>
+							<view class="row-c">
+								<!--									<image class="picture m-r20 r-10" :src="property.data.certificate_img"-->
+								<!--										mode="aspectFill">-->
+								<!--									</image>-->
+								<en-image @onShowImg="onPreviewImage" :img="creditData.credit_img"></en-image>
+							</view>
+						</view>
+					</view>
+				</view>
+			</view>
+		</view>
+	</view>
 </template>
 
 <script>
-import EnImage from "@/components/en-utils/en-image/en-image.vue";
-import EnBlank from "@/components/en-utils/en-blank/en-blank.vue";
-
-export default {
-  name:'credit-list',
-  components: {
-    EnBlank,
-    EnImage
-  },
-  props: {
-    creditData: {
-      default: {
-        'credit_img':[]
-      }
-    },
-    creditKey: {
-      default: []
-    }
-  },
-  data() {
-    return {}
-  },
-  methods: {
-    onPreviewImage(url) {
-      this.$emit('onShowImg', url)
-    },
-    verifyKey(key) {
-      return this.creditKey.indexOf(key) >= 0
-    }
-  },
-}
+	import EnImage from "@/components/en-utils/en-image/en-image.vue";
+	import EnBlank from "@/components/en-utils/en-blank/en-blank.vue";
+
+	export default {
+		name: 'credit-list',
+		components: {
+			EnBlank,
+			EnImage
+		},
+		props: {
+			height: {
+				type: String,
+				default: '100%'
+			},
+			creditData: {
+				default: {
+					'credit_img': []
+				}
+			},
+			creditKey: {
+				default: []
+			}
+		},
+		data() {
+			return {}
+		},
+		methods: {
+			onPreviewImage(url) {
+				this.$emit('onShowImg', url)
+			},
+			verifyKey(key) {
+				return this.creditKey.indexOf(key) >= 0
+			}
+		},
+	}
 </script>
 
 <style lang="scss" scoped>
-.property {
-  padding-top: 90rpx;
-  background-image: url('/page_task/static/img/task-details/bg-four.png');
-  background-repeat: no-repeat;
-  background-size: 100% auto;
-
-
-  .bg-box {
-    width: 710rpx;
-    height: 80rpx;
-  }
-
-  .dot {
-    width: 12rpx;
-    height: 12rpx;
-    border-radius: 50%;
-    background: #0FB160;
-  }
-}
-
-.property-box-one {
-  position: relative;
-  background: linear-gradient(161deg, #B0FDB3 0%, #5EDE9D 100%);
-
-}
-
-.property-box-two {
-  position: relative;
-  background: linear-gradient(161deg, #FFE2E2 0%, #FBCACA 100%);
-
-  .two-text {
-    background: #D06565;
-  }
-
-  .picture {
-    width: 160rpx;
-    height: 190rpx;
-    display: block;
-  }
-}
-
-.property-box-three {
-  position: relative;
-  background: linear-gradient(161deg, #CDE7FD 0%, #B6D2FB 100%);
-
-  .two-text {
-    background: #2E86F4;
-  }
-
-  .picture {
-    width: 160rpx;
-    height: 90rpx;
-    display: block;
-    border-radius: 10rpx;
-  }
-}
-
-.property-box-four {
-  position: relative;
-  background: linear-gradient(161deg, #F4DCBD 0%, #EEC398 100%);
-
-  .two-text {
-    background: #C37929;
-  }
-
-  .picture {
-    width: 160rpx;
-    height: 90rpx;
-    display: block;
-    border-radius: 10rpx;
-  }
-}
-
-.property-icon {
-  position: absolute;
-  right: 40rpx;
-  top: 12rpx;
-}
-
-.capsule {
-  color: #fff;
-  height: 50rpx;
-  line-height: 50rpx;
-  border-radius: 99rpx;
-  background: #0FB160;
-  text-align: center;
-  padding: 0 20rpx;
-  margin-right: 10rpx;
-}
-
-.card {
-  width: 160rpx;
-  height: 90rpx;
-  display: block;
-}
+	.property {
+		padding-top: 90rpx;
+		background-image: url('/page_task/static/img/task-details/bg-four.png');
+		background-repeat: no-repeat;
+		background-size: 100% auto;
+		overflow: hidden;
+
+		.bg-box {
+			width: 710rpx;
+			height: 80rpx;
+		}
+
+		.dot {
+			width: 12rpx;
+			height: 12rpx;
+			border-radius: 50%;
+			background: #0FB160;
+		}
+	}
+
+	.property-box-one {
+		position: relative;
+		background: linear-gradient(161deg, #B0FDB3 0%, #5EDE9D 100%);
+
+	}
+
+	.property-box-two {
+		position: relative;
+		background: linear-gradient(161deg, #FFE2E2 0%, #FBCACA 100%);
+
+		.two-text {
+			background: #D06565;
+		}
+
+		.picture {
+			width: 160rpx;
+			height: 190rpx;
+			display: block;
+		}
+	}
+
+	.property-box-three {
+		position: relative;
+		background: linear-gradient(161deg, #CDE7FD 0%, #B6D2FB 100%);
+
+		.two-text {
+			background: #2E86F4;
+		}
+
+		.picture {
+			width: 160rpx;
+			height: 90rpx;
+			display: block;
+			border-radius: 10rpx;
+		}
+	}
+
+	.property-box-four {
+		position: relative;
+		background: linear-gradient(161deg, #F4DCBD 0%, #EEC398 100%);
+
+		.two-text {
+			background: #C37929;
+		}
+
+		.picture {
+			width: 160rpx;
+			height: 90rpx;
+			display: block;
+			border-radius: 10rpx;
+		}
+	}
+
+	.property-icon {
+		position: absolute;
+		right: 40rpx;
+		top: 12rpx;
+	}
+
+	.capsule {
+		color: #fff;
+		height: 50rpx;
+		line-height: 50rpx;
+		border-radius: 99rpx;
+		background: #0FB160;
+		text-align: center;
+		padding: 0 20rpx;
+		margin-right: 10rpx;
+	}
+
+	.card {
+		width: 160rpx;
+		height: 90rpx;
+		display: block;
+	}
 </style>

+ 6 - 1
page_task/task_details/module/apply.vue

@@ -1,6 +1,6 @@
 <template>
 	<view class="text-color-333" style="position: relative;left: -15rpx;">
-		<view class="content p-30 sys-background-fff r-30 size-26 apply">
+		<view class="content p-30 sys-background-fff r-30 size-26 apply" :style="{height}">
 			<view class="">
 				<view class="row-c">
 					<view class="dot"></view>
@@ -120,6 +120,10 @@
 <script>
 	export default {
 		props: {
+			height: {
+				type: String,
+				default: '100%'
+			},
 			applyFor: {
 				default: {}
 			},
@@ -158,6 +162,7 @@
 		background-image: url('/page_task/static/img/task-details/bg-two.png');
 		background-repeat: no-repeat;
 		background-size: 100% auto;
+		overflow: hidden;
 
 		.bg-box {
 			width: 710rpx;

+ 15 - 7
page_task/task_details/module/identity.vue

@@ -1,6 +1,6 @@
 <template>
 	<view class="text-color-12 " style="position: relative;left: -15rpx;">
-		<view class="content p-30 sys-background-fff r-30 identity">
+		<view class="content p-30 sys-background-fff r-30 identity" :style="{height}">
 			<view class="row-c">
 				<view class="dot"></view>
 				<text class="size-26 sys-weight-600 m-l10">基本信息</text>
@@ -14,7 +14,8 @@
 					</view>
 				</view>
 				<view class="row-c m-t20" v-if="verifyKey('identity_one')">
-					<en-image @onShowImg="onPreviewImage" :img="[business.identity_one,business.identity_two]"></en-image>
+					<en-image @onShowImg="onPreviewImage"
+						:img="[business.identity_one,business.identity_two]"></en-image>
 				</view>
 				<view class="row-c m-b20" v-if="verifyKey('id_number')">
 					<image class="wh-30 m-r20" src="/page_task/static/img/task-details/number.png" mode="aspectFill">
@@ -78,13 +79,15 @@
 					<view class="size-24 text-color-333 m-b20" v-if="verifyKey('marriage_type')">
 						{{business.marriage_name}}
 					</view>
-					<en-image @onShowImg="onPreviewImage" v-if="business.marriage_type>1" :img=" business.marriage_img"></en-image>
+					<en-image @onShowImg="onPreviewImage" v-if="business.marriage_type>1"
+						:img=" business.marriage_img"></en-image>
 					<view v-if="business.marriage_type===2">
 						<view class="spouse p-l20">
 							<text class="size-24">配偶信息</text>
 						</view>
 						<view class="row-c m-t20" v-if="verifyKey('m_identity_one')">
-							<en-image @onShowImg="onPreviewImage" :img="[business.m_identity_one,business.m_identity_two]"></en-image>
+							<en-image @onShowImg="onPreviewImage"
+								:img="[business.m_identity_one,business.m_identity_two]"></en-image>
 						</view>
 						<view class="">
 							<text class="size-26 sys-weight-600"
@@ -154,6 +157,10 @@
 
 	export default {
 		props: {
+			height: {
+				type: String,
+				default: '100%'
+			},
 			business: {
 				default: {}
 			},
@@ -174,9 +181,9 @@
 			return {}
 		},
 		methods: {
-      onPreviewImage(url){
-        this.$emit('onShowImg',url)
-      },
+			onPreviewImage(url) {
+				this.$emit('onShowImg', url)
+			},
 			verifyKey(key, type) {
 				if (type !== 2) {
 					return this.clientKey.indexOf(key) >= 0
@@ -195,6 +202,7 @@
 		background-image: url('/page_task/static/img/task-details/bg-one.png');
 		background-repeat: no-repeat;
 		background-size: 100% auto;
+		overflow: hidden;
 
 		.bg-box {
 			width: 710rpx;

+ 17 - 9
page_task/task_details/module/property.vue

@@ -1,6 +1,6 @@
 <template>
 	<view class="text-color-333" style="position: relative;left: -15rpx;">
-		<view class="property content p-30 sys-background-fff r-30">
+		<view class="property content p-30 sys-background-fff r-30" :style="{height}">
 			<view v-for="(property,propertyKey) in propertyList" class=" size-26">
 				<!-- 房产信息 -->
 				<view v-if="property.property_type===1" class="">
@@ -48,7 +48,8 @@
 									<!--									<image class="picture m-r20 r-10" :src="property.data.certificate_img"-->
 									<!--										mode="aspectFill">-->
 									<!--									</image>-->
-									<en-image @onShowImg="onPreviewImage" :img="property.data.certificate_img"></en-image>
+									<en-image @onShowImg="onPreviewImage"
+										:img="property.data.certificate_img"></en-image>
 								</view>
 							</view>
 							<view class="sys-from-background-color m-t20 p-20 r-20"
@@ -136,18 +137,21 @@
 								<view class="row-c" v-if="verifyKey('register_img')">
 									<view class="">
 										<text class="size-24">行驶证</text>
-										<en-image @onShowImg="onPreviewImage" :img="property.data.register_img"></en-image>
+										<en-image @onShowImg="onPreviewImage"
+											:img="property.data.register_img"></en-image>
 									</view>
 									<view class="m-l20" v-if="verifyKey('driving_img')">
 										<text class="size-24">车辆登记证</text>
-										<en-image  @onShowImg="onPreviewImage":img="property.data.driving_img"></en-image>
+										<en-image @onShowImg="onPreviewImage"
+											:img="property.data.driving_img"></en-image>
 									</view>
 								</view>
 								<view class="row-c" v-if="verifyKey('car_img')">
 									<view class="">
 										<view class="size-24 m-t20">车辆照片</view>
 										<view class="row-c m-t10" style="flex-wrap: wrap;">
-											<en-image @onShowImg="onPreviewImage" :img="property.data.car_img"></en-image>
+											<en-image @onShowImg="onPreviewImage"
+												:img="property.data.car_img"></en-image>
 										</view>
 									</view>
 								</view>
@@ -225,6 +229,10 @@
 			EnImage
 		},
 		props: {
+			height: {
+				type: String,
+				default: '100%'
+			},
 			propertyList: {
 				default: []
 			},
@@ -236,9 +244,9 @@
 			return {}
 		},
 		methods: {
-      onPreviewImage(url){
-        this.$emit('onShowImg',url)
-      },
+			onPreviewImage(url) {
+				this.$emit('onShowImg', url)
+			},
 			verifyKey(key) {
 				return this.propertyKey.indexOf(key) >= 0
 			}
@@ -252,7 +260,7 @@
 		background-image: url('/page_task/static/img/task-details/bg-four.png');
 		background-repeat: no-repeat;
 		background-size: 100% auto;
-
+		overflow: hidden;
 
 		.bg-box {
 			width: 710rpx;

+ 13 - 6
page_task/task_details/module/third_party.vue

@@ -1,6 +1,6 @@
 <template>
 	<view class="text-color-333" style="position: relative;left: -15rpx;">
-		<view class="apply content p-30 sys-background-fff r-30">
+		<view class="apply content p-30 sys-background-fff r-30" :style="{height}">
 			<view class="size-26" v-for="(item,index) in tripartite">
 				<view class="">
 					<view class="row-c">
@@ -20,7 +20,8 @@
 							</view>
 						</view>
 						<view class="row-c m-t20" v-if="verifyKey('identity_one')">
-							<EnImage @onShowImg="onPreviewImage" :list="[item.identity_one,item.identity_two]"></EnImage>
+							<EnImage @onShowImg="onPreviewImage" :list="[item.identity_one,item.identity_two]">
+							</EnImage>
 						</view>
 					</view>
 					<!-- 担保类型 -->
@@ -37,7 +38,8 @@
 							<text class="text-color-666">营业执照编号</text>
 							<text class="m-l16">{{item.id_number}}</text>
 						</view>
-						<EnImage @onShowImg="onPreviewImage" v-if="verifyKey('identity_one')" :list="[item.identity_one]"></EnImage>
+						<EnImage @onShowImg="onPreviewImage" v-if="verifyKey('identity_one')"
+							:list="[item.identity_one]"></EnImage>
 					</view>
 				</view>
 				<view class="m-t20 color-4A2600">
@@ -69,6 +71,10 @@
 			EnImage
 		},
 		props: {
+			height: {
+				type: String,
+				default: '100%'
+			},
 			tripartite: {
 				default: []
 			},
@@ -80,9 +86,9 @@
 			return {}
 		},
 		methods: {
-      onPreviewImage(url){
-        this.$emit('onShowImg',url)
-      },
+			onPreviewImage(url) {
+				this.$emit('onShowImg', url)
+			},
 			verifyKey(key) {
 				return this.tripartiteKey.indexOf(key) >= 0
 			}
@@ -96,6 +102,7 @@
 		background-image: url('/page_task/static/img/task-details/bg-three.png');
 		background-repeat: no-repeat;
 		background-size: 100% auto;
+		overflow: hidden;
 
 		.bg-box {
 			width: 710rpx;

+ 150 - 26
page_task/task_details/task_details.vue

@@ -7,7 +7,8 @@
 					</image>
 					<view class="detail-content">
 						<view class="row-c m-l10 m-t16 m-b20 text-color-fff">
-							<image class="wh-45 r-circle" src="/page_task/static/img/task-details/head.png" mode="aspectFill">
+							<image class="wh-45 r-circle" src="/page_task/static/img/task-details/head.png"
+								mode="aspectFill">
 							</image>
 							<text class="size-28 m-lr16">{{business.name}}</text>
 							<view class="size-24 row-c sys-bg-007038 r-100 p-lr16 p-tb6">
@@ -45,25 +46,33 @@
 		<view class="sys-list-background-color task-tabs-width page-env-160"
 			:style="{top:`${$tools.topHeight()+fixedHeight}px`}">
 			<view class="m-t20">
-				<z-swiper ref="zSwiper" v-model="list" :options="options" v-if="current === 1">
+				<z-swiper ref="zSwiper" v-model="list" :options="options" v-if="current === 1" @slideChange="onSwiper">
 					<z-swiper-item>
-						<Identity :business="business" :linkman="linkman" :client-key="clientKey" @onShowImg="onShowImg"
-							:linkman-key="linkmanKey"></Identity>
+						<Identity ref="identity" class="identity" :height="`${maxHeight-60}px`" :business="business"
+							:linkman="linkman" :client-key="clientKey" @onShowImg="onShowImg" :linkman-key="linkmanKey">
+						</Identity>
 					</z-swiper-item>
 					<z-swiper-item v-show="product.product_types.indexOf('3')>=0">
-						<Apply :apply-for="applyFor" :applies-key="appliesKey" @onShowImg="onShowImg"></Apply>
+						<Apply class="apply" :height="`${maxHeight-60}px`" :apply-for="applyFor"
+							:applies-key="appliesKey" @onShowImg="onShowImg">
+						</Apply>
 					</z-swiper-item>
 					<z-swiper-item v-show="product.product_types.indexOf('4')>=0">
-						<ThirdParty :tripartite="tripartite" :tripartite-key="tripartiteKey" @onShowImg="onShowImg"></ThirdParty>
+						<ThirdParty class="third_party" :height="`${maxHeight-60}px`" :tripartite="tripartite"
+							:tripartite-key="tripartiteKey" @onShowImg="onShowImg">
+						</ThirdParty>
 					</z-swiper-item>
 				</z-swiper>
 				<view class="" v-if="current === 2">
-					<z-swiper v-model="lists" :options="options">
+					<z-swiper v-model="lists" :options="options" @slideChange="onCreditSwiper">
 						<z-swiper-item>
-							<Property :property-list="propertyList" :property-key="propertyKey" @onShowImg="onShowImg"></Property>
+							<Property class="property" :height="`${creditHeight-60}px`" :property-list="propertyList"
+								:property-key="propertyKey" @onShowImg="onShowImg">
+							</Property>
 						</z-swiper-item>
 						<z-swiper-item>
-							<credit-list :credit-data="credit" :credit-key="creditKey" @onShowImg="onShowImg"></credit-list>
+							<credit-list class="credit_list" :height="`${creditHeight-60}px`" :credit-data="credit"
+								:credit-key="creditKey" @onShowImg="onShowImg"></credit-list>
 						</z-swiper-item>
 					</z-swiper>
 				</view>
@@ -122,11 +131,11 @@
 		takeTask
 	} from "@/api/task";
 	import EnSelect from "@/components/en-utils/en-select/en-select.vue";
-  import CreditList from "@/page_task/credit/credit.vue";
+	import CreditList from "@/page_task/credit/credit.vue";
 
 	export default {
 		components: {
-      CreditList,
+			CreditList,
 			EnSelect,
 			TaskTab,
 			Identity,
@@ -139,13 +148,13 @@
 		data() {
 			return {
 				current: 1,
-				fixedHeight: 195,
+				fixedHeight: 200,
 				options: {
-					autoHeight: true,
+					// autoHeight: true,
 					slidesPerView: 1.1,
 					centeredSlides: true,
 					spaceBetween: 10,
-          effect: 'cards'
+					effect: 'cards'
 				},
 				list: [1, 2, 3],
 				lists: [1, 2],
@@ -156,7 +165,7 @@
 					type_data: []
 				},
 				business: {
-          id:0,
+					id: 0,
 					status: 0,
 					phone: '',
 					identity_one: '',
@@ -199,6 +208,13 @@
 				tripartiteKey: [],
 				linkmanKey: [],
 				creditKey: [],
+				identityHeight: 0,
+				thirdPartHeight: 0,
+				applyHeight: 0,
+				propertyHeight: 0,
+				creditListHeight: 0,
+				maxHeight: '100%',
+				creditHeight: '100%',
 			}
 		},
 		onLoad(data) {
@@ -215,16 +231,16 @@
 			}
 		},
 		methods: {
-      onShowImg(item) {
-        if (this.businessFile.imgList.length < 0) {
-          this.businessFile.imgList.push(item)
-        }
-        // 预览图片
-        uni.previewImage({
-          current: item,
-          urls: this.businessFile.imgList,
-        });
-      },
+			onShowImg(item) {
+				if (this.businessFile.imgList.length < 0) {
+					this.businessFile.imgList.push(item)
+				}
+				// 预览图片
+				uni.previewImage({
+					current: item,
+					urls: this.businessFile.imgList,
+				});
+			},
 			auditTask(auditType) {
 				uni.navigateTo({
 					url: '/page_task/task_operate/task_operate?businessId=' + this.business.id +
@@ -365,6 +381,7 @@
 					this.businessFile = res.data.businessFile
 					this.$refs.recordObj.startData()
 					this.$refs.zSwiper.swiper
+					this.getDetailsHeight()
 					this.setShowKey()
 				} else {
 					tools.leftClick()
@@ -394,9 +411,116 @@
 					}
 				})
 			},
-
+			// 获取详情高度Swiper
+			getDetailsHeight() {
+				this.maxHeight = '100%'
+				this.$nextTick(() => {
+					const query = uni.createSelectorQuery().in(this);
+					setTimeout(() => {
+						query.select('.identity').boundingClientRect(data => {
+							if (data) {
+								this.maxHeight = data.height
+								console.log(this.maxHeight);
+								uni.pageScrollTo({
+									scrollTop: 0,
+									duration: 300
+								});
+							}
+						}).exec();
+					}, 200)
+				})
+			},
+			// 获取详情高度Swiper
+			onSwiper(swiper, index) {
+				this.maxHeight = '100%'
+				const query = uni.createSelectorQuery().in(this);
+				this.$nextTick(() => {
+					setTimeout(() => {
+						if (index == 0) {
+							query.select('.identity').boundingClientRect(data => {
+								if (data) {
+									this.maxHeight = data.height
+								}
+							}).exec();
+						}
+						if (index == 1) {
+							query.select('.apply').boundingClientRect(data => {
+								if (data) {
+									this.maxHeight = data.height
+								}
+							}).exec();
+						}
+						if (index == 2) {
+							query.select('.third_party').boundingClientRect(data => {
+								if (data) {
+									this.maxHeight = data.height
+								}
+							}).exec();
+						}
+						uni.pageScrollTo({
+							scrollTop: 0,
+							duration: 300
+						});
+					}, 200)
+				})
+			},
+			// 征信高度Swiper
+			onCreditHeight() {
+				this.creditHeight = '100%'
+				this.$nextTick(() => {
+					const query = uni.createSelectorQuery().in(this);
+					setTimeout(() => {
+						query.select('.property').boundingClientRect(data => {
+							if (data) {
+								this.creditHeight = data.height
+								uni.pageScrollTo({
+									scrollTop: 0,
+									duration: 300
+								});
+							}
+						}).exec();
+					}, 200)
+				})
+			},
+			// 征信高度Swiper
+			onCreditSwiper(swiper, index) {
+				this.creditHeight = '100%'
+				const query = uni.createSelectorQuery().in(this);
+				this.$nextTick(() => {
+					setTimeout(() => {
+						if (index == 0) {
+							query.select('.property').boundingClientRect(data => {
+								if (data) {
+									this.creditHeight = data.height
+								}
+							}).exec();
+						}
+						if (index == 1) {
+							query.select('.credit_list').boundingClientRect(data => {
+								if (data) {
+									this.creditHeight = data.height
+								}
+							}).exec();
+						}
+						uni.pageScrollTo({
+							scrollTop: 0,
+							duration: 300
+						});
+					}, 200)
+				})
+			},
 			onChange(current) {
+				if (current == 1) {
+					this.getDetailsHeight()
+				}
+				if (current == 2) {
+					this.onCreditHeight()
+				}
 				this.current = current
+				uni.pageScrollTo({
+					scrollTop: 0,
+					duration: 300
+				});
 			},
 			onSubmit() {
 				uni.navigateTo({

+ 14 - 0
pages.json

@@ -60,6 +60,20 @@
       "style": {
         "navigationBarTextStyle": "white"
       }
+    },
+    {
+    	"path" : "pages/loan/module/rate_compare",
+    	"style" : 
+    	{
+    		"navigationBarTitleText" : ""
+    	}
+    },
+    {
+    	"path" : "pages/loan/module/perfect_rate",
+    	"style" : 
+    	{
+    		"navigationBarTitleText" : ""
+    	}
     }
   ],
   "subPackages": [

+ 1 - 1
pages/index/index.vue

@@ -20,7 +20,7 @@
 				</view>
 			</view>
 		</view>
-		<view class="page-box-bg-fff m-t30 m-lr20 m-b20 r-30 row-justify-sb flex p-b30">
+		<view class="page-box-bg-fff m-t30 m-lr20 r-30 row-justify-sb flex p-b30">
 			<view class="">
 				<view class="row-c">
 					<view class="pie_chart column-c">

+ 18 - 20
pages/index/module/index_column.vue

@@ -73,27 +73,25 @@
 		<view class="column_item m-l20 column-justify-sb">
 			<view class="task_item row-justify center r-30" @click="onGetTask">
 				<view class="m-l40" style="position: relative;">
-					<image class="wh-90 r-circle m-r20"
-						src="https://wealfavor-1257406827.cos.ap-beijing.myqcloud.com/new-xcx/index/index-task.png"
-						mode="">
-					</image>
-					<view class="task_num text-color-fff size-20" style="" v-if="memberData.take_num>0">
-						{{memberData.take_num}}
-					</view>
+					<uni-badge class="" :text="memberData.take_num" absolute="rightTop" size="normal" :offset="[5,5]">
+						<image class="wh-90 r-circle"
+							src="https://wealfavor-1257406827.cos.ap-beijing.myqcloud.com/new-xcx/index/index-task.png"
+							mode="">
+						</image>
+					</uni-badge>
 				</view>
-				<text class="text-color-12 sys-weight-600">领取任务</text>
+				<text class="text-color-12 sys-weight-600 m-l20">领取任务</text>
 			</view>
 			<view class="task_message row-justify center r-30" @click="onMessage">
 				<view class="m-l40" style="position: relative;">
-					<image class="wh-90 r-circle m-r20"
-						src="https://wealfavor-1257406827.cos.ap-beijing.myqcloud.com/new-xcx/index/index-money.png"
-						mode="">
-					</image>
-					<view class="task_num text-color-fff size-20" style="" v-if="memberData.noticeNum>0">
-						{{Number(memberData.noticeNum)>99?'99+':memberData.noticeNum}}
-					</view>
+					<uni-badge class="" :text="memberData.noticeNum" absolute="rightTop" size="normal" :offset="[5,5]">
+						<image class="wh-90 r-circle"
+							src="https://wealfavor-1257406827.cos.ap-beijing.myqcloud.com/new-xcx/index/index-money.png"
+							mode="">
+						</image>
+					</uni-badge>
 				</view>
-				<text class="text-color-12 sys-weight-600">任务消息</text>
+				<text class="text-color-12 sys-weight-600 m-l20">任务消息</text>
 			</view>
 		</view>
 	</view>
@@ -260,12 +258,12 @@
 	}
 
 	.task_num {
-		border-radius: 40rpx;
-		background: red;
-		padding: 2rpx 10rpx;
+		// border-radius: 40rpx;
+		// background: red;
+		// padding: 2rpx 10rpx;
 		position: absolute;
 		right: 10rpx;
 		top: -10rpx;
-		line-height: 30rpx;
+		// line-height: 30rpx;
 	}
 </style>

+ 71 - 0
pages/loan/components/column-item.vue

@@ -0,0 +1,71 @@
+<template>
+	<view class="" :class="is_center?'column-center':'column-box'">
+		<view class="m-t30">
+			<text class="center-text">8万</text>
+			<view class="text">贷款金额</view>
+		</view>
+		<view class="m-t30">
+			<text class="center-text">23.76%</text>
+			<view class="text">年利率</view>
+		</view>
+		<view class="m-t30">
+			<text class="center-text">8万</text>
+			<view class="text">贷款期数</view>
+		</view>
+		<view class="m-t30">
+			<text class="center-text">8万</text>
+			<view class="text">每期还款金额</view>
+		</view>
+		<view class="m-t30">
+			<text class="center-text">8万</text>
+			<view class="text">总利息</view>
+		</view>
+		<view class="m-t30">
+			<text class="center-text">8万</text>
+			<view class="text">总还款金额</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		props: {
+			is_center: {
+				type: Boolean,
+				default: false
+			}
+		},
+		data() {
+			return {}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.column-box {
+		color: #333;
+		font-size: 28rpx;
+
+		.text {
+			color: #666;
+			font-size: 24rpx;
+			padding-top: 6rpx;
+		}
+	}
+
+	.column-center {
+		color: #333;
+		font-size: 28rpx;
+
+		.center-text {
+			color: #F91517;
+			font-weight: 600;
+		}
+
+		.text {
+			color: #666;
+			font-size: 24rpx;
+			padding-top: 6rpx;
+		}
+	}
+</style>

+ 44 - 2
pages/loan/loan.vue

@@ -13,10 +13,33 @@
 			</view>
 			<EnScroll ref="scroll" :navHeight="100" is_tabHeight @onRefresh="onRefresh"
 				@onScrollBottom="onScrollBottom">
-				<LoanItem :task-list="list"></LoanItem>
+				<LoanItem :task-list="list" @onCalculate="onCalculate"></LoanItem>
 			</EnScroll>
 			<Tab :tab-index="2"></Tab>
+			<uni-popup ref="popup" type="bottom" @touchmove.stop.prevent="moveHandle">
+				<view class="page-env-160 sys-background-fff r-20">
+					<view class="row-justify-sb center p-lr30 p-t30">
+						<view class="wh-25"></view>
+						<text class="size-30 sys-weight-600">核算年利率</text>
+						<image class="wh-25" src="/page_task/static/img/task-details/close.png" mode="aspectFill"
+							@click="onClose">
+						</image>
+					</view>
+					<view class="p-30 size-28 m-b50">
+						<view class="">
+							<text>当前年利率</text><text class="sys-weight-600 color-FF730E m-l10">23.7%</text>
+						</view>
+						<view class="row-justify-sb center r-10 sys-from-background-color p-30 m-t30">
+							<input class="flex" type="text" placeholder="请输入新年利率" />
+							<text class="text-color-12">%</text>
+						</view>
+					</view>
+					<EnButton text="确认核算" @onSubmit=""></EnButton>
+				</view>
+			</uni-popup>
 		</view>
+		<EnButton :is_both="1" leftText="利率对比" rightText="完善贷后" @onLeftSubmit="onRateCompare" @onSubmit="onPerfectRate">
+		</EnButton>
 	</view>
 
 </template>
@@ -112,6 +135,25 @@
 				}, 1000)
 				console.log("到底部了");
 			},
+			onRateCompare() {
+				uni.navigateTo({
+					url: "/pages/loan/module/rate_compare"
+				})
+			},
+			onPerfectRate() {
+				uni.navigateTo({
+					url: "/pages/loan/module/perfect_rate"
+				})
+			},
+			onCalculate() {
+				this.$refs.popup.open('bottom')
+			},
+			onClose() {
+				this.$refs.popup.close('bottom')
+			},
+			moveHandle() {
+				return false
+			}
 		},
 	}
 </script>
@@ -136,4 +178,4 @@
 	//   }
 
 	// }
-</style>
+</style>

+ 49 - 0
pages/loan/module/perfect_rate.vue

@@ -0,0 +1,49 @@
+<template>
+	<view class="total-page page_env-160 page-box sys-list-background-color task-bg">
+		<Nav title="完善贷后" :genre="1" is_fixed></Nav>
+		<view class="m-20">
+			<view class="sys-background-fff p-lr20 p-t20 r-30">
+				<view class="size-28 sys-weight-600">贷款信息</view>
+				<en-select label="贷款银行" v-model="number" :local-data="enterpriseTypeArr"
+					placeholder="请选择贷款银行"></en-select>
+				<en-input label="贷款金额" type="number" v-model="number" placeholder="请输入贷款金额" rightText="万元"></en-input>
+				<en-input label="贷款期数" type="number" v-model="number" placeholder="请输入贷款期数"></en-input>
+				<en-input label="年化利率" :is_border="false" type="number" v-model="number" placeholder="请输入年化利率"
+					rightText="%"></en-input>
+			</view>
+			<view class="sys-background-fff m-t20 p-lr20 p-t20 r-30">
+				<view class="size-28 sys-weight-600">贷款信息</view>
+				<en-select label="还款方式" v-model="number" :local-data="enterpriseTypeArr"
+					placeholder="请选择还款方式"></en-select>
+				<en-input label="还款日期" type="number" v-model="number" placeholder="请输入还款日期"></en-input>
+				<en-input label="还款金额" :is_border="false" type="number" v-model="number" placeholder="请输入还款金额"
+					rightText="元"></en-input>
+			</view>
+		</view>
+		<EnButton text="保存" @onSubmit="onSave"></EnButton>
+	</view>
+</template>
+
+<script>
+	import EnInput from "@/components/en-from/en-input/en-input.vue";
+	import EnSelect from "@/components/en-from/en-select/en-select.vue";
+
+	export default {
+		components: {
+			EnInput,
+			EnSelect
+		},
+		data() {
+			return {
+				number: ''
+			}
+		},
+		methods: {
+			onSave() {}
+		}
+	}
+</script>
+
+<style>
+
+</style>

+ 155 - 0
pages/loan/module/rate_compare.vue

@@ -0,0 +1,155 @@
+<template>
+	<view class="total-page page_env-20 page-box sys-list-background-color task-bg">
+		<Nav title="利率对比" :genre="1" is_fixed></Nav>
+		<view class="m-20 text-color-12">
+			<view class="sys-background-fff p-30 r-30">
+				<view class="center">
+					<image class="wh-60" src="/static/img/task/task-else.png" mode="aspectFill"></image>
+					<text class="size-28 sys-weight-600 m-l20">垫资还款</text>
+				</view>
+				<view class="row-justify-sb center sys-from-background-color p-tb30 p-lr40 r-30 m-t30">
+					<view class="column-c">
+						<view class="size-26 m-b10">张三</view>
+						<text class="size-24 text-color-666">贷款人姓名</text>
+					</view>
+					<view class="column-c">
+						<view class="size-26 m-b10">{{hidePhone('17717888888')}}</view>
+						<text class="size-24 text-color-666">联系方式</text>
+					</view>
+					<view class="column-c">
+						<view class="size-26 m-b10">2024-08-08</view>
+						<text class="size-24 text-color-666">申请时间</text>
+					</view>
+				</view>
+			</view>
+			<view class="sys-background-fff p-tb30 r-30 m-t20">
+				<view class="container">
+					<view class="box left-box">
+						<text class="title">核算前年利率</text>
+						<text class="number">23.27%</text>
+					</view>
+					<view class="box middle-box">
+						<text class="title">差值</text>
+						<text class="number">5.76%</text>
+					</view>
+					<view class="box right-box">
+						<text class="title">核算后年利率</text>
+						<text class="number">18.00%</text>
+					</view>
+				</view>
+				<view class="p-lr30 flex row-justify-sb">
+					<view class="list-item">
+						<ColumnItem></ColumnItem>
+					</view>
+					<view class="list-item">
+						<ColumnItem is_center></ColumnItem>
+					</view>
+					<view class="list-item">
+						<ColumnItem></ColumnItem>
+					</view>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import tools from "@/service/tools";
+	import ColumnItem from "../components/column-item.vue"
+
+	export default {
+		components: {
+			ColumnItem
+		},
+		data() {
+			return {}
+		},
+		methods: {
+			hidePhone(phone) {
+				return tools.hidePhone(phone)
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.list-item {
+		flex: 1;
+		text-align: center;
+	}
+
+	.container {
+		display: flex;
+		justify-content: space-between;
+	}
+
+	.box {
+		position: relative;
+		flex: 1;
+		height: 125rpx;
+		background-color: red;
+		text-align: center;
+		color: #fff;
+		flex-direction: column;
+		display: flex;
+		justify-content: center;
+
+		.title {
+			font-size: 26rpx
+		}
+
+		.number {
+			font-size: 28;
+			font-weight: 600;
+			padding-top: 10rpx;
+		}
+	}
+
+	.left-box {
+		position: relative;
+		right: -15px;
+		border-radius: 20rpx 0 0 20rpx;
+		clip-path: polygon(0% 0%, 100% 0%, 80% 100%, 0% 100%);
+		background: linear-gradient(270deg, #FEA666 0%, #F9503D 100%);
+	}
+
+	.middle-box {
+		position: relative;
+		clip-path: polygon(20% 0%, 80% 0%, 100% 100%, 0% 100%);
+		background: linear-gradient(270deg, #745CFF 0%, #3993FD 100%);
+	}
+
+	.right-box {
+
+		position: relative;
+		left: -15px;
+		border-radius: 0 20rpx 20rpx 0;
+		clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 20% 100%);
+		background: linear-gradient(270deg, #F9503D 0%, #FEA666 100%);
+
+	}
+
+	.box::before {
+		content: '';
+		position: absolute;
+		top: -15rpx;
+		left: -15rpx;
+		right: -15rpx;
+		bottom: -15rpx;
+		border: 15rpx solid #f0f0f0;
+		border-radius: 20rpx;
+		pointer-events: none;
+	}
+
+	.left-box::before {
+		clip-path: polygon(0% 0%, 100% 0%, 90% 100%, 0% 100%);
+	}
+
+	.middle-box::before {
+		clip-path: polygon(20% 0%, 80% 0%, 100% 100%, 0% 100%);
+	}
+
+	.right-box::before {
+		clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 20% 100%);
+	}
+</style>

+ 452 - 406
pages/login/index.vue

@@ -1,411 +1,457 @@
 <template>
-  <view class="page-box login-box"  >
-      <view class="login-form sys-radius-50" >
-        <view class="from-title">
-          <view class="title-list">
-            <view class="title-text  sys-size-28 text-center sys-weight-600" :class="{'default-text':type===1}" @click="setType(1)">验证码登录</view>
-            <view class="title-text sys-size-28 text-center sys-weight-600" :class="{'default-text':type===2}" @click="setType(2)">密码登录</view>
-          </view>
-          <view class="title-bg sys-background-fff" :class="{'title-bg-two':type===2}">
-            <view class="bg-icon sys-background-dominant "></view>
-          </view>
-        </view>
-        <view class="from-box sys-background-fff " :class="{'one-from':type===1,'two-from':type===2}">
-          <view class="from-animation animate__animated animate__fadeIn"  v-if="type===1" >
-            <view class="input-item  sys-from-background-color sys-radius-30 " :class="{'apply-shake':phoneShake}">
-              <en-input type="number" class="login-input" placeholder="请输入手机号"  v-model="loginData.phone" maxlength="11"></en-input>
-            </view>
-            <view class="input-item  input-send sys-from-background-color sys-radius-30" :class="{'apply-shake':codedShake}">
-              <en-input type="number" class="login-input" placeholder="请输入验证码"  v-model="loginData.code" maxlength="6"></en-input>
-              <view class="login-send 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>
-          </view>
-          <view class="from-animation animate__animated animate__fadeIn" v-else>
-            <view class="input-item  sys-from-background-color sys-radius-30 " :class="{'apply-shake':phoneShake}">
-              <en-input type="number" class="login-input" placeholder="请输入手机号"  v-model="loginData.phone" maxlength="11"></en-input>
-            </view>
-            <view class="input-item input-send  sys-from-background-color sys-radius-30" :class="{'apply-shake':passwordShake}">
-              <en-input type="password" class="login-input" placeholder="请输入密码"  v-model="loginData.password"></en-input>
-              <view class="login-send text-color-dominant sys-size-28 sys-weight-400" @click="goToUrl(2)" >忘记密码?</view>
-            </view>
-          </view>
-          <agreement v-model="isConsent" ref="agreement"></agreement>
-
-          <view
-              class="input-but sys-background-dominant text-color-fff sys-size-30 sys-radius-100 sys-weight-600"
-              :class="{'sys-selected-but':isLogin,'sys-unselected-but':!isLogin}"
-              @click="login"
-          >登陆</view>
-        </view>
-      </view>
-      <view class="register-box">
-        <view class="register-text sys-weight-400 sys-size-24 text-color-666">还没有账号?</view>
-        <view class="register-text text-color-dominant sys-size-24 sys-weight-400" @click="goToUrl(1)">立即注册</view>
-      </view>
-      <view class="wx-box">
-        <view class="wx-title">
-          <view class="wx-wire"></view>
-          <view class="wx-text sys-size-24 text-color-7c sys-weight-400">第三方登录</view>
-          <view class="wx-wire"></view>
-        </view>
-        <image @click="wxLogin" class="wx-logo" src="https://wealfavor-1257406827.cos.ap-beijing.myqcloud.com/new-xcx/login/wx-img.png" mode="aspectFill"></image>
-      </view>
-
-
-  </view>
+	<view class="page-box login-box">
+		<view class="login-form sys-radius-50">
+			<view class="from-title">
+				<view class="title-list">
+					<view class="title-text  sys-size-28 text-center sys-weight-600" :class="{'default-text':type===1}"
+						@click="setType(1)">验证码登录</view>
+					<view class="title-text sys-size-28 text-center sys-weight-600" :class="{'default-text':type===2}"
+						@click="setType(2)">密码登录</view>
+				</view>
+				<view class="title-bg sys-background-fff" :class="{'title-bg-two':type===2}">
+					<view class="bg-icon sys-background-dominant "></view>
+				</view>
+			</view>
+			<view class="from-box sys-background-fff " :class="{'one-from':type===1,'two-from':type===2}">
+				<view class="from-animation animate__animated animate__fadeIn" v-if="type===1">
+					<view class="input-item  sys-from-background-color sys-radius-30 "
+						:class="{'apply-shake':phoneShake}">
+						<en-input type="number" class="login-input" placeholder="请输入手机号" v-model="loginData.phone"
+							maxlength="11"></en-input>
+					</view>
+					<view class="input-item  input-send sys-from-background-color sys-radius-30"
+						:class="{'apply-shake':codedShake}">
+						<en-input type="number" class="login-input" placeholder="请输入验证码" v-model="loginData.code"
+							maxlength="6"></en-input>
+						<view class="login-send 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>
+				</view>
+				<view class="from-animation animate__animated animate__fadeIn" v-else>
+					<view class="input-item  sys-from-background-color sys-radius-30 "
+						:class="{'apply-shake':phoneShake}">
+						<en-input type="number" class="login-input" placeholder="请输入手机号" v-model="loginData.phone"
+							maxlength="11"></en-input>
+					</view>
+					<view class="input-item input-send  sys-from-background-color sys-radius-30"
+						:class="{'apply-shake':passwordShake}">
+						<en-input type="password" class="login-input" placeholder="请输入密码"
+							v-model="loginData.password"></en-input>
+						<view class="login-send text-color-dominant sys-size-28 sys-weight-400" @click="goToUrl(2)">
+							忘记密码?</view>
+					</view>
+				</view>
+				<agreement v-model="isConsent" ref="agreement"></agreement>
+
+				<view class="input-but sys-background-dominant text-color-fff sys-size-30 sys-radius-100 sys-weight-600"
+					:class="{'sys-selected-but':isLogin,'sys-unselected-but':!isLogin}" @click="login">登陆</view>
+			</view>
+		</view>
+		<view class="register-box">
+			<view class="register-text sys-weight-400 sys-size-24 text-color-666">还没有账号?</view>
+			<view class="register-text text-color-dominant sys-size-24 sys-weight-400" @click="goToUrl(1)">立即注册</view>
+		</view>
+		<view class="wx-box">
+			<view class="wx-title">
+				<view class="wx-wire"></view>
+				<view class="wx-text sys-size-24 text-color-7c sys-weight-400">第三方登录</view>
+				<view class="wx-wire"></view>
+			</view>
+			<image @click="wxLogin" class="wx-logo"
+				src="https://wealfavor-1257406827.cos.ap-beijing.myqcloud.com/new-xcx/login/wx-img.png"
+				mode="aspectFill"></image>
+		</view>
+
+
+	</view>
 </template>
 <script>
-  import {commonSend} from "@/api/common";
-  import tools from "@/service/tools";
-  import {login, wxLogin} from "@/api/login";
-  import EnInput from "@/components/en-from/en-input/index.vue";
-  import Agreement from "@/pages/login/model/agreement.vue";
-
-  export default {
-    components: {
-      Agreement,
-      EnInput
-
-    },
-    data() {
-      return {
-        type:1,
-        loginData: {
-          phone: '13900139110',
-          password: '',
-          code: '1234',
-        },
-        phoneShake:false,
-        passwordShake:false,
-        codedShake:false,
-        consentShake:false,
-        isConsent:false,
-        isLogin:false,
-        timeNum: 0,
-        timer: null,
-      }
-    },
-    watch:{
-      'loginData': {
-        handler() {
-          this.verifyData()
-        },
-        deep: true
-      },
-      'isConsent':function () {
-        this.verifyData()
-      }
-    },
-    mounted() {
-
-    },
-    methods: {
-      wxLogin() {
-        if (!this.isConsent) {
-          tools.error('请阅读并同意协议');
-          this.$refs.agreement.setConsentShake()
-          return;
-        }
-        if (this.isAjax) {
-          return false;
-        }
-        this.isAjax = true;
-        uni.login({
-          provider: "weixin",
-          success: (loginRes) =>{
-            wxLogin({'code':loginRes.code}).then((res)=>{
-              if(res.code===403){
-                tools.error('当前用户未注册')
-                uni.setStorageSync('openid',res.data.openid)
-                setTimeout(()=>{
-                  //跳转至注册页面
-                  uni.navigateTo({
-                    url: '/pages/login/register'
-                  });
-                },1500)
-              }else if(res.code===1){
-                tools.setLoginData(res.data,true)
-              }else {
-                tools.error(res.msg)
-              }
-              this.isAjax = false;
-            })
-          }
-        })
-
-
-      },
-      goToUrl(type){
-        if(type===1){
-          uni.navigateTo({
-            'url':'/pages/login/register'
-          })
-        }else if(type===2){
-          uni.navigateTo({
-            'url':'/pages/login/forget'
-          })
-        }else {
-
-        }
-      },
-      setShake(type){
-        if(type===1){
-          this.phoneShake=true
-        }else if(type===2){
-          this.passwordShake=true
-        }else if(type===3){
-          this.codedShake=true
-        }else if(type===4){
-          this.$refs.agreement.setConsentShake()
-        }
-        setTimeout(()=>{
-          this.phoneShake=false
-          this.passwordShake=false
-          this.codedShake=false
-        },500)
-      },
-      login() {
-        if (this.loginData.phone === '') {
-          tools.error('请输入手机号码');
-          this.setShake(1)
-          return;
-        }
-        if (this.type !== 1) {
-          if (this.loginData.password === '') {
-            this.setShake(2)
-            tools.error('请输入登陆密码');
-            return;
-          }
-        } else {
-          if (this.loginData.code === '') {
-            this.setShake(3)
-            tools.error('请输入验证码');
-            return;
-          }
-        }
-        if (!this.isConsent) {
-          this.setShake(4)
-          tools.error('请阅读并同意协议');
-          return;
-        }
-        login(this.loginData).then((res) => {
-          if (res.code === 1) {
-            tools.setLoginData(res.data, true)
-          } else {
-            tools.error(res.msg)
-          }
-        })
-      },
-      verifyData(){
-        if(this.type===1){
-          this.isLogin=this.loginData.phone!=='' && this.loginData.code!=='' && this.isConsent
-        }else {
-          this.isLogin=this.loginData.phone!=='' && this.loginData.password!=='' && this.isConsent
-        }
-      },
-      getVerifiedCode() {
-        if (this.timeNum > 0) {
-          return;
-        }
-        if (this.loginData.phone === '') {
-          tools.error("请输入手机号码")
-          return;
-        }
-        let regPhone = /^(?:(?:\+|00)86)?1\d{10}$/;
-        if (!regPhone.test(this.loginData.phone)) {
-          tools.error("手机号码格式错误")
-          return;
-        }
-        commonSend({
-          'phone': this.loginData.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);
-          }
-        })
-      },
-      setIsConsent(){
-        this.isConsent=!this.isConsent
-      },
-      setType(type){
-        if(type!==this.type){
-          this.type=type
-        }
-      },
-
-    },
-  }
+	import {
+		commonSend
+	} from "@/api/common";
+	import tools from "@/service/tools";
+	import {
+		login,
+		wxLogin
+	} from "@/api/login";
+	import EnInput from "@/components/en-from/en-input/index.vue";
+	import Agreement from "@/pages/login/model/agreement.vue";
+
+	export default {
+		components: {
+			Agreement,
+			EnInput
+
+		},
+		data() {
+			return {
+				type: 1,
+				loginData: {
+					phone: '18623152213',
+					password: '',
+					code: '1234',
+				},
+				phoneShake: false,
+				passwordShake: false,
+				codedShake: false,
+				consentShake: false,
+				isConsent: false,
+				isLogin: false,
+				timeNum: 0,
+				timer: null,
+			}
+		},
+		watch: {
+			'loginData': {
+				handler() {
+					this.verifyData()
+				},
+				deep: true
+			},
+			'isConsent': function() {
+				this.verifyData()
+			}
+		},
+		mounted() {
+
+		},
+		methods: {
+			wxLogin() {
+				if (!this.isConsent) {
+					tools.error('请阅读并同意协议');
+					this.$refs.agreement.setConsentShake()
+					return;
+				}
+				if (this.isAjax) {
+					return false;
+				}
+				this.isAjax = true;
+				uni.login({
+					provider: "weixin",
+					success: (loginRes) => {
+						wxLogin({
+							'code': loginRes.code
+						}).then((res) => {
+							if (res.code === 403) {
+								tools.error('当前用户未注册')
+								uni.setStorageSync('openid', res.data.openid)
+								setTimeout(() => {
+									//跳转至注册页面
+									uni.navigateTo({
+										url: '/pages/login/register'
+									});
+								}, 1500)
+							} else if (res.code === 1) {
+								tools.setLoginData(res.data, true)
+							} else {
+								tools.error(res.msg)
+							}
+							this.isAjax = false;
+						})
+					}
+				})
+
+
+			},
+			goToUrl(type) {
+				if (type === 1) {
+					uni.navigateTo({
+						'url': '/pages/login/register'
+					})
+				} else if (type === 2) {
+					uni.navigateTo({
+						'url': '/pages/login/forget'
+					})
+				} else {
+
+				}
+			},
+			setShake(type) {
+				if (type === 1) {
+					this.phoneShake = true
+				} else if (type === 2) {
+					this.passwordShake = true
+				} else if (type === 3) {
+					this.codedShake = true
+				} else if (type === 4) {
+					this.$refs.agreement.setConsentShake()
+				}
+				setTimeout(() => {
+					this.phoneShake = false
+					this.passwordShake = false
+					this.codedShake = false
+				}, 500)
+			},
+			login() {
+				if (this.loginData.phone === '') {
+					tools.error('请输入手机号码');
+					this.setShake(1)
+					return;
+				}
+				if (this.type !== 1) {
+					if (this.loginData.password === '') {
+						this.setShake(2)
+						tools.error('请输入登陆密码');
+						return;
+					}
+				} else {
+					if (this.loginData.code === '') {
+						this.setShake(3)
+						tools.error('请输入验证码');
+						return;
+					}
+				}
+				if (!this.isConsent) {
+					this.setShake(4)
+					tools.error('请阅读并同意协议');
+					return;
+				}
+				login(this.loginData).then((res) => {
+					if (res.code === 1) {
+						tools.setLoginData(res.data, true)
+					} else {
+						tools.error(res.msg)
+					}
+				})
+			},
+			verifyData() {
+				if (this.type === 1) {
+					this.isLogin = this.loginData.phone !== '' && this.loginData.code !== '' && this.isConsent
+				} else {
+					this.isLogin = this.loginData.phone !== '' && this.loginData.password !== '' && this.isConsent
+				}
+			},
+			getVerifiedCode() {
+				if (this.timeNum > 0) {
+					return;
+				}
+				if (this.loginData.phone === '') {
+					tools.error("请输入手机号码")
+					return;
+				}
+				let regPhone = /^(?:(?:\+|00)86)?1\d{10}$/;
+				if (!regPhone.test(this.loginData.phone)) {
+					tools.error("手机号码格式错误")
+					return;
+				}
+				commonSend({
+					'phone': this.loginData.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);
+					}
+				})
+			},
+			setIsConsent() {
+				this.isConsent = !this.isConsent
+			},
+			setType(type) {
+				if (type !== this.type) {
+					this.type = type
+				}
+			},
+
+		},
+	}
 </script>
 <style lang="scss" scoped>
-  @import "/static/css/login.css";
-  .page-box{
-    position: relative;
-    padding-top: 370rpx;
-    .login-form{
-      margin: 0 30rpx;
-      border-radius: 50rpx;
-      background-color: rgba(255,255,255,0.5);
-      .from-title{
-        border-radius: 50rpx 50rpx 0 0;
-        height: 110rpx;
-        position: relative;
-
-        .title-bg{
-          position: absolute;
-          border-radius: 50rpx 50rpx 0 0;
-          top: 0;
-          left: 0;
-          height:110rpx;
-          width: 50%;
-          transition: .5s ease;
-          z-index: 0;
-          .bg-icon{
-            margin: 86rpx auto 0 auto;
-            width: 36rpx;
-            height: 6rpx;
-            border-radius: 99rpx;
-          }
-        }
-        .title-bg-two{
-          left: 50%;
-          transition: .5s ease;
-        }
-        //.title-bg::after {
-        //  content: '';
-        //  position: absolute;
-        //  right: -50rpx;
-        //  bottom: 0;
-        //  width: 50rpx;
-        //  height:50rpx;
-        //  background-color: #fff;
-        //  border-radius: 50rpx 50rpx  50rpx 0;
-        //}
-        .title-list{
-          display: flex;
-          justify-content: space-between;
-          z-index: 1;
-          position: relative;
-          .title-text{
-            width: 50%;
-            height: 110rpx;
-            line-height: 102rpx;
-            color: #333333;
-          }
-          .default-text{
-            color: #10B261;
-          }
-          .title-text:first-child{
-            background-image: url("https://wealfavor-1257406827.cos.ap-beijing.myqcloud.com/new-xcx/login/right.png");
-            background-repeat: no-repeat;
-            background-position: right bottom;
-          }
-          .title-text:last-child{
-            background-image: url("https://wealfavor-1257406827.cos.ap-beijing.myqcloud.com/new-xcx/login/left.png");
-            background-repeat: no-repeat;
-            background-position: left bottom;
-          }
-        }
-
-      }
-      .from-box{
-        box-shadow: 0rpx 4rpx 36rpx 0rpx rgba(196,196,196,0.25);
-        border-radius: 0 0 50rpx 50rpx;
-        padding: 50rpx 45rpx;
-        .input-item{
-          height: 96rpx;
-          padding: 28rpx 40rpx;
-          box-sizing: border-box;
-          .login-input{
-            width: 100%;
-          }
-        }
-        .input-item:last-child{
-          margin-top: 30rpx;
-        }
-        .input-send{
-          display: flex;
-          justify-content: flex-start;
-          align-items: center;
-          .login-input{
-            width:calc(100% - 140rpx) ;
-          }
-          .login-send{
-            width: 140rpx;
-            text-align: center;
-          }
-        }
-
-        .input-but{
-          margin-top: 40rpx;
-          width: 100%;
-          height: 96rpx;
-          line-height: 96rpx;
-          text-align: center;
-        }
-      }
-      .one-from{
-        border-radius: 0 50rpx 50rpx 50rpx;
-        transition: 0.85s ease;
-      }
-      .two-from{
-        border-radius:  50rpx 0 50rpx 50rpx;
-        transition: 0.85s ease;
-      }
-    }
-    .register-box{
-      display: flex;
-      justify-content: center;
-      align-items: center;
-      margin-top: 60rpx;
-      .register-text{
-        height: 34rpx;
-        line-height: 34rpx;
-      }
-      .register-text:last-child{
-        margin-right: 5rpx;
-      }
-    }
-    .wx-box{
-      position: absolute;
-      bottom: calc(60rpx + env(safe-area-inset-bottom));
-      left: 0;
-      width: 100%;
-      display: flex;
-      flex-direction: column;
-      justify-content: center;
-      .wx-title{
-        display: flex;
-        justify-content: center;
-        align-items: center;
-        .wx-wire{
-          background: linear-gradient( 90deg, #D9D9D9 0%, rgba(115,115,115,0) 100%);
-          width: 174rpx;
-          height: 2rpx;
-          border-radius: 50%;
-        }
-        .wx-text{
-          margin: 0 15rpx;
-          height: 34rpx;
-          line-height: 34rpx;
-        }
-      }
-      .wx-logo{
-        margin: 25rpx auto 0 auto;
-        width: 80rpx;
-        height: 80rpx;
-      }
-    }
-
-  }
-</style>
+	@import "/static/css/login.css";
+
+	.page-box {
+		position: relative;
+		padding-top: 370rpx;
+
+		.login-form {
+			margin: 0 30rpx;
+			border-radius: 50rpx;
+			background-color: rgba(255, 255, 255, 0.5);
+
+			.from-title {
+				border-radius: 50rpx 50rpx 0 0;
+				height: 110rpx;
+				position: relative;
+
+				.title-bg {
+					position: absolute;
+					border-radius: 50rpx 50rpx 0 0;
+					top: 0;
+					left: 0;
+					height: 110rpx;
+					width: 50%;
+					transition: .5s ease;
+					z-index: 0;
+
+					.bg-icon {
+						margin: 86rpx auto 0 auto;
+						width: 36rpx;
+						height: 6rpx;
+						border-radius: 99rpx;
+					}
+				}
+
+				.title-bg-two {
+					left: 50%;
+					transition: .5s ease;
+				}
+
+				//.title-bg::after {
+				//  content: '';
+				//  position: absolute;
+				//  right: -50rpx;
+				//  bottom: 0;
+				//  width: 50rpx;
+				//  height:50rpx;
+				//  background-color: #fff;
+				//  border-radius: 50rpx 50rpx  50rpx 0;
+				//}
+				.title-list {
+					display: flex;
+					justify-content: space-between;
+					z-index: 1;
+					position: relative;
+
+					.title-text {
+						width: 50%;
+						height: 110rpx;
+						line-height: 102rpx;
+						color: #333333;
+					}
+
+					.default-text {
+						color: #10B261;
+					}
+
+					.title-text:first-child {
+						background-image: url("https://wealfavor-1257406827.cos.ap-beijing.myqcloud.com/new-xcx/login/right.png");
+						background-repeat: no-repeat;
+						background-position: right bottom;
+					}
+
+					.title-text:last-child {
+						background-image: url("https://wealfavor-1257406827.cos.ap-beijing.myqcloud.com/new-xcx/login/left.png");
+						background-repeat: no-repeat;
+						background-position: left bottom;
+					}
+				}
+
+			}
+
+			.from-box {
+				box-shadow: 0rpx 4rpx 36rpx 0rpx rgba(196, 196, 196, 0.25);
+				border-radius: 0 0 50rpx 50rpx;
+				padding: 50rpx 45rpx;
+
+				.input-item {
+					height: 96rpx;
+					padding: 28rpx 40rpx;
+					box-sizing: border-box;
+
+					.login-input {
+						width: 100%;
+					}
+				}
+
+				.input-item:last-child {
+					margin-top: 30rpx;
+				}
+
+				.input-send {
+					display: flex;
+					justify-content: flex-start;
+					align-items: center;
+
+					.login-input {
+						width: calc(100% - 140rpx);
+					}
+
+					.login-send {
+						width: 140rpx;
+						text-align: center;
+					}
+				}
+
+				.input-but {
+					margin-top: 40rpx;
+					width: 100%;
+					height: 96rpx;
+					line-height: 96rpx;
+					text-align: center;
+				}
+			}
+
+			.one-from {
+				border-radius: 0 50rpx 50rpx 50rpx;
+				transition: 0.85s ease;
+			}
+
+			.two-from {
+				border-radius: 50rpx 0 50rpx 50rpx;
+				transition: 0.85s ease;
+			}
+		}
+
+		.register-box {
+			display: flex;
+			justify-content: center;
+			align-items: center;
+			margin-top: 60rpx;
+
+			.register-text {
+				height: 34rpx;
+				line-height: 34rpx;
+			}
+
+			.register-text:last-child {
+				margin-right: 5rpx;
+			}
+		}
+
+		.wx-box {
+			position: absolute;
+			bottom: calc(60rpx + env(safe-area-inset-bottom));
+			left: 0;
+			width: 100%;
+			display: flex;
+			flex-direction: column;
+			justify-content: center;
+
+			.wx-title {
+				display: flex;
+				justify-content: center;
+				align-items: center;
+
+				.wx-wire {
+					background: linear-gradient(90deg, #D9D9D9 0%, rgba(115, 115, 115, 0) 100%);
+					width: 174rpx;
+					height: 2rpx;
+					border-radius: 50%;
+				}
+
+				.wx-text {
+					margin: 0 15rpx;
+					height: 34rpx;
+					line-height: 34rpx;
+				}
+			}
+
+			.wx-logo {
+				margin: 25rpx auto 0 auto;
+				width: 80rpx;
+				height: 80rpx;
+			}
+		}
+
+	}
+</style>

+ 8 - 8
pages/task/task.vue

@@ -9,8 +9,8 @@
 					:current="current" :bar-animate-mode="'worm'" @change="tabsChange" />
 			</view>
 		</view>
-    <task-ima-tab @setProductId="setProductId" :num-type="0"></task-ima-tab>
-		<EnScroll ref="scroll" :navHeight="105" is_tabHeight @onRefresh="onRefresh" @onScrollBottom="onScrollBottom">
+		<task-ima-tab @setProductId="setProductId" :num-type="0"></task-ima-tab>
+		<EnScroll ref="scroll" :navHeight="190" is_tabHeight @onRefresh="onRefresh" @onScrollBottom="onScrollBottom">
 			<view v-if="current===1">
 				<TaskItem :type="5" is_bottom :task-list="backlogList"></TaskItem>
 			</view>
@@ -103,15 +103,15 @@
 
 			},
 			getDayBacklogList() {
-        if (this.isAjax || (this.totalNum <= this.backlogList.length)) {
-          return;
-        }
+				if (this.isAjax || (this.totalNum <= this.backlogList.length)) {
+					return;
+				}
 				getDayBacklogList({
 					'selectStr': this.selectStr
 				}).then((res) => {
 					if (res.code === 1) {
 						this.backlogList = res.data.items
-            this.totalNum = res.data.totalNum
+						this.totalNum = res.data.totalNum
 					}
 				})
 			},
@@ -143,7 +143,7 @@
 			tabsChange(index) {
 				if (index !== this.current) {
 					this.current = index;
-          this.startList()
+					this.startList()
 				}
 			},
 			// 下拉刷新
@@ -166,4 +166,4 @@
 </script>
 <style lang="scss" scoped>
 
-</style>
+</style>

+ 4 - 0
static/css/common.css

@@ -198,6 +198,10 @@
 	padding-top: 40rpx;
 }
 
+.p-t60 {
+	padding-top: 60rpx;
+}
+
 .p-r10 {
 	padding-right: 10rpx;
 }

File diff ditekan karena terlalu besar
+ 3 - 5
uni_modules/qiun-data-charts/components/qiun-error/qiun-error.vue


+ 33 - 0
uni_modules/uni-badge/changelog.md

@@ -0,0 +1,33 @@
+## 1.2.2(2023-01-28)
+- 修复 运行/打包 控制台警告问题
+## 1.2.1(2022-09-05)
+- 修复 当 text 超过 max-num 时,badge 的宽度计算是根据 text 的长度计算,更改为 css 计算实际展示宽度,详见:[https://ask.dcloud.net.cn/question/150473](https://ask.dcloud.net.cn/question/150473)
+## 1.2.0(2021-11-19)
+- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
+- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-badge](https://uniapp.dcloud.io/component/uniui/uni-badge)
+## 1.1.7(2021-11-08)
+- 优化 升级ui
+- 修改 size 属性默认值调整为 small
+- 修改 type 属性,默认值调整为 error,info 替换 default
+## 1.1.6(2021-09-22)
+- 修复 在字节小程序上样式不生效的 bug
+## 1.1.5(2021-07-30)
+- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
+## 1.1.4(2021-07-29)
+- 修复 去掉 nvue 不支持css 的 align-self 属性,nvue 下不暂支持 absolute 属性
+## 1.1.3(2021-06-24)
+- 优化 示例项目
+## 1.1.1(2021-05-12)
+- 新增 组件示例地址
+## 1.1.0(2021-05-12)
+- 新增 uni-badge 的 absolute 属性,支持定位
+- 新增 uni-badge 的 offset 属性,支持定位偏移
+- 新增 uni-badge 的 is-dot 属性,支持仅显示有一个小点
+- 新增 uni-badge 的 max-num 属性,支持自定义封顶的数字值,超过 99 显示99+
+- 优化 uni-badge 属性 custom-style, 支持以对象形式自定义样式
+## 1.0.7(2021-05-07)
+- 修复 uni-badge 在 App 端,数字小于10时不是圆形的bug
+- 修复 uni-badge 在父元素不是 flex 布局时,宽度缩小的bug
+- 新增 uni-badge 属性 custom-style, 支持自定义样式
+## 1.0.6(2021-02-04)
+- 调整为uni_modules目录规范

+ 268 - 0
uni_modules/uni-badge/components/uni-badge/uni-badge.vue

@@ -0,0 +1,268 @@
+<template>
+	<view class="uni-badge--x">
+		<slot />
+		<text v-if="text" :class="classNames" :style="[positionStyle, customStyle, dotStyle]"
+			class="uni-badge" @click="onClick()">{{displayValue}}</text>
+	</view>
+</template>
+
+<script>
+	/**
+	 * Badge 数字角标
+	 * @description 数字角标一般和其它控件(列表、9宫格等)配合使用,用于进行数量提示,默认为实心灰色背景
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=21
+	 * @property {String} text 角标内容
+	 * @property {String} size = [normal|small] 角标内容
+	 * @property {String} type = [info|primary|success|warning|error] 颜色类型
+	 * 	@value info 灰色
+	 * 	@value primary 蓝色
+	 * 	@value success 绿色
+	 * 	@value warning 黄色
+	 * 	@value error 红色
+	 * @property {String} inverted = [true|false] 是否无需背景颜色
+	 * @property {Number} maxNum 展示封顶的数字值,超过 99 显示 99+
+	 * @property {String} absolute = [rightTop|rightBottom|leftBottom|leftTop] 开启绝对定位, 角标将定位到其包裹的标签的四角上
+	 * 	@value rightTop 右上
+	 * 	@value rightBottom 右下
+	 * 	@value leftTop 左上
+	 * 	@value leftBottom 左下
+	 * @property {Array[number]} offset	距定位角中心点的偏移量,只有存在 absolute 属性时有效,例如:[-10, -10] 表示向外偏移 10px,[10, 10] 表示向 absolute 指定的内偏移 10px
+	 * @property {String} isDot = [true|false] 是否显示为一个小点
+	 * @event {Function} click 点击 Badge 触发事件
+	 * @example <uni-badge text="1"></uni-badge>
+	 */
+
+	export default {
+		name: 'UniBadge',
+		emits: ['click'],
+		props: {
+			type: {
+				type: String,
+				default: 'error'
+			},
+			inverted: {
+				type: Boolean,
+				default: false
+			},
+			isDot: {
+				type: Boolean,
+				default: false
+			},
+			maxNum: {
+				type: Number,
+				default: 99
+			},
+			absolute: {
+				type: String,
+				default: ''
+			},
+			offset: {
+				type: Array,
+				default () {
+					return [0, 0]
+				}
+			},
+			text: {
+				type: [String, Number],
+				default: ''
+			},
+			size: {
+				type: String,
+				default: 'small'
+			},
+			customStyle: {
+				type: Object,
+				default () {
+					return {}
+				}
+			}
+		},
+		data() {
+			return {};
+		},
+		computed: {
+			width() {
+				return String(this.text).length * 8 + 12
+			},
+			classNames() {
+				const {
+					inverted,
+					type,
+					size,
+					absolute
+				} = this
+				return [
+					inverted ? 'uni-badge--' + type + '-inverted' : '',
+					'uni-badge--' + type,
+					'uni-badge--' + size,
+					absolute ? 'uni-badge--absolute' : ''
+				].join(' ')
+			},
+			positionStyle() {
+				if (!this.absolute) return {}
+				let w = this.width / 2,
+					h = 10
+				if (this.isDot) {
+					w = 5
+					h = 5
+				}
+				const x = `${- w  + this.offset[0]}px`
+				const y = `${- h + this.offset[1]}px`
+
+				const whiteList = {
+					rightTop: {
+						right: x,
+						top: y
+					},
+					rightBottom: {
+						right: x,
+						bottom: y
+					},
+					leftBottom: {
+						left: x,
+						bottom: y
+					},
+					leftTop: {
+						left: x,
+						top: y
+					}
+				}
+				const match = whiteList[this.absolute]
+				return match ? match : whiteList['rightTop']
+			},
+			dotStyle() {
+				if (!this.isDot) return {}
+				return {
+					width: '10px',
+					minWidth: '0',
+					height: '10px',
+					padding: '0',
+					borderRadius: '10px'
+				}
+			},
+			displayValue() {
+				const {
+					isDot,
+					text,
+					maxNum
+				} = this
+				return isDot ? '' : (Number(text) > maxNum ? `${maxNum}+` : text)
+			}
+		},
+		methods: {
+			onClick() {
+				this.$emit('click');
+			}
+		}
+	};
+</script>
+
+<style lang="scss" >
+	$uni-primary: #2979ff !default;
+	$uni-success: #4cd964 !default;
+	$uni-warning: #f0ad4e !default;
+	$uni-error: #dd524d !default;
+	$uni-info: #909399 !default;
+
+
+	$bage-size: 12px;
+	$bage-small: scale(0.8);
+
+	.uni-badge--x {
+		/* #ifdef APP-NVUE */
+		// align-self: flex-start;
+		/* #endif */
+		/* #ifndef APP-NVUE */
+		display: inline-block;
+		/* #endif */
+		position: relative;
+	}
+
+	.uni-badge--absolute {
+		position: absolute;
+	}
+
+	.uni-badge--small {
+		transform: $bage-small;
+		transform-origin: center center;
+	}
+
+	.uni-badge {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		overflow: hidden;
+		box-sizing: border-box;
+		font-feature-settings: "tnum";
+		min-width: 20px;
+		/* #endif */
+		justify-content: center;
+		flex-direction: row;
+		height: 20px;
+		padding: 0 4px;
+		line-height: 18px;
+		color: #fff;
+		border-radius: 100px;
+		background-color: $uni-info;
+		background-color: transparent;
+		border: 1px solid #fff;
+		text-align: center;
+		font-family: 'Helvetica Neue', Helvetica, sans-serif;
+		font-size: $bage-size;
+		/* #ifdef H5 */
+		z-index: 999;
+		cursor: pointer;
+		/* #endif */
+
+		&--info {
+			color: #fff;
+			background-color: $uni-info;
+		}
+
+		&--primary {
+			background-color: $uni-primary;
+		}
+
+		&--success {
+			background-color: $uni-success;
+		}
+
+		&--warning {
+			background-color: $uni-warning;
+		}
+
+		&--error {
+			background-color: $uni-error;
+		}
+
+		&--inverted {
+			padding: 0 5px 0 0;
+			color: $uni-info;
+		}
+
+		&--info-inverted {
+			color: $uni-info;
+			background-color: transparent;
+		}
+
+		&--primary-inverted {
+			color: $uni-primary;
+			background-color: transparent;
+		}
+
+		&--success-inverted {
+			color: $uni-success;
+			background-color: transparent;
+		}
+
+		&--warning-inverted {
+			color: $uni-warning;
+			background-color: transparent;
+		}
+
+		&--error-inverted {
+			color: $uni-error;
+			background-color: transparent;
+		}
+
+	}
+</style>

+ 85 - 0
uni_modules/uni-badge/package.json

@@ -0,0 +1,85 @@
+{
+  "id": "uni-badge",
+  "displayName": "uni-badge 数字角标",
+  "version": "1.2.2",
+  "description": "数字角标(徽章)组件,在元素周围展示消息提醒,一般用于列表、九宫格、按钮等地方。",
+  "keywords": [
+    "",
+    "badge",
+    "uni-ui",
+    "uniui",
+    "数字角标",
+    "徽章"
+],
+  "repository": "https://github.com/dcloudio/uni-ui",
+  "engines": {
+    "HBuilderX": ""
+  },
+  "directories": {
+    "example": "../../temps/example_temps"
+  },
+"dcloudext": {
+    "sale": {
+      "regular": {
+        "price": "0.00"
+      },
+      "sourcecode": {
+        "price": "0.00"
+      }
+    },
+    "contact": {
+      "qq": ""
+    },
+    "declaration": {
+      "ads": "无",
+      "data": "无",
+      "permissions": "无"
+    },
+    "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
+    "type": "component-vue"
+  },
+  "uni_modules": {
+    "dependencies": ["uni-scss"],
+    "encrypt": [],
+    "platforms": {
+      "cloud": {
+        "tcb": "y",
+        "aliyun": "y"
+      },
+      "client": {
+        "App": {
+          "app-vue": "y",
+          "app-nvue": "y"
+        },
+        "H5-mobile": {
+          "Safari": "y",
+          "Android Browser": "y",
+          "微信浏览器(Android)": "y",
+          "QQ浏览器(Android)": "y"
+        },
+        "H5-pc": {
+          "Chrome": "y",
+          "IE": "y",
+          "Edge": "y",
+          "Firefox": "y",
+          "Safari": "y"
+        },
+        "小程序": {
+          "微信": "y",
+          "阿里": "y",
+          "百度": "y",
+          "字节跳动": "y",
+          "QQ": "y"
+        },
+        "快应用": {
+          "华为": "y",
+          "联盟": "y"
+        },
+        "Vue": {
+            "vue2": "y",
+            "vue3": "y"
+        }
+      }
+    }
+  }
+}

+ 10 - 0
uni_modules/uni-badge/readme.md

@@ -0,0 +1,10 @@
+## Badge 数字角标
+> **组件名:uni-badge**
+> 代码块: `uBadge`
+
+数字角标一般和其它控件(列表、9宫格等)配合使用,用于进行数量提示,默认为实心灰色背景,
+
+### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-badge)
+#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 
+
+

Beberapa file tidak ditampilkan karena terlalu banyak file yang berubah dalam diff ini