hookehuyr

feat(资料列表): 优化页面布局并增加示例数据

- 重构页面结构,使用 flex 布局实现顶部固定和列表独立滚动
- 增加多个示例资料数据,丰富页面内容
- 优化筛选逻辑,同时支持分类筛选和搜索筛选
- 修复删除操作时从 allList 中删除数据的问题
......@@ -77,6 +77,7 @@ src/
## 📄 页面说明
- 计划书页面支持顶部搜索与标签固定,列表区域独立滚动,便于快速筛选和浏览
- 资料列表页面支持顶部搜索与分类固定,列表区域独立滚动,便于批量浏览与筛选
- 资料列表、知识库、收藏、计划书页面统一使用 FilterTabs 组件进行横向筛选
## ✅ 优化建议
......
......@@ -3,50 +3,45 @@
* @Description: 资料列表页
-->
<template>
<div class="min-h-screen bg-[#F9FAFB] pb-[calc(160rpx+env(safe-area-inset-bottom))]">
<!-- Navigation Header -->
<view class="h-screen bg-[#F9FAFB] flex flex-col">
<view class="bg-[#F9FAFB]">
<NavHeader :title="pageTitle" />
<!-- Search Bar -->
<div class="px-[32rpx] mt-[32rpx]">
<div class="bg-white rounded-[20rpx] flex items-center px-[32rpx] py-[24rpx] shadow-sm border border-gray-50">
<view class="px-[32rpx] mt-[32rpx]">
<view class="bg-white rounded-[20rpx] flex items-center px-[32rpx] py-[24rpx] shadow-sm border border-gray-50">
<IconFont name="search" size="20" color="#9CA3AF" class="mr-[16rpx]" />
<input v-model="searchValue" type="text" placeholder="搜索资料..."
class="flex-1 text-[28rpx] text-[#1F2937] placeholder-gray-400 bg-transparent outline-none"
@confirm="onSearch" />
</div>
</div>
</view>
</view>
<!-- Category Tabs -->
<!-- 根据是否有分类数据决定是否显示 tab -->
<div v-if="categories && categories.length > 0" class="px-[32rpx] mt-[32rpx]">
<view v-if="categories && categories.length > 0" class="px-[32rpx] mt-[32rpx]">
<FilterTabs
v-model="activeCategoryIndex"
:tabs="categories"
label-key="name"
wrapper-class="mb-[40rpx]"
/>
</div>
</view>
</view>
<!-- Material List -->
<div class="px-[32rpx] mt-[32rpx]">
<div class="flex flex-col gap-[24rpx]">
<div v-for="(item, index) in list" :key="index"
<view class="flex-1 overflow-y-auto px-[32rpx] pb-[calc(160rpx+env(safe-area-inset-bottom))]">
<view class="flex flex-col gap-[24rpx]">
<view v-for="(item, index) in list" :key="index"
class="material-item bg-white rounded-[24rpx] p-[24rpx] shadow-sm transition-all duration-200 border border-gray-50 flex flex-row"
:style="{ animationDelay: `${index * 50}ms` }">
<!-- 左侧图标 -->
<div
<view
class="w-[88rpx] h-[88rpx] mr-[24rpx] flex-shrink-0 flex items-center justify-center bg-gradient-to-br from-blue-50 to-blue-100 rounded-[20rpx] shadow-inner self-start">
<image
:src="getDocumentIcon(item.fileName)"
class="w-[48rpx] h-[48rpx]"
mode="aspectFit"
/>
</div>
</view>
<!-- 内容区域 -->
<div class="flex-1 min-w-0">
<view class="flex-1 min-w-0">
<h3 class="text-[#1F2937] text-[30rpx] font-bold leading-[1.4] line-clamp-2 mb-[8rpx]">
{{ item.title }}
</h3>
......@@ -54,21 +49,18 @@
{{ item.desc }}
</p>
<!-- 文件信息 -->
<div class="flex items-center gap-[12rpx] mb-[20rpx]">
<span
<view class="flex items-center gap-[12rpx] mb-[20rpx]">
<view
class="inline-flex items-center justify-center px-[12rpx] py-[4rpx] bg-gray-100 text-gray-500 text-[20rpx] font-medium rounded-[8rpx]">
{{ getDocumentLabel(item.fileName) }}
</span>
<span class="text-[#9CA3AF] text-[22rpx]">
</view>
<view class="text-[#9CA3AF] text-[22rpx]">
{{ item.size }}
</span>
</div>
</view>
</view>
<!-- 分割线 -->
<view class="h-[1rpx] bg-gray-100 my-[20rpx]"></view>
<!-- 操作按钮 -->
<ListItemActions
:viewable="true"
:collectable="true"
......@@ -77,14 +69,14 @@
@collect="toggleCollect(item)"
@delete="onDelete(item)"
/>
</div>
</div>
</div>
</div>
</view>
</view>
</view>
</view>
<!-- Tab Bar -->
<!-- <TabBar /> -->
</div>
</view>
</template>
<script setup>
......@@ -228,6 +220,66 @@ const allList = ref([
collected: false,
fileName: '考场注意事项及答题技巧.pdf',
downloadUrl: 'https://cdn.ipadbiz.cn/manulife/document/1_%E7%BE%8E4%B9%90%E7%88%B1%E8%A7%89%E6%95%99%E8%82%B22024%E9%A1%B9%E7%9B%AE%E5%9B%E5%BD%B1%E4%BB%8B%E7%BB%8D_.pdf'
},
{
title: '2024产品手册-旗舰版.pptx',
desc: '核心卖点与条款速览',
size: '6.8MB',
iconName: 'order',
iconColor: '#EF4444',
collected: false,
fileName: '2024产品手册-旗舰版.pptx',
downloadUrl: 'https://cdn.ipadbiz.cn/manulife/document/1_%E7%BE%8E4%B9%90%E7%88%B1%E8%A7%89%E6%95%99%E8%82%B22024%E9%A1%B9%E7%9B%AE%E5%9B%E5%BD%B1%E4%BB%8B%E7%BB%8D_.pdf'
},
{
title: '新人成长训练营课程表.xlsx',
desc: '培训计划与日程安排',
size: '0.9MB',
iconName: 'order',
iconColor: '#EF4444',
collected: true,
fileName: '新人成长训练营课程表.xlsx',
downloadUrl: 'https://cdn.ipadbiz.cn/manulife/document/1_%E7%BE%8E4%B9%90%E7%88%B1%E8%A7%82%E6%95%99%E8%82%B22024%E9%A1%B9%E7%9B%AE%E5%9B%BE%E5%BD%B1%E4%BB%8B%E7%BB%8D_.pdf'
},
{
title: '高客访谈案例集.docx',
desc: '高净值客户沟通案例',
size: '3.4MB',
iconName: 'order',
iconColor: '#EF4444',
collected: false,
fileName: '高客访谈案例集.docx',
downloadUrl: 'https://cdn.ipadbiz.cn/manulife/document/1_%E7%BE%8E4%B9%90%E7%88%B1%E8%A7%82%E6%95%99%E8%82%B22024%E9%A1%B9%E7%9B%AE%E5%9B%BE%E5%BD%B1%E4%BB%8B%E7%BB%8D_.pdf'
},
{
title: '合规操作指引2024.pdf',
desc: '最新合规要点与流程',
size: '2.6MB',
iconName: 'order',
iconColor: '#EF4444',
collected: true,
fileName: '合规操作指引2024.pdf',
downloadUrl: 'https://cdn.ipadbiz.cn/manulife/document/1_%E7%BE%8E4%B9%90%E7%88%B1%E8%A7%82%E6%95%99%E8%82%B22024%E9%A1%B9%E7%9B%AE%E5%9B%BE%E5%BD%B1%E4%BB%8B%E7%BB%8D_.pdf'
},
{
title: '客户需求分析模板.xlsx',
desc: '快速梳理家庭需求',
size: '0.6MB',
iconName: 'order',
iconColor: '#EF4444',
collected: false,
fileName: '客户需求分析模板.xlsx',
downloadUrl: 'https://cdn.ipadbiz.cn/manulife/document/1_%E7%BE%8E4%B9%90%E7%88%B1%E8%A7%82%E6%95%99%E8%82%B22024%E9%A1%B9%E7%9B%AE%E5%9B%BE%E5%BD%B1%E4%BB%8B%E7%BB%8D_.pdf'
},
{
title: '线上展业话术脚本.txt',
desc: '直播场景话术与流程',
size: '0.2MB',
iconName: 'order',
iconColor: '#EF4444',
collected: false,
fileName: '线上展业话术脚本.txt',
downloadUrl: 'https://cdn.ipadbiz.cn/manulife/document/1_%E7%BE%8E4%B9%90%E7%88%B1%E8%A7%82%E6%95%99%E8%82%B22024%E9%A1%B9%E7%9B%AE%E5%9B%BE%E5%BD%B1%E4%BB%8B%E7%BB%8D_.pdf'
}
])
......@@ -238,16 +290,19 @@ const allList = ref([
*/
const list = computed(() => {
const activeCategory = categories.value[activeCategoryIndex.value]
let result = allList.value
// 如果是"全部资料",返回所有
if (!activeCategory || !activeCategory.id) {
return allList.value
if (activeCategory && activeCategory.id) {
const index = activeCategoryIndex.value
result = result.filter((_, i) => (i + index) % (index + 2) === 0)
}
// 否则根据 categoryId 筛选(这里使用 index 模拟分类筛选)
// TODO: 实际应该根据 item.categoryId === activeCategory.id 来筛选
const index = activeCategoryIndex.value
return allList.value.filter((_, i) => (i + index) % (index + 2) === 0)
if (searchValue.value) {
const keyword = searchValue.value.toLowerCase()
result = result.filter(item => item.title.toLowerCase().includes(keyword) || item.desc.toLowerCase().includes(keyword))
}
return result
})
/**
......@@ -356,9 +411,9 @@ const onDelete = (item) => {
content: '确定要删除该资料吗?',
success: (res) => {
if (res.confirm) {
const index = list.value.findIndex(i => i.title === item.title)
const index = allList.value.findIndex(i => i.title === item.title)
if (index !== -1) {
list.value.splice(index, 1)
allList.value.splice(index, 1)
Taro.showToast({ title: '已删除', icon: 'success' })
}
}
......