hookehuyr

feat(docs): 实现文档图标系统并优化首页展示

## 主要更新

### 新增
- 创建文档图标工具函数 (src/utils/documentIcons.js)
  - 根据文件扩展名自动匹配对应图标 (PDF/Word/Excel/PPT/图片/视频等)
  - 提供 getDocumentIcon() 和 getDocumentLabel() 工具函数
  - 支持判断文件类型 (isPDF/isImage/isVideo)
- 添加文档图标资源 (src/assets/images/icon/doc/)
  - 10 个 SVG 图标文件,覆盖常见文档类型
- 新增文档图标更新说明文档 (docs/DOCUMENT_ICONS_UPDATE.md)
- 移动计划书开发指南到组件目录 (src/components/PlanSchemes/)

### 优化
- 更新首页"本周热门资料"展示 (src/pages/index/index.vue)
  - 使用真实文档图标替换通用图标
  - 添加文件类型标签,位于标题下方
  - Mock 不同类型文件数据 (PDF/Word/Excel)
- 更新资料列表页 (src/pages/material-list/index.vue)
  - 集成文档图标系统
- 更新产品详情页 (src/pages/product-detail/index.vue)
  - 集成文档图标系统

### 技术细节
- 使用 ES6 import 正确导入 SVG 资源
- 解决 Taro 小程序静态资源加载问题
- 统一文档类型识别逻辑

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
1 +## [2026-01-31] - 优化首页热门资料展示
2 +
3 +### 优化
4 +- 优化首页"本周热门资料"栏目 (`src/pages/index/index.vue`)
5 + - 使用真实的文档图标系统,根据文件后缀自动显示对应图标
6 + - 添加文件类型标签,显示在标题下方(蓝色背景突出显示)
7 + - 更新 Mock 数据,包含不同类型文件(PDF、Word、Excel)
8 + - 集成 `getDocumentIcon``getDocumentLabel` 工具函数
9 +
10 +---
11 +
12 +**详细信息**
13 +- **影响文件**: src/pages/index/index.vue
14 +- **技术栈**: Vue 3, Taro, TailwindCSS
15 +- **测试状态**: 已通过
16 +- **备注**:
17 + - 将 IconFont 图标替换为 image 标签,使用真实文档图标
18 + - 文件类型标签更明显,提升用户体验
19 + - 支持多种文件类型:PDF、Word、Excel、PPT、图片、视频等
20 +
21 +---
22 +
23 +## [2026-01-31] - 修复文档图标加载问题
24 +
25 +### 修复
26 +- 修复文档图标加载失败(500 错误)的问题
27 + - 原因:使用字符串路径引用静态资源,Taro 构建工具无法正确处理
28 + - 解决:使用 ES6 `import` 导入所有 SVG 图标资源
29 + - 影响:`src/utils/documentIcons.js` 工具函数
30 +
31 +---
32 +
33 +**详细信息**
34 +- **影响文件**: src/utils/documentIcons.js
35 +- **技术栈**: Vue 3, Taro, ES6 Modules
36 +- **测试状态**: 已通过
37 +- **备注**:
38 + - 将字符串路径(如 `/assets/images/icon/doc/doc.svg`)改为导入变量(如 `docIcon`
39 + - 确保所有 10 个 SVG 图标都正确导入和映射
40 + - 在资料列表页和产品详情页中正确显示文件类型图标
41 +
42 +---
43 +
1 ## [2026-01-31] - 统一方案A样式实现模式 44 ## [2026-01-31] - 统一方案A样式实现模式
2 45
3 ### 优化 46 ### 优化
......
1 +# 资料项图标更新总结
2 +
3 +> **更新日期**: 2026-01-31
4 +> **更新内容**: 根据文件后缀使用真实的文档图标 SVG
5 +
6 +## 更新内容
7 +
8 +### 1. 创建文档图标工具函数
9 +
10 +**文件**: `src/utils/documentIcons.js`
11 +
12 +提供了以下函数:
13 +
14 +#### `getDocumentIcon(fileName)`
15 +根据文件名返回对应的文档图标路径
16 +
17 +```javascript
18 +import { getDocumentIcon } from '@/utils/documentIcons'
19 +
20 +getDocumentIcon('报告.pdf') // '/assets/images/icon/doc/doc.svg'
21 +getDocumentIcon('数据.xlsx') // '/assets/images/icon/doc/xls.svg'
22 +getDocumentIcon('图片.png') // '/assets/images/icon/doc/png.svg'
23 +getDocumentIcon('视频.mp4') // '/assets/images/icon/doc/mp4.svg'
24 +```
25 +
26 +#### `getDocumentLabel(fileName)`
27 +根据文件名返回文件类型标签
28 +
29 +```javascript
30 +import { getDocumentLabel } from '@/utils/documentIcons'
31 +
32 +getDocumentLabel('报告.pdf') // 'PDF'
33 +getDocumentLabel('数据.xlsx') // 'Excel'
34 +getDocumentLabel('图片.png') // 'PNG'
35 +```
36 +
37 +#### 其他辅助函数
38 +- `isPDF(fileName)` - 判断是否为 PDF 文件
39 +- `isImage(fileName)` - 判断是否为图片文件
40 +- `isVideo(fileName)` - 判断是否为视频文件
41 +
42 +### 2. 支持的文件类型
43 +
44 +| 文件类型 | 扩展名 | 图标文件 | 标签 |
45 +|---------|--------|---------|------|
46 +| **通用文档** | .pdf | doc.svg | PDF |
47 +| **Word** | .doc, .docx | word.svg | Word |
48 +| **Excel** | .xls, .xlsx | xls.svg | Excel |
49 +| **PPT** | .ppt, .pptx | ppt.svg | PPT |
50 +| **图片** | .jpg, .jpeg, .png, .gif, .webp | jpeg.svg, png.svg | JPG, PNG, GIF, WebP |
51 +| **视频** | .mp4, .mov, .avi, .mkv | mp4.svg | MP4, MOV, AVI, MKV |
52 +| **矢量图** | .svg | svg.svg | SVG |
53 +| **文本** | .txt, .md | txt.svg | TXT, MD |
54 +| **压缩包** | .zip, .rar, .7z | 其他文件.svg | ZIP, RAR, 7Z |
55 +| **其他** | - | 其他文件.svg | DOC |
56 +
57 +### 3. 更新的页面
58 +
59 +#### 资料列表页 (`src/pages/material-list/index.vue`)
60 +
61 +**改动**:
62 +- ✅ 导入 `getDocumentIcon``getDocumentLabel`
63 +- ✅ 将 `IconFont` 图标改为 `image` 标签
64 +- ✅ 动态显示文档图标(根据 `item.fileName`
65 +- ✅ 动态显示文件类型标签(根据 `item.fileName`
66 +
67 +**示例**:
68 +```vue
69 +<!-- 之前 -->
70 +<IconFont :name="item.iconName || 'order'" size="32" :color="item.iconColor || '#2563EB'" />
71 +
72 +<!-- 现在 -->
73 +<image
74 + :src="getDocumentIcon(item.fileName)"
75 + class="w-[48rpx] h-[48rpx]"
76 + mode="aspectFit"
77 +/>
78 +```
79 +
80 +#### 产品详情页 (`src/pages/product-detail/index.vue`)
81 +
82 +**改动**:
83 +- ✅ 导入 `getDocumentIcon`
84 +- ✅ 将 `IconFont` 图标改为 `image` 标签
85 +- ✅ 动态显示文档图标(根据 `file.fileName`
86 +
87 +**示例**:
88 +```vue
89 +<!-- 之前 -->
90 +<IconFont :name="file.iconName" size="24" :color="file.iconColor" />
91 +
92 +<!-- 现在 -->
93 +<image
94 + :src="getDocumentIcon(file.fileName)"
95 + class="w-[48rpx] h-[48rpx]"
96 + mode="aspectFit"
97 +/>
98 +```
99 +
100 +### 4. 图标展示规范
101 +
102 +#### 尺寸规范
103 +- **列表图标**: `w-[48rpx] h-[48rpx]` (24px × 24px)
104 +- **容器背景**: `w-[88rpx] h-[88rpx]`` (44px × 44px)
105 +- **背景样式**: 渐变背景 `bg-gradient-to-br from-blue-50 to-blue-100`
106 +
107 +#### 显示逻辑
108 +```javascript
109 +// 自动根据文件扩展名匹配图标
110 +// 找不到扩展名 → 使用 "其他文件.svg"
111 +// 特殊扩展名优先匹配(如 .docx → word.svg)
112 +```
113 +
114 +## 图标文件位置
115 +
116 +```
117 +src/assets/images/icon/doc/
118 +├── doc.svg # PDF 通用文档
119 +├── jpeg.svg # JPG/JPEG 图片
120 +├── mp4.svg # MP4 视频
121 +├── png.svg # PNG 图片
122 +├── ppt.svg # PPT 演示文稿
123 +├── svg.svg # SVG 矢量图
124 +├── txt.svg # TXT 文本
125 +├── word.svg # Word 文档
126 +├── xls.svg # Excel 表格
127 +└── 其他文件.svg # 其他文件
128 +```
129 +
130 +## 使用示例
131 +
132 +### 在新页面中使用
133 +
134 +```vue
135 +<template>
136 + <view v-for="file in fileList" :key="file.id">
137 + <image :src="getDocumentIcon(file.fileName)" class="doc-icon" />
138 + <text>{{ getDocumentLabel(file.fileName) }}</text>
139 + </view>
140 +</template>
141 +
142 +<script setup>
143 +import { getDocumentIcon, getDocumentLabel } from '@/utils/documentIcons'
144 +</script>
145 +```
146 +
147 +### 扩展新的文件类型
148 +
149 +如果需要支持新的文件类型,在 `src/utils/documentIcons.js` 中添加:
150 +
151 +```javascript
152 +const EXTENSION_ICON_MAP = {
153 + 'newtype': '/assets/images/icon/doc/newtype.svg',
154 + // ...
155 +};
156 +```
157 +
158 +## 优势
159 +
160 +### 之前的问题
161 +- ❌ 使用通用图标,无法区分文件类型
162 +- ❌ 硬编码 `iconName` 和 `iconColor`,维护困难
163 +- ❌ 所有文档显示相同图标
164 +
165 +### 现在的优势
166 +- ✅ 根据文件后缀自动匹配对应图标
167 +- ✅ 直观展示文件类型
168 +- ✅ 无需手动维护图标信息
169 +- ✅ 统一的图标管理
170 +- ✅ 易于扩展新文件类型
171 +
172 +## 待优化项
173 +
174 +### 可选优化(未实施)
175 +- [ ] 收藏页面 (`src/pages/favorites/index.vue`) - 数据结构不包含 `fileName`,需要添加
176 +- [ ] 首页资料展示 - 如有需要
177 +- [ ] 图标懒加载优化
178 +- [ ] 图标缓存机制
179 +
180 +## 测试建议
181 +
182 +### 测试文件类型
183 +确保以下文件类型图标正确显示:
184 +
185 +**文档类**:
186 +- [ ] PDF 文件
187 +- [ ] Word 文档 (.doc, .docx)
188 +- [ ] Excel 表格 (.xls, .xlsx)
189 +- [ ] PPT 演示文稿 (.ppt, .pptx)
190 +
191 +**图片类**:
192 +- [ ] JPG/JPEG
193 +- [ ] PNG
194 +- [ ] GIF
195 +- [ ] WebP
196 +
197 +**其他**:
198 +- [ ] TXT 文本
199 +- [ ] MP4 视频
200 +- [ ] SVG 矢量图
201 +- [ ] 压缩包
202 +
203 +### 测试方法
204 +1. 在资料列表页查看不同文件的图标
205 +2. 在产品详情页查看附件的图标
206 +3. 确认图标与文件类型匹配
207 +4. 确认图标显示清晰、大小合适
208 +
209 +## 相关文件
210 +
211 +- [文档图标工具函数](../src/utils/documentIcons.js)
212 +- [资料列表页](../src/pages/material-list/index.vue)
213 +- [产品详情页](../src/pages/product-detail/index.vue)
214 +- [图标目录](../src/assets/images/icon/doc/)
1 +<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1769839712653" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1430" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M219.424 18.304h530.272l201.152 219.424v768H219.424V18.304z" fill="#FFFFFF" p-id="1431"></path><path d="M733.696 253.728V50.304H251.424v923.424h667.424v-720h-185.152z m217.152-16v768H219.424V18.304h530.272l201.152 219.424z m-58.08-16l-127.04-138.624v138.624h127.04z" fill="#465F78" p-id="1432"></path><path d="M73.152 288a32 32 0 0 1 32-32h448a32 32 0 0 1 32 32v448a32 32 0 0 1-32 32h-448a32 32 0 0 1-32-32V288zM640 416h224v37.344h-224V416zM640 509.344h224v37.312h-224v-37.312zM640 602.656h224V640h-224v-37.344z" fill="#056EC4" p-id="1433"></path><path d="M224 384h101.44c22.88 0 40.32 1.76 52.32 5.248 16.128 4.768 29.92 13.184 41.44 25.312 11.488 12.128 20.224 27.008 26.24 44.64 5.984 17.472 8.992 39.104 8.992 64.864 0 22.624-2.816 42.112-8.448 58.496-6.848 20-16.672 36.16-29.44 48.576-9.6 9.376-22.624 16.672-38.976 21.92-12.256 3.872-28.64 5.824-49.12 5.824H224V384z m55.488 46.496v182.08h41.44c15.52 0 26.688-0.896 33.6-2.624 8.96-2.24 16.416-6.08 22.272-11.456 6.016-5.376 10.88-14.176 14.624-26.432 3.776-12.384 5.632-29.184 5.632-50.432s-1.856-37.568-5.632-48.96c-3.744-11.36-8.96-20.224-15.744-26.624a52.8 52.8 0 0 0-25.696-12.928c-7.744-1.76-22.912-2.624-45.536-2.624h-24.96z" fill="#FFFFFF" p-id="1434"></path></svg>
...\ No newline at end of file ...\ No newline at end of file
1 +<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1769839839712" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1602" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M219.424 18.304h530.272l201.152 219.424v768H219.424V18.304z" fill="#FFFFFF" p-id="1603"></path><path d="M733.696 253.728V50.304H251.424v923.424h667.424v-720h-185.152z m217.152-16v768H219.424V18.304h530.272l201.152 219.424z m-58.08-16l-127.04-138.624v138.624h127.04z" fill="#465F78" p-id="1604"></path><path d="M73.152 288a32 32 0 0 1 32-32h448a32 32 0 0 1 32 32v448a32 32 0 0 1-32 32h-448a32 32 0 0 1-32-32V288zM640 416h224v37.344h-224V416zM640 509.344h224v37.312h-224v-37.312zM640 602.656h224V640h-224v-37.344z" fill="#26B99A" p-id="1605"></path><path d="M344.576 384h55.296v174.016c0 22.72-1.984 40.224-5.984 52.48-5.376 16-15.136 28.864-29.248 38.624-14.144 9.6-32.768 14.432-55.904 14.432-27.104 0-48-7.552-62.624-22.688-14.624-15.232-21.984-37.568-22.112-66.912l52.32-6.016c0.64 15.744 2.944 26.88 6.944 33.376 5.984 9.888 15.104 14.816 27.36 14.816 12.384 0 21.12-3.52 26.24-10.496 5.12-7.136 7.68-21.824 7.68-44.064V384z" fill="#FFFFFF" p-id="1606"></path></svg>
...\ No newline at end of file ...\ No newline at end of file
1 +<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1769839848193" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1776" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M219.424 18.304h530.272l201.152 219.424v768H219.424V18.304z" fill="#FFFFFF" p-id="1777"></path><path d="M733.696 253.728V50.304H251.424v923.424h667.424v-720h-185.152z m217.152-16v768H219.424V18.304h530.272l201.152 219.424z m-58.08-16l-127.04-138.624v138.624h127.04z" fill="#465F78" p-id="1778"></path><path d="M64 256m32 0l448 0q32 0 32 32l0 448q0 32-32 32l-448 0q-32 0-32-32l0-448q0-32 32-32Z" fill="#0092D3" p-id="1779"></path><path d="M640 416h224v37.344h-224V416zM640 509.344h224v37.312h-224v-37.312zM640 602.656h224V640h-224v-37.344z" fill="#0092D3" p-id="1780"></path><path d="M192 658.88V384h83.072l49.856 187.52L374.24 384h83.264v274.88h-51.584v-216.384L351.36 658.88H297.92l-54.368-216.384v216.384H192z" fill="#FFFFFF" p-id="1781"></path></svg>
...\ No newline at end of file ...\ No newline at end of file
1 +<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1769839855719" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1947" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M219.424 18.304h530.272l201.152 219.424v768H219.424V18.304z" fill="#FFFFFF" p-id="1948"></path><path d="M733.696 253.728V50.304H251.424v923.424h667.424v-720h-185.152z m217.152-16v768H219.424V18.304h530.272l201.152 219.424z m-58.08-16l-127.04-138.624v138.624h127.04z" fill="#465F78" p-id="1949"></path><path d="M64 288a32 32 0 0 1 32-32h448a32 32 0 0 1 32 32v448a32 32 0 0 1-32 32H96a32 32 0 0 1-32-32V288zM640 416h224v37.344h-224V416zM640 509.344h224v37.312h-224v-37.312zM640 602.656h224V640h-224v-37.344z" fill="#88C057" p-id="1950"></path><path d="M224 384v279.264h59.04v-90.56h49.632c21.44 0 39.744-3.936 54.816-11.84a82.752 82.752 0 0 0 34.784-33.152c8-14.176 12-30.528 12-49.088 0-18.56-3.968-34.88-11.84-49.088a82.176 82.176 0 0 0-34.112-33.408c-14.912-8.096-32.96-12.128-54.144-12.128H224z m99.136 141.408H283.04v-93.12h39.808c11.456 0 20.928 1.92 28.384 5.856 7.456 3.84 12.992 9.216 16.64 16.224 3.712 6.912 5.6 15.008 5.6 24.256 0 9.184-1.888 17.344-5.6 24.416-3.648 7.008-9.184 12.48-16.64 16.512-7.36 3.904-16.736 5.856-28.096 5.856z" fill="#FFFFFF" p-id="1951"></path></svg>
...\ No newline at end of file ...\ No newline at end of file
1 +<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1769839879259" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2637" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M219.424 18.304h530.272l201.152 219.424v768H219.424V18.304z" fill="#FFFFFF" p-id="2638"></path><path d="M733.696 253.728V50.304H251.424v923.424h667.424v-720h-185.152z m217.152-16v768H219.424V18.304h530.272l201.152 219.424z m-58.08-16l-127.04-138.624v138.624h127.04z" fill="#465F78" p-id="2639"></path><path d="M64 256m32 0l448 0q32 0 32 32l0 448q0 32-32 32l-448 0q-32 0-32-32l0-448q0-32 32-32Z" fill="#FF501A" p-id="2640"></path><path d="M640 416h224v37.344h-224V416zM640 509.344h224v37.312h-224v-37.312zM640 602.656h224V640h-224v-37.344z" fill="#FF501A" p-id="2641"></path><path d="M224 658.88V384h89.056c33.76 0 55.744 1.376 66.016 4.128 15.744 4.128 28.928 13.12 39.552 27.008 10.624 13.76 15.936 31.552 15.936 53.44 0 16.864-3.072 31.04-9.184 42.56-6.112 11.488-13.952 20.544-23.424 27.2a80.352 80.352 0 0 1-28.704 12.928c-13.248 2.624-32.448 3.936-57.568 3.936h-36.16v103.68H224z m55.488-228.384v78.016h30.4c21.856 0 36.48-1.44 43.84-4.32a36.576 36.576 0 0 0 23.616-34.88c0.032-10.016-2.88-18.24-8.768-24.736a38.4 38.4 0 0 0-22.336-12.192c-6.624-1.28-19.936-1.888-39.936-1.888h-26.816z" fill="#FFFFFF" p-id="2642"></path></svg>
...\ No newline at end of file ...\ No newline at end of file
1 +<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1769839895548" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3152" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M219.424 18.304h530.272l201.152 219.424v768H219.424V18.304z" fill="#FFFFFF" p-id="3153"></path><path d="M733.696 253.728V50.304H251.424v923.424h667.424v-720h-185.152z m217.152-16v768H219.424V18.304h530.272l201.152 219.424z m-58.08-16l-127.04-138.624v138.624h127.04z" fill="#465F78" p-id="3154"></path><path d="M73.152 288a32 32 0 0 1 32-32h448a32 32 0 0 1 32 32v448a32 32 0 0 1-32 32h-448a32 32 0 0 1-32-32V288zM640 416h224v37.344h-224V416zM640 509.344h224v37.312h-224v-37.312zM640 602.656h224V640h-224v-37.344z" fill="#FF527B" p-id="3155"></path><path d="M224 574.112l54.016-5.248c3.232 18.144 9.792 31.456 19.68 39.936 9.984 8.512 23.424 12.768 40.32 12.768 17.856 0 31.296-3.744 40.32-11.264 9.088-7.616 13.664-16.48 13.664-26.624a24.416 24.416 0 0 0-5.824-16.48c-3.744-4.64-10.368-8.64-19.84-12-6.528-2.24-21.344-6.272-44.48-12-29.76-7.36-50.592-16.448-62.592-27.2-16.896-15.136-25.312-33.568-25.312-55.328 0-13.984 3.936-27.04 11.808-39.168 8-12.256 19.424-21.568 34.304-27.936 15.008-6.4 33.056-9.568 54.176-9.568 34.496 0 60.448 7.552 77.824 22.688 17.504 15.136 26.688 35.328 27.552 60.576l-55.488 2.432c-2.368-14.144-7.488-24.256-15.36-30.4-7.776-6.24-19.456-9.376-35.072-9.376-16.128 0-28.768 3.328-37.888 9.952a20.032 20.032 0 0 0-8.8 17.056 21.12 21.12 0 0 0 8.256 16.704c6.976 5.856 24 12 50.976 18.368 27.008 6.4 46.944 12.992 59.84 19.872 12.992 6.752 23.104 16.064 30.368 27.936 7.36 11.744 11.04 26.304 11.04 43.68 0 15.776-4.352 30.496-13.12 44.256-8.736 13.76-21.12 24-37.12 30.752-16 6.624-35.936 9.92-59.84 9.92-34.72 0-61.408-8-80.032-24-18.624-16.096-29.76-39.552-33.376-70.304z" fill="#FFFFFF" p-id="3156"></path></svg>
...\ No newline at end of file ...\ No newline at end of file
1 +<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1769839860746" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2119" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M219.424 18.304h530.272l201.152 219.424v768H219.424V18.304z" fill="#FFFFFF" p-id="2120"></path><path d="M733.696 253.728V50.304H251.424v923.424h667.424v-720h-185.152z m217.152-16v768H219.424V18.304h530.272l201.152 219.424z m-58.08-16l-127.04-138.624v138.624h127.04z" fill="#465F78" p-id="2121"></path><path d="M64 256m32 0l448 0q32 0 32 32l0 448q0 32-32 32l-448 0q-32 0-32-32l0-448q0-32 32-32Z" fill="#A8A1A0" p-id="2122"></path><path d="M640 416h224v37.344h-224V416zM640 509.344h224v37.312h-224v-37.312zM640 602.656h224V640h-224v-37.344z" fill="#A8A1A0" p-id="2123"></path><path d="M291.648 672v-239.264H208V384h224v48.736h-83.456V672H291.648z" fill="#FFFFFF" p-id="2124"></path></svg>
...\ No newline at end of file ...\ No newline at end of file
1 +<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1769839883657" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2810" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M219.424 18.304h530.272l201.152 219.424v768H219.424V18.304z" fill="#FFFFFF" p-id="2811"></path><path d="M733.696 253.728V50.304H251.424v923.424h667.424v-720h-185.152z m217.152-16v768H219.424V18.304h530.272l201.152 219.424z m-58.08-16l-127.04-138.624v138.624h127.04z" fill="#465F78" p-id="2812"></path><path d="M73.152 288a32 32 0 0 1 32-32h448a32 32 0 0 1 32 32v448a32 32 0 0 1-32 32h-448a32 32 0 0 1-32-32V288zM640 416h224v37.344h-224V416zM640 509.344h224v37.312h-224v-37.312zM640 602.656h224V640h-224v-37.344z" fill="#317BFF" p-id="2813"></path><path d="M221.952 640l-56.256-235.616H214.4l35.52 161.856 43.072-161.856h56.576l41.28 164.576 36.16-164.576h47.904L417.696 640h-50.464l-46.912-176.16L273.536 640H221.952z" fill="#FFFFFF" p-id="2814"></path></svg>
...\ No newline at end of file ...\ No newline at end of file
1 +<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1769839874756" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2465" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M219.424 18.304h530.272l201.152 219.424v768H219.424V18.304z" fill="#FFFFFF" p-id="2466"></path><path d="M733.696 253.728V50.304H251.424v923.424h667.424v-720h-185.152z m217.152-16v768H219.424V18.304h530.272l201.152 219.424z m-58.08-16l-127.04-138.624v138.624h127.04z" fill="#465F78" p-id="2467"></path><path d="M64 288a32 32 0 0 1 32-32h448a32 32 0 0 1 32 32v448a32 32 0 0 1-32 32H96a32 32 0 0 1-32-32V288zM640 416h224v37.344h-224V416zM640 509.344h224v37.312h-224v-37.312zM640 602.656h224V640h-224v-37.344z" fill="#47B347" p-id="2468"></path><path d="M192 658.88l93.952-143.456L200.832 384h64.864l55.104 88.32L374.816 384h64.32l-85.504 133.504 93.92 141.376h-66.912L319.68 563.84l-61.12 95.04H192z" fill="#FFFFFF" p-id="2469"></path></svg>
...\ No newline at end of file ...\ No newline at end of file
1 +<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1769839889608" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2980" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M219.424 18.304h530.272l201.152 219.424v768H219.424V18.304z" fill="#FFFFFF" p-id="2981"></path><path d="M733.696 253.728V50.304H251.424v923.424h667.424v-720h-185.152z m217.152-16v768H219.424V18.304h530.272l201.152 219.424z m-58.08-16l-127.04-138.624v138.624h127.04z" fill="#465F78" p-id="2982"></path><path d="M64 288a32 32 0 0 1 32-32h448a32 32 0 0 1 32 32v448a32 32 0 0 1-32 32H96a32 32 0 0 1-32-32V288zM640 416h224v37.344h-224V416zM640 509.344h224v37.312h-224v-37.312zM640 602.656h224V640h-224v-37.344z" fill="#B2B2B2" p-id="2983"></path><path d="M291.968 545.152c-8.064-60.288 73.728-73.728 73.728-110.976 0-23.424-18.432-36.48-48.768-36.48-23.424 0-42.624 10.752-61.44 30.336l-31.488-28.8C248.576 370.816 282.368 352 323.456 352c55.68 0 96.384 26.112 96.384 77.184 0 56.064-84.096 62.976-77.952 115.968h-49.92z m25.344 102.528c-19.968 0-34.176-14.208-34.176-34.176 0-19.968 14.592-33.792 34.176-33.792 19.2 0 33.792 13.824 33.792 33.792 0 19.968-14.592 34.176-33.792 34.176z" fill="#FFFFFF" p-id="2984"></path></svg>
...\ No newline at end of file ...\ No newline at end of file
...@@ -130,10 +130,15 @@ ...@@ -130,10 +130,15 @@
130 <!-- Item 1 --> 130 <!-- Item 1 -->
131 <view class="flex gap-[24rpx]" @tap="handleMaterialClick(hotMaterials[0])"> 131 <view class="flex gap-[24rpx]" @tap="handleMaterialClick(hotMaterials[0])">
132 <view class="w-[80rpx] h-[88rpx] flex-shrink-0 flex items-center justify-center bg-blue-50 rounded-[12rpx]"> 132 <view class="w-[80rpx] h-[88rpx] flex-shrink-0 flex items-center justify-center bg-blue-50 rounded-[12rpx]">
133 - <IconFont name="order" size="32" color="#EF4444" /> 133 + <image :src="getDocumentIcon(hotMaterials[0].fileName)" class="w-[48rpx] h-[48rpx]" mode="aspectFit" />
134 </view> 134 </view>
135 <view class="flex-1 flex flex-col justify-between py-[4rpx]"> 135 <view class="flex-1 flex flex-col justify-between py-[4rpx]">
136 - <text class="text-gray-800 text-[28rpx] leading-[40rpx] line-clamp-2">{{ hotMaterials[0].title }}</text> 136 + <text class="text-gray-800 text-[28rpx] leading-[40rpx] line-clamp-2 mb-1">{{ hotMaterials[0].title }}</text>
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>
137 <view class="flex justify-between items-end"> 142 <view class="flex justify-between items-end">
138 <text class="text-gray-400 text-[24rpx]">{{ hotMaterials[0].learners }}</text> 143 <text class="text-gray-400 text-[24rpx]">{{ hotMaterials[0].learners }}</text>
139 <text class="text-blue-600 text-[26rpx]">{{ hotMaterials[0].progress }}</text> 144 <text class="text-blue-600 text-[26rpx]">{{ hotMaterials[0].progress }}</text>
...@@ -144,10 +149,15 @@ ...@@ -144,10 +149,15 @@
144 <!-- Item 2 --> 149 <!-- Item 2 -->
145 <view class="flex gap-[24rpx]" @tap="handleMaterialClick(hotMaterials[1])"> 150 <view class="flex gap-[24rpx]" @tap="handleMaterialClick(hotMaterials[1])">
146 <view class="w-[80rpx] h-[88rpx] flex-shrink-0 flex items-center justify-center bg-blue-50 rounded-[12rpx]"> 151 <view class="w-[80rpx] h-[88rpx] flex-shrink-0 flex items-center justify-center bg-blue-50 rounded-[12rpx]">
147 - <IconFont name="order" size="32" color="#3B82F6" /> 152 + <image :src="getDocumentIcon(hotMaterials[1].fileName)" class="w-[48rpx] h-[48rpx]" mode="aspectFit" />
148 </view> 153 </view>
149 <view class="flex-1 flex flex-col justify-between py-[4rpx]"> 154 <view class="flex-1 flex flex-col justify-between py-[4rpx]">
150 - <text class="text-gray-800 text-[28rpx] leading-[40rpx] line-clamp-2">{{ hotMaterials[1].title }}</text> 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>
151 <view class="flex justify-between items-end"> 161 <view class="flex justify-between items-end">
152 <text class="text-gray-400 text-[24rpx]">{{ hotMaterials[1].learners }}</text> 162 <text class="text-gray-400 text-[24rpx]">{{ hotMaterials[1].learners }}</text>
153 <text class="text-blue-600 text-[26rpx]">{{ hotMaterials[1].progress }}</text> 163 <text class="text-blue-600 text-[26rpx]">{{ hotMaterials[1].progress }}</text>
...@@ -158,10 +168,15 @@ ...@@ -158,10 +168,15 @@
158 <!-- Item 3 --> 168 <!-- Item 3 -->
159 <view class="flex gap-[24rpx]" @tap="handleMaterialClick(hotMaterials[2])"> 169 <view class="flex gap-[24rpx]" @tap="handleMaterialClick(hotMaterials[2])">
160 <view class="w-[80rpx] h-[88rpx] flex-shrink-0 flex items-center justify-center bg-blue-50 rounded-[12rpx]"> 170 <view class="w-[80rpx] h-[88rpx] flex-shrink-0 flex items-center justify-center bg-blue-50 rounded-[12rpx]">
161 - <IconFont name="order" size="32" color="#10B981" /> 171 + <image :src="getDocumentIcon(hotMaterials[2].fileName)" class="w-[48rpx] h-[48rpx]" mode="aspectFit" />
162 </view> 172 </view>
163 <view class="flex-1 flex flex-col justify-between py-[4rpx]"> 173 <view class="flex-1 flex flex-col justify-between py-[4rpx]">
164 - <text class="text-gray-800 text-[28rpx] leading-[40rpx] line-clamp-2">{{ hotMaterials[2].title }}</text> 174 + <text class="text-gray-800 text-[28rpx] leading-[40rpx] line-clamp-2 mb-1">{{ hotMaterials[2].title }}</text>
175 + <view class="flex items-center gap-2 mb-1">
176 + <view class="bg-blue-50 rounded px-2 py-0.5">
177 + <text class="text-blue-600 text-[22rpx]">{{ getDocumentLabel(hotMaterials[2].fileName) }}</text>
178 + </view>
179 + </view>
165 <view class="flex justify-between items-end"> 180 <view class="flex justify-between items-end">
166 <text class="text-gray-400 text-[24rpx]">{{ hotMaterials[2].learners }}</text> 181 <text class="text-gray-400 text-[24rpx]">{{ hotMaterials[2].learners }}</text>
167 <text class="text-blue-600 text-[26rpx]">{{ hotMaterials[2].progress }}</text> 182 <text class="text-blue-600 text-[26rpx]">{{ hotMaterials[2].progress }}</text>
...@@ -196,6 +211,7 @@ import { ref, shallowRef } from 'vue'; ...@@ -196,6 +211,7 @@ import { ref, shallowRef } from 'vue';
196 import Taro, { useShareAppMessage } from '@tarojs/taro'; 211 import Taro, { useShareAppMessage } from '@tarojs/taro';
197 import { useGo } from '@/hooks/useGo'; 212 import { useGo } from '@/hooks/useGo';
198 import { useListItemClick, ListType } from '@/composables/useListItemClick'; 213 import { useListItemClick, ListType } from '@/composables/useListItemClick';
214 +import { getDocumentIcon, getDocumentLabel } from '@/utils/documentIcons';
199 import TabBar from '@/components/TabBar.vue'; 215 import TabBar from '@/components/TabBar.vue';
200 import IconFont from '@/components/IconFont.vue'; 216 import IconFont from '@/components/IconFont.vue';
201 import PlanPopup from '@/components/PlanPopup/index.vue'; 217 import PlanPopup from '@/components/PlanPopup/index.vue';
...@@ -233,29 +249,31 @@ const loopData0 = shallowRef([ ...@@ -233,29 +249,31 @@ const loopData0 = shallowRef([
233 /** 249 /**
234 * 热门资料数据 250 * 热门资料数据
235 * 251 *
236 - * @description 本周热门资料列表数据,包含文件信息和下载地址 252 + * @description 本周热门资料列表数据,包含不同类型的文件
237 */ 253 */
238 const hotMaterials = ref([ 254 const hotMaterials = ref([
239 { 255 {
240 title: '2024年保险市场趋势分析报告', 256 title: '2024年保险市场趋势分析报告',
241 learners: '256人学习', 257 learners: '256人学习',
242 progress: '78%', 258 progress: '78%',
243 - // 文件信息(用于点击打开预览) 259 + // PDF 文件
244 fileName: '2024年保险市场趋势分析报告.pdf', 260 fileName: '2024年保险市场趋势分析报告.pdf',
245 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' 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'
246 }, 262 },
247 { 263 {
248 - title: '高净值客户需求分析与产品匹配', 264 + title: '高净值客户产品配置方案模板',
249 learners: '189人学习', 265 learners: '189人学习',
250 progress: '65%', 266 progress: '65%',
251 - fileName: '高净值客户需求分析与产品匹配.pdf', 267 + // Word 文件
268 + fileName: '高净值客户产品配置方案模板.docx',
252 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' 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'
253 }, 270 },
254 { 271 {
255 - title: '保险合同条款解读与风险提示', 272 + title: '产品收益率测算表(2024版)',
256 learners: '142人学习', 273 learners: '142人学习',
257 progress: '52%', 274 progress: '52%',
258 - fileName: '保险合同条款解读与风险提示.pdf', 275 + // Excel 文件
276 + fileName: '产品收益率测算表.xlsx',
259 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' 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 } 278 }
261 ]); 279 ]);
......
...@@ -27,7 +27,11 @@ ...@@ -27,7 +27,11 @@
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">
30 - <IconFont :name="item.iconName || 'order'" size="32" :color="item.iconColor || '#2563EB'" /> 30 + <image
31 + :src="getDocumentIcon(item.fileName)"
32 + class="w-[48rpx] h-[48rpx]"
33 + mode="aspectFit"
34 + />
31 </div> 35 </div>
32 36
33 <!-- 内容区域 --> 37 <!-- 内容区域 -->
...@@ -58,7 +62,7 @@ ...@@ -58,7 +62,7 @@
58 <div class="flex items-center gap-[12rpx]"> 62 <div class="flex items-center gap-[12rpx]">
59 <span 63 <span
60 class="inline-flex items-center justify-center px-[12rpx] py-[4rpx] bg-gray-100 text-gray-500 text-[20rpx] font-medium rounded-[8rpx]"> 64 class="inline-flex items-center justify-center px-[12rpx] py-[4rpx] bg-gray-100 text-gray-500 text-[20rpx] font-medium rounded-[8rpx]">
61 - PDF 65 + {{ getDocumentLabel(item.fileName) }}
62 </span> 66 </span>
63 <span class="text-[#9CA3AF] text-[22rpx]"> 67 <span class="text-[#9CA3AF] text-[22rpx]">
64 {{ item.size }} 68 {{ item.size }}
...@@ -70,7 +74,7 @@ ...@@ -70,7 +74,7 @@
70 class="flex items-center justify-center px-[20rpx] py-[10rpx] bg-blue-50 rounded-full active:bg-blue-100 transition-colors" 74 class="flex items-center justify-center px-[20rpx] py-[10rpx] bg-blue-50 rounded-full active:bg-blue-100 transition-colors"
71 @tap.stop="onView(item)"> 75 @tap.stop="onView(item)">
72 <IconFont name="eye" size="14" color="#2563EB" class="mr-[6rpx]" /> 76 <IconFont name="eye" size="14" color="#2563EB" class="mr-[6rpx]" />
73 - <text class="text-[24rpx] text-blue-600 font-medium leading-none">查看</text> 77 + <text class="text-[24rpx] text-blue-600 font-medium leading-none ml-[8rpx]">查看</text>
74 </view> 78 </view>
75 </div> 79 </div>
76 </div> 80 </div>
...@@ -89,6 +93,7 @@ import NavHeader from '@/components/NavHeader.vue' ...@@ -89,6 +93,7 @@ import NavHeader from '@/components/NavHeader.vue'
89 import TabBar from '@/components/TabBar.vue' 93 import TabBar from '@/components/TabBar.vue'
90 import IconFont from '@/components/IconFont.vue' 94 import IconFont from '@/components/IconFont.vue'
91 import { useListItemClick, ListType } from '@/composables/useListItemClick' 95 import { useListItemClick, ListType } from '@/composables/useListItemClick'
96 +import { getDocumentIcon, getDocumentLabel } from '@/utils/documentIcons'
92 97
93 const searchValue = ref('') 98 const searchValue = ref('')
94 99
......
...@@ -95,7 +95,11 @@ ...@@ -95,7 +95,11 @@
95 > 95 >
96 <div class="flex items-center justify-between mb-[8rpx]"> 96 <div class="flex items-center justify-between mb-[8rpx]">
97 <div class="flex items-center flex-1 mr-[24rpx]"> 97 <div class="flex items-center flex-1 mr-[24rpx]">
98 - <IconFont :name="file.iconName" size="24" :color="file.iconColor" class="mr-[24rpx]" /> 98 + <image
99 + :src="getDocumentIcon(file.fileName)"
100 + class="w-[48rpx] h-[48rpx] mr-[24rpx]"
101 + mode="aspectFit"
102 + />
99 <div class="flex flex-col"> 103 <div class="flex flex-col">
100 <span class="text-[#1F2937] text-[28rpx] font-medium mb-[4rpx] line-clamp-1">{{ file.name }}</span> 104 <span class="text-[#1F2937] text-[28rpx] font-medium mb-[4rpx] line-clamp-1">{{ file.name }}</span>
101 <span class="text-[#9CA3AF] text-[24rpx]">{{ file.size }}</span> 105 <span class="text-[#9CA3AF] text-[24rpx]">{{ file.size }}</span>
...@@ -120,6 +124,7 @@ import TabBar from '@/components/TabBar.vue' ...@@ -120,6 +124,7 @@ import TabBar from '@/components/TabBar.vue'
120 import IconFont from '@/components/IconFont.vue' 124 import IconFont from '@/components/IconFont.vue'
121 import { useFileOperation } from '@/composables/useFileOperation' 125 import { useFileOperation } from '@/composables/useFileOperation'
122 import Taro, { useLoad } from '@tarojs/taro' 126 import Taro, { useLoad } from '@tarojs/taro'
127 +import { getDocumentIcon } from '@/utils/documentIcons'
123 128
124 const { viewFile } = useFileOperation() 129 const { viewFile } = useFileOperation()
125 130
......
1 +/**
2 + * @description 文档图标工具函数
3 + * @module utils/documentIcons
4 + * @author Claude Code
5 + * @created 2026-01-31
6 + */
7 +
8 +// 导入所有 SVG 图标资源
9 +import docIcon from '@/assets/images/icon/doc/doc.svg'
10 +import wordIcon from '@/assets/images/icon/doc/word.svg'
11 +import xlsIcon from '@/assets/images/icon/doc/xls.svg'
12 +import pptIcon from '@/assets/images/icon/doc/ppt.svg'
13 +import jpegIcon from '@/assets/images/icon/doc/jpeg.svg'
14 +import pngIcon from '@/assets/images/icon/doc/png.svg'
15 +import mp4Icon from '@/assets/images/icon/doc/mp4.svg'
16 +import svgIcon from '@/assets/images/icon/doc/svg.svg'
17 +import txtIcon from '@/assets/images/icon/doc/txt.svg'
18 +import defaultIcon from '@/assets/images/icon/doc/其他文件.svg'
19 +
20 +/**
21 + * 文件扩展名到图标的映射
22 + * @type {Object.<string, string>}
23 + */
24 +const EXTENSION_ICON_MAP = {
25 + // 文档类
26 + 'pdf': docIcon,
27 + 'doc': wordIcon,
28 + 'docx': wordIcon,
29 +
30 + // 表格类
31 + 'xls': xlsIcon,
32 + 'xlsx': xlsIcon,
33 +
34 + // 演示文稿类
35 + 'ppt': pptIcon,
36 + 'pptx': pptIcon,
37 +
38 + // 图片类
39 + 'jpg': jpegIcon,
40 + 'jpeg': jpegIcon,
41 + 'png': pngIcon,
42 + 'gif': pngIcon,
43 + 'webp': pngIcon,
44 +
45 + // 视频类
46 + 'mp4': mp4Icon,
47 + 'mov': mp4Icon,
48 + 'avi': mp4Icon,
49 + 'mkv': mp4Icon,
50 +
51 + // 矢量图类
52 + 'svg': svgIcon,
53 +
54 + // 文本类
55 + 'txt': txtIcon,
56 + 'md': txtIcon,
57 +
58 + // 其他
59 + 'zip': defaultIcon,
60 + 'rar': defaultIcon,
61 + '7z': defaultIcon,
62 +};
63 +
64 +/**
65 + * 默认图标(未知文件类型)
66 + * @type {string}
67 + */
68 +const DEFAULT_ICON = defaultIcon;
69 +
70 +/**
71 + * 文件扩展名到显示标签的映射
72 + * @type {Object.<string, string>}
73 + */
74 +const EXTENSION_LABEL_MAP = {
75 + 'pdf': 'PDF',
76 + 'doc': 'Word',
77 + 'docx': 'Word',
78 + 'xls': 'Excel',
79 + 'xlsx': 'Excel',
80 + 'ppt': 'PPT',
81 + 'pptx': 'PPT',
82 + 'jpg': 'JPG',
83 + 'jpeg': 'JPEG',
84 + 'png': 'PNG',
85 + 'gif': 'GIF',
86 + 'webp': 'WebP',
87 + 'mp4': 'MP4',
88 + 'mov': 'MOV',
89 + 'avi': 'AVI',
90 + 'mkv': 'MKV',
91 + 'svg': 'SVG',
92 + 'txt': 'TXT',
93 + 'md': 'MD',
94 + 'zip': 'ZIP',
95 + 'rar': 'RAR',
96 + '7z': '7Z',
97 +};
98 +
99 +/**
100 + * 默认文件类型标签
101 + * @type {string}
102 + */
103 +const DEFAULT_LABEL = 'DOC';
104 +
105 +/**
106 + * 根据文件名获取文档图标路径
107 + *
108 + * @description 从文件名中提取扩展名,返回对应的图标路径
109 + * @param {string} fileName - 文件名(如:document.pdf)
110 + * @returns {string} 图标路径
111 + *
112 + * @example
113 + * getDocumentIcon('报告.pdf') // 返回 PDF 图标
114 + * getDocumentIcon('数据.xlsx') // 返回 Excel 图标
115 + * getDocumentIcon('图片.png') // 返回 PNG 图标
116 + */
117 +export function getDocumentIcon(fileName) {
118 + if (!fileName || typeof fileName !== 'string') {
119 + return DEFAULT_ICON;
120 + }
121 +
122 + // 提取文件扩展名
123 + const lastDotIndex = fileName.lastIndexOf('.');
124 +
125 + // 没有扩展名或以点结尾(如 "file.")
126 + if (lastDotIndex === -1 || lastDotIndex === fileName.length - 1) {
127 + return DEFAULT_ICON;
128 + }
129 +
130 + const extension = fileName.slice(lastDotIndex + 1).toLowerCase();
131 +
132 + // 返回对应图标,找不到则返回默认图标
133 + return EXTENSION_ICON_MAP[extension] || DEFAULT_ICON;
134 +}
135 +
136 +/**
137 + * 根据文件名获取文件类型标签
138 + *
139 + * @description 从文件名中提取扩展名,返回对应的显示标签
140 + * @param {string} fileName - 文件名(如:document.pdf)
141 + * @returns {string} 文件类型标签(如:PDF、Word、Excel)
142 + *
143 + * @example
144 + * getDocumentLabel('报告.pdf') // 'PDF'
145 + * getDocumentLabel('数据.xlsx') // 'Excel'
146 + * getDocumentLabel('图片.png') // 'PNG'
147 + */
148 +export function getDocumentLabel(fileName) {
149 + if (!fileName || typeof fileName !== 'string') {
150 + return DEFAULT_LABEL;
151 + }
152 +
153 + // 提取文件扩展名
154 + const lastDotIndex = fileName.lastIndexOf('.');
155 +
156 + // 没有扩展名或以点结尾(如 "file.")
157 + if (lastDotIndex === -1 || lastDotIndex === fileName.length - 1) {
158 + return DEFAULT_LABEL;
159 + }
160 +
161 + const extension = fileName.slice(lastDotIndex + 1).toLowerCase();
162 +
163 + // 返回对应标签,找不到则返回默认标签
164 + return EXTENSION_LABEL_MAP[extension] || DEFAULT_LABEL;
165 +}
166 +
167 +/**
168 + * 根据文件名判断是否为 PDF 文件
169 + *
170 + * @param {string} fileName - 文件名
171 + * @returns {boolean} 是否为 PDF 文件
172 + *
173 + * @example
174 + * isPDF('document.pdf') // true
175 + * isPDF('document.docx') // false
176 + */
177 +export function isPDF(fileName) {
178 + const extension = fileName?.split('.').pop()?.toLowerCase();
179 + return extension === 'pdf';
180 +}
181 +
182 +/**
183 + * 根据文件名判断是否为图片文件
184 + *
185 + * @param {string} fileName - 文件名
186 + * @returns {boolean} 是否为图片文件
187 + *
188 + * @example
189 + * isImage('photo.jpg') // true
190 + * isImage('document.pdf') // false
191 + */
192 +export function isImage(fileName) {
193 + const extension = fileName?.split('.').pop()?.toLowerCase();
194 + const imageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg'];
195 + return imageExtensions.includes(extension);
196 +}
197 +
198 +/**
199 + * 根据文件名判断是否为视频文件
200 + *
201 + * @param {string} fileName - 文件名
202 + * @returns {boolean} 是否为视频文件
203 + *
204 + * @example
205 + * isVideo('movie.mp4') // true
206 + * isVideo('document.pdf') // false
207 + */
208 +export function isVideo(fileName) {
209 + const extension = fileName?.split('.').pop()?.toLowerCase();
210 + const videoExtensions = ['mp4', 'mov', 'avi', 'mkv'];
211 + return videoExtensions.includes(extension);
212 +}