GPT对话UI--通义千问API
GPT对话UI
项目介绍
一个基于 GPT 的智能对话界面,提供简洁优雅的用户体验。本项目使用纯前端技术栈实现,无需后端服务器即可运行。
功能特点
- 💬 实时对话:支持与 AI 进行实时对话交互
- 🌓 主题切换:支持浅色/深色主题,自动跟随系统设置
- 📝 Markdown 渲染:AI 回复支持 Markdown 格式,包含代码高亮
- 💾 本地存储:对话历史保存在本地,刷新页面不会丢失
- 🔍 智能推荐:每次对话后自动推荐相关问题
- ⌨️ 快捷操作:支持快捷键发送消息,Shift+Enter 换行
- 📊 代码优化:支持代码块语法高亮和一键复制
- 🔧 可配置:支持配置 API 密钥
- 📱 响应式:适配不同屏幕尺寸
- 🎨 字体调节:支持动态调整界面字体大小
快速开始
- 克隆项目到本地
git clone https://gitee.com/anxwefndu/gpt-chat-ui.git
- 打开项目目录
cd gpt-chat-ui
- 在浏览器中打开 index.html 文件即可使用
使用说明
-
首次使用需要配置 API 密钥
- 点击左侧边栏的"配置密钥"按钮
- 输入你的 API 密钥
- 点击保存即可使用
-
基本操作
- 在输入框输入问题后点击发送或按回车键发送
- 使用 Shift + Enter 可以在输入框换行
- 点击推荐问题可以快速发送相关提问
- 可以通过左侧的滑块调整字体大小
- 点击主题按钮切换深色/浅色模式
-
历史记录
- 所有对话记录会自动保存在本地
- 可以通过"清除历史记录"按钮清空所有对话
- 刷新页面不会丢失历史记录
技术栈
- HTML5
- CSS3 (Tailwind CSS)
- JavaScript
- Marked.js (Markdown 渲染)
- Highlight.js (代码高亮)
- Font Awesome (图标)
注意事项
- 本项目需要有效的 API 密钥才能正常使用
- 建议使用现代浏览器访问以获得最佳体验
- 所有数据均存储在本地,清除浏览器数据会导致历史记录丢失
- 目前文件上传这一块还没来得及加,后续添加上该块内容
源码下载
GPT对话UI
演示截图
1.系统首页
2.使用说明
3.配色API-key
4.系统首页-暗色
核心代码
index.html
<!DOCTYPE html>
<html lang="zh"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>AI 智能助手</title><link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=Pacifico&display=swap" rel="stylesheet"><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"><script src="https://cdn.tailwindcss.com"></script><script src="./resource/marked.min.js"></script><link rel="stylesheet" href="./resource/github.min.css"><script src="./resource/highlight.min.js"></script><script>tailwind.config = {theme: {extend: {colors: {primary: '#4A90E2',secondary: '#E3F2FD','primary-dark': '#2B4C7E','text-dark': '#E2E8F0' // 暗色主题下的主要文字颜色},borderRadius: {'none': '0px','sm': '2px',DEFAULT: '4px','md': '8px','lg': '12px','xl': '16px','2xl': '20px','3xl': '24px','full': '9999px','button': '4px'}}},darkMode: 'class' // 启用暗色模式}
</script><style>/* 保留原有样式 */.message-bubble {max-width: 80%;margin: 8px 0;padding: 12px 16px;border-radius: 12px;width: fit-content;}.user-message {background-color: #E3F2FD;margin-left: auto;border-top-right-radius: 4px;}.ai-message {background-color: #F5F5F5;margin-right: auto;border-top-left-radius: 4px;}/* 自定义滚动条样式 */.custom-scrollbar::-webkit-scrollbar {width: 8px;}.custom-scrollbar::-webkit-scrollbar-track {background: #f1f1f1;border-radius: 4px;}.custom-scrollbar::-webkit-scrollbar-thumb {background: #c1c1c1;border-radius: 4px;}.custom-scrollbar::-webkit-scrollbar-thumb:hover {background: #a8a8a8;}/* 暗色主题样式 */.dark .message-bubble.ai-message {background-color: #2D3748;}.dark .message-bubble.user-message {background-color: #2B4C7E;}.dark .custom-scrollbar::-webkit-scrollbar-track {background: #1A202C;}.dark .custom-scrollbar::-webkit-scrollbar-thumb {background: #4A5568;}.dark .custom-scrollbar::-webkit-scrollbar-thumb:hover {background: #718096;}input[type="range"] {appearance: none;width: 100%;height: 4px;background: #E5E7EB;border-radius: 2px;}input[type="range"]::-webkit-slider-thumb {appearance: none;width: 16px;height: 16px;background: #4A90E2;border-radius: 50%;cursor: pointer;}/* 添加 Markdown 样式 */.markdown-body {font-size: 1em;line-height: 1.6;}.markdown-body h1,.markdown-body h2,.markdown-body h3,.markdown-body h4,.markdown-body h5,.markdown-body h6 {margin-top: 1.5em;margin-bottom: 0.5em;font-weight: 600;}.markdown-body code {color: #0f172a;padding: 0.2em 0.4em;font-size: 0.9em;background-color: rgba(175, 184, 193, 0.2);border-radius: 6px;}.markdown-body pre {padding: 16px;overflow: auto;border-radius: 6px;background-color: #f6f8fa;margin: 1em 0;}.markdown-body pre code {padding: 0;background-color: #f6f8fa;white-space: pre;font-size: 0.95em;font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;}.dark .markdown-body pre {background-color: #f6f8fa;}/* 代码块内的复制按钮样式 */.copy-button {position: absolute;top: 8px;right: 8px;padding: 4px 8px;font-size: 12px;color: #64748b;background-color: #f1f5f9;border: 1px solid #e2e8f0;border-radius: 4px;opacity: 0;transition: all 0.2s ease;}.copy-button:hover {color: #0f172a;background-color: #e2e8f0;border-color: #cbd5e1;}/* 暗色主题下的复制按钮 */.dark .copy-button {color: #94a3b8;background-color: #1e293b;border-color: #334155;}.dark .copy-button:hover {color: #f1f5f9;background-color: #334155;border-color: #475569;}/* 鼠标悬停在代码块上时显示复制按钮 */.markdown-body pre:hover .copy-button {opacity: 1;}/* 代码高亮主题颜色优化 */.markdown-body code {padding: 0.2em 0.4em;font-size: 0.95em;background-color: #f1f5f9;border-radius: 4px;font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;}.markdown-body ul,.markdown-body ol {padding-left: 2em;margin: 1em 0;}.markdown-body li {margin: 0.5em 0;}.markdown-body p {margin: 1em 0;}.markdown-body blockquote {padding: 0 1em;color: #57606a;border-left: 0.25em solid #d0d7de;margin: 1em 0;}.dark .markdown-body blockquote {color: #8b949e;border-left-color: #3b434b;}.markdown-body table {border-collapse: collapse;margin: 1em 0;width: 100%;}.markdown-body table th,.markdown-body table td {padding: 6px 13px;border: 1px solid #d0d7de;}.dark .markdown-body table th,.dark .markdown-body table td {border-color: #3b434b;}.markdown-body table tr:nth-child(2n) {background-color: #f6f8fa;}.dark .markdown-body table tr:nth-child(2n) {background-color: #161b22;}</style>
</head><body class="bg-white dark:bg-gray-900 transition-colors"><div class="flex h-screen"><!-- 修改侧边栏背景色,使其与主内容区一致 --><aside class="w-64 bg-white dark:bg-gray-900 border-r border-gray-200 dark:border-gray-700 flex flex-col"><div class="p-6 border-b border-gray-200"><h1 class="text-2xl font-['Pacifico'] text-primary">logo</h1></div><nav class="flex-1 p-6 space-y-6"><div class="space-y-2"><label class="text-sm font-medium text-gray-700 dark:text-gray-300">主题模式</label><buttononclick="toggleTheme()"class="flex items-center justify-between w-full px-4 py-2 text-sm text-gray-700 dark:text-gray-300 bg-gray-50 dark:bg-gray-700 rounded-button hover:bg-gray-100 dark:hover:bg-gray-600"><span id="themeText">浅色模式</span><i class="fas fa-sun dark:fa-moon text-primary"></i></button></div><div class="space-y-2"><label class="text-sm font-medium text-gray-700 dark:text-gray-300">字体大小</label><div class="flex items-center space-x-2"><span class="text-xs text-gray-500 dark:text-gray-400">小</span><input type="range" min="1" max="3" value="2" class="w-full"><span class="text-xs text-gray-500 dark:text-gray-400">大</span></div></div><buttonclass="flex items-center w-full px-4 py-2 text-sm text-white bg-primary dark:bg-primary-dark rounded-button hover:bg-primary/90 dark:hover:bg-primary-dark/80 whitespace-nowrap"><i class="fas fa-trash-alt mr-2"></i>清除历史记录</button><button onclick="showHelp()"class="flex items-center w-full px-4 py-2 text-sm text-gray-700 dark:text-gray-300 bg-gray-50 dark:bg-gray-800 rounded-button hover:bg-gray-100 dark:hover:bg-gray-700 whitespace-nowrap"><i class="fas fa-question-circle mr-2"></i>使用帮助</button><button onclick="showConfig()"class="flex items-center w-full px-4 py-2 text-sm text-gray-700 dark:text-gray-300 bg-gray-50 dark:bg-gray-800 rounded-button hover:bg-gray-100 dark:hover:bg-gray-700 whitespace-nowrap"><i class="fas fa-cog mr-2"></i>配置密钥</button></nav></aside><main class="flex-1 flex flex-col"><div class="flex-1 overflow-y-auto p-6 space-y-4 custom-scrollbar"></div><div class="border-t border-gray-200 dark:border-gray-700 p-6"><div class="relative"><textareaclass="w-full h-24 px-4 py-3 text-gray-700 dark:text-text-dark border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 rounded-lg focus:outline-none focus:border-primary resize-none"placeholder="输入你的问题..."></textarea><div class="absolute bottom-3 right-3 flex space-x-2"><button class="p-2 text-gray-400 hover:text-primary dark:hover:text-primary"><i class="fas fa-paperclip"></i></button><button class="px-4 py-2 bg-primary dark:bg-primary-dark text-white rounded-button hover:bg-primary/90 dark:hover:bg-primary-dark/80 whitespace-nowrap">发送 <i class="fas fa-paper-plane ml-2"></i></button></div></div><div class="mt-2 flex flex-wrap gap-2"><span class="px-3 py-1 text-sm text-primary dark:text-gray-300 bg-secondary dark:bg-gray-800 rounded-full cursor-pointer hover:bg-primary hover:text-white dark:hover:bg-primary-dark">如何优化代码?</span><span class="px-3 py-1 text-sm text-primary bg-secondary dark:bg-gray-700 rounded-full cursor-pointer hover:bg-primary hover:text-white">写一篇营销文案</span><span class="px-3 py-1 text-sm text-primary bg-secondary dark:bg-gray-700 rounded-full cursor-pointer hover:bg-primary hover:text-white">数据分析方法</span></div></div></main></div><script>function toggleTheme() {const html = document.documentElement;const themeText = document.getElementById('themeText');const isDark = html.classList.toggle('dark');themeText.textContent = isDark ? '深色模式' : '浅色模式';localStorage.setItem('theme', isDark ? 'dark' : 'light');}// 初始化主题if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {document.documentElement.classList.add('dark');document.getElementById('themeText').textContent = '深色模式';}function callGpt(questionText, onProgress, onDone) {const sk = loadSK();// 获取历史消息记录const messages = loadMessages();// 构建消息历史const messageHistory = messages.map(msg => ({role: msg.role === 'user' ? 'user' : 'assistant',content: msg.content}));// 添加当前问题messageHistory.push({role: 'user',content: questionText});fetch("https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions", {method: "post",headers: {"Content-Type": "application/json","Authorization": `Bearer ${sk}`},body: JSON.stringify({model: "qwen-plus",messages: messageHistory, // 使用完整的消息历史stream: true}),}).then(response => {const reader = response.body.getReader();const decoder = new TextDecoder('utf-8');let fullText = "";function read() {reader.read().then(({done, value}) => {if (done) {onDone(fullText);return;}const text = decoder.decode(value, {stream: true});const chunks = text.replace("data: [DONE]", "").replace("data: ", "").split("data: ");for (let i = 0; i < chunks.length; i++) {try {const obj = JSON.parse(chunks[i]);const choices = obj.choices;const delta = choices[0].delta;fullText += delta.content;// 调用回调函数,实时更新内容onProgress(fullText);} catch (e) {console.log(chunks[i])read();}}read();}).catch(error => {console.log(error);alert("工具处理出错");});}read();}).catch(error => {console.log(error);alert("工具处理出错:" + error);});}// 消息记录管理const MESSAGE_STORAGE_KEY = 'chat_messages';function loadMessages() {const stored = localStorage.getItem(MESSAGE_STORAGE_KEY);return stored ? JSON.parse(stored) : [];}function saveMessages(messages) {localStorage.setItem(MESSAGE_STORAGE_KEY, JSON.stringify(messages));}function appendMessage(role, content) {const messages = loadMessages();messages.push({role,content,timestamp: new Date().toLocaleTimeString('zh', { hour: '2-digit', minute: '2-digit' })});saveMessages(messages);renderMessages();}function clearMessages() {localStorage.removeItem(MESSAGE_STORAGE_KEY);renderMessages();}// 配置 marked 选项marked.setOptions({highlight: function(code, lang) {if (lang && hljs.getLanguage(lang)) {return hljs.highlight(code, { language: lang }).value;}return code;},breaks: true,gfm: true});function renderMessages() {const messagesContainer = document.querySelector('.custom-scrollbar');const messages = loadMessages();messagesContainer.innerHTML = messages.map(msg => `<div class="message-bubble ${msg.role === 'user' ? 'user-message' : 'ai-message'}"><div class="text-gray-800 dark:text-text-dark markdown-body">${msg.role === 'user' ? msg.content : marked.parse(msg.content)}</div><span class="text-xs text-gray-400 dark:text-gray-500 mt-1 block">${msg.timestamp}</span></div>`).join('');// 处理代码块的高亮和复制功能const codeBlocks = messagesContainer.querySelectorAll('pre code');codeBlocks.forEach(block => {// 应用代码高亮hljs.highlightElement(block);// 添加复制按钮const copyButton = document.createElement('button');copyButton.className = 'copy-button';copyButton.innerHTML = '<i class="fas fa-copy mr-1"></i>复制';copyButton.onclick = async () => {try {await navigator.clipboard.writeText(block.textContent);copyButton.innerHTML = '<i class="fas fa-check mr-1"></i>已复制';setTimeout(() => {copyButton.innerHTML = '<i class="fas fa-copy mr-1"></i>复制';}, 2000);} catch (err) {console.error('复制失败:', err);}};// 为代码块容器添加相对定位const preBlock = block.parentElement;preBlock.style.position = 'relative';preBlock.appendChild(copyButton);});// 滚动到底部messagesContainer.scrollTop = messagesContainer.scrollHeight;}// 发送消息function sendMessage() {const textarea = document.querySelector('textarea');const content = textarea.value.trim();if (!content) return;const sk = loadSK();if (!sk) {alert('请先配置 API 密钥');showConfig();return;}// 添加用户消息appendMessage('user', content);textarea.value = '';// 调用 GPTcallGpt(content, (text) => {const messages = loadMessages();if (messages[messages.length - 1].role === 'assistant') {messages[messages.length - 1].content = text;} else {messages.push({role: 'assistant',content: text,timestamp: new Date().toLocaleTimeString('zh', {hour: '2-digit', minute: '2-digit'})});}saveMessages(messages);renderMessages();}, (finalText) => {console.log('回复完成:', finalText);renderSuggestion();});}// 事件处理document.addEventListener('DOMContentLoaded', () => {const textarea = document.querySelector('textarea');const sendButton = document.querySelector('button:has(.fa-paper-plane)');const clearButton = document.querySelector('button:has(.fa-trash-alt)');const suggestionSpans = document.querySelectorAll('.mt-2.flex.flex-wrap.gap-2 span');// 绑定事件sendButton.addEventListener('click', sendMessage);textarea.addEventListener('keydown', (e) => {if (e.key === 'Enter' && !e.shiftKey) {e.preventDefault();sendMessage();}});// 快捷提问suggestionSpans.forEach(span => {span.addEventListener('click', () => {textarea.value = span.textContent.trim();sendMessage();});});// 初始化消息记录renderMessages();// 修改清除按钮事件clearButton.addEventListener('click', confirmClear);renderSuggestion();});function showHelp() {const helpContent = `<div class="bg-white dark:bg-gray-800 p-6 rounded-lg max-w-2xl mx-auto" style="z-index: 999"><h2 class="text-xl font-bold text-gray-900 dark:text-text-dark mb-4">使用说明</h2><div class="space-y-3 text-gray-700 dark:text-text-dark"><p>1. 在输入框中输入您的问题,点击发送按钮或按回车键发送</p><p>2. 支持快捷提问,点击下方预设的问题直接发送</p><p>3. 可以使用 Shift + Enter 在输入框中换行</p><p>4. 支持深色/浅色主题切换,可根据个人喜好选择</p><p>5. 支持调整字体大小,通过滑块可以设置小、中、大三种字号</p><p>6. 所有对话记录会保存在本地,刷新页面不会丢失</p><p>7. 清除历史记录会删除所有本地保存的对话</p><p>8. 需要配置 API 密钥才能使用对话功能</p><p>9. 每次对话完成后会自动推荐相关的后续问题</p><p>10. 支持实时显示 AI 回复内容</p></div><button onclick="closeHelp()" class="mt-6 px-4 py-2 bg-primary dark:bg-primary-dark text-white rounded-button hover:bg-primary/90 dark:hover:bg-primary-dark/80" style=" margin-left: auto; display: flex; justify-content: right">知道了</button></div><div class="fixed inset-0 bg-black bg-opacity-50 z-40" onclick="closeHelp()"></div>`;const helpDiv = document.createElement('div');helpDiv.id = 'helpModal';helpDiv.className = 'fixed inset-0 flex items-center justify-center z-50';helpDiv.innerHTML = helpContent;document.body.appendChild(helpDiv);}function closeHelp() {const helpModal = document.getElementById('helpModal');if (helpModal) {helpModal.remove();}}function confirmClear() {const confirmContent = `<div class="bg-white s dark:bg-gray-800 p-6 rounded-lg max-w-md mx-auto" style="z-index: 999"><h2 class="text-xl font-bold text-gray-900 dark:text-text-dark mb-4">确认清除</h2><p class="text-gray-700 dark:text-text-dark">确定要清除所有对话记录吗?此操作不可恢复。</p><div class="mt-6 flex space-x-4" style="display: flex; justify-content: right"><button onclick="closeConfirm()" class="px-4 py-2 text-gray-700 dark:text-gray-300 bg-gray-100 dark:bg-gray-700 rounded-button hover:bg-gray-200 dark:hover:bg-gray-600">取消</button><button onclick="clearAndClose()" class="px-4 py-2 bg-red-500 text-white rounded-button hover:bg-red-600">确认清除</button></div></div><div class="fixed inset-0 bg-black bg-opacity-50 z-40" onclick="closeConfirm()"></div>`;const confirmDiv = document.createElement('div');confirmDiv.id = 'confirmModal';confirmDiv.className = 'fixed inset-0 flex items-center justify-center z-50';confirmDiv.innerHTML = confirmContent;document.body.appendChild(confirmDiv);}function closeConfirm() {const confirmModal = document.getElementById('confirmModal');if (confirmModal) {confirmModal.remove();}}function clearAndClose() {clearMessages();closeConfirm();}function renderSuggestion() {const messages = loadMessages();// 获取最近的对话内容if (messages.length > 0) {const recentMessages = messages.slice(-3).map(msg => msg.content).join('\n');getSuggestions(recentMessages, (suggestions) => {const suggestionContainer = document.querySelector('.mt-2.flex.flex-wrap.gap-2');suggestionContainer.innerHTML = suggestions.map(suggestion => `<span class="px-3 py-1 text-sm text-primary dark:text-gray-300 bg-secondary dark:bg-gray-800 rounded-full cursor-pointer hover:bg-primary hover:text-white dark:hover:bg-primary-dark">${suggestion}</span>`).join('');// 重新绑定事件const suggestionSpans = document.querySelectorAll('.mt-2.flex.flex-wrap.gap-2 span');suggestionSpans.forEach(span => {span.addEventListener('click', () => {document.querySelector('textarea').value = span.textContent.trim();sendMessage();});});});}}function getSuggestions(context, callback) {const sk = loadSK();if (!sk) {callback(['如何继续优化这个方案?', '有什么相关的最佳实践?', '还有其他方面需要考虑吗?']);return;}fetch("https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions", {method: "post",headers: {"Content-Type": "application/json","Authorization": `Bearer ${sk}`},body: JSON.stringify({model: "qwen-plus",messages: [{role: "user",content: `基于以下对话内容,推荐3个相关的后续提问,直接返回3个问题,用|||分隔:\n${context}`}],stream: false}),}).then(response => response.json()).then(data => {const suggestions = data.choices[0].message.content.split('|||').map(q => q.trim());callback(suggestions);}).catch(error => {console.error('获取建议问题失败:', error);// 发生错误时使用默认建议callback(['如何继续优化这个方案?', '有什么相关的最佳实践?', '还有其他方面需要考虑吗?']);});}const SK_STORAGE_KEY = 'chat_sk';function loadSK() {return localStorage.getItem(SK_STORAGE_KEY) || '';}function saveSK(sk) {localStorage.setItem(SK_STORAGE_KEY, sk);}function showConfig() {const currentSK = loadSK();const configContent = `<div class="bg-white dark:bg-gray-800 p-6 rounded-lg max-w-md mx-auto" style="z-index: 999; min-width: 500px"><h2 class="text-xl font-bold text-gray-900 dark:text-text-dark mb-4">配置 API 密钥</h2><div class="space-y-4"><div><label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">API 密钥 (SK)</label><input type="password" id="skInput" value="${currentSK}"class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-primary focus:border-primary dark:bg-gray-700 dark:text-white"placeholder="请输入你的 API 密钥"></div><div class="text-sm text-gray-500 dark:text-gray-400">请妥善保管你的 API 密钥,不要泄露给他人。</div></div><div class="mt-6 flex justify-end space-x-4"><button onclick="closeConfig()" class="px-4 py-2 text-gray-700 dark:text-gray-300 bg-gray-100 dark:bg-gray-700 rounded-button hover:bg-gray-200 dark:hover:bg-gray-600">取消</button><button onclick="saveConfig()" class="px-4 py-2 bg-primary dark:bg-primary-dark text-white rounded-button hover:bg-primary/90 dark:hover:bg-primary-dark/80">保存</button></div></div><div class="fixed inset-0 bg-black bg-opacity-50 z-40" onclick="closeConfig()"></div>`;const configDiv = document.createElement('div');configDiv.id = 'configModal';configDiv.className = 'fixed inset-0 flex items-center justify-center z-50';configDiv.innerHTML = configContent;document.body.appendChild(configDiv);}function closeConfig() {const configModal = document.getElementById('configModal');if (configModal) {configModal.remove();}}function saveConfig() {const skInput = document.getElementById('skInput');const sk = skInput.value.trim();if (!sk) {alert('请输入有效的 API 密钥');return;}saveSK(sk);closeConfig();}const FONT_SIZE_KEY = 'chat_font_size';function loadFontSize() {return localStorage.getItem(FONT_SIZE_KEY) || '2';}function saveFontSize(size) {localStorage.setItem(FONT_SIZE_KEY, size);}function updateFontSize(size) {const html = document.documentElement;switch(size) {case '1':html.style.fontSize = '14px';break;case '2':html.style.fontSize = '16px';break;case '3':html.style.fontSize = '18px';break;}}document.addEventListener('DOMContentLoaded', () => {// 初始化字体大小const fontSizeSlider = document.querySelector('input[type="range"]');fontSizeSlider.value = loadFontSize();updateFontSize(fontSizeSlider.value);// 监听字体大小变化fontSizeSlider.addEventListener('change', (e) => {const size = e.target.value;saveFontSize(size);updateFontSize(size);});});</script>
</body></html>
相关文章:
GPT对话UI--通义千问API
GPT对话UI 项目介绍 一个基于 GPT 的智能对话界面,提供简洁优雅的用户体验。本项目使用纯前端技术栈实现,无需后端服务器即可运行。 功能特点 💬 实时对话:支持与 AI 进行实时对话交互🌓 主题切换:支持…...
智能体数据分析
数据概览: 展示智能体的累计对话次数、累计对话用户数、对话满意度、累计曝光次数。数据分析: 统计对话分析、流量分析、用户分析、行为分析数据指标,帮助开发者完成精准的全面分析。 ps:数据T1更新,当日12点更新前一天…...
泛型算法——只读算法(一)
在 C 标准库中,泛型算法的“只读算法”指那些 不会改变它们所操作的容器中的元素,仅用于访问或获取信息的算法,例如查找、计数、遍历等操作。 accumulate std::accumulate()是 C 标准库**numeric**头文件中提供的算法,用于对序列…...
树莓派超全系列教程文档--(29)config.txt介绍
config.txt介绍 什么是 config.txt ?文件格式高级功能include条件过滤 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 什么是 config.txt ? Raspberry Pi 设备使用名为 config.txt 的配置文件,而不是传统 PC …...
第十六届蓝桥杯大赛软件赛省赛 C++ 大学 B 组 部分题解
赛时参加的是Python组,这是赛后写的题解,还有两题暂时还不会,待更新 题目链接题目列表 - 洛谷 | 计算机科学教育新生态 A 移动距离 答案:1576 C 可分解的正整数 Python3 import itertools from functools import cmp_to_ke…...
C++栈与堆内存详解:Visual Studio实战指南
C++栈与堆内存详解:Visual Studio实战指南 IDE环境:Visual Studio 2022 一、内存分区与核心概念 在C++程序中,内存分为**栈(Stack)和堆(Heap)**两大核心区域,两者的管理方式、生命周期和适用场景差异显著。 1. 栈内存(Stack Memory) • 特性: • 自动管理:由编…...
在Ubuntu服务器上部署xinference
一、拉取镜像 docker pull xprobe/xinference:latest二、启动容器(GPU) docker run -d --name xinference -e XINFERENCE_MODEL_SRCmodelscope -p 9997:9997 --gpus all xprobe/xinference:latest xinference-local -H 0.0.0.0 # 启动一个新的Docker容…...
非洲电商争夺战:中国闪电战遭遇本土游击队的降维打击
2024年5月,南非电商市场爆发史诗级对决——Temu闪电突袭下载量破百万,却在30天内遭遇Takealot的本土化反击致留存率腰斩。这场价值500亿美元市场的攻防战,揭开了非洲电商最残酷的生存法则:低价利刃砍不动本土化铁壁。 一、跨境模式…...
亚瑟阿伦36问
问 36 个问题,你就能爱上一个人,对方也能爱上你。 第一组 聚焦个人背景与价值观 例如“你最感激生命中的什么?”、“如果可以改变成长经历,你会改变什么?” 1、如果可以跟世上任何人共进晚餐,你会选择谁&…...
Ubuntu 20.04.6编译安装COMFAST CF-AX90无线网卡驱动
目录 0 前言 1 CF-AX90无线网卡驱动 1.1 驱动下载 1.2 驱动准备 2 编译安装驱动 2.1 拷贝驱动依赖到系统 2.2 驱动安装编译 3 重启 0 前言 COMFAST CF-AX90或者说AIC8800D80的Linux版本驱动不支持高版本的linux内核,实测目前仅支持最高5.15的内核。Ubuntu2…...
函数的极限与连续(强化和真题)
强化错题如下:...
4.15【Q】netsafe
我正在学习网络空间安全,” Cookie:使用防hash技术防御SYN泛洪攻击,减少服务器内存消耗“什么意思?什么是SYN泛洪攻击?什么又是防hash技术防御? ?详细解释,越细节越好 连接成功率 …...
多个路由器互通(静态路由)无单臂路由(简单版)
多个路由器互通(静态路由)无单臂路由(简单版) 开启端口并配ip地址 维护1 Router>en Router#conf t Router(config)#int g0/0 Router(config-if)#no shutdown Router(config-if)#ip address 192.168.10.254 255.255.255.0 Ro…...
opencv HSV的具体描述
色调H: 使用角度度量,取值范围为0\~360,从红色开始按逆时针方向计算,红色为0,绿色为120,蓝色为240。它们的补色是:黄色为60,青色为180,紫色为300。通过改变H的值&#x…...
ubuntu磁盘挂载
1、查看磁盘设备及分区 命令:列出所有块设备(磁盘及分区) lsblk 0表示此块未挂载 2、格式化分区 sudo mkfs.ext4 /dev/sdb 注意sdb换成自己的块名称 3、创建挂载点目录 sudo mkdir -p /mnt/data4、永久挂载 sudo blkid /dev…...
Visual Studio C++引入第三方库
前言 欢迎来到我的博客 个人主页:北岭敲键盘的荒漠猫-CSDN博客 本文主要整理visual studio C导入第三方库的注意事项与操作 bilibili配套视频:【visual studio C导入第三方库-哔哩哔哩】 https://b23.tv/vphfXnv 运行库选项 右键项目 -> 属性 -> C/C ->代码生成->…...
2025中国移动云智算大会回顾:云智变革,AI+跃迁
4月10日,2025中国移动云智算大会在苏州举办。会上,中国移动开启“由云向智”新范式,以“智”为核心开辟算网新生态,彰显其在AI新时代的战略远见与技术引领力。 “云智算”将如何通过算网基础设施与人工智能核心技术的深度融合&am…...
海珠区公示人工智能大模型应用示范区第二批资金奖励企业名单,助力产业蓬勃发展
2025 年 4 月 15 日,广州琶洲人工智能与数字经济试验区管理委员会在广州市海珠区人民政府门户网站发布重要通知,对人工智能大模型应用示范区政策兑现工作(第二批)(大模型专题)资金奖励企业名单予以公示。这…...
golang处理时间的包time一次性全面了解
本文旨在对官方time包有个全面学习了解。不钻抠细节,但又有全面了解,重点介绍常用的内容,一些低频的可能这辈子可能都用不上。主打一个花最少时间办最大事。 Duration对象: 两个time实例经过的时间,以长度为int64的纳秒来计数。 常见的durati…...
文件的加密与解密学习笔记
一些可能想知道的: cryptography库:密码学工具包 Fernet 是crytography 里的一个模块,用于对称加密 with open() as file #为了保证无论是否出错都能正确地关闭文件,与try...finally...相同 open() #用于读文件(默认…...
《TCP/IP网络编程》学习笔记 | Chapter 24:制作 HTTP 服务器端
《TCP/IP网络编程》学习笔记 | Chapter 24:制作 HTTP 服务器端 《TCP/IP网络编程》学习笔记 | Chapter 24:制作 HTTP 服务器端HTTP 概要理解 Web 服务器端无状态的 Stateless 协议请求消息(Request Message)的结构响应消息&#x…...
Apache POI(笔记)
介绍: 坐标: 写入Excel表格: 读取Excel表格:...
Table类型的表单
形如下面的图片 1 label与prop属性 const columns[{label: "文件名",prop: "fileName",scopedSlots: "fileName",},{ label: "删除时间",prop: "recoveryTime",width: "200",},{ label: "大小",prop:…...
Spring 中的验证、数据绑定和类型转换
🧑 博主简介:CSDN博客专家,历代文学网(PC端可以访问:https://literature.sinhy.com/#/literature?__c1000,移动端可微信小程序搜索“历代文学”)总架构师,15年工作经验,…...
【技术派后端篇】canal实现MySQL/Redis缓存一致性
1 前言 在探讨如何利用canal实现MySQL/Redis缓存一致性之前,强烈建议大家先阅读以下几篇相关文章,因为本文是基于这些文章的基础上展开的: 《深度剖析 MySQL 与 Redis 缓存一致性:理论、方案与实战》 :该文详细阐述了…...
华清远见STM32F103智能小车重磅上线!循迹避障红外遥控WiFi远程控制,0基础小白从入门到单片机软硬件项目实战!
STM32F103智能云控小车是由华清远见倾力打造的一款多功能智能小车,专为高校教学、学生毕业设计、创新竞赛、单片机入门学习及项目实践量身定制。这款小车集红外遥控、远程物联网控制、智能巡线、高精度避障和交互式显示屏五大核心功能于一体,融合了物联网…...
李飞飞团队新作WorldScore:“世界生成”能力迎来统一评测,3D/4D/视频模型同台PK
从古老神话中对世界起源的幻想,到如今科学家们在实验室里对虚拟世界的构建,人类探索世界生成奥秘的脚步从未停歇。如今,随着人工智能和计算机图形学的深度融合,我们已站在一个全新的起点,能够以前所未有的精度和效率去…...
seaborn库详解
Seaborn 是一个基于 Python 的统计数据可视化库,它建立在 matplotlib 之上,旨在提供更高级、更美观、更具统计意义的可视化功能。 CONTENT 1. 单变量分布可视化功能代码 2. 双变量联合分布可视化功能代码 3. 分类数据柱状图可视化功能代码 4. 箱线图可视…...
UNACMS PHP对象注入漏洞复现(CVE-2025-32101)(附脚本)
免责申明: 本文所描述的漏洞及其复现步骤仅供网络安全研究与教育目的使用。任何人不得将本文提供的信息用于非法目的或未经授权的系统测试。作者不对任何由于使用本文信息而导致的直接或间接损害承担责任。如涉及侵权,请及时与我们联系,我们将尽快处理并删除相关内容。 前言…...
应用篇02-镜头标定(上)
本节主要介绍相机的标定方法,包括其内、外参数的求解,以及如何使用HALCON标定助手实现标定。 计算机视觉——相机标定(Camera Calibration)_摄像机标定-CSDN博客 1. 原理 本节介绍与相机标定相关的理论知识,不一定全,可以参考相…...
游戏引擎学习第230天
回顾并为今天的内容定下基调 今天是我们进行“排序”工作的第二天。昨天我们在渲染器中实现了排序功能。这其实是从一开始就知道必须做的事情,只是一直没有合适的时机。而昨天终于迎来了这个时机,不知道为什么,可能就是突然有了冲动和想法&a…...
3.串口通信之SPI
—>1.串口通信之UART见这篇<— —>2.串口通信之IIC见这篇<— 1.SPI特点 SPI(Serial Peripheral Interface)即串行外设接口,有4条总线,分别是SCLK(SPI Clock),MISO(Master Input Slave Output),MOSI(Mast…...
无人机姿态稳定与动态控制模块概述!
一、设计难点 1. 动态算力需求与硬件能力的不匹配** 无人机边缘计算设备通常受限于体积和重量,导致其计算单元(如CPU、GPU)的算力有限,难以应对突发的高负载任务(如实时图像处理、AI推理)。 挑战&am…...
【shell】终端文本的颜色和样式打印
在Shell脚本中,\033[XXm 是 ANSI转义序列,用于控制终端文本的颜色和样式。以下是完整的颜色和样式代码列表: 1. 基本格式 echo -e "\033[CODEm你的文本\033[0m"\033[:转义序列开始(\e[ 或 \x1b[ 等效&#…...
模型加载常见问题
safetensors_rust.SafetensorError: Error while deserializing header: HeaderTooLarge 问题代码: model AutoModelForVision2Seq.from_pretrained( "/data-nvme/yang/Qwen2.5-VL-32B-Instruct", trust_remote_codeTrue, torch_dtypetorc…...
HCIA-Access V2.5_16_3_数据业务维护
查询ONT上的业务配置 查询ONU上的业务配置 查询OLT上网业务 查询上网业务流量 查询上网业务相关MAC地址 删除故障ONT 删除故障ONU...
Java设计开发商城抢票功能
在开发一个商城抢购功能时,需要考虑几个关键方面,包括并发控制、数据一致性、用户体验以及系统的可扩展性。下面我将通过一个简单的步骤指南来介绍如何设计这样一个功能。 1. 需求分析 首先,明确抢购功能的需求: 限制购买数量。…...
【APM】Build an environment for Traces, Metrics and Logs of App by OpenTelemetry
系列文章目录 此系列文章介绍如何搭建Observability(可观测性)环境(Opentelemetry-Collector、Tempo、Prometheus、Loki和Grafana),以及应用。 【APM】Observability Solution 【APM】Build an environment for Traces, Metrics and Logs …...
全自动驾驶(FSD,Full Self-Driving)自动驾驶热点技术的成熟之处就是能判断道路修复修路,能自动利用类似“人眼”的摄像头进行驾驶!值得学习!
全自动驾驶(FSD,Full Self-Driving)软件是自动驾驶领域中的热点技术,其核心目标是实现车辆在各种复杂交通环境下的安全、稳定、高效自动驾驶。FSD软件的技术核心涉及多个方面的交叉技术,下面将详细分析说明其主要核心技…...
需要处理哪些响应数据?
在调用淘宝商品搜索 API 时,响应数据通常是一个 JSON 对象,包含了搜索结果的详细信息。以下是需要处理的主要响应数据字段及其说明: 响应数据结构 示例 JSON 数据 JSON {"code": "0","errorMessage": &quo…...
【NLP 63、大模型应用 —— Agent】
人与人最大的差距就是勇气和执行力,也是唯一的差距 —— 25.4.16 一、Agent 相关工作 二、Agent 特点 核心特征: 1.专有场景(针对某个垂直领域) 2.保留记忆(以一个特定顺序做一些特定任务,记忆当前任务的前…...
Windows 图形显示驱动开发-WDDM 1.2功能—Windows 8 中的 DirectX 功能改进(三)
一、与目标无关的光栅化 (TIR) 独立于目标的光栅化 (TIR) 为涉及结构化图形的高质量抗锯齿的 Direct2D 使用方案提供高性能抗锯齿路径。 TIR 使 Direct2D 能够将光栅化步骤从 CPU 移动到 GPU,同时保留 Direct2D 抗锯齿语义和质量。 使用此功能,软件层可…...
总结【过往部分项目经历二(计算机图形学方向)】
过往部分项目经历二 1.加强杆自动生成算法2.牙龈线序列批量算法3.光伏多阵列排布算法 1.加强杆自动生成算法 介绍: 主要用于牙科正畸定制化应用。采用纯数字化技术,一键导入加强杆后,自动摆放、加强杆结构设计、网格重建、接触部分自动定位、…...
鸿蒙智行多款重磅新品发布,开启智慧出行新篇章
4月16日,鸿蒙智行新品发布会在上海隆重举办。问界M8、问界新M7全新配色、享界S9增程版上市;尊界S800内饰设计公布;SAIC尚界品牌首次官宣。 本次发布会后,鸿蒙智行将在4月17日举办智界品牌之夜,携手用户共同探索未来出…...
冰川流域提取分析——ArcGIS pro
一、河网提取和流域提取视频详细GIS小熊 || 6分钟学会水文分析—河网提取(以宜宾市为例)_哔哩哔哩_bilibili 首先你要生成研究区域DEM,然后依次是填洼→流向→流量→栅格计算器→河网分级→栅格河网矢量化(得到河网.shpÿ…...
SpringBoot——配置文件
目录 前言 1.参数配置化 1.1使用application.properties 2.yml配置文件 3.ConfigurationProperties 3.1ConfigurationProperties 4.总结 前言 我们在配置一个类的对象属性时如果我们直接写在类里就属于硬编码,如果我们在做项目时每设计到一个第三方服务就将其…...
Lambda 函数与 peek 操作的使用案例
Lambda 函数和 peek 操作是 Java 8 Stream API 中非常有用的特性,下面我将介绍它们的使用案例。 Lambda 函数使用案例 Lambda 表达式是 Java 8 引入的一种简洁的匿名函数表示方式。 集合操作 List<String> names Arrays.asList("Alice", "B…...
Java 中常用队列用法详解
Java 中常用队列用法详解 在Java编程中,队列是一种非常重要的数据结构,广泛应用于任务调度、消息传递以及多线程通信等场景。以下将详细介绍几种常用的Java队列及其使用方法。 1. Queue 接口概述 Queue 是Java集合框架中的一个接口,它定义…...
IoT FEM射频前端模组芯片(2.4G PA)三伍微电子GSR2401 兼容替代RFX2401
型号:GSR2401应用:适用于蓝牙(BT)、ZigBee及物联网(IoT)设备 功能:集成了功率放大器(PA)、开关(Switch)和低噪声放大器(LNAÿ…...
android如何在生产环境中做到详实的日志收集而不影响性能?
在Android应用的生命周期中,日志收集贯穿于开发、测试到生产环境的每一个阶段。特别是在生产环境中,当应用部署到成千上万的用户设备上时,开发者无法直接访问用户的运行环境,也无法像在开发阶段那样通过调试工具实时查看代码执行情况。这时,日志就成为连接开发者与用户设备…...