feat: 更新组件样式和逻辑,优化用户体验

This commit is contained in:
Wenjie Zhang 2025-11-06 21:59:48 +08:00
parent 3a6e76e06e
commit f0275aaadc
7 changed files with 279 additions and 314 deletions

View File

@ -46,7 +46,7 @@
</div> </div>
</div> </div>
<div class="batch-actions-compact" v-if="selectedRowKeys.length > 0"> <div class="batch-actions" v-if="selectedRowKeys.length > 0">
<div class="batch-info"> <div class="batch-info">
<span>{{ selectedRowKeys.length }} </span> <span>{{ selectedRowKeys.length }} </span>
<a-button <a-button
@ -74,7 +74,7 @@
:columns="columnsCompact" :columns="columnsCompact"
:data-source="filteredFiles" :data-source="filteredFiles"
row-key="file_id" row-key="file_id"
class="my-table-compact" class="my-table"
size="small" size="small"
:pagination="paginationCompact" :pagination="paginationCompact"
:row-selection="{ :row-selection="{
@ -373,7 +373,8 @@ import { parseToShanghai } from '@/utils/time';
.file-table-container { .file-table-container {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 100%; max-height: 100%;
overflow: hidden;
border-radius: 12px; border-radius: 12px;
border: 1px solid var(--gray-150); border: 1px solid var(--gray-150);
padding-top: 6px; padding-top: 6px;
@ -416,12 +417,12 @@ import { parseToShanghai } from '@/utils/time';
} }
} }
.batch-actions-compact { .batch-actions {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
padding: 2px 4px; padding: 2px 12px;
background-color: var(--main-5); background-color: var(--main-10);
border-radius: 4px; border-radius: 4px;
margin-bottom: 4px; margin-bottom: 4px;
flex-shrink: 0; flex-shrink: 0;
@ -434,31 +435,31 @@ import { parseToShanghai } from '@/utils/time';
} }
.batch-info span { .batch-info span {
font-size: 11px; font-size: 12px;
font-weight: 500; font-weight: 500;
color: var(--gray-700); color: var(--gray-700);
} }
.batch-actions-compact .ant-btn { .batch-actions .ant-btn {
font-size: 11px; font-size: 12px;
padding: 0 6px; padding: 0 6px;
height: 22px; height: 22px;
border-radius: 3px; border-radius: 3px;
} }
.batch-actions-compact .ant-btn:hover { .batch-actions .ant-btn:hover {
background-color: var(--main-20); background-color: var(--main-20);
color: var(--main-color); color: var(--main-color);
} }
.my-table-compact { .my-table {
flex: 1; flex: 1;
overflow: auto; overflow: auto;
background-color: transparent; background-color: transparent;
min-height: 0; /* 让 flex 子项可以正确缩小 */ min-height: 0; /* 让 flex 子项可以正确缩小 */
} }
.my-table-compact .main-btn { .my-table .main-btn {
padding: 0; padding: 0;
height: auto; height: auto;
line-height: 1.4; line-height: 1.4;
@ -468,32 +469,32 @@ import { parseToShanghai } from '@/utils/time';
text-decoration: none; text-decoration: none;
} }
.my-table-compact .main-btn:hover { .my-table .main-btn:hover {
cursor: pointer; cursor: pointer;
color: var(--main-color); color: var(--main-color);
} }
.my-table-compact .del-btn { .my-table .del-btn {
color: var(--gray-500); color: var(--gray-500);
} }
.my-table-compact .download-btn { .my-table .download-btn {
color: var(--gray-500); color: var(--gray-500);
} }
.my-table-compact .download-btn:hover { .my-table .download-btn:hover {
color: var(--main-color); color: var(--main-color);
} }
.my-table-compact .del-btn:hover { .my-table .del-btn:hover {
color: var(--error-color); color: var(--error-color);
} }
.my-table-compact .del-btn:disabled { .my-table .del-btn:disabled {
cursor: not-allowed; cursor: not-allowed;
} }
.my-table-compact .span-type { .my-table .span-type {
display: inline-block; display: inline-block;
padding: 1px 5px; padding: 1px 5px;
font-size: 10px; font-size: 10px;

View File

@ -21,12 +21,12 @@
<div class="add-files-content"> <div class="add-files-content">
<div class="upload-header"> <div class="upload-header">
<div class="source-selector"> <div class="source-selector">
<div class="upload-mode-selector" @click="uploadMode = 'file'" :class="{ active: uploadMode === 'file' }"> <a-segmented
<FileOutlined /> 上传文件 v-model:value="uploadMode"
</div> :options="uploadModeOptions"
<div class="upload-mode-selector" @click="uploadMode = 'url'" :class="{ active: uploadMode === 'url' }"> size="large"
<LinkOutlined /> 输入网址 class="source-segmented"
</div> />
</div> </div>
<div class="config-controls"> <div class="config-controls">
<a-button type="dashed" @click="showChunkConfigModal" v-if="!isGraphBased"> <a-button type="dashed" @click="showChunkConfigModal" v-if="!isGraphBased">
@ -35,7 +35,7 @@
</div> </div>
</div> </div>
<div class="ocr-config"> <div class="ocr-config" v-if="uploadMode === 'file'">
<a-form layout="horizontal"> <a-form layout="horizontal">
<a-form-item label="使用OCR" name="enable_ocr"> <a-form-item label="使用OCR" name="enable_ocr">
<div class="ocr-controls"> <div class="ocr-controls">
@ -67,36 +67,6 @@
</a-form> </a-form>
</div> </div>
<div class="qa-split-config" v-if="isQaSplitSupported">
<a-form layout="horizontal">
<a-form-item label="QA分割模式" name="use_qa_split">
<div class="toggle-controls">
<a-switch
v-model:checked="chunkParams.use_qa_split"
style="margin-right: 12px;"
/>
<span class="param-description">
{{ chunkParams.use_qa_split ? '启用QA分割忽略chunk大小设置' : '使用普通分割模式' }}
</span>
</div>
</a-form-item>
<a-form-item
v-if="chunkParams.use_qa_split"
label="QA分隔符"
name="qa_separator"
>
<a-input
v-model:value="chunkParams.qa_separator"
placeholder="输入QA分隔符"
style="width: 200px; margin-right: 12px;"
/>
<span class="param-description">
用于分割不同QA对的分隔符默认为3个换行符
</span>
</a-form-item>
</a-form>
</div>
<!-- PDF/图片OCR提醒 --> <!-- PDF/图片OCR提醒 -->
<div v-if="uploadMode === 'file' && hasPdfOrImageFiles && !isOcrEnabled" class="ocr-warning-alert"> <div v-if="uploadMode === 'file' && hasPdfOrImageFiles && !isOcrEnabled" class="ocr-warning-alert">
检测到PDF或图片文件请启用OCR功能以提取文本内容 检测到PDF或图片文件请启用OCR功能以提取文本内容
@ -181,7 +151,7 @@
</template> </template>
<script setup> <script setup>
import { ref, computed, onMounted } from 'vue'; import { ref, computed, onMounted, watch } from 'vue';
import { message, Upload } from 'ant-design-vue'; import { message, Upload } from 'ant-design-vue';
import { useUserStore } from '@/stores/user'; import { useUserStore } from '@/stores/user';
import { useDatabaseStore } from '@/stores/database'; import { useDatabaseStore } from '@/stores/database';
@ -295,6 +265,24 @@ const chunkLoading = computed(() => store.state.chunkLoading);
// //
const uploadMode = ref('file'); const uploadMode = ref('file');
const previousOcrSelection = ref('disable');
const uploadModeOptions = computed(() => [
{
value: 'file',
label: h('div', { class: 'segmented-option' }, [
h(FileOutlined, { class: 'option-icon' }),
h('span', { class: 'option-text' }, '上传文件'),
]),
},
{
value: 'url',
label: h('div', { class: 'segmented-option' }, [
h(LinkOutlined, { class: 'option-icon' }),
h('span', { class: 'option-text' }, '输入网址'),
]),
},
]);
// //
const fileList = ref([]); const fileList = ref([]);
@ -349,6 +337,15 @@ const isOcrEnabled = computed(() => {
return chunkParams.value.enable_ocr !== 'disable'; return chunkParams.value.enable_ocr !== 'disable';
}); });
watch(uploadMode, (mode, previous) => {
if (mode === 'url') {
previousOcrSelection.value = chunkParams.value.enable_ocr;
chunkParams.value.enable_ocr = 'disable';
} else if (mode === 'file' && previous === 'url') {
chunkParams.value.enable_ocr = previousOcrSelection.value || 'disable';
}
});
// PDF // PDF
const hasPdfOrImageFiles = computed(() => { const hasPdfOrImageFiles = computed(() => {
if (fileList.value.length === 0) { if (fileList.value.length === 0) {
@ -647,25 +644,7 @@ const chunkData = async () => {
.source-selector { .source-selector {
display: flex; display: flex;
gap: 12px; align-items: center;
}
.upload-mode-selector {
padding: 8px 16px;
border: 1px solid var(--gray-300);
border-radius: 6px;
cursor: pointer;
transition: all 0.3s;
}
.upload-mode-selector:hover {
border-color: var(--main-color);
}
.upload-mode-selector.active {
border-color: var(--main-color);
background-color: var(--main-30);
color: var(--main-color);
} }
.config-controls { .config-controls {
@ -673,19 +652,52 @@ const chunkData = async () => {
align-items: center; align-items: center;
} }
.ocr-config, .source-segmented {
.qa-split-config { background-color: var(--gray-100);
border-radius: 999px;
padding: 4px;
border: 1px solid var(--gray-200);
}
.source-segmented :deep(.ant-segmented-item) {
border-radius: 999px;
}
.source-segmented :deep(.ant-segmented-item-label) {
display: flex;
align-items: center;
gap: 8px;
padding: 6px 16px;
font-weight: 500;
color: var(--gray-600);
}
.source-segmented :deep(.ant-segmented-thumb) {
background-color: var(--main-30);
border-radius: 999px;
}
.source-segmented :deep(.ant-segmented-item-selected .ant-segmented-item-label) {
color: var(--main-color);
}
.source-segmented :deep(.segmented-option) {
display: flex;
align-items: center;
gap: 8px;
}
.source-segmented :deep(.option-icon) {
font-size: 16px;
}
.ocr-config {
margin-bottom: 20px; margin-bottom: 20px;
padding: 16px; padding: 16px;
background-color: var(--gray-50); background-color: var(--gray-50);
border-radius: 6px; border-radius: 6px;
} }
.toggle-controls {
display: flex;
align-items: center;
}
.param-description { .param-description {
font-size: 12px; font-size: 12px;
color: var(--gray-600); color: var(--gray-600);

View File

@ -113,17 +113,25 @@
</template> </template>
<script setup> <script setup>
import { ref, computed, watch } from 'vue'; import { ref, computed, watch, nextTick, onUnmounted } from 'vue';
import { useDatabaseStore } from '@/stores/database'; import { useDatabaseStore } from '@/stores/database';
import { ReloadOutlined, ExpandOutlined } from '@ant-design/icons-vue'; import { ReloadOutlined, ExpandOutlined } from '@ant-design/icons-vue';
import { message } from 'ant-design-vue';
import KnowledgeGraphViewer from '@/components/KnowledgeGraphViewer.vue'; import KnowledgeGraphViewer from '@/components/KnowledgeGraphViewer.vue';
import { h } from 'vue'; import { h } from 'vue';
import { getKbTypeLabel } from '@/utils/kb_utils';
const props = defineProps({
active: {
type: Boolean,
default: false,
},
});
const store = useDatabaseStore(); const store = useDatabaseStore();
const databaseId = computed(() => store.databaseId); const databaseId = computed(() => store.databaseId);
const kbType = computed(() => store.database.kb_type); const kbType = computed(() => store.database.kb_type);
const kbTypeLabel = computed(() => getKbTypeLabel(kbType.value || 'lightrag'));
const graphStats = computed({ const graphStats = computed({
get: () => store.graphStats, get: () => store.graphStats,
set: (stats) => store.graphStats = stats set: (stats) => store.graphStats = stats
@ -146,10 +154,9 @@ const isGraphSupported = computed(() => {
return type === 'lightrag'; return type === 'lightrag';
}); });
let pendingLoadTimer = null;
const loadGraph = () => { const loadGraph = () => {
if (!(Object.keys(store.database?.files).length > 0)) {
return;
}
if (graphViewerRef.value && typeof graphViewerRef.value.loadFullGraph === 'function') { if (graphViewerRef.value && typeof graphViewerRef.value.loadFullGraph === 'function') {
graphViewerRef.value.loadFullGraph(); graphViewerRef.value.loadFullGraph();
} }
@ -220,12 +227,45 @@ const applySettings = () => {
// } // }
// }; // };
watch(isGraphSupported, (supported) => { const scheduleGraphLoad = (delay = 200) => {
if (supported) { if (!props.active || !isGraphSupported.value) {
setTimeout(() => { return;
loadGraph(); }
}, 800); if (pendingLoadTimer) {
clearTimeout(pendingLoadTimer);
}
pendingLoadTimer = setTimeout(() => {
nextTick(() => {
loadGraph();
});
}, delay);
};
watch(
() => props.active,
(active) => {
if (active) {
scheduleGraphLoad();
} }
},
{ immediate: true }
);
watch(databaseId, () => {
scheduleGraphLoad(300);
});
watch(isGraphSupported, (supported) => {
if (supported) {
scheduleGraphLoad(200);
}
});
onUnmounted(() => {
if (pendingLoadTimer) {
clearTimeout(pendingLoadTimer);
pendingLoadTimer = null;
}
}); });
</script> </script>

View File

@ -444,7 +444,7 @@ onBeforeUnmount(() => {
} }
&:disabled { &:disabled {
background-color: var(--gray-400); opacity: 0.5;
cursor: not-allowed; cursor: not-allowed;
transform: none; transform: none;
box-shadow: none; box-shadow: none;

View File

@ -3,63 +3,37 @@
<div class="query-section-layout"> <div class="query-section-layout">
<!-- 主内容区域 --> <!-- 主内容区域 -->
<div class="query-main"> <div class="query-main">
<div class="query-header">
<div class="header-status">
<a-switch
v-model:checked="showRawData"
size="small"
checked-children="原始数据"
un-checked-children="格式化"
/>
<span v-if="searchLoading" class="search-status">
<a-spin size="small" />
搜索中...
</span>
<span v-else-if="queryResult" class="result-count">
找到 {{ resultCount }} 条结果
</span>
</div>
</div>
<div class="query-input-container"> <div class="query-input-container">
<a-textarea <div class="search-input-wrapper">
v-model:value="queryText" <a-textarea
placeholder="输入查询内容" v-model:value="queryText"
:auto-size="{ minRows: 3, maxRows: 6 }" placeholder="输入查询内容..."
class="compact-query-textarea" :auto-size="{ minRows: 2, maxRows: 6 }"
/> class="search-textarea"
<div class="query-actions-row"> @press-enter.prevent="onQuery"
<a-button />
@click="onQuery" <div class="search-actions">
:loading="searchLoading" <a-switch
class="search-button" v-model:checked="showRawData"
type="primary" checked-children="格式化"
> un-checked-children="原始"
<template #icon> />
<SearchOutlined /> <a-button
</template> @click="onQuery"
搜索 :loading="searchLoading"
</a-button> class="search-button"
<div class="query-examples-compact"> type="primary"
<span class="examples-label">示例</span> :disabled="!queryText.trim()"
<div class="examples-container"> :icon=h(SearchOutlined)
<a-button shape="circle"
type="text" />
:key="currentExampleIndex"
@click="useQueryExample(queryExamples[currentExampleIndex])"
size="small"
class="example-btn"
>
{{ queryExamples[currentExampleIndex] }}
</a-button>
</div>
</div> </div>
</div> </div>
</div> </div>
<div class="query-results" v-if="queryResult"> <div class="query-results" v-if="queryResult">
<!-- 原始数据显示 --> <!-- 原始数据显示 -->
<div v-if="showRawData" class="result-raw"> <div v-if="!showRawData" class="result-raw">
<pre>{{ JSON.stringify(queryResult, null, 2) }}</pre> <pre>{{ JSON.stringify(queryResult, null, 2) }}</pre>
</div> </div>
@ -128,16 +102,13 @@
</template> </template>
<script setup> <script setup>
import { ref, computed, onMounted, onUnmounted } from 'vue'; import { ref, computed, onMounted, h } from 'vue';
import { useDatabaseStore } from '@/stores/database'; import { useDatabaseStore } from '@/stores/database';
import { message } from 'ant-design-vue'; import { message } from 'ant-design-vue';
import { queryApi } from '@/apis/knowledge_api'; import { queryApi } from '@/apis/knowledge_api';
import { import {
SearchOutlined, SearchOutlined,
SettingOutlined,
CloseOutlined,
} from '@ant-design/icons-vue'; } from '@ant-design/icons-vue';
import { h } from 'vue';
const store = useDatabaseStore(); const store = useDatabaseStore();
@ -155,30 +126,11 @@ const props = defineProps({
const searchLoading = computed(() => store.state.searchLoading); const searchLoading = computed(() => store.state.searchLoading);
const queryResult = ref(''); const queryResult = ref('');
const showRawData = ref(false); const showRawData = ref(true);
const resultCount = ref(0);
// //
const queryText = ref(''); const queryText = ref('');
//
const queryExamples = ref([
'孕妇应该避免吃哪些水果?',
'荔枝应该怎么清洗?',
'如何判断西瓜是否成熟?',
'苹果有哪些营养价值?',
'什么季节最适合吃梨?',
'如何保存草莓以延长保质期?',
'香蕉变黑后还能吃吗?',
'橙子皮可以用来做什么?'
]);
//
const currentExampleIndex = ref(0);
//
let exampleCarouselInterval = null;
const onQuery = async () => { const onQuery = async () => {
if (!queryText.value.trim()) { if (!queryText.value.trim()) {
@ -195,57 +147,19 @@ const onQuery = async () => {
const data = await queryApi.queryTest(store.database.db_id, queryText.value.trim(), queryMeta); const data = await queryApi.queryTest(store.database.db_id, queryText.value.trim(), queryMeta);
queryResult.value = data; queryResult.value = data;
//
if (data?.data && Array.isArray(data.data)) {
resultCount.value = data.data.length;
} else if (data) {
resultCount.value = 1;
}
} catch (error) { } catch (error) {
console.error(error); console.error(error);
message.error(error.message); message.error(error.message);
queryResult.value = ''; queryResult.value = '';
resultCount.value = 0;
} finally { } finally {
store.state.searchLoading = false; store.state.searchLoading = false;
} }
}; };
const useQueryExample = (example) => { //
queryText.value = example;
onQuery();
};
const startExampleCarousel = () => {
if (exampleCarouselInterval) return;
exampleCarouselInterval = setInterval(() => {
currentExampleIndex.value = (currentExampleIndex.value + 1) % queryExamples.value.length;
}, 6000); // 6
};
const stopExampleCarousel = () => {
if (exampleCarouselInterval) {
clearInterval(exampleCarouselInterval);
exampleCarouselInterval = null;
}
};
//
onMounted(() => { onMounted(() => {
//
startExampleCarousel();
// //
store.loadQueryParams(); store.loadQueryParams();
});
//
onUnmounted(() => {
//
stopExampleCarousel();
}); });
</script> </script>
@ -267,98 +181,92 @@ onUnmounted(() => {
flex-direction: column; flex-direction: column;
overflow: hidden; overflow: hidden;
min-width: 0; min-width: 0;
} max-height: 100%;
.query-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 16px;
border-bottom: 1px solid var(--gray-200);
background-color: #fff;
.header-status {
display: flex;
align-items: center;
gap: 16px;
font-size: 13px;
color: var(--gray-600);
.search-status {
display: flex;
align-items: center;
gap: 6px;
}
.result-count {
font-weight: 500;
}
}
} }
.query-input-container { .query-input-container {
padding: 16px; padding: 16px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 16px; gap: 12px;
background-color: #fff; background-color: var(--gray-0);
border-bottom: 1px solid var(--gray-200);
} }
.compact-query-textarea { .search-input-wrapper {
flex: 1; display: flex;
flex-direction: column;
gap: 12px;
padding: 12px 16px 8px;
border-radius: 12px;
border: 1px solid var(--gray-200);
background-color: var(--gray-0);
box-shadow: 0 1px 3px rgba(23, 23, 23, 0.05);
transition: border-color 0.5s ease, box-shadow 0.5s ease;
&:focus { &:hover {
border-color: var(--main-400);
box-shadow: 0 4px 12px rgba(1, 97, 121, 0.08);
}
:deep(.ant-input) {
border-radius: 8px;
background-color: var(--gray-0);
color: var(--gray-1000);
outline: none; outline: none;
border: none;
box-shadow: none;
padding: 0;
transition: border-color 0.3s ease, box-shadow 0.3s ease;
&:focus {
outline: none;
border: none;
}
&::placeholder {
color: var(--gray-500);
}
} }
} }
.query-actions-row { .search-actions {
display: flex; display: flex;
gap: 16px;
align-items: center; align-items: center;
justify-content: space-between;
gap: 12px;
} }
.search-button { .search-button {
flex-shrink: 0; background-color: var(--main-color);
} border-color: var(--main-color);
box-shadow: 0 2px 4px rgba(1, 97, 121, 0.15);
transition: all 0.2s ease;
.query-examples-compact { &:hover:not(:disabled) {
display: flex; background-color: var(--main-bright);
flex-direction: row; border-color: var(--main-bright);
align-items: center; box-shadow: 0 4px 8px rgba(1, 136, 166, 0.25);
gap: 8px; transform: translateY(-1px);
flex-wrap: wrap; }
}
.examples-label { &:disabled {
font-size: 12px; opacity: 0.5;
color: #8c8c8c; color: var(--gray-0);
white-space: nowrap; cursor: not-allowed;
} box-shadow: none;
transform: none;
.examples-container { }
min-height: 24px;
display: flex;
}
.example-btn {
text-align: left;
white-space: normal;
height: auto;
padding: 4px 8px;
font-size: 12px;
} }
.query-results { .query-results {
flex: 1; flex: 1;
overflow-y: auto; overflow-y: auto;
padding: 20px; padding: 16px;
background-color: #fafafa; background-color: var(--gray-25);
min-height: 0; min-height: 0;
.result-raw { .result-raw {
background-color: #f8f9fa; background-color: var(--gray-50);
border: 1px solid var(--gray-200); border: 1px solid var(--gray-200);
border-radius: 6px; border-radius: 6px;
padding: 16px; padding: 16px;
@ -369,6 +277,7 @@ onUnmounted(() => {
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
font-size: 12px; font-size: 12px;
line-height: 1.5; line-height: 1.5;
color: var(--gray-1000);
white-space: pre-wrap; white-space: pre-wrap;
word-break: break-word; word-break: break-word;
} }
@ -378,32 +287,36 @@ onUnmounted(() => {
white-space: pre-wrap; white-space: pre-wrap;
word-break: break-word; word-break: break-word;
line-height: 1.6; line-height: 1.6;
color: var(--gray-1000);
} }
.result-list { .result-list {
.no-results { .no-results {
text-align: center; text-align: center;
padding: 32px; padding: 32px;
color: #999; color: var(--gray-500);
} }
.result-summary { .result-summary {
margin-bottom: 12px; margin-bottom: 12px;
padding: 8px 12px; padding: 8px 12px;
background-color: #e6f7ff; background-color: var(--main-50);
border-left: 3px solid var(--main-color); border-left: 3px solid var(--main-color);
border-radius: 2px; border-radius: 2px;
color: var(--gray-800);
} }
.result-item { .result-item {
background-color: white; background-color: var(--gray-0);
border: 1px solid #e8e8e8; border: 1px solid var(--gray-200);
border-radius: 6px; border-radius: 6px;
padding: 12px; padding: 12px;
margin-bottom: 12px; margin-bottom: 12px;
transition: all 0.2s ease;
&:hover { &:hover {
border-color: var(--gray-400); border-color: var(--main-300);
box-shadow: 0 2px 8px rgba(1, 97, 121, 0.08);
} }
&:last-child { &:last-child {
@ -416,7 +329,7 @@ onUnmounted(() => {
gap: 12px; gap: 12px;
margin-bottom: 8px; margin-bottom: 8px;
padding-bottom: 8px; padding-bottom: 8px;
border-bottom: 1px solid #f0f0f0; border-bottom: 1px solid var(--gray-150);
.result-index { .result-index {
font-weight: 600; font-weight: 600;
@ -429,20 +342,20 @@ onUnmounted(() => {
font-size: 12px; font-size: 12px;
padding: 2px 8px; padding: 2px 8px;
border-radius: 12px; border-radius: 12px;
background-color: #f0f0f0; background-color: var(--gray-100);
color: #666; color: var(--gray-700);
} }
.result-rerank-score { .result-rerank-score {
background-color: #fff7e6; background-color: var(--stats-warning-bg);
color: #fa8c16; color: var(--stats-warning-color);
} }
} }
.result-content { .result-content {
padding: 8px 0; padding: 8px 0;
line-height: 1.6; line-height: 1.6;
color: #333; color: var(--gray-900);
white-space: pre-wrap; white-space: pre-wrap;
word-break: break-word; word-break: break-word;
} }
@ -453,14 +366,14 @@ onUnmounted(() => {
gap: 12px; gap: 12px;
margin-top: 8px; margin-top: 8px;
padding-top: 8px; padding-top: 8px;
border-top: 1px solid #f0f0f0; border-top: 1px solid var(--gray-150);
.metadata-item { .metadata-item {
font-size: 12px; font-size: 12px;
color: #666; color: var(--gray-700);
strong { strong {
color: #999; color: var(--gray-500);
font-weight: 500; font-weight: 500;
margin-right: 4px; margin-right: 4px;
} }
@ -471,11 +384,13 @@ onUnmounted(() => {
.result-unknown { .result-unknown {
pre { pre {
background-color: white; background-color: var(--gray-0);
border: 1px solid var(--gray-200);
padding: 12px; padding: 12px;
border-radius: 4px; border-radius: 4px;
overflow-x: auto; overflow-x: auto;
font-size: 12px; font-size: 12px;
color: var(--gray-1000);
} }
} }
} }

View File

@ -56,6 +56,7 @@
<a-tab-pane key="graph" tab="知识图谱" v-if="isGraphSupported"> <a-tab-pane key="graph" tab="知识图谱" v-if="isGraphSupported">
<KnowledgeGraphSection <KnowledgeGraphSection
:visible="true" :visible="true"
:active="activeTab === 'graph'"
@toggle-visible="() => {}" @toggle-visible="() => {}"
/> />
</a-tab-pane> </a-tab-pane>
@ -108,19 +109,27 @@ const isGraphSupported = computed(() => {
// Tab - // Tab -
const activeTab = ref('query'); const activeTab = ref('query');
const previousDatabaseId = ref(null);
// IDTab // LightRAG
watch(databaseId, (newDbId, oldDbId) => { watch(
if (newDbId && newDbId !== oldDbId) { () => [databaseId.value, isGraphSupported.value],
// ([newDbId, supported], oldValue = []) => {
// 使setTimeouttick const [oldDbId, previouslySupported] = oldValue;
setTimeout(() => { if (!newDbId) {
activeTab.value = isGraphSupported.value ? 'graph' : 'query'; return;
}, 0); }
previousDatabaseId.value = newDbId;
} if (supported && (newDbId !== oldDbId || previouslySupported === false || previouslySupported === undefined)) {
}, { immediate: true }); activeTab.value = 'graph';
return;
}
if (!supported && activeTab.value === 'graph') {
activeTab.value = 'query';
}
},
{ immediate: true }
);
// / // /
const toggleRightPanel = () => { const toggleRightPanel = () => {
@ -261,10 +270,12 @@ const handleMouseUp = () => {
} }
.left-panel { .left-panel {
display: flex;
flex-shrink: 0; flex-shrink: 0;
flex-grow: 1; flex-grow: 1;
background-color: var(--gray-0); background-color: var(--gray-0);
padding-right: 0; padding-right: 0;
// max-height: calc(100% - 16px);
} }
.right-panel { .right-panel {
@ -315,7 +326,6 @@ const handleMouseUp = () => {
} }
} }
/* Simplify resize handle */ /* Simplify resize handle */
.resize-handle { .resize-handle {
opacity: 0.8; opacity: 0.8;

View File

@ -163,15 +163,6 @@
<!-- 数据库列表 --> <!-- 数据库列表 -->
<div v-else class="databases"> <div v-else class="databases">
<div class="new-database dbcard" @click="state.openNewDatabaseModel=true">
<div class="top">
<div class="icon"><BookPlus /></div>
<div class="info">
<h3>新建知识库</h3>
</div>
</div>
<p>导入您自己的文本数据或通过Webhook实时写入数据以增强 LLM 的上下文</p>
</div>
<div <div
v-for="database in databases" v-for="database in databases"
:key="database.db_id" :key="database.db_id"
@ -224,7 +215,7 @@ import { ref, onMounted, reactive, watch, computed } from 'vue'
import { useRouter, useRoute } from 'vue-router'; import { useRouter, useRoute } from 'vue-router';
import { useConfigStore } from '@/stores/config'; import { useConfigStore } from '@/stores/config';
import { message } from 'ant-design-vue' import { message } from 'ant-design-vue'
import { BookPlus, Database, Zap, FileDigit, Waypoints, Building2 } from 'lucide-vue-next'; import { Database, Zap, FileDigit, Waypoints, Building2 } from 'lucide-vue-next';
import { LockOutlined, InfoCircleOutlined, QuestionCircleOutlined } from '@ant-design/icons-vue'; import { LockOutlined, InfoCircleOutlined, QuestionCircleOutlined } from '@ant-design/icons-vue';
import { databaseApi, typeApi } from '@/apis/knowledge_api'; import { databaseApi, typeApi } from '@/apis/knowledge_api';
import HeaderComponent from '@/components/HeaderComponent.vue'; import HeaderComponent from '@/components/HeaderComponent.vue';
@ -877,10 +868,6 @@ onMounted(() => {
display: grid; display: grid;
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
gap: 16px; gap: 16px;
.new-database {
background-color: #F0F3F4;
}
} }
.database, .graphbase { .database, .graphbase {