创建登录页面。

main
ZhouXY108 2022-12-29 00:37:02 +08:00
parent 6d9fb30bbd
commit d52055a0ae
6 changed files with 222 additions and 9 deletions

View File

@ -1,11 +1,11 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="zh-CN">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<link rel="icon" href="/favicon.ico"> <link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vite App</title> <title>Plusone Admin</title>
</head> </head>
<body> <body>

View File

@ -3,7 +3,7 @@
"version": "0.0.0", "version": "0.0.0",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "vite", "dev": "vite --host",
"build": "run-p type-check build-only", "build": "run-p type-check build-only",
"preview": "vite preview", "preview": "vite preview",
"build-only": "vite build", "build-only": "vite build",

View File

@ -1,7 +1,28 @@
<template> <template>
<RouterView /> <n-config-provider :theme-overrides="themeOverrides">
<n-message-provider>
<router-view />
</n-message-provider>
</n-config-provider>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { RouterView } from 'vue-router' import { NConfigProvider, type GlobalThemeOverrides } from 'naive-ui'
const themeOverrides: GlobalThemeOverrides = {
common: {
primaryColor: '#42b883',
borderRadius: '4px',
},
// Button: {
// textColor: '#FF0000',
// },
// Select: {
// peers: {
// InternalSelection: {
// textColor: '#FF0000',
// },
// },
// },
}
</script> </script>

View File

@ -18,6 +18,13 @@ import {
NLayoutSider, NLayoutSider,
NMenu, NMenu,
NScrollbar, NScrollbar,
NForm,
NFormItem,
NInput,
NRow,
NCol,
NMessageProvider,
NCard,
} from 'naive-ui' } from 'naive-ui'
const naive = create({ const naive = create({
@ -30,6 +37,13 @@ const naive = create({
NLayoutSider, NLayoutSider,
NMenu, NMenu,
NScrollbar, NScrollbar,
NForm,
NFormItem,
NInput,
NRow,
NCol,
NMessageProvider,
NCard,
], ],
}) })

View File

@ -7,8 +7,7 @@ const router = createRouter({
routes: [ routes: [
{ {
path: '/', path: '/',
name: 'home', redirect: '/login',
component: HomeView,
}, },
{ {
path: '/login', path: '/login',

View File

@ -1,5 +1,184 @@
<template> <template>
<h1>This is login page</h1> <div class="login-page">
<div class="login-form-container">
<div class="title">
<span style="color: #42b883">Plusone Admin&nbsp;</span>
<span>登录</span>
</div>
<n-form ref="formRef" :model="model" :rules="rules">
<n-form-item path="principal" :show-label="false">
<n-input
placeholder="用户名 / 邮箱地址 / 手机号"
v-model:value="model.principal"
@keydown.enter.prevent
/>
</n-form-item>
<n-form-item path="password" :show-label="false">
<n-input
placeholder="密码"
v-model:value="model.password"
type="password"
@input="handlePasswordInput"
@keydown.enter.prevent
/>
</n-form-item>
<n-form-item ref="rPasswordFormItemRef" first path="reenteredPassword" :show-label="false">
<n-input
placeholder="重复密码"
v-model:value="model.reenteredPassword"
:disabled="!model.password"
type="password"
@keydown.enter.prevent
/>
</n-form-item>
<n-row :gutter="[0, 24]">
<n-col :span="24">
<div style="display: flex; justify-content: flex-end">
<n-button
:disabled="model.principal === null"
round
type="primary"
@click="handleBtnLoginClick"
>
登录
</n-button>
</div>
</n-col>
</n-row>
</n-form>
</div>
</div>
<pre>{{ JSON.stringify(model, null, 2) }}</pre>
</template> </template>
<script lang="ts" setup></script> <script lang="ts">
import { defineComponent, ref } from 'vue'
import type { FormInst, FormItemInst, FormItemRule, FormRules } from 'naive-ui'
import { useMessage } from 'naive-ui'
interface LoginByPasswordCommand {
principal: string
password: string
reenteredPassword: string
}
export default defineComponent({
setup() {
const formRef = ref<FormInst | null>(null)
const rPasswordFormItemRef = ref<FormItemInst | null>(null)
const message = useMessage()
const modelRef = ref<LoginByPasswordCommand>({
principal: '',
password: '',
reenteredPassword: '',
})
function validatePasswordStartWith(rule: FormItemRule, value: string): boolean {
return (
!!modelRef.value.password &&
modelRef.value.password.startsWith(value) &&
modelRef.value.password.length >= value.length
)
}
function validatePasswordSame(rule: FormItemRule, value: string): boolean {
return value === modelRef.value.password
}
const rules: FormRules = {
age: [
{
required: true,
validator(rule: FormItemRule, value: string) {
if (!value) {
return new Error('需要年龄')
} else if (!/^\d*$/.test(value)) {
return new Error('年龄应该为整数')
} else if (Number(value) < 18) {
return new Error('年龄应该超过十八岁')
}
return true
},
trigger: ['input', 'blur'],
},
],
password: [
{
required: true,
message: '请输入密码',
},
],
reenteredPassword: [
{
required: true,
message: '请再次输入密码',
trigger: ['input', 'blur'],
},
{
validator: validatePasswordStartWith,
message: '两次密码输入不一致',
trigger: 'input',
},
{
validator: validatePasswordSame,
message: '两次密码输入不一致',
trigger: ['blur', 'password-input'],
},
],
}
return {
formRef,
rPasswordFormItemRef,
model: modelRef,
rules,
handlePasswordInput() {
if (modelRef.value.reenteredPassword) {
rPasswordFormItemRef.value?.validate({ trigger: 'password-input' })
}
},
handleBtnLoginClick(e: MouseEvent) {
e.preventDefault()
formRef.value?.validate((errors) => {
if (!errors) {
message.success('验证成功')
} else {
console.log(errors)
message.error('验证失败')
}
})
},
}
},
})
</script>
<style scoped>
.login-page {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
display: flex;
align-items: center;
justify-content: center;
}
.login-page > .login-form-container {
width: 320px;
background-color: #f3f3f3;
padding: 40px;
border-radius: 4px;
border: solid 1px #efeff5;
}
.title {
font-size: 24px;
font-weight: 500;
}
.title::after {
height: 24px;
display: block;
content: '';
}
</style>