// 新用户引导组件 Component({ properties: { show: { type: Boolean, value: false }, steps: { type: Array, value: [] } }, data: { currentStep: 0, totalSteps: 0, title: '', description: '', spotlightStyle: {}, contentStyle: '', arrowDirection: 'top', showArrow: true }, observers: { 'show': function(show) { if (show && this.data.steps.length > 0) { this.setData({ currentStep: 0, totalSteps: this.data.steps.length }) this.showStep(0) } } }, methods: { // 显示指定步骤 showStep(stepIndex) { if (stepIndex < 0 || stepIndex >= this.data.steps.length) { return } const step = this.data.steps[stepIndex] this.setData({ currentStep: stepIndex, title: step.title, description: step.description, showArrow: step.showArrow !== false }) // 延迟获取元素位置,确保页面已渲染 setTimeout(() => { this.updateSpotlight(step) }, 50) }, // 更新高亮区域 updateSpotlight(step) { const query = wx.createSelectorQuery().in(this) // 查询目标元素(在页面中) const pageQuery = wx.createSelectorQuery() pageQuery.select(step.selector).boundingClientRect() pageQuery.selectViewport().scrollOffset() pageQuery.exec((res) => { if (!res || !res[0]) { console.warn('未找到目标元素:', step.selector) return } const rect = res[0] const scroll = res[1] // 计算高亮区域样式(添加padding扩展区域) const padding = step.padding || 8 const spotlightStyle = { top: rect.top - padding, left: rect.left - padding, width: rect.width + padding * 2, height: rect.height + padding * 2, borderRadius: step.borderRadius || 16 } // 计算提示内容位置 const contentStyle = this.calculateContentPosition(spotlightStyle, step.position || 'bottom') this.setData({ spotlightStyle, contentStyle: contentStyle.style, arrowDirection: contentStyle.arrowDirection }) }) }, // 计算提示内容位置 calculateContentPosition(spotlightStyle, preferredPosition) { const windowInfo = wx.getWindowInfo() const screenHeight = windowInfo.windowHeight const screenWidth = windowInfo.windowWidth const contentWidth = 600 // rpx转换为px约300 const contentMaxHeight = 400 // 估算最大高度 let position = preferredPosition let top = 0 let left = 0 let arrowDirection = 'top' // 计算中心点 const spotlightCenterX = spotlightStyle.left + spotlightStyle.width / 2 const spotlightCenterY = spotlightStyle.top + spotlightStyle.height / 2 // 根据位置偏好计算 switch (position) { case 'top': top = spotlightStyle.top - contentMaxHeight - 40 left = Math.max(20, Math.min(spotlightCenterX - contentWidth / 2, screenWidth - contentWidth - 20)) arrowDirection = 'bottom' // 如果顶部空间不足,改为底部 if (top < 20) { position = 'bottom' } break case 'bottom': top = spotlightStyle.top + spotlightStyle.height + 20 left = Math.max(20, Math.min(spotlightCenterX - contentWidth / 2, screenWidth - contentWidth - 20)) arrowDirection = 'top' // 如果底部空间不足,改为顶部 if (top + contentMaxHeight > screenHeight - 20) { position = 'top' top = spotlightStyle.top - contentMaxHeight - 40 arrowDirection = 'bottom' } break case 'left': left = spotlightStyle.left - contentWidth - 20 top = Math.max(20, Math.min(spotlightCenterY - 100, screenHeight - contentMaxHeight - 20)) arrowDirection = 'right' // 如果左侧空间不足,改为右侧 if (left < 20) { position = 'right' } break case 'right': left = spotlightStyle.left + spotlightStyle.width + 20 top = Math.max(20, Math.min(spotlightCenterY - 100, screenHeight - contentMaxHeight - 20)) arrowDirection = 'left' // 如果右侧空间不足,改为左侧 if (left + contentWidth > screenWidth - 20) { position = 'left' left = spotlightStyle.left - contentWidth - 20 arrowDirection = 'right' } break } // 如果重新计算了位置,递归调用 if (position !== preferredPosition) { return this.calculateContentPosition(spotlightStyle, position) } return { style: `top: ${top}px; left: ${left}px;`, arrowDirection } }, // 下一步 handleNext() { const nextStep = this.data.currentStep + 1 if (nextStep >= this.data.steps.length) { // 引导完成 this.handleComplete() } else { // 显示下一步 this.showStep(nextStep) } }, // 跳过引导 handleSkip() { this.handleComplete() }, // 完成引导 handleComplete() { this.triggerEvent('complete') }, // 点击遮罩(不做任何操作,防止穿透) handleMaskTap() { // 可选:点击遮罩也进入下一步 // this.handleNext() }, // 阻止滚动穿透 preventMove() {} } })