<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>免费AI助手</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/styles/atom-one-dark.min.css">
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
:root {
--primary-color: #6c5ce7;
--secondary-color: #a29bfe;
--light-color: #f7f9ff;
--dark-color: #1a1c2c;
--user-bubble: #6c5ce7;
--ai-bubble: #f0f4ff;
--success-color: #00cec9;
--error-color: #ff7675;
}
body {
background: linear-gradient(135deg, #74b9ff, #6c5ce7);
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
color: var(--dark-color);
}
.container {
width: 100%;
max-width: 900px;
background: rgba(255, 255, 255, 0.97);
border-radius: 20px;
box-shadow: 0 15px 50px rgba(0, 0, 0, 0.2);
overflow: hidden;
display: flex;
flex-direction: column;
height: 90vh;
}
.header {
background: linear-gradient(to right, var(--primary-color), var(--secondary-color));
color: white;
padding: 20px;
text-align: center;
position: relative;
}
.header h1 {
font-size: 1.8rem;
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
animation: pulse 2s infinite;
}
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.05); }
100% { transform: scale(1); }
}
.header p {
margin-top: 8px;
opacity: 0.9;
font-size: 0.9rem;
}
.features {
display: flex;
justify-content: center;
flex-wrap: wrap;
gap: 15px;
margin-top: 15px;
}
.feature {
background: rgba(255, 255, 255, 0.2);
padding: 8px 15px;
border-radius: 30px;
font-size: 0.85rem;
display: flex;
align-items: center;
gap: 5px;
}
.chat-container {
flex: 1;
overflow-y: auto;
padding: 25px;
display: flex;
flex-direction: column;
gap: 20px;
background: var(--light-color);
}
.message {
display: flex;
gap: 15px;
max-width: 85%;
animation: fadeIn 0.3s ease;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.user-message {
align-self: flex-end;
flex-direction: row-reverse;
}
.avatar {
width: 40px;
height: 40px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
font-weight: bold;
}
.user-avatar {
background: var(--user-bubble);
color: white;
box-shadow: 0 4px 8px rgba(108, 92, 231, 0.3);
}
.ai-avatar {
background: var(--primary-color);
color: white;
box-shadow: 0 4px 8px rgba(108, 92, 231, 0.3);
}
.message-content {
padding: 15px 20px;
border-radius: 18px;
line-height: 1.5;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
position: relative;
}
.user-message .message-content {
background: var(--user-bubble);
color: white;
border-bottom-right-radius: 5px;
}
.ai-message .message-content {
background: var(--ai-bubble);
border-bottom-left-radius: 5px;
}
.ai-message .message-content pre {
background: #282c34;
color: #abb2bf;
padding: 15px;
border-radius: 8px;
overflow-x: auto;
margin-top: 10px;
}
.input-container {
padding: 20px;
background: white;
border-top: 1px solid #eee;
display: flex;
gap: 15px;
position: relative;
}
.message-input {
flex: 1;
padding: 15px 20px;
border-radius: 30px;
border: 2px solid #e0e0e0;
outline: none;
font-size: 1rem;
transition: border-color 0.3s;
padding-right: 50px;
}
.message-input:focus {
border-color: var(--primary-color);
}
.send-btn {
background: linear-gradient(to right, var(--primary-color), var(--secondary-color));
color: white;
border: none;
width: 50px;
height: 50px;
border-radius: 50%;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: transform 0.3s, opacity 0.3s;
position: absolute;
right: 25px;
bottom: 25px;
box-shadow: 0 4px 10px rgba(108, 92, 231, 0.4);
}
.send-btn:hover {
transform: scale(1.05);
}
.send-btn:disabled {
opacity: 0.6;
cursor: not-allowed;
}
.typing-indicator {
display: flex;
align-items: center;
gap: 5px;
padding: 15px 20px;
background: var(--ai-bubble);
border-radius: 18px;
width: fit-content;
margin-top: 10px;
opacity: 0;
transition: opacity 0.3s;
}
.typing-indicator.show {
opacity: 1;
}
.dot {
width: 8px;
height: 8px;
background: var(--primary-color);
border-radius: 50%;
animation: bounce 1.5s infinite;
}
.dot:nth-child(2) {
animation-delay: 0.2s;
}
.dot:nth-child(3) {
animation-delay: 0.4s;
}
@keyframes bounce {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-5px); }
}
.info-box {
background: #e3f2fd;
border-left: 4px solid var(--primary-color);
padding: 15px;
border-radius: 0 8px 8px 0;
margin-top: 10px;
font-size: 0.9rem;
}
.info-box ul {
padding-left: 20px;
margin-top: 10px;
}
.info-box li {
margin-bottom: 8px;
line-height: 1.6;
}
.code-header {
display: flex;
justify-content: space-between;
align-items: center;
background: #1e1e1e;
padding: 8px 15px;
border-radius: 8px 8px 0 0;
color: #9cdcfe;
font-size: 0.9rem;
}
.copy-btn {
background: #3a3a3a;
color: white;
border: none;
padding: 5px 10px;
border-radius: 4px;
cursor: pointer;
transition: background 0.3s;
display: flex;
align-items: center;
gap: 5px;
}
.copy-btn:hover {
background: var(--primary-color);
}
.model-selector {
display: flex;
justify-content: center;
gap: 15px;
margin-top: 15px;
flex-wrap: wrap;
}
.model-btn {
background: rgba(255, 255, 255, 0.2);
border: none;
color: white;
padding: 8px 15px;
border-radius: 30px;
cursor: pointer;
transition: all 0.3s;
font-size: 0.9rem;
}
.model-btn.active {
background: white;
color: var(--primary-color);
font-weight: bold;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}
.model-btn:hover:not(.active) {
background: rgba(255, 255, 255, 0.3);
}
@media (max-width: 768px) {
.container {
height: 95vh;
}
.message {
max-width: 95%;
}
.header h1 {
font-size: 1.5rem;
}
.features {
gap: 8px;
}
.feature {
padding: 6px 12px;
font-size: 0.8rem;
}
}
.notification {
position: fixed;
top: 20px;
left: 50%;
transform: translateX(-50%);
background: var(--success-color);
color: white;
padding: 12px 25px;
border-radius: 30px;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
z-index: 1000;
opacity: 0;
transition: opacity 0.3s;
display: flex;
align-items: center;
gap: 10px;
}
.notification.show {
opacity: 1;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1><i class="fas fa-robot"></i> 免费AI助手</h1>
<p>无需API密钥,直接使用多个免费AI服务</p>
<div class="model-selector">
<button class="model-btn active" data-model="huggingface">HuggingFace</button>
<button class="model-btn" data-model="deepseek">DeepSeek</button>
<button class="model-btn" data-model="cohere">Cohere</button>
</div>
<div class="features">
<div class="feature"><i class="fas fa-code"></i> 代码生成</div>
<div class="feature"><i class="fas fa-question-circle"></i> 问题解答</div>
<div class="feature"><i class="fas fa-comment"></i> 自然对话</div>
<div class="feature"><i class="fas fa-lightbulb"></i> 创意写作</div>
<div class="feature"><i class="fas fa-graduation-cap"></i> 学习辅助</div>
</div>
</div>
<div class="chat-container" id="chatContainer">
<div class="message ai-message">
<div class="avatar ai-avatar">
<i class="fas fa-robot"></i>
</div>
<div class="message-content">
<p>您好!我是您的免费AI助手。请问有什么可以帮您的?</p>
<div class="info-box">
<strong>使用说明:</strong>
<ul>
<li>在下方输入框输入问题或请求</li>
<li>我可以帮助解答问题、编写代码、解释概念等</li>
<li>支持多种AI模型,点击顶部按钮切换</li>
<li>示例问题:<br>
"用JavaScript实现快速排序"<br>
"解释一下CSS Flex布局"<br>
"如何用Python读取CSV文件?"
</li>
</ul>
</div>
</div>
</div>
</div>
<div class="typing-indicator" id="typingIndicator">
<div class="dot"></div>
<div class="dot"></div>
<div class="dot"></div>
</div>
<div class="input-container">
<input type="text" class="message-input" id="messageInput" placeholder="输入您的问题或请求...">
<button class="send-btn" id="sendBtn">
<i class="fas fa-paper-plane"></i>
</button>
</div>
</div>
<div class="notification" id="notification">
<i class="fas fa-check-circle"></i>
<span>代码已复制到剪贴板!</span>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js"></script>
<script>
// 页面加载完成后执行
document.addEventListener('DOMContentLoaded', function() {
const chatContainer = document.getElementById('chatContainer');
const messageInput = document.getElementById('messageInput');
const sendBtn = document.getElementById('sendBtn');
const typingIndicator = document.getElementById('typingIndicator');
const notification = document.getElementById('notification');
const modelButtons = document.querySelectorAll('.model-btn');
let currentModel = 'huggingface';
// 设置当前模型
modelButtons.forEach(button => {
button.addEventListener('click', function() {
modelButtons.forEach(btn => btn.classList.remove('active'));
this.classList.add('active');
currentModel = this.dataset.model;
// 添加系统消息
addMessage(`已切换到 ${getModelName(currentModel)} 模型`, 'ai');
});
});
// 获取模型名称
function getModelName(model) {
const models = {
'huggingface': 'HuggingFace',
'deepseek': 'DeepSeek',
'cohere': 'Cohere'
};
return models[model] || '未知模型';
}
// 发送消息
function sendMessage() {
const message = messageInput.value.trim();
if (!message) return;
// 添加用户消息到聊天框
addMessage(message, 'user');
// 清空输入框
messageInput.value = '';
// 显示"正在输入"指示器
typingIndicator.classList.add('show');
// 调用AI API(模拟)
simulateAIResponse(message);
}
// 添加消息到聊天框
function addMessage(content, sender) {
const messageDiv = document.createElement('div');
messageDiv.className = `message ${sender}-message`;
const avatarDiv = document.createElement('div');
avatarDiv.className = `avatar ${sender}-avatar`;
avatarDiv.innerHTML = sender === 'user' ? '<i class="fas fa-user"></i>' : '<i class="fas fa-robot"></i>';
const contentDiv = document.createElement('div');
contentDiv.className = 'message-content';
// 检查内容是否包含代码块
const hasCode = content.includes('```');
if (hasCode) {
// 处理代码块
const parts = content.split('```');
let htmlContent = '';
for (let i = 0; i < parts.length; i++) {
if (i % 2 === 0) {
// 普通文本
htmlContent += `<p>${formatText(parts[i])}</p>`;
} else {
// 代码块
const codeParts = parts[i].split('\n');
const language = codeParts[0].trim() || 'javascript';
const code = codeParts.slice(1).join('\n').trim();
htmlContent += `
<div class="code-block">
<div class="code-header">
<span>${language}</span>
<button class="copy-btn"><i class="fas fa-copy"></i> 复制</button>
</div>
<pre><code class="language-${language}">${escapeHtml(code)}</code></pre>
</div>
`;
}
}
contentDiv.innerHTML = htmlContent;
} else {
// 没有代码块
contentDiv.innerHTML = `<p>${formatText(content)}</p>`;
}
messageDiv.appendChild(avatarDiv);
messageDiv.appendChild(contentDiv);
chatContainer.appendChild(messageDiv);
// 高亮代码块
if (hasCode) {
document.querySelectorAll('pre code').forEach((block) => {
hljs.highlightElement(block);
// 添加复制按钮功能
const copyBtn = block.parentElement.previousElementSibling.querySelector('.copy-btn');
if (copyBtn) {
copyBtn.addEventListener('click', function() {
const codeText = block.innerText;
navigator.clipboard.writeText(codeText).then(() => {
showNotification('代码已复制到剪贴板!');
});
});
}
});
}
// 滚动到底部
chatContainer.scrollTop = chatContainer.scrollHeight;
}
// 显示通知
function showNotification(message) {
notification.querySelector('span').textContent = message;
notification.classList.add('show');
setTimeout(() => {
notification.classList.remove('show');
}, 3000);
}
// 格式化文本(将换行符转换为<br>)
function formatText(text) {
return text.replace(/\n/g, '<br>');
}
// 转义HTML特殊字符
function escapeHtml(unsafe) {
return unsafe
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}
// 模拟AI响应
function simulateAIResponse(message) {
// 模拟延迟
setTimeout(() => {
// 隐藏"正在输入"指示器
typingIndicator.classList.remove('show');
// 根据用户问题生成模拟响应
let response = '';
if (message.toLowerCase().includes('hello') || message.toLowerCase().includes('你好')) {
response = "你好!我是AI助手,有什么我可以帮助您的吗?";
} else if (message.toLowerCase().includes('javascript') && message.toLowerCase().includes('排序')) {
response = `当然,这是一个用JavaScript实现的快速排序算法:
\`\`\`javascript
function quickSort(arr) {
if (arr.length <= 1) return arr;
const pivot = arr[0];
const left = [];
const right = [];
for (let i = 1; i < arr.length; i++) {
if (arr[i] < pivot) {
left.push(arr[i]);
} else {
right.push(arr[i]);
}
}
return [...quickSort(left), pivot, ...quickSort(right)];
}
// 示例用法
const unsortedArray = [3, 6, 8, 10, 1, 2, 1];
const sortedArray = quickSort(unsortedArray);
console.log(sortedArray); // 输出: [1, 1, 2, 3, 6, 8, 10]
\`\`\`
这个实现使用了递归和分治策略,时间复杂度为O(n log n)。`;
} else if (message.toLowerCase().includes('python') && message.toLowerCase().includes('csv')) {
response = `在Python中读取CSV文件可以使用内置的csv模块。以下是一个示例:
\`\`\`python
import csv
# 读取CSV文件
with open('data.csv', 'r', newline='', encoding='utf-8') as file:
reader = csv.reader(file)
header = next(reader) # 读取标题行
data = [row for row in reader]
# 打印数据
print("标题:", header)
for i, row in enumerate(data, 1):
print(f"行 {i}: {row}")
\`\`\`
对于更复杂的数据处理,也可以使用pandas库:
\`\`\`python
import pandas as pd
# 读取CSV文件
df = pd.read_csv('data.csv')
# 显示前5行
print(df.head())
\`\`\``;
} else if (message.toLowerCase().includes('flex')) {
response = `CSS Flex布局是一种一维布局模型,用于在容器中对项目进行排列和分配空间。以下是关键概念:
1. **Flex容器**:通过设置 \`display: flex;\` 的元素
2. **Flex项目**:Flex容器中的直接子元素
3. **主轴**:项目排列的主要方向(由 \`flex-direction\` 设置)
4. **交叉轴**:垂直于主轴的轴
常用容器属性:
- \`flex-direction\`: 设置主轴方向 (row, row-reverse, column, column-reverse)
- \`justify-content\`: 主轴对齐方式 (flex-start, center, flex-end, space-between, space-around)
- \`align-items\`: 交叉轴对齐方式 (stretch, flex-start, center, flex-end, baseline)
- \`flex-wrap\`: 是否换行 (nowrap, wrap, wrap-reverse)
示例:
\`\`\`css
.container {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
gap: 10px;
}
.item {
flex: 1; /* 项目均匀分配空间 */
}
\`\`\``;
} else {
// 通用响应
response = `感谢您的查询!基于您的问题:"${message}",我可以提供以下信息:
在${getModelName(currentModel)}模型下,我理解您需要帮助解决这个问题。虽然这是一个模拟响应,但在真实环境中,我会通过以下步骤帮助您:
1. 分析您的问题关键词:${extractKeywords(message)}
2. 搜索相关知识库
3. 生成结构化的回答
4. 提供代码示例(如需要)
如果您需要更具体的帮助,请尝试包含更多细节,例如:
- 您使用的编程语言
- 相关技术栈
- 期望的输出结果
- 您已经尝试过的解决方案
例如:"如何用JavaScript实现一个倒计时组件?"`;
}
addMessage(response, 'ai');
}, 1500);
}
// 提取关键词(简化版)
function extractKeywords(message) {
const words = message.split(' ');
return words.slice(0, 5).join(', ');
}
// 发送按钮点击事件
sendBtn.addEventListener('click', sendMessage);
// 输入框回车事件
messageInput.addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
sendMessage();
}
});
});
</script>
</body>
</html>
index.html
index.html