298 lines
7.4 KiB
Markdown
298 lines
7.4 KiB
Markdown
# 新手引导修改实现计划
|
||
|
||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||
|
||
**Goal:** 修改新手引导流程:点击"去做"后直接从第一项引导开始,存储步骤进度,高亮区域支持点击触发动作
|
||
|
||
**Architecture:** 修改 GuideStartModal 的 start 事件触发逻辑,添加步骤进度存储,支持高亮区域点击事件
|
||
|
||
**Tech Stack:** Vue 3 / Vuex / uni-app
|
||
|
||
---
|
||
|
||
### Task 1: 修改 GuideStartModal 点击"去做"后直接开始引导
|
||
|
||
**Files:**
|
||
- Modify: `frontend/pages/square/square.vue:751-754`
|
||
- Modify: `frontend/components/GuideStartModal.vue:39-45`
|
||
|
||
- [ ] **Step 1: 修改 square.vue 的 handleGuideStart 方法**
|
||
|
||
```javascript
|
||
// 原本代码
|
||
const handleGuideStart = () => {
|
||
showGuideStartModal.value = false;
|
||
showGuideListModal.value = true;
|
||
};
|
||
|
||
// 修改为:直接开始第一个未完成的引导
|
||
const handleGuideStart = () => {
|
||
showGuideStartModal.value = false;
|
||
|
||
// 获取所有引导keys,按顺序获取第一个未完成的
|
||
const keys = getAllGuideKeys();
|
||
const firstUndoneKey = keys.find(key => !isGuideDone(key));
|
||
|
||
if (firstUndoneKey) {
|
||
store.dispatch('guide/initGuide', firstUndoneKey);
|
||
}
|
||
};
|
||
```
|
||
|
||
需要 import:
|
||
```javascript
|
||
import { getAllGuideKeys, isGuideDone } from '@/utils/guideConfig.js';
|
||
```
|
||
|
||
- [ ] **Step 2: 验证修改**
|
||
|
||
运行项目,点击"去做"按钮,验证是否直接进入第一个引导而不是显示列表弹窗。
|
||
|
||
- [ ] **Step 3: 提交**
|
||
|
||
---
|
||
|
||
### Task 2: 添加引导步骤进度存储功能
|
||
|
||
**Files:**
|
||
- Modify: `frontend/utils/guideConfig.js:252-315`
|
||
- Modify: `frontend/store/modules/guide.js:60-74`
|
||
|
||
- [ ] **Step 1: 在 guideConfig.js 添加步骤进度存储函数**
|
||
|
||
```javascript
|
||
/**
|
||
* 获取引导的当前步骤
|
||
* @param {string} key 引导key
|
||
* @returns {number} 当前步骤索引,默认0
|
||
*/
|
||
export function getGuideCurrentStep(key) {
|
||
return uni.getStorageSync(`guide_step_${key}`) || 0
|
||
}
|
||
|
||
/**
|
||
* 设置引导的当前步骤
|
||
* @param {string} key 引导key
|
||
* @param {number} step 步骤索引
|
||
*/
|
||
export function setGuideCurrentStep(key, step) {
|
||
uni.setStorageSync(`guide_step_${key}`, step)
|
||
}
|
||
|
||
/**
|
||
* 获取下一个未完成的引导key
|
||
* @returns {string|null} 引导key或null
|
||
*/
|
||
export function getNextUndoneGuideKey() {
|
||
const keys = getAllGuideKeys()
|
||
return keys.find(key => !isGuideDone(key)) || null
|
||
}
|
||
```
|
||
|
||
- [ ] **Step 2: 修改 store/modules/guide.js 的 END_GUIDE mutation**
|
||
|
||
```javascript
|
||
// 在 mutations 中修改
|
||
END_GUIDE(state) {
|
||
if (state.currentGuide) {
|
||
const key = state.currentGuide.key
|
||
markGuideAsShown(key)
|
||
markGuideDone(key)
|
||
|
||
// 重置步骤进度
|
||
setGuideCurrentStep(key, 0)
|
||
}
|
||
state.currentGuide = null
|
||
state.currentStep = 0
|
||
state.isActive = false
|
||
state.isNavigating = false
|
||
},
|
||
```
|
||
|
||
- [ ] **Step 3: 修改 NEXT_STEP mutation 添加步骤存储**
|
||
|
||
```javascript
|
||
NEXT_STEP(state) {
|
||
if (state.currentGuide && state.currentStep < state.currentGuide.steps.length - 1) {
|
||
state.currentStep++
|
||
// 存储当前步骤
|
||
setGuideCurrentStep(state.currentGuide.key, state.currentStep)
|
||
}
|
||
},
|
||
```
|
||
|
||
- [ ] **Step 4: 修改 START_GUIDE mutation 恢复步骤进度**
|
||
|
||
```javascript
|
||
START_GUIDE(state, guideConfig) {
|
||
state.currentGuide = guideConfig
|
||
// 恢复之前保存的步骤进度
|
||
state.currentStep = getGuideCurrentStep(guideConfig.key) || 0
|
||
state.isActive = true
|
||
state.isNavigating = false
|
||
},
|
||
```
|
||
|
||
- [ ] **Step 5: 提交**
|
||
|
||
---
|
||
|
||
### Task 3: 高亮区域支持点击触发动作
|
||
|
||
**Files:**
|
||
- Modify: `frontend/utils/guideConfig.js:37-157` (steps 配置)
|
||
- Modify: `frontend/components/GuideOverlay.vue:245-248`
|
||
|
||
- [ ] **Step 1: 扩展 step 配置结构**
|
||
|
||
在 guideConfig.js 的 steps 配置中添加 action 字段:
|
||
|
||
```javascript
|
||
// action 支持的类型:
|
||
// 1. { type: 'navigate', url: '/pages/xxx/xxx' } - 路由跳转
|
||
// 2. { type: 'component', name: 'xxx' } - 打开组件
|
||
// 3. { type: 'function', handler: 'xxx' } - 执行函数
|
||
|
||
// 示例配置:
|
||
{
|
||
target: '.banner-carousel',
|
||
content: '这里是轮播图,点击可以查看排行榜和活动',
|
||
center: true,
|
||
buttons: ['next'],
|
||
action: {
|
||
type: 'navigate',
|
||
url: '/pages/rank/rank'
|
||
}
|
||
}
|
||
```
|
||
|
||
- [ ] **Step 2: 修改 GuideOverlay 的 handleHighlightClick 方法**
|
||
|
||
```javascript
|
||
// 点击高亮区域
|
||
function handleHighlightClick() {
|
||
const stepConfigVal = stepConfig.value
|
||
if (stepConfigVal && stepConfigVal.action) {
|
||
const { action } = stepConfigVal
|
||
|
||
if (action.type === 'navigate' && action.url) {
|
||
// 路由跳转
|
||
uni.navigateTo({
|
||
url: action.url,
|
||
fail: (err) => {
|
||
console.error('[Guide] 页面跳转失败:', err)
|
||
}
|
||
})
|
||
} else if (action.type === 'component' && action.name) {
|
||
// 打开组件(通过事件通知父组件)
|
||
store.dispatch('guide/openComponent', action.name)
|
||
} else if (action.type === 'function' && action.handler) {
|
||
// 执行函数
|
||
store.dispatch('guide/executeFunction', action.handler)
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
- [ ] **Step 3: 在 store 中添加处理 action 的 actions**
|
||
|
||
```javascript
|
||
// 在 store/modules/guide.js 中添加
|
||
actions: {
|
||
// ... existing actions ...
|
||
|
||
/**
|
||
* 打开组件
|
||
*/
|
||
openComponent({ commit }, componentName) {
|
||
console.log(`[Guide] 打开组件: ${componentName}`)
|
||
commit('OPEN_COMPONENT', componentName)
|
||
},
|
||
|
||
/**
|
||
* 执行函数
|
||
*/
|
||
executeFunction({ commit }, handler) {
|
||
console.log(`[Guide] 执行函数: ${handler}`)
|
||
commit('EXECUTE_FUNCTION', handler)
|
||
}
|
||
}
|
||
|
||
// 添加对应的 mutations
|
||
mutations: {
|
||
// ... existing mutations ...
|
||
|
||
OPEN_COMPONENT(state, componentName) {
|
||
state.pendingAction = { type: 'component', name: componentName }
|
||
},
|
||
|
||
EXECUTE_FUNCTION(state, handler) {
|
||
state.pendingAction = { type: 'function', handler }
|
||
}
|
||
}
|
||
|
||
// 在 state 中添加
|
||
state: {
|
||
// ... existing state ...
|
||
pendingAction: null, // 待执行的行动
|
||
}
|
||
```
|
||
|
||
- [ ] **Step 4: 提交**
|
||
|
||
---
|
||
|
||
### Task 4: 验证个人中心引导列表弹窗不受影响
|
||
|
||
**Files:**
|
||
- Review: `frontend/pages/profile/profile.vue:914-936`
|
||
|
||
- [ ] **Step 1: 检查 profile.vue 中 handleDoGuide 方法**
|
||
|
||
当前代码:
|
||
```javascript
|
||
const handleDoGuide = (key) => {
|
||
showGuideListModal.value = false;
|
||
if (isFirstEntry) {
|
||
store.dispatch("guide/initGuide", key);
|
||
} else {
|
||
store.dispatch("guide/initGuide", key);
|
||
}
|
||
};
|
||
```
|
||
|
||
验证逻辑保持不变:用户从个人中心点击进入某个引导时,触发 initGuide 动作。这与 Task 1 的修改不冲突,因为:
|
||
- square.vue 的修改只影响新用户首次点击"去做"的行为
|
||
- profile.vue 中的 GuideListModal 保持原有逻辑
|
||
|
||
- [ ] **Step 2: 测试验证**
|
||
|
||
1. 从个人中心进入引导列表
|
||
2. 点击某个引导的"去做"按钮
|
||
3. 确认引导能正常启动,且列表弹窗行为不变
|
||
|
||
- [ ] **Step 3: 提交**
|
||
|
||
---
|
||
|
||
### Task 5: 端到端测试
|
||
|
||
- [ ] **Step 1: 测试完整流程**
|
||
|
||
1. 模拟新用户登录
|
||
2. 验证 GuideStartModal 显示
|
||
3. 点击"去做"
|
||
4. 验证直接进入第一个引导而不是显示列表
|
||
5. 完成引导步骤
|
||
6. 验证步骤进度被正确存储
|
||
7. 验证高亮区域点击能触发相应动作(配置了 action 的情况下)
|
||
|
||
- [ ] **Step 2: 测试个人中心入口**
|
||
|
||
1. 进入个人中心
|
||
2. 点击引导按钮
|
||
3. 验证 GuideListModal 正常显示
|
||
4. 选择一个引导执行
|
||
5. 验证行为与之前一致
|
||
|
||
- [ ] **Step 3: 提交** |