u
This commit is contained in:
1 parent
861b529d9b
commit
c4bd60d0cc
16 files changed
+689
-87
No files matched your search
Binary file not shown.
|
After Width: | Height: | Size: 4.0 KiB |
@@ -11,9 +11,9 @@ onMounted(() => {
|
||||
})
|
||||
|
||||
function handleInitData() {
|
||||
getAreaListApi().then((res) => {
|
||||
commonOptions.areaOptions.splice(0, commonOptions.areaOptions.length, ...res.data || [])
|
||||
})
|
||||
// getAreaListApi().then((res) => {
|
||||
// commonOptions.areaOptions.splice(0, commonOptions.areaOptions.length, ...res.data || [])
|
||||
// })
|
||||
|
||||
getPropertyListApi().then((res) => {
|
||||
commonOptions.propertyOptions.splice(0, commonOptions.propertyOptions.length, ...res.data || [])
|
||||
|
||||
Whitespace-only changes.
@@ -1,15 +1,10 @@
|
||||
<script lang="ts" setup>
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
|
||||
const props = defineProps({
|
||||
isLogined: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
})
|
||||
import { useUserStore } from '@/store/user'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const userStore = useUserStore()
|
||||
|
||||
const menuList = [
|
||||
{ name: '首页', path: '/' },
|
||||
@@ -22,6 +17,12 @@ const menuList = [
|
||||
function navigateTo(path: string) {
|
||||
router.push(path)
|
||||
}
|
||||
|
||||
function exitLogin() {
|
||||
userStore.clearUserInfo()
|
||||
|
||||
navigateTo('/auth/login')
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -43,22 +44,24 @@ function navigateTo(path: string) {
|
||||
<div class="action">
|
||||
<i-tdesign:search class="action-item" />
|
||||
|
||||
<i-tdesign:notification v-if="props.isLogined" class="action-item" />
|
||||
<i-tdesign:notification v-if="userStore.isLogined" class="action-item" />
|
||||
|
||||
<template v-if="props.isLogined">
|
||||
<template v-if="userStore.isLogined">
|
||||
<el-divider direction="vertical" />
|
||||
|
||||
<el-dropdown>
|
||||
<div class="user">
|
||||
<el-avatar :size="32" src="https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png" />
|
||||
<el-avatar :size="32" :src="userStore.userInfo.avatar" />
|
||||
<span class="name">
|
||||
远通G3
|
||||
{{ userStore.userInfo.name }}
|
||||
</span>
|
||||
</div>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item>个人中心</el-dropdown-item>
|
||||
<el-dropdown-item>退出登录</el-dropdown-item>
|
||||
<el-dropdown-item @click="exitLogin()">
|
||||
退出登录
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts" setup>
|
||||
import Header from './components/header.vue'
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
import type { forgetApiParams, loginApiParams, loginApiResult, registerApiParams } from '@/types/auth'
|
||||
import { httpPostJson } from '../utils'
|
||||
|
||||
export function loginApi(params: loginApiParams) {
|
||||
return httpPostJson<loginApiResult>(`/auth/login`, params)
|
||||
}
|
||||
export function registerApi(params: registerApiParams) {
|
||||
return httpPostJson<null>(`/auth/register`, params)
|
||||
}
|
||||
export function forgetApi(params: forgetApiParams) {
|
||||
return httpPostJson<null>(`/auth/forget`, params)
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { AxiosError, AxiosInstance, AxiosResponse, InternalAxiosRequestConfig } from 'axios'
|
||||
import axios from 'axios'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { router } from '@/router/index'
|
||||
import { useUserStore } from '@/store/user'
|
||||
|
||||
function buildBaseUrl() {
|
||||
@@ -42,7 +42,7 @@ const NoFreePaths = [
|
||||
// 请求拦截器
|
||||
axiosInstance.interceptors.request.use(
|
||||
(config: InternalAxiosRequestConfig) => {
|
||||
const router = useRouter()
|
||||
// const router = useRouter()
|
||||
const userStore = useUserStore()
|
||||
const path = config.url || ''
|
||||
|
||||
@@ -62,7 +62,7 @@ axiosInstance.interceptors.request.use(
|
||||
return config
|
||||
},
|
||||
(error: AxiosError) => {
|
||||
console.log("===ReqError===");
|
||||
console.log('===ReqError===')
|
||||
return Promise.reject(error)
|
||||
},
|
||||
)
|
||||
@@ -80,18 +80,29 @@ axiosInstance.interceptors.response.use(
|
||||
}
|
||||
|
||||
const resData = response.data as ResponseData<any>
|
||||
response.data.data = parseJsonOrReturnString(response.data.data)
|
||||
|
||||
if (resData.code === 200) {
|
||||
return Promise.resolve(response.data)
|
||||
if (resData.code === 401) {
|
||||
router.push('/auth/login')
|
||||
return Promise.reject(response.data)
|
||||
}
|
||||
else if (resData.code === 404) {
|
||||
router.push('/error/404')
|
||||
return Promise.reject(response.data)
|
||||
}
|
||||
else {
|
||||
return Promise.reject(resData)
|
||||
resData.isSuccess = resData.code === 200
|
||||
return Promise.resolve(response.data)
|
||||
}
|
||||
},
|
||||
(error: AxiosError) => {
|
||||
console.log("===ResError===");
|
||||
|
||||
if (error.status === 500) {
|
||||
router.push('/error/500')
|
||||
}
|
||||
|
||||
if (error.status === 404) {
|
||||
router.push('/error/404')
|
||||
}
|
||||
|
||||
return Promise.reject(error)
|
||||
},
|
||||
)
|
||||
|
||||
@@ -45,6 +45,8 @@ const router = createRouter({
|
||||
scrollBehavior: () => ({ left: 0, top: 0 }),
|
||||
})
|
||||
|
||||
export { router }
|
||||
|
||||
export function setupRouter(app: App) {
|
||||
app.use(router)
|
||||
}
|
||||
@@ -2,7 +2,7 @@ import { defineStore } from 'pinia'
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
interface IUserInfo {
|
||||
id: number
|
||||
id: string
|
||||
account?: string
|
||||
email?: string
|
||||
name?: string
|
||||
@@ -14,7 +14,7 @@ export const useUserStore = defineStore(
|
||||
'user',
|
||||
() => {
|
||||
const userInfo = ref<IUserInfo>({
|
||||
id: 0,
|
||||
id: '',
|
||||
})
|
||||
|
||||
const setUserInfo = (val: IUserInfo) => {
|
||||
@@ -29,7 +29,7 @@ export const useUserStore = defineStore(
|
||||
return !!(
|
||||
(
|
||||
userInfo.value.token
|
||||
&& userInfo.value.id !== 0
|
||||
&& userInfo.value.id
|
||||
&& userInfo.value.account
|
||||
)
|
||||
)
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
export interface loginApiParams {
|
||||
loginCredential: string
|
||||
password: string
|
||||
}
|
||||
|
||||
export interface loginApiResult {
|
||||
id: string
|
||||
account: string
|
||||
name: string
|
||||
email: string
|
||||
token: string
|
||||
avatar: string
|
||||
}
|
||||
|
||||
export interface registerApiParams {
|
||||
account: string
|
||||
name: string
|
||||
email: string
|
||||
password: string
|
||||
}
|
||||
|
||||
export interface forgetApiParams {
|
||||
forgetCredential: string
|
||||
}
|
||||
@@ -19,5 +19,6 @@ declare module 'vue' {
|
||||
QueryCompany: typeof import('./../components/QueryCompany.vue')['default']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
SafeImage: typeof import('./../components/SafeImage.vue')['default']
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ export {}
|
||||
|
||||
declare global {
|
||||
interface ResponseData<T> {
|
||||
isSuccess: boolean
|
||||
code: number
|
||||
message: string
|
||||
data: T
|
||||
|
||||
@@ -9,6 +9,34 @@ function navigateTo(path: string) {
|
||||
router.push(path)
|
||||
}
|
||||
|
||||
const carouselList = [
|
||||
{
|
||||
title: '值得信赖',
|
||||
company: '恒星科技有限公司',
|
||||
content: '我们已经连续三年使用该平台,数据处理稳定高效,团队协作体验非常顺畅。',
|
||||
},
|
||||
{
|
||||
title: '安全 · 高效',
|
||||
company: '博越信息技术',
|
||||
content: '平台的权限管理做得很好,操作简单清晰,极大提升了内部管理效率。',
|
||||
},
|
||||
{
|
||||
title: '稳定可靠',
|
||||
company: '远航供应链集团',
|
||||
content: '系统运行非常稳定,几乎没有出现过宕机情况,客服响应速度也很快。',
|
||||
},
|
||||
{
|
||||
title: '智能化体验',
|
||||
company: '蓝海数据服务中心',
|
||||
content: '智能报表和自动化功能非常实用,减少了大量人工整理数据的时间。',
|
||||
},
|
||||
{
|
||||
title: '灵活易用',
|
||||
company: '中联创投有限公司',
|
||||
content: 'UI设计简洁友好,新员工上手很快,各项业务流程也能灵活配置。',
|
||||
},
|
||||
]
|
||||
|
||||
// do not use same name with ref
|
||||
const form = reactive({
|
||||
name: '',
|
||||
@@ -35,10 +63,16 @@ function onSubmit() {
|
||||
height="150px"
|
||||
indicator-position="none"
|
||||
>
|
||||
<el-carousel-item v-for="item in 4" :key="item">
|
||||
<h3 class="small justify-center" text="2xl">
|
||||
{{ item }}
|
||||
</h3>
|
||||
<el-carousel-item v-for="(item, index) in carouselList" :key="index">
|
||||
<div class="carousel-item">
|
||||
<h2 class="title">
|
||||
{{ item.title }}
|
||||
</h2>
|
||||
|
||||
<span class="content">{{ item.content }}</span>
|
||||
|
||||
<span class="company"> - {{ item.company }}</span>
|
||||
</div>
|
||||
</el-carousel-item>
|
||||
</el-carousel>
|
||||
</div>
|
||||
@@ -99,13 +133,30 @@ function onSubmit() {
|
||||
.bg-content {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
bottom: 12%;
|
||||
bottom: 10%;
|
||||
transform: translateX(-50%);
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
width: 600px;
|
||||
|
||||
.carousel-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.title {
|
||||
font-size: 28px;
|
||||
line-height: 28px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.content {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.company {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.form-label-flex {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
@@ -122,7 +173,7 @@ function onSubmit() {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 48px 56px 24px 56px;
|
||||
padding: 36px;
|
||||
|
||||
.logo {
|
||||
height: 70px;
|
||||
|
||||
@@ -1,35 +1,125 @@
|
||||
<script lang="ts" setup>
|
||||
import { reactive } from 'vue'
|
||||
|
||||
import type { FormInstance } from 'element-plus'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { reactive, ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { loginApi } from '@/request/api/auth'
|
||||
import { useUserStore } from '@/store/user'
|
||||
|
||||
const userStore = useUserStore()
|
||||
const router = useRouter()
|
||||
|
||||
const carouselList = [
|
||||
{
|
||||
title: '值得信赖',
|
||||
company: '恒星科技有限公司',
|
||||
content: '我们已经连续三年使用该平台,数据处理稳定高效,团队协作体验非常顺畅。',
|
||||
},
|
||||
{
|
||||
title: '安全 · 高效',
|
||||
company: '博越信息技术',
|
||||
content: '平台的权限管理做得很好,操作简单清晰,极大提升了内部管理效率。',
|
||||
},
|
||||
{
|
||||
title: '稳定可靠',
|
||||
company: '远航供应链集团',
|
||||
content: '系统运行非常稳定,几乎没有出现过宕机情况,客服响应速度也很快。',
|
||||
},
|
||||
{
|
||||
title: '智能化体验',
|
||||
company: '蓝海数据服务中心',
|
||||
content: '智能报表和自动化功能非常实用,减少了大量人工整理数据的时间。',
|
||||
},
|
||||
{
|
||||
title: '灵活易用',
|
||||
company: '中联创投有限公司',
|
||||
content: 'UI设计简洁友好,新员工上手很快,各项业务流程也能灵活配置。',
|
||||
},
|
||||
]
|
||||
|
||||
const form = reactive<{
|
||||
loginCredential: string
|
||||
password: string
|
||||
rememberMe: boolean
|
||||
}>({
|
||||
loginCredential: '',
|
||||
password: '',
|
||||
rememberMe: true,
|
||||
})
|
||||
const formRef = ref<FormInstance>()
|
||||
const formRules = {
|
||||
loginCredential: [
|
||||
{ required: true, message: '邮箱 / 账号不能为空', trigger: 'blur' },
|
||||
],
|
||||
password: [
|
||||
{ required: true, message: '密码不能为空', trigger: 'blur' },
|
||||
],
|
||||
}
|
||||
|
||||
const saveLoading = ref(false)
|
||||
|
||||
async function submitForm(formEl: FormInstance | undefined) {
|
||||
if (!formEl)
|
||||
return
|
||||
await formEl.validate((valid) => {
|
||||
if (valid) {
|
||||
saveLoading.value = true
|
||||
loginApi({
|
||||
loginCredential: form.loginCredential,
|
||||
password: form.password,
|
||||
}).then((res) => {
|
||||
if (res.isSuccess) {
|
||||
userStore.setUserInfo({
|
||||
id: res.data.id,
|
||||
account: res.data.account,
|
||||
email: res.data.email,
|
||||
name: res.data.name,
|
||||
token: res.data.token,
|
||||
avatar: res.data.avatar || '/no-avatar.png',
|
||||
})
|
||||
|
||||
ElMessage.success('登录成功')
|
||||
router.push('/')
|
||||
}
|
||||
else {
|
||||
ElMessage.error(res.message || '登录失败,请稍后再试!')
|
||||
}
|
||||
}).catch((err) => {
|
||||
console.log(err)
|
||||
}).finally(() => {
|
||||
saveLoading.value = false
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function navigateTo(path: string) {
|
||||
router.push(path)
|
||||
}
|
||||
|
||||
// do not use same name with ref
|
||||
const form = reactive({
|
||||
name: '',
|
||||
region: '',
|
||||
date1: '',
|
||||
date2: '',
|
||||
delivery: false,
|
||||
type: [],
|
||||
resource: '',
|
||||
desc: '',
|
||||
})
|
||||
|
||||
function onSubmit() {
|
||||
console.log('submit!')
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="auth-wrapper">
|
||||
<div class="auth-bg">
|
||||
<img src="/auth-bg.jpg">
|
||||
<div class="bg-content">
|
||||
<el-carousel
|
||||
height="150px"
|
||||
indicator-position="none"
|
||||
>
|
||||
<el-carousel-item v-for="(item, index) in carouselList" :key="index">
|
||||
<div class="carousel-item">
|
||||
<h2 class="title">
|
||||
{{ item.title }}
|
||||
</h2>
|
||||
|
||||
<span class="content">{{ item.content }}</span>
|
||||
|
||||
<span class="company"> - {{ item.company }}</span>
|
||||
</div>
|
||||
</el-carousel-item>
|
||||
</el-carousel>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="login-form">
|
||||
@@ -43,29 +133,38 @@ function onSubmit() {
|
||||
输入你的 邮箱/账号 和 密码 登录账户.
|
||||
</p>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="formRules"
|
||||
label-width="auto"
|
||||
label-position="top"
|
||||
hide-required-asterisk
|
||||
>
|
||||
<el-form-item label="邮箱 / 账号">
|
||||
<el-input v-model="form.name" placeholder="请输入邮箱 / 账号" size="large" />
|
||||
<el-form-item label="邮箱 / 账号" prop="loginCredential">
|
||||
<el-input v-model="form.loginCredential" placeholder="请输入邮箱 / 账号" size="large" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
|
||||
<el-form-item prop="password">
|
||||
<template #label>
|
||||
<div class="form-label-flex">
|
||||
<span class="form-label-flex">
|
||||
<span>密码</span>
|
||||
<span class="forgot" @click="navigateTo('/auth/forget')">忘记密码?</span>
|
||||
</div>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<el-input v-model="form.name" placeholder="请输入密码" size="large" />
|
||||
<el-input
|
||||
v-model="form.password"
|
||||
placeholder="请输入密码"
|
||||
size="large"
|
||||
@keyup.enter="submitForm(formRef)"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-checkbox label="记住我" class="remeber" />
|
||||
<el-checkbox v-model="form.rememberMe" label="记住我" class="remeber" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button type="primary" class="form-btn">
|
||||
<el-button type="primary" class="form-btn" :loading="saveLoading" @click="submitForm(formRef)">
|
||||
登录
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
@@ -89,12 +188,39 @@ function onSubmit() {
|
||||
.auth-bg {
|
||||
width: 70%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.bg-content {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
bottom: 10%;
|
||||
transform: translateX(-50%);
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
width: 600px;
|
||||
|
||||
.carousel-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.title {
|
||||
font-size: 28px;
|
||||
line-height: 28px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.content {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.company {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.form-label-flex {
|
||||
display: flex;
|
||||
@@ -112,7 +238,7 @@ function onSubmit() {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 48px 56px 24px 56px;
|
||||
padding: 36px;
|
||||
|
||||
.logo {
|
||||
height: 70px;
|
||||
|
||||
@@ -1,28 +1,128 @@
|
||||
<script lang="ts" setup>
|
||||
import { reactive } from 'vue'
|
||||
import type { FormInstance } from 'element-plus'
|
||||
import { computed, onUnmounted, reactive, ref } from 'vue'
|
||||
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
function navigateTo(path: string) {
|
||||
router.push(path)
|
||||
const carouselList = [
|
||||
{
|
||||
title: '值得信赖',
|
||||
company: '恒星科技有限公司',
|
||||
content: '我们已经连续三年使用该平台,数据处理稳定高效,团队协作体验非常顺畅。',
|
||||
},
|
||||
{
|
||||
title: '安全 · 高效',
|
||||
company: '博越信息技术',
|
||||
content: '平台的权限管理做得很好,操作简单清晰,极大提升了内部管理效率。',
|
||||
},
|
||||
{
|
||||
title: '稳定可靠',
|
||||
company: '远航供应链集团',
|
||||
content: '系统运行非常稳定,几乎没有出现过宕机情况,客服响应速度也很快。',
|
||||
},
|
||||
{
|
||||
title: '智能化体验',
|
||||
company: '蓝海数据服务中心',
|
||||
content: '智能报表和自动化功能非常实用,减少了大量人工整理数据的时间。',
|
||||
},
|
||||
{
|
||||
title: '灵活易用',
|
||||
company: '中联创投有限公司',
|
||||
content: 'UI设计简洁友好,新员工上手很快,各项业务流程也能灵活配置。',
|
||||
},
|
||||
]
|
||||
|
||||
const form = reactive<{
|
||||
email: string
|
||||
code: string
|
||||
name: string
|
||||
account: string
|
||||
password: string
|
||||
confirmPassword: string
|
||||
}>({
|
||||
email: '',
|
||||
code: '',
|
||||
name: '',
|
||||
account: '',
|
||||
password: '',
|
||||
confirmPassword: '',
|
||||
})
|
||||
const formRef = ref<FormInstance>()
|
||||
const formRules = {
|
||||
email: [
|
||||
{ required: true, message: '邮箱不能为空', trigger: 'blur' },
|
||||
{ type: 'email', message: '请输入正确的邮箱格式', trigger: ['blur', 'change'] },
|
||||
],
|
||||
code: [
|
||||
{ required: true, message: '验证码不能为空', trigger: 'blur' },
|
||||
],
|
||||
name: [
|
||||
{ required: true, message: '名称不能为空', trigger: 'blur' },
|
||||
{ min: 2, max: 20, message: '名称长度需在 2 到 20 个字符之间', trigger: 'blur' },
|
||||
],
|
||||
password: [
|
||||
{ required: true, message: '密码不能为空', trigger: 'blur' },
|
||||
{ min: 6, max: 20, message: '密码长度需在 6 到 20 个字符之间', trigger: 'blur' },
|
||||
],
|
||||
confirmPassword: [
|
||||
{ required: true, message: '请再次输入密码', trigger: 'blur' },
|
||||
{
|
||||
validator: (rule: any, value: any, callback: any) => {
|
||||
if (value !== form.password) {
|
||||
callback(new Error('两次输入的密码不一致'))
|
||||
}
|
||||
else {
|
||||
callback()
|
||||
}
|
||||
},
|
||||
trigger: ['blur', 'change'],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
// do not use same name with ref
|
||||
const form = reactive({
|
||||
name: '',
|
||||
region: '',
|
||||
date1: '',
|
||||
date2: '',
|
||||
delivery: false,
|
||||
type: [],
|
||||
resource: '',
|
||||
desc: '',
|
||||
// --- 邮箱校验(简单正则) ---
|
||||
const emailRegex = /^\w+(?:[-.]\w+)*@\w+(?:[-.]\w+)*\.\w+(?:[-.]\w+)*$/
|
||||
|
||||
const isEmailValid = computed(() => emailRegex.test(form.email.trim()))
|
||||
|
||||
// --- 发送验证码状态 & 倒计时 ---
|
||||
const sending = ref(false)
|
||||
const countdown = ref(0)
|
||||
let timer: number | undefined
|
||||
|
||||
function startCountdown(seconds = 60) {
|
||||
countdown.value = seconds
|
||||
timer = window.setInterval(() => {
|
||||
countdown.value -= 1
|
||||
if (countdown.value <= 0) {
|
||||
clearInterval(timer)
|
||||
timer = undefined
|
||||
sending.value = false
|
||||
}
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
// 清理
|
||||
onUnmounted(() => {
|
||||
if (timer) {
|
||||
clearInterval(timer)
|
||||
timer = undefined
|
||||
}
|
||||
})
|
||||
|
||||
function onSubmit() {
|
||||
console.log('submit!')
|
||||
function handleSendEmailCode() {
|
||||
sending.value = true
|
||||
startCountdown()
|
||||
}
|
||||
|
||||
async function submitForm() {
|
||||
|
||||
}
|
||||
|
||||
function navigateTo(path: string) {
|
||||
router.push(path)
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -30,8 +130,25 @@ function onSubmit() {
|
||||
<div class="auth-wrapper">
|
||||
<div class="auth-bg">
|
||||
<img src="/auth-bg.jpg">
|
||||
</div>
|
||||
<div class="bg-content">
|
||||
<el-carousel
|
||||
height="150px"
|
||||
indicator-position="none"
|
||||
>
|
||||
<el-carousel-item v-for="(item, index) in carouselList" :key="index">
|
||||
<div class="carousel-item">
|
||||
<h2 class="title">
|
||||
{{ item.title }}
|
||||
</h2>
|
||||
|
||||
<span class="content">{{ item.content }}</span>
|
||||
|
||||
<span class="company"> - {{ item.company }}</span>
|
||||
</div>
|
||||
</el-carousel-item>
|
||||
</el-carousel>
|
||||
</div>
|
||||
</div>
|
||||
<div class="login-form">
|
||||
<img src="/logo.png" class="logo" @click="navigateTo('/')">
|
||||
|
||||
@@ -43,25 +160,62 @@ function onSubmit() {
|
||||
请填写必要的注册信息,完成账户创建。
|
||||
</p>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="formRules"
|
||||
label-width="auto"
|
||||
label-position="top"
|
||||
>
|
||||
<el-form-item label="邮箱">
|
||||
<el-input v-model="form.name" placeholder="请输入邮箱" size="large" />
|
||||
<el-form-item
|
||||
label="邮箱"
|
||||
prop="email"
|
||||
>
|
||||
<el-input
|
||||
v-model="form.email"
|
||||
placeholder="请输入邮箱"
|
||||
size="large"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="账号">
|
||||
<el-input v-model="form.name" placeholder="请输入账号" size="large" />
|
||||
<el-form-item
|
||||
label="邮箱验证码"
|
||||
prop="code"
|
||||
>
|
||||
<el-input
|
||||
v-model="form.account" placeholder="请输入邮箱验证码" size="large"
|
||||
class="email-code"
|
||||
:class="(!isEmailValid || sending) ? 'disabled' : ''"
|
||||
>
|
||||
<template #append>
|
||||
<el-button :disabled="!isEmailValid || sending" @click="handleSendEmailCode()">
|
||||
{{ sending ? `${countdown}s` : "发送验证码" }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="公司名称">
|
||||
<!-- <el-form-item label="账号">
|
||||
<el-input v-model="form.account" placeholder="请输入账号" size="large" />
|
||||
</el-form-item> -->
|
||||
<el-form-item
|
||||
label="公司名称"
|
||||
prop="name"
|
||||
>
|
||||
<el-input v-model="form.name" placeholder="请输入公司名称" size="large" />
|
||||
</el-form-item>
|
||||
<el-form-item label="密码">
|
||||
<el-input v-model="form.name" placeholder="请输入密码" size="large" />
|
||||
<el-form-item
|
||||
label="密码"
|
||||
prop="password"
|
||||
>
|
||||
<el-input v-model="form.password" placeholder="请输入密码" size="large" />
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
label="确认密码"
|
||||
prop="confirmPassword"
|
||||
>
|
||||
<el-input v-model="form.confirmPassword" placeholder="请输入确认密码" size="large" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button type="primary" class="form-btn">
|
||||
<el-button type="primary" class="form-btn" @click="submitForm()">
|
||||
注册
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
@@ -77,6 +231,21 @@ function onSubmit() {
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.email-code {
|
||||
:deep(.el-input-group__append) {
|
||||
background-color: #eceffd;
|
||||
color: #4041d1;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
:deep(.el-input-group__append) {
|
||||
background-color: #f5f7fa;
|
||||
color: #c0c4cc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.auth-wrapper {
|
||||
display: flex;
|
||||
height: 100vh;
|
||||
@@ -85,12 +254,39 @@ function onSubmit() {
|
||||
.auth-bg {
|
||||
width: 70%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.bg-content {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
bottom: 10%;
|
||||
transform: translateX(-50%);
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
width: 600px;
|
||||
|
||||
.carousel-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.title {
|
||||
font-size: 28px;
|
||||
line-height: 28px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.content {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.company {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.form-label-flex {
|
||||
display: flex;
|
||||
@@ -105,16 +301,17 @@ function onSubmit() {
|
||||
}
|
||||
|
||||
.login-form {
|
||||
overflow-y: auto;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 48px 56px 24px 56px;
|
||||
padding: 36px;
|
||||
|
||||
.logo {
|
||||
height: 70px;
|
||||
width: 160px;
|
||||
cursor: pointer;
|
||||
margin-bottom: 48px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.main {
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>注册示例</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
background: #f5f5f5;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.register-form {
|
||||
background: #fff;
|
||||
padding: 30px 40px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
||||
width: 350px;
|
||||
}
|
||||
|
||||
.register-form h2 {
|
||||
margin-bottom: 20px;
|
||||
font-weight: 600;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.form-item {
|
||||
margin-bottom: 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.form-item label {
|
||||
margin-bottom: 6px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.form-item input {
|
||||
padding: 8px 12px;
|
||||
font-size: 14px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
/* 验证码输入+按钮布局 */
|
||||
.input-with-button {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.input-with-button input {
|
||||
flex: 1;
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
|
||||
.input-with-button button {
|
||||
padding: 0 15px;
|
||||
border: 1px solid #ccc;
|
||||
border-left: none;
|
||||
border-radius: 0 4px 4px 0;
|
||||
background-color: #eceffd;
|
||||
color: #4041d1;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.input-with-button button.disabled {
|
||||
background-color: #f5f7fa;
|
||||
color: #c0c4cc;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.form-item button[type="submit"] {
|
||||
width: 100%;
|
||||
padding: 10px 0;
|
||||
background-color: #4041d1;
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="register-form">
|
||||
<h2>注册</h2>
|
||||
|
||||
<div class="form-item">
|
||||
<label for="email">邮箱</label>
|
||||
<input type="email" id="email" placeholder="请输入邮箱">
|
||||
</div>
|
||||
|
||||
<div class="form-item">
|
||||
<label for="code">邮箱验证码</label>
|
||||
<div class="input-with-button">
|
||||
<input type="text" id="code" placeholder="请输入验证码">
|
||||
<button id="sendCode" class="disabled" disabled>发送验证码</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-item">
|
||||
<label for="password">密码</label>
|
||||
<input type="password" id="password" placeholder="请输入密码">
|
||||
</div>
|
||||
|
||||
<div class="form-item">
|
||||
<label for="confirmPassword">确认密码</label>
|
||||
<input type="password" id="confirmPassword" placeholder="请输入确认密码">
|
||||
</div>
|
||||
|
||||
<div class="form-item">
|
||||
<button type="submit">注册</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const emailInput = document.getElementById('email');
|
||||
const sendBtn = document.getElementById('sendCode');
|
||||
let countdown = 60;
|
||||
let timer = null;
|
||||
|
||||
// 简单邮箱正则
|
||||
const emailRegex = /^\w+([.-]?\w+)*@\w+([.-]?\w+)*\.\w+([.-]?\w+)*$/;
|
||||
|
||||
// 检查邮箱是否有效
|
||||
emailInput.addEventListener('input', () => {
|
||||
if (emailRegex.test(emailInput.value.trim())) {
|
||||
sendBtn.disabled = false;
|
||||
sendBtn.classList.remove('disabled');
|
||||
} else {
|
||||
sendBtn.disabled = true;
|
||||
sendBtn.classList.add('disabled');
|
||||
}
|
||||
});
|
||||
|
||||
// 点击发送验证码
|
||||
sendBtn.addEventListener('click', () => {
|
||||
if (sendBtn.disabled) return;
|
||||
|
||||
sendBtn.disabled = true;
|
||||
sendBtn.classList.add('disabled');
|
||||
sendBtn.textContent = `${countdown}s`;
|
||||
|
||||
// 模拟倒计时
|
||||
timer = setInterval(() => {
|
||||
countdown--;
|
||||
if (countdown <= 0) {
|
||||
clearInterval(timer);
|
||||
countdown = 60;
|
||||
sendBtn.textContent = '发送验证码';
|
||||
// 如果邮箱有效,恢复可点击
|
||||
if (emailRegex.test(emailInput.value.trim())) {
|
||||
sendBtn.disabled = false;
|
||||
sendBtn.classList.remove('disabled');
|
||||
}
|
||||
} else {
|
||||
sendBtn.textContent = `${countdown}s`;
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
// TODO: 在这里调用你的发送验证码接口
|
||||
console.log('发送验证码给邮箱:', emailInput.value.trim());
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Reference in new issue
Block a user