当前位置: 首页 > wzjs >正文

打电话叫人做网站wordpress 投稿 图片

打电话叫人做网站,wordpress 投稿 图片,互联网网站建设门户网,网站开发简直使用 React 搭建一个现代化的聊天界面&#xff0c;支持与 Ollama 本地部署的大语言模型进行多轮对话。界面清爽、功能完整&#xff0c;支持 Markdown 渲染、代码高亮、<think> 隐藏思考标签、流式渐进反馈、暗黑模式适配等特性。 &#x1f9e9; 核心功能亮点 ✅ 模型选择…

使用 React 搭建一个现代化的聊天界面,支持与 Ollama 本地部署的大语言模型进行多轮对话。界面清爽、功能完整,支持 Markdown 渲染、代码高亮、<think> 隐藏思考标签、流式渐进反馈、暗黑模式适配等特性。


🧩 核心功能亮点

✅ 模型选择支持
  • 启动时自动请求 http://localhost:11434/api/tags 获取所有本地模型。

  • 允许用户通过下拉框动态切换聊天使用的模型。

✅ 多轮对话支持
  • 聊天上下文由历史消息 messages 组成,发送请求时一并传入。

  • 用户每次发送内容后,bot 的响应将基于历史记录生成。

✅ 实时流式响应 + <think> 处理
  • 使用 ReadableStream 实现逐段渲染。

  • <think>...</think> 区块被识别并自动隐藏,直到关闭 </think> 后再更新 UI。

✅ Markdown 渲染 & 代码高亮
  • 借助 react-markdown + remark-gfm 支持 GitHub 风格 Markdown。

  • 使用 react-syntax-highlighter 实现代码块高亮显示,自动识别语言。

✅ 响应式 UI & 暗黑模式适配
  • 使用 Tailwind CSS 快速构建布局。

  • 检测 HTML dark 类名切换对应代码主题(oneLight / oneDark)。


import React, { useState, useRef, useEffect } from 'react';
import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
import { oneLight, oneDark } from 'react-syntax-highlighter/dist/cjs/styles/prism';type Message = { text: string; sender: 'user' | 'bot' };type Props = { value: string; onChange: (e: React.ChangeEvent<HTMLInputElement>) => void; onSend: () => void };
const ChatInput: React.FC<Props> = React.memo(({ value, onChange, onSend }) => (<div className="mt-2 flex"><inputclassName="flex-1 px-3 py-2 border rounded-l"value={value}onChange={onChange}onKeyDown={e => e.key === 'Enter' && onSend()}/><button onClick={onSend} className="px-4 bg-neutral-600 text-white rounded-r">发送</button></div>
));const ChatWindow: React.FC = () => {const [models, setModels] = useState<string[]>([]);const [selectedModel, setSelectedModel] = useState<string>('');const [messages, setMessages] = useState<Message[]>([{ text: '你好,我是 Ollama!请选择模型后开始聊天。', sender: 'bot' },]);const [input, setInput] = useState('');const [isThinking, setIsThinking] = useState(false);const messagesEndRef = useRef<HTMLDivElement>(null);const isDark = document.documentElement.classList.contains('dark');const scrollToBottom = () => messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });useEffect(scrollToBottom, [messages, isThinking]);// 获取模型列表useEffect(() => {fetch('http://localhost:11434/api/tags').then(res => res.json()).then(data => {const names = data.models?.map((m: any) => m.name) || [];setModels(names);if (names.length) setSelectedModel(names[0]);}).catch(err => {console.error('获取模型失败:', err);setMessages(prev => [...prev, { text: '无法获取模型列表', sender: 'bot' }]);});}, []);const handleSend = async () => {if (!input.trim() || !selectedModel) return;// 1. 把用户消息加入setMessages(prev => [...prev, { text: input, sender: 'user' }]);setInput('');// 2. 预插入一条 bot 占位,用于后面一次性更新setMessages(prev => [...prev, { text: '', sender: 'bot' }]);// 清洗 <think>…</think> 的工具const cleanThink = (text: string) => text.replace(/<think>[\s\S]*?<\/think>/g, '');let fullText = '';let thinkOpen = false;  // 标记是否在 <think>…</think> 区间try {const response = await fetch('http://localhost:11434/api/chat', {method: 'POST',headers: { 'Content-Type': 'application/json' },body: JSON.stringify({model: selectedModel,messages: [{ role: 'user', content: input }],}),});const reader = response.body!.getReader();const decoder = new TextDecoder('utf-8');while (true) {const { value, done } = await reader.read();if (done) break;const chunk = decoder.decode(value, { stream: true });const lines = chunk.split('\n').filter(l => l.trim());for (const line of lines) {try {const data = JSON.parse(line);const c = data.message?.content || '';// 检测思考开始if (c.includes('<think>')) {thinkOpen = true;setIsThinking(true);}fullText += c;// 检测思考结束if (c.includes('</think>')) {thinkOpen = false;setIsThinking(false);// 这时才做一次性更新:清洗掉所有 think 内容,并写入 UIconst display = cleanThink(fullText).trim();setMessages(prev => {const copy = [...prev];copy[copy.length - 1] = { text: display, sender: 'bot' };return copy;});}} catch (e) {console.warn('解析流片段失败:', e);}}}// 如果整个流结束后,之前从未触发 </think>(比如模型不输出 think),那也一次性更新if (!thinkOpen) {// 每次都更新显示const display = cleanThink(fullText).trim();setMessages(prev => {const copy = [...prev];copy[copy.length - 1] = { text: display, sender: 'bot' };return copy;});}} catch (err) {console.error('请求出错:', err);setMessages(prev => [...prev,{ text: '请求出错,请检查服务是否开启。', sender: 'bot' },]);setIsThinking(false);}};return (<div className="h-screen flex flex-col p-4 bg-gray-100 dark:bg-gray-900">{/* 模型选择 */}<div className="mb-2"><label className="mr-2 text-sm text-gray-700 dark:text-gray-300">选择模型:</label><selectvalue={selectedModel}onChange={e => setSelectedModel(e.target.value)}className="p-1 text-sm border rounded dark:bg-gray-700 dark:text-white">{models.map(m => (<option key={m} value={m}>{m}</option>))}</select></div>{/* 聊天记录 */}<div className="flex-1 overflow-y-auto p-4 space-y-4 bg-white dark:bg-gray-800 rounded">{/* 聊天记录渲染 */}{messages.map((msg, i) => (<div key={i} className={msg.sender === 'bot' ? '' : 'text-right'}>{msg.sender === 'bot' ? (<div className="prose dark:prose-invert"><ReactMarkdownremarkPlugins={[remarkGfm]}components={{code(props: any) {const { inline, className, children, ...rest } = props;const match = /language-(\w+)/.exec(className || '');if (!inline && match) {return (<SyntaxHighlighterstyle={isDark ? oneDark : oneLight}language={match[1]}PreTag="div"{...rest}>{String(children).replace(/\n$/, '')}</SyntaxHighlighter>);}return (<code className="bg-gray-200 dark:bg-gray-700 px-1 rounded text-sm" {...rest}>{children}</code>);}}}>{msg.text}</ReactMarkdown></div>) : (<div className="inline-block px-3 py-1 bg-neutral-300 dark:bg-neutral-600 rounded text-sm">{msg.text}</div>)}</div>))}{isThinking && <div className="italic text-gray-500">正在思考中…</div>}<div ref={messagesEndRef} /></div>{/* 输入区 */}<ChatInput value={input} onChange={e => setInput(e.target.value)} onSend={handleSend} /></div>);
};export default ChatWindow;  


文章转载自:

http://tG96SrWu.pgjsg.cn
http://ARG2OpIv.pgjsg.cn
http://MRsCUgGW.pgjsg.cn
http://PtyLXtkD.pgjsg.cn
http://ePNbBwxd.pgjsg.cn
http://le8LrKcc.pgjsg.cn
http://yLNTyO6d.pgjsg.cn
http://BbYuJoWT.pgjsg.cn
http://mUvPN24X.pgjsg.cn
http://BhF3rXjJ.pgjsg.cn
http://M5AWOXTI.pgjsg.cn
http://sAJOyYbo.pgjsg.cn
http://viEaNmn8.pgjsg.cn
http://IN5Y04Ik.pgjsg.cn
http://1niyvcvF.pgjsg.cn
http://Lpi89V70.pgjsg.cn
http://tuvSc66p.pgjsg.cn
http://9scMID0g.pgjsg.cn
http://EyTCDZtP.pgjsg.cn
http://sBg8h4tp.pgjsg.cn
http://bokZwttj.pgjsg.cn
http://EGilsazJ.pgjsg.cn
http://1TsosMg8.pgjsg.cn
http://BRqbHZeB.pgjsg.cn
http://faYN8in2.pgjsg.cn
http://f6weRQ5b.pgjsg.cn
http://J8zAE4Nc.pgjsg.cn
http://ssmoISsO.pgjsg.cn
http://L5mbqsgl.pgjsg.cn
http://FgiIutP1.pgjsg.cn
http://www.dtcms.com/wzjs/731785.html

相关文章:

  • dw怎么做购物网站网站推广一般在哪个网做
  • 电商网站seo优化目标分解用flash做的经典网站
  • iis2008如何做网站建设专业网站运营团队
  • 数据库网站建设多少钱wordpress模板转为emlog
  • 一个网站有几个域名路北网站制作
  • 你知道的在线视频观看的vue做网站如何优化seo
  • 企业网站建设的一般要素包括6建网站要大约多少钱
  • 用html5做的音乐网站wordpress背景图像
  • 哪个网站上做ppt比较好看网站建设更新
  • 网站中的ppt链接怎么做广元城乡建设部网站首页
  • 网站自适应 如何做杭州互联网大厂
  • 做网站英文编辑有前途吗余姚做网站
  • 查备案网站两学一做 答题 网站
  • 网站托管平台东莞营销型高端网站建设
  • 运营一个网站的成本友情链接搜读
  • 网站数据库建设access做电视的视频网站
  • 遵义网站优化青岛网站建设咨询
  • 网上如何做网站洪梅镇仿做网站
  • 济南企业建站改行做网站
  • 企业官网门户网站管理系统网站菜单导航怎么做
  • 做商务网站公司代码实现wordpress百度地图
  • 做网站深圳企业网站公告怎么做
  • 青岛做网站推广网站开发 教材
  • 如何创建自己的网站黄页网站推广公司
  • 沈阳网站建设哪家做得好如何推广网站运营
  • 免费图片制作网站模板夜晚必备直播软件
  • 上海网站建设公司wordpress 播放
  • 网站搭建技术方案网站运营 宣传团队建设
  • 科技企业网站建设抖音seo排名优化软件
  • 网站创建数据库wordpress 响应式教程