feat: 对话滚动的调整
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 6.8 KiB |
@@ -10,9 +10,13 @@ import McChat from './components/McChat.vue';
|
|||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.app-container {
|
.app-container {
|
||||||
height: 100vh;
|
height: calc(100vh - 16px);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:deep(.mc-bubble) {
|
||||||
|
gap: 8px !important;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -83,4 +83,5 @@ body {
|
|||||||
text-rendering: optimizeLegibility;
|
text-rendering: optimizeLegibility;
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 6.8 KiB |
@@ -23,8 +23,9 @@
|
|||||||
@itemClick="onSubmit($event.label)"
|
@itemClick="onSubmit($event.label)"
|
||||||
></McPrompt>
|
></McPrompt>
|
||||||
</McLayoutContent>
|
</McLayoutContent>
|
||||||
<McLayoutContent class="content-container" v-else>
|
<McLayoutContent v-else>
|
||||||
<template v-for="(msg, idx) in messages" :key="idx">
|
<div class="content-container" ref="contentRef" @scroll="onScroll">
|
||||||
|
<template v-for="(msg, idx) in messages" :key="idx">
|
||||||
<McBubble
|
<McBubble
|
||||||
v-if="msg.from === 'user'"
|
v-if="msg.from === 'user'"
|
||||||
:content="msg.content"
|
:content="msg.content"
|
||||||
@@ -36,6 +37,7 @@
|
|||||||
<McMarkdownCard :content="msg.content"> </McMarkdownCard>
|
<McMarkdownCard :content="msg.content"> </McMarkdownCard>
|
||||||
</McBubble>
|
</McBubble>
|
||||||
</template>
|
</template>
|
||||||
|
</div>
|
||||||
</McLayoutContent>
|
</McLayoutContent>
|
||||||
<div class="shortcut" style="display: flex; align-items: center; gap: 8px">
|
<div class="shortcut" style="display: flex; align-items: center; gap: 8px">
|
||||||
<McPrompt
|
<McPrompt
|
||||||
@@ -81,7 +83,7 @@ import logoImg from '@/assets/logo.png';
|
|||||||
import avatarImg from '@/assets/chat_avatar.svg';
|
import avatarImg from '@/assets/chat_avatar.svg';
|
||||||
import chatLogoImg from '@/assets/chat_logo.png';
|
import chatLogoImg from '@/assets/chat_logo.png';
|
||||||
|
|
||||||
import { ref } from 'vue';
|
import { ref, watch, nextTick } from 'vue';
|
||||||
import { Button } from 'vue-devui/button';
|
import { Button } from 'vue-devui/button';
|
||||||
import 'vue-devui/button/style.css';
|
import 'vue-devui/button/style.css';
|
||||||
import { CozeAPI } from "@coze/api";
|
import { CozeAPI } from "@coze/api";
|
||||||
@@ -135,7 +137,7 @@ const inputFootIcons = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
const apiClient = new CozeAPI({
|
const apiClient = new CozeAPI({
|
||||||
token: "cztei_lZ7Dx1q6NKcVFg0d6iT0Aesdmo5vnhgBH7TCacaxEBk9D2fIBTOwNCgxFh0a7Vo7Z",
|
token: "sat_V4vKS7SSOpYg15gvD0i0xBgQFc0D30Bj6EjMMt1eSPSCl9SKj2tCNLTEw4g4hyGa",
|
||||||
baseURL: "https://api.coze.cn",
|
baseURL: "https://api.coze.cn",
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -199,6 +201,49 @@ const onSubmit = async (evt) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const contentRef = ref(null); // 滚动区域 DOM
|
||||||
|
const autoScroll = ref(true); // 是否自动滚动
|
||||||
|
|
||||||
|
/** 判断用户是否滚到了最底部 */
|
||||||
|
const isAtBottom = () => {
|
||||||
|
const el = contentRef.value;
|
||||||
|
if (!el) return false;
|
||||||
|
const threshold = 10; // 收敛误差
|
||||||
|
let atBottom = el.scrollHeight - el.scrollTop - el.clientHeight < threshold;
|
||||||
|
return atBottom
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 滚动事件:只要用户滚动离开底部,就禁用自动滚动 */
|
||||||
|
const onScroll = () => {
|
||||||
|
autoScroll.value = isAtBottom();
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 自动滚动到底部 */
|
||||||
|
const scrollToBottom = async () => {
|
||||||
|
await nextTick();
|
||||||
|
const el = contentRef.value;
|
||||||
|
if (!el) return;
|
||||||
|
el.scrollTo({
|
||||||
|
top: el.scrollHeight + 99999,
|
||||||
|
behavior: "smooth",
|
||||||
|
});
|
||||||
|
|
||||||
|
autoScroll.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 监听消息变化(AI 回答时) */
|
||||||
|
const timer = ref(null);
|
||||||
|
watch(messages.value, async (value) => {
|
||||||
|
if (autoScroll.value) {
|
||||||
|
await nextTick();
|
||||||
|
if (timer.value) {
|
||||||
|
clearTimeout(timer.value);
|
||||||
|
}
|
||||||
|
timer.value = setTimeout(() => {
|
||||||
|
scrollToBottom();
|
||||||
|
}, 200);
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
@@ -216,8 +261,15 @@ const onSubmit = async (evt) => {
|
|||||||
.content-container {
|
.content-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
overflow: auto;
|
overflow: auto; /* 或 scroll */
|
||||||
|
scrollbar-width: none; /* Firefox */
|
||||||
|
-ms-overflow-style: none; /* IE & Edge */
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-container::-webkit-scrollbar {
|
||||||
|
display: none; /* Chrome / Safari / Edge / Opera */
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-foot-wrapper {
|
.input-foot-wrapper {
|
||||||
|
|||||||
Reference in New Issue
Block a user