feat(FileTree): 为文件树节点添加类型化图标和颜色
- 使用 `#icon` 插槽为树节点添加自定义图标组件 - 重构图标和颜色逻辑,使用 `FILE_TYPE_CONFIG` 对象集中管理文件类型配置 - 为文件夹添加展开/收起状态的不同图标,并使用黄色区分 - 调整树节点标题的字体大小和行高以改善视觉一致性
This commit is contained in:
parent
495a8a0bb4
commit
7b200dcb68
|
|
@ -42,6 +42,12 @@
|
||||||
:load-data="onLoadData"
|
:load-data="onLoadData"
|
||||||
@select="onSelect"
|
@select="onSelect"
|
||||||
>
|
>
|
||||||
|
<template #icon="{ dataRef, expanded }">
|
||||||
|
<component
|
||||||
|
:is="getFileIcon(dataRef.name, dataRef.type, expanded)"
|
||||||
|
:style="{ color: getFileIconColor(dataRef.name, dataRef.type), fontSize: '18px', marginRight: '6px' }"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
<template #title="{ dataRef }">
|
<template #title="{ dataRef }">
|
||||||
<a-dropdown :trigger="['contextmenu']">
|
<a-dropdown :trigger="['contextmenu']">
|
||||||
<span class="tree-node-title" :title="dataRef.name">{{ dataRef.name }}</span>
|
<span class="tree-node-title" :title="dataRef.name">{{ dataRef.name }}</span>
|
||||||
|
|
@ -90,7 +96,20 @@
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed, ref, watch } from 'vue';
|
import { computed, ref, watch } from 'vue';
|
||||||
import { EyeInvisibleOutlined, EyeOutlined, ReloadOutlined, SearchOutlined } from '@ant-design/icons-vue';
|
import {
|
||||||
|
EyeInvisibleOutlined,
|
||||||
|
EyeOutlined,
|
||||||
|
ReloadOutlined,
|
||||||
|
SearchOutlined,
|
||||||
|
FileOutlined,
|
||||||
|
FileMarkdownOutlined,
|
||||||
|
FileImageOutlined,
|
||||||
|
FilePdfOutlined,
|
||||||
|
CodeOutlined,
|
||||||
|
FileTextOutlined,
|
||||||
|
FolderFilled,
|
||||||
|
FolderOpenFilled
|
||||||
|
} from '@ant-design/icons-vue';
|
||||||
import { useFileTree } from '../composables/useFileTree';
|
import { useFileTree } from '../composables/useFileTree';
|
||||||
import { useTabs } from '../composables/useTabs';
|
import { useTabs } from '../composables/useTabs';
|
||||||
import { useConfig } from '../composables/useConfig';
|
import { useConfig } from '../composables/useConfig';
|
||||||
|
|
@ -189,71 +208,51 @@ const onSelect = (keys, { node }) => {
|
||||||
|
|
||||||
const isMdFile = (name) => name && name.toLowerCase().endsWith('.md');
|
const isMdFile = (name) => name && name.toLowerCase().endsWith('.md');
|
||||||
|
|
||||||
const getFileIcon = (fileName) => {
|
const FILE_TYPE_CONFIG = {
|
||||||
if (!fileName) return FileOutlined;
|
md: { icon: FileMarkdownOutlined, color: '#1890ff' },
|
||||||
const ext = fileName.split('.').pop().toLowerCase();
|
markdown: { icon: FileMarkdownOutlined, color: '#1890ff' },
|
||||||
switch (ext) {
|
png: { icon: FileImageOutlined, color: '#722ed1' },
|
||||||
case 'md':
|
jpg: { icon: FileImageOutlined, color: '#722ed1' },
|
||||||
case 'markdown':
|
jpeg: { icon: FileImageOutlined, color: '#722ed1' },
|
||||||
return FileMarkdownOutlined;
|
gif: { icon: FileImageOutlined, color: '#722ed1' },
|
||||||
case 'png':
|
webp: { icon: FileImageOutlined, color: '#722ed1' },
|
||||||
case 'jpg':
|
svg: { icon: FileImageOutlined, color: '#722ed1' },
|
||||||
case 'jpeg':
|
ico: { icon: FileImageOutlined, color: '#722ed1' },
|
||||||
case 'gif':
|
pdf: { icon: FilePdfOutlined, color: '#f5222d' },
|
||||||
case 'webp':
|
js: { icon: CodeOutlined, color: '#52c41a' },
|
||||||
case 'svg':
|
json: { icon: CodeOutlined, color: '#52c41a' },
|
||||||
case 'ico':
|
ts: { icon: CodeOutlined, color: '#52c41a' },
|
||||||
return FileImageOutlined;
|
css: { icon: CodeOutlined, color: '#52c41a' },
|
||||||
case 'pdf':
|
html: { icon: CodeOutlined, color: '#52c41a' },
|
||||||
return FilePdfOutlined;
|
vue: { icon: CodeOutlined, color: '#52c41a' },
|
||||||
case 'js':
|
xml: { icon: CodeOutlined, color: '#52c41a' },
|
||||||
case 'json':
|
yaml: { icon: CodeOutlined, color: '#52c41a' },
|
||||||
case 'ts':
|
yml: { icon: CodeOutlined, color: '#52c41a' },
|
||||||
case 'css':
|
txt: { icon: FileTextOutlined, color: 'inherit' },
|
||||||
case 'html':
|
log: { icon: FileTextOutlined, color: 'inherit' },
|
||||||
case 'vue':
|
|
||||||
case 'xml':
|
|
||||||
case 'yaml':
|
|
||||||
case 'yml':
|
|
||||||
return CodeOutlined;
|
|
||||||
case 'txt':
|
|
||||||
case 'log':
|
|
||||||
return FileTextOutlined;
|
|
||||||
default:
|
|
||||||
return FileOutlined;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const getFileIconColor = (fileName) => {
|
const getFileExtension = (fileName) => {
|
||||||
if (!fileName) return 'inherit';
|
if (!fileName) return '';
|
||||||
const ext = fileName.split('.').pop().toLowerCase();
|
const lastDotIndex = fileName.lastIndexOf('.');
|
||||||
switch (ext) {
|
if (lastDotIndex === -1 || lastDotIndex === 0) return '';
|
||||||
case 'md':
|
return fileName.substring(lastDotIndex + 1).toLowerCase();
|
||||||
case 'markdown':
|
};
|
||||||
return '#1890ff';
|
|
||||||
case 'png':
|
const getFileIcon = (fileName, type, expanded) => {
|
||||||
case 'jpg':
|
if (type === 'directory') {
|
||||||
case 'jpeg':
|
return expanded ? FolderOpenFilled : FolderFilled;
|
||||||
case 'gif':
|
|
||||||
case 'webp':
|
|
||||||
case 'svg':
|
|
||||||
case 'ico':
|
|
||||||
return '#722ed1';
|
|
||||||
case 'pdf':
|
|
||||||
return '#f5222d';
|
|
||||||
case 'js':
|
|
||||||
case 'json':
|
|
||||||
case 'ts':
|
|
||||||
case 'css':
|
|
||||||
case 'html':
|
|
||||||
case 'vue':
|
|
||||||
case 'xml':
|
|
||||||
case 'yaml':
|
|
||||||
case 'yml':
|
|
||||||
return '#52c41a';
|
|
||||||
default:
|
|
||||||
return 'inherit';
|
|
||||||
}
|
}
|
||||||
|
const ext = getFileExtension(fileName);
|
||||||
|
return FILE_TYPE_CONFIG[ext]?.icon || FileOutlined;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getFileIconColor = (fileName, type) => {
|
||||||
|
if (type === 'directory') {
|
||||||
|
return '#ffc60a'; // 文件夹黄色
|
||||||
|
}
|
||||||
|
const ext = getFileExtension(fileName);
|
||||||
|
return FILE_TYPE_CONFIG[ext]?.color || 'inherit';
|
||||||
};
|
};
|
||||||
|
|
||||||
const isPinned = (path) => config.pinnedFiles.includes(path);
|
const isPinned = (path) => config.pinnedFiles.includes(path);
|
||||||
|
|
@ -431,13 +430,15 @@ const handleModalOk = async () => {
|
||||||
flex: 1; /* 让文字占满剩余空间 */
|
flex: 1; /* 让文字占满剩余空间 */
|
||||||
min-width: 0; /* 关键:允许在 flex 容器中收缩,从而触发省略号 */
|
min-width: 0; /* 关键:允许在 flex 容器中收缩,从而触发省略号 */
|
||||||
margin-left: 4px; /* spacing between icon and text */
|
margin-left: 4px; /* spacing between icon and text */
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 28px;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.ant-tree-node-content-wrapper) {
|
:deep(.ant-tree-node-content-wrapper) {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
min-height: 28px; /* improve touch target / spacing */
|
min-height: 32px; /* improve touch target / spacing */
|
||||||
padding: 0 4px;
|
padding: 0 4px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
transition: all 0.2s;
|
transition: all 0.2s;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue