Files
dialect-chat/frontend/index.html
hubian 376cdacf5e 初始化方言版AI对话助手
功能特点:
- 支持8种方言(普通话、四川话、粤语、上海话、客家话、闽南话、东北话、河南话)
- 用户注册登录系统(用户名、手机、邮箱可选、密码确认)
- 对话功能(文字输入、语音识别、文件图片上传)
- 多对话管理(新建、切换、删除)
- 移动端适配(响应式设计、触摸友好)

技术栈:
- Flask后端API
- 原生HTML/CSS/JS前端
- Web Speech API语音识别
- JWT用户认证
- 大模型: qwen3.5-4b

端口: 19002
2026-04-08 12:09:46 +08:00

217 lines
11 KiB
HTML

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>方言AI助手 - 登录</title>
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://cdn.jsdelivr.net/npm/remixicon@3.5.0/fonts/remixicon.css" rel="stylesheet">
<style>
* { -webkit-tap-highlight-color: transparent; }
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
.gradient-bg { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); }
</style>
</head>
<body class="bg-gray-100 min-h-screen">
<div class="min-h-screen flex flex-col">
<!-- 头部 -->
<div class="gradient-bg text-white p-6 pb-12">
<h1 class="text-2xl font-bold text-center">🗣️ 方言AI助手</h1>
<p class="text-center text-white/80 mt-2 text-sm">用家乡话聊天,更亲切</p>
</div>
<!-- 表单区域 -->
<div class="flex-1 -mt-6">
<div class="bg-white rounded-t-3xl shadow-lg p-6 max-w-md mx-auto">
<!-- Tab切换 -->
<div class="flex mb-6 bg-gray-100 rounded-lg p-1">
<button id="loginTab" onclick="showTab('login')"
class="flex-1 py-2 rounded-lg bg-white shadow text-gray-800 font-medium transition-all">
登录
</button>
<button id="registerTab" onclick="showTab('register')"
class="flex-1 py-2 rounded-lg text-gray-500 transition-all">
注册
</button>
</div>
<!-- 登录表单 -->
<form id="loginForm" class="space-y-4">
<div>
<label class="block text-sm text-gray-600 mb-1">用户名</label>
<div class="relative">
<i class="ri-user-line absolute left-3 top-3 text-gray-400"></i>
<input type="text" id="loginUsername" placeholder="请输入用户名"
class="w-full pl-10 pr-4 py-3 border border-gray-200 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent">
</div>
</div>
<div>
<label class="block text-sm text-gray-600 mb-1">密码</label>
<div class="relative">
<i class="ri-lock-line absolute left-3 top-3 text-gray-400"></i>
<input type="password" id="loginPassword" placeholder="请输入密码"
class="w-full pl-10 pr-4 py-3 border border-gray-200 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent">
</div>
</div>
<button type="submit"
class="w-full py-3 gradient-bg text-white rounded-lg font-medium hover:opacity-90 transition-opacity">
登录
</button>
</form>
<!-- 注册表单 -->
<form id="registerForm" class="space-y-4 hidden">
<div>
<label class="block text-sm text-gray-600 mb-1">用户名 <span class="text-red-500">*</span></label>
<div class="relative">
<i class="ri-user-line absolute left-3 top-3 text-gray-400"></i>
<input type="text" id="regUsername" placeholder="至少2个字符"
class="w-full pl-10 pr-4 py-3 border border-gray-200 rounded-lg focus:ring-2 focus:ring-purple-500">
</div>
</div>
<div>
<label class="block text-sm text-gray-600 mb-1">手机号 <span class="text-red-500">*</span></label>
<div class="relative">
<i class="ri-phone-line absolute left-3 top-3 text-gray-400"></i>
<input type="tel" id="regPhone" placeholder="请输入手机号"
class="w-full pl-10 pr-4 py-3 border border-gray-200 rounded-lg focus:ring-2 focus:ring-purple-500">
</div>
</div>
<div>
<label class="block text-sm text-gray-600 mb-1">邮箱 <span class="text-gray-400">(可选)</span></label>
<div class="relative">
<i class="ri-mail-line absolute left-3 top-3 text-gray-400"></i>
<input type="email" id="regEmail" placeholder="选填"
class="w-full pl-10 pr-4 py-3 border border-gray-200 rounded-lg focus:ring-2 focus:ring-purple-500">
</div>
</div>
<div>
<label class="block text-sm text-gray-600 mb-1">密码 <span class="text-red-500">*</span></label>
<div class="relative">
<i class="ri-lock-line absolute left-3 top-3 text-gray-400"></i>
<input type="password" id="regPassword" placeholder="至少6个字符"
class="w-full pl-10 pr-4 py-3 border border-gray-200 rounded-lg focus:ring-2 focus:ring-purple-500">
</div>
</div>
<div>
<label class="block text-sm text-gray-600 mb-1">确认密码 <span class="text-red-500">*</span></label>
<div class="relative">
<i class="ri-lock-2-line absolute left-3 top-3 text-gray-400"></i>
<input type="password" id="regConfirmPassword" placeholder="再次输入密码"
class="w-full pl-10 pr-4 py-3 border border-gray-200 rounded-lg focus:ring-2 focus:ring-purple-500">
</div>
</div>
<button type="submit"
class="w-full py-3 gradient-bg text-white rounded-lg font-medium hover:opacity-90">
注册
</button>
</form>
<!-- 错误提示 -->
<div id="errorMsg" class="mt-4 p-3 bg-red-50 text-red-500 rounded-lg text-sm hidden"></div>
</div>
</div>
<!-- 底部 -->
<div class="text-center p-4 text-gray-400 text-sm">
方言AI助手 © 2026
</div>
</div>
<script>
const API_URL = '';
let currentTab = 'login';
function showTab(tab) {
currentTab = tab;
document.getElementById('loginForm').classList.toggle('hidden', tab !== 'login');
document.getElementById('registerForm').classList.toggle('hidden', tab !== 'register');
document.getElementById('loginTab').classList.toggle('bg-white', tab === 'login');
document.getElementById('loginTab').classList.toggle('shadow', tab === 'login');
document.getElementById('loginTab').classList.toggle('text-gray-800', tab === 'login');
document.getElementById('loginTab').classList.toggle('text-gray-500', tab !== 'login');
document.getElementById('registerTab').classList.toggle('bg-white', tab === 'register');
document.getElementById('registerTab').classList.toggle('shadow', tab === 'register');
document.getElementById('registerTab').classList.toggle('text-gray-800', tab === 'register');
document.getElementById('registerTab').classList.toggle('text-gray-500', tab !== 'register');
document.getElementById('errorMsg').classList.add('hidden');
}
function showError(msg) {
const el = document.getElementById('errorMsg');
el.textContent = msg;
el.classList.remove('hidden');
}
// 登录
document.getElementById('loginForm').addEventListener('submit', async (e) => {
e.preventDefault();
const username = document.getElementById('loginUsername').value.trim();
const password = document.getElementById('loginPassword').value;
if (!username || !password) {
showError('请输入用户名和密码');
return;
}
try {
const res = await fetch('/api/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, password })
});
const data = await res.json();
if (data.success) {
localStorage.setItem('token', data.token);
localStorage.setItem('user', JSON.stringify(data.user));
window.location.href = '/chat';
} else {
showError(data.error);
}
} catch (err) {
showError('网络错误,请重试');
}
});
// 注册
document.getElementById('registerForm').addEventListener('submit', async (e) => {
e.preventDefault();
const username = document.getElementById('regUsername').value.trim();
const phone = document.getElementById('regPhone').value.trim();
const email = document.getElementById('regEmail').value.trim();
const password = document.getElementById('regPassword').value;
const confirm_password = document.getElementById('regConfirmPassword').value;
if (!username || !phone || !password || !confirm_password) {
showError('请填写必填项');
return;
}
try {
const res = await fetch('/api/register', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, phone, email, password, confirm_password })
});
const data = await res.json();
if (data.success) {
localStorage.setItem('token', data.token);
localStorage.setItem('user', JSON.stringify(data.user));
window.location.href = '/chat';
} else {
showError(data.error);
}
} catch (err) {
showError('网络错误,请重试');
}
});
// 检查是否已登录
if (localStorage.getItem('token')) {
window.location.href = '/chat';
}
</script>
</body>
</html>