Kaynağa Gözat

Merge branch 'master' of http://www.sysuimars.cn:3000/feiniao/feiniao-youwei-uniapp

lxf 3 hafta önce
ebeveyn
işleme
3c3d99e289

+ 3 - 5
components/checkinPopup/checkinPopup.vue

@@ -72,8 +72,6 @@
 				runningDays.value = res.data.runningDays
 				getRules();
 				showPopup.value = true;
-			} else {
-				closeCheckPopup()
 			}
 		});
 	};
@@ -115,11 +113,11 @@
 
 			.left-image {
 				top: 0;
-				left: 84rpx;
+				left: -14rpx;
 			}
 
 			.right-image {
-				right: 84rpx;
+				right: -14rpx;
 				bottom: 0;
 			}
 
@@ -129,7 +127,7 @@
 				width: 600rpx;
 				height: 104rpx;
 				bottom: -44rpx;
-				left: 84rpx;
+				left: -14rpx;
 				@include ossBg("treePage/check-title-bg.png");
 			}
 		}

+ 170 - 0
components/danmakuManager/README.md

@@ -0,0 +1,170 @@
+# 弹幕组件使用说明
+
+## 功能特性
+
+- 固定高度200px的弹幕容器
+- 支持无限滚动和单次滚动模式
+- 多轨道智能分配,避免弹幕叠加
+- 弹幕宽度根据文字长度自适应
+- 支持用户头像显示(必显示)
+- 自动清理已播放的弹幕
+- 可自定义滚动速度
+- 智能轨道占用管理
+
+## 使用方法
+
+### 基础用法
+
+```vue
+<template>
+  <danmakuManager 
+    :danmakuList="danmakuList" 
+    :height="200" 
+    :infinite="true"
+    :speed="80"
+    @danmakuComplete="onDanmakuComplete"
+  ></danmakuManager>
+</template>
+
+<script setup>
+import danmakuManager from "@/components/danmakuManager/danmakuManager.vue"
+
+const danmakuList = ref([
+  {
+    text: "晨曦微露,阳光洒满大地",
+    avatar: "头像URL"
+  },
+  {
+    text: "守护果树,收获美好",
+    avatar: null
+  }
+])
+
+const onDanmakuComplete = (id) => {
+  console.log('弹幕播放完成:', id)
+}
+</script>
+```
+
+### Props 参数
+
+| 参数 | 类型 | 默认值 | 说明 |
+|------|------|--------|------|
+| danmakuList | Array | [] | 弹幕数据列表 |
+| height | Number | 200 | 容器高度(px) |
+| infinite | Boolean | true | 是否无限循环 |
+| speed | Number | 100 | 弹幕滚动速度(px/s) |
+| trackCount | Number | 8 | 弹幕轨道数量 |
+| spacing | Number | 20 | 弹幕间距 |
+
+### 弹幕数据格式
+
+```javascript
+{
+  text: "弹幕文本内容",
+  avatar: "头像URL" // 必填,如果为null会使用默认头像
+}
+```
+
+### 事件
+
+| 事件名 | 参数 | 说明 |
+|--------|------|------|
+| danmakuComplete | id | 弹幕播放完成时触发 |
+
+### 方法
+
+组件暴露了以下方法供父组件调用:
+
+- `addDanmaku(text, avatar)` - 动态添加弹幕
+- `clearDanmakus()` - 清空所有弹幕
+- `startDanmakuAnimation()` - 开始弹幕动画
+- `stopDanmakuAnimation()` - 停止弹幕动画
+
+### 无限滚动 vs 单次滚动
+
+```vue
+<!-- 无限滚动 -->
+<danmakuManager :infinite="true" :danmakuList="danmakuList"></danmakuManager>
+
+<!-- 单次滚动 -->
+<danmakuManager :infinite="false" :danmakuList="danmakuList"></danmakuManager>
+```
+
+## 样式定制
+
+弹幕组件使用8个不同的轨道,每个轨道有不同的背景色:
+
+- track-0: 红色
+- track-1: 绿色  
+- track-2: 蓝色
+- track-3: 黄色
+- track-4: 紫色
+- track-5: 青色
+- track-6: 橙色
+- track-7: 紫罗兰色
+
+可以通过修改CSS来自定义轨道样式。
+
+## 新增功能说明
+
+### 1. 自适应宽度
+- 弹幕宽度根据文字长度自动调整
+- 使用 `width: fit-content` 实现自适应
+- 设置最小宽度确保视觉效果
+
+### 2. 必显示头像
+- 每个弹幕都会显示用户头像
+- 如果没有提供头像,会使用默认头像
+- 头像大小固定为32rpx,圆形显示
+
+### 3. 智能轨道管理
+- 避免弹幕在同一轨道叠加
+- 避免弹幕在相邻轨道叠加
+- 智能分配可用轨道,确保轨道间距
+- 记录轨道占用时间,自动释放
+- 如果所有轨道都被占用,选择最早释放的轨道
+
+### 4. 弹幕消失优化
+- 确保弹幕完全离开可视范围才消失
+- 增加额外的消失距离,避免弹幕突然消失
+- 优化弹幕播放间隔,避免过于密集
+
+### 5. 高度自适应优化
+- 根据容器高度动态调整轨道间距
+- 防止弹幕超出容器边界显示不完整
+- 动态调整播放间隔,避免低高度容器中弹幕叠加
+- 添加边界保护,确保弹幕完全显示
+
+### 6. 弹幕稳定性优化
+- 修复弹幕在滚动过程中高度掉下来的问题
+- 确保弹幕在容器边界内完全显示
+- 优化轨道分配算法,避免超出容器边界
+- 固定弹幕元素高度,防止滚动时变形
+
+### 7. 弹幕显示完整性优化
+- 增加弹幕消失的缓冲距离,确保完全显示后再消失
+- 优化弹幕宽度计算,更精确地估算文字宽度
+- 增加内边距和文字间距,确保文字显示完整
+- 延长弹幕动画时间,避免过早消失
+
+### 8. 低高度容器优化
+- 针对100px等低高度容器进行特殊优化
+- 增加轨道间距,避免弹幕垂直叠加
+- 减少可用轨道数量,确保弹幕有足够空间
+- 增加播放间隔时间,避免弹幕过于密集
+- 优化时间间隔计算,确保相邻弹幕不会叠加
+
+### 9. 彻底解决叠加问题
+- 采用严格轨道分配策略,只使用完全空闲的轨道
+- 当没有空闲轨道时,暂停添加新弹幕
+- 进一步减少低高度容器的可用轨道数量(最多2个)
+- 大幅增加播放间隔时间,确保弹幕完全分离
+- 避免任何形式的弹幕叠加,保证观看体验
+
+### 10. 修复显示问题
+- 优化轨道分配策略,确保弹幕能正常显示
+- 采用三级轨道选择策略:完全空闲 > 任何空闲 > 最早释放
+- 调整低高度容器的可用轨道数量为3个,平衡显示和防叠加
+- 优化播放间隔时间,确保弹幕流畅显示
+- 修复过于严格的轨道分配导致的弹幕不显示问题

+ 373 - 0
components/danmakuManager/danmakuManager.vue

@@ -0,0 +1,373 @@
+<template>
+	<view class="danmaku-container" :style="{ height: height + 'px' }">
+		<view class="danmaku-stage">
+			<view 
+				v-for="item in visibleDanmakus" 
+				:key="item.id"
+				class="danmaku-item"
+				:class="[`danmaku-track`, { 'danmaku-scrolling': item.isScrolling }]"
+				:style="getDanmakuStyle(item)"
+			>
+				<view class="danmaku-content">
+					<image class="danmaku-avatar" :src="item.avatar || defaultAvatar" mode="aspectFill"></image>
+					<view class="danmaku-text">{{ item.text }}</view>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script setup>
+import { ref, computed, onMounted, onUnmounted, watch } from 'vue'
+
+const props = defineProps({
+	danmakuList: {
+		type: Array,
+		default: () => []
+	},
+	height: {
+		type: Number,
+		default: 200
+	},
+	infinite: {
+		type: Boolean,
+		default: true
+	},
+	speed: {
+		type: Number,
+		default: 100
+	},
+	trackCount: {
+		type: Number,
+		default: 8
+	},
+	spacing: {
+		type: Number,
+		default: 20
+	}
+})
+
+const emit = defineEmits(['danmakuComplete'])
+
+const visibleDanmakus = ref([])
+const animationTimer = ref(null)
+const danmakuIdCounter = ref(0)
+const trackOccupancy = ref(new Array(props.trackCount).fill(0)) // 记录每个轨道的占用情况
+
+const trackHeight = computed(() => props.height / props.trackCount)
+
+// 默认头像
+const defaultAvatar = 'https://birdseye-img.sysuimars.com/youwei-uniapp/img/default-avatar.png'
+
+const getDanmakuStyle = (danmaku) => {
+	// 获取屏幕宽度,兼容不同平台
+	const screenWidth = uni.getSystemInfoSync().screenWidth || 375
+	// 增加更多的额外距离确保弹幕完全离开可视范围
+	const extraDistance = 200 // 增加缓冲距离
+	const duration = (screenWidth + danmaku.width + extraDistance) / props.speed
+	const trackSpacing = calculateTrackSpacing()
+	const danmakuHeight = calculateDanmakuHeight()
+	
+	// 计算弹幕的垂直位置,确保在轨道中居中且不超出容器边界
+	const trackTop = danmaku.track * trackSpacing
+	const centerOffset = trackSpacing / 2
+	const topPosition = Math.max(
+		danmakuHeight / 2, // 确保不超出顶部
+		Math.min(
+			trackTop + centerOffset, // 轨道中心位置
+			props.height - danmakuHeight / 2 // 确保不超出底部
+		)
+	)
+	
+	return {
+		top: `${topPosition}px`,
+		left: `${danmaku.left}px`,
+		transition: `left ${duration}s linear`
+	}
+}
+
+// 计算弹幕实际宽度
+const calculateDanmakuWidth = (text) => {
+	// 更精确的宽度计算:每个字符约16px,头像32px,内边距40px,额外缓冲20px
+	return text.length * 16 + 32 + 40 + 20
+}
+
+// 计算弹幕实际高度
+const calculateDanmakuHeight = () => {
+	return 48 // 弹幕高度约48rpx
+}
+
+// 计算轨道间距
+const calculateTrackSpacing = () => {
+	const danmakuHeight = calculateDanmakuHeight()
+	const trackHeight = props.height / props.trackCount
+	// 对于低高度容器,增加轨道间距避免叠加
+	const minSpacing = props.height < 150 ? danmakuHeight + 20 : danmakuHeight + 10
+	return Math.max(minSpacing, trackHeight) // 确保轨道间距足够
+}
+
+// 查找可用轨道
+const findAvailableTrack = () => {
+	const screenWidth = uni.getSystemInfoSync().screenWidth || 375
+	const currentTime = Date.now()
+	const danmakuHeight = calculateDanmakuHeight()
+	const trackSpacing = calculateTrackSpacing()
+	
+	// 清理过期的轨道占用记录
+	for (let i = 0; i < trackOccupancy.value.length; i++) {
+		if (trackOccupancy.value[i] < currentTime) {
+			trackOccupancy.value[i] = 0
+		}
+	}
+	
+	// 计算可用的轨道数量,确保弹幕不会超出容器边界
+	const maxTracks = Math.floor((props.height - danmakuHeight) / trackSpacing) + 1
+	const availableTracks = Math.min(props.trackCount, maxTracks)
+	
+	// 对于低高度容器,减少可用轨道数量避免叠加
+	const finalTracks = props.height < 150 ? Math.min(availableTracks, 3) : availableTracks
+	
+	// 首先尝试寻找完全空闲的轨道
+	for (let i = 0; i < finalTracks; i++) {
+		if (trackOccupancy.value[i] === 0) {
+			// 检查相邻轨道是否被占用
+			const prevTrack = i > 0 ? trackOccupancy.value[i - 1] : 0
+			const nextTrack = i < finalTracks - 1 ? trackOccupancy.value[i + 1] : 0
+			
+			// 优先选择完全空闲的轨道
+			if (prevTrack === 0 && nextTrack === 0) {
+				return i
+			}
+		}
+	}
+	
+	// 如果没有完全空闲的轨道,寻找任何空闲轨道
+	for (let i = 0; i < finalTracks; i++) {
+		if (trackOccupancy.value[i] === 0) {
+			return i
+		}
+	}
+	
+	// 如果所有轨道都被占用,选择最早释放的轨道
+	let earliestTrack = 0
+	let earliestTime = trackOccupancy.value[0]
+	for (let i = 1; i < finalTracks; i++) {
+		if (trackOccupancy.value[i] < earliestTime) {
+			earliestTime = trackOccupancy.value[i]
+			earliestTrack = i
+		}
+	}
+	
+	return earliestTrack
+}
+
+const createDanmaku = (text, avatar = null) => {
+	const id = ++danmakuIdCounter.value
+	const track = findAvailableTrack()
+	
+	const width = calculateDanmakuWidth(text)
+	// 获取屏幕宽度,兼容不同平台
+	const screenWidth = uni.getSystemInfoSync().screenWidth || 375
+	const left = screenWidth + props.spacing
+	
+	// 计算弹幕持续时间并记录轨道占用,增加缓冲时间
+	const extraDistance = 200
+	const duration = (screenWidth + width + extraDistance) / props.speed
+	const endTime = Date.now() + duration * 1000
+	trackOccupancy.value[track] = endTime
+	
+	return {
+		id,
+		text,
+		avatar,
+		track,
+		width,
+		left,
+		isScrolling: false,
+		startTime: Date.now()
+	}
+}
+
+const addDanmaku = (text, avatar = null) => {
+	const danmaku = createDanmaku(text, avatar)
+	
+	visibleDanmakus.value.push(danmaku)
+	
+	setTimeout(() => {
+		danmaku.isScrolling = true
+		// 确保弹幕完全离开可视范围才消失,增加更多缓冲距离
+		danmaku.left = -(danmaku.width + 100)
+	}, 50)
+	
+	// 获取屏幕宽度,兼容不同平台
+	const screenWidth = uni.getSystemInfoSync().screenWidth || 375
+	// 增加更多的额外距离确保弹幕完全离开可视范围
+	const extraDistance = 200 // 增加缓冲距离
+	const duration = (screenWidth + danmaku.width + extraDistance) / props.speed
+	setTimeout(() => {
+		removeDanmaku(danmaku.id)
+	}, duration * 1000)
+}
+
+const removeDanmaku = (id) => {
+	const index = visibleDanmakus.value.findIndex(item => item.id === id)
+	if (index > -1) {
+		visibleDanmakus.value.splice(index, 1)
+		emit('danmakuComplete', id)
+	}
+}
+
+const clearDanmakus = () => {
+	visibleDanmakus.value = []
+	// 清空轨道占用记录
+	trackOccupancy.value = new Array(props.trackCount).fill(0)
+}
+
+const startDanmakuAnimation = () => {
+	if (props.danmakuList.length === 0) return
+	
+	let currentIndex = 0
+	
+	const playNext = () => {
+		if (currentIndex >= props.danmakuList.length) {
+			if (props.infinite) {
+				currentIndex = 0
+			} else {
+				return
+			}
+		}
+		
+		const danmaku = props.danmakuList[currentIndex]
+		addDanmaku(danmaku.text, danmaku.avatar)
+		currentIndex++
+		
+		// 根据容器高度动态调整间隔时间,低高度容器需要更大的间隔避免叠加
+		const baseInterval = props.height < 150 ? 
+			Math.max(2000, props.height * 2) : // 低高度容器使用适中的基础间隔
+			Math.max(1500, props.height * 2)   // 正常高度容器
+		const minInterval = baseInterval
+		const maxInterval = baseInterval + (props.height < 150 ? 2000 : 2000)
+		const interval = Math.random() * (maxInterval - minInterval) + minInterval
+		animationTimer.value = setTimeout(playNext, interval)
+	}
+	
+	playNext()
+}
+
+const stopDanmakuAnimation = () => {
+	if (animationTimer.value) {
+		clearTimeout(animationTimer.value)
+		animationTimer.value = null
+	}
+}
+
+watch(() => props.danmakuList, (newList) => {
+	if (newList.length > 0) {
+		stopDanmakuAnimation()
+		clearDanmakus()
+		startDanmakuAnimation()
+	}
+}, { deep: true })
+
+onMounted(() => {
+	if (props.danmakuList.length > 0) {
+		startDanmakuAnimation()
+	}
+})
+
+onUnmounted(() => {
+	stopDanmakuAnimation()
+})
+
+defineExpose({
+	addDanmaku,
+	clearDanmakus,
+	startDanmakuAnimation,
+	stopDanmakuAnimation
+})
+</script>
+
+<style lang="scss" scoped>
+.danmaku-container {
+	width: 100%;
+	position: relative;
+	overflow: hidden;
+	// background: rgba(0, 0, 0, 0.1);
+	border-radius: 8rpx;
+}
+
+.danmaku-stage {
+	width: 100%;
+	height: 100%;
+	position: relative;
+}
+
+.danmaku-item {
+	position: absolute;
+	top: 0;
+	left: 0;
+	z-index: 10;
+	pointer-events: none;
+	
+	&.danmaku-scrolling {
+		transition: left linear;
+	}
+	
+	// 确保弹幕在轨道中居中显示
+	transform: translateY(-50%);
+}
+
+.danmaku-content {
+	display: flex;
+	align-items: center;
+	background: rgba(0, 0, 0, 0.6);
+	border-radius: 20rpx;
+	padding: 8rpx 20rpx;
+	backdrop-filter: blur(10rpx);
+	border: 1rpx solid rgba(255, 255, 255, 0.2);
+	// box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.3);
+	width: fit-content;
+	min-width: 120rpx;
+	// 确保弹幕内容不会超出容器边界
+	max-height: 48rpx;
+	overflow: hidden;
+	// 防止弹幕在滚动过程中高度变化
+	line-height: 1.2;
+	// 增加内边距确保文字有足够空间
+	box-sizing: border-box;
+}
+
+.danmaku-avatar {
+	width: 32rpx;
+	height: 32rpx;
+	border-radius: 50%;
+	margin-right: 12rpx;
+	border: 2rpx solid rgba(255, 255, 255, 0.3);
+	flex-shrink: 0;
+	// 确保头像高度稳定
+	object-fit: cover;
+}
+
+.danmaku-text {
+	color: #000;
+	font-size: 24rpx;
+	font-weight: 500;
+	// text-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.5);
+	white-space: nowrap;
+	overflow: hidden;
+	text-overflow: ellipsis;
+	max-width: 400rpx;
+	flex: 1;
+	// 确保文字高度稳定
+	line-height: 1.2;
+	height: 32rpx;
+	display: flex;
+	align-items: center;
+	// 增加文字间距确保显示完整
+	letter-spacing: 1rpx;
+}
+
+.danmaku-track .danmaku-content {
+	background: rgba(244, 246, 248, 0.6);
+}
+</style>

+ 2 - 1
components/posterPopup/posterPopup.vue

@@ -180,7 +180,8 @@
 			}
 
 			.qr-code {
-				text-align: right;
+				text-align: right;
+				font-family: "SweiSpringCJKtc";
 
 				.image {
 					width: 112rpx;

+ 16 - 4
pages/tabBar/tree/components/memberLevel.vue

@@ -90,15 +90,25 @@
 <style lang="scss" scoped>
 	@import "@/static/style/mixin.scss";
 
-	.member-level {
+	.member-level {
+		position: relative;
+		&::before{
+			content: '';
+			position: absolute;
+			top: -20rpx;
+			width: 100%;
+			height: 474rpx;
+			background-image: linear-gradient(180deg,#008DFD,transparent);
+		}
 		.user-wrap {
 			display: flex;
 			border-radius: 70rpx 0 0 70rpx;
 			padding: 16rpx 20rpx;
 			margin: 0 0 10rpx 24rpx;
-			position: relative;
+			position: relative;
 			width: calc(100% - 24rpx);
-			box-sizing: border-box;
+			box-sizing: border-box;
+			backdrop-filter: blur(14rpx);
 			background-image: linear-gradient(90deg, rgba(255, 255, 255, 0.4), transparent);
 
 			.user-info {
@@ -172,7 +182,9 @@
 			align-items: center;
 			font-size: 28rpx;
 			font-weight: 500;
-			margin: auto;
+			margin: auto;
+			position: relative;
+			z-index: 2;
 
 			.info-text {
 				text-align: center;

+ 344 - 279
pages/tabBar/tree/subPages/diary.vue

@@ -1,9 +1,12 @@
 <template>
 	<view class="sub-base-container">
 		<view class="tree-info">
-			<image class="image" mode="aspectFill" :src="getImageUrl(treeData.treeImages[0].filename)"></image>
+			<image class="image" mode="aspectFill"
+				:src="getImageUrl(treeData.treeImages && treeData.treeImages[0].filename)"></image>
 			<view class="info-cont">
 				<view class="tree-name">果树档案</view>
+				<image @click="handleEditName" class="photo-icon"
+					:src="`${config.BASIC_IMG}img/treePage/blue-edit.png`"></image>
 				<view class="text-wrap">
 					<view class="row-text">
 						<text class="label">树木名称</text>
@@ -11,7 +14,7 @@
 					</view>
 					<view class="row-text">
 						<text class="label">树木编号</text>
-						<text>{{treeData.buyList[0].code}}</text>
+						<text>{{treeData.buyList && treeData.buyList[0].code}}</text>
 					</view>
 					<view class="row-text">
 						<text class="label">来自农场</text>
@@ -25,159 +28,191 @@
 			</view>
 		</view>
 		<view class="tree-diary">
-			<view class="diary-head">
-				<view class="date" @click="handleDate">
-					<image class="img" :src="`${config.BASIC_IMG}img/subTreePage/date-icon.png`"></image>
-					<text>{{formatDate(dateVal)}}</text>
-				</view>
-				<view class="arrow-group">
-					<view class="arrow-icon" @click="changeDay(-1)">
-						<up-icon name="arrow-left" size="17" bold color="#fff"></up-icon>
-					</view>
-					<view class="arrow-icon" @click="changeDay(+1)">
-						<up-icon name="arrow-right" size="17" bold color="#fff"></up-icon>
-					</view>
-				</view>
-			</view>
-			<view class="diary-cont">
-				<template v-if="active === 0">
-					<view class="text">{{diaryData.content}}</view>
-					<view class="photo">
-						<image class="img" src="https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png"></image>
-					</view>
-				</template>
-				<view class="template-1" v-else-if="active === 1">
-					<view class="photo-group">
-						<image class="img" src="https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png"></image>
-						<image class="img float" src="https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png"></image>
-					</view>
-					<view class="text">{{diaryData.content}}</view>
-				</view>
-				<view class="template-2" v-else="active === 2">
-					<view class="photo-group">
-						<image class="img" src="https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png"></image>
-						<image class="img float" src="https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png"></image>
-					</view>
-					<view class="text">{{diaryData.content}}</view>
-				</view>
+			<view class="diary-head">
+				<view class="date" @click="handleDate">
+					<image class="img" :src="`${config.BASIC_IMG}img/subTreePage/date-icon.png`"></image>
+					<text>{{formatDate(dateVal)}}</text>
+				</view>
+				<view class="arrow-group">
+					<view class="arrow-icon" @click="changeDay(-1)">
+						<up-icon name="arrow-left" size="17" bold color="#fff"></up-icon>
+					</view>
+					<view class="arrow-icon" @click="changeDay(+1)">
+						<up-icon name="arrow-right" size="17" bold color="#fff"></up-icon>
+					</view>
+				</view>
+			</view>
+			<view class="diary-cont">
+				<template v-if="active === 0">
+					<view class="text">{{diaryData.content}}</view>
+					<view class="photo">
+						<image class="img" src="https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png">
+						</image>
+					</view>
+				</template>
+				<view class="template-1" v-else-if="active === 1">
+					<view class="photo-group">
+						<image class="img" src="https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png">
+						</image>
+						<image class="img float"
+							src="https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png"></image>
+					</view>
+					<view class="text">{{diaryData.content}}</view>
+				</view>
+				<view class="template-2" v-else-if="active === 2">
+					<view class="photo-group">
+						<image class="img" src="https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png">
+						</image>
+						<image class="img float"
+							src="https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png"></image>
+					</view>
+					<view class="text">{{diaryData.content}}</view>
+				</view>
 			</view>
 		</view>
-	</view>
-	<up-datetime-picker
-		:show="showDatetim"
-		v-model="dateVal"
-		mode="date"
-		:maxDate="Date.now()"
-		:formatter="formatter"
-		@cancel="cancel"
-		@confirm="confirm"
-	></up-datetime-picker>
+		<up-calendar ref="calendar" :show="showDatetime" mode="single" :defaultDate="selectedDate" @confirm="confirm" @close="cancel"
+			:minDate="formatDate(new Date(Date.now() - 365 * 24 * 60 * 60 * 1000),'-')" :maxDate="formatDate(new Date(),'-')" monthNum="13"
+			:formatter="formatter"></up-calendar>
+		<editNamePopup ref="editNameRef" @editEnd="getBySampleId"></editNamePopup>
+	</view>
 </template>
 
 <script setup>
-	import config from "@/api/config.js"
-	import {ref} from "vue"
-	import TREE from '@/api/tree.js'
-	import {
-		onLoad
-	} from '@dcloudio/uni-app'
-	const resize = "?x-oss-process=image/resize,w_1000";
-	
-	const getImageUrl = (filename) =>{
-		if (filename?.startsWith("https")) {
-			return filename; // 直接使用完整 URL
-		} else {
-			return config.BASE_IMG_URL + filename + resize; // 拼接基础 URL
-		}
-	};
-	
-	const sampleId = ref('')
-	onLoad((options)=>{
-		sampleId.value = options.sampleId
-		featchGetSampleDiary()
-		getBySampleId()
-	})
-	
-	const diaryData = ref({})
-	const featchGetSampleDiary = () =>{
-		const params = {
-			sampleId:110950,
-			createDateStart:'2025-04-13',
-			createDateEnd:'2025-04-13',
-			page:1,
-			limit:1
-		}
-		TREE.getSampleDiary(params).then(res =>{
-			if(res.data.length){
-				diaryData.value = res.data[0]
-			}
-		})
-	}
-	
-	const treeData = ref({})
-	const treeName = ref('')
-	const getBySampleId = () => {
-		TREE.getBySampleId({
-			sampleId: sampleId.value
-		}).then(({
-			data
-		}) => {
-			treeData.value = data || {}
-			treeName.value = data.buyList[0].treeName || (data.buyList[0].nickname.length ? data.buyList[0]
-				.nickname.slice(0, 3) + "荔" : data.buyList[0].owner.slice(0, 3) + "荔")
-		})
-	}
-	
-	function formatDate(timestamp,type) {
-	  const date = new Date(timestamp);
-	  const year = date.getFullYear();
-	  const month = String(date.getMonth() + 1)
-	  const day = String(date.getDate()).padStart(2, '0');         // 日期补零
-	  return `${year}${type || '年'}${month}${type || '月'}${day}${type?'':'日'}`;
-	}
-	
-	const currentDate = ref(new Date())
-	function changeDay(type) {
-		currentDate.value = new Date(currentDate.value);
-		currentDate.value.setDate(currentDate.value.getDate() + type);
-		if(currentDate.value > new Date()){
-			currentDate.value.setDate(currentDate.value.getDate() - 1);
-			return
-		}
-		dateVal.value = Date.parse(currentDate.value);
-		//获取 0-2 的随机整数
-		const randomInt = Math.floor(Math.random() * 3);
-		active.value = randomInt
-	}
-	
-	const showDatetim = ref(false)
-	const dateVal = ref(Date.now())
-	
-	const handleDate = () =>{
-		showDatetim.value = true
-	}
-	const cancel = () =>{
-		showDatetim.value = false
-	}
-	const confirm = ()=>{
-		const aaa = formatDate(date.value,'-')
-		console.log(date.value,aaa)
-		cancel()
-	}
-	
-	const active = ref(2)
-	const formatter = (type, value) => {
-	  if (type === 'year') {
-	    return `${value}年`;
-	  }
-	  if (type === 'month') {
-	    return `${value}月`;
-	  }
-	  if (type === 'day') {
-	    return `${value}日`;
-	  }
-	  return value;
+	import config from "@/api/config.js"
+	import { ref } from "vue"
+	import TREE from '@/api/tree.js'
+	import { onLoad, onReady } from '@dcloudio/uni-app'
+	
+	const resize = "?x-oss-process=image/resize,w_1000";
+
+	const getImageUrl = (filename) => {
+		if (filename?.startsWith("https")) {
+			return filename;
+		} else {
+			return config.BASE_IMG_URL + filename + resize;
+		}
+	};
+
+	const sampleId = ref('')
+	const farmBuyId = ref('')
+	
+	onLoad((options) => {
+		sampleId.value = options.sampleId
+		farmBuyId.value = options.farmBuyId
+		featchGetSampleDiary()
+		getBySampleId()
+	})
+
+	const editNameRef = ref(null)
+	const formatDatePoint = (dateStr) => {
+		return dateStr.split(" ")[0].replace(/-/g, ".");
+	};
+	
+	const handleEditName = () => {
+		editNameRef.value.showPopup({
+			id: farmBuyId.value,
+			treeName: treeName.value,
+			nickname: treeData.value.buyList[0].nickname,
+			showName: treeData.value.buyList[0].showName,
+			createDate: formatDatePoint(treeData.value.buyList[0].createDate),
+		})
+	}
+
+	const diaryData = ref({})
+	const featchGetSampleDiary = () => {
+		const currentDateStr = selectedDate.value[0]
+		const params = {
+			sampleId: sampleId.value,
+			createDateStart: currentDateStr,
+			createDateEnd: currentDateStr,
+			page: 1,
+			limit: 1
+		}
+		TREE.getSampleDiary(params).then(res => {
+			if (res.data.length) {
+				diaryData.value = res.data[0]
+			} else {
+				diaryData.value = {}
+			}
+		})
+	}
+
+	const treeData = ref({})
+	const treeName = ref('')
+	const getBySampleId = () => {
+		TREE.getBySampleId({
+			sampleId: sampleId.value
+		}).then(({ data }) => {
+			treeData.value = data || {}
+			treeName.value = data.buyList[0].treeName || (data.buyList[0].nickname.length ? data.buyList[0]
+				.nickname.slice(0, 3) + "荔" : data.buyList[0].owner.slice(0, 3) + "荔")
+		})
+	}
+
+	function formatDate(timestamp, type) {
+		const date = new Date(timestamp);
+		const year = date.getFullYear();
+		const month = String(date.getMonth() + 1).padStart(2, '0');
+		const day = String(date.getDate()).padStart(2, '0');
+		return `${year}${type || '年'}${month}${type || '月'}${day}${type?'':'日'}`;
+	}
+
+	const currentDate = ref(new Date())
+
+	function changeDay(type) {
+		currentDate.value = new Date(currentDate.value);
+		currentDate.value.setDate(currentDate.value.getDate() + type);
+		if (currentDate.value > new Date()) {
+			currentDate.value.setDate(currentDate.value.getDate() - 1);
+			return
+		}
+		dateVal.value = Date.parse(currentDate.value);
+		selectedDate.value = [formatDate(currentDate.value, '-')];
+		featchGetSampleDiary();
+		const randomInt = Math.floor(Math.random() * 3);
+		active.value = randomInt
+	}
+
+	const showDatetime = ref(false)
+	const dateVal = ref(Date.now())
+	const selectedDate = ref([formatDate(Date.now(),'-')])
+	const calendar = ref(null)
+
+	const handleDate = () => {
+		// 更新选择的日期
+		selectedDate.value = [formatDate(dateVal.value, '-')]
+		// 显示日历
+		showDatetime.value = true
+	}
+	
+	const cancel = () => {
+		showDatetime.value = false
+	}
+	
+	const confirm = (e) => {
+		selectedDate.value = e
+		dateVal.value = Date.parse(e)
+		currentDate.value = new Date(e)
+		featchGetSampleDiary()
+		cancel()
+	}
+
+	const active = ref(0)
+
+	// 格式化函数
+	const formatter = (day) => {
+		// 可以在这里添加自定义的日期格式化逻辑
+		return day;
 	};
+
+	// 页面渲染完成后初始化
+	onReady(()=> {
+		// 如果需要兼容微信小程序的话,需要用此写法
+		if (calendar.value) {
+			calendar.value.setFormatter(formatter);
+			console.log('日历组件已初始化')
+		}
+	}) 
 </script>
 
 <style lang="scss" scoped>
@@ -187,7 +222,7 @@
 		background-image: linear-gradient(0deg, #FFFFFF, rgba(33, 153, 248, 0.6));
 		max-height: 484rpx;
 		min-height: 484rpx;
-		padding-top: 52rpx;
+		padding-top: 52rpx;
 
 		.tree-info {
 			display: flex;
@@ -204,6 +239,7 @@
 				width: calc(100% - 220rpx - 36rpx);
 				border: 2rpx solid #fff;
 				border-radius: 16rpx;
+				position: relative;
 				background-image: linear-gradient(180deg, rgba(241, 249, 255, .6), rgba(255, 255, 255, .75));
 
 				.tree-name {
@@ -217,6 +253,14 @@
 					text-align: center;
 				}
 
+				.photo-icon {
+					position: absolute;
+					top: 18rpx;
+					right: 22rpx;
+					width: 28rpx;
+					height: 28rpx;
+				}
+
 				.text-wrap {
 					padding: 8rpx 0 0 20rpx;
 					line-height: 36rpx;
@@ -235,136 +279,157 @@
 
 		.tree-diary {
 			width: 100%;
-			min-height: 960rpx;
-			box-sizing: border-box;
+			min-height: 960rpx;
+			box-sizing: border-box;
 			padding: 70rpx 0 56rpx 30rpx;
-			@include ossBg("subTreePage/diary-bg.png");
-			.diary-head{
-				display: flex;
-				justify-content: space-between;
-				.date{
-					font-family: 'PangMenZhengDao';
-					font-size: 29rpx;
-					color: #2199F8;
-					display: flex;
-					align-items: center;
-					.img{
-						width: 26rpx;
-						height: 26rpx;
-						margin-right: 10rpx;
-					}
-				}
-				.arrow-group{
-					display: flex;
-					.arrow-icon{
-						border-radius: 50%;
-						background: rgba(33, 153, 248, 0.2);
-						width: 56rpx;
-						height: 56rpx;
-						display: flex;
-						align-items: center;
-						justify-content: center;
-					}
-					.arrow-icon + .arrow-icon{
-						margin-left: 20rpx;
-					}
-				}
-			}
-			.diary-cont{
-				padding: 80rpx 60rpx 0 26rpx;
-				.text{
-					width: 72%;
-					margin: 60rpx auto;
-					font-weight: 500;
-					position: relative;
-					&::before{
-						content: '“';
-						position: absolute;
-						top: -85rpx;
-						left: -90rpx;
-						font-size: 140rpx;
-						color: rgba(33, 153, 248, 0.2);
-					}
-					&::after{
-						content: '”';
-						position: absolute;
-						bottom: -130rpx;
-						right: -110rpx;
-						font-size: 140rpx;
-						color: rgba(33, 153, 248, 0.2);
-					}
-				}
-				.photo{
-					width: 100%;
-					height: 420rpx;
-					margin-top: 90rpx;
-					border: 10rpx solid #d0e6fb;
-					box-shadow:  0 4rpx 8rpx 0 rgba(0, 0, 0, 0.1);
-					position: relative;
-					&::before{
-						content: '';
-						position: absolute;
-						z-index: 2;
-						right: -34rpx;
-						top: 0;
-						background: rgba(33, 153, 248, 0.56);
-						transform: rotate(38deg);
-						width: 102rpx;
-						height: 30rpx;
-					}
-					.img{
-						width: 100%;
-						height: 100%;
-						object-fit: cover;
-					}
-				}
-				.template-1{
-					.photo-group{
-						margin:0 -14rpx 240rpx 10rpx;
-						.img{
-							width: 354rpx;
-							height: 240rpx;
-							border: 10rpx solid rgba(255, 255, 255, 0.6);
-							box-shadow:  0 4rpx 8rpx 0 rgba(0, 0, 0, 0.1);
-						}
-						.float{
-							width: 400rpx;
-							height: 264rpx;
-							float: right;
-							margin-top: -130rpx;
-						}
-					}
-					.text{
-						width: 55%;
-						margin: 0 auto;
-					}
-				}
-				.template-2{
-					.photo-group{
-						margin:0 -10rpx 220rpx 10rpx;
-						.img{
-							position: relative;
-							z-index: 2;
-							width: 440rpx;
-							height: 300rpx;
-							border: 10rpx solid rgba(255, 255, 255, 0.3);
-							box-shadow:  0 4rpx 8rpx 0 rgba(0, 0, 0, 0.1);
-						}
-						.float{
-							width: 332rpx;
-							height: 222rpx;
-							float: right;
-							margin-top: -100rpx;
-							transform: rotate(10deg);
-							position: relative;
-							z-index: 1;
-						}
-					}
-					.text{
-						width: 66%;
-						margin: 0 auto;
-					}
-				}
+			@include ossBg("subTreePage/diary-bg.png");
+
+			.diary-head {
+				display: flex;
+				justify-content: space-between;
+
+				.date {
+					font-family: 'PangMenZhengDao';
+					font-size: 29rpx;
+					color: #2199F8;
+					display: flex;
+					align-items: center;
+
+					.img {
+						width: 26rpx;
+						height: 26rpx;
+						margin-right: 10rpx;
+					}
+				}
+
+				.arrow-group {
+					display: flex;
+
+					.arrow-icon {
+						border-radius: 50%;
+						background: rgba(33, 153, 248, 0.2);
+						width: 56rpx;
+						height: 56rpx;
+						display: flex;
+						align-items: center;
+						justify-content: center;
+					}
+
+					.arrow-icon+.arrow-icon {
+						margin-left: 20rpx;
+					}
+				}
+			}
+
+			.diary-cont {
+				padding: 80rpx 60rpx 0 26rpx;
+
+				.text {
+					width: 72%;
+					margin: 60rpx auto;
+					font-weight: 500;
+					position: relative;
+
+					&::before {
+						content: '"';
+						position: absolute;
+						top: -85rpx;
+						left: -90rpx;
+						font-size: 140rpx;
+						color: rgba(33, 153, 248, 0.2);
+					}
+
+					&::after {
+						content: '"';
+						position: absolute;
+						bottom: -130rpx;
+						right: -110rpx;
+						font-size: 140rpx;
+						color: rgba(33, 153, 248, 0.2);
+					}
+				}
+
+				.photo {
+					width: 100%;
+					height: 420rpx;
+					margin-top: 90rpx;
+					border: 10rpx solid #d0e6fb;
+					box-shadow: 0 4rpx 8rpx 0 rgba(0, 0, 0, 0.1);
+					position: relative;
+
+					&::before {
+						content: '';
+						position: absolute;
+						z-index: 2;
+						right: -34rpx;
+						top: 0;
+						background: rgba(33, 153, 248, 0.56);
+						transform: rotate(38deg);
+						width: 102rpx;
+						height: 30rpx;
+					}
+
+					.img {
+						width: 100%;
+						height: 100%;
+						object-fit: cover;
+					}
+				}
+
+				.template-1 {
+					.photo-group {
+						margin: 0 -14rpx 240rpx 10rpx;
+
+						.img {
+							width: 354rpx;
+							height: 240rpx;
+							border: 10rpx solid rgba(255, 255, 255, 0.6);
+							box-shadow: 0 4rpx 8rpx 0 rgba(0, 0, 0, 0.1);
+						}
+
+						.float {
+							width: 400rpx;
+							height: 264rpx;
+							float: right;
+							margin-top: -130rpx;
+						}
+					}
+
+					.text {
+						width: 55%;
+						margin: 0 auto;
+					}
+				}
+
+				.template-2 {
+					.photo-group {
+						margin: 0 -10rpx 220rpx 10rpx;
+
+						.img {
+							position: relative;
+							z-index: 2;
+							width: 440rpx;
+							height: 300rpx;
+							border: 10rpx solid rgba(255, 255, 255, 0.3);
+							box-shadow: 0 4rpx 8rpx 0 rgba(0, 0, 0, 0.1);
+						}
+
+						.float {
+							width: 332rpx;
+							height: 222rpx;
+							float: right;
+							margin-top: -100rpx;
+							transform: rotate(10deg);
+							position: relative;
+							z-index: 1;
+						}
+					}
+
+					.text {
+						width: 66%;
+						margin: 0 auto;
+					}
+				}
 			}
 		}
 	}

+ 1 - 1
pages/tabBar/tree/subPages/friendTree.vue

@@ -25,7 +25,7 @@
 			</view>
 		</view>
 		<!-- 祝福弹窗 -->
-		<blessingsPopup :show="showBlessingsPopup" clockinType="3" :farmBuyId="farmBuyId" @clockinCallback="getBySampleId"></blessingsPopup>
+		<blessingsPopup :show="showBlessingsPopup" clockinType="3" :farmBuyId="farmBuyId" @clockinCallback="getBySampleId" isFriend></blessingsPopup>
 	</view>
 </template>
 

+ 233 - 115
pages/tabBar/tree/tree.vue

@@ -1,82 +1,106 @@
 <template>
-	<view class="base-container">
-		<member-level :treeData="treeData">
-			<view class="toogle" @click="handleShow">切换 {{name}}<up-icon class="icon" name="arrow-down" color="#fff"
-					size="12"></up-icon></view>
-		</member-level>
-		<view class="tree-cont">
-			<view class="tree-name">
-				<view>{{treeName}}</view>
-				<image @click="handleEditName" class="edit-icon" :src="`${config.BASIC_IMG}img/edit-icon.png`">
-				</image>
-			</view>
-			<image class="drone-icon" :src="`${config.BASIC_IMG}img/treePage/drone-icon.png`"></image>
-			<view class="tool-wrap">
-				<view class="tool-left">
-					<view :class="['tool-item',item.className]" v-for="(item,index) in toolLeftList" :key="index"
-						@click="handleToolItem(item)">
-						<image class="icon" :src="`${config.BASIC_IMG}img/treePage/l-tree-icon-${index+1}.png`">
-						</image>
-						<view class="name">{{item.name}}</view>
-					</view>
+	<view class="page-container">
+		<view class="base-container">
+			<member-level :treeData="treeData">
+				<view class="toogle" @click="handleShow">切换 {{name}}<up-icon class="icon" name="arrow-down" color="#fff"
+						size="12"></up-icon></view>
+			</member-level>
+			<view class="tree-cont">
+				<!-- 弹幕组件 -->
+				<view class="danmaku-wrapper">
+					<danmakuManager 
+						:danmakuList="danmakuList" 
+						:height="100" 
+						:infinite="true"
+						:speed="80"
+						@danmakuComplete="onDanmakuComplete"
+					></danmakuManager>
+				</view>
+				<view class="dialogue-tips">
+					<view class="tips-name">
+						<image class="tip-img" :src="`${config.BASIC_IMG}img/treePage/sun-icon.png`"></image>
+						<text>亲爱的主人!</text>
+					</view>
+					<view class="tips-cont">今天是我开花的日子,我很开心!</view>
+				</view>
+				<view class="tree-name">
+					<view>{{treeName}}</view>
+					<image @click="handleEditName" class="edit-icon" :src="`${config.BASIC_IMG}img/edit-icon.png`">
+					</image>
 				</view>
-				<view class="tool-right">
-					<view :class="['tool-item',item.className]" v-for="(item,index) in toolRightList" :key="index"
-						@click="handleToolItem(item)">
-						<image class="icon" :src="`${config.BASIC_IMG}img/treePage/r-tree-icon-${index+1}.png`">
-						</image>
-						<view class="name">{{item.name}}</view>
+				<image class="drone-icon" :src="`${config.BASIC_IMG}img/treePage/drone-icon.png`"></image>
+				<view class="tool-wrap">
+					<view class="tool-left">
+						<view :class="['tool-item',item.className]" v-for="(item,index) in toolLeftList" :key="index"
+							@click="handleToolItem(item)">
+							<image class="icon" :src="`${config.BASIC_IMG}img/treePage/l-tree-icon-${index+1}.png`">
+							</image>
+							<view class="name">{{item.name}}</view>
+						</view>
+					</view>
+					<view class="tool-right">
+						<view :class="['tool-item',item.className]" v-for="(item,index) in toolRightList" :key="index"
+							@click="handleToolItem(item)">
+							<image class="icon" :src="`${config.BASIC_IMG}img/treePage/r-tree-icon-${index+1}.png`">
+							</image>
+							<view class="name">{{item.name}}</view>
+						</view>
 					</view>
 				</view>
 			</view>
-		</view>
-		<view class="tree-footer">
-			<view class="footer-item" v-for="(item,index) in footerList" :key="index" @click="handleItem(index)">
-				<view @click="requestSubscribe">
-					<button class="share-btn" open-type="share" v-if="index === 2">
-						<image class="icon" :src="`${config.BASIC_IMG}img/treePage/b-tree-icon-${index+1}.png`">
-						</image>
-					</button>
-					<image v-else class="icon" :src="`${config.BASIC_IMG}img/treePage/b-tree-icon-${index+1}.png`">
-					</image>
-					<view class="name">{{item}}</view>
-				</view>
+			<view class="tree-footer">
+				<template v-if="userInfo.tel">
+					<view class="footer-item" v-for="(item,index) in footerList" :key="index" @click="handleItem(index)">
+						<view @click="requestSubscribe">
+							<button class="share-btn" open-type="share" v-if="index === 2">
+								<image class="icon" :src="`${config.BASIC_IMG}img/treePage/b-tree-icon-${index+1}.png`">
+								</image>
+							</button>
+							<image v-else class="icon" :src="`${config.BASIC_IMG}img/treePage/b-tree-icon-${index+1}.png`">
+							</image>
+							<view class="name">{{item}}</view>
+						</view>
+					</view>
+				</template>
+				<view v-else class="button" @click="handlePage">开启我的守护</view>
 			</view>
 		</view>
-	</view>
-	<!-- 引导弹窗 -->
-	<view class="guide-bg" v-if="showMask" @click="handleClose">
-		<image class="guide-icon" :src="`${config.BASIC_IMG}img/treePage/guide-1.png`"></image>
+		<!-- 引导弹窗 -->
+		<view class="guide-bg" v-if="showMask" @click="handleClose">
+			<image class="guide-icon" :src="`${config.BASIC_IMG}img/treePage/guide-1.png`"></image>
+		</view>
+		<!-- 切换 -->
+		<up-picker :show="showPicker" :columns="columns" :defaultIndex="[0]" @cancel="handleCancel"
+			@confirm="handleConfirm" keyName="name" valueName="speciesId"></up-picker>
+		<!-- 签到打卡 -->
+		<checkinPopup @closedCheckPopup="closedCheckPopup"></checkinPopup>
+		<!-- 编辑树名称 -->
+		<editNamePopup ref="editNameRef" @editEnd="getBySampleId"></editNamePopup>
+		<!-- 海报弹窗 -->
+		<posterPopup :showPoster="showPoster" :farmBuyId="farmBuyId" :treeName="treeName"></posterPopup>
+		<!-- 果树成功弹窗 -->
+		<guardSuccessPopup :show="showGuardSuccess" :treeData="treeData"></guardSuccessPopup>
+		<!-- 果树相册弹窗 -->
+		<tree-album-popup :show="showAlbum" :farmBuyId="farmBuyId" :sampleId="sampleId"></tree-album-popup>
+		<!-- 等级升级成功弹窗 -->
+		<levelSuccessPopup></levelSuccessPopup>
+		<!-- 祝福弹窗 -->
+		<blessingsPopup :show="showBlessingsPopup" :showSuccess="showSuccess" :farmBuyId="farmBuyId"
+			:clockinType="clockinType" @clockinCallback="getBySampleId"></blessingsPopup>
 	</view>
-	<!-- 切换 -->
-	<up-picker :show="showPicker" :columns="columns" :defaultIndex="[0]" @cancel="handleCancel"
-		@confirm="handleConfirm" keyName="name" valueName="speciesId"></up-picker>
-	<!-- 签到打卡 -->
-	<checkinPopup></checkinPopup>
-	<!-- 编辑树名称 -->
-	<editNamePopup ref="editNameRef" @editEnd="getBySampleId"></editNamePopup>
-	<!-- 海报弹窗 -->
-	<posterPopup :showPoster="showPoster" :farmBuyId="farmBuyId" :treeName="treeName"></posterPopup>
-	<!-- 果树成功弹窗 -->
-	<guardSuccessPopup :show="showGuardSuccess" :treeData="treeData"></guardSuccessPopup>
-	<!-- 果树相册弹窗 -->
-	<tree-album-popup :show="showAlbum" :farmBuyId="farmBuyId" :sampleId="sampleId"></tree-album-popup>
-	<!-- 等级升级成功弹窗 -->
-	<levelSuccessPopup></levelSuccessPopup>
-	<!-- 祝福弹窗 -->
-	<blessingsPopup :show="showBlessingsPopup" :showSuccess="showSuccess" :farmBuyId="farmBuyId"
-		:clockinType="clockinType" @clockinCallback="getBySampleId"></blessingsPopup>
 </template>
 
 <script setup>
 	import config from "@/api/config.js"
 	import {
 		onLoad,
-		onShareAppMessage
+		onShareAppMessage,
+		onShow
 	} from '@dcloudio/uni-app'
 	import memberLevel from "./components/memberLevel.vue"
 	import treeAlbumPopup from "./components/treeAlbumPopup.vue"
 	import blessingsPopup from "./components/blessingsPopup.vue"
+	import danmakuManager from "@/components/danmakuManager/danmakuManager.vue"
 	import TREE from '@/api/tree.js'
 	import USER from '@/api/user.js'
 	import {
@@ -153,17 +177,20 @@
 
 	onLoad(() => {
 		featchIsLookedGuide()
-		featchLightTree()
 		featchCategoryList()
-	})
-	
-	//品类查询所有
-	const featchCategoryList = () =>{
-		TREE.categoryList().then(({
-			data
-		}) => {
-			columns.value[0] = data || []
-		})
+	})
+	
+	onShow(()=>{
+		featchLightTree()
+	})
+	
+	//品类查询所有
+	const featchCategoryList = () =>{
+		TREE.categoryList().then(({
+			data
+		}) => {
+			columns.value[0] = data || []
+		})
 	}
 
 	const showMask = ref(false)
@@ -174,43 +201,49 @@
 			data
 		}) => {
 			if (data == false) {
-				showMask.value = true
+				showMask.value = true
+				
 			}
 		})
-	}
-	
-	const handleClose = async () =>{
-		const {code} = await USER.lookedGuide({type:1})
-		if(code === 0){
-			showMask.value = false
-		}
+	}
+	
+	const handleClose = async () =>{
+		const {code} = await USER.lookedGuide({type:1})
+		if(code === 0){
+			showMask.value = false
+		}
 	}
 
 	const featchSessionStore = () => {
 		USER.getSessionStore({
 			key: 'successTree',
 			miniUserId: userInfo.id
-		}).then((({data}) => {
+		}).then((({data}) => {
 			if (data?.val == 0) {
 				showGuardSuccess.value = !showGuardSuccess.value
 				USER.setSessionStore({
 					key: 'successTree',
-					miniUserId: userInfo.id
+					miniUserId: userInfo.id,
+					val:1
 				})
 			}
 		}))
 	}
 	
-	const sampleId = ref('')
-	//通过品类查询当前树
-	const featchLightTree = () =>{
-		TREE.getLightTree({farmId:766}).then((res =>{
-			sampleId.value = res.data.id
-			getBySampleId()
-		}))
+	const closedCheckPopup = () =>{
+		showPoster.value = !showPoster.value
+	}
+	
+	const sampleId = ref('')
+	//通过品类查询当前树
+	const featchLightTree = () =>{
+		TREE.getLightTree({farmId:766}).then((res =>{
+			sampleId.value = res.data.id
+			getBySampleId()
+		}))
 	}
 
-	const treeData = ref({})
+	const treeData = ref({})
 	const farmBuyId = ref('')
 	const treeName = ref('')
 	const userInfo = uni.getStorageSync('userInfo')
@@ -227,9 +260,15 @@
 			} else {
 				treeName.value = '飞鸟守护'
 			}
-			farmBuyId.value = data.buyList[0].id
-			featchSessionStore()
+			farmBuyId.value = data.buyList[0].id
+			featchSessionStore()
 		})
+	}
+	
+	const handlePage = () =>{
+		uni.navigateTo({
+			url: `/pages/login/index`
+		});
 	}
 
 	const showPoster = ref(false)
@@ -253,9 +292,50 @@
 	const showBlessingsPopup = ref(false)
 	const showSuccess = ref(false)
 	const clockinType = ref('1')
+	
+	// 弹幕相关数据
+	const danmakuList = ref([
+		{
+			text: "晨曦微露,阳光洒满大地",
+			avatar: `${userInfo.icon}`
+		},
+		{
+			text: "守护果树,收获美好",
+			avatar: `${userInfo.icon}`
+		},
+		{
+			text: "今天也要给果树浇水哦",
+			avatar: `${userInfo.icon}`
+		},
+		{
+			text: "果树长得好快啊",
+			avatar: `${config.BASIC_IMG}img/treePage/drone-icon.png`
+		},
+		{
+			text: "期待果实成熟的那一天",
+			avatar: `${config.BASIC_IMG}img/treePage/drone-icon.png`
+		},
+		{
+			text: "和好友一起守护果树",
+			avatar: `${config.BASIC_IMG}img/treePage/drone-icon.png`
+		},
+		{
+			text: "每日签到,果树茁壮成长",
+			avatar: `${config.BASIC_IMG}img/treePage/drone-icon.png`
+		},
+		{
+			text: "分享给更多朋友",
+			avatar: `${config.BASIC_IMG}img/treePage/drone-icon.png`
+		}
+	])
+	
+	// 弹幕完成回调
+	const onDanmakuComplete = (id) => {
+		console.log('弹幕播放完成:', id)
+	}
 	const handleItem = (index) => {
 		if (index === 0) {
-			clockinType.value = 1
+			clockinType.value = 1
 			if (treeData.value.buyList[0].level.clockinMap['1']) {
 				uni.showToast({
 					title: '今日已守护',
@@ -331,32 +411,42 @@
 </script>
 
 <style lang="scss" scoped>
-	@import "@/static/style/mixin.scss";
-	.guide-bg{
-		position: fixed;
-		top: 0;
-		width: 100%;
-		height: 107vh;
-		@include ossBg("treePage/guide-bg.png");
-		z-index: 999;
-		display: flex;
-		align-items: center;
-		justify-content: center;
-		.guide-icon{
-			width: 360rpx;
-			height: 236rpx
-		}
-	}
-	/* #ifdef H5 */
-	.guide-bg{
-		height: 102vh;
-	}
+	@import "@/static/style/mixin.scss";
+	.guide-bg{
+		position: fixed;
+		top: 0;
+		width: 100%;
+		height: 107vh;
+		@include ossBg("treePage/guide-bg.png");
+		z-index: 999;
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		.guide-icon{
+			width: 360rpx;
+			height: 236rpx
+		}
+	}
+	/* #ifdef H5 */
+	.guide-bg{
+		height: 102vh;
+	}
 	/* #endif */
 
 	.base-container {
 		@include ossBg("tree-bg.png");
 		// background-position: top left;
 		padding: 22rpx 0;
+		position: relative;
+		
+		.danmaku-wrapper {
+			position: absolute;
+			top: 21%;
+			left: 0;
+			right: 0;
+			z-index: 100;
+			pointer-events: none;
+		}
 
 		.toogle {
 			position: absolute;
@@ -376,12 +466,32 @@
 
 		.tree-cont {
 			width: 100%;
-			margin-top: 10rpx;
+			margin-top: 10rpx;
+			.dialogue-tips{
+				position: absolute;
+				top: 31.5%;
+				right: 80rpx;
+				font-size: 24rpx;
+				@include ossBg("treePage/tips-bg.png");
+				padding: 10rpx 10rpx 30rpx 20rpx;
+				.tips-name{
+					font-size: 28rpx;
+					font-weight: 500;
+					margin-bottom: 6rpx;
+					display: flex;
+					align-items: center;
+					.tip-img{
+						width: 40rpx;
+						height: 40rpx;
+						margin-right: 4rpx;
+					}
+				}
+			}
 
 			.tree-name {
 				position: absolute;
 				top: 60.45%;
-				left: calc(50% - 32rpx);
+				left: calc(50% - 32rpx);
 				z-index: 2;
 				font-size: 22rpx;
 				font-family: 'SweiSpringCJKtc';
@@ -486,7 +596,15 @@
 			bottom: 76rpx;
 			width: 100%;
 			display: flex;
-			justify-content: center;
+			justify-content: center;
+			.button {
+				padding: 26rpx 80rpx;
+				border-radius: 50rpx;
+				border: 2rpx solid #fff;
+				background-image: linear-gradient(120deg, #FFD887, #ED9E1E);
+				margin-top: 20rpx;
+				color: #fff;
+			}
 
 			.footer-item {
 				width: 18%;