Browse Source

feat(frontend): 文档列表位置列显示目录路径并修复一屏滚动布局

- 文档列表分类列改为「位置」列:沿 folderMap 回溯 parentId 拼出目录路径(不含分类名),直接挂分类根显示 —,长路径省略号截断+悬停 title 看全
- 修复文档卡片一屏高度布局:补 .t-loading__parent 参与 flex 高度链,目录树/表格在面板内滚动不再撑高整页
Spring-AI-1.1.2
wanghanlin 1 week ago
parent
commit
06edac2738
  1. 35
      frontend/src/views/DocList.vue

35
frontend/src/views/DocList.vue

@ -82,7 +82,9 @@
</template> </template>
<template #fileType="{ row }"><t-tag size="small" variant="light">{{ row.fileType }}</t-tag></template> <template #fileType="{ row }"><t-tag size="small" variant="light">{{ row.fileType }}</t-tag></template>
<template #fileSize="{ row }"><span style="white-space:nowrap;">{{ formatSize(row.fileSize) }}</span></template> <template #fileSize="{ row }"><span style="white-space:nowrap;">{{ formatSize(row.fileSize) }}</span></template>
<template #categoryId="{ row }"><span>{{ categoryStore.getCategoryName(row.categoryId) }}</span></template>
<template #categoryId="{ row }">
<span class="doc-pos" :title="docPath(row)">{{ docPath(row) }}</span>
</template>
<template #status="{ row }"> <template #status="{ row }">
<t-tag size="small" :theme="row.status === 'READY' ? 'success' : row.status === 'PROCESSING' ? 'warning' : 'danger'" <t-tag size="small" :theme="row.status === 'READY' ? 'success' : row.status === 'PROCESSING' ? 'warning' : 'danger'"
variant="light">{{ statusLabel(row.status) }}</t-tag> variant="light">{{ statusLabel(row.status) }}</t-tag>
@ -189,6 +191,8 @@ const isResizing = ref(false)
const MIN_TREE_WIDTH = 200 const MIN_TREE_WIDTH = 200
const MAX_TREE_WIDTH = 480 const MAX_TREE_WIDTH = 480
let resizeCleanup: (() => void) | null = null let resizeCleanup: (() => void) | null = null
// id { name, parentId }沿 parentId
const folderMap = ref<Record<string, { name: string; parentId: string | number }>>({})
// //
const sortInfo = ref<{ sortBy: string; descending: boolean } | null>(null) const sortInfo = ref<{ sortBy: string; descending: boolean } | null>(null)
@ -232,7 +236,7 @@ const columns = [
{ colKey: 'title', title: '标题', width: 220, sorter: true }, { colKey: 'title', title: '标题', width: 220, sorter: true },
{ colKey: 'fileType', title: '类型', width: 70, sorter: true }, { colKey: 'fileType', title: '类型', width: 70, sorter: true },
{ colKey: 'fileSize', title: '大小', width: 80, sorter: true }, { colKey: 'fileSize', title: '大小', width: 80, sorter: true },
{ colKey: 'categoryId', title: '分类', width: 100 },
{ colKey: 'categoryId', title: '位置', width: 200 },
{ colKey: 'status', title: '状态', width: 80 }, { colKey: 'status', title: '状态', width: 80 },
{ colKey: 'enabled', title: '启用', width: 60 }, { colKey: 'enabled', title: '启用', width: 60 },
{ colKey: 'chunkCount', title: '分块', width: 60, sorter: true }, { colKey: 'chunkCount', title: '分块', width: 60, sorter: true },
@ -262,6 +266,20 @@ function getDocTags(doc: any): string[] {
function statusLabel(s: string) { return s === 'READY' ? '已完成' : s === 'PROCESSING' ? '处理中' : s === 'FAILED' ? '失败' : s } function statusLabel(s: string) { return s === 'READY' ? '已完成' : s === 'PROCESSING' ? '处理中' : s === 'FAILED' ? '失败' : s }
function formatSize(b: number) { return b ? formatBytes(b) : '-' } function formatSize(b: number) { return b ? formatBytes(b) : '-' }
/** 计算文档所在目录路径(不含分类名);直接挂分类根/无目录时返回「—」 */
function docPath(row: any): string {
let fid = row.folderId != null ? String(row.folderId) : '0'
const chain: string[] = []
let guard = 0
while (fid && fid !== '0' && guard++ < 20) {
const f = folderMap.value[fid]
if (!f) break
chain.unshift(f.name)
fid = String(f.parentId ?? '0')
}
return chain.length ? chain.join(' / ') : '—'
}
// ==================== ==================== // ==================== ====================
const debouncedLoad = debounce(() => load(1)) const debouncedLoad = debounce(() => load(1))
@ -342,6 +360,9 @@ async function loadTreeData() {
const cats = catRes.success ? (catRes.data || []) : [] const cats = catRes.success ? (catRes.data || []) : []
const folders = folderRes.success ? (folderRes.data || []) : [] const folders = folderRes.success ? (folderRes.data || []) : []
treeData.value = buildTree(cats, folders) treeData.value = buildTree(cats, folders)
//
folderMap.value = {}
for (const f of folders || []) folderMap.value[String(f.id)] = { name: f.name, parentId: f.parentId }
} catch (e: any) { } catch (e: any) {
toast('加载目录树失败:' + e.message, 'error') toast('加载目录树失败:' + e.message, 'error')
} finally { } finally {
@ -626,10 +647,16 @@ loadData()
<style scoped> <style scoped>
.selected-count { font-size: 12px; font-weight: 600; color: var(--td-text-color-primary); } .selected-count { font-size: 12px; font-weight: 600; color: var(--td-text-color-primary); }
/* 位置列:长路径省略号截断,悬停 title 看全 */
.doc-pos { display: inline-block; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; vertical-align: bottom; }
/* 左右分栏布局 */ /* 左右分栏布局 */
/* 卡片锁定一屏高度:左侧目录树独立滚动,右侧工具栏固定 + 表格内部滚动 */ /* 卡片锁定一屏高度:左侧目录树独立滚动,右侧工具栏固定 + 表格内部滚动 */
.doc-list-card { height: 100%; display: flex; flex-direction: column; }
.doc-list-card { height: 100%; min-height: 0; overflow: hidden; display: flex; flex-direction: column; }
.doc-list-card :deep(.t-card__header) { flex-shrink: 0; }
/* TDesign header body .t-loading__parent flex
否则 .t-card__body flex:1 不生效目录树无法在面板内滚动而撑高整页 */
.doc-list-card :deep(.t-loading__parent) { flex: 1; min-height: 0; display: flex; flex-direction: column; overflow: hidden; }
.doc-list-card :deep(.t-card__body) { flex: 1; min-height: 0; overflow: hidden; display: flex; flex-direction: column; } .doc-list-card :deep(.t-card__body) { flex: 1; min-height: 0; overflow: hidden; display: flex; flex-direction: column; }
.doc-layout { flex: 1; min-height: 0; display: flex; align-items: stretch; } .doc-layout { flex: 1; min-height: 0; display: flex; align-items: stretch; }
@ -653,7 +680,7 @@ loadData()
.tree-panel-body { flex: 1; min-height: 0; overflow-y: auto; overflow-x: hidden; } .tree-panel-body { flex: 1; min-height: 0; overflow-y: auto; overflow-x: hidden; }
.doc-content { flex: 1; min-width: 0; overflow: hidden; padding-left: 16px; display: flex; flex-direction: column; } .doc-content { flex: 1; min-width: 0; overflow: hidden; padding-left: 16px; display: flex; flex-direction: column; }
/* 表格宿主:撑满工具栏下方剩余高度,表格 max-height:100% 相对它解析,实现内部滚动与固定表头 */ /* 表格宿主:撑满工具栏下方剩余高度,表格 max-height:100% 相对它解析,实现内部滚动与固定表头 */
.doc-table-wrap { flex: 1; min-height: 0; }
.doc-table-wrap { flex: 1; min-height: 0; overflow: hidden; }
.tree-panel-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; } .tree-panel-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; }
.tree-panel-title { font-size: 13px; font-weight: 600; } .tree-panel-title { font-size: 13px; font-weight: 600; }

Loading…
Cancel
Save