hookehuyr

docs(ui): 添加 components 和 composables 目录文档索引

- 新增 src/components/README.md:组件目录索引,包含导航、卡片、表单、列表、文档、计划书等模块的组件说明和使用示例
- 新增 src/composables/README.md:Composables 目录索引,包含权限检查、文件操作、列表管理、收藏、埋点等可复用逻辑的说明

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 +# Components 目录
2 +
3 +本项目组件使用 Vue 3 Composition API + `<script setup>` 编写,按功能模块组织。
4 +
5 +---
6 +
7 +## 📚 目录结构
8 +
9 +```
10 +components/
11 +├── cards/ # 卡片组件
12 +├── documents/ # 文档预览组件
13 +├── examples/ # 示例组件
14 +├── forms/ # 表单组件
15 +├── icons/ # 图标组件
16 +├── list/ # 列表组件
17 +├── navigation/ # 导航组件
18 +└── plan/ # 计划书组件
19 +```
20 +
21 +---
22 +
23 +## 📦 组件索引
24 +
25 +### 导航组件 (navigation/)
26 +
27 +| 组件 | 功能 | Props |
28 +|------|------|-------|
29 +| [`NavHeader`](#navheadervue) | 页面顶部导航栏 | title, showBack, preventDefaultBack |
30 +| [`TabBar`](#tabbarvue) | 底部标签导航栏 | current, tabs |
31 +
32 +**使用示例**
33 +```vue
34 +<template>
35 + <NavHeader title="页面标题" />
36 + <TabBar current="home" />
37 +</template>
38 +
39 +<script setup>
40 +import NavHeader from '@/components/navigation/NavHeader.vue'
41 +import TabBar from '@/components/navigation/TabBar.vue'
42 +</script>
43 +```
44 +
45 +---
46 +
47 +### 卡片组件 (cards/)
48 +
49 +| 组件 | 功能 | Props |
50 +|------|------|-------|
51 +| [`MaterialCard`](#materialcardvue) | 资料/文档卡片 | title, icon, learners, docType, collected |
52 +| [`ProductCard`](#productcardvue) | 产品卡片 | name, type, tags, collected |
53 +
54 +**使用示例**
55 +```vue
56 +<template>
57 + <MaterialCard
58 + :title="item.title"
59 + :icon="item.icon"
60 + :learners="item.learners"
61 + :doc-type="item.docType"
62 + :collected="item.collected"
63 + @view="handleView"
64 + />
65 +</template>
66 +
67 +<script setup>
68 +import MaterialCard from '@/components/cards/MaterialCard.vue'
69 +</script>
70 +```
71 +
72 +---
73 +
74 +### 表单组件 (forms/)
75 +
76 +| 组件 | 功能 | Props |
77 +|------|------|-------|
78 +| [`SearchBar`](#searchbarvue) | 搜索栏 | modelValue, placeholder, disabled |
79 +| [`FilterTabs`](#filtertabsvue) | 筛选标签 | options, modelValue |
80 +
81 +**使用示例**
82 +```vue
83 +<template>
84 + <SearchBar v-model="searchText" placeholder="搜索资料" />
85 + <FilterTabs v-model="activeTab" :options="tabs" />
86 +</template>
87 +
88 +<script setup>
89 +import SearchBar from '@/components/forms/SearchBar.vue'
90 +import FilterTabs from '@/components/forms/FilterTabs.vue'
91 +</script>
92 +```
93 +
94 +---
95 +
96 +### 列表组件 (list/)
97 +
98 +| 组件 | 功能 | Props |
99 +|------|------|-------|
100 +| [`SectionCard`](#sectioncardvue) | 分组卡片容器 | title |
101 +| [`SectionItem`](#sectionitemvue) | 分组列表项 | title, subtitle, icon |
102 +| [`LoadMoreList`](#loadmorelist) | 加载更多列表 | items, hasMore, loading |
103 +| [`ListItemActions`](#listitemactions) | 列表项操作按钮 | viewable, collectable, deletable |
104 +
105 +**使用示例**
106 +```vue
107 +<template>
108 + <SectionCard title="常用功能">
109 + <SectionItem
110 + v-for="item in items"
111 + :key="item.id"
112 + :title="item.title"
113 + :subtitle="item.subtitle"
114 + :icon="item.icon"
115 + @click="handleClick"
116 + />
117 + </SectionCard>
118 +
119 + <LoadMoreList
120 + :items="list"
121 + :hasMore="hasMore"
122 + :loading="loading"
123 + @load-more="loadMore"
124 + />
125 +</template>
126 +
127 +<script setup>
128 +import SectionCard from '@/components/list/SectionCard.vue'
129 +import SectionItem from '@/components/list/SectionItem.vue'
130 +import LoadMoreList from '@/components/list/LoadMoreList/index.vue'
131 +</script>
132 +```
133 +
134 +---
135 +
136 +### 文档组件 (documents/)
137 +
138 +| 组件 | 功能 | Props |
139 +|------|------|-------|
140 +| [`PdfPreview`](#pdfpreviewvue) | PDF 预览 | url |
141 +| [`OfficeViewer`](#officeviewervue) | Office 文档查看器 | url |
142 +| [`DocumentPreview`](#documentpreview) | 通用文档预览 | file |
143 +
144 +---
145 +
146 +### 计划书组件 (plan/)
147 +
148 +#### 弹窗容器
149 +| 组件 | 功能 |
150 +|------|------|
151 +| [`PlanPopupNew`](#planpopupnewvue) | 计划书弹窗容器 |
152 +| [`PlanFormContainer`](#planformcontainervue) | 计划书表单容器 |
153 +
154 +#### 表单字段 (PlanFields/)
155 +| 组件 | 功能 |
156 +|------|------|
157 +| [`NameInput`](#nameinputvue) | 姓名输入 |
158 +| [`AgePickerGlobal`](#agepickerglobalvue) | 年龄选择器 |
159 +| [`DatePickerGlobal`](##datepickerglobalvue) | 日期选择器 |
160 +| [`SelectPickerGlobal`](#selectpickerglobalvue) | 选项选择器 |
161 +| [`AmountKeyboard`](#amountkeyboardvue) | 金额键盘 |
162 +| [`PaymentPeriodRadio`](#paymentperiodradiovue) | 缴费年期单选 |
163 +| [`RadioGroup`](#radiogroupvue) | 单选分组 |
164 +
165 +#### 模板 (PlanTemplates/)
166 +| 组件 | 功能 |
167 +|------|------|
168 +| [`SavingsTemplate`](#savingstemplatevue) | 储蓄险模板 |
169 +| [`LifeInsuranceTemplate`](#lifeinsurancetemplatevue) | 寿险模板 |
170 +| [`CriticalIllnessTemplate`](#criticalillnesstemplatevue) | 重疾险模板 |
171 +
172 +---
173 +
174 +### 图标组件 (icons/)
175 +
176 +| 组件 | 功能 | Props |
177 +|------|------|-------|
178 +| [`IconFont`](#iconfontvue) | IconFont 图标 | name, size, color |
179 +
180 +**使用示例**
181 +```vue
182 +<template>
183 + <IconFont name="home" :size="28" color="#3B82F6" />
184 + <IconFont name="search" />
185 +</template>
186 +
187 +<script setup>
188 +import IconFont from '@/components/icons/IconFont.vue'
189 +</script>
190 +```
191 +
192 +---
193 +
194 +## 🎯 组件使用规范
195 +
196 +### Props 定义
197 +
198 +```vue
199 +<script setup>
200 +// ✅ GOOD - 有类型和默认值
201 +const props = defineProps({
202 + title: {
203 + type: String,
204 + required: true
205 + },
206 + count: {
207 + type: Number,
208 + default: 0
209 + }
210 +})
211 +
212 +// ❌ BAD - 缺少类型
213 +const props = defineProps(['title', 'count'])
214 +</script>
215 +```
216 +
217 +### Emits 定义
218 +
219 +```vue
220 +<script setup>
221 +// ✅ GOOD - 明确定义事件
222 +const emit = defineEmits(['update:modelValue', 'change', 'submit'])
223 +
224 +// 使用
225 +emit('update:modelValue', newValue)
226 +emit('change', newValue)
227 +</script>
228 +```
229 +
230 +### 样式规范
231 +
232 +```vue
233 +<style scoped>
234 +/* ✅ 使用 TailwindCSS(80%) */
235 +.container {
236 + @apply flex items-center justify-center p-4 bg-white rounded-lg;
237 +}
238 +
239 +/* ✅ 使用 Less(20%)- 深度选择器、特定样式 */
240 +:deep(.nut-input) {
241 + border-radius: 12rpx !important;
242 +}
243 +</style>
244 +```
245 +
246 +---
247 +
248 +## 🔧 核心组件详解
249 +
250 +### NavHeader.vue
251 +
252 +页面顶部导航栏,支持返回按钮。
253 +
254 +**Props**
255 +| Prop | 类型 | 默认值 | 说明 |
256 +|------|------|--------|------|
257 +| title | String | - | 页面标题 |
258 +| showBack | Boolean | true | 是否显示返回按钮 |
259 +| preventDefaultBack | Boolean | false | 阻止默认返回行为 |
260 +
261 +**Events**
262 +| Event | 说明 |
263 +|-------|------|
264 +| back | 点击返回按钮时触发 |
265 +
266 +**使用示例**
267 +```vue
268 +<NavHeader
269 + title="资料详情"
270 + :show-back="true"
271 + :prevent-default-back="true"
272 + @back="handleCustomBack"
273 +/>
274 +```
275 +
276 +---
277 +
278 +### TabBar.vue
279 +
280 +底部标签导航栏,支持红点提醒。
281 +
282 +**Props**
283 +| Prop | 类型 | 默认值 | 说明 |
284 +|------|------|--------|------|
285 +| current | String | 'home' | 当前激活的标签 |
286 +
287 +**红点自动同步**
288 +- 组件自动从 `userStore.tabBarBadges` 读取红点状态
289 +- 无需手动传入 badges prop
290 +
291 +**使用示例**
292 +```vue
293 +<TabBar current="home" />
294 +```
295 +
296 +---
297 +
298 +### MaterialCard.vue
299 +
300 +资料/文档卡片,展示资料信息和操作按钮。
301 +
302 +**Props**
303 +| Prop | 类型 | 说明 |
304 +|------|------|------|
305 +| title | String | 资料标题 |
306 +| icon | String | 图标名称 |
307 +| learners | String | 学习人数 |
308 +| docType | String | 文档类型 |
309 +| fileSize | String | 文件大小 |
310 +| collected | Boolean | 是否已收藏 |
311 +| id | Number/String | 资料 ID |
312 +
313 +**Events**
314 +| Event | 说明 |
315 +|-------|------|
316 +| view | 查看资料 |
317 +| collect | 收藏/取消收藏 |
318 +
319 +---
320 +
321 +### SearchBar.vue
322 +
323 +搜索栏组件,支持清除和搜索。
324 +
325 +**Props**
326 +| Prop | 类型 | 默认值 | 说明 |
327 +|------|------|--------|------|
328 +| modelValue | String | '' | 绑定值(v-model) |
329 +| placeholder | String | '搜索' | 占位文本 |
330 +| disabled | Boolean | false | 是否禁用 |
331 +| showClear | Boolean | true | 是否显示清除按钮 |
332 +
333 +**Events**
334 +| Event | 说明 |
335 +|-------|------|
336 +| update:modelValue | 输入变化 |
337 +| search | 点击搜索或回车 |
338 +
339 +**使用示例**
340 +```vue
341 +<SearchBar
342 + v-model="searchText"
343 + placeholder="搜索资料"
344 + @search="handleSearch"
345 +/>
346 +```
347 +
348 +---
349 +
350 +### IconFont.vue
351 +
352 +IconFont 图标组件。
353 +
354 +**Props**
355 +| Prop | 类型 | 默认值 | 说明 |
356 +|------|------|--------|------|
357 +| name | String | - | 图标名称 |
358 +| size | Number/String | 20 | 图标大小 |
359 +| color | String | '#333' | 图标颜色 |
360 +
361 +**使用示例**
362 +```vue
363 +<IconFont name="home" :size="28" color="#3B82F6" />
364 +```
365 +
366 +---
367 +
368 +## 📖 最佳实践
369 +
370 +### 组件抽取原则
371 +
372 +**必须抽取为组件**
373 +- ✅ UI 在 3 个及以上页面重复
374 +- ✅ 复杂的业务逻辑需要封装
375 +- ✅ 可复用的表单控件
376 +
377 +### 组件命名规范
378 +
379 +```javascript
380 +// ✅ GOOD - 多词组合,PascalCase
381 +MaterialCard.vue
382 +SearchBar.vue
383 +AgePickerGlobal.vue
384 +
385 +// ❌ BAD - 单词,不清晰
386 +Card.vue
387 +Input.vue
388 +Picker.vue
389 +```
390 +
391 +### Props vs Emits
392 +
393 +```vue
394 +<script setup>
395 +// ✅ GOOD - 数据向下传递
396 +const props = defineProps({
397 + modelValue: String
398 +})
399 +
400 +// ✅ GOOD - 事件向上传递
401 +const emit = defineEmits(['update:modelValue', 'change'])
402 +
403 +// 使用
404 +emit('update:modelValue', newValue)
405 +</script>
406 +```
407 +
408 +---
409 +
410 +## 🔗 相关文档
411 +
412 +- [Composables 参考](../composables/README.md)
413 +- [项目最佳实践](../../docs/best-practices.md)
414 +- [Vue 3 组件官方文档](https://vuejs.org/guide/essentials/component-basics.html)
1 +# Composables 目录
2 +
3 +本项目使用 Vue 3 Composition API 的 Composables 模式封装可复用逻辑。
4 +
5 +---
6 +
7 +## 📚 快速索引
8 +
9 +| Composable | 功能描述 | 使用场景 |
10 +|-----------|---------|---------|
11 +| [`usePermission`](#usepermissionjs) | 权限检查(登录/VIP/认证) | 页面权限控制 |
12 +| [`useFileOperation`](#usefileoperationjs) | 文件下载、打开、预览 | 资料库、文档查看 |
13 +| [`useSectionList`](#usesectionlistjs) | 分组列表管理 | 设置页面、帮助中心 |
14 +| [`useCollectOperation`](#usecollectoperationjs) | 收藏/取消收藏 | 产品收藏、资料收藏 |
15 +| [`usePlanSubmit`](#useplansubmitjs) | 计划书提交后处理 | 计划书功能 |
16 +| [`useEventTracking`](#useeventtrackingjs) | 事件埋点 | 用户行为追踪 |
17 +| [`useListItemClick`](#uselistitemclickjs) | 列表项点击处理 | 列表导航 |
18 +| [`useFieldDependencies`](#usefielddependenciesjs) | 字段依赖管理 | 表单联动 |
19 +| [`useFieldValueTransform`](#usefieldvaluetransformjs) | 字段值转换 | 表单数据处理 |
20 +| [`usePlanView`](#useplanviewjs) | 计划书视图管理 | 计划书查看 |
21 +
22 +---
23 +
24 +## 详细说明
25 +
26 +### usePermission.js
27 +
28 +**功能**:统一的权限检查逻辑
29 +
30 +**核心方法**
31 +- `requireLogin(callback, options)` - 检查登录权限(最常用)
32 +- `checkPermission(type, callback, options)` - 通用权限检查
33 +- `hasPermission(type)` - 静默检查权限(不弹窗)
34 +- `isLoggedIn()` - 获取登录状态
35 +
36 +**使用示例**
37 +```javascript
38 +import { usePermission } from '@/composables/usePermission'
39 +
40 +const { requireLogin } = usePermission()
41 +
42 +// 查看资料需要登录
43 +const viewMaterial = (item) => {
44 + requireLogin(() => {
45 + openDocument(item.url)
46 + }, {
47 + content: '请先登录后查看完整资料',
48 + confirmText: '立即登录'
49 + })
50 +}
51 +```
52 +
53 +---
54 +
55 +### useFileOperation.js
56 +
57 +**功能**:文件下载、打开、预览
58 +
59 +**核心方法**
60 +- `viewFile(item)` - 入口函数,自动判断文件类型
61 +- `openFile(filePath, item)` - 使用 Taro.openDocument 打开文件
62 +- `downloadAndOpenFile(item)` - 下载并打开文件
63 +
64 +**支持的文件类型**
65 +- 图片:jpg, jpeg, png, gif, webp, bmp, svg
66 +- 视频:mp4, mov 等(跳转播放页面)
67 +- 文档:pdf(直接预览),Office 文档(提示不支持)
68 +
69 +**使用示例**
70 +```javascript
71 +import { useFileOperation } from '@/composables/useFileOperation'
72 +
73 +const { viewFile } = useFileOperation()
74 +
75 +// 打开文件
76 +await viewFile({
77 + downloadUrl: 'https://example.com/file.pdf',
78 + fileName: 'document.pdf',
79 + extension: 'pdf' // 可选,优先使用此字段判断类型
80 +})
81 +```
82 +
83 +---
84 +
85 +### useSectionList.js
86 +
87 +**功能**:分组列表页面管理
88 +
89 +**核心方法**
90 +- `sections` - 分组列表数据(shallowRef)
91 +- `handleItemClick(item)` - 列表项点击处理
92 +
93 +**使用示例**
94 +```javascript
95 +import { useSectionList } from '@/composables/useSectionList'
96 +
97 +const { sections, handleItemClick } = useSectionList(
98 + [
99 + {
100 + title: '常用功能',
101 + items: [
102 + { title: '我的收藏', icon: 'star', subtitle: '查看收藏内容' }
103 + ]
104 + }
105 + ],
106 + (item, go) => {
107 + // 自定义导航逻辑
108 + go('/pages/favorites/index')
109 + }
110 +)
111 +```
112 +
113 +---
114 +
115 +### useCollectOperation.js
116 +
117 +**功能**:收藏/取消收藏(乐观更新)
118 +
119 +**核心方法**
120 +- `toggleCollect(item, successMsg, errorMsg)` - 切换收藏状态
121 +
122 +**特性**
123 +- 乐观更新 UI(先更新界面,后调用 API)
124 +- 失败自动回滚
125 +- 发送事件通知其他页面
126 +
127 +**使用示例**
128 +```javascript
129 +import { useCollectOperation } from '@/composables/useCollectOperation'
130 +
131 +const { toggleCollect } = useCollectOperation()
132 +
133 +// 切换收藏
134 +await toggleCollect(item, '收藏成功', '已取消收藏')
135 +```
136 +
137 +---
138 +
139 +### usePlanSubmit.js
140 +
141 +**功能**:计划书提交后统一处理
142 +
143 +**核心方法**
144 +- `handlePlanSubmit(result, callbacks)` - 处理提交结果
145 +
146 +**特性**
147 +- 自动关闭弹窗
148 +- 自动清空选中产品
149 +- 支持导航前后回调
150 +
151 +**使用示例**
152 +```javascript
153 +import { usePlanSubmit } from '@/composables/usePlanSubmit'
154 +
155 +const { handlePlanSubmit } = usePlanSubmit({
156 + getPopupState: () => showPlanPopup.value,
157 + setPopupState: (state) => { showPlanPopup.value = state },
158 + clearSelectedProduct: () => { selectedProduct.value = null }
159 +})
160 +
161 +await handlePlanSubmit(result, {
162 + beforeNav: async () => { console.log('导航前') },
163 + afterNav: async () => { console.log('导航后') }
164 +})
165 +```
166 +
167 +---
168 +
169 +### useEventTracking.js
170 +
171 +**功能**:事件埋点
172 +
173 +**核心方法**
174 +- `trackEvent(type, objectId, extraData)` - 追踪事件
175 +- `trackFileRead(fileId, extraData)` - 追踪文件阅读
176 +
177 +**使用示例**
178 +```javascript
179 +import { useEventTracking, EventType } from '@/composables/useEventTracking'
180 +
181 +const { trackFileRead } = useEventTracking()
182 +
183 +// 追踪文件阅读
184 +await trackFileRead('file-id-123', {
185 + title: '保险产品手册',
186 + category: '产品资料'
187 +})
188 +```
189 +
190 +---
191 +
192 +### useListItemClick.js
193 +
194 +**功能**:列表项点击处理(带权限检查)
195 +
196 +**核心方法**
197 +- `handleListItemClick(item, config)` - 处理列表项点击
198 +
199 +**使用示例**
200 +```javascript
201 +import { useListItemClick } from '@/composables/useListItemClick'
202 +
203 +const { handleListItemClick } = useListItemClick()
204 +
205 +// 点击列表项(自动检查登录权限)
206 +handleListItemClick(item, {
207 + needLogin: true,
208 + onClick: (item) => {
209 + console.log('点击了', item)
210 + }
211 +})
212 +```
213 +
214 +---
215 +
216 +### useFieldDependencies.js
217 +
218 +**功能**:表单字段依赖管理
219 +
220 +**核心方法**
221 +- 当字段 A 变化时,自动更新字段 B
222 +
223 +---
224 +
225 +### useFieldValueTransform.js
226 +
227 +**功能**:字段值转换
228 +
229 +**核心方法**
230 +- 将用户输入转换为后端需要的格式
231 +
232 +---
233 +
234 +### usePlanView.js
235 +
236 +**功能**:计划书视图管理
237 +
238 +**核心方法**
239 +- 管理计划书的显示状态
240 +
241 +---
242 +
243 +## 🎯 最佳实践
244 +
245 +### 命名规范
246 +
247 +```javascript
248 +// ✅ GOOD - 清晰的命名
249 +usePermission()
250 +useFileOperation()
251 +useSectionList()
252 +
253 +// ❌ BAD - 过于通用
254 +useData()
255 +useList()
256 +```
257 +
258 +### 抽取原则
259 +
260 +**必须抽取 Composable**
261 +- ✅ 业务逻辑在 ≥ 3 个组件中重复
262 +- ✅ 相同的状态管理模式重复
263 +- ✅ 复杂的异步操作模式重复
264 +
265 +### 返回值规范
266 +
267 +```javascript
268 +// ✅ 返回响应式 refs 和函数
269 +return {
270 + // 响应式状态
271 + users, // ref
272 + loading, // ref
273 + // 方法
274 + fetchUsers,
275 + refresh // 别名方法
276 +}
277 +```
278 +
279 +---
280 +
281 +## 📖 参考文档
282 +
283 +- [Vue 3 Composables 官方文档](https://vuejs.org/guide/reusability/composables.html)
284 +- [项目最佳实践](../../docs/best-practices.md)