refactor(web): 文件/文件夹图标统一改用 FileTypeIcon
- 文件预览、工作区文件列表、侧边栏文件树、知识库 FileTable、上传弹窗、 @mention、附件预览等处统一渲染彩色文件/文件夹图标 - 移除 file_utils 旧的 Ant 单色图标映射与 mention_icon_utils 的 file 分支 - 清理各组件因替换产生的孤儿图标导入;按钮图标保持不变
This commit is contained in:
parent
3c7ac61ca4
commit
a5663a8bb2
@ -7,11 +7,7 @@
|
||||
:title="`打开 ${file.name}`"
|
||||
@click="openPreview(file)"
|
||||
>
|
||||
<component
|
||||
:is="getFileIcon(file.path)"
|
||||
class="item-icon"
|
||||
:style="{ color: getFileIconColor(file.path) }"
|
||||
/>
|
||||
<FileTypeIcon :name="file.path" :size="20" class="item-icon" />
|
||||
<div class="item-meta">
|
||||
<div class="item-name">{{ file.name }}</div>
|
||||
<div class="item-desc">{{ getFileMetaLabel(file.path) }}</div>
|
||||
@ -41,7 +37,7 @@ import { computed, ref } from 'vue'
|
||||
import { message } from 'ant-design-vue'
|
||||
import { Download, LoaderCircle, Save } from 'lucide-vue-next'
|
||||
import { threadApi } from '@/apis/agent_api'
|
||||
import { getFileIcon, getFileIconColor } from '@/utils/file_utils'
|
||||
import FileTypeIcon from '@/components/common/FileTypeIcon.vue'
|
||||
import { downloadViewerFile } from '@/apis/viewer_filesystem'
|
||||
|
||||
const props = defineProps({
|
||||
|
||||
@ -246,10 +246,10 @@
|
||||
</div>
|
||||
<div v-if="currentStateFiles.length" class="state-list">
|
||||
<div v-for="file in currentStateFiles" :key="file.key" class="state-list-item">
|
||||
<component
|
||||
:is="file.icon"
|
||||
<FileTypeIcon
|
||||
:name="file.name || file.path"
|
||||
:size="18"
|
||||
class="state-list-item-icon"
|
||||
:style="{ color: file.iconColor }"
|
||||
/>
|
||||
<div class="state-list-item-body">
|
||||
<div class="state-list-item-title">{{ file.name }}</div>
|
||||
@ -274,10 +274,10 @@
|
||||
:title="`打开 ${file.name}`"
|
||||
@click="openPanelPreview(file)"
|
||||
>
|
||||
<component
|
||||
:is="file.icon"
|
||||
<FileTypeIcon
|
||||
:name="file.name || file.path"
|
||||
:size="18"
|
||||
class="state-list-item-icon"
|
||||
:style="{ color: file.iconColor }"
|
||||
/>
|
||||
<div class="state-list-item-body">
|
||||
<div class="state-list-item-title">{{ file.name }}</div>
|
||||
@ -362,7 +362,8 @@ import {
|
||||
} from 'vue'
|
||||
import { message } from 'ant-design-vue'
|
||||
import { RefreshCw, SquareCheck } from 'lucide-vue-next'
|
||||
import { getFileIcon, getFileIconColor, formatFileSize } from '@/utils/file_utils'
|
||||
import { formatFileSize } from '@/utils/file_utils'
|
||||
import FileTypeIcon from '@/components/common/FileTypeIcon.vue'
|
||||
import { generatePixelAvatar } from '@/utils/pixelAvatar'
|
||||
import {
|
||||
CheckCircleOutlined,
|
||||
@ -720,9 +721,7 @@ const currentArtifactFiles = computed(() =>
|
||||
.map((path) => ({
|
||||
path,
|
||||
name: getPanelFileName({ path }),
|
||||
meta: getArtifactMetaLabel(path),
|
||||
icon: getFileIcon(path),
|
||||
iconColor: getFileIconColor(path)
|
||||
meta: getArtifactMetaLabel(path)
|
||||
}))
|
||||
)
|
||||
const currentTodos = computed(() => {
|
||||
@ -780,9 +779,7 @@ const currentStateFiles = computed(() => {
|
||||
key: path,
|
||||
path,
|
||||
name,
|
||||
meta: [status, sizeLabel === '-' ? '' : sizeLabel, path].filter(Boolean).join(' · '),
|
||||
icon: getFileIcon(name || path),
|
||||
iconColor: getFileIconColor(name || path)
|
||||
meta: [status, sizeLabel === '-' ? '' : sizeLabel, path].filter(Boolean).join(' · ')
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -5,11 +5,7 @@
|
||||
>
|
||||
<div v-if="showHeader" class="preview-header">
|
||||
<div class="file-title">
|
||||
<component
|
||||
v-if="showFileIcon"
|
||||
:is="getFileIcon(filePath)"
|
||||
:style="{ color: getFileIconColor(filePath), fontSize: '18px' }"
|
||||
/>
|
||||
<FileTypeIcon v-if="showFileIcon" :name="filePath" :size="18" />
|
||||
<span class="file-path-title">{{ filePath }}</span>
|
||||
</div>
|
||||
<div class="modal-actions">
|
||||
@ -269,8 +265,8 @@ import {
|
||||
} from 'lucide-vue-next'
|
||||
import hljs from 'highlight.js/lib/common'
|
||||
import MarkdownPreview from '@/components/common/MarkdownPreview.vue'
|
||||
import FileTypeIcon from '@/components/common/FileTypeIcon.vue'
|
||||
import { useThemeStore } from '@/stores/theme'
|
||||
import { getFileIcon, getFileIconColor } from '@/utils/file_utils'
|
||||
import { escapeHtml } from '@/utils/html'
|
||||
import {
|
||||
getCodeLanguageByPath,
|
||||
|
||||
@ -47,8 +47,8 @@
|
||||
:key="attachment.fileId"
|
||||
class="attachment-file-card"
|
||||
>
|
||||
<div class="attachment-file-icon" :style="{ color: attachment.iconColor }">
|
||||
<component :is="attachment.icon" />
|
||||
<div class="attachment-file-icon">
|
||||
<FileTypeIcon :name="attachment.name" :size="18" />
|
||||
</div>
|
||||
<div class="attachment-file-body">
|
||||
<div class="attachment-file-name" :title="attachment.name">{{ attachment.name }}</div>
|
||||
@ -95,6 +95,7 @@ import ImagePreviewComponent from '@/components/ImagePreviewComponent.vue'
|
||||
import AttachmentOptionsComponent from '@/components/AttachmentOptionsComponent.vue'
|
||||
import { X } from 'lucide-vue-next'
|
||||
import { normalizeAttachmentPreviews } from '@/utils/file_utils'
|
||||
import FileTypeIcon from '@/components/common/FileTypeIcon.vue'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: { type: String, default: '' },
|
||||
|
||||
@ -121,8 +121,8 @@
|
||||
:key="attachment.fileId"
|
||||
class="message-attachment-file"
|
||||
>
|
||||
<div class="message-attachment-icon" :style="{ color: attachment.iconColor }">
|
||||
<component :is="attachment.icon" />
|
||||
<div class="message-attachment-icon">
|
||||
<FileTypeIcon :name="attachment.name" :size="18" />
|
||||
</div>
|
||||
<div class="message-attachment-body">
|
||||
<div class="message-attachment-name" :title="attachment.name">
|
||||
@ -148,6 +148,7 @@ import { storeToRefs } from 'pinia'
|
||||
import { MessageProcessor } from '@/utils/messageProcessor'
|
||||
import { normalizeAttachmentPreviews } from '@/utils/file_utils'
|
||||
import { buildMentionDisplayLabels } from '@/utils/mention_utils'
|
||||
import FileTypeIcon from '@/components/common/FileTypeIcon.vue'
|
||||
import { enrichTaskToolCalls } from '@/components/ToolCallingResult/toolRegistry'
|
||||
|
||||
const props = defineProps({
|
||||
|
||||
@ -16,11 +16,7 @@
|
||||
:title="tab.path"
|
||||
@click="activatePreviewTab(tab.path)"
|
||||
>
|
||||
<component
|
||||
:is="getFileIcon(tab.path)"
|
||||
class="preview-tab-icon"
|
||||
:style="{ color: getFileIconColor(tab.path) }"
|
||||
/>
|
||||
<FileTypeIcon :name="tab.path" :size="16" class="preview-tab-icon" />
|
||||
<span class="preview-tab-name">{{ tab.name }}</span>
|
||||
</button>
|
||||
<button
|
||||
@ -137,7 +133,7 @@ import { Download, Folders, RefreshCw, Trash2, X } from 'lucide-vue-next'
|
||||
import { Modal, message } from 'ant-design-vue'
|
||||
import FileTreeComponent from '@/components/FileTreeComponent.vue'
|
||||
import AgentFilePreview from '@/components/AgentFilePreview.vue'
|
||||
import { getFileIcon, getFileIconColor } from '@/utils/file_utils'
|
||||
import FileTypeIcon from '@/components/common/FileTypeIcon.vue'
|
||||
import {
|
||||
deleteViewerFile,
|
||||
downloadViewerFile,
|
||||
|
||||
@ -22,8 +22,8 @@
|
||||
|
||||
<div v-if="fileItems.length" class="attachment-list">
|
||||
<div v-for="item in fileItems" :key="item.localId" class="attachment-item">
|
||||
<div class="attachment-file-icon" :style="{ color: getFileIconColor(item.fileName) }">
|
||||
<component :is="getFileIcon(item.fileName)" />
|
||||
<div class="attachment-file-icon">
|
||||
<FileTypeIcon :name="item.fileName" :size="20" />
|
||||
</div>
|
||||
|
||||
<div class="attachment-item-content">
|
||||
@ -152,7 +152,7 @@ import { message } from 'ant-design-vue'
|
||||
import { ChevronDown, ChevronUp, X } from 'lucide-vue-next'
|
||||
import { threadApi } from '@/apis'
|
||||
import { ocrApi } from '@/apis/system_api'
|
||||
import { getFileIcon, getFileIconColor } from '@/utils/file_utils'
|
||||
import FileTypeIcon from '@/components/common/FileTypeIcon.vue'
|
||||
|
||||
const props = defineProps({
|
||||
open: { type: Boolean, default: false },
|
||||
|
||||
@ -12,7 +12,7 @@
|
||||
<div class="modal-title-wrapper">
|
||||
<!-- 左侧:文件名和图标 -->
|
||||
<div class="file-title">
|
||||
<component :is="fileIcon" :style="{ color: fileIconColor, fontSize: '18px' }" />
|
||||
<FileTypeIcon :name="file?.filename" :size="18" />
|
||||
<span class="file-name">{{ file?.filename || '文件详情' }}</span>
|
||||
</div>
|
||||
|
||||
@ -123,9 +123,9 @@ import { useDatabaseStore } from '@/stores/database'
|
||||
import { message } from 'ant-design-vue'
|
||||
import { documentApi } from '@/apis/knowledge_api'
|
||||
import { mergeChunks } from '@/utils/chunkUtils'
|
||||
import { getFileIcon, getFileIconColor } from '@/utils/file_utils'
|
||||
import { getPreviewTypeByPath } from '@/utils/file_preview'
|
||||
import MarkdownPreview from '@/components/common/MarkdownPreview.vue'
|
||||
import FileTypeIcon from '@/components/common/FileTypeIcon.vue'
|
||||
import { Download, ChevronDown, FileText, X } from 'lucide-vue-next'
|
||||
|
||||
const store = useDatabaseStore()
|
||||
@ -138,10 +138,6 @@ const visible = computed({
|
||||
const file = computed(() => store.selectedFile)
|
||||
const loading = computed(() => store.state.fileDetailLoading)
|
||||
|
||||
// 文件图标
|
||||
const fileIcon = computed(() => getFileIcon(file.value?.filename))
|
||||
const fileIconColor = computed(() => getFileIconColor(file.value?.filename))
|
||||
|
||||
const downloadingOriginal = ref(false)
|
||||
const downloadingMarkdown = ref(false)
|
||||
const sourcePreviewLoading = ref(false)
|
||||
|
||||
@ -226,24 +226,12 @@
|
||||
<div v-if="column.key === 'filename'">
|
||||
<template v-if="record.is_folder">
|
||||
<span class="folder-row" @click="toggleExpand(record)">
|
||||
<component
|
||||
:is="
|
||||
expandedRowKeys.includes(record.file_id) ? h(FolderOpenFilled) : h(FolderFilled)
|
||||
"
|
||||
style="margin-right: 8px; color: #ffb800; font-size: 16px"
|
||||
/>
|
||||
<FileTypeIcon is-dir :size="16" :style="{ marginRight: '8px' }" />
|
||||
{{ record.filename }}
|
||||
</span>
|
||||
</template>
|
||||
<a-button v-else class="main-btn" type="link" @click="openFileDetail(record)">
|
||||
<component
|
||||
:is="getFileIcon(record.displayName || text)"
|
||||
:style="{
|
||||
marginRight: '0',
|
||||
color: getFileIconColor(record.displayName || text),
|
||||
fontSize: '16px'
|
||||
}"
|
||||
/>
|
||||
<FileTypeIcon :name="record.displayName || text" :size="16" :style="{ marginRight: '8px' }" />
|
||||
{{ record.displayName || text }}
|
||||
</a-button>
|
||||
</div>
|
||||
@ -389,8 +377,6 @@ import {
|
||||
HourglassFilled,
|
||||
CloseCircleFilled,
|
||||
ClockCircleFilled,
|
||||
FolderFilled,
|
||||
FolderOpenFilled,
|
||||
FileTextFilled
|
||||
} from '@ant-design/icons-vue'
|
||||
import {
|
||||
@ -1167,9 +1153,10 @@ const handleIndexConfigCancel = () => {
|
||||
}
|
||||
|
||||
// 导入工具函数
|
||||
import { getFileIcon, getFileIconColor, formatStandardTime } from '@/utils/file_utils'
|
||||
import { formatStandardTime } from '@/utils/file_utils'
|
||||
import { buildChunkParamsPayload, isPlainObject } from '@/utils/chunk_presets'
|
||||
import ChunkParamsConfig from '@/components/ChunkParamsConfig.vue'
|
||||
import FileTypeIcon from '@/components/common/FileTypeIcon.vue'
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@ -1282,6 +1269,8 @@ import ChunkParamsConfig from '@/components/ChunkParamsConfig.vue'
|
||||
}
|
||||
|
||||
.my-table .main-btn {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: 0;
|
||||
height: auto;
|
||||
line-height: 1.4;
|
||||
|
||||
@ -17,11 +17,7 @@
|
||||
<template #icon="{ data, expanded }">
|
||||
<slot name="icon" :node="data" :expanded="expanded">
|
||||
<template v-if="data.isLeaf">
|
||||
<component
|
||||
v-if="useFileIcons"
|
||||
:is="getFileIcon(data.key)"
|
||||
:style="{ color: getFileIconColor(data.key), fontSize: '16px' }"
|
||||
/>
|
||||
<FileTypeIcon v-if="useFileIcons" :name="data.key" :size="16" />
|
||||
<FileText v-else :size="16" class="file-icon" />
|
||||
</template>
|
||||
<template v-else>
|
||||
@ -30,8 +26,7 @@
|
||||
class="folder-loading-icon"
|
||||
aria-label="正在加载"
|
||||
></span>
|
||||
<FolderOpen v-else-if="expanded" :size="18" class="folder-icon open" />
|
||||
<Folder v-else :size="18" class="folder-icon" />
|
||||
<FileTypeIcon v-else is-dir :size="18" />
|
||||
</template>
|
||||
</slot>
|
||||
</template>
|
||||
@ -55,8 +50,8 @@
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { Folder, FolderOpen, FileText } from 'lucide-vue-next'
|
||||
import { getFileIcon, getFileIconColor } from '@/utils/file_utils'
|
||||
import { FileText } from 'lucide-vue-next'
|
||||
import FileTypeIcon from '@/components/common/FileTypeIcon.vue'
|
||||
|
||||
const props = defineProps({
|
||||
treeData: {
|
||||
|
||||
@ -293,7 +293,7 @@
|
||||
@click="openWorkspaceDirectory(item.path)"
|
||||
>
|
||||
<a-checkbox disabled />
|
||||
<Folder :size="14" class="workspace-file-icon" />
|
||||
<FileTypeIcon is-dir :size="16" class="workspace-file-icon" />
|
||||
<span class="workspace-file-name" :title="item.path">{{ item.name }}</span>
|
||||
</button>
|
||||
|
||||
@ -308,7 +308,7 @@
|
||||
:disabled="!item.supported || chunkLoading"
|
||||
@change="toggleWorkspacePath(item.path, $event.target.checked)"
|
||||
/>
|
||||
<FileText :size="14" class="workspace-file-icon" />
|
||||
<FileTypeIcon :name="item.path" :size="16" class="workspace-file-icon" />
|
||||
<span class="workspace-file-name" :title="item.path">{{ item.path }}</span>
|
||||
<span class="workspace-file-size">{{ formatFileSize(item.size) }}</span>
|
||||
</label>
|
||||
@ -423,9 +423,7 @@ import { ReloadOutlined } from '@ant-design/icons-vue'
|
||||
import {
|
||||
FileUp,
|
||||
FolderUp,
|
||||
Folder,
|
||||
FolderOpen,
|
||||
FileText,
|
||||
ArrowLeft,
|
||||
RotateCw,
|
||||
CircleHelp,
|
||||
@ -439,6 +437,7 @@ import {
|
||||
} from 'lucide-vue-next'
|
||||
import { buildChunkParamsPayload } from '@/utils/chunk_presets'
|
||||
import ChunkParamsConfig from '@/components/ChunkParamsConfig.vue'
|
||||
import FileTypeIcon from '@/components/common/FileTypeIcon.vue'
|
||||
|
||||
const props = defineProps({
|
||||
visible: {
|
||||
|
||||
@ -60,9 +60,10 @@
|
||||
@click="insertMention(item)"
|
||||
>
|
||||
<div class="file-info-left">
|
||||
<component
|
||||
:is="item.is_dir ? FolderFilled : getFileIcon(item.label)"
|
||||
:style="{ color: item.is_dir ? '#ffa940' : getFileIconColor(item.label) }"
|
||||
<FileTypeIcon
|
||||
:name="item.label"
|
||||
:is-dir="item.is_dir"
|
||||
:size="16"
|
||||
class="file-type-icon"
|
||||
/>
|
||||
<span class="file-name" :title="item.label">
|
||||
@ -280,10 +281,10 @@ import {
|
||||
h,
|
||||
render
|
||||
} from 'vue'
|
||||
import { SendOutlined, ArrowUpOutlined, PauseOutlined, FolderFilled } from '@ant-design/icons-vue'
|
||||
import { SendOutlined, ArrowUpOutlined, PauseOutlined } from '@ant-design/icons-vue'
|
||||
import { Paperclip } from 'lucide-vue-next'
|
||||
import { searchMentionFiles } from '@/apis/mention_api'
|
||||
import { getFileIcon, getFileIconColor } from '@/utils/file_utils'
|
||||
import FileTypeIcon from '@/components/common/FileTypeIcon.vue'
|
||||
import {
|
||||
getMentionIconComponent,
|
||||
getMentionIconStyle,
|
||||
@ -472,14 +473,25 @@ const createEditorMentionElement = (segment) => {
|
||||
const icon = document.createElement('span')
|
||||
icon.className = 'mention-ref-icon'
|
||||
icon.dataset.vueIcon = 'true'
|
||||
Object.assign(icon.style, getMentionIconStyle(segment.type, segment.value) || {})
|
||||
render(
|
||||
h(getMentionIconComponent(segment.type, segment.value), {
|
||||
size: MENTION_ICON_SIZE,
|
||||
strokeWidth: MENTION_ICON_STROKE_WIDTH
|
||||
}),
|
||||
icon
|
||||
)
|
||||
if (segment.type === 'file') {
|
||||
render(
|
||||
h(FileTypeIcon, {
|
||||
name: segment.value,
|
||||
isDir: segment.value.endsWith('/'),
|
||||
size: MENTION_ICON_SIZE
|
||||
}),
|
||||
icon
|
||||
)
|
||||
} else {
|
||||
Object.assign(icon.style, getMentionIconStyle(segment.type, segment.value) || {})
|
||||
render(
|
||||
h(getMentionIconComponent(segment.type, segment.value), {
|
||||
size: MENTION_ICON_SIZE,
|
||||
strokeWidth: MENTION_ICON_STROKE_WIDTH
|
||||
}),
|
||||
icon
|
||||
)
|
||||
}
|
||||
token.appendChild(icon)
|
||||
|
||||
const label = document.createElement('span')
|
||||
|
||||
@ -12,7 +12,15 @@
|
||||
:data-mention-value="editable ? segment.value : undefined"
|
||||
:title="segment.raw"
|
||||
>
|
||||
<FileTypeIcon
|
||||
v-if="segment.type === 'file'"
|
||||
class="mention-ref-icon"
|
||||
:name="segment.value"
|
||||
:is-dir="segment.value.endsWith('/')"
|
||||
:size="MENTION_ICON_SIZE"
|
||||
/>
|
||||
<component
|
||||
v-else
|
||||
:is="getMentionIconComponent(segment.type, segment.value)"
|
||||
class="mention-ref-icon"
|
||||
:style="getMentionIconStyle(segment.type, segment.value)"
|
||||
@ -34,6 +42,7 @@ import {
|
||||
MENTION_ICON_STROKE_WIDTH
|
||||
} from '@/utils/mention_icon_utils'
|
||||
import { getMentionDisplayLabel, parseMentionText } from '@/utils/mention_utils'
|
||||
import FileTypeIcon from '@/components/common/FileTypeIcon.vue'
|
||||
|
||||
const props = defineProps({
|
||||
content: {
|
||||
|
||||
@ -86,12 +86,7 @@
|
||||
/>
|
||||
</span>
|
||||
<span class="name-cell">
|
||||
<Folder v-if="entry.is_dir" :size="17" class="folder-icon" />
|
||||
<component
|
||||
v-else
|
||||
:is="getFileIcon(entry.name || entry.path)"
|
||||
:style="{ color: getFileIconColor(entry.name || entry.path), fontSize: '16px' }"
|
||||
/>
|
||||
<FileTypeIcon :name="entry.name || entry.path" :is-dir="entry.is_dir" :size="17" />
|
||||
<span class="entry-name" :title="entry.name">{{ entry.name }}</span>
|
||||
</span>
|
||||
<span>{{ entry.is_dir ? '-' : formatFileSize(entry.size) }}</span>
|
||||
@ -147,13 +142,9 @@
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import { Download, Folder, ListChecks, MoreHorizontal, Trash2 } from 'lucide-vue-next'
|
||||
import {
|
||||
formatFileSize,
|
||||
formatRelativeTime,
|
||||
getFileIcon,
|
||||
getFileIconColor
|
||||
} from '@/utils/file_utils'
|
||||
import { Download, ListChecks, MoreHorizontal, Trash2 } from 'lucide-vue-next'
|
||||
import { formatFileSize, formatRelativeTime } from '@/utils/file_utils'
|
||||
import FileTypeIcon from '@/components/common/FileTypeIcon.vue'
|
||||
|
||||
const props = defineProps({
|
||||
entries: { type: Array, default: () => [] },
|
||||
|
||||
@ -1,95 +1,6 @@
|
||||
import {
|
||||
FileTextFilled,
|
||||
FileMarkdownFilled,
|
||||
FilePdfFilled,
|
||||
FileWordFilled,
|
||||
FileExcelFilled,
|
||||
FileImageFilled,
|
||||
FileFilled,
|
||||
FilePptFilled,
|
||||
FileZipFilled,
|
||||
LinkOutlined,
|
||||
CodeFilled
|
||||
} from '@ant-design/icons-vue'
|
||||
import { getPreviewFileExtension, getPreviewTypeByPath } from '@/utils/file_preview'
|
||||
import { formatRelative, parseToShanghai } from '@/utils/time'
|
||||
|
||||
const DEFAULT_FILE_ICON = { icon: FileFilled, color: 'var(--gray-600)' }
|
||||
const LINK_FILE_ICON = { icon: LinkOutlined, color: 'var(--color-info-500)' }
|
||||
|
||||
const FILE_ICON_CONFIG = {
|
||||
txt: { icon: FileTextFilled, color: 'var(--color-info-500)' },
|
||||
text: { icon: FileTextFilled, color: 'var(--color-info-500)' },
|
||||
log: { icon: FileTextFilled, color: 'var(--color-info-500)' },
|
||||
md: { icon: FileMarkdownFilled, color: 'var(--gray-700)' },
|
||||
markdown: { icon: FileMarkdownFilled, color: 'var(--gray-700)' },
|
||||
pdf: { icon: FilePdfFilled, color: 'var(--color-error-500)' },
|
||||
doc: { icon: FileWordFilled, color: 'var(--color-info-700)' },
|
||||
docx: { icon: FileWordFilled, color: 'var(--color-info-700)' },
|
||||
xls: { icon: FileExcelFilled, color: 'var(--color-success-500)' },
|
||||
xlsx: { icon: FileExcelFilled, color: 'var(--color-success-500)' },
|
||||
csv: { icon: FileExcelFilled, color: 'var(--color-success-500)' },
|
||||
ppt: { icon: FilePptFilled, color: 'var(--color-warning-700)' },
|
||||
pptx: { icon: FilePptFilled, color: 'var(--color-warning-700)' },
|
||||
apng: { icon: FileImageFilled, color: 'var(--color-accent-700)' },
|
||||
avif: { icon: FileImageFilled, color: 'var(--color-accent-700)' },
|
||||
jpg: { icon: FileImageFilled, color: 'var(--color-accent-700)' },
|
||||
jpeg: { icon: FileImageFilled, color: 'var(--color-accent-700)' },
|
||||
png: { icon: FileImageFilled, color: 'var(--color-accent-700)' },
|
||||
gif: { icon: FileImageFilled, color: 'var(--color-accent-700)' },
|
||||
bmp: { icon: FileImageFilled, color: 'var(--color-accent-700)' },
|
||||
svg: { icon: FileImageFilled, color: 'var(--color-accent-700)' },
|
||||
webp: { icon: FileImageFilled, color: 'var(--color-accent-700)' },
|
||||
zip: { icon: FileZipFilled, color: 'var(--gray-700)' },
|
||||
rar: { icon: FileZipFilled, color: 'var(--gray-700)' },
|
||||
'7z': { icon: FileZipFilled, color: 'var(--gray-700)' },
|
||||
tar: { icon: FileZipFilled, color: 'var(--gray-700)' },
|
||||
gz: { icon: FileZipFilled, color: 'var(--gray-700)' },
|
||||
py: { icon: CodeFilled, color: 'var(--color-primary-700)' },
|
||||
js: { icon: CodeFilled, color: 'var(--color-primary-700)' },
|
||||
jsx: { icon: CodeFilled, color: 'var(--color-primary-700)' },
|
||||
ts: { icon: CodeFilled, color: 'var(--color-primary-700)' },
|
||||
tsx: { icon: CodeFilled, color: 'var(--color-primary-700)' },
|
||||
vue: { icon: CodeFilled, color: 'var(--color-primary-700)' },
|
||||
sh: { icon: CodeFilled, color: 'var(--color-primary-700)' },
|
||||
go: { icon: CodeFilled, color: 'var(--color-primary-700)' },
|
||||
rs: { icon: CodeFilled, color: 'var(--color-primary-700)' },
|
||||
cpp: { icon: CodeFilled, color: 'var(--color-primary-700)' },
|
||||
c: { icon: CodeFilled, color: 'var(--color-primary-700)' },
|
||||
h: { icon: CodeFilled, color: 'var(--color-primary-700)' },
|
||||
java: { icon: CodeFilled, color: 'var(--color-primary-700)' },
|
||||
html: { icon: CodeFilled, color: 'var(--color-primary-700)' },
|
||||
htm: { icon: CodeFilled, color: 'var(--color-primary-700)' },
|
||||
css: { icon: CodeFilled, color: 'var(--color-primary-700)' },
|
||||
less: { icon: CodeFilled, color: 'var(--color-primary-700)' },
|
||||
scss: { icon: CodeFilled, color: 'var(--color-primary-700)' },
|
||||
sql: { icon: CodeFilled, color: 'var(--color-primary-700)' },
|
||||
json: { icon: FileTextFilled, color: 'var(--color-primary-700)' },
|
||||
yaml: { icon: FileTextFilled, color: 'var(--color-primary-700)' },
|
||||
yml: { icon: FileTextFilled, color: 'var(--color-primary-700)' },
|
||||
toml: { icon: FileTextFilled, color: 'var(--color-primary-700)' },
|
||||
ini: { icon: FileTextFilled, color: 'var(--color-primary-700)' },
|
||||
conf: { icon: FileTextFilled, color: 'var(--color-primary-700)' },
|
||||
env: { icon: FileTextFilled, color: 'var(--color-primary-700)' }
|
||||
}
|
||||
|
||||
const getFileIconConfig = (filename) => {
|
||||
const normalizedName = String(filename || '')
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
if (!normalizedName) return DEFAULT_FILE_ICON
|
||||
if (normalizedName.startsWith('http://') || normalizedName.startsWith('https://'))
|
||||
return LINK_FILE_ICON
|
||||
|
||||
const cleanName = normalizedName.split(/[?#]/)[0]
|
||||
const extension = cleanName.includes('.') ? cleanName.split('.').pop() : cleanName
|
||||
return FILE_ICON_CONFIG[extension] || DEFAULT_FILE_ICON
|
||||
}
|
||||
|
||||
export const getFileIcon = (filename) => getFileIconConfig(filename).icon
|
||||
|
||||
export const getFileIconColor = (filename) => getFileIconConfig(filename).color
|
||||
|
||||
export const formatRelativeTime = (value) => formatRelative(value)
|
||||
|
||||
export const formatStandardTime = (value) => {
|
||||
@ -152,9 +63,7 @@ export const normalizeAttachmentPreview = (attachment) => {
|
||||
name,
|
||||
previewUrl: attachment?.original_artifact_url || attachment?.artifact_url || '',
|
||||
isImage: fileType.startsWith('image/') || getPreviewTypeByPath(name) === 'image',
|
||||
meta: [typeLabel, sizeLabel === '-' ? '' : sizeLabel].filter(Boolean).join(' · '),
|
||||
icon: getFileIcon(name),
|
||||
iconColor: getFileIconColor(name)
|
||||
meta: [typeLabel, sizeLabel === '-' ? '' : sizeLabel].filter(Boolean).join(' · ')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,10 +1,9 @@
|
||||
import { FolderFilled } from '@ant-design/icons-vue'
|
||||
import { BookMarked, BookOpen, Bot, Plug } from 'lucide-vue-next'
|
||||
import { getFileIcon, getFileIconColor } from '@/utils/file_utils'
|
||||
|
||||
export const MENTION_ICON_SIZE = 15
|
||||
export const MENTION_ICON_STROKE_WIDTH = 2.2
|
||||
|
||||
// 注意:file 类型的图标由 FileTypeIcon 组件直接渲染,此处仅处理其余 mention 类型。
|
||||
const MENTION_TYPE_ICON_COMPONENTS = {
|
||||
knowledge: BookOpen,
|
||||
skill: BookMarked,
|
||||
@ -12,16 +11,6 @@ const MENTION_TYPE_ICON_COMPONENTS = {
|
||||
subagent: Bot
|
||||
}
|
||||
|
||||
export const getMentionIconComponent = (type, value = '') => {
|
||||
if (type === 'file') {
|
||||
return String(value || '').endsWith('/') ? FolderFilled : getFileIcon(value)
|
||||
}
|
||||
return MENTION_TYPE_ICON_COMPONENTS[type] || Plug
|
||||
}
|
||||
export const getMentionIconComponent = (type) => MENTION_TYPE_ICON_COMPONENTS[type] || Plug
|
||||
|
||||
export const getMentionIconStyle = (type, value = '') => {
|
||||
if (type !== 'file') return null
|
||||
return {
|
||||
color: String(value || '').endsWith('/') ? '#ffa940' : getFileIconColor(value)
|
||||
}
|
||||
}
|
||||
export const getMentionIconStyle = () => null
|
||||
|
||||
Loading…
Reference in New Issue
Block a user