mirror of https://github.com/halo-dev/halo
refactor: layout of login related page (#5413)
#### What type of PR is this? /area ui /kind improvement /milestone 2.13.0 #### What this PR does / why we need it: 优化登录相关页面的布局,修复在不同分辨率下的样式问题。 #### Which issue(s) this PR fixes: Fixes https://github.com/halo-dev/halo/issues/5346 #### Special notes for your reviewer: 测试登录或者注册页面,任意放大或者缩小页面,观察页面样式是否正常。 #### Does this PR introduce a user-facing change? ```release-note 优化登录相关页面的布局,修复在不同分辨率下的样式问题。 ```pull/5422/head^2
parent
2bfa20d316
commit
827030dd68
|
@ -0,0 +1,13 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import IconLogo from "~icons/core/logo?width=5rem&height=2rem";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="flex h-screen flex-col items-center overflow-auto bg-white/90 pt-[30vh]"
|
||||||
|
>
|
||||||
|
<IconLogo class="mb-8 flex-none" />
|
||||||
|
|
||||||
|
<RouterView />
|
||||||
|
</div>
|
||||||
|
</template>
|
|
@ -1,98 +0,0 @@
|
||||||
<script lang="ts" setup>
|
|
||||||
import { computed, watch } from "vue";
|
|
||||||
import IconLogo from "~icons/core/logo?width=5rem&height=2rem";
|
|
||||||
import LoginForm from "@/components/login/LoginForm.vue";
|
|
||||||
import { useRouteQuery } from "@vueuse/router";
|
|
||||||
import SignupForm from "@/components/signup/SignupForm.vue";
|
|
||||||
import SocialAuthProviders from "@/components/login/SocialAuthProviders.vue";
|
|
||||||
import { useGlobalInfoFetch } from "@console/composables/use-global-info";
|
|
||||||
import { useTitle } from "@vueuse/core";
|
|
||||||
import { useI18n } from "vue-i18n";
|
|
||||||
import { AppName } from "@/constants/app";
|
|
||||||
import MdiKeyboardBackspace from "~icons/mdi/keyboard-backspace";
|
|
||||||
import LocaleChange from "@/components/common/LocaleChange.vue";
|
|
||||||
|
|
||||||
const { globalInfo } = useGlobalInfoFetch();
|
|
||||||
const { t } = useI18n();
|
|
||||||
|
|
||||||
const SIGNUP_TYPE = "signup";
|
|
||||||
|
|
||||||
function onLoginSucceed() {
|
|
||||||
window.location.reload();
|
|
||||||
}
|
|
||||||
|
|
||||||
function onSignupSucceed() {
|
|
||||||
window.location.href = "/uc";
|
|
||||||
}
|
|
||||||
|
|
||||||
const type = useRouteQuery<string>("type", "");
|
|
||||||
|
|
||||||
function handleChangeType() {
|
|
||||||
type.value = type.value === SIGNUP_TYPE ? "" : SIGNUP_TYPE;
|
|
||||||
}
|
|
||||||
|
|
||||||
const isLoginType = computed(() => type.value !== SIGNUP_TYPE);
|
|
||||||
|
|
||||||
// page title
|
|
||||||
const title = useTitle();
|
|
||||||
watch(
|
|
||||||
() => type.value,
|
|
||||||
(value) => {
|
|
||||||
const routeTitle = t(
|
|
||||||
`core.${value === SIGNUP_TYPE ? SIGNUP_TYPE : "login"}.title`
|
|
||||||
);
|
|
||||||
title.value = [routeTitle, AppName].join(" - ");
|
|
||||||
}
|
|
||||||
);
|
|
||||||
</script>
|
|
||||||
<template>
|
|
||||||
<div class="flex h-screen flex-col items-center bg-white/90 pt-[30vh]">
|
|
||||||
<IconLogo class="mb-8" />
|
|
||||||
<div class="flex w-72 flex-col">
|
|
||||||
<SignupForm v-if="type === 'signup'" @succeed="onSignupSucceed" />
|
|
||||||
<LoginForm v-else @succeed="onLoginSucceed" />
|
|
||||||
<SocialAuthProviders />
|
|
||||||
<div class="flex justify-center gap-2 pt-3.5 text-xs">
|
|
||||||
<div v-if="globalInfo?.allowRegistration" class="space-x-0.5">
|
|
||||||
<span class="text-slate-500">
|
|
||||||
{{
|
|
||||||
isLoginType
|
|
||||||
? $t("core.login.operations.signup.label")
|
|
||||||
: $t("core.login.operations.return_login.label")
|
|
||||||
}},
|
|
||||||
</span>
|
|
||||||
<span
|
|
||||||
class="cursor-pointer text-secondary hover:text-gray-600"
|
|
||||||
@click="handleChangeType"
|
|
||||||
>
|
|
||||||
{{
|
|
||||||
isLoginType
|
|
||||||
? $t("core.login.operations.signup.button")
|
|
||||||
: $t("core.login.operations.return_login.button")
|
|
||||||
}}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<RouterLink
|
|
||||||
:to="{ name: 'ResetPassword' }"
|
|
||||||
class="text-secondary hover:text-gray-600"
|
|
||||||
>
|
|
||||||
{{ $t("core.login.operations.reset_password.button") }}
|
|
||||||
</RouterLink>
|
|
||||||
</div>
|
|
||||||
<div class="flex justify-center pt-3.5">
|
|
||||||
<a
|
|
||||||
class="inline-flex items-center gap-0.5 text-xs text-gray-600 hover:text-gray-900"
|
|
||||||
href="/"
|
|
||||||
>
|
|
||||||
<MdiKeyboardBackspace class="!h-3.5 !w-3.5" />
|
|
||||||
<span> {{ $t("core.login.operations.return_site") }} </span>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="bottom-0 mb-10 mt-auto flex items-center justify-center gap-2.5"
|
|
||||||
>
|
|
||||||
<LocaleChange />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
|
@ -3,10 +3,8 @@ import BasicLayout from "@console/layouts/BasicLayout.vue";
|
||||||
import UserStatsWidget from "./widgets/UserStatsWidget.vue";
|
import UserStatsWidget from "./widgets/UserStatsWidget.vue";
|
||||||
import UserList from "./UserList.vue";
|
import UserList from "./UserList.vue";
|
||||||
import UserDetail from "./UserDetail.vue";
|
import UserDetail from "./UserDetail.vue";
|
||||||
import Login from "./Login.vue";
|
|
||||||
import { IconUserSettings } from "@halo-dev/components";
|
import { IconUserSettings } from "@halo-dev/components";
|
||||||
import { markRaw } from "vue";
|
import { markRaw } from "vue";
|
||||||
import Binding from "./Binding.vue";
|
|
||||||
import NotificationWidget from "./widgets/NotificationWidget.vue";
|
import NotificationWidget from "./widgets/NotificationWidget.vue";
|
||||||
|
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
|
@ -15,22 +13,6 @@ export default definePlugin({
|
||||||
NotificationWidget,
|
NotificationWidget,
|
||||||
},
|
},
|
||||||
routes: [
|
routes: [
|
||||||
{
|
|
||||||
path: "/login",
|
|
||||||
name: "Login",
|
|
||||||
component: Login,
|
|
||||||
meta: {
|
|
||||||
title: "core.login.title",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "/binding/:provider",
|
|
||||||
name: "Binding",
|
|
||||||
component: Binding,
|
|
||||||
meta: {
|
|
||||||
title: "core.binding.title",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: "/users",
|
path: "/users",
|
||||||
name: "UsersRoot",
|
name: "UsersRoot",
|
||||||
|
|
|
@ -2,10 +2,13 @@ import type { RouteRecordRaw } from "vue-router";
|
||||||
import NotFound from "@/views/exceptions/NotFound.vue";
|
import NotFound from "@/views/exceptions/NotFound.vue";
|
||||||
import Forbidden from "@/views/exceptions/Forbidden.vue";
|
import Forbidden from "@/views/exceptions/Forbidden.vue";
|
||||||
import BasicLayout from "@console/layouts/BasicLayout.vue";
|
import BasicLayout from "@console/layouts/BasicLayout.vue";
|
||||||
|
import GatewayLayout from "@console/layouts/GatewayLayout.vue";
|
||||||
import Setup from "@console/views/system/Setup.vue";
|
import Setup from "@console/views/system/Setup.vue";
|
||||||
import Redirect from "@console/views/system/Redirect.vue";
|
import Redirect from "@console/views/system/Redirect.vue";
|
||||||
import SetupInitialData from "@console/views/system/SetupInitialData.vue";
|
import SetupInitialData from "@console/views/system/SetupInitialData.vue";
|
||||||
import ResetPassword from "@console/views/system/ResetPassword.vue";
|
import ResetPassword from "@console/views/system/ResetPassword.vue";
|
||||||
|
import Login from "@console/views/system/Login.vue";
|
||||||
|
import Binding from "@console/views/system/Binding.vue";
|
||||||
|
|
||||||
export const routes: Array<RouteRecordRaw> = [
|
export const routes: Array<RouteRecordRaw> = [
|
||||||
{
|
{
|
||||||
|
@ -24,14 +27,48 @@ export const routes: Array<RouteRecordRaw> = [
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "/login",
|
||||||
|
component: GatewayLayout,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "",
|
||||||
|
name: "Login",
|
||||||
|
component: Login,
|
||||||
|
meta: {
|
||||||
|
title: "core.login.title",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/binding/:provider",
|
||||||
|
component: GatewayLayout,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "",
|
||||||
|
name: "Binding",
|
||||||
|
component: Binding,
|
||||||
|
meta: {
|
||||||
|
title: "core.binding.title",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: "/setup",
|
path: "/setup",
|
||||||
|
component: GatewayLayout,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "",
|
||||||
name: "Setup",
|
name: "Setup",
|
||||||
component: Setup,
|
component: Setup,
|
||||||
meta: {
|
meta: {
|
||||||
title: "core.setup.title",
|
title: "core.setup.title",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: "/setup-initial-data",
|
path: "/setup-initial-data",
|
||||||
name: "SetupInitialData",
|
name: "SetupInitialData",
|
||||||
|
@ -47,12 +84,18 @@ export const routes: Array<RouteRecordRaw> = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/reset-password",
|
path: "/reset-password",
|
||||||
|
component: GatewayLayout,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "",
|
||||||
name: "ResetPassword",
|
name: "ResetPassword",
|
||||||
component: ResetPassword,
|
component: ResetPassword,
|
||||||
meta: {
|
meta: {
|
||||||
title: "core.reset_password.title",
|
title: "core.reset_password.title",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export default routes;
|
export default routes;
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, onBeforeMount, onMounted } from "vue";
|
import { computed, onBeforeMount, onMounted } from "vue";
|
||||||
import router from "@console/router";
|
import router from "@console/router";
|
||||||
import IconLogo from "~icons/core/logo?width=5rem&height=2rem";
|
|
||||||
import { useUserStore } from "@/stores/user";
|
import { useUserStore } from "@/stores/user";
|
||||||
import LoginForm from "@/components/login/LoginForm.vue";
|
import LoginForm from "@/components/login/LoginForm.vue";
|
||||||
import { useRoute } from "vue-router";
|
import { useRoute } from "vue-router";
|
||||||
|
@ -51,8 +50,6 @@ function handleChangeType() {
|
||||||
const isLoginType = computed(() => type.value !== "signup");
|
const isLoginType = computed(() => type.value !== "signup");
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="flex h-screen flex-col items-center bg-white/90 pt-[30vh]">
|
|
||||||
<IconLogo class="mb-8" />
|
|
||||||
<div class="flex w-72 flex-col">
|
<div class="flex w-72 flex-col">
|
||||||
<SignupForm
|
<SignupForm
|
||||||
v-if="type === 'signup'"
|
v-if="type === 'signup'"
|
||||||
|
@ -87,5 +84,4 @@ const isLoginType = computed(() => type.value !== "signup");
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
|
@ -0,0 +1,94 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed, watch } from "vue";
|
||||||
|
import LoginForm from "@/components/login/LoginForm.vue";
|
||||||
|
import { useRouteQuery } from "@vueuse/router";
|
||||||
|
import SignupForm from "@/components/signup/SignupForm.vue";
|
||||||
|
import SocialAuthProviders from "@/components/login/SocialAuthProviders.vue";
|
||||||
|
import { useGlobalInfoFetch } from "@console/composables/use-global-info";
|
||||||
|
import { useTitle } from "@vueuse/core";
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
|
import { AppName } from "@/constants/app";
|
||||||
|
import MdiKeyboardBackspace from "~icons/mdi/keyboard-backspace";
|
||||||
|
import LocaleChange from "@/components/common/LocaleChange.vue";
|
||||||
|
|
||||||
|
const { globalInfo } = useGlobalInfoFetch();
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const SIGNUP_TYPE = "signup";
|
||||||
|
|
||||||
|
function onLoginSucceed() {
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onSignupSucceed() {
|
||||||
|
window.location.href = "/uc";
|
||||||
|
}
|
||||||
|
|
||||||
|
const type = useRouteQuery<string>("type", "");
|
||||||
|
|
||||||
|
function handleChangeType() {
|
||||||
|
type.value = type.value === SIGNUP_TYPE ? "" : SIGNUP_TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isLoginType = computed(() => type.value !== SIGNUP_TYPE);
|
||||||
|
|
||||||
|
// page title
|
||||||
|
const title = useTitle();
|
||||||
|
watch(
|
||||||
|
() => type.value,
|
||||||
|
(value) => {
|
||||||
|
const routeTitle = t(
|
||||||
|
`core.${value === SIGNUP_TYPE ? SIGNUP_TYPE : "login"}.title`
|
||||||
|
);
|
||||||
|
title.value = [routeTitle, AppName].join(" - ");
|
||||||
|
}
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<div class="flex w-72 flex-col">
|
||||||
|
<SignupForm v-if="type === 'signup'" @succeed="onSignupSucceed" />
|
||||||
|
<LoginForm v-else @succeed="onLoginSucceed" />
|
||||||
|
<SocialAuthProviders />
|
||||||
|
<div class="flex justify-center gap-2 pt-3.5 text-xs">
|
||||||
|
<div v-if="globalInfo?.allowRegistration" class="space-x-0.5">
|
||||||
|
<span class="text-slate-500">
|
||||||
|
{{
|
||||||
|
isLoginType
|
||||||
|
? $t("core.login.operations.signup.label")
|
||||||
|
: $t("core.login.operations.return_login.label")
|
||||||
|
}},
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
class="cursor-pointer text-secondary hover:text-gray-600"
|
||||||
|
@click="handleChangeType"
|
||||||
|
>
|
||||||
|
{{
|
||||||
|
isLoginType
|
||||||
|
? $t("core.login.operations.signup.button")
|
||||||
|
: $t("core.login.operations.return_login.button")
|
||||||
|
}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<RouterLink
|
||||||
|
:to="{ name: 'ResetPassword' }"
|
||||||
|
class="text-secondary hover:text-gray-600"
|
||||||
|
>
|
||||||
|
{{ $t("core.login.operations.reset_password.button") }}
|
||||||
|
</RouterLink>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-center pt-3.5">
|
||||||
|
<a
|
||||||
|
class="inline-flex items-center gap-0.5 text-xs text-gray-600 hover:text-gray-900"
|
||||||
|
href="/"
|
||||||
|
>
|
||||||
|
<MdiKeyboardBackspace class="!h-3.5 !w-3.5" />
|
||||||
|
<span> {{ $t("core.login.operations.return_site") }} </span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="bottom-0 mb-10 mt-auto flex items-center justify-center gap-2.5 pt-3.5"
|
||||||
|
>
|
||||||
|
<LocaleChange />
|
||||||
|
</div>
|
||||||
|
</template>
|
|
@ -3,7 +3,6 @@ import { apiClient } from "@/utils/api-client";
|
||||||
import { Toast, VButton } from "@halo-dev/components";
|
import { Toast, VButton } from "@halo-dev/components";
|
||||||
import { ref } from "vue";
|
import { ref } from "vue";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
import IconLogo from "~icons/core/logo?width=5rem&height=2rem";
|
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
@ -38,8 +37,6 @@ const inputClasses = {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="flex h-screen flex-col items-center bg-white/90 pt-[30vh]">
|
|
||||||
<IconLogo class="mb-8" />
|
|
||||||
<div class="flex w-72 flex-col">
|
<div class="flex w-72 flex-col">
|
||||||
<FormKit
|
<FormKit
|
||||||
id="reset-password-form"
|
id="reset-password-form"
|
||||||
|
@ -81,5 +78,4 @@ const inputClasses = {
|
||||||
{{ $t("core.reset_password.operations.send.label") }}
|
{{ $t("core.reset_password.operations.send.label") }}
|
||||||
</VButton>
|
</VButton>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import IconLogo from "~icons/core/logo?width=5rem&height=2rem";
|
|
||||||
import { apiClient } from "@/utils/api-client";
|
import { apiClient } from "@/utils/api-client";
|
||||||
import { Toast, VButton } from "@halo-dev/components";
|
import { Toast, VButton } from "@halo-dev/components";
|
||||||
import { ref } from "vue";
|
import { ref } from "vue";
|
||||||
|
@ -44,8 +43,6 @@ const inputClasses = {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="flex h-screen flex-col items-center bg-white/90 pt-[30vh]">
|
|
||||||
<IconLogo class="mb-8" />
|
|
||||||
<div class="flex w-72 flex-col">
|
<div class="flex w-72 flex-col">
|
||||||
<FormKit
|
<FormKit
|
||||||
id="setup-form"
|
id="setup-form"
|
||||||
|
@ -121,10 +118,7 @@ const inputClasses = {
|
||||||
{{ $t("core.setup.operations.submit.button") }}
|
{{ $t("core.setup.operations.submit.button") }}
|
||||||
</VButton>
|
</VButton>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div class="bottom-0 mb-10 mt-auto flex items-center justify-center gap-2.5">
|
||||||
class="bottom-0 mb-10 mt-auto flex items-center justify-center gap-2.5"
|
|
||||||
>
|
|
||||||
<LocaleChange />
|
<LocaleChange />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -46,8 +46,10 @@ const inputClasses = {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="flex h-screen flex-col items-center bg-white/90 pt-[30vh]">
|
<div
|
||||||
<IconLogo class="mb-8" />
|
class="flex h-screen flex-col items-center overflow-auto bg-white/90 pt-[30vh]"
|
||||||
|
>
|
||||||
|
<IconLogo class="mb-8 flex-none" />
|
||||||
<div class="flex w-72 flex-col">
|
<div class="flex w-72 flex-col">
|
||||||
<FormKit
|
<FormKit
|
||||||
id="reset-password-form"
|
id="reset-password-form"
|
||||||
|
|
Loading…
Reference in New Issue