You need to sign in or sign up before continuing.
hookehuyr

docs(readme): 重构项目文档结构

- 优化 CLAUDE.md 文档,添加设计原则说明
- 新增 CHANGELOG.md 版本更新日志
- 新增最佳实践指南
- 新增调试指南
- 新增 API 集成、导航、页面开发指南
- 新增参考文档目录

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 +# 变更日志
2 +
3 +记录项目开发过程中的重要变更和完成任务。
4 +
5 +## 2026-02-24
6 +
7 +### 23:59:18 - 完成任务
8 +
9 +**影响文件**:
10 +- `.husky/README.md`
11 +- `.husky/post-commit`
12 +- `docs/CHANGELOG.md`
13 +- `scripts/changelog/README.md`
14 +- `scripts/changelog/update-changelog.sh`
15 +
16 +**变更摘要**:
17 +- 无详细描述
This diff is collapsed. Click to expand it.
1 +# 最佳实践
2 +
3 +本文档列出项目开发中的最佳实践。
4 +
5 +## 组件开发
6 +
7 +### ✅ 推荐做法
8 +
9 +```vue
10 +<script setup>
11 +// 1. 使用 <script setup> 语法
12 +import { ref, computed } from 'vue'
13 +
14 +// 2. Props 应该有类型定义
15 +const props = defineProps({
16 + title: {
17 + type: String,
18 + required: true
19 + },
20 + count: {
21 + type: Number,
22 + default: 0
23 + }
24 +})
25 +
26 +// 3. 使用 emit 进行子到父通信
27 +const emit = defineEmits(['update', 'delete'])
28 +
29 +// 4. 使用 Composables 处理可复用逻辑
30 +const { data, loading } = use fetchData()
31 +</script>
32 +```
33 +
34 +### ❌ 避免做法
35 +
36 +```vue
37 +<script>
38 +// ❌ 不要使用 Options API
39 +export default {
40 + data() {
41 + return { }
42 + }
43 +}
44 +</script>
45 +
46 +<script setup>
47 +// ❌ 不要省略 Props 类型
48 +const props = defineProps(['title'])
49 +
50 +// ❌ 不要直接修改 props
51 +props.title = 'new title'
52 +</script>
53 +```
54 +
55 +---
56 +
57 +## 样式开发
58 +
59 +### TailwindCSS vs Less
60 +
61 +| 场景 | 使用 | 比例 |
62 +|------|------|------|
63 +| 布局(flex、grid、absolute) | TailwindCSS | 80% |
64 +| 间距(padding、margin、gap) | TailwindCSS | |
65 +| 排版(font-size、text-align) | TailwindCSS | |
66 +| 颜色(bg-*、text-*、border-*) | TailwindCSS | |
67 +| 响应式设计(sm:、md:、lg:) | TailwindCSS | |
68 +| 组件特定样式(需要 scoped) | Less | 20% |
69 +| 深度选择器(`:deep()`) | Less | |
70 +| 动画和过渡 | Less | |
71 +| 伪元素(`::before``::after`) | Less | |
72 +
73 +### ✅ 优先使用 TailwindCSS
74 +
75 +```vue
76 +<template>
77 + <!-- ✅ 使用 TailwindCSS -->
78 + <view class="flex items-center gap-4 p-4 bg-white rounded-lg">
79 + <text class="text-lg font-semibold">标题</text>
80 + </view>
81 +</template>
82 +```
83 +
84 +### ❌ 避免过度使用 Less
85 +
86 +```vue
87 +<style lang="less" scoped>
88 +// ❌ 能用 TailwindCSS 的就不要用 Less
89 +.custom-container {
90 + display: flex;
91 + align-items: center;
92 + padding: 16px;
93 +}
94 +</style>
95 +```
96 +
97 +---
98 +
99 +## API 集成
100 +
101 +### ✅ 推荐做法
102 +
103 +```javascript
104 +// 1. 始终检查 res.code === 1
105 +const res = await fn(yourAPI(params))
106 +if (res.code === 1) {
107 + // 成功处理
108 +}
109 +
110 +// 2. 使用 try/catch 进行错误处理
111 +try {
112 + const res = await fn(yourAPI(params))
113 +} catch (err) {
114 + console.error('API 错误:', err)
115 +}
116 +
117 +// 3. 请求期间显示加载状态
118 +loading.value = true
119 +await fn(yourAPI(params))
120 +loading.value = false
121 +
122 +// 4. 优雅地处理网络错误
123 +try {
124 + await fn(yourAPI(params))
125 +} catch (err) {
126 + if (is_network_error(err)) {
127 + Taro.showToast({ title: '网络错误,请重试', icon: 'none' })
128 + }
129 +}
130 +```
131 +
132 +### ❌ 避免做法
133 +
134 +```javascript
135 +// ❌ 不要只检查 res.code
136 +if (res.code) { }
137 +
138 +// ❌ 不要忽略错误处理
139 +await fn(yourAPI(params))
140 +
141 +// ❌ 不要不显示加载状态
142 +await fn(yourAPI(params)) // 用户不知道发生了什么
143 +```
144 +
145 +---
146 +
147 +## 性能优化
148 +
149 +### ✅ 推荐做法
150 +
151 +```javascript
152 +// 1. 使用页面懒加载(分包)
153 +// 在 app.config.js 中配置分包
154 +subPackages: [
155 + {
156 + root: 'pages/business',
157 + pages: ['/*']
158 + }
159 +]
160 +
161 +// 2. 使用 CDN 参数优化图片
162 +const imageUrl = 'https://cdn.example.com/image.jpg?w=750&q=80'
163 +
164 +// 3. 避免无分页的大数据集
165 +const res = await fn(getListAPI({ page: 1, limit: 20 }))
166 +
167 +// 4. 在 onUnmounted 中清理
168 +onUnmounted(() => {
169 + timer && clearInterval(timer)
170 +})
171 +
172 +// 5. 使用 shallowRef + markRaw 处理组件对象
173 +import { shallowRef, markRaw } from 'vue'
174 +
175 +const menuItems = shallowRef([
176 + { icon: markRaw(IconFont), name: 'heart' }
177 +])
178 +```
179 +
180 +### ❌ 避免做法
181 +
182 +```javascript
183 +// ❌ 不要一次性加载所有数据
184 +const allData = await fn(getAllDataAPI())
185 +
186 +// ❌ 不要忘记清理定时器
187 +const timer = setInterval(() => { }, 1000)
188 +// 没有清理
189 +
190 +// ❌ 不要深度代理组件对象
191 +const menuItems = ref([
192 + { icon: IconFont, name: 'heart' } // Vue 会深度代理
193 +])
194 +```
195 +
196 +---
197 +
198 +## 代码质量
199 +
200 +### ✅ 推荐做法
201 +
202 +```javascript
203 +// 1. 遵循 Vue 3 Composition API 模式
204 +<script setup>
205 +// ...
206 +</script>
207 +
208 +// 2. 使用描述性变量名
209 +const isLoadingUserFavorites = ref(false)
210 +
211 +// 3. 保持函数聚焦且简短(< 50 行)
212 +const fetchUserData = async () => {
213 + // 单一职责
214 +}
215 +
216 +// 4. 所有函数必须有 JSDoc 注释
217 +/**
218 + * 获取用户数据
219 + * @param {number} userId - 用户 ID
220 + * @returns {Promise<User>} 用户数据
221 + */
222 +async function getUserData(userId) { }
223 +
224 +// 5. 提交前运行 pnpm lint
225 +```
226 +
227 +### ❌ 避免做法
228 +
229 +```javascript
230 +// ❌ 不要使用无意义的变量名
231 +const a = ref(false)
232 +const temp = ref(null)
233 +
234 +// ❌ 不要写超长函数(> 50 行)
235 +const doEverything = async () => {
236 + // 100+ 行代码
237 +}
238 +
239 +// ❌ 不要省略函数注释
240 +function process(data) { }
241 +```
242 +
243 +---
244 +
245 +## 代码复用
246 +
247 +### 第 3 次出现原则
248 +
249 +当相同代码模式出现 3 次时,**必须**抽取为 Composable 或组件。
250 +
251 +### ✅ 推荐做法
252 +
253 +```javascript
254 +// 抽取为 Composable
255 +// src/composables/useUserData.js
256 +export function useUserData() {
257 + const user = ref(null)
258 + const loading = ref(false)
259 +
260 + const fetchUser = async () => {
261 + loading.value = true
262 + // ...
263 + }
264 +
265 + return { user, loading, fetchUser }
266 +}
267 +
268 +// 在组件中使用
269 +const { user, loading, fetchUser } = useUserData()
270 +```
271 +
272 +### 组件自包含原则
273 +
274 +对于重复的UI结构,抽取为可复用组件:
275 +
276 +```vue
277 +<!-- MaterialCard.vue - 自包含业务逻辑 -->
278 +<script setup>
279 +const props = defineProps(['item'])
280 +const emit = defineEmits(['view', 'collect'])
281 +
282 +// 组件内部处理业务逻辑
283 +const handleView = () => {
284 + emit('view', props.item.id)
285 +}
286 +</script>
287 +
288 +<!-- 父组件只需要传递数据 -->
289 +<MaterialCard :item="material" @view="handleView" />
290 +```
291 +
292 +---
293 +
294 +## 安全性
295 +
296 +### ✅ 推荐做法
297 +
298 +```javascript
299 +// 1. 用户输入验证
300 +const validateInput = (input) => {
301 + if (!input || input.length > 100) {
302 + return false
303 + }
304 + return true
305 +}
306 +
307 +// 2. XSS 防护(使用 v-html 时净化)
308 +import DOMPurify from 'dompurify'
309 +const sanitizedHtml = DOMPurify.sanitize(rawHtml)
310 +
311 +// 3. 敏感数据不存储在 localStorage
312 +// ❌ 不要存储
313 +localStorage.setItem('password', password)
314 +
315 +// ✅ 使用 Pinia(内存存储)
316 +const authStore = useAuthStore()
317 +authStore.setToken(token)
318 +```
319 +
320 +---
321 +
322 +## 相关文档
323 +
324 +- **[经验教训总结](lessons-learned.md)** - 常见陷阱和解决方案
325 +- **[调试指南](debugging.md)** - 调试技巧
1 +# 调试指南
2 +
3 +本文档介绍项目调试的常用技巧和工具。
4 +
5 +## 检查环境配置
6 +
7 +### 验证 BASE_URL
8 +
9 +```bash
10 +# 查看 src/utils/config.js 中的 BASE_URL 配置
11 +cat src/utils/config.js | grep BASE_URL
12 +```
13 +
14 +确保:
15 +- 开发环境指向测试服务器
16 +- 生产环境指向正式服务器
17 +
18 +### 检查业务参数
19 +
20 +```javascript
21 +// src/utils/config.js
22 +REQUEST_DEFAULT_PARAMS: {
23 + f: 'your_module', // 业务模块标识符
24 + client_name: 'your_app' // 应用名称
25 +}
26 +```
27 +
28 +---
29 +
30 +## 验证身份认证
31 +
32 +### 检查 sessionid
33 +
34 +```javascript
35 +// 在浏览器控制台或微信开发者工具中执行
36 +console.log(localStorage.sessionid)
37 +```
38 +
39 +### 启用详细日志
40 +
41 +`src/utils/request.js` 拦截器中启用详细日志:
42 +
43 +```javascript
44 +// 请求拦截器
45 +config.headers = {
46 + 'cookie': `sessionid=${localStorage.sessionid}`,
47 + // 添加调试日志
48 + 'X-Debug': 'true'
49 +}
50 +
51 +console.log('[Request]', config.url, config.data)
52 +```
53 +
54 +### 测试 401 刷新流程
55 +
56 +```javascript
57 +// 1. 清除 sessionid 模拟过期
58 +localStorage.removeItem('sessionid')
59 +
60 +// 2. 发送请求触发 401
61 +await fn(someAPI())
62 +
63 +// 3. 观察是否自动刷新并重试
64 +```
65 +
66 +---
67 +
68 +## 网络问题调试
69 +
70 +### 检查网络状态
71 +
72 +```javascript
73 +import Taro from '@tarojs/taro'
74 +
75 +// 获取网络类型
76 +Taro.getNetworkType({
77 + success: (res) => {
78 + console.log('网络类型:', res.networkType)
79 + // wifi, 4g, 5g, none
80 + }
81 +})
82 +
83 +// 监听网络变化
84 +Taro.onNetworkStatusChange((result) => {
85 + console.log('网络状态变化:', result)
86 +})
87 +```
88 +
89 +### 检查弱网络场景
90 +
91 +项目支持弱网络降级,通过以下方式检测:
92 +
93 +```javascript
94 +// src/utils/request.js
95 +import { is_timeout_error, is_network_error } from '@/utils/network'
96 +
97 +try {
98 + const res = await fn(yourAPI())
99 +} catch (err) {
100 + if (is_timeout_error(err)) {
101 + console.log('请求超时')
102 + } else if (is_network_error(err)) {
103 + console.log('网络错误')
104 + }
105 +}
106 +```
107 +
108 +---
109 +
110 +## 样式问题调试
111 +
112 +### 确认设计宽度
113 +
114 +项目中使用双设计宽度系统:
115 +- **NutUI 组件**:375px
116 +- **其他组件**:750px
117 +
118 +```javascript
119 +// config/index.js
120 +designWidth: 750, // 默认
121 +
122 +// NutUI 特殊配置
123 +nutui: {
124 + designWidth: 375
125 +}
126 +```
127 +
128 +### 检查样式应用
129 +
130 +```vue
131 +<template>
132 + <!-- 添加调试类名 -->
133 + <view class="debug-component">
134 + 内容
135 + </view>
136 +</template>
137 +
138 +<style lang="less" scoped>
139 +.debug-component {
140 + /* 调试边框 */
141 + border: 1px solid red;
142 +}
143 +</style>
144 +```
145 +
146 +### 验证 TailwindCSS 类
147 +
148 +```bash
149 +# 检查 TailwindCSS 配置
150 +cat tailwind.config.js
151 +
152 +# 检查类是否生效
153 +# 在微信开发者工具中查看元素的 computed styles
154 +```
155 +
156 +---
157 +
158 +## 导航问题调试
159 +
160 +### 检查路由注册
161 +
162 +```javascript
163 +// src/app.config.js
164 +export default {
165 + pages: [
166 + 'pages/your-page/index', // 确认已注册
167 + ]
168 +}
169 +```
170 +
171 +### 验证页面目录结构
172 +
173 +```bash
174 +# 确认页面文件存在
175 +ls -la src/pages/your-page/
176 +
177 +# 应该包含:
178 +# - index.vue
179 +# - index.config.js
180 +```
181 +
182 +### 调试导航参数
183 +
184 +```javascript
185 +// 发送页面
186 +go('/pages/detail/index', { id: 123 })
187 +
188 +// 接收页面
189 +useLoad((options) => {
190 + console.log('接收到的参数:', options)
191 + console.log('ID:', options.id) // 应该输出 123
192 +})
193 +```
194 +
195 +---
196 +
197 +## API 问题调试
198 +
199 +### 查看 API 请求
200 +
201 +```javascript
202 +// src/utils/request.js
203 +// 在拦截器中添加日志
204 +interceptors.request.use((config) => {
205 + console.log('[API Request]', {
206 + url: config.url,
207 + method: config.method,
208 + data: config.data,
209 + headers: config.headers
210 + })
211 + return config
212 +})
213 +
214 +interceptors.response.use((response) => {
215 + console.log('[API Response]', {
216 + status: response.status,
217 + data: response.data
218 + })
219 + return response
220 +})
221 +```
222 +
223 +### 测试 API 调用
224 +
225 +```javascript
226 +// 独立测试 API
227 +import { yourAPI } from '@/api'
228 +import { fn } from '@/api/fn'
229 +
230 +async function testAPI() {
231 + try {
232 + const url = yourAPI({ id: 123 })
233 + console.log('API URL:', url)
234 +
235 + const res = await fn(url)
236 + console.log('API Response:', res)
237 +
238 + if (res.code === 1) {
239 + console.log('API 调用成功')
240 + } else {
241 + console.log('API 业务错误:', res.msg)
242 + }
243 + } catch (err) {
244 + console.error('API 网络错误:', err)
245 + }
246 +}
247 +
248 +testAPI()
249 +```
250 +
251 +---
252 +
253 +## 常见问题
254 +
255 +### Q: NutUI 组件样式不生效
256 +
257 +A: 检查设计宽度配置,NutUI 使用 375px 设计宽度:
258 +
259 +```javascript
260 +// config/index.js
261 +nutui: {
262 + designWidth: 375 // 确认配置正确
263 +}
264 +```
265 +
266 +### Q: 页面参数接收不到
267 +
268 +A: 检查以下几点:
269 +
270 +1. 确认使用 `useLoad` 接收参数
271 +2. 确认参数名称正确
272 +3. 确认参数类型(数字 vs 字符串)
273 +
274 +```javascript
275 +useLoad((options) => {
276 + console.log(options) // 先打印看看有什么
277 +})
278 +```
279 +
280 +### Q: API 请求不发送
281 +
282 +A: 检查以下几点:
283 +
284 +1. 确认 BASE_URL 配置正确
285 +2. 确认网络权限
286 +3. 查看控制台是否有错误
287 +4. 检查 request.js 拦截器
288 +
289 +### Q: 组件不更新
290 +
291 +A: 可能的原因:
292 +
293 +1. 响应式数据未正确声明
294 +2. 使用了深度嵌套的对象
295 +3. Key 值未正确设置
296 +
297 +```javascript
298 +// 确保使用 ref 或 reactive
299 +const data = ref(null) // ✅
300 +const data = null // ❌
301 +
302 +// 列表使用 key
303 +<view v-for="item in list" :key="item.id"> // ✅
304 +<view v-for="item in list"> // ❌
305 +```
306 +
307 +---
308 +
309 +## 调试工具
310 +
311 +### 微信开发者工具
312 +
313 +- **Console** - 查看日志输出
314 +- **Network** - 查看网络请求
315 +- **AppData** - 查看 AppData 数据
316 +- **Storage** - 查看 localStorage
317 +- **Wxml** - 查看 DOM 结构
318 +
319 +### Vue DevTools
320 +
321 +Taro 支持 Vue DevTools,可以:
322 +- 查看组件树
323 +- 查看 Vuex/Pinia 状态
324 +- 查看事件监听器
325 +
326 +---
327 +
328 +## 相关文档
329 +
330 +- **[API 集成指南](guides/api-integration.md)** - API 调用详解
331 +- **[最佳实践](best-practices.md)** - 代码质量建议
1 +# API 集成指南
2 +
3 +本文档介绍如何在项目中添加和调用 API。
4 +
5 +## API 定义模式
6 +
7 +### 步骤 1:在 src/api/index.js 中定义 API
8 +
9 +```javascript
10 +export const getProductListAPI = (params) => {
11 + return buildApiUrl('product_list', params)
12 +}
13 +
14 +export const getProductDetailAPI = (params) => {
15 + return buildApiUrl('product_detail', params)
16 +}
17 +```
18 +
19 +### 步骤 2:在页面中使用
20 +
21 +```javascript
22 +import { getProductListAPI } from '@/api'
23 +import { fn } from '@/api/fn'
24 +
25 +const fetchProducts = async () => {
26 + try {
27 + const res = await fn(getProductListAPI({ page: 1 }))
28 + if (res.code === 1) {
29 + products.value = res.data
30 + }
31 + } catch (err) {
32 + console.error('获取产品失败:', err)
33 + }
34 +}
35 +```
36 +
37 +## 请求包装器(fn.js)
38 +
39 +所有 API 调用都应通过 `src/api/fn.js` 的包装器:
40 +
41 +- ✅ 处理常见错误场景
42 +- ✅ 统一错误提示
43 +-**始终检查 `res.code === 1` 判断成功**
44 +
45 +## 完整示例
46 +
47 +### 带加载状态的 API 调用
48 +
49 +```javascript
50 +import { ref } from 'vue'
51 +import { getProductListAPI } from '@/api'
52 +import { fn } from '@/api/fn'
53 +
54 +const products = ref([])
55 +const loading = ref(false)
56 +const error = ref(null)
57 +
58 +const fetchProducts = async (page = 1) => {
59 + loading.value = true
60 + error.value = null
61 +
62 + try {
63 + const res = await fn(getProductListAPI({ page, limit: 20 }))
64 +
65 + if (res.code === 1) {
66 + products.value = res.data.list
67 + } else {
68 + error.value = res.msg || '获取失败'
69 + }
70 + } catch (err) {
71 + console.error('API 错误:', err)
72 + error.value = '网络错误,请重试'
73 + } finally {
74 + loading.value = false
75 + }
76 +}
77 +```
78 +
79 +### 带分页的 API 调用
80 +
81 +```javascript
82 +const page = ref(0)
83 +const hasMore = ref(true)
84 +const loading = ref(false)
85 +
86 +const loadMore = async () => {
87 + if (loading.value || !hasMore.value) return
88 +
89 + loading.value = true
90 + try {
91 + const res = await fn(getProductListAPI({ page: page.value, limit: 20 }))
92 +
93 + if (res.code === 1) {
94 + products.value.push(...res.data.list)
95 + page.value++
96 + hasMore.value = res.data.list.length >= 20
97 + }
98 + } catch (err) {
99 + console.error('加载更多失败:', err)
100 + } finally {
101 + loading.value = false
102 + }
103 +}
104 +```
105 +
106 +## 错误处理
107 +
108 +### 网络错误
109 +
110 +```javascript
111 +try {
112 + const res = await fn(yourAPI(params))
113 + // ...
114 +} catch (err) {
115 + if (is_network_error(err)) {
116 + // 网络错误
117 + Taro.showToast({ title: '网络错误', icon: 'none' })
118 + } else if (is_timeout_error(err)) {
119 + // 超时
120 + Taro.showToast({ title: '请求超时', icon: 'none' })
121 + } else {
122 + // 其他错误
123 + Taro.showToast({ title: '请求失败', icon: 'none' })
124 + }
125 +}
126 +```
127 +
128 +### 业务错误
129 +
130 +```javascript
131 +const res = await fn(yourAPI(params))
132 +
133 +if (res.code === 1) {
134 + // 成功
135 +} else if (res.code === 401) {
136 + // 未登录(通常会被拦截器自动处理)
137 +} else {
138 + // 业务错误
139 + Taro.showToast({ title: res.msg || '操作失败', icon: 'none' })
140 +}
141 +```
142 +
143 +## API 规范
144 +
145 +### 请求格式
146 +
147 +```javascript
148 +// 查询列表
149 +yourAPI({ page: 1, limit: 20, keyword: '搜索词' })
150 +
151 +// 获取详情
152 +detailAPI({ id: 123 })
153 +
154 +// 提交表单
155 +submitAPI({ field1: 'value1', field2: 'value2' })
156 +```
157 +
158 +### 响应格式
159 +
160 +```javascript
161 +// 成功
162 +{
163 + code: 1,
164 + msg: 'success',
165 + data: { /* 业务数据 */ }
166 +}
167 +
168 +// 失败
169 +{
170 + code: 0, // 或其他错误码
171 + msg: '错误信息',
172 + data: null
173 +}
174 +```
175 +
176 +## 最佳实践
177 +
178 +### ✅ 推荐做法
179 +
180 +```javascript
181 +// 1. 使用 async/await
182 +const res = await fn(yourAPI(params))
183 +
184 +// 2. 检查 res.code === 1
185 +if (res.code === 1) {
186 + // 成功处理
187 +}
188 +
189 +// 3. 使用 try/catch
190 +try {
191 + const res = await fn(yourAPI(params))
192 +} catch (err) {
193 + // 错误处理
194 +}
195 +
196 +// 4. 显示加载状态
197 +loading.value = true
198 +// ... API 调用
199 +loading.value = false
200 +```
201 +
202 +### ❌ 避免做法
203 +
204 +```javascript
205 +// 1. 不要只检查 res.code
206 +if (res.code) { } // ❌
207 +
208 +// 2. 不要忽略错误
209 +const res = await fn(yourAPI(params)) // ❌ 无 try/catch
210 +
211 +// 3. 不要硬编码 API URL
212 +fetch('/srv/?a=your_action') // ❌
213 +```
214 +
215 +## 参考文档
216 +
217 +- **[API 接口文档](docs/api-specs/)** - 完整的 API 接口规范
218 +- **[接口联调日志](docs/api-integration-log.md)** - API 集成状态
1 +# 导航系统指南
2 +
3 +本文档介绍项目中的导航系统使用方法。
4 +
5 +## useGo Hook(推荐)
6 +
7 +`useGo` 是增强的导航 Hook,提供自动路径补全和便捷方法。
8 +
9 +### 基础用法
10 +
11 +```javascript
12 +import { useGo } from '@/hooks/useGo'
13 +
14 +const go = useGo()
15 +
16 +// 导航到页面
17 +go('/pages/detail/index')
18 +
19 +// 带参数导航(例如产品 ID)
20 +go('/pages/product-detail/index', { id: 123 })
21 +
22 +// 带多个参数导航
23 +go('/pages/material-list/index', { category: 'insurance', page: 1 })
24 +
25 +// 返回上一页
26 +go.back()
27 +```
28 +
29 +### 路径自动补全
30 +
31 +`useGo` 会自动补全相对路径为绝对路径:
32 +
33 +```javascript
34 +go('detail') // → /pages/detail/index
35 +go('product-detail') // → /pages/product-detail/index
36 +```
37 +
38 +### 返回导航
39 +
40 +```javascript
41 +// 返回上一页
42 +go.back()
43 +
44 +// 返回多页
45 +go.back(2)
46 +
47 +// 返回首页
48 +go('/pages/index/index')
49 +```
50 +
51 +## 在目标页面接收参数
52 +
53 +```javascript
54 +import { useLoad } from '@tarojs/taro'
55 +import { ref } from 'vue'
56 +
57 +const productId = ref(null)
58 +
59 +useLoad((options) => {
60 + // 访问导航参数
61 + console.log('接收到的参数:', options)
62 + productId.value = options.id
63 +
64 + // 根据参数获取数据
65 + fetchProductDetail(options.id)
66 +})
67 +```
68 +
69 +## Taro 内置导航(备选方案)
70 +
71 +如果需要更底层的控制,可以直接使用 Taro 导航 API:
72 +
73 +### navigateTo - 保留当前页
74 +
75 +```javascript
76 +import Taro from '@tarojs/taro'
77 +
78 +Taro.navigateTo({
79 + url: '/pages/detail/index?id=123'
80 +})
81 +```
82 +
83 +### redirectTo - 不保留当前页
84 +
85 +```javascript
86 +Taro.redirectTo({
87 + url: '/pages/login/index'
88 +})
89 +```
90 +
91 +### switchTab - 切换 Tab
92 +
93 +```javascript
94 +Taro.switchTab({
95 + url: '/pages/index/index'
96 +})
97 +```
98 +
99 +### reLaunch - 重启应用
100 +
101 +```javascript
102 +Taro.reLaunch({
103 + url: '/pages/index/index'
104 +})
105 +```
106 +
107 +### navigateBack - 返回
108 +
109 +```javascript
110 +// 返回上一页
111 +Taro.navigateBack()
112 +
113 +// 返回多页
114 +Taro.navigateBack({ delta: 2 })
115 +```
116 +
117 +## 路由状态管理
118 +
119 +### router Store
120 +
121 +项目使用 `src/stores/router.js` 维护已访问路由的栈,主要用于认证回调导航。
122 +
123 +```javascript
124 +import { useRouterStore } from '@/stores/router'
125 +
126 +const routerStore = useRouterStore()
127 +
128 +// 获取上一页路径
129 +const previousPage = routerStore.previousRoute
130 +```
131 +
132 +### 导航钩子
133 +
134 +```javascript
135 +import { useLoad, useShow, useHide, useUnload } from '@tarojs/taro'
136 +
137 +// 页面加载时(只执行一次)
138 +useLoad((options) => {
139 + console.log('页面加载', options)
140 +})
141 +
142 +// 页面显示时(每次返回都会执行)
143 +useShow(() => {
144 + console.log('页面显示')
145 +})
146 +
147 +// 页面隐藏时
148 +useHide(() => {
149 + console.log('页面隐藏')
150 +})
151 +
152 +// 页面卸载时
153 +useUnload(() => {
154 + console.log('页面卸载')
155 +})
156 +```
157 +
158 +## 认证场景导航
159 +
160 +### 登录后返回原页面
161 +
162 +```javascript
163 +import { useGo } from '@/hooks/useGo'
164 +import { useRouterStore } from '@/stores/router'
165 +
166 +const go = useGo()
167 +const routerStore = useRouterStore()
168 +
169 +// 1. 保存当前路径
170 +routerStore.setRedirect('/pages/some-page/index')
171 +
172 +// 2. 跳转登录页
173 +go('/pages/login/index')
174 +
175 +// 3. 登录成功后返回
176 +const redirectUrl = routerStore.redirect
177 +if (redirectUrl) {
178 + go(redirectUrl)
179 + routerStore.setRedirect(null)
180 +}
181 +```
182 +
183 +## 常见问题
184 +
185 +### Q: 导航后页面不刷新?
186 +
187 +A: 使用 `useShow` 钩子,每次页面显示时都会执行:
188 +
189 +```javascript
190 +useShow(() => {
191 + // 重新加载数据
192 + fetchData()
193 +})
194 +```
195 +
196 +### Q: 参数传递丢失?
197 +
198 +A: 确保参数类型正确:
199 +
200 +```javascript
201 +// ❌ 错误:对象作为参数
202 +go('/pages/detail/index', { data: { id: 1 } })
203 +
204 +// ✅ 正确:扁平参数
205 +go('/pages/detail/index', { id: 1, type: 'insurance' })
206 +```
207 +
208 +### Q: 如何清除页面栈?
209 +
210 +A: 使用 `redirectTo``reLaunch`
211 +
212 +```javascript
213 +// 不保留当前页
214 +Taro.redirectTo({ url: '/pages/login/index' })
215 +
216 +// 清空所有页面栈
217 +Taro.reLaunch({ url: '/pages/index/index' })
218 +```
219 +
220 +## 参考文档
221 +
222 +- **[Taro 导航文档](https://docs.taro.zone/docs/vue-navigation)** - 官方文档
223 +- **[页面开发指南](guides/page-development.md)** - 页面创建和路由注册
1 +# 页面开发指南
2 +
3 +本文档介绍如何在项目中添加新页面。
4 +
5 +## 目录结构
6 +
7 +所有页面遵循以下目录结构:
8 +```
9 +src/pages/your-page/
10 +├── index.vue # 页面组件(必须使用 <script setup>)
11 +├── index.config.js # 页面配置(navigationBarTitleText 等)
12 +└── assets/ # 页面特定资源(可选)
13 +```
14 +
15 +## 步骤 1:创建页面目录和文件
16 +
17 +```bash
18 +mkdir -p src/pages/your-page
19 +touch src/pages/your-page/index.vue
20 +touch src/pages/your-page/index.config.js
21 +```
22 +
23 +## 步骤 2:配置页面
24 +
25 +**`index.config.js`**
26 +```javascript
27 +export default {
28 + navigationBarTitleText: '您的页面标题',
29 + enablePullDownRefresh: true,
30 + backgroundColor: '#f5f5f5'
31 +}
32 +```
33 +
34 +## 步骤 3:编写页面组件
35 +
36 +**`index.vue`**
37 +```vue
38 +<script setup>
39 +import { ref } from 'vue'
40 +import { useLoad, useShow } from '@tarojs/taro'
41 +
42 +const pageId = ref(null)
43 +
44 +useLoad((options) => {
45 + console.log('页面加载,参数:', options)
46 + // 接收导航参数
47 + if (options.id) {
48 + pageId.value = options.id
49 + // 根据 ID 获取数据
50 + }
51 +})
52 +
53 +useShow(() => {
54 + console.log('页面显示')
55 +})
56 +
57 +// 您的组件逻辑
58 +</script>
59 +
60 +<template>
61 + <view class="page">
62 + <!-- 页面内容 -->
63 + </view>
64 +</template>
65 +
66 +<style lang="less" scoped>
67 +.page {
68 + padding: 20px;
69 +}
70 +</style>
71 +```
72 +
73 +## 步骤 4:注册路由
74 +
75 +**`src/app.config.js`** 中注册路由:
76 +
77 +```javascript
78 +export default {
79 + pages: [
80 + 'pages/your-page/index',
81 + // ... 其他页面
82 + ],
83 + // ...
84 +}
85 +```
86 +
87 +## 步骤 5:添加导航(可选)
88 +
89 +### 使用 useGo Hook(推荐)
90 +
91 +```javascript
92 +import { useGo } from '@/hooks/useGo'
93 +const go = useGo()
94 +
95 +// 带查询参数导航
96 +go('/pages/your-page/index', { id: 123, type: 'insurance' })
97 +```
98 +
99 +### 使用 Taro 内置导航
100 +
101 +```javascript
102 +import Taro from '@tarojs/taro'
103 +
104 +Taro.navigateTo({
105 + url: '/pages/your-page/index?id=123'
106 +})
107 +```
108 +
109 +## 接收导航参数
110 +
111 +在目标页面的 `useLoad` 中接收参数:
112 +
113 +```javascript
114 +useLoad((options) => {
115 + console.log('接收到的参数:', options)
116 + const { id, type } = options
117 +
118 + // 根据参数获取数据
119 + fetchData(id, type)
120 +})
121 +```
122 +
123 +## TabBar 集成(可选)
124 +
125 +如果页面需要底部导航栏:
126 +
127 +1. 导入 `TabBar` 组件
128 +2. 根据路由配置激活状态
129 +
130 +```vue
131 +<script setup>
132 +import TabBar from '@/components/TabBar.vue'
133 +</script>
134 +
135 +<template>
136 + <view class="page">
137 + <!-- 页面内容 -->
138 + <TabBar />
139 + </view>
140 +</template>
141 +```
142 +
143 +## 常见问题
144 +
145 +### Q: 页面注册后还是 404?
146 +
147 +A: 检查以下几点:
148 +1. 路由路径是否正确(`pages/your-page/index`
149 +2. 是否重启了开发服务器
150 +3. `index.config.js` 是否存在
151 +
152 +### Q: 如何隐藏原生导航栏?
153 +
154 +A: 在 `index.config.js` 中设置:
155 +```javascript
156 +export default {
157 + navigationStyle: 'custom'
158 +}
159 +```
160 +
161 +### Q: 页面参数丢失?
162 +
163 +A: 检查参数是否正确编码:
164 +```javascript
165 +// ❌ 错误
166 +go('/pages/detail/index', { id: '123' })
167 +
168 +// ✅ 正确
169 +go('/pages/detail/index', { id: 123 }) // 数字类型
170 +// 或
171 +go('/pages/detail/index?id=123') // 字符串类型
172 +```
1 +# 组件库参考文档
2 +
3 +本文档列出项目中所有组件的详细说明。
4 +
5 +## 导航与布局组件
6 +
7 +### TabBar.vue - 底部导航栏
8 +
9 +**位置**`src/components/TabBar.vue`
10 +
11 +**功能**:固定底部导航栏,自动适配安全区域
12 +
13 +**关键特性**
14 +- 支持图标 + 文字布局
15 +- 激活状态高亮
16 +
17 +**使用页面**:首页、我的、家办、知识库、签单页面
18 +
19 +---
20 +
21 +### NavHeader.vue - 自定义导航头
22 +
23 +**位置**`src/components/NavHeader.vue`
24 +
25 +**功能**:带返回按钮的自定义导航头
26 +
27 +**关键特性**
28 +- 透明/背景变体
29 +- 刘海屏设备的安全区域内边距
30 +
31 +---
32 +
33 +### indexNav.vue - 首页网格导航
34 +
35 +**位置**`src/components/indexNav.vue`
36 +
37 +**功能**:首页网格导航
38 +
39 +---
40 +
41 +## 图标与媒体组件
42 +
43 +### IconFont.vue - 图标字体包装器
44 +
45 +**位置**`src/components/IconFont.vue`
46 +
47 +**功能**:自定义图标的图标字体包装器
48 +
49 +**⚠️ 注意**:动态切换时需添加 `:key="name"`
50 +
51 +```vue
52 +<IconFont :name="iconName" :key="iconName" />
53 +```
54 +
55 +---
56 +
57 +### qrCode.vue - 二维码显示
58 +
59 +**位置**`src/components/qrCode.vue`
60 +
61 +**功能**:二维码显示
62 +
63 +---
64 +
65 +### qrCodeSearch.vue - 二维码扫描
66 +
67 +**位置**`src/components/qrCodeSearch.vue`
68 +
69 +**功能**:二维码扫描
70 +
71 +---
72 +
73 +## 列表与展示组件
74 +
75 +### SectionCard.vue - 分组卡片
76 +
77 +**位置**`src/components/SectionCard.vue`
78 +
79 +**功能**:分组卡片组件
80 +
81 +---
82 +
83 +### SectionItem.vue - 分组列表项
84 +
85 +**位置**`src/components/SectionItem.vue`
86 +
87 +**功能**:分组列表项组件
88 +
89 +---
90 +
91 +### ListItemActions/ - 列表项操作按钮
92 +
93 +**位置**`src/components/list/ListItemActions/`
94 +
95 +**功能**:列表项操作按钮组
96 +
97 +**关键特性**
98 +- 权限检查
99 +- 自动埋点上报
100 +- 查看按钮、收藏按钮等
101 +
102 +---
103 +
104 +### MaterialCard.vue - 资料卡片(可复用)
105 +
106 +**位置**`src/components/MaterialCard.vue`
107 +
108 +**功能**:资料展示卡片
109 +
110 +**关键特性**
111 +- 自包含业务逻辑:查看、收藏
112 +- 支持动态标签
113 +- 文件大小格式化
114 +- 学习人数显示
115 +- 支持图片文件预览(使用 Taro.previewImage)
116 +
117 +**使用页面**:首页、搜索页、周热门资料页
118 +
119 +**使用的 Composable**
120 +- useCollectOperation
121 +- useListItemClick
122 +
123 +---
124 +
125 +### ProductCard.vue - 产品卡片(可复用)
126 +
127 +**位置**`src/components/ProductCard.vue`
128 +
129 +**功能**:产品展示卡片
130 +
131 +**关键特性**
132 +- 自定义样式:动态标签、封面图
133 +- 支持产品详情查看
134 +- 支持计划书功能
135 +
136 +**使用页面**:首页、搜索页、产品中心页
137 +
138 +---
139 +
140 +## 表单与输入组件
141 +
142 +### FilterTabs.vue - 过滤标签
143 +
144 +**位置**`src/components/FilterTabs.vue`
145 +
146 +**功能**:过滤标签组件
147 +
148 +---
149 +
150 +### SearchBar.vue - 搜索栏
151 +
152 +**位置**`src/components/SearchBar.vue`
153 +
154 +**功能**:搜索栏组件
155 +
156 +---
157 +
158 +## 文档预览组件
159 +
160 +### DocumentPreview/ - 文档预览
161 +
162 +**位置**`src/components/DocumentPreview/`
163 +
164 +**功能**:文档预览组件
165 +
166 +---
167 +
168 +### PdfPreview.vue - PDF 预览
169 +
170 +**位置**`src/components/PdfPreview.vue`
171 +
172 +**功能**:PDF 文件预览
173 +
174 +---
175 +
176 +### OfficeViewer.vue - Office 文档查看器
177 +
178 +**位置**`src/components/OfficeViewer.vue`
179 +
180 +**功能**:Office 文档查看
181 +
182 +---
183 +
184 +## 业务组件
185 +
186 +### PlanSchemes/ - 计划方案
187 +
188 +**位置**`src/components/plan/PlanSchemes/`
189 +
190 +**功能**:计划方案组件(SchemeA, SchemeB)
191 +
192 +---
193 +
194 +### PlanPopup/ - 计划弹窗
195 +
196 +**位置**`src/components/plan/PlanPopup/`
197 +
198 +**功能**:计划弹窗组件
199 +
200 +**关键特性**
201 +- 使用 provide/inject 模式实现父子弹窗通信
202 +- 子弹窗打开时自动隐藏父级 footer
203 +- 支持 AgePicker、DatePicker、SelectPicker 等字段组件
204 +
205 +---
206 +
207 +### PlanFields/ - 计划书表单字段
208 +
209 +**位置**`src/components/plan/PlanFields/`
210 +
211 +**功能**:计划书表单字段组件集
212 +
213 +**子组件**
214 +- `AgePicker.vue` - 年龄选择器
215 +- `AmountInput.vue` - 金额输入框
216 +- `DatePicker.vue` - 日期选择器
217 +- `SelectPicker.vue` - 下拉选择器
218 +- `RadioGroup.vue` - 单选按钮组
219 +
220 +---
221 +
222 +### PlanTemplates/ - 计划模板
223 +
224 +**位置**`src/components/plan/PlanTemplates/`
225 +
226 +**功能**:计划模板组件
227 +
228 +---
229 +
230 +## 工具组件
231 +
232 +### PosterBuilder/ - 海报生成器
233 +
234 +**位置**`src/components/PosterBuilder/`
235 +
236 +**功能**:海报生成
237 +
238 +---
239 +
240 +### time-picker-data/ - 时间选择器数据
241 +
242 +**位置**`src/components/time-picker-data/`
243 +
244 +**功能**:时间选择器数据配置
245 +
246 +---
247 +
248 +## 可选功能组件
249 +
250 +以下组件如果不需要,可以移除:
251 +
252 +- **微信支付**`src/utils/wechatPay.js``src/api/wx/pay.js`
253 +- **二维码**`src/components/qrCode.vue``src/components/qrCodeSearch.vue`
254 +- **海报生成器**`src/components/PosterBuilder/`
255 +- **时间选择器**`src/components/time-picker-data/`
256 +
257 +---
258 +
259 +## 组件使用原则
260 +
261 +### 第 3 次出现原则
262 +
263 +当相同代码模式出现 3 次时,**必须**抽取为组件。
264 +
265 +### 组件自包含原则
266 +
267 +对于重复的UI结构,抽取为可复用组件(如 MaterialCard、ProductCard):
268 +- 组件应该自包含业务逻辑(查看、收藏等)
269 +- 通过事件与父组件通信
270 +- 避免在父组件中重复编写相同的逻辑代码
271 +
272 +## 相关文档
273 +
274 +- **[Composables 参考](composables.md)** - 可复用逻辑
1 +# Composables 参考文档
2 +
3 +本文档列出项目中所有 Composables 的详细说明。
4 +
5 +## 项目中的 Composables
6 +
7 +| Composable | 用途 | 文档 |
8 +|-----------|------|------|
9 +| `useSectionList` | 分组列表管理 | [详情](#usesectionlist) |
10 +| `useFileOperation` | 文件下载、预览、打开 | [详情](#usefileoperation) |
11 +| `useListItemClick` | 统一的列表点击处理 | [详情](#uselistitemclick) |
12 +| `useCollectOperation` | 收藏操作 | [详情](#usecollectoperation) |
13 +| `useEventTracking` | 事件埋点 | [详情](#useeventtracking) |
14 +| `useGo` | 增强导航 | [详情](#usego) |
15 +| `usePlanPermission` | 计划书权限检查 | [详情](#useplanpermission) |
16 +
17 +---
18 +
19 +## useSectionList
20 +
21 +**位置**`src/composables/useSectionList.js`
22 +
23 +**功能**:分组列表管理
24 +
25 +**用途**:处理分组数据的展开/收起、过滤等逻辑
26 +
27 +**示例**
28 +```javascript
29 +import { useSectionList } from '@/composables/useSectionList'
30 +
31 +const { sections, toggleSection, isExpanded } = useSectionList(data)
32 +```
33 +
34 +---
35 +
36 +## useFileOperation
37 +
38 +**位置**`src/composables/useFileOperation.js`
39 +
40 +**功能**:文件操作(下载、预览、打开)
41 +
42 +**用途**:统一的文件操作逻辑,支持多种文件类型
43 +
44 +**示例**
45 +```javascript
46 +import { useFileOperation } from '@/composables/useFileOperation'
47 +
48 +const { downloadFile, previewFile, openFile } = useFileOperation()
49 +
50 +// 下载文件
51 +await downloadFile(file)
52 +
53 +// 预览文件
54 +await previewFile(file)
55 +```
56 +
57 +---
58 +
59 +## useListItemClick
60 +
61 +**位置**`src/composables/useListItemClick.js`
62 +
63 +**功能**:统一的列表点击处理
64 +
65 +**用途**:处理列表项的点击事件,包含权限检查和埋点
66 +
67 +**示例**
68 +```javascript
69 +import { useListItemClick } from '@/composables/useListItemClick'
70 +
71 +const { handleItemClick } = useListItemClick()
72 +
73 +// 处理点击
74 +await handleItemClick(item, () => {
75 + // 点击后的操作
76 +})
77 +```
78 +
79 +---
80 +
81 +## useCollectOperation
82 +
83 +**位置**`src/composables/useCollectOperation.js`
84 +
85 +**功能**:收藏/取消收藏操作
86 +
87 +**用途**:处理收藏状态切换和 API 调用
88 +
89 +**示例**
90 +```javascript
91 +import { useCollectOperation } from '@/composables/useCollectOperation'
92 +
93 +const { isCollected, toggleCollect } = useCollectOperation(metaId)
94 +
95 +// 切换收藏状态
96 +await toggleCollect()
97 +```
98 +
99 +---
100 +
101 +## useEventTracking
102 +
103 +**位置**`src/composables/useEventTracking.js`
104 +
105 +**功能**:事件埋点
106 +
107 +**用途**:统一的事件埋点功能,支持多种埋点类型
108 +
109 +**事件类型**
110 +- `READ_FILE` - 阅读素材
111 +
112 +**示例**
113 +```javascript
114 +import { useEventTracking } from '@/composables/useEventTracking'
115 +
116 +const { trackEvent, trackFileRead } = useEventTracking()
117 +
118 +// 追踪阅读事件
119 +await trackFileRead('file-id-123')
120 +
121 +// 追踪自定义事件
122 +await trackEvent('CUSTOM_EVENT', 'object-id', {
123 + title: '文档标题',
124 + category: '分类'
125 +})
126 +```
127 +
128 +---
129 +
130 +## useGo
131 +
132 +**位置**`src/hooks/useGo.js`
133 +
134 +**功能**:增强导航
135 +
136 +**用途**:自动路径补全和便捷导航方法
137 +
138 +**示例**
139 +```javascript
140 +import { useGo } from '@/hooks/useGo'
141 +
142 +const go = useGo()
143 +
144 +// 导航到页面
145 +go('/pages/detail/index')
146 +
147 +// 带参数导航
148 +go('/pages/product-detail/index', { id: 123 })
149 +
150 +// 返回
151 +go.back()
152 +```
153 +
154 +---
155 +
156 +## usePlanPermission
157 +
158 +**位置**`src/composables/usePlanPermission.js`
159 +
160 +**功能**:计划书权限检查
161 +
162 +**用途**:检查用户是否有权限操作计划书
163 +
164 +**示例**
165 +```javascript
166 +import { usePlanPermission } from '@/composables/usePlanPermission'
167 +
168 +const { checkPlanPermission } = usePlanPermission()
169 +
170 +// 检查权限后执行操作
171 +await checkPlanPermission(() => {
172 + // 有权限后的操作
173 +})
174 +```
175 +
176 +---
177 +
178 +## 抽取原则
179 +
180 +**第 3 次出现原则**
181 +
182 +当相同代码模式出现 3 次时,**必须**抽取为 Composable。
183 +
184 +**示例**
185 +
186 +```javascript
187 +// ❌ BAD - 在多个组件中重复
188 +const handleClick = async () => {
189 + if (!isLoggedIn()) {
190 + Taro.showToast({ title: '请先登录', icon: 'none' })
191 + return
192 + }
193 + // ... 业务逻辑
194 +}
195 +
196 +// ✅ GOOD - 抽取为 Composable
197 +const { requireLogin } = usePermission()
198 +await requireLogin(() => {
199 + // ... 业务逻辑
200 +})
201 +```
202 +
203 +---
204 +
205 +## 创建新的 Composable
206 +
207 +### 命名规范
208 +
209 +- 使用 `use` 前缀
210 +- 使用驼峰命名
211 +- 名称应描述功能
212 +
213 +**示例**
214 +-`useUserData`
215 +-`useFormValidation`
216 +-`userData`
217 +-`validation`
218 +
219 +### 基本结构
220 +
221 +```javascript
222 +/**
223 + * 使用 XXX 功能
224 + *
225 + * @description 功能描述
226 + * @returns {Object} 返回值描述
227 + */
228 +export function useXxx() {
229 + // 响应式状态
230 + const state = ref(null)
231 +
232 + // 方法
233 + const method = () => {
234 + // ...
235 + }
236 +
237 + // 返回公共 API
238 + return {
239 + state,
240 + method
241 + }
242 +}
243 +```
244 +
245 +### 使用示例
246 +
247 +```javascript
248 +// 在组件中使用
249 +import { useXxx } from '@/composables/useXxx'
250 +
251 +const { state, method } = useXxx()
252 +```
253 +
254 +---
255 +
256 +## 相关文档
257 +
258 +- **[组件参考](components.md)** - 可复用组件
259 +- **[最佳实践](../best-practices.md)** - 代码复用原则
1 +# 页面参考文档
2 +
3 +本文档列出项目中所有页面的详细说明。
4 +
5 +## 核心页面
6 +
7 +### 1. pages/index/index - 首页
8 +
9 +**功能**:产品展示、搜索、网格导航
10 +
11 +**关键特性**
12 +- 热门产品的"产品资料"按钮跳转到 `product-detail` 页面,带产品 ID
13 +- 热门资料的"查看更多"跳转到 `material-list` 页面
14 +- 网格导航图标跳转到各个业务页面
15 +
16 +**使用的组件**
17 +- MaterialCard - 资料卡片
18 +- ProductCard - 产品卡片
19 +
20 +---
21 +
22 +### 2. pages/search/index - 搜索页
23 +
24 +**功能**:产品和资料搜索
25 +
26 +**关键特性**
27 +- 支持实时搜索(输入关键字自动调用 searchAPI)
28 +- 双Tab切换(产品、资料)
29 +- 支持分页加载和触底加载更多
30 +
31 +**使用的组件**
32 +- MaterialCard
33 +- ProductCard
34 +
35 +---
36 +
37 +### 3. pages/webview/index - WebView 包装器
38 +
39 +**功能**:外部 URL 的 WebView 包装器
40 +
41 +---
42 +
43 +### 4. pages/document-preview/index - 文档预览页
44 +
45 +**功能**:文档预览
46 +
47 +---
48 +
49 +### 5. pages/document-demo/index - 文档预览演示页
50 +
51 +**功能**:文档预览演示
52 +
53 +---
54 +
55 +### 6. pages/onboarding/index - 新用户引导
56 +
57 +**功能**:新用户引导流程
58 +
59 +---
60 +
61 +## 业务页面
62 +
63 +### 7. pages/family-office/index - 家族办公室
64 +
65 +**功能**:家族办公室服务
66 +
67 +---
68 +
69 +### 8. pages/product-center/index - 产品中心
70 +
71 +**功能**:产品列表展示
72 +
73 +**关键特性**
74 +- 分类筛选
75 +- 分页加载
76 +
77 +---
78 +
79 +### 9. pages/category-list/index - 分类列表
80 +
81 +**功能**:分类列表展示
82 +
83 +---
84 +
85 +### 10. pages/product-detail/index - 产品详情
86 +
87 +**功能**:产品详情展示
88 +
89 +**关键特性**
90 +- 通过 Taro 的 `useLoad` hook 接收 `id` 参数
91 +- 导航示例:`go('/pages/product-detail/index', { id: 1 })`
92 +- 参数可用于从 API 获取产品详情
93 +
94 +---
95 +
96 +### 11. pages/material-list/index - 资料列表
97 +
98 +**功能**:资料/文档列表展示
99 +
100 +**关键特性**
101 +- 分类筛选
102 +- 分页加载
103 +- 触底加载更多
104 +
105 +---
106 +
107 +### 12. pages/week-hot-material/index - 周热门资料
108 +
109 +**功能**:热门资料展示
110 +
111 +**关键特性**
112 +- 使用 MaterialCard 组件展示热门资料
113 +- 支持分页加载和触底加载更多
114 +
115 +---
116 +
117 +### 13. pages/signing/index - 签约
118 +
119 +**功能**:签约流程
120 +
121 +---
122 +
123 +### 14. pages/mine/index - 我的
124 +
125 +**功能**:用户资料页面
126 +
127 +**关键特性**
128 +- 用户信息展示
129 +- 设置入口
130 +
131 +---
132 +
133 +### 15. pages/plan/index - 计划书管理
134 +
135 +**功能**:业务计划管理
136 +
137 +**关键特性**
138 +- 使用 PlanSchemes 和 PlanPopup 组件
139 +- 支持嵌套弹窗交互(provide/inject 模式)
140 +- 支持滚动加载和分页
141 +
142 +---
143 +
144 +### 16. pages/plan-submit-result/index - 计划提交结果
145 +
146 +**功能**:计划提交结果展示
147 +
148 +**关键特性**
149 +- 导航按钮:返回上一页(非首页)
150 +
151 +---
152 +
153 +## 用户相关页面
154 +
155 +### 17. pages/favorites/index - 收藏
156 +
157 +**功能**:用户收藏列表
158 +
159 +---
160 +
161 +### 18. pages/avatar/index - 头像设置
162 +
163 +**功能**:用户头像上传和设置
164 +
165 +---
166 +
167 +### 19. pages/message/index - 消息列表
168 +
169 +**功能**:消息列表展示
170 +
171 +**关键特性**
172 +- 未读消息红点
173 +- 消息分类
174 +
175 +---
176 +
177 +### 20. pages/message-detail/index - 消息详情
178 +
179 +**功能**:消息详情展示
180 +
181 +---
182 +
183 +### 21. pages/feedback-list/index - 反馈列表
184 +
185 +**功能**:意见反馈列表
186 +
187 +---
188 +
189 +### 22. pages/feedback/index - 用户反馈
190 +
191 +**功能**:提交用户反馈
192 +
193 +---
194 +
195 +### 23. pages/login/index - 登录
196 +
197 +**功能**:用户登录
198 +
199 +**关键特性**
200 +- 微信授权登录
201 +- 登录状态检查
202 +
203 +---
204 +
205 +### 24. pages/help-center/index - 帮助中心
206 +
207 +**功能**:帮助中心和常见问题
208 +
209 +---
210 +
211 +## 开发测试页面
212 +
213 +### 25. pages/test-tabs/index - 标签页测试
214 +
215 +**功能**:仅开发环境,用于测试标签页组件
216 +
217 +---
218 +
219 +## 页面注册
220 +
221 +所有页面在 `src/app.config.js` 中注册:
222 +
223 +```javascript
224 +export default {
225 + pages: [
226 + 'pages/index/index',
227 + 'pages/search/index',
228 + // ... 其他页面
229 + ]
230 +}
231 +```
232 +
233 +## 相关文档
234 +
235 +- **[页面开发指南](guides/page-development.md)** - 如何添加新页面
236 +- **[导航系统指南](guides/navigation.md)** - 导航和参数传递