frontend layout and infrastructure
This commit is contained in:
@@ -1,63 +1,55 @@
|
||||
<script setup>
|
||||
const router = useRouter()
|
||||
|
||||
const goToRegister = () => {
|
||||
router.push('/register')
|
||||
}
|
||||
|
||||
const goToLogin = () => {
|
||||
router.push('/login')
|
||||
}
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
ShoppingCartOutlined,
|
||||
ShoppingOutlined,
|
||||
DollarOutlined,
|
||||
CloudServerOutlined,
|
||||
} from '@ant-design/icons-vue'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="home-container">
|
||||
<div class="home-content">
|
||||
<h1 class="home-title">欢迎来到 DataHub</h1>
|
||||
<p class="home-description">数据管理平台</p>
|
||||
|
||||
<div class="home-actions">
|
||||
<a-button type="primary" size="large" @click="goToLogin"> 登录 </a-button>
|
||||
<a-button size="large" @click="goToRegister"> 注册 </a-button>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h2 class="text-xl font-semibold mb-4">Dashboard</h2>
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="6">
|
||||
<a-card :bordered="false">
|
||||
<a-statistic title="订单总数" value="--">
|
||||
<template #prefix>
|
||||
<ShoppingCartOutlined />
|
||||
</template>
|
||||
</a-statistic>
|
||||
</a-card>
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-card :bordered="false">
|
||||
<a-statistic title="产品总数" value="--">
|
||||
<template #prefix>
|
||||
<ShoppingOutlined />
|
||||
</template>
|
||||
</a-statistic>
|
||||
</a-card>
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-card :bordered="false">
|
||||
<a-statistic title="退款总数" value="--">
|
||||
<template #prefix>
|
||||
<DollarOutlined />
|
||||
</template>
|
||||
</a-statistic>
|
||||
</a-card>
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-card :bordered="false">
|
||||
<a-statistic title="队列消息" value="--">
|
||||
<template #prefix>
|
||||
<CloudServerOutlined />
|
||||
</template>
|
||||
</a-statistic>
|
||||
</a-card>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-card class="mt-4" title="欢迎使用 DataHub 数据管理平台">
|
||||
<p class="text-gray-500">请从左侧菜单选择功能模块开始使用。</p>
|
||||
</a-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.home-container {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
}
|
||||
|
||||
.home-content {
|
||||
text-align: center;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.home-title {
|
||||
font-size: 48px;
|
||||
font-weight: 700;
|
||||
margin: 0 0 16px 0;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.home-description {
|
||||
font-size: 20px;
|
||||
margin: 0 0 40px 0;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.home-actions {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.home-actions .ant-btn {
|
||||
min-width: 120px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,28 +1,29 @@
|
||||
<script setup>
|
||||
<script setup lang="ts">
|
||||
import Brand from '@/components/Brand.vue'
|
||||
import { UserOutlined, LockOutlined } from '@ant-design/icons-vue'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
|
||||
// 表单数据
|
||||
const formState = reactive({
|
||||
username: '',
|
||||
password: '',
|
||||
remember: true,
|
||||
})
|
||||
|
||||
// 表单规则
|
||||
const rules = {
|
||||
username: [
|
||||
{ required: true, message: '请输入用户名', trigger: 'blur' },
|
||||
{ type: 'string' as const, required: true, message: '请输入用户名', trigger: 'blur' as const },
|
||||
],
|
||||
password: [
|
||||
{ required: true, message: '请输入密码', trigger: 'blur' },
|
||||
{ type: 'string' as const, required: true, message: '请输入密码', trigger: 'blur' as const },
|
||||
],
|
||||
}
|
||||
|
||||
const formRef = ref()
|
||||
const loading = ref(false)
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const userStore = useUserStore()
|
||||
|
||||
// 提交登录
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
await formRef.value.validate()
|
||||
@@ -30,9 +31,7 @@ const handleSubmit = async () => {
|
||||
|
||||
const response = await fetch('/api/auth/login', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
username: formState.username,
|
||||
password: formState.password,
|
||||
@@ -42,21 +41,19 @@ const handleSubmit = async () => {
|
||||
const data = await response.json()
|
||||
|
||||
if (data.code === 0) {
|
||||
// 保存 token
|
||||
localStorage.setItem('access_token', data.data.access_token)
|
||||
localStorage.setItem('refresh_token', data.data.refresh_token)
|
||||
localStorage.setItem('user', JSON.stringify(data.data.user))
|
||||
userStore.setToken(data.data.access_token, data.data.refresh_token)
|
||||
userStore.setUser(data.data.user)
|
||||
|
||||
message.success('登录成功!')
|
||||
const redirect = (route.query.redirect as string) || '/'
|
||||
setTimeout(() => {
|
||||
router.push('/')
|
||||
router.push(redirect)
|
||||
}, 500)
|
||||
} else {
|
||||
message.error(data.message || '登录失败,请重试')
|
||||
}
|
||||
} catch (error) {
|
||||
if (error.errorFields) {
|
||||
// 表单验证错误
|
||||
} catch (error: unknown) {
|
||||
if (error && typeof error === 'object' && 'errorFields' in error) {
|
||||
return
|
||||
}
|
||||
console.error('登录错误:', error)
|
||||
@@ -66,7 +63,6 @@ const handleSubmit = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
// 跳转到注册页
|
||||
const goToRegister = () => {
|
||||
router.push('/register')
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<script setup>
|
||||
<script setup lang="ts">
|
||||
import Brand from '@/components/Brand.vue'
|
||||
import { UserOutlined, LockOutlined, MailOutlined } from '@ant-design/icons-vue'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
|
||||
// 表单数据
|
||||
const formState = reactive({
|
||||
username: '',
|
||||
email: '',
|
||||
@@ -9,30 +10,29 @@ const formState = reactive({
|
||||
confirmPassword: '',
|
||||
})
|
||||
|
||||
// 表单规则
|
||||
const rules = {
|
||||
username: [
|
||||
{ required: true, message: '请输入用户名', trigger: 'blur' },
|
||||
{ min: 3, max: 20, message: '用户名长度应为 3-20 个字符', trigger: 'blur' },
|
||||
{ type: 'string' as const, required: true, message: '请输入用户名', trigger: 'blur' as const },
|
||||
{ type: 'string' as const, min: 3, max: 20, message: '用户名长度应为 3-20 个字符', trigger: 'blur' as const },
|
||||
],
|
||||
email: [
|
||||
{ required: true, message: '请输入邮箱', trigger: 'blur' },
|
||||
{ type: 'email', message: '请输入有效的邮箱地址', trigger: 'blur' },
|
||||
{ type: 'string' as const, required: true, message: '请输入邮箱', trigger: 'blur' as const },
|
||||
{ type: 'email' as const, message: '请输入有效的邮箱地址', trigger: 'blur' as const },
|
||||
],
|
||||
password: [
|
||||
{ required: true, message: '请输入密码', trigger: 'blur' },
|
||||
{ min: 6, message: '密码长度至少为 6 个字符', trigger: 'blur' },
|
||||
{ type: 'string' as const, required: true, message: '请输入密码', trigger: 'blur' as const },
|
||||
{ type: 'string' as const, min: 6, message: '密码长度至少为 6 个字符', trigger: 'blur' as const },
|
||||
],
|
||||
confirmPassword: [
|
||||
{ required: true, message: '请确认密码', trigger: 'blur' },
|
||||
{ type: 'string' as const, required: true, message: '请确认密码', trigger: 'blur' as const },
|
||||
{
|
||||
validator: (rule, value) => {
|
||||
validator: (_rule: unknown, value: string) => {
|
||||
if (value !== formState.password) {
|
||||
return Promise.reject('两次输入的密码不一致')
|
||||
}
|
||||
return Promise.resolve()
|
||||
},
|
||||
trigger: 'blur',
|
||||
trigger: 'blur' as const,
|
||||
},
|
||||
],
|
||||
}
|
||||
@@ -40,8 +40,8 @@ const rules = {
|
||||
const formRef = ref()
|
||||
const loading = ref(false)
|
||||
const router = useRouter()
|
||||
const userStore = useUserStore()
|
||||
|
||||
// 提交注册
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
await formRef.value.validate()
|
||||
@@ -49,9 +49,7 @@ const handleSubmit = async () => {
|
||||
|
||||
const response = await fetch('/api/auth/register', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
username: formState.username,
|
||||
email: formState.email,
|
||||
@@ -62,6 +60,14 @@ const handleSubmit = async () => {
|
||||
const data = await response.json()
|
||||
|
||||
if (data.code === 0) {
|
||||
// 注册成功后自动设置 token(如果后端返回)
|
||||
if (data.data?.access_token) {
|
||||
userStore.setToken(data.data.access_token, data.data.refresh_token)
|
||||
if (data.data.user) {
|
||||
userStore.setUser(data.data.user)
|
||||
}
|
||||
}
|
||||
|
||||
message.success('注册成功!即将跳转到登录页...')
|
||||
setTimeout(() => {
|
||||
router.push('/login')
|
||||
@@ -69,9 +75,8 @@ const handleSubmit = async () => {
|
||||
} else {
|
||||
message.error(data.message || '注册失败,请重试')
|
||||
}
|
||||
} catch (error) {
|
||||
if (error.errorFields) {
|
||||
// 表单验证错误
|
||||
} catch (error: unknown) {
|
||||
if (error && typeof error === 'object' && 'errorFields' in error) {
|
||||
return
|
||||
}
|
||||
console.error('注册错误:', error)
|
||||
@@ -81,7 +86,6 @@ const handleSubmit = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
// 跳转到登录页
|
||||
const goToLogin = () => {
|
||||
router.push('/login')
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user