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