hookehuyr

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

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

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
# Components 目录
本项目组件使用 Vue 3 Composition API + `<script setup>` 编写,按功能模块组织。
---
## 📚 目录结构
```
components/
├── cards/ # 卡片组件
├── documents/ # 文档预览组件
├── examples/ # 示例组件
├── forms/ # 表单组件
├── icons/ # 图标组件
├── list/ # 列表组件
├── navigation/ # 导航组件
└── plan/ # 计划书组件
```
---
## 📦 组件索引
### 导航组件 (navigation/)
| 组件 | 功能 | Props |
|------|------|-------|
| [`NavHeader`](#navheadervue) | 页面顶部导航栏 | title, showBack, preventDefaultBack |
| [`TabBar`](#tabbarvue) | 底部标签导航栏 | current, tabs |
**使用示例**
```vue
<template>
<NavHeader title="页面标题" />
<TabBar current="home" />
</template>
<script setup>
import NavHeader from '@/components/navigation/NavHeader.vue'
import TabBar from '@/components/navigation/TabBar.vue'
</script>
```
---
### 卡片组件 (cards/)
| 组件 | 功能 | Props |
|------|------|-------|
| [`MaterialCard`](#materialcardvue) | 资料/文档卡片 | title, icon, learners, docType, collected |
| [`ProductCard`](#productcardvue) | 产品卡片 | name, type, tags, collected |
**使用示例**
```vue
<template>
<MaterialCard
:title="item.title"
:icon="item.icon"
:learners="item.learners"
:doc-type="item.docType"
:collected="item.collected"
@view="handleView"
/>
</template>
<script setup>
import MaterialCard from '@/components/cards/MaterialCard.vue'
</script>
```
---
### 表单组件 (forms/)
| 组件 | 功能 | Props |
|------|------|-------|
| [`SearchBar`](#searchbarvue) | 搜索栏 | modelValue, placeholder, disabled |
| [`FilterTabs`](#filtertabsvue) | 筛选标签 | options, modelValue |
**使用示例**
```vue
<template>
<SearchBar v-model="searchText" placeholder="搜索资料" />
<FilterTabs v-model="activeTab" :options="tabs" />
</template>
<script setup>
import SearchBar from '@/components/forms/SearchBar.vue'
import FilterTabs from '@/components/forms/FilterTabs.vue'
</script>
```
---
### 列表组件 (list/)
| 组件 | 功能 | Props |
|------|------|-------|
| [`SectionCard`](#sectioncardvue) | 分组卡片容器 | title |
| [`SectionItem`](#sectionitemvue) | 分组列表项 | title, subtitle, icon |
| [`LoadMoreList`](#loadmorelist) | 加载更多列表 | items, hasMore, loading |
| [`ListItemActions`](#listitemactions) | 列表项操作按钮 | viewable, collectable, deletable |
**使用示例**
```vue
<template>
<SectionCard title="常用功能">
<SectionItem
v-for="item in items"
:key="item.id"
:title="item.title"
:subtitle="item.subtitle"
:icon="item.icon"
@click="handleClick"
/>
</SectionCard>
<LoadMoreList
:items="list"
:hasMore="hasMore"
:loading="loading"
@load-more="loadMore"
/>
</template>
<script setup>
import SectionCard from '@/components/list/SectionCard.vue'
import SectionItem from '@/components/list/SectionItem.vue'
import LoadMoreList from '@/components/list/LoadMoreList/index.vue'
</script>
```
---
### 文档组件 (documents/)
| 组件 | 功能 | Props |
|------|------|-------|
| [`PdfPreview`](#pdfpreviewvue) | PDF 预览 | url |
| [`OfficeViewer`](#officeviewervue) | Office 文档查看器 | url |
| [`DocumentPreview`](#documentpreview) | 通用文档预览 | file |
---
### 计划书组件 (plan/)
#### 弹窗容器
| 组件 | 功能 |
|------|------|
| [`PlanPopupNew`](#planpopupnewvue) | 计划书弹窗容器 |
| [`PlanFormContainer`](#planformcontainervue) | 计划书表单容器 |
#### 表单字段 (PlanFields/)
| 组件 | 功能 |
|------|------|
| [`NameInput`](#nameinputvue) | 姓名输入 |
| [`AgePickerGlobal`](#agepickerglobalvue) | 年龄选择器 |
| [`DatePickerGlobal`](##datepickerglobalvue) | 日期选择器 |
| [`SelectPickerGlobal`](#selectpickerglobalvue) | 选项选择器 |
| [`AmountKeyboard`](#amountkeyboardvue) | 金额键盘 |
| [`PaymentPeriodRadio`](#paymentperiodradiovue) | 缴费年期单选 |
| [`RadioGroup`](#radiogroupvue) | 单选分组 |
#### 模板 (PlanTemplates/)
| 组件 | 功能 |
|------|------|
| [`SavingsTemplate`](#savingstemplatevue) | 储蓄险模板 |
| [`LifeInsuranceTemplate`](#lifeinsurancetemplatevue) | 寿险模板 |
| [`CriticalIllnessTemplate`](#criticalillnesstemplatevue) | 重疾险模板 |
---
### 图标组件 (icons/)
| 组件 | 功能 | Props |
|------|------|-------|
| [`IconFont`](#iconfontvue) | IconFont 图标 | name, size, color |
**使用示例**
```vue
<template>
<IconFont name="home" :size="28" color="#3B82F6" />
<IconFont name="search" />
</template>
<script setup>
import IconFont from '@/components/icons/IconFont.vue'
</script>
```
---
## 🎯 组件使用规范
### Props 定义
```vue
<script setup>
// ✅ GOOD - 有类型和默认值
const props = defineProps({
title: {
type: String,
required: true
},
count: {
type: Number,
default: 0
}
})
// ❌ BAD - 缺少类型
const props = defineProps(['title', 'count'])
</script>
```
### Emits 定义
```vue
<script setup>
// ✅ GOOD - 明确定义事件
const emit = defineEmits(['update:modelValue', 'change', 'submit'])
// 使用
emit('update:modelValue', newValue)
emit('change', newValue)
</script>
```
### 样式规范
```vue
<style scoped>
/* ✅ 使用 TailwindCSS(80%) */
.container {
@apply flex items-center justify-center p-4 bg-white rounded-lg;
}
/* ✅ 使用 Less(20%)- 深度选择器、特定样式 */
:deep(.nut-input) {
border-radius: 12rpx !important;
}
</style>
```
---
## 🔧 核心组件详解
### NavHeader.vue
页面顶部导航栏,支持返回按钮。
**Props**
| Prop | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| title | String | - | 页面标题 |
| showBack | Boolean | true | 是否显示返回按钮 |
| preventDefaultBack | Boolean | false | 阻止默认返回行为 |
**Events**
| Event | 说明 |
|-------|------|
| back | 点击返回按钮时触发 |
**使用示例**
```vue
<NavHeader
title="资料详情"
:show-back="true"
:prevent-default-back="true"
@back="handleCustomBack"
/>
```
---
### TabBar.vue
底部标签导航栏,支持红点提醒。
**Props**
| Prop | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| current | String | 'home' | 当前激活的标签 |
**红点自动同步**
- 组件自动从 `userStore.tabBarBadges` 读取红点状态
- 无需手动传入 badges prop
**使用示例**
```vue
<TabBar current="home" />
```
---
### MaterialCard.vue
资料/文档卡片,展示资料信息和操作按钮。
**Props**
| Prop | 类型 | 说明 |
|------|------|------|
| title | String | 资料标题 |
| icon | String | 图标名称 |
| learners | String | 学习人数 |
| docType | String | 文档类型 |
| fileSize | String | 文件大小 |
| collected | Boolean | 是否已收藏 |
| id | Number/String | 资料 ID |
**Events**
| Event | 说明 |
|-------|------|
| view | 查看资料 |
| collect | 收藏/取消收藏 |
---
### SearchBar.vue
搜索栏组件,支持清除和搜索。
**Props**
| Prop | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| modelValue | String | '' | 绑定值(v-model) |
| placeholder | String | '搜索' | 占位文本 |
| disabled | Boolean | false | 是否禁用 |
| showClear | Boolean | true | 是否显示清除按钮 |
**Events**
| Event | 说明 |
|-------|------|
| update:modelValue | 输入变化 |
| search | 点击搜索或回车 |
**使用示例**
```vue
<SearchBar
v-model="searchText"
placeholder="搜索资料"
@search="handleSearch"
/>
```
---
### IconFont.vue
IconFont 图标组件。
**Props**
| Prop | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| name | String | - | 图标名称 |
| size | Number/String | 20 | 图标大小 |
| color | String | '#333' | 图标颜色 |
**使用示例**
```vue
<IconFont name="home" :size="28" color="#3B82F6" />
```
---
## 📖 最佳实践
### 组件抽取原则
**必须抽取为组件**
- ✅ UI 在 3 个及以上页面重复
- ✅ 复杂的业务逻辑需要封装
- ✅ 可复用的表单控件
### 组件命名规范
```javascript
// ✅ GOOD - 多词组合,PascalCase
MaterialCard.vue
SearchBar.vue
AgePickerGlobal.vue
// ❌ BAD - 单词,不清晰
Card.vue
Input.vue
Picker.vue
```
### Props vs Emits
```vue
<script setup>
// ✅ GOOD - 数据向下传递
const props = defineProps({
modelValue: String
})
// ✅ GOOD - 事件向上传递
const emit = defineEmits(['update:modelValue', 'change'])
// 使用
emit('update:modelValue', newValue)
</script>
```
---
## 🔗 相关文档
- [Composables 参考](../composables/README.md)
- [项目最佳实践](../../docs/best-practices.md)
- [Vue 3 组件官方文档](https://vuejs.org/guide/essentials/component-basics.html)
# Composables 目录
本项目使用 Vue 3 Composition API 的 Composables 模式封装可复用逻辑。
---
## 📚 快速索引
| Composable | 功能描述 | 使用场景 |
|-----------|---------|---------|
| [`usePermission`](#usepermissionjs) | 权限检查(登录/VIP/认证) | 页面权限控制 |
| [`useFileOperation`](#usefileoperationjs) | 文件下载、打开、预览 | 资料库、文档查看 |
| [`useSectionList`](#usesectionlistjs) | 分组列表管理 | 设置页面、帮助中心 |
| [`useCollectOperation`](#usecollectoperationjs) | 收藏/取消收藏 | 产品收藏、资料收藏 |
| [`usePlanSubmit`](#useplansubmitjs) | 计划书提交后处理 | 计划书功能 |
| [`useEventTracking`](#useeventtrackingjs) | 事件埋点 | 用户行为追踪 |
| [`useListItemClick`](#uselistitemclickjs) | 列表项点击处理 | 列表导航 |
| [`useFieldDependencies`](#usefielddependenciesjs) | 字段依赖管理 | 表单联动 |
| [`useFieldValueTransform`](#usefieldvaluetransformjs) | 字段值转换 | 表单数据处理 |
| [`usePlanView`](#useplanviewjs) | 计划书视图管理 | 计划书查看 |
---
## 详细说明
### usePermission.js
**功能**:统一的权限检查逻辑
**核心方法**
- `requireLogin(callback, options)` - 检查登录权限(最常用)
- `checkPermission(type, callback, options)` - 通用权限检查
- `hasPermission(type)` - 静默检查权限(不弹窗)
- `isLoggedIn()` - 获取登录状态
**使用示例**
```javascript
import { usePermission } from '@/composables/usePermission'
const { requireLogin } = usePermission()
// 查看资料需要登录
const viewMaterial = (item) => {
requireLogin(() => {
openDocument(item.url)
}, {
content: '请先登录后查看完整资料',
confirmText: '立即登录'
})
}
```
---
### useFileOperation.js
**功能**:文件下载、打开、预览
**核心方法**
- `viewFile(item)` - 入口函数,自动判断文件类型
- `openFile(filePath, item)` - 使用 Taro.openDocument 打开文件
- `downloadAndOpenFile(item)` - 下载并打开文件
**支持的文件类型**
- 图片:jpg, jpeg, png, gif, webp, bmp, svg
- 视频:mp4, mov 等(跳转播放页面)
- 文档:pdf(直接预览),Office 文档(提示不支持)
**使用示例**
```javascript
import { useFileOperation } from '@/composables/useFileOperation'
const { viewFile } = useFileOperation()
// 打开文件
await viewFile({
downloadUrl: 'https://example.com/file.pdf',
fileName: 'document.pdf',
extension: 'pdf' // 可选,优先使用此字段判断类型
})
```
---
### useSectionList.js
**功能**:分组列表页面管理
**核心方法**
- `sections` - 分组列表数据(shallowRef)
- `handleItemClick(item)` - 列表项点击处理
**使用示例**
```javascript
import { useSectionList } from '@/composables/useSectionList'
const { sections, handleItemClick } = useSectionList(
[
{
title: '常用功能',
items: [
{ title: '我的收藏', icon: 'star', subtitle: '查看收藏内容' }
]
}
],
(item, go) => {
// 自定义导航逻辑
go('/pages/favorites/index')
}
)
```
---
### useCollectOperation.js
**功能**:收藏/取消收藏(乐观更新)
**核心方法**
- `toggleCollect(item, successMsg, errorMsg)` - 切换收藏状态
**特性**
- 乐观更新 UI(先更新界面,后调用 API)
- 失败自动回滚
- 发送事件通知其他页面
**使用示例**
```javascript
import { useCollectOperation } from '@/composables/useCollectOperation'
const { toggleCollect } = useCollectOperation()
// 切换收藏
await toggleCollect(item, '收藏成功', '已取消收藏')
```
---
### usePlanSubmit.js
**功能**:计划书提交后统一处理
**核心方法**
- `handlePlanSubmit(result, callbacks)` - 处理提交结果
**特性**
- 自动关闭弹窗
- 自动清空选中产品
- 支持导航前后回调
**使用示例**
```javascript
import { usePlanSubmit } from '@/composables/usePlanSubmit'
const { handlePlanSubmit } = usePlanSubmit({
getPopupState: () => showPlanPopup.value,
setPopupState: (state) => { showPlanPopup.value = state },
clearSelectedProduct: () => { selectedProduct.value = null }
})
await handlePlanSubmit(result, {
beforeNav: async () => { console.log('导航前') },
afterNav: async () => { console.log('导航后') }
})
```
---
### useEventTracking.js
**功能**:事件埋点
**核心方法**
- `trackEvent(type, objectId, extraData)` - 追踪事件
- `trackFileRead(fileId, extraData)` - 追踪文件阅读
**使用示例**
```javascript
import { useEventTracking, EventType } from '@/composables/useEventTracking'
const { trackFileRead } = useEventTracking()
// 追踪文件阅读
await trackFileRead('file-id-123', {
title: '保险产品手册',
category: '产品资料'
})
```
---
### useListItemClick.js
**功能**:列表项点击处理(带权限检查)
**核心方法**
- `handleListItemClick(item, config)` - 处理列表项点击
**使用示例**
```javascript
import { useListItemClick } from '@/composables/useListItemClick'
const { handleListItemClick } = useListItemClick()
// 点击列表项(自动检查登录权限)
handleListItemClick(item, {
needLogin: true,
onClick: (item) => {
console.log('点击了', item)
}
})
```
---
### useFieldDependencies.js
**功能**:表单字段依赖管理
**核心方法**
- 当字段 A 变化时,自动更新字段 B
---
### useFieldValueTransform.js
**功能**:字段值转换
**核心方法**
- 将用户输入转换为后端需要的格式
---
### usePlanView.js
**功能**:计划书视图管理
**核心方法**
- 管理计划书的显示状态
---
## 🎯 最佳实践
### 命名规范
```javascript
// ✅ GOOD - 清晰的命名
usePermission()
useFileOperation()
useSectionList()
// ❌ BAD - 过于通用
useData()
useList()
```
### 抽取原则
**必须抽取 Composable**
- ✅ 业务逻辑在 ≥ 3 个组件中重复
- ✅ 相同的状态管理模式重复
- ✅ 复杂的异步操作模式重复
### 返回值规范
```javascript
// ✅ 返回响应式 refs 和函数
return {
// 响应式状态
users, // ref
loading, // ref
// 方法
fetchUsers,
refresh // 别名方法
}
```
---
## 📖 参考文档
- [Vue 3 Composables 官方文档](https://vuejs.org/guide/reusability/composables.html)
- [项目最佳实践](../../docs/best-practices.md)