提交 a09a0eed 编写于 作者: V vben

perf: improve login logic

上级 be3a3ed6
......@@ -13,7 +13,6 @@
"log": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0",
"clean:cache": "rimraf node_modules/.cache/ && rimraf node_modules/.vite",
"clean:lib": "npx rimraf node_modules",
"typecheck": "vuedx-typecheck .",
"lint:eslint": "eslint \"{src,mock}/**/*.{vue,ts,tsx}\" --fix",
"lint:prettier": "prettier --write --loglevel warn \"src/**/*.{js,json,tsx,css,less,scss,vue,html,md}\"",
"lint:stylelint": "stylelint --fix \"**/*.{vue,less,postcss,css,scss}\" --cache --cache-location node_modules/.cache/stylelint/",
......@@ -27,7 +26,7 @@
},
"dependencies": {
"@iconify/iconify": "^2.0.0-rc.6",
"@vueuse/core": "^4.1.1",
"@vueuse/core": "^4.2.1",
"@zxcvbn-ts/core": "^0.2.0",
"ant-design-vue": "2.0.0",
"apexcharts": "^3.25.0",
......@@ -52,7 +51,7 @@
"devDependencies": {
"@commitlint/cli": "^11.0.0",
"@commitlint/config-conventional": "^11.0.0",
"@iconify/json": "^1.1.306",
"@iconify/json": "^1.1.307",
"@ls-lint/ls-lint": "^1.9.2",
"@purge-icons/generated": "^0.7.0",
"@types/fs-extra": "^9.0.7",
......@@ -70,8 +69,6 @@
"@vitejs/plugin-vue": "^1.1.4",
"@vitejs/plugin-vue-jsx": "^1.1.0",
"@vue/compiler-sfc": "^3.0.5",
"@vuedx/typecheck": "^0.6.3",
"@vuedx/typescript-plugin-vue": "^0.6.3",
"autoprefixer": "^10.2.4",
"commitizen": "^4.2.3",
"conventional-changelog-cli": "^2.1.1",
......@@ -84,7 +81,7 @@
"esno": "^0.4.4",
"fs-extra": "^9.1.0",
"http-server": "^0.12.3",
"husky": "^5.0.9",
"husky": "^5.1.0",
"is-ci": "^3.0.0",
"less": "^4.1.1",
"lint-staged": "^10.5.4",
......@@ -100,14 +97,14 @@
"typescript": "^4.1.5",
"vite": "2.0.1",
"vite-plugin-compression": "^0.2.1",
"vite-plugin-html": "^2.0.0",
"vite-plugin-html": "^2.0.1",
"vite-plugin-imagemin": "^0.2.7",
"vite-plugin-mock": "^2.1.4",
"vite-plugin-purge-icons": "^0.7.0",
"vite-plugin-pwa": "^0.5.2",
"vite-plugin-pwa": "^0.5.3",
"vite-plugin-style-import": "^0.7.3",
"vite-plugin-theme": "^0.4.3",
"vite-plugin-windicss": "0.4.3",
"vite-plugin-windicss": "0.4.4",
"vue-eslint-parser": "^7.5.0",
"yargs": "^16.2.0"
},
......
<template>
<div :class="prefixCls">
<AInput v-bind="$attrs" :size="size" v-model:value="state">
<template #addonAfter>
<CountButton :size="size" :count="count" :beforeStartFunc="sendCodeApi" />
</template>
</AInput>
</div>
<AInput v-bind="$attrs" :class="prefixCls" :size="size">
<template #addonAfter>
<CountButton :size="size" :count="count" :beforeStartFunc="sendCodeApi" />
</template>
</AInput>
</template>
<script lang="ts">
import { defineComponent, PropType } from 'vue';
......@@ -32,7 +30,6 @@
},
setup(props) {
const { prefixCls } = useDesign('countdown-input');
const [state] = useRuleFormItem(props);
return { prefixCls, state };
},
......
......@@ -2,17 +2,32 @@
// ==============屏幕断点============
// =================================
// Extra small screen / phone
@screen-xs: 480px;
@screen-xs-min: @screen-xs;
// Small screen / tablet
@screen-sm: 640px;
@screen-sm: 576px;
@screen-sm-min: @screen-sm;
// Medium screen / desktop
@screen-md: 768px;
@screen-md-min: @screen-md;
// Large screen / wide desktop
@screen-lg: 1024px;
@screen-lg: 992px;
@screen-lg-min: @screen-lg;
// Extra large screen / full hd
@screen-xl: 1280px;
@screen-xl: 1200px;
@screen-xl-min: @screen-xl;
// Extra extra large screen / large desktop
@screen-2xl: 1536px;
@screen-2xl: 1600px;
@screen-2xl-min: @screen-2xl;
@screen-xs-max: (@screen-sm-min - 1px);
@screen-sm-max: (@screen-md-min - 1px);
@screen-md-max: (@screen-lg-min - 1px);
@screen-lg-max: (@screen-xl-min - 1px);
@screen-xl-max: (@screen-2xl-min - 1px);
......@@ -17,7 +17,7 @@ export function useRuleFormItem<T extends Indexable>(
const defaultState = readonly(innerState);
const setState = (val: UnwrapRef<T[keyof T]>) => {
const setState = (val: UnwrapRef<T[keyof T]>): void => {
innerState.value = val as T[keyof T];
};
......
import { useTimeoutFn } from '/@/hooks/core/useTimeout';
import { tryOnUnmounted } from '/@/utils/helper/vueHelper';
import { unref, Ref, nextTick } from 'vue';
import type { EChartsType, EChartsOption } from 'echarts';
import type { EChartsOption } from 'echarts';
import { useDebounce } from '/@/hooks/core/useDebounce';
import { useEventListener } from '/@/hooks/event/useEventListener';
import { useBreakpoint } from '/@/hooks/event/useBreakpoint';
......@@ -12,7 +12,7 @@ export function useECharts(
elRef: Ref<HTMLDivElement>,
theme: 'light' | 'dark' | 'default' = 'light'
) {
let chartInstance: Nullable<EChartsType> = null;
let chartInstance: echarts.ECharts | null = null;
let resizeFn: Fn = resize;
let removeResizeFn: Fn = () => {};
......
import { Ref, ref, onMounted, nextTick } from 'vue';
import { useRect } from '/@/hooks/web/useRect';
export const useHeight = (element: Element | Ref<Element>) => {
const height = ref();
onMounted(() => {
nextTick(() => {
height.value = useRect(element).height;
});
});
return height;
};
......@@ -11,23 +11,24 @@ type I18nGlobalTranslation = {
type I18nTranslationRestParameters = [string, any];
function getKey(namespace: string | undefined, key: string) {
if (!namespace) {
return key;
}
if (key.startsWith(namespace)) {
return key;
}
return `${namespace}.${key}`;
}
export function useI18n(
namespace?: string
): {
t: I18nGlobalTranslation;
} {
function getKey(key: string) {
if (!namespace) {
return key;
}
if (key.startsWith(namespace)) {
return key;
}
return `${namespace}.${key}`;
}
const normalFn = {
t: (key: string) => {
return getKey(key);
return getKey(namespace, key);
},
};
......@@ -39,7 +40,7 @@ export function useI18n(
const tFn: I18nGlobalTranslation = (key: string, ...arg: any[]) => {
if (!key) return '';
return t(getKey(key), ...(arg as I18nTranslationRestParameters));
return t(getKey(namespace, key), ...(arg as I18nTranslationRestParameters));
};
return {
...methods,
......
import { Ref, unref } from 'vue';
import { isWindow } from '/@/utils/is';
export const useRect = (elementRef: (Element | Window) | Ref<Element | Window | undefined>) => {
const element = unref(elementRef);
if (isWindow(element)) {
const width = element.innerWidth;
const height = element.innerHeight;
return {
top: 0,
left: 0,
right: width,
bottom: height,
width,
height,
};
}
if (element && element.getBoundingClientRect) {
return element.getBoundingClientRect();
}
return {
top: 0,
left: 0,
right: 0,
bottom: 0,
width: 0,
height: 0,
};
};
<template>
<Dropdown placement="bottomLeft" :overlayClassName="`${prefixCls}-dropdown-overlay`">
<span :class="[prefixCls, `${prefixCls}--${theme}`]">
<span :class="[prefixCls, `${prefixCls}--${theme}`]" class="flex">
<img :class="`${prefixCls}__header`" :src="headerImg" />
<span :class="`${prefixCls}__info`">
<span :class="`${prefixCls}__name`" class="truncate">{{ getUserInfo.realName }}</span>
<span :class="`${prefixCls}__info hidden md:block`">
<span :class="`${prefixCls}__name `" class="truncate">
{{ getUserInfo.realName }}
</span>
</span>
</span>
......@@ -121,9 +123,7 @@
@prefix-cls: ~'@{namespace}-header-user-dropdown';
.@{prefix-cls} {
display: flex;
height: @header-height;
min-width: 100px;
padding: 0 0 0 10px;
padding-right: 10px;
overflow: hidden;
......
......@@ -16,6 +16,7 @@ import { isDevMode } from '/@/utils/env';
const app = createApp(App);
// Register global components
registerGlobComp(app);
// Multilingual configuration
......
......@@ -2,7 +2,9 @@ import type { AppRouteRecordRaw } from '/@/router/types';
import ParentLayout from '/@/layouts/page/ParentView.vue';
import { t } from '/@/hooks/web/useI18n';
const EXCEPTION_COMPONENT = () => import('../views/sys/exception/Exception.vue');
export const REDIRECT_NAME = 'Redirect';
export const EXCEPTION_COMPONENT = () => import('../views/sys/exception/Exception.vue');
/**
* @description: default layout
......@@ -44,8 +46,6 @@ export const PAGE_NOT_FOUND_ROUTE: AppRouteRecordRaw = {
],
};
export const REDIRECT_NAME = 'Redirect';
export const REDIRECT_ROUTE: AppRouteRecordRaw = {
path: '/redirect',
name: REDIRECT_NAME,
......
......@@ -2,6 +2,10 @@ import type { Router } from 'vue-router';
import { useProjectSetting } from '/@/hooks/setting';
import { AxiosCanceler } from '/@/utils/http/axios/axiosCancel';
/**
* The interface used to close the current page to complete the request when the route is switched
* @param router
*/
export function createHttpGuard(router: Router) {
const { removeAllHttpPending } = useProjectSetting();
let axiosCanceler: Nullable<AxiosCanceler>;
......
......@@ -4,6 +4,10 @@ import { Modal, notification } from 'ant-design-vue';
import { warn } from '/@/utils/log';
/**
* Used to close the message instance when the route is switched
* @param router
*/
export function createMessageGuard(router: Router) {
const { closeMessageOnSwitch } = useProjectSetting();
......
......@@ -6,7 +6,6 @@ import { createRouter, createWebHashHistory } from 'vue-router';
import { createGuard } from './guard/';
import { basicRoutes } from './routes/';
import { scrollBehavior } from './scrollBehavior';
import { REDIRECT_NAME } from './constant';
// app router
......@@ -14,7 +13,7 @@ const router = createRouter({
history: createWebHashHistory(),
routes: (basicRoutes as unknown) as RouteRecordRaw[],
strict: true,
scrollBehavior: scrollBehavior,
scrollBehavior: () => ({ left: 0, top: 0 }),
});
// reset router
......
// see https://github.com/vuejs/vue-router-next/blob/master/playground/scrollWaiter.ts
import type { RouteLocationNormalized } from 'vue-router';
// class ScrollQueue {
// private resolve: (() => void) | null = null;
// private promise: Promise<any> | null = null;
// add() {
// this.promise = new Promise((resolve) => {
// this.resolve = resolve as () => void;
// });
// }
// flush() {
// this.resolve && this.resolve();
// this.resolve = null;
// this.promise = null;
// }
// async wait() {
// await this.promise;
// }
// }
// const scrollWaiter = new ScrollQueue();
/**
* Handles the scroll behavior on route navigation
*
* @param {object} to Route object of next page
* @param {object} from Route object of previous page
* @param {object} savedPosition Used by popstate navigations
* @returns {(object|boolean)} Scroll position or `false`
*/
// @ts-ignore
export async function scrollBehavior(to, from, savedPosition) {
// await scrollWaiter.wait();
// Use predefined scroll behavior if defined, defaults to no scroll behavior
const behavior = 'smooth';
// Returning the `savedPosition` (if available) will result in a native-like
// behavior when navigating with back/forward buttons
if (savedPosition) {
return { ...savedPosition, behavior };
}
// Scroll to anchor by returning the selector
if (to.hash) {
return { el: decodeURI(to.hash), behavior };
}
// Check if any matched route config has meta that discourages scrolling to top
if (to.matched.some((m: RouteLocationNormalized) => m.meta.scrollToTop === false)) {
// Leave scroll as it is
return false;
}
// Always scroll to top
return { left: 0, top: 0, behavior };
}
......@@ -89,12 +89,5 @@ export interface MenuModule {
menu: Menu;
}
// interface RouteModule {
// layout: AppRouteRecordRaw;
// routes: AppRouteRecordRaw[];
// children?: AppRouteRecordRaw[];
// component?: Component;
// }
// export type AppRouteModule = RouteModule | AppRouteRecordRaw;
export type AppRouteModule = AppRouteRecordRaw;
import type { App } from 'vue';
import {
createStore,
// createLogger, Plugin
} from 'vuex';
import { createStore } from 'vuex';
import { config } from 'vuex-module-decorators';
import { isDevMode } from '/@/utils/env';
config.rawError = true;
const isDev = isDevMode();
// const plugins: Plugin<any>[] = isDev ? [createLogger()] : [];
const store = createStore({
// modules: {},
strict: isDev,
// plugins,
strict: isDevMode(),
});
export function setupStore(app: App<Element>) {
......
import CryptoES from 'crypto-es';
export interface EncryptionParams {
key: string;
iv: string;
}
export class Encryption {
private key;
......@@ -16,7 +18,7 @@ export class Encryption {
get getOptions(): CryptoES.lib.CipherCfg {
return {
mode: CryptoES.mode.CBC as any,
mode: CryptoES.mode.CBC,
padding: CryptoES.pad.Pkcs7,
iv: this.iv,
};
......
<template>
<Form class="p-4" :model="formData" :rules="getFormRules" ref="formRef">
<FormItem name="account" class="enter-x">
<Input size="large" v-model:value="formData.account" :placeholder="t('sys.login.userName')" />
</FormItem>
<template v-if="getShow">
<LoginFormTitle class="enter-x" />
<Form class="p-4 enter-x" :model="formData" :rules="getFormRules" ref="formRef">
<FormItem name="account" class="enter-x">
<Input
size="large"
v-model:value="formData.account"
:placeholder="t('sys.login.userName')"
/>
</FormItem>
<FormItem name="mobile" class="enter-x">
<Input size="large" v-model:value="formData.mobile" :placeholder="t('sys.login.mobile')" />
</FormItem>
<FormItem name="sms" class="enter-x">
<CountdownInput
size="large"
v-model:value="formData.sms"
:placeholder="t('sys.login.smsCode')"
/>
</FormItem>
<FormItem name="mobile" class="enter-x">
<Input size="large" v-model:value="formData.mobile" :placeholder="t('sys.login.mobile')" />
</FormItem>
<FormItem name="sms" class="enter-x">
<CountdownInput
size="large"
v-model:value="formData.sms"
:placeholder="t('sys.login.smsCode')"
/>
</FormItem>
<FormItem class="enter-x">
<Button
type="primary"
size="large"
block
@click="handleReset"
:loading="loading"
class="enter-x"
>
{{ t('common.resetText') }}
</Button>
<Button size="large" block class="mt-4 enter-x" @click="handleBackLogin">
{{ t('sys.login.backSignIn') }}
</Button>
</FormItem>
</Form>
<FormItem class="enter-x">
<Button type="primary" size="large" block @click="handleReset" :loading="loading">
{{ t('common.resetText') }}
</Button>
<Button size="large" block class="mt-4" @click="handleBackLogin">
{{ t('sys.login.backSignIn') }}
</Button>
</FormItem>
</Form>
</template>
</template>
<script lang="ts">
import { defineComponent, reactive, ref } from 'vue';
import { defineComponent, reactive, ref, computed, unref } from 'vue';
import LoginFormTitle from './LoginFormTitle.vue';
import { Form, Input, Button } from 'ant-design-vue';
import { CountdownInput } from '/@/components/CountDown';
import { useI18n } from '/@/hooks/web/useI18n';
import { LoginStateEnum, useLoginState, useFormRules, useFormValid } from './useLogin';
import { useLoginState, useFormRules, useFormValid, LoginStateEnum } from './useLogin';
export default defineComponent({
name: 'ForgetPasswordForm',
......@@ -49,10 +50,11 @@
FormItem: Form.Item,
Input,
CountdownInput,
LoginFormTitle,
},
setup() {
const { t } = useI18n();
const { setLoginState } = useLoginState();
const { handleBackLogin, getLoginState } = useLoginState();
const { getFormRules } = useFormRules();
const formRef = ref<any>(null);
......@@ -66,16 +68,14 @@
const { validForm } = useFormValid(formRef);
const getShow = computed(() => unref(getLoginState) === LoginStateEnum.RESET_PASSWORD);
async function handleReset() {
const data = await validForm();
if (!data) return;
console.log(data);
}
function handleBackLogin() {
setLoginState(LoginStateEnum.LOGIN);
}
return {
t,
formRef,
......@@ -84,6 +84,7 @@
handleReset,
loading,
handleBackLogin,
getShow,
};
},
});
......
......@@ -9,7 +9,7 @@
<AppLogo :alwaysShowTitle="true" />
</span>
<div class="container relative h-full py-2 mx-auto sm:px-10">
<div class="container relative h-full py-2 mx-auto sm:px-10 -enter-x">
<div class="flex h-full">
<div class="hidden xl:flex xl:flex-col xl:w-6/12 min-h-full mr-4 pl-4">
<AppLogo class="-enter-x" />
......@@ -31,14 +31,11 @@
<div
class="my-auto mx-auto xl:ml-20 bg-white xl:bg-transparent px-5 py-8 sm:px-8 xl:p-0 rounded-md shadow-md xl:shadow-none w-full sm:w-3/4 lg:w-2/4 xl:w-auto enter-x relative"
>
<h2 class="font-bold text-2xl xl:text-3xl enter-x text-center xl:text-left mb-6">
{{ getFormTitle }}
</h2>
<LoginForm v-show="getShowLogin" />
<ForgetPasswordForm v-if="getShowResetPassword" />
<RegisterForm v-if="getShowRegister" />
<MobileForm v-if="getShowMobile" />
<QrCodeForm v-if="getShowQrCode" />
<LoginForm />
<ForgetPasswordForm />
<RegisterForm />
<MobileForm />
<QrCodeForm />
</div>
</div>
</div>
......@@ -59,7 +56,6 @@
import { useGlobSetting, useProjectSetting } from '/@/hooks/setting';
import { useI18n } from '/@/hooks/web/useI18n';
import { useDesign } from '/@/hooks/web/useDesign';
import { useShowLoginForm, useFormTitle } from './useLogin';
export default defineComponent({
name: 'Login',
......@@ -74,7 +70,6 @@
},
setup() {
const globSetting = useGlobSetting();
const { getFormTitle } = useFormTitle();
const { prefixCls } = useDesign('login');
const { locale } = useProjectSetting();
const { t } = useI18n();
......@@ -84,8 +79,6 @@
prefixCls,
title: computed(() => globSetting?.title ?? ''),
showLocale: computed(() => locale.show),
getFormTitle,
...useShowLoginForm(),
};
},
});
......
<template>
<Form class="p-4" :model="formData" :rules="getFormRules" ref="formRef">
<LoginFormTitle v-show="getShow" class="enter-x" />
<Form class="p-4 enter-x" :model="formData" :rules="getFormRules" ref="formRef" v-show="getShow">
<FormItem name="account" class="enter-x">
<Input size="large" v-model:value="formData.account" :placeholder="t('sys.login.userName')" />
</FormItem>
......@@ -32,14 +33,7 @@
</ARow>
<FormItem class="enter-x">
<Button
type="primary"
size="large"
block
@click="handleLogin"
:loading="loading"
class="enter-x"
>
<Button type="primary" size="large" block @click="handleLogin" :loading="loading">
{{ t('sys.login.loginButton') }}
</Button>
<!-- <Button size="large" class="mt-4 enter-x" block @click="handleRegister">
......@@ -64,7 +58,7 @@
</ACol>
</ARow>
<Divider>{{ t('sys.login.otherSignIn') }}</Divider>
<Divider class="enter-x">{{ t('sys.login.otherSignIn') }}</Divider>
<div class="flex justify-evenly enter-x" :class="`${prefixCls}-sign-in-way`">
<GithubFilled />
......@@ -76,7 +70,7 @@
</Form>
</template>
<script lang="ts">
import { defineComponent, reactive, ref, toRaw } from 'vue';
import { defineComponent, reactive, ref, toRaw, unref, computed } from 'vue';
import { Checkbox, Form, Input, Row, Col, Button, Divider } from 'ant-design-vue';
import {
......@@ -86,6 +80,7 @@
GoogleCircleFilled,
TwitterCircleFilled,
} from '@ant-design/icons-vue';
import LoginFormTitle from './LoginFormTitle.vue';
import { useI18n } from '/@/hooks/web/useI18n';
import { useMessage } from '/@/hooks/web/useMessage';
......@@ -97,15 +92,16 @@
export default defineComponent({
name: 'LoginForm',
components: {
[Col.name]: Col,
[Row.name]: Row,
Checkbox,
Button,
Form,
FormItem: Form.Item,
Input,
Divider,
LoginFormTitle,
InputPassword: Input.Password,
[Col.name]: Col,
[Row.name]: Row,
GithubFilled,
WechatFilled,
AlipayCircleFilled,
......@@ -117,7 +113,7 @@
const { notification } = useMessage();
const { prefixCls } = useDesign('login');
const { setLoginState } = useLoginState();
const { setLoginState, getLoginState } = useLoginState();
const { getFormRules } = useFormRules();
const formRef = ref<any>(null);
......@@ -131,6 +127,8 @@
const { validForm } = useFormValid(formRef);
const getShow = computed(() => unref(getLoginState) === LoginStateEnum.LOGIN);
async function handleLogin() {
const data = await validForm();
if (!data) return;
......@@ -165,6 +163,7 @@
loading,
setLoginState,
LoginStateEnum,
getShow,
};
},
});
......
<template>
<h2 class="font-bold text-2xl xl:text-3xl enter-x text-center xl:text-left mb-6">
{{ getFormTitle }}
</h2>
</template>
<script lang="ts">
import { defineComponent, computed, unref } from 'vue';
import { useI18n } from '/@/hooks/web/useI18n';
import { LoginStateEnum, useLoginState } from './useLogin';
export default defineComponent({
name: 'LoginFormTitle',
setup() {
const { t } = useI18n();
const { getLoginState } = useLoginState();
const getFormTitle = computed(() => {
const titleObj = {
[LoginStateEnum.RESET_PASSWORD]: t('sys.login.forgetFormTitle'),
[LoginStateEnum.LOGIN]: t('sys.login.signInFormTitle'),
[LoginStateEnum.REGISTER]: t('sys.login.signUpFormTitle'),
[LoginStateEnum.MOBILE]: t('sys.login.mobileSignInFormTitle'),
[LoginStateEnum.QR_CODE]: t('sys.login.qrSignInFormTitle'),
};
return titleObj[unref(getLoginState)];
});
return {
getFormTitle,
};
},
});
</script>
<template>
<Form class="p-4" :model="formData" :rules="getFormRules" ref="formRef">
<FormItem name="mobile" class="enter-x">
<Input size="large" v-model:value="formData.mobile" :placeholder="t('sys.login.mobile')" />
</FormItem>
<FormItem name="sms" class="enter-x">
<CountdownInput
size="large"
v-model:value="formData.sms"
:placeholder="t('sys.login.smsCode')"
/>
</FormItem>
<template v-if="getShow">
<LoginFormTitle class="enter-x" />
<Form class="p-4 enter-x" :model="formData" :rules="getFormRules" ref="formRef">
<FormItem name="mobile" class="enter-x">
<Input size="large" v-model:value="formData.mobile" :placeholder="t('sys.login.mobile')" />
</FormItem>
<FormItem name="sms" class="enter-x">
<CountdownInput
size="large"
v-model:value="formData.sms"
:placeholder="t('sys.login.smsCode')"
/>
</FormItem>
<FormItem class="enter-x">
<Button
type="primary"
size="large"
block
@click="handleLogin"
:loading="loading"
class="enter-x"
>
{{ t('sys.login.loginButton') }}
</Button>
<Button size="large" block class="mt-4 enter-x" @click="handleBackLogin">
{{ t('sys.login.backSignIn') }}
</Button>
</FormItem>
</Form>
<FormItem class="enter-x">
<Button type="primary" size="large" block @click="handleLogin" :loading="loading">
{{ t('sys.login.loginButton') }}
</Button>
<Button size="large" block class="mt-4" @click="handleBackLogin">
{{ t('sys.login.backSignIn') }}
</Button>
</FormItem>
</Form>
</template>
</template>
<script lang="ts">
import { defineComponent, reactive, ref } from 'vue';
import { defineComponent, reactive, ref, computed, unref } from 'vue';
import { Form, Input, Button } from 'ant-design-vue';
import { CountdownInput } from '/@/components/CountDown';
import LoginFormTitle from './LoginFormTitle.vue';
import { useI18n } from '/@/hooks/web/useI18n';
import { LoginStateEnum, useLoginState, useFormRules, useFormValid } from './useLogin';
import { useLoginState, useFormRules, useFormValid, LoginStateEnum } from './useLogin';
export default defineComponent({
name: 'MobileForm',
......@@ -45,10 +42,11 @@
FormItem: Form.Item,
Input,
CountdownInput,
LoginFormTitle,
},
setup() {
const { t } = useI18n();
const { setLoginState } = useLoginState();
const { handleBackLogin, getLoginState } = useLoginState();
const { getFormRules } = useFormRules();
const formRef = ref<any>(null);
......@@ -61,16 +59,14 @@
const { validForm } = useFormValid(formRef);
const getShow = computed(() => unref(getLoginState) === LoginStateEnum.MOBILE);
async function handleLogin() {
const data = await validForm();
if (!data) return;
console.log(data);
}
function handleBackLogin() {
setLoginState(LoginStateEnum.LOGIN);
}
return {
t,
formRef,
......@@ -79,6 +75,7 @@
handleLogin,
loading,
handleBackLogin,
getShow,
};
},
});
......
<template>
<div class="enter-x min-w-64 min-h-64">
<QrCode :value="qrCodeUrl" class="enter-x flex justify-center xl:justify-start" :width="280" />
<Divider>{{ t('sys.login.scanSign') }}</Divider>
<Button size="large" block class="mt-4 enter-x" @click="handleBackLogin">
{{ t('sys.login.backSignIn') }}
</Button>
</div>
<template v-if="getShow">
<LoginFormTitle class="enter-x" />
<div class="enter-x min-w-64 min-h-64">
<QrCode
:value="qrCodeUrl"
class="enter-x flex justify-center xl:justify-start"
:width="280"
/>
<Divider class="enter-x">{{ t('sys.login.scanSign') }}</Divider>
<Button size="large" block class="mt-4 enter-x" @click="handleBackLogin">
{{ t('sys.login.backSignIn') }}
</Button>
</div>
</template>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { defineComponent, computed, unref } from 'vue';
import LoginFormTitle from './LoginFormTitle.vue';
import { Button, Divider } from 'ant-design-vue';
import { QrCode } from '/@/components/Qrcode/index';
import { useI18n } from '/@/hooks/web/useI18n';
import { LoginStateEnum, useLoginState } from './useLogin';
import { QrCode } from '/@/components/Qrcode/index';
import { useLoginState, LoginStateEnum } from './useLogin';
const qrCodeUrl = 'https://vvbin.cn/next/login';
export default defineComponent({
name: 'QrCodeForm',
......@@ -22,18 +31,19 @@
Button,
QrCode,
Divider,
LoginFormTitle,
},
setup() {
const { t } = useI18n();
const { setLoginState } = useLoginState();
const { handleBackLogin, getLoginState } = useLoginState();
const getShow = computed(() => unref(getLoginState) === LoginStateEnum.QR_CODE);
function handleBackLogin() {
setLoginState(LoginStateEnum.LOGIN);
}
return {
t,
handleBackLogin,
qrCodeUrl,
getShow,
};
},
});
......
<template>
<Form class="p-4" :model="formData" :rules="getFormRules" ref="formRef">
<FormItem name="account" class="enter-x">
<Input size="large" v-model:value="formData.account" :placeholder="t('sys.login.userName')" />
</FormItem>
<FormItem name="mobile" class="enter-x">
<Input size="large" v-model:value="formData.mobile" :placeholder="t('sys.login.mobile')" />
</FormItem>
<FormItem name="sms" class="enter-x">
<CountdownInput
size="large"
v-model:value="formData.sms"
:placeholder="t('sys.login.smsCode')"
/>
</FormItem>
<FormItem name="password" class="enter-x">
<StrengthMeter
size="large"
v-model:value="formData.password"
:placeholder="t('sys.login.password')"
/>
</FormItem>
<FormItem name="confirmPassword" class="enter-x">
<InputPassword
size="large"
visibilityToggle
v-model:value="formData.confirmPassword"
:placeholder="t('sys.login.confirmPassword')"
/>
</FormItem>
<template v-if="getShow">
<LoginFormTitle class="enter-x" />
<Form class="p-4 enter-x" :model="formData" :rules="getFormRules" ref="formRef">
<FormItem name="account" class="enter-x">
<Input
size="large"
v-model:value="formData.account"
:placeholder="t('sys.login.userName')"
/>
</FormItem>
<FormItem name="mobile" class="enter-x">
<Input size="large" v-model:value="formData.mobile" :placeholder="t('sys.login.mobile')" />
</FormItem>
<FormItem name="sms" class="enter-x">
<CountdownInput
size="large"
v-model:value="formData.sms"
:placeholder="t('sys.login.smsCode')"
/>
</FormItem>
<FormItem name="password" class="enter-x">
<StrengthMeter
size="large"
v-model:value="formData.password"
:placeholder="t('sys.login.password')"
/>
</FormItem>
<FormItem name="confirmPassword" class="enter-x">
<InputPassword
size="large"
visibilityToggle
v-model:value="formData.confirmPassword"
:placeholder="t('sys.login.confirmPassword')"
/>
</FormItem>
<FormItem class="enter-x" name="policy">
<!-- No logic, you need to deal with it yourself -->
<Checkbox v-model:checked="formData.policy" size="small">
{{ t('sys.login.policy') }}
</Checkbox>
</FormItem>
<FormItem class="enter-x" name="policy">
<!-- No logic, you need to deal with it yourself -->
<Checkbox v-model:checked="formData.policy" size="small">
{{ t('sys.login.policy') }}
</Checkbox>
</FormItem>
<Button
type="primary"
size="large"
block
@click="handleReset"
:loading="loading"
class="enter-x"
>
{{ t('sys.login.registerButton') }}
</Button>
<Button size="large" block class="mt-4 enter-x" @click="handleBackLogin">
{{ t('sys.login.backSignIn') }}
</Button>
</Form>
<Button
type="primary"
class="enter-x"
size="large"
block
@click="handleReset"
:loading="loading"
>
{{ t('sys.login.registerButton') }}
</Button>
<Button size="large" block class="enter-x mt-4" @click="handleBackLogin">
{{ t('sys.login.backSignIn') }}
</Button>
</Form>
</template>
</template>
<script lang="ts">
import { defineComponent, reactive, ref } from 'vue';
import { defineComponent, reactive, ref, unref, computed } from 'vue';
import LoginFormTitle from './LoginFormTitle.vue';
import { Form, Input, Button, Checkbox } from 'ant-design-vue';
import { StrengthMeter } from '/@/components/StrengthMeter';
import { CountdownInput } from '/@/components/CountDown';
import { useI18n } from '/@/hooks/web/useI18n';
import { LoginStateEnum, useLoginState, useFormRules, useFormValid } from './useLogin';
import { useLoginState, useFormRules, useFormValid, LoginStateEnum } from './useLogin';
export default defineComponent({
name: 'RegisterPasswordForm',
......@@ -72,10 +80,11 @@
Checkbox,
StrengthMeter,
CountdownInput,
LoginFormTitle,
},
setup() {
const { t } = useI18n();
const { setLoginState } = useLoginState();
const { handleBackLogin, getLoginState } = useLoginState();
const formRef = ref<any>(null);
const loading = ref(false);
......@@ -92,16 +101,14 @@
const { getFormRules } = useFormRules(formData);
const { validForm } = useFormValid(formRef);
const getShow = computed(() => unref(getLoginState) === LoginStateEnum.REGISTER);
async function handleReset() {
const data = await validForm();
if (!data) return;
console.log(data);
}
function handleBackLogin() {
setLoginState(LoginStateEnum.LOGIN);
}
return {
t,
formRef,
......@@ -110,6 +117,7 @@
handleReset,
loading,
handleBackLogin,
getShow,
};
},
});
......
......@@ -12,22 +12,6 @@ export enum LoginStateEnum {
const currentState = ref(LoginStateEnum.LOGIN);
export function useFormTitle() {
const { t } = useI18n();
const getFormTitle = computed(() => {
const titleObj = {
[LoginStateEnum.RESET_PASSWORD]: t('sys.login.forgetFormTitle'),
[LoginStateEnum.LOGIN]: t('sys.login.signInFormTitle'),
[LoginStateEnum.REGISTER]: t('sys.login.signUpFormTitle'),
[LoginStateEnum.MOBILE]: t('sys.login.mobileSignInFormTitle'),
[LoginStateEnum.QR_CODE]: t('sys.login.qrSignInFormTitle'),
};
return titleObj[unref(currentState)];
});
return { getFormTitle };
}
export function useLoginState() {
function setLoginState(state: LoginStateEnum) {
currentState.value = state;
......@@ -35,19 +19,11 @@ export function useLoginState() {
const getLoginState = computed(() => currentState.value);
return { setLoginState, getLoginState };
}
export function useShowLoginForm() {
const getShowLogin = computed(() => unref(currentState) === LoginStateEnum.LOGIN);
const getShowResetPassword = computed(
() => unref(currentState) === LoginStateEnum.RESET_PASSWORD
);
const getShowRegister = computed(() => unref(currentState) === LoginStateEnum.REGISTER);
const getShowMobile = computed(() => unref(currentState) === LoginStateEnum.MOBILE);
const getShowQrCode = computed(() => unref(currentState) === LoginStateEnum.QR_CODE);
function handleBackLogin() {
setLoginState(LoginStateEnum.LOGIN);
}
return { getShowLogin, getShowResetPassword, getShowRegister, getShowMobile, getShowQrCode };
return { setLoginState, getLoginState, handleBackLogin };
}
export function useFormValid<T extends Object = any>(formRef: Ref<any>) {
......@@ -96,6 +72,7 @@ export function useFormRules(formData?: Recordable) {
mobile: mobileFormRule,
};
switch (unref(currentState)) {
// register form rules
case LoginStateEnum.REGISTER:
return {
account: accountFormRule,
......@@ -106,13 +83,19 @@ export function useFormRules(formData?: Recordable) {
policy: [{ validator: validatePolicy, trigger: 'change' }],
...mobileRule,
};
// reset password form rules
case LoginStateEnum.RESET_PASSWORD:
return {
account: accountFormRule,
...mobileRule,
};
// mobile form rules
case LoginStateEnum.MOBILE:
return mobileRule;
// login form rules
default:
return {
account: accountFormRule,
......
......@@ -25,11 +25,6 @@
"/@/*": ["src/*"]
}
},
"plugins": [
{
"name": "@vuedx/typescript-plugin-vue"
}
],
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
"exclude": ["node_modules", "dist", "**/*.js"]
}
......@@ -10,14 +10,13 @@ export default defineConfig({
extend: {
colors,
},
// screen: {
// sm: '576px',
// md: '768px',
// lg: '992px',
// xl: '1200px',
// '2xl': '1600px',
// },
screens: {
sm: '576px',
md: '768px',
lg: '992px',
xl: '1200px',
'2xl': '1600px',
},
},
});
......
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册