hookehuyr

feat(components): 新增可复用的 SectionCard 和 SectionItem 组件

- 创建 SectionItem 组件用于展示单个项目,包含图标、标题、副标题和点击事件
- 创建 SectionCard 组件作为分组卡片容器,可配置标题、背景和项目列表
- 在 signing、onboarding 和 family-office 页面中使用新组件替换重复的 UI 代码
- 更新 components.d.ts 类型声明文件以包含新组件
......@@ -21,6 +21,8 @@ declare module 'vue' {
QrCodeSearch: typeof import('./src/components/qrCodeSearch.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
SectionCard: typeof import('./src/components/SectionCard.vue')['default']
SectionItem: typeof import('./src/components/SectionItem.vue')['default']
TabBar: typeof import('./src/components/TabBar.vue')['default']
}
}
......
<template>
<view class="bg-white rounded-[32rpx] mb-[32rpx] pb-[56rpx] overflow-hidden shadow-sm">
<!-- Section Header -->
<view v-if="title" class="px-[40rpx] py-[32rpx]" :style="{ background: bgGradient }">
<text class="text-[#1f2937] text-[32rpx] font-normal">{{ title }}</text>
</view>
<!-- Section Items -->
<view class="flex flex-col">
<view v-for="(item, index) in items" :key="index" class="flex flex-col">
<SectionItem
:icon="item.icon"
:title="item.title"
:subtitle="item.subtitle"
:route="item.route"
@click="handleItemClick(item)"
/>
<!-- Divider -->
<view v-if="index < items.length - 1" class="w-[626rpx] h-[2rpx] bg-[#e5e7eb] mx-auto mt-[32rpx]"></view>
</view>
</view>
</view>
</template>
<script setup>
import SectionItem from './SectionItem.vue'
/**
* Section Card Component
* @description 可复用的分组卡片组件,用于展示带标题的项目列表
*/
defineProps({
/**
* Section 标题
*/
title: {
type: String,
default: ''
},
/**
* Section 背景渐变色
* @example 'linear-gradient(90deg, #F3E8FF 0%, #E9D5FF 100%)'
*/
bgGradient: {
type: String,
default: ''
},
/**
* 项目列表
* @example [{ icon: 'Edit', title: '标题', subtitle: '副标题', route: '/page/path' }]
*/
items: {
type: Array,
required: true,
default: () => []
}
})
const emit = defineEmits(['item-click'])
/**
* 处理项目点击
* @param {Object} item - 点击的项目数据
*/
const handleItemClick = (item) => {
emit('item-click', item)
}
</script>
<script>
export default {
name: 'SectionCard'
}
</script>
<template>
<view class="flex items-center justify-between px-[40rpx] mt-[56rpx] cursor-pointer" @tap="handleClick">
<view class="flex items-center">
<view class="w-[96rpx] h-[96rpx] mr-[32rpx] flex items-center justify-center bg-gray-50 rounded-full">
<IconFont :name="icon" class="text-blue-600" size="32" />
</view>
<view class="flex flex-col">
<text class="text-[#1f2937] text-[28rpx] font-normal leading-[40rpx]">{{ title }}</text>
<text class="text-[#6b7280] text-[24rpx] mt-[8rpx] leading-[32rpx]">{{ subtitle }}</text>
</view>
</view>
<IconFont name="RectRight" class="text-gray-400" size="16" />
</view>
</template>
<script setup>
import IconFont from './IconFont.vue'
/**
* Section Item Component
* @description 单个展示项目组件,包含图标、标题、副标题和右侧箭头
*/
defineProps({
/**
* 图标名称(对应 IconFont 组件的 name)
* @example 'Edit', 'Check', 'Order'
*/
icon: {
type: String,
required: true
},
/**
* 标题
*/
title: {
type: String,
required: true
},
/**
* 副标题/描述
*/
subtitle: {
type: String,
default: ''
},
/**
* 路由路径(可选,用于跳转)
*/
route: {
type: String,
default: ''
}
})
const emit = defineEmits(['click'])
/**
* 处理点击事件
*/
const handleClick = () => {
emit('click')
}
</script>
<script>
export default {
name: 'SectionItem'
}
</script>
......@@ -5,35 +5,14 @@
<!-- Content List -->
<div class="px-[40rpx] mt-[40rpx] relative z-10">
<div v-for="(section, index) in sections" :key="index"
class="bg-white rounded-[32rpx] mb-[32rpx] pb-[56rpx] overflow-hidden shadow-sm">
<!-- Section Header -->
<div class="px-[40rpx] py-[32rpx]" :style="{ background: section.bgGradient }">
<span class="text-[#1f2937] text-[32rpx] font-normal">{{ section.title }}</span>
</div>
<!-- Section Items -->
<div class="flex flex-col">
<div v-for="(item, itemIndex) in section.items" :key="itemIndex" class="flex flex-col">
<div class="flex items-center justify-between px-[40rpx] mt-[56rpx] cursor-pointer"
@tap="handleItemClick(item)">
<div class="flex items-center">
<div class="w-[96rpx] h-[96rpx] mr-[32rpx] flex items-center justify-center bg-gray-50 rounded-full">
<IconFont :name="item.icon" class="text-blue-600" size="32" />
</div>
<div class="flex flex-col">
<span class="text-[#1f2937] text-[28rpx] font-normal leading-[40rpx]">{{ item.title }}</span>
<span class="text-[#6b7280] text-[24rpx] mt-[8rpx] leading-[32rpx]">{{ item.subtitle }}</span>
</div>
</div>
<IconFont name="RectRight" class="text-gray-400" size="16" />
</div>
<!-- Divider -->
<div v-if="itemIndex < section.items.length - 1"
class="w-[626rpx] h-[2rpx] bg-[#e5e7eb] mx-auto mt-[32rpx]"></div>
</div>
</div>
</div>
<SectionCard
v-for="(section, index) in sections"
:key="index"
:title="section.title"
:bg-gradient="section.bgGradient"
:items="section.items"
@item-click="handleItemClick"
/>
</div>
<!-- Tab Bar -->
......@@ -46,7 +25,7 @@ import { shallowRef } from 'vue'
import { useGo } from '@/hooks/useGo'
import TabBar from '@/components/TabBar.vue'
import NavHeader from '@/components/NavHeader.vue'
import IconFont from '@/components/IconFont.vue'
import SectionCard from '@/components/SectionCard.vue'
const go = useGo()
......
......@@ -5,32 +5,14 @@
<!-- Content List -->
<div class="px-[40rpx] mt-[40rpx] relative z-10">
<div v-for="(section, index) in sections" :key="index" class="bg-white rounded-[32rpx] mb-[32rpx] pb-[56rpx] overflow-hidden shadow-sm">
<!-- Section Header -->
<div class="px-[40rpx] py-[32rpx]" :style="{ background: section.bgGradient }">
<span class="text-[#1f2937] text-[32rpx] font-normal">{{ section.title }}</span>
</div>
<!-- Section Items -->
<div class="flex flex-col">
<div v-for="(item, itemIndex) in section.items" :key="itemIndex" class="flex flex-col">
<div class="flex items-center justify-between px-[40rpx] mt-[56rpx] cursor-pointer" @tap="handleItemClick(item)">
<div class="flex items-center">
<div class="w-[96rpx] h-[96rpx] mr-[32rpx] flex items-center justify-center bg-gray-50 rounded-full">
<IconFont :name="item.icon" class="text-blue-600" size="32" />
</div>
<div class="flex flex-col">
<span class="text-[#1f2937] text-[28rpx] font-normal leading-[40rpx]">{{ item.title }}</span>
<span class="text-[#6b7280] text-[24rpx] mt-[8rpx] leading-[32rpx]">{{ item.subtitle }}</span>
</div>
</div>
<IconFont name="RectRight" class="text-gray-400" size="16" />
</div>
<!-- Divider -->
<div v-if="itemIndex < section.items.length - 1" class="w-[626rpx] h-[2rpx] bg-[#e5e7eb] mx-auto mt-[32rpx]"></div>
</div>
</div>
</div>
<SectionCard
v-for="(section, index) in sections"
:key="index"
:title="section.title"
:bg-gradient="section.bgGradient"
:items="section.items"
@item-click="handleItemClick"
/>
</div>
<!-- Tab Bar -->
......@@ -43,7 +25,7 @@ import { shallowRef } from 'vue'
import { useGo } from '@/hooks/useGo'
import TabBar from '@/components/TabBar.vue'
import NavHeader from '@/components/NavHeader.vue'
import IconFont from '@/components/IconFont.vue'
import SectionCard from '@/components/SectionCard.vue'
const go = useGo()
......
......@@ -5,32 +5,14 @@
<!-- Content List -->
<div class="px-[40rpx] mt-[40rpx] relative z-10">
<div v-for="(section, index) in sections" :key="index" class="bg-white rounded-[32rpx] mb-[32rpx] pb-[56rpx] overflow-hidden shadow-sm">
<!-- Section Header -->
<div class="px-[40rpx] py-[32rpx]" :style="{ background: section.bgGradient }">
<span class="text-[#1f2937] text-[32rpx] font-normal">{{ section.title }}</span>
</div>
<!-- Section Items -->
<div class="flex flex-col">
<div v-for="(item, itemIndex) in section.items" :key="itemIndex" class="flex flex-col">
<div class="flex items-center justify-between px-[40rpx] mt-[56rpx] cursor-pointer" @tap="handleItemClick(item)">
<div class="flex items-center">
<div class="w-[96rpx] h-[96rpx] mr-[32rpx] flex items-center justify-center bg-gray-50 rounded-full">
<IconFont :name="item.icon" class="text-blue-600" size="32" />
</div>
<div class="flex flex-col">
<span class="text-[#1f2937] text-[28rpx] font-normal leading-[40rpx]">{{ item.title }}</span>
<span class="text-[#6b7280] text-[24rpx] mt-[8rpx] leading-[32rpx]">{{ item.subtitle }}</span>
</div>
</div>
<IconFont name="RectRight" class="text-gray-400" size="16" />
</div>
<!-- Divider -->
<div v-if="itemIndex < section.items.length - 1" class="w-[626rpx] h-[2rpx] bg-[#e5e7eb] mx-auto mt-[32rpx]"></div>
</div>
</div>
</div>
<SectionCard
v-for="(section, index) in sections"
:key="index"
:title="section.title"
:bg-gradient="section.bgGradient"
:items="section.items"
@item-click="handleItemClick"
/>
</div>
<!-- Tab Bar -->
......@@ -43,7 +25,7 @@ import { shallowRef } from 'vue'
import { useGo } from '@/hooks/useGo'
import TabBar from '@/components/TabBar.vue'
import NavHeader from '@/components/NavHeader.vue'
import IconFont from '@/components/IconFont.vue'
import SectionCard from '@/components/SectionCard.vue'
const go = useGo()
......