工具助手添加
This commit is contained in:
@@ -0,0 +1,220 @@
|
||||
/* AI助手Web客户端样式 - 100%还原原型设计 */
|
||||
|
||||
@import url('https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css');
|
||||
@import url('https://fonts.googleapis.com/css2?family=Pacifico&family=Inter:wght@300;400;500;600;700&display=swap');
|
||||
|
||||
/* 全局样式 */
|
||||
body {
|
||||
min-height: 1024px;
|
||||
background: linear-gradient(135deg, #0f172a 0%, #1e293b 50%, #0f172a 100%);
|
||||
overflow: hidden;
|
||||
font-family: 'Inter', sans-serif;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* 玻璃效果 */
|
||||
.glass-effect {
|
||||
background: rgba(30, 41, 59, 0.3);
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid rgba(100, 100, 100, 0.2);
|
||||
}
|
||||
|
||||
/* 霓虹边框 */
|
||||
.neon-border {
|
||||
box-shadow: 0 0 10px rgba(99, 102, 241, 0.5), 0 0 20px rgba(99, 102, 241, 0.3);
|
||||
}
|
||||
|
||||
/* 用户消息气泡 */
|
||||
.user-bubble {
|
||||
background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);
|
||||
box-shadow: 0 0 15px rgba(99, 102, 241, 0.4);
|
||||
}
|
||||
|
||||
/* AI消息气泡 */
|
||||
.ai-bubble {
|
||||
background: linear-gradient(135deg, #06b6d4 0%, #10b981 100%);
|
||||
box-shadow: 0 0 15px rgba(6, 182, 212, 0.4);
|
||||
}
|
||||
|
||||
/* 脉冲动画 */
|
||||
.pulse {
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0% { box-shadow: 0 0 0 0 rgba(99, 102, 241, 0.7); }
|
||||
70% { box-shadow: 0 0 0 10px rgba(99, 102, 241, 0); }
|
||||
100% { box-shadow: 0 0 0 0 rgba(99, 102, 241, 0); }
|
||||
}
|
||||
|
||||
/* 发光按钮 */
|
||||
.glow-button {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.glow-button:hover {
|
||||
box-shadow: 0 0 15px rgba(99, 102, 241, 0.8);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
/* 粒子背景 */
|
||||
.particles {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: -1;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.particle {
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
background: rgba(99, 102, 241, 0.3);
|
||||
animation: float 6s infinite ease-in-out;
|
||||
}
|
||||
|
||||
@keyframes float {
|
||||
0%, 100% { transform: translateY(0) translateX(0); opacity: 0.3; }
|
||||
50% { transform: translateY(-20px) translateX(10px); opacity: 0.8; }
|
||||
}
|
||||
|
||||
/* 打字指示器 */
|
||||
.typing-indicator {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.typing-dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background: #10b981;
|
||||
border-radius: 50%;
|
||||
margin: 0 2px;
|
||||
animation: typing 1.4s infinite ease-in-out;
|
||||
}
|
||||
|
||||
.typing-dot:nth-child(2) { animation-delay: 0.2s; }
|
||||
.typing-dot:nth-child(3) { animation-delay: 0.4s; }
|
||||
|
||||
@keyframes typing {
|
||||
0%, 60%, 100% { transform: translateY(0); }
|
||||
30% { transform: translateY(-5px); }
|
||||
}
|
||||
|
||||
/* 滑入动画 */
|
||||
.slide-in {
|
||||
animation: slideIn 0.3s ease-out;
|
||||
}
|
||||
|
||||
@keyframes slideIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* 淡入动画 */
|
||||
.fade-in {
|
||||
animation: fadeIn 0.5s ease-in;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
/* 通知徽章 */
|
||||
.notification-badge {
|
||||
position: absolute;
|
||||
top: -5px;
|
||||
right: -5px;
|
||||
background: #ef4444;
|
||||
color: white;
|
||||
border-radius: 50%;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 12px;
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
|
||||
/* 语音波形 */
|
||||
.voice-wave {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.wave-bar {
|
||||
width: 2px;
|
||||
height: 10px;
|
||||
background: #6366f1;
|
||||
margin: 0 1px;
|
||||
animation: wave 1.2s infinite ease-in-out;
|
||||
}
|
||||
|
||||
.wave-bar:nth-child(2) { animation-delay: 0.2s; }
|
||||
.wave-bar:nth-child(3) { animation-delay: 0.4s; }
|
||||
.wave-bar:nth-child(4) { animation-delay: 0.6s; }
|
||||
.wave-bar:nth-child(5) { animation-delay: 0.8s; }
|
||||
|
||||
@keyframes wave {
|
||||
0%, 100% { height: 10px; }
|
||||
50% { height: 20px; }
|
||||
}
|
||||
|
||||
/* 滚动条样式 */
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: rgba(30, 41, 59, 0.3);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: rgba(99, 102, 241, 0.5);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: rgba(99, 102, 241, 0.7);
|
||||
}
|
||||
|
||||
/* 主题颜色变量 */
|
||||
:root {
|
||||
--primary: #6366f1;
|
||||
--secondary: #8b5cf6;
|
||||
--bg-dark: #0f172a;
|
||||
--bg-slate: #1e293b;
|
||||
--text-white: #ffffff;
|
||||
--text-slate: #94a3b8;
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 768px) {
|
||||
.user-bubble, .ai-bubble {
|
||||
max-width: 80% !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* 隐藏元素 */
|
||||
.hidden {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* 加载动画 */
|
||||
.loading {
|
||||
opacity: 0.6;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,324 @@
|
||||
/**
|
||||
* AI助手Web客户端 - 前端交互逻辑
|
||||
* 100%还原原型设计的交互效果
|
||||
*/
|
||||
|
||||
// 全局变量
|
||||
let currentChatId = null;
|
||||
let currentAppId = 15; // 默认使用人岗匹配应用
|
||||
let isTyping = false;
|
||||
|
||||
// API配置
|
||||
const API_BASE_URL = ''; // 使用相对路径
|
||||
|
||||
/**
|
||||
* 创建动态粒子背景
|
||||
*/
|
||||
function createParticles() {
|
||||
const particlesContainer = document.getElementById('particles');
|
||||
const particleCount = 50;
|
||||
|
||||
for (let i = 0; i < particleCount; i++) {
|
||||
const particle = document.createElement('div');
|
||||
particle.className = 'particle';
|
||||
|
||||
// 随机位置和大小
|
||||
const size = Math.random() * 4 + 1;
|
||||
const posX = Math.random() * 100;
|
||||
const posY = Math.random() * 100;
|
||||
|
||||
particle.style.width = `${size}px`;
|
||||
particle.style.height = `${size}px`;
|
||||
particle.style.left = `${posX}%`;
|
||||
particle.style.top = `${posY}%`;
|
||||
|
||||
// 随机动画延迟
|
||||
particle.style.animationDelay = `${Math.random() * 5}s`;
|
||||
|
||||
particlesContainer.appendChild(particle);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加消息到聊天容器
|
||||
*/
|
||||
function addMessage(message, isUser = true) {
|
||||
const chatContainer = document.getElementById('chat-container');
|
||||
|
||||
// 移除打字指示器
|
||||
removeTypingIndicator();
|
||||
|
||||
const messageDiv = document.createElement('div');
|
||||
messageDiv.className = `flex ${isUser ? 'justify-end' : 'justify-start'} slide-in`;
|
||||
|
||||
const bubbleClass = isUser ? 'user-bubble' : 'ai-bubble';
|
||||
const roundedClass = isUser ? 'rounded-br-md' : 'rounded-bl-md';
|
||||
|
||||
messageDiv.innerHTML = `
|
||||
<div class="max-w-xs lg:max-w-md ${bubbleClass} text-white p-4 rounded-2xl ${roundedClass}">
|
||||
<p>${escapeHtml(message)}</p>
|
||||
</div>
|
||||
`;
|
||||
|
||||
chatContainer.appendChild(messageDiv);
|
||||
scrollToBottom();
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示打字指示器
|
||||
*/
|
||||
function showTypingIndicator() {
|
||||
if (isTyping) return;
|
||||
|
||||
isTyping = true;
|
||||
const chatContainer = document.getElementById('chat-container');
|
||||
|
||||
const typingDiv = document.createElement('div');
|
||||
typingDiv.id = 'typing-indicator';
|
||||
typingDiv.className = 'flex justify-start';
|
||||
typingDiv.innerHTML = `
|
||||
<div class="max-w-xs lg:max-w-md ai-bubble text-white p-4 rounded-2xl rounded-bl-md">
|
||||
<div class="typing-indicator">
|
||||
<div class="typing-dot"></div>
|
||||
<div class="typing-dot"></div>
|
||||
<div class="typing-dot"></div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
chatContainer.appendChild(typingDiv);
|
||||
scrollToBottom();
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除打字指示器
|
||||
*/
|
||||
function removeTypingIndicator() {
|
||||
const typingIndicator = document.getElementById('typing-indicator');
|
||||
if (typingIndicator) {
|
||||
typingIndicator.remove();
|
||||
isTyping = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 滚动到底部
|
||||
*/
|
||||
function scrollToBottom() {
|
||||
const chatContainer = document.getElementById('chat-container');
|
||||
chatContainer.scrollTop = chatContainer.scrollHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* HTML转义
|
||||
*/
|
||||
function escapeHtml(text) {
|
||||
const div = document.createElement('div');
|
||||
div.textContent = text;
|
||||
return div.innerHTML;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送消息
|
||||
*/
|
||||
async function sendMessage() {
|
||||
const input = document.getElementById('message-input');
|
||||
const message = input.value.trim();
|
||||
|
||||
if (!message) return;
|
||||
|
||||
// 添加用户消息
|
||||
addMessage(message, true);
|
||||
input.value = '';
|
||||
|
||||
// 显示打字指示器
|
||||
showTypingIndicator();
|
||||
|
||||
try {
|
||||
// 调用API
|
||||
const response = await fetch(`${API_BASE_URL}/api/chat/send`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
appId: currentAppId,
|
||||
message: message,
|
||||
chatId: currentChatId,
|
||||
stream: false
|
||||
})
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
// 移除打字指示器
|
||||
removeTypingIndicator();
|
||||
|
||||
if (data.code === 200 && data.data) {
|
||||
// 添加AI回复
|
||||
addMessage(data.data.answer, false);
|
||||
|
||||
// 更新chatId
|
||||
if (data.data.chatId) {
|
||||
currentChatId = data.data.chatId;
|
||||
}
|
||||
} else {
|
||||
addMessage(`错误: ${data.message || '未知错误'}`, false);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('发送消息失败:', error);
|
||||
removeTypingIndicator();
|
||||
addMessage('抱歉,发送消息失败,请稍后重试。', false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 快捷回复按钮点击
|
||||
*/
|
||||
function handleQuickReply(text) {
|
||||
const input = document.getElementById('message-input');
|
||||
input.value = text;
|
||||
input.focus();
|
||||
}
|
||||
|
||||
/**
|
||||
* 主题切换
|
||||
*/
|
||||
function changeTheme(theme) {
|
||||
const body = document.body;
|
||||
|
||||
// 移除所有主题类
|
||||
body.classList.remove('theme-dark', 'theme-blue', 'theme-purple', 'theme-green');
|
||||
|
||||
// 添加新主题类
|
||||
body.classList.add(`theme-${theme}`);
|
||||
|
||||
// 更新背景渐变
|
||||
const gradients = {
|
||||
dark: 'linear-gradient(135deg, #0f172a 0%, #1e293b 50%, #0f172a 100%)',
|
||||
blue: 'linear-gradient(135deg, #0c4a6e 0%, #075985 50%, #0c4a6e 100%)',
|
||||
purple: 'linear-gradient(135deg, #581c87 0%, #6b21a8 50%, #581c87 100%)',
|
||||
green: 'linear-gradient(135deg, #064e3b 0%, #065f46 50%, #064e3b 100%)'
|
||||
};
|
||||
|
||||
body.style.background = gradients[theme] || gradients.dark;
|
||||
}
|
||||
|
||||
/**
|
||||
* 字体切换
|
||||
*/
|
||||
function changeFont(font) {
|
||||
const chatContainer = document.getElementById('chat-container');
|
||||
chatContainer.style.fontFamily = font;
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始新会话
|
||||
*/
|
||||
function startNewChat() {
|
||||
currentChatId = null;
|
||||
const chatContainer = document.getElementById('chat-container');
|
||||
|
||||
// 清空聊天记录(保留欢迎消息)
|
||||
const messages = chatContainer.querySelectorAll('.slide-in');
|
||||
messages.forEach(msg => msg.remove());
|
||||
|
||||
// 添加欢迎消息
|
||||
addMessage('您好!我是AI助手,有什么可以帮助您的吗?', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 待办事项管理
|
||||
*/
|
||||
function addTodoItem() {
|
||||
const input = document.getElementById('todo-input');
|
||||
const text = input.value.trim();
|
||||
|
||||
if (!text) return;
|
||||
|
||||
const todoList = document.getElementById('todo-list');
|
||||
const todoItem = document.createElement('div');
|
||||
todoItem.className = 'flex items-center space-x-2 p-2 bg-slate-800/30 rounded-lg';
|
||||
todoItem.innerHTML = `
|
||||
<input type="checkbox" class="rounded text-primary">
|
||||
<span class="text-slate-300 text-sm">${escapeHtml(text)}</span>
|
||||
`;
|
||||
|
||||
todoList.appendChild(todoItem);
|
||||
input.value = '';
|
||||
}
|
||||
|
||||
/**
|
||||
* 页面加载完成后初始化
|
||||
*/
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
console.log('AI助手Web客户端已加载');
|
||||
|
||||
// 创建粒子背景
|
||||
createParticles();
|
||||
|
||||
// 绑定发送按钮事件
|
||||
const sendButton = document.getElementById('send-button');
|
||||
if (sendButton) {
|
||||
sendButton.addEventListener('click', sendMessage);
|
||||
}
|
||||
|
||||
// 绑定输入框回车事件
|
||||
const messageInput = document.getElementById('message-input');
|
||||
if (messageInput) {
|
||||
messageInput.addEventListener('keypress', function(e) {
|
||||
if (e.key === 'Enter' && !e.shiftKey) {
|
||||
e.preventDefault();
|
||||
sendMessage();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 绑定快捷回复按钮
|
||||
const quickReplyButtons = document.querySelectorAll('.quick-reply-btn');
|
||||
quickReplyButtons.forEach(btn => {
|
||||
btn.addEventListener('click', function() {
|
||||
const text = this.textContent.trim();
|
||||
// 移除图标文本
|
||||
const cleanText = text.replace(/[💡📅✅]/g, '').trim();
|
||||
handleQuickReply(cleanText);
|
||||
});
|
||||
});
|
||||
|
||||
// 绑定主题切换按钮
|
||||
const themeButtons = document.querySelectorAll('.theme-btn');
|
||||
themeButtons.forEach(btn => {
|
||||
btn.addEventListener('click', function() {
|
||||
const theme = this.dataset.theme;
|
||||
changeTheme(theme);
|
||||
});
|
||||
});
|
||||
|
||||
// 绑定字体选择
|
||||
const fontSelect = document.getElementById('font-select');
|
||||
if (fontSelect) {
|
||||
fontSelect.addEventListener('change', function() {
|
||||
changeFont(this.value);
|
||||
});
|
||||
}
|
||||
|
||||
// 绑定待办事项添加
|
||||
const todoAddButton = document.getElementById('todo-add-btn');
|
||||
const todoInput = document.getElementById('todo-input');
|
||||
if (todoAddButton && todoInput) {
|
||||
todoAddButton.addEventListener('click', addTodoItem);
|
||||
todoInput.addEventListener('keypress', function(e) {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
addTodoItem();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 添加欢迎消息
|
||||
setTimeout(() => {
|
||||
addMessage('您好!我是AI助手,有什么可以帮助您的吗?', false);
|
||||
}, 500);
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user