docs(ui): 添加 components 和 composables 目录文档索引
- 新增 src/components/README.md:组件目录索引,包含导航、卡片、表单、列表、文档、计划书等模块的组件说明和使用示例 - 新增 src/composables/README.md:Composables 目录索引,包含权限检查、文件操作、列表管理、收藏、埋点等可复用逻辑的说明 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Showing
2 changed files
with
698 additions
and
0 deletions
src/components/README.md
0 → 100644
| 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) |
src/composables/README.md
0 → 100644
| 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) |
-
Please register or login to post a comment