feat: 登录页面
This commit is contained in:
98
src/App.vue
98
src/App.vue
@@ -1,81 +1,29 @@
|
||||
<script setup lang="ts">
|
||||
import { RouterLink, RouterView } from 'vue-router'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<header>
|
||||
<img alt="Vue logo" class="logo" src="@/assets/logo.svg" width="125" height="125" />
|
||||
|
||||
<div class="wrapper bg-amber-400">
|
||||
<nav>
|
||||
<RouterLink to="/">Home</RouterLink>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<RouterView />
|
||||
<NConfigProvider
|
||||
:locale="zhCN"
|
||||
:date-locale="dateZhCN"
|
||||
:theme="lightTheme"
|
||||
:theme-overrides="themeOverrides"
|
||||
class="h-full"
|
||||
>
|
||||
<NNotificationProvider>
|
||||
<NMessageProvider>
|
||||
<router-view />
|
||||
</NMessageProvider>
|
||||
</NNotificationProvider>
|
||||
</NConfigProvider>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
header {
|
||||
line-height: 1.5;
|
||||
max-height: 100vh;
|
||||
}
|
||||
<script setup lang="ts">
|
||||
import { RouterView } from 'vue-router'
|
||||
import { zhCN, dateZhCN, lightTheme, useThemeVars, type GlobalThemeOverrides } from 'naive-ui'
|
||||
|
||||
.logo {
|
||||
display: block;
|
||||
margin: 0 auto 2rem;
|
||||
}
|
||||
|
||||
nav {
|
||||
width: 100%;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
nav a.router-link-exact-active {
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
nav a.router-link-exact-active:hover {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
nav a {
|
||||
display: inline-block;
|
||||
padding: 0 1rem;
|
||||
border-left: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
nav a:first-of-type {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
header {
|
||||
display: flex;
|
||||
place-items: center;
|
||||
padding-right: calc(var(--section-gap) / 2);
|
||||
}
|
||||
|
||||
.logo {
|
||||
margin: 0 2rem 0 0;
|
||||
}
|
||||
|
||||
header .wrapper {
|
||||
display: flex;
|
||||
place-items: flex-start;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
nav {
|
||||
text-align: left;
|
||||
margin-left: -1rem;
|
||||
font-size: 1rem;
|
||||
|
||||
padding: 1rem 0;
|
||||
margin-top: 1rem;
|
||||
const themeOverrides: GlobalThemeOverrides = {
|
||||
common: {
|
||||
primaryColor: '#2080F0',
|
||||
primaryColorHover: '#4098fc',
|
||||
primaryColorPressed: '#1060c9',
|
||||
primaryColorSuppl: '#4098fc'
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</script>
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
@import 'tailwindcss';
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
2
src/assets/style/index.ts
Normal file
2
src/assets/style/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
import './tailwind.css'
|
||||
import './base.css'
|
||||
14
src/assets/style/tailwind.css
Normal file
14
src/assets/style/tailwind.css
Normal file
@@ -0,0 +1,14 @@
|
||||
@import 'tailwindcss';
|
||||
|
||||
@utility flex-center {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
@utility flex-col-center {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
@@ -1,9 +1,43 @@
|
||||
<template>
|
||||
<div class="flex min-h-lvh flex-1 overflow-x-hidden select-none">
|
||||
<router-view></router-view>
|
||||
<div class="relative flex-center w-full">
|
||||
<div class="login-background absolute top-0 left-0 size-full"></div>
|
||||
<div
|
||||
class="flex-col-center relative bg-white rounded-3xl pb-10 px-6 py-10 md:w-2/3 lg:w-1/2 xl:w-[36%]"
|
||||
>
|
||||
<RouterView v-slot="{ Component, route }">
|
||||
<Transition appear mode="out-in" name="slide-right">
|
||||
<KeepAlive :include="['Login']">
|
||||
<component
|
||||
:is="Component"
|
||||
:key="route.fullPath"
|
||||
class="side-content mt-6 w-full sm:mx-auto md:max-w-md"
|
||||
/>
|
||||
</KeepAlive>
|
||||
</Transition>
|
||||
</RouterView>
|
||||
<div class="absolute bottom-3 flex text-center text-xs">
|
||||
<span style="color: #71717a">Copyright © 2024</span>
|
||||
<a href="javascript:void(0)" class="ml-2" style="color: #2080f0">MarchGlow</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts"></script>
|
||||
<script setup lang="ts">
|
||||
import { useThemeVars } from 'naive-ui'
|
||||
const themeVars = useThemeVars()
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
<style scoped>
|
||||
.login-background {
|
||||
background: linear-gradient(
|
||||
154deg,
|
||||
#07070915 30%,
|
||||
color-mix(in srgb, v-bind(themeVars.primaryColor) 30%, transparent) 48%,
|
||||
#07070915 64%
|
||||
);
|
||||
filter: blur(100px);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import './assets/style/base.css'
|
||||
import './assets/style'
|
||||
|
||||
import { createApp } from 'vue'
|
||||
import { initPinia } from './stores'
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
import { ref, computed } from 'vue'
|
||||
import { defineStore } from 'pinia'
|
||||
|
||||
export const useCounterStore = defineStore('counter', () => {
|
||||
const count = ref(0)
|
||||
const doubleCount = computed(() => count.value * 2)
|
||||
function increment() {
|
||||
count.value++
|
||||
}
|
||||
|
||||
return { count, doubleCount, increment }
|
||||
})
|
||||
@@ -1,2 +1,2 @@
|
||||
export * from './initPinia.ts'
|
||||
export * from './counter.ts'
|
||||
export * from './modules'
|
||||
|
||||
37
src/stores/modules/account.ts
Normal file
37
src/stores/modules/account.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref } from 'vue'
|
||||
|
||||
/** 账户信息 */
|
||||
export interface AccountState {
|
||||
/** 账户ID */
|
||||
id: string
|
||||
/** 用户名 */
|
||||
username: string
|
||||
/** 年龄 */
|
||||
age?: number
|
||||
/** 性别 */
|
||||
sex?: string
|
||||
/** 手机号 */
|
||||
phone?: string
|
||||
/** 邮箱 */
|
||||
email?: string
|
||||
/** 头像 */
|
||||
avatar?: string
|
||||
/** 生日 */
|
||||
birthday?: string
|
||||
}
|
||||
|
||||
export const useAccountStore = defineStore(
|
||||
'account',
|
||||
() => {
|
||||
const accountInfo = ref<AccountState>()
|
||||
const token = ref('')
|
||||
|
||||
return { accountInfo, token }
|
||||
},
|
||||
{
|
||||
persist: {
|
||||
pick: ['accountInfo', 'token']
|
||||
}
|
||||
}
|
||||
)
|
||||
1
src/stores/modules/index.ts
Normal file
1
src/stores/modules/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './account.ts'
|
||||
@@ -1,7 +1,94 @@
|
||||
<template>
|
||||
<div class="h-40 w-40 bg-amber-200"></div>
|
||||
<div class="flex-col-center">
|
||||
<div class="w-full">
|
||||
<h2 class="text-3xl/9 mb-3 font-bold lg:text-4xl">欢迎回来 👋🏻</h2>
|
||||
<p>
|
||||
<span style="color: #71717a">登录账号以进入对战</span>
|
||||
</p>
|
||||
</div>
|
||||
<n-form
|
||||
ref="formRef"
|
||||
:model="loginModel"
|
||||
:rules="rules"
|
||||
:show-label="false"
|
||||
class="w-full mt-10"
|
||||
>
|
||||
<n-form-item path="username">
|
||||
<n-input v-model:value="loginModel.username" placeholder="请输入账号" :maxlength="20" />
|
||||
</n-form-item>
|
||||
<n-form-item path="password">
|
||||
<n-input
|
||||
v-model:value="loginModel.password"
|
||||
type="password"
|
||||
show-password-on="mousedown"
|
||||
placeholder="密码"
|
||||
:maxlength="20"
|
||||
/>
|
||||
</n-form-item>
|
||||
<n-form-item>
|
||||
<div class="flex flex-1 items-center justify-between">
|
||||
<n-checkbox>记住账号</n-checkbox>
|
||||
<a href="javascript:void(0)" style="color: #2080f0">忘记密码?</a>
|
||||
</div>
|
||||
</n-form-item>
|
||||
<n-form-item>
|
||||
<n-button :loading="loginLoading" style="width: 100%" type="primary" @click="userLoginFun"
|
||||
>登录</n-button
|
||||
>
|
||||
</n-form-item>
|
||||
<n-form-item>
|
||||
<div class="flex-center flex-1">
|
||||
<span>没有账号?</span>
|
||||
<router-link to="/auth/register" style="color: #2080f0">注册</router-link>
|
||||
</div>
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts"></script>
|
||||
<script setup lang="ts">
|
||||
import { reactive, ref, useTemplateRef } from 'vue'
|
||||
import type { FormItemRule, FormRules, FormInst } from 'naive-ui'
|
||||
import { useAccountStore } from '@/stores'
|
||||
|
||||
const accountStore = useAccountStore()
|
||||
const formRef = useTemplateRef<FormInst>('formRef')
|
||||
const loginModel = reactive({
|
||||
username: '',
|
||||
password: ''
|
||||
})
|
||||
const loginLoading = ref(false)
|
||||
|
||||
const rules: FormRules = {
|
||||
username: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入登陆账号',
|
||||
trigger: ['blur']
|
||||
}
|
||||
],
|
||||
password: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入登陆密码',
|
||||
trigger: ['blur']
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
const userLoginFun = () => {
|
||||
formRef.value?.validate((errors) => {
|
||||
if (!errors) {
|
||||
loginLoading.value = true
|
||||
console.log('登录成功')
|
||||
// TODO 登录方法
|
||||
accountStore.token = '123456'
|
||||
setTimeout(() => {
|
||||
loginLoading.value = false
|
||||
}, 1000)
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
|
||||
Reference in New Issue
Block a user