Files
pit-router/frontend/src/views/LoginView.vue
feifei.xu e651f21324 feat: Step 6 - 前端聊天界面 (v0.9.5)
- 创建 Vue.js 3 前端项目 (frontend/)
- 实现核心功能:
  - 登录页面 (LoginView)
  - 首页 - 机器人和会话列表 (HomeView)
  - 聊天页面 (ChatView)
  - 聊天侧边栏 (ChatSidebar)
  - 聊天窗口 (ChatWindow)
  - 机器人选择器 (BotSelector)
- 集成功能:
  - Socket.io WebSocket 连接
  - Pinia 状态管理
  - Axios API 客户端
  - JWT 认证
- 更新版本号到 0.9.5
2026-03-15 10:57:50 +08:00

167 lines
3.5 KiB
Vue

<script setup lang="ts">
import { ref } from 'vue'
import { useRouter } from 'vue-router'
import { useAuthStore } from '@/stores/auth'
const router = useRouter()
const authStore = useAuthStore()
const username = ref('')
const password = ref('')
const error = ref('')
async function handleLogin() {
error.value = ''
if (!username.value || !password.value) {
error.value = '请输入用户名和密码'
return
}
const success = await authStore.login(username.value, password.value)
if (success) {
router.push('/')
} else {
error.value = '登录失败,请检查用户名和密码'
}
}
</script>
<template>
<div class="login-page">
<div class="login-card">
<div class="logo">
<h1>🐕 智队中枢</h1>
<p>Personal Intelligent Team</p>
</div>
<form @submit.prevent="handleLogin" class="login-form">
<div class="form-group">
<label for="username">用户名</label>
<input
id="username"
v-model="username"
type="text"
placeholder="请输入用户名"
autocomplete="username"
/>
</div>
<div class="form-group">
<label for="password">密码</label>
<input
id="password"
v-model="password"
type="password"
placeholder="请输入密码"
autocomplete="current-password"
/>
</div>
<div v-if="error" class="error-message">
{{ error }}
</div>
<button type="submit" class="login-btn" :disabled="authStore.loading">
{{ authStore.loading ? '登录中...' : '登录' }}
</button>
</form>
</div>
</div>
</template>
<style scoped>
.login-page {
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background: var(--bg-secondary);
}
.login-card {
background: var(--bg-primary);
border-radius: var(--border-radius-lg);
padding: var(--spacing-xl);
width: 100%;
max-width: 400px;
box-shadow: var(--shadow-lg);
}
.logo {
text-align: center;
margin-bottom: var(--spacing-xl);
}
.logo h1 {
font-size: var(--font-size-xl);
color: var(--primary-color);
margin-bottom: var(--spacing-xs);
}
.logo p {
color: var(--text-secondary);
font-size: var(--font-size-sm);
}
.login-form {
display: flex;
flex-direction: column;
gap: var(--spacing-md);
}
.form-group {
display: flex;
flex-direction: column;
gap: var(--spacing-xs);
}
.form-group label {
font-size: var(--font-size-sm);
font-weight: 500;
color: var(--text-primary);
}
.form-group input {
padding: var(--spacing-sm) var(--spacing-md);
border: 1px solid var(--border-color);
border-radius: var(--border-radius);
font-size: var(--font-size-md);
background: var(--bg-primary);
color: var(--text-primary);
}
.form-group input:focus {
outline: none;
border-color: var(--primary-color);
}
.error-message {
color: var(--error-color);
font-size: var(--font-size-sm);
text-align: center;
}
.login-btn {
padding: var(--spacing-sm) var(--spacing-md);
background: var(--primary-color);
color: white;
border: none;
border-radius: var(--border-radius);
font-size: var(--font-size-md);
font-weight: 600;
cursor: pointer;
transition: background 0.2s;
}
.login-btn:hover:not(:disabled) {
background: var(--primary-dark);
}
.login-btn:disabled {
opacity: 0.6;
cursor: not-allowed;
}
</style>