• 首页

  • 写作

  • 文章归档

  • 照片

  • 友情链接

  • 旅行

  • 读书

  • 日志

  • 随记

  • 人文历史

  • linux

  • 前端
b l o g
b l o g

admin

lzp

01月
11
前端
react ui

使用 tailwind 完善 chagpt UI 界面

发表于 2025-01-11 • 字数统计 4101 • 被 6 人看爆

##2024/10/9 21:01:09:

使用 tailwind 完善 chagpt UI 界面

网站:

https://2024-09-26-vite-react-tailwind-chatgpt-demo.pages.dev/chatgpt

提交信息:

Commits on Oct 9, 2024
update ui padding
https://github.com/qyzhizi/2024-09-26-vite-react-tailwind-chatgpt-demo/commit/f60b4c35eaeb6c46df5b54d049a6ba50c76bb7ec
update chatgpt ui sticky
https://github.com/qyzhizi/2024-09-26-vite-react-tailwind-chatgpt-demo/commit/1acbce1784c24f590a1dae99da3dea424ef1eb65
update chatgpt ui
https://github.com/qyzhizi/2024-09-26-vite-react-tailwind-chatgpt-demo/commit/a5d3e91c87b4167b4109396a6f068925bf3a4477

@@ 实现聊天 上下文

使用一个 zustand库 存储 messages 列表,通过 messageLength 设置窗口长度,保持最新的消息,丢弃旧的超出窗口长度的消息。

import { create } from 'zustand';
const messageLength = -13 // 保持数组长度
const useChatStore = create((set) => ({
  messages: [],
  addMessage: (message) => set((state) => {
    const newMessages = [...state.messages, message];
    return { messages: newMessages.slice(messageLength) };
  }),
}));

export { useChatStore };

使用 zustand库 遇到一个问题:@react zustand set 函数是异步调用的,调用addMessage({ role: 'user', content: input });韩式时,涉及的变量messages 不会立即生效,如何解决异步问题 解决方法:

addMessage({ role: 'user', content: input });

setTimeout(async () => {
    const { messages } = useChatStore.getState(); 
    const result = await axios.post('/api/azurechatgpt', 
        { messages: messages }, 
        { headers: { 'Content-Type': 'application/json' } }
    );
    // Further actions
}, 0); // Ensures it runs after the state update

先使用 setTimeout 将其中的函数 稍后执行,一般是放入事件循环的队列中,等到该函数执行时,前面的 message 变量已经更新了,为了保险一点,使用const { messages } = useChatStore.getState(); 获取最新的状态,单独使用 const { messages } = useChatStore.getState(); 而不用 setTimeout 也行。这里把两者都写上,为了体现 setTimeout 的特性。
messages 是最新的上下文,包括用户最新的提问,这里设置了前 13 条信息,一起提交到 一个 pages function 接口(azure openai )

@@ 调整 UI:用户的输入框固定在底部

让后 用户的输入框固定在底部

<div className="chatgptroot text-center h-screen overflow-auto">
    <h2 className="text-2xl mb-10">ChatGPT</h2>
    <div className="messages mt-2 p-2 border rounded bg-gray-100">
        {renderMessages()}
    </div>
    <form onSubmit={handleSubmit} className="sticky bottom-0 bg-white">
        <textarea
            id="auto-resize-textarea"
            className="border rounded p-2 w-full resize-none"
            placeholder="Your question here..."
            value={input}
            onChange={(e) => setInput(e.target.value)}
            rows="3" // 设置初始行数
        />
        <button type="submit" className="ml-2 p-2 bg-blue-500 text-white rounded">Ask</button>
    </form>

    {errorResponse && (
        <div className="mt-4 p-2 border rounded bg-gray-100">
            <h3 className="font-bold">Response:</h3>
            <p>{errorResponse}</p>
        </div>
    )}
</div>

关键的css sticky bottom-0 在 form 元素上设置 sticky ,bottom-0 表示当 form 元素滚动到父元素底部时,不再向下滚动。这里 from 元素中包含了 textarea 元素,form 元素的父元素是 chatgptroot ,它的 css 设置也比较关键:
h-screen overflow-auto 表示占用全屏空间,verflow-auto 表示高度超出后会出现溢出,伴随滚动条。而 verflow-auto 正是 sticky 所需要的,因为sticky元素的父元素需要有滚动的空间。

@@ 调整 UI:chatgpt 返回的文字需要保留空白字符串,并自动换行

chatgpt 返回代码需要保留空白字符串,因此渲染时需要使用 pre 元素

    // 渲染消息列表
    const renderMessages = () => {
        return messages.map((message, index) => (
            <div key={index} className={`border rounded p-2 ${message.role === 'user' ? 'bg-blue-100' : 'bg-green-100'}`}>
                <pre className="text-left whitespace-pre-wrap"> 
                    {message.content}
                </pre>
            </div>
        ));
    };

text-left whitespace-pre-wrap 是关键

@@ 调整 UI:useEffect 实现 textarea 随文字高度自动调整textarea的高度

useEffect(() => {
    const maxHeight = 300; // 你可以根据需要调整最大高度
    const textarea = document.getElementById('auto-resize-textarea');
    const adjustHeight = () => {
        textarea.style.height = 'auto'; // 先将高度设置为 auto,以便重新计算高度
        textarea.style.height = Math.min(textarea.scrollHeight, maxHeight) + 'px'; // 根据内容设置高度,但不超过最大高度
    };
    // 添加事件监听器
    textarea.addEventListener('input', adjustHeight);
    // 初始化时调整高度(例如预填充内容)
    adjustHeight();
    // 清理事件监听器
    return () => {
        textarea.removeEventListener('input', adjustHeight);
    };
    }, [input]); // 依赖 input,当 input 发生变化时触发

useEffect 是 react 的功能,当 input 变化时,执行注册的函数,在注册的函数中实现高度的自动调整

@@ 调整 UI:useEffect 实现页面自动滚动到最底部

useEffect(() => {
    const chatGptRootContainer = document.querySelector('.chatgptroot');
    chatGptRootContainer.scrollTop = chatGptRootContainer.scrollHeight;
    window.scrollTo(0, document.body.scrollHeight);
  }, [messages]);

监听变化的对象是:messages
前两行表示 chatgptroot 的元素滚动到最底部,好处是当提交信息或者返回信息后,自动滚动到最底部

window.scrollTo(0, document.body.scrollHeight); 表示页面滚动到最底部,因为 chatgptroot 容器外面还有页面其他内容,保证 用户输入框显示在最底部,不被遮挡。

分享到:
next.js login github and deploy it to vercel
当我提交输入框的信息后, react 是如何更新 UI 界面的?
  • 文章目录
  • 站点概览
admin

! lzp

hello

Github Twitter QQ Email Telegram RSS
看爆 Top5
  • 历史与人文 视频链接 189次看爆
  • 2022日志随笔 175次看爆
  • 我的青海湖骑行 164次看爆
  • 读书随笔 124次看爆
  • rs2 设置教程 97次看爆

站点已萌萌哒运行 00 天 00 小时 00 分 00 秒(●'◡'●)ノ♥

Copyright © 2025 admin

由 Halo 强力驱动 · Theme by Sagiri · 站点地图