style: 更新文件管理功能,支持HTML文件图标和颜色,优化文件选择状态管理
This commit is contained in:
parent
66abbc986b
commit
fdc4f174a9
@ -18,10 +18,9 @@
|
||||
- [ ] 统一图谱数据结构,优化可视化方式 [#298](https://github.com/xerrors/Yuxi-Know/issues/298) <Badge type="info" text="0.4" />
|
||||
- [ ] 集成智能体评估,首先使用命令行来实现,然后考虑放在 UI 里面展示
|
||||
- [ ] 开发与生产环境隔离,构建生产镜像 <Badge type="info" text="0.4" />
|
||||
- [ ] 支持 MinerU 2.5 的解析方法 <Badge type="info" text="0.3.5" />
|
||||
- [ ] MinerU 处理的结果转义字符没有处理
|
||||
- [ ] 文件管理:(1)文件选择的时候会跨数据库;(2)文件校验会算上失败的文件;
|
||||
- [ ] Tasker 中获取历史任务的时候,仅获取 top100 个 task。
|
||||
- [x] 支持 MinerU 2.5 的解析方法 <Badge type="info" text="0.3.5" />
|
||||
- [x] 文件管理:(1)文件选择的时候会跨数据库;(2)文件校验会算上失败的文件;
|
||||
- [x] Tasker 中获取历史任务的时候,仅获取 top100 个 task。
|
||||
|
||||
|
||||
## Later
|
||||
|
||||
@ -359,6 +359,8 @@ class KnowledgeBaseManager:
|
||||
for file_info in kb_instance.files_meta.values():
|
||||
if file_info.get("database_id") != db_id:
|
||||
continue
|
||||
if file_info.get("status") == "failed":
|
||||
continue
|
||||
if file_info.get("content_hash") == content_hash:
|
||||
return True
|
||||
|
||||
|
||||
@ -10,12 +10,21 @@
|
||||
>添加文件</a-button>
|
||||
</div>
|
||||
<div class="panel-actions">
|
||||
<a-input
|
||||
v-model:value="filenameFilter"
|
||||
placeholder="搜索文件名"
|
||||
size="small"
|
||||
class="action-searcher"
|
||||
allow-clear
|
||||
@change="onFilterChange"
|
||||
/>
|
||||
<a-button
|
||||
type="text"
|
||||
@click="handleRefresh"
|
||||
:loading="refreshing"
|
||||
:icon="h(ReloadOutlined)"
|
||||
:icon="h(RefreshCcw)"
|
||||
title="刷新"
|
||||
class="panel-action-btn"
|
||||
/>
|
||||
<!-- <a-button
|
||||
@click="toggleAutoRefresh"
|
||||
@ -26,22 +35,14 @@
|
||||
>
|
||||
Auto
|
||||
</a-button> -->
|
||||
<a-input
|
||||
v-model:value="filenameFilter"
|
||||
placeholder="搜索文件名"
|
||||
size="small"
|
||||
style="width: 120px; margin-right: 8px; border-radius: 6px; padding: 4px 8px;"
|
||||
allow-clear
|
||||
@change="onFilterChange"
|
||||
/>
|
||||
<!-- <a-button
|
||||
<a-button
|
||||
type="text"
|
||||
@click="toggleRightPanel"
|
||||
:icon="h(ChevronLast)"
|
||||
title="切换右侧面板"
|
||||
class="panel-action-btn expand"
|
||||
:class="{ 'expanded': props.rightPanelVisible }"
|
||||
/> -->
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -134,10 +135,9 @@ import {
|
||||
ClockCircleFilled,
|
||||
DeleteOutlined,
|
||||
PlusOutlined,
|
||||
ReloadOutlined,
|
||||
DownloadOutlined,
|
||||
} from '@ant-design/icons-vue';
|
||||
import { ChevronLast } from 'lucide-vue-next';
|
||||
import { ChevronLast, RefreshCcw } from 'lucide-vue-next';
|
||||
|
||||
const store = useDatabaseStore();
|
||||
const userStore = useUserStore();
|
||||
@ -403,6 +403,14 @@ import { parseToShanghai } from '@/utils/time';
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
|
||||
.action-searcher {
|
||||
width: 120px;
|
||||
margin-right: 8px;
|
||||
border-radius: 6px;
|
||||
padding: 4px 8px;
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
.batch-actions-compact {
|
||||
@ -500,25 +508,27 @@ import { parseToShanghai } from '@/utils/time';
|
||||
}
|
||||
|
||||
.panel-action-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 6px;
|
||||
border: 1px solid var(--gray-300);
|
||||
background-color: var(--gray-50);
|
||||
/* border: 1px solid var(--gray-300); */
|
||||
/* background-color: var(--gray-50); */
|
||||
color: var(--gray-700);
|
||||
transition: all 0.1s ease;
|
||||
font-size: 12px;
|
||||
|
||||
svg {
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
&.expand {
|
||||
transform: scaleX(-1);
|
||||
}
|
||||
}
|
||||
|
||||
.panel-action-btn.expanded {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transform: scaleX(1);
|
||||
padding: 4px;
|
||||
&.expanded {
|
||||
transform: scaleX(1);
|
||||
}
|
||||
}
|
||||
|
||||
.panel-action-btn.auto-refresh-btn.ant-btn-primary {
|
||||
@ -535,19 +545,19 @@ import { parseToShanghai } from '@/utils/time';
|
||||
.panel-action-btn:hover {
|
||||
background-color: var(--main-5);
|
||||
color: var(--main-color);
|
||||
border: 1px solid var(--main-color);
|
||||
}
|
||||
|
||||
.panel-action-btn.expanded:hover {
|
||||
background-color: var(--main-5);
|
||||
border-color: var(--main-color);
|
||||
color: var(--main-color);
|
||||
}
|
||||
|
||||
/* Table row selection styling */
|
||||
:deep(.ant-table-tbody > tr.ant-table-row-selected > td) {
|
||||
background-color: var(--main-5);
|
||||
}
|
||||
|
||||
:deep(.ant-table-tbody > tr.ant-table-row-selected.ant-table-row:hover > td) {
|
||||
background-color: var(--main-20);
|
||||
}
|
||||
|
||||
:deep(.ant-table-tbody > tr:hover > td) {
|
||||
background-color: var(--main-5);
|
||||
}
|
||||
|
||||
@ -40,6 +40,8 @@ export const useDatabaseStore = defineStore('database', () => {
|
||||
});
|
||||
|
||||
let refreshInterval = null;
|
||||
let autoRefreshSource = null; // Tracks whether auto-refresh was user-triggered or automatic
|
||||
let autoRefreshManualOverride = false; // Indicates user explicitly disabled auto-refresh
|
||||
|
||||
// Actions
|
||||
async function getDatabaseInfo(id) {
|
||||
@ -51,6 +53,7 @@ export const useDatabaseStore = defineStore('database', () => {
|
||||
try {
|
||||
const data = await databaseApi.getDatabaseInfo(db_id);
|
||||
database.value = data;
|
||||
ensureAutoRefreshForProcessing(data?.files);
|
||||
await loadQueryParams(db_id);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
@ -180,6 +183,40 @@ export const useDatabaseStore = defineStore('database', () => {
|
||||
});
|
||||
}
|
||||
|
||||
const processingStatuses = new Set(['processing', 'waiting']);
|
||||
|
||||
function enableAutoRefresh(source = 'auto') {
|
||||
if (autoRefreshManualOverride && source === 'auto') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!state.autoRefresh) {
|
||||
state.autoRefresh = true;
|
||||
autoRefreshSource = source;
|
||||
autoRefreshManualOverride = false;
|
||||
startAutoRefresh();
|
||||
return;
|
||||
}
|
||||
|
||||
if (source === 'auto' && autoRefreshSource !== 'manual') {
|
||||
autoRefreshSource = 'auto';
|
||||
}
|
||||
}
|
||||
|
||||
function ensureAutoRefreshForProcessing(filesMap) {
|
||||
const files = Object.values(filesMap || {});
|
||||
const hasPending = files.some((file) => file && processingStatuses.has(file.status));
|
||||
if (hasPending) {
|
||||
enableAutoRefresh('auto');
|
||||
} else if (autoRefreshSource === 'auto' && state.autoRefresh) {
|
||||
state.autoRefresh = false;
|
||||
autoRefreshSource = null;
|
||||
autoRefreshManualOverride = false;
|
||||
stopAutoRefresh();
|
||||
}
|
||||
return hasPending;
|
||||
}
|
||||
|
||||
async function addFiles({ items, contentType, params }) {
|
||||
if (items.length === 0) {
|
||||
message.error(contentType === 'file' ? '请先上传文件' : '请输入有效的网页链接');
|
||||
@ -191,6 +228,7 @@ export const useDatabaseStore = defineStore('database', () => {
|
||||
const data = await documentApi.addDocuments(databaseId.value, items, { ...params, content_type: contentType });
|
||||
if (data.status === 'success' || data.status === 'queued') {
|
||||
const itemType = contentType === 'file' ? '文件' : 'URL';
|
||||
enableAutoRefresh('auto');
|
||||
message.success(data.message || `${itemType}已提交处理,请在任务中心查看进度`);
|
||||
if (data.task_id) {
|
||||
taskerStore.registerQueuedTask({
|
||||
@ -299,10 +337,15 @@ export const useDatabaseStore = defineStore('database', () => {
|
||||
}
|
||||
|
||||
function toggleAutoRefresh() {
|
||||
state.autoRefresh = !state.autoRefresh;
|
||||
if (state.autoRefresh) {
|
||||
const nextState = !state.autoRefresh;
|
||||
state.autoRefresh = nextState;
|
||||
if (nextState) {
|
||||
autoRefreshSource = 'manual';
|
||||
autoRefreshManualOverride = false;
|
||||
startAutoRefresh();
|
||||
} else {
|
||||
autoRefreshManualOverride = true;
|
||||
autoRefreshSource = null;
|
||||
stopAutoRefresh();
|
||||
}
|
||||
}
|
||||
|
||||
@ -38,6 +38,10 @@ export const getFileIcon = (filename) => {
|
||||
'bmp': FileImageFilled,
|
||||
'svg': FileImageFilled,
|
||||
'webp': FileImageFilled,
|
||||
|
||||
// HTML文件
|
||||
'html': FileTextFilled,
|
||||
'htm': FileTextFilled,
|
||||
}
|
||||
|
||||
return iconMap[extension] || FileUnknownFilled
|
||||
@ -79,6 +83,10 @@ export const getFileIconColor = (filename) => {
|
||||
'bmp': '#722ed1',
|
||||
'svg': '#722ed1',
|
||||
'webp': '#722ed1',
|
||||
|
||||
// HTML文件 - 橙色
|
||||
'html': '#fa8c16',
|
||||
'htm': '#fa8c16',
|
||||
}
|
||||
|
||||
return colorMap[extension] || '#8c8c8c'
|
||||
|
||||
@ -175,8 +175,15 @@ const toggleGraphMaximize = () => {
|
||||
isGraphMaximized.value = !isGraphMaximized.value;
|
||||
};
|
||||
|
||||
const resetFileSelectionState = () => {
|
||||
store.selectedRowKeys = [];
|
||||
store.selectedFile = null;
|
||||
store.state.fileDetailModalVisible = false;
|
||||
};
|
||||
|
||||
watch(() => route.params.database_id, async (newId) => {
|
||||
store.databaseId = newId;
|
||||
resetFileSelectionState();
|
||||
store.stopAutoRefresh();
|
||||
await store.getDatabaseInfo(newId);
|
||||
store.startAutoRefresh();
|
||||
@ -187,6 +194,7 @@ watch(() => route.params.database_id, async (newId) => {
|
||||
// 组件挂载时启动示例轮播
|
||||
onMounted(() => {
|
||||
store.databaseId = route.params.database_id;
|
||||
resetFileSelectionState();
|
||||
store.getDatabaseInfo();
|
||||
store.startAutoRefresh();
|
||||
|
||||
@ -549,4 +557,4 @@ const handleMouseUpHorizontal = () => {
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user