feat(login): 实现登录注册组件及流程
- 添加 Login.vue 组件,包含用户名、密码输入及登录、注册、取消功能 - 添加 LoginProcess.vue 组件,管理登录注册流程切换 - 添加 Register.vue 组件占位 - 配置 axios 请求拦截器及环境变量类型定义 - 更新 App.vue 登录流程,替换原有 passkey 登录逻辑 - 添加 slide-up 动画样式并移除旧动画定义 - 更新依赖 tdesign-mobile-vue 为 tdesign-vue-next - 配置开发环境与生产环境 BASE_URL - 调整布局样式,优化登录界面显示效果
This commit is contained in:
parent
5abf344572
commit
0c7aaa848d
1
.env.development
Normal file
1
.env.development
Normal file
@ -0,0 +1 @@
|
|||||||
|
BASE_URL=http://localhost:8080/
|
||||||
10
auto-imports.d.ts
vendored
10
auto-imports.d.ts
vendored
@ -1,10 +0,0 @@
|
|||||||
/* eslint-disable */
|
|
||||||
/* prettier-ignore */
|
|
||||||
// @ts-nocheck
|
|
||||||
// noinspection JSUnusedGlobalSymbols
|
|
||||||
// Generated by unplugin-auto-import
|
|
||||||
// biome-ignore lint: disable
|
|
||||||
export {}
|
|
||||||
declare global {
|
|
||||||
|
|
||||||
}
|
|
||||||
25
components.d.ts
vendored
25
components.d.ts
vendored
@ -1,25 +0,0 @@
|
|||||||
/* eslint-disable */
|
|
||||||
// @ts-nocheck
|
|
||||||
// biome-ignore lint: disable
|
|
||||||
// oxlint-disable
|
|
||||||
// ------
|
|
||||||
// Generated by unplugin-vue-components
|
|
||||||
// Read more: https://github.com/vuejs/core/pull/3399
|
|
||||||
|
|
||||||
export {}
|
|
||||||
|
|
||||||
/* prettier-ignore */
|
|
||||||
declare module 'vue' {
|
|
||||||
export interface GlobalComponents {
|
|
||||||
Card: typeof import('./src/components/Card.vue')['default']
|
|
||||||
IconCommunity: typeof import('./src/components/icons/IconCommunity.vue')['default']
|
|
||||||
IconDocumentation: typeof import('./src/components/icons/IconDocumentation.vue')['default']
|
|
||||||
IconEcosystem: typeof import('./src/components/icons/IconEcosystem.vue')['default']
|
|
||||||
IconSupport: typeof import('./src/components/icons/IconSupport.vue')['default']
|
|
||||||
IconTooling: typeof import('./src/components/icons/IconTooling.vue')['default']
|
|
||||||
RouterLink: typeof import('vue-router')['RouterLink']
|
|
||||||
RouterView: typeof import('vue-router')['RouterView']
|
|
||||||
TAvatar: typeof import('tdesign-mobile-vue')['Avatar']
|
|
||||||
TButton: typeof import('tdesign-mobile-vue')['Button']
|
|
||||||
}
|
|
||||||
}
|
|
||||||
4682
package-lock.json
generated
4682
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -15,12 +15,13 @@
|
|||||||
"format": "prettier --write src/"
|
"format": "prettier --write src/"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"axios": "^1.13.2",
|
||||||
"gsap": "^3.13.0",
|
"gsap": "^3.13.0",
|
||||||
"pinia": "^3.0.3",
|
"pinia": "^3.0.3",
|
||||||
"sass-loader": "^16.0.6",
|
"sass-loader": "^16.0.6",
|
||||||
"scss": "^0.2.4",
|
"scss": "^0.2.4",
|
||||||
"tdesign-icons-vue-next": "^0.4.1",
|
"tdesign-icons-vue-next": "^0.4.1",
|
||||||
"tdesign-mobile-vue": "^1.11.0",
|
"tdesign-vue-next": "^1.17.2",
|
||||||
"vue": "^3.5.22",
|
"vue": "^3.5.22",
|
||||||
"vue-router": "^4.6.3"
|
"vue-router": "^4.6.3"
|
||||||
},
|
},
|
||||||
|
|||||||
87
src/App.vue
87
src/App.vue
@ -7,7 +7,7 @@
|
|||||||
<div class="login-info">
|
<div class="login-info">
|
||||||
<transition name="slide-up" mode="out-in">
|
<transition name="slide-up" mode="out-in">
|
||||||
<div v-if="!login">
|
<div v-if="!login">
|
||||||
<t-button :disabled="!passkey" @click="handleLogin">登录</t-button>
|
<LoginProcess @login="handleLogin"/>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="!weCome">
|
<div v-else-if="!weCome">
|
||||||
<transition name="slide-up" mode="out-in">
|
<transition name="slide-up" mode="out-in">
|
||||||
@ -34,6 +34,7 @@
|
|||||||
import { RouterView } from 'vue-router'
|
import { RouterView } from 'vue-router'
|
||||||
import { ref, watch } from 'vue'
|
import { ref, watch } from 'vue'
|
||||||
import avatar from '@/assets/logo.svg'
|
import avatar from '@/assets/logo.svg'
|
||||||
|
import LoginProcess from '@/components/login/LoginProcess.vue'
|
||||||
|
|
||||||
const login = ref(false)
|
const login = ref(false)
|
||||||
const weCome = ref(false)
|
const weCome = ref(false)
|
||||||
@ -48,33 +49,34 @@ if (!navigator.credentials) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handleLogin(){
|
function handleLogin(){
|
||||||
navigator.credentials.create({
|
// navigator.credentials.create({
|
||||||
publicKey: {
|
// publicKey: {
|
||||||
challenge: new Uint8Array(32),
|
// challenge: new Uint8Array(32),
|
||||||
timeout: 60000,
|
// timeout: 60000,
|
||||||
rp: {
|
// rp: {
|
||||||
id: window.location.host,
|
// id: window.location.host,
|
||||||
name: "Animo"
|
// name: "Animo"
|
||||||
},
|
// },
|
||||||
user: {
|
// user: {
|
||||||
id: new Uint8Array(32),
|
// id: new Uint8Array(32),
|
||||||
name: "Amico",
|
// name: "Amico",
|
||||||
displayName: "Amico",
|
// displayName: "Amico",
|
||||||
},
|
// },
|
||||||
pubKeyCredParams: [{
|
// pubKeyCredParams: [{
|
||||||
alg: -7, type: "public-key"
|
// alg: -7, type: "public-key"
|
||||||
},{
|
// },{
|
||||||
alg: -257, type: "public-key"
|
// alg: -257, type: "public-key"
|
||||||
}],
|
// }],
|
||||||
excludeCredentials: [],
|
// excludeCredentials: [],
|
||||||
authenticatorSelection: {
|
// authenticatorSelection: {
|
||||||
authenticatorAttachment: "platform",
|
// authenticatorAttachment: "platform",
|
||||||
requireResidentKey: true,
|
// requireResidentKey: true,
|
||||||
}
|
// }
|
||||||
},
|
// },
|
||||||
}).then(resp => {
|
// }).then(resp => {
|
||||||
|
// login.value = true
|
||||||
|
// })
|
||||||
login.value = true
|
login.value = true
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 登录动画流程
|
// 登录动画流程
|
||||||
@ -112,7 +114,9 @@ header {
|
|||||||
padding: 2vh;
|
padding: 2vh;
|
||||||
transition: all 0.5s ease-out;
|
transition: all 0.5s ease-out;
|
||||||
.logo {
|
.logo {
|
||||||
display: block;
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
margin: 0 calc(50vw - 62.5px);
|
margin: 0 calc(50vw - 62.5px);
|
||||||
transition: all 0.3s ease-out;
|
transition: all 0.3s ease-out;
|
||||||
position: relative;
|
position: relative;
|
||||||
@ -121,6 +125,7 @@ header {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.login-info {
|
.login-info {
|
||||||
|
flex: 1;
|
||||||
margin: 1rem;
|
margin: 1rem;
|
||||||
line-height: 2rem;
|
line-height: 2rem;
|
||||||
height: 2rem;
|
height: 2rem;
|
||||||
@ -149,6 +154,7 @@ header {
|
|||||||
justify-content: start;
|
justify-content: start;
|
||||||
height: 10vh;
|
height: 10vh;
|
||||||
.logo {
|
.logo {
|
||||||
|
flex: none;
|
||||||
height: 6vh;
|
height: 6vh;
|
||||||
width: 6vh;
|
width: 6vh;
|
||||||
margin: 0 0;
|
margin: 0 0;
|
||||||
@ -210,29 +216,4 @@ nav a:first-of-type {
|
|||||||
margin-top: 1rem;
|
margin-top: 1rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.slide-up-enter-active,
|
|
||||||
.slide-up-leave-active {
|
|
||||||
transition: all 0.25s ease-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
.slide-up-enter-from {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateX(30px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.slide-up-leave-to {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateX(-30px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.fade-enter-active,
|
|
||||||
.fade-leave-active {
|
|
||||||
transition: opacity 0.5s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fade-enter-from,
|
|
||||||
.fade-leave-to {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -19,3 +19,18 @@ a,
|
|||||||
background-color: hsla(160, 100%, 37%, 0.2);
|
background-color: hsla(160, 100%, 37%, 0.2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.slide-up-enter-active,
|
||||||
|
.slide-up-leave-active {
|
||||||
|
transition: all 0.25s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-up-enter-from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateX(30px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-up-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateX(-30px);
|
||||||
|
}
|
||||||
|
|||||||
32
src/components/login/Login.vue
Normal file
32
src/components/login/Login.vue
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<t-input v-model="username" placeholder="请输入用户名"></t-input>
|
||||||
|
<t-input v-model="password" placeholder="请输入密码"></t-input>
|
||||||
|
<t-link @click="handleRegister">没有账号?去注册</t-link>
|
||||||
|
<t-button @click="handleLogin">登录</t-button>
|
||||||
|
<t-button @click="handleUsePasskey">取消</t-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { reactive, toRefs } from 'vue'
|
||||||
|
|
||||||
|
const { username, password } = toRefs(
|
||||||
|
reactive({
|
||||||
|
username: '',
|
||||||
|
password: '',
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
const emits = defineEmits(['complete', 'register'])
|
||||||
|
function handleLogin() {
|
||||||
|
emits('complete')
|
||||||
|
}
|
||||||
|
function handleUsePasskey() {
|
||||||
|
emits('complete')
|
||||||
|
}
|
||||||
|
function handleRegister() {
|
||||||
|
emits('register')
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss"></style>
|
||||||
27
src/components/login/LoginProcess.vue
Normal file
27
src/components/login/LoginProcess.vue
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<transition name="slide-up">
|
||||||
|
<Login v-if="step === 'login'" @complete="handleLogin" @register="handleRegister"></Login>
|
||||||
|
<Register v-else-if="step === 'register'" @complete="handleLogin"></Register>
|
||||||
|
</transition>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import Login from '@/components/login/Login.vue'
|
||||||
|
import Register from '@/components/login/Register.vue'
|
||||||
|
|
||||||
|
const emits = defineEmits(['login'])
|
||||||
|
const step = ref('login')
|
||||||
|
|
||||||
|
function handleLogin() {
|
||||||
|
emits('login')
|
||||||
|
}
|
||||||
|
function handleRegister() {
|
||||||
|
step.value = 'register'
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss"></style>
|
||||||
5
src/components/login/Register.vue
Normal file
5
src/components/login/Register.vue
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<template></template>
|
||||||
|
|
||||||
|
<script setup lang="ts"></script>
|
||||||
|
|
||||||
|
<style scoped lang="scss"></style>
|
||||||
@ -1,5 +1,5 @@
|
|||||||
import './assets/main.css'
|
import './assets/main.css'
|
||||||
import 'tdesign-mobile-vue/es/style/index.css';
|
import 'tdesign-vue-next/es/style/index.css'
|
||||||
|
|
||||||
import { createApp } from 'vue'
|
import { createApp } from 'vue'
|
||||||
import { createPinia } from 'pinia'
|
import { createPinia } from 'pinia'
|
||||||
|
|||||||
15
src/request/axios.ts
Normal file
15
src/request/axios.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import axios, { type InternalAxiosRequestConfig } from 'axios'
|
||||||
|
|
||||||
|
axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8';
|
||||||
|
|
||||||
|
const request = axios.create({
|
||||||
|
timeout: 10000,
|
||||||
|
baseURL: import.meta.env.BASE_URL
|
||||||
|
})
|
||||||
|
|
||||||
|
request.interceptors.request.use((config: InternalAxiosRequestConfig) => {
|
||||||
|
|
||||||
|
return Promise.reject()
|
||||||
|
})
|
||||||
|
|
||||||
|
export default request
|
||||||
8
src/types/env.d.ts
vendored
Normal file
8
src/types/env.d.ts
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
// 环境变量
|
||||||
|
interface ImportMetaEnv {
|
||||||
|
BASE_URL: string;
|
||||||
|
}
|
||||||
|
interface ImportMeta {
|
||||||
|
readonly env: ImportMetaEnv;
|
||||||
|
// readonly glob: any;
|
||||||
|
}
|
||||||
@ -9,7 +9,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"include": [
|
"include": [
|
||||||
"node_modules/tdesign-mobile-vue/global.d.ts",
|
"node_modules/tdesign-vue-next/global.d.ts",
|
||||||
"src/types/**/*.d.ts"
|
"src/types/**/*.d.ts"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,12 +14,12 @@ export default defineConfig({
|
|||||||
vueDevTools(),
|
vueDevTools(),
|
||||||
AutoImport({
|
AutoImport({
|
||||||
resolvers: [TDesignResolver({
|
resolvers: [TDesignResolver({
|
||||||
library: 'mobile-vue'
|
library: 'vue-next'
|
||||||
})],
|
})],
|
||||||
}),
|
}),
|
||||||
Components({
|
Components({
|
||||||
resolvers: [TDesignResolver({
|
resolvers: [TDesignResolver({
|
||||||
library: 'mobile-vue'
|
library: 'vue-next'
|
||||||
})],
|
})],
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user