Browse Source

添加引导部分

lalalashen 2 months ago
parent
commit
bcf6ee4d44
3 changed files with 363 additions and 4 deletions
  1. 329 0
      components/guide/GuideManager.vue
  2. 17 3
      pages/isLand/homeLand.vue
  3. 17 1
      pages/isLand/mainLand.vue

+ 329 - 0
components/guide/GuideManager.vue

@@ -0,0 +1,329 @@
+<template>
+  <view class="guide-manager">
+    <!-- 遮罩层 -->
+    <view class="guide-mask" v-if="currentStage" @click="handleMaskClick">
+      <!-- 高亮区域 -->
+      <view class="highlight-area" :style="highlightStyle"></view>
+      
+      <!-- 提示框 -->
+      <view class="guide-tips" :style="tipsStyle">
+        <view class="tips-content">
+          <text class="tips-text">{{ currentStep.tips }}</text>
+          <view class="tips-buttons">
+            <text class="skip-btn" @click.stop="skipStage">跳过</text>
+            <text class="next-btn" @click.stop="nextStep">{{ isLastStep ? '完成' : '下一步' }}</text>
+          </view>
+        </view>
+        <!-- 箭头 -->
+        <view class="arrow" :style="arrowStyle"></view>
+      </view>
+    </view>
+  </view>
+</template>
+
+<script>
+export default {
+  name: 'GuideManager',
+  data() {
+    return {
+      // 当前引导阶段
+      currentStage: null,
+      // 当前步骤索引
+      currentStepIndex: 0,
+      // 已完成阶段
+      completedStages: [],
+      // 引导配置
+      guideConfig: {
+        // 主岛引导
+        mainLand: {
+          id: 'mainLand',
+          steps: [
+            {
+              target: '.house-image',
+              tips: '这是你的房屋,点击可以进入查看',
+              position: 'bottom'
+            },
+            {
+              target: '.farm-image',
+              tips: '这是农场,可以种植作物',
+              position: 'bottom'
+            },
+            {
+              target: '.shop-image',
+              tips: '这是商店,可以购买物品',
+              position: 'bottom'
+            }
+          ]
+        },
+        // 家园引导
+        homeLand: {
+          id: 'homeLand',
+          steps: [
+            {
+              target: '.house-image',
+              tips: '这是你的家,点击可以进入',
+              position: 'bottom'
+            },
+            {
+              target: '.table-image',
+              tips: '这是工作台,可以制作物品',
+              position: 'bottom'
+            },
+            {
+              target: '.taskBoard-image',
+              tips: '这是任务板,可以查看任务',
+              position: 'bottom'
+            }
+          ]
+        }
+      }
+    }
+  },
+  computed: {
+    // 当前步骤
+    currentStep() {
+      if (!this.currentStage) return null
+      return this.currentStage.steps[this.currentStepIndex]
+    },
+    // 是否是最后一步
+    isLastStep() {
+      if (!this.currentStage) return false
+      return this.currentStepIndex === this.currentStage.steps.length - 1
+    },
+    // 高亮区域样式
+    highlightStyle() {
+      if (!this.currentStep) return {}
+      return this.currentStep.rect ? {
+        top: `${this.currentStep.rect.top}px`,
+        left: `${this.currentStep.rect.left}px`,
+        width: `${this.currentStep.rect.width}px`,
+        height: `${this.currentStep.rect.height}px`
+      } : {}
+    },
+    // 提示框样式
+    tipsStyle() {
+      if (!this.currentStep || !this.currentStep.rect) return {}
+      
+      const rect = this.currentStep.rect
+      const position = this.currentStep.position
+      const systemInfo = uni.getSystemInfoSync()
+      
+      let style = {}
+      switch (position) {
+        case 'top':
+          style = {
+            bottom: `${systemInfo.windowHeight - rect.top + 10}px`,
+            left: `${rect.left + rect.width / 2}px`,
+            transform: 'translateX(-50%)'
+          }
+          break
+        case 'bottom':
+          style = {
+            top: `${rect.bottom + 10}px`,
+            left: `${rect.left + rect.width / 2}px`,
+            transform: 'translateX(-50%)'
+          }
+          break
+        case 'left':
+          style = {
+            top: `${rect.top + rect.height / 2}px`,
+            right: `${systemInfo.windowWidth - rect.left + 10}px`,
+            transform: 'translateY(-50%)'
+          }
+          break
+        case 'right':
+          style = {
+            top: `${rect.top + rect.height / 2}px`,
+            left: `${rect.right + 10}px`,
+            transform: 'translateY(-50%)'
+          }
+          break
+      }
+      
+      return style
+    },
+    // 箭头样式
+    arrowStyle() {
+      if (!this.currentStep) return {}
+      const position = this.currentStep.position
+      
+      let style = {}
+      switch (position) {
+        case 'top':
+          style = {
+            transform: 'rotate(180deg)',
+            bottom: '0'
+          }
+          break
+        case 'bottom':
+          style = {
+            transform: 'rotate(0deg)',
+            top: '0'
+          }
+          break
+        case 'left':
+          style = {
+            transform: 'rotate(90deg)',
+            right: '0'
+          }
+          break
+        case 'right':
+          style = {
+            transform: 'rotate(-90deg)',
+            left: '0'
+          }
+          break
+      }
+      
+      return style
+    }
+  },
+  methods: {
+    // 开始引导
+    startGuide(stageId) {
+      // 检查是否已完成
+      if (this.completedStages.includes(stageId)) {
+        return
+      }
+      
+      // 设置当前阶段
+      this.currentStage = this.guideConfig[stageId]
+      this.currentStepIndex = 0
+      
+      // 等待DOM更新后更新位置
+      this.$nextTick(() => {
+        this.updatePositions()
+      })
+    },
+    // 下一步
+    nextStep() {
+      if (this.isLastStep) {
+        this.completeStage()
+      } else {
+        this.currentStepIndex++
+        this.$nextTick(() => {
+          this.updatePositions()
+        })
+      }
+    },
+    // 跳过当前阶段
+    skipStage() {
+      this.completeStage()
+    },
+    // 完成当前阶段
+    completeStage() {
+      if (this.currentStage) {
+        // 添加到已完成列表
+        this.completedStages.push(this.currentStage.id)
+        // 保存到本地存储
+        this.saveCompletedStages()
+        // 重置状态
+        this.currentStage = null
+        this.currentStepIndex = 0
+      }
+    },
+    // 更新位置
+    updatePositions() {
+      if (!this.currentStep) return
+      
+      const query = uni.createSelectorQuery().in(this)
+      query.select(this.currentStep.target).boundingClientRect(data => {
+        if (data) {
+          // 更新当前步骤的位置信息
+          this.$set(this.currentStage.steps[this.currentStepIndex], 'rect', data)
+        }
+      }).exec()
+    },
+    // 保存已完成阶段
+    saveCompletedStages() {
+      uni.setStorageSync('completedGuideStages', this.completedStages)
+    },
+    // 加载已完成阶段
+    loadCompletedStages() {
+      const stages = uni.getStorageSync('completedGuideStages')
+      if (stages) {
+        this.completedStages = stages
+      }
+    },
+    // 处理遮罩层点击
+    handleMaskClick() {
+      // 可以在这里添加点击遮罩层的处理逻辑
+    }
+  },
+  mounted() {
+    // 加载已完成阶段
+    this.loadCompletedStages()
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.guide-manager {
+  .guide-mask {
+    position: fixed;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    background-color: rgba(0, 0, 0, 0.7);
+    z-index: 9999;
+    
+    .highlight-area {
+      position: absolute;
+      box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.7);
+      border-radius: 4px;
+      transition: all 0.3s ease;
+    }
+    
+    .guide-tips {
+      position: absolute;
+      background: #fff;
+      border-radius: 8px;
+      padding: 16px;
+      min-width: 200px;
+      box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
+      transition: all 0.3s ease;
+      
+      .tips-content {
+        .tips-text {
+          font-size: 14px;
+          color: #333;
+          line-height: 1.5;
+          margin-bottom: 12px;
+        }
+        
+        .tips-buttons {
+          display: flex;
+          justify-content: space-between;
+          align-items: center;
+          
+          .skip-btn,
+          .next-btn {
+            font-size: 14px;
+            padding: 4px 12px;
+            border-radius: 4px;
+            cursor: pointer;
+          }
+          
+          .skip-btn {
+            color: #999;
+          }
+          
+          .next-btn {
+            color: #fff;
+            background-color: #1890ff;
+          }
+        }
+      }
+      
+      .arrow {
+        position: absolute;
+        width: 0;
+        height: 0;
+        border: 8px solid transparent;
+        border-bottom-color: #fff;
+      }
+    }
+  }
+}
+</style> 

+ 17 - 3
pages/isLand/homeLand.vue

@@ -66,6 +66,9 @@
 
     <!-- 制造台对话框组件 -->
     <crafting-dialog :visible.sync="craftingVisible" @close="craftingVisible = false"></crafting-dialog>
+
+    <!-- 引导管理器 -->
+    <guide-manager ref="guideManager"></guide-manager>
   </view> 
 </template>
 
@@ -77,6 +80,7 @@ import ShopDialog from '@/components/dialogs/ShopDialog.vue'
 import TaskDialog from './TaskDialog.vue'
 import TalkGuide from './talkGuide.vue'
 import CraftingDialog from '@/components/dialogs/CraftingDialog.vue'
+import GuideManager from '@/components/guide/GuideManager.vue'
 
 export default {
 	components: {
@@ -85,7 +89,8 @@ export default {
 		ShopDialog,
 		TaskDialog,
 		TalkGuide,
-		CraftingDialog
+		CraftingDialog,
+		GuideManager
 	},
 	data() {
 		return {
@@ -144,7 +149,8 @@ export default {
 					isMirror: true
 				}
 			],
-			craftingVisible: false
+			craftingVisible: false,
+			guideManager: null,
 		}
 	},
 	onLoad() {
@@ -158,7 +164,8 @@ export default {
 		});
 	},
 	onShow() {
-		// this.loadData();
+		// 检查是否需要显示引导
+		this.checkAndShowGuide();
 	},
 	onReady() {
 		// 在组件渲染完成后获取图片尺寸
@@ -425,6 +432,13 @@ export default {
 			console.log('引导完成')
 			// 这里可以添加引导完成后的逻辑
 			uni.setStorageSync('isGuideCompleted', true)
+		},
+		// 检查并显示引导
+		checkAndShowGuide() {
+			// 延迟执行以确保DOM已经渲染
+			setTimeout(() => {
+				this.$refs.guideManager && this.$refs.guideManager.startGuide('homeLand');
+			}, 500);
 		}
 	},
 	beforeDestroy() {

+ 17 - 1
pages/isLand/mainLand.vue

@@ -83,6 +83,9 @@
     <shop-dialog :visible.sync="shopVisible" :shopName="currentShopName" @close="onShopClose" @buy="onShopBuy"></shop-dialog>
     <hua-tian :visible.sync="huaTianVisible" @close="onHuaTianClose" ref="huaTian"></hua-tian>
     <task-dialog :visible.sync="taskDialogVisible" @close="onTaskDialogClose"></task-dialog>
+
+    <!-- 引导管理器 -->
+    <guide-manager ref="guideManager"></guide-manager>
   </view> 
 </template>
 
@@ -93,6 +96,7 @@ import CharacterDialog from '@/components/dialogs/CharacterDialog.vue'
 import ShopDialog from '@/components/dialogs/ShopDialog.vue'
 import HuaTian from './HuaTian.vue'
 import TaskDialog from './TaskDialog.vue'
+import GuideManager from '@/components/guide/GuideManager.vue'
 
 export default {
 	components: {
@@ -100,7 +104,8 @@ export default {
 		CharacterDialog,
 		ShopDialog,
 		HuaTian,
-		TaskDialog
+		TaskDialog,
+		GuideManager
 	},
 	data() {
 		return {
@@ -131,6 +136,7 @@ export default {
 			homeArrowAnimating: false,
 			homeArrowVisible: false,
 			currentShopName: '商店',
+			guideManager: null,
 		}
 	},
 	onLoad() {
@@ -145,6 +151,8 @@ export default {
 	},
 	onShow() {
 		// this.loadData();
+		// 检查是否需要显示引导
+		this.checkAndShowGuide();
 	},
 	onReady() {
 		// 在组件渲染完成后获取图片尺寸
@@ -422,6 +430,14 @@ export default {
 			console.log('Closing task dialog...')
 			this.taskDialogVisible = false
 		},
+		
+		// 检查并显示引导
+		checkAndShowGuide() {
+			// 延迟执行以确保DOM已经渲染
+			setTimeout(() => {
+				this.$refs.guideManager && this.$refs.guideManager.startGuide('mainLand');
+			}, 500);
+		},
 	}
 }
 </script>