fix(web): 修复滚动到底部未生效的问题,并优化 markdown 渲染 padding

This commit is contained in:
Wenjie Zhang 2026-03-03 00:05:28 +08:00
parent a4e5486e51
commit b144b72de0
6 changed files with 45 additions and 27 deletions

View File

@ -7,19 +7,12 @@
- 集成 LangFuse (观望) 添加用户日志与用户反馈模块,可以在 AgentView 中查看信息 - 集成 LangFuse (观望) 添加用户日志与用户反馈模块,可以在 AgentView 中查看信息
- 集成 neo4j mcp (或者自己构建工具) - 集成 neo4j mcp (或者自己构建工具)
- chat_model 的 call 需要异步
- 优化智能体文件系统:
- 使用 CompositeBackend 和 自定义后端系统,链接 minio 中的知识库文件。提供除检索以外的方法
- 考虑修改附件的处理逻辑,考虑使用文件系统,将附件解析后放到文件系统中,智能体按需读取(当前是完全解析,放进上下文)
- 【后续】用户可以使用 @ 来引用附件,例如 @file:reports.md 相较于现在的处理逻辑感觉会更加自然一点。
- skills 如何实现还需要继续调研
- 增加 paddle-vl 以及 deepseek-ocr 的支持deepseek-ocr 已支持),重构 OCR 的支持情况,并支持 Paddle 官方的解析 API https://aistudio.baidu.com/paddleocr/task - 增加 paddle-vl 以及 deepseek-ocr 的支持deepseek-ocr 已支持),重构 OCR 的支持情况,并支持 Paddle 官方的解析 API https://aistudio.baidu.com/paddleocr/task
- 系统层面添加 apikey在智能体、知识库调用中支持 apikey 以支持外部调用 - 系统层面添加 apikey在智能体、知识库调用中支持 apikey 以支持外部调用
- 支持更多类型的文档源的导入功能(支持后端配置的白名单的 URL 导入)
- 部分场景应该使用默认模型作为默认值而不是空值 - 部分场景应该使用默认模型作为默认值而不是空值
- 文件上传解析后,如何提示用户需要入库
- 检索测试中,添加问答 - 检索测试中,添加问答
- 丰富当前智能体的 Prompt最好支持从 markdown 解析 - 丰富当前智能体的 Prompt最好支持从 markdown 解析
- 探索 subagents 的体系
### Bugs ### Bugs
@ -33,6 +26,10 @@
- FileTable 的自动刷新失效 - FileTable 的自动刷新失效
- 生成基准测试会把所有的向量都计算一遍不合理 - 生成基准测试会把所有的向量都计算一遍不合理
## v0.6
## v0.5 ## v0.5
### 新增 ### 新增
@ -50,6 +47,7 @@
- 优化 liaghtrag 的知识库调用结果,提供 content/graph/both 多个选项 - 优化 liaghtrag 的知识库调用结果,提供 content/graph/both 多个选项
- 优化数据库查询工具,可通过设计环境变量添加描述,让模型更好的调用 - 优化数据库查询工具,可通过设计环境变量添加描述,让模型更好的调用
- 优化任务组件,改用 postgresql 存储,并新增删除任务的接口 - 优化任务组件,改用 postgresql 存储,并新增删除任务的接口
- 支持更多类型的文档源的导入功能(支持后端配置的白名单的 URL 导入)
### 修复 ### 修复

View File

@ -49,11 +49,4 @@
font-weight: 600; font-weight: 600;
} }
} }
} }
// 背景透明场景
.flat-md-preview--transparent {
.md-editor {
background: transparent;
}
}

View File

@ -40,7 +40,7 @@
@click="$emit('toggle-panel')" @click="$emit('toggle-panel')"
title="查看工作状态" title="查看工作状态"
> >
<FolderCode :size="14" /> <FolderCode :size="18" />
<span>状态</span> <span>状态</span>
</div> </div>
</div> </div>
@ -187,7 +187,7 @@ defineExpose({
padding: 0 8px; padding: 0 8px;
height: 28px; height: 28px;
border-radius: 8px; border-radius: 8px;
font-size: 13px; font-size: 14px;
color: var(--gray-600); color: var(--gray-600);
cursor: pointer; cursor: pointer;
transition: all 0.2s ease; transition: all 0.2s ease;

View File

@ -793,7 +793,6 @@ const stopResize = () => {
max-height: 60vh; max-height: 60vh;
overflow-y: auto; overflow-y: auto;
border-radius: 6px; border-radius: 6px;
padding: 16px;
&::-webkit-scrollbar { &::-webkit-scrollbar {
width: 8px; width: 8px;

View File

@ -6,7 +6,7 @@
:footer="null" :footer="null"
:destroyOnClose="true" :destroyOnClose="true"
wrap-class-name="chunk-detail-modal" wrap-class-name="chunk-detail-modal"
:bodyStyle="{ maxHeight: '72vh', overflowY: 'auto', padding: '12px 16px' }" :bodyStyle="{ maxHeight: '72vh', overflowY: 'auto', padding: '0' }"
> >
<div v-if="chunk" class="detail-meta"> <div v-if="chunk" class="detail-meta">
<span v-if="typeof chunk.score === 'number'" class="score" <span v-if="typeof chunk.score === 'number'" class="score"

View File

@ -68,12 +68,25 @@ export class ScrollController {
}, this.options.scrollDelay) }, this.options.scrollDelay)
} }
/**
* 等待 DOM 布局稳定
* @returns {Promise<void>}
*/
async waitForLayoutStable() {
// 使用 requestAnimationFrame 确保 DOM 渲染完成
await new Promise((resolve) => requestAnimationFrame(resolve))
// 额外等待一小段时间确保 CSS 布局完成
await new Promise((resolve) => setTimeout(resolve, 50))
}
/** /**
* 智能滚动到底部 * 智能滚动到底部
* @param {boolean} force - 是否强制滚动 * @param {boolean} force - 是否强制滚动
*/ */
async scrollToBottom(force = false) { async scrollToBottom(force = false) {
await nextTick() await nextTick()
// 等待 DOM 布局稳定
await this.waitForLayoutStable()
// 只有在应该自动滚动时才执行(除非强制) // 只有在应该自动滚动时才执行(除非强制)
if (!force && !this.shouldAutoScroll) return if (!force && !this.shouldAutoScroll) return
@ -84,6 +97,9 @@ export class ScrollController {
// 标记为程序性滚动 // 标记为程序性滚动
this.isProgrammaticScroll = true this.isProgrammaticScroll = true
// 记录滚动前的容器高度
const initialHeight = container.scrollHeight
const scrollOptions = { const scrollOptions = {
top: container.scrollHeight, top: container.scrollHeight,
behavior: 'smooth' behavior: 'smooth'
@ -92,16 +108,28 @@ export class ScrollController {
// 立即滚动 // 立即滚动
container.scrollTo(scrollOptions) container.scrollTo(scrollOptions)
// 多次重试确保滚动成功 // 多次重试确保滚动成功,包括等待输入框等动态元素布局完成
this.options.retryDelays.forEach((delay, index) => { const retryDelays = [50, 100, 200, 400]
retryDelays.forEach((delay, index) => {
setTimeout(() => { setTimeout(() => {
if (force || this.shouldAutoScroll) { if (force || this.shouldAutoScroll) {
this.isProgrammaticScroll = true this.isProgrammaticScroll = true
const behavior = index === this.options.retryDelays.length - 1 ? 'auto' : 'smooth' const behavior = index === retryDelays.length - 1 ? 'auto' : 'smooth'
container.scrollTo({
top: container.scrollHeight, // 如果高度变化了,说明可能有动态内容正在渲染,再次等待
behavior if (container.scrollHeight !== initialHeight && index < retryDelays.length - 1) {
}) this.waitForLayoutStable().then(() => {
container.scrollTo({
top: container.scrollHeight,
behavior
})
})
} else {
container.scrollTo({
top: container.scrollHeight,
behavior
})
}
} }
}, delay) }, delay)
}) })