feat(list): 新增资料列表操作组件并统一页面样式
- 新增 ListItemActions 组件,支持查看、收藏、删除操作 - 修复资料列表页布局问题(图标和文字水平排列) - 统一首页和资料列表页的资料项样式和交互 - 更新收藏页、知识库页、计划页使用新组件 - 添加完整的组件文档和类型定义 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Showing
8 changed files
with
298 additions
and
167 deletions
| ... | @@ -10,14 +10,10 @@ declare module 'vue' { | ... | @@ -10,14 +10,10 @@ declare module 'vue' { |
| 10 | DocumentPreview: typeof import('./src/components/DocumentPreview/index.vue')['default'] | 10 | DocumentPreview: typeof import('./src/components/DocumentPreview/index.vue')['default'] |
| 11 | IconFont: typeof import('./src/components/IconFont.vue')['default'] | 11 | IconFont: typeof import('./src/components/IconFont.vue')['default'] |
| 12 | IndexNav: typeof import('./src/components/indexNav.vue')['default'] | 12 | IndexNav: typeof import('./src/components/indexNav.vue')['default'] |
| 13 | + ListItemActions: typeof import('./src/components/ListItemActions/index.vue')['default'] | ||
| 13 | NavHeader: typeof import('./src/components/NavHeader.vue')['default'] | 14 | NavHeader: typeof import('./src/components/NavHeader.vue')['default'] |
| 14 | NutAvatar: typeof import('@nutui/nutui-taro')['Avatar'] | 15 | NutAvatar: typeof import('@nutui/nutui-taro')['Avatar'] |
| 15 | NutButton: typeof import('@nutui/nutui-taro')['Button'] | 16 | NutButton: typeof import('@nutui/nutui-taro')['Button'] |
| 16 | - NutCheckbox: typeof import('@nutui/nutui-taro')['Checkbox'] | ||
| 17 | - NutCheckboxGroup: typeof import('@nutui/nutui-taro')['CheckboxGroup'] | ||
| 18 | - NutForm: typeof import('@nutui/nutui-taro')['Form'] | ||
| 19 | - NutFormItem: typeof import('@nutui/nutui-taro')['FormItem'] | ||
| 20 | - NutIcon: typeof import('@nutui/nutui-taro')['Icon'] | ||
| 21 | NutInput: typeof import('@nutui/nutui-taro')['Input'] | 17 | NutInput: typeof import('@nutui/nutui-taro')['Input'] |
| 22 | NutPicker: typeof import('@nutui/nutui-taro')['Picker'] | 18 | NutPicker: typeof import('@nutui/nutui-taro')['Picker'] |
| 23 | NutPopup: typeof import('@nutui/nutui-taro')['Popup'] | 19 | NutPopup: typeof import('@nutui/nutui-taro')['Popup'] |
| ... | @@ -26,7 +22,6 @@ declare module 'vue' { | ... | @@ -26,7 +22,6 @@ declare module 'vue' { |
| 26 | NutSearchbar: typeof import('@nutui/nutui-taro')['Searchbar'] | 22 | NutSearchbar: typeof import('@nutui/nutui-taro')['Searchbar'] |
| 27 | NutTabPane: typeof import('@nutui/nutui-taro')['TabPane'] | 23 | NutTabPane: typeof import('@nutui/nutui-taro')['TabPane'] |
| 28 | NutTabs: typeof import('@nutui/nutui-taro')['Tabs'] | 24 | NutTabs: typeof import('@nutui/nutui-taro')['Tabs'] |
| 29 | - NutTextarea: typeof import('@nutui/nutui-taro')['Textarea'] | ||
| 30 | NutUploader: typeof import('@nutui/nutui-taro')['Uploader'] | 25 | NutUploader: typeof import('@nutui/nutui-taro')['Uploader'] |
| 31 | OfficeViewer: typeof import('./src/components/OfficeViewer.vue')['default'] | 26 | OfficeViewer: typeof import('./src/components/OfficeViewer.vue')['default'] |
| 32 | PdfPreview: typeof import('./src/components/PdfPreview.vue')['default'] | 27 | PdfPreview: typeof import('./src/components/PdfPreview.vue')['default'] | ... | ... |
src/components/ListItemActions/index.vue
0 → 100644
| 1 | +<!-- | ||
| 2 | + 列表项操作按钮组件 | ||
| 3 | + | ||
| 4 | + @description 统一的列表项操作按钮组件,支持查看、收藏、删除三种操作 | ||
| 5 | + @example | ||
| 6 | + <ListItemActions | ||
| 7 | + :viewable="true" | ||
| 8 | + :collectable="true" | ||
| 9 | + :deletable="true" | ||
| 10 | + :collected="item.collected" | ||
| 11 | + @view="onView(item)" | ||
| 12 | + @collect="onCollect(item)" | ||
| 13 | + @delete="onDelete(item)" | ||
| 14 | + /> | ||
| 15 | +--> | ||
| 16 | +<template> | ||
| 17 | + <view class="flex justify-end gap-[24rpx]"> | ||
| 18 | + <!-- 查看按钮 --> | ||
| 19 | + <view v-if="viewable" class="flex items-center text-blue-600" @tap="handleView"> | ||
| 20 | + <IconFont name="eye" size="14" class="mr-[8rpx]" /> | ||
| 21 | + <text class="text-[24rpx]">查看</text> | ||
| 22 | + </view> | ||
| 23 | + | ||
| 24 | + <!-- 收藏按钮 --> | ||
| 25 | + <view v-if="collectable" class="flex items-center" :class="isCollected ? 'text-red-500' : 'text-gray-400'" @tap="handleCollect"> | ||
| 26 | + <IconFont :name="isCollected ? 'heart-fill' : 'heart'" size="14" class="mr-[8rpx]" /> | ||
| 27 | + <text class="text-[24rpx]">{{ isCollected ? '已收藏' : '收藏' }}</text> | ||
| 28 | + </view> | ||
| 29 | + | ||
| 30 | + <!-- 删除按钮 --> | ||
| 31 | + <view v-if="deletable" class="flex items-center text-red-500" @tap="handleDelete"> | ||
| 32 | + <IconFont name="del" size="14" class="mr-[8rpx]" /> | ||
| 33 | + <text class="text-[24rpx]">删除</text> | ||
| 34 | + </view> | ||
| 35 | + </view> | ||
| 36 | +</template> | ||
| 37 | + | ||
| 38 | +<script setup> | ||
| 39 | +import { computed } from 'vue' | ||
| 40 | +import IconFont from '@/components/IconFont.vue' | ||
| 41 | + | ||
| 42 | +/** | ||
| 43 | + * 组件属性 | ||
| 44 | + */ | ||
| 45 | +const props = defineProps({ | ||
| 46 | + /** | ||
| 47 | + * 是否显示查看按钮 | ||
| 48 | + * @type {boolean} | ||
| 49 | + * @default true | ||
| 50 | + */ | ||
| 51 | + viewable: { | ||
| 52 | + type: Boolean, | ||
| 53 | + default: true | ||
| 54 | + }, | ||
| 55 | + /** | ||
| 56 | + * 是否显示收藏按钮 | ||
| 57 | + * @type {boolean} | ||
| 58 | + * @default false | ||
| 59 | + */ | ||
| 60 | + collectable: { | ||
| 61 | + type: Boolean, | ||
| 62 | + default: false | ||
| 63 | + }, | ||
| 64 | + /** | ||
| 65 | + * 是否显示删除按钮 | ||
| 66 | + * @type {boolean} | ||
| 67 | + * @default false | ||
| 68 | + */ | ||
| 69 | + deletable: { | ||
| 70 | + type: Boolean, | ||
| 71 | + default: false | ||
| 72 | + }, | ||
| 73 | + /** | ||
| 74 | + * 是否已收藏 | ||
| 75 | + * @type {boolean} | ||
| 76 | + * @default false | ||
| 77 | + */ | ||
| 78 | + collected: { | ||
| 79 | + type: Boolean, | ||
| 80 | + default: false | ||
| 81 | + } | ||
| 82 | +}) | ||
| 83 | + | ||
| 84 | +/** | ||
| 85 | + * 组件事件 | ||
| 86 | + */ | ||
| 87 | +const emit = defineEmits({ | ||
| 88 | + /** | ||
| 89 | + * 点击查看按钮时触发 | ||
| 90 | + */ | ||
| 91 | + view: null, | ||
| 92 | + /** | ||
| 93 | + * 点击收藏按钮时触发 | ||
| 94 | + */ | ||
| 95 | + collect: null, | ||
| 96 | + /** | ||
| 97 | + * 点击删除按钮时触发 | ||
| 98 | + */ | ||
| 99 | + delete: null | ||
| 100 | +}) | ||
| 101 | + | ||
| 102 | +const isCollected = computed(() => props.collected) | ||
| 103 | + | ||
| 104 | +/** | ||
| 105 | + * 处理查看点击 | ||
| 106 | + */ | ||
| 107 | +const handleView = () => { | ||
| 108 | + emit('view') | ||
| 109 | +} | ||
| 110 | + | ||
| 111 | +/** | ||
| 112 | + * 处理收藏点击 | ||
| 113 | + */ | ||
| 114 | +const handleCollect = () => { | ||
| 115 | + emit('collect') | ||
| 116 | +} | ||
| 117 | + | ||
| 118 | +/** | ||
| 119 | + * 处理删除点击 | ||
| 120 | + */ | ||
| 121 | +const handleDelete = () => { | ||
| 122 | + emit('delete') | ||
| 123 | +} | ||
| 124 | +</script> | ||
| 125 | + | ||
| 126 | +<style lang="less" scoped> | ||
| 127 | +</style> |
| ... | @@ -43,16 +43,12 @@ | ... | @@ -43,16 +43,12 @@ |
| 43 | <view class="h-[1rpx] bg-gray-100 mb-[20rpx]"></view> | 43 | <view class="h-[1rpx] bg-gray-100 mb-[20rpx]"></view> |
| 44 | 44 | ||
| 45 | <!-- Actions --> | 45 | <!-- Actions --> |
| 46 | - <view class="flex justify-end gap-[24rpx]"> | 46 | + <ListItemActions |
| 47 | - <view class="flex items-center text-blue-600" @tap="viewFile({...item, fileName: item.title})"> | 47 | + :viewable="true" |
| 48 | - <IconFont name="eye" size="14" class="mr-[8rpx]" /> | 48 | + :deletable="true" |
| 49 | - <text class="text-[24rpx]">查看</text> | 49 | + @view="viewFile({...item, fileName: item.title})" |
| 50 | - </view> | 50 | + @delete="onDelete(item)" |
| 51 | - <view class="flex items-center text-red-500" @tap="onDelete(item)"> | 51 | + /> |
| 52 | - <IconFont name="del" size="14" class="mr-[8rpx]" /> | ||
| 53 | - <text class="text-[24rpx]">删除</text> | ||
| 54 | - </view> | ||
| 55 | - </view> | ||
| 56 | </view> | 52 | </view> |
| 57 | 53 | ||
| 58 | <!-- Empty State --> | 54 | <!-- Empty State --> |
| ... | @@ -77,6 +73,7 @@ import { getDocumentIcon } from '@/utils/documentIcons' | ... | @@ -77,6 +73,7 @@ import { getDocumentIcon } from '@/utils/documentIcons' |
| 77 | import IconFont from '@/components/IconFont.vue' | 73 | import IconFont from '@/components/IconFont.vue' |
| 78 | import TabBar from '@/components/TabBar.vue' | 74 | import TabBar from '@/components/TabBar.vue' |
| 79 | import NavHeader from '@/components/NavHeader.vue' | 75 | import NavHeader from '@/components/NavHeader.vue' |
| 76 | +import ListItemActions from '@/components/ListItemActions/index.vue' | ||
| 80 | 77 | ||
| 81 | const go = useGo() | 78 | const go = useGo() |
| 82 | const { viewFile } = useFileOperation() | 79 | const { viewFile } = useFileOperation() | ... | ... |
| ... | @@ -126,61 +126,40 @@ | ... | @@ -126,61 +126,40 @@ |
| 126 | </view> | 126 | </view> |
| 127 | 127 | ||
| 128 | <!-- Material List --> | 128 | <!-- Material List --> |
| 129 | - <view class="flex flex-col gap-[32rpx]"> | 129 | + <view class="flex flex-col gap-[24rpx]"> |
| 130 | - <!-- Item 1 --> | 130 | + <!-- Material Items --> |
| 131 | - <view class="flex gap-[24rpx]" @tap="handleMaterialClick(hotMaterials[0])"> | 131 | + <view v-for="(item, index) in hotMaterials" :key="index" |
| 132 | - <view class="w-[80rpx] h-[88rpx] flex-shrink-0 flex items-center justify-center bg-blue-50 rounded-[12rpx]"> | 132 | + class="flex flex-row bg-white rounded-[24rpx] p-[24rpx] border border-gray-50"> |
| 133 | - <image :src="getDocumentIcon(hotMaterials[0].fileName)" class="w-[48rpx] h-[48rpx]" mode="aspectFit" /> | 133 | + |
| 134 | - </view> | 134 | + <!-- 左侧图标 --> |
| 135 | - <view class="flex-1 flex flex-col justify-between py-[4rpx]"> | 135 | + <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"> |
| 136 | - <text class="text-gray-800 text-[28rpx] leading-[40rpx] line-clamp-2 mb-1">{{ hotMaterials[0].title }}</text> | 136 | + <image :src="getDocumentIcon(item.fileName)" class="w-[48rpx] h-[48rpx]" mode="aspectFit" /> |
| 137 | - <view class="flex items-center gap-2 mb-1"> | ||
| 138 | - <view class="bg-blue-50 rounded px-2 py-0.5"> | ||
| 139 | - <text class="text-blue-600 text-[22rpx]">{{ getDocumentLabel(hotMaterials[0].fileName) }}</text> | ||
| 140 | - </view> | ||
| 141 | - </view> | ||
| 142 | - <view class="flex justify-between items-end"> | ||
| 143 | - <text class="text-gray-400 text-[24rpx]">{{ hotMaterials[0].learners }}</text> | ||
| 144 | - <text class="text-blue-600 text-[26rpx]">{{ hotMaterials[0].progress }}</text> | ||
| 145 | - </view> | ||
| 146 | - </view> | ||
| 147 | - </view> | ||
| 148 | - <view class="h-[2rpx] bg-gray-100"></view> | ||
| 149 | - <!-- Item 2 --> | ||
| 150 | - <view class="flex gap-[24rpx]" @tap="handleMaterialClick(hotMaterials[1])"> | ||
| 151 | - <view class="w-[80rpx] h-[88rpx] flex-shrink-0 flex items-center justify-center bg-blue-50 rounded-[12rpx]"> | ||
| 152 | - <image :src="getDocumentIcon(hotMaterials[1].fileName)" class="w-[48rpx] h-[48rpx]" mode="aspectFit" /> | ||
| 153 | - </view> | ||
| 154 | - <view class="flex-1 flex flex-col justify-between py-[4rpx]"> | ||
| 155 | - <text class="text-gray-800 text-[28rpx] leading-[40rpx] line-clamp-2 mb-1">{{ hotMaterials[1].title }}</text> | ||
| 156 | - <view class="flex items-center gap-2 mb-1"> | ||
| 157 | - <view class="bg-blue-50 rounded px-2 py-0.5"> | ||
| 158 | - <text class="text-blue-600 text-[22rpx]">{{ getDocumentLabel(hotMaterials[1].fileName) }}</text> | ||
| 159 | - </view> | ||
| 160 | - </view> | ||
| 161 | - <view class="flex justify-between items-end"> | ||
| 162 | - <text class="text-gray-400 text-[24rpx]">{{ hotMaterials[1].learners }}</text> | ||
| 163 | - <text class="text-blue-600 text-[26rpx]">{{ hotMaterials[1].progress }}</text> | ||
| 164 | - </view> | ||
| 165 | - </view> | ||
| 166 | - </view> | ||
| 167 | - <view class="h-[2rpx] bg-gray-100"></view> | ||
| 168 | - <!-- Item 3 --> | ||
| 169 | - <view class="flex gap-[24rpx]" @tap="handleMaterialClick(hotMaterials[2])"> | ||
| 170 | - <view class="w-[80rpx] h-[88rpx] flex-shrink-0 flex items-center justify-center bg-blue-50 rounded-[12rpx]"> | ||
| 171 | - <image :src="getDocumentIcon(hotMaterials[2].fileName)" class="w-[48rpx] h-[48rpx]" mode="aspectFit" /> | ||
| 172 | </view> | 137 | </view> |
| 173 | - <view class="flex-1 flex flex-col justify-between py-[4rpx]"> | 138 | + |
| 174 | - <text class="text-gray-800 text-[28rpx] leading-[40rpx] line-clamp-2 mb-1">{{ hotMaterials[2].title }}</text> | 139 | + <!-- 内容区域 --> |
| 175 | - <view class="flex items-center gap-2 mb-1"> | 140 | + <view class="flex-1 min-w-0"> |
| 176 | - <view class="bg-blue-50 rounded px-2 py-0.5"> | 141 | + <text class="text-[#1F2937] text-[30rpx] font-bold leading-[1.4] line-clamp-2 mb-[8rpx]"> |
| 177 | - <text class="text-blue-600 text-[22rpx]">{{ getDocumentLabel(hotMaterials[2].fileName) }}</text> | 142 | + {{ item.title }} |
| 143 | + </text> | ||
| 144 | + <view class="flex items-center gap-[12rpx] mb-[16rpx]"> | ||
| 145 | + <view class="inline-flex items-center justify-center px-[12rpx] py-[4rpx] bg-gray-100 text-gray-500 text-[20rpx] font-medium rounded-[8rpx]"> | ||
| 146 | + <text>{{ getDocumentLabel(item.fileName) }}</text> | ||
| 178 | </view> | 147 | </view> |
| 148 | + <text class="text-[#9CA3AF] text-[22rpx]">{{ item.learners }}</text> | ||
| 179 | </view> | 149 | </view> |
| 180 | - <view class="flex justify-between items-end"> | 150 | + |
| 181 | - <text class="text-gray-400 text-[24rpx]">{{ hotMaterials[2].learners }}</text> | 151 | + <!-- 分割线 --> |
| 182 | - <text class="text-blue-600 text-[26rpx]">{{ hotMaterials[2].progress }}</text> | 152 | + <view class="h-[1rpx] bg-gray-100 my-[20rpx]"></view> |
| 183 | - </view> | 153 | + |
| 154 | + <!-- 操作按钮 --> | ||
| 155 | + <ListItemActions | ||
| 156 | + :viewable="true" | ||
| 157 | + :collectable="true" | ||
| 158 | + :deletable="false" | ||
| 159 | + :collected="item.collected" | ||
| 160 | + @view="onViewMaterial(item)" | ||
| 161 | + @collect="toggleMaterialCollect(item)" | ||
| 162 | + /> | ||
| 184 | </view> | 163 | </view> |
| 185 | </view> | 164 | </view> |
| 186 | </view> | 165 | </view> |
| ... | @@ -217,6 +196,7 @@ import IconFont from '@/components/IconFont.vue'; | ... | @@ -217,6 +196,7 @@ import IconFont from '@/components/IconFont.vue'; |
| 217 | import PlanPopup from '@/components/PlanPopup/index.vue'; | 196 | import PlanPopup from '@/components/PlanPopup/index.vue'; |
| 218 | import SchemeA from '@/components/PlanSchemes/SchemeA.vue'; | 197 | import SchemeA from '@/components/PlanSchemes/SchemeA.vue'; |
| 219 | import SchemeB from '@/components/PlanSchemes/SchemeB.vue'; | 198 | import SchemeB from '@/components/PlanSchemes/SchemeB.vue'; |
| 199 | +import ListItemActions from '@/components/ListItemActions/index.vue'; | ||
| 220 | 200 | ||
| 221 | // Plan Popup State | 201 | // Plan Popup State |
| 222 | const showPlanPopup = ref(false); | 202 | const showPlanPopup = ref(false); |
| ... | @@ -256,6 +236,7 @@ const hotMaterials = ref([ | ... | @@ -256,6 +236,7 @@ const hotMaterials = ref([ |
| 256 | title: '2024年保险市场趋势分析报告', | 236 | title: '2024年保险市场趋势分析报告', |
| 257 | learners: '256人学习', | 237 | learners: '256人学习', |
| 258 | progress: '78%', | 238 | progress: '78%', |
| 239 | + collected: false, | ||
| 259 | // PDF 文件 | 240 | // PDF 文件 |
| 260 | fileName: '2024年保险市场趋势分析报告.pdf', | 241 | fileName: '2024年保险市场趋势分析报告.pdf', |
| 261 | downloadUrl: 'https://cdn.ipadbiz.cn/manulife/document/1_%E7%BE%8E%E4%B9%90%E7%88%B1%E8%A7%89%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' | 242 | downloadUrl: 'https://cdn.ipadbiz.cn/manulife/document/1_%E7%BE%8E%E4%B9%90%E7%88%B1%E8%A7%89%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' |
| ... | @@ -264,6 +245,7 @@ const hotMaterials = ref([ | ... | @@ -264,6 +245,7 @@ const hotMaterials = ref([ |
| 264 | title: '高净值客户产品配置方案模板', | 245 | title: '高净值客户产品配置方案模板', |
| 265 | learners: '189人学习', | 246 | learners: '189人学习', |
| 266 | progress: '65%', | 247 | progress: '65%', |
| 248 | + collected: true, | ||
| 267 | // Word 文件 | 249 | // Word 文件 |
| 268 | fileName: '高净值客户产品配置方案模板.docx', | 250 | fileName: '高净值客户产品配置方案模板.docx', |
| 269 | downloadUrl: 'https://cdn.ipadbiz.cn/manulife/document/1_%E7%BE%8E%E4%B9%90%E7%88%B1%E8%A7%89%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' | 251 | downloadUrl: 'https://cdn.ipadbiz.cn/manulife/document/1_%E7%BE%8E%E4%B9%90%E7%88%B1%E8%A7%89%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' |
| ... | @@ -272,6 +254,7 @@ const hotMaterials = ref([ | ... | @@ -272,6 +254,7 @@ const hotMaterials = ref([ |
| 272 | title: '产品收益率测算表(2024版)', | 254 | title: '产品收益率测算表(2024版)', |
| 273 | learners: '142人学习', | 255 | learners: '142人学习', |
| 274 | progress: '52%', | 256 | progress: '52%', |
| 257 | + collected: false, | ||
| 275 | // Excel 文件 | 258 | // Excel 文件 |
| 276 | fileName: '产品收益率测算表.xlsx', | 259 | fileName: '产品收益率测算表.xlsx', |
| 277 | downloadUrl: 'https://cdn.ipadbiz.cn/manulife/document/1_%E7%BE%8E%E4%B9%90%E7%88%B1%E8%A7%89%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' | 260 | downloadUrl: 'https://cdn.ipadbiz.cn/manulife/document/1_%E7%BE%8E%E4%B9%90%E7%88%B1%E8%A7%89%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' |
| ... | @@ -286,13 +269,28 @@ const go = useGo(); | ... | @@ -286,13 +269,28 @@ const go = useGo(); |
| 286 | * | 269 | * |
| 287 | * @description 配置为文件类型列表,点击时打开文件预览 | 270 | * @description 配置为文件类型列表,点击时打开文件预览 |
| 288 | */ | 271 | */ |
| 289 | -const { handleClick: handleMaterialClick } = useListItemClick({ | 272 | +const { handleClick: onViewMaterial } = useListItemClick({ |
| 290 | listType: ListType.FILE, | 273 | listType: ListType.FILE, |
| 291 | onAfterClick: (item) => { | 274 | onAfterClick: (item) => { |
| 292 | console.log('用户打开了资料:', item.title); | 275 | console.log('用户打开了资料:', item.title); |
| 293 | } | 276 | } |
| 294 | }); | 277 | }); |
| 295 | 278 | ||
| 279 | +/** | ||
| 280 | + * 切换资料收藏状态 | ||
| 281 | + * | ||
| 282 | + * @description 切换热门资料的收藏状态 | ||
| 283 | + * @param {Object} item - 资料项 | ||
| 284 | + */ | ||
| 285 | +const toggleMaterialCollect = (item) => { | ||
| 286 | + item.collected = !item.collected; | ||
| 287 | + Taro.showToast({ | ||
| 288 | + title: item.collected ? '已收藏' : '已取消收藏', | ||
| 289 | + icon: 'success', | ||
| 290 | + duration: 1000 | ||
| 291 | + }); | ||
| 292 | +}; | ||
| 293 | + | ||
| 296 | // Handle grid navigation click | 294 | // Handle grid navigation click |
| 297 | const handleGridNav = (item) => { | 295 | const handleGridNav = (item) => { |
| 298 | if (!item.route) { | 296 | if (!item.route) { | ... | ... |
| ... | @@ -45,7 +45,8 @@ | ... | @@ -45,7 +45,8 @@ |
| 45 | </div> | 45 | </div> |
| 46 | 46 | ||
| 47 | <!-- Desc --> | 47 | <!-- Desc --> |
| 48 | - <div class="mt-auto self-start bg-[#F3F4F6] text-[#6B7280] text-[22rpx] px-[12rpx] py-[4rpx] rounded-full"> | 48 | + <div class="mt-auto self-start text-[22rpx] px-[12rpx] py-[4rpx] rounded-full" |
| 49 | + :class="[getDescColor(item.id).bg, getDescColor(item.id).text]"> | ||
| 49 | {{ item.desc }} | 50 | {{ item.desc }} |
| 50 | </div> | 51 | </div> |
| 51 | </div> | 52 | </div> |
| ... | @@ -68,6 +69,34 @@ const activeTab = ref(0) | ... | @@ -68,6 +69,34 @@ const activeTab = ref(0) |
| 68 | const tabs = ['全部产品', '人寿保险', '医疗保险', '意外保险'] | 69 | const tabs = ['全部产品', '人寿保险', '医疗保险', '意外保险'] |
| 69 | 70 | ||
| 70 | /** | 71 | /** |
| 72 | + * Desc 颜色调色板 | ||
| 73 | + * | ||
| 74 | + * @description 为不同描述提供柔和的背景色和对应的文字颜色 | ||
| 75 | + */ | ||
| 76 | +const descColorPalette = [ | ||
| 77 | + { bg: 'bg-blue-50', text: 'text-blue-600' }, // 蓝色 | ||
| 78 | + { bg: 'bg-green-50', text: 'text-green-600' }, // 绿色 | ||
| 79 | + { bg: 'bg-purple-50', text: 'text-purple-600' }, // 紫色 | ||
| 80 | + { bg: 'bg-orange-50', text: 'text-orange-600' }, // 橙色 | ||
| 81 | + { bg: 'bg-pink-50', text: 'text-pink-600' }, // 粉色 | ||
| 82 | + { bg: 'bg-teal-50', text: 'text-teal-600' }, // 青色 | ||
| 83 | + { bg: 'bg-indigo-50', text: 'text-indigo-600' }, // 靛蓝色 | ||
| 84 | + { bg: 'bg-red-50', text: 'text-red-600' }, // 红色 | ||
| 85 | +] | ||
| 86 | + | ||
| 87 | +/** | ||
| 88 | + * 获取 desc 的颜色样式 | ||
| 89 | + * | ||
| 90 | + * @description 根据 ID 获取固定的背景色和文字颜色 | ||
| 91 | + * @param {number} id - 产品 ID | ||
| 92 | + * @returns {Object} 包含 bg 和 text 属性的颜色对象 | ||
| 93 | + */ | ||
| 94 | +const getDescColor = (id) => { | ||
| 95 | + const index = id % descColorPalette.length | ||
| 96 | + return descColorPalette[index] | ||
| 97 | +} | ||
| 98 | + | ||
| 99 | +/** | ||
| 71 | * 生成产品数据 | 100 | * 生成产品数据 |
| 72 | * | 101 | * |
| 73 | * @description 生成模拟产品列表数据,每个产品包含唯一 id | 102 | * @description 生成模拟产品列表数据,每个产品包含唯一 id | ... | ... |
| 1 | <!-- | 1 | <!-- |
| 2 | - * @Date: 2026-01-30 | 2 | + * @Date: 2026-01-31 |
| 3 | * @Description: 资料列表页 | 3 | * @Description: 资料列表页 |
| 4 | --> | 4 | --> |
| 5 | <template> | 5 | <template> |
| ... | @@ -21,12 +21,12 @@ | ... | @@ -21,12 +21,12 @@ |
| 21 | <div class="px-[32rpx] mt-[32rpx]"> | 21 | <div class="px-[32rpx] mt-[32rpx]"> |
| 22 | <div class="flex flex-col gap-[24rpx]"> | 22 | <div class="flex flex-col gap-[24rpx]"> |
| 23 | <div v-for="(item, index) in list" :key="index" | 23 | <div v-for="(item, index) in list" :key="index" |
| 24 | - class="material-item bg-white rounded-[24rpx] p-[24rpx] shadow-sm flex items-start transition-all duration-200 border border-gray-50" | 24 | + class="material-item bg-white rounded-[24rpx] p-[24rpx] shadow-sm transition-all duration-200 border border-gray-50 flex flex-row" |
| 25 | :style="{ animationDelay: `${index * 50}ms` }"> | 25 | :style="{ animationDelay: `${index * 50}ms` }"> |
| 26 | 26 | ||
| 27 | <!-- 左侧图标 --> | 27 | <!-- 左侧图标 --> |
| 28 | <div | 28 | <div |
| 29 | - 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"> | 29 | + 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"> |
| 30 | <image | 30 | <image |
| 31 | :src="getDocumentIcon(item.fileName)" | 31 | :src="getDocumentIcon(item.fileName)" |
| 32 | class="w-[48rpx] h-[48rpx]" | 32 | class="w-[48rpx] h-[48rpx]" |
| ... | @@ -36,47 +36,36 @@ | ... | @@ -36,47 +36,36 @@ |
| 36 | 36 | ||
| 37 | <!-- 内容区域 --> | 37 | <!-- 内容区域 --> |
| 38 | <div class="flex-1 min-w-0"> | 38 | <div class="flex-1 min-w-0"> |
| 39 | - <div class="flex justify-between items-start gap-[16rpx]"> | 39 | + <h3 class="text-[#1F2937] text-[30rpx] font-bold leading-[1.4] line-clamp-2 mb-[8rpx]"> |
| 40 | - <div class="flex-1 pr-[8rpx]"> | 40 | + {{ item.title }} |
| 41 | - <h3 class="text-[#1F2937] text-[30rpx] font-bold leading-[1.4] line-clamp-2 mb-[8rpx]"> | 41 | + </h3> |
| 42 | - {{ item.title }} | 42 | + <p class="text-[#6B7280] text-[24rpx] leading-[1.4] line-clamp-1 mb-[16rpx]"> |
| 43 | - </h3> | 43 | + {{ item.desc }} |
| 44 | - <p class="text-[#6B7280] text-[24rpx] leading-[1.4] line-clamp-1 mb-[16rpx]"> | 44 | + </p> |
| 45 | - {{ item.desc }} | ||
| 46 | - </p> | ||
| 47 | - </div> | ||
| 48 | 45 | ||
| 49 | - <!-- 收藏图标 --> | 46 | + <!-- 文件信息 --> |
| 50 | - <div | 47 | + <div class="flex items-center gap-[12rpx] mb-[20rpx]"> |
| 51 | - class="heart-btn w-[64rpx] h-[64rpx] -mt-[12rpx] -mr-[12rpx] flex items-center justify-center rounded-full active:bg-gray-50 transition-colors" | 48 | + <span |
| 52 | - @click.stop="toggleCollect(item)"> | 49 | + class="inline-flex items-center justify-center px-[12rpx] py-[4rpx] bg-gray-100 text-gray-500 text-[20rpx] font-medium rounded-[8rpx]"> |
| 53 | - <div :class="{ 'is-collected': item.collected }" class="transform transition-transform duration-300"> | 50 | + {{ getDocumentLabel(item.fileName) }} |
| 54 | - <IconFont :name="item.collected ? 'heart-fill' : 'heart'" size="24" | 51 | + </span> |
| 55 | - :color="item.collected ? '#EF4444' : '#D1D5DB'" /> | 52 | + <span class="text-[#9CA3AF] text-[22rpx]"> |
| 56 | - </div> | 53 | + {{ item.size }} |
| 57 | - </div> | 54 | + </span> |
| 58 | </div> | 55 | </div> |
| 59 | 56 | ||
| 60 | - <!-- 底部信息:文件类型、大小和查看按钮 --> | 57 | + <!-- 分割线 --> |
| 61 | - <div class="flex items-center justify-between"> | 58 | + <view class="h-[1rpx] bg-gray-100 my-[20rpx]"></view> |
| 62 | - <div class="flex items-center gap-[12rpx]"> | ||
| 63 | - <span | ||
| 64 | - class="inline-flex items-center justify-center px-[12rpx] py-[4rpx] bg-gray-100 text-gray-500 text-[20rpx] font-medium rounded-[8rpx]"> | ||
| 65 | - {{ getDocumentLabel(item.fileName) }} | ||
| 66 | - </span> | ||
| 67 | - <span class="text-[#9CA3AF] text-[22rpx]"> | ||
| 68 | - {{ item.size }} | ||
| 69 | - </span> | ||
| 70 | - </div> | ||
| 71 | 59 | ||
| 72 | - <!-- 查看按钮 --> | 60 | + <!-- 操作按钮 --> |
| 73 | - <view | 61 | + <ListItemActions |
| 74 | - class="flex items-center justify-center px-[20rpx] py-[10rpx] bg-blue-50 rounded-full active:bg-blue-100 transition-colors" | 62 | + :viewable="true" |
| 75 | - @tap.stop="onView(item)"> | 63 | + :collectable="true" |
| 76 | - <IconFont name="eye" size="14" color="#2563EB" class="mr-[6rpx]" /> | 64 | + :collected="item.collected" |
| 77 | - <text class="text-[24rpx] text-blue-600 font-medium leading-none ml-[8rpx]">查看</text> | 65 | + @view="onView(item)" |
| 78 | - </view> | 66 | + @collect="toggleCollect(item)" |
| 79 | - </div> | 67 | + @delete="onDelete(item)" |
| 68 | + /> | ||
| 80 | </div> | 69 | </div> |
| 81 | </div> | 70 | </div> |
| 82 | </div> | 71 | </div> |
| ... | @@ -92,8 +81,10 @@ import { ref } from 'vue' | ... | @@ -92,8 +81,10 @@ import { ref } from 'vue' |
| 92 | import NavHeader from '@/components/NavHeader.vue' | 81 | import NavHeader from '@/components/NavHeader.vue' |
| 93 | import TabBar from '@/components/TabBar.vue' | 82 | import TabBar from '@/components/TabBar.vue' |
| 94 | import IconFont from '@/components/IconFont.vue' | 83 | import IconFont from '@/components/IconFont.vue' |
| 84 | +import ListItemActions from '@/components/ListItemActions/index.vue' | ||
| 95 | import { useListItemClick, ListType } from '@/composables/useListItemClick' | 85 | import { useListItemClick, ListType } from '@/composables/useListItemClick' |
| 96 | import { getDocumentIcon, getDocumentLabel } from '@/utils/documentIcons' | 86 | import { getDocumentIcon, getDocumentLabel } from '@/utils/documentIcons' |
| 87 | +import Taro from '@tarojs/taro' | ||
| 97 | 88 | ||
| 98 | const searchValue = ref('') | 89 | const searchValue = ref('') |
| 99 | 90 | ||
| ... | @@ -111,7 +102,7 @@ const list = ref([ | ... | @@ -111,7 +102,7 @@ const list = ref([ |
| 111 | iconColor: '#EF4444', | 102 | iconColor: '#EF4444', |
| 112 | collected: true, | 103 | collected: true, |
| 113 | fileName: '2024年保险代理人考试大纲.pdf', | 104 | fileName: '2024年保险代理人考试大纲.pdf', |
| 114 | - downloadUrl: 'https://cdn.ipadbiz.cn/manulife/document/1_%E7%BE%8E%E4%B9%90%E7%88%B1%E8%A7%89%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' | 105 | + 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%BE%E5%BD%B1%E4%BB%8B%E7%BB%8D_.pdf' |
| 115 | }, | 106 | }, |
| 116 | { | 107 | { |
| 117 | title: '历年真题汇总及解析.pdf', | 108 | title: '历年真题汇总及解析.pdf', |
| ... | @@ -121,7 +112,7 @@ const list = ref([ | ... | @@ -121,7 +112,7 @@ const list = ref([ |
| 121 | iconColor: '#EF4444', | 112 | iconColor: '#EF4444', |
| 122 | collected: false, | 113 | collected: false, |
| 123 | fileName: '历年真题汇总及解析.pdf', | 114 | fileName: '历年真题汇总及解析.pdf', |
| 124 | - downloadUrl: 'https://cdn.ipadbiz.cn/manulife/document/1_%E7%BE%8E%E4%B9%90%E7%88%B1%E8%A7%89%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' | 115 | + 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%BE%E5%BD%B1%E4%BB%8B%E7%BB%8D_.pdf' |
| 125 | }, | 116 | }, |
| 126 | { | 117 | { |
| 127 | title: '考试技巧与经验分享.pdf', | 118 | title: '考试技巧与经验分享.pdf', |
| ... | @@ -131,7 +122,7 @@ const list = ref([ | ... | @@ -131,7 +122,7 @@ const list = ref([ |
| 131 | iconColor: '#EF4444', | 122 | iconColor: '#EF4444', |
| 132 | collected: false, | 123 | collected: false, |
| 133 | fileName: '考试技巧与经验分享.pdf', | 124 | fileName: '考试技巧与经验分享.pdf', |
| 134 | - downloadUrl: 'https://cdn.ipadbiz.cn/manulife/document/1_%E7%BE%8E%E4%B9%90%E7%88%B1%E8%A7%89%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' | 125 | + 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%BE%E5%BD%B1%E4%BB%8B%E7%BB%8D_.pdf' |
| 135 | }, | 126 | }, |
| 136 | { | 127 | { |
| 137 | title: '保险基础知识速记手册.pdf', | 128 | title: '保险基础知识速记手册.pdf', |
| ... | @@ -141,7 +132,7 @@ const list = ref([ | ... | @@ -141,7 +132,7 @@ const list = ref([ |
| 141 | iconColor: '#EF4444', | 132 | iconColor: '#EF4444', |
| 142 | collected: false, | 133 | collected: false, |
| 143 | fileName: '保险基础知识速记手册.pdf', | 134 | fileName: '保险基础知识速记手册.pdf', |
| 144 | - downloadUrl: 'https://cdn.ipadbiz.cn/manulife/document/1_%E7%BE%8E%E4%B9%90%E7%88%B1%E8%A7%89%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' | 135 | + 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%BE%E5%BD%B1%E4%BB%8B%E7%BB%8D_.pdf' |
| 145 | }, | 136 | }, |
| 146 | { | 137 | { |
| 147 | title: '模拟试卷10套及答案.pdf', | 138 | title: '模拟试卷10套及答案.pdf', |
| ... | @@ -151,7 +142,7 @@ const list = ref([ | ... | @@ -151,7 +142,7 @@ const list = ref([ |
| 151 | iconColor: '#EF4444', | 142 | iconColor: '#EF4444', |
| 152 | collected: true, | 143 | collected: true, |
| 153 | fileName: '模拟试卷10套及答案.pdf', | 144 | fileName: '模拟试卷10套及答案.pdf', |
| 154 | - downloadUrl: 'https://cdn.ipadbiz.cn/manulife/document/1_%E7%BE%8E%E4%B9%90%E7%88%B1%E8%A7%89%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' | 145 | + 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%BE%E5%BD%B1%E4%BB%8B%E7%BB%8D_.pdf' |
| 155 | }, | 146 | }, |
| 156 | { | 147 | { |
| 157 | title: '法律法规重点条款解读.pdf', | 148 | title: '法律法规重点条款解读.pdf', |
| ... | @@ -161,7 +152,7 @@ const list = ref([ | ... | @@ -161,7 +152,7 @@ const list = ref([ |
| 161 | iconColor: '#EF4444', | 152 | iconColor: '#EF4444', |
| 162 | collected: false, | 153 | collected: false, |
| 163 | fileName: '法律法规重点条款解读.pdf', | 154 | fileName: '法律法规重点条款解读.pdf', |
| 164 | - downloadUrl: 'https://cdn.ipadbiz.cn/manulife/document/1_%E7%BE%8E%E4%B9%90%E7%88%B1%E8%A7%89%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' | 155 | + 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%BE%E5%BD%B1%E4%BB%8B%E7%BB%8D_.pdf' |
| 165 | }, | 156 | }, |
| 166 | { | 157 | { |
| 167 | title: '考试常见易错题分析.pdf', | 158 | title: '考试常见易错题分析.pdf', |
| ... | @@ -171,7 +162,7 @@ const list = ref([ | ... | @@ -171,7 +162,7 @@ const list = ref([ |
| 171 | iconColor: '#EF4444', | 162 | iconColor: '#EF4444', |
| 172 | collected: false, | 163 | collected: false, |
| 173 | fileName: '考试常见易错题分析.pdf', | 164 | fileName: '考试常见易错题分析.pdf', |
| 174 | - downloadUrl: 'https://cdn.ipadbiz.cn/manulife/document/1_%E7%BE%8E%E4%B9%90%E7%88%B1%E8%A7%89%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' | 165 | + 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' |
| 175 | }, | 166 | }, |
| 176 | { | 167 | { |
| 177 | title: '案例分析题库及解答.pdf', | 168 | title: '案例分析题库及解答.pdf', |
| ... | @@ -181,7 +172,7 @@ const list = ref([ | ... | @@ -181,7 +172,7 @@ const list = ref([ |
| 181 | iconColor: '#EF4444', | 172 | iconColor: '#EF4444', |
| 182 | collected: false, | 173 | collected: false, |
| 183 | fileName: '案例分析题库及解答.pdf', | 174 | fileName: '案例分析题库及解答.pdf', |
| 184 | - downloadUrl: 'https://cdn.ipadbiz.cn/manulife/document/1_%E7%BE%8E%E4%B9%90%E7%88%B1%E8%A7%89%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' | 175 | + 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%BE%E5%BD%B1%E4%BB%8B%E7%BB%8D_.pdf' |
| 185 | }, | 176 | }, |
| 186 | { | 177 | { |
| 187 | title: '考前冲刺复习资料.pdf', | 178 | title: '考前冲刺复习资料.pdf', |
| ... | @@ -191,7 +182,7 @@ const list = ref([ | ... | @@ -191,7 +182,7 @@ const list = ref([ |
| 191 | iconColor: '#EF4444', | 182 | iconColor: '#EF4444', |
| 192 | collected: false, | 183 | collected: false, |
| 193 | fileName: '考前冲刺复习资料.pdf', | 184 | fileName: '考前冲刺复习资料.pdf', |
| 194 | - downloadUrl: 'https://cdn.ipadbiz.cn/manulife/document/1_%E7%BE%8E%E4%B9%90%E7%88%B1%E8%A7%89%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' | 185 | + 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%BE%E5%BD%B1%E4%BB%8B%E7%BB%8D_.pdf' |
| 195 | }, | 186 | }, |
| 196 | { | 187 | { |
| 197 | title: '考场注意事项及答题技巧.pdf', | 188 | title: '考场注意事项及答题技巧.pdf', |
| ... | @@ -201,7 +192,7 @@ const list = ref([ | ... | @@ -201,7 +192,7 @@ const list = ref([ |
| 201 | iconColor: '#EF4444', | 192 | iconColor: '#EF4444', |
| 202 | collected: false, | 193 | collected: false, |
| 203 | fileName: '考场注意事项及答题技巧.pdf', | 194 | fileName: '考场注意事项及答题技巧.pdf', |
| 204 | - downloadUrl: 'https://cdn.ipadbiz.cn/manulife/document/1_%E7%BE%8E%E4%B9%90%E7%88%B1%E8%A7%89%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' | 195 | + 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' |
| 205 | } | 196 | } |
| 206 | ]) | 197 | ]) |
| 207 | 198 | ||
| ... | @@ -215,6 +206,18 @@ const onSearch = () => { | ... | @@ -215,6 +206,18 @@ const onSearch = () => { |
| 215 | } | 206 | } |
| 216 | 207 | ||
| 217 | /** | 208 | /** |
| 209 | + * 使用文件列表点击处理器 | ||
| 210 | + * | ||
| 211 | + * @description 配置为文件类型列表,点击时打开文件预览 | ||
| 212 | + */ | ||
| 213 | +const { handleClick: onView } = useListItemClick({ | ||
| 214 | + listType: ListType.FILE, | ||
| 215 | + onAfterClick: (item) => { | ||
| 216 | + console.log('用户打开了资料:', item.title) | ||
| 217 | + } | ||
| 218 | +}) | ||
| 219 | + | ||
| 220 | +/** | ||
| 218 | * 切换收藏状态 | 221 | * 切换收藏状态 |
| 219 | * | 222 | * |
| 220 | * @description 切换资料的收藏状态 | 223 | * @description 切换资料的收藏状态 |
| ... | @@ -222,19 +225,31 @@ const onSearch = () => { | ... | @@ -222,19 +225,31 @@ const onSearch = () => { |
| 222 | */ | 225 | */ |
| 223 | const toggleCollect = (item) => { | 226 | const toggleCollect = (item) => { |
| 224 | item.collected = !item.collected | 227 | item.collected = !item.collected |
| 228 | + Taro.showToast({ | ||
| 229 | + title: item.collected ? '已收藏' : '已取消收藏', | ||
| 230 | + icon: 'success', | ||
| 231 | + duration: 1000 | ||
| 232 | + }) | ||
| 225 | } | 233 | } |
| 226 | 234 | ||
| 227 | /** | 235 | /** |
| 228 | - * 使用文件列表点击处理器 | 236 | + * 删除资料 |
| 229 | - * | ||
| 230 | - * @description 配置为文件类型列表,点击时打开文件预览 | ||
| 231 | */ | 237 | */ |
| 232 | -const { handleClick: onView } = useListItemClick({ | 238 | +const onDelete = (item) => { |
| 233 | - listType: ListType.FILE, | 239 | + Taro.showModal({ |
| 234 | - onAfterClick: (item) => { | 240 | + title: '提示', |
| 235 | - console.log('用户打开了资料:', item.title) | 241 | + content: '确定要删除该资料吗?', |
| 236 | - } | 242 | + success: (res) => { |
| 237 | -}) | 243 | + if (res.confirm) { |
| 244 | + const index = list.value.findIndex(i => i.title === item.title) | ||
| 245 | + if (index !== -1) { | ||
| 246 | + list.value.splice(index, 1) | ||
| 247 | + Taro.showToast({ title: '已删除', icon: 'success' }) | ||
| 248 | + } | ||
| 249 | + } | ||
| 250 | + } | ||
| 251 | + }) | ||
| 252 | +} | ||
| 238 | </script> | 253 | </script> |
| 239 | 254 | ||
| 240 | <style lang="less" scoped> | 255 | <style lang="less" scoped> |
| ... | @@ -253,31 +268,4 @@ const { handleClick: onView } = useListItemClick({ | ... | @@ -253,31 +268,4 @@ const { handleClick: onView } = useListItemClick({ |
| 253 | .material-item { | 268 | .material-item { |
| 254 | animation: slideIn 0.5s cubic-bezier(0.2, 0.8, 0.2, 1) backwards; | 269 | animation: slideIn 0.5s cubic-bezier(0.2, 0.8, 0.2, 1) backwards; |
| 255 | } | 270 | } |
| 256 | - | ||
| 257 | -/* 收藏成功的动画 */ | ||
| 258 | -.is-collected { | ||
| 259 | - animation: heartBeat 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275); | ||
| 260 | -} | ||
| 261 | - | ||
| 262 | -@keyframes heartBeat { | ||
| 263 | - 0% { | ||
| 264 | - transform: scale(1); | ||
| 265 | - } | ||
| 266 | - | ||
| 267 | - 14% { | ||
| 268 | - transform: scale(1.3); | ||
| 269 | - } | ||
| 270 | - | ||
| 271 | - 28% { | ||
| 272 | - transform: scale(1); | ||
| 273 | - } | ||
| 274 | - | ||
| 275 | - 42% { | ||
| 276 | - transform: scale(1.3); | ||
| 277 | - } | ||
| 278 | - | ||
| 279 | - 70% { | ||
| 280 | - transform: scale(1); | ||
| 281 | - } | ||
| 282 | -} | ||
| 283 | </style> | 271 | </style> | ... | ... |
| ... | @@ -23,7 +23,7 @@ | ... | @@ -23,7 +23,7 @@ |
| 23 | </view> | 23 | </view> |
| 24 | 24 | ||
| 25 | <!-- Arrow --> | 25 | <!-- Arrow --> |
| 26 | - <IconFont name="rectRight" size="20" color="#9CA3AF" /> | 26 | + <IconFont name="rect-right" size="20" color="#9CA3AF" /> |
| 27 | </view> | 27 | </view> |
| 28 | 28 | ||
| 29 | <!-- Menu List --> | 29 | <!-- Menu List --> | ... | ... |
| ... | @@ -53,16 +53,12 @@ | ... | @@ -53,16 +53,12 @@ |
| 53 | <view class="h-[1rpx] bg-gray-100 my-[20rpx]"></view> | 53 | <view class="h-[1rpx] bg-gray-100 my-[20rpx]"></view> |
| 54 | 54 | ||
| 55 | <!-- Actions --> | 55 | <!-- Actions --> |
| 56 | - <view class="flex justify-end gap-[24rpx]"> | 56 | + <ListItemActions |
| 57 | - <view class="flex items-center text-blue-600" @tap="onView(item)"> | 57 | + :viewable="true" |
| 58 | - <IconFont name="eye" size="14" class="mr-[8rpx]" /> | 58 | + :deletable="true" |
| 59 | - <text class="text-[24rpx]">查看</text> | 59 | + @view="onView(item)" |
| 60 | - </view> | 60 | + @delete="onDelete(item)" |
| 61 | - <view class="flex items-center text-red-500" @tap="onDelete(item)"> | 61 | + /> |
| 62 | - <IconFont name="del" size="14" class="mr-[8rpx]" /> | ||
| 63 | - <text class="text-[24rpx]">删除</text> | ||
| 64 | - </view> | ||
| 65 | - </view> | ||
| 66 | </view> | 62 | </view> |
| 67 | 63 | ||
| 68 | <!-- Empty State --> | 64 | <!-- Empty State --> |
| ... | @@ -85,6 +81,7 @@ import { useFileOperation } from '@/composables/useFileOperation' | ... | @@ -85,6 +81,7 @@ import { useFileOperation } from '@/composables/useFileOperation' |
| 85 | import IconFont from '@/components/IconFont.vue' | 81 | import IconFont from '@/components/IconFont.vue' |
| 86 | import TabBar from '@/components/TabBar.vue' | 82 | import TabBar from '@/components/TabBar.vue' |
| 87 | import NavHeader from '@/components/NavHeader.vue' | 83 | import NavHeader from '@/components/NavHeader.vue' |
| 84 | +import ListItemActions from '@/components/ListItemActions/index.vue' | ||
| 88 | import Taro from '@tarojs/taro' | 85 | import Taro from '@tarojs/taro' |
| 89 | 86 | ||
| 90 | const go = useGo() | 87 | const go = useGo() | ... | ... |
-
Please register or login to post a comment