Browse Source

第三期静态页面完成

xiaoxin 2 years ago
parent
commit
6ac6586084
62 changed files with 5166 additions and 7672 deletions
  1. 129 0
      components/commentChild2.vue
  2. 37 6
      pages.json
  3. 225 0
      pages/addPlace/addPlace.vue
  4. 3 1
      pages/appraise/appraise.vue
  5. 358 0
      pages/community/community.vue
  6. 2 1
      pages/couponCenter/couponCenter.vue
  7. 108 8
      pages/home3/home3.vue
  8. 4 4
      pages/identity/identity.vue
  9. 1 1
      pages/index/index.vue
  10. 109 0
      pages/likeList/likeList.vue
  11. 4 4
      pages/my/my.vue
  12. 265 14
      pages/myEvaluate/myEvaluate.vue
  13. 430 0
      pages/myHome/myHome.vue
  14. 218 10
      pages/send/send.vue
  15. 927 0
      pages/tweetDetail/tweetDetail.vue
  16. BIN
      static/images/community-active.png
  17. BIN
      static/images/community.png
  18. BIN
      static/index/audit.png
  19. BIN
      static/index/close3.png
  20. BIN
      static/index/like-active.png
  21. BIN
      static/index/like.png
  22. BIN
      static/index/rejected.png
  23. BIN
      static/index/upvote.png
  24. BIN
      static/search/icon.png
  25. BIN
      static/search/icon2.png
  26. BIN
      static/search/icon3.png
  27. BIN
      static/search/icon4.png
  28. BIN
      static/search/icon5.png
  29. BIN
      static/search/icon6.png
  30. BIN
      static/search/icon7.png
  31. BIN
      static/search/icon8.png
  32. 23 0
      uni_modules/uni-fab/changelog.md
  33. 491 0
      uni_modules/uni-fab/components/uni-fab/uni-fab.vue
  34. 84 0
      uni_modules/uni-fab/package.json
  35. 9 0
      uni_modules/uni-fab/readme.md
  36. 5 0
      uni_modules/uv-avatar/changelog.md
  37. 53 0
      uni_modules/uv-avatar/components/uv-avatar-group/props.js
  38. 106 0
      uni_modules/uv-avatar/components/uv-avatar-group/uv-avatar-group.vue
  39. 80 0
      uni_modules/uv-avatar/components/uv-avatar/props.js
  40. 175 0
      uni_modules/uv-avatar/components/uv-avatar/uv-avatar.vue
  41. 89 0
      uni_modules/uv-avatar/package.json
  42. 11 0
      uni_modules/uv-avatar/readme.md
  43. 26 0
      uni_modules/uv-image/changelog.md
  44. 95 0
      uni_modules/uv-image/components/uv-image/props.js
  45. 284 0
      uni_modules/uv-image/components/uv-image/uv-image.vue
  46. 89 0
      uni_modules/uv-image/package.json
  47. 15 0
      uni_modules/uv-image/readme.md
  48. 2 0
      uni_modules/uv-loading-icon/changelog.md
  49. 7 0
      uni_modules/uv-loading-icon/components/uv-loading-icon/props.js
  50. 3 2
      uni_modules/uv-loading-icon/components/uv-loading-icon/uv-loading-icon.vue
  51. 3 3
      uni_modules/uv-loading-icon/package.json
  52. 12 4
      uni_modules/uv-loading-icon/readme.md
  53. 4 0
      uni_modules/uv-text/changelog.md
  54. 203 213
      uni_modules/uv-text/components/uv-text/uv-text.vue
  55. 2 2
      uni_modules/uv-text/package.json
  56. 9 3
      uni_modules/uv-text/readme.md
  57. 24 0
      uni_modules/uv-waterfall/changelog.md
  58. 69 0
      uni_modules/uv-waterfall/components/uv-waterfall/props.js
  59. 265 0
      uni_modules/uv-waterfall/components/uv-waterfall/uv-waterfall.vue
  60. 89 0
      uni_modules/uv-waterfall/package.json
  61. 19 0
      uni_modules/uv-waterfall/readme.md
  62. 0 7396
      util/mapData.js

+ 129 - 0
components/commentChild2.vue

@@ -0,0 +1,129 @@
+<template>
+	<view class="item_child">
+		<view class="child_box" v-for="item in list" :key="item.id" @click.stop="handleComment(item)">
+			<view class="box_user">
+				<img mode="aspectFill" :src="item.headPhoto" />
+				<view class="user_info">
+					{{ item.userName }}
+				</view>
+			</view>
+			<view class="box_content">
+				<text class="content_key">回复{{ item.commentName }}:</text>
+				{{ item.content }}
+				<view class="content_bottom">
+					{{ item.dateTime }}
+				</view>
+			</view>
+
+			<view>
+				<Child v-if="item.commentVoList" :list="item.commentVoList" :commentParentId="commentParentId"></Child>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+var dayjs = require('dayjs')
+// uniapp不兼容递归组件,需要重新引入注册使用
+import Child from '@/components/commentChild2.vue'
+export default {
+	components: { Child },
+	props: {
+		list: Array,
+		commentParentId: String
+	},
+	methods: {
+		handleComment(item) {
+			uni.showModal({
+				title: '请输入评论',
+				editable: true,
+				success: async (res) => {
+					if (res.confirm) {
+						const result = res.content
+						if (!res.content) {
+							uni.showToast({
+								title: '评论内容不能为空',
+								icon: 'none',
+								mask: true
+							})
+							setTimeout(() => {
+								this.handleComment()
+							}, 1500)
+						} else {
+							let time = dayjs(new Date()).format('YYYY-MM-DD HH:mm:ss')
+							const res = await this.$myRequest({
+								url: '/mhotel/abcareplyComment.action',
+								data: {
+									commentId: item.id,
+									commentParentId: this.commentParentId,
+									commentName: item.userName,
+									content: result,
+									commentStatus: 1,
+									createId: uni.getStorageSync('userInfo').id,
+									createUsername: uni.getStorageSync('userInfo').user_name,
+									createDate: time,
+									modifyDate: time
+								}
+							})
+							// console.log(res);
+							if (res.code === 200) {
+								uni.showToast({
+									title: res.message,
+									icon: 'success',
+									mask: true
+								})
+								setTimeout(() => {
+									uni.$emit('getData')
+								}, 1500)
+							}
+						}
+					}
+				}
+			})
+		}
+	}
+}
+</script>
+
+<style lang="scss" scoped>
+.item_child {
+	// margin-left: 50rpx;
+
+	.child_box {
+		margin-bottom: 10rpx;
+		.box_user {
+			display: flex;
+			align-items: center;
+			height: 70rpx;
+
+			img {
+				width: 70rpx;
+				height: 70rpx;
+				border-radius: 50%;
+			}
+
+			.user_info {
+				display: flex;
+				align-items: center;
+				margin-left: 18rpx;
+				height: 70rpx;
+				font-size: 28rpx;
+			}
+		}
+
+		.box_content {
+			margin-left: 80rpx;
+			font-size: 24rpx;
+
+			.content_key {
+				color: #808080;
+			}
+
+			.content_bottom {
+				margin: 10rpx 0;
+				color: #808080;
+			}
+		}
+	}
+}
+</style>

+ 37 - 6
pages.json

@@ -254,6 +254,37 @@
 				"navigationBarTitleText": "",
 				"enablePullDownRefresh": false
 			}
+		}, {
+			"path": "pages/community/community",
+			"style": {
+				"navigationBarTitleText": "社区",
+				"enablePullDownRefresh": false
+			}
+		}, {
+			"path": "pages/addPlace/addPlace",
+			"style": {
+				"navigationBarTitleText": "添加地点",
+				"enablePullDownRefresh": false
+			}
+		}, {
+			"path": "pages/tweetDetail/tweetDetail",
+			"style": {
+				"navigationBarTitleText": "推文详情",
+				"enablePullDownRefresh": false
+			}
+		}, {
+			"path": "pages/myHome/myHome",
+			"style": {
+				"navigationBarTitleText": "个人主页",
+				"enablePullDownRefresh": false,
+				"navigationStyle": "custom"
+			}
+		}, {
+			"path": "pages/likeList/likeList",
+			"style": {
+				"navigationBarTitleText": "点赞过的用户",
+				"enablePullDownRefresh": false
+			}
 		}
 	],
 	"tabBar": {
@@ -267,12 +298,12 @@
 				"selectedIconPath": "static/images/index-active.png",
 				"text": "首页"
 			},
-			// {
-			// 	"pagePath": "pages/setMeal/setMeal",
-			// 	"iconPath": "static/images/setMeal.png",
-			// 	"selectedIconPath": "static/images/setMeal-active.png",
-			// 	"text": "套餐"
-			// },
+			{
+				"pagePath": "pages/community/community",
+				"iconPath": "static/images/community.png",
+				"selectedIconPath": "static/images/community-active.png",
+				"text": "社区"
+			},
 			// {
 			// 	"pagePath": "pages/search/search",
 			// 	"iconPath": "static/images/search.png",

+ 225 - 0
pages/addPlace/addPlace.vue

@@ -0,0 +1,225 @@
+<template>
+	<view class="container" v-if="townList.length && hotelList.length">
+		<view class="title">选择乡镇</view>
+		<!-- 乡镇列表区域 -->
+		<view class="town_list">
+			<!-- 每一个乡镇 -->
+			<view class="town_box" :class="{active:activeIndex ===index}" v-for="(item, index) in townList" :key="index"
+				@click="handleChange(index)">{{ item.name }}</view>
+		</view>
+		<view class="title2">关联民宿</view>
+		<!-- 民宿列表区域 -->
+		<view class="hotel_list">
+			<!-- 每一个民宿 -->
+			<view class="hotel_box" v-for="item in hotelList" :key="item.id">
+				<view class="box_radio">
+					<radio style="transform:scale(1.2)" color="#096562" :checked="item.is_collect_hotel"
+						@click="handleClickRadio(item)" /></label>
+				</view>
+				<view class="box_info">
+					<img mode="aspectFill" :src="item.coverImg">
+					<view class="info_detail">
+						<view class="detail_name">
+							{{item.hotel_name}}
+						</view>
+						<view class="detail_leave">
+							{{item.hTypeName}}
+						</view>
+						<view class="detail_rate">
+							{{item.score.toFixed(1)}}分
+						</view>
+					</view>
+				</view>
+			</view>
+		</view>
+
+		<!-- 确定按钮区域 -->
+		<view class="btn" @click="handleClickBtn">
+			确定
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				// 乡镇数组
+				townList: [],
+				// 民宿列表
+				hotelList: [],
+				// 当前高亮索引
+				activeIndex: 0
+			}
+		},
+		onLoad() {
+			this.getTownList()
+			this.getHotelList()
+		},
+		methods: {
+			// 获取乡镇列表数组
+			async getTownList() {
+				const res = await this.$myRequest({
+					url: '/mhotel/ahpgetResidueCount.action'
+				})
+				// console.log(res)
+				if (res.code === 200) {
+					this.townList = res.data
+				}
+			},
+			// 获取民宿列表数组
+			async getHotelList() {
+				const res = await this.$myRequest({
+					url: '/mhotel/ahphomePage.action',
+					data: {
+						page: 1,
+						rows: 4,
+						type: 3,
+						userId: uni.getStorageSync('userInfo') ? uni.getStorageSync('userInfo').id : ''
+					}
+				})
+				// console.log(res)
+				if (res.code === 200) {
+					this.hotelList = res.data.pageList
+				}
+			},
+			// 切换乡镇回调
+			handleChange(index) {
+				this.activeIndex = index
+			},
+			// 点击radio回调
+			handleClickRadio(item) {
+				item.is_collect_hotel = !item.is_collect_hotel
+			},
+			// 确定按钮点击回调
+			handleClickBtn() {
+				let temList = this.hotelList.filter(ele => ele.is_collect_hotel)
+				if (temList.length) {
+					uni.$emit('add', {
+						list: temList
+					})
+					uni.navigateBack(1)
+				} else {
+					uni.showToast({
+						title: "请关联至少一个民宿",
+						icon: 'none',
+						mask: true
+					})
+				}
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.container {
+		padding: 0 19rpx 80rpx;
+		min-height: 100vh;
+		background-color: #fff;
+
+		.title {
+			height: 90rpx;
+			line-height: 90rpx;
+			font-size: 32rpx;
+			font-weight: bold;
+		}
+
+		.town_list {
+			display: flex;
+			flex-wrap: wrap;
+
+			.town_box {
+				display: flex;
+				justify-content: center;
+				align-items: center;
+				padding: 0 39rpx;
+				margin-right: 19rpx;
+				margin-bottom: 24rpx;
+				height: 65rpx;
+				color: #808080;
+				font-size: 28rpx;
+				border-radius: 74rpx;
+				background-color: #e6e6e6;
+			}
+
+			.active {
+				color: #fff;
+				background-color: #096562;
+			}
+		}
+
+		.title2 {
+			height: 80rpx;
+			font-size: 32rpx;
+			font-weight: bold;
+		}
+
+		.hotel_list {
+			.hotel_box {
+				display: flex;
+				margin-bottom: 20rpx;
+				width: 710rpx;
+				height: 150rpx;
+
+				.box_radio {
+					display: flex;
+					justify-content: center;
+					align-items: center;
+					width: 95rpx;
+				}
+
+				.box_info {
+					flex: 1;
+					display: flex;
+					background-color: #F2F2F2;
+					overflow: hidden;
+
+					img {
+						width: 126rpx;
+						height: 150rpx;
+					}
+
+					.info_detail {
+						flex: 1;
+						display: flex;
+						flex-direction: column;
+						justify-content: space-evenly;
+						padding: 0 23rpx;
+						overflow: hidden;
+
+						.detail_name {
+							font-size: 28rpx;
+							font-weight: bold;
+							overflow: hidden;
+							text-overflow: ellipsis;
+							white-space: nowrap;
+						}
+
+						.detail_leave {
+							color: #808080;
+							font-size: 24rpx;
+						}
+
+						.detail_rate {
+							color: #FF5733;
+							font-size: 24rpx;
+						}
+					}
+				}
+			}
+		}
+
+		.btn {
+			display: flex;
+			justify-content: center;
+			align-items: center;
+			margin: 200rpx auto 0;
+			width: 710rpx;
+			height: 100rpx;
+			color: #fff;
+			font-size: 32rpx;
+			border-radius: 64rpx;
+			background-color: #096562;
+		}
+	}
+</style>

+ 3 - 1
pages/appraise/appraise.vue

@@ -59,7 +59,9 @@
 				</view>
 
 				<!-- 入住时间区域 -->
-				<view class="box_time">{{ item.liveTime.slice(0, 10) }}入住,{{ item.commentTime.slice(0, 10) }}发表 | {{ item.houseName }}</view>
+				<view class="box_time" v-if="item.liveTime && item.commentTime">
+					{{ item.liveTime.slice(0, 10) }}入住,{{ item.commentTime.slice(0, 10) }}发表 | {{ item.houseName }}
+				</view>
 
 				<!-- 评价内容区域 -->
 				<uv-read-more show-height="85rpx" closeText="全文" color="#096663" fontSize="24rpx" textIndent="0" :toggle="true" :shadowStyle="shadowStyle">

+ 358 - 0
pages/community/community.vue

@@ -0,0 +1,358 @@
+<template>
+	<view class="container">
+		<!-- 搜索框区域 -->
+		<view class="content">
+			<uv-row custom-style="margin: 10px 0px" gutter="10">
+				<picker @change="bindPickerChange" range-key="name" :value="placeIndex" :range="placeList">
+					<view class="address">
+						<view class="address_text">{{ placeList[placeIndex].name }}</view>
+						<img src="../../static/index/bottom.png" />
+					</view>
+				</picker>
+
+				<view class="search">
+					<view class="add">
+						<image class="img" src="../../static/index/search.png" mode="aspectFit"></image>
+					</view>
+					<input class="inp" type="text" v-model="keywords" placeholder="请输入关键字搜索" />
+					<view class="btnSearch" @click="searchHandler">搜索</view>
+				</view>
+			</uv-row>
+		</view>
+
+		<!-- 分段器区域 -->
+		<view class="control">
+			<uni-segmented-control :current="current" :values="items" style-type="text" active-color="#096562" @clickItem="onClickItem" />
+		</view>
+
+		<!-- 数据列表区域 -->
+		<scroll-view v-if="list.length" class="body" scroll-y @scrolltolower="handleTolower">
+			<uv-waterfall ref="waterfall" v-model="list" :add-time="80" :left-gap="0" :right-gap="0" :column-gap="8" @changeList="changeList">
+				<!-- 第一列数据 -->
+				<template v-slot:list1>
+					<!-- 为了磨平部分平台的BUG,必须套一层view -->
+					<view>
+						<view v-for="(item, index) in list1" :key="item.id" class="waterfall_item" @click="goPageDetail(item)">
+							<img mode="aspectFill" class="item_cover" :src="item.coverImg" />
+							<view class="item_desc">{{ item.hposition }}</view>
+							<view class="item_info">
+								<img mode="aspectFill" :src="item.coverImg" />
+								<view class="text">{{ item.hotel_name }}</view>
+								<img
+									class="img"
+									:src="item.is_collect_hotel ? '../../static/index/like-active.png' : '../../static/index/like.png'"
+									@click.stop="handleClickLike(item)"
+								/>
+								<view class="count">{{ item.roomNumber }}</view>
+							</view>
+						</view>
+					</view>
+				</template>
+				<!-- 第二列数据 -->
+				<template v-slot:list2>
+					<!-- 为了磨平部分平台的BUG,必须套一层view -->
+					<view>
+						<view v-for="(item, index) in list2" :key="item.id" class="waterfall_item" @click="goPageDetail(item)">
+							<img mode="aspectFill" class="item_cover" :src="item.coverImg" />
+							<view class="item_desc">{{ item.hposition }}</view>
+							<view class="item_info">
+								<img mode="aspectFill" :src="item.coverImg" />
+								<view class="text">{{ item.hotel_name }}</view>
+								<img
+									class="img"
+									:src="item.is_collect_hotel ? '../../static/index/like-active.png' : '../../static/index/like.png'"
+									@click.stop="handleClickLike(item)"
+								/>
+								<view class="count">{{ item.roomNumber }}</view>
+							</view>
+						</view>
+					</view>
+				</template>
+			</uv-waterfall>
+		</scroll-view>
+
+		<!-- 没有数据时展示的区域 -->
+		<view class="noData" v-if="!list.length">
+			<img src="../../static/images/noData.png" />
+			暂无数据
+		</view>
+
+		<!-- 悬浮按钮区域 -->
+		<uni-fab :pattern="pattern" horizontal="right" @fabClick="handleClickBtn"></uni-fab>
+	</view>
+</template>
+
+<script>
+export default {
+	data() {
+		return {
+			// 悬浮按钮样式
+			pattern: {
+				buttonColor: '#096562'
+			},
+			keywords: '',
+			// 当前选择地区索引
+			placeIndex: 0,
+			// 地区数组
+			placeList: [
+				{
+					name: '靖安县'
+				}
+			],
+			// 分段器数组
+			items: ['发现', '关注', '收藏'],
+			// 当前索引
+			current: 0,
+			// 当前页
+			page: 1,
+			// 每页多少条
+			rows: 6,
+			// 总条数
+			total: null,
+			// 瀑布流全部数据
+			list: [],
+			// 瀑布流第一列数据
+			list1: [],
+			// 瀑布流第二列数据
+			list2: []
+		}
+	},
+	onLoad() {
+		this.getHotelList()
+	},
+	methods: {
+		// 获取列表数组
+		async getHotelList() {
+			const res = await this.$myRequest({
+				url: '/mhotel/ahphomePage.action',
+				data: {
+					page: this.page,
+					rows: this.rows,
+					type: 3,
+					userId: uni.getStorageSync('userInfo') ? uni.getStorageSync('userInfo').id : ''
+				}
+			})
+			// console.log(res)
+			if (res.code === 200) {
+				this.list = [...this.list, ...res.data.pageList]
+				this.total = res.data.total
+			}
+		},
+		changeList(e) {
+			if (e.name === 'list1') {
+				this.list1.push(e.value)
+			} else {
+				this.list2.push(e.value)
+			}
+		},
+		// 点击爱心回调
+		handleClickLike(item) {
+			item.is_collect_hotel = !item.is_collect_hotel
+		},
+		// 点击悬浮按钮回调
+		handleClickBtn() {
+			uni.navigateTo({
+				url: '/pages/send/send'
+			})
+		},
+		// 搜索按钮点击回调
+		searchHandler() {},
+		// 选择地区时的回调
+		bindPickerChange(e) {
+			this.placeIndex = e.detail.value
+		},
+		// 分段器切换回调
+		onClickItem(e) {
+			this.current = e.currentIndex
+			this.list = []
+			this.$refs.waterfall.clear()
+			this.list1 = []
+			this.list2 = []
+			this.page = 1
+			if (this.current === 0) {
+				this.getHotelList()
+			}
+		},
+		// 页面触底回调
+		handleTolower() {
+			if (this.total > this.list.length) {
+				this.page++
+				this.getHotelList()
+			} else {
+				uni.showToast({
+					title: '没有更多数据了',
+					icon: 'none',
+					maks: true
+				})
+			}
+		},
+		// 点击每一个推文回调
+		goPageDetail(item) {
+			uni.navigateTo({
+				url: `/pages/tweetDetail/tweetDetail?id=${item.id}`
+			})
+		}
+	}
+}
+</script>
+
+<style lang="scss" scoped>
+.container {
+	display: flex;
+	flex-direction: column;
+	align-items: center;
+	height: 100vh;
+	overflow: hidden;
+	background-color: #f7f7f7;
+
+	.content {
+		background-color: #f7f7f7;
+
+		.address {
+			display: flex;
+			width: 152rpx;
+			font-size: 28rpx;
+
+			.address_text {
+				width: 104rpx;
+				text-align: center;
+				overflow: hidden;
+				white-space: nowrap;
+				text-overflow: ellipsis;
+			}
+
+			img {
+				width: 48rpx;
+				height: 48rpx;
+			}
+		}
+
+		.search {
+			display: flex;
+			justify-content: space-between;
+			align-items: center;
+			width: 538rpx;
+			height: 80rpx;
+			opacity: 1;
+			border-radius: 70px;
+			background-color: #fff;
+
+			.add {
+				display: flex;
+				justify-content: center;
+				align-items: center;
+				margin-left: 10rpx;
+				width: 60rpx;
+				font-size: 50rpx;
+				height: 60rpx;
+				line-height: 60rpx;
+				color: rgba(30, 125, 251, 1);
+
+				.img {
+					width: 30rpx;
+					height: 30rpx;
+				}
+			}
+
+			.inp {
+				height: 60rpx;
+				line-height: 60rpx;
+				flex-grow: 1;
+				font-size: 28rpx;
+			}
+
+			.btnSearch {
+				width: 100rpx;
+				text-align: center;
+				margin-right: 10rpx;
+				height: 60rpx;
+				line-height: 60rpx;
+				opacity: 1;
+				font-size: 28rpx;
+				font-weight: 400;
+				height: 2rem;
+				color: #096562;
+			}
+		}
+	}
+
+	.control {
+		margin: 0 auto 20rpx;
+		padding-bottom: 10rpx;
+		width: 690rpx;
+		height: 90rpx;
+		border-radius: 8rpx;
+		background-color: #fff;
+	}
+
+	.body {
+		margin: auto;
+		width: 690rpx;
+		height: calc(100vh - 220rpx);
+
+		.waterfall_item {
+			overflow: hidden;
+			margin-bottom: 20rpx;
+			width: 335rpx;
+			border-radius: 10rpx;
+			background-color: #fff;
+
+			.item_cover {
+				width: 335rpx;
+				height: 463rpx;
+				border: 10rpx 10rpx 0 0;
+			}
+
+			.item_desc {
+				padding: 0 22rpx;
+				font-size: 28rpx;
+				font-weight: bold;
+			}
+
+			.item_info {
+				padding: 15rpx 22rpx;
+				display: flex;
+				align-items: center;
+				color: #666666;
+				font-size: 20rpx;
+
+				img {
+					width: 30rpx;
+					height: 30rpx;
+					border-radius: 50%;
+				}
+
+				.text {
+					margin-left: 12rpx;
+					width: 150rpx;
+					overflow: hidden;
+					text-overflow: ellipsis;
+					white-space: nowrap;
+				}
+
+				.img {
+					margin-left: auto;
+				}
+
+				.count {
+					margin-left: 5rpx;
+				}
+			}
+		}
+	}
+
+	.noData {
+		display: flex;
+		flex-direction: column;
+		justify-content: center;
+		align-items: center;
+		padding-bottom: 65rpx;
+
+		img {
+			margin-top: 80rpx;
+			width: 600rpx;
+			height: 600rpx;
+		}
+	}
+}
+</style>

+ 2 - 1
pages/couponCenter/couponCenter.vue

@@ -92,7 +92,8 @@ export default {
 				url: '/mhotel/hccouponCollection.action',
 				data: {
 					page: this.page,
-					rows: this.rows
+					rows: this.rows,
+					userId: uni.getStorageSync('userInfo').id
 				}
 			})
 			// console.log(res);

+ 108 - 8
pages/home3/home3.vue

@@ -94,7 +94,6 @@
 				</swiper-item>
 			</swiper>
 
-			<!-- 民宿列表区域 -->
 			<view class="body">
 				<!-- 精选攻略区域 -->
 				<view class="body_top">
@@ -111,7 +110,6 @@
 					<img mode="aspectfill" src="https://chtech.ncjti.edu.cn/hotelReservation/fileload/download/1933617026前台.jpg" />
 					<view class="strategy_info">
 						<view class="info_title">这里不是瑞士!是江西靖安!</view>
-						<!-- <view class="info_time">2023-09-04 15:15:15</view> -->
 						<view class="info_tags">
 							<view class="tag">南昌周边游</view>
 							<view class="tag">江西旅游</view>
@@ -119,12 +117,42 @@
 						</view>
 						<view class="info_desc">
 							南昌周边度假的宝藏民宿🏠享受山野间森呼吸🌿很适合逃离城市奔向大自然的快乐躺平☁️阴雨绵绵也抵挡不住它的美☔心向山野 尽请赴约 🍃一房一景 设计独特
-							🪐室内有着巨大落地窗🚪地址⛺️宜春市靖安县宁福线中源乡合港村南段组10号交通🚘南昌自驾开车约2H风格⛱民宿是新中式生活美学的格调
-							非常高级还配备中餐厅、商务会议室、茶室、休闲娱乐包厢等🎞适合情侣、家庭自驾游玩
-							💕🔆附近景区游玩推荐:北岭花海、九岭瀑布、九门楼、九岭尖云海、日出、冰雪、风车、草甸、露营、游客中心、清水平台观景、河道游船、观赏荷花、采莲、客家民俗、农俗活动及客家美食、小吃、果脯、高山滑雪、梯田等
 						</view>
 					</view>
 				</view>
+
+				<!-- 招商资讯区域 -->
+				<view class="body_top">
+					<view class="circle"></view>
+					<view class="circle color"></view>
+					<view class="top_title">招商资讯 .</view>
+					<view class="top_msg">以招商引资,推动镇域经济</view>
+					<view class="top_more" @click="handleTest">
+						更多
+						<img src="https://chtech.ncjti.edu.cn/hotelReservation/image/13.png" />
+					</view>
+				</view>
+
+				<view class="body_strategy">
+					<img mode="aspectfill" src="https://chtech.ncjti.edu.cn/hotelReservation/fileload/download/1933617026前台.jpg" />
+					<view class="strategy_info">
+						<view class="info_title2">以招商引资,推动镇域经济!以招商引资,推动镇域经济以招商引资,推动镇域经济!</view>
+						<view class="info_desc">
+							南昌周边度假的宝藏民宿🏠享受山野间森呼吸🌿很适合逃离城市奔向大自然的快乐躺平☁️阴雨绵绵也抵挡不住它的美☔心向山野 尽请赴约 🍃一房一景 设计独特
+						</view>
+					</view>
+				</view>
+
+				<view class="body_strategy">
+					<img mode="aspectfill" src="https://chtech.ncjti.edu.cn/hotelReservation/fileload/download/1933617026前台.jpg" />
+					<view class="strategy_info">
+						<view class="info_title2">以招商引资,推动镇域经济!以招商引资,推动镇域经济以招商引资,推动镇域经济!</view>
+						<view class="info_desc">
+							南昌周边度假的宝藏民宿🏠享受山野间森呼吸🌿很适合逃离城市奔向大自然的快乐躺平☁️阴雨绵绵也抵挡不住它的美☔心向山野 尽请赴约 🍃一房一景 设计独特
+						</view>
+					</view>
+				</view>
+
 				<!-- 精选推荐区域 -->
 				<view class="body_top">
 					<view class="circle"></view>
@@ -136,7 +164,6 @@
 						<img src="https://chtech.ncjti.edu.cn/hotelReservation/image/13.png" />
 					</view>
 				</view>
-				<!-- 列表区域 -->
 				<view class="body_list">
 					<!-- 每一个民宿区域 -->
 					<view class="list_item" v-for="item in hotelList" :key="item.id" @click="goPageDetail(item)">
@@ -156,6 +183,7 @@
 					</view>
 				</view>
 
+				<!-- 没有数据时展示的页面 -->
 				<view class="noData" v-if="!hotelList.length">
 					<img src="../../static/images/noData.png" />
 					暂无推荐数据
@@ -191,6 +219,18 @@
 				</view>
 			</view>
 		</uni-popup>
+
+		<!-- 公告弹窗区域 -->
+		<uv-popup ref="popup_sale" mode="center" :closeOnClickOverlay="false" bgColor="none">
+			<view class="popupClass">
+				<img src="../../static/index/popup_bg.png" />
+				<!-- 立即前往区域 -->
+				<!-- <view class="btn_go" @click="handleGo"></view> -->
+				<view class="btn_text">系统处于迭代试用状态,请勿下单预订酒店,如需预订请电联店家老板确认预订渠道。</view>
+				<!-- 关闭按钮区域 -->
+				<view class="btn_close" @click="handleClose"></view>
+			</view>
+		</uv-popup>
 	</view>
 </template>
 
@@ -282,8 +322,8 @@ export default {
 		}, 1000)
 	},
 	onLoad() {
-		this.getLocation()
 		this.getResidueCount()
+		this.getLocation()
 	},
 	methods: {
 		// 获取用户当前位置
@@ -382,6 +422,7 @@ export default {
 						ele.distance = this.calculateDistance(lat, lng)
 					})
 				}
+				this.$refs.popup_sale.open()
 			}
 		},
 		handleOpen() {
@@ -464,6 +505,14 @@ export default {
 				url: `/pages/detail/detail?id=${item.id}&distance=${item.distance}`
 			})
 		},
+		// 弹窗关闭按钮回调
+		handleClose() {
+			this.$refs.popup_sale.close()
+		},
+		// 立即前往按钮回调
+		handleGo() {
+			console.log(111)
+		},
 		// 计算两个点之间的距离
 		calculateDistance(lat, lng) {
 			let centerLat = lat
@@ -833,7 +882,14 @@ export default {
 						white-space: nowrap;
 					}
 
-					.info_time {
+					.info_title2 {
+						font-size: 32rpx;
+						font-weight: bold;
+						color: #000;
+						display: -webkit-box;
+						-webkit-box-orient: vertical;
+						-webkit-line-clamp: 2;
+						overflow: hidden;
 					}
 
 					.info_tags {
@@ -1030,5 +1086,49 @@ export default {
 			}
 		}
 	}
+
+	.popupClass {
+		position: relative;
+		width: 481rpx;
+		height: 764rpx;
+
+		img {
+			width: 100%;
+			height: 665rpx;
+		}
+
+		.btn_go {
+			position: absolute;
+			top: 415rpx;
+			left: 40rpx;
+			width: 396rpx;
+			height: 76rpx;
+			border-radius: 43rpx;
+		}
+
+		.btn_text {
+			position: absolute;
+			top: 265rpx;
+			left: 40rpx;
+			display: flex;
+			justify-content: center;
+			align-items: center;
+			width: 400rpx;
+			height: 260rpx;
+			color: #0f194d;
+			font-size: 28rpx;
+			border-radius: 43rpx;
+			background-color: #fff;
+		}
+
+		.btn_close {
+			position: absolute;
+			top: 586rpx;
+			left: 212rpx;
+			width: 58rpx;
+			height: 58rpx;
+			border-radius: 50%;
+		}
+	}
 }
 </style>

+ 4 - 4
pages/identity/identity.vue

@@ -34,11 +34,11 @@ export default {
 				})
 				if (res.code === 200) {
 					let data = JSON.stringify(res.data)
-					uni.navigateTo({
+					uni.redirectTo({
 						url: `/pages/shopInfo/shopInfo?type=1&data=${data}`
 					})
 				} else {
-					uni.navigateTo({
+					uni.redirectTo({
 						url: '/pages/shop/shop'
 					})
 				}
@@ -52,11 +52,11 @@ export default {
 				})
 				if (res.code === 200) {
 					let data = JSON.stringify(res.data)
-					uni.navigateTo({
+					uni.redirectTo({
 						url: `/pages/shopInfo/shopInfo?type=2&data=${data}`
 					})
 				} else {
-					uni.navigateTo({
+					uni.redirectTo({
 						url: '/pages/shop2/shop2'
 					})
 				}

+ 1 - 1
pages/index/index.vue

@@ -20,7 +20,7 @@
 
 <script>
 // 靖安县地图数据
-import { mapData } from '@/util/mapData.js'
+// import { mapData } from '@/util/mapData.js'
 
 export default {
 	data() {

+ 109 - 0
pages/likeList/likeList.vue

@@ -0,0 +1,109 @@
+<template>
+	<view class="container">
+		<view class="list">
+			<!-- 每一个用户区域 -->
+			<view class="list_box" v-for="item in list" :key="item.id">
+				<img mode="aspectFill" :src="item.url" />
+				<view class="box_info">
+					<view class="info_name">{{ item.name }}</view>
+					<view class="info_num">
+						<view class="num_article">{{ item.count }}篇推文</view>
+						<view class="num_fans">{{ item.fans }}粉丝</view>
+					</view>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+export default {
+	data() {
+		return {
+			list: [
+				{
+					id: 1,
+					url: 'https://chtech.ncjti.edu.cn/hotelReservation/fileload/download/1933617026前台.jpg',
+					name: '鸣人',
+					count: 6,
+					fans: 10
+				},
+				{
+					id: 2,
+					url: 'https://chtech.ncjti.edu.cn/hotelReservation/fileload/download/1933617026前台.jpg',
+					name: '路飞',
+					count: 6,
+					fans: 10
+				},
+				{
+					id: 3,
+					url: 'https://chtech.ncjti.edu.cn/hotelReservation/fileload/download/1933617026前台.jpg',
+					name: '黑崎一护',
+					count: 6,
+					fans: 10
+				},
+				{
+					id: 4,
+					url: 'https://chtech.ncjti.edu.cn/hotelReservation/fileload/download/1933617026前台.jpg',
+					name: '灰太狼',
+					count: 6,
+					fans: 10
+				},
+				{
+					id: 5,
+					url: 'https://chtech.ncjti.edu.cn/hotelReservation/fileload/download/1933617026前台.jpg',
+					name: '哆啦A梦',
+					count: 6,
+					fans: 10
+				}
+			]
+		}
+	}
+}
+</script>
+
+<style lang="scss" scoped>
+.container {
+	min-height: 100vh;
+	background-color: #fff;
+
+	.list {
+		padding: 10rpx 20rpx;
+
+		.list_box {
+			display: flex;
+			align-items: center;
+			height: 100rpx;
+			border-bottom: 1rpx solid #e5e5e5;
+
+			img {
+				width: 60rpx;
+				height: 60rpx;
+				border-radius: 50%;
+			}
+
+			.box_info {
+				margin-left: 15rpx;
+				font-size: 24rpx;
+
+				.info_name {
+					font-weight: bold;
+				}
+
+				.info_num {
+					display: flex;
+					align-items: center;
+					margin-top: 5rpx;
+					color: #808080;
+
+					.num_article {
+					}
+					.num_fans {
+						margin-left: 30rpx;
+					}
+				}
+			}
+		}
+	}
+}
+</style>

+ 4 - 4
pages/my/my.vue

@@ -9,12 +9,12 @@
 		<view class="header">
 			<img src="https://chtech.ncjti.edu.cn/hotelReservation/image/18.png" />
 			<!-- 头像区域 -->
-			<img class="img" mode="aspectFill" v-if="flag" :src="userInfo.headPhoto" />
-			<img class="img" v-else src="../../static/my/portrait.png" />
+			<img class="img" mode="aspectFill" v-if="flag" :src="userInfo.headPhoto" @click="handleGoPage('/pages/myHome/myHome')" />
+			<img class="img" v-else src="../../static/my/portrait.png" @click="handleGoPage('/pages/myHome/myHome')" />
 			<!-- 姓名区域 -->
-			<view class="name" v-if="flag">{{ userInfo.user_name }}</view>
+			<view class="name" v-if="flag" @click="handleGoPage('/pages/myHome/myHome')">{{ userInfo.user_name }}</view>
 			<!-- 用户id区域 -->
-			<view class="number" v-if="flag">ID:{{ userInfo.id }}</view>
+			<view class="number" v-if="flag" @click="handleGoPage('/pages/myHome/myHome')">ID:{{ userInfo.id }}</view>
 			<!-- 是否实名认证区域 -->
 			<!-- <view class="real" v-if="userInfo.card_number">
 				<img src="../../static/my/true.png" />

+ 265 - 14
pages/myEvaluate/myEvaluate.vue

@@ -10,14 +10,14 @@
 			<!-- 每一个盒子区域 -->
 			<view class="box" v-for="(item, index) in list" :key="index" @click="handleGoDetail(item)">
 				<!-- 头部区域 -->
-				<view class="box_top">
+				<view class="box_top" v-if="activeCurrent === 0">
 					<img mode="aspectFill" src="../../static/my/hotel.png" />
 					<view class="top_name">{{ item.hotelName }}</view>
 					<view class="box_type">已消费</view>
 				</view>
 
 				<!-- 房间信息区域 -->
-				<view class="box_center">
+				<view class="box_center" v-if="activeCurrent === 0">
 					<img mode="aspectFill" :src="item.url" />
 					<view class="center_info">
 						<view>{{ item.houseOrderNumber }}间,{{ item.houseName }}</view>
@@ -30,6 +30,59 @@
 				<view class="box_btn" v-if="activeCurrent === 0">
 					<view class="btn_eva" @click.stop="handleGoPage(item)">去评价</view>
 				</view>
+
+				<!-- 审核中 已驳回 已评价 样式区域 -->
+				<view class="box_top2" v-if="activeCurrent !== 0">
+					<view class="top_name">{{ item.subTime }}发表</view>
+					<view class="box_type color" v-if="activeCurrent === 1">审核中</view>
+					<view class="box_type color2" v-if="activeCurrent === 2">已驳回</view>
+				</view>
+				<view class="box_rate" v-if="activeCurrent !== 0">
+					评分:
+					<uni-rate readonly activeColor="#FFC300" :size="16" :value="item.score" />
+				</view>
+				<view class="box_content" v-if="activeCurrent !== 0">
+					审核中审核中审核中审核中审核中审核中审核中审核中审核中审核中审核中审核中审核中审核中审核中审核中审核中审核中审核中审核中审核中
+				</view>
+
+				<!-- 图片列表区域 -->
+				<view class="box_img" v-if="activeCurrent !== 0">
+					<img
+						v-for="(ele, index) in item.urls"
+						:key="index"
+						mode="aspectFill"
+						v-if="ele.indexOf('jpg') !== -1 || ele.indexOf('png') !== -1"
+						:src="ele"
+						@click.stop="handleLookImg(ele, index)"
+					/>
+					<video
+						:id="item.id + index2"
+						class="video"
+						:show-fullscreen-btn="false"
+						:show-play-btn="false"
+						v-for="(video, index2) in item.urls"
+						:key="index2"
+						v-if="video.indexOf('mp4') !== -1"
+						:src="video"
+						@fullscreenchange="fullscreenchange"
+						@click.stop="handleClickVideo(item.id + index2)"
+					></video>
+				</view>
+
+				<!-- 民宿信息区域 -->
+				<view class="box_hotel" v-if="activeCurrent !== 0" @click.stop="">
+					<img mode="aspectFill" :src="item.urls[0]" />
+					<view class="hotel_info">
+						<view class="info_name">{{ item.hotelName }}</view>
+						<view class="info_tags">{{ item.houseOrderNumber }}间,{{ item.houseName }}</view>
+						<view class="info_tags">{{ item.liveTime.slice(0, 10) }} - {{ item.checkOutTime.slice(0, 10) }}</view>
+						<view class="info_tags">总价:¥{{ item.payAccount }}</view>
+					</view>
+				</view>
+				<view class="box_desc" v-if="activeCurrent === 2">
+					<view class="desc_key">备注:</view>
+					<view class="desc_value">{{ item.desc }}</view>
+				</view>
 			</view>
 
 			<view class="noData" v-if="list.length === 0">
@@ -47,20 +100,61 @@ export default {
 			// 分段器当前激活索引
 			activeCurrent: 0,
 			// 分段器数组
-			headerList: ['待评价', '已评价'],
+			headerList: ['待评价', '审核中', '已驳回', '已评价'],
 			// 列表数据
-			list: [],
+			list: [
+				{
+					id: 1,
+					hotelName: '民宿名称',
+					url: 'https://chtech.ncjti.edu.cn/hotelReservation/fileload/download/1933617026前台.jpg',
+					houseOrderNumber: 1,
+					houseName: '单间',
+					payAccount: 229,
+					subTime: '2023-09-22 16:54:20',
+					liveTime: '2023-09-22 16:54:20',
+					checkOutTime: '2023-09-22 16:54:20',
+					score: 5,
+					urls: [
+						'https://chtech.ncjti.edu.cn/hotelReservation/fileload/download/1933617026前台.jpg',
+						'https://chtech.ncjti.edu.cn/hotelReservation/fileload/download/1933617026前台.jpg',
+						'https://chtech.ncjti.edu.cn/hotelReservation/fileload/download/1933617026前台.jpg',
+						'https://chtech.ncjti.edu.cn/hotelReservation/fileload/download/1933617026前台.jpg'
+					],
+					desc: '内容不实,驳回评价'
+				},
+				{
+					id: 2,
+					hotelName: '民宿名称',
+					url: 'https://chtech.ncjti.edu.cn/hotelReservation/fileload/download/1933617026前台.jpg',
+					houseOrderNumber: 1,
+					houseName: '单间',
+					payAccount: 229,
+					subTime: '2023-09-22 16:54:20',
+					liveTime: '2023-09-22 16:54:20',
+					checkOutTime: '2023-09-22 16:54:20',
+					score: 5,
+					urls: [
+						'https://chtech.ncjti.edu.cn/hotelReservation/fileload/download/1933617026前台.jpg',
+						'https://chtech.ncjti.edu.cn/hotelReservation/fileload/download/1933617026前台.jpg'
+					],
+					desc: '内容不实,驳回评价'
+				}
+			],
 			noDataMsg: '暂无待评价数据',
 			// 当前页
 			page: 1,
 			// 每页多少条
 			rows: 6,
 			// 总条数
-			total: null
+			total: null,
+			status: null,
+			// 是否是全屏状态
+			videoContext: null,
+			isFullScreen: false
 		}
 	},
 	onLoad() {
-		this.getData()
+		// this.getData()
 	},
 	methods: {
 		async getData() {
@@ -68,7 +162,7 @@ export default {
 				url: '/mhotel/abcapersonageComment.action',
 				data: {
 					usersId: uni.getStorageSync('userInfo').id,
-					status: this.activeCurrent,
+					status: this.status,
 					page: this.page,
 					rows: this.rows
 				}
@@ -77,20 +171,28 @@ export default {
 			if (res.code === 200 && res.page.pageList) {
 				this.list = [...this.list, ...res.page.pageList]
 				this.total = res.total
-				this.headerList = [`待评价(${res.data.waitingCount})`, `已评价(${res.data.ratedCount})`]
+				this.headerList = [`待评价(${res.data.waitingCount})`, `审核中(${res.data.auditCount})`, `已驳回(${res.data.refuseAuditCount})`, `已评价(${res.data.ratedCount})`]
 			}
 		},
 		// 切换分段器回调
 		onClickItem(e) {
 			this.activeCurrent = e.currentIndex
-			if (this.activeCurrent === 0) {
+			if (e.currentIndex === 0) {
 				this.noDataMsg = '暂无待评价数据'
-			} else {
+				this.status = 0
+			} else if (e.currentIndex === 1) {
+				this.noDataMsg = '暂无审核中数据'
+				this.status = 2
+			} else if (e.currentIndex === 2) {
+				this.noDataMsg = '暂无已驳回数据'
+				this.status = 3
+			} else if (e.currentIndex === 3) {
 				this.noDataMsg = '暂无已评价数据'
+				this.status = 1
 			}
-			this.list = []
-			this.page = 1
-			this.getData()
+			// this.list = []
+			// this.page = 1
+			// this.getData()
 		},
 		// 列表下拉到底部回调
 		handlePull() {
@@ -121,6 +223,29 @@ export default {
 					url: `/pages/orderDetail/orderDetail?id=${item.id}`
 				})
 			}
+		},
+		// 点击图片回调
+		handleLookImg(url, current) {
+			this.videoContext.stop()
+			uni.previewImage({
+				urls: [url],
+				current
+			})
+		},
+		// 点击视频控件时触发的回调
+		handleClickVideo(id) {
+			this.videoContext = uni.createVideoContext(id)
+			if (this.isFullScreen) {
+				this.videoContext.stop()
+				this.videoContext.exitFullScreen()
+			} else {
+				this.videoContext.requestFullScreen()
+				this.videoContext.play()
+			}
+		},
+		// 进入全屏和退出全屏时触发的回调
+		fullscreenchange(e) {
+			this.isFullScreen = e.detail.fullScreen
 		}
 	}
 }
@@ -134,7 +259,7 @@ export default {
 
 	.segmented {
 		box-sizing: border-box;
-		padding-bottom: 28rpx;
+		padding: 0 50rpx;
 		height: 100rpx;
 		background-color: #fff;
 	}
@@ -174,6 +299,38 @@ export default {
 				}
 			}
 
+			.box_top2 {
+				display: flex;
+				align-items: center;
+				height: 80rpx;
+				border-bottom: 1rpx solid #e5e5e5;
+
+				img {
+					width: 47rpx;
+					height: 47rpx;
+					border-radius: 50%;
+				}
+
+				.top_name {
+					font-size: 28rpx;
+					font-weight: bold;
+				}
+
+				.box_type {
+					margin-left: auto;
+					color: #808080;
+					font-size: 28rpx;
+				}
+
+				.color {
+					color: #e6a23c;
+				}
+
+				.color2 {
+					color: #f56c6c;
+				}
+			}
+
 			.box_center {
 				display: flex;
 
@@ -209,6 +366,100 @@ export default {
 					border: 1rpx solid #808080;
 				}
 			}
+
+			.box_rate {
+				display: flex;
+				align-items: center;
+				height: 70rpx;
+				color: #808080;
+				font-size: 28rpx;
+			}
+
+			.box_content {
+				font-size: 24rpx;
+			}
+
+			.box_img {
+				display: grid;
+				grid-template-columns: 1fr 1fr 1fr;
+				grid-auto-rows: auto;
+				padding: 20rpx 0 40rpx;
+				gap: 10rpx;
+
+				img {
+					width: 216rpx;
+					height: 216rpx;
+					border-radius: 20rpx;
+				}
+
+				.video {
+					width: 216rpx;
+					height: 216rpx;
+					border-radius: 20rpx;
+				}
+			}
+
+			.box_hotel {
+				display: flex;
+				align-items: center;
+				padding: 20rpx 0;
+				border-top: 1rpx solid #e6e6e6;
+				background-color: #fff;
+
+				img {
+					width: 120rpx;
+					height: 170rpx;
+					border-radius: 7rpx;
+				}
+
+				.hotel_info {
+					display: flex;
+					flex-direction: column;
+					justify-content: space-between;
+					margin-left: 20rpx;
+					height: 170rpx;
+
+					.info_name {
+						font-size: 32rpx;
+						font-weight: bold;
+					}
+					.info_star {
+						display: flex;
+						align-items: center;
+
+						.star_num {
+							margin-left: 10rpx;
+							color: #ffc300;
+							font-size: 24rpx;
+						}
+					}
+
+					.info_tags {
+						display: flex;
+						color: #a6a6a6;
+						font-size: 24rpx;
+
+						.tag_item {
+							margin-right: 20rpx;
+						}
+					}
+				}
+			}
+
+			.box_desc {
+				display: flex;
+				padding-top: 15rpx;
+				font-size: 28rpx;
+				border-top: 1rpx solid #e6e6e6;
+
+				.desc_key {
+				}
+
+				.desc_value {
+					flex: 1;
+					color: #808080;
+				}
+			}
 		}
 
 		.noData {

+ 430 - 0
pages/myHome/myHome.vue

@@ -0,0 +1,430 @@
+<template>
+	<view class="container">
+		<!-- 页面标题 -->
+		<view class="title" :style="{ height: customBarH * 2 + 'rpx', top: statusBarH * 2 + 'rpx' }" v-if="!headerType">个人主页</view>
+
+		<view class="title_icon" :style="{ height: customBarH * 2 + 'rpx', top: statusBarH * 2 + 'rpx' }" v-if="!headerType" @click="handleBack">
+			<img src="../../static/index/left.png" />
+		</view>
+
+		<view class="title" :style="{ height: customBarH * 2 + 'rpx', paddingTop: statusBarH * 2 + 'rpx', backgroundColor: '#fff', color: '#000' }" v-if="headerType">
+			个人主页
+		</view>
+
+		<view class="title_icon" :style="{ height: customBarH * 2 + 'rpx', top: statusBarH * 2 + 'rpx' }" v-if="headerType" @click="handleBack">
+			<img src="../../static/index/left2.png" />
+		</view>
+
+		<!-- 顶部用户信息区域 -->
+		<view class="header">
+			<img src="https://chtech.ncjti.edu.cn/hotelReservation/image/18.png" />
+			<!-- 头像区域 -->
+			<img class="img" mode="aspectFill" :src="userInfo.headPhoto || '../../static/my/portrait.png'" />
+			<!-- 姓名区域 -->
+			<view class="name">{{ userInfo.user_name }}</view>
+			<!-- 用户id区域 -->
+			<view class="number">ID:{{ userInfo.id }}</view>
+			<!-- 简介区域 -->
+			<view class="desc" v-if="!showInput" @click="showInput = true">点击这里,填写简介</view>
+			<view class="desc_input" v-else>
+				<input type="text" auto-focus placeholder-style="color:#ccc" placeholder="请输入简介" @blur="handleBlur" @confirm="handleClickDesc" />
+			</view>
+			<!-- 粉丝 关注 获赞 区域 -->
+			<view class="info">
+				<!-- 粉丝区域 -->
+				<view class="info_box">
+					<view class="box_num">17</view>
+					<view class="box_key">粉丝</view>
+				</view>
+				<!-- 关注区域 -->
+				<view class="info_box">
+					<view class="box_num">18</view>
+					<view class="box_key">关注</view>
+				</view>
+				<!-- 获赞区域 -->
+				<view class="info_box">
+					<view class="box_num">12</view>
+					<view class="box_key">获赞</view>
+				</view>
+			</view>
+		</view>
+
+		<!-- 内容区域 -->
+		<view class="body">
+			<!-- 分段器区域 -->
+			<view class="control">
+				<uni-segmented-control :current="current" :values="items" style-type="text" active-color="#096562" @clickItem="onClickItem" />
+			</view>
+
+			<!-- 推文列表区域 -->
+			<view class="list">
+				<!-- 每一个推文区域 -->
+				<view class="item_box" v-for="item in list" :key="item.id">
+					<!-- 视频推文 -->
+					<view class="box_video" v-if="item.type === 'video'">
+						<video :src="item.url"></video>
+					</view>
+
+					<!-- 图片推文 -->
+					<view class="box_image" v-if="item.type === 'image'">
+						<view class="image_box" v-for="(ele, index) in item.url" :key="index">
+							<img mode="acpectFill" :src="ele" />
+						</view>
+					</view>
+					<!-- 标题区域 -->
+					<view class="box_title">{{ item.title }}</view>
+					<view class="box_info">
+						<view class="info_time">{{ item.time }}</view>
+						<view class="info_town">{{ item.town }}</view>
+						<img class="img" src="../../static/index/upvote.png" />
+						<view class="info_like">{{ item.like }}</view>
+						<img class="img2" src="../../static/index/comment.png" />
+
+						<view class="info_comment">{{ item.comment }}</view>
+
+						<!-- 审核中印章区域 -->
+						<img v-if="current === 1" class="box_audit" src="../../static/index/audit.png" />
+						<!-- 已驳回印章区域 -->
+						<img v-if="current === 2" class="box_rejected" src="../../static/index/rejected.png" />
+					</view>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+export default {
+	data() {
+		return {
+			// 用户信息
+			userInfo: {},
+			// 状态栏高度
+			statusBarH: 0,
+			// 胶囊按钮栏高度
+			customBarH: 0,
+			// 顶部页面标题栏显示隐藏控制
+			headerType: false,
+			// 简介输入框显示隐藏控制
+			showInput: false,
+			current: 0,
+			items: ['全部推文', '审核中', '驳回'],
+			list: [
+				{
+					id: 1,
+					title: '靖安最美的景色,绝对值得来',
+					time: '09-20',
+					town: '双溪镇',
+					like: 99,
+					comment: 24,
+					type: 'image',
+					url: [
+						'https://chtech.ncjti.edu.cn/hotelReservation/fileload/download/1933617026前台.jpg',
+						'https://chtech.ncjti.edu.cn/hotelReservation/fileload/download/1933617026前台.jpg',
+						'https://chtech.ncjti.edu.cn/hotelReservation/fileload/download/1933617026前台.jpg',
+						'https://chtech.ncjti.edu.cn/hotelReservation/fileload/download/1933617026前台.jpg',
+						'https://chtech.ncjti.edu.cn/hotelReservation/fileload/download/1933617026前台.jpg',
+						'https://chtech.ncjti.edu.cn/hotelReservation/fileload/download/1933617026前台.jpg'
+					]
+				},
+				{
+					id: 2,
+					title: '靖安最美的景色,绝对值得来',
+					time: '09-20',
+					town: '双溪镇',
+					like: 66,
+					comment: 26,
+					type: 'video',
+					url: 'https://jinganminsu-1320402385.cos.ap-nanjing.myqcloud.com/static//20230927113127_ytGv5IurFspj2d102676797c75b76ac4d13238401c8f.mp4'
+				},
+				{
+					id: 3,
+					title: '靖安最美的景色,绝对值得来',
+					time: '09-20',
+					town: '双溪镇',
+					like: 66,
+					comment: 26,
+					type: 'video',
+					url: 'https://jinganminsu-1320402385.cos.ap-nanjing.myqcloud.com/static//20230927113127_ytGv5IurFspj2d102676797c75b76ac4d13238401c8f.mp4'
+				},
+				{
+					id: 4,
+					title: '靖安最美的景色,绝对值得来',
+					time: '09-20',
+					town: '双溪镇',
+					like: 99,
+					comment: 24,
+					type: 'image',
+					url: ['https://chtech.ncjti.edu.cn/hotelReservation/fileload/download/1933617026前台.jpg']
+				}
+			]
+		}
+	},
+	created() {
+		// 获取系统信息
+		uni.getSystemInfo({
+			success: (e) => {
+				// 获取状态栏高度
+				this.statusBarH = e.statusBarHeight + 10
+				// // 获取菜单按钮栏高度
+				let custom = uni.getMenuButtonBoundingClientRect()
+				this.customBarH = custom.height + 10
+			}
+		})
+	},
+	onPageScroll(e) {
+		if (e.scrollTop > 50) {
+			this.headerType = true
+		} else {
+			this.headerType = false
+		}
+	},
+	onShow() {
+		this.userInfo = uni.getStorageSync('userInfo')
+	},
+	methods: {
+		handleBack() {
+			uni.navigateBack(1)
+		},
+		handleClickDesc(e) {
+			console.log(e.detail)
+			this.showInput = false
+		},
+		handleBlur() {
+			this.showInput = false
+		},
+		onClickItem(e) {
+			this.current = e.currentIndex
+		}
+	}
+}
+</script>
+
+<style lang="scss" scoped>
+.container {
+	position: relative;
+	min-height: 100vh;
+
+	.title {
+		z-index: 999;
+		position: fixed;
+		top: 0;
+		left: 0;
+		right: 0;
+		font-size: 28rpx;
+		color: #fff;
+		text-align: center;
+	}
+
+	.title_icon {
+		z-index: 999;
+		position: fixed;
+		top: 0;
+		left: 10rpx;
+		right: 0;
+
+		img {
+			width: 47rpx;
+			height: 47rpx;
+		}
+	}
+
+	.header {
+		position: relative;
+		height: 510rpx;
+		color: #fff;
+		overflow: hidden;
+
+		img {
+			width: 100%;
+			height: 100%;
+		}
+
+		.img {
+			position: absolute;
+			top: 190rpx;
+			left: 32rpx;
+			width: 140rpx;
+			height: 140rpx;
+			border-radius: 50%;
+		}
+
+		.name {
+			position: absolute;
+			top: 200rpx;
+			left: 202rpx;
+			font-size: 40rpx;
+			font-weight: bold;
+		}
+
+		.number {
+			position: absolute;
+			top: 265rpx;
+			left: 202rpx;
+			font-size: 24rpx;
+			opacity: 0.5;
+		}
+
+		.desc {
+			position: absolute;
+			top: 350rpx;
+			left: 32rpx;
+			font-size: 28rpx;
+			color: #fff;
+		}
+
+		.desc_input {
+			position: absolute;
+			top: 350rpx;
+			left: 32rpx;
+			width: 300rpx;
+			height: 40rpx;
+			line-height: 40rpx;
+			font-size: 28rpx;
+			border-radius: 15rpx;
+			border: 1rpx solid #fff;
+
+			input {
+				box-sizing: border-box;
+				padding: 0 10rpx;
+				width: 100%;
+				height: 100%;
+			}
+		}
+
+		.info {
+			position: absolute;
+			top: 410rpx;
+			left: 0rpx;
+			display: flex;
+			font-size: 28rpx;
+			color: #fff;
+
+			.info_box {
+				display: flex;
+				flex-direction: column;
+				align-items: center;
+				padding: 0 20rpx 0 35rpx;
+
+				.box_num {
+				}
+				.box_key {
+				}
+			}
+		}
+	}
+
+	.body {
+		position: absolute;
+		top: 510rpx;
+		box-sizing: border-box;
+		width: 100%;
+		min-height: calc(100vh - 510rpx);
+		border-radius: 20rpx 20rpx 0 0;
+		background-color: #f7f7f7;
+
+		.control {
+			padding: 0 100rpx;
+			height: 95rpx;
+			background-color: #fff;
+		}
+
+		.list {
+			padding: 20rpx 30rpx;
+
+			.item_box {
+				margin-bottom: 22rpx;
+				padding-bottom: 30rpx;
+				width: 690rpx;
+				border-radius: 12rpx;
+				border-bottom: 1rpx solid #e6e6e6;
+				background-color: #fff;
+
+				.box_video {
+					height: 335rpx;
+					border-radius: 12rpx;
+					background-color: skyblue;
+
+					video {
+						width: 100%;
+						height: 100%;
+					}
+				}
+
+				.box_image {
+					display: grid;
+					grid-template-columns: repeat(3, 1fr);
+					grid-auto-rows: auto;
+					gap: 20rpx;
+
+					.image_box {
+						width: 216rpx;
+						height: 126rpx;
+
+						img {
+							width: 100%;
+							height: 100%;
+							border-radius: 12rpx;
+						}
+					}
+				}
+
+				.box_title {
+					padding: 10rpx 25rpx;
+					font-size: 28rpx;
+					font-weight: bold;
+				}
+
+				.box_info {
+					position: relative;
+					display: flex;
+					align-items: center;
+					padding-left: 25rpx;
+					color: #999999;
+					font-size: 24rpx;
+
+					.info_time {
+					}
+					.info_town {
+						margin-left: 15rpx;
+					}
+
+					.img {
+						margin-left: auto;
+						width: 28rpx;
+						height: 28rpx;
+					}
+
+					.info_like {
+						margin-left: 10rpx;
+					}
+					.img2 {
+						margin-left: 15rpx;
+						width: 25rpx;
+						height: 25rpx;
+					}
+					.info_comment {
+						margin: 0 10rpx;
+					}
+
+					.box_audit {
+						position: absolute;
+						top: -45rpx;
+						left: 410rpx;
+						width: 100rpx;
+						height: 100rpx;
+					}
+
+					.box_rejected {
+						position: absolute;
+						top: -45rpx;
+						left: 410rpx;
+						width: 100rpx;
+						height: 100rpx;
+					}
+				}
+			}
+		}
+	}
+}
+</style>

+ 218 - 10
pages/send/send.vue

@@ -1,18 +1,23 @@
 <template>
 	<view class="container">
 		<!-- 页面标题区域 -->
-		<view class="header" :style="{ height: customBarH * 2 + 'rpx', paddingTop: statusBarH * 2 + 'rpx' }">
-			<img src="../../static/index/left2.png" :style="{ paddingTop: statusBarH * 2 + 'rpx' }" />
+		<view v-if="!headerType" class="header" :style="{ height: customBarH * 2 + 'rpx', paddingTop: statusBarH * 2 + 'rpx' }">
+			<img src="../../static/index/left2.png" :style="{ paddingTop: statusBarH * 2 + 'rpx' }" @click="handleBack" />
+			<view class="header_text">发推文</view>
+		</view>
+
+		<view v-else class="header2" :style="{ height: customBarH * 2 + 'rpx', paddingTop: statusBarH * 2 + 'rpx' }">
+			<img src="../../static/index/left2.png" :style="{ paddingTop: statusBarH * 2 + 'rpx' }" @click="handleBack" />
 			<view class="header_text">发推文</view>
 		</view>
 
 		<!-- 图片视频上传展示区域 -->
-		<view class="upload">
-			<view class="upload_img">
+		<view class="upload" :style="{ paddingTop: statusBarH * 2 + customBarH * 2 + 'rpx' }">
+			<view class="upload_img" @click="handleImage">
 				<img src="../../static/index/upload-img.png" />
 				添加图片
 			</view>
-			<view class="upload_video">
+			<view class="upload_video" @click="handleVideo">
 				<img src="../../static/index/upload-video.png" />
 				添加视频
 			</view>
@@ -31,13 +36,34 @@
 		</view>
 
 		<!-- 添加地点区域 -->
-		<view class="add_place">
+		<view class="add_place" @click="handleGoPage('/pages/addPlace/addPlace')">
 			<img class="img" src="../../static/index/send-address.png" />
 			添加地点
 			<img class="img2" src="../../static/index/send-right.png" />
 		</view>
 
 		<!-- 关联民宿区域 -->
+		<view class="and">
+			<img class="img" src="../../static/index/send-binding.png" />
+			关联民宿
+		</view>
+		<!-- 关联民宿列表区域 -->
+		<view class="and_list">
+			<!-- 每一个民宿区域 -->
+			<view class="and_box" :class="{ active: item.is_collect_hotel }" v-for="item in andList" :key="item.id" @click="handleChange(item)">{{ item.hotel_name }}</view>
+		</view>
+
+		<!-- 发布按钮区域 -->
+		<view class="sub_btn" @click="handleClickSub">发布</view>
+
+		<!-- 状态弹窗区域 -->
+		<uni-popup ref="popup" :is-mask-click="false" type="center">
+			<view class="popup_body">
+				<img src="../../static/index/success.png" />
+				<view class="msg">发布成功</view>
+				<view class="btn" @click="handleClickLook">查看推文</view>
+			</view>
+		</uni-popup>
 	</view>
 </template>
 
@@ -45,6 +71,8 @@
 export default {
 	data() {
 		return {
+			// 顶部页面标题栏显示隐藏控制
+			headerType: false,
 			// 状态栏高度
 			statusBarH: 0,
 			// 胶囊按钮栏高度
@@ -52,7 +80,10 @@ export default {
 			// 标题数据
 			titleValue: '',
 			// 推文内容区域
-			contentValue: ''
+			contentValue: '',
+			// activeIndex: null,
+			// 关联民宿列表
+			andList: []
 		}
 	},
 	created() {
@@ -66,19 +97,107 @@ export default {
 				this.customBarH = custom.height + 10
 			}
 		})
+	},
+	onLoad() {
+		uni.$on('add', this.add)
+	},
+	onPageScroll(e) {
+		if (e.scrollTop > 50) {
+			this.headerType = true
+		} else {
+			this.headerType = false
+		}
+	},
+	methods: {
+		// 切换关联民宿回调
+		handleChange(item) {
+			item.is_collect_hotel = !item.is_collect_hotel
+			// this.activeIndex = index
+		},
+		// 点击添加图片回调
+		handleImage() {
+			uni.chooseMedia({
+				count: 9,
+				mediaType: ['image'],
+				success: (res) => {
+					console.log(res.tempFiles)
+				}
+			})
+		},
+		// 点击添加视频回调
+		handleVideo() {
+			uni.chooseMedia({
+				count: 9,
+				mediaType: ['video'],
+				success: (res) => {
+					console.log(res.tempFiles)
+				}
+			})
+		},
+		// 点击添加地点回调
+		handleGoPage(url) {
+			uni.navigateTo({
+				url
+			})
+		},
+		// 全局自定义事件
+		add(e) {
+			this.andList = e.list
+		},
+		// 点击发布按钮回调
+		handleClickSub() {
+			console.log(this.andList)
+			let temList = this.andList.filter((ele) => ele.is_collect_hotel)
+			console.log(temList)
+			this.$refs.popup.open()
+		},
+		// 点击弹窗查看推文按钮回调
+		handleClickLook() {
+			this.$refs.popup.close()
+		},
+		handleBack() {
+			uni.navigateBack(1)
+		}
 	}
 }
 </script>
 
 <style lang="scss">
 .container {
-	height: 100vh;
+	padding-bottom: 80rpx;
+	min-height: 100vh;
 	background-color: #fff;
 
 	.header {
+		position: fixed;
+		top: 0;
+		left: 0;
+		right: 0;
+		display: flex;
+		justify-content: center;
+		// position: relative;
+		background-color: #fff;
+
+		img {
+			position: absolute;
+			top: 0;
+			left: 0;
+			width: 47rpx;
+			height: 47rpx;
+		}
+
+		.header_text {
+			font-size: 34rpx;
+		}
+	}
+
+	.header2 {
+		position: fixed;
+		top: 0;
+		left: 0;
+		right: 0;
 		display: flex;
 		justify-content: center;
-		position: relative;
 		background-color: #fff;
 
 		img {
@@ -95,7 +214,8 @@ export default {
 	}
 
 	.upload {
-		padding: 30rpx 0 10rpx;
+		margin-top: 30rpx;
+		padding-bottom: 10rpx;
 		display: flex;
 
 		.upload_img {
@@ -195,5 +315,93 @@ export default {
 			height: 40rpx;
 		}
 	}
+
+	.and {
+		display: flex;
+		align-items: center;
+		margin: auto;
+		width: 710rpx;
+		height: 88rpx;
+		font-size: 28rpx;
+
+		.img {
+			margin-left: 10rpx;
+			margin-right: 8rpx;
+			width: 42rpx;
+			height: 42rpx;
+		}
+	}
+
+	.and_list {
+		display: flex;
+		flex-wrap: wrap;
+		margin: auto;
+		width: 710rpx;
+
+		.and_box {
+			margin-bottom: 20rpx;
+			margin-right: 16rpx;
+			padding: 0 44rpx;
+			display: flex;
+			justify-content: center;
+			align-items: center;
+			height: 55rpx;
+			color: #999999;
+			font-size: 24rpx;
+			border-radius: 66rpx;
+			background-color: #f2f2f2;
+		}
+
+		.active {
+			color: #fff;
+			background-color: #096562;
+		}
+	}
+
+	.sub_btn {
+		display: flex;
+		justify-content: center;
+		align-items: center;
+		margin: 310rpx auto 0;
+		width: 710rpx;
+		height: 100rpx;
+		color: #fff;
+		font-size: 32rpx;
+		border-radius: 64rpx;
+		background-color: #096562;
+	}
+
+	.popup_body {
+		display: flex;
+		flex-direction: column;
+		justify-content: space-evenly;
+		align-items: center;
+		width: 481rpx;
+		height: 404rpx;
+		border-radius: 23rpx;
+		background-color: #fff;
+
+		img {
+			width: 112rpx;
+			height: 112rpx;
+		}
+
+		.msg {
+			color: #0f194d;
+			font-size: 28rpx;
+		}
+
+		.btn {
+			display: flex;
+			justify-content: center;
+			align-items: center;
+			width: 396rpx;
+			height: 76rpx;
+			font-size: 26rpx;
+			color: #fff;
+			border-radius: 43rpx;
+			background: linear-gradient(90deg, rgba(11, 193, 150, 1) 0%, rgba(9, 101, 98, 1) 100%);
+		}
+	}
 }
 </style>

+ 927 - 0
pages/tweetDetail/tweetDetail.vue

@@ -0,0 +1,927 @@
+<template>
+	<view class="container" :style="'overflow:' + (showPage ? 'hidden' : 'visible')" v-if="info">
+		<!-- 作者信息区域 -->
+		<view class="author">
+			<img mode="aspectFill" :src="info.coverImg" />
+			<view class="author_name">{{ info.hotel_name }}</view>
+			<view class="author_btn">关注</view>
+		</view>
+
+		<!-- 轮播图区域 -->
+		<swiper indicator-color="#ccc" indicator-active-color="#096562" indicator-dots autoplay circular class="swiper">
+			<swiper-item class="swiper_item" v-for="(item, current) in info.hotelFileInfoList" :key="item.id" @click="handleClickSwiper(info.hotelFileInfoList, current)">
+				<img mode="aspectFill" class="img" :src="item.url" />
+			</swiper-item>
+		</swiper>
+
+		<!-- 推文信息区域 -->
+		<view class="tweet">
+			<view class="tweet_title">{{ info.hposition }}</view>
+			<view class="tweet_msg">{{ info.remark }}</view>
+			<view class="tweet_info">{{ info.openTime.slice(5, 10) }} {{ info.hotelTownshipName }}</view>
+		</view>
+
+		<!-- 关联民宿区域 -->
+		<view class="and">
+			<!-- 每一个关联民宿区域 -->
+			<view class="and_box" v-for="item in info.houseList.slice(0, 2)" :key="item.id">
+				<img mode="aspectFill" :src="item.fileInfoList[0].url" />
+				<view class="box_detail">
+					<view class="detail_name">
+						{{ item.hName }}
+						<img class="img" src="../../static/index/like.png" />
+					</view>
+					<view class="detail_leave">
+						{{ item.hName }}
+					</view>
+					<view class="detail_num">
+						<view class="num_rate">{{ item.number.toFixed(1) }}分</view>
+						<view class="num_comment">{{ item.number }}条评论</view>
+						<view class="num_price">
+							¥{{ item.price }}
+							<text>起</text>
+						</view>
+					</view>
+				</view>
+			</view>
+
+			<!-- 关联民宿查看全部区域 -->
+			<view class="and_all" @click="handleClickALL">查看全部 >></view>
+		</view>
+
+		<!-- 点赞人数区域 -->
+		<view class="like">
+			<uv-avatar-group :urls="info.hotelFileInfoList" keyName="url" size="30" gap="0.3" maxCount="10"></uv-avatar-group>
+			<view class="like_text" @click="handleGoPage('/pages/likeList/likeList')">{{ info.roomNumber }}人已赞</view>
+		</view>
+
+		<!-- 评论总条数区域 -->
+		<view class="total">共15条评论</view>
+
+		<!-- 评论输入框区域 -->
+		<view class="input_box">
+			<input type="text" confirm-type="send" placeholder="爱评论的人运气都不差" @confirm="handleInput" />
+		</view>
+
+		<!-- 评论列表区域 -->
+		<view class="comment_list">
+			<!-- 每一个评论区域 -->
+			<view class="comment_box" v-for="item in info.houseList.slice(0, 3)" :key="item.id">
+				<view class="box_left">
+					<img mode="aspectFill" :src="item.fileInfoList[0].url" />
+				</view>
+				<view class="box_right">
+					<view class="right_name">{{ item.hName }}</view>
+					<view class="right_content">{{ item.hConfig }}</view>
+					<view class="right_time">{{ item.createDate.slice(6, 11) }}</view>
+				</view>
+			</view>
+
+			<!-- 查看全部评论区域 -->
+			<view class="comment_all" @click="handleClickComment">查看全部评论 >></view>
+		</view>
+
+		<!-- 相关推文区域 -->
+		<view class="related" v-if="info.houseList.length">
+			<view class="related_title">相关推文</view>
+			<!-- 相关推文列表区域 -->
+			<view class="related_list">
+				<!-- 每一个推文区域 -->
+				<view class="related_box" v-for="item in info.houseList.slice(0, 2)" :key="item.id">
+					<img mode="aspectFill" :src="item.fileInfoList[0].url" />
+					<view class="box_content">{{ item.hConfig }}</view>
+					<view class="box_info">
+						<img class="img" mode="aspectFill" :src="item.fileInfoList[0].url" />
+						<view class="info_name">{{ item.hName }}</view>
+						<img class="img2" mode="aspectFill" src="../../static/index/like.png" />
+						<view class="info_count">{{ item.number }}</view>
+					</view>
+					<!-- 乡镇信息区域 -->
+					<view class="box_town">{{ item.hName }}</view>
+				</view>
+			</view>
+		</view>
+
+		<!-- 底部点赞 评论 收藏区域 -->
+		<view class="tab">
+			<!-- 点赞 -->
+			<view class="tab_box">
+				<img class="img" src="../../static/index/upvote.png" />
+				123
+			</view>
+			<!-- 评论 -->
+			<view class="tab_box" @click="handleClickComment">
+				<img class="img2" src="../../static/index/comment.png" />
+				123
+			</view>
+			<!-- 收藏 -->
+			<view class="tab_box">
+				<img class="img" src="../../static/index/like.png" />
+				123
+			</view>
+		</view>
+
+		<!-- 点击评论弹窗区域 -->
+		<uv-popup ref="popup" bgColor="none" :safeAreaInsetBottom="false">
+			<view class="body_pop">
+				<!-- 评论总数区域 -->
+				<view class="pop_title">
+					共25条评论
+					<img class="pop_icon" src="../../static/index/close3.png" @click="handleClosePop" />
+				</view>
+
+				<!-- 评论列表区域 -->
+				<view class="pop_list" v-if="commentList.length">
+					<!-- 每一条评论区域 -->
+					<view class="pop_item" v-for="item in commentList" :key="item.id">
+						<!-- 用户区域 -->
+						<view class="item_user" @click="handleComment(item)">
+							<img mode="aspectFill" :src="item.headPhoto" />
+							<view class="user_info">
+								{{ item.userName }}
+							</view>
+						</view>
+						<!-- 评价内容区域 -->
+						<view class="item_content" @click="handleComment(item)">
+							<view class="content_top">
+								{{ item.content }}
+							</view>
+							<view class="content_bottom">
+								{{ item.dateTime }}
+							</view>
+						</view>
+
+						<!-- 二级评论区域 -->
+						<view class="item_child">
+							<CommentChild v-if="item.commentVoList" :list="item.commentVoList" :commentParentId="info.id" />
+						</view>
+					</view>
+				</view>
+			</view>
+
+			<!-- 评论输入框区域 -->
+			<view class="body_input">
+				<view class="input_box">
+					<input type="text" placeholder="说点什么呢~" @confirm="handleInputPop" />
+				</view>
+			</view>
+		</uv-popup>
+
+		<!-- 点击关联民宿弹窗区域 -->
+		<uv-popup ref="popup_bind" bgColor="none" :safeAreaInsetBottom="false">
+			<view class="body_pop">
+				<!-- 评论总数区域 -->
+				<view class="pop_title">
+					全部
+					<img class="pop_icon" src="../../static/index/close3.png" @click="handleClosePopBind" />
+				</view>
+
+				<!-- 关联民宿列表区域 -->
+				<view class="pop_list" v-if="bindList.length">
+					<!-- 每一个民宿区域 -->
+					<view class="pop_item_bind" v-for="item in bindList" :key="item.id">
+						<img mode="aspectFill" :src="item.url" />
+						<view class="box_detail">
+							<view class="detail_name">
+								{{ item.hName }}
+								<img class="img" src="../../static/index/like.png" />
+							</view>
+							<view class="detail_leave">
+								{{ item.hName }}
+							</view>
+							<view class="detail_num">
+								<view class="num_rate">{{ item.number.toFixed(1) }}分</view>
+								<view class="num_comment">{{ item.number }}条评论</view>
+								<view class="num_price">
+									¥{{ item.price }}
+									<text>起</text>
+								</view>
+							</view>
+						</view>
+					</view>
+				</view>
+			</view>
+		</uv-popup>
+	</view>
+</template>
+
+<script>
+import CommentChild from '@/components/commentChild2.vue'
+export default {
+	components: { CommentChild },
+	data() {
+		return {
+			// 滚动穿透控制
+			showPage: false,
+			hotelId: '',
+			startTime: '',
+			endTime: '',
+			info: null,
+			commentList: [
+				{
+					id: 1,
+					headPhoto: 'https://chtech.ncjti.edu.cn/hotelReservation/fileload/download/1374205869tmp_ec40ba855cc6003b2a47877a654dceaf9fc74230a8e9cf25.jpg',
+					userName: '张三',
+					dateTime: '2023-09-22 16:54:38',
+					content: '策划斯哈斯是回家哦啊好啦干哈会离开是尴尬尬策划斯哈斯是回家哦啊好啦干哈会离开是尴尬尬策划斯哈斯是回家哦啊好啦干哈会离开是尴尬尬',
+					commentVoList: [
+						{
+							id: 11,
+							headPhoto: 'https://chtech.ncjti.edu.cn/hotelReservation/fileload/download/1374205869tmp_ec40ba855cc6003b2a47877a654dceaf9fc74230a8e9cf25.jpg',
+							userName: '小王',
+							commentName: '老六',
+							dateTime: '2023-09-22 16:54:38',
+							content: '策划斯哈斯是回家哦啊好啦干哈会离开是尴尬尬策划斯哈斯是回家哦啊好啦干哈会离开是尴尬尬策划斯哈斯是回家哦啊好啦干哈会离开是尴尬尬',
+							commentVoList: [
+								{
+									id: 111,
+									headPhoto: 'https://chtech.ncjti.edu.cn/hotelReservation/fileload/download/1374205869tmp_ec40ba855cc6003b2a47877a654dceaf9fc74230a8e9cf25.jpg',
+									userName: '小王',
+									commentName: '老六',
+									dateTime: '2023-09-22 16:54:38',
+									content: '策划斯哈斯是回家哦啊好啦干哈会离开是尴尬尬策划斯哈斯是回家哦啊好啦干哈会离开是尴尬尬策划斯哈斯是回家哦啊好啦干哈会离开是尴尬尬',
+									commentVoList: []
+								}
+							]
+						}
+					]
+				},
+				{
+					id: 2,
+					headPhoto: 'https://chtech.ncjti.edu.cn/hotelReservation/fileload/download/1374205869tmp_ec40ba855cc6003b2a47877a654dceaf9fc74230a8e9cf25.jpg',
+					userName: '李四',
+					dateTime: '2023-09-22 16:54:38',
+					content: '呜呜呜呜呜呜吾问无为谓策划斯哈斯是回家哦啊好啦干哈会离开是尴尬尬',
+					commentVoList: []
+				}
+			],
+			bindList: [
+				{
+					id: 1,
+					url: 'https://chtech.ncjti.edu.cn/hotelReservation/fileload/download/14479079250812_2.jpg',
+					hName: '开心',
+					number: 5,
+					price: 166
+				},
+				{
+					id: 2,
+					url: 'https://chtech.ncjti.edu.cn/hotelReservation/fileload/download/14479079250812_2.jpg',
+					hName: '健康',
+					number: 6,
+					price: 186
+				},
+				{
+					id: 3,
+					url: 'https://chtech.ncjti.edu.cn/hotelReservation/fileload/download/14479079250812_2.jpg',
+					hName: '幸福',
+					number: 9,
+					price: 126
+				},
+				{
+					id: 4,
+					url: 'https://chtech.ncjti.edu.cn/hotelReservation/fileload/download/14479079250812_2.jpg',
+					hName: '开心',
+					number: 5,
+					price: 166
+				},
+				{
+					id: 5,
+					url: 'https://chtech.ncjti.edu.cn/hotelReservation/fileload/download/14479079250812_2.jpg',
+					hName: '健康',
+					number: 6,
+					price: 186
+				},
+				{
+					id: 6,
+					url: 'https://chtech.ncjti.edu.cn/hotelReservation/fileload/download/14479079250812_2.jpg',
+					hName: '幸福',
+					number: 9,
+					price: 126
+				}
+			]
+		}
+	},
+	onLoad(options) {
+		this.hotelId = options.id
+		this.getTimes()
+		this.getHotelInfo()
+	},
+	methods: {
+		// 获取民宿信息
+		async getHotelInfo(id) {
+			const res = await this.$myRequest({
+				url: '/mhotel/ahpgetHouseByHotelId.action',
+				data: {
+					hotelId: id ? id : this.hotelId,
+					userId: uni.getStorageSync('userInfo').id,
+					queryStartTime: this.startTime,
+					queryEndTime: this.endTime
+				}
+			})
+			// console.log(res)
+			if (res.code === 200) {
+				this.info = res.data.data
+			}
+		},
+		// 点击轮播图图片回调
+		handleClickSwiper(urls, current) {
+			const temList = urls.map((ele) => ele.url)
+			uni.previewImage({
+				urls: temList,
+				current
+			})
+		},
+		handleInput(e) {
+			console.log(e)
+			console.log(999)
+		},
+		// 评论弹窗输入框回调
+		handleInputPop(e) {
+			console.log(e)
+		},
+		// 点击底部tab评论按钮回调
+		handleClickComment() {
+			this.showPage = true
+			this.$refs.popup.open('bottom')
+		},
+		// 点击评论弹窗关闭图标回调
+		handleClosePop() {
+			this.showPage = false
+			this.$refs.popup.close()
+		},
+		// 点击关联民宿查看全部按钮回调
+		handleClickALL() {
+			this.showPage = true
+			this.$refs.popup_bind.open('bottom')
+		},
+		// 点击关联民宿弹窗关闭图标回调
+		handleClosePopBind() {
+			this.showPage = false
+			this.$refs.popup_bind.close()
+		},
+		handleGoPage(url) {
+			uni.navigateTo({
+				url
+			})
+		},
+		handleComment() {},
+		// 获取今明两天的日期 YYYY-MM-DD
+		getTimes() {
+			// 今天
+			let today = new Date()
+			// 明天
+			let tomorrow = new Date(today.getTime() + 24 * 60 * 60 * 1000)
+			let late = new Date(today.getTime() + 24 * 60 * 60 * 1000 * 14)
+
+			this.startTime = `${today.getFullYear()}-${(today.getMonth() + 1).toString().padStart(2, 0)}-${today.getDate().toString().padStart(2, 0)}`
+			this.endTime = `${tomorrow.getFullYear()}-${(tomorrow.getMonth() + 1).toString().padStart(2, 0)}-${tomorrow.getDate().toString().padStart(2, 0)}`
+		}
+	}
+}
+</script>
+
+<style lang="scss" scoped>
+.container {
+	min-height: 100vh;
+	background-color: #fff;
+
+	.author {
+		display: flex;
+		align-items: center;
+		padding: 0 20rpx;
+		height: 100rpx;
+
+		img {
+			width: 50rpx;
+			height: 50rpx;
+			border-radius: 50%;
+		}
+
+		.author_name {
+			margin-left: 15rpx;
+			font-size: 28rpx;
+			font-weight: bold;
+		}
+
+		.author_btn {
+			display: flex;
+			justify-content: center;
+			align-items: center;
+			margin-left: auto;
+			width: 107rpx;
+			height: 50rpx;
+			color: #096562;
+			font-size: 24rpx;
+			border-radius: 69rpx;
+			border: 1rpx solid #096562;
+		}
+	}
+
+	.swiper {
+		height: 495rpx;
+
+		.swiper_item {
+			width: 100%;
+			height: 100%;
+
+			.img {
+				width: 100%;
+				height: 440rpx;
+			}
+		}
+	}
+
+	.tweet {
+		margin: auto;
+		width: 710rpx;
+		border-bottom: 1rpx solid #e6e6e6;
+
+		.tweet_title {
+			line-height: 50rpx;
+			font-size: 32rpx;
+			font-weight: bold;
+		}
+
+		.tweet_msg {
+			margin-top: 10rpx;
+			line-height: 40rpx;
+			font-size: 28rpx;
+		}
+
+		.tweet_info {
+			margin-top: 20rpx;
+			height: 55rpx;
+			color: #999999;
+			font-size: 24rpx;
+		}
+	}
+
+	.and {
+		padding: 20rpx 0;
+		margin: auto;
+		width: 710rpx;
+		border-bottom: 1rpx solid #e6e6e6;
+
+		.and_box {
+			display: flex;
+			margin-bottom: 20rpx;
+			width: 710rpx;
+			height: 150rpx;
+			background-color: #f2f2f2;
+
+			img {
+				width: 126rpx;
+				height: 150rpx;
+			}
+
+			.box_detail {
+				flex: 1;
+				display: flex;
+				flex-direction: column;
+				justify-content: space-evenly;
+				padding: 0 23rpx;
+				overflow: hidden;
+
+				.detail_name {
+					display: flex;
+					justify-content: space-between;
+					align-items: center;
+					font-size: 28rpx;
+					font-weight: bold;
+					overflow: hidden;
+					text-overflow: ellipsis;
+					white-space: nowrap;
+
+					.img {
+						width: 40rpx;
+						height: 40rpx;
+					}
+				}
+
+				.detail_leave {
+					color: #808080;
+					font-size: 24rpx;
+				}
+
+				.detail_num {
+					display: flex;
+					align-items: center;
+					font-size: 24rpx;
+
+					.num_rate {
+						color: #ff5733;
+					}
+
+					.num_comment {
+						margin-left: 15rpx;
+						color: #a6a6a6;
+					}
+
+					.num_price {
+						margin-left: auto;
+						font-size: 28rpx;
+						color: #ff5733;
+
+						text {
+							margin-left: 5rpx;
+							font-size: 20rpx;
+							color: #a6a6a6;
+						}
+					}
+				}
+			}
+		}
+
+		.and_all {
+			display: flex;
+			justify-content: center;
+			align-items: center;
+			width: 710rpx;
+			height: 80rpx;
+			color: #096562;
+			font-size: 28rpx;
+			background-color: #f2f2f2;
+		}
+	}
+
+	.like {
+		display: flex;
+		align-items: center;
+		padding: 0 20rpx;
+		height: 108rpx;
+		color: #808080;
+		font-size: 28rpx;
+
+		.like_text {
+			margin-left: 18rpx;
+		}
+	}
+
+	.total {
+		margin: auto;
+		width: 710rpx;
+		height: 60rpx;
+		line-height: 60rpx;
+		font-size: 28rpx;
+		color: #808080;
+	}
+
+	.input_box {
+		margin: auto;
+		width: 710rpx;
+		height: 66rpx;
+		font-size: 24rpx;
+		border-radius: 47rpx;
+		background-color: #f2f2f2;
+
+		input {
+			box-sizing: border-box;
+			padding: 0 33rpx;
+			width: 100%;
+			height: 100%;
+		}
+	}
+
+	.comment_list {
+		margin: auto;
+		width: 710rpx;
+
+		.comment_box {
+			display: flex;
+			box-sizing: border-box;
+			padding-top: 30rpx;
+
+			.box_left {
+				width: 75rpx;
+
+				img {
+					width: 60rpx;
+					height: 60rpx;
+					border-radius: 50%;
+				}
+			}
+
+			.box_right {
+				flex: 1;
+				flex-wrap: wrap;
+				font-size: 24rpx;
+				border-bottom: 1rpx solid #e6e6e6;
+
+				.right_name {
+					color: #808080;
+				}
+				.right_content {
+					margin-top: 12rpx;
+					word-break: break-all;
+				}
+				.right_time {
+					margin: 12rpx 0 20rpx;
+					color: #808080;
+				}
+			}
+		}
+
+		.comment_all {
+			display: flex;
+			justify-content: center;
+			align-items: center;
+			width: 710rpx;
+			height: 80rpx;
+			color: #096562;
+			font-size: 28rpx;
+			background-color: #f2f2f2;
+		}
+	}
+
+	.related {
+		margin: auto;
+		padding-bottom: 130rpx;
+		width: 710rpx;
+
+		.related_title {
+			line-height: 105rpx;
+			font-size: 32rpx;
+			font-weight: bold;
+		}
+		.related_list {
+			display: grid;
+			grid-template-columns: 1fr 1fr;
+			grid-auto-rows: auto;
+			gap: 40rpx;
+
+			.related_box {
+				position: relative;
+				width: 335rpx;
+				background-color: #f7f7f7;
+
+				img {
+					width: 335rpx;
+					height: 463rpx;
+					border-radius: 10rpx 10rpx 0 0;
+				}
+
+				.box_content {
+					padding: 10rpx 60rpx 10rpx 20rpx;
+					font-size: 28rpx;
+					font-weight: bold;
+					word-break: break-all;
+					overflow: hidden;
+					text-overflow: ellipsis;
+					white-space: nowrap;
+				}
+
+				.box_info {
+					padding: 0 20rpx;
+					display: flex;
+					align-items: center;
+					height: 50rpx;
+					font-size: 20rpx;
+					color: #666666;
+
+					.img {
+						width: 30rpx;
+						height: 30rpx;
+						border-radius: 50%;
+					}
+
+					.info_name {
+						margin-left: 10rpx;
+					}
+
+					.img2 {
+						margin-left: auto;
+						width: 28rpx;
+						height: 28rpx;
+					}
+
+					.info_count {
+						margin-left: 5rpx;
+					}
+				}
+
+				.box_town {
+					position: absolute;
+					top: 405rpx;
+					left: 22rpx;
+					padding: 0 20rpx;
+					display: flex;
+					justify-content: center;
+					align-items: center;
+					height: 43rpx;
+					color: #fff;
+					font-size: 20rpx;
+					border-radius: 42rpx;
+					background-color: rgba(0, 0, 0, 0.3);
+				}
+			}
+		}
+	}
+
+	.tab {
+		position: fixed;
+		bottom: 0;
+		left: 0;
+		z-index: 999;
+		display: flex;
+		justify-content: space-evenly;
+		align-items: center;
+		width: 100%;
+		height: 110rpx;
+		border-top: 1rpx solid #e6e6e6;
+		background-color: #fff;
+
+		.tab_box {
+			display: flex;
+			justify-content: center;
+			align-items: center;
+			width: 200rpx;
+
+			.img {
+				margin-right: 10rpx;
+				width: 45rpx;
+				height: 45rpx;
+			}
+
+			.img2 {
+				margin-right: 10rpx;
+				width: 35rpx;
+				height: 35rpx;
+			}
+		}
+	}
+
+	.body_pop {
+		position: relative;
+		max-height: 955rpx;
+		border-radius: 22rpx 22rpx 0 0;
+		background-color: #fff;
+		overflow-y: auto;
+
+		.pop_title {
+			position: fixed;
+			top: 0;
+			left: 0;
+			right: 0;
+			display: flex;
+			justify-content: center;
+			align-items: center;
+			height: 110rpx;
+			font-size: 28rpx;
+			background-color: #fff;
+
+			.pop_icon {
+				position: absolute;
+				top: 35rpx;
+				right: 35rpx;
+				width: 36rpx;
+				height: 36rpx;
+			}
+		}
+
+		.pop_list {
+			padding: 110rpx 20rpx 30rpx;
+
+			.pop_item {
+				margin-bottom: 10rpx;
+				padding-bottom: 10rpx;
+				border-bottom: 1rpx solid #e6e6e6;
+
+				.item_user {
+					display: flex;
+					align-items: center;
+					height: 70rpx;
+
+					img {
+						width: 60rpx;
+						height: 60rpx;
+						border-radius: 50%;
+					}
+
+					.user_info {
+						display: flex;
+						align-items: center;
+						margin-left: 18rpx;
+						height: 70rpx;
+						color: #808080;
+						font-size: 24rpx;
+					}
+				}
+
+				.item_content {
+					margin-left: 80rpx;
+					font-size: 24rpx;
+
+					.content_top {
+					}
+
+					.content_bottom {
+						margin: 10rpx 0;
+						color: #808080;
+					}
+
+					.content_key {
+						color: #808080;
+					}
+				}
+
+				.item_child {
+					margin-left: 50rpx;
+				}
+			}
+
+			.pop_item_bind {
+				display: flex;
+				margin-bottom: 20rpx;
+				width: 710rpx;
+				height: 150rpx;
+				background-color: #f2f2f2;
+
+				img {
+					width: 126rpx;
+					height: 150rpx;
+				}
+
+				.box_detail {
+					flex: 1;
+					display: flex;
+					flex-direction: column;
+					justify-content: space-evenly;
+					padding: 0 23rpx;
+					overflow: hidden;
+
+					.detail_name {
+						display: flex;
+						justify-content: space-between;
+						align-items: center;
+						font-size: 28rpx;
+						font-weight: bold;
+						overflow: hidden;
+						text-overflow: ellipsis;
+						white-space: nowrap;
+
+						.img {
+							width: 40rpx;
+							height: 40rpx;
+						}
+					}
+
+					.detail_leave {
+						color: #808080;
+						font-size: 24rpx;
+					}
+
+					.detail_num {
+						display: flex;
+						align-items: center;
+						font-size: 24rpx;
+
+						.num_rate {
+							color: #ff5733;
+						}
+
+						.num_comment {
+							margin-left: 15rpx;
+							color: #a6a6a6;
+						}
+
+						.num_price {
+							margin-left: auto;
+							font-size: 28rpx;
+							color: #ff5733;
+
+							text {
+								margin-left: 5rpx;
+								font-size: 20rpx;
+								color: #a6a6a6;
+							}
+						}
+					}
+				}
+			}
+		}
+	}
+
+	.body_input {
+		display: flex;
+		justify-content: center;
+		align-items: center;
+		height: 118rpx;
+		border-top: 1rpx solid #cccccc;
+		background-color: #fff;
+
+		.input_box {
+			width: 710rpx;
+			border-radius: 77rpx;
+			font-size: 24rpx;
+			background-color: #f2f2f2;
+
+			input {
+				box-sizing: border-box;
+				padding: 0 30rpx;
+				width: 100%;
+				height: 100%;
+			}
+		}
+	}
+}
+</style>

BIN
static/images/community-active.png


BIN
static/images/community.png


BIN
static/index/audit.png


BIN
static/index/close3.png


BIN
static/index/like-active.png


BIN
static/index/like.png


BIN
static/index/rejected.png


BIN
static/index/upvote.png


BIN
static/search/icon.png


BIN
static/search/icon2.png


BIN
static/search/icon3.png


BIN
static/search/icon4.png


BIN
static/search/icon5.png


BIN
static/search/icon6.png


BIN
static/search/icon7.png


BIN
static/search/icon8.png


+ 23 - 0
uni_modules/uni-fab/changelog.md

@@ -0,0 +1,23 @@
+## 1.2.5(2023-03-29)
+- 新增 pattern.icon 属性,可自定义图标
+## 1.2.4(2022-09-07)
+小程序端由于 style 使用了对象导致报错,[详情](https://ask.dcloud.net.cn/question/152790?item_id=211778&rf=false)
+## 1.2.3(2022-09-05)
+- 修复 nvue 环境下,具有 tabBar 时,fab 组件下部位置无法正常获取 --window-bottom 的bug,详见:[https://ask.dcloud.net.cn/question/110638?notification_id=826310](https://ask.dcloud.net.cn/question/110638?notification_id=826310)
+## 1.2.2(2021-12-29)
+- 更新 组件依赖
+## 1.2.1(2021-11-19)
+- 修复 阴影颜色不正确的bug
+## 1.2.0(2021-11-19)
+- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
+- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-fab](https://uniapp.dcloud.io/component/uniui/uni-fab)
+## 1.1.1(2021-11-09) 
+- 新增 提供组件设计资源,组件样式调整
+## 1.1.0(2021-07-30)
+- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
+## 1.0.7(2021-05-12)
+- 新增 组件示例地址
+## 1.0.6(2021-02-05)
+- 调整为uni_modules目录规范
+- 优化 按钮背景色调整
+- 优化 兼容pc端

+ 491 - 0
uni_modules/uni-fab/components/uni-fab/uni-fab.vue

@@ -0,0 +1,491 @@
+<template>
+	<view class="uni-cursor-point">
+		<view v-if="popMenu && (leftBottom||rightBottom||leftTop||rightTop) && content.length > 0" :class="{
+        'uni-fab--leftBottom': leftBottom,
+        'uni-fab--rightBottom': rightBottom,
+        'uni-fab--leftTop': leftTop,
+        'uni-fab--rightTop': rightTop
+      }" class="uni-fab"
+				:style="nvueBottom"
+			>
+			<view :class="{
+          'uni-fab__content--left': horizontal === 'left',
+          'uni-fab__content--right': horizontal === 'right',
+          'uni-fab__content--flexDirection': direction === 'vertical',
+          'uni-fab__content--flexDirectionStart': flexDirectionStart,
+          'uni-fab__content--flexDirectionEnd': flexDirectionEnd,
+		  'uni-fab__content--other-platform': !isAndroidNvue
+        }" :style="{ width: boxWidth, height: boxHeight, backgroundColor: styles.backgroundColor }"
+				class="uni-fab__content" elevation="5">
+				<view v-if="flexDirectionStart || horizontalLeft" class="uni-fab__item uni-fab__item--first" />
+				<view v-for="(item, index) in content" :key="index" :class="{ 'uni-fab__item--active': isShow }"
+					class="uni-fab__item" @click="_onItemClick(index, item)">
+					<image :src="item.active ? item.selectedIconPath : item.iconPath" class="uni-fab__item-image"
+						mode="aspectFit" />
+					<text class="uni-fab__item-text"
+						:style="{ color: item.active ? styles.selectedColor : styles.color }">{{ item.text }}</text>
+				</view>
+				<view v-if="flexDirectionEnd || horizontalRight" class="uni-fab__item uni-fab__item--first" />
+			</view>
+		</view>
+		<view :class="{
+		  'uni-fab__circle--leftBottom': leftBottom,
+		  'uni-fab__circle--rightBottom': rightBottom,
+		  'uni-fab__circle--leftTop': leftTop,
+		  'uni-fab__circle--rightTop': rightTop,
+		  'uni-fab__content--other-platform': !isAndroidNvue
+		}" class="uni-fab__circle uni-fab__plus" :style="{ 'background-color': styles.buttonColor, 'bottom': nvueBottom }" @click="_onClick">
+			<uni-icons class="fab-circle-icon" :type="styles.icon" :color="styles.iconColor" size="32"
+				:class="{'uni-fab__plus--active': isShow && content.length > 0}"></uni-icons>
+			<!-- <view class="fab-circle-v"  :class="{'uni-fab__plus--active': isShow && content.length > 0}"></view>
+			<view class="fab-circle-h" :class="{'uni-fab__plus--active': isShow  && content.length > 0}"></view> -->
+		</view>
+	</view>
+</template>
+
+<script>
+	let platform = 'other'
+	// #ifdef APP-NVUE
+	platform = uni.getSystemInfoSync().platform
+	// #endif
+
+	/**
+	 * Fab 悬浮按钮
+	 * @description 点击可展开一个图形按钮菜单
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=144
+	 * @property {Object} pattern 可选样式配置项
+	 * @property {Object} horizontal = [left | right] 水平对齐方式
+	 * 	@value left 左对齐
+	 * 	@value right 右对齐
+	 * @property {Object} vertical = [bottom | top] 垂直对齐方式
+	 * 	@value bottom 下对齐
+	 * 	@value top 上对齐
+	 * @property {Object} direction = [horizontal | vertical] 展开菜单显示方式
+	 * 	@value horizontal 水平显示
+	 * 	@value vertical 垂直显示
+	 * @property {Array} content 展开菜单内容配置项
+	 * @property {Boolean} popMenu 是否使用弹出菜单
+	 * @event {Function} trigger 展开菜单点击事件,返回点击信息
+	 * @event {Function} fabClick 悬浮按钮点击事件
+	 */
+	export default {
+		name: 'UniFab',
+		emits: ['fabClick', 'trigger'],
+		props: {
+			pattern: {
+				type: Object,
+				default () {
+					return {}
+				}
+			},
+			horizontal: {
+				type: String,
+				default: 'left'
+			},
+			vertical: {
+				type: String,
+				default: 'bottom'
+			},
+			direction: {
+				type: String,
+				default: 'horizontal'
+			},
+			content: {
+				type: Array,
+				default () {
+					return []
+				}
+			},
+			show: {
+				type: Boolean,
+				default: false
+			},
+			popMenu: {
+				type: Boolean,
+				default: true
+			}
+		},
+		data() {
+			return {
+				fabShow: false,
+				isShow: false,
+				isAndroidNvue: platform === 'android',
+				styles: {
+					color: '#3c3e49',
+					selectedColor: '#007AFF',
+					backgroundColor: '#fff',
+					buttonColor: '#007AFF',
+					iconColor: '#fff',
+					icon: 'plusempty'
+				}
+			}
+		},
+		computed: {
+			contentWidth(e) {
+				return (this.content.length + 1) * 55 + 15 + 'px'
+			},
+			contentWidthMin() {
+				return '55px'
+			},
+			// 动态计算宽度
+			boxWidth() {
+				return this.getPosition(3, 'horizontal')
+			},
+			// 动态计算高度
+			boxHeight() {
+				return this.getPosition(3, 'vertical')
+			},
+			// 计算左下位置
+			leftBottom() {
+				return this.getPosition(0, 'left', 'bottom')
+			},
+			// 计算右下位置
+			rightBottom() {
+				return this.getPosition(0, 'right', 'bottom')
+			},
+			// 计算左上位置
+			leftTop() {
+				return this.getPosition(0, 'left', 'top')
+			},
+			rightTop() {
+				return this.getPosition(0, 'right', 'top')
+			},
+			flexDirectionStart() {
+				return this.getPosition(1, 'vertical', 'top')
+			},
+			flexDirectionEnd() {
+				return this.getPosition(1, 'vertical', 'bottom')
+			},
+			horizontalLeft() {
+				return this.getPosition(2, 'horizontal', 'left')
+			},
+			horizontalRight() {
+				return this.getPosition(2, 'horizontal', 'right')
+			},
+			// 计算 nvue bottom
+			nvueBottom() {
+				const safeBottom = uni.getSystemInfoSync().windowBottom;
+				// #ifdef APP-NVUE
+				return 30 + safeBottom
+				// #endif
+				// #ifndef APP-NVUE
+				return 30
+				// #endif
+			}
+		},
+		watch: {
+			pattern: {
+				handler(val, oldVal) {
+					this.styles = Object.assign({}, this.styles, val)
+				},
+				deep: true
+			}
+		},
+		created() {
+			this.isShow = this.show
+			if (this.top === 0) {
+				this.fabShow = true
+			}
+			// 初始化样式
+			this.styles = Object.assign({}, this.styles, this.pattern)
+		},
+		methods: {
+			_onClick() {
+				this.$emit('fabClick')
+				if (!this.popMenu) {
+					return
+				}
+				this.isShow = !this.isShow
+			},
+			open() {
+				this.isShow = true
+			},
+			close() {
+				this.isShow = false
+			},
+			/**
+			 * 按钮点击事件
+			 */
+			_onItemClick(index, item) {
+				if (!this.isShow) {
+					return
+				}
+				this.$emit('trigger', {
+					index,
+					item
+				})
+			},
+			/**
+			 * 获取 位置信息
+			 */
+			getPosition(types, paramA, paramB) {
+				if (types === 0) {
+					return this.horizontal === paramA && this.vertical === paramB
+				} else if (types === 1) {
+					return this.direction === paramA && this.vertical === paramB
+				} else if (types === 2) {
+					return this.direction === paramA && this.horizontal === paramB
+				} else {
+					return this.isShow && this.direction === paramA ? this.contentWidth : this.contentWidthMin
+				}
+			}
+		}
+	}
+</script>
+
+<style lang="scss" >
+	$uni-shadow-base:0 1px 5px 2px rgba($color: #000000, $alpha: 0.3) !default;
+
+	.uni-fab {
+		position: fixed;
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		justify-content: center;
+		align-items: center;
+		z-index: 10;
+		border-radius: 45px;
+		box-shadow: $uni-shadow-base;
+	}
+
+	.uni-cursor-point {
+		/* #ifdef H5 */
+		cursor: pointer;
+		/* #endif */
+	}
+
+	.uni-fab--active {
+		opacity: 1;
+	}
+
+	.uni-fab--leftBottom {
+		left: 15px;
+		bottom: 30px;
+		/* #ifdef H5 */
+		left: calc(15px + var(--window-left));
+		bottom: calc(30px + var(--window-bottom));
+		/* #endif */
+		// padding: 10px;
+	}
+
+	.uni-fab--leftTop {
+		left: 15px;
+		top: 30px;
+		/* #ifdef H5 */
+		left: calc(15px + var(--window-left));
+		top: calc(30px + var(--window-top));
+		/* #endif */
+		// padding: 10px;
+	}
+
+	.uni-fab--rightBottom {
+		right: 15px;
+		bottom: 30px;
+		/* #ifdef H5 */
+		right: calc(15px + var(--window-right));
+		bottom: calc(30px + var(--window-bottom));
+		/* #endif */
+		// padding: 10px;
+	}
+
+	.uni-fab--rightTop {
+		right: 15px;
+		top: 30px;
+		/* #ifdef H5 */
+		right: calc(15px + var(--window-right));
+		top: calc(30px + var(--window-top));
+		/* #endif */
+		// padding: 10px;
+	}
+
+	.uni-fab__circle {
+		position: fixed;
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		justify-content: center;
+		align-items: center;
+		width: 55px;
+		height: 55px;
+		background-color: #3c3e49;
+		border-radius: 45px;
+		z-index: 11;
+		// box-shadow: $uni-shadow-base;
+	}
+
+	.uni-fab__circle--leftBottom {
+		left: 15px;
+		bottom: 30px;
+		/* #ifdef H5 */
+		left: calc(15px + var(--window-left));
+		bottom: calc(30px + var(--window-bottom));
+		/* #endif */
+	}
+
+	.uni-fab__circle--leftTop {
+		left: 15px;
+		top: 30px;
+		/* #ifdef H5 */
+		left: calc(15px + var(--window-left));
+		top: calc(30px + var(--window-top));
+		/* #endif */
+	}
+
+	.uni-fab__circle--rightBottom {
+		right: 15px;
+		bottom: 30px;
+		/* #ifdef H5 */
+		right: calc(15px + var(--window-right));
+		bottom: calc(30px + var(--window-bottom));
+		/* #endif */
+	}
+
+	.uni-fab__circle--rightTop {
+		right: 15px;
+		top: 30px;
+		/* #ifdef H5 */
+		right: calc(15px + var(--window-right));
+		top: calc(30px + var(--window-top));
+		/* #endif */
+	}
+
+	.uni-fab__circle--left {
+		left: 0;
+	}
+
+	.uni-fab__circle--right {
+		right: 0;
+	}
+
+	.uni-fab__circle--top {
+		top: 0;
+	}
+
+	.uni-fab__circle--bottom {
+		bottom: 0;
+	}
+
+	.uni-fab__plus {
+		font-weight: bold;
+	}
+
+	// .fab-circle-v {
+	// 	position: absolute;
+	// 	width: 2px;
+	// 	height: 24px;
+	// 	left: 0;
+	// 	top: 0;
+	// 	right: 0;
+	// 	bottom: 0;
+	// 	/* #ifndef APP-NVUE */
+	// 	margin: auto;
+	// 	/* #endif */
+	// 	background-color: white;
+	// 	transform: rotate(0deg);
+	// 	transition: transform 0.3s;
+	// }
+
+	// .fab-circle-h {
+	// 	position: absolute;
+	// 	width: 24px;
+	// 	height: 2px;
+	// 	left: 0;
+	// 	top: 0;
+	// 	right: 0;
+	// 	bottom: 0;
+	// 	/* #ifndef APP-NVUE */
+	// 	margin: auto;
+	// 	/* #endif */
+	// 	background-color: white;
+	// 	transform: rotate(0deg);
+	// 	transition: transform 0.3s;
+	// }
+
+	.fab-circle-icon {
+		transform: rotate(0deg);
+		transition: transform 0.3s;
+		font-weight: 200;
+	}
+
+	.uni-fab__plus--active {
+		transform: rotate(135deg);
+	}
+
+	.uni-fab__content {
+		/* #ifndef APP-NVUE */
+		box-sizing: border-box;
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		border-radius: 55px;
+		overflow: hidden;
+		transition-property: width, height;
+		transition-duration: 0.2s;
+		width: 55px;
+		border-color: #DDDDDD;
+		border-width: 1rpx;
+		border-style: solid;
+	}
+
+	.uni-fab__content--other-platform {
+		border-width: 0px;
+		box-shadow: $uni-shadow-base;
+	}
+
+	.uni-fab__content--left {
+		justify-content: flex-start;
+	}
+
+	.uni-fab__content--right {
+		justify-content: flex-end;
+	}
+
+	.uni-fab__content--flexDirection {
+		flex-direction: column;
+		justify-content: flex-end;
+	}
+
+	.uni-fab__content--flexDirectionStart {
+		flex-direction: column;
+		justify-content: flex-start;
+	}
+
+	.uni-fab__content--flexDirectionEnd {
+		flex-direction: column;
+		justify-content: flex-end;
+	}
+
+	.uni-fab__item {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: column;
+		justify-content: center;
+		align-items: center;
+		width: 55px;
+		height: 55px;
+		opacity: 0;
+		transition: opacity 0.2s;
+	}
+
+	.uni-fab__item--active {
+		opacity: 1;
+	}
+
+	.uni-fab__item-image {
+		width: 20px;
+		height: 20px;
+		margin-bottom: 4px;
+	}
+
+	.uni-fab__item-text {
+		color: #FFFFFF;
+		font-size: 12px;
+		line-height: 12px;
+		margin-top: 2px;
+	}
+
+	.uni-fab__item--first {
+		width: 55px;
+	}
+</style>

+ 84 - 0
uni_modules/uni-fab/package.json

@@ -0,0 +1,84 @@
+{
+  "id": "uni-fab",
+  "displayName": "uni-fab 悬浮按钮",
+  "version": "1.2.5",
+  "description": "悬浮按钮 fab button ,点击可展开一个图标按钮菜单。",
+  "keywords": [
+    "uni-ui",
+    "uniui",
+    "按钮",
+    "悬浮按钮",
+    "fab"
+],
+  "repository": "https://github.com/dcloudio/uni-ui",
+  "engines": {
+    "HBuilderX": ""
+  },
+  "directories": {
+    "example": "../../temps/example_temps"
+  },
+"dcloudext": {
+    "sale": {
+      "regular": {
+        "price": "0.00"
+      },
+      "sourcecode": {
+        "price": "0.00"
+      }
+    },
+    "contact": {
+      "qq": ""
+    },
+    "declaration": {
+      "ads": "无",
+      "data": "无",
+      "permissions": "无"
+    },
+    "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
+    "type": "component-vue"
+  },
+  "uni_modules": {
+    "dependencies": ["uni-scss","uni-icons"],
+    "encrypt": [],
+    "platforms": {
+      "cloud": {
+        "tcb": "y",
+        "aliyun": "y"
+      },
+      "client": {
+        "App": {
+          "app-vue": "y",
+          "app-nvue": "y"
+        },
+        "H5-mobile": {
+          "Safari": "y",
+          "Android Browser": "y",
+          "微信浏览器(Android)": "y",
+          "QQ浏览器(Android)": "y"
+        },
+        "H5-pc": {
+          "Chrome": "y",
+          "IE": "y",
+          "Edge": "y",
+          "Firefox": "y",
+          "Safari": "y"
+        },
+        "小程序": {
+          "微信": "y",
+          "阿里": "y",
+          "百度": "y",
+          "字节跳动": "y",
+          "QQ": "y"
+        },
+        "快应用": {
+          "华为": "u",
+          "联盟": "u"
+        },
+        "Vue": {
+            "vue2": "y",
+            "vue3": "y"
+        }
+      }
+    }
+  }
+}

+ 9 - 0
uni_modules/uni-fab/readme.md

@@ -0,0 +1,9 @@
+## Fab 悬浮按钮
+> **组件名:uni-fab**
+> 代码块: `uFab`
+
+
+点击可展开一个图形按钮菜单
+
+### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-fab)
+#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 

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

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

+ 53 - 0
uni_modules/uv-avatar/components/uv-avatar-group/props.js

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

+ 106 - 0
uni_modules/uv-avatar/components/uv-avatar-group/uv-avatar-group.vue

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

+ 80 - 0
uni_modules/uv-avatar/components/uv-avatar/props.js

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

File diff suppressed because it is too large
+ 175 - 0
uni_modules/uv-avatar/components/uv-avatar/uv-avatar.vue


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

@@ -0,0 +1,89 @@
+{
+	"id": "uv-avatar",
+	"displayName": "uv-avatar 头像 全面兼容小程序、nvue、vue2、vue3等多端",
+	"version": "1.0.1",
+	"description": "uv-avatar 本组件一般用于展示头像的地方,如个人中心,或者评论列表页的用户头像展示等场所。",
+	"keywords": [
+        "uv-avatar",
+        "uvui",
+        "uv-ui",
+        "avatar",
+        "头像"
+    ],
+	"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-icon",
+			"uv-text"
+		],
+		"encrypt": [],
+		"platforms": {
+			"cloud": {
+				"tcb": "y",
+				"aliyun": "y"
+			},
+			"client": {
+				"Vue": {
+					"vue2": "y",
+					"vue3": "y"
+				},
+				"App": {
+					"app-vue": "y",
+					"app-nvue": "y"
+				},
+				"H5-mobile": {
+					"Safari": "y",
+					"Android Browser": "y",
+					"微信浏览器(Android)": "y",
+					"QQ浏览器(Android)": "y"
+				},
+				"H5-pc": {
+					"Chrome": "y",
+					"IE": "y",
+					"Edge": "y",
+					"Firefox": "y",
+					"Safari": "y"
+				},
+				"小程序": {
+					"微信": "y",
+					"阿里": "y",
+					"百度": "y",
+					"字节跳动": "y",
+					"QQ": "y",
+					"钉钉": "u",
+					"快手": "u",
+					"飞书": "u",
+					"京东": "u"
+				},
+				"快应用": {
+					"华为": "u",
+					"联盟": "u"
+				}
+			}
+		}
+	}
+}

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

@@ -0,0 +1,11 @@
+## Avatar 头像
+
+> **组件名:uv-avatar**
+
+本组件一般用于展示头像的地方,如个人中心,或者评论列表页的用户头像展示等场所。
+
+### <a href="https://www.uvui.cn/components/avatar.html" target="_blank">查看文档</a>
+
+### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+#### 如使用过程中有任何问题,或者您对uv-ui有一些好的建议,欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a>、<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>

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

@@ -0,0 +1,26 @@
+## 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
+	}
+}

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

@@ -0,0 +1,284 @@
+<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-menuv-by-longpress="showMenuByLongpress"
+				:lazy-load="lazyLoad"
+				class="uv-image__image"
+				:style="[imageStyle]"
+			></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;
+			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.11",
+  "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>

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

@@ -1,3 +1,5 @@
+## 1.0.3(2023-08-14)
+1. 新增参数textStyle,自定义文本样式
 ## 1.0.2(2023-06-27)
 优化
 ## 1.0.1(2023-05-16)

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

@@ -35,6 +35,13 @@ export default {
 			type: [String, Number],
 			default: 15
 		},
+		// 文字样式
+		textStyle: {
+			type: Object,
+			default () {
+				return {}
+			}
+		},
 		// 文字内容
 		text: {
 			type: [String, Number],

+ 3 - 2
uni_modules/uv-loading-icon/components/uv-loading-icon/uv-loading-icon.vue

@@ -49,10 +49,10 @@
 		<text
 			v-if="text"
 			class="uv-loading-icon__text"
-			:style="{
+			:style="[{
 				fontSize: $uv.addUnit(textSize),
 				color: textColor,
-			}"
+			},$uv.addStyle(textStyle)]"
 		>{{text}}</text>
 	</view>
 </template>
@@ -77,6 +77,7 @@
 	 * @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时的暗边颜色 

+ 3 - 3
uni_modules/uv-loading-icon/package.json

@@ -1,8 +1,8 @@
 {
   "id": "uv-loading-icon",
-  "displayName": "uv-loading-icon 加载动画  全面兼容小程序、nvue、vue2、vue3等多端",
-  "version": "1.0.2",
-  "description": "uv-loading-icon 此组件为一个小动画,目前用在uv-ui的loadMore加载更多等组件的正在加载状态场景。",
+  "displayName": "uv-loading-icon 加载动画 全面兼容vue3+2、app、h5、小程序等多端",
+  "version": "1.0.3",
+  "description": "此组件为一个小动画,目前用在uv-ui的uv-load-more加载更多等组件,还可以运用在项目中正在加载状态场景。",
   "keywords": [
     "uv-loading-icon",
     "uvui",

+ 12 - 4
uni_modules/uv-loading-icon/readme.md

@@ -2,10 +2,18 @@
 
 > **组件名:uv-loading-icon**
 
-此组件为一个小动画,目前用在uv-ui的loadMore加载更多等组件的正在加载状态场景。
+此组件为一个小动画,目前用在 `uv-ui` 的 `uv-load-more` 加载更多等组件,还可以运用在项目中正在加载状态场景。
 
-### <a href="https://www.uvui.cn/components/loadingIcon.html" target="_blank">查看文档</a>
+# <a href="https://www.uvui.cn/components/loadingIcon.html" target="_blank">查看文档</a>
 
-### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+## [下载完整示例项目](https://ext.dcloud.net.cn/plugin?name=uv-ui) <small>(请不要 下载插件ZIP)</small>
 
-#### 如使用过程中有任何问题,或者您对uv-ui有一些好的建议,欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a>、<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
+### [更多插件,请关注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>

+ 4 - 0
uni_modules/uv-text/changelog.md

@@ -1,3 +1,7 @@
+## 1.0.4(2023-08-29)
+1. nvue修复设置align不生效的BUG
+## 1.0.3(2023-08-24)
+1. 修复在nvue不能换行的问题
 ## 1.0.2(2023-05-24)
 1. 去掉多余的data-index属性,避免警告
 ## 1.0.1(2023-05-16)

+ 203 - 213
uni_modules/uv-text/components/uv-text/uv-text.vue

@@ -1,228 +1,218 @@
 <template>
     <view
-        class="uv-text"
-        :class="[]"
-        v-if="show"
-        :style="{
-            margin: margin,
-			justifyContent: align === 'left' ? 'flex-start' : align === 'center' ? 'center' : 'flex-end'
-        }"
-        @tap="clickHandler"
+      class="uv-text"
+      :class="[]"
+      v-if="show"
+      :style="{
+        margin: margin,
+				justifyContent: align === 'left' ? 'flex-start' : align === 'center' ? 'center' : 'flex-end'
+      }"
+      @tap="clickHandler"
     >
-        <text
-            :class="['uv-text__price', type && `uv-text__value--${type}`]"
-            v-if="mode === 'price'"
-            :style="[valueStyle]"
-            >¥</text
+      <text
+        :class="['uv-text__price', type && `uv-text__value--${type}`]"
+        v-if="mode === 'price'"
+        :style="[valueStyle]"
+        >¥</text
+      >
+      <view class="uv-text__prefix-icon" v-if="prefixIcon">
+        <uv-icon
+          :name="prefixIcon"
+          :customStyle="$uv.addStyle(iconStyle)"
+        ></uv-icon>
+      </view>
+      <uv-link
+        v-if="mode === 'link'"
+        :text="value"
+        :href="href"
+        underLine
+      ></uv-link>
+      <template v-else-if="openType && isMp">
+        <button
+          class="uv-reset-button uv-text__value"
+          :style="[valueStyle]"
+          :openType="openType"
+          @getuserinfo="onGetUserInfo"
+          @contact="onContact"
+          @getphonenumber="onGetPhoneNumber"
+          @error="onError"
+          @launchapp="onLaunchApp"
+          @opensetting="onOpenSetting"
+          :lang="lang"
+          :session-from="sessionFrom"
+          :send-message-title="sendMessageTitle"
+          :send-message-path="sendMessagePath"
+          :send-message-img="sendMessageImg"
+          :show-message-card="showMessageCard"
+          :app-parameter="appParameter"
         >
-        <view class="uv-text__prefix-icon" v-if="prefixIcon">
-            <uv-icon
-                :name="prefixIcon"
-                :customStyle="$uv.addStyle(iconStyle)"
-            ></uv-icon>
-        </view>
-        <uv-link
-            v-if="mode === 'link'"
-            :text="value"
-            :href="href"
-            underLine
-        ></uv-link>
-        <template v-else-if="openType && isMp">
-            <button
-                class="uv-reset-button uv-text__value"
-                :style="[valueStyle]"
-                :openType="openType"
-                @getuserinfo="onGetUserInfo"
-                @contact="onContact"
-                @getphonenumber="onGetPhoneNumber"
-                @error="onError"
-                @launchapp="onLaunchApp"
-                @opensetting="onOpenSetting"
-                :lang="lang"
-                :session-from="sessionFrom"
-                :send-message-title="sendMessageTitle"
-                :send-message-path="sendMessagePath"
-                :send-message-img="sendMessageImg"
-                :show-message-card="showMessageCard"
-                :app-parameter="appParameter"
-            >
-                {{ value }}
-            </button>
-        </template>
-        <text
-            v-else
-            class="uv-text__value"
-            :style="[valueStyle]"
-            :class="[
-                type && `uv-text__value--${type}`,
-                lines && `uv-line-${lines}`
-            ]"
-            >{{ value }}</text
-        >
-        <view class="uv-text__suffix-icon" v-if="suffixIcon">
-            <uv-icon
-                :name="suffixIcon"
-                :customStyle="$uv.addStyle(iconStyle)"
-            ></uv-icon>
-        </view>
+          {{ value }}
+        </button>
+      </template>
+      <text
+        v-else
+        class="uv-text__value"
+        :style="[valueStyle]"
+        :class="[
+          type && `uv-text__value--${type}`,
+          lines && `uv-line-${lines}`
+        ]"
+        >{{ value }}</text
+      >
+      <view class="uv-text__suffix-icon" v-if="suffixIcon">
+        <uv-icon
+          :name="suffixIcon"
+          :customStyle="$uv.addStyle(iconStyle)"
+        ></uv-icon>
+      </view>
     </view>
 </template>
-
 <script>
-import value from './value.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 button from '@/uni_modules/uv-ui-tools/libs/mixin/button.js'
-import openType from '@/uni_modules/uv-ui-tools/libs/mixin/openType.js'
-import props from './props.js'
-/**
- * Text 文本
- * @description 此组件集成了文本类在项目中的常用功能,包括状态,拨打电话,格式化日期,*替换,超链接...等功能。 您大可不必在使用特殊文本时自己定义,text组件几乎涵盖您能使用的大部分场景。
- * @tutorial https://www.uvui.cn/components/loading.html
- * @property {String} 					type		主题颜色
- * @property {Boolean} 					show		是否显示(默认 true )
- * @property {String | Number}			text		显示的值
- * @property {String}					prefixIcon	前置图标
- * @property {String} 					suffixIcon	后置图标
- * @property {String} 					mode		文本处理的匹配模式 text-普通文本,price-价格,phone-手机号,name-姓名,date-日期,link-超链接
- * @property {String} 					href		mode=link下,配置的链接
- * @property {String | Function} 		format		格式化规则
- * @property {Boolean} 					call		mode=phone时,点击文本是否拨打电话(默认 false )
- * @property {String} 					openType	小程序的打开方式
- * @property {Boolean} 					bold		是否粗体,默认normal(默认 false )
- * @property {Boolean} 					block		是否块状(默认 false )
- * @property {String | Number} 			lines		文本显示的行数,如果设置,超出此行数,将会显示省略号
- * @property {String} 					color		文本颜色(默认 '#303133' )
- * @property {String | Number} 			size		字体大小(默认 15 )
- * @property {Object | String} 			iconStyle	图标的样式 (默认 {fontSize: '15px'} )
- * @property {String} 					decoration	文字装饰,下划线,中划线等,可选值 none|underline|line-through(默认 'none' )
- * @property {Object | String | Number}	margin		外边距,对象、字符串,数值形式均可(默认 0 )
- * @property {String | Number} 			lineHeight	文本行高
- * @property {String} 					align		文本对齐方式,可选值left|center|right(默认 'left' )
- * @property {String} 					wordWrap	文字换行,可选值break-word|normal|anywhere(默认 'normal' )
- * @event {Function} click  点击触发事件
- * @example <uv-text text="我用十年青春,赴你最后之约"></uv-text>
- */
-export default {
-    name: 'uv-text',
+	import value from './value.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 button from '@/uni_modules/uv-ui-tools/libs/mixin/button.js'
+	import openType from '@/uni_modules/uv-ui-tools/libs/mixin/openType.js'
+	import props from './props.js'
+	/**
+	 * Text 文本
+	 * @description 此组件集成了文本类在项目中的常用功能,包括状态,拨打电话,格式化日期,*替换,超链接...等功能。 您大可不必在使用特殊文本时自己定义,text组件几乎涵盖您能使用的大部分场景。
+	 * @tutorial https://www.uvui.cn/components/loading.html
+	 * @property {String} 					type		主题颜色
+	 * @property {Boolean} 					show		是否显示(默认 true )
+	 * @property {String | Number}			text		显示的值
+	 * @property {String}					prefixIcon	前置图标
+	 * @property {String} 					suffixIcon	后置图标
+	 * @property {String} 					mode		文本处理的匹配模式 text-普通文本,price-价格,phone-手机号,name-姓名,date-日期,link-超链接
+	 * @property {String} 					href		mode=link下,配置的链接
+	 * @property {String | Function} 		format		格式化规则
+	 * @property {Boolean} 					call		mode=phone时,点击文本是否拨打电话(默认 false )
+	 * @property {String} 					openType	小程序的打开方式
+	 * @property {Boolean} 					bold		是否粗体,默认normal(默认 false )
+	 * @property {Boolean} 					block		是否块状(默认 false )
+	 * @property {String | Number} 			lines		文本显示的行数,如果设置,超出此行数,将会显示省略号
+	 * @property {String} 					color		文本颜色(默认 '#303133' )
+	 * @property {String | Number} 			size		字体大小(默认 15 )
+	 * @property {Object | String} 			iconStyle	图标的样式 (默认 {fontSize: '15px'} )
+	 * @property {String} 					decoration	文字装饰,下划线,中划线等,可选值 none|underline|line-through(默认 'none' )
+	 * @property {Object | String | Number}	margin		外边距,对象、字符串,数值形式均可(默认 0 )
+	 * @property {String | Number} 			lineHeight	文本行高
+	 * @property {String} 					align		文本对齐方式,可选值left|center|right(默认 'left' )
+	 * @property {String} 					wordWrap	文字换行,可选值break-word|normal|anywhere(默认 'normal' )
+	 * @event {Function} click  点击触发事件
+	 * @example <uv-text text="我用十年青春,赴你最后之约"></uv-text>
+	 */
+	export default {
+		name: 'uv-text',
 		emits: ['click'],
-    // #ifdef MP
-    mixins: [mpMixin, mixin, value, button, openType, props],
-    // #endif
-    // #ifndef MP
-    mixins: [mpMixin, mixin, value, props],
-    // #endif
-    computed: {
-        valueStyle() {
-            const style = {
-                textDecoration: this.decoration,
-                fontWeight: this.bold ? 'bold' : 'normal',
-                wordWrap: this.wordWrap,
-                fontSize: this.$uv.addUnit(this.size)
-            }
-            !this.type && (style.color = this.color)
-            this.isNvue && this.lines && (style.lines = this.lines)
-            this.lineHeight &&
-                (style.lineHeight = this.$uv.addUnit(this.lineHeight))
-            !this.isNvue && this.block && (style.display = 'block')
-            return this.$uv.deepMerge(style, this.$uv.addStyle(this.customStyle))
-        },
-        isNvue() {
-            let nvue = false
-            // #ifdef APP-NVUE
-            nvue = true
-            // #endif
-            return nvue
-        },
-        isMp() {
-            let mp = false
-            // #ifdef MP
-            mp = true
-            // #endif
-            return mp
-        }
-    },
-    data() {
-        return {}
-    },
-    methods: {
-        clickHandler() {
-            // 如果为手机号模式,拨打电话
-            if (this.call && this.mode === 'phone') {
-                uni.makePhoneCall({
-                    phoneNumber: this.text
-                })
-            }
-            this.$emit('click')
-        }
-    }
-}
+		// #ifdef MP
+		mixins: [mpMixin, mixin, value, button, openType, props],
+		// #endif
+		// #ifndef MP
+		mixins: [mpMixin, mixin, value, props],
+		// #endif
+		computed: {
+			valueStyle() {
+				const style = {
+					textDecoration: this.decoration,
+					fontWeight: this.bold ? 'bold' : 'normal',
+					wordWrap: this.wordWrap,
+					fontSize: this.$uv.addUnit(this.size)
+				};
+				!this.type && (style.color = this.color);
+				this.isNvue && this.lines && (style.lines = this.lines);
+				if(this.isNvue && this.mode != 'price' && !this.prefixIcon && !this.suffixIcon) {
+					 style.flex = 1;
+					 style.textAlign = this.align === 'left' ? 'flex-start' : this.align === 'center' ? 'center' : 'right';
+				}
+				this.lineHeight && (style.lineHeight = this.$uv.addUnit(this.lineHeight));
+				!this.isNvue && this.block && (style.display = 'block');
+				return this.$uv.deepMerge(style, this.$uv.addStyle(this.customStyle));
+			},
+			isNvue() {
+				let nvue = false
+				// #ifdef APP-NVUE
+				nvue = true
+				// #endif
+				return nvue
+			},
+			isMp() {
+				let mp = false
+				// #ifdef MP
+				mp = true
+				// #endif
+				return mp
+			}
+		},
+		data() {
+			return {}
+		},
+		methods: {
+			clickHandler() {
+				// 如果为手机号模式,拨打电话
+				if (this.call && this.mode === 'phone') {
+					uni.makePhoneCall({
+						phoneNumber: this.text
+					})
+				}
+				this.$emit('click')
+			}
+		}
+	}
 </script>
-
 <style lang="scss" scoped>
 	$show-lines: 1;
 	$show-reset-button: 1;
 	@import '@/uni_modules/uv-ui-tools/libs/css/variable.scss';
 	@import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
 	@import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
-.uv-text {
-    @include flex(row);
-    align-items: center;
-    flex-wrap: nowrap;
-    flex: 1;
-	/* #ifndef APP-NVUE */
-	width: 100%;
-	/* #endif */
-
-    &__price {
-        font-size: 14px;
-        color: $uv-content-color;
-    }
-
-    &__value {
-        font-size: 14px;
-        @include flex;
-        color: $uv-content-color;
-        flex-wrap: wrap;
-        // flex: 1;
-        text-overflow: ellipsis;
-        align-items: center;
-
-        &--primary {
-            color: $uv-primary;
-        }
-
-        &--warning {
-            color: $uv-warning;
-        }
-
-        &--success {
-            color: $uv-success;
-        }
-
-        &--info {
-            color: $uv-info;
-        }
-
-        &--error {
-            color: $uv-error;
-        }
-
-        &--main {
-            color: $uv-main-color;
-        }
-
-        &--content {
-            color: $uv-content-color;
-        }
-
-        &--tips {
-            color: $uv-tips-color;
-        }
-
-        &--light {
-            color: $uv-light-color;
-        }
-    }
-}
-</style>
+	.uv-text {
+		@include flex(row);
+		align-items: center;
+		flex-wrap: nowrap;
+		flex: 1;
+		/* #ifndef APP-NVUE */
+		width: 100%;
+		/* #endif */
+		&__price {
+			font-size: 14px;
+			color: $uv-content-color;
+		}
+		&__value {
+			font-size: 14px;
+			@include flex;
+			color: $uv-content-color;
+			flex-wrap: wrap;
+			// flex: 1;
+			text-overflow: ellipsis;
+			align-items: center;
+			&--primary {
+				color: $uv-primary;
+			}
+			&--warning {
+				color: $uv-warning;
+			}
+			&--success {
+				color: $uv-success;
+			}
+			&--info {
+				color: $uv-info;
+			}
+			&--error {
+				color: $uv-error;
+			}
+			&--main {
+				color: $uv-main-color;
+			}
+			&--content {
+				color: $uv-content-color;
+			}
+			&--tips {
+				color: $uv-tips-color;
+			}
+			&--light {
+				color: $uv-light-color;
+			}
+		}
+	}
+</style>

+ 2 - 2
uni_modules/uv-text/package.json

@@ -1,7 +1,7 @@
 {
   "id": "uv-text",
-  "displayName": "uv-text 文本  全面兼容小程序、nvue、vue2、vue3等多端",
-  "version": "1.0.2",
+  "displayName": "uv-text 文本 全面兼容vue3+2、app、h5、小程序等多端",
+  "version": "1.0.4",
   "description": "此组件集成了文本类在项目中的常用功能,包括状态,拨打电话,格式化日期,*替换,超链接...等功能。 您大可不必在使用特殊文本时自己定义,text组件涵盖您能使用的大部分场景。",
   "keywords": [
     "uv-text",

+ 9 - 3
uni_modules/uv-text/readme.md

@@ -4,8 +4,14 @@
 
 此组件集成了文本类在项目中的常用功能,包括状态,拨打电话,格式化日期,*替换,超链接...等功能。 您大可不必在使用特殊文本时自己定义,text组件涵盖您能使用的大部分场景。
 
-### <a href="https://www.uvui.cn/components/text.html" target="_blank">查看文档</a>
+# <a href="https://www.uvui.cn/components/text.html" target="_blank">查看文档</a>
 
-### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+### [更多插件,请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui)
 
-#### 如使用过程中有任何问题,或者您对uv-ui有一些好的建议,欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a>、<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
+<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>

+ 24 - 0
uni_modules/uv-waterfall/changelog.md

@@ -0,0 +1,24 @@
+## 1.0.8(2023-08-17)
+1. 修复只有一条数据切换时可能存在位置错误的BUG
+## 1.0.7(2023-07-22)
+1. 避免快速切换报错的BUG
+## 1.0.6(2023-07-17)
+1. 优化文档
+2. 优化其他
+## 1.0.5(2023-07-14)
+1. 优化changeList未处理数据时,正确返回对应列的数据,避免误导
+## 1.0.4(2023-05-27)
+1. 修复在百度小程序中可能存在的BUG
+2. 去掉原有的slot方式
+## 1.0.3(2023-05-23)
+1. 修复在百度/头条小程序显示异常等BUG
+2. 增加changeList回调函数处理数据
+3. 更新示例
+## 1.0.2(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.1(2023-05-12)
+1. 增加clear回调函数
+2. 增加remove回调函数
+## 1.0.0(2023-05-10)
+uv-waterfall 瀑布流

+ 69 - 0
uni_modules/uv-waterfall/components/uv-waterfall/props.js

@@ -0,0 +1,69 @@
+export default {
+	props: {
+		// 瀑布流数据
+		// #ifdef VUE2
+		value: {
+			type: Array,
+			default: () => []
+		},
+		// #endif
+		// #ifdef VUE3
+		modelValue: {
+			type: Array,
+			default: () => []
+		},
+		// #endif
+		// 数据的id值,根据id值对数据执行删除操作
+		// 如数据为:{id: 1, name: 'uv-ui'},那么该值设置为id
+		idKey: {
+			type: String,
+			default: 'id'
+		},
+		// 每次插入数据的事件间隔,间隔越长能保证两列高度相近,但是用户体验不好,单位ms
+		addTime: {
+			type: Number,
+			default: 200
+		},
+		// 瀑布流的列数,默认2,最高为5
+		columnCount: {
+			type: [Number, String],
+			default: 2
+		},
+		// 列与列的间隙,默认20
+		columnGap: {
+			type: [Number, String],
+			default: 20
+		},
+		// 左边和列表的间隙
+		leftGap: {
+			type: [Number, String],
+			default: 0
+		},
+		// 右边和列表的间隙
+		rightGap: {
+			type: [Number, String],
+			default: 0
+		},
+		// 是否显示滚动条,仅nvue生效
+		showScrollbar: {
+			type: [Boolean],
+			default: false
+		},
+		// 列宽,nvue生效
+		columnWidth: {
+			type: [Number, String],
+			default: 'auto'
+		},
+		// 瀑布流的宽度,nvue生效
+		width: {
+			type: [Number, String],
+			default: ''
+		},
+		// 瀑布流的高度,nvue生效
+		height: {
+			type: [Number, String],
+			default: ''
+		},
+		...uni.$uv?.props?.waterfall
+	}
+}

+ 265 - 0
uni_modules/uv-waterfall/components/uv-waterfall/uv-waterfall.vue

@@ -0,0 +1,265 @@
+<template>
+	<view class="uv-waterfall">
+		<!-- #ifndef APP-NVUE -->
+		<view class="uv-waterfall__gap_left" :style="[gapLeftStyle]"></view>
+		<template v-if="columnNum>=1">
+			<view id="uv-waterfall-1" class="uv-waterfall__column">
+				<slot name="list1"></slot>
+			</view>
+		</template>
+		<template v-if="columnNum>=2">
+			<view class="uv-waterfall__gap_center" :style="[gapCenterStyle]"></view>
+			<view id="uv-waterfall-2" class="uv-waterfall__column">
+				<slot name="list2"></slot>
+			</view>
+		</template>
+		<template v-if="columnNum>=3">
+			<view class="uv-waterfall__gap_center" :style="[gapCenterStyle]"></view>
+			<view id="uv-waterfall-3" class="uv-waterfall__column">
+				<slot name="list3"></slot>
+			</view>
+		</template>
+		<template v-if="columnNum>=4">
+			<view class="uv-waterfall__gap_center" :style="[gapCenterStyle]">
+			</view>
+			<view id="uv-waterfall-4" class="uv-waterfall__column">
+				<slot name="list4"></slot>
+			</view>
+		</template>
+		<template v-if="columnNum>=5">
+			<view class="uv-waterfall__gap_center" :style="[gapCenterStyle]">
+			</view>
+			<view id="uv-waterfall-5" class="uv-waterfall__column">
+				<slot name="list5"></slot>
+			</view>
+		</template>
+		<view class="uv-waterfall__gap_right" :style="[gapRightStyle]">
+		</view>
+		<!-- #endif -->
+		<!-- #ifdef APP-NVUE -->
+		<view class="waterfall-warapper">
+			<waterfall :column-count="columnNum" :show-scrollbar="false" column-width="auto" :column-gap="columnGap" :left-gap="leftGap" :right-gap="rightGap" :always-scrollable-vertical="true" :style="[nvueWaterfallStyle]"
+				@loadmore="scrolltolower">
+				<slot></slot>
+			</waterfall>
+		</view>
+		<!-- #endif -->
+	</view>
+</template>
+<script>
+	import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+	import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+	import props from './props.js';
+	/**
+	 * 瀑布流
+	 * @description 该组件兼容所有端,nvue参考https://uniapp.dcloud.net.cn/component/waterfall.html
+	 * @tutorial https://www.uvui.cn/components/list.html
+	 * @property {Array}	value/modelValue	瀑布流数组数据,非nvue生效 (默认 [] )
+	 * @property {String}	idKey	  数据的id值,根据id值对数据执行删除操作,如数据为:{id: 1, name: 'uv-ui'},那么该值设置为id,非nvue有效 (默认 '' )
+	 * @property {String | Number}	addTime		每次插入数据的事件间隔,间隔越长能保证两列高度相近,但是用户体验不好,单位ms,非nvue生效(默认 200 )
+	 * @property {String | Number}	columnCount		瀑布流的列数(默认 2 )
+	 * @property {String | Number}			columnGap		列与列的间隙(默认 0 )
+	 * @property {String | Number}			leftGap		左边和列表的间隙(默认 0 )
+	 * @property {String | Number}			rightGap	右边和列表的间隙(默认 0 )
+	 * @property {Boolean}	showScrollbar		控制是否出现滚动条,仅nvue有效 (默认 false )
+	 * @property {String | Number}		columnWidth		描述瀑布流每一列的列宽,nvue生效 (默认 auto)
+	 * @property {String | Number}	  width	  瀑布流的宽度,nvue生效 (默认 屏幕宽 )
+	 * @property {String | Number}		height	 瀑布流的高度,nvue生效 (默认 屏幕高 )
+	 * @property {Object}	customStyle		定义需要用到的外部样式
+	 *
+	 * @example <uv-waterfall v-model="list"></uv-waterfall>
+	 */
+	export default {
+		name: 'uv-waterfall',
+		mixins: [mpMixin, mixin, props],
+		data() {
+			return {
+				list1: [],
+				list2: [],
+				list3: [],
+				list4: [],
+				list5: [],
+				// 临时列表
+				tempList: []
+			}
+		},
+		computed: {
+			// 破坏value变量引用,否则数据会保持不变
+			copyValue() {
+				// #ifdef VUE2
+				return this.$uv.deepClone(this.value)
+				// #endif
+				// #ifdef VUE3
+				return this.$uv.deepClone(this.modelValue)
+				// #endif
+			},
+			columnNum() {
+				return this.columnCount <= 0 ? 0 : this.columnCount >= 5 ? 5 : this.columnCount;
+			},
+			gapLeftStyle() {
+				const style = {}
+				style.width = this.$uv.addUnit(this.leftGap)
+				return style;
+			},
+			gapRightStyle() {
+				const style = {}
+				style.width = this.$uv.addUnit(this.rightGap)
+				return style;
+			},
+			gapCenterStyle() {
+				const style = {}
+				style.width = this.$uv.addUnit(this.columnGap)
+				return style;
+			},
+			nvueWaterfallStyle() {
+				const style = {};
+				if (this.width != 0) style.width = this.$uv.addUnit(this.width)
+				if (this.height != 0) style.height = this.$uv.addUnit(this.height)
+				// 如果没有定义列表高度,则默认使用屏幕高度
+				if (!style.width) style.width = this.$uv.addUnit(this.$uv.sys().windowWidth, 'px')
+				if (!style.height) style.height = this.$uv.addUnit(this.$uv.sys().windowHeight, 'px')
+				return this.$uv.deepMerge(style, this.$uv.addStyle(this.customStyle))
+			}
+		},
+		watch: {
+			copyValue(nVal, oVal) {
+				// #ifndef APP-NVUE
+				if (nVal.length != 0) {
+					// 取出数组发生变化的部分
+					let startIndex = Array.isArray(oVal) && oVal.length > 0 ? oVal.length : 0
+					// 拼接原有数据
+					this.tempList = this.tempList.concat(this.$uv.deepClone(nVal.slice(startIndex)))
+					this.splitData()
+				}
+				// #endif
+			}
+		},
+		mounted() {
+			// #ifndef APP-NVUE
+			this.tempList = this.$uv.deepClone(this.copyValue)
+			this.splitData()
+			// #endif
+		},
+		methods: {
+			// 滚动到底部触发事件
+			scrolltolower(e) {
+				this.$uv.sleep(30).then(() => {
+					this.$emit('scrolltolower')
+				})
+			},
+			// 拆分数据
+			async splitData() {
+				let rectArr = [];
+				let emitList = {};
+				if (!this.tempList.length) return
+				for (let i = 1; i <= this.columnNum; i++) {
+					const rect = await this.$uvGetRect(`#uv-waterfall-${i}`);
+					rectArr.push({ ...rect, name: i });
+				}
+				let item = this.tempList[0]
+				// 因为经过上面两个await节点查询和定时器,数组有可能会变成空[],导致item的值为undefined
+				// 解决多次快速滚动会导致数据乱的问题
+				if (!item) return
+				const minCol = this.getMin(rectArr);
+				// 列宽可能使用的到
+				item.width = minCol.width;
+				this[`list${minCol.name}`].push(item);
+				emitList.name = `list${minCol.name}`;
+				emitList.value = item;
+				this.$emit('changeList', emitList);
+				// 移除临时数组中已处理的数据
+				this.tempList.splice(0, 1)
+				// 如果还有数据则继续执行
+				if (this.tempList.length) {
+					let _timeout = this.addTime;
+					// 部分平台在延时较短的情况会出现BUG
+					// #ifdef MP-BAIDU
+					_timeout = _timeout < 200 ? 200 : _timeout;
+					// #endif
+					await this.$uv.sleep(_timeout);
+					this.splitData()
+				} else {
+					this.$emit('finish')
+				}
+			},
+			getMin(arr) {
+				let result = null;
+				const filter = arr.filter(item => item.height == 0);
+				if (!filter.length) {
+					const min = Math.min.apply(Math, arr.map(item => {
+						return item.height;
+					}))
+					const [item] = arr.filter(item => item.height == min);
+					result = item;
+				} else {
+					let newArr = [];
+					arr.map((item, index) => {
+						newArr.push({ len: this[`list${index+1}`].length, item: item });
+					});
+					const minLen = Math.min.apply(Math, newArr.map(item => {
+						return item.len;
+					}))
+					try {
+						const { item } = newArr.find(item => item.len == minLen && item.item.height == 0);
+						result = item;
+					} catch (e) {
+						const { item } = newArr.find(item => item.item.height == 0);
+						result = item;
+					}
+				}
+				return result;
+			},
+			// 清空数据列表
+			async clear() {
+				// 清除数据
+				for (let i = 0; i < this.columnCount; i++) {
+					this[`list${i+1}`] = [];
+				}
+				// #ifdef VUE2
+				this.$emit('input', [])
+				// #endif
+				// #ifdef VUE3
+				this.$emit('update:modelValue', [])
+				// #endif
+				this.tempList = []
+				await this.$uv.sleep(300);
+				this.$emit('clear');
+			},
+			// 清除指定的某一条数据,根据id来实现
+			remove(id) {
+				let index = -1
+				// 删除组件数据
+				for (let i = 1; i <= this.columnCount; i++) {
+					index = this[`list${i}`].findIndex(item => item[this.idKey] == id)
+					if (index != -1) {
+						this[`list${i}`].splice(index, 1)
+					}
+				}
+				// 同时删除父组件对应的数据
+				// #ifdef VUE2
+				index = this.value.findIndex(item => item[this.idKey] == id)
+				if (index != -1) this.$emit('input', this.value.splice(index, 1))
+				// #endif
+				// #ifdef VUE3
+				index = this.modelValue.findIndex(item => item[this.idKey] == id)
+				if (index != -1) this.$emit('update:modelValue', this.modelValue.splice(index, 1))
+				// #endif
+				this.$emit('remove', id);
+			}
+		}
+	}
+</script>
+<style lang="scss" scoped>
+	@import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+	.uv-waterfall {
+		@include flex(row);
+		align-items: flex-start;
+		&__column {
+			@include flex(column);
+			flex: 1;
+			// #ifndef APP-NVUE
+			height: auto;
+			// #endif
+		}
+	}
+</style>

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

@@ -0,0 +1,89 @@
+{
+  "id": "uv-waterfall",
+  "displayName": "uv-waterfall 瀑布流 全面兼容vue3+2、app、h5、小程序等多端",
+  "version": "1.0.8",
+  "description": "该组件主要用于瀑布流式布局显示,视觉表现为参差不齐的多栏布局,随着页面滚动条向下滚动,这种布局还会不断加载数据块并附加至当前尾部,同时集成nvue的原生瀑布流。",
+  "keywords": [
+    "uv-waterfall",
+    "uvui",
+    "uv-ui",
+    "waterfall",
+    "瀑布流"
+],
+  "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-image",
+			"uv-loading-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"
+				}
+			}
+		}
+  }
+}

+ 19 - 0
uni_modules/uv-waterfall/readme.md

@@ -0,0 +1,19 @@
+## Waterfall 瀑布流
+
+> **组件名:uv-waterfall**
+
+该组件主要用于瀑布流式布局显示,视觉表现为参差不齐的多栏布局,随着页面滚动条向下滚动,这种布局还会不断加载数据块并附加至当前尾部,同时集成`nvue`的原生瀑布流用于`app-nvue`。常用于一些电商商品展示等,如某宝首页、x红书等。
+
+研究uniapp瀑布流多年,**该方式是目前小程序端最佳方案**,灵活配置,简单易用,开箱即用。
+
+该插件请根据文档耐心查看,`vue`的写法稍微麻烦点,但是效果是很好的,比之前上线的两个版本的瀑布流适用,更有扩展性,我自己的上线项目也是用的此插件。
+
+# <a href="https://www.uvui.cn/components/waterfall.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>

File diff suppressed because it is too large
+ 0 - 7396
util/mapData.js