README.md 13.9 KB

Components 目录

本项目组件使用 Vue 3 Composition API + <script setup> 编写,按功能模块组织。


📚 目录结构

components/
├── cards/              # 卡片组件
├── documents/          # 文档预览组件
├── examples/           # 示例组件
├── forms/              # 表单组件
├── icons/              # 图标组件
├── list/               # 列表组件
├── navigation/         # 导航组件
├── plan/               # 计划书组件
└── RichTextRenderer.vue  # 富文本渲染组件

📦 组件索引

导航组件 (navigation/)

组件 功能 Props
NavHeader 页面顶部导航栏 title, showBack, preventDefaultBack
TabBar 底部标签导航栏 current, tabs

使用示例

<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 资料/文档卡片 title, icon, learners, docType, collected
ProductCard 产品卡片 name, type, tags, collected
ArticleCard 文章卡片 id, title, excerpt, coverUrl, date, learners, readPeoplePercent, collected, showCover

使用示例

<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 搜索栏 modelValue, placeholder, disabled
FilterTabs 筛选标签 options, modelValue

使用示例

<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 分组卡片容器 title
SectionItem 分组列表项 title, subtitle, icon
LoadMoreList 加载更多列表 items, hasMore, loading
ListItemActions 列表项操作按钮 viewable, collectable, deletable

使用示例

<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 PDF 预览 url
OfficeViewer Office 文档查看器 url
DocumentPreview 通用文档预览 file

富文本组件

组件 功能 Props
RichTextRenderer 富文本渲染 content, enableTransform

使用示例

<template>
  <RichTextRenderer
    :content="htmlContent"
    :enable-transform="true"
    @image-preview="handlePreview"
    @file-click="handleFileClick"
  />
</template>

<script setup>
import RichTextRenderer from '@/components/RichTextRenderer.vue'

const htmlContent = '<p>HTML 内容</p>'
</script>

计划书组件 (plan/)

弹窗容器

组件 功能
PlanPopupNew 计划书弹窗容器
PlanFormContainer 计划书表单容器

表单字段 (PlanFields/)

组件 功能
NameInput 姓名输入
AgePickerGlobal 年龄选择器
DatePickerGlobal 日期选择器
SelectPickerGlobal 选项选择器
AmountKeyboard 金额键盘
PaymentPeriodRadio 缴费年期单选
PeriodInput 提取期自定义输入
RadioGroup 单选分组

模板 (PlanTemplates/)

组件 功能
SavingsTemplate 储蓄险模板
LifeInsuranceTemplate 寿险模板
CriticalIllnessTemplate 重疾险模板

图标组件 (icons/)

组件 功能 Props
IconFont IconFont 图标 name, size, color

使用示例

<template>
  <IconFont name="home" :size="28" color="#3B82F6" />
  <IconFont name="search" />
</template>

<script setup>
import IconFont from '@/components/icons/IconFont.vue'
</script>

🎯 组件使用规范

Props 定义

<script setup>
// ✅ GOOD - 有类型和默认值
const props = defineProps({
  title: {
    type: String,
    required: true
  },
  count: {
    type: Number,
    default: 0
  }
})

// ❌ BAD - 缺少类型
const props = defineProps(['title', 'count'])
</script>

Emits 定义

<script setup>
// ✅ GOOD - 明确定义事件
const emit = defineEmits(['update:modelValue', 'change', 'submit'])

// 使用
emit('update:modelValue', newValue)
emit('change', newValue)
</script>

样式规范

<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 | 点击返回按钮时触发 |

使用示例

<NavHeader
  title="资料详情"
  :show-back="true"
  :prevent-default-back="true"
  @back="handleCustomBack"
/>

TabBar.vue

底部标签导航栏,支持红点提醒。

Props: | Prop | 类型 | 默认值 | 说明 | |------|------|--------|------| | current | String | 'home' | 当前激活的标签 |

红点自动同步

  • 组件自动从 userStore.tabBarBadges 读取红点状态
  • 无需手动传入 badges prop

使用示例

<TabBar current="home" />

ArticleCard.vue

文章卡片,展示文章标题、简介、封面图、日期和操作按钮。

Props: | Prop | 类型 | 默认值 | 说明 | |------|------|--------|------| | id | Number/String | - | 文章 ID | | title | String | - | 文章标题 | | excerpt | String | '' | 文章简介 | | coverUrl | String | '' | 封面图 URL | | date | String | '' | 发布日期(格式:YYYY-MM-DD HH:mm:ss) | | learners | String/Number | '' | 学习人数 | | readPeoplePercent | Number | null | 学习人数百分比(热度) | | collected | Boolean | false | 是否已收藏 | | showCover | Boolean | false | 是否显示封面图 |

Events: | Event | 说明 | 参数 | |-------|------|------| | viewed | 查看文章时触发 | { id } | | collectChanged | 收藏状态改变时触发 | { id, title, excerpt, coverUrl, date, collected } |

使用示例

<template>
  <ArticleCard
    :id="article.id"
    :title="article.title"
    :excerpt="article.excerpt"
    :cover-url="article.coverUrl"
    :date="article.createdAt"
    :learners="article.learners"
    :read-people-percent="article.readPeoplePercent"
    :collected="article.collected"
    :show-cover="true"
    @viewed="handleViewed"
    @collect-changed="handleCollectChanged"
  />
</template>

<script setup>
import ArticleCard from '@/components/cards/ArticleCard.vue'
</script>

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 | 点击搜索或回车 |

使用示例

<SearchBar
  v-model="searchText"
  placeholder="搜索资料"
  @search="handleSearch"
/>

IconFont.vue

IconFont 图标组件。

Props: | Prop | 类型 | 默认值 | 说明 | |------|------|--------|------| | name | String | - | 图标名称 | | size | Number/String | 20 | 图标大小 | | color | String | '#333' | 图标颜色 |

使用示例

<IconFont name="home" :size="28" color="#3B82F6" />

RichTextRenderer.vue

富文本渲染组件,基于 Taro v-html 实现。

Props: | Prop | 类型 | 默认值 | 说明 | |------|------|--------|------| | content | String | '' | HTML 内容字符串 | | enableTransform | Boolean | true | 是否启用图片自动处理 |

Events: | Event | 说明 | 参数 | |-------|------|------| | image-preview | 图片预览时触发 | { src } | | file-click | 文件链接点击时触发 | { url, fileName } |

功能特性

  • HTML 实体自动解码
  • <a> 标签自动替换为 <div data-href="">
  • 图片长按预览
  • 文件链接点击处理
  • 自动处理图片样式(widthFix, max-width: 100%)

使用示例

<template>
  <RichTextRenderer
    :content="article.content"
    :enable-transform="true"
    @image-preview="handlePreview"
    @file-click="handleFileClick"
  />
</template>

<script setup>
import RichTextRenderer from '@/components/RichTextRenderer.vue'

const handlePreview = ({ src }) => {
  console.log('预览图片:', src)
}

const handleFileClick = ({ url, fileName }) => {
  console.log('打开文件:', fileName, url)
}
</script>

PeriodInput.vue

提取期自定义输入组件,支持整数年期和快捷选项(终身、一笔过)。

Props: | Prop | 类型 | 默认值 | 说明 | |------|------|--------|------| | visible | Boolean | false | 弹窗显示状态(v-model:visible) | | modelValue | String | '' | 绑定的值(v-model) | | label | String | '' | 标签文本 | | required | Boolean | false | 是否必填 | | placeholder | String | '请选择或输入提取期' | 占位符文本 | | inputLabel | String | '请输入提取期' | 弹窗内输入提示文本 | | inputPlaceholder | String | '请输入年数' | 输入框占位符 | | validationRules | Object | { min: 1, max: 100, ... } | 验证规则 | | hideTrigger | Boolean | false | 是否隐藏触发区域 |

Events: | Event | 说明 | 参数 | |-------|------|------| | update:visible | 更新弹窗显示状态 | boolean | | update:modelValue | 更新绑定值 | string | | confirm | 确认输入 | string(确认的提取期值) | | cancel | 取消输入 | - |

功能特性

  • 整数年期约束(1-100年)
  • 快捷选项:终身、一笔过
  • 实时验证输入格式
  • 可扩展的验证器接口
  • 支持全局弹窗管理

使用示例

<template>
  <PeriodInput
    v-model:visible="showPeriodInput"
    v-model="periodValue"
    label="提取期"
    input-label="请输入提取期"
    :validation-rules="{ min: 1, max: 100 }"
    @confirm="handlePeriodConfirm"
    @cancel="handleCancel"
  />
</template>

<script setup>
import { ref } from 'vue'
import PeriodInput from '@/components/plan/PlanFields/PeriodInput.vue'

const showPeriodInput = ref(false)
const periodValue = ref('')

const handlePeriodConfirm = (value) => {
  console.log('确认提取期:', value)
}

const handleCancel = () => {
  console.log('取消输入')
}
</script>

📖 最佳实践

组件抽取原则

必须抽取为组件

  • ✅ UI 在 3 个及以上页面重复
  • ✅ 复杂的业务逻辑需要封装
  • ✅ 可复用的表单控件

组件命名规范

// ✅ GOOD - 多词组合,PascalCase
MaterialCard.vue
SearchBar.vue
AgePickerGlobal.vue

// ❌ BAD - 单词,不清晰
Card.vue
Input.vue
Picker.vue

Props vs Emits

<script setup>
// ✅ GOOD - 数据向下传递
const props = defineProps({
  modelValue: String
})

// ✅ GOOD - 事件向上传递
const emit = defineEmits(['update:modelValue', 'change'])

// 使用
emit('update:modelValue', newValue)
</script>

🔗 相关文档