fix(frontend): polish knowledge list sticky header and floating batch bar

- Detect sticky state via IntersectionObserver and flatten the table
  header's top corners when stuck so the rounded cut-outs no longer
  reveal the list container's background while scrolling.
- Lift the batch-action toolbar out of flex flow and float it 12px above
  the scroll container bottom so selecting rows no longer shrinks the
  visible list; strengthen its shadow to match the floating treatment.
This commit is contained in:
wizardchen
2026-04-29 20:07:55 +08:00
committed by lyingbug
parent 7fcd92994b
commit 9af5fb30ac
3 changed files with 52 additions and 6 deletions
+15 -3
View File
@@ -3024,6 +3024,7 @@ async function createNewSession(value: string): Promise<void> {
display: flex;
flex-direction: column;
min-height: 0;
position: relative; /* 作为批量工具栏悬浮的定位上下文 */
}
.doc-filter-bar {
@@ -3137,10 +3138,21 @@ async function createNewSession(value: string): Promise<void> {
}
}
/* 批量条在滚动区外,始终贴主内容列底部,不列表高度在列内上下漂移 */
/* 批量条悬浮在滚动区底部,不挤占列表高度 */
.doc-batch-bar-anchor {
flex-shrink: 0;
padding-top: 4px;
position: absolute;
left: 0;
right: 0;
bottom: 12px;
z-index: 6;
display: flex;
justify-content: center;
padding: 0 16px;
pointer-events: none;
& > * {
pointer-events: auto;
}
}
// Header 样式(无底部分割线,留更多空间给下方内容区)
@@ -61,7 +61,7 @@ const { t } = useI18n();
background: var(--td-bg-color-container);
border: 1px solid var(--td-component-stroke);
border-radius: 8px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.08);
}
.batch-bar-left {
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { computed, ref } from 'vue';
import { computed, onBeforeUnmount, onMounted, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { formatFileSize, getFileIcon } from '@/utils/files';
@@ -124,6 +124,25 @@ const onMoreVisible = (id: string, visible: boolean) => {
moreOpen.value = visible ? id : null;
};
// 吸顶检测:哨兵离开视口说明 header 已吸附在滚动容器顶部
const stickySentinel = ref<HTMLElement | null>(null);
const headerStuck = ref(false);
let stickyObserver: IntersectionObserver | null = null;
onMounted(() => {
if (!stickySentinel.value || typeof IntersectionObserver === 'undefined') return;
stickyObserver = new IntersectionObserver(
(entries) => {
headerStuck.value = !entries[0].isIntersecting;
},
{ threshold: 0 },
);
stickyObserver.observe(stickySentinel.value);
});
onBeforeUnmount(() => {
stickyObserver?.disconnect();
stickyObserver = null;
});
const handleAction = (action: 'edit' | 'reparse' | 'move' | 'delete', item: KnowledgeItem) => {
moreOpen.value = null;
item.isMore = false;
@@ -134,7 +153,8 @@ const handleAction = (action: 'edit' | 'reparse' | 'move' | 'delete', item: Know
<template>
<div class="doc-list-view" :class="{ 'is-loading': loading }">
<div class="doc-list-header" role="row">
<div ref="stickySentinel" class="doc-list-sticky-sentinel" aria-hidden="true"></div>
<div class="doc-list-header" :class="{ 'is-stuck': headerStuck }" role="row">
<div class="cell cell-check" role="columnheader" @click.stop>
<t-checkbox
class="doc-list-check"
@@ -305,6 +325,14 @@ const handleAction = (action: 'edit' | 'reparse' | 'move' | 'delete', item: Know
padding: 0 16px;
}
.doc-list-sticky-sentinel {
height: 0;
margin: 0;
padding: 0;
border: 0;
pointer-events: none;
}
.doc-list-header {
position: sticky;
top: 0;
@@ -318,6 +346,12 @@ const handleAction = (action: 'edit' | 'reparse' | 'move' | 'delete', item: Know
border-bottom: 1px solid var(--td-component-stroke);
border-radius: 8px 8px 0 0;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
transition: border-radius 0.15s ease, box-shadow 0.2s ease;
&.is-stuck {
border-radius: 0;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.08);
}
}
.doc-list-body {