refactor: 重构组件和样式以改善UI/UX
- 重构FileDetailModal.vue,增加了下载选项下拉菜单并优化了布局。 - 移除部分页面的响应式媒体查询,不做响应式适配,仅适配大众尺寸 - 改进了ChunkParamsConfig.vue的布局,更好地分组参数。 - 调整了FileTable.vue,改善排序和筛选UI,包括状态筛选逻辑。 - 优化了FileUploadModal.vue的布局,提高OCR引擎配置的可见性。 - 其他 AiTextarea.vue / DataBaseView.vue / AppLayout.vue / file_utils.js / DataBaseInfoView.vue / FeedbackModalComponent.vue / FileTable.vue
This commit is contained in:
parent
55dbbf1846
commit
62685b7ffe
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="chat-container" ref="chatContainerRef">
|
||||
<div class="chat-container">
|
||||
<ChatSidebarComponent
|
||||
:current-chat-id="currentChatId"
|
||||
:chats-list="chatsList"
|
||||
@ -16,13 +16,10 @@
|
||||
@toggle-sidebar="toggleSidebar"
|
||||
@open-agent-modal="openAgentModal"
|
||||
:class="{
|
||||
'floating-sidebar': isSmallContainer,
|
||||
'sidebar-open': chatUIStore.isSidebarOpen,
|
||||
'no-transition': localUIState.isInitialRender,
|
||||
'collapsed': isSmallContainer && !chatUIStore.isSidebarOpen
|
||||
'no-transition': localUIState.isInitialRender
|
||||
}"
|
||||
/>
|
||||
<div class="sidebar-backdrop" v-if="chatUIStore.isSidebarOpen && isSmallContainer" @click="toggleSidebar"></div>
|
||||
<div class="chat">
|
||||
<div class="chat-header">
|
||||
<div class="header__left">
|
||||
@ -39,7 +36,7 @@
|
||||
>
|
||||
<LoaderCircle v-if="chatUIStore.creatingNewChat" class="nav-btn-icon loading-icon" size="18"/>
|
||||
<MessageCirclePlus v-else class="nav-btn-icon" size="18"/>
|
||||
<span class="text" :class="{'hide-text': isMediumContainer}">新对话</span>
|
||||
<span class="text">新对话</span>
|
||||
</div>
|
||||
<div
|
||||
v-if="!props.singleMode"
|
||||
@ -68,7 +65,7 @@
|
||||
<span v-if="hasAgentStateContent" class="text">状态</span>
|
||||
</div>
|
||||
</AgentPopover>
|
||||
<slot name="header-right" :is-medium-container="isMediumContainer"></slot>
|
||||
<slot name="header-right"></slot>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -239,7 +236,6 @@ const threadMessages = ref({});
|
||||
// 本地 UI 状态(仅在本组件使用)
|
||||
const localUIState = reactive({
|
||||
isInitialRender: true,
|
||||
containerWidth: 0,
|
||||
});
|
||||
|
||||
// AgentState Popover 状态
|
||||
@ -367,25 +363,12 @@ const isStreaming = computed(() => {
|
||||
return threadState ? threadState.isStreaming : false;
|
||||
});
|
||||
const isProcessing = computed(() => isStreaming.value);
|
||||
const isSmallContainer = computed(() => localUIState.containerWidth <= 520);
|
||||
const isMediumContainer = computed(() => localUIState.containerWidth <= 768);
|
||||
|
||||
// ==================== SCROLL & RESIZE HANDLING ====================
|
||||
const chatContainerRef = ref(null);
|
||||
const scrollController = new ScrollController('.chat');
|
||||
let resizeObserver = null;
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
if (chatContainerRef.value) {
|
||||
localUIState.containerWidth = chatContainerRef.value.offsetWidth;
|
||||
resizeObserver = new ResizeObserver(entries => {
|
||||
for (let entry of entries) {
|
||||
localUIState.containerWidth = entry.contentRect.width;
|
||||
}
|
||||
});
|
||||
resizeObserver.observe(chatContainerRef.value);
|
||||
}
|
||||
const chatContainer = document.querySelector('.chat');
|
||||
if (chatContainer) {
|
||||
chatContainer.addEventListener('scroll', scrollController.handleScroll, { passive: true });
|
||||
@ -395,7 +378,6 @@ onMounted(() => {
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
if (resizeObserver) resizeObserver.disconnect();
|
||||
scrollController.cleanup();
|
||||
// 清理所有线程状态
|
||||
resetOnGoingConv();
|
||||
@ -1029,38 +1011,6 @@ watch(conversations, () => {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.sidebar-backdrop {
|
||||
display: none; /* 默认隐藏,通过v-if控制显示 */
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(0, 0, 0, 0.4);
|
||||
z-index: 99;
|
||||
animation: fadeIn 0.3s ease;
|
||||
}
|
||||
|
||||
.floating-sidebar {
|
||||
position: absolute !important;
|
||||
z-index: 1001;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
transform: translateX(0);
|
||||
transition: transform 0.3s ease;
|
||||
width: 80% !important;
|
||||
max-width: 300px;
|
||||
|
||||
&.no-transition {
|
||||
transition: none !important;
|
||||
}
|
||||
|
||||
&.collapsed {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
}
|
||||
|
||||
.chat {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
@ -1332,14 +1282,7 @@ watch(conversations, () => {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.chat-sidebar.collapsed {
|
||||
width: 0;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.chat-header {
|
||||
.header__left {
|
||||
.text {
|
||||
@ -1348,56 +1291,17 @@ watch(conversations, () => {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 520px) {
|
||||
.sidebar-backdrop {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.chat-box {
|
||||
padding: 1rem 1rem;
|
||||
}
|
||||
|
||||
.bottom {
|
||||
padding: 0.5rem 0.5rem;
|
||||
}
|
||||
|
||||
.chat-header {
|
||||
padding: 0.5rem 0 !important;
|
||||
|
||||
}
|
||||
|
||||
.floating-sidebar {
|
||||
position: fixed;
|
||||
z-index: 100;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
transform: translateX(0);
|
||||
transition: transform 0.3s ease;
|
||||
width: 80% !important;
|
||||
max-width: 300px;
|
||||
|
||||
&.collapsed {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.hide-text {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="less">
|
||||
.agent-nav-btn {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
padding: 6px 14px;
|
||||
padding: 6px 8px;
|
||||
height: 32px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-radius: 12px;
|
||||
border-radius: 8px;
|
||||
color: var(--gray-900);
|
||||
cursor: pointer;
|
||||
width: auto;
|
||||
@ -1407,7 +1311,7 @@ watch(conversations, () => {
|
||||
background: transparent;
|
||||
|
||||
&:hover:not(.is-disabled) {
|
||||
background-color: var(--gray-50);
|
||||
background-color: var(--gray-100);
|
||||
}
|
||||
|
||||
&.is-disabled {
|
||||
@ -1417,7 +1321,7 @@ watch(conversations, () => {
|
||||
}
|
||||
|
||||
.nav-btn-icon {
|
||||
height: 24px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
.loading-icon {
|
||||
|
||||
@ -643,6 +643,7 @@ const resetConfig = async () => {
|
||||
|
||||
.save-btn {
|
||||
flex: 1;
|
||||
height: 42px;
|
||||
background-color: var(--gray-100);
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
@ -1133,7 +1134,7 @@ const resetConfig = async () => {
|
||||
// 响应式适配
|
||||
@media (max-width: 768px) {
|
||||
.agent-config-sidebar.open {
|
||||
width: 100vw;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -16,9 +16,9 @@
|
||||
@click="generateDescription"
|
||||
>
|
||||
<template #icon>
|
||||
<Sparkles size="14" />
|
||||
<WandSparkles size="14" />
|
||||
</template>
|
||||
<span v-if="!loading" class="ai-text">润色</span>
|
||||
<span v-if="!loading" class="ai-text">{{ modelValue?.trim() ? '润色' : '生成' }}</span>
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
</div>
|
||||
@ -28,7 +28,7 @@
|
||||
import { ref } from 'vue'
|
||||
import { message } from 'ant-design-vue'
|
||||
import { databaseApi } from '@/apis/knowledge_api'
|
||||
import { Sparkles } from 'lucide-vue-next'
|
||||
import { WandSparkles } from 'lucide-vue-next'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
@ -97,7 +97,7 @@ const generateDescription = async () => {
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
gap: 4px;
|
||||
padding: 2px 6px;
|
||||
height: 24px;
|
||||
color: var(--main-color);
|
||||
|
||||
@ -243,12 +243,6 @@ const toggleCollapse = () => {
|
||||
transform: translateX(-12px);
|
||||
}
|
||||
|
||||
&.floating-sidebar .sidebar-content {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
&.no-transition {
|
||||
transition: none !important;
|
||||
}
|
||||
|
||||
@ -9,15 +9,17 @@
|
||||
autocomplete="off"
|
||||
layout="vertical"
|
||||
>
|
||||
<a-form-item label="Chunk Size" name="chunk_size">
|
||||
<a-input-number v-model:value="tempChunkParams.chunk_size" :min="100" :max="10000" style="width: 100%;" />
|
||||
<p class="param-description">每个文本片段的最大字符数</p>
|
||||
</a-form-item>
|
||||
<a-form-item label="Chunk Overlap" name="chunk_overlap">
|
||||
<a-input-number v-model:value="tempChunkParams.chunk_overlap" :min="0" :max="1000" style="width: 100%;" />
|
||||
<p class="param-description">相邻文本片段间的重叠字符数</p>
|
||||
</a-form-item>
|
||||
<a-form-item v-if="showQaSplit" label="分隔符 (Separator)" name="qa_separator">
|
||||
<div class="chunk-row">
|
||||
<a-form-item label="Chunk Size" name="chunk_size">
|
||||
<a-input-number v-model:value="tempChunkParams.chunk_size" :min="100" :max="10000" style="width: 100%;" />
|
||||
<p class="param-description">每个文本片段的最大字符数</p>
|
||||
</a-form-item>
|
||||
<a-form-item label="Chunk Overlap" name="chunk_overlap">
|
||||
<a-input-number v-model:value="tempChunkParams.chunk_overlap" :min="0" :max="1000" style="width: 100%;" />
|
||||
<p class="param-description">相邻文本片段间的重叠字符数</p>
|
||||
</a-form-item>
|
||||
</div>
|
||||
<a-form-item v-if="showQaSplit" class="qa-separator-item" label="分隔符 (Separator)" name="qa_separator">
|
||||
<a-input
|
||||
v-model:value="tempChunkParams.qa_separator"
|
||||
placeholder="输入分隔符,例如 \n\n\n 或 ---"
|
||||
@ -59,10 +61,26 @@ defineProps({
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.chunk-row {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.chunk-row > .ant-form-item {
|
||||
flex: 1;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.qa-separator-item {
|
||||
margin-top: 8px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.param-description {
|
||||
font-size: 12px;
|
||||
color: var(--gray-400);
|
||||
margin: 4px 0 0 0;
|
||||
line-height: 1.4;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@ -3,63 +3,118 @@
|
||||
v-model:open="visible"
|
||||
width="1200px"
|
||||
:footer="null"
|
||||
:closable="false"
|
||||
wrap-class-name="file-detail"
|
||||
@after-open-change="afterOpenChange"
|
||||
:bodyStyle="{ height: '80vh', padding: '0' }"
|
||||
>
|
||||
<template #title>
|
||||
<div class="modal-title-wrapper">
|
||||
<span>{{ file?.filename || '文件详情' }}</span>
|
||||
<div class="download-buttons" v-if="file">
|
||||
<a-button
|
||||
type="text"
|
||||
size="small"
|
||||
@click="handleDownloadOriginal"
|
||||
:loading="downloadingOriginal"
|
||||
:disabled="!file.file_id"
|
||||
:icon="h(DownloadOutlined)"
|
||||
>
|
||||
下载原文
|
||||
</a-button>
|
||||
<a-button
|
||||
type="text"
|
||||
size="small"
|
||||
@click="handleDownloadMarkdown"
|
||||
:loading="downloadingMarkdown"
|
||||
:disabled="!((file.lines && file.lines.length > 0) || file.content)"
|
||||
:icon="h(DownloadOutlined)"
|
||||
>
|
||||
下载 Markdown
|
||||
</a-button>
|
||||
<!-- 左侧:文件名和图标 -->
|
||||
<div class="file-title">
|
||||
<component :is="fileIcon" :style="{ color: fileIconColor, fontSize: '18px' }" />
|
||||
<span class="file-name">{{ file?.filename || '文件详情' }}</span>
|
||||
</div>
|
||||
|
||||
<div class="header-controls">
|
||||
<!-- 字符数/片段数显示在 segment 左边 -->
|
||||
<span class="view-info">
|
||||
{{ viewMode === 'chunks' ? chunkCount + ' 个片段' : formatTextLength(charCount) + ' 字符' }}
|
||||
</span>
|
||||
|
||||
<!-- 视图模式切换 -->
|
||||
<div class="view-controls" v-if="file && hasContent">
|
||||
<a-segmented
|
||||
v-model:value="viewMode"
|
||||
:options="viewModeOptions"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 下载按钮下拉菜单 -->
|
||||
<a-dropdown trigger="click" v-if="file">
|
||||
<a-button type="default" class="download-btn">
|
||||
<template #icon><Download :size="16" /></template>
|
||||
下载
|
||||
<ChevronDown :size="16" style="margin-left: 4px;" />
|
||||
</a-button>
|
||||
<template #overlay>
|
||||
<a-menu @click="handleDownloadMenuClick">
|
||||
<a-menu-item key="original" :disabled="!file.file_id">
|
||||
<template #icon><Download :size="16" /></template>
|
||||
下载原文
|
||||
</a-menu-item>
|
||||
<a-menu-item key="markdown" :disabled="!((file.lines && file.lines.length > 0) || file.content)">
|
||||
<template #icon><FileText :size="16" /></template>
|
||||
下载 Markdown
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
|
||||
<!-- 自定义关闭按钮 -->
|
||||
<button class="custom-close-btn" @click="visible = false">
|
||||
<X :size="16" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<div class="file-detail-content" v-if="file">
|
||||
<div class="file-content-section" v-if="(file.lines && file.lines.length > 0) || file.content">
|
||||
<MarkdownContentViewer :chunks="file.lines" :content="file.content" />
|
||||
<!-- Markdown 模式 -->
|
||||
<div v-if="viewMode === 'markdown'" class="content-panel">
|
||||
<MdPreview
|
||||
v-if="mergedContent"
|
||||
:modelValue="mergedContent"
|
||||
:theme="theme"
|
||||
previewTheme="github"
|
||||
class="markdown-content"
|
||||
/>
|
||||
<div v-else class="empty-content">
|
||||
<p>暂无文件内容</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else-if="loading" class="loading-container">
|
||||
<a-spin />
|
||||
<!-- Chunks 模式:使用 Grid 布局 -->
|
||||
<div v-else-if="viewMode === 'chunks'" class="chunks-panel">
|
||||
<div class="chunk-grid">
|
||||
<div
|
||||
v-for="chunk in mappedChunks"
|
||||
:key="chunk.id"
|
||||
class="chunk-card"
|
||||
>
|
||||
<div class="chunk-card-header">
|
||||
<span class="chunk-order">#{{ chunk.chunk_order_index }}</span>
|
||||
</div>
|
||||
<div class="chunk-card-content">
|
||||
{{ chunk.content.replace(/\n+/g, ' ') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="mappedChunks.length === 0" class="empty-content">
|
||||
<p>暂无分块信息</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else class="empty-content">
|
||||
<p>暂无文件内容</p>
|
||||
</div>
|
||||
<div v-else-if="loading" class="loading-container">
|
||||
<a-spin />
|
||||
</div>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, ref, h } from 'vue';
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { useDatabaseStore } from '@/stores/database';
|
||||
import { useThemeStore } from '@/stores/theme';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { DownloadOutlined } from '@ant-design/icons-vue';
|
||||
import { documentApi } from '@/apis/knowledge_api';
|
||||
import { mergeChunks } from '@/utils/chunkUtils';
|
||||
import MarkdownContentViewer from './MarkdownContentViewer.vue';
|
||||
import { getFileIcon, getFileIconColor } from '@/utils/file_utils';
|
||||
import { MdPreview } from 'md-editor-v3';
|
||||
import 'md-editor-v3/lib/preview.css';
|
||||
import { Download, ChevronDown, FileText, X } from 'lucide-vue-next';
|
||||
|
||||
const store = useDatabaseStore();
|
||||
const themeStore = useThemeStore();
|
||||
|
||||
const visible = computed({
|
||||
get: () => store.state.fileDetailModalVisible,
|
||||
@ -68,12 +123,70 @@ const visible = computed({
|
||||
|
||||
const file = computed(() => store.selectedFile);
|
||||
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 downloadingMarkdown = ref(false);
|
||||
|
||||
// 主题设置
|
||||
const theme = computed(() => themeStore.isDark ? 'dark' : 'light');
|
||||
|
||||
// 视图模式
|
||||
const viewMode = ref('markdown');
|
||||
const hasIndexed = computed(() => ['done', 'indexed'].includes(file.value?.status));
|
||||
const hasContent = computed(() => (file.value?.lines && file.value?.lines.length > 0) || file.value?.content);
|
||||
|
||||
const viewModeOptions = computed(() => {
|
||||
const options = [{ label: 'Markdown', value: 'markdown' }];
|
||||
if (hasIndexed.value) {
|
||||
options.push({ label: 'Chunks', value: 'chunks' });
|
||||
}
|
||||
return options;
|
||||
});
|
||||
|
||||
// 监听文件状态变化,重置视图模式
|
||||
watch(file, (newFile) => {
|
||||
if (newFile) {
|
||||
if (!hasIndexed.value) {
|
||||
viewMode.value = 'markdown';
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 统计信息
|
||||
const mergeResult = computed(() => mergeChunks(file.value?.lines || []));
|
||||
const mappedChunks = computed(() => mergeResult.value.chunks);
|
||||
const mergedContent = computed(() => file.value?.content || mergeResult.value.content || '');
|
||||
const charCount = computed(() => mergedContent.value.length);
|
||||
const chunkCount = computed(() => mappedChunks.value.length || file.value?.lines?.length || 0);
|
||||
|
||||
// 格式化文本长度
|
||||
function formatTextLength(length) {
|
||||
if (!length && length !== 0) return '0 字符';
|
||||
|
||||
if (length < 1000) {
|
||||
return `${length}`;
|
||||
} else {
|
||||
return `${(length / 1000).toFixed(1)}k`;
|
||||
}
|
||||
}
|
||||
|
||||
const afterOpenChange = (open) => {
|
||||
if (!open) {
|
||||
store.selectedFile = null;
|
||||
viewMode.value = 'markdown';
|
||||
}
|
||||
};
|
||||
|
||||
// 下载菜单点击处理
|
||||
const handleDownloadMenuClick = ({ key }) => {
|
||||
if (key === 'original') {
|
||||
handleDownloadOriginal();
|
||||
} else if (key === 'markdown') {
|
||||
handleDownloadMarkdown();
|
||||
}
|
||||
};
|
||||
|
||||
@ -143,12 +256,7 @@ const handleDownloadOriginal = async () => {
|
||||
|
||||
// 下载 Markdown
|
||||
const handleDownloadMarkdown = () => {
|
||||
let content = '';
|
||||
if (file.value.content) {
|
||||
content = file.value.content;
|
||||
} else if (file.value.lines && file.value.lines.length > 0) {
|
||||
content = mergeChunks(file.value.lines).content;
|
||||
}
|
||||
const content = mergedContent.value;
|
||||
|
||||
if (!content) {
|
||||
message.error('没有可下载的 Markdown 内容');
|
||||
@ -198,10 +306,17 @@ const handleDownloadMarkdown = () => {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.file-content-section h4 {
|
||||
margin-bottom: 12px;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
.content-panel,
|
||||
.chunks-panel {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 16px 0;
|
||||
background: var(--gray-0);
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.markdown-content {
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
.loading-container {
|
||||
@ -215,6 +330,50 @@ const handleDownloadMarkdown = () => {
|
||||
text-align: center;
|
||||
padding: 40px 0;
|
||||
color: var(--gray-400);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Chunks 面板样式 */
|
||||
.chunk-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.chunk-card {
|
||||
background: var(--gray-0);
|
||||
border: 1px solid var(--gray-200);
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.chunk-card:hover {
|
||||
border-color: var(--main-color);
|
||||
box-shadow: 0 2px 8px rgba(1, 97, 121, 0.1);
|
||||
}
|
||||
|
||||
.chunk-card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.chunk-order {
|
||||
font-weight: 600;
|
||||
color: var(--main-color);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.chunk-card-content {
|
||||
font-size: 12px;
|
||||
color: var(--gray-600);
|
||||
line-height: 1.5;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 4;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -239,10 +398,146 @@ const handleDownloadMarkdown = () => {
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
.download-buttons {
|
||||
/* 文件标题样式 */
|
||||
.file-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin: 0 16px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
.file-name {
|
||||
font-weight: 600;
|
||||
font-size: 15px;
|
||||
color: var(--gray-900);
|
||||
}
|
||||
|
||||
.title-info {
|
||||
font-size: 13px;
|
||||
color: var(--gray-600);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.header-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
/* 下载按钮样式 */
|
||||
.download-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 4px 8px;
|
||||
height: 28px;
|
||||
font-size: 13px;
|
||||
line-height: 1;
|
||||
border-radius: 6px;
|
||||
gap: 4px;
|
||||
|
||||
svg {
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
/* 自定义关闭按钮 */
|
||||
.custom-close-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border: none;
|
||||
background: transparent;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
color: var(--gray-500);
|
||||
transition: all 0.2s;
|
||||
|
||||
&:hover {
|
||||
background: var(--gray-100);
|
||||
color: var(--gray-700);
|
||||
}
|
||||
}
|
||||
|
||||
/* 视图切换控件 */
|
||||
.view-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.view-info {
|
||||
font-size: 12px;
|
||||
color: var(--gray-500);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* 下拉菜单样式 */
|
||||
.ant-dropdown-menu {
|
||||
border-radius: 8px;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.ant-dropdown-menu-item {
|
||||
border-radius: 6px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 8px 12px;
|
||||
|
||||
svg {
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="less">
|
||||
/* MdPreview 覆盖样式 - 非 scoped */
|
||||
.markdown-content {
|
||||
.md-editor-preview-wrapper {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#md-editor-v-2-preview-wrapper {
|
||||
font-size: 14px;
|
||||
line-height: 1.75;
|
||||
color: var(--gray-1000);
|
||||
}
|
||||
|
||||
#md-editor-v-2-preview-wrapper h1 {
|
||||
font-size: 1.5rem;
|
||||
margin: 16px 0 12px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
#md-editor-v-2-preview-wrapper h2 {
|
||||
font-size: 1.4rem;
|
||||
margin: 16px 0 12px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
#md-editor-v-2-preview-wrapper h3 {
|
||||
font-size: 1.3rem;
|
||||
margin: 14px 0 10px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
#md-editor-v-2-preview-wrapper h4 {
|
||||
font-size: 1.2rem;
|
||||
margin: 14px 0 10px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
#md-editor-v-2-preview-wrapper h5 {
|
||||
font-size: 1.1rem;
|
||||
margin: 12px 0 8px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
#md-editor-v-2-preview-wrapper h6 {
|
||||
font-size: 1rem;
|
||||
margin: 12px 0 8px;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -39,7 +39,7 @@
|
||||
</a-input>
|
||||
|
||||
<a-dropdown trigger="click">
|
||||
<a-button type="text" class="panel-action-btn" title="排序">
|
||||
<a-button type="text" class="panel-action-btn" :class="{ 'active': sortField !== 'filename' }" title="排序">
|
||||
<template #icon><ArrowUpDown size="16" /></template>
|
||||
</a-button>
|
||||
<template #overlay>
|
||||
@ -52,11 +52,11 @@
|
||||
</a-dropdown>
|
||||
|
||||
<a-dropdown trigger="click">
|
||||
<a-button type="text" class="panel-action-btn" :class="{ 'active': statusFilter }" title="筛选状态">
|
||||
<a-button type="text" class="panel-action-btn" :class="{ 'active': statusFilter !== 'all' }" title="筛选状态">
|
||||
<template #icon><Filter size="16" /></template>
|
||||
</a-button>
|
||||
<template #overlay>
|
||||
<a-menu :selectedKeys="statusFilter ? [statusFilter] : []" @click="handleStatusMenuClick">
|
||||
<a-menu :selectedKeys="[statusFilter]" @click="handleStatusMenuClick">
|
||||
<a-menu-item key="all">全部状态</a-menu-item>
|
||||
<a-menu-item v-for="opt in statusOptions" :key="opt.value">
|
||||
{{ opt.label }}
|
||||
@ -114,7 +114,7 @@
|
||||
/>
|
||||
<span>{{ selectedRowKeys.length }} 项</span>
|
||||
</div>
|
||||
<div style="display: flex; gap: 8px;">
|
||||
<div style="display: flex; gap: 2px;">
|
||||
<a-button
|
||||
type="link"
|
||||
@click="handleBatchParse"
|
||||
@ -202,7 +202,7 @@
|
||||
<div v-if="column.key === 'filename'">
|
||||
<template v-if="record.is_folder">
|
||||
<span class="folder-row" @click="toggleExpand(record)">
|
||||
<component :is="expandedRowKeys.includes(record.file_id) ? h(FolderOpenFilled) : h(FolderFilled)" style="margin-right: 12px; color: #ffb800;" />
|
||||
<component :is="expandedRowKeys.includes(record.file_id) ? h(FolderOpenFilled) : h(FolderFilled)" style="margin-right: 8px; color: #ffb800; font-size: 16px;" />
|
||||
{{ record.filename }}
|
||||
</span>
|
||||
</template>
|
||||
@ -216,7 +216,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<a-button class="main-btn" type="link" @click="openFileDetail(record)">
|
||||
<component :is="getFileIcon(record.displayName || text)" :style="{ marginRight: '6px', color: getFileIconColor(record.displayName || text) }" />
|
||||
<component :is="getFileIcon(record.displayName || text)" :style="{ marginRight: '0', color: getFileIconColor(record.displayName || text), fontSize: '16px' }" />
|
||||
{{ record.displayName || text }}
|
||||
</a-button>
|
||||
</a-popover>
|
||||
@ -343,11 +343,7 @@ const handleSortMenuClick = (e) => {
|
||||
};
|
||||
|
||||
const handleStatusMenuClick = (e) => {
|
||||
if (e.key === 'all') {
|
||||
statusFilter.value = null;
|
||||
} else {
|
||||
statusFilter.value = e.key;
|
||||
}
|
||||
statusFilter.value = e.key;
|
||||
// 状态筛选变化时重置到第一页
|
||||
paginationConfig.value.current = 1;
|
||||
};
|
||||
@ -406,9 +402,9 @@ const allSelectableFiles = computed(() => {
|
||||
// Follow getCheckboxProps logic
|
||||
if (lock.value || file.status === 'processing' || file.status === 'waiting') return false;
|
||||
|
||||
if (nameFilter || status) {
|
||||
if (nameFilter || status !== 'all') {
|
||||
const nameMatch = !nameFilter || (file.filename && file.filename.toLowerCase().includes(nameFilter));
|
||||
const statusMatch = !status || file.status === status ||
|
||||
const statusMatch = status === 'all' || file.status === status ||
|
||||
(status === 'indexed' && file.status === 'done') ||
|
||||
(status === 'error_indexing' && file.status === 'failed');
|
||||
return nameMatch && statusMatch;
|
||||
@ -625,7 +621,7 @@ const handleTableChange = (pagination) => {
|
||||
|
||||
// 文件名过滤
|
||||
const filenameFilter = ref('');
|
||||
const statusFilter = ref(null);
|
||||
const statusFilter = ref('all');
|
||||
const statusOptions = [
|
||||
{ label: '已上传', value: 'uploaded' },
|
||||
{ label: '解析中', value: 'parsing' },
|
||||
@ -796,11 +792,11 @@ const filteredFiles = computed(() => {
|
||||
const status = statusFilter.value;
|
||||
|
||||
// 应用过滤
|
||||
if (nameFilter || status) {
|
||||
if (nameFilter || status !== 'all') {
|
||||
// 搜索/过滤模式下使用扁平列表
|
||||
return files.value.filter(file => {
|
||||
const nameMatch = !nameFilter || (file.filename && file.filename.toLowerCase().includes(nameFilter));
|
||||
const statusMatch = !status || file.status === status ||
|
||||
const statusMatch = status === 'all' || file.status === status ||
|
||||
(status === 'indexed' && file.status === 'done') ||
|
||||
(status === 'error_indexing' && file.status === 'failed');
|
||||
return nameMatch && statusMatch;
|
||||
@ -1134,7 +1130,7 @@ import ChunkParamsConfig from '@/components/ChunkParamsConfig.vue';
|
||||
.panel-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
gap: 6px;
|
||||
|
||||
.action-searcher {
|
||||
width: 120px;
|
||||
@ -1142,6 +1138,7 @@ import ChunkParamsConfig from '@/components/ChunkParamsConfig.vue';
|
||||
border-radius: 6px;
|
||||
padding: 4px 8px;
|
||||
border: none;
|
||||
box-shadow: 0 0 0 1px var(--shadow-1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1188,7 +1185,9 @@ import ChunkParamsConfig from '@/components/ChunkParamsConfig.vue';
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
background-color: transparent;
|
||||
min-height: 0; /* 让 flex 子项可以正确缩小 */
|
||||
min-height: 0;
|
||||
table-layout: fixed;
|
||||
padding-left: 4px;
|
||||
}
|
||||
|
||||
.my-table .main-btn {
|
||||
@ -1262,6 +1261,12 @@ import ChunkParamsConfig from '@/components/ChunkParamsConfig.vue';
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.my-table .span-type.md,
|
||||
.my-table .span-type.markdown {
|
||||
background-color: var(--gray-200);
|
||||
color: var(--gray-800);
|
||||
}
|
||||
|
||||
.auto-refresh-btn {
|
||||
height: 24px;
|
||||
padding: 0 8px;
|
||||
@ -1272,15 +1277,13 @@ import ChunkParamsConfig from '@/components/ChunkParamsConfig.vue';
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 6px;
|
||||
padding: 0 4px;
|
||||
color: var(--gray-700);
|
||||
border-radius: 8px;
|
||||
padding: 4px;
|
||||
color: var(--gray-600);
|
||||
transition: all 0.1s ease;
|
||||
font-size: 12px;
|
||||
|
||||
svg {
|
||||
height: 16px;
|
||||
}
|
||||
width: auto;
|
||||
height: auto;
|
||||
|
||||
&.expand {
|
||||
transform: scaleX(-1);
|
||||
@ -1307,6 +1310,7 @@ import ChunkParamsConfig from '@/components/ChunkParamsConfig.vue';
|
||||
color: var(--main-color);
|
||||
background-color: var(--gray-100);
|
||||
font-weight: 600;
|
||||
box-shadow: 0 0 0 1px var(--shadow-1);
|
||||
}
|
||||
|
||||
.action-trigger-btn {
|
||||
|
||||
@ -39,28 +39,26 @@
|
||||
|
||||
<!-- 2. 配置面板 -->
|
||||
<div class="settings-panel">
|
||||
<!-- 第一行:存储位置 -->
|
||||
<div class="setting-row">
|
||||
<div class="setting-label">存储位置</div>
|
||||
<div class="setting-content flex-row">
|
||||
<a-tree-select
|
||||
v-model:value="selectedFolderId"
|
||||
show-search
|
||||
class="folder-select"
|
||||
:dropdown-style="{ maxHeight: '400px', overflow: 'auto' }"
|
||||
placeholder="选择目标文件夹(默认为根目录)"
|
||||
allow-clear
|
||||
tree-default-expand-all
|
||||
:tree-data="folderTreeData"
|
||||
tree-node-filter-prop="title"
|
||||
>
|
||||
</a-tree-select>
|
||||
<!-- 第一行:存储位置 + OCR 引擎 -->
|
||||
<div class="setting-row two-cols">
|
||||
<div class="col-item">
|
||||
<div class="setting-label">存储位置</div>
|
||||
<div class="setting-content flex-row">
|
||||
<a-tree-select
|
||||
v-model:value="selectedFolderId"
|
||||
show-search
|
||||
class="folder-select"
|
||||
:dropdown-style="{ maxHeight: '400px', overflow: 'auto' }"
|
||||
placeholder="选择目标文件夹(默认为根目录)"
|
||||
allow-clear
|
||||
tree-default-expand-all
|
||||
:tree-data="folderTreeData"
|
||||
tree-node-filter-prop="title"
|
||||
>
|
||||
</a-tree-select>
|
||||
</div>
|
||||
<p class="param-description">选择文件保存的目标文件夹</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 第二行:OCR (单列布局) -->
|
||||
<div class="setting-row">
|
||||
<!-- OCR 配置 -->
|
||||
<div class="col-item">
|
||||
<div class="setting-label">
|
||||
OCR 引擎
|
||||
@ -80,15 +78,20 @@
|
||||
:disabled="ocrHealthChecking"
|
||||
class="ocr-select"
|
||||
/>
|
||||
<!-- 紧凑的状态提示 -->
|
||||
<div class="status-mini-tip" v-if="chunkParams.enable_ocr !== 'disable'">
|
||||
<span v-if="selectedOcrStatus === 'healthy'" class="text-success">
|
||||
<CheckCircleFilled /> {{ selectedOcrMessage || '服务正常' }}
|
||||
</span>
|
||||
<span v-else-if="selectedOcrStatus && selectedOcrStatus !== 'unknown'" class="text-warning">
|
||||
⚠️ {{ selectedOcrMessage || '服务异常' }}
|
||||
</span>
|
||||
</div>
|
||||
<p class="param-description">
|
||||
<template v-if="!isOcrEnabled">
|
||||
不启用 OCR,仅处理文本文件
|
||||
</template>
|
||||
<template v-else-if="selectedOcrStatus === 'healthy'">
|
||||
{{ selectedOcrMessage || '服务正常' }}
|
||||
</template>
|
||||
<template v-else-if="selectedOcrStatus === 'unknown'">
|
||||
点击刷新图标检查服务状态
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ selectedOcrMessage || '服务异常' }}
|
||||
</template>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -878,7 +881,6 @@ const chunkData = async () => {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0 4px;
|
||||
}
|
||||
|
||||
.help-link-btn {
|
||||
@ -934,7 +936,7 @@ const chunkData = async () => {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
gap: 4px;
|
||||
min-width: 0; // Fix flex overflow
|
||||
}
|
||||
}
|
||||
@ -983,15 +985,28 @@ const chunkData = async () => {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.status-mini-tip {
|
||||
margin-top: 6px;
|
||||
.param-description {
|
||||
font-size: 12px;
|
||||
color: var(--gray-400);
|
||||
margin: 4px 0 0 0;
|
||||
line-height: 1.4;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
|
||||
.text-success { color: var(--color-success-500); }
|
||||
.text-warning { color: var(--color-warning-500); }
|
||||
.text-success {
|
||||
color: var(--color-success-500);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.text-warning {
|
||||
color: var(--color-warning-500);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Chunk Display Card */
|
||||
|
||||
@ -13,7 +13,8 @@
|
||||
@click="toggleChunkPanel"
|
||||
:title="chunkPanelVisible ? '隐藏片段列表' : '显示片段列表'"
|
||||
>
|
||||
{{ chunkPanelVisible ? '«' : '»' }}
|
||||
<ChevronLeft v-if="chunkPanelVisible" :size="14" />
|
||||
<ChevronRight v-else :size="14" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@ -98,6 +99,7 @@ import { MdPreview } from 'md-editor-v3';
|
||||
import 'md-editor-v3/lib/preview.css';
|
||||
import { mergeChunks, getChunkPreview } from '@/utils/chunkUtils';
|
||||
import { useThemeStore } from '@/stores/theme';
|
||||
import { ChevronRight, ChevronLeft } from 'lucide-vue-next';
|
||||
|
||||
const props = defineProps({
|
||||
chunks: {
|
||||
@ -209,16 +211,18 @@ onUnmounted(() => {
|
||||
}
|
||||
|
||||
.toggle-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: var(--gray-100);
|
||||
/* color: var(--gray-0); */
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
width: 28px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
padding: 0;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
transition: all 0.2s ease;
|
||||
color: var(--gray-700);
|
||||
}
|
||||
|
||||
.toggle-btn:hover {
|
||||
|
||||
@ -460,6 +460,7 @@ watch(() => props.agentId, () => {
|
||||
|
||||
// 空状态
|
||||
.empty-state {
|
||||
grid-column: 1 / -1;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
@ -192,10 +192,6 @@ provide('settingsModal', {
|
||||
<UserInfoComponent />
|
||||
</div>
|
||||
</div>
|
||||
<div class="header-mobile">
|
||||
<RouterLink to="/agent" class="nav-item" active-class="active">对话</RouterLink>
|
||||
<RouterLink to="/database" class="nav-item" active-class="active">知识</RouterLink>
|
||||
</div>
|
||||
<router-view v-slot="{ Component, route }" id="app-router-view">
|
||||
<keep-alive v-if="route.meta.keepAlive !== false">
|
||||
<component :is="Component" />
|
||||
@ -235,10 +231,6 @@ provide('settingsModal', {
|
||||
height: 100vh;
|
||||
min-width: var(--min-width);
|
||||
|
||||
.header-mobile {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.debug-panel {
|
||||
position: absolute;
|
||||
z-index: 100;
|
||||
@ -293,7 +285,7 @@ div.header, #app-router-view {
|
||||
.logo {
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
margin: 12px 0 20px 0;
|
||||
margin: 6px 0 20px 0;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
@ -417,49 +409,6 @@ div.header, #app-router-view {
|
||||
}
|
||||
|
||||
|
||||
@media (max-width: 520px) {
|
||||
.app-layout {
|
||||
flex-direction: column-reverse;
|
||||
|
||||
div.header {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.debug-panel {
|
||||
bottom: 10rem;
|
||||
}
|
||||
|
||||
}
|
||||
.app-layout div.header-mobile {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
width: 100%;
|
||||
padding: 0 20px;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
flex: 0 0 60px;
|
||||
border-right: none;
|
||||
height: 40px;
|
||||
|
||||
.nav-item {
|
||||
text-decoration: none;
|
||||
width: 40px;
|
||||
color: var(--gray-900);
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
transition: color 0.1s ease-in-out, font-size 0.1s ease-in-out;
|
||||
|
||||
&.active {
|
||||
color: var(--gray-10000);
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
.app-layout .chat-box::webkit-scrollbar {
|
||||
width: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.app-layout.use-top-bar {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
@ -63,9 +63,9 @@ export const getFileIconColor = (filename) => {
|
||||
'text': '#1890ff',
|
||||
'log': '#1890ff',
|
||||
|
||||
// Markdown文件 - 深蓝色
|
||||
'md': '#0050b3',
|
||||
'markdown': '#0050b3',
|
||||
// Markdown文件 - 深灰色
|
||||
'md': '#595959',
|
||||
'markdown': '#595959',
|
||||
|
||||
// PDF文件 - 红色
|
||||
'pdf': '#ff4d4f',
|
||||
|
||||
@ -39,6 +39,7 @@
|
||||
@click="openSearchConfigModal"
|
||||
>
|
||||
<SettingOutlined />
|
||||
<span class="config-text">检索配置</span>
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
@ -540,12 +541,13 @@ const handleMouseUp = () => {
|
||||
}
|
||||
|
||||
.config-btn {
|
||||
color: var(--gray-500);
|
||||
color: var(--gray-600);
|
||||
font-size: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 32px;
|
||||
gap: 4px;
|
||||
padding: 4px 8px;
|
||||
height: 32px;
|
||||
border-radius: 6px;
|
||||
transition: all 0.2s;
|
||||
@ -554,24 +556,9 @@ const handleMouseUp = () => {
|
||||
color: var(--main-color);
|
||||
background-color: var(--gray-100);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Responsive design for smaller screens */
|
||||
@media (max-width: 768px) {
|
||||
.unified-layout {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.unified-layout .left-panel {
|
||||
border-right: none;
|
||||
border-bottom: 1px solid var(--gray-200);
|
||||
}
|
||||
|
||||
.unified-layout .resize-handle {
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
cursor: row-resize;
|
||||
.config-text {
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -90,7 +90,7 @@
|
||||
size="default"
|
||||
/>
|
||||
<span style="margin-left: 12px;">设置为私有知识库</span>
|
||||
<a-tooltip title="在部分智能体的设计中,可以根据隐私标志来决定启用什么模型和策略。例如,对于私有知识库,可以选择更严格的数据处理和访问控制策略,以保护敏感信息的安全性和隐私性。">
|
||||
<a-tooltip title="当前未使用此属性。在部分智能体的设计中,可以根据隐私标志来决定启用什么模型和策略。例如,对于私有知识库,可以选择更严格的数据处理和访问控制策略,以保护敏感信息的安全性和隐私性。">
|
||||
<InfoCircleOutlined style="margin-left: 8px; color: var(--gray-500); cursor: help;" />
|
||||
</a-tooltip>
|
||||
</div>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user