zhaogongxue há 10 meses atrás
pai
commit
5e0b99e3d6
100 ficheiros alterados com 10819 adições e 148 exclusões
  1. 140 0
      components/kj-flow/kj-flow.vue
  2. 1 1
      components/kj-tabbar/kj-tabbar.vue
  3. 292 0
      components/zh-slidingMenu/zhSlidingMenu/zhSlidingMenu.vue
  4. 43 4
      pageA/counter.vue
  5. 91 4
      pageA/productdetails.vue
  6. 298 0
      pageA/shoplist.vue
  7. 124 0
      pageA/shoptype.vue
  8. BIN
      pageA/static/images/shaixuan.png
  9. BIN
      pageA/static/images/shaixuana.png
  10. BIN
      pageA/static/images/tabs.png
  11. BIN
      pageA/static/images/up.png
  12. 2 2
      pageB/delivery.vue
  13. 546 0
      pageB/end.vue
  14. 104 0
      pageB/expresstype.vue
  15. 54 43
      pageB/goods.vue
  16. 17 6
      pageB/list/list.vue
  17. 5 3
      pageB/myPackage.vue
  18. 138 0
      pageB/orderinfo.vue
  19. 99 0
      pageB/search.vue
  20. 50 49
      pageC/orderForm/orderForm.vue
  21. 55 4
      pages.json
  22. 346 4
      pages/cart/cart.vue
  23. 87 18
      pages/express/express.vue
  24. 13 5
      pages/index/index.vue
  25. 179 5
      pages/shopping/shopping.vue
  26. BIN
      static/shopping/btn.png
  27. BIN
      static/shopping/search.png
  28. BIN
      static/shopping/shop.png
  29. BIN
      static/shopping/title.png
  30. 8 0
      uni_modules/cc-beautyCate/changelog.md
  31. 206 0
      uni_modules/cc-beautyCate/components/cc-beautyCate/cc-beautyCate.vue
  32. 86 0
      uni_modules/cc-beautyCate/package.json
  33. 1049 0
      uni_modules/cc-beautyCate/readme.md
  34. 20 0
      uni_modules/cc-defineCateList/changelog.md
  35. 227 0
      uni_modules/cc-defineCateList/components/cc-defineCateList/cc-defineCateList.vue
  36. 86 0
      uni_modules/cc-defineCateList/package.json
  37. 227 0
      uni_modules/cc-defineCateList/readme.md
  38. 16 0
      uni_modules/liu-waterfall/changelog.md
  39. 272 0
      uni_modules/liu-waterfall/components/liu-waterfall/liu-waterfall.vue
  40. 6 0
      uni_modules/liu-waterfall/license.md
  41. 85 0
      uni_modules/liu-waterfall/package.json
  42. 156 0
      uni_modules/liu-waterfall/readme.md
  43. 31 0
      uni_modules/uv-icon/changelog.md
  44. 160 0
      uni_modules/uv-icon/components/uv-icon/icons.js
  45. 90 0
      uni_modules/uv-icon/components/uv-icon/props.js
  46. 226 0
      uni_modules/uv-icon/components/uv-icon/uv-icon.vue
  47. BIN
      uni_modules/uv-icon/components/uv-icon/uvicons.ttf
  48. 83 0
      uni_modules/uv-icon/package.json
  49. 15 0
      uni_modules/uv-icon/readme.md
  50. 36 0
      uni_modules/uv-image/changelog.md
  51. 95 0
      uni_modules/uv-image/components/uv-image/props.js
  52. 287 0
      uni_modules/uv-image/components/uv-image/uv-image.vue
  53. 89 0
      uni_modules/uv-image/package.json
  54. 15 0
      uni_modules/uv-image/readme.md
  55. 9 0
      uni_modules/uv-loading-icon/changelog.md
  56. 67 0
      uni_modules/uv-loading-icon/components/uv-loading-icon/props.js
  57. 347 0
      uni_modules/uv-loading-icon/components/uv-loading-icon/uv-loading-icon.vue
  58. 87 0
      uni_modules/uv-loading-icon/package.json
  59. 19 0
      uni_modules/uv-loading-icon/readme.md
  60. 19 0
      uni_modules/uv-transition/changelog.md
  61. 131 0
      uni_modules/uv-transition/components/uv-transition/createAnimation.js
  62. 31 0
      uni_modules/uv-transition/components/uv-transition/props.js
  63. 320 0
      uni_modules/uv-transition/components/uv-transition/uv-transition.vue
  64. 87 0
      uni_modules/uv-transition/package.json
  65. 15 0
      uni_modules/uv-transition/readme.md
  66. 76 0
      uni_modules/uv-ui-tools/changelog.md
  67. 6 0
      uni_modules/uv-ui-tools/components/uv-ui-tools/uv-ui-tools.vue
  68. 79 0
      uni_modules/uv-ui-tools/index.js
  69. 7 0
      uni_modules/uv-ui-tools/index.scss
  70. 34 0
      uni_modules/uv-ui-tools/libs/config/config.js
  71. 32 0
      uni_modules/uv-ui-tools/libs/css/color.scss
  72. 100 0
      uni_modules/uv-ui-tools/libs/css/common.scss
  73. 23 0
      uni_modules/uv-ui-tools/libs/css/components.scss
  74. 111 0
      uni_modules/uv-ui-tools/libs/css/variable.scss
  75. 40 0
      uni_modules/uv-ui-tools/libs/css/vue.scss
  76. 134 0
      uni_modules/uv-ui-tools/libs/function/colorGradient.js
  77. 29 0
      uni_modules/uv-ui-tools/libs/function/debounce.js
  78. 167 0
      uni_modules/uv-ui-tools/libs/function/digit.js
  79. 734 0
      uni_modules/uv-ui-tools/libs/function/index.js
  80. 75 0
      uni_modules/uv-ui-tools/libs/function/platform.js
  81. 287 0
      uni_modules/uv-ui-tools/libs/function/test.js
  82. 30 0
      uni_modules/uv-ui-tools/libs/function/throttle.js
  83. 132 0
      uni_modules/uv-ui-tools/libs/luch-request/adapters/index.js
  84. 51 0
      uni_modules/uv-ui-tools/libs/luch-request/core/InterceptorManager.js
  85. 201 0
      uni_modules/uv-ui-tools/libs/luch-request/core/Request.js
  86. 20 0
      uni_modules/uv-ui-tools/libs/luch-request/core/buildFullPath.js
  87. 33 0
      uni_modules/uv-ui-tools/libs/luch-request/core/defaults.js
  88. 6 0
      uni_modules/uv-ui-tools/libs/luch-request/core/dispatchRequest.js
  89. 126 0
      uni_modules/uv-ui-tools/libs/luch-request/core/mergeConfig.js
  90. 16 0
      uni_modules/uv-ui-tools/libs/luch-request/core/settle.js
  91. 64 0
      uni_modules/uv-ui-tools/libs/luch-request/helpers/buildURL.js
  92. 14 0
      uni_modules/uv-ui-tools/libs/luch-request/helpers/combineURLs.js
  93. 14 0
      uni_modules/uv-ui-tools/libs/luch-request/helpers/isAbsoluteURL.js
  94. 197 0
      uni_modules/uv-ui-tools/libs/luch-request/index.d.ts
  95. 2 0
      uni_modules/uv-ui-tools/libs/luch-request/index.js
  96. 135 0
      uni_modules/uv-ui-tools/libs/luch-request/utils.js
  97. 264 0
      uni_modules/uv-ui-tools/libs/luch-request/utils/clone.js
  98. 13 0
      uni_modules/uv-ui-tools/libs/mixin/button.js
  99. 172 0
      uni_modules/uv-ui-tools/libs/mixin/mixin.js
  100. 8 0
      uni_modules/uv-ui-tools/libs/mixin/mpMixin.js

+ 140 - 0
components/kj-flow/kj-flow.vue

@@ -0,0 +1,140 @@
+<template>
+	<view>
+		<view class="u-flex u-row-between">
+			<view class=""></view>
+			<view class="title">跨境物流</view>
+			<u-icon name="close" @click="close" color="background: #333333;" size="28"></u-icon>
+		</view>
+		<view class="u-flex" style="margin-top: 56rpx;">
+			<scroll-view scroll-y="true" class="left">
+				<view v-for="(item,idx) in 6"
+					style="height: 134rpx;display: flex;flex-direction: column;align-items: center;justify-content: center;"
+					:style="{background:idx==0? '':'#F7F7F7'}">
+					<view class="ititle">海运散货</view>
+					<view class="itime" v-if="idx==0">预计到货时间 </view>
+					<view class="itime" v-if="idx==0">2023-12-31</view>
+				</view>
+			</scroll-view>
+			<scroll-view scroll-y="true" class="right" style="margin-left: 36rpx;">
+				<view v-for="(item,idx) in 6"
+					style="height: 134rpx;display: flex;flex-direction: column;align-items: center;justify-content: center;" class="iitem">
+					<view class="ititle u-flex u-row-between" style="width: 100%;" >
+						<view class="ititle">发柜时间 2023-12-15</view>
+						<view>海运1号柜</view>
+					</view>
+					<view class="u-flex u-row-between" style="margin-top: 16rpx;width: 100%;">
+						<view class="price">
+							<text style='font-size="24rpx"'>¥</text>
+							<text style='font-size="32rpx"'>12</text>
+							<text style='font-size="28rpx"'>kg</text>
+						</view>
+						<u-icon v-if="idx==0" name="checkbox-mark" color="rgba(248, 50, 36, 1)" size="28"></u-icon>
+						<view v-else class=""></view>
+					</view>
+				</view>
+			</scroll-view>
+		</view>
+		<view class="bottom">
+			<view class="btn" @click="enter">确定</view>
+		</view>
+		<view class=""></view>
+	</view>
+</template>
+
+<script>
+	export default {
+		name: "kj-flow",
+		data() {
+			return {
+
+			};
+		},
+		methods:{
+			close(){
+				this.$emit('close')
+			},
+			enter(){
+				this.$emit('success')
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.iitem{
+		border-bottom: 2rpx solid rgba(151, 151, 151, 0.1);
+		box-sizing: border-box;
+	}
+	.price {
+		font-family: SFPro, SFPro;
+		font-weight: 500;
+		font-size: 24rpx;
+		color: #F83224;
+		line-height: 28rpx;
+		text-align: right;
+		font-style: normal;
+	}
+
+	.right {
+		flex: 1;
+		width: 474rpx;
+		height: 642rpx;
+	}
+
+	.bottom {
+		width: 750rpx;
+		height: 166rpx;
+		background: #FFFFFF;
+		padding: 20rpx 24rpx;
+		box-sizing: border-box;
+	}
+
+	.btn {
+		width: 702rpx;
+		height: 84rpx;
+		background: #F83224;
+		border-radius: 44rpx;
+		font-family: PingFangSC, PingFang SC;
+		font-weight: 500;
+		font-size: 32rpx;
+		color: #FFFFFF;
+		line-height: 84rpx;
+		text-align: center;
+		font-style: normal;
+	}
+
+	.title {
+		font-family: PingFangSC, PingFang SC;
+		font-weight: 500;
+		font-size: 36rpx;
+		color: #333333;
+		line-height: 50rpx;
+		text-align: left;
+		font-style: normal;
+	}
+
+	.left {
+		width: 212rpx;
+		height: 642rpx;
+	}
+
+	.ititle {
+		font-family: PingFangSC, PingFang SC;
+		font-weight: 500;
+		font-size: 28rpx;
+		color: #222222;
+		line-height: 40rpx;
+		text-align: right;
+		font-style: normal;
+	}
+
+	.itime {
+		font-family: PingFangSC, PingFang SC;
+		font-weight: 400;
+		font-size: 20rpx;
+		color: #333333;
+		line-height: 28rpx;
+		text-align: right;
+		font-style: normal;
+	}
+</style>

+ 1 - 1
components/kj-tabbar/kj-tabbar.vue

@@ -1,5 +1,5 @@
 <template>
-	<view>
+	<view style="position: relative;z-index: 1000;">
 		<u-tabbar :list="list" :fixed="true" :safeAreaInsetBottom="true" active-color="#F83224" inactive-color="#333"
 			:value="value1">
 			<u-tabbar-item :text="item.text" v-for="(item,idx) in list" @click="click1" :key="idx">

+ 292 - 0
components/zh-slidingMenu/zhSlidingMenu/zhSlidingMenu.vue

@@ -0,0 +1,292 @@
+<template>
+	<view class="content" :style="{'height': `${scrollH}px`}">
+		<view class="cont">
+			<scroll-view scroll-y scroll-with-animation class="cont_view menu-scroll-view" :scroll-top="scrollTop"
+				:scroll-into-view="itemId" :style="[menuLeft]">
+				<view v-for="(item,index) in tabbar" :key="index" class="menu_item"
+					:style="[menuLeftActive,current == index?menuLeftActivea:{}]" @tap.stop="swichMenu(index)">
+					<view class="menu_item_name">{{item[keyName]}}</view>
+					<view class="y_teg" :style="[{'background':tegColor}]" v-if="current == index && isTeg"></view>
+				</view>
+			</scroll-view>
+			<scroll-view :scroll-top="scrollRightTop" scroll-y scroll-with-animation :style="[menuRight]"
+				@scroll="rightScroll">
+				<view class="menu_right_view">
+					<view class="menu_right_item" :id="'item' + index" v-for="(item , index) in tabbar" :key="index">
+						<view class="menu_right_title">{{item[keyName]}}</view>
+						<view class="">
+							<slot :scroll_list='item[tabbarName]'></slot>
+						</view>
+					</view>
+				</view>
+			</scroll-view>
+		</view>
+	</view>
+</template>
+<script>
+	/*
+	 * zhSlidingMenu 分类商品-单向联动 + 双向联动,支持自定义商品内容
+	 * @property {Array} tabbar 分类数据
+	 * @property {String} keyName 从tabbar元素对象中读取的键名
+	 * @property {Number} scrollH scroll高度
+	 * @property {Object} menuLeft 左侧样式
+	 * @property {Object} menuLeftActive 左侧未选中样式
+	 * @property {Object} menuLeftActivea 左侧选中样式
+	 * @property {Object} menuRight 右侧样式
+	 * @property {Object} tabbarName tabbar数组中指定对象的目标属性名
+	 * @property {Boolean} isTeg 左侧竖条标记
+	 * @property {String} tegColor 左侧竖条标记颜色
+	 */
+	export default {
+		props: {
+			tabbar: {
+				typeof: Array,
+				default: () => []
+			},
+			keyName: {
+				typeof: String,
+				default: () => 'name'
+			},
+			scrollH: {
+				typeof: Number,
+				default: () => 500
+			},
+			menuLeft: {
+				typeof: Object,
+				default: () => ({
+					'width': '200rpx',
+					'background': '#F5F5F5',
+				})
+			},
+			menuLeftActive: {
+				typeof: Object,
+				default: () => ({
+					'color': '#333333',
+					'font-weight': '400',
+					'font-size': '30rpx',
+				})
+			},
+			menuLeftActivea: {
+				typeof: Object,
+				default: () => ({
+					'color': '#222222',
+					'font-weight': '500',
+					'background': '#fff',
+				})
+			},
+			menuRight: {
+				typeof: Object,
+				default: () => ({
+					background: '#fff',
+				})
+			},
+			tabbarName: {
+				typeof: String,
+				default: () => 'children'
+			},
+			isTeg: {
+				typeof: Boolean,
+				default: () => true
+			},
+			tegColor: {
+				typeof: String,
+				default: () => 'rgba(255, 21, 21, 1)'
+			}
+		},
+		data() {
+			return {
+				scrollTop: 0, //tab标题的滚动条位置
+				oldScrollTop: 0,
+				current: 0, // 预设当前项的值
+				menuHeight: 0, // 左边菜单的高度
+				menuItemHeight: 0, // 左边菜单item的高度
+				itemId: '', // 栏目右边scroll-view用于滚动的id
+				arr: [], //左侧菜单距离登录距离列表
+				scrollRightTop: 0, // 右边栏目scroll-view的滚动条高度
+				timer: null, // 定时器
+			}
+		},
+		mounted() {
+			this.getMenuItemTop()
+		},
+		methods: {
+			// 点击左边的栏目切换
+			async swichMenu(index) {
+				if (this.arr.length == 0) {
+					await this.getMenuItemTop();
+				}
+				if (index == this.current) return;
+				this.scrollRightTop = this.oldScrollTop;
+				this.$nextTick(() => {
+					this.scrollRightTop = this.arr[index];
+					this.current = index;
+					this.leftMenuStatus(index);
+				})
+			},
+			// 获取一个目标元素的高度
+			getElRect(elClass, dataVal) {
+				new Promise((resolve, reject) => {
+					const query = uni.createSelectorQuery().in(this);
+					query.select('.' + elClass).fields({
+						size: true
+					}, res => {
+						// 如果节点尚未生成,res值为null,循环调用执行
+						if (!res) {
+							setTimeout(() => {
+								this.getElRect(elClass);
+							}, 10);
+							return;
+						}
+						this[dataVal] = res.height;
+						resolve();
+					}).exec();
+				})
+			},
+			// 观测元素相交状态
+			async observer() {
+				this.tabbar.map((val, index) => {
+					let observer = uni.createIntersectionObserver(this);
+					// 检测右边scroll-view的id为itemxx的元素与right-box的相交状态
+					// 如果跟.right-box底部相交,就动态设置左边栏目的活动状态
+					observer.relativeTo('.right-box', {
+						top: 0
+					}).observe('#item' + index, res => {
+						if (res.intersectionRatio > 0) {
+							let id = res.id.substring(4);
+							this.leftMenuStatus(id);
+						}
+					})
+				})
+			},
+			// 设置左边菜单的滚动状态
+			async leftMenuStatus(index) {
+				this.current = index;
+				// 如果为0,意味着尚未初始化
+				if (this.menuHeight == 0 || this.menuItemHeight == 0) {
+					await this.getElRect('menu-scroll-view', 'menuHeight');
+					await this.getElRect('menu_item', 'menuItemHeight');
+				}
+				// 将菜单活动item垂直居中
+				this.scrollTop = index * this.menuItemHeight + this.menuItemHeight / 2 - this.menuHeight / 2;
+			},
+			// 获取右边菜单每个item到顶部的距离
+			getMenuItemTop() {
+				new Promise(resolve => {
+					let selectorQuery = uni.createSelectorQuery().in(this);
+					selectorQuery.selectAll('.menu_right_item').boundingClientRect((rects) => {
+						// 如果节点尚未生成,rects值为[](因为用selectAll,所以返回的是数组),循环调用执行
+						if (!rects.length) {
+							setTimeout(() => {
+								this.getMenuItemTop();
+							}, 10);
+							return;
+						}
+						rects.forEach((rect) => {
+							// 这里减去rects[0].top,是因为第一项顶部可能不是贴到导航栏(比如有个搜索框的情况)
+							this.arr.push(rect.top - rects[0].top);
+							resolve();
+						})
+					}).exec()
+				})
+			},
+			// 右边菜单滚动
+			async rightScroll(e) {
+				this.oldScrollTop = e.detail.scrollTop;
+				if (this.arr.length == 0) {
+					await this.getMenuItemTop();
+				}
+				if (this.timer) return;
+				if (!this.menuHeight) {
+					await this.getElRect('menu-scroll-view', 'menuHeight');
+				}
+				setTimeout(() => { // 节流
+					this.timer = null;
+					// scrollHeight为右边菜单垂直中点位置
+					let scrollHeight = e.detail.scrollTop;
+					for (let i = 0; i < this.arr.length; i++) {
+						let height1 = this.arr[i];
+						let height2 = this.arr[i + 1];
+						// 如果不存在height2,意味着数据循环已经到了最后一个,设置左边菜单为最后一项即可
+						if (!height2 || scrollHeight >= height1 && scrollHeight < height2) {
+							this.leftMenuStatus(i);
+							return;
+						}
+					}
+				}, 10)
+			},
+			reset() { //父级重置
+				this.$nextTick(async () => {
+					await this.getMenuItemTop();
+					this.current = 0
+					this.scrollTop = 0
+					this.scrollRightTop = 0
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.content {
+		display: flex;
+		flex-direction: column;
+
+		.cont {
+			flex: 1;
+			display: flex;
+			overflow: hidden;
+
+			.cont_view {
+				height: 100%;
+
+				.menu_item {
+					position: relative;
+					padding: 24rpx 12rpx;
+
+					.menu_item_name {
+						display: -webkit-box;
+						-webkit-box-orient: vertical;
+						-webkit-line-clamp: 1;
+						overflow: hidden;
+						text-align: center;
+					}
+
+					.y_teg {
+						width: 6upx;
+						height: 40upx;
+						border-radius: 3upx;
+						position: absolute;
+						left: 0%;
+						top: 50%;
+						z-index: 99;
+						transform: translate(0, -50%);
+					}
+				}
+			}
+		}
+	}
+
+	.menu_right_view {
+		padding: 24upx;
+
+		.menu_right_item {
+			margin-bottom: 30rpx;
+		}
+
+		.menu_right_item:last-child {
+			min-height: 100vh;
+		}
+	}
+
+	.menu_right_title {
+		font-family: PingFangSC, PingFang SC;
+		font-size: 28rpx;
+		color: #222222;
+		font-weight: bold;
+		font-size: 28rpx;
+		display: -webkit-box;
+		-webkit-box-orient: vertical;
+		-webkit-line-clamp: 1;
+		overflow: hidden;
+	}
+</style>

+ 43 - 4
pageA/counter.vue

@@ -1,4 +1,5 @@
 <template>
+	<!-- 物流报价器 -->
 	<view class="back">
 		<!-- 城市 -->
 		<view class="add u-flex u-row-around">
@@ -13,14 +14,14 @@
 				<image src="" mode="" style="width: 12rpx;height: 12rpx;"></image>
 			</view>
 			<view class="u-flex" style="margin-top: 24rpx;">
-				<view class="left">普通商品</view>
-				<view class="right" style="margin-left: 20rpx;">特殊商品</view>
+				<view :class="index==idx?'left':'right'" @click="good(idx)" style="margin-right: 20rpx;" v-for="(item,idx) in goods" :key="idx">{{item.name}}</view>
+				<!-- <view class="right" style="margin-left: 20rpx;">特殊商品</view> -->
 			</view>
 		</view>
 		<!-- 跨境物流 -->
 		<view class="logistics u-flex u-row-between">
 			<text style="font-weight: 600;">跨境物流</text>
-			<view class="u-flex">
+			<view class="u-flex" @click="logshow=true">
 				<text class="change">请选择</text>
 				<u-icon name="arrow-right" size='16'></u-icon>
 			</view>
@@ -60,6 +61,12 @@
 		</view>
 		<!-- 地址选择 -->
 		<u-picker :show="show" ref="uPicker" :columns="columns" @confirm="confirm" @change="changeHandler"></u-picker>
+		<!-- 跨境物流 -->
+		<u-popup round='28' :show="logshow" @close="close">
+			<view class="pop">
+				<kj-flow @success='success' @close='logshow=false'></kj-flow>
+			</view>
+		</u-popup>
 	</view>
 </template>
 
@@ -67,6 +74,7 @@
 	export default {
 		data() {
 			return {
+				logshow: false, //物流选择
 				value: 1,
 				show: false,
 				original: '', //起始地
@@ -81,10 +89,29 @@
 				columnData: [
 					['深圳', '厦门', '上海', '拉萨'],
 					['得州', '华盛顿', '纽约', '阿拉斯加']
-				]
+				],
+				goods:[
+					{
+						name:'普通商品'
+					},
+					{
+						name:'特殊商品'
+					}
+				],
+				index:0
 			};
 		},
 		methods: {
+			success(){
+				this.logshow = false
+			},
+			close() {
+				this.logshow = false
+			},
+			//切换商品类型
+			good(idx){
+				this.index = idx
+			},
 			//物流详情
 			logistics(){
 				uni.navigateTo({
@@ -132,6 +159,18 @@
 </script>
 
 <style lang="scss" scoped>
+	.pop {
+		font-family: PingFangSC, PingFang SC;
+		font-weight: 500;
+		font-size: 36rpx;
+		color: #333333;
+		line-height: 50rpx;
+		text-align: left;
+		font-style: normal;
+		padding:32rpx 28rpx 0 0 ;
+		box-sizing: border-box;
+	}
+	
 	.back {
 		background-color: rgba(244, 244, 244, 1);
 		padding: 20rpx 24rpx;

+ 91 - 4
pageA/productdetails.vue

@@ -4,7 +4,7 @@
 		<u-navbar :safeAreaInsetTop="true" :placeholder='true'>
 			<view class="u-nav-slot u-flex" slot="left" style="display: flex;">
 				<view class="u-flex">
-					<u-icon name="arrow-left" size="19"></u-icon>
+					<u-icon name="arrow-left" size="19" @click="navigateBack()"></u-icon>
 					<u-tabs duration='0' @click="click" :list="list1" lineColor='#fff' :activeStyle=" {color:'#FF1515'}"
 						:inactiveStyle="{
 						color:' #666666' }"></u-tabs>
@@ -135,7 +135,7 @@
 						</image>
 						<view class="ke">客服</view>
 					</view>
-					<view class="u-flex" style="justify-content: center;flex-direction: column;">
+					<view class="u-flex" style="justify-content: center;flex-direction: column;" @click="join">
 						<image src="../static/cart1.png" style='width:40rpx;height:40rpx' mode=""></image>
 						<view class="ke">购物车</view>
 					</view>
@@ -162,6 +162,32 @@
 						<text class="guige">已选:鸡肉;2kg【单包装】</text>
 					</view>
 				</view>
+				<view class="title1" style="margin-top: 40rpx;margin-bottom: 32rpx;">
+					食品口味
+				</view>
+				<view class="u-flex" style="column-gap: 18rpx;flex-wrap: wrap;">
+					<view class="tabs">
+						鸡肉
+					</view>
+				</view>
+				<view class="title1" style="margin-top: 40rpx;margin-bottom: 32rpx;">
+					净含量
+				</view>
+				<view class="u-flex" style="column-gap: 18rpx;flex-wrap: wrap;">
+					<view class="tabs">
+						鸡肉
+					</view>
+				</view>
+				<view class="u-flex u-row-between" style="margin-top: 30rpx;">
+					<text class="title1">数量</text>
+					<u-number-box v-model="value" @change="valChange"></u-number-box>
+				</view>
+				<view class="" style="height: 200rpx;"></view>
+				<view class="bottoma">
+					<view class="btn">
+						加入购物车
+					</view>
+				</view>
 			</view>
 		</u-popup>
 		<u-popup :show="serve">
@@ -241,15 +267,22 @@
 				//服务
 				serve: false,
 				//分享
-				share: false
+				share: false,
+				value: 0, //步进器的值
 			};
 		},
 		methods: {
+			valChange(e) {
+				console.log('当前值为: ' + e.value)
+			},
+			navigateBack() {
+				uni.navigateBack()
+			},
 			click(item) {
 				this.current = item
 			},
 			join() {
-
+				this.show = true
 			},
 			//通知
 			inform() {
@@ -287,6 +320,60 @@
 </script>
 
 <style lang="scss" scoped>
+	.actabs {
+		width: 116rpx;
+		height: 68rpx;
+		background: rgba(248, 50, 36, 0.06);
+		border-radius: 6rpx;
+		border: 2rpx solid #F83224;
+	}
+
+	.bottoma {
+		width: 750rpx;
+		height: 166rpx;
+		background: #FFFFFF;
+		padding: 16rpx 44rpx 0;
+		position: fixed;
+		bottom: 0;
+		left: 0;
+
+		.btn {
+			width: 662rpx;
+			height: 84rpx;
+			background: #F83224;
+			border-radius: 44rpx;
+			font-family: PingFangSC, PingFang SC;
+			font-weight: 500;
+			font-size: 32rpx;
+			color: #FFFFFF;
+			line-height: 84rpx;
+			text-align: center;
+			font-style: normal;
+		}
+	}
+
+	.title1 {
+		font-family: PingFangSC, PingFang SC;
+		font-weight: 500;
+		font-size: 32rpx;
+		color: #222222;
+		line-height: 44rpx;
+		text-align: left;
+		font-style: normal;
+	}
+
+	.tabs {
+		// width: 276rpx;
+		height: 68rpx;
+		padding: 14rpx 30rpx;
+		text-align: center;
+		background: #F4F4F4;
+		border-radius: 6rpx;
+		box-sizing: border-box;
+		display: inline;
+		// line-height: 68rpx;
+	}
+
 	.link {
 		font-family: PingFangSC, PingFang SC;
 		font-weight: 400;

+ 298 - 0
pageA/shoplist.vue

@@ -0,0 +1,298 @@
+<template>
+	<view>
+		<u-tabs lineColor="rgba(0,0,0,0)" :activeStyle="{
+            color: 'rgba(255, 21, 21, 1)',
+            fontWeight: 'bold',
+            transform: 'scale(1.05)'
+        }" :list="list1" @click="click"></u-tabs>
+		<view class="box" style="position: relative;">
+			<view class="u-flex u-row-between">
+				<view @click="current=0" class="u-flex" :style="{color:current==0?'rgba(255, 21, 21, 1)':''}">
+					<view>综合</view>
+					<u-icon name="arrow-down-fill" color="rgba(255, 21, 21, 1)" size="8"></u-icon>
+				</view>
+				<text @click="current=1" :style="{color:current==1?'rgba(255, 21, 21, 1)':''}">销量</text>
+				<view @click="current=2" class=" u-flex" :style="{color:current==2?'rgba(255, 21, 21, 1)':''}">
+					<view>价格</view>
+					<image src="static/images/up.png" style="width: 16rpx;height: 16rpx;" mode=""></image>
+				</view>
+				<text @click="current=3" :style="{color:current==3?'rgba(255, 21, 21, 1)':''}">≤1kg</text>
+				<text>|</text>
+				<view class="u-flex" :style="{color:current==4?'rgba(255, 21, 21, 1)':''}" @click="shaixuan">
+					<view>筛选</view>
+					<image v-if="current==4" src="static/images/shaixuana.png" style="width: 24rpx;height: 24rpx;"
+						mode=""></image>
+					<image v-else src="static/images/shaixuan.png" style="width: 24rpx;height: 24rpx;" mode=""></image>
+				</view>
+			</view>
+			<liu-waterfall :dataList="data" :column="columns" @click="click"></liu-waterfall>
+			<view class="pop" style="position: absolute;top:80rpx" v-if="shai">
+				<view class="title">价格区间</view>
+				<view class="u-flex u-row-between">
+					<view class="input u-flex u-row-center">
+						<input style="text-align: center;" type="text" placeholder="最低价" />
+					</view>
+					<text>-</text>
+					<view class="input u-flex u-row-center">
+						<input style="text-align: center;" type="text" placeholder="最高价" />
+					</view>
+				</view>
+				<view class="title" style="margin-top: 36rpx;">重量区间</view>
+				<view class="u-flex u-row-between">
+					<view class="input u-flex u-row-center">
+						<input style="text-align: center;" type="text" placeholder="最低价" />
+					</view>
+					<text>-</text>
+					<view class="input u-flex u-row-center">
+						<input style="text-align: center;" type="text" placeholder="最高价" />
+					</view>
+				</view>
+				<view style="margin-top: 36rpx;" class="title">品牌</view>
+				<view class="u-flex " style="flex-wrap: wrap;column-gap: 34rpx;">
+					<view @click="changei(idx)" class="item" v-for="(item,idx) in 8" :key="idx"
+						:class="index==idx?'acitem':'item'">
+						PURICH/醇粹
+					</view>
+				</view>
+				<view style="margin-top: 36rpx;" class="title">自营商品</view>
+				<view class="item">
+					是
+				</view>
+				<view class="bottom u-flex" style="margin-top: 58rpx;">
+					<view class="btn1">
+						重置
+					</view>
+					<view class="btn2">
+						确定
+					</view>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				shai: false,
+				current: 0,
+				curess: 0,
+				index: '',
+				list1: [{
+					name: '关注',
+				}, {
+					name: '推荐',
+				}, {
+					name: '电影'
+				}, {
+					name: '科技'
+				}, {
+					name: '音乐'
+				}, {
+					name: '美食'
+				}, {
+					name: '文化'
+				}, {
+					name: '财经'
+				}, {
+					name: '手工'
+				}],
+				data: [{
+					picUrl: 'https://cdn.pixabay.com/photo/2020/05/19/13/32/cartoon-5190837_1280.jpg',
+					title: '思考',
+					desc: '我是第1个图片',
+					id: 1,
+				}, {
+					picUrl: 'https://cdn.pixabay.com/photo/2021/07/22/11/25/rabbit-6485072_1280.jpg',
+					title: '兔子',
+					desc: '我是第2个图片',
+					id: 2,
+				}, {
+					picUrl: 'https://cdn.pixabay.com/photo/2020/05/19/13/35/cartoon-5190860_1280.jpg',
+					title: '雨天',
+					desc: '我是第3个图片',
+					id: 3,
+				}, {
+					picUrl: 'https://cdn.pixabay.com/photo/2022/03/31/14/53/camp-7103189_1280.png',
+					title: '日落',
+					desc: '我是第4个图片',
+					id: 4,
+				}, {
+					picUrl: 'https://cdn.pixabay.com/photo/2022/11/29/19/05/boho-7625140_1280.jpg',
+					title: '植物',
+					desc: '我是第5个图片',
+					id: 5,
+				}, {
+					picUrl: 'https://cdn.pixabay.com/photo/2022/08/25/23/06/woman-7411414_1280.png',
+					title: '时尚',
+					desc: '我是第6个图片',
+					id: 6,
+				}, {
+					picUrl: 'https://cdn.pixabay.com/photo/2023/03/07/12/45/child-7835677_1280.jpg',
+					title: '生活',
+					desc: '我是第7个图片',
+					id: 7,
+				}, {
+					picUrl: 'https://cdn.pixabay.com/photo/2020/05/19/13/32/cartoon-5190837_1280.jpg',
+					title: '思考',
+					desc: '我是第8个图片',
+					id: 8,
+				}, {
+					picUrl: 'https://cdn.pixabay.com/photo/2021/07/22/11/25/rabbit-6485072_1280.jpg',
+					title: '兔子',
+					desc: '我是第9个图片',
+					id: 9,
+				}, {
+					picUrl: 'https://cdn.pixabay.com/photo/2020/05/19/13/35/cartoon-5190860_1280.jpg',
+					title: '雨天',
+					desc: '我是第10个图片',
+					id: 10,
+				}, {
+					picUrl: 'https://cdn.pixabay.com/photo/2022/11/29/19/05/boho-7625140_1280.jpg',
+					title: '植物',
+					desc: '我是第11个图片',
+					id: 11,
+				}, {
+					picUrl: 'https://cdn.pixabay.com/photo/2022/08/25/23/06/woman-7411414_1280.png',
+					title: '时尚',
+					desc: '我是第12个图片',
+					id: 12,
+				}, {
+					picUrl: 'https://cdn.pixabay.com/photo/2023/03/07/12/45/child-7835677_1280.jpg',
+					title: '生活',
+					desc: '我是第13个图片',
+					id: 13,
+				}, {
+					picUrl: 'https://cdn.pixabay.com/photo/2020/05/19/13/35/cartoon-5190860_1280.jpg',
+					title: '雨天',
+					desc: '我是第14个图片',
+					id: 14,
+				}, {
+					picUrl: 'https://cdn.pixabay.com/photo/2023/03/07/12/45/child-7835677_1280.jpg',
+					title: '生活',
+					desc: '我是第15个图片',
+					id: 15,
+				}, {
+					picUrl: 'https://cdn.pixabay.com/photo/2020/05/19/13/32/cartoon-5190837_1280.jpg',
+					title: '思考',
+					desc: '我是第16个图片',
+					id: 16,
+				}],
+				columns: 2,
+			};
+		},
+		methods: {
+			shaixuan() {
+				this.shai = true,
+					this.current = 4
+			},
+			click(item) {
+				console.log('item', item);
+				this.curess = item.index
+			},
+			changei(idx) {
+				this.index = idx
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.box {
+		width: 750rpx;
+		min-height: 1360rpx;
+		background: #FFFFFF;
+		border-radius: 28rpx 28rpx 0rpx 0rpx;
+		padding: 38rpx 20rpx 20rpx;
+		box-sizing: border-box;
+		margin-top: 20rpx;
+	}
+
+	.pop {
+		width: 750rpx;
+		// height: 904rpx;
+		background: #FFFFFF;
+		padding: 36rpx 32rpx;
+		box-sizing: border-box;
+
+		.btn1 {
+			width: 702rpx;
+			height: 84rpx;
+			background: rgba(248, 50, 36, 0.1);
+			border-radius: 42rpx;
+			font-family: PingFangSC, PingFang SC;
+			font-weight: 500;
+			font-size: 32rpx;
+			color: #F83224;
+			line-height: 84rpx;
+			text-align: center;
+			font-style: normal;
+			border-radius: 42rpx 0 0 42rpx;
+		}
+
+		.btn2 {
+			width: 702rpx;
+			height: 84rpx;
+			background: #F83224;
+			border-radius: 42rpx;
+			font-family: PingFangSC, PingFang SC;
+			font-weight: 500;
+			font-size: 32rpx;
+			color: #FFFFFF;
+			line-height: 84rpx;
+			text-align: center;
+			font-style: normal;
+			border-radius: 0rpx 42rpx 42rpx 0;
+		}
+
+		.item {
+			width: 206rpx;
+			height: 64rpx;
+			background: #F4F4F4;
+			border-radius: 36rpx;
+			font-family: PingFangSC, PingFang SC;
+			font-weight: 400;
+			font-size: 24rpx;
+			color: #131415;
+			line-height: 64rpx;
+			text-align: center;
+			font-style: normal;
+			margin-top: 26rpx;
+		}
+
+		.acitem {
+			width: 206rpx;
+			height: 64rpx;
+			font-family: PingFangSC, PingFang SC;
+			font-weight: 400;
+			font-size: 24rpx;
+			color: rgba(248, 50, 36, 1);
+			line-height: 64rpx;
+			text-align: center;
+			font-style: normal;
+			margin-top: 26rpx;
+			background: rgba(248, 50, 36, 0.1);
+			border: 1rpx solid #F83224;
+			box-sizing: border-box;
+		}
+
+		.title {
+			font-family: PingFangSC, PingFang SC;
+			font-weight: 500;
+			font-size: 26rpx;
+			color: #131415;
+			line-height: 36rpx;
+			text-align: left;
+			font-style: normal;
+			margin-bottom: 24rpx;
+		}
+
+		.input {
+			width: 312rpx;
+			height: 64rpx;
+			background: #F4F4F4;
+			border-radius: 36rpx;
+		}
+	}
+</style>

+ 124 - 0
pageA/shoptype.vue

@@ -0,0 +1,124 @@
+<template>
+    <view class="">
+        <zhSlidingMenu ref='zhSlidingMenu' :tabbar='list' :scrollH="scrollH">
+            <template #default="{scroll_list}">
+                <view class="goods">
+                    <view class="goods_item" v-for="(item, index) in scroll_list" :key="index"
+                        @click="clickGoods(item)">
+                        <image class="goods_item_img" :src="item.icon" mode=""></image>
+                        <view class="goods_item_name">{{item.name}}</view>
+                    </view>
+                </view>
+            </template>
+        </zhSlidingMenu>
+    </view>
+</template>
+
+<script>
+	import zhSlidingMenu from '@/components/zh-slidingMenu/zhSlidingMenu/zhSlidingMenu.vue'
+    export default {
+        components: {
+            zhSlidingMenu
+        },
+        data() {
+            return {
+                scrollH: 0, //scroll高度
+                list: [{
+                        name: "女装",
+                        children: [{
+                                name: "A字裙",
+                                icon: "https://cdn.uviewui.com/uview/common/classify/1/1.jpg",
+                            },
+                            {
+                                name: "礼服/婚纱",
+                                icon: "https://cdn.uviewui.com/uview/common/classify/1/14.jpg",
+                            }
+                        ]
+                    },
+                    {
+                        name: "美食",
+                        children: [{
+                                name: "火锅",
+                                icon: "https://cdn.uviewui.com/uview/common/classify/2/1.jpg",
+                            },
+                            {
+                                name: "精品茗茶",
+                                icon: "https://cdn.uviewui.com/uview/common/classify/2/7.jpg",
+                            },
+                            {
+                                name: "休闲食品",
+                                icon: "https://cdn.uviewui.com/uview/common/classify/2/8.jpg",
+                            },
+                        ]
+                    },
+                    {
+                        name: "美妆",
+                        children: [{
+                                name: "化妆刷",
+                                icon: "https://cdn.uviewui.com/uview/common/classify/3/1.jpg",
+                            },
+                            {
+                                name: "防晒品",
+                                icon: "https://cdn.uviewui.com/uview/common/classify/3/14.jpg",
+                            },
+                            {
+                                name: "美甲",
+                                icon: "https://cdn.uviewui.com/uview/common/classify/3/15.jpg",
+                            }
+
+                        ]
+                    }
+                ],
+            }
+        },
+        onLoad() {
+            uni.getSystemInfo({
+                success: (res) => {
+                    this.scrollH = res.windowHeight
+                }
+            });
+        },
+        methods: {
+            clickGoods(item) { //点击商品
+                console.log(item);
+				uni.navigateTo({
+					url:'/pageA/shoplist'
+				})
+            },
+        },
+    }
+</script>
+
+<style lang="scss" scoped>
+    .goods {
+        display: flex;
+        flex-wrap: wrap;
+
+        .goods_item {
+            width: 33.3%;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            flex-direction: column;
+            margin-top: 20rpx;
+
+            .goods_item_img {
+                width: 120rpx;
+                height: 120rpx;
+            }
+
+            .goods_item_name {
+                color: #333;
+                font-size: 28rpx;
+                font-weight: 500rpx;
+                display: -webkit-box;
+                -webkit-box-orient: vertical;
+                -webkit-line-clamp: 1;
+                overflow: hidden;
+                word-break: break-all;
+                text-align: center;
+            }
+        }
+
+    }
+</style>

BIN
pageA/static/images/shaixuan.png


BIN
pageA/static/images/shaixuana.png


BIN
pageA/static/images/tabs.png


BIN
pageA/static/images/up.png


+ 2 - 2
pageB/delivery.vue

@@ -60,7 +60,7 @@
 					</view>
 					<view class="u-flex">
 						<text class="change">请选择物品信息</text>
-						<image src="../../static/express/next1.png"
+						<image src="/static/express/next1.png"
 							style="width: 28rpx;height: 28rpx;margin-left: 6rpx;" mode=""></image>
 					</view>
 				</view>
@@ -71,7 +71,7 @@
 					</view>
 					<view class="u-flex">
 						<text class="change" style="color: rgba(248, 50, 36, 1);">请选择</text>
-						<image src="../../static/express/next1.png"
+						<image src="/static/express/next1.png"
 							style="width: 28rpx;height: 28rpx;margin-left: 6rpx;" mode=""></image>
 					</view>
 				</view>

+ 546 - 0
pageB/end.vue

@@ -0,0 +1,546 @@
+<template>
+	<!-- 寄快递 -->
+	<view class="" style="padding: 0 24rpx;">
+		<view style="position: relative;padding-bottom: 20rpx;">
+			<!-- 自定义导航栏 -->
+			<image src="/static/images/index/back.png"
+				style="width: 750rpx;height: 1200rpx;position: absolute;top: 0;z-index: -1;left:-24rpx;" mode="">
+			</image>
+			<u-navbar title='寄快递' bgColor="rgba(248, 50, 36, 0.2)" :placeholder='true' :safeAreaInsetTop="true"
+				style="position: absolute;z-index: 110;background-color: rgba(0,0,0,0);">
+				<view class="u-nav-slot" slot="left" style="display: flex;">
+					<u-icon name="arrow-left" color="rgba(0, 0, 0, 1)" size="24" @click="uni.navigateBack()"></u-icon>
+				</view>
+			</u-navbar>
+			<!-- 邮寄地址 -->
+			<view class="mail">
+				<view class="u-flex u-row-around" style="margin-top: 40rpx;padding: 0 80rpx;">
+					<view class="flexc">
+						<view class="entrepotname">东莞仓库</view>
+						<view class="adda">陈盼盼</view>
+					</view>
+					<image src="/pageB/static/images/arrows.png" style="width: 130rpx;height: 10rpx;" mode=""></image>
+					<view class="flexc">
+						<view class="entrepotname">澳大利亚</view>
+						<view class="adda">Curtis Morris</view>
+					</view>
+				</view>
+				<view class="boxinfo">
+					<view class="">
+						<text class="abouta">类型:</text>
+						<text class="abouta" style='color:rgba(34, 34, 34, 1)'>日用品</text>
+						<text class="abouta" style='margin:0 16rpx'>|</text>
+						<text class="abouta">重量:</text>
+						<text class="abouta" style='color:rgba(34, 34, 34, 1)'>30.00kg</text>
+						<text class="abouta" style='margin:0 16rpx'>|</text>
+						<text class="abouta">数量:</text>
+						<text class="abouta" style='color:rgba(34, 34, 34, 1)'>20件</text>
+					</view>
+					<view class="" style="margin-top: 20rpx;">
+						<text class="abouta">跨境物流:</text>
+						<text class="abouta" style='color:rgba(34, 34, 34, 1)'>号海运柜</text>
+					</view>
+				</view>
+			</view>
+			<!-- 地址 -->
+			<view class="address">
+				<view class="left u-flex" style="display: flex;">
+					<view class="u-row-between left" style="display: flex;flex-direction: column;">
+						<image src="/static/express/ji.png" style='width: 40rpx;;height:40rpx ;' mode=""></image>
+						<image src="/static/express/change.png" style='width: 40rpx;;height:40rpx ;' mode="">
+						</image>
+					</view>
+					<view class=" u-row-between left"
+						style="flex: 1;margin-left: 28rpx;border-bottom:2rpx solid rgba(151, 151, 151, 0.08) ;display: flex;">
+						<view class="u-row-between " style="">
+							<view class="pople">新建寄件人</view>
+							<view class="jiadd" style="margin-top: 14rpx;">点击填写寄件地址信息</view>
+						</view>
+						<view class="u-flex">
+							<view class="" style="color: rgba(151, 151, 151, 1);">|</view>
+							<text class="right">地址簿</text>
+						</view>
+					</view>
+				</view>
+				<view class="left u-flex"
+					style="display: flex;margin-top: 26rpx;border-bottom:2rpx solid rgba(151, 151, 151, 0.08)">
+					<view class="u-row-between left" style="display: flex;flex-direction: column;">
+						<image src="/static/express/shou.png" style='width: 40rpx;;height:40rpx ;' mode=""></image>
+						<!-- <image src="../../static/express/change.png" style='width: 40rpx;;height:40rpx ;' mode=""> -->
+						</image>
+					</view>
+					<view class=" u-row-between left" style="flex: 1;margin-left: 28rpx;display: flex;">
+						<view class="u-row-between " style="">
+							<view class="pople">新建收件人</view>
+							<view class="jiadd" style="margin-top: 14rpx;">点击填写寄件地址信息</view>
+						</view>
+						<view class="u-flex">
+							<view class="" style="color: rgba(151, 151, 151, 1);">|</view>
+							<text class="right">地址簿</text>
+						</view>
+					</view>
+				</view>
+			</view>
+			<!-- 快递服务 -->
+			<view class="wup u-flex u-row-between">
+				<view>快递服务</view>
+				<view class="u-flex" @click="toexpress">
+					<view>{{expname||'请选择'}}</view>
+					<image src="../static/express/next1.png" style="width: 30rpx;height: 30rpx;" mode=""></image>
+				</view>
+			</view>
+			<!-- 下一步 -->
+			<view class="next  u-flex-between" style="display: flex;">
+				<!-- 				<view class="" style="flex: 1;">
+					<view class="sum">
+						<text>预估总价</text>
+						<text style="color: rgba(248, 50, 36, 1);">¥ - -</text>
+					</view>
+					<view class="deal u-flex" style="margin-top: 16rpx;">
+						<u-checkbox-group shape='circle' v-model="checkboxValue1" placement="column" @change="checkboxChange">
+							<u-checkbox :customStyle="{marginBottom: '8px',marginTop:'5px'}" :name="deal" >
+							</u-checkbox>
+						</u-checkbox-group>
+						<text>我阅读并同意</text>
+						<text style="color: rgba(248, 50, 36, 1);">《国际快递服务协议》</text>
+					</view>
+				</view> -->
+				<view class="tonext " style="">
+					确认下单
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				deal: -1,
+				checkboxValue1: [],
+				expname: ''
+			}
+		},
+		onLoad(options) {
+
+		},
+		methods: {
+			checkboxChange(n) {
+				console.log('change', n);
+			},
+			toexpress() {
+				uni.navigateTo({
+					url: '/pageB/expresstype',
+					events: {
+						todcp: (res) => {
+							this.expname = res
+						}
+					}
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.mail {
+		width: 702rpx;
+		height: 332rpx;
+		background: #FFFFFF;
+		border-radius: 16rpx;
+		margin-top: 38rpx;
+		padding: 1rpx 20rpx 28rpx;
+		box-sizing: border-box;
+
+		.abouta {
+			font-family: PingFangSC, PingFang SC;
+			font-weight: 400;
+			font-size: 24rpx;
+			color: #777777;
+			line-height: 34rpx;
+			text-align: left;
+			font-style: normal;
+		}
+
+		.boxinfo {
+			margin-top: 26rpx;
+			width: 662rpx;
+			height: 148rpx;
+			background: #F4F4F4;
+			border-radius: 12rpx;
+			padding: 28rpx 20rpx;
+			box-sizing: border-box;
+		}
+
+		.adda {
+			margin-top: 8rpx;
+			font-family: PingFangSC, PingFang SC;
+			font-weight: 400;
+			font-size: 24rpx;
+			color: #222222;
+			line-height: 34rpx;
+			text-align: right;
+			font-style: normal;
+		}
+
+		.entrepotname {
+			font-family: PingFangSC, PingFang SC;
+			font-weight: 500;
+			font-size: 32rpx;
+			color: #222222;
+			line-height: 44rpx;
+			text-align: left;
+			font-style: normal;
+		}
+	}
+
+	.sum {
+		font-family: PingFangSC, PingFang SC;
+		font-weight: 500;
+		font-size: 24rpx;
+		color: #222222;
+		line-height: 34rpx;
+		text-align: left;
+		font-style: normal;
+	}
+
+	.deal {
+		font-family: PingFangSC, PingFang SC;
+		font-weight: 400;
+		font-size: 20rpx;
+		color: #666666;
+		line-height: 28rpx;
+		text-align: left;
+		font-style: normal;
+	}
+
+	.tonext {
+		width: 686rpx;
+		height: 88rpx;
+		background: #F83224;
+		box-shadow: 0rpx 16rpx 40rpx -12rpx rgba(255, 21, 21, 0.5);
+		border-radius: 44rpx;
+		font-family: PingFangSC, PingFang SC;
+		font-weight: 500;
+		font-size: 32rpx;
+		color: #FFFFFF;
+		line-height: 88rpx;
+		text-align: center;
+		font-style: normal;
+	}
+
+	.next {
+		width: 750rpx;
+		height: 166rpx;
+		background: #FFFFFF;
+		position: fixed;
+		bottom: 0;
+		left: 0;
+		padding: 12rpx 24rpx;
+		box-sizing: border-box;
+	}
+
+	.wup {
+		width: 704rpx;
+		height: 76rpx;
+		background: #FFFFFF;
+		border-radius: 16rpx;
+		margin-top: 20rpx;
+		padding: 0rpx 20rpx;
+		box-sizing: border-box;
+	}
+
+	.info {
+		height: 100rpx;
+		border-bottom: 2rpx solid rgba(151, 151, 151, 0.08);
+
+
+
+		.wupin {
+			font-family: PingFangSC, PingFang SC;
+			font-weight: 400;
+			font-size: 28rpx;
+			color: #333333;
+			line-height: 40rpx;
+			text-align: left;
+			font-style: normal;
+		}
+
+		.change {
+			font-family: PingFangSC, PingFang SC;
+			font-weight: 400;
+			font-size: 28rpx;
+			color: #666666;
+			line-height: 40rpx;
+			text-align: left;
+			font-style: normal;
+		}
+
+		.mast {
+			width: 62rpx;
+			height: 30rpx;
+			border-radius: 16rpx;
+			border: 1rpx solid #F83224;
+			font-family: PingFangSC, PingFang SC;
+			font-weight: 400;
+			font-size: 20rpx;
+			color: #FF1515;
+			line-height: 30rpx;
+			text-align: center;
+			font-style: normal;
+			margin-left: 12rpx;
+		}
+	}
+
+	.mon {
+		font-family: HarmonyOS_Sans_Medium;
+		font-size: 20rpx;
+		color: #F83224;
+		line-height: 26rpx;
+		text-align: left;
+		font-style: normal;
+	}
+
+	.bigtitle {
+		font-family: PingFangSC, PingFang SC;
+		font-weight: 600;
+		font-size: 28rpx;
+		color: #222222;
+		line-height: 40rpx;
+		text-align: left;
+		font-style: normal;
+	}
+
+	.timeb {
+		font-family: PingFangSC, PingFang SC;
+		font-weight: 400;
+		font-size: 22rpx;
+		color: #666666;
+		line-height: 32rpx;
+		text-align: left;
+		font-style: normal;
+	}
+
+	.timea {
+		font-family: PingFangSC, PingFang SC;
+		font-weight: 400;
+		font-size: 22rpx;
+		color: #555555;
+		line-height: 32rpx;
+		text-align: left;
+		font-style: normal;
+	}
+
+	.pinb {
+		width: 148rpx;
+		height: 64rpx;
+		background: #FF1515;
+		border-radius: 42rpx;
+		font-family: PingFangSC, PingFang SC;
+		font-weight: 500;
+		font-size: 28rpx;
+		color: #FFFFFF;
+		line-height: 64rpx;
+		text-align: center;
+		font-style: normal;
+	}
+
+	.label {
+		margin-top: 20rpx;
+		width: 702rpx;
+		height: 288rpx;
+		background: #FFFFFF;
+		border-radius: 16rpx;
+		padding: 32rpx 20rpx;
+		box-sizing: border-box;
+
+		.left {
+			width: 144rpx;
+			height: 144rpx;
+			background: #F5F5F5;
+			border-radius: 8rpx;
+
+			.bourn {
+				font-family: PingFangSC, PingFang SC;
+				font-weight: 400;
+				font-size: 20rpx;
+				color: #222222;
+				line-height: 28rpx;
+				text-align: left;
+				font-style: normal;
+			}
+
+			.addres {
+				font-family: PingFangSC, PingFang SC;
+				font-weight: 500;
+				font-size: 28rpx;
+				color: #222222;
+				line-height: 40rpx;
+				text-align: left;
+				font-style: normal;
+			}
+		}
+	}
+
+	.flexc {
+		display: flex;
+		flex-direction: column;
+		justify-content: center;
+		align-items: center;
+	}
+
+	.hai {
+		font-family: PingFangSC, PingFang SC;
+		font-weight: 600;
+		font-size: 32rpx;
+		color: #222222;
+		line-height: 44rpx;
+		text-align: left;
+		font-style: normal;
+		margin-right: 10rpx;
+	}
+
+	.more {
+		font-family: PingFangSC, PingFang SC;
+		font-weight: 400;
+		font-size: 24rpx;
+		color: #F83224;
+		line-height: 34rpx;
+		text-align: left;
+		font-style: normal;
+	}
+
+	.parcel {
+		width: 702rpx;
+		height: 260rpx;
+		background: #FFFFFF;
+		border-radius: 16rpx;
+		margin-top: 20rpx;
+		padding: 24rpx;
+		box-sizing: border-box;
+
+		.myparcel {
+			font-family: PingFangSC, PingFang SC;
+			font-weight: 600;
+			font-size: 28rpx;
+			color: #222222;
+			line-height: 40rpx;
+			text-align: left;
+			font-style: normal;
+		}
+
+		.add {
+			width: 240rpx;
+			height: 48rpx;
+			background: rgba(248, 50, 36, 0.06);
+			border-radius: 24rpx;
+
+			.addtext {
+				font-family: PingFangSC, PingFang SC;
+				font-weight: 600;
+				font-size: 20rpx;
+				color: #000000;
+				line-height: 28rpx;
+				text-align: left;
+				font-style: normal;
+			}
+		}
+	}
+
+	.address {
+		width: 702rpx;
+		// height: 776rpx;
+		background: #FFFFFF;
+		border-radius: 20rpx;
+		padding: 48rpx 24rpx 36rpx;
+		box-sizing: border-box;
+		margin-top: 20rpx;
+
+		.bottom {
+			padding: 0 76rpx;
+			box-sizing: border-box;
+			margin-top: 48rpx;
+
+			.friend {
+				font-family: PingFangSC, PingFang SC;
+				font-weight: 400;
+				font-size: 28rpx;
+				color: #555555;
+				line-height: 40rpx;
+				text-align: left;
+				font-style: normal;
+				margin-left: 12rpx;
+			}
+		}
+
+		.mail {
+			width: 646rpx;
+			height: 84rpx;
+			background: #F83224;
+			box-shadow: 0rpx 16rpx 40rpx -12rpx rgba(255, 21, 21, 0.5);
+			border-radius: 44rpx;
+			font-family: PingFangSC, PingFang SC;
+			font-weight: 500;
+			font-size: 32rpx;
+			color: #FFFFFF;
+			line-height: 84rpx;
+			text-align: center;
+			font-style: normal;
+		}
+
+
+
+		.left {
+			height: 128rpx;
+
+			.pople {
+				font-family: PingFangSC, PingFang SC;
+				font-weight: 500;
+				font-size: 30rpx;
+				color: #222222;
+				line-height: 42rpx;
+				text-align: left;
+				font-style: normal;
+			}
+
+			.jiadd {
+				font-family: PingFangSC, PingFang SC;
+				font-weight: 400;
+				font-size: 26rpx;
+				color: #777777;
+				line-height: 36rpx;
+				text-align: left;
+				font-style: normal;
+			}
+
+			.right {
+				font-family: PingFangSC, PingFang SC;
+				font-weight: 500;
+				font-size: 26rpx;
+				color: #222222;
+				line-height: 36rpx;
+				text-align: left;
+				font-style: normal;
+				margin-left: 18rpx;
+			}
+		}
+	}
+
+	::v-deep .u-navbar__content.data-v-1194bf80 {
+		background-color: rgba(0, 0, 0, 0) !important;
+	}
+
+	::v-deep .u-status-bar.data-v-13f16680 {
+		background-color: rgba(0, 0, 0, 0) !important;
+	}
+
+	::v-deep .u-subsection--button.data-v-b14d3440 {
+		/* padding: 6rpx 34rpx !important; */
+		border-radius: 64rpx !important;
+	}
+
+	::v-deep .u-subsection--button__bar.data-v-b14d3440 {
+		border-radius: 32rpx !important;
+	}
+</style>

+ 104 - 0
pageB/expresstype.vue

@@ -0,0 +1,104 @@
+<template>
+	<view class="box">
+		<view class="exp">
+			<u-radio-group v-model="radiovalue1" placement="column" @change="groupChange">
+				<view class="item u-flex u-row-between" v-for="(item,idx) in radiolist1" :key="idx">
+					<text>{{item.name}}</text>
+					<u-radio :name="item.name" @change="radioChange">
+					</u-radio>
+				</view>
+			</u-radio-group>
+		</view>
+		<view class="bottom">
+			<view class="btn" @click="toteach">
+				确定
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				concant:'',
+				radiolist1: [{
+						name: '顺丰标快',
+						disabled: false
+					},
+					{
+						name: '中通快递',
+						disabled: false
+					},
+					{
+						name: '韵达快递',
+						disabled: false
+					}, {
+						name: '圆通快递',
+						disabled: false
+					}
+				],
+			};
+		},
+		methods: {
+			toteach() {
+				const eventChannel = this.getOpenerEventChannel();
+				eventChannel.emit('todcp', this.concant);
+				uni.navigateBack()
+			},
+			groupChange(n) {
+				console.log('groupChange', n);
+			},
+			radioChange(n) {
+				console.log('radioChange', n);
+				this.concant = n
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.box {
+		padding: 20rpx 24rpx;
+		box-sizing: border-box;
+
+		.bottom {
+			width: 750rpx;
+			height: 166rpx;
+			background: #FFFFFF;
+			padding: 18rpx 32rpx;
+			box-sizing: border-box;
+			position: fixed;
+			bottom: 0;
+			left: 0;
+
+			.btn {
+				width: 686rpx;
+				height: 88rpx;
+				background: #F83224;
+				border-radius: 44rpx;
+				font-family: PingFangSC, PingFang SC;
+				font-weight: 500;
+				font-size: 32rpx;
+				color: #FFFFFF;
+				line-height: 88rpx;
+				text-align: center;
+				font-style: normal;
+			}
+		}
+
+		.item {
+			width: 654rpx;
+			height: 112rpx;
+			border-bottom: 2rpx solid rgba(151, 151, 151, 0.1);
+		}
+
+		.exp {
+			width: 702rpx;
+			// height: 564rpx;
+			background: #FFFFFF;
+			border-radius: 16rpx;
+			padding: 0 20rpx;
+		}
+	}
+</style>

+ 54 - 43
pageB/goods.vue

@@ -6,13 +6,22 @@
 				<text class="title">物品信息</text>
 				<view class="mast">必填</view>
 			</view>
-			<view class="info">填写准确的寄送物品可帮助您推荐更合适的产品</view>
-			<view class="input">
-				<input type="text" placeholder="请输入物品名称" style="width: 100%;" />
-			</view>
-			<view class="u-flex u-row-between" style="margin-top: 28rpx;flex-wrap: wrap;">
-				<view class="about" v-for="(item,idx) in 10">文件</view>
+			<view class="" style="margin-top: 30rpx;">
+				<view class="about u-flex"
+					style="color: rgba(248, 50, 36, 1);border: 2rpx solid rgba(248, 50, 36, 1);justify-content: center;">
+					<text style="margin-right: 18rpx;">文件</text>
+					<u-icon name="close" color="rgba(248, 50, 36, 1)" size="12"></u-icon>
+				</view>
 			</view>
+			<!-- 			<view class="">
+				<view class="info">填写准确的寄送物品可帮助您推荐更合适的产品</view>
+				<view class="input">
+					<input type="text" placeholder="请输入物品名称" style="width: 100%;" />
+				</view>
+				<view class="u-flex u-row-between" style="margin-top: 28rpx;flex-wrap: wrap;">
+					<view class="about" v-for="(item,idx) in 10">文件</view>
+				</view>
+			</view> -->
 		</view>
 		<view class="type  u-flex u-row-between">
 			<view class="">
@@ -38,7 +47,8 @@
 					</u-number-box>
 				</view>
 			</view>
-			<view class="u-flex u-row-between" style="border-top: 2rpx solid rgba(151, 151, 151, 0.1);padding-top: 30rpx;margin-top: 32rpx;">
+			<view class="u-flex u-row-between"
+				style="border-top: 2rpx solid rgba(151, 151, 151, 0.1);padding-top: 30rpx;margin-top: 32rpx;">
 				<view class="title">件数</view>
 				<view class="">
 					<u-number-box v-model="value">
@@ -69,48 +79,49 @@
 	export default {
 		data() {
 			return {
-				value:1
+				value: 1
 			};
 		}
 	}
 </script>
 
 <style lang="scss" scoped>
-		.minus {
-			width: 22px;
-			height: 22px;
-			border-width: 1px;
-			border-color: rgba(0, 0, 0, 0);
-			border-style: solid;
-			border-top-left-radius: 100px;
-			border-top-right-radius: 100px;
-			border-bottom-left-radius: 100px;
-			border-bottom-right-radius: 100px;
-			@include flex;
-			justify-content: center;
-			align-items: center;
-		}
-	
-		.inpu {
-			padding: 0 20px;
-			width: 150rpx;
-			height: 52rpx;
-			background: #F4F4F4;
-			text-align: center;
-			line-height: 52rpx;
-		}
-	
-		.plus {
-			width: 22px;
-			height: 22px;
-			// background-color: #FF0000;
-			border-radius: 50%;
-			/* #ifndef APP-NVUE */
-			display: flex;
-			/* #endif */
-			justify-content: center;
-			align-items: center;
-		}
+	.minus {
+		width: 22px;
+		height: 22px;
+		border-width: 1px;
+		border-color: rgba(0, 0, 0, 0);
+		border-style: solid;
+		border-top-left-radius: 100px;
+		border-top-right-radius: 100px;
+		border-bottom-left-radius: 100px;
+		border-bottom-right-radius: 100px;
+		@include flex;
+		justify-content: center;
+		align-items: center;
+	}
+
+	.inpu {
+		padding: 0 20px;
+		width: 150rpx;
+		height: 52rpx;
+		background: #F4F4F4;
+		text-align: center;
+		line-height: 52rpx;
+	}
+
+	.plus {
+		width: 22px;
+		height: 22px;
+		// background-color: #FF0000;
+		border-radius: 50%;
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		justify-content: center;
+		align-items: center;
+	}
+
 	.back {
 		padding: 20rpx 24rpx;
 

+ 17 - 6
pageB/list/list.vue

@@ -45,7 +45,7 @@
 			</view>
 			<view class="u-flex u-row-between" style="margin-top: 32rpx;">
 				<text class="timea">开始时间:2023-12-31/截止时间:2023-12-31</text>
-				<view class="pinb">去拼包</view>
+				<view class="pinb" @click="toaccount">去拼包</view>
 			</view>
 		</view>
 	</view>
@@ -57,6 +57,14 @@
 			return {
 				percentage: 50, //进度条
 			};
+		},
+		methods: {
+			//拼包快递
+			toaccount() {
+				uni.navigateTo({
+					url: '/pageB/ParcelExpress'
+				})
+			},
 		}
 	}
 </script>
@@ -68,10 +76,12 @@
 		justify-content: center;
 		align-items: center;
 	}
-	.back{
+
+	.back {
 		padding: 20rpx 24rpx;
 		box-sizing: border-box;
 	}
+
 	.pinb {
 		width: 148rpx;
 		height: 64rpx;
@@ -85,7 +95,7 @@
 		text-align: center;
 		font-style: normal;
 	}
-	
+
 	.mon {
 		font-family: HarmonyOS_Sans_Medium;
 		font-size: 20rpx;
@@ -94,7 +104,7 @@
 		text-align: left;
 		font-style: normal;
 	}
-	
+
 	.bigtitle {
 		font-family: PingFangSC, PingFang SC;
 		font-weight: 600;
@@ -104,7 +114,7 @@
 		text-align: left;
 		font-style: normal;
 	}
-	
+
 	.timeb {
 		font-family: PingFangSC, PingFang SC;
 		font-weight: 400;
@@ -114,7 +124,7 @@
 		text-align: left;
 		font-style: normal;
 	}
-	
+
 	.timea {
 		font-family: PingFangSC, PingFang SC;
 		font-weight: 400;
@@ -124,6 +134,7 @@
 		text-align: left;
 		font-style: normal;
 	}
+
 	.label {
 		margin-top: 20rpx;
 		width: 702rpx;

+ 5 - 3
pageB/myPackage.vue

@@ -8,7 +8,7 @@
 			</view>
 			<!-- rgba(252, 97, 74, 1) -->
 			<view class="">
-				<u-tabs :scrollable='false' :list="list1" @click="click"></u-tabs>
+				<u-tabs :scrollable='false' :current='current' :list="list1" @click="click"></u-tabs>
 			</view>
 		</view>
 		<view class="content">
@@ -75,6 +75,10 @@
 				}]
 			};
 		},
+		onLoad(options) {
+			console.log(options);
+			this.current = options.index
+		},
 		onShow() {
 
 		},
@@ -102,8 +106,6 @@
 		padding: 20rpx 24rpx;
 		box-sizing: border-box;
 
-
-
 		.content-bottom {
 			border-top: 2rpx solid rgba(151, 151, 151, 0.1);
 			margin-top: 30rpx;

+ 138 - 0
pageB/orderinfo.vue

@@ -0,0 +1,138 @@
+<template>
+	<view class="back">
+		<view class="top u-flex u-row-between">
+			<text class="success">下单成功</text>
+			<text class="sun">待快递员上门取件</text>
+		</view>
+		<view class="address">
+			<view class="" style="margin-top: 44rpx;">
+				<view class="u-flex">
+					<image src="/static/express/ji.png" style="width: 44rpx;height: 44rpx;" mode=""></image>
+					<text class="name" style="margin-left: 28rpx;">陈盼盼</text>
+					<text class="name" style="margin-left: 12rpx;">134****8314</text>
+				</view>
+				<view style="margin-top: 20rpx;margin-left: 70rpx;" class="info">
+					上海浦东新区南汇新城镇上海市浦东新区保税
+				</view>
+			</view>
+			<view class="" style="margin: 44rpx 0;">
+				<view class="u-flex">
+					<image src="/static/express/ji.png" style="width: 44rpx;height: 44rpx;" mode=""></image>
+					<text class="name" style="margin-left: 28rpx;">陈盼盼</text>
+					<text class="name" style="margin-left: 12rpx;">134****8314</text>
+				</view>
+				<view style="margin-top: 20rpx;margin-left: 70rpx;" class="info">
+					上海浦东新区南汇新城镇上海市浦东新区保税
+				</view>
+			</view>
+		</view>
+		<view class="orderinfo">
+			<view class="num">
+				<text class='num'>订单编号:</text>
+				<text class='num'>JDV18873891934</text>
+			</view>
+			<view class="num" style="margin-top: 50rpx;">
+				<text class='num'>下单时间::</text>
+				<text class='num'>2023-12-03 22:44:15</text>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+
+			};
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.orderinfo {
+		width: 702rpx;
+		height: 184rpx;
+		background: #FFFFFF;
+		border-radius: 16rpx;
+		margin-top: 20rpx;
+		padding: 36rpx 28rpx 0;
+		box-sizing: border-box;
+
+		.num {
+			font-family: SFPro, SFPro;
+			font-weight: 400;
+			font-size: 26rpx;
+			color: #555555;
+			line-height: 30rpx;
+			text-align: left;
+			font-style: normal;
+		}
+	}
+
+	.back {
+		padding: 20rpx 24rpx;
+		box-sizing: border-box;
+
+		.top {
+			width: 702rpx;
+			height: 100rpx;
+			background: linear-gradient(291deg, #FFF4F4 0%, #FBDFDF 100%, #FBDFDF 100%);
+			border-radius: 16rpx 16rpx 0 0;
+			border: 1rpx solid;
+			border-image: linear-gradient(270deg, rgba(255, 255, 255, 1), rgba(255, 255, 255, 0)) 1 1;
+			padding: 0 24rpx;
+			box-sizing: border-box;
+
+			.success {
+				font-family: PingFangSC, PingFang SC;
+				font-weight: 500;
+				font-size: 32rpx;
+				color: #222222;
+				line-height: 44rpx;
+				text-align: left;
+				font-style: normal;
+			}
+
+			.sun {
+				font-family: PingFangSC, PingFang SC;
+				font-weight: 400;
+				font-size: 24rpx;
+				color: #F83224;
+				line-height: 34rpx;
+				text-align: left;
+				font-style: normal;
+			}
+		}
+
+		.address {
+			width: 702rpx;
+			// height: 396rpx;
+			background: #FFFFFF;
+			border-radius: 16rpx;
+			padding: 1rpx 32rpx;
+			box-sizing: border-box;
+
+			.name {
+				font-family: PingFangSC, PingFang SC;
+				font-weight: 500;
+				font-size: 32rpx;
+				color: #222222;
+				line-height: 44rpx;
+				text-align: left;
+				font-style: normal;
+			}
+
+			.info {
+				font-family: PingFangSC, PingFang SC;
+				font-weight: 400;
+				font-size: 26rpx;
+				color: #777777;
+				line-height: 36rpx;
+				text-align: left;
+				font-style: normal;
+				margin-top: 18rpx;
+			}
+		}
+	}
+</style>

+ 99 - 0
pageB/search.vue

@@ -0,0 +1,99 @@
+<template>
+	<view class="back">
+		<!-- 搜索 -->
+		<view class="u-flex u-row-between">
+			<view class="" style="width: 622rpx;">
+				<u-search @blur='blur' @focus='focus' @click="search" placeholder="请输入物品名称" v-model="keyword"
+					:showAction='false'></u-search>
+			</view>
+			<text @click="clear" v-if="keyword==''">取消</text>
+			<text @click="enter" v-else>确认</text>
+		</view>
+		<view v-if="blurshow==false && keyword==''" class="title" style="margin-top: 32rpx;">
+			热门寄件
+		</view>
+		<view v-if="blurshow==false && keyword==''" class="u-flex " style="column-gap: 20rpx;flex-wrap: wrap;">
+			<view class="tabs" v-for="(item,idx) in 8" :key="idx" @click="select">
+				行李
+			</view>
+		</view>
+		<scroll-view  v-if="blurshow && keyword" :scroll-y='true'>
+			<view class="searchitem" v-for="(item,index) in 6">草莓</view>
+		</scroll-view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				keyword: '',
+				blurshow: false, //是否显示热门寄件
+			};
+		},
+		methods: {
+			//取消
+			clear() {
+				uni.navigateBack()
+			},
+			enter() {
+
+			},
+			search() {
+
+			},
+			// 失去焦点
+			blur() {
+				this.blurshow = false
+			},
+			//获取焦点
+			focus() {
+				this.blurshow = true
+			},
+			//选中
+			select() {
+
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.searchitem{
+		height: 98rpx;
+		border-bottom: 2rpx solid rgba(151, 151, 151, 0.1);
+		line-height: 98rpx;
+	}
+	.title {
+		font-family: PingFangSC, PingFang SC;
+		font-weight: 500;
+		font-size: 28rpx;
+		color: #222222;
+		line-height: 40rpx;
+		text-align: left;
+		font-style: normal;
+	}
+
+	.tabs {
+		height: 58rpx;
+		background: #F5F5F5;
+		border-radius: 36rpx;
+		font-family: PingFangSC, PingFang SC;
+		font-weight: 400;
+		font-size: 24rpx;
+		color: #333333;
+		// line-height: 58rpx;
+		text-align: center;
+		font-style: normal;
+		display: inline;
+		padding: 12rpx 24rpx;
+		box-sizing: border-box;
+		margin-top: 24rpx;
+	}
+
+	.back {
+		padding: 28rpx 20rpx !important;
+		background-color: #fff;
+		min-height: calc(100vh - 44rpx);
+	}
+</style>

+ 50 - 49
pageC/orderForm/orderForm.vue

@@ -13,60 +13,61 @@
 				</view>
 			</view>
 		</view>
+	</view>
 </template>
 
 <script>
-export default {
-    data() {
-        const currentDate = this.getDate({
-            format: true
-        })
-        return {
-            title: 'picker',
-            array: ['中国', '美国', '巴西', '日本'],
-            index: 0,
-            date: currentDate,
-            time: '12:01'
-        }
-    },
-    computed: {
-        startDate() {
-            return this.getDate('start');
-        },
-        endDate() {
-            return this.getDate('end');
-        }
-    },
-    methods: {
-        bindPickerChange: function(e) {
-            console.log('picker发送选择改变,携带值为', e.detail.value)
-            this.index = e.detail.value
-        },
-        bindDateChange: function(e) {
-            this.date = e.detail.value
-        },
-        bindTimeChange: function(e) {
-            this.time = e.detail.value
-        },
-        getDate(type) {
-            const date = new Date();
-            let year = date.getFullYear();
-            let month = date.getMonth() + 1;
-            let day = date.getDate();
+	export default {
+		data() {
+			const currentDate = this.getDate({
+				format: true
+			})
+			return {
+				title: 'picker',
+				array: ['中国', '美国', '巴西', '日本'],
+				index: 0,
+				date: currentDate,
+				time: '12:01'
+			}
+		},
+		computed: {
+			startDate() {
+				return this.getDate('start');
+			},
+			endDate() {
+				return this.getDate('end');
+			}
+		},
+		methods: {
+			bindPickerChange: function(e) {
+				console.log('picker发送选择改变,携带值为', e.detail.value)
+				this.index = e.detail.value
+			},
+			bindDateChange: function(e) {
+				this.date = e.detail.value
+			},
+			bindTimeChange: function(e) {
+				this.time = e.detail.value
+			},
+			getDate(type) {
+				const date = new Date();
+				let year = date.getFullYear();
+				let month = date.getMonth() + 1;
+				let day = date.getDate();
 
-            if (type === 'start') {
-                year = year - 60;
-            } else if (type === 'end') {
-                year = year + 2;
-            }
-            month = month > 9 ? month : '0' + month;
-            day = day > 9 ? day : '0' + day;
-            return `${year}-${month}-${day}`;
-        }
-    }
-}
+				if (type === 'start') {
+					year = year - 60;
+				} else if (type === 'end') {
+					year = year + 2;
+				}
+				month = month > 9 ? month : '0' + month;
+				day = day > 9 ? day : '0' + day;
+				return `${year}-${month}-${day}`;
+			}
+		}
+	}
 </script>
 
 <style lang="scss">
 
-</style>
+</style>

+ 55 - 4
pages.json

@@ -30,14 +30,16 @@
 			"path": "pages/shopping/shopping",
 			"style": {
 				"navigationBarTitleText": "",
-				"enablePullDownRefresh": false
+				"enablePullDownRefresh": false,
+				"navigationStyle": "custom"
 			}
 		},
 		{
 			"path": "pages/cart/cart",
 			"style": {
 				"navigationBarTitleText": "",
-				"enablePullDownRefresh": false
+				"enablePullDownRefresh": false,
+				"navigationStyle": "custom"
 			}
 		},
 		{
@@ -136,6 +138,22 @@
 						"navigationBarTitleText": "支付结果",
 						"enablePullDownRefresh": false
 					}
+				},
+				{
+					"path" : "shoptype",
+					"style" : 
+					{
+						"navigationBarTitleText" : "商品分类",
+						"enablePullDownRefresh" : false
+					}
+				},
+				{
+					"path" : "shoplist",
+					"style" : 
+					{
+						"navigationBarTitleText" : "",
+						"enablePullDownRefresh" : false
+					}
 				}
 			]
 		},
@@ -194,7 +212,7 @@
 				{
 					"path": "list/list",
 					"style": {
-						"navigationBarTitleText": "拼列表",
+						"navigationBarTitleText": "拼列表",
 						"enablePullDownRefresh": false
 					}
 				},
@@ -223,6 +241,39 @@
 						"navigationBarTitleText" : "我的包裹",
 						"enablePullDownRefresh" : false
 					}
+				},
+				{
+					"path" : "end",
+					"style" : 
+					{
+						"navigationBarTitleText" : "",
+						"enablePullDownRefresh" : false,
+						"navigationStyle": "custom"
+					}
+				},
+				{
+					"path" : "expresstype",
+					"style" : 
+					{
+						"navigationBarTitleText" : "快递服务",
+						"enablePullDownRefresh" : false
+					}
+				},
+				{
+					"path" : "orderinfo",
+					"style" : 
+					{
+						"navigationBarTitleText" : "订单详情",
+						"enablePullDownRefresh" : false
+					}
+				},
+				{
+					"path" : "search",
+					"style" : 
+					{
+						"navigationBarTitleText" : "物品信息",
+						"enablePullDownRefresh" : false
+					}
 				}
 			]
 		},
@@ -361,7 +412,7 @@
 	],
 	"globalStyle": {
 		"navigationBarTextStyle": "black",
-		"navigationBarTitleText": "uni-app",
+		"navigationBarTitleText": "跨境电商",
 		"navigationBarBackgroundColor": "#fff",
 		"backgroundColor": "#222"
 	},

+ 346 - 4
pages/cart/cart.vue

@@ -1,7 +1,93 @@
 <template>
 	<!-- 购物车 -->
 	<view>
-
+		<u-navbar :safeAreaInsetTop="true" :placeholder='true'>
+			<view class="u-nav-slot" slot="left">
+				<view class="">
+					<text class="shopcart">购物车</text>
+					<text class="sum">(12)</text>
+				</view>
+			</view>
+			<view class="u-nav-slot" slot="right">
+				<view class="u-flex">
+					<text @click="admin" v-if="success">管理</text>
+					<text @click="admin" v-else>完成</text>
+					<view class="" style="width: 200rpx;"></view>
+				</view>
+			</view>
+		</u-navbar>
+		<u-checkbox-group v-model="checkboxValue1" placement="column" @change="checkboxChange">
+			<view class="box" v-for="(item,idx) in 6" :key="idx">
+				<view class="item">
+					<view class="shopname u-flex">
+						<image src="/static/images/logo.png" style="width: 28rpx;height: 28rpx;" mode=""></image>
+						<text>妞妞团🇨🇳进群抢秒杀❗️❗️</text>
+						<image src="/static/express/next1.png" style="width: 32rpx;height: 32rpx;" mode=""></image>
+					</view>
+					<u-swipe-action>
+						<view class="u-flex" style="margin-top: 30rpx;width: 100%;" v-for="(chid,index) in 2"
+							:key="index">
+							<u-swipe-action-item :options="options2">
+								<u-checkbox shape='circle' :customStyle="{marginBottom: '8px'}" :name="shopitem">
+								</u-checkbox>
+								<image src="../../static/images/logo.png" style="width: 172rpx;height: 172rpx;" mode="">
+								</image>
+								<view class="good" style="flex: 1;">
+									<view class="goodname">布兰德超级锁鲜全价主食冻干成</view>
+									<view class="goods">兔肉味-提高免疫 主食白金包</view>
+									<view class="" style="margin-top: 21rpx;">
+										<view class="goodtab">包邮</view>
+									</view>
+									<view class="u-flex u-row-between" style="width: 100%;margin-top: 20rpx;">
+										<view class="">
+											<text class="money">¥</text>
+											<text class="money" style="font-size: 30rpx;">179</text>
+											<text class="money">.00</text>
+											<text class="weight">500g</text>
+										</view>
+										<view class="">
+											<view class="" v-if="num&&success">
+												<u-number-box v-model="value" @change="valChange"></u-number-box>
+											</view>
+											<view class="num" v-if="num==false&&success" @click="num = true">
+												x1
+											</view>
+										</view>
+										<view v-if="success==false" class="icon">
+											<u-icon name="trash" color="#fff" size="20"></u-icon>
+										</view>
+									</view>
+								</view>
+							</u-swipe-action-item>
+						</view>
+					</u-swipe-action>
+				</view>
+			</view>
+		</u-checkbox-group>
+		<view class="" style="height: 140rpx;"></view>
+		<view class="bottom u-flex u-row-between" style="position: fixed;left: 0;z-index: 222;"
+			:style="{bottom:tabarheight + 'px'}">
+			<view class="u-flex">
+				<u-checkbox-group v-model="all" placement="column" @change="checkAll">
+					<u-checkbox shape="circle" :name="selectall">
+					</u-checkbox>
+				</u-checkbox-group>
+				<text>全选</text>
+			</view>
+			<view v-if="success==false" class="dle">
+				删除
+			</view>
+			<view class="u-flex" v-else>
+				<view class="">
+					<text class="allweight">总重量 50kg,</text>
+					<text class="sum">合计:</text>
+					<text class='summon'>¥</text>
+					<text class='summon' style="font-size: 36rpx;">188</text>
+					<text class='summon'>.34</text>
+				</view>
+				<view class="settle">去结算</view>
+			</view>
+		</view>
 		<kj-tabbar :value1=3></kj-tabbar>
 	</view>
 </template>
@@ -10,12 +96,268 @@
 	export default {
 		data() {
 			return {
-				
+				success: true,
+				shopitem: -1,
+				checkboxValue1: [], //商品数组
+				value: 0,
+				all: [], //全选数组
+				selectall: -1,
+				num: false,
+				options2: [{
+					text: '收藏',
+					style: {
+						backgroundColor: '#3c9cff'
+					}
+				}, {
+					text: '删除',
+					style: {
+						backgroundColor: '#f56c6c'
+					}
+				}],
+				tabarheight: ''
 			};
+		},
+		onLoad() {
+			const systemInfo = wx.getSystemInfoSync()
+			console.log(systemInfo);
+			const tab = systemInfo.screenHeight - systemInfo.safeArea.bottom + systemInfo.statusBarHeight;
+			this.tabarheight = tab
+			console.log(this.tabarheight);
+		},
+		methods: {
+			admin() {
+				this.success = !this.success
+				console.log(this.success);
+			},
+			valChange(e) {
+				console.log('当前值为: ' + e.value)
+			},
+			//全选
+			checkAll(e) {
+				console.log('all', e);
+			},
+			checkboxChange(n) {
+				console.log('change', n);
+			}
 		}
 	}
 </script>
 
-<style lang="scss">
+<style lang="scss" scoped>
+	.allweight {
+		font-family: PingFangSC, PingFang SC;
+		font-weight: 400;
+		font-size: 22rpx;
+		color: #333333;
+		line-height: 32rpx;
+		text-align: left;
+		font-style: normal;
+	}
+
+	.sum {
+		font-family: PingFangSC, PingFang SC;
+		font-weight: 400;
+		font-size: 22rpx;
+		color: #333333;
+		line-height: 32rpx;
+		text-align: left;
+		font-style: normal;
+	}
+
+	.summon {
+		font-family: JDZhengHT, JDZhengHT;
+		font-weight: 400;
+		font-size: 24rpx;
+		color: #FF1515;
+		line-height: 30rpx;
+		text-align: left;
+		font-style: normal;
+	}
+
+	.settle {
+		width: 176rpx;
+		height: 68rpx;
+		background: #F83224;
+		border-radius: 38rpx;
+		font-family: PingFangSC, PingFang SC;
+		font-weight: 500;
+		font-size: 28rpx;
+		color: #FFFFFF;
+		line-height: 68rpx;
+		text-align: center;
+		font-style: normal;
+		margin-left: 20rpx;
+	}
+
+	.icon {
+		width: 40rpx;
+		height: 40rpx;
+		background: #F83224;
+		border-radius: 12rpx;
+		display: flex;
+		justify-content: center;
+		align-items: center;
+	}
+
+	.dle {
+		width: 136rpx;
+		height: 60rpx;
+		border-radius: 38rpx;
+		border: 1rpx solid #979797;
+		font-family: PingFangSC, PingFang SC;
+		font-weight: 400;
+		font-size: 26rpx;
+		color: #333333;
+		line-height: 60rpx;
+		text-align: center;
+		font-style: normal;
+	}
+
+	::v-deep .u-swipe-action-item__content {
+		flex-direction: row !important;
+	}
+
+	::v-deep .u-swipe-action-item {
+		width: 100% !important;
+	}
+
+	::v-deep .u-swipe-action-item__right {
+		top: 2rpx !important;
+		right: 2rpx !important;
+		bottom: 2rpx !important;
+	}
 
-</style>
+	.bottom {
+		width: 750rpx;
+		height: 100rpx;
+		background: #FFFFFF;
+		padding: 0 24rpx;
+		box-sizing: border-box;
+	}
+
+	.num {
+		width: 40rpx;
+		height: 40rpx;
+		border-radius: 20rpx;
+		border: 1rpx solid rgba(151, 151, 151, 0.4);
+		font-family: HarmonyOS_Sans;
+		font-size: 20rpx;
+		color: #222222;
+		line-height: 40rpx;
+		text-align: center;
+		font-style: normal;
+	}
+
+	.box {
+		padding: 0rpx 24rpx 20rpx;
+		margin-top: 20rpx;
+		box-sizing: border-box;
+
+		.item {
+			padding: 36rpx 20rpx;
+			box-sizing: border-box;
+			width: 702rpx;
+			// height: 556rpx;
+			background: #FFFFFF;
+			border-radius: 16rpx;
+
+			.money {
+				font-family: HarmonyOS_Sans_Medium;
+				font-size: 20rpx;
+				color: #FF1515;
+				line-height: 26rpx;
+				text-align: left;
+				font-style: normal;
+			}
+
+			.weight {
+				font-family: SFPro, SFPro;
+				font-weight: 400;
+				font-size: 20rpx;
+				color: #333333;
+				line-height: 24rpx;
+				text-align: left;
+				font-style: normal;
+				margin-left: 12rpx;
+			}
+
+			.goodtab {
+				// width: 96rpx;
+				height: 32rpx;
+				border-radius: 4rpx;
+				border: 1rpx solid #FF1515;
+				padding: 2rpx 8rpx;
+				box-sizing: border-box;
+				text-align: center;
+				line-height: 32rpx;
+				display: inline;
+				font-family: PingFangSC, PingFang SC;
+				font-weight: 400;
+				font-size: 20rpx;
+				color: #FF1515;
+				line-height: 28rpx;
+				text-align: left;
+				font-style: normal;
+			}
+
+			.goods {
+				// width: 284rpx;
+				height: 40rpx;
+				background: #F4F4F4;
+				border-radius: 8rpx;
+				display: inline;
+				text-align: center;
+				line-height: 40rpx;
+				margin-top: 20rpx;
+				padding: 6rpx 12rpx;
+				box-sizing: border-box;
+				font-family: PingFangSC, PingFang SC;
+				font-weight: 400;
+				font-size: 20rpx;
+				color: #555555;
+				line-height: 28rpx;
+				text-align: left;
+				font-style: normal;
+			}
+
+			.goodname {
+				font-family: PingFangSC, PingFang SC;
+				font-weight: 500;
+				font-size: 28rpx;
+				color: #333333;
+				line-height: 40rpx;
+				text-align: left;
+				font-style: normal;
+			}
+
+			.shopname {
+				font-family: PingFangSC, PingFang SC;
+				font-weight: 500;
+				font-size: 26rpx;
+				color: #333333;
+				line-height: 36rpx;
+				text-align: left;
+				font-style: normal;
+			}
+		}
+	}
+
+	.shopcart {
+		font-family: PingFangSC, PingFang SC;
+		font-weight: 500;
+		font-size: 36rpx;
+		color: #222222;
+		line-height: 50rpx;
+		text-align: right;
+		font-style: normal;
+	}
+
+	.sum {
+		font-family: HarmonyOS_Sans;
+		font-size: 22rpx;
+		color: #333333;
+		line-height: 32rpx;
+		text-align: left;
+		font-style: normal;
+	}
+</style>

+ 87 - 18
pages/express/express.vue

@@ -11,7 +11,7 @@
 				<view class="u-nav-slot" slot="left" style="display: flex;">
 					<image src="../../static/images/index/xiaoxi.png" mode="" style="width: 40rpx;height: 40rpx;">
 					</image>
-					<image src="../../static/images/index/compile.png" mode=""
+					<image @click="counter" src="../../static/images/index/compile.png" mode=""
 						style="width: 40rpx;height: 40rpx;margin-left: 22rpx;"></image>
 					<image src="../../static/images/index/dianhua.png" mode=""
 						style="width: 40rpx;height: 40rpx;margin-left: 22rpx;"></image>
@@ -31,7 +31,7 @@
 					</view>
 					<view class=" u-row-between left"
 						style="flex: 1;margin-left: 28rpx;border-bottom:2rpx solid rgba(151, 151, 151, 0.08) ;display: flex;">
-						<view class="u-row-between " style="">
+						<view class="u-row-between " style="" @click="send">
 							<view class="pople">新建寄件人</view>
 							<view class="jiadd" style="margin-top: 14rpx;">点击填写寄件地址信息</view>
 						</view>
@@ -49,7 +49,7 @@
 						</image>
 					</view>
 					<view class=" u-row-between left" style="flex: 1;margin-left: 28rpx;display: flex;">
-						<view class="u-row-between " style="">
+						<view class="u-row-between " style="" @click="recipirnt">
 							<view class="pople">新建收件人</view>
 							<view class="jiadd" style="margin-top: 14rpx;">点击填写寄件地址信息</view>
 						</view>
@@ -65,7 +65,7 @@
 						<text class="wupin">物品信息</text>
 						<view class="mast">必填</view>
 					</view>
-					<view class="u-flex">
+					<view class="u-flex" @click='goods'>
 						<text class="change">请选择物品信息</text>
 						<image src="../../static/express/next1.png"
 							style="width: 28rpx;height: 28rpx;margin-left: 6rpx;" mode=""></image>
@@ -76,7 +76,7 @@
 					<view class="u-flex">
 						<text class="wupin">跨境物流</text>
 					</view>
-					<view class="u-flex">
+					<view class="u-flex" @click="logshow=true">
 						<text class="change" style="color: rgba(248, 50, 36, 1);">请选择</text>
 						<image src="../../static/express/next1.png"
 							style="width: 28rpx;height: 28rpx;margin-left: 6rpx;" mode=""></image>
@@ -84,7 +84,7 @@
 				</view>
 				<!-- 去寄件 -->
 				<view class="" style="margin-top: 34rpx;">
-					<view class="mail">
+					<view class="mail" @click="count">
 						去寄件
 					</view>
 				</view>
@@ -94,7 +94,7 @@
 						<text class="friend">分享给好友</text>
 					</view>
 					<view class="friend">|</view>
-					<view class="u-flex">
+					<view class="u-flex" @click="counter">
 						<image src="/static/images/index/compile.png" style="width: 36rpx;height: 36rpx;" mode="">
 						</image>
 						<text class="friend">物流计算器</text>
@@ -114,22 +114,22 @@
 					</view>
 				</view>
 				<view class="u-flex u-row-around" style="margin-top: 44rpx;">
-					<view class="flexc" style="position: relative;">
+					<view class="flexc" style="position: relative;" @click="pack(0)">
 						<!-- <view class="" style="position: absolute;top: 0;right: 0;"> -->
 						<u-badge :absolute='true' :offset="[0,10]" :type="type" max="99" :value="value"></u-badge>
 						<!-- </view> -->
 						<image src="/static/express/address.png" style='width:68rpx;height:68rpx' mode=""></image>
-						<text style='margin-top:16rpx'>发往中转点</text>
+						<text style='margin-top:16rpx' >发往中转点</text>
 					</view>
-					<view class="flexc" style="position: relative;">
+					<view class="flexc" style="position: relative;" @click="pack(1)">
 						<u-badge :absolute='true' :offset="[0,10]" :type="type" max="99" :value="value"></u-badge>
 						<image src="/static/express/sign.png" style='width:68rpx;height:68rpx' mode=""></image>
-						<text style='margin-top:16rpx'>中转已签收</text>
+						<text style='margin-top:16rpx' >中转已签收</text>
 					</view>
-					<view class="flexc" style="position: relative;">
+					<view class="flexc" style="position: relative;" @click="pack(2)">
 						<u-badge :absolute='true' :offset="[0,10]" :type="type" max="99" :value="value"></u-badge>
 						<image src="../../static/express/send.png" style='width:68rpx;height:68rpx' mode=""></image>
-						<text style='margin-top:16rpx'>正发往海外</text>
+						<text style='margin-top:16rpx' >正发往海外</text>
 					</view>
 				</view>
 			</view>
@@ -140,7 +140,7 @@
 					<image style="width: 180rpx;height: 32rpx;" src="/static/express/label.png" mode=""></image>
 				</view>
 				<view class="u-flex">
-					<text class="more">更多</text>
+					<text class="more" @click="more">更多</text>
 					<image src="../../static/express/next2.png" style="width: 30rpx;height: 30rpx;" mode=""></image>
 				</view>
 			</view>
@@ -189,10 +189,16 @@
 				</view>
 				<view class="u-flex u-row-between" style="margin-top: 32rpx;">
 					<text class="timea">开始时间:2023-12-31/截止时间:2023-12-31</text>
-					<view class="pinb">去拼包</view>
+					<view class="pinb" @click="toaccount">去拼包</view>
 				</view>
 			</view>
 		</view>
+		<u-popup round='28' :show="logshow" @close="close">
+			<view class="pop">
+				<kj-flow @success='success' @close='logshow=false'></kj-flow>
+			</view>
+		</u-popup>
+		<!-- <u-button @click="show = true">打开</u-button> -->
 		<kj-tabbar :value1=0></kj-tabbar>
 	</view>
 </template>
@@ -207,6 +213,7 @@
 				type: "error",
 				value: 1, //徽标数量
 				percentage: 50, //进度条
+				logshow: false, //物流选择
 			}
 		},
 		onLoad(options) {
@@ -215,6 +222,56 @@
 			}
 		},
 		methods: {
+			count(){
+				uni.navigateTo({
+					url:'/pageB/account'
+				})
+			},
+			pack(index){
+				uni.navigateTo({
+					url:'/pageB/myPackage?index='+index
+				})
+			},
+			more(){
+				uni.navigateTo({
+					url:'/pageB/list/list'    
+				})
+			},
+			//拼包快递
+			toaccount(){
+				uni.navigateTo({
+					url:'/pageB/ParcelExpress'
+				})
+			},
+			//物流计算器
+			counter(){
+				uni.navigateTo({
+					url:'/pageA/counter'
+				})
+			},
+			//物品信息
+			goods(){
+				uni.navigateTo({
+					url:'/pageB/goods'
+				})
+			},
+			//寄件人信息
+			send(){
+				uni.navigateTo({
+					url:'/pageB/Senderinfor'
+				})
+			},
+			recipirnt(){
+				uni.navigateTo({
+					url:'/pageB/recipientinfo'
+				})
+			},
+			success(){
+				this.logshow = false
+			},
+			close() {
+				this.logshow = false
+			},
 			//分段器切换
 			sectionChange(index) {
 				this.current = index;
@@ -226,9 +283,9 @@
 					this.current = 0
 				}
 			},
-			toparcel(){
+			toparcel() {
 				uni.navigateTo({
-					url:'/pageB/parcel'
+					url: '/pageB/parcel'
 				})
 			}
 		}
@@ -236,6 +293,18 @@
 </script>
 
 <style lang="scss" scoped>
+	.pop {
+		font-family: PingFangSC, PingFang SC;
+		font-weight: 500;
+		font-size: 36rpx;
+		color: #333333;
+		line-height: 50rpx;
+		text-align: left;
+		font-style: normal;
+		padding:32rpx 28rpx 0 0 ;
+		box-sizing: border-box;
+	}
+
 	.mon {
 		font-family: HarmonyOS_Sans_Medium;
 		font-size: 20rpx;
@@ -374,7 +443,7 @@
 		}
 
 		.add {
-			width: 240rpx;
+			// width: 240rpx;
 			height: 48rpx;
 			background: rgba(248, 50, 36, 0.06);
 			border-radius: 24rpx;

+ 13 - 5
pages/index/index.vue

@@ -6,7 +6,7 @@
 			<!-- 自定义导航栏 -->
 			<image src="../../static/images/index/back.png"
 				style="width: 750rpx;height: 1200rpx;position: absolute;top: 0;z-index: -1;" mode=""></image>
-			<u-navbar bgColor="rgba(248, 50, 36, 0.2)" :placeholder='true' :safeAreaInsetTop="true"
+			<u-navbar bgColor="rgba(248, 50, 36, 0.2)" :placeholder='true'  :fixed='true'
 				style="position: absolute;z-index: 110;background-color: rgba(0,0,0,0);">
 				<view class="u-nav-slot" slot="left" style="display: flex;">
 					<image src="../../static/images/index/xiaoxi.png" mode="" style="width: 40rpx;height: 40rpx;">
@@ -30,16 +30,15 @@
 					</view>
 					<view class="right">
 						搜索
-					</view>
+					</view>     
 				</view>
 				<!-- tabs -->
 				<view class=""
 					style="display: flex;margin-top: 20rpx;align-items: center;justify-content: space-between;">
 					<u-tabs :list="list1" @click="click"></u-tabs>
 					<view class="border">
-
 					</view>
-					<view class="u-flex" style="margin-left: 24rpx;">
+					<view class="u-flex" style="margin-left: 24rpx;width: 100rpx;" @click="shoptype">
 						<image src="../../static/images/index/fenlei.png" style="width: 24rpx;height: 24rpx;;" mode="">
 						</image>
 						<text class="fenlei">分类</text>
@@ -209,6 +208,12 @@
 			}
 		},
 		methods: {
+			//商品分类
+			shoptype(){
+				uni.navigateTo({
+					url:'/pageA/shoptype'
+				})
+			},
 			//分段器切换
 			sectionChange(index) {
 				this.current = index;
@@ -231,6 +236,9 @@
 </script>
 
 <style lang="scss" scoped>
+	::v-deep .u-tabs{
+		width: 560rpx;
+	}
 	.taber {
 		border-radius: 4rpx;
 		padding: 4rpx 8rpx;
@@ -295,7 +303,7 @@
 		// height: 28rpx;
 		border-radius: 14rpx 14rpx 14rpx 0rpx;
 		border: 1rpx solid #FFFFFF;
-		padding: 6rpx 14rpx 6rpx 14rpx;
+		padding: 6rpx ;
 		text-align: center;
 		font-family: PingFangSC, PingFang SC;
 		font-weight: 400;

+ 179 - 5
pages/shopping/shopping.vue

@@ -1,8 +1,59 @@
 <template>
 	<!-- 十元购 -->
-	<view>
-
-		<kj-tabbar :value1 = 2></kj-tabbar>
+	<view style="position: relative;">
+		<view class="back" style="z-index: -1;">
+		</view>
+		<u-navbar bgColor="#FE2E2E" :safeAreaInsetTop="true" :placeholder='true'>
+			<view class="u-nav-slot" slot="left">
+				<image src="../../static/shopping/title.png" style="width: 390rpx;height: 48rpx;" mode=""></image>
+			</view>
+		</u-navbar>
+		<view class="input u-flex u-row-between" style="margin-top: 16rpx;">
+			<view class="u-flex">
+				<image src="../../static/shopping/search.png" style="width: 32rpx;height: 32rpx;" mode=""></image>
+				<input type="text" placeholder="请输入商品名称搜索" style="width: 500rpx;margin-left: 14rpx;" />
+			</view>
+			<view class="search">搜索</view>
+		</view>
+		<view class="content">
+			<view class="">
+				<u-tabs lineColor="#f56c6c" :activeStyle="{
+            color: 'rgba(248, 53, 39, 1)',
+            fontWeight: 'bold',
+            transform: 'scale(1.05)'
+        }" :list="list1" @click="click"></u-tabs>
+			</view>
+			<view class="u-flex u-row-between" style="margin-top: 24rpx;flex-wrap: wrap;" >
+				<view class="item" v-for="(item,idx) in 6" :key="idx" style="margin-bottom: 20rpx;">
+					<image src="/static/images/logo.png" style="width: 340rpx;height: 340rpx;" mode=""></image>
+					<view class="" style="padding: 20rpx;box-sizing: border-box;">
+						<text class="title">可心柔 V9系列婴儿保wadhbiuawd</text>
+						<view class="u-flex" style="margin-top: 12rpx;flex-wrap: wrap;column-gap: 12rpx;">
+							<view class="tabsa" v-for="(item,index) in 3">
+								坏单包赔
+							</view>
+						</view>
+						<view class="num">
+							已售5000+件
+						</view>
+						<view class="" style="margin-top: 16rpx;position: relative;">
+							<image src="/static/shopping/btn.png" style="width: 300rpx;height: 68rpx;position: absolute;top:0;left:0;" mode=""></image>
+							<view class="u-flex u-row-between"
+								style="padding:10rpx 20rpx 0 26rpx;width: 85%;position: relative;z-index: 1;">
+								<view class="" style="margin-top: 10rpx;">
+									<text class="money">¥</text>
+									<text class="money" style="font-size: 40rpx;">13.2</text>
+								</view>
+								<image src="../../static/shopping/shop.png" style="width: 82rpx;height: 38rpx;" mode="">
+								</image>
+							</view>
+						</view>
+					</view>
+				</view>
+			</view>
+		</view>
+		<!-- <view class="" style="height: 80rpx;"></view> -->
+		<kj-tabbar :value1=2></kj-tabbar>
 	</view>
 </template>
 
@@ -10,12 +61,135 @@
 	export default {
 		data() {
 			return {
-
+				list1: [{
+					name: '关注',
+				}, {
+					name: '推荐',
+				}, {
+					name: '电影'
+				}, {
+					name: '科技'
+				}, {
+					name: '音乐'
+				}, {
+					name: '美食'
+				}, {
+					name: '文化'
+				}, {
+					name: '财经'
+				}, {
+					name: '手工'
+				}]
 			};
+		},
+		methods: {
+			click(item) {
+				console.log('item', item);
+			}
 		}
 	}
 </script>
 
-<style lang="scss">
+<style lang="scss" scoped>
+	.content {
+		width: 750rpx;
+		// height: 1332rpx;
+		background: linear-gradient(180deg, #FDFCFB 0%, #F4F4F4 100%);
+		border-radius: 30rpx 30rpx 0rpx 0rpx;
+		margin-top: 32rpx;
+		padding: 32rpx;
+		box-sizing: border-box;
+
+		.item {
+			width: 340rpx;
+			height: 592rpx;
+			background: #FFFFFF;
+			border-radius: 16rpx;
+
+			.title {
+				font-family: PingFangSC, PingFang SC;
+				font-weight: 400;
+				font-size: 26rpx;
+				color: #222222;
+				line-height: 36rpx;
+				text-align: left;
+				font-style: normal;
+				display: block;
+				width: 100%;
+				overflow: hidden;
+				text-overflow: ellipsis;
+				white-space: nowrap;
+			}
+
+			.tabsa {
+				// width: 96rpx;
+				height: 32rpx;
+				border-radius: 4rpx;
+				border: 1rpx solid #ED0909;
+				font-family: PingFangSC, PingFang SC;
+				font-weight: 400;
+				font-size: 20rpx;
+				color: #ED0909;
+				// line-height: 32rpx;
+				text-align: center;
+				font-style: normal;
+				display: inline;
+				padding: 0 4rpx;
+				box-sizing: border-box;
+			}
+
+			.num {
+				font-family: SFPro, SFPro;
+				font-weight: 400;
+				font-size: 20rpx;
+				color: #555555;
+				line-height: 24rpx;
+				text-align: left;
+				font-style: normal;
+				margin-top: 16rpx;
+			}
+
+			.money {
+				font-family: HarmonyOS_Sans_Medium;
+				font-size: 20rpx;
+				color: #F83224;
+				line-height: 26rpx;
+				text-align: left;
+				font-style: normal;
+			}
+		}
+	}
+
+	.back {
+		position: absolute;
+		top: 0;
+		left: 0;
+		width: 750rpx;
+		height: 800rpx;
+		background: #FE2E2E;
+	}
 
+	.input {
+		width: 702rpx;
+		height: 68rpx;
+		background: #FFFFFF;
+		border-radius: 34rpx;
+		margin-left: 24rpx;
+		box-sizing: border-box;
+		padding: 0 8rpx 0 24rpx;
+
+		.search {
+			width: 100rpx;
+			height: 56rpx;
+			background: #F83224;
+			border-radius: 28rpx;
+			font-family: PingFangSC, PingFang SC;
+			font-weight: 400;
+			font-size: 26rpx;
+			color: #FFFFFF;
+			line-height: 56rpx;
+			text-align: center;
+			font-style: normal;
+		}
+	}
 </style>

BIN
static/shopping/btn.png


BIN
static/shopping/search.png


BIN
static/shopping/shop.png


BIN
static/shopping/title.png


+ 8 - 0
uni_modules/cc-beautyCate/changelog.md

@@ -0,0 +1,8 @@
+## 2.3(2024-05-04)
+组件优化
+## 2.2(2024-04-24)
+组件优化
+## 2.0.0(2023-08-04)
+修复组件不能置顶问题
+## 1.0.0(2023-07-30)
+组件初始化

+ 206 - 0
uni_modules/cc-beautyCate/components/cc-beautyCate/cc-beautyCate.vue

@@ -0,0 +1,206 @@
+<template>
+	<view>
+		<!-- 此组件因为版权问题,请关注前端组件开发公众号,私信后获得授权 -->
+
+	</view>
+</template>
+<script>
+	//高德SDK
+	export default {
+		
+		props: {
+		
+			categoryList: {
+				type: Array,
+				default () {
+					return []
+				}
+			}
+		
+		
+		},
+		data() {
+			return {
+				showCategoryIndex: 1,
+				headerPosition: 'fixed',
+        	};
+		},
+		
+		onPageScroll(e) {
+			//兼容iOS端下拉时顶部漂移
+			if (e.scrollTop >= 0) {
+				this.headerPosition = 'fixed';
+			} else {
+				this.headerPosition = 'absolute';
+			}
+		},
+		onLoad() {
+			this.$Request.get("/api/super_classify/apikey/maxd").then(res => {
+				if (res.code === 1) {
+					this.categoryList = res.general_classify;
+				}
+			});
+		},
+		methods: {
+			//分类切换显示
+			showCategory(index) {
+				this.showCategoryIndex = index;
+			},
+			toCategory(item) {
+				
+				this.$emit("itemClick",item);
+			}
+		}
+	};
+</script>
+
+<style lang="scss">
+	
+	// 	<view class="category-list">
+	// 		<!-- 左侧分类导航 -->
+	// 		<scroll-view scroll-y="true" class="left">
+	// 			<view class="row" v-for="(category, index) in categoryList" :key="category.cid" :class="[category.cid == showCategoryIndex ? 'on' : '']"
+	// 			 @click="showCategory(category.cid)">
+	// 				<view class="text">
+	// 					<view class="block"></view>
+	// 					{{ category.main_name }}
+	// 				</view>
+	// 			</view>
+	// 		</scroll-view>
+	// 		<!-- 右侧子导航 -->
+	// 		<scroll-view scroll-y="true" class="right">
+	// 			<view class="category" v-for="(category, index) in categoryList" :key="index" v-show="category.cid == showCategoryIndex">
+	// 				<view v-for="(box, i) in category.data" :key="i">
+	// 					<view style="text-align: center">
+	// 						<text style="color: gainsboro">—</text>
+	// 						<text style="color: #000000;margin-right: 8px;margin-left: 8px">{{ box.next_name }}</text>
+	// 						<text style="color: gainsboro">—</text>
+	// 					</view>
+	// 					<view class="list" v-if="box.info.length>0" >
+	// 						<view class="box" v-for="(item, i1) in box.info" :key="i1">
+	// 							<image  @click="toCategory(item)" :src="item.imgurl" lazy-load="https://taobao.xianmxkj.com/logo.png"></image>
+	// 							<view class="text">{{ item.son_name }}</view>
+	// 						</view>
+	// 					</view>
+	// 				</view>
+	// 			</view>
+	// 		</scroll-view>
+	// 	</view>
+	
+	
+	.category-list {
+		width: 100%;
+		background-color: #fff;
+		display: flex;
+		
+
+		.left,
+		.right {
+			// 修复不能置顶问题
+			top: 0px;
+
+			bottom: 0upx;
+		}
+
+		.left {
+			position: fixed;
+			width: 24%;
+			left: 0upx;
+			background-color: #f2f2f2;
+
+			.row {
+				width: 100%;
+				height: 90upx;
+				display: flex;
+				align-items: center;
+
+				.text {
+					width: 100%;
+					position: relative;
+					font-size: 28upx;
+					display: flex;
+					justify-content: center;
+					color: #3c3c3c;
+
+					.block {
+						position: absolute;
+						width: 0upx;
+						left: 0;
+					}
+				}
+
+				&.on {
+					height: 100upx;
+					background-color: #fff;
+
+					.text {
+						font-size: 30upx;
+						font-weight: 600;
+						color: #2d2d2d;
+
+						.block {
+							width: 10upx;
+							height: 80%;
+							top: 10%;
+							background-color: #f06c7a;
+						}
+					}
+				}
+			}
+		}
+
+		.right {
+			margin-left: 24%;
+			width: 76%;
+			left: 24%;
+
+			.category {
+				width: 94%;
+				padding: 20upx 3%;
+
+				.banner {
+					width: 100%;
+					height: 24.262vw;
+					border-radius: 10upx;
+					overflow: hidden;
+					box-shadow: 0upx 5upx 20upx rgba(0, 0, 0, 0.3);
+
+					image {
+						width: 100%;
+						height: 24.262vw;
+					}
+				}
+
+				.list {
+					margin-top: 40upx;
+					width: 100%;
+					display: flex;
+					flex-wrap: wrap;
+
+					.box {
+						width: calc(65.44vw / 3);
+						margin-bottom: 30upx;
+						display: flex;
+						justify-content: center;
+						align-items: center;
+						flex-wrap: wrap;
+
+						image {
+							width: 60%;
+							height: calc(71.44vw / 3 * 0.6);
+						}
+
+						.text {
+							margin-top: 5upx;
+							width: 100%;
+							display: flex;
+							justify-content: center;
+							font-size: 26upx;
+						}
+					}
+				}
+			}
+		}
+	}
+</style>
+

+ 86 - 0
uni_modules/cc-beautyCate/package.json

@@ -0,0 +1,86 @@
+{
+  "id": "cc-beautyCate",
+  "displayName": "自定义精美商品分类组件category 可用于电商应用分类页面",
+  "version": "2.3",
+  "description": "自定义精美商品分类组件category 可用于电商应用分类页面",
+  "keywords": [
+    "category",
+    "分类",
+    "电商",
+    "分类组件",
+    "列表"
+],
+  "repository": "",
+  "engines": {
+    "HBuilderX": "^3.6.0"
+  },
+  "dcloudext": {
+    "type": "component-vue",
+    "sale": {
+      "regular": {
+        "price": "0.00"
+      },
+      "sourcecode": {
+        "price": "0.00"
+      }
+    },
+    "contact": {
+      "qq": ""
+    },
+    "declaration": {
+      "ads": "无",
+      "data": "无",
+      "permissions": "无"
+    },
+    "npmurl": ""
+  },
+  "uni_modules": {
+    "dependencies": [],
+    "encrypt": [],
+    "platforms": {
+      "cloud": {
+        "tcb": "y",
+        "aliyun": "y",
+        "alipay": "n"
+      },
+      "client": {
+        "Vue": {
+          "vue2": "y",
+          "vue3": "y"
+        },
+        "App": {
+          "app-vue": "y",
+          "app-nvue": "y"
+        },
+        "H5-mobile": {
+          "Safari": "y",
+          "Android Browser": "y",
+          "微信浏览器(Android)": "y",
+          "QQ浏览器(Android)": "y"
+        },
+        "H5-pc": {
+          "Chrome": "y",
+          "IE": "y",
+          "Edge": "y",
+          "Firefox": "y",
+          "Safari": "y"
+        },
+        "小程序": {
+          "微信": "y",
+          "阿里": "y",
+          "百度": "y",
+          "字节跳动": "y",
+          "QQ": "y",
+          "钉钉": "y",
+          "快手": "y",
+          "飞书": "y",
+          "京东": "y"
+        },
+        "快应用": {
+          "华为": "y",
+          "联盟": "y"
+        }
+      }
+    }
+  }
+}

+ 1049 - 0
uni_modules/cc-beautyCate/readme.md

@@ -0,0 +1,1049 @@
+# cc-beautyCate 自定义精美分类组件 可用于电商商品分类页面
+
+#### 使用方法 
+```使用方法
+<!-- category-list:分类列表  @itemClick:条目点击 -->
+<cc-beautyCate :category-list="categoryList" @itemClick="toCategory"></cc-beautyCate>
+
+若出现不能置顶问题, 或修改组件位置
+请修改组件内: .left,
+		  .right {
+			// 修复不能置顶问题
+			top: 0px;
+
+			bottom: 0upx;
+		}
+```
+
+#### HTML代码实现部分
+```html
+<template>
+	<view>
+		<!-- category-list:分类列表  @itemClick:条目点击 -->
+		<cc-beautyCate :category-list="categoryList" @itemClick="toCategory"></cc-beautyCate>
+	</view>
+</template>
+<script>
+	//高德SDK
+	export default {
+		data() {
+			return {
+
+				categoryList: [{
+						cid: 1,
+						main_name: '女装',
+						data: [{
+								next_name: '裙装',
+								info: [{
+										son_name: '连衣裙',
+										imgurl: 'http://img.haodanku.com/89937f347f81f5c5539f9da9b35b7a62-600'
+									},
+									{
+										son_name: '雪纺裙',
+										imgurl: 'http://img.haodanku.com/3deb054da8cb2f4b1b5a07ab530e7e41-600'
+									},
+									{
+										son_name: '半身裙',
+										imgurl: 'http://img.haodanku.com/b68bc66ab1a81db336110b7c1196b5a9-600'
+									},
+									{
+										son_name: '印花裙',
+										imgurl: 'http://img.haodanku.com/3ce7249ba847286308c82bed97f7817d-600'
+									},
+									{
+										son_name: '吊带裙',
+										imgurl: 'http://img.haodanku.com/0716abc13652355b130dc3c83d39a7dc-600'
+									},
+									{
+										son_name: '纯色裙',
+										imgurl: 'http://img.haodanku.com/de464503dab5d20a5d6505573f1624bd-600'
+									}
+								]
+							},
+							{
+								next_name: '套装',
+								info: [{
+										son_name: '两件套',
+										imgurl: 'http://img.haodanku.com/dcabee1a81b9c631bbc903597fad52a2-600'
+									},
+									{
+										son_name: '夏季套装',
+										imgurl: 'http://img.haodanku.com/fcf45b47afad11fe7ac05c179de174c1-600'
+									},
+									{
+										son_name: '大码女装',
+										imgurl: 'http://img.haodanku.com/005f603379aa285718b3f7c99c1ca88a-600'
+									},
+									{
+										son_name: '妈妈装',
+										imgurl: 'http://img.haodanku.com/cf445d5d9ddad49a38c0e542be22b565-600'
+									},
+									{
+										son_name: '婚纱',
+										imgurl: 'http://img.haodanku.com/d40c79df78c0a0cfbbb05605891950db-600'
+									},
+									{
+										son_name: '小香风',
+										imgurl: 'http://img.haodanku.com/2907a1a4faf78674c4ff422ce9ca16eb-600'
+									},
+									{
+										son_name: '运动套装',
+										imgurl: 'http://img.haodanku.com/97340565f9420afafc7a37966095da75-600'
+									},
+									{
+										son_name: '雪纺套装',
+										imgurl: 'http://img.haodanku.com/233b900fde6dc193a0c4b8886d121002-600'
+									}
+								]
+							},
+							{
+								next_name: 'T恤',
+								info: [{
+										son_name: 'T恤',
+										imgurl: 'http://img.haodanku.com/397fc31d9f3abdef5177ab1ec82a254c-600'
+									},
+									{
+										son_name: '一字肩',
+										imgurl: 'http://img.haodanku.com/f4ca5e271d74fd5c29d051c7b1106f04-600'
+									},
+									{
+										son_name: '印花雪纺',
+										imgurl: 'http://img.haodanku.com/60e743e4a53b475f2b01a606b61ab217-600'
+									},
+									{
+										son_name: '吊带T恤',
+										imgurl: 'http://img.haodanku.com/7aad5119d9e8d49c74ab97e31944ba63-600'
+									},
+									{
+										son_name: '娃娃衫',
+										imgurl: 'http://img.haodanku.com/9a596fb1d0c7008cdfad30e5562b3b3f-600'
+									},
+									{
+										son_name: '情侣T恤',
+										imgurl: 'http://img.haodanku.com/9880baf8c939776a65a64c6ec6b37621-600'
+									},
+									{
+										son_name: '白衬衣',
+										imgurl: 'http://img.haodanku.com/1c60edccb596c5f3496d883be75869c5-600'
+									},
+									{
+										son_name: '短袖T恤',
+										imgurl: 'http://img.haodanku.com/81ee9efd599bcfb76668e0ab3d08df6d-600'
+									},
+									{
+										son_name: '纯色T恤',
+										imgurl: 'http://img.haodanku.com/44d365c3c459a4a04185f1fa947e7353-600'
+									},
+									{
+										son_name: '蕾丝拼接',
+										imgurl: 'http://img.haodanku.com/f77f58f4f0f67b4c3cd830954354462a-600'
+									},
+									{
+										son_name: '蕾丝衫',
+										imgurl: 'http://img.haodanku.com/fe7c5357cb05b2128bf95c014c402092-600'
+									},
+									{
+										son_name: '防晒衫',
+										imgurl: 'http://img.haodanku.com/99d63b5ef04f0599ef94a71b1a247fef-600'
+									},
+									{
+										son_name: '露肩上衣',
+										imgurl: 'http://img.haodanku.com/a39d41be029747367e3889ea195043b1-600'
+									},
+									{
+										son_name: '长袖T恤',
+										imgurl: 'http://img.haodanku.com/d0ae23ec9b6d46e16bd2e0e924b2bd83-600'
+									}
+								]
+							},
+							{
+								next_name: '内搭',
+								info: [{
+										son_name: '喇叭袖',
+										imgurl: 'http://img.haodanku.com/6d8739d3e2aba86cfe98c3d8c631cb18-600'
+									},
+									{
+										son_name: '开衫',
+										imgurl: 'http://img.haodanku.com/051f12a2106ca222a4a651ef556419b2-600'
+									},
+									{
+										son_name: '打底毛衣',
+										imgurl: 'http://img.haodanku.com/d93f276b06e1aa4d9f5a7c4718ca675d-600'
+									},
+									{
+										son_name: '毛衣',
+										imgurl: 'http://img.haodanku.com/5ae75e2e972aec9126ac7ef6e44bd279-600'
+									},
+									{
+										son_name: '针织衫',
+										imgurl: 'http://img.haodanku.com/d989f5485e411a23b53d903787e9e8ae-600'
+									},
+									{
+										son_name: '高领',
+										imgurl: 'http://img.haodanku.com/226f1370ec92bd4804621851e4a45cd4-600'
+									}
+								]
+							},
+							{
+								next_name: '外套',
+								info: [{
+										son_name: '卫衣',
+										imgurl: 'http://img.haodanku.com/2329f94b4030aa27e819e159d64969c4-600'
+									},
+									{
+										son_name: '夹克',
+										imgurl: 'http://img.haodanku.com/ab499244178c525025d8a3e1ff4ed36e-600'
+									},
+									{
+										son_name: '棉服',
+										imgurl: 'http://img.haodanku.com/6d3898d409060a49ebc6a80c150d15b2-600'
+									},
+									{
+										son_name: '毛呢',
+										imgurl: 'http://img.haodanku.com/5b397df30169b79af64c569606b7e0af-600'
+									},
+									{
+										son_name: '牛仔',
+										imgurl: 'http://img.haodanku.com/38a001153e2f30933f3cae16f2b2a171-600'
+									},
+									{
+										son_name: '皮衣',
+										imgurl: 'http://img.haodanku.com/78c7fcecea8eaf4b329002b3308b3545-600'
+									},
+									{
+										son_name: '短外套',
+										imgurl: 'http://img.haodanku.com/26a08a44cce036a5cf49a3322e4cbf37-600'
+									},
+									{
+										son_name: '羽绒',
+										imgurl: 'http://img.haodanku.com/8b319845442d1ca43866388b3cdc9008-600'
+									},
+									{
+										son_name: '西装',
+										imgurl: 'http://img.haodanku.com/8760d56b968fff5f4542cba45398f911-600'
+									},
+									{
+										son_name: '风衣',
+										imgurl: 'http://img.haodanku.com/fce2eacae26fdd2be1e56319570db29e-600'
+									},
+									{
+										son_name: '马甲',
+										imgurl: 'http://img.haodanku.com/72c6ef4b40de18dddb9ae5dd38a9a051-600'
+									}
+								]
+							},
+							{
+								next_name: '裤子',
+								info: [{
+										son_name: '休闲裤',
+										imgurl: 'http://img.haodanku.com/047d30183d119f6805767a2649e05047-600'
+									},
+									{
+										son_name: '哈伦裤',
+										imgurl: 'http://img.haodanku.com/a1711789779fbd5c921fe1672619879f-600'
+									},
+									{
+										son_name: '棉麻裤',
+										imgurl: 'http://img.haodanku.com/a3e99f528f0cf163b96b0f6090226364-600'
+									},
+									{
+										son_name: '牛仔裤',
+										imgurl: 'http://img.haodanku.com/d1c7bf2b3a3d52047bf2bba87527ce2c-600'
+									},
+									{
+										son_name: '短裤',
+										imgurl: 'http://img.haodanku.com/d370086ea3d2a9c4ba569cfb8daf0d8e-600'
+									},
+									{
+										son_name: '破洞牛仔裤',
+										imgurl: 'http://img.haodanku.com/82462d589beb34111e9fccf66c170c7d-600'
+									},
+									{
+										son_name: '裤子',
+										imgurl: 'http://img.haodanku.com/049ea9c85a7b917eb9f0844df3910d4b-600'
+									},
+									{
+										son_name: '阔腿裤',
+										imgurl: 'http://img.haodanku.com/d59ec97908e5f2a3af232cbb23c54f31-600'
+									}
+								]
+							}
+						]
+					},
+					{
+						cid: 2,
+						main_name: '男装',
+						data: [{
+								next_name: '内搭',
+								info: [{
+										son_name: '长袖T恤',
+										imgurl: 'http://img.haodanku.com/0cd092e3914e60ed74954bbba638d093-600'
+									},
+									{
+										son_name: 'polo衫',
+										imgurl: 'http://img.haodanku.com/0503477c4c5d9618b5be5dc3f7712dcf-600'
+									},
+									{
+										son_name: 'T恤',
+										imgurl: 'http://img.haodanku.com/439ba8ee07892f939714783e5f5fa49d-600'
+									},
+									{
+										son_name: '卫衣',
+										imgurl: 'http://img.haodanku.com/fa31cffcb202ac6523853af99810afb9-600'
+									},
+									{
+										son_name: '短袖T恤',
+										imgurl: 'http://img.haodanku.com/c12f373877a7a4b9e4856d552c91e3b3-600'
+									},
+									{
+										son_name: '衬衣',
+										imgurl: 'http://img.haodanku.com/9d792370897c7f393059514b17a0cb14-600'
+									}
+								]
+							},
+							{
+								next_name: '外套',
+								info: [{
+										son_name: '马甲',
+										imgurl: 'http://img.haodanku.com/325254ca7f6e1f03c438d818cb4c67cb-600'
+									},
+									{
+										son_name: '呢大衣',
+										imgurl: 'http://img.haodanku.com/f3c0fd52abf5945b5d253a64f8fce62d-600'
+									},
+									{
+										son_name: '夹克',
+										imgurl: 'http://img.haodanku.com/250eefdd7acef0934e9f0fafffa75c88-600'
+									},
+									{
+										son_name: '棉衣',
+										imgurl: 'http://img.haodanku.com/6b5cd4e0f1c6f346b552651209cf78a3-600'
+									},
+									{
+										son_name: '棒球服',
+										imgurl: 'http://img.haodanku.com/2c296a0d7e6600da4b5f572c1c1878e4-600'
+									},
+									{
+										son_name: '牛仔外套',
+										imgurl: 'http://img.haodanku.com/d55f8511d3916346c8a808dbe5b0868a-600'
+									},
+									{
+										son_name: '皮衣',
+										imgurl: 'http://img.haodanku.com/14f79fee301aa6bb5a69318d9d95a2ef-600'
+									},
+									{
+										son_name: '羽绒服',
+										imgurl: 'http://img.haodanku.com/51ee457f0c7b8c2ddb6f4e5e9dd9818e-600'
+									},
+									{
+										son_name: '西装',
+										imgurl: 'http://img.haodanku.com/09290b9079002c5d957b245bf8d5dc22-600'
+									},
+									{
+										son_name: '风衣',
+										imgurl: 'http://img.haodanku.com/16a6f06b799c96f30289e124ee084f9a-600'
+									}
+								]
+							},
+							{
+								next_name: '下装',
+								info: [{
+										son_name: '七分裤',
+										imgurl: 'http://img.haodanku.com/cc410e901bbb46c0ccb39de7d834186d-600'
+									},
+									{
+										son_name: '九分裤',
+										imgurl: 'http://img.haodanku.com/fae33cdacda6852a7628c206f9cc3691-600'
+									},
+									{
+										son_name: '休闲裤',
+										imgurl: 'http://img.haodanku.com/28261afc8638e81810396e804be7bb4e-600'
+									},
+									{
+										son_name: '哈伦裤',
+										imgurl: 'http://img.haodanku.com/87ee360c875520d8df6d1fc326fceebf-600'
+									},
+									{
+										son_name: '工装裤',
+										imgurl: 'http://img.haodanku.com/de243065fe1031a3b673911ca3f5d554-600'
+									},
+									{
+										son_name: '沙滩裤',
+										imgurl: 'http://img.haodanku.com/890d879df68a83fc884af2ac40590f9f-600'
+									},
+									{
+										son_name: '牛仔裤',
+										imgurl: 'http://img.haodanku.com/f7aed5f42224b34532e92e57d667fb35-600'
+									},
+									{
+										son_name: '短裤',
+										imgurl: 'http://img.haodanku.com/957ec585e9c7af6b740b28e03e94484d-600'
+									},
+									{
+										son_name: '西裤',
+										imgurl: 'http://img.haodanku.com/883c7c0972e09d96ee7ba2df0990de48-600'
+									},
+									{
+										son_name: '运动裤',
+										imgurl: 'http://img.haodanku.com/11f8a9fe9f1c81a720870c65c3d95347-600'
+									}
+								]
+							},
+							{
+								next_name: '针织衫',
+								info: [{
+										son_name: '套头',
+										imgurl: 'http://img.haodanku.com/fad26efeddcbb8b28b31365f0e3ebdcf-600'
+									},
+									{
+										son_name: '开衫',
+										imgurl: 'http://img.haodanku.com/9a1297fddeb4ec33d63e1a353975fef9-600'
+									},
+									{
+										son_name: '毛衣',
+										imgurl: 'http://img.haodanku.com/4f488cd57da11a19dc5dc0f6a1d48eff-600'
+									},
+									{
+										son_name: '羊毛衫',
+										imgurl: 'http://img.haodanku.com/469ad3c34a07e7296bd52a6e49e3e7cc-600'
+									},
+									{
+										son_name: '针织衫',
+										imgurl: 'http://img.haodanku.com/79967f9058dd6b94dbc43714b652b090-600'
+									},
+									{
+										son_name: '高领',
+										imgurl: 'http://img.haodanku.com/0edca60c499cd86fe5d9ad2afb85e9c7-600'
+									}
+								]
+							}
+						]
+					},
+					{
+						cid: 3,
+						main_name: '内衣',
+						data: [{
+								next_name: '内衣',
+								info: [{
+										son_name: '保暖背心',
+										imgurl: 'http://img.haodanku.com/e093297d8e26e30a3d5aac4a613ec4f3-600'
+									},
+									{
+										son_name: '内衣套装',
+										imgurl: 'http://img.haodanku.com/8f0f258453126d0eba37806e99d61f3c-600'
+									},
+									{
+										son_name: '内裤女',
+										imgurl: 'http://img.haodanku.com/8fb1969ae13d3854a019176068cb19fd-600'
+									},
+									{
+										son_name: '内裤男',
+										imgurl: 'http://img.haodanku.com/bd263e2ae6f0b6d55f58aa45e98f8d5a-600'
+									},
+									{
+										son_name: '打底裤',
+										imgurl: 'http://img.haodanku.com/48ecf13aa01d4df986d08f807f5bde8a-600'
+									},
+									{
+										son_name: '文胸',
+										imgurl: 'http://img.haodanku.com/5515534e4b57c437ff99823194af9efa-600'
+									},
+									{
+										son_name: '塑身衣',
+										imgurl: 'http://img.haodanku.com/d4e8eb149c618aa91d74e2ea98c54bab-600'
+									},
+									{
+										son_name: '秋裤',
+										imgurl: 'http://img.haodanku.com/df2ce30731a1f5328163f02315cbcdf6-600'
+									}
+								]
+							},
+							{
+								next_name: '睡衣',
+								info: [{
+										son_name: '保暖睡衣',
+										imgurl: 'http://img.haodanku.com/4d20bb96196142cd1789ffb9608e0e6c-600'
+									},
+									{
+										son_name: '卡通睡衣',
+										imgurl: 'http://img.haodanku.com/8972e51a91b6564fdad5efd05dac9f8a-600'
+									},
+									{
+										son_name: '夹棉睡衣',
+										imgurl: 'http://img.haodanku.com/b2c95bf4420cd9ae947a715da3713625-600'
+									},
+									{
+										son_name: '女士睡衣',
+										imgurl: 'http://img.haodanku.com/9e1cbba89c247ed6df0a0db7267bf964-600'
+									},
+									{
+										son_name: '情侣睡衣',
+										imgurl: 'http://img.haodanku.com/8b005a55b45644839498aa75f4ee3e56-600'
+									},
+									{
+										son_name: '珊瑚绒',
+										imgurl: 'http://img.haodanku.com/079c725c7efe42cd8a005ea47542d939-600'
+									},
+									{
+										son_name: '男士睡衣',
+										imgurl: 'http://img.haodanku.com/c34cc2e6422f7493edac577c4b403146-600'
+									},
+									{
+										son_name: '睡袍',
+										imgurl: 'http://img.haodanku.com/7e5f5f97ab66f4d6c7b9fcf972e30e55-600'
+									},
+									{
+										son_name: '睡裙',
+										imgurl: 'http://img.haodanku.com/9348b8b75ef49b82e6cca3ef9d05bcf8-600'
+									},
+									{
+										son_name: '短袖睡衣',
+										imgurl: 'http://img.haodanku.com/360d6c896363e0158be255278923a900-600'
+									},
+									{
+										son_name: '长袖睡衣',
+										imgurl: 'http://img.haodanku.com/3c2a90a42396671688c5aaed75ee2b2a-600'
+									}
+								]
+							},
+							{
+								next_name: '袜子',
+								info: [{
+										son_name: '女袜',
+										imgurl: 'http://img.haodanku.com/5721a24e445fcd88ae8afa4b31432a0f-600'
+									},
+									{
+										son_name: '棉袜',
+										imgurl: 'http://img.haodanku.com/b08b0ad37e100ddef347205270bbc260-600'
+									},
+									{
+										son_name: '男袜',
+										imgurl: 'http://img.haodanku.com/1866e702f375974facb979e2a87e95df-600'
+									},
+									{
+										son_name: '裤袜',
+										imgurl: 'http://img.haodanku.com/95a4cd9d01b7ca5c544afeb5a35969f9-600'
+									},
+									{
+										son_name: '长筒袜',
+										imgurl: 'http://img.haodanku.com/edf2befbb6c1abd0fa67fbee47cb0ae9-600'
+									}
+								]
+							}
+						]
+					},
+					{
+						cid: 4,
+						main_name: '美妆',
+						data: [{
+								next_name: '个人护理',
+								info: [{
+										son_name: '头发造型',
+										imgurl: 'http://img.haodanku.com/d07c4bf91101a3b6a0a8a340bf068ac4-600'
+									},
+									{
+										son_name: '护发素',
+										imgurl: 'http://img.haodanku.com/a9e63479e7edc40aedde33cca15f03f7-600'
+									},
+									{
+										son_name: '染发膏',
+										imgurl: 'http://img.haodanku.com/1b8e6a4cd2961a1d468557b4d766eca6-600'
+									},
+									{
+										son_name: '沐浴露',
+										imgurl: 'http://img.haodanku.com/14bfdb8fc38493aa985d310d390aad19-600'
+									},
+									{
+										son_name: '洗发水',
+										imgurl: 'http://img.haodanku.com/162652a65fc931cf7aba34436a5882cd-600'
+									},
+									{
+										son_name: '清洁剂',
+										imgurl: 'http://img.haodanku.com/cc34507e016b01cc6005d028b55aa1cc-600'
+									},
+									{
+										son_name: '刷子',
+										imgurl: 'http://img.haodanku.com/529884a59eb3a2ed7cad27cc215f042c-600'
+									},
+									{
+										son_name: '私处护理',
+										imgurl: 'http://img.haodanku.com/28ca7a798ebef550b71616c0158f5a3f-600'
+									},
+									{
+										son_name: '足浴',
+										imgurl: 'http://img.haodanku.com/14291c443185a267818e2d434d8a3342-600'
+									},
+									{
+										son_name: '足贴',
+										imgurl: 'http://img.haodanku.com/cb2b79782adeff70c42b6a77d9ee7fbc-600'
+									},
+									{
+										son_name: '香薰',
+										imgurl: 'http://img.haodanku.com/d4e749698878f0318254e2844a2ab4b1-600'
+									}
+								]
+							},
+							{
+								next_name: '美妆',
+								info: [{
+										son_name: 'BB霜',
+										imgurl: 'http://img.haodanku.com/489dc81a627234b735e508ea3c5a30d6-600'
+									},
+									{
+										son_name: '乳液',
+										imgurl: 'http://img.haodanku.com/2da72ef1545f4e6e729daca73469daa7-600'
+									},
+									{
+										son_name: '卸妆',
+										imgurl: 'http://img.haodanku.com/11f4c31e57040ca6578e395764685f9d-600'
+									},
+									{
+										son_name: '唇膏',
+										imgurl: 'http://img.haodanku.com/73dce1ba7e1e6c2f087a82e6e2daaf0f-600'
+									},
+									{
+										son_name: '彩妆品盘',
+										imgurl: 'http://img.haodanku.com/885defc8e1c9263ff87a2bc661118c63-600'
+									},
+									{
+										son_name: '洁面仪',
+										imgurl: 'http://img.haodanku.com/e7a50ccfa072697de187a8257a63938e-600'
+									},
+									{
+										son_name: '洗面奶',
+										imgurl: 'http://img.haodanku.com/0390ae3565930d395244524603d38605-600'
+									},
+									{
+										son_name: '爽肤水',
+										imgurl: 'http://img.haodanku.com/5dc1013d4ed73422d9963e03d24ca8f2-600'
+									},
+									{
+										son_name: '男士护理',
+										imgurl: 'http://img.haodanku.com/0821d27e39fffa2bc6cb2372800d33b3-600'
+									},
+									{
+										son_name: '眼线',
+										imgurl: 'http://img.haodanku.com/4216c4cdc29f8e9bce9722bf2329f4d4-600'
+									},
+									{
+										son_name: '眼霜',
+										imgurl: 'http://img.haodanku.com/9bcc8b7e61d0958a4cf24efa92d8d7d9-600'
+									},
+									{
+										son_name: '睫毛膏',
+										imgurl: 'http://img.haodanku.com/917ca505a2916d4308258c1df00aa6bc-600'
+									},
+									{
+										son_name: '粉底液',
+										imgurl: 'http://img.haodanku.com/b8c50f1f046846de3a22b68b681799d0-600'
+									},
+									{
+										son_name: '精华',
+										imgurl: 'http://img.haodanku.com/c236b1acfcf8aeaffef5b01ab73e5608-600'
+									},
+									{
+										son_name: '精油',
+										imgurl: 'http://img.haodanku.com/c57b417129f661124f5b7f863fc2a5ed-600'
+									},
+									{
+										son_name: '纤体',
+										imgurl: 'http://img.haodanku.com/900cbfe080fcea90a276c9b0367252ef-600'
+									},
+									{
+										son_name: '脱毛',
+										imgurl: 'http://img.haodanku.com/bd495c3062c688f6c1a00a6f02a57655-600'
+									},
+									{
+										son_name: '腮红',
+										imgurl: 'http://img.haodanku.com/1305d219f6095577b2d4930531a1b3ee-600'
+									},
+									{
+										son_name: '身体护理',
+										imgurl: 'http://img.haodanku.com/31610649476cc77a474959eb39f1b929-600'
+									},
+									{
+										son_name: '防晒',
+										imgurl: 'http://img.haodanku.com/1d23fffba3e6ed348369ecf8fdab6659-600'
+									},
+									{
+										son_name: '隔离霜',
+										imgurl: 'http://img.haodanku.com/76c233634b9d2eb0ba2353d1c834a070-600'
+									},
+									{
+										son_name: '面膜',
+										imgurl: 'http://img.haodanku.com/f9ddb9d89e9bfd8312056b02c4081bcd-600'
+									},
+									{
+										son_name: '面霜',
+										imgurl: 'http://img.haodanku.com/e57cb97f45eacb673b10d4179ce54079-600'
+									},
+									{
+										son_name: '香水',
+										imgurl: 'http://img.haodanku.com/04e330d7bfe0dca66883d8a52286552c-600'
+									}
+								]
+							},
+							{
+								next_name: '营养保健',
+								info: [{
+										son_name: 'B族维生素',
+										imgurl: 'http://img.haodanku.com/1f59496fee3c1610426c711c3d1d4ec4-600'
+									},
+									{
+										son_name: '大豆异黄酮',
+										imgurl: 'http://img.haodanku.com/12c9c22137b4bcba282fd3df6f0fa01e-600'
+									},
+									{
+										son_name: '左旋',
+										imgurl: 'http://img.haodanku.com/dd4e18688356aa319c81ea718f50a447-600'
+									},
+									{
+										son_name: '氨基葡萄糖',
+										imgurl: 'http://img.haodanku.com/81b23d4f172a49b845757548992c9c86-600'
+									},
+									{
+										son_name: '维生素',
+										imgurl: 'http://img.haodanku.com/affa0845d378957e98cc2362b2c8fe78-600'
+									},
+									{
+										son_name: '维生素C',
+										imgurl: 'http://img.haodanku.com/815ca9b2b1af15cefe5254c15656a8ab-600'
+									},
+									{
+										son_name: '胶原蛋白',
+										imgurl: 'http://img.haodanku.com/1e22d5f3d6e3066d103cbb81d56aa413-600'
+									},
+									{
+										son_name: '葡萄籽',
+										imgurl: 'http://img.haodanku.com/d2d1641b79898540be56943a26e9fbf1-600'
+									},
+									{
+										son_name: '螺旋藻',
+										imgurl: 'http://img.haodanku.com/5e0f7000c10daed73047db311e9140bf-600'
+									},
+									{
+										son_name: '褪黑素',
+										imgurl: 'http://img.haodanku.com/69210ef9179d6f60456d8c35d953ef83-600'
+									},
+									{
+										son_name: '软骨素',
+										imgurl: 'http://img.haodanku.com/8598d5b6d46da97fc2e2bacc0ab6701a-600'
+									},
+									{
+										son_name: '辅酶Q10',
+										imgurl: 'http://img.haodanku.com/6d3244a7bc47008fec9c5776db5a4431-600'
+									},
+									{
+										son_name: '酵素',
+										imgurl: 'http://img.haodanku.com/6607fec9d51f603dd680aa56c8d367b7-600'
+									},
+									{
+										son_name: 'B族维生素',
+										imgurl: 'http://img.haodanku.com/1f59496fee3c1610426c711c3d1d4ec4-600'
+									},
+									{
+										son_name: 'DHA',
+										imgurl: 'http://img.haodanku.com/a9146cfb07408e86776b0f8e720302c8-600'
+									},
+									{
+										son_name: '大豆异黄酮',
+										imgurl: 'http://img.haodanku.com/12c9c22137b4bcba282fd3df6f0fa01e-600'
+									},
+									{
+										son_name: '左旋',
+										imgurl: 'http://img.haodanku.com/dd4e18688356aa319c81ea718f50a447-600'
+									},
+									{
+										son_name: '月见草油',
+										imgurl: 'http://img.haodanku.com/b7b3febf2ac9a51fc9ae956da2fa3de6-600'
+									},
+									{
+										son_name: '氨基葡萄糖',
+										imgurl: 'http://img.haodanku.com/81b23d4f172a49b845757548992c9c86-600'
+									},
+									{
+										son_name: '玛咖',
+										imgurl: 'http://img.haodanku.com/6eed576e355b970a83afafaa7e18ab1d-600'
+									},
+									{
+										son_name: '益生菌',
+										imgurl: 'http://img.haodanku.com/b12d21f28f7ec2bd4fd91bebe5ca9e63-600'
+									},
+									{
+										son_name: '维生素',
+										imgurl: 'http://img.haodanku.com/affa0845d378957e98cc2362b2c8fe78-600'
+									},
+									{
+										son_name: '维生素C',
+										imgurl: 'http://img.haodanku.com/815ca9b2b1af15cefe5254c15656a8ab-600'
+									},
+									{
+										son_name: '胶原蛋白',
+										imgurl: 'http://img.haodanku.com/1e22d5f3d6e3066d103cbb81d56aa413-600'
+									},
+									{
+										son_name: '葡萄籽',
+										imgurl: 'http://img.haodanku.com/d2d1641b79898540be56943a26e9fbf1-600'
+									},
+									{
+										son_name: '螺旋藻',
+										imgurl: 'http://img.haodanku.com/5e0f7000c10daed73047db311e9140bf-600'
+									},
+									{
+										son_name: '褪黑素',
+										imgurl: 'http://img.haodanku.com/69210ef9179d6f60456d8c35d953ef83-600'
+									},
+									{
+										son_name: '软骨素',
+										imgurl: 'http://img.haodanku.com/8598d5b6d46da97fc2e2bacc0ab6701a-600'
+									},
+									{
+										son_name: '辅酶Q10',
+										imgurl: 'http://img.haodanku.com/6d3244a7bc47008fec9c5776db5a4431-600'
+									},
+									{
+										son_name: '酵素',
+										imgurl: 'http://img.haodanku.com/6607fec9d51f603dd680aa56c8d367b7-600'
+									},
+									{
+										son_name: '钙',
+										imgurl: 'http://img.haodanku.com/98d655571845e533cbcbab620db00f6b-600'
+									},
+									{
+										son_name: '鱼油',
+										imgurl: 'http://img.haodanku.com/5b041d1a105e86d3c5c7f2e3c4ad713f-600'
+									}
+								]
+							}
+						]
+					},
+					{
+						cid: 5,
+						main_name: '配饰',
+						data: [{
+								next_name: '帽子',
+								info: [{
+										son_name: '套头帽',
+										imgurl: 'http://img.haodanku.com/125f6e478053e17b2c0ba682bd0a5a39-600'
+									},
+									{
+										son_name: '毛线帽',
+										imgurl: 'http://img.haodanku.com/e1187ddf4721580e67efb50dac464dab-600'
+									},
+									{
+										son_name: '渔夫帽',
+										imgurl: 'http://img.haodanku.com/b3828f4435ede4fa3463e55dde914b40-600'
+									},
+									{
+										son_name: '爵士帽',
+										imgurl: 'http://img.haodanku.com/980c97dd05f40ed47ac16a79612dd167-600'
+									},
+									{
+										son_name: '盆帽',
+										imgurl: 'http://img.haodanku.com/676817946cacdbe83c377d9cc9860aa9-600'
+									},
+									{
+										son_name: '礼帽',
+										imgurl: 'http://img.haodanku.com/cc142d12454557ef22b4ceb3ce4fe6ec-600'
+									},
+									{
+										son_name: '贝雷帽',
+										imgurl: 'http://img.haodanku.com/6d5eebc640a964a4b55fb03046a2f8fb-600'
+									},
+									{
+										son_name: '针织帽',
+										imgurl: 'http://img.haodanku.com/0b376d73ab661bbab0c83d8aefc50aca-600'
+									},
+									{
+										son_name: '鸭舌帽',
+										imgurl: 'http://img.haodanku.com/1de98e064ef1e8dd92d4e95eda311105-600'
+									}
+								]
+							},
+							{
+								next_name: '配饰',
+								info: [{
+										son_name: '半指手套',
+										imgurl: 'http://img.haodanku.com/2439ed86402cb2d8eff39995ef1fc4e4-600'
+									},
+									{
+										son_name: '手套',
+										imgurl: 'http://img.haodanku.com/e5183b8d3236027d4c0cd2fe923f7d34-600'
+									},
+									{
+										son_name: '真皮腰带',
+										imgurl: 'http://img.haodanku.com/cd8ad3794ee3fbb1a8c8bce8f88f7ef1-600'
+									},
+									{
+										son_name: '腰带',
+										imgurl: 'http://img.haodanku.com/7586c7f14be6334d52ea6f862048ce3a-600'
+									},
+									{
+										son_name: '触屏手套',
+										imgurl: 'http://img.haodanku.com/a69d879b523d78d0203fe4879109486f-600'
+									},
+									{
+										son_name: '雨伞',
+										imgurl: 'http://img.haodanku.com/eb9cb749ec8237a84227614d356bfd0f-600'
+									}
+								]
+							},
+							{
+								next_name: '围巾',
+								info: [{
+										son_name: '披肩围巾',
+										imgurl: 'http://img.haodanku.com/e7a8efb7077e6e0e71f038582f7a6ba8-600'
+									},
+									{
+										son_name: '棉麻围巾',
+										imgurl: 'http://img.haodanku.com/ac5962ed20036929d9d533e467a85fd0-600'
+									},
+									{
+										son_name: '真丝围巾',
+										imgurl: 'http://img.haodanku.com/c371383e6e2f9b234f30d32c0a1295ef-600'
+									},
+									{
+										son_name: '羊毛围巾',
+										imgurl: 'http://img.haodanku.com/2959aea355069c58f07a5bd4312c0750-600'
+									},
+									{
+										son_name: '羊绒围巾',
+										imgurl: 'http://img.haodanku.com/9870a6e83a4e97ce9f8b8d2bf2fe057e-600'
+									}
+								]
+							}
+						]
+					},
+					{
+						cid: 6,
+						main_name: '鞋品',
+						data: [{
+								next_name: '男鞋',
+								info: [{
+										son_name: '乐福鞋',
+										imgurl: 'http://img.haodanku.com/9843bf86c735fca4495b93cbb9371b56-600'
+									},
+									{
+										son_name: '休闲鞋',
+										imgurl: 'http://img.haodanku.com/52ac4c99b933215aa4767dfbcf1ee459-600'
+									},
+									{
+										son_name: '凉鞋',
+										imgurl: 'http://img.haodanku.com/6f80f7d856fef73155d88ac0ec633d93-600'
+									},
+									{
+										son_name: '增高鞋',
+										imgurl: 'http://img.haodanku.com/5dfbd1e8047e1fa4bc9424fc44a81037-600'
+									},
+									{
+										son_name: '帆布鞋',
+										imgurl: 'http://img.haodanku.com/b1ec5218812aac9249071123cc9ff37d-600'
+									},
+									{
+										son_name: '板鞋',
+										imgurl: 'http://img.haodanku.com/c0ea8e1a9a0e9c486c2f3b571b495395-600'
+									},
+									{
+										son_name: '网布鞋',
+										imgurl: 'http://img.haodanku.com/acaf3517b2b374e568e512ea2c1c0334-600'
+									},
+									{
+										son_name: '豆豆鞋',
+										imgurl: 'http://img.haodanku.com/6f74d2cd0dc9de9ea1b746ab78ba6b66-600'
+									},
+									{
+										son_name: '运动鞋',
+										imgurl: 'http://img.haodanku.com/7b3bc2187dfeaf64dbb61ad6d3eb358f-600'
+									}
+								]
+							},
+							{
+								next_name: '女鞋',
+								info: [{
+										son_name: '中跟鞋',
+										imgurl: 'http://img.haodanku.com/5a52d7f0375aadd03558aab5a2489a81-600'
+									},
+									{
+										son_name: '乐福鞋',
+										imgurl: 'http://img.haodanku.com/8ebb992fc125df908039e1468b634c1f-600'
+									},
+									{
+										son_name: '低跟鞋',
+										imgurl: 'http://img.haodanku.com/19c7f3e00da194115690d71b5b0f6691-600'
+									},
+									{
+										son_name: '妈妈鞋',
+										imgurl: 'http://img.haodanku.com/6056eae45f0a96110f028f5f145a7c57-600'
+									},
+									{
+										son_name: '小白鞋',
+										imgurl: 'http://img.haodanku.com/c8bdc977c2f8b484f7066e82f9019417-600'
+									},
+									{
+										son_name: '帆布鞋',
+										imgurl: 'http://img.haodanku.com/70841a66d31d46050b053a7a559aad5c-600'
+									},
+									{
+										son_name: '平底凉鞋',
+										imgurl: 'http://img.haodanku.com/5c245c0d40166fad781505b47e952b81-600'
+									},
+									{
+										son_name: '平底鞋',
+										imgurl: 'http://img.haodanku.com/8773b3b2620fbbc9d33cee40cbeff145-600'
+									},
+									{
+										son_name: '松糕厚底',
+										imgurl: 'http://img.haodanku.com/f8eee922c6d05a17e9bee9dcda4811ac-600'
+									},
+									{
+										son_name: '猫跟鞋',
+										imgurl: 'http://img.haodanku.com/aac30a76edc144585e38c0a5a3560bcc-600'
+									},
+									{
+										son_name: '玛丽珍鞋',
+										imgurl: 'http://img.haodanku.com/b010a4fdd9bb85af6ca67c916c317929-600'
+									},
+									{
+										son_name: '豆豆鞋',
+										imgurl: 'http://img.haodanku.com/32f1673e6784389d2c9aa793c7b2abbe-600'
+									},
+									{
+										son_name: '运动鞋',
+										imgurl: 'http://img.haodanku.com/9449c2e63a06f058a48f788c82591d00-600'
+									},
+									{
+										son_name: '高跟鞋',
+										imgurl: 'http://img.haodanku.com/0ee8f1f3483c3faa69f5ef1f129cfac5-600'
+									}
+								]
+							}
+						]
+					},
+
+
+				]
+			};
+		},
+
+		onLoad() {
+
+		},
+		methods: {
+			// 分类条目点击
+			toCategory(item) {
+
+				uni.showModal({
+					title: '温馨提示',
+					content: '点击条目数据 = ' + JSON.stringify(item)
+				})
+			}
+		}
+	};
+</script>
+<style lang="scss">
+
+</style>
+
+
+
+```

+ 20 - 0
uni_modules/cc-defineCateList/changelog.md

@@ -0,0 +1,20 @@
+## 2.5(2024-05-04)
+组件优化
+## 2.4(2024-05-04)
+组件优化
+## 2.3(2024-04-29)
+组件说明优化
+## 2.2(2024-04-26)
+组件说明优化
+## 2.1(2024-04-26)
+组件授权方式更改
+## 2.0.3(2024-04-24)
+组件优化
+## 2.0.2(2024-01-07)
+组件优化
+## 2.0.1(2024-01-07)
+组件优化
+## 2.0.0(2023-12-10)
+更新优化组件, 修复小程序及滚动bug
+## 1.0.0(2023-06-21)
+组件初始化

+ 227 - 0
uni_modules/cc-defineCateList/components/cc-defineCateList/cc-defineCateList.vue

@@ -0,0 +1,227 @@
+<template>
+
+	<view>
+
+	<!-- 此组件因为版权问题,请关注"前端组件开发"公众号,私信后获得授权 -->
+	</view>
+
+</template>
+
+<script>
+	export default {
+		props: {
+
+			data: {
+				type: Array,
+				default () {
+					return []
+				}
+			}
+
+
+		},
+		data() {
+			return {
+				currentTab: 0,
+				mainCur: 0,
+				verticalNavTop: 0,
+				list: [{}],
+
+			}
+		},
+		mounted() {
+			this.getHeight();
+		},
+		methods: {
+			itemClick(item) {
+
+				this.$emit('itemClick', item)
+
+			},
+			//点击左侧按钮
+			tapselect(index) {
+				this.currentTab = index;
+				this.mainCur = index;
+				this.verticalNavTop = (index - 1) * 50;
+			},
+			//右侧滚动
+			mainScroll(e) {
+				let scrollTop = e.detail.scrollTop + 10;
+				for (let i = 0; i < this.list.length; i++) {
+					if (scrollTop > this.list[i].top && scrollTop < this.list[i].bottom) {
+						// 此处屏蔽,避免点击最后一个回退, 但是没有右边滚动指示
+						if(this.currentTab != this.list.length - 1){
+							this.currentTab = i;
+							this.verticalNavTop = (i - 1) * 50
+							return false
+						}
+						
+					}
+				}
+			},
+			//获取右侧每项的top和bottom,里面需获取对应dom元素的高度,所以只能在mounted生命周期里调用
+			getHeight() {
+				var top = 0;
+				var bottom = 0;
+				var temp = 0;
+				for (let i = 0; i < this.data.length; i++) {
+					let view = uni.createSelectorQuery().select("#cate-" + i)
+
+					// #ifdef  MP-WEIXIN
+					view = uni.createSelectorQuery().in(this).select("#cate-" + i);
+					//	#endif
+
+
+					view.fields({
+						size: true
+					}, data => {
+						top = temp;
+						bottom = temp + data.height;
+						this.list[i] = {
+							top: top,
+							bottom: bottom
+						};
+						temp = bottom;
+					}).exec();
+				}
+
+			}
+		}
+
+
+	}
+</script>
+
+<style>
+	.cateBox {
+		position: absolute;
+		top: 120rpx;
+		bottom: 0;
+		background: #fff;
+		width: 100%;
+		display: flex;
+	}
+
+	.cate-left {
+		width: 200rpx;
+		background: #f4f4f4;
+		height: 100%;
+	}
+
+	.cate-left-item {
+		background: #f4f4f4;
+		height: 50px;
+		display: flex;
+		align-items: center;
+	}
+
+	.cate-left-item.activeItem {
+		background: #fff;
+	}
+	/* 	<view class="cateBox">
+			<!-- 左侧列表 -->
+			<scroll-view class="cate-left" scroll-y="true" :scroll-top="verticalNavTop" scroll-with-animation>
+				<view class="cate-left-list" v-for="(item,index) in data" :key="index" @click="tapselect(index)">
+					<!-- activeItem为选中状态 -->
+					<view class="cate-left-item " :class="{activeItem:currentTab==index}">
+						<label></label><text>{{item.navtitle}}</text>
+					</view>
+				</view>
+				
+				<!-- 右侧列表 -->
+				<scroll-view class="cate-right" scroll-y="true" @scroll="mainScroll" :scroll-into-view="'cate-'+mainCur"
+					scroll-with-animation>
+					<view v-for="(item,index) in data" :key="index" :id="'cate-'+index">
+						<view class="cate-right-title">
+							<view class="line"></view>
+							<text>{{item.navtitle}}</text>
+						</view>
+						<view class="cate-right-list">
+							<view class="cate-right-item" v-for="(child,childindex) in item.shop" :key="childindex"
+								@click="itemClick(child)">
+								<image :src="child.shopimage" mode=""></image>
+								<text>{{child.shoptitle}}</text>
+							</view>
+						</view>
+					</view>
+					
+					<view style="height: 100px;"></view>
+				</scroll-view>
+			</scroll-view>
+
+			
+		</view>
+ */
+
+	.cate-left-item label {
+		width: 4rpx;
+		height: 15px;
+		float: left;
+		background: #f4f4f4;
+	}
+
+	.cate-left-item.activeItem label {
+		background: #0bbbef;
+	}
+
+	.cate-left-item text {
+		width: 196rpx;
+		font-size: 28rpx;
+		display: block;
+		text-align: center;
+	}
+
+	/* 右侧*/
+	.cate-right {
+		flex: 1;
+	}
+
+	.cate-right-title {
+		text-align: center;
+		font-size: 28rpx;
+		margin-top: 20rpx;
+		line-height: 80rpx;
+		position: relative;
+	}
+
+	.cate-right-title .line {
+		position: absolute;
+		height: 2rpx;
+		width: 300rpx;
+		background: orange;
+		top: 39rpx;
+		left: 50%;
+		margin-left: -150rpx;
+	}
+
+	.cate-right-title text {
+		background: #fff;
+		padding: 0 10px;
+		position: relative;
+		color: #999;
+	}
+
+	.cate-right-list {
+		height: auto;
+		overflow: hidden;
+	}
+
+	.cate-right-item {
+		width: 33.33%;
+		float: left;
+		margin-top: 20rpx;
+	}
+
+	.cate-right-item image {
+		width: 160rpx;
+		height: 160rpx;
+		display: block;
+		margin: 0 auto;
+	}
+
+	.cate-right-item text {
+		display: block;
+		text-align: center;
+		font-size: 24rpx;
+	}
+</style>

+ 86 - 0
uni_modules/cc-defineCateList/package.json

@@ -0,0 +1,86 @@
+{
+  "id": "cc-defineCateList",
+  "displayName": "自定义简单好用商品分类列表组件 可用于电商分类列表",
+  "version": "2.5",
+  "description": "自定义简单好用商品分类列表组件 可用于电商分类列表",
+  "keywords": [
+    "cate",
+    "cateList",
+    "分类",
+    "商品分类",
+    "分类列表"
+],
+  "repository": "",
+  "engines": {
+    "HBuilderX": "^3.8.0"
+  },
+  "dcloudext": {
+    "type": "component-vue",
+    "sale": {
+      "regular": {
+        "price": "0.00"
+      },
+      "sourcecode": {
+        "price": "0.00"
+      }
+    },
+    "contact": {
+      "qq": ""
+    },
+    "declaration": {
+      "ads": "无",
+      "data": "无",
+      "permissions": "无"
+    },
+    "npmurl": ""
+  },
+  "uni_modules": {
+    "dependencies": [],
+    "encrypt": [],
+    "platforms": {
+      "cloud": {
+        "tcb": "y",
+        "aliyun": "y",
+        "alipay": "n"
+      },
+      "client": {
+        "Vue": {
+          "vue2": "y",
+          "vue3": "y"
+        },
+        "App": {
+          "app-vue": "y",
+          "app-nvue": "y"
+        },
+        "H5-mobile": {
+          "Safari": "y",
+          "Android Browser": "y",
+          "微信浏览器(Android)": "y",
+          "QQ浏览器(Android)": "y"
+        },
+        "H5-pc": {
+          "Chrome": "y",
+          "IE": "y",
+          "Edge": "y",
+          "Firefox": "y",
+          "Safari": "y"
+        },
+        "小程序": {
+          "微信": "y",
+          "阿里": "y",
+          "百度": "y",
+          "字节跳动": "y",
+          "QQ": "y",
+          "钉钉": "y",
+          "快手": "y",
+          "飞书": "y",
+          "京东": "y"
+        },
+        "快应用": {
+          "华为": "y",
+          "联盟": "y"
+        }
+      }
+    }
+  }
+}

+ 227 - 0
uni_modules/cc-defineCateList/readme.md

@@ -0,0 +1,227 @@
+# cc-defineCateList
+# 
+### 关注我的技术微信公众号
+
+查看更多前端组件和框架信息,请关注我的技术微信公众号【前端组件开发】
+
+![图片](https://i.postimg.cc/RZ0sjnYP/front-End-Component.jpg)
+
+
+#### 使用方法
+
+```使用方法
+
+<!-- 此组件因为版权问题,请关注"前端组件开发"公众号,私信后获得授权 -->
+
+
+<!-- data:商品列表数组[{navtitle:标题 shop:[] 展示列表}] @itemClick:商品条目点击事件-->
+<cc-defineCateList :data="data" @itemClick="cateItemClick"></cc-defineCateList>
+
+
+```
+
+#### HTML代码实现部分
+```html
+
+<template>
+	<view class="content">
+
+		<!-- 自定义顶部搜索框 用于搜索跳转 skipUrl:跳转url为绝对路径 /pages开头 -->
+		<cc-headSearch skipUrl="/pages/index/search"></cc-headSearch>
+
+		<!-- data:商品列表数组[{navtitle:标题 shop:展示列表}] @click:商品条目点击事件-->
+		<cc-defineCateList :data="data" @itemClick="cateItemClick"></cc-defineCateList>
+
+
+
+	</view>
+</template>
+
+<script>
+	export default {
+
+
+		data() {
+			return {
+				// 列表数组 navtitle:标题 shop:展示列表
+				data: [{
+						navtitle: '精品推荐1',
+						shop: [{
+								shoptitle: "手机一",
+								shopimage: "https://cdn.pixabay.com/photo/2014/08/05/10/27/iphone-410311_1280.jpg",
+							},
+							{
+								shoptitle: "手机二",
+								shopimage: "https://cdn.pixabay.com/photo/2015/05/12/09/13/social-media-763731_1280.jpg",
+							},
+							{
+								shoptitle: "手机三",
+								shopimage: "https://cdn.pixabay.com/photo/2016/11/20/08/33/camera-1842202_1280.jpg",
+							},
+							{
+								shoptitle: "手机四",
+								shopimage: "../../static/image/p4.png",
+							}
+						]
+					},
+					{
+						navtitle: '手机数码2',
+						shop: [{
+								shoptitle: "手机一",
+								shopimage: "https://cdn.pixabay.com/photo/2014/08/05/10/27/iphone-410311_1280.jpg",
+							},
+							{
+								shoptitle: "手机二",
+								shopimage: "https://cdn.pixabay.com/photo/2015/05/12/09/13/social-media-763731_1280.jpg",
+							},
+							{
+								shoptitle: "手机三",
+								shopimage: "https://cdn.pixabay.com/photo/2016/11/20/08/33/camera-1842202_1280.jpg",
+							},
+							{
+								shoptitle: "手机四",
+								shopimage: "../../static/image/p4.png",
+							}
+						]
+					},
+					{
+						navtitle: '声学设备3',
+						shop: [{
+								shoptitle: "手机一",
+								shopimage: "https://cdn.pixabay.com/photo/2014/08/05/10/27/iphone-410311_1280.jpg",
+							},
+							{
+								shoptitle: "手机二",
+								shopimage: "https://cdn.pixabay.com/photo/2015/05/12/09/13/social-media-763731_1280.jpg",
+							},
+							{
+								shoptitle: "手机三",
+								shopimage: "https://cdn.pixabay.com/photo/2016/11/20/08/33/camera-1842202_1280.jpg",
+							},
+							{
+								shoptitle: "手机四",
+								shopimage: "../../static/image/p4.png",
+							}
+						]
+					},
+					{
+						navtitle: '精品推荐4',
+						shop: [{
+								shoptitle: "手机一",
+								shopimage: "https://cdn.pixabay.com/photo/2014/08/05/10/27/iphone-410311_1280.jpg",
+							},
+							{
+								shoptitle: "手机二",
+								shopimage: "https://cdn.pixabay.com/photo/2015/05/12/09/13/social-media-763731_1280.jpg",
+							},
+							{
+								shoptitle: "手机三",
+								shopimage: "https://cdn.pixabay.com/photo/2016/11/20/08/33/camera-1842202_1280.jpg",
+							},
+							{
+								shoptitle: "手机四",
+								shopimage: "../../static/image/p4.png",
+							}
+						]
+					},
+					{
+						navtitle: '手机数码5',
+						shop: [{
+								shoptitle: "手机一",
+								shopimage: "https://cdn.pixabay.com/photo/2014/08/05/10/27/iphone-410311_1280.jpg",
+							},
+							{
+								shoptitle: "手机二",
+								shopimage: "https://cdn.pixabay.com/photo/2015/05/12/09/13/social-media-763731_1280.jpg",
+							},
+							{
+								shoptitle: "手机三",
+								shopimage: "https://cdn.pixabay.com/photo/2016/11/20/08/33/camera-1842202_1280.jpg",
+							},
+							{
+								shoptitle: "手机四",
+								shopimage: "../../static/image/p4.png",
+							}
+						]
+					},
+					{
+						navtitle: '手机数码6',
+						shop: [{
+								shoptitle: "手机一",
+								shopimage: "https://cdn.pixabay.com/photo/2014/08/05/10/27/iphone-410311_1280.jpg",
+							},
+							{
+								shoptitle: "手机二",
+								shopimage: "https://cdn.pixabay.com/photo/2015/05/12/09/13/social-media-763731_1280.jpg",
+							},
+							{
+								shoptitle: "手机三",
+								shopimage: "https://cdn.pixabay.com/photo/2016/11/20/08/33/camera-1842202_1280.jpg",
+							},
+							{
+								shoptitle: "手机四",
+								shopimage: "../../static/image/p4.png",
+							}
+						]
+					},
+					{
+						navtitle: '手机数码7',
+						shop: [{
+								shoptitle: "手机一",
+								shopimage: "https://cdn.pixabay.com/photo/2014/08/05/10/27/iphone-410311_1280.jpg",
+							},
+							{
+								shoptitle: "手机二",
+								shopimage: "https://cdn.pixabay.com/photo/2015/05/12/09/13/social-media-763731_1280.jpg",
+							},
+							{
+								shoptitle: "手机三",
+								shopimage: "https://cdn.pixabay.com/photo/2016/11/20/08/33/camera-1842202_1280.jpg",
+							},
+							{
+								shoptitle: "手机四",
+								shopimage: "../../static/image/p4.png",
+							}
+						]
+					},
+
+				]
+
+
+			}
+		},
+		mounted() {
+
+		},
+		methods: {
+
+			cateItemClick(item) {
+
+				uni.showModal({
+					title: '点击条目',
+					content: '点击条目数据 = ' + JSON.stringify(item)
+				})
+			}
+
+		}
+	}
+</script>
+
+<style>
+	page {
+
+		background-color: #f7f7f7;
+	}
+
+	.content {
+		display: flex;
+		flex-direction: column;
+
+	}
+</style>
+
+
+
+
+
+```

+ 16 - 0
uni_modules/liu-waterfall/changelog.md

@@ -0,0 +1,16 @@
+## 1.0.7(2023-06-08)
+增加预览二维码
+## 1.0.6(2023-05-31)
+增加license
+## 1.0.5(2023-05-20)
+优化
+## 1.0.4(2023-05-16)
+增加示例
+## 1.0.3(2023-05-15)
+超级好用版本发布
+## 1.0.2(2023-04-14)
+增加示例
+## 1.0.1(2023-04-04)
+更新文档
+## 1.0.0(2023-04-04)
+初版发布

+ 272 - 0
uni_modules/liu-waterfall/components/liu-waterfall/liu-waterfall.vue

@@ -0,0 +1,272 @@
+<template>
+	<view class="waterfall" :style="'background-color:'+bgColor" style="margin-top: 28rpx;">
+		<view v-for="(item, index) in columnData" :key="index" class="column" :style="{ width: columnWidth + 'px' }">
+			<view v-for="(childItem, childIndex) in item" :key="childIndex" style="width: 100%"
+				:id="'item' + childItem.id" @click.stop="click(childItem)">
+				<view class="item"
+					:style="'background-color:'+cardBgColor+';margin:'+margin+'rpx;border-radius:'+radius+'rpx;'">
+					<image :src="childItem.picUrl" mode="widthFix" lazy-load
+						:style="{height:childItem.height,width: '100%'}">
+					</image>
+					<view class="title-info">
+						<view class="item-title">{{ childItem.title }}</view>
+						<!-- <view class="item-desc">{{ childItem.desc }}</view> -->
+						<view class="">
+							<text class="money">¥5999.00</text>
+							<text style="margin-left: 14rpx;" class="kg">¥500g</text>
+						</view>
+						<view class="tabs">
+							<view class="tab">自营</view>
+							<view class="tab">次日达</view>
+							<view class="tab">包邮</view>
+						</view>
+						<view class="">
+							<text class="name">伯纳天纯 自营旗舰店</text>
+							<text class="shop">进店</text>
+						</view>
+					</view>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		props: {
+			//数据源
+			dataList: {
+				type: Array,
+				required: true,
+				default: []
+			},
+			//显示列数
+			column: {
+				type: Number,
+				required: true,
+				default: 2
+			},
+			//卡片margin(rpx)
+			margin: {
+				type: Number,
+				default: 10
+			},
+			//卡片圆角(rpx)
+			radius: {
+				type: Number,
+				default: 8
+			},
+			//页面背景颜色
+			bgColor: {
+				type: String,
+				// default: '#edeef2'
+			},
+			//卡片背景颜色
+			cardBgColor: {
+				type: String,
+				default: '#FFFFFF'
+			},
+		},
+		data() {
+			return {
+				columnData: [],
+				columnWidth: 0,
+				loading: false,
+			};
+		},
+		watch: {
+			dataList: {
+				immediate: true,
+				deep: true,
+				handler(newValue, oldValue) {
+					this.$nextTick(() => {
+						this.setColumnWidth()
+						this.setData()
+					})
+				},
+			},
+			column: {
+				immediate: false,
+				deep: true,
+				handler(newValue) {
+					this.$nextTick(() => {
+						this.setColumnWidth()
+						this.setData()
+					})
+				},
+			},
+		},
+		methods: {
+			//计算每列的高度
+			getElemHeight(index) {
+				this.$nextTick(() => {
+					var arr = [];
+					this.dataList.map((item, index) => {
+						uni.getImageInfo({
+							src: item.picUrl,
+							success: (e) => {
+								item.height = (e.height * (this.columnWidth / e.width)) + 'px'
+								this.createSelectorQuery().select('#item' + item.id)
+									.boundingClientRect(res => {
+										arr.push({
+											...{
+												itemHeight: res.height
+											},
+											...item
+										});
+										if (arr.length == this.dataList.length) {
+											this.columnData = this.distributeToNArrays(arr,
+												this.column);
+										}
+									}).exec();
+							}
+						})
+					})
+				})
+			},
+			distributeToNArrays(arr, n) {
+				let sums = new Array(n).fill(0);
+				return arr.reduce(
+					(arrays, item) => {
+						let minSum = Math.min(...sums);
+						let minIndex = sums.indexOf(minSum);
+						arrays[minIndex].push(item);
+						sums[minIndex] += item.itemHeight;
+						return arrays;
+					},
+					new Array(n).fill().map(() => []),
+				)
+			},
+			setColumnWidth() {
+				let width = uni.getSystemInfoSync().windowWidth
+				this.columnWidth = Math.floor(width / this.column)
+			},
+			setData() {
+				const resultArray = this.dataList.reduce(
+					(acc, cur, index) => {
+						const targetIndex = index % this.column;
+						acc[targetIndex].push(cur);
+						return acc;
+					},
+					Array.from(Array(this.column), () => []),
+				);
+				this.columnData = resultArray
+				this.getElemHeight()
+			},
+			click(item) {
+				this.$emit('click', item)
+			}
+		},
+	};
+</script>
+
+<style scoped lang="scss" scoped>
+	.money {
+		font-family: HarmonyOS_Sans_Medium;
+		font-size: 20rpx;
+		color: #FF1515;
+		line-height: 26rpx;
+		text-align: left;
+		font-style: normal;
+	}
+
+	.name {
+		font-family: PingFangSC, PingFang SC;
+		font-weight: 400;
+		font-size: 20rpx;
+		color: #222222;
+		line-height: 28rpx;
+		text-align: left;
+		font-style: normal;
+	}
+
+	.shop {
+		font-family: PingFangSC, PingFang SC;
+		font-weight: 400;
+		font-size: 20rpx;
+		color: #222222;
+		line-height: 28rpx;
+		text-align: left;
+		font-style: normal;
+		margin-left: 20rpx;
+	}
+
+	.kg {
+		font-family: SFPro, SFPro;
+		font-weight: 400;
+		font-size: 20rpx;
+		color: #555555;
+		line-height: 24rpx;
+		text-align: left;
+		font-style: normal;
+	}
+
+	.tabs {
+		margin-top: 20rpx;
+		display: flex;
+		column-gap: 12rpx;
+		flex-wrap: wrap;
+
+		.tab {
+			/* width: 56rpx; */
+			height: 32rpx;
+			border-radius: 4rpx;
+			border: 1rpx solid #E5BC78;
+			padding: 4rpx 8rpx;
+			// box-sizing: border-box;
+			line-height: 32rpx;
+			font-family: PingFangSC, PingFang SC;
+			font-weight: 400;
+			font-size: 20rpx;
+			color: #D48700;
+			line-height: 28rpx;
+			text-align: left;
+			font-style: normal;
+		}
+	}
+
+	.waterfall {
+		display: flex;
+		flex-direction: row;
+		/* flex-wrap: wrap; */
+	}
+
+	.column {
+		display: flex;
+		flex-direction: column;
+		align-items: center;
+	}
+
+	.item {
+		overflow: hidden;
+	}
+
+	.title-info {
+		padding: 0rpx 20rpx 20rpx 20rpx;
+	}
+
+	.item-title {
+		font-size: 30rpx;
+		color: #333333;
+		line-height: 46rpx;
+		text-align: justify;
+		overflow: hidden;
+		text-overflow: ellipsis;
+		display: -webkit-box;
+		-webkit-box-orient: vertical;
+		-webkit-line-clamp: 2;
+	}
+
+	.item-desc {
+		margin-top: 4rpx;
+		font-size: 26rpx;
+		color: #666666;
+		line-height: 34rpx;
+		text-align: justify;
+		overflow: hidden;
+		text-overflow: ellipsis;
+		display: -webkit-box;
+		-webkit-box-orient: vertical;
+		-webkit-line-clamp: 2;
+	}
+</style>

+ 6 - 0
uni_modules/liu-waterfall/license.md

@@ -0,0 +1,6 @@
+### 1、本插件可免费下载使用;
+### 2、未经许可,严禁复制本插件派生同类插件上传插件市场;
+### 3、未经许可,严禁在插件市场恶意复制抄袭本插件进行违规获利;
+### 4、对本软件的任何使用都必须遵守这些条款,违反这些条款的个人或组织将面临法律追究。
+
+

+ 85 - 0
uni_modules/liu-waterfall/package.json

@@ -0,0 +1,85 @@
+{
+	"id": "liu-waterfall",
+	"displayName": "瀑布流、自定义瀑布流、超级好用的瀑布流组件",
+	"version": "1.0.7",
+	"description": "(超级好用)瀑布流,根据内容自动计算进行流式布局,简单参数配置,支持多列布局(走过路过不要错过)",
+	"keywords": [
+        "瀑布流",
+        "自定义瀑布流",
+        "瀑布流式布局",
+        "瀑布流布局",
+        "瀑布"
+    ],
+	"repository": "",
+	"engines": {
+		"HBuilderX": "^3.1.0"
+	},
+	"dcloudext": {
+		"type": "component-vue",
+		"sale": {
+			"regular": {
+				"price": "0.00"
+			},
+			"sourcecode": {
+				"price": "0.00"
+			}
+		},
+		"contact": {
+			"qq": ""
+		},
+		"declaration": {
+			"ads": "无",
+			"data": "无",
+			"permissions": "无"
+		},
+		"npmurl": ""
+	},
+	"uni_modules": {
+		"dependencies": [],
+		"encrypt": [],
+		"platforms": {
+			"cloud": {
+				"tcb": "y",
+				"aliyun": "y"
+			},
+			"client": {
+				"Vue": {
+					"vue2": "y",
+					"vue3": "u"
+				},
+				"App": {
+					"app-vue": "u",
+					"app-nvue": "u"
+				},
+				"H5-mobile": {
+					"Safari": "y",
+					"Android Browser": "y",
+					"微信浏览器(Android)": "y",
+					"QQ浏览器(Android)": "y"
+				},
+				"H5-pc": {
+					"Chrome": "u",
+					"IE": "u",
+					"Edge": "u",
+					"Firefox": "u",
+					"Safari": "u"
+				},
+				"小程序": {
+					"微信": "y",
+					"阿里": "u",
+					"百度": "u",
+					"字节跳动": "u",
+					"QQ": "u",
+					"钉钉": "u",
+					"快手": "u",
+					"飞书": "u",
+					"京东": "u"
+				},
+				"快应用": {
+					"华为": "u",
+					"联盟": "u"
+				}
+			}
+		}
+	}
+}

+ 156 - 0
uni_modules/liu-waterfall/readme.md

@@ -0,0 +1,156 @@
+# liu-waterfall适用于uni-app项目的瀑布流组件
+### 本组件目前兼容微信小程序、H5
+### 本组件是超级好用的瀑布流,根据内容自动计算进行流式布局,简单参数配置,支持多列布局(走过路过不要错过)
+# --- 扫码预览、关注我们 ---
+
+## 扫码关注公众号,查看更多插件信息,预览插件效果! 
+
+![](https://uni.ckapi.pro/uniapp/publicize.png)
+
+### 使用示例
+``` 
+<template>
+	<view>
+		<view class="bar-list">
+			<view @click="columns=2" :style="columns==2?'background-color:red':''">2列</view>
+			<view @click="columns=3" :style="columns==3?'background-color:red':''">3列</view>
+			<view @click="columns=4" :style="columns==4?'background-color:red':''">4列</view>
+		</view>
+		<liu-waterfall :dataList="data" :column="columns" @click="click"></liu-waterfall>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				data: [{
+					picUrl: 'https://cdn.pixabay.com/photo/2020/05/19/13/32/cartoon-5190837_1280.jpg',
+					title: '思考',
+					desc: '我是第1个图片',
+					id: 1,
+				}, {
+					picUrl: 'https://cdn.pixabay.com/photo/2021/07/22/11/25/rabbit-6485072_1280.jpg',
+					title: '兔子',
+					desc: '我是第2个图片',
+					id: 2,
+				}, {
+					picUrl: 'https://cdn.pixabay.com/photo/2020/05/19/13/35/cartoon-5190860_1280.jpg',
+					title: '雨天',
+					desc: '我是第3个图片',
+					id: 3,
+				}, {
+					picUrl: 'https://cdn.pixabay.com/photo/2022/03/31/14/53/camp-7103189_1280.png',
+					title: '日落',
+					desc: '我是第4个图片',
+					id: 4,
+				}, {
+					picUrl: 'https://cdn.pixabay.com/photo/2022/11/29/19/05/boho-7625140_1280.jpg',
+					title: '植物',
+					desc: '我是第5个图片',
+					id: 5,
+				}, {
+					picUrl: 'https://cdn.pixabay.com/photo/2022/08/25/23/06/woman-7411414_1280.png',
+					title: '时尚',
+					desc: '我是第6个图片',
+					id: 6,
+				}, {
+					picUrl: 'https://cdn.pixabay.com/photo/2023/03/07/12/45/child-7835677_1280.jpg',
+					title: '生活',
+					desc: '我是第7个图片',
+					id: 7,
+				}, {
+					picUrl: 'https://cdn.pixabay.com/photo/2020/05/19/13/32/cartoon-5190837_1280.jpg',
+					title: '思考',
+					desc: '我是第8个图片',
+					id: 8,
+				}, {
+					picUrl: 'https://cdn.pixabay.com/photo/2021/07/22/11/25/rabbit-6485072_1280.jpg',
+					title: '兔子',
+					desc: '我是第9个图片',
+					id: 9,
+				}, {
+					picUrl: 'https://cdn.pixabay.com/photo/2020/05/19/13/35/cartoon-5190860_1280.jpg',
+					title: '雨天',
+					desc: '我是第10个图片',
+					id: 10,
+				}, {
+					picUrl: 'https://cdn.pixabay.com/photo/2022/11/29/19/05/boho-7625140_1280.jpg',
+					title: '植物',
+					desc: '我是第11个图片',
+					id: 11,
+				}, {
+					picUrl: 'https://cdn.pixabay.com/photo/2022/08/25/23/06/woman-7411414_1280.png',
+					title: '时尚',
+					desc: '我是第12个图片',
+					id: 12,
+				}, {
+					picUrl: 'https://cdn.pixabay.com/photo/2023/03/07/12/45/child-7835677_1280.jpg',
+					title: '生活',
+					desc: '我是第13个图片',
+					id: 13,
+				}, {
+					picUrl: 'https://cdn.pixabay.com/photo/2020/05/19/13/35/cartoon-5190860_1280.jpg',
+					title: '雨天',
+					desc: '我是第14个图片',
+					id: 14,
+				}, {
+					picUrl: 'https://cdn.pixabay.com/photo/2023/03/07/12/45/child-7835677_1280.jpg',
+					title: '生活',
+					desc: '我是第15个图片',
+					id: 15,
+				}, {
+					picUrl: 'https://cdn.pixabay.com/photo/2020/05/19/13/32/cartoon-5190837_1280.jpg',
+					title: '思考',
+					desc: '我是第16个图片',
+					id: 16,
+				}],
+				columns: 2,
+			};
+		},
+		methods: {
+			click(e) {
+				console.log('点击内容', e)
+			}
+		},
+	};
+</script>
+
+<style lang="scss" scoped>
+	page {
+		background-color: #edeef2;
+	}
+
+	.bar-list {
+		margin: 20rpx;
+		display: flex;
+		align-items: center;
+		justify-content: flex-start;
+
+		view {
+			width: 33%;
+			height: 70rpx;
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			font-size: 28rpx;
+			color: #FFFFFF;
+			background-color: #59a3f8;
+			border-radius: 8rpx;
+			margin-right: 20rpx;
+		}
+	}
+</style>
+```
+
+### 属性说明
+| 名称                         | 类型            | 默认值                 | 描述             |
+| ----------------------------|--------------- | ---------------------- | ---------------|
+| dataList                    | Array          | []                     | 数据源
+| column                      | Number         | 2                      | 显示列数
+| margin                      | Number         | 10                     | 卡片margin(rpx)
+| radius                      | Number         | 8                      | 卡片圆角(rpx)
+| bgColor                     | String         | #edeef2                | 页面背景颜色
+| cardBgColor                 | String         | #FFFFFF                | 卡片背景颜色
+| @click                      | Function       |                        | 点击卡片回调事件
+

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

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

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

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

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

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

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

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

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


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

@@ -0,0 +1,83 @@
+{
+  "id": "uv-icon",
+  "displayName": "uv-icon 图标 全面兼容vue3+2、app、h5、小程序等多端",
+  "version": "1.0.13",
+  "description": "基于字体的图标集,包含了大多数常见场景的图标,支持自定义,支持自定义图片图标等。可自定义颜色、大小。",
+  "keywords": [
+    "uv-ui,uvui,uv-icon,icon,图标,字体图标"
+],
+  "repository": "",
+  "engines": {
+    "HBuilderX": "^3.1.0"
+  },
+  "dcloudext": {
+    "type": "component-vue",
+    "sale": {
+      "regular": {
+        "price": "0.00"
+      },
+      "sourcecode": {
+        "price": "0.00"
+      }
+    },
+    "contact": {
+      "qq": ""
+    },
+    "declaration": {
+      "ads": "无",
+      "data": "插件不采集任何数据",
+      "permissions": "无"
+    },
+    "npmurl": ""
+  },
+  "uni_modules": {
+    "dependencies": [
+			"uv-ui-tools"
+		],
+    "encrypt": [],
+    "platforms": {
+      "cloud": {
+        "tcb": "y",
+        "aliyun": "y"
+      },
+      "client": {
+        "Vue": {
+          "vue2": "y",
+          "vue3": "y"
+        },
+        "App": {
+          "app-vue": "y",
+          "app-nvue": "y"
+        },
+        "H5-mobile": {
+          "Safari": "y",
+          "Android Browser": "y",
+          "微信浏览器(Android)": "y",
+          "QQ浏览器(Android)": "y"
+        },
+        "H5-pc": {
+          "Chrome": "y",
+          "IE": "y",
+          "Edge": "y",
+          "Firefox": "y",
+          "Safari": "y"
+        },
+        "小程序": {
+          "微信": "y",
+          "阿里": "y",
+          "百度": "y",
+          "字节跳动": "y",
+          "QQ": "y",
+          "钉钉": "u",
+          "快手": "u",
+          "飞书": "u",
+          "京东": "u"
+        },
+        "快应用": {
+          "华为": "u",
+          "联盟": "u"
+        }
+      }
+    }
+  }
+}

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

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

+ 36 - 0
uni_modules/uv-image/changelog.md

@@ -0,0 +1,36 @@
+## 1.0.16(2023-12-21)
+1. 修复设置show-menu-by-longpress不生效的BUG
+## 1.0.15(2023-12-06)
+1. 优化
+## 1.0.14(2023-12-06)
+1. 阻止事件冒泡问题
+## 1.0.13(2023-11-15)
+1. 修复webp之前未使用的BUG
+## 1.0.12(2023-10-11)
+1. 修复懒加载报错:https://gitee.com/climblee/uv-ui/issues/I869JS
+## 1.0.11(2023-08-31)
+1. 修复设置widthFix时出现显示不全的BUG
+2. 修复抖音等平台在width和height属性改变时出现不显示的BUG
+## 1.0.10(2023-08-29)
+1. 修复异步修改宽高不生效的问题,问题来源:https://gitee.com/climblee/uv-ui/issues/I7WUQ3
+## 1.0.9(2023-08-21)
+1. 修复设置宽高为百分比不生效的BUG
+## 1.0.8(2023-07-24)
+1. 优化 nvue模式下增加cellChild参数,是否在list中cell节点下,nvue中cell下建议设置成true
+## 1.0.7(2023-07-02)
+修复VUE3模式下可能不显示的BUG
+## 1.0.6(2023-07-02)
+优化修改
+## 1.0.5(2023-06-28)
+修复duration属性不生效的BUG
+## 1.0.4(2023-05-27)
+1. 修复可能报错的问题
+## 1.0.3(2023-05-24)
+1. 去掉template中存在的this.导致头条小程序编译警告
+## 1.0.2(2023-05-23)
+1. 优化
+## 1.0.1(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.0(2023-05-10)
+uv-image 图片

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

@@ -0,0 +1,95 @@
+export default {
+	props: {
+		// 图片地址
+		src: {
+			type: String,
+			default: ''
+		},
+		// 裁剪模式
+		mode: {
+			type: String,
+			default: 'aspectFill'
+		},
+		// 宽度,单位任意
+		width: {
+			type: [String, Number],
+			default: '300'
+		},
+		// 高度,单位任意
+		height: {
+			type: [String, Number],
+			default: '225'
+		},
+		// 图片形状,circle-圆形,square-方形
+		shape: {
+			type: String,
+			default: 'square'
+		},
+		// 圆角,单位任意
+		radius: {
+			type: [String, Number],
+			default: 0
+		},
+		// 是否懒加载,微信小程序、App、百度小程序、字节跳动小程序
+		lazyLoad: {
+			type: Boolean,
+			default: true
+		},
+		// 是否开启observer懒加载,nvue不生效
+		observeLazyLoad: {
+			type: Boolean,
+			default: false
+		},
+		// 开启长按图片显示识别微信小程序码菜单
+		showMenuByLongpress: {
+			type: Boolean,
+			default: true
+		},
+		// 加载中的图标,或者小图片
+		loadingIcon: {
+			type: String,
+			default: 'photo'
+		},
+		// 加载失败的图标,或者小图片
+		errorIcon: {
+			type: String,
+			default: 'error-circle'
+		},
+		// 是否显示加载中的图标或者自定义的slot
+		showLoading: {
+			type: Boolean,
+			default: true
+		},
+		// 是否显示加载错误的图标或者自定义的slot
+		showError: {
+			type: Boolean,
+			default: true
+		},
+		// 是否需要淡入效果
+		fade: {
+			type: Boolean,
+			default: true
+		},
+		// 只支持网络资源,只对微信小程序有效
+		webp: {
+			type: Boolean,
+			default: false
+		},
+		// 过渡时间,单位ms
+		duration: {
+			type: [String, Number],
+			default: 500
+		},
+		// 背景颜色,用于深色页面加载图片时,为了和背景色融合
+		bgColor: {
+			type: String,
+			default: '#f3f4f6'
+		},
+		// nvue模式下 是否直接显示,在uv-list等cell下面使用就需要设置
+		cellChild: {
+			type: Boolean,
+			default: false
+		},
+		...uni.$uv?.props?.image
+	}
+}

+ 287 - 0
uni_modules/uv-image/components/uv-image/uv-image.vue

@@ -0,0 +1,287 @@
+<template>
+	<uv-transition
+		v-if="show"
+		:show="show"
+		mode="fade"
+		:duration="fade ? duration : 0"
+		:cell-child="cellChild"
+		:custom-style="wrapStyle"
+	>
+		<view
+			class="uv-image"
+			:class="[`uv-image--${elIndex}`]"
+			@tap="onClick"
+			:style="[wrapStyle, backgroundStyle]"
+		>
+			<image
+				v-if="!isError && observeShow"
+				:src="src"
+				:mode="mode"
+				@error="onErrorHandler"
+				@load="onLoadHandler"
+				:show-menu-by-longpress="showMenuByLongpress"
+				:lazy-load="lazyLoad"
+				class="uv-image__image"
+				:style="[imageStyle]"
+				:webp="webp"
+			></image>
+			<view
+				v-if="showLoading && loading"
+				class="uv-image__loading"
+				:style="{
+					borderRadius: shape == 'circle' ? '50%' : $uv.addUnit(radius),
+					backgroundColor: bgColor,
+					width: $uv.addUnit(width),
+					height: $uv.addUnit(height)
+				}"
+			>
+				<slot name="loading">
+					<uv-icon
+						:name="loadingIcon"
+						:width="width"
+						:height="height"
+					></uv-icon>
+				</slot>
+			</view>
+			<view
+				v-if="showError && isError && !loading"
+				class="uv-image__error"
+				:style="{
+					borderRadius: shape == 'circle' ? '50%' : $uv.addUnit(radius),
+					width: $uv.addUnit(width),
+					height: $uv.addUnit(height)
+				}"
+			>
+				<slot name="error">
+					<uv-icon
+						:name="errorIcon"
+						:width="width"
+						:height="height"
+					></uv-icon>
+				</slot>
+			</view>
+		</view>
+	</uv-transition>
+</template>
+
+<script>
+	import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+	import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+	import props from './props.js';
+	/**
+	 * Image 图片
+	 * @description 此组件为uni-app的image组件的加强版,在继承了原有功能外,还支持淡入动画、加载中、加载失败提示、圆角值和形状等。
+	 * @tutorial https://www.uvui.cn/components/image.html
+	 * @property {String}			src 				图片地址
+	 * @property {String}			mode 				裁剪模式,见官网说明 (默认 'aspectFill' )
+	 * @property {String | Number}	width 				宽度,单位任意,如果为数值,则为px单位 (默认 '300' )
+	 * @property {String | Number}	height 				高度,单位任意,如果为数值,则为px单位 (默认 '225' )
+	 * @property {String}			shape 				图片形状,circle-圆形,square-方形 (默认 'square' )
+	 * @property {String | Number}	radius		 		圆角值,单位任意,如果为数值,则为px单位 (默认 0 )
+	 * @property {Boolean}			lazyLoad			是否懒加载,仅微信小程序、App、百度小程序、字节跳动小程序有效 (默认 true )
+	 * @property {Boolean}			showMenuByLongpress	是否开启长按图片显示识别小程序码菜单,仅微信小程序有效 (默认 true )
+	 * @property {String}			loadingIcon 		加载中的图标,或者小图片 (默认 'photo' )
+	 * @property {String}			errorIcon 			加载失败的图标,或者小图片 (默认 'error-circle' )
+	 * @property {Boolean}			showLoading 		是否显示加载中的图标或者自定义的slot (默认 true )
+	 * @property {Boolean}			showError 			是否显示加载错误的图标或者自定义的slot (默认 true )
+	 * @property {Boolean}			fade 				是否需要淡入效果 (默认 true )
+	 * @property {Boolean}			webp 				只支持网络资源,只对微信小程序有效 (默认 false )
+	 * @property {String | Number}	duration 			搭配fade参数的过渡时间,单位ms (默认 500 )
+	 * @property {String}			bgColor 			背景颜色,用于深色页面加载图片时,为了和背景色融合  (默认 '#f3f4f6' )
+	 * @property {Object}			customStyle  		定义需要用到的外部样式
+	 * @event {Function}	click	点击图片时触发
+	 * @event {Function}	error	图片加载失败时触发
+	 * @event {Function} load 图片加载成功时触发
+	 * @example <uv-image width="100%" height="300px" :src="src"></uv-image>
+	 */
+	export default {
+		name: 'uv-image',
+		emits: ['click','load','error'],
+		mixins: [mpMixin, mixin, props],
+		data() {
+			return {
+				// 图片是否加载错误,如果是,则显示错误占位图
+				isError: false,
+				// 初始化组件时,默认为加载中状态
+				loading: true,
+				// 图片加载完成时,去掉背景颜色,因为如果是png图片,就会显示灰色的背景
+				backgroundStyle: {},
+				// 用于fade模式的控制组件显示与否
+				show: false,
+				// 是否开启图片出现在可视范围进行加载(另一种懒加载)
+				observeShow: !this.observeLazyLoad,
+				elIndex: '',
+				// 因为props的值无法修改,故需要一个中间值
+				imgWidth: this.width,
+				// 因为props的值无法修改,故需要一个中间值
+				imgHeight: this.height,
+				thresholdValue: 50
+			};
+		},
+		watch: {
+			src: {
+				immediate: true,
+				handler(n) {
+					if (!n) {
+						// 如果传入null或者'',或者false,或者undefined,标记为错误状态
+						this.isError = true
+					} else {
+						this.isError = false;
+						this.loading = true;
+					}
+				}
+			},
+			width(newVal){
+				// 这样做的目的是避免在更新时候,某些平台动画会恢复关闭状态
+				this.show = false;
+				this.$uv.sleep(2).then(res=>{
+					this.show = true;
+				});
+				this.imgWidth = newVal;
+			},
+			height(newVal){
+				// 这样做的目的是避免在更新时候,某些平台动画会恢复关闭状态
+				this.show = false;
+				this.$uv.sleep(2).then(res=>{
+					this.show = true;
+				});
+				this.imgHeight = newVal;
+			}
+		},
+		computed: {
+			wrapStyle() {
+				let style = {};
+				// 通过调用addUnit()方法,如果有单位,如百分比,px单位等,直接返回,如果是纯粹的数值,则加上rpx单位
+				if(this.mode !== 'heightFix') {
+					style.width = this.$uv.addUnit(this.imgWidth);
+				}
+				if(this.mode !== 'widthFix') {
+					style.height = this.$uv.addUnit(this.imgHeight);
+				}
+				// 如果是显示圆形,设置一个很多的半径值即可
+				style.borderRadius = this.shape == 'circle' ? '10000px' : this.$uv.addUnit(this.radius)
+				// 如果设置圆角,必须要有hidden,否则可能圆角无效
+				style.overflow = this.radius > 0 ? 'hidden' : 'visible'
+				return this.$uv.deepMerge(style, this.$uv.addStyle(this.customStyle));
+			},
+			imageStyle() {
+				let style = {};
+				style.borderRadius = this.shape == 'circle' ? '10000px' : this.$uv.addUnit(this.radius);
+				// #ifdef APP-NVUE
+				style.width = this.$uv.addUnit(this.imgWidth);
+				style.height = this.$uv.addUnit(this.imgHeight);
+				// #endif
+				return style;
+			}
+		},
+		created() {
+			this.elIndex = this.$uv.guid();
+			this.observer = {}
+			this.observerName = 'lazyLoadContentObserver'
+		},
+		mounted() {
+			this.show = true;
+			this.$nextTick(()=>{
+				if(this.observeLazyLoad) this.observerFn();
+			})
+		},
+		methods: {
+			// 点击图片
+			onClick() {
+				this.$emit('click')
+			},
+			// 图片加载失败
+			onErrorHandler(err) {
+				this.loading = false
+				this.isError = true
+				this.$emit('error', err)
+			},
+			// 图片加载完成,标记loading结束
+			onLoadHandler(event) {
+				if(this.mode == 'widthFix') this.imgHeight = 'auto'
+				if(this.mode == 'heightFix') this.imgWidth = 'auto'
+				this.loading = false
+				this.isError = false
+				this.$emit('load', event)
+				this.removeBgColor()
+			},
+			// 移除图片的背景色
+			removeBgColor() {
+				// 淡入动画过渡完成后,将背景设置为透明色,否则png图片会看到灰色的背景
+				this.backgroundStyle = {
+					backgroundColor: 'transparent'
+				};
+			},
+			// 观察图片是否在可见视口
+			observerFn(){
+				// 在需要用到懒加载的页面,在触发底部的时候触发tOnLazyLoadReachBottom事件,保证所有图片进行加载
+				this.$nextTick(() => {
+					uni.$once('onLazyLoadReachBottom', () => {
+						if (!this.observeShow) this.observeShow = true
+					})
+				})
+				setTimeout(() => {
+					// #ifndef APP-NVUE
+					this.disconnectObserver(this.observerName)
+					const contentObserver = uni.createIntersectionObserver(this)
+					contentObserver.relativeToViewport({
+						bottom: this.thresholdValue
+					}).observe(`.uv-image--${this.elIndex}`, (res) => {
+						if (res.intersectionRatio > 0) {
+							// 懒加载状态改变
+							this.observeShow = true
+							// 如果图片已经加载,去掉监听,减少性能消耗
+							this.disconnectObserver(this.observerName)
+						}
+					})
+					this[this.observerName] = contentObserver
+					// #endif
+					// #ifdef APP-NVUE
+					this.observeShow = true;
+					// #endif
+				}, 50)
+			},
+			disconnectObserver(observerName) {
+				const observer = this[observerName]
+				observer && observer.disconnect()
+			}
+		}
+	};
+</script>
+
+<style lang="scss" scoped>
+	@import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+	@import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
+	$uv-image-error-top:0px !default;
+	$uv-image-error-left:0px !default;
+	$uv-image-error-width:100% !default;
+	$uv-image-error-hight:100% !default;
+	$uv-image-error-background-color:$uv-bg-color !default;
+	$uv-image-error-color:$uv-tips-color !default;
+	$uv-image-error-font-size: 46rpx !default;
+
+	.uv-image {
+		position: relative;
+		transition: opacity 0.5s ease-in-out;
+
+		&__image {
+			width: 100%;
+			height: 100%;
+		}
+
+		&__loading,
+		&__error {
+			position: absolute;
+			top: $uv-image-error-top;
+			left: $uv-image-error-left;
+			width: $uv-image-error-width;
+			height: $uv-image-error-hight;
+			@include flex;
+			align-items: center;
+			justify-content: center;
+			background-color: $uv-image-error-background-color;
+			color: $uv-image-error-color;
+			font-size: $uv-image-error-font-size;
+		}
+	}
+</style>

+ 89 - 0
uni_modules/uv-image/package.json

@@ -0,0 +1,89 @@
+{
+  "id": "uv-image",
+  "displayName": "uv-image 图片 全面兼容vue3+2、app、h5、小程序等多端",
+  "version": "1.0.16",
+  "description": "uv-image 此组件为uni-app的image组件的加强版,在继承了原有功能外,增加observer懒加载功能,还支持淡入动画、加载中、加载失败提示、圆角值和形状等。",
+  "keywords": [
+    "uv-image",
+    "uvui",
+    "uv-ui",
+    "image",
+    "图片"
+],
+  "repository": "",
+  "engines": {
+    "HBuilderX": "^3.1.0"
+  },
+  "dcloudext": {
+    "type": "component-vue",
+    "sale": {
+      "regular": {
+        "price": "0.00"
+      },
+      "sourcecode": {
+        "price": "0.00"
+      }
+    },
+    "contact": {
+      "qq": ""
+    },
+    "declaration": {
+    	"ads": "无",
+    	"data": "插件不采集任何数据",
+    	"permissions": "无"
+    },
+    "npmurl": ""
+  },
+  "uni_modules": {
+    "dependencies": [
+			"uv-ui-tools",
+			"uv-transition",
+			"uv-icon"
+		],
+    "encrypt": [],
+    "platforms": {
+			"cloud": {
+				"tcb": "y",
+				"aliyun": "y"
+			},
+			"client": {
+				"Vue": {
+					"vue2": "y",
+					"vue3": "y"
+				},
+				"App": {
+					"app-vue": "y",
+					"app-nvue": "y"
+				},
+				"H5-mobile": {
+					"Safari": "y",
+					"Android Browser": "y",
+					"微信浏览器(Android)": "y",
+					"QQ浏览器(Android)": "y"
+				},
+				"H5-pc": {
+					"Chrome": "y",
+					"IE": "y",
+					"Edge": "y",
+					"Firefox": "y",
+					"Safari": "y"
+				},
+				"小程序": {
+					"微信": "y",
+					"阿里": "y",
+					"百度": "y",
+					"字节跳动": "y",
+					"QQ": "y",
+					"钉钉": "u",
+					"快手": "u",
+					"飞书": "u",
+					"京东": "u"
+				},
+				"快应用": {
+					"华为": "u",
+					"联盟": "u"
+				}
+			}
+		}
+  }
+}

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

@@ -0,0 +1,15 @@
+## Image 图片
+
+> **组件名:uv-image**
+
+此组件为`uni-app`的`image`组件的加强版,在继承了原有功能外,增加`observer`懒加载功能,还支持淡入动画、加载中、加载失败提示、圆角值和形状等。
+
+# <a href="https://www.uvui.cn/components/image.html" target="_blank">查看文档</a>
+
+## [下载完整示例项目](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+### [更多插件,请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+![image](https://mp-a667b617-c5f1-4a2d-9a54-683a67cff588.cdn.bspapp.com/uv-ui/banner.png)
+
+#### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>

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

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

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

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

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

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

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

@@ -0,0 +1,87 @@
+{
+  "id": "uv-loading-icon",
+  "displayName": "uv-loading-icon 加载动画 全面兼容vue3+2、app、h5、小程序等多端",
+  "version": "1.0.3",
+  "description": "此组件为一个小动画,目前用在uv-ui的uv-load-more加载更多等组件,还可以运用在项目中正在加载状态场景。",
+  "keywords": [
+    "uv-loading-icon",
+    "uvui",
+    "uv-ui",
+    "loading",
+    "加载动画"
+],
+  "repository": "",
+  "engines": {
+    "HBuilderX": "^3.1.0"
+  },
+  "dcloudext": {
+    "type": "component-vue",
+    "sale": {
+      "regular": {
+        "price": "0.00"
+      },
+      "sourcecode": {
+        "price": "0.00"
+      }
+    },
+    "contact": {
+      "qq": ""
+    },
+    "declaration": {
+    	"ads": "无",
+    	"data": "插件不采集任何数据",
+    	"permissions": "无"
+    },
+    "npmurl": ""
+  },
+  "uni_modules": {
+    "dependencies": [
+			"uv-ui-tools"
+		],
+    "encrypt": [],
+    "platforms": {
+			"cloud": {
+				"tcb": "y",
+				"aliyun": "y"
+			},
+			"client": {
+				"Vue": {
+					"vue2": "y",
+					"vue3": "y"
+				},
+				"App": {
+					"app-vue": "y",
+					"app-nvue": "y"
+				},
+				"H5-mobile": {
+					"Safari": "y",
+					"Android Browser": "y",
+					"微信浏览器(Android)": "y",
+					"QQ浏览器(Android)": "y"
+				},
+				"H5-pc": {
+					"Chrome": "y",
+					"IE": "y",
+					"Edge": "y",
+					"Firefox": "y",
+					"Safari": "y"
+				},
+				"小程序": {
+					"微信": "y",
+					"阿里": "y",
+					"百度": "y",
+					"字节跳动": "y",
+					"QQ": "y",
+					"钉钉": "u",
+					"快手": "u",
+					"飞书": "u",
+					"京东": "u"
+				},
+				"快应用": {
+					"华为": "u",
+					"联盟": "u"
+				}
+			}
+		}
+  }
+}

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

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

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

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

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

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

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

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

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

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

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

@@ -0,0 +1,87 @@
+{
+  "id": "uv-transition",
+  "displayName": "uv-transition 动画 全面兼容vue3+2、app、h5、小程序等多端",
+  "version": "1.0.8",
+  "description": "transition 该组件用于组件的动画过渡效果。",
+  "keywords": [
+    "uv-transition",
+    "uvui",
+    "uv-ui",
+    "transition",
+    "动画"
+],
+  "repository": "",
+  "engines": {
+    "HBuilderX": "^3.1.0"
+  },
+  "dcloudext": {
+    "type": "component-vue",
+    "sale": {
+      "regular": {
+        "price": "0.00"
+      },
+      "sourcecode": {
+        "price": "0.00"
+      }
+    },
+    "contact": {
+      "qq": ""
+    },
+    "declaration": {
+    	"ads": "无",
+    	"data": "插件不采集任何数据",
+    	"permissions": "无"
+    },
+    "npmurl": ""
+  },
+  "uni_modules": {
+    "dependencies": [
+			"uv-ui-tools"
+		],
+    "encrypt": [],
+    "platforms": {
+			"cloud": {
+				"tcb": "y",
+				"aliyun": "y"
+			},
+			"client": {
+				"Vue": {
+					"vue2": "y",
+					"vue3": "y"
+				},
+				"App": {
+					"app-vue": "y",
+					"app-nvue": "y"
+				},
+				"H5-mobile": {
+					"Safari": "y",
+					"Android Browser": "y",
+					"微信浏览器(Android)": "y",
+					"QQ浏览器(Android)": "y"
+				},
+				"H5-pc": {
+					"Chrome": "y",
+					"IE": "y",
+					"Edge": "y",
+					"Firefox": "y",
+					"Safari": "y"
+				},
+				"小程序": {
+					"微信": "y",
+					"阿里": "y",
+					"百度": "y",
+					"字节跳动": "y",
+					"QQ": "y",
+					"钉钉": "u",
+					"快手": "u",
+					"飞书": "u",
+					"京东": "u"
+				},
+				"快应用": {
+					"华为": "u",
+					"联盟": "u"
+				}
+			}
+		}
+  }
+}

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

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

+ 76 - 0
uni_modules/uv-ui-tools/changelog.md

@@ -0,0 +1,76 @@
+## 1.1.25(2024-01-20)
+1.1.20版本更新
+## 1.1.24(2023-12-21)
+1. luch-request更新
+## 1.1.23(2023-12-12)
+1. 1.1.19版本
+## 1.1.22(2023-11-28)
+1. 优化
+## 1.1.21(2023-11-10)
+1. 1.1.17版本
+## 1.1.20(2023-10-30)
+1. 1.1.16版本
+## 1.1.19(2023-10-13)
+1. 兼容vue3
+## 1.1.18(2023-10-12)
+1. 1.1.15版本
+## 1.1.17(2023-09-27)
+1. 1.1.14版本发布
+## 1.1.16(2023-09-15)
+1. 1.1.13版本发布
+## 1.1.15(2023-09-15)
+1. 更新button.js相关按钮支持open-type="agreePrivacyAuthorization"
+## 1.1.14(2023-09-14)
+1. 优化dayjs
+## 1.1.13(2023-09-13)
+1. 优化,$uv中增加unit参数,方便组件中使用
+## 1.1.12(2023-09-10)
+1. 升级版本
+## 1.1.11(2023-09-04)
+1. 1.1.11版本
+## 1.1.10(2023-08-31)
+1. 修复customStyle和customClass存在冲突的问题
+## 1.1.9(2023-08-27)
+1. 版本升级
+2. 优化
+## 1.1.8(2023-08-24)
+1. 版本升级
+## 1.1.7(2023-08-22)
+1. 版本升级
+## 1.1.6(2023-08-18)
+uvui版本:1.1.6
+## 1.0.15(2023-08-14)
+1. 更新uvui版本号
+## 1.0.13(2023-08-06)
+1. 优化
+## 1.0.12(2023-08-06)
+1. 修改版本号
+## 1.0.11(2023-08-06)
+1. 路由增加events参数
+2. 路由拦截修复
+## 1.0.10(2023-08-01)
+1. 优化
+## 1.0.9(2023-06-28)
+优化openType.js
+## 1.0.8(2023-06-15)
+1. 修改支付宝报错的BUG
+## 1.0.7(2023-06-07)
+1. 解决微信小程序使用uvui提示 Some selectors are not allowed in component wxss, including tag name selectors, ID selectors, and attribute selectors
+2. 解决上述提示,需要在uni.scss配置$uvui-nvue-style: false; 然后在APP.vue下面引入uvui内置的基础样式:@import '@/uni_modules/uv-ui-tools/index.scss';
+## 1.0.6(2023-06-04)
+1.  uv-ui-tools 优化工具组件,兼容更多功能
+2.  小程序分享功能优化等
+## 1.0.5(2023-06-02)
+1. 修改扩展使用mixin中方法的问题
+## 1.0.4(2023-05-23)
+1. 兼容百度小程序修改bem函数
+## 1.0.3(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.2(2023-05-10)
+1. 增加Http请求封装
+2. 优化
+## 1.0.1(2023-05-04)
+1. 修改名称及备注
+## 1.0.0(2023-05-04)
+1. uv-ui工具集首次发布

+ 6 - 0
uni_modules/uv-ui-tools/components/uv-ui-tools/uv-ui-tools.vue

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

+ 79 - 0
uni_modules/uv-ui-tools/index.js

@@ -0,0 +1,79 @@
+// 全局挂载引入http相关请求拦截插件
+import Request from './libs/luch-request'
+
+// 引入全局mixin
+import mixin from './libs/mixin/mixin.js'
+// 小程序特有的mixin
+import mpMixin from './libs/mixin/mpMixin.js'
+// #ifdef MP
+import mpShare from './libs/mixin/mpShare.js'
+// #endif
+
+// 路由封装
+import route from './libs/util/route.js'
+// 公共工具函数
+import * as index from './libs/function/index.js'
+// 防抖方法
+import debounce from './libs/function/debounce.js'
+// 节流方法
+import throttle from './libs/function/throttle.js'
+// 规则检验
+import * as test from './libs/function/test.js'
+
+// 颜色渐变相关,colorGradient-颜色渐变,hexToRgb-十六进制颜色转rgb颜色,rgbToHex-rgb转十六进制
+import * as colorGradient from './libs/function/colorGradient.js'
+
+// 配置信息
+import config from './libs/config/config.js'
+// 平台
+import platform from './libs/function/platform'
+
+const $uv = {
+	route,
+	config,
+	test,
+	date: index.timeFormat, // 另名date
+	...index,
+	colorGradient: colorGradient.colorGradient,
+	hexToRgb: colorGradient.hexToRgb,
+	rgbToHex: colorGradient.rgbToHex,
+	colorToRgba: colorGradient.colorToRgba,
+	http: new Request(),
+	debounce,
+	throttle,
+	platform,
+	mixin,
+	mpMixin
+}
+uni.$uv = $uv;
+const install = (Vue,options={}) => {
+		// #ifndef APP-NVUE
+		const cloneMixin = index.deepClone(mixin);
+		delete cloneMixin?.props?.customClass;
+		delete cloneMixin?.props?.customStyle;
+		Vue.mixin(cloneMixin);
+		// #ifdef MP
+		if(options.mpShare){
+			Vue.mixin(mpShare);
+		}
+		// #endif
+		// #endif
+		// #ifdef VUE2
+		// 时间格式化,同时两个名称,date和timeFormat
+		Vue.filter('timeFormat', (timestamp, format) => uni.$uv.timeFormat(timestamp, format));
+		Vue.filter('date', (timestamp, format) => uni.$uv.timeFormat(timestamp, format));
+		// 将多久以前的方法,注入到全局过滤器
+		Vue.filter('timeFrom', (timestamp, format) => uni.$uv.timeFrom(timestamp, format));
+		// 同时挂载到uni和Vue.prototype中
+		// #ifndef APP-NVUE
+		// 只有vue,挂载到Vue.prototype才有意义,因为nvue中全局Vue.prototype和Vue.mixin是无效的
+		Vue.prototype.$uv = $uv;
+		// #endif
+		// #endif
+		// #ifdef VUE3
+		Vue.config.globalProperties.$uv = $uv;
+		// #endif
+}
+export default {
+	install
+}

+ 7 - 0
uni_modules/uv-ui-tools/index.scss

@@ -0,0 +1,7 @@
+// 引入公共基础类
+@import "./libs/css/common.scss";
+
+// 非nvue的样式
+/* #ifndef APP-NVUE */
+@import "./libs/css/vue.scss";
+/* #endif */

+ 34 - 0
uni_modules/uv-ui-tools/libs/config/config.js

@@ -0,0 +1,34 @@
+// 此版本发布于2024-01-20
+const version = '1.1.20'
+
+// 开发环境才提示,生产环境不会提示
+if (process.env.NODE_ENV === 'development') {
+	console.log(`\n %c uvui V${version} https://www.uvui.cn/ \n\n`, 'color: #ffffff; background: #3c9cff; padding:5px 0; border-radius: 5px;');
+}
+
+export default {
+    v: version,
+    version,
+    // 主题名称
+    type: [
+        'primary',
+        'success',
+        'info',
+        'error',
+        'warning'
+    ],
+    // 颜色部分,本来可以通过scss的:export导出供js使用,但是奈何nvue不支持
+    color: {
+        'uv-primary': '#2979ff',
+        'uv-warning': '#ff9900',
+        'uv-success': '#19be6b',
+        'uv-error': '#fa3534',
+        'uv-info': '#909399',
+        'uv-main-color': '#303133',
+        'uv-content-color': '#606266',
+        'uv-tips-color': '#909399',
+        'uv-light-color': '#c0c4cc'
+    },
+	// 默认单位,可以通过配置为rpx,那么在用于传入组件大小参数为数值时,就默认为rpx
+	unit: 'px'
+}

+ 32 - 0
uni_modules/uv-ui-tools/libs/css/color.scss

@@ -0,0 +1,32 @@
+$uv-main-color: #303133 !default;
+$uv-content-color: #606266 !default;
+$uv-tips-color: #909193 !default;
+$uv-light-color: #c0c4cc !default;
+$uv-border-color: #dadbde !default;
+$uv-bg-color: #f3f4f6 !default;
+$uv-disabled-color: #c8c9cc !default;
+
+$uv-primary: #3c9cff !default;
+$uv-primary-dark: #398ade !default;
+$uv-primary-disabled: #9acafc !default;
+$uv-primary-light: #ecf5ff !default;
+
+$uv-warning: #f9ae3d !default;
+$uv-warning-dark: #f1a532 !default;
+$uv-warning-disabled: #f9d39b !default;
+$uv-warning-light: #fdf6ec !default;
+
+$uv-success: #5ac725 !default;
+$uv-success-dark: #53c21d !default;
+$uv-success-disabled: #a9e08f !default;
+$uv-success-light: #f5fff0;
+
+$uv-error: #f56c6c !default;
+$uv-error-dark: #e45656 !default;
+$uv-error-disabled: #f7b2b2 !default;
+$uv-error-light: #fef0f0 !default;
+
+$uv-info: #909399 !default;
+$uv-info-dark: #767a82 !default;
+$uv-info-disabled: #c4c6c9 !default;
+$uv-info-light: #f4f4f5 !default;

+ 100 - 0
uni_modules/uv-ui-tools/libs/css/common.scss

@@ -0,0 +1,100 @@
+// 超出行数,自动显示行尾省略号,最多5行
+// 来自uvui的温馨提示:当您在控制台看到此报错,说明需要在App.vue的style标签加上【lang="scss"】
+@for $i from 1 through 5 {
+	.uv-line-#{$i} {
+		/* #ifdef APP-NVUE */
+		// nvue下,可以直接使用lines属性,这是weex特有样式
+		lines: $i;
+		text-overflow: ellipsis;
+		overflow: hidden;
+		flex: 1;
+		/* #endif */
+
+		/* #ifndef APP-NVUE */
+		// vue下,单行和多行显示省略号需要单独处理
+		@if $i == '1' {
+			overflow: hidden;
+			white-space: nowrap;
+			text-overflow: ellipsis;
+		} @else {
+			display: -webkit-box!important;
+			overflow: hidden;
+			text-overflow: ellipsis;
+			word-break: break-all;
+			-webkit-line-clamp: $i;
+			-webkit-box-orient: vertical!important;
+		}
+		/* #endif */
+	}
+}
+$uv-bordercolor: #dadbde;
+@if variable-exists(uv-border-color) {
+	$uv-bordercolor: $uv-border-color;
+}
+
+// 此处加上!important并非随意乱用,而是因为目前*.nvue页面编译到H5时,
+// App.vue的样式会被uni-app的view元素的自带border属性覆盖,导致无效
+// 综上,这是uni-app的缺陷导致我们为了多端兼容,而必须要加上!important
+// 移动端兼容性较好,直接使用0.5px去实现细边框,不使用伪元素形式实现
+.uv-border {
+	border-width: 0.5px!important;
+	border-color: $uv-bordercolor!important;
+    border-style: solid;
+}
+
+.uv-border-top {
+	border-top-width: 0.5px!important;
+	border-color: $uv-bordercolor!important;
+    border-top-style: solid;
+}
+
+.uv-border-left {
+	border-left-width: 0.5px!important;
+	border-color: $uv-bordercolor!important;
+    border-left-style: solid;
+}
+
+.uv-border-right {
+	border-right-width: 0.5px!important;
+	border-color: $uv-bordercolor!important;
+    border-right-style: solid;
+}
+
+.uv-border-bottom {
+	border-bottom-width: 0.5px!important;
+	border-color: $uv-bordercolor!important;
+    border-bottom-style: solid;
+}
+
+.uv-border-top-bottom {
+	border-top-width: 0.5px!important;
+	border-bottom-width: 0.5px!important;
+	border-color: $uv-bordercolor!important;
+    border-top-style: solid;
+    border-bottom-style: solid;
+}
+
+// 去除button的所有默认样式,让其表现跟普通的view、text元素一样
+.uv-reset-button {
+	padding: 0;
+	background-color: transparent;
+	/* #ifndef APP-PLUS */
+	font-size: inherit;
+	line-height: inherit;
+	color: inherit;
+	/* #endif */
+	/* #ifdef APP-NVUE */
+	border-width: 0;
+	/* #endif */
+}
+
+/* #ifndef APP-NVUE */
+.uv-reset-button::after {
+   border: none;
+}
+/* #endif */
+
+.uv-hover-class {
+	opacity: 0.7;
+}
+

+ 23 - 0
uni_modules/uv-ui-tools/libs/css/components.scss

@@ -0,0 +1,23 @@
+@mixin flex($direction: row) {
+	/* #ifndef APP-NVUE */
+	display: flex;
+	/* #endif */
+	flex-direction: $direction;
+}
+
+/* #ifndef APP-NVUE */
+// 由于uvui是基于nvue环境进行开发的,此环境中普通元素默认为flex-direction: column;
+// 所以在非nvue中,需要对元素进行重置为flex-direction: column; 否则可能会表现异常
+$uvui-nvue-style: true !default;
+@if $uvui-nvue-style == true {
+	view, scroll-view, swiper-item {
+		display: flex;
+		flex-direction: column;
+		flex-shrink: 0;
+		flex-grow: 0;
+		flex-basis: auto;
+		align-items: stretch;
+		align-content: flex-start;
+	}
+}
+/* #endif */

+ 111 - 0
uni_modules/uv-ui-tools/libs/css/variable.scss

@@ -0,0 +1,111 @@
+// 超出行数,自动显示行尾省略号,最多5行
+// 来自uvui的温馨提示:当您在控制台看到此报错,说明需要在App.vue的style标签加上【lang="scss"】
+@if variable-exists(show-lines) {
+	@for $i from 1 through 5 {
+		.uv-line-#{$i} {
+			/* #ifdef APP-NVUE */
+			// nvue下,可以直接使用lines属性,这是weex特有样式
+			lines: $i;
+			text-overflow: ellipsis;
+			overflow: hidden;
+			flex: 1;
+			/* #endif */
+
+			/* #ifndef APP-NVUE */
+			// vue下,单行和多行显示省略号需要单独处理
+			@if $i == '1' {
+				overflow: hidden;
+				white-space: nowrap;
+				text-overflow: ellipsis;
+			} @else {
+				display: -webkit-box!important;
+				overflow: hidden;
+				text-overflow: ellipsis;
+				word-break: break-all;
+				-webkit-line-clamp: $i;
+				-webkit-box-orient: vertical!important;
+			}
+			/* #endif */
+		}
+	}
+}
+@if variable-exists(show-border) {
+	$uv-bordercolor: #dadbde;
+	@if variable-exists(uv-border-color) {
+		$uv-bordercolor: $uv-border-color;
+	}
+	// 此处加上!important并非随意乱用,而是因为目前*.nvue页面编译到H5时,
+	// App.vue的样式会被uni-app的view元素的自带border属性覆盖,导致无效
+	// 综上,这是uni-app的缺陷导致我们为了多端兼容,而必须要加上!important
+	// 移动端兼容性较好,直接使用0.5px去实现细边框,不使用伪元素形式实现
+	@if variable-exists(show-border-surround) {
+		.uv-border {
+			border-width: 0.5px!important;
+			border-color: $uv-bordercolor!important;
+			border-style: solid;
+		}
+	}
+	@if variable-exists(show-border-top) {
+		.uv-border-top {
+			border-top-width: 0.5px!important;
+			border-color: $uv-bordercolor!important;
+			border-top-style: solid;
+		}
+	}
+	@if variable-exists(show-border-left) {
+		.uv-border-left {
+			border-left-width: 0.5px!important;
+			border-color: $uv-bordercolor!important;
+			border-left-style: solid;
+		}
+	}
+	@if variable-exists(show-border-right) {
+		.uv-border-right {
+			border-right-width: 0.5px!important;
+			border-color: $uv-bordercolor!important;
+			border-right-style: solid;
+		}
+	}
+	@if variable-exists(show-border-bottom) {
+		.uv-border-bottom {
+			border-bottom-width: 0.5px!important;
+			border-color: $uv-bordercolor!important;
+				border-bottom-style: solid;
+		}
+	}
+	@if variable-exists(show-border-top-bottom) {
+		.uv-border-top-bottom {
+			border-top-width: 0.5px!important;
+			border-bottom-width: 0.5px!important;
+			border-color: $uv-bordercolor!important;
+			border-top-style: solid;
+			border-bottom-style: solid;
+		}
+	}
+}
+@if variable-exists(show-reset-button) {
+	// 去除button的所有默认样式,让其表现跟普通的view、text元素一样
+	.uv-reset-button {
+		padding: 0;
+		background-color: transparent;
+		/* #ifndef APP-PLUS */
+		font-size: inherit;
+		line-height: inherit;
+		color: inherit;
+		/* #endif */
+		/* #ifdef APP-NVUE */
+		border-width: 0;
+		/* #endif */
+	}
+
+	/* #ifndef APP-NVUE */
+	.uv-reset-button::after {
+		 border: none;
+	}
+	/* #endif */
+}
+@if variable-exists(show-hover) {
+	.uv-hover-class {
+		opacity: 0.7;
+	}
+}

+ 40 - 0
uni_modules/uv-ui-tools/libs/css/vue.scss

@@ -0,0 +1,40 @@
+// 历遍生成4个方向的底部安全区
+@each $d in top, right, bottom, left {
+	.uv-safe-area-inset-#{$d} {
+		padding-#{$d}: 0;
+		padding-#{$d}: constant(safe-area-inset-#{$d});  
+		padding-#{$d}: env(safe-area-inset-#{$d});  
+	}
+}
+
+//提升H5端uni.toast()的层级,避免被uvui的modal等遮盖
+/* #ifdef H5 */
+uni-toast {
+    z-index: 10090;
+}
+uni-toast .uni-toast {
+   z-index: 10090;
+}
+/* #endif */
+
+// 隐藏scroll-view的滚动条
+::-webkit-scrollbar {
+    display: none;  
+    width: 0 !important;  
+    height: 0 !important;  
+    -webkit-appearance: none;  
+    background: transparent;  
+}
+
+$uvui-nvue-style: true !default;
+@if $uvui-nvue-style == false {
+	view, scroll-view, swiper-item {
+		display: flex;
+		flex-direction: column;
+		flex-shrink: 0;
+		flex-grow: 0;
+		flex-basis: auto;
+		align-items: stretch;
+		align-content: flex-start;
+	}
+}

+ 134 - 0
uni_modules/uv-ui-tools/libs/function/colorGradient.js

@@ -0,0 +1,134 @@
+/**
+ * 求两个颜色之间的渐变值
+ * @param {string} startColor 开始的颜色
+ * @param {string} endColor 结束的颜色
+ * @param {number} step 颜色等分的份额
+ * */
+function colorGradient(startColor = 'rgb(0, 0, 0)', endColor = 'rgb(255, 255, 255)', step = 10) {
+    const startRGB = hexToRgb(startColor, false) // 转换为rgb数组模式
+    const startR = startRGB[0]
+    const startG = startRGB[1]
+    const startB = startRGB[2]
+
+    const endRGB = hexToRgb(endColor, false)
+    const endR = endRGB[0]
+    const endG = endRGB[1]
+    const endB = endRGB[2]
+
+    const sR = (endR - startR) / step // 总差值
+    const sG = (endG - startG) / step
+    const sB = (endB - startB) / step
+    const colorArr = []
+    for (let i = 0; i < step; i++) {
+        // 计算每一步的hex值
+        let hex = rgbToHex(`rgb(${Math.round((sR * i + startR))},${Math.round((sG * i + startG))},${Math.round((sB
+			* i + startB))})`)
+        // 确保第一个颜色值为startColor的值
+        if (i === 0) hex = rgbToHex(startColor)
+        // 确保最后一个颜色值为endColor的值
+        if (i === step - 1) hex = rgbToHex(endColor)
+        colorArr.push(hex)
+    }
+    return colorArr
+}
+
+// 将hex表示方式转换为rgb表示方式(这里返回rgb数组模式)
+function hexToRgb(sColor, str = true) {
+    const reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/
+    sColor = String(sColor).toLowerCase()
+    if (sColor && reg.test(sColor)) {
+        if (sColor.length === 4) {
+            let sColorNew = '#'
+            for (let i = 1; i < 4; i += 1) {
+                sColorNew += sColor.slice(i, i + 1).concat(sColor.slice(i, i + 1))
+            }
+            sColor = sColorNew
+        }
+        // 处理六位的颜色值
+        const sColorChange = []
+        for (let i = 1; i < 7; i += 2) {
+            sColorChange.push(parseInt(`0x${sColor.slice(i, i + 2)}`))
+        }
+        if (!str) {
+            return sColorChange
+        }
+        return `rgb(${sColorChange[0]},${sColorChange[1]},${sColorChange[2]})`
+    } if (/^(rgb|RGB)/.test(sColor)) {
+        const arr = sColor.replace(/(?:\(|\)|rgb|RGB)*/g, '').split(',')
+        return arr.map((val) => Number(val))
+    }
+    return sColor
+}
+
+// 将rgb表示方式转换为hex表示方式
+function rgbToHex(rgb) {
+    const _this = rgb
+    const reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/
+    if (/^(rgb|RGB)/.test(_this)) {
+        const aColor = _this.replace(/(?:\(|\)|rgb|RGB)*/g, '').split(',')
+        let strHex = '#'
+        for (let i = 0; i < aColor.length; i++) {
+            let hex = Number(aColor[i]).toString(16)
+            hex = String(hex).length == 1 ? `${0}${hex}` : hex // 保证每个rgb的值为2位
+            if (hex === '0') {
+                hex += hex
+            }
+            strHex += hex
+        }
+        if (strHex.length !== 7) {
+            strHex = _this
+        }
+        return strHex
+    } if (reg.test(_this)) {
+        const aNum = _this.replace(/#/, '').split('')
+        if (aNum.length === 6) {
+            return _this
+        } if (aNum.length === 3) {
+            let numHex = '#'
+            for (let i = 0; i < aNum.length; i += 1) {
+                numHex += (aNum[i] + aNum[i])
+            }
+            return numHex
+        }
+    } else {
+        return _this
+    }
+}
+
+/**
+* JS颜色十六进制转换为rgb或rgba,返回的格式为 rgba(255,255,255,0.5)字符串
+* sHex为传入的十六进制的色值
+* alpha为rgba的透明度
+*/
+function colorToRgba(color, alpha) {
+    color = rgbToHex(color)
+    // 十六进制颜色值的正则表达式
+    const reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/
+    /* 16进制颜色转为RGB格式 */
+    let sColor = String(color).toLowerCase()
+    if (sColor && reg.test(sColor)) {
+        if (sColor.length === 4) {
+            let sColorNew = '#'
+            for (let i = 1; i < 4; i += 1) {
+                sColorNew += sColor.slice(i, i + 1).concat(sColor.slice(i, i + 1))
+            }
+            sColor = sColorNew
+        }
+        // 处理六位的颜色值
+        const sColorChange = []
+        for (let i = 1; i < 7; i += 2) {
+            sColorChange.push(parseInt(`0x${sColor.slice(i, i + 2)}`))
+        }
+        // return sColorChange.join(',')
+        return `rgba(${sColorChange.join(',')},${alpha})`
+    }
+
+    return sColor
+}
+
+export {
+    colorGradient,
+    hexToRgb,
+    rgbToHex,
+    colorToRgba
+}

+ 29 - 0
uni_modules/uv-ui-tools/libs/function/debounce.js

@@ -0,0 +1,29 @@
+let timeout = null
+
+/**
+ * 防抖原理:一定时间内,只有最后一次操作,再过wait毫秒后才执行函数
+ *
+ * @param {Function} func 要执行的回调函数
+ * @param {Number} wait 延时的时间
+ * @param {Boolean} immediate 是否立即执行
+ * @return null
+ */
+function debounce(func, wait = 500, immediate = false) {
+    // 清除定时器
+    if (timeout !== null) clearTimeout(timeout)
+    // 立即执行,此类情况一般用不到
+    if (immediate) {
+        const callNow = !timeout
+        timeout = setTimeout(() => {
+            timeout = null
+        }, wait)
+        if (callNow) typeof func === 'function' && func()
+    } else {
+        // 设置定时器,当最后一次操作后,timeout不会再被清除,所以在延时wait毫秒后执行func回调方法
+        timeout = setTimeout(() => {
+            typeof func === 'function' && func()
+        }, wait)
+    }
+}
+
+export default debounce

+ 167 - 0
uni_modules/uv-ui-tools/libs/function/digit.js

@@ -0,0 +1,167 @@
+let _boundaryCheckingState = true; // 是否进行越界检查的全局开关
+
+/**
+ * 把错误的数据转正
+ * @private
+ * @example strip(0.09999999999999998)=0.1
+ */
+function strip(num, precision = 15) {
+  return +parseFloat(Number(num).toPrecision(precision));
+}
+
+/**
+ * Return digits length of a number
+ * @private
+ * @param {*number} num Input number
+ */
+function digitLength(num) {
+  // Get digit length of e
+  const eSplit = num.toString().split(/[eE]/);
+  const len = (eSplit[0].split('.')[1] || '').length - +(eSplit[1] || 0);
+  return len > 0 ? len : 0;
+}
+
+/**
+ * 把小数转成整数,如果是小数则放大成整数
+ * @private
+ * @param {*number} num 输入数
+ */
+function float2Fixed(num) {
+  if (num.toString().indexOf('e') === -1) {
+    return Number(num.toString().replace('.', ''));
+  }
+  const dLen = digitLength(num);
+  return dLen > 0 ? strip(Number(num) * Math.pow(10, dLen)) : Number(num);
+}
+
+/**
+ * 检测数字是否越界,如果越界给出提示
+ * @private
+ * @param {*number} num 输入数
+ */
+function checkBoundary(num) {
+  if (_boundaryCheckingState) {
+    if (num > Number.MAX_SAFE_INTEGER || num < Number.MIN_SAFE_INTEGER) {
+      console.warn(`${num} 超出了精度限制,结果可能不正确`);
+    }
+  }
+}
+
+/**
+ * 把递归操作扁平迭代化
+ * @param {number[]} arr 要操作的数字数组
+ * @param {function} operation 迭代操作
+ * @private
+ */
+function iteratorOperation(arr, operation) {
+  const [num1, num2, ...others] = arr;
+  let res = operation(num1, num2);
+
+  others.forEach((num) => {
+    res = operation(res, num);
+  });
+
+  return res;
+}
+
+/**
+ * 高精度乘法
+ * @export
+ */
+export function times(...nums) {
+  if (nums.length > 2) {
+    return iteratorOperation(nums, times);
+  }
+
+  const [num1, num2] = nums;
+  const num1Changed = float2Fixed(num1);
+  const num2Changed = float2Fixed(num2);
+  const baseNum = digitLength(num1) + digitLength(num2);
+  const leftValue = num1Changed * num2Changed;
+
+  checkBoundary(leftValue);
+
+  return leftValue / Math.pow(10, baseNum);
+}
+
+/**
+ * 高精度加法
+ * @export
+ */
+export function plus(...nums) {
+  if (nums.length > 2) {
+    return iteratorOperation(nums, plus);
+  }
+
+  const [num1, num2] = nums;
+  // 取最大的小数位
+  const baseNum = Math.pow(10, Math.max(digitLength(num1), digitLength(num2)));
+  // 把小数都转为整数然后再计算
+  return (times(num1, baseNum) + times(num2, baseNum)) / baseNum;
+}
+
+/**
+ * 高精度减法
+ * @export
+ */
+export function minus(...nums) {
+  if (nums.length > 2) {
+    return iteratorOperation(nums, minus);
+  }
+
+  const [num1, num2] = nums;
+  const baseNum = Math.pow(10, Math.max(digitLength(num1), digitLength(num2)));
+  return (times(num1, baseNum) - times(num2, baseNum)) / baseNum;
+}
+
+/**
+ * 高精度除法
+ * @export
+ */
+export function divide(...nums) {
+  if (nums.length > 2) {
+    return iteratorOperation(nums, divide);
+  }
+
+  const [num1, num2] = nums;
+  const num1Changed = float2Fixed(num1);
+  const num2Changed = float2Fixed(num2);
+  checkBoundary(num1Changed);
+  checkBoundary(num2Changed);
+  // 重要,这里必须用strip进行修正
+  return times(num1Changed / num2Changed, strip(Math.pow(10, digitLength(num2) - digitLength(num1))));
+}
+
+/**
+ * 四舍五入
+ * @export
+ */
+export function round(num, ratio) {
+  const base = Math.pow(10, ratio);
+  let result = divide(Math.round(Math.abs(times(num, base))), base);
+  if (num < 0 && result !== 0) {
+    result = times(result, -1);
+  }
+  // 位数不足则补0
+  return result;
+}
+
+/**
+ * 是否进行边界检查,默认开启
+ * @param flag 标记开关,true 为开启,false 为关闭,默认为 true
+ * @export
+ */
+export function enableBoundaryChecking(flag = true) {
+  _boundaryCheckingState = flag;
+}
+
+
+export default {
+  times,
+  plus,
+  minus,
+  divide,
+  round,
+  enableBoundaryChecking,
+};
+

+ 734 - 0
uni_modules/uv-ui-tools/libs/function/index.js

@@ -0,0 +1,734 @@
+import { number, empty } from './test.js'
+import { round } from './digit.js'
+/**
+ * @description 如果value小于min,取min;如果value大于max,取max
+ * @param {number} min
+ * @param {number} max
+ * @param {number} value
+ */
+function range(min = 0, max = 0, value = 0) {
+	return Math.max(min, Math.min(max, Number(value)))
+}
+
+/**
+ * @description 用于获取用户传递值的px值  如果用户传递了"xxpx"或者"xxrpx",取出其数值部分,如果是"xxxrpx"还需要用过uni.upx2px进行转换
+ * @param {number|string} value 用户传递值的px值
+ * @param {boolean} unit
+ * @returns {number|string}
+ */
+function getPx(value, unit = false) {
+	if (number(value)) {
+		return unit ? `${value}px` : Number(value)
+	}
+	// 如果带有rpx,先取出其数值部分,再转为px值
+	if (/(rpx|upx)$/.test(value)) {
+		return unit ? `${uni.upx2px(parseInt(value))}px` : Number(uni.upx2px(parseInt(value)))
+	}
+	return unit ? `${parseInt(value)}px` : parseInt(value)
+}
+
+/**
+ * @description 进行延时,以达到可以简写代码的目的 比如: await uni.$uv.sleep(20)将会阻塞20ms
+ * @param {number} value 堵塞时间 单位ms 毫秒
+ * @returns {Promise} 返回promise
+ */
+function sleep(value = 30) {
+	return new Promise((resolve) => {
+		setTimeout(() => {
+			resolve()
+		}, value)
+	})
+}
+/**
+ * @description 运行期判断平台
+ * @returns {string} 返回所在平台(小写)
+ * @link 运行期判断平台 https://uniapp.dcloud.io/frame?id=判断平台
+ */
+function os() {
+	return uni.getSystemInfoSync().platform.toLowerCase()
+}
+/**
+ * @description 获取系统信息同步接口
+ * @link 获取系统信息同步接口 https://uniapp.dcloud.io/api/system/info?id=getsysteminfosync
+ */
+function sys() {
+	return uni.getSystemInfoSync()
+}
+
+/**
+ * @description 取一个区间数
+ * @param {Number} min 最小值
+ * @param {Number} max 最大值
+ */
+function random(min, max) {
+	if (min >= 0 && max > 0 && max >= min) {
+		const gab = max - min + 1
+		return Math.floor(Math.random() * gab + min)
+	}
+	return 0
+}
+
+/**
+ * @param {Number} len uuid的长度
+ * @param {Boolean} firstU 将返回的首字母置为"u"
+ * @param {Nubmer} radix 生成uuid的基数(意味着返回的字符串都是这个基数),2-二进制,8-八进制,10-十进制,16-十六进制
+ */
+function guid(len = 32, firstU = true, radix = null) {
+	const chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('')
+	const uuid = []
+	radix = radix || chars.length
+
+	if (len) {
+		// 如果指定uuid长度,只是取随机的字符,0|x为位运算,能去掉x的小数位,返回整数位
+		for (let i = 0; i < len; i++) uuid[i] = chars[0 | Math.random() * radix]
+	} else {
+		let r
+		// rfc4122标准要求返回的uuid中,某些位为固定的字符
+		uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-'
+		uuid[14] = '4'
+
+		for (let i = 0; i < 36; i++) {
+			if (!uuid[i]) {
+				r = 0 | Math.random() * 16
+				uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r]
+			}
+		}
+	}
+	// 移除第一个字符,并用u替代,因为第一个字符为数值时,该guuid不能用作id或者class
+	if (firstU) {
+		uuid.shift()
+		return `u${uuid.join('')}`
+	}
+	return uuid.join('')
+}
+
+/**
+* @description 获取父组件的参数,因为支付宝小程序不支持provide/inject的写法
+   this.$parent在非H5中,可以准确获取到父组件,但是在H5中,需要多次this.$parent.$parent.xxx
+   这里默认值等于undefined有它的含义,因为最顶层元素(组件)的$parent就是undefined,意味着不传name
+   值(默认为undefined),就是查找最顶层的$parent
+*  @param {string|undefined} name 父组件的参数名
+*/
+function $parent(name = undefined) {
+	let parent = this.$parent
+	// 通过while历遍,这里主要是为了H5需要多层解析的问题
+	while (parent) {
+		// 父组件
+		if (parent.$options && parent.$options.name !== name) {
+			// 如果组件的name不相等,继续上一级寻找
+			parent = parent.$parent
+		} else {
+			return parent
+		}
+	}
+	return false
+}
+
+/**
+ * @description 样式转换
+ * 对象转字符串,或者字符串转对象
+ * @param {object | string} customStyle 需要转换的目标
+ * @param {String} target 转换的目的,object-转为对象,string-转为字符串
+ * @returns {object|string}
+ */
+function addStyle(customStyle, target = 'object') {
+	// 字符串转字符串,对象转对象情形,直接返回
+	if (empty(customStyle) || typeof(customStyle) === 'object' && target === 'object' || target === 'string' &&
+		typeof(customStyle) === 'string') {
+		return customStyle
+	}
+	// 字符串转对象
+	if (target === 'object') {
+		// 去除字符串样式中的两端空格(中间的空格不能去掉,比如padding: 20px 0如果去掉了就错了),空格是无用的
+		customStyle = trim(customStyle)
+		// 根据";"将字符串转为数组形式
+		const styleArray = customStyle.split(';')
+		const style = {}
+		// 历遍数组,拼接成对象
+		for (let i = 0; i < styleArray.length; i++) {
+			// 'font-size:20px;color:red;',如此最后字符串有";"的话,会导致styleArray最后一个元素为空字符串,这里需要过滤
+			if (styleArray[i]) {
+				const item = styleArray[i].split(':')
+				style[trim(item[0])] = trim(item[1])
+			}
+		}
+		return style
+	}
+	// 这里为对象转字符串形式
+	let string = ''
+	for (const i in customStyle) {
+		// 驼峰转为中划线的形式,否则css内联样式,无法识别驼峰样式属性名
+		const key = i.replace(/([A-Z])/g, '-$1').toLowerCase()
+		string += `${key}:${customStyle[i]};`
+	}
+	// 去除两端空格
+	return trim(string)
+}
+
+/**
+ * @description 添加单位,如果有rpx,upx,%,px等单位结尾或者值为auto,直接返回,否则加上px单位结尾
+ * @param {string|number} value 需要添加单位的值
+ * @param {string} unit 添加的单位名 比如px
+ */
+function addUnit(value = 'auto', unit = uni?.$uv?.config?.unit ? uni?.$uv?.config?.unit : 'px') {
+	value = String(value)
+	// 用uvui内置验证规则中的number判断是否为数值
+	return number(value) ? `${value}${unit}` : value
+}
+
+/**
+ * @description 深度克隆
+ * @param {object} obj 需要深度克隆的对象
+ * @param cache 缓存
+ * @returns {*} 克隆后的对象或者原值(不是对象)
+ */
+function deepClone(obj, cache = new WeakMap()) {
+	if (obj === null || typeof obj !== 'object') return obj;
+	if (cache.has(obj)) return cache.get(obj);
+	let clone;
+	if (obj instanceof Date) {
+		clone = new Date(obj.getTime());
+	} else if (obj instanceof RegExp) {
+		clone = new RegExp(obj);
+	} else if (obj instanceof Map) {
+		clone = new Map(Array.from(obj, ([key, value]) => [key, deepClone(value, cache)]));
+	} else if (obj instanceof Set) {
+		clone = new Set(Array.from(obj, value => deepClone(value, cache)));
+	} else if (Array.isArray(obj)) {
+		clone = obj.map(value => deepClone(value, cache));
+	} else if (Object.prototype.toString.call(obj) === '[object Object]') {
+		clone = Object.create(Object.getPrototypeOf(obj));
+		cache.set(obj, clone);
+		for (const [key, value] of Object.entries(obj)) {
+			clone[key] = deepClone(value, cache);
+		}
+	} else {
+		clone = Object.assign({}, obj);
+	}
+	cache.set(obj, clone);
+	return clone;
+}
+
+/**
+ * @description JS对象深度合并
+ * @param {object} target 需要拷贝的对象
+ * @param {object} source 拷贝的来源对象
+ * @returns {object|boolean} 深度合并后的对象或者false(入参有不是对象)
+ */
+function deepMerge(target = {}, source = {}) {
+	target = deepClone(target)
+	if (typeof target !== 'object' || target === null || typeof source !== 'object' || source === null) return target;
+	const merged = Array.isArray(target) ? target.slice() : Object.assign({}, target);
+	for (const prop in source) {
+		if (!source.hasOwnProperty(prop)) continue;
+		const sourceValue = source[prop];
+		const targetValue = merged[prop];
+		if (sourceValue instanceof Date) {
+			merged[prop] = new Date(sourceValue);
+		} else if (sourceValue instanceof RegExp) {
+			merged[prop] = new RegExp(sourceValue);
+		} else if (sourceValue instanceof Map) {
+			merged[prop] = new Map(sourceValue);
+		} else if (sourceValue instanceof Set) {
+			merged[prop] = new Set(sourceValue);
+		} else if (typeof sourceValue === 'object' && sourceValue !== null) {
+			merged[prop] = deepMerge(targetValue, sourceValue);
+		} else {
+			merged[prop] = sourceValue;
+		}
+	}
+	return merged;
+}
+
+/**
+ * @description error提示
+ * @param {*} err 错误内容
+ */
+function error(err) {
+	// 开发环境才提示,生产环境不会提示
+	if (process.env.NODE_ENV === 'development') {
+		console.error(`uvui提示:${err}`)
+	}
+}
+
+/**
+ * @description 打乱数组
+ * @param {array} array 需要打乱的数组
+ * @returns {array} 打乱后的数组
+ */
+function randomArray(array = []) {
+	// 原理是sort排序,Math.random()产生0<= x < 1之间的数,会导致x-0.05大于或者小于0
+	return array.sort(() => Math.random() - 0.5)
+}
+
+// padStart 的 polyfill,因为某些机型或情况,还无法支持es7的padStart,比如电脑版的微信小程序
+// 所以这里做一个兼容polyfill的兼容处理
+if (!String.prototype.padStart) {
+	// 为了方便表示这里 fillString 用了ES6 的默认参数,不影响理解
+	String.prototype.padStart = function(maxLength, fillString = ' ') {
+		if (Object.prototype.toString.call(fillString) !== '[object String]') {
+			throw new TypeError(
+				'fillString must be String'
+			)
+		}
+		const str = this
+		// 返回 String(str) 这里是为了使返回的值是字符串字面量,在控制台中更符合直觉
+		if (str.length >= maxLength) return String(str)
+
+		const fillLength = maxLength - str.length
+		let times = Math.ceil(fillLength / fillString.length)
+		while (times >>= 1) {
+			fillString += fillString
+			if (times === 1) {
+				fillString += fillString
+			}
+		}
+		return fillString.slice(0, fillLength) + str
+	}
+}
+
+/**
+ * @description 格式化时间
+ * @param {String|Number} dateTime 需要格式化的时间戳
+ * @param {String} fmt 格式化规则 yyyy:mm:dd|yyyy:mm|yyyy年mm月dd日|yyyy年mm月dd日 hh时MM分等,可自定义组合 默认yyyy-mm-dd
+ * @returns {string} 返回格式化后的字符串
+ */
+function timeFormat(dateTime = null, formatStr = 'yyyy-mm-dd') {
+	let date
+	// 若传入时间为假值,则取当前时间
+	if (!dateTime) {
+		date = new Date()
+	}
+	// 若为unix秒时间戳,则转为毫秒时间戳(逻辑有点奇怪,但不敢改,以保证历史兼容)
+	else if (/^\d{10}$/.test(dateTime?.toString().trim())) {
+		date = new Date(dateTime * 1000)
+	}
+	// 若用户传入字符串格式时间戳,new Date无法解析,需做兼容
+	else if (typeof dateTime === 'string' && /^\d+$/.test(dateTime.trim())) {
+		date = new Date(Number(dateTime))
+	}
+	// 处理平台性差异,在Safari/Webkit中,new Date仅支持/作为分割符的字符串时间
+	// 处理 '2022-07-10 01:02:03',跳过 '2022-07-10T01:02:03'
+	else if (typeof dateTime === 'string' && dateTime.includes('-') && !dateTime.includes('T')) {
+		date = new Date(dateTime.replace(/-/g, '/'))
+	}
+	// 其他都认为符合 RFC 2822 规范
+	else {
+		date = new Date(dateTime)
+	}
+
+	const timeSource = {
+		'y': date.getFullYear().toString(), // 年
+		'm': (date.getMonth() + 1).toString().padStart(2, '0'), // 月
+		'd': date.getDate().toString().padStart(2, '0'), // 日
+		'h': date.getHours().toString().padStart(2, '0'), // 时
+		'M': date.getMinutes().toString().padStart(2, '0'), // 分
+		's': date.getSeconds().toString().padStart(2, '0') // 秒
+		// 有其他格式化字符需求可以继续添加,必须转化成字符串
+	}
+
+	for (const key in timeSource) {
+		const [ret] = new RegExp(`${key}+`).exec(formatStr) || []
+		if (ret) {
+			// 年可能只需展示两位
+			const beginIndex = key === 'y' && ret.length === 2 ? 2 : 0
+			formatStr = formatStr.replace(ret, timeSource[key].slice(beginIndex))
+		}
+	}
+
+	return formatStr
+}
+
+/**
+ * @description 时间戳转为多久之前
+ * @param {String|Number} timestamp 时间戳
+ * @param {String|Boolean} format
+ * 格式化规则如果为时间格式字符串,超出一定时间范围,返回固定的时间格式;
+ * 如果为布尔值false,无论什么时间,都返回多久以前的格式
+ * @returns {string} 转化后的内容
+ */
+function timeFrom(timestamp = null, format = 'yyyy-mm-dd') {
+	if (timestamp == null) timestamp = Number(new Date())
+	timestamp = parseInt(timestamp)
+	// 判断用户输入的时间戳是秒还是毫秒,一般前端js获取的时间戳是毫秒(13位),后端传过来的为秒(10位)
+	if (timestamp.toString().length == 10) timestamp *= 1000
+	let timer = (new Date()).getTime() - timestamp
+	timer = parseInt(timer / 1000)
+	// 如果小于5分钟,则返回"刚刚",其他以此类推
+	let tips = ''
+	switch (true) {
+		case timer < 300:
+			tips = '刚刚'
+			break
+		case timer >= 300 && timer < 3600:
+			tips = `${parseInt(timer / 60)}分钟前`
+			break
+		case timer >= 3600 && timer < 86400:
+			tips = `${parseInt(timer / 3600)}小时前`
+			break
+		case timer >= 86400 && timer < 2592000:
+			tips = `${parseInt(timer / 86400)}天前`
+			break
+		default:
+			// 如果format为false,则无论什么时间戳,都显示xx之前
+			if (format === false) {
+				if (timer >= 2592000 && timer < 365 * 86400) {
+					tips = `${parseInt(timer / (86400 * 30))}个月前`
+				} else {
+					tips = `${parseInt(timer / (86400 * 365))}年前`
+				}
+			} else {
+				tips = timeFormat(timestamp, format)
+			}
+	}
+	return tips
+}
+
+/**
+ * @description 去除空格
+ * @param String str 需要去除空格的字符串
+ * @param String pos both(左右)|left|right|all 默认both
+ */
+function trim(str, pos = 'both') {
+	str = String(str)
+	if (pos == 'both') {
+		return str.replace(/^\s+|\s+$/g, '')
+	}
+	if (pos == 'left') {
+		return str.replace(/^\s*/, '')
+	}
+	if (pos == 'right') {
+		return str.replace(/(\s*$)/g, '')
+	}
+	if (pos == 'all') {
+		return str.replace(/\s+/g, '')
+	}
+	return str
+}
+
+/**
+ * @description 对象转url参数
+ * @param {object} data,对象
+ * @param {Boolean} isPrefix,是否自动加上"?"
+ * @param {string} arrayFormat 规则 indices|brackets|repeat|comma
+ */
+function queryParams(data = {}, isPrefix = true, arrayFormat = 'brackets') {
+	const prefix = isPrefix ? '?' : ''
+	const _result = []
+	if (['indices', 'brackets', 'repeat', 'comma'].indexOf(arrayFormat) == -1) arrayFormat = 'brackets'
+	for (const key in data) {
+		const value = data[key]
+		// 去掉为空的参数
+		if (['', undefined, null].indexOf(value) >= 0) {
+			continue
+		}
+		// 如果值为数组,另行处理
+		if (value.constructor === Array) {
+			// e.g. {ids: [1, 2, 3]}
+			switch (arrayFormat) {
+				case 'indices':
+					// 结果: ids[0]=1&ids[1]=2&ids[2]=3
+					for (let i = 0; i < value.length; i++) {
+						_result.push(`${key}[${i}]=${value[i]}`)
+					}
+					break
+				case 'brackets':
+					// 结果: ids[]=1&ids[]=2&ids[]=3
+					value.forEach((_value) => {
+						_result.push(`${key}[]=${_value}`)
+					})
+					break
+				case 'repeat':
+					// 结果: ids=1&ids=2&ids=3
+					value.forEach((_value) => {
+						_result.push(`${key}=${_value}`)
+					})
+					break
+				case 'comma':
+					// 结果: ids=1,2,3
+					let commaStr = ''
+					value.forEach((_value) => {
+						commaStr += (commaStr ? ',' : '') + _value
+					})
+					_result.push(`${key}=${commaStr}`)
+					break
+				default:
+					value.forEach((_value) => {
+						_result.push(`${key}[]=${_value}`)
+					})
+			}
+		} else {
+			_result.push(`${key}=${value}`)
+		}
+	}
+	return _result.length ? prefix + _result.join('&') : ''
+}
+
+/**
+ * 显示消息提示框
+ * @param {String} title 提示的内容,长度与 icon 取值有关。
+ * @param {Number} duration 提示的延迟时间,单位毫秒,默认:2000
+ */
+function toast(title, duration = 2000) {
+	uni.showToast({
+		title: String(title),
+		icon: 'none',
+		duration
+	})
+}
+
+/**
+ * @description 根据主题type值,获取对应的图标
+ * @param {String} type 主题名称,primary|info|error|warning|success
+ * @param {boolean} fill 是否使用fill填充实体的图标
+ */
+function type2icon(type = 'success', fill = false) {
+	// 如果非预置值,默认为success
+	if (['primary', 'info', 'error', 'warning', 'success'].indexOf(type) == -1) type = 'success'
+	let iconName = ''
+	// 目前(2019-12-12),info和primary使用同一个图标
+	switch (type) {
+		case 'primary':
+			iconName = 'info-circle'
+			break
+		case 'info':
+			iconName = 'info-circle'
+			break
+		case 'error':
+			iconName = 'close-circle'
+			break
+		case 'warning':
+			iconName = 'error-circle'
+			break
+		case 'success':
+			iconName = 'checkmark-circle'
+			break
+		default:
+			iconName = 'checkmark-circle'
+	}
+	// 是否是实体类型,加上-fill,在icon组件库中,实体的类名是后面加-fill的
+	if (fill) iconName += '-fill'
+	return iconName
+}
+
+/**
+ * @description 数字格式化
+ * @param {number|string} number 要格式化的数字
+ * @param {number} decimals 保留几位小数
+ * @param {string} decimalPoint 小数点符号
+ * @param {string} thousandsSeparator 千分位符号
+ * @returns {string} 格式化后的数字
+ */
+function priceFormat(number, decimals = 0, decimalPoint = '.', thousandsSeparator = ',') {
+	number = (`${number}`).replace(/[^0-9+-Ee.]/g, '')
+	const n = !isFinite(+number) ? 0 : +number
+	const prec = !isFinite(+decimals) ? 0 : Math.abs(decimals)
+	const sep = (typeof thousandsSeparator === 'undefined') ? ',' : thousandsSeparator
+	const dec = (typeof decimalPoint === 'undefined') ? '.' : decimalPoint
+	let s = ''
+
+	s = (prec ? round(n, prec) + '' : `${Math.round(n)}`).split('.')
+	const re = /(-?\d+)(\d{3})/
+	while (re.test(s[0])) {
+		s[0] = s[0].replace(re, `$1${sep}$2`)
+	}
+
+	if ((s[1] || '').length < prec) {
+		s[1] = s[1] || ''
+		s[1] += new Array(prec - s[1].length + 1).join('0')
+	}
+	return s.join(dec)
+}
+
+/**
+ * @description 获取duration值
+ * 如果带有ms或者s直接返回,如果大于一定值,认为是ms单位,小于一定值,认为是s单位
+ * 比如以30位阈值,那么300大于30,可以理解为用户想要的是300ms,而不是想花300s去执行一个动画
+ * @param {String|number} value 比如: "1s"|"100ms"|1|100
+ * @param {boolean} unit  提示: 如果是false 默认返回number
+ * @return {string|number}
+ */
+function getDuration(value, unit = true) {
+	const valueNum = parseInt(value)
+	if (unit) {
+		if (/s$/.test(value)) return value
+		return value > 30 ? `${value}ms` : `${value}s`
+	}
+	if (/ms$/.test(value)) return valueNum
+	if (/s$/.test(value)) return valueNum > 30 ? valueNum : valueNum * 1000
+	return valueNum
+}
+
+/**
+ * @description 日期的月或日补零操作
+ * @param {String} value 需要补零的值
+ */
+function padZero(value) {
+	return `00${value}`.slice(-2)
+}
+
+/**
+ * @description 在uv-form的子组件内容发生变化,或者失去焦点时,尝试通知uv-form执行校验方法
+ * @param {*} instance
+ * @param {*} event
+ */
+function formValidate(instance, event) {
+	const formItem = $parent.call(instance, 'uv-form-item')
+	const form = $parent.call(instance, 'uv-form')
+	// 如果发生变化的input或者textarea等,其父组件中有uv-form-item或者uv-form等,就执行form的validate方法
+	// 同时将form-item的pros传递给form,让其进行精确对象验证
+	if (formItem && form) {
+		form.validateField(formItem.prop, () => {}, event)
+	}
+}
+
+/**
+ * @description 获取某个对象下的属性,用于通过类似'a.b.c'的形式去获取一个对象的的属性的形式
+ * @param {object} obj 对象
+ * @param {string} key 需要获取的属性字段
+ * @returns {*}
+ */
+function getProperty(obj, key) {
+	if (!obj) {
+		return
+	}
+	if (typeof key !== 'string' || key === '') {
+		return ''
+	}
+	if (key.indexOf('.') !== -1) {
+		const keys = key.split('.')
+		let firstObj = obj[keys[0]] || {}
+
+		for (let i = 1; i < keys.length; i++) {
+			if (firstObj) {
+				firstObj = firstObj[keys[i]]
+			}
+		}
+		return firstObj
+	}
+	return obj[key]
+}
+
+/**
+ * @description 设置对象的属性值,如果'a.b.c'的形式进行设置
+ * @param {object} obj 对象
+ * @param {string} key 需要设置的属性
+ * @param {string} value 设置的值
+ */
+function setProperty(obj, key, value) {
+	if (!obj) {
+		return
+	}
+	// 递归赋值
+	const inFn = function(_obj, keys, v) {
+		// 最后一个属性key
+		if (keys.length === 1) {
+			_obj[keys[0]] = v
+			return
+		}
+		// 0~length-1个key
+		while (keys.length > 1) {
+			const k = keys[0]
+			if (!_obj[k] || (typeof _obj[k] !== 'object')) {
+				_obj[k] = {}
+			}
+			const key = keys.shift()
+			// 自调用判断是否存在属性,不存在则自动创建对象
+			inFn(_obj[k], keys, v)
+		}
+	}
+
+	if (typeof key !== 'string' || key === '') {
+
+	} else if (key.indexOf('.') !== -1) { // 支持多层级赋值操作
+		const keys = key.split('.')
+		inFn(obj, keys, value)
+	} else {
+		obj[key] = value
+	}
+}
+
+/**
+ * @description 获取当前页面路径
+ */
+function page() {
+	const pages = getCurrentPages();
+	const route = pages[pages.length - 1]?.route;
+	// 某些特殊情况下(比如页面进行redirectTo时的一些时机),pages可能为空数组
+	return `/${route ? route : ''}`
+}
+
+/**
+ * @description 获取当前路由栈实例数组
+ */
+function pages() {
+	const pages = getCurrentPages()
+	return pages
+}
+
+/**
+ * 获取页面历史栈指定层实例
+ * @param back {number} [0] - 0或者负数,表示获取历史栈的哪一层,0表示获取当前页面实例,-1 表示获取上一个页面实例。默认0。
+ */
+function getHistoryPage(back = 0) {
+	const pages = getCurrentPages()
+	const len = pages.length
+	return pages[len - 1 + back]
+}
+
+
+
+/**
+ * @description 修改uvui内置属性值
+ * @param {object} props 修改内置props属性
+ * @param {object} config 修改内置config属性
+ * @param {object} color 修改内置color属性
+ * @param {object} zIndex 修改内置zIndex属性
+ */
+function setConfig({
+	props = {},
+	config = {},
+	color = {},
+	zIndex = {}
+}) {
+	const {
+		deepMerge,
+	} = uni.$uv
+	uni.$uv.config = deepMerge(uni.$uv.config, config)
+	uni.$uv.props = deepMerge(uni.$uv.props, props)
+	uni.$uv.color = deepMerge(uni.$uv.color, color)
+	uni.$uv.zIndex = deepMerge(uni.$uv.zIndex, zIndex)
+}
+
+export {
+	range,
+	getPx,
+	sleep,
+	os,
+	sys,
+	random,
+	guid,
+	$parent,
+	addStyle,
+	addUnit,
+	deepClone,
+	deepMerge,
+	error,
+	randomArray,
+	timeFormat,
+	timeFrom,
+	trim,
+	queryParams,
+	toast,
+	type2icon,
+	priceFormat,
+	getDuration,
+	padZero,
+	formValidate,
+	getProperty,
+	setProperty,
+	page,
+	pages,
+	getHistoryPage,
+	setConfig
+}

+ 75 - 0
uni_modules/uv-ui-tools/libs/function/platform.js

@@ -0,0 +1,75 @@
+/**
+ * 注意:
+ * 此部分内容,在vue-cli模式下,需要在vue.config.js加入如下内容才有效:
+ * module.exports = {
+ *     transpileDependencies: ['uview-v2']
+ * }
+ */
+
+let platform = 'none'
+
+// #ifdef VUE3
+platform = 'vue3'
+// #endif
+
+// #ifdef VUE2
+platform = 'vue2'
+// #endif
+
+// #ifdef APP-PLUS
+platform = 'plus'
+// #endif
+
+// #ifdef APP-NVUE
+platform = 'nvue'
+// #endif
+
+// #ifdef H5
+platform = 'h5'
+// #endif
+
+// #ifdef MP-WEIXIN
+platform = 'weixin'
+// #endif
+
+// #ifdef MP-ALIPAY
+platform = 'alipay'
+// #endif
+
+// #ifdef MP-BAIDU
+platform = 'baidu'
+// #endif
+
+// #ifdef MP-TOUTIAO
+platform = 'toutiao'
+// #endif
+
+// #ifdef MP-QQ
+platform = 'qq'
+// #endif
+
+// #ifdef MP-KUAISHOU
+platform = 'kuaishou'
+// #endif
+
+// #ifdef MP-360
+platform = '360'
+// #endif
+
+// #ifdef MP
+platform = 'mp'
+// #endif
+
+// #ifdef QUICKAPP-WEBVIEW
+platform = 'quickapp-webview'
+// #endif
+
+// #ifdef QUICKAPP-WEBVIEW-HUAWEI
+platform = 'quickapp-webview-huawei'
+// #endif
+
+// #ifdef QUICKAPP-WEBVIEW-UNION
+platform = 'quckapp-webview-union'
+// #endif
+
+export default platform

+ 287 - 0
uni_modules/uv-ui-tools/libs/function/test.js

@@ -0,0 +1,287 @@
+/**
+ * 验证电子邮箱格式
+ */
+function email(value) {
+    return /^\w+((-\w+)|(\.\w+))*\@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z0-9]+$/.test(value)
+}
+
+/**
+ * 验证手机格式
+ */
+function mobile(value) {
+    return /^1([3589]\d|4[5-9]|6[1-2,4-7]|7[0-8])\d{8}$/.test(value)
+}
+
+/**
+ * 验证URL格式
+ */
+function url(value) {
+    return /^((https|http|ftp|rtsp|mms):\/\/)(([0-9a-zA-Z_!~*'().&=+$%-]+: )?[0-9a-zA-Z_!~*'().&=+$%-]+@)?(([0-9]{1,3}.){3}[0-9]{1,3}|([0-9a-zA-Z_!~*'()-]+.)*([0-9a-zA-Z][0-9a-zA-Z-]{0,61})?[0-9a-zA-Z].[a-zA-Z]{2,6})(:[0-9]{1,4})?((\/?)|(\/[0-9a-zA-Z_!~*'().;?:@&=+$,%#-]+)+\/?)$/
+        .test(value)
+}
+
+/**
+ * 验证日期格式
+ */
+function date(value) {
+    if (!value) return false
+    // 判断是否数值或者字符串数值(意味着为时间戳),转为数值,否则new Date无法识别字符串时间戳
+    if (number(value)) value = +value
+    return !/Invalid|NaN/.test(new Date(value).toString())
+}
+
+/**
+ * 验证ISO类型的日期格式
+ */
+function dateISO(value) {
+    return /^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])$/.test(value)
+}
+
+/**
+ * 验证十进制数字
+ */
+function number(value) {
+    return /^[\+-]?(\d+\.?\d*|\.\d+|\d\.\d+e\+\d+)$/.test(value)
+}
+
+/**
+ * 验证字符串
+ */
+function string(value) {
+    return typeof value === 'string'
+}
+
+/**
+ * 验证整数
+ */
+function digits(value) {
+    return /^\d+$/.test(value)
+}
+
+/**
+ * 验证身份证号码
+ */
+function idCard(value) {
+    return /^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$/.test(
+        value
+    )
+}
+
+/**
+ * 是否车牌号
+ */
+function carNo(value) {
+    // 新能源车牌
+    const xreg = /^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}(([0-9]{5}[DF]$)|([DF][A-HJ-NP-Z0-9][0-9]{4}$))/
+    // 旧车牌
+    const creg = /^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}[A-HJ-NP-Z0-9]{4}[A-HJ-NP-Z0-9挂学警港澳]{1}$/
+    if (value.length === 7) {
+        return creg.test(value)
+    } if (value.length === 8) {
+        return xreg.test(value)
+    }
+    return false
+}
+
+/**
+ * 金额,只允许2位小数
+ */
+function amount(value) {
+    // 金额,只允许保留两位小数
+    return /^[1-9]\d*(,\d{3})*(\.\d{1,2})?$|^0\.\d{1,2}$/.test(value)
+}
+
+/**
+ * 中文
+ */
+function chinese(value) {
+    const reg = /^[\u4e00-\u9fa5]+$/gi
+    return reg.test(value)
+}
+
+/**
+ * 只能输入字母
+ */
+function letter(value) {
+    return /^[a-zA-Z]*$/.test(value)
+}
+
+/**
+ * 只能是字母或者数字
+ */
+function enOrNum(value) {
+    // 英文或者数字
+    const reg = /^[0-9a-zA-Z]*$/g
+    return reg.test(value)
+}
+
+/**
+ * 验证是否包含某个值
+ */
+function contains(value, param) {
+    return value.indexOf(param) >= 0
+}
+
+/**
+ * 验证一个值范围[min, max]
+ */
+function range(value, param) {
+    return value >= param[0] && value <= param[1]
+}
+
+/**
+ * 验证一个长度范围[min, max]
+ */
+function rangeLength(value, param) {
+    return value.length >= param[0] && value.length <= param[1]
+}
+
+/**
+ * 是否固定电话
+ */
+function landline(value) {
+    const reg = /^\d{3,4}-\d{7,8}(-\d{3,4})?$/
+    return reg.test(value)
+}
+
+/**
+ * 判断是否为空
+ */
+function empty(value) {
+    switch (typeof value) {
+    case 'undefined':
+        return true
+    case 'string':
+        if (value.replace(/(^[ \t\n\r]*)|([ \t\n\r]*$)/g, '').length == 0) return true
+        break
+    case 'boolean':
+        if (!value) return true
+        break
+    case 'number':
+        if (value === 0 || isNaN(value)) return true
+        break
+    case 'object':
+        if (value === null || value.length === 0) return true
+        for (const i in value) {
+            return false
+        }
+        return true
+    }
+    return false
+}
+
+/**
+ * 是否json字符串
+ */
+function jsonString(value) {
+    if (typeof value === 'string') {
+        try {
+            const obj = JSON.parse(value)
+            if (typeof obj === 'object' && obj) {
+                return true
+            }
+            return false
+        } catch (e) {
+            return false
+        }
+    }
+    return false
+}
+
+/**
+ * 是否数组
+ */
+function array(value) {
+    if (typeof Array.isArray === 'function') {
+        return Array.isArray(value)
+    }
+    return Object.prototype.toString.call(value) === '[object Array]'
+}
+
+/**
+ * 是否对象
+ */
+function object(value) {
+    return Object.prototype.toString.call(value) === '[object Object]'
+}
+
+/**
+ * 是否短信验证码
+ */
+function code(value, len = 6) {
+    return new RegExp(`^\\d{${len}}$`).test(value)
+}
+
+/**
+ * 是否函数方法
+ * @param {Object} value
+ */
+function func(value) {
+    return typeof value === 'function'
+}
+
+/**
+ * 是否promise对象
+ * @param {Object} value
+ */
+function promise(value) {
+    return object(value) && func(value.then) && func(value.catch)
+}
+
+/** 是否图片格式
+ * @param {Object} value
+ */
+function image(value) {
+    const newValue = value.split('?')[0]
+    const IMAGE_REGEXP = /\.(jpeg|jpg|gif|png|svg|webp|jfif|bmp|dpg)/i
+    return IMAGE_REGEXP.test(newValue)
+}
+
+/**
+ * 是否视频格式
+ * @param {Object} value
+ */
+function video(value) {
+    const VIDEO_REGEXP = /\.(mp4|mpg|mpeg|dat|asf|avi|rm|rmvb|mov|wmv|flv|mkv|m3u8)/i
+    return VIDEO_REGEXP.test(value)
+}
+
+/**
+ * 是否为正则对象
+ * @param {Object}
+ * @return {Boolean}
+ */
+function regExp(o) {
+    return o && Object.prototype.toString.call(o) === '[object RegExp]'
+}
+
+export {
+    email,
+    mobile,
+    url,
+    date,
+    dateISO,
+    number,
+    digits,
+    idCard,
+    carNo,
+    amount,
+    chinese,
+    letter,
+    enOrNum,
+    contains,
+    range,
+    rangeLength,
+    empty,
+    jsonString,
+    landline,
+    object,
+    array,
+    code,
+    func,
+    promise,
+    video,
+    image,
+    regExp,
+    string
+}

+ 30 - 0
uni_modules/uv-ui-tools/libs/function/throttle.js

@@ -0,0 +1,30 @@
+let timer; let
+    flag
+/**
+ * 节流原理:在一定时间内,只能触发一次
+ *
+ * @param {Function} func 要执行的回调函数
+ * @param {Number} wait 延时的时间
+ * @param {Boolean} immediate 是否立即执行
+ * @return null
+ */
+function throttle(func, wait = 500, immediate = true) {
+    if (immediate) {
+        if (!flag) {
+            flag = true
+            // 如果是立即执行,则在wait毫秒内开始时执行
+            typeof func === 'function' && func()
+            timer = setTimeout(() => {
+                flag = false
+            }, wait)
+        }
+    } else if (!flag) {
+        flag = true
+        // 如果是非立即执行,则在wait毫秒内的结束处执行
+        timer = setTimeout(() => {
+            flag = false
+            typeof func === 'function' && func()
+        }, wait)
+    }
+}
+export default throttle

+ 132 - 0
uni_modules/uv-ui-tools/libs/luch-request/adapters/index.js

@@ -0,0 +1,132 @@
+import buildURL from '../helpers/buildURL'
+import buildFullPath from '../core/buildFullPath'
+import settle from '../core/settle'
+import {isUndefined} from "../utils"
+
+/**
+ * 返回可选值存在的配置
+ * @param {Array} keys - 可选值数组
+ * @param {Object} config2 - 配置
+ * @return {{}} - 存在的配置项
+ */
+const mergeKeys = (keys, config2) => {
+  let config = {}
+  keys.forEach(prop => {
+    if (!isUndefined(config2[prop])) {
+      config[prop] = config2[prop]
+    }
+  })
+  return config
+}
+export default (config) => {
+  return new Promise((resolve, reject) => {
+    let fullPath = buildURL(buildFullPath(config.baseURL, config.url), config.params, config.paramsSerializer)
+    const _config = {
+      url: fullPath,
+      header: config.header,
+      complete: (response) => {
+        config.fullPath = fullPath
+        response.config = config
+        response.rawData = response.data
+        try {
+          let jsonParseHandle = false
+          const forcedJSONParsingType = typeof config.forcedJSONParsing
+          if (forcedJSONParsingType === 'boolean') {
+            jsonParseHandle = config.forcedJSONParsing
+          } else if (forcedJSONParsingType === 'object') {
+            const includesMethod = config.forcedJSONParsing.include || []
+            jsonParseHandle = includesMethod.includes(config.method)
+          }
+
+          // 对可能字符串不是json 的情况容错
+          if (jsonParseHandle && typeof response.data === 'string') {
+            response.data = JSON.parse(response.data)
+          }
+          // eslint-disable-next-line no-empty
+        } catch (e) {
+        }
+        settle(resolve, reject, response)
+      }
+    }
+    let requestTask
+    if (config.method === 'UPLOAD') {
+      delete _config.header['content-type']
+      delete _config.header['Content-Type']
+      let otherConfig = {
+        // #ifdef MP-ALIPAY
+        fileType: config.fileType,
+        // #endif
+        filePath: config.filePath,
+        name: config.name
+      }
+      const optionalKeys = [
+        // #ifdef APP-PLUS || H5
+        'files',
+        // #endif
+        // #ifdef H5
+        'file',
+        // #endif
+        // #ifdef H5 || APP-PLUS || MP-WEIXIN || MP-ALIPAY || MP-TOUTIAO || MP-KUAISHOU
+        'timeout',
+        // #endif
+        'formData'
+      ]
+      requestTask = uni.uploadFile({..._config, ...otherConfig, ...mergeKeys(optionalKeys, config)})
+    } else if (config.method === 'DOWNLOAD') {
+      const optionalKeys = [
+        // #ifdef H5 || APP-PLUS || MP-WEIXIN || MP-ALIPAY || MP-TOUTIAO || MP-KUAISHOU
+        'timeout',
+        // #endif
+        // #ifdef MP
+        'filePath',
+        // #endif
+      ]
+      requestTask = uni.downloadFile({..._config, ...mergeKeys(optionalKeys, config)})
+    } else {
+      const optionalKeys = [
+        'data',
+        'method',
+        // #ifdef H5 || APP-PLUS || MP-ALIPAY || MP-WEIXIN
+        'timeout',
+        // #endif
+        'dataType',
+        // #ifndef MP-ALIPAY
+        'responseType',
+        // #endif
+        // #ifdef APP-PLUS
+        'sslVerify',
+        // #endif
+        // #ifdef H5
+        'withCredentials',
+        // #endif
+        // #ifdef APP-PLUS
+        'firstIpv4',
+        // #endif
+        // #ifdef MP-WEIXIN
+        'enableHttp2',
+        'enableQuic',
+        // #endif
+        // #ifdef MP-TOUTIAO || MP-WEIXIN
+        'enableCache',
+        // #endif
+        // #ifdef MP-WEIXIN
+        'enableHttpDNS',
+        'httpDNSServiceId',
+        'enableChunked',
+        'forceCellularNetwork',
+        // #endif
+        // #ifdef MP-ALIPAY
+        'enableCookie',
+        // #endif
+        // #ifdef MP-BAIDU
+        'cloudCache',
+        'defer'
+        // #endif
+      ]
+      requestTask = uni.request({..._config, ...mergeKeys(optionalKeys, config)})
+    }
+    if (config.getTask) {
+      config.getTask(requestTask, config)
+    }
+  })
+}

+ 51 - 0
uni_modules/uv-ui-tools/libs/luch-request/core/InterceptorManager.js

@@ -0,0 +1,51 @@
+'use strict'
+
+
+function InterceptorManager() {
+  this.handlers = []
+}
+
+/**
+ * Add a new interceptor to the stack
+ *
+ * @param {Function} fulfilled The function to handle `then` for a `Promise`
+ * @param {Function} rejected The function to handle `reject` for a `Promise`
+ *
+ * @return {Number} An ID used to remove interceptor later
+ */
+InterceptorManager.prototype.use = function use(fulfilled, rejected) {
+  this.handlers.push({
+    fulfilled: fulfilled,
+    rejected: rejected
+  })
+  return this.handlers.length - 1
+}
+
+/**
+ * Remove an interceptor from the stack
+ *
+ * @param {Number} id The ID that was returned by `use`
+ */
+InterceptorManager.prototype.eject = function eject(id) {
+  if (this.handlers[id]) {
+    this.handlers[id] = null
+  }
+}
+
+/**
+ * Iterate over all the registered interceptors
+ *
+ * This method is particularly useful for skipping over any
+ * interceptors that may have become `null` calling `eject`.
+ *
+ * @param {Function} fn The function to call for each interceptor
+ */
+InterceptorManager.prototype.forEach = function forEach(fn) {
+  this.handlers.forEach(h => {
+    if (h !== null) {
+      fn(h)
+    }
+  })
+}
+
+export default InterceptorManager

+ 201 - 0
uni_modules/uv-ui-tools/libs/luch-request/core/Request.js

@@ -0,0 +1,201 @@
+/**
+ * @Class Request
+ * @description luch-request http请求插件
+ * @Author lu-ch
+ * @Email webwork.s@qq.com
+ * 文档: https://www.quanzhan.co/luch-request/
+ * github: https://github.com/lei-mu/luch-request
+ * DCloud: http://ext.dcloud.net.cn/plugin?id=392
+ */
+
+
+import dispatchRequest from './dispatchRequest'
+import InterceptorManager from './InterceptorManager'
+import mergeConfig from './mergeConfig'
+import defaults from './defaults'
+import { isPlainObject } from '../utils'
+import clone from '../utils/clone'
+
+export default class Request {
+  /**
+   * @param {Object} arg - 全局配置
+   * @param {String} arg.baseURL - 全局根路径
+   * @param {Object} arg.header - 全局header
+   * @param {String} arg.method = [GET|POST|PUT|DELETE|CONNECT|HEAD|OPTIONS|TRACE] - 全局默认请求方式
+   * @param {String} arg.dataType = [json] - 全局默认的dataType
+   * @param {String} arg.responseType = [text|arraybuffer] - 全局默认的responseType。支付宝小程序不支持
+   * @param {Object} arg.custom - 全局默认的自定义参数
+   * @param {Number} arg.timeout - 全局默认的超时时间,单位 ms。默认60000。H5(HBuilderX 2.9.9+)、APP(HBuilderX 2.9.9+)、微信小程序(2.10.0)、支付宝小程序
+   * @param {Boolean} arg.sslVerify - 全局默认的是否验证 ssl 证书。默认true.仅App安卓端支持(HBuilderX 2.3.3+)
+   * @param {Boolean} arg.withCredentials - 全局默认的跨域请求时是否携带凭证(cookies)。默认false。仅H5支持(HBuilderX 2.6.15+)
+   * @param {Boolean} arg.firstIpv4 - 全DNS解析时优先使用ipv4。默认false。仅 App-Android 支持 (HBuilderX 2.8.0+)
+   * @param {Function(statusCode):Boolean} arg.validateStatus - 全局默认的自定义验证器。默认statusCode >= 200 && statusCode < 300
+   */
+  constructor(arg = {}) {
+    if (!isPlainObject(arg)) {
+      arg = {}
+      console.warn('设置全局参数必须接收一个Object')
+    }
+    this.config = clone({...defaults, ...arg})
+    this.interceptors = {
+      request: new InterceptorManager(),
+      response: new InterceptorManager()
+    }
+  }
+
+  /**
+   * @Function
+   * @param {Request~setConfigCallback} f - 设置全局默认配置
+   */
+  setConfig(f) {
+    this.config = f(this.config)
+  }
+
+  middleware(config) {
+    config = mergeConfig(this.config, config)
+    let chain = [dispatchRequest, undefined]
+    let promise = Promise.resolve(config)
+
+    this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
+      chain.unshift(interceptor.fulfilled, interceptor.rejected)
+    })
+
+    this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
+      chain.push(interceptor.fulfilled, interceptor.rejected)
+    })
+
+    while (chain.length) {
+      promise = promise.then(chain.shift(), chain.shift())
+    }
+
+    return promise
+  }
+
+  /**
+   * @Function
+   * @param {Object} config - 请求配置项
+   * @prop {String} options.url - 请求路径
+   * @prop {Object} options.data - 请求参数
+   * @prop {Object} [options.responseType = config.responseType] [text|arraybuffer] - 响应的数据类型
+   * @prop {Object} [options.dataType = config.dataType] - 如果设为 json,会尝试对返回的数据做一次 JSON.parse
+   * @prop {Object} [options.header = config.header] - 请求header
+   * @prop {Object} [options.method = config.method] - 请求方法
+   * @returns {Promise<unknown>}
+   */
+  request(config = {}) {
+    return this.middleware(config)
+  }
+
+  get(url, options = {}) {
+    return this.middleware({
+      url,
+      method: 'GET',
+      ...options
+    })
+  }
+
+  post(url, data, options = {}) {
+    return this.middleware({
+      url,
+      data,
+      method: 'POST',
+      ...options
+    })
+  }
+
+  // #ifndef MP-ALIPAY || MP-KUAISHOU || MP-JD
+  put(url, data, options = {}) {
+    return this.middleware({
+      url,
+      data,
+      method: 'PUT',
+      ...options
+    })
+  }
+
+  // #endif
+
+  // #ifdef APP-PLUS || H5 || MP-WEIXIN || MP-BAIDU
+  delete(url, data, options = {}) {
+    return this.middleware({
+      url,
+      data,
+      method: 'DELETE',
+      ...options
+    })
+  }
+
+  // #endif
+
+  // #ifdef H5 || MP-WEIXIN
+  connect(url, data, options = {}) {
+    return this.middleware({
+      url,
+      data,
+      method: 'CONNECT',
+      ...options
+    })
+  }
+
+  // #endif
+
+  // #ifdef  H5 || MP-WEIXIN || MP-BAIDU
+  head(url, data, options = {}) {
+    return this.middleware({
+      url,
+      data,
+      method: 'HEAD',
+      ...options
+    })
+  }
+
+  // #endif
+
+  // #ifdef APP-PLUS || H5 || MP-WEIXIN || MP-BAIDU
+  options(url, data, options = {}) {
+    return this.middleware({
+      url,
+      data,
+      method: 'OPTIONS',
+      ...options
+    })
+  }
+
+  // #endif
+
+  // #ifdef H5 || MP-WEIXIN
+  trace(url, data, options = {}) {
+    return this.middleware({
+      url,
+      data,
+      method: 'TRACE',
+      ...options
+    })
+  }
+
+  // #endif
+
+  upload(url, config = {}) {
+    config.url = url
+    config.method = 'UPLOAD'
+    return this.middleware(config)
+  }
+
+  download(url, config = {}) {
+    config.url = url
+    config.method = 'DOWNLOAD'
+    return this.middleware(config)
+  }
+
+  get version () {
+    return '3.1.0'
+  }
+}
+
+
+/**
+ * setConfig回调
+ * @return {Object} - 返回操作后的config
+ * @callback Request~setConfigCallback
+ * @param {Object} config - 全局默认config
+ */

+ 20 - 0
uni_modules/uv-ui-tools/libs/luch-request/core/buildFullPath.js

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

+ 33 - 0
uni_modules/uv-ui-tools/libs/luch-request/core/defaults.js

@@ -0,0 +1,33 @@
+/**
+ * 默认的全局配置
+ */
+
+
+export default {
+  baseURL: '',
+  header: {},
+  method: 'GET',
+  dataType: 'json',
+  paramsSerializer: null,
+  // #ifndef MP-ALIPAY
+  responseType: 'text',
+  // #endif
+  custom: {},
+  // #ifdef H5 || APP-PLUS || MP-WEIXIN || MP-ALIPAY || MP-TOUTIAO || MP-KUAISHOU
+  timeout: 60000,
+  // #endif
+  // #ifdef APP-PLUS
+  sslVerify: true,
+  // #endif
+  // #ifdef H5
+  withCredentials: false,
+  // #endif
+  // #ifdef APP-PLUS
+  firstIpv4: false,
+  // #endif
+  validateStatus: function validateStatus(status) {
+    return status >= 200 && status < 300
+  },
+  // 是否尝试将响应数据json化
+  forcedJSONParsing: true
+}

+ 6 - 0
uni_modules/uv-ui-tools/libs/luch-request/core/dispatchRequest.js

@@ -0,0 +1,6 @@
+import adapter from '../adapters/index'
+
+
+export default (config) => {
+  return adapter(config)
+}

+ 126 - 0
uni_modules/uv-ui-tools/libs/luch-request/core/mergeConfig.js

@@ -0,0 +1,126 @@
+import {deepMerge, isUndefined} from '../utils'
+
+/**
+ * 合并局部配置优先的配置,如果局部有该配置项则用局部,如果全局有该配置项则用全局
+ * @param {Array} keys - 配置项
+ * @param {Object} globalsConfig - 当前的全局配置
+ * @param {Object} config2 - 局部配置
+ * @return {{}}
+ */
+const mergeKeys = (keys, globalsConfig, config2) => {
+  let config = {}
+  keys.forEach(prop => {
+    if (!isUndefined(config2[prop])) {
+      config[prop] = config2[prop]
+    } else if (!isUndefined(globalsConfig[prop])) {
+      config[prop] = globalsConfig[prop]
+    }
+  })
+  return config
+}
+/**
+ *
+ * @param globalsConfig - 当前实例的全局配置
+ * @param config2 - 当前的局部配置
+ * @return - 合并后的配置
+ */
+export default (globalsConfig, config2 = {}) => {
+  const method = config2.method || globalsConfig.method || 'GET'
+  let config = {
+    baseURL: config2.baseURL || globalsConfig.baseURL || '',
+    method: method,
+    url: config2.url || '',
+    params: config2.params || {},
+    custom: {...(globalsConfig.custom || {}), ...(config2.custom || {})},
+    header: deepMerge(globalsConfig.header || {}, config2.header || {})
+  }
+  const defaultToConfig2Keys = ['getTask', 'validateStatus', 'paramsSerializer', 'forcedJSONParsing']
+  config = {...config, ...mergeKeys(defaultToConfig2Keys, globalsConfig, config2)}
+
+  // eslint-disable-next-line no-empty
+  if (method === 'DOWNLOAD') {
+    const downloadKeys = [
+      // #ifdef H5 || APP-PLUS || MP-WEIXIN || MP-ALIPAY || MP-TOUTIAO || MP-KUAISHOU
+      'timeout',
+      // #endif
+      // #ifdef MP
+      'filePath',
+      // #endif
+    ]
+    config = {...config, ...mergeKeys(downloadKeys, globalsConfig, config2)}
+  } else if (method === 'UPLOAD') {
+    delete config.header['content-type']
+    delete config.header['Content-Type']
+    const uploadKeys = [
+      // #ifdef APP-PLUS || H5
+      'files',
+      // #endif
+      // #ifdef MP-ALIPAY
+      'fileType',
+      // #endif
+      // #ifdef H5
+      'file',
+      // #endif
+      'filePath',
+      'name',
+      // #ifdef H5 || APP-PLUS || MP-WEIXIN || MP-ALIPAY || MP-TOUTIAO || MP-KUAISHOU
+      'timeout',
+      // #endif
+      'formData',
+    ]
+    uploadKeys.forEach(prop => {
+      if (!isUndefined(config2[prop])) {
+        config[prop] = config2[prop]
+      }
+    })
+    // #ifdef H5 || APP-PLUS || MP-WEIXIN || MP-ALIPAY || MP-TOUTIAO || MP-KUAISHOU
+    if (isUndefined(config.timeout) && !isUndefined(globalsConfig.timeout)) {
+      config['timeout'] = globalsConfig['timeout']
+    }
+    // #endif
+  } else {
+    const defaultsKeys = [
+      'data',
+      // #ifdef H5 || APP-PLUS || MP-ALIPAY || MP-WEIXIN
+      'timeout',
+      // #endif
+      'dataType',
+      // #ifndef MP-ALIPAY
+      'responseType',
+      // #endif
+      // #ifdef APP-PLUS
+      'sslVerify',
+      // #endif
+      // #ifdef H5
+      'withCredentials',
+      // #endif
+      // #ifdef APP-PLUS
+      'firstIpv4',
+      // #endif
+      // #ifdef MP-WEIXIN
+      'enableHttp2',
+      'enableQuic',
+      // #endif
+      // #ifdef MP-TOUTIAO || MP-WEIXIN
+      'enableCache',
+      // #endif
+      // #ifdef MP-WEIXIN
+      'enableHttpDNS',
+      'httpDNSServiceId',
+      'enableChunked',
+      'forceCellularNetwork',
+      // #endif
+      // #ifdef MP-ALIPAY
+      'enableCookie',
+      // #endif
+      // #ifdef MP-BAIDU
+      'cloudCache',
+      'defer'
+      // #endif
+
+    ]
+    config = {...config, ...mergeKeys(defaultsKeys, globalsConfig, config2)}
+  }
+
+  return config
+}

+ 16 - 0
uni_modules/uv-ui-tools/libs/luch-request/core/settle.js

@@ -0,0 +1,16 @@
+/**
+ * Resolve or reject a Promise based on response status.
+ *
+ * @param {Function} resolve A function that resolves the promise.
+ * @param {Function} reject A function that rejects the promise.
+ * @param {object} response The response.
+ */
+export default function settle(resolve, reject, response) {
+  const validateStatus = response.config.validateStatus
+  const status = response.statusCode
+  if (status && (!validateStatus || validateStatus(status))) {
+    resolve(response)
+  } else {
+    reject(response)
+  }
+}

+ 64 - 0
uni_modules/uv-ui-tools/libs/luch-request/helpers/buildURL.js

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

+ 14 - 0
uni_modules/uv-ui-tools/libs/luch-request/helpers/combineURLs.js

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

+ 14 - 0
uni_modules/uv-ui-tools/libs/luch-request/helpers/isAbsoluteURL.js

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

+ 197 - 0
uni_modules/uv-ui-tools/libs/luch-request/index.d.ts

@@ -0,0 +1,197 @@
+export type HttpTask = UniApp.RequestTask | UniApp.UploadTask | UniApp.DownloadTask;
+
+export type HttpRequestTask = UniApp.RequestTask;
+
+export type HttpUploadTask = UniApp.UploadTask;
+
+export type HttpDownloadTask = UniApp.DownloadTask;
+
+export type HttpMethod =
+    "GET"
+    | "POST"
+    | "PUT"
+    | "DELETE"
+    | "CONNECT"
+    | "HEAD"
+    | "OPTIONS"
+    | "TRACE"
+    | "UPLOAD"
+    | "DOWNLOAD";
+
+export type HttpRequestHeader = Record<string, string>;
+
+export type HttpParams = Record<string, any>;
+
+export type HttpData = Record<string, any>;
+
+export type HttpResponseType = 'arraybuffer' | 'text';
+
+export type HttpCustom = Record<string, any>;
+
+export type HttpFileType = 'image' | 'video' | 'audio';
+
+export type HttpFormData = Record<string, any>;
+
+export type HttpResponseHeader = Record<string, string> & {
+    "set-cookie"?: string[]
+};
+
+export interface HttpRequestConfig<T = HttpTask> {
+    /** @desc 请求服务器接口地址 */
+    url?: string;
+    /** @desc 请求方式,默认为 GET */
+    method?: HttpMethod;
+    /** @desc 请求基地址 */
+    baseURL?: string;
+    /** @desc 请求头信息,不能设置 Referer,App、H5 端会自动带上 cookie,且 H5 端不可手动修改 */
+    header?: HttpRequestHeader;
+    /** @desc 请求查询参数,自动拼接为查询字符串 */
+    params?: HttpParams;
+    /** @desc 请求体参数 */
+    data?: HttpData;
+    /** @desc 超时时间,单位 ms,默认为 60000,仅 H5 (HBuilderX 2.9.9+)、APP (HBuilderX 2.9.9+)、微信小程序 (2.10.0)、支付宝小程序支持 */
+    timeout?: number;
+    /** @desc 跨域请求时是否携带凭证 (cookies),默认为 false,仅 H5 (HBuilderX 2.6.15+) 支持 */
+    withCredentials?: boolean;
+    /** @desc 设置响应的数据类型,支付宝小程序不支持 */
+    responseType?: HttpResponseType;
+    /** @desc 全局自定义验证器 */
+    validateStatus?: ((statusCode: number) => boolean) | null;
+
+
+    /** params 参数自定义处理 */
+    paramsSerializer?: (params: AnyObject) => string | void;
+
+    /** @desc 默认为 json,如果设为 json,会尝试对返回的数据做一次 JSON.parse */
+    dataType?: string;
+    /** @desc DNS 解析时是否优先使用 ipv4,默认为 false,仅 App-Android (HBuilderX 2.8.0+) 支持 */
+    firstIpv4?: boolean;
+    /** @desc 是否验证 SSL 证书,默认为 true,仅 App-Android (HBuilderX 2.3.3+) 支持 */
+    sslVerify?: boolean;
+
+    /** @desc 开启 http2;微信小程序 */
+    enableHttp2?: boolean;
+
+    /** @desc 开启 quic;微信小程序 */
+    enableQuic?: boolean;
+    /** @desc 开启 cache;微信小程序、字节跳动小程序 2.31.0+ */
+    enableCache?: boolean;
+    /** @desc 开启 httpDNS;微信小程序 */
+    enableHttpDNS?: boolean;
+    /** @desc httpDNS 服务商;微信小程序 */
+    httpDNSServiceId?: string;
+    /** @desc 开启 transfer-encoding chunked;微信小程序 */
+    enableChunked?: boolean;
+    /** @desc wifi下使用移动网络发送请求;微信小程序 */
+    forceCellularNetwork?: boolean;
+    /** @desc 开启后可在headers中编辑cookie;支付宝小程序 10.2.33+ */
+    enableCookie?: boolean;
+    /** @desc 是否开启云加速;百度小程序 3.310.11+ */
+    cloudCache?: boolean | object;
+    /** @desc 控制当前请求是否延时至首屏内容渲染后发送;百度小程序 3.310.11+ */
+    defer?: boolean;
+
+    /** @desc 自定义参数 */
+    custom?: HttpCustom;
+
+    /** @desc 返回当前请求的 task 和 options,不要在这里修改 options */
+    getTask?: (task: T, options: HttpRequestConfig<T>) => void;
+
+    /** @desc 需要上传的文件列表,使用 files 时,filePath 和 name 不生效,仅支持 App、H5 (2.6.15+) */
+    files?: { name?: string; file?: File; uri: string; }[];
+    /** @desc 文件类型,仅支付宝小程序支持且为必填项 */
+    fileType?: HttpFileType;
+    /** @desc 要上传的文件对象,仅 H5 (2.6.15+) 支持 */
+    file?: File;
+    /** @desc 要上传文件资源的路径,使用 files 时,filePath 和 name 不生效 */
+    filePath?: string;
+    /** @desc 文件对应的 key,开发者在服务器端通过这个 key 可以获取到文件二进制内容,使用 files 时,filePath 和 name 不生效 */
+    name?: string;
+    /** @desc 请求中其他额外的 form data */
+    formData?: HttpFormData;
+}
+
+export interface HttpResponse<T = any, D = HttpTask> {
+    data: T;
+    statusCode: number;
+    header: HttpResponseHeader;
+    config: HttpRequestConfig<D>;
+    cookies: string[];
+    errMsg: string;
+    rawData: any;
+}
+
+export interface HttpUploadResponse<T = any, D = HttpTask> {
+    data: T;
+    statusCode: number;
+    config: HttpRequestConfig<D>;
+    errMsg: string;
+    rawData: any;
+}
+
+export interface HttpDownloadResponse extends HttpResponse {
+    tempFilePath: string;
+    apFilePath?: string;
+    filePath?: string;
+    fileContent?: string;
+}
+
+export interface HttpError<T = any, D = HttpTask> {
+    data?: T;
+    statusCode?: number;
+    header?: HttpResponseHeader;
+    config: HttpRequestConfig<D>;
+    cookies?: string[];
+    errMsg: string;
+}
+
+export interface HttpPromise<T = any> extends Promise<HttpResponse<T>> {
+}
+
+export interface HttpInterceptorManager<V, E = V> {
+    use(onFulfilled?: (value: V) => V | Promise<V>, onRejected?: (error: E) => T | Promise<E>): void;
+
+    eject(id: number): void;
+}
+
+export abstract class HttpRequestAbstract {
+    constructor(config?: HttpRequestConfig);
+
+    interceptors: {
+        request: HttpInterceptorManager<HttpRequestConfig>;
+        response: HttpInterceptorManager<HttpResponse, HttpError>;
+    }
+
+    request<T = any, R = HttpResponse<T>, D = HttpRequestTask>(config: HttpRequestConfig<D>): Promise<R>;
+
+    get<T = any, R = HttpResponse<T>, D = HttpRequestTask>(url: string, config?: HttpRequestConfig<D>): Promise<R>;
+
+    delete<T = any, R = HttpResponse<T>, D = HttpRequestTask>(url: string, data?: HttpData, config?: HttpRequestConfig<D>): Promise<R>;
+
+    head<T = any, R = HttpResponse<T>, D = HttpRequestTask>(url: string, data?: HttpData, config?: HttpRequestConfig<D>): Promise<R>;
+
+    options<T = any, R = HttpResponse<T>, D = HttpRequestTask>(url: string, data?: HttpData, config?: HttpRequestConfig<D>): Promise<R>;
+
+    post<T = any, R = HttpResponse<T>, D = HttpRequestTask>(url: string, data?: HttpData, config?: HttpRequestConfig<D>): Promise<R>;
+
+    put<T = any, R = HttpResponse<T>, D = HttpRequestTask>(url: string, data?: HttpData, config?: HttpRequestConfig<D>): Promise<R>;
+
+    config: HttpRequestConfig;
+
+    setConfig<D = HttpTask>(onSend: (config: HttpRequestConfig<D>) => HttpRequestConfig<D>): void;
+
+    connect<T = any, R = HttpResponse<T>, D = HttpRequestTask>(url: string, data?: HttpData, config?: HttpRequestConfig<D>): Promise<R>;
+
+    trace<T = any, R = HttpResponse<T>, D = HttpRequestTask>(url: string, data?: HttpData, config?: HttpRequestConfig<D>): Promise<R>;
+
+    upload<T = any, R = HttpUploadResponse<T>, D = HttpUploadTask>(url: string, config?: HttpRequestConfig<D>): Promise<R>;
+
+    download<T = any, R = HttpDownloadResponse<T>, D = HttpDownloadTask>(url: string, config?: HttpRequestConfig<D>): Promise<R>;
+
+    middleware<T = any, R = HttpResponse<T>, D = HttpTask>(config: HttpRequestConfig<D>): Promise<R>;
+}
+
+declare class HttpRequest extends HttpRequestAbstract {
+}
+
+export default HttpRequest;

+ 2 - 0
uni_modules/uv-ui-tools/libs/luch-request/index.js

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

+ 135 - 0
uni_modules/uv-ui-tools/libs/luch-request/utils.js

@@ -0,0 +1,135 @@
+'use strict'
+
+// utils is a library of generic helper functions non-specific to axios
+
+var toString = Object.prototype.toString
+
+/**
+ * Determine if a value is an Array
+ *
+ * @param {Object} val The value to test
+ * @returns {boolean} True if value is an Array, otherwise false
+ */
+export function isArray (val) {
+  return toString.call(val) === '[object Array]'
+}
+
+
+/**
+ * Determine if a value is an Object
+ *
+ * @param {Object} val The value to test
+ * @returns {boolean} True if value is an Object, otherwise false
+ */
+export function isObject (val) {
+  return val !== null && typeof val === 'object'
+}
+
+/**
+ * Determine if a value is a Date
+ *
+ * @param {Object} val The value to test
+ * @returns {boolean} True if value is a Date, otherwise false
+ */
+export function isDate (val) {
+  return toString.call(val) === '[object Date]'
+}
+
+/**
+ * Determine if a value is a URLSearchParams object
+ *
+ * @param {Object} val The value to test
+ * @returns {boolean} True if value is a URLSearchParams object, otherwise false
+ */
+export function isURLSearchParams (val) {
+  return typeof URLSearchParams !== 'undefined' && val instanceof URLSearchParams
+}
+
+
+/**
+ * Iterate over an Array or an Object invoking a function for each item.
+ *
+ * If `obj` is an Array callback will be called passing
+ * the value, index, and complete array for each item.
+ *
+ * If 'obj' is an Object callback will be called passing
+ * the value, key, and complete object for each property.
+ *
+ * @param {Object|Array} obj The object to iterate
+ * @param {Function} fn The callback to invoke for each item
+ */
+export function forEach (obj, fn) {
+  // Don't bother if no value provided
+  if (obj === null || typeof obj === 'undefined') {
+    return
+  }
+
+  // Force an array if not already something iterable
+  if (typeof obj !== 'object') {
+    /*eslint no-param-reassign:0*/
+    obj = [obj]
+  }
+
+  if (isArray(obj)) {
+    // Iterate over array values
+    for (var i = 0, l = obj.length; i < l; i++) {
+      fn.call(null, obj[i], i, obj)
+    }
+  } else {
+    // Iterate over object keys
+    for (var key in obj) {
+      if (Object.prototype.hasOwnProperty.call(obj, key)) {
+        fn.call(null, obj[key], key, obj)
+      }
+    }
+  }
+}
+
+/**
+ * 是否为boolean 值
+ * @param val
+ * @returns {boolean}
+ */
+export function isBoolean(val) {
+  return typeof val === 'boolean'
+}
+
+/**
+ * 是否为真正的对象{} new Object
+ * @param {any} obj - 检测的对象
+ * @returns {boolean}
+ */
+export function isPlainObject(obj) {
+  return Object.prototype.toString.call(obj) === '[object Object]'
+}
+
+
+
+/**
+ * Function equal to merge with the difference being that no reference
+ * to original objects is kept.
+ *
+ * @see merge
+ * @param {Object} obj1 Object to merge
+ * @returns {Object} Result of all merge properties
+ */
+export function deepMerge(/* obj1, obj2, obj3, ... */) {
+  let result = {}
+  function assignValue(val, key) {
+    if (typeof result[key] === 'object' && typeof val === 'object') {
+      result[key] = deepMerge(result[key], val)
+    } else if (typeof val === 'object') {
+      result[key] = deepMerge({}, val)
+    } else {
+      result[key] = val
+    }
+  }
+  for (let i = 0, l = arguments.length; i < l; i++) {
+    forEach(arguments[i], assignValue)
+  }
+  return result
+}
+
+export function isUndefined (val) {
+  return typeof val === 'undefined'
+}

+ 264 - 0
uni_modules/uv-ui-tools/libs/luch-request/utils/clone.js

@@ -0,0 +1,264 @@
+/* eslint-disable */
+var clone = (function() {
+  'use strict';
+
+  function _instanceof(obj, type) {
+    return type != null && obj instanceof type;
+  }
+
+  var nativeMap;
+  try {
+    nativeMap = Map;
+  } catch(_) {
+    // maybe a reference error because no `Map`. Give it a dummy value that no
+    // value will ever be an instanceof.
+    nativeMap = function() {};
+  }
+
+  var nativeSet;
+  try {
+    nativeSet = Set;
+  } catch(_) {
+    nativeSet = function() {};
+  }
+
+  var nativePromise;
+  try {
+    nativePromise = Promise;
+  } catch(_) {
+    nativePromise = function() {};
+  }
+
+  /**
+   * Clones (copies) an Object using deep copying.
+   *
+   * This function supports circular references by default, but if you are certain
+   * there are no circular references in your object, you can save some CPU time
+   * by calling clone(obj, false).
+   *
+   * Caution: if `circular` is false and `parent` contains circular references,
+   * your program may enter an infinite loop and crash.
+   *
+   * @param `parent` - the object to be cloned
+   * @param `circular` - set to true if the object to be cloned may contain
+   *    circular references. (optional - true by default)
+   * @param `depth` - set to a number if the object is only to be cloned to
+   *    a particular depth. (optional - defaults to Infinity)
+   * @param `prototype` - sets the prototype to be used when cloning an object.
+   *    (optional - defaults to parent prototype).
+   * @param `includeNonEnumerable` - set to true if the non-enumerable properties
+   *    should be cloned as well. Non-enumerable properties on the prototype
+   *    chain will be ignored. (optional - false by default)
+   */
+  function clone(parent, circular, depth, prototype, includeNonEnumerable) {
+    if (typeof circular === 'object') {
+      depth = circular.depth;
+      prototype = circular.prototype;
+      includeNonEnumerable = circular.includeNonEnumerable;
+      circular = circular.circular;
+    }
+    // maintain two arrays for circular references, where corresponding parents
+    // and children have the same index
+    var allParents = [];
+    var allChildren = [];
+
+    var useBuffer = typeof Buffer != 'undefined';
+
+    if (typeof circular == 'undefined')
+      circular = true;
+
+    if (typeof depth == 'undefined')
+      depth = Infinity;
+
+    // recurse this function so we don't reset allParents and allChildren
+    function _clone(parent, depth) {
+      // cloning null always returns null
+      if (parent === null)
+        return null;
+
+      if (depth === 0)
+        return parent;
+
+      var child;
+      var proto;
+      if (typeof parent != 'object') {
+        return parent;
+      }
+
+      if (_instanceof(parent, nativeMap)) {
+        child = new nativeMap();
+      } else if (_instanceof(parent, nativeSet)) {
+        child = new nativeSet();
+      } else if (_instanceof(parent, nativePromise)) {
+        child = new nativePromise(function (resolve, reject) {
+          parent.then(function(value) {
+            resolve(_clone(value, depth - 1));
+          }, function(err) {
+            reject(_clone(err, depth - 1));
+          });
+        });
+      } else if (clone.__isArray(parent)) {
+        child = [];
+      } else if (clone.__isRegExp(parent)) {
+        child = new RegExp(parent.source, __getRegExpFlags(parent));
+        if (parent.lastIndex) child.lastIndex = parent.lastIndex;
+      } else if (clone.__isDate(parent)) {
+        child = new Date(parent.getTime());
+      } else if (useBuffer && Buffer.isBuffer(parent)) {
+        if (Buffer.from) {
+          // Node.js >= 5.10.0
+          child = Buffer.from(parent);
+        } else {
+          // Older Node.js versions
+          child = new Buffer(parent.length);
+          parent.copy(child);
+        }
+        return child;
+      } else if (_instanceof(parent, Error)) {
+        child = Object.create(parent);
+      } else {
+        if (typeof prototype == 'undefined') {
+          proto = Object.getPrototypeOf(parent);
+          child = Object.create(proto);
+        }
+        else {
+          child = Object.create(prototype);
+          proto = prototype;
+        }
+      }
+
+      if (circular) {
+        var index = allParents.indexOf(parent);
+
+        if (index != -1) {
+          return allChildren[index];
+        }
+        allParents.push(parent);
+        allChildren.push(child);
+      }
+
+      if (_instanceof(parent, nativeMap)) {
+        parent.forEach(function(value, key) {
+          var keyChild = _clone(key, depth - 1);
+          var valueChild = _clone(value, depth - 1);
+          child.set(keyChild, valueChild);
+        });
+      }
+      if (_instanceof(parent, nativeSet)) {
+        parent.forEach(function(value) {
+          var entryChild = _clone(value, depth - 1);
+          child.add(entryChild);
+        });
+      }
+
+      for (var i in parent) {
+        var attrs = Object.getOwnPropertyDescriptor(parent, i);
+        if (attrs) {
+          child[i] = _clone(parent[i], depth - 1);
+        }
+
+        try {
+          var objProperty = Object.getOwnPropertyDescriptor(parent, i);
+          if (objProperty.set === 'undefined') {
+            // no setter defined. Skip cloning this property
+            continue;
+          }
+          child[i] = _clone(parent[i], depth - 1);
+        } catch(e){
+          if (e instanceof TypeError) {
+            // when in strict mode, TypeError will be thrown if child[i] property only has a getter
+            // we can't do anything about this, other than inform the user that this property cannot be set.
+            continue
+          } else if (e instanceof ReferenceError) {
+            //this may happen in non strict mode
+            continue
+          }
+        }
+
+      }
+
+      if (Object.getOwnPropertySymbols) {
+        var symbols = Object.getOwnPropertySymbols(parent);
+        for (var i = 0; i < symbols.length; i++) {
+          // Don't need to worry about cloning a symbol because it is a primitive,
+          // like a number or string.
+          var symbol = symbols[i];
+          var descriptor = Object.getOwnPropertyDescriptor(parent, symbol);
+          if (descriptor && !descriptor.enumerable && !includeNonEnumerable) {
+            continue;
+          }
+          child[symbol] = _clone(parent[symbol], depth - 1);
+          Object.defineProperty(child, symbol, descriptor);
+        }
+      }
+
+      if (includeNonEnumerable) {
+        var allPropertyNames = Object.getOwnPropertyNames(parent);
+        for (var i = 0; i < allPropertyNames.length; i++) {
+          var propertyName = allPropertyNames[i];
+          var descriptor = Object.getOwnPropertyDescriptor(parent, propertyName);
+          if (descriptor && descriptor.enumerable) {
+            continue;
+          }
+          child[propertyName] = _clone(parent[propertyName], depth - 1);
+          Object.defineProperty(child, propertyName, descriptor);
+        }
+      }
+
+      return child;
+    }
+
+    return _clone(parent, depth);
+  }
+
+  /**
+   * Simple flat clone using prototype, accepts only objects, usefull for property
+   * override on FLAT configuration object (no nested props).
+   *
+   * USE WITH CAUTION! This may not behave as you wish if you do not know how this
+   * works.
+   */
+  clone.clonePrototype = function clonePrototype(parent) {
+    if (parent === null)
+      return null;
+
+    var c = function () {};
+    c.prototype = parent;
+    return new c();
+  };
+
+// private utility functions
+
+  function __objToStr(o) {
+    return Object.prototype.toString.call(o);
+  }
+  clone.__objToStr = __objToStr;
+
+  function __isDate(o) {
+    return typeof o === 'object' && __objToStr(o) === '[object Date]';
+  }
+  clone.__isDate = __isDate;
+
+  function __isArray(o) {
+    return typeof o === 'object' && __objToStr(o) === '[object Array]';
+  }
+  clone.__isArray = __isArray;
+
+  function __isRegExp(o) {
+    return typeof o === 'object' && __objToStr(o) === '[object RegExp]';
+  }
+  clone.__isRegExp = __isRegExp;
+
+  function __getRegExpFlags(re) {
+    var flags = '';
+    if (re.global) flags += 'g';
+    if (re.ignoreCase) flags += 'i';
+    if (re.multiline) flags += 'm';
+    return flags;
+  }
+  clone.__getRegExpFlags = __getRegExpFlags;
+
+  return clone;
+})();
+
+export default clone

+ 13 - 0
uni_modules/uv-ui-tools/libs/mixin/button.js

@@ -0,0 +1,13 @@
+export default {
+    props: {
+        lang: String,
+        sessionFrom: String,
+        sendMessageTitle: String,
+        sendMessagePath: String,
+        sendMessageImg: String,
+        showMessageCard: Boolean,
+        appParameter: String,
+        formType: String,
+        openType: String
+    }
+}

+ 172 - 0
uni_modules/uv-ui-tools/libs/mixin/mixin.js

@@ -0,0 +1,172 @@
+import * as index from '../function/index.js';
+import * as test from '../function/test.js';
+import route from '../util/route.js';
+import debounce from '../function/debounce.js';
+import throttle from '../function/throttle.js';
+export default {
+	// 定义每个组件都可能需要用到的外部样式以及类名
+	props: {
+		// 每个组件都有的父组件传递的样式,可以为字符串或者对象形式
+		customStyle: {
+			type: [Object, String],
+			default: () => ({})
+		},
+		customClass: {
+			type: String,
+			default: ''
+		},
+		// 跳转的页面路径
+		url: {
+			type: String,
+			default: ''
+		},
+		// 页面跳转的类型
+		linkType: {
+			type: String,
+			default: 'navigateTo'
+		}
+	},
+	data() {
+		return {}
+	},
+	onLoad() {
+		// getRect挂载到$uv上,因为这方法需要使用in(this),所以无法把它独立成一个单独的文件导出
+		this.$uv.getRect = this.$uvGetRect
+	},
+	created() {
+		// 组件当中,只有created声明周期,为了能在组件使用,故也在created中将方法挂载到$uv
+		this.$uv.getRect = this.$uvGetRect
+	},
+	computed: {
+		$uv() {
+			return {
+				...index,
+				test,
+				route,
+				debounce,
+				throttle,
+				unit: uni?.$uv?.config?.unit
+			}
+		},
+		/**
+		 * 生成bem规则类名
+		 * 由于微信小程序,H5,nvue之间绑定class的差异,无法通过:class="[bem()]"的形式进行同用
+		 * 故采用如下折中做法,最后返回的是数组(一般平台)或字符串(支付宝和字节跳动平台),类似['a', 'b', 'c']或'a b c'的形式
+		 * @param {String} name 组件名称
+		 * @param {Array} fixed 一直会存在的类名
+		 * @param {Array} change 会根据变量值为true或者false而出现或者隐藏的类名
+		 * @returns {Array|string}
+		 */
+		bem() {
+			return function(name, fixed, change) {
+				// 类名前缀
+				const prefix = `uv-${name}--`
+				const classes = {}
+				if (fixed) {
+					fixed.map((item) => {
+						// 这里的类名,会一直存在
+						classes[prefix + this[item]] = true
+					})
+				}
+				if (change) {
+					change.map((item) => {
+						// 这里的类名,会根据this[item]的值为true或者false,而进行添加或者移除某一个类
+						this[item] ? (classes[prefix + item] = this[item]) : (delete classes[prefix + item])
+					})
+				}
+				return Object.keys(classes)
+					// 支付宝,头条小程序无法动态绑定一个数组类名,否则解析出来的结果会带有",",而导致失效
+					// #ifdef MP-ALIPAY || MP-TOUTIAO || MP-LARK || MP-BAIDU
+					.join(' ')
+				// #endif
+			}
+		}
+	},
+	methods: {
+		// 跳转某一个页面
+		openPage(urlKey = 'url') {
+			const url = this[urlKey]
+			if (url) {
+				// 执行类似uni.navigateTo的方法
+				uni[this.linkType]({
+					url
+				})
+			}
+		},
+		// 查询节点信息
+		// 目前此方法在支付宝小程序中无法获取组件跟接点的尺寸,为支付宝的bug(2020-07-21)
+		// 解决办法为在组件根部再套一个没有任何作用的view元素
+		$uvGetRect(selector, all) {
+			return new Promise((resolve) => {
+				uni.createSelectorQuery()
+					.in(this)[all ? 'selectAll' : 'select'](selector)
+					.boundingClientRect((rect) => {
+						if (all && Array.isArray(rect) && rect.length) {
+							resolve(rect)
+						}
+						if (!all && rect) {
+							resolve(rect)
+						}
+					})
+					.exec()
+			})
+		},
+		getParentData(parentName = '') {
+			// 避免在created中去定义parent变量
+			if (!this.parent) this.parent = {}
+			// 这里的本质原理是,通过获取父组件实例(也即类似uv-radio的父组件uv-radio-group的this)
+			// 将父组件this中对应的参数,赋值给本组件(uv-radio的this)的parentData对象中对应的属性
+			// 之所以需要这么做,是因为所有端中,头条小程序不支持通过this.parent.xxx去监听父组件参数的变化
+			// 此处并不会自动更新子组件的数据,而是依赖父组件uv-radio-group去监听data的变化,手动调用更新子组件的方法去重新获取
+			this.parent = this.$uv.$parent.call(this, parentName)
+			if (this.parent.children) {
+				// 如果父组件的children不存在本组件的实例,才将本实例添加到父组件的children中
+				this.parent.children.indexOf(this) === -1 && this.parent.children.push(this)
+			}
+			if (this.parent && this.parentData) {
+				// 历遍parentData中的属性,将parent中的同名属性赋值给parentData
+				Object.keys(this.parentData).map((key) => {
+					this.parentData[key] = this.parent[key]
+				})
+			}
+		},
+		// 阻止事件冒泡
+		preventEvent(e) {
+			e && typeof(e.stopPropagation) === 'function' && e.stopPropagation()
+		},
+		// 空操作
+		noop(e) {
+			this.preventEvent(e)
+		}
+	},
+	onReachBottom() {
+		uni.$emit('uvOnReachBottom')
+	},
+	beforeDestroy() {
+		// 判断当前页面是否存在parent和chldren,一般在checkbox和checkbox-group父子联动的场景会有此情况
+		// 组件销毁时,移除子组件在父组件children数组中的实例,释放资源,避免数据混乱
+		if (this.parent && test.array(this.parent.children)) {
+			// 组件销毁时,移除父组件中的children数组中对应的实例
+			const childrenList = this.parent.children
+			childrenList.map((child, index) => {
+				// 如果相等,则移除
+				if (child === this) {
+					childrenList.splice(index, 1)
+				}
+			})
+		}
+	},
+	// 兼容vue3
+	unmounted() {
+		if (this.parent && test.array(this.parent.children)) {
+			// 组件销毁时,移除父组件中的children数组中对应的实例
+			const childrenList = this.parent.children
+			childrenList.map((child, index) => {
+				// 如果相等,则移除
+				if (child === this) {
+					childrenList.splice(index, 1)
+				}
+			})
+		}
+	}
+}

+ 8 - 0
uni_modules/uv-ui-tools/libs/mixin/mpMixin.js

@@ -0,0 +1,8 @@
+export default {
+    // #ifdef MP-WEIXIN
+    // 将自定义节点设置成虚拟的(去掉自定义组件包裹层),更加接近Vue组件的表现,能更好的使用flex属性
+    options: {
+        virtualHost: true
+    }
+    // #endif
+}

Alguns ficheiros não foram mostrados porque muitos ficheiros mudaram neste diff