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}`"
|
:title="`打开 ${file.name}`"
|
||||||
@click="openPreview(file)"
|
@click="openPreview(file)"
|
||||||
>
|
>
|
||||||
<component
|
<FileTypeIcon :name="file.path" :size="20" class="item-icon" />
|
||||||
:is="getFileIcon(file.path)"
|
|
||||||
class="item-icon"
|
|
||||||
:style="{ color: getFileIconColor(file.path) }"
|
|
||||||
/>
|
|
||||||
<div class="item-meta">
|
<div class="item-meta">
|
||||||
<div class="item-name">{{ file.name }}</div>
|
<div class="item-name">{{ file.name }}</div>
|
||||||
<div class="item-desc">{{ getFileMetaLabel(file.path) }}</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 { message } from 'ant-design-vue'
|
||||||
import { Download, LoaderCircle, Save } from 'lucide-vue-next'
|
import { Download, LoaderCircle, Save } from 'lucide-vue-next'
|
||||||
import { threadApi } from '@/apis/agent_api'
|
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'
|
import { downloadViewerFile } from '@/apis/viewer_filesystem'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
|||||||
@ -246,10 +246,10 @@
|
|||||||
</div>
|
</div>
|
||||||
<div v-if="currentStateFiles.length" class="state-list">
|
<div v-if="currentStateFiles.length" class="state-list">
|
||||||
<div v-for="file in currentStateFiles" :key="file.key" class="state-list-item">
|
<div v-for="file in currentStateFiles" :key="file.key" class="state-list-item">
|
||||||
<component
|
<FileTypeIcon
|
||||||
:is="file.icon"
|
:name="file.name || file.path"
|
||||||
|
:size="18"
|
||||||
class="state-list-item-icon"
|
class="state-list-item-icon"
|
||||||
:style="{ color: file.iconColor }"
|
|
||||||
/>
|
/>
|
||||||
<div class="state-list-item-body">
|
<div class="state-list-item-body">
|
||||||
<div class="state-list-item-title">{{ file.name }}</div>
|
<div class="state-list-item-title">{{ file.name }}</div>
|
||||||
@ -274,10 +274,10 @@
|
|||||||
:title="`打开 ${file.name}`"
|
:title="`打开 ${file.name}`"
|
||||||
@click="openPanelPreview(file)"
|
@click="openPanelPreview(file)"
|
||||||
>
|
>
|
||||||
<component
|
<FileTypeIcon
|
||||||
:is="file.icon"
|
:name="file.name || file.path"
|
||||||
|
:size="18"
|
||||||
class="state-list-item-icon"
|
class="state-list-item-icon"
|
||||||
:style="{ color: file.iconColor }"
|
|
||||||
/>
|
/>
|
||||||
<div class="state-list-item-body">
|
<div class="state-list-item-body">
|
||||||
<div class="state-list-item-title">{{ file.name }}</div>
|
<div class="state-list-item-title">{{ file.name }}</div>
|
||||||
@ -362,7 +362,8 @@ import {
|
|||||||
} from 'vue'
|
} from 'vue'
|
||||||
import { message } from 'ant-design-vue'
|
import { message } from 'ant-design-vue'
|
||||||
import { RefreshCw, SquareCheck } from 'lucide-vue-next'
|
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 { generatePixelAvatar } from '@/utils/pixelAvatar'
|
||||||
import {
|
import {
|
||||||
CheckCircleOutlined,
|
CheckCircleOutlined,
|
||||||
@ -720,9 +721,7 @@ const currentArtifactFiles = computed(() =>
|
|||||||
.map((path) => ({
|
.map((path) => ({
|
||||||
path,
|
path,
|
||||||
name: getPanelFileName({ path }),
|
name: getPanelFileName({ path }),
|
||||||
meta: getArtifactMetaLabel(path),
|
meta: getArtifactMetaLabel(path)
|
||||||
icon: getFileIcon(path),
|
|
||||||
iconColor: getFileIconColor(path)
|
|
||||||
}))
|
}))
|
||||||
)
|
)
|
||||||
const currentTodos = computed(() => {
|
const currentTodos = computed(() => {
|
||||||
@ -780,9 +779,7 @@ const currentStateFiles = computed(() => {
|
|||||||
key: path,
|
key: path,
|
||||||
path,
|
path,
|
||||||
name,
|
name,
|
||||||
meta: [status, sizeLabel === '-' ? '' : sizeLabel, path].filter(Boolean).join(' · '),
|
meta: [status, sizeLabel === '-' ? '' : sizeLabel, path].filter(Boolean).join(' · ')
|
||||||
icon: getFileIcon(name || path),
|
|
||||||
iconColor: getFileIconColor(name || path)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -5,11 +5,7 @@
|
|||||||
>
|
>
|
||||||
<div v-if="showHeader" class="preview-header">
|
<div v-if="showHeader" class="preview-header">
|
||||||
<div class="file-title">
|
<div class="file-title">
|
||||||
<component
|
<FileTypeIcon v-if="showFileIcon" :name="filePath" :size="18" />
|
||||||
v-if="showFileIcon"
|
|
||||||
:is="getFileIcon(filePath)"
|
|
||||||
:style="{ color: getFileIconColor(filePath), fontSize: '18px' }"
|
|
||||||
/>
|
|
||||||
<span class="file-path-title">{{ filePath }}</span>
|
<span class="file-path-title">{{ filePath }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-actions">
|
<div class="modal-actions">
|
||||||
@ -269,8 +265,8 @@ import {
|
|||||||
} from 'lucide-vue-next'
|
} from 'lucide-vue-next'
|
||||||
import hljs from 'highlight.js/lib/common'
|
import hljs from 'highlight.js/lib/common'
|
||||||
import MarkdownPreview from '@/components/common/MarkdownPreview.vue'
|
import MarkdownPreview from '@/components/common/MarkdownPreview.vue'
|
||||||
|
import FileTypeIcon from '@/components/common/FileTypeIcon.vue'
|
||||||
import { useThemeStore } from '@/stores/theme'
|
import { useThemeStore } from '@/stores/theme'
|
||||||
import { getFileIcon, getFileIconColor } from '@/utils/file_utils'
|
|
||||||
import { escapeHtml } from '@/utils/html'
|
import { escapeHtml } from '@/utils/html'
|
||||||
import {
|
import {
|
||||||
getCodeLanguageByPath,
|
getCodeLanguageByPath,
|
||||||
|
|||||||
@ -47,8 +47,8 @@
|
|||||||
:key="attachment.fileId"
|
:key="attachment.fileId"
|
||||||
class="attachment-file-card"
|
class="attachment-file-card"
|
||||||
>
|
>
|
||||||
<div class="attachment-file-icon" :style="{ color: attachment.iconColor }">
|
<div class="attachment-file-icon">
|
||||||
<component :is="attachment.icon" />
|
<FileTypeIcon :name="attachment.name" :size="18" />
|
||||||
</div>
|
</div>
|
||||||
<div class="attachment-file-body">
|
<div class="attachment-file-body">
|
||||||
<div class="attachment-file-name" :title="attachment.name">{{ attachment.name }}</div>
|
<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 AttachmentOptionsComponent from '@/components/AttachmentOptionsComponent.vue'
|
||||||
import { X } from 'lucide-vue-next'
|
import { X } from 'lucide-vue-next'
|
||||||
import { normalizeAttachmentPreviews } from '@/utils/file_utils'
|
import { normalizeAttachmentPreviews } from '@/utils/file_utils'
|
||||||
|
import FileTypeIcon from '@/components/common/FileTypeIcon.vue'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
modelValue: { type: String, default: '' },
|
modelValue: { type: String, default: '' },
|
||||||
|
|||||||
@ -121,8 +121,8 @@
|
|||||||
:key="attachment.fileId"
|
:key="attachment.fileId"
|
||||||
class="message-attachment-file"
|
class="message-attachment-file"
|
||||||
>
|
>
|
||||||
<div class="message-attachment-icon" :style="{ color: attachment.iconColor }">
|
<div class="message-attachment-icon">
|
||||||
<component :is="attachment.icon" />
|
<FileTypeIcon :name="attachment.name" :size="18" />
|
||||||
</div>
|
</div>
|
||||||
<div class="message-attachment-body">
|
<div class="message-attachment-body">
|
||||||
<div class="message-attachment-name" :title="attachment.name">
|
<div class="message-attachment-name" :title="attachment.name">
|
||||||
@ -148,6 +148,7 @@ import { storeToRefs } from 'pinia'
|
|||||||
import { MessageProcessor } from '@/utils/messageProcessor'
|
import { MessageProcessor } from '@/utils/messageProcessor'
|
||||||
import { normalizeAttachmentPreviews } from '@/utils/file_utils'
|
import { normalizeAttachmentPreviews } from '@/utils/file_utils'
|
||||||
import { buildMentionDisplayLabels } from '@/utils/mention_utils'
|
import { buildMentionDisplayLabels } from '@/utils/mention_utils'
|
||||||
|
import FileTypeIcon from '@/components/common/FileTypeIcon.vue'
|
||||||
import { enrichTaskToolCalls } from '@/components/ToolCallingResult/toolRegistry'
|
import { enrichTaskToolCalls } from '@/components/ToolCallingResult/toolRegistry'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
|||||||
@ -16,11 +16,7 @@
|
|||||||
:title="tab.path"
|
:title="tab.path"
|
||||||
@click="activatePreviewTab(tab.path)"
|
@click="activatePreviewTab(tab.path)"
|
||||||
>
|
>
|
||||||
<component
|
<FileTypeIcon :name="tab.path" :size="16" class="preview-tab-icon" />
|
||||||
:is="getFileIcon(tab.path)"
|
|
||||||
class="preview-tab-icon"
|
|
||||||
:style="{ color: getFileIconColor(tab.path) }"
|
|
||||||
/>
|
|
||||||
<span class="preview-tab-name">{{ tab.name }}</span>
|
<span class="preview-tab-name">{{ tab.name }}</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
@ -137,7 +133,7 @@ import { Download, Folders, RefreshCw, Trash2, X } from 'lucide-vue-next'
|
|||||||
import { Modal, message } from 'ant-design-vue'
|
import { Modal, message } from 'ant-design-vue'
|
||||||
import FileTreeComponent from '@/components/FileTreeComponent.vue'
|
import FileTreeComponent from '@/components/FileTreeComponent.vue'
|
||||||
import AgentFilePreview from '@/components/AgentFilePreview.vue'
|
import AgentFilePreview from '@/components/AgentFilePreview.vue'
|
||||||
import { getFileIcon, getFileIconColor } from '@/utils/file_utils'
|
import FileTypeIcon from '@/components/common/FileTypeIcon.vue'
|
||||||
import {
|
import {
|
||||||
deleteViewerFile,
|
deleteViewerFile,
|
||||||
downloadViewerFile,
|
downloadViewerFile,
|
||||||
|
|||||||
@ -22,8 +22,8 @@
|
|||||||
|
|
||||||
<div v-if="fileItems.length" class="attachment-list">
|
<div v-if="fileItems.length" class="attachment-list">
|
||||||
<div v-for="item in fileItems" :key="item.localId" class="attachment-item">
|
<div v-for="item in fileItems" :key="item.localId" class="attachment-item">
|
||||||
<div class="attachment-file-icon" :style="{ color: getFileIconColor(item.fileName) }">
|
<div class="attachment-file-icon">
|
||||||
<component :is="getFileIcon(item.fileName)" />
|
<FileTypeIcon :name="item.fileName" :size="20" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="attachment-item-content">
|
<div class="attachment-item-content">
|
||||||
@ -152,7 +152,7 @@ import { message } from 'ant-design-vue'
|
|||||||
import { ChevronDown, ChevronUp, X } from 'lucide-vue-next'
|
import { ChevronDown, ChevronUp, X } from 'lucide-vue-next'
|
||||||
import { threadApi } from '@/apis'
|
import { threadApi } from '@/apis'
|
||||||
import { ocrApi } from '@/apis/system_api'
|
import { ocrApi } from '@/apis/system_api'
|
||||||
import { getFileIcon, getFileIconColor } from '@/utils/file_utils'
|
import FileTypeIcon from '@/components/common/FileTypeIcon.vue'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
open: { type: Boolean, default: false },
|
open: { type: Boolean, default: false },
|
||||||
|
|||||||
@ -12,7 +12,7 @@
|
|||||||
<div class="modal-title-wrapper">
|
<div class="modal-title-wrapper">
|
||||||
<!-- 左侧:文件名和图标 -->
|
<!-- 左侧:文件名和图标 -->
|
||||||
<div class="file-title">
|
<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>
|
<span class="file-name">{{ file?.filename || '文件详情' }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -123,9 +123,9 @@ import { useDatabaseStore } from '@/stores/database'
|
|||||||
import { message } from 'ant-design-vue'
|
import { message } from 'ant-design-vue'
|
||||||
import { documentApi } from '@/apis/knowledge_api'
|
import { documentApi } from '@/apis/knowledge_api'
|
||||||
import { mergeChunks } from '@/utils/chunkUtils'
|
import { mergeChunks } from '@/utils/chunkUtils'
|
||||||
import { getFileIcon, getFileIconColor } from '@/utils/file_utils'
|
|
||||||
import { getPreviewTypeByPath } from '@/utils/file_preview'
|
import { getPreviewTypeByPath } from '@/utils/file_preview'
|
||||||
import MarkdownPreview from '@/components/common/MarkdownPreview.vue'
|
import MarkdownPreview from '@/components/common/MarkdownPreview.vue'
|
||||||
|
import FileTypeIcon from '@/components/common/FileTypeIcon.vue'
|
||||||
import { Download, ChevronDown, FileText, X } from 'lucide-vue-next'
|
import { Download, ChevronDown, FileText, X } from 'lucide-vue-next'
|
||||||
|
|
||||||
const store = useDatabaseStore()
|
const store = useDatabaseStore()
|
||||||
@ -138,10 +138,6 @@ const visible = computed({
|
|||||||
const file = computed(() => store.selectedFile)
|
const file = computed(() => store.selectedFile)
|
||||||
const loading = computed(() => store.state.fileDetailLoading)
|
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 downloadingOriginal = ref(false)
|
||||||
const downloadingMarkdown = ref(false)
|
const downloadingMarkdown = ref(false)
|
||||||
const sourcePreviewLoading = ref(false)
|
const sourcePreviewLoading = ref(false)
|
||||||
|
|||||||
@ -226,24 +226,12 @@
|
|||||||
<div v-if="column.key === 'filename'">
|
<div v-if="column.key === 'filename'">
|
||||||
<template v-if="record.is_folder">
|
<template v-if="record.is_folder">
|
||||||
<span class="folder-row" @click="toggleExpand(record)">
|
<span class="folder-row" @click="toggleExpand(record)">
|
||||||
<component
|
<FileTypeIcon is-dir :size="16" :style="{ marginRight: '8px' }" />
|
||||||
:is="
|
|
||||||
expandedRowKeys.includes(record.file_id) ? h(FolderOpenFilled) : h(FolderFilled)
|
|
||||||
"
|
|
||||||
style="margin-right: 8px; color: #ffb800; font-size: 16px"
|
|
||||||
/>
|
|
||||||
{{ record.filename }}
|
{{ record.filename }}
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<a-button v-else class="main-btn" type="link" @click="openFileDetail(record)">
|
<a-button v-else class="main-btn" type="link" @click="openFileDetail(record)">
|
||||||
<component
|
<FileTypeIcon :name="record.displayName || text" :size="16" :style="{ marginRight: '8px' }" />
|
||||||
:is="getFileIcon(record.displayName || text)"
|
|
||||||
:style="{
|
|
||||||
marginRight: '0',
|
|
||||||
color: getFileIconColor(record.displayName || text),
|
|
||||||
fontSize: '16px'
|
|
||||||
}"
|
|
||||||
/>
|
|
||||||
{{ record.displayName || text }}
|
{{ record.displayName || text }}
|
||||||
</a-button>
|
</a-button>
|
||||||
</div>
|
</div>
|
||||||
@ -389,8 +377,6 @@ import {
|
|||||||
HourglassFilled,
|
HourglassFilled,
|
||||||
CloseCircleFilled,
|
CloseCircleFilled,
|
||||||
ClockCircleFilled,
|
ClockCircleFilled,
|
||||||
FolderFilled,
|
|
||||||
FolderOpenFilled,
|
|
||||||
FileTextFilled
|
FileTextFilled
|
||||||
} from '@ant-design/icons-vue'
|
} from '@ant-design/icons-vue'
|
||||||
import {
|
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 { buildChunkParamsPayload, isPlainObject } from '@/utils/chunk_presets'
|
||||||
import ChunkParamsConfig from '@/components/ChunkParamsConfig.vue'
|
import ChunkParamsConfig from '@/components/ChunkParamsConfig.vue'
|
||||||
|
import FileTypeIcon from '@/components/common/FileTypeIcon.vue'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@ -1282,6 +1269,8 @@ import ChunkParamsConfig from '@/components/ChunkParamsConfig.vue'
|
|||||||
}
|
}
|
||||||
|
|
||||||
.my-table .main-btn {
|
.my-table .main-btn {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
height: auto;
|
height: auto;
|
||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
|
|||||||
@ -17,11 +17,7 @@
|
|||||||
<template #icon="{ data, expanded }">
|
<template #icon="{ data, expanded }">
|
||||||
<slot name="icon" :node="data" :expanded="expanded">
|
<slot name="icon" :node="data" :expanded="expanded">
|
||||||
<template v-if="data.isLeaf">
|
<template v-if="data.isLeaf">
|
||||||
<component
|
<FileTypeIcon v-if="useFileIcons" :name="data.key" :size="16" />
|
||||||
v-if="useFileIcons"
|
|
||||||
:is="getFileIcon(data.key)"
|
|
||||||
:style="{ color: getFileIconColor(data.key), fontSize: '16px' }"
|
|
||||||
/>
|
|
||||||
<FileText v-else :size="16" class="file-icon" />
|
<FileText v-else :size="16" class="file-icon" />
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
@ -30,8 +26,7 @@
|
|||||||
class="folder-loading-icon"
|
class="folder-loading-icon"
|
||||||
aria-label="正在加载"
|
aria-label="正在加载"
|
||||||
></span>
|
></span>
|
||||||
<FolderOpen v-else-if="expanded" :size="18" class="folder-icon open" />
|
<FileTypeIcon v-else is-dir :size="18" />
|
||||||
<Folder v-else :size="18" class="folder-icon" />
|
|
||||||
</template>
|
</template>
|
||||||
</slot>
|
</slot>
|
||||||
</template>
|
</template>
|
||||||
@ -55,8 +50,8 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { Folder, FolderOpen, FileText } from 'lucide-vue-next'
|
import { FileText } from 'lucide-vue-next'
|
||||||
import { getFileIcon, getFileIconColor } from '@/utils/file_utils'
|
import FileTypeIcon from '@/components/common/FileTypeIcon.vue'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
treeData: {
|
treeData: {
|
||||||
|
|||||||
@ -293,7 +293,7 @@
|
|||||||
@click="openWorkspaceDirectory(item.path)"
|
@click="openWorkspaceDirectory(item.path)"
|
||||||
>
|
>
|
||||||
<a-checkbox disabled />
|
<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>
|
<span class="workspace-file-name" :title="item.path">{{ item.name }}</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
@ -308,7 +308,7 @@
|
|||||||
:disabled="!item.supported || chunkLoading"
|
:disabled="!item.supported || chunkLoading"
|
||||||
@change="toggleWorkspacePath(item.path, $event.target.checked)"
|
@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-name" :title="item.path">{{ item.path }}</span>
|
||||||
<span class="workspace-file-size">{{ formatFileSize(item.size) }}</span>
|
<span class="workspace-file-size">{{ formatFileSize(item.size) }}</span>
|
||||||
</label>
|
</label>
|
||||||
@ -423,9 +423,7 @@ import { ReloadOutlined } from '@ant-design/icons-vue'
|
|||||||
import {
|
import {
|
||||||
FileUp,
|
FileUp,
|
||||||
FolderUp,
|
FolderUp,
|
||||||
Folder,
|
|
||||||
FolderOpen,
|
FolderOpen,
|
||||||
FileText,
|
|
||||||
ArrowLeft,
|
ArrowLeft,
|
||||||
RotateCw,
|
RotateCw,
|
||||||
CircleHelp,
|
CircleHelp,
|
||||||
@ -439,6 +437,7 @@ import {
|
|||||||
} from 'lucide-vue-next'
|
} from 'lucide-vue-next'
|
||||||
import { buildChunkParamsPayload } from '@/utils/chunk_presets'
|
import { buildChunkParamsPayload } from '@/utils/chunk_presets'
|
||||||
import ChunkParamsConfig from '@/components/ChunkParamsConfig.vue'
|
import ChunkParamsConfig from '@/components/ChunkParamsConfig.vue'
|
||||||
|
import FileTypeIcon from '@/components/common/FileTypeIcon.vue'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
visible: {
|
visible: {
|
||||||
|
|||||||
@ -60,9 +60,10 @@
|
|||||||
@click="insertMention(item)"
|
@click="insertMention(item)"
|
||||||
>
|
>
|
||||||
<div class="file-info-left">
|
<div class="file-info-left">
|
||||||
<component
|
<FileTypeIcon
|
||||||
:is="item.is_dir ? FolderFilled : getFileIcon(item.label)"
|
:name="item.label"
|
||||||
:style="{ color: item.is_dir ? '#ffa940' : getFileIconColor(item.label) }"
|
:is-dir="item.is_dir"
|
||||||
|
:size="16"
|
||||||
class="file-type-icon"
|
class="file-type-icon"
|
||||||
/>
|
/>
|
||||||
<span class="file-name" :title="item.label">
|
<span class="file-name" :title="item.label">
|
||||||
@ -280,10 +281,10 @@ import {
|
|||||||
h,
|
h,
|
||||||
render
|
render
|
||||||
} from 'vue'
|
} 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 { Paperclip } from 'lucide-vue-next'
|
||||||
import { searchMentionFiles } from '@/apis/mention_api'
|
import { searchMentionFiles } from '@/apis/mention_api'
|
||||||
import { getFileIcon, getFileIconColor } from '@/utils/file_utils'
|
import FileTypeIcon from '@/components/common/FileTypeIcon.vue'
|
||||||
import {
|
import {
|
||||||
getMentionIconComponent,
|
getMentionIconComponent,
|
||||||
getMentionIconStyle,
|
getMentionIconStyle,
|
||||||
@ -472,14 +473,25 @@ const createEditorMentionElement = (segment) => {
|
|||||||
const icon = document.createElement('span')
|
const icon = document.createElement('span')
|
||||||
icon.className = 'mention-ref-icon'
|
icon.className = 'mention-ref-icon'
|
||||||
icon.dataset.vueIcon = 'true'
|
icon.dataset.vueIcon = 'true'
|
||||||
Object.assign(icon.style, getMentionIconStyle(segment.type, segment.value) || {})
|
if (segment.type === 'file') {
|
||||||
render(
|
render(
|
||||||
h(getMentionIconComponent(segment.type, segment.value), {
|
h(FileTypeIcon, {
|
||||||
size: MENTION_ICON_SIZE,
|
name: segment.value,
|
||||||
strokeWidth: MENTION_ICON_STROKE_WIDTH
|
isDir: segment.value.endsWith('/'),
|
||||||
}),
|
size: MENTION_ICON_SIZE
|
||||||
icon
|
}),
|
||||||
)
|
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)
|
token.appendChild(icon)
|
||||||
|
|
||||||
const label = document.createElement('span')
|
const label = document.createElement('span')
|
||||||
|
|||||||
@ -12,7 +12,15 @@
|
|||||||
:data-mention-value="editable ? segment.value : undefined"
|
:data-mention-value="editable ? segment.value : undefined"
|
||||||
:title="segment.raw"
|
: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
|
<component
|
||||||
|
v-else
|
||||||
:is="getMentionIconComponent(segment.type, segment.value)"
|
:is="getMentionIconComponent(segment.type, segment.value)"
|
||||||
class="mention-ref-icon"
|
class="mention-ref-icon"
|
||||||
:style="getMentionIconStyle(segment.type, segment.value)"
|
:style="getMentionIconStyle(segment.type, segment.value)"
|
||||||
@ -34,6 +42,7 @@ import {
|
|||||||
MENTION_ICON_STROKE_WIDTH
|
MENTION_ICON_STROKE_WIDTH
|
||||||
} from '@/utils/mention_icon_utils'
|
} from '@/utils/mention_icon_utils'
|
||||||
import { getMentionDisplayLabel, parseMentionText } from '@/utils/mention_utils'
|
import { getMentionDisplayLabel, parseMentionText } from '@/utils/mention_utils'
|
||||||
|
import FileTypeIcon from '@/components/common/FileTypeIcon.vue'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
content: {
|
content: {
|
||||||
|
|||||||
@ -86,12 +86,7 @@
|
|||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<span class="name-cell">
|
<span class="name-cell">
|
||||||
<Folder v-if="entry.is_dir" :size="17" class="folder-icon" />
|
<FileTypeIcon :name="entry.name || entry.path" :is-dir="entry.is_dir" :size="17" />
|
||||||
<component
|
|
||||||
v-else
|
|
||||||
:is="getFileIcon(entry.name || entry.path)"
|
|
||||||
:style="{ color: getFileIconColor(entry.name || entry.path), fontSize: '16px' }"
|
|
||||||
/>
|
|
||||||
<span class="entry-name" :title="entry.name">{{ entry.name }}</span>
|
<span class="entry-name" :title="entry.name">{{ entry.name }}</span>
|
||||||
</span>
|
</span>
|
||||||
<span>{{ entry.is_dir ? '-' : formatFileSize(entry.size) }}</span>
|
<span>{{ entry.is_dir ? '-' : formatFileSize(entry.size) }}</span>
|
||||||
@ -147,13 +142,9 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { Download, Folder, ListChecks, MoreHorizontal, Trash2 } from 'lucide-vue-next'
|
import { Download, ListChecks, MoreHorizontal, Trash2 } from 'lucide-vue-next'
|
||||||
import {
|
import { formatFileSize, formatRelativeTime } from '@/utils/file_utils'
|
||||||
formatFileSize,
|
import FileTypeIcon from '@/components/common/FileTypeIcon.vue'
|
||||||
formatRelativeTime,
|
|
||||||
getFileIcon,
|
|
||||||
getFileIconColor
|
|
||||||
} from '@/utils/file_utils'
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
entries: { type: Array, default: () => [] },
|
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 { getPreviewFileExtension, getPreviewTypeByPath } from '@/utils/file_preview'
|
||||||
import { formatRelative, parseToShanghai } from '@/utils/time'
|
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 formatRelativeTime = (value) => formatRelative(value)
|
||||||
|
|
||||||
export const formatStandardTime = (value) => {
|
export const formatStandardTime = (value) => {
|
||||||
@ -152,9 +63,7 @@ export const normalizeAttachmentPreview = (attachment) => {
|
|||||||
name,
|
name,
|
||||||
previewUrl: attachment?.original_artifact_url || attachment?.artifact_url || '',
|
previewUrl: attachment?.original_artifact_url || attachment?.artifact_url || '',
|
||||||
isImage: fileType.startsWith('image/') || getPreviewTypeByPath(name) === 'image',
|
isImage: fileType.startsWith('image/') || getPreviewTypeByPath(name) === 'image',
|
||||||
meta: [typeLabel, sizeLabel === '-' ? '' : sizeLabel].filter(Boolean).join(' · '),
|
meta: [typeLabel, sizeLabel === '-' ? '' : sizeLabel].filter(Boolean).join(' · ')
|
||||||
icon: getFileIcon(name),
|
|
||||||
iconColor: getFileIconColor(name)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,10 +1,9 @@
|
|||||||
import { FolderFilled } from '@ant-design/icons-vue'
|
|
||||||
import { BookMarked, BookOpen, Bot, Plug } from 'lucide-vue-next'
|
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_SIZE = 15
|
||||||
export const MENTION_ICON_STROKE_WIDTH = 2.2
|
export const MENTION_ICON_STROKE_WIDTH = 2.2
|
||||||
|
|
||||||
|
// 注意:file 类型的图标由 FileTypeIcon 组件直接渲染,此处仅处理其余 mention 类型。
|
||||||
const MENTION_TYPE_ICON_COMPONENTS = {
|
const MENTION_TYPE_ICON_COMPONENTS = {
|
||||||
knowledge: BookOpen,
|
knowledge: BookOpen,
|
||||||
skill: BookMarked,
|
skill: BookMarked,
|
||||||
@ -12,16 +11,6 @@ const MENTION_TYPE_ICON_COMPONENTS = {
|
|||||||
subagent: Bot
|
subagent: Bot
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getMentionIconComponent = (type, value = '') => {
|
export const getMentionIconComponent = (type) => MENTION_TYPE_ICON_COMPONENTS[type] || Plug
|
||||||
if (type === 'file') {
|
|
||||||
return String(value || '').endsWith('/') ? FolderFilled : getFileIcon(value)
|
|
||||||
}
|
|
||||||
return MENTION_TYPE_ICON_COMPONENTS[type] || Plug
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getMentionIconStyle = (type, value = '') => {
|
export const getMentionIconStyle = () => null
|
||||||
if (type !== 'file') return null
|
|
||||||
return {
|
|
||||||
color: String(value || '').endsWith('/') ? '#ffa940' : getFileIconColor(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user