feat: 新增租户切换组件和css目录优化

This commit is contained in:
Ray.Hao
2025-12-15 08:04:05 +08:00
parent 6e0597437e
commit 62e0af68a6
23 changed files with 193 additions and 166 deletions

View File

@@ -19,7 +19,5 @@ VITE_MOCK_DEV_SERVER=false
# 多租户功能开关 # 多租户功能开关
# ============================================ # ============================================
# 是否启用多租户功能默认false # 是否启用多租户功能默认false
# true: 启用多租户,显示租户切换器,发送 tenant-id 请求头
# false: 禁用多租户隐藏租户相关UI不发送 tenant-id 请求头
# 注意前端开关需要与后端配置youlai.tenant.enabled保持一致 # 注意前端开关需要与后端配置youlai.tenant.enabled保持一致
VITE_APP_TENANT_ENABLED=false VITE_APP_TENANT_ENABLED=true

View File

@@ -1,4 +1,6 @@
import request from "@/utils/request"; import request from "@/utils/request";
import type { MenuTypeEnum } from "@/enums/business";
const MENU_BASE_URL = "/api/v1/menus"; const MENU_BASE_URL = "/api/v1/menus";
const MenuAPI = { const MenuAPI = {
@@ -42,7 +44,6 @@ export interface MenuQuery {
/** 搜索关键字 */ /** 搜索关键字 */
keywords?: string; keywords?: string;
} }
import type { MenuTypeEnum } from "@/enums/system/menu-enum";
export interface MenuVO { export interface MenuVO {
/** 子菜单 */ /** 子菜单 */
children?: MenuVO[]; children?: MenuVO[];

View File

@@ -20,7 +20,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ComponentSize } from "@/enums/settings/layout-enum"; import { ComponentSize } from "@/enums/settings";
import { useAppStore } from "@/store/modules/app-store"; import { useAppStore } from "@/store/modules/app-store";
const { t } = useI18n(); const { t } = useI18n();

View File

@@ -0,0 +1,35 @@
<template>
<el-select
v-if="tenantList.length > 0"
v-model="currentTenantIdRef"
placeholder="选择租户"
style="width: 180px"
@change="onChange"
>
<el-option v-for="item in tenantList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</template>
<script setup lang="ts">
import { computed } from "vue";
import { useTenantStoreHook } from "@/store/modules/tenant-store";
const emit = defineEmits<{
(e: "change", tenantId: number): void;
}>();
const tenantStore = useTenantStoreHook();
const tenantList = computed(() => tenantStore.tenantList);
const currentTenantIdRef = computed<number | null>({
get: () => tenantStore.currentTenantId,
set: (val) => {
tenantStore.currentTenantId = val;
},
});
function onChange(tenantId: number) {
emit("change", tenantId);
}
</script>

View File

@@ -20,7 +20,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { type RouteLocationNormalized } from "vue-router"; import { type RouteLocationNormalized } from "vue-router";
import { useSettingsStore, useTagsViewStore } from "@/store"; import { useSettingsStore, useTagsViewStore } from "@/store";
import variables from "@/styles/variables.scss"; import variables from "@/styles/variables.module.scss";
import Error404 from "@/views/error/404.vue"; import Error404 from "@/views/error/404.vue";
const { cachedViews } = toRefs(useTagsViewStore()); const { cachedViews } = toRefs(useTagsViewStore());

View File

@@ -33,7 +33,7 @@ import { SidebarColor } from "@/enums/settings";
import { useSettingsStore, useAppStore } from "@/store"; import { useSettingsStore, useAppStore } from "@/store";
import { isExternal } from "@/utils/index"; import { isExternal } from "@/utils/index";
import MenuItem from "./components/MenuItem.vue"; import MenuItem from "./components/MenuItem.vue";
import variables from "@/styles/variables.scss"; import variables from "@/styles/variables.module.scss";
const props = defineProps({ const props = defineProps({
data: { data: {

View File

@@ -39,7 +39,7 @@ defineOptions({
import { LocationQueryRaw, RouteRecordRaw } from "vue-router"; import { LocationQueryRaw, RouteRecordRaw } from "vue-router";
import { usePermissionStore, useAppStore, useSettingsStore } from "@/store"; import { usePermissionStore, useAppStore, useSettingsStore } from "@/store";
import variables from "@/styles/variables.scss"; import variables from "@/styles/variables.module.scss";
import { SidebarColor } from "@/enums/settings"; import { SidebarColor } from "@/enums/settings";
const router = useRouter(); const router = useRouter();

View File

@@ -29,7 +29,7 @@
<!-- 租户选择如果启用多租户 --> <!-- 租户选择如果启用多租户 -->
<div v-if="showTenantSelect" class="navbar-actions__item"> <div v-if="showTenantSelect" class="navbar-actions__item">
<TenantSelect /> <TenantSwitcher @change="handleTenantChange" />
</div> </div>
</template> </template>
@@ -83,7 +83,7 @@ import Fullscreen from "@/components/Fullscreen/index.vue";
import SizeSelect from "@/components/SizeSelect/index.vue"; import SizeSelect from "@/components/SizeSelect/index.vue";
import LangSelect from "@/components/LangSelect/index.vue"; import LangSelect from "@/components/LangSelect/index.vue";
import Notification from "@/components/Notification/index.vue"; import Notification from "@/components/Notification/index.vue";
import TenantSelect from "@/components/TenantSelect/index.vue"; import TenantSwitcher from "@/components/TenantSwitcher/index.vue";
import { useTenantStoreHook } from "@/store/modules/tenant-store"; import { useTenantStoreHook } from "@/store/modules/tenant-store";
const { t } = useI18n(); const { t } = useI18n();
@@ -113,6 +113,18 @@ const showTenantSelect = computed(() => {
return true; return true;
}); });
function handleTenantChange(tenantId: number) {
tenantStore
.switchTenant(tenantId)
.then(() => {
ElMessage.success("切换租户成功");
window.location.reload();
})
.catch((error: any) => {
ElMessage.error(error?.message || "切换租户失败");
});
}
/** /**
* 打开个人中心页面 * 打开个人中心页面
*/ */

View File

@@ -14,7 +14,7 @@ import LeftLayout from "@/layouts/modes/left/index.vue";
import TopLayout from "@/layouts/modes/top/index.vue"; import TopLayout from "@/layouts/modes/top/index.vue";
import MixLayout from "@/layouts/modes/mix/index.vue"; import MixLayout from "@/layouts/modes/mix/index.vue";
import Settings from "./components/Settings/index.vue"; import Settings from "./components/Settings/index.vue";
import { LayoutMode } from "@/enums/settings/layout-enum"; import { LayoutMode } from "@/enums/settings";
import { defaultSettings } from "@/settings"; import { defaultSettings } from "@/settings";
const { currentLayout } = useLayout(); const { currentLayout } = useLayout();

View File

@@ -69,7 +69,7 @@ import TagsView from "../../components/TagsView/index.vue";
import AppMain from "../../components/AppMain/index.vue"; import AppMain from "../../components/AppMain/index.vue";
import MenuItem from "../../components/Menu/components/MenuItem.vue"; import MenuItem from "../../components/Menu/components/MenuItem.vue";
import Hamburger from "@/components/Hamburger/index.vue"; import Hamburger from "@/components/Hamburger/index.vue";
import variables from "@/styles/variables.scss"; import variables from "@/styles/variables.module.scss";
import { isExternal } from "@/utils/index"; import { isExternal } from "@/utils/index";
import { useAppStore, usePermissionStore } from "@/store"; import { useAppStore, usePermissionStore } from "@/store";

View File

@@ -11,7 +11,6 @@ import App from "./App.vue";
// ===== 样式导入 ===== // ===== 样式导入 =====
import "element-plus/theme-chalk/dark/css-vars.css"; import "element-plus/theme-chalk/dark/css-vars.css";
import "vxe-table/lib/style.css"; import "vxe-table/lib/style.css";
import "@/styles/dark/css-vars.css";
import "@/styles/index.scss"; import "@/styles/index.scss";
import "uno.css"; import "uno.css";
import "animate.css"; import "animate.css";

View File

@@ -24,7 +24,7 @@ export const defaultSettings: AppSettings = {
size: ComponentSize.DEFAULT, size: ComponentSize.DEFAULT,
// 语言 // 语言
language: LanguageEnum.ZH_CN, language: LanguageEnum.ZH_CN,
// 主题颜色 - 修改此值时需同步修改 src/styles/variables.scss // 主题颜色 - 修改此值时需同步修改 src/styles/element-plus-vars.scss
themeColor: "#4080FF", themeColor: "#4080FF",
// 是否显示水印 // 是否显示水印
showWatermark: false, showWatermark: false,
@@ -52,7 +52,7 @@ export const authConfig = {
} as const; } as const;
// 主题色预设 - 经典配色方案 // 主题色预设 - 经典配色方案
// 注意:修改默认主题色时,需要同步修改 src/styles/variables.scss 中的 primary.base 值 // 注意:修改默认主题色时,需要同步修改 src/styles/element-plus-vars.scss 中的 primary.base 值
export const themeColorPresets = [ export const themeColorPresets = [
"#4080FF", // Arco Design 蓝 - 现代感强 "#4080FF", // Arco Design 蓝 - 现代感强
"#1890FF", // Ant Design 蓝 - 经典商务 "#1890FF", // Ant Design 蓝 - 经典商务

View File

@@ -108,35 +108,38 @@ export const useTenantStore = defineStore("tenant", () => {
* *
* @param tenantId 目标租户ID * @param tenantId 目标租户ID
*/ */
function switchTenant(tenantId: number) { async function switchTenant(tenantId: number): Promise<void> {
return new Promise<void>((resolve, reject) => { try {
TenantAPI.switchTenant(tenantId) // 调用后端切换接口
.then((tenantInfo) => { const tenantInfo = await TenantAPI.switchTenant(tenantId);
// 后端返回切换后的租户信息
if (tenantInfo) { // 后端返回切换后的租户信息
setCurrentTenant(tenantInfo); if (tenantInfo) {
} else { setCurrentTenant(tenantInfo);
// 如果后端未返回,从租户列表中找到对应的租户信息 } else {
const tenant = tenantList.value.find((t) => t.id === tenantId); // 如果后端未返回,从租户列表中找到对应的租户信息
if (tenant) { const tenant = tenantList.value.find((t) => t.id === tenantId);
setCurrentTenant(tenant); if (tenant) {
setCurrentTenant(tenant);
} else {
// 如果列表中没有,重新获取租户信息
try {
const info = await TenantAPI.getCurrentTenant();
if (info) {
setCurrentTenant(info);
} else { } else {
// 如果列表中没有,重新获取租户信息 throw new Error("无法获取租户信息");
TenantAPI.getCurrentTenant()
.then((info) => {
if (info) {
setCurrentTenant(info);
}
})
.catch(console.error);
} }
} catch (error) {
console.error("获取租户信息失败:", error);
throw new Error("切换租户后无法获取租户信息");
} }
resolve(); }
}) }
.catch((error) => { } catch (error) {
reject(error); console.error("切换租户失败:", error);
}); throw error;
}); }
} }
/** /**
@@ -150,6 +153,13 @@ export const useTenantStore = defineStore("tenant", () => {
localStorage.removeItem(STORAGE_KEYS.TENANT_INFO); localStorage.removeItem(STORAGE_KEYS.TENANT_INFO);
} }
/**
* 设置租户列表
*/
function setTenantList(list: TenantInfo[]) {
tenantList.value = list || [];
}
// 恢复本地租户信息 // 恢复本地租户信息
restoreTenant(); restoreTenant();
@@ -159,6 +169,7 @@ export const useTenantStore = defineStore("tenant", () => {
tenantList, tenantList,
loadTenant, loadTenant,
fetchTenantList, fetchTenantList,
setTenantList,
setCurrentTenant, setCurrentTenant,
switchTenant, switchTenant,
clearTenant, clearTenant,

View File

@@ -1,7 +0,0 @@
/* 暗黑模式通过 CSS 自定义变量官方链接https://element-plus.org/zh-CN/guide/dark-mode.html#%E9%80%9A%E8%BF%87-css */
html.dark {
.el-table {
/* 自定义表格选中高亮时当前行的背景颜色 */
--el-table-current-row-bg-color: var(--el-fill-color-light);
}
}

View File

@@ -0,0 +1,30 @@
/**
* Element Plus 变量覆盖
*
* 此文件用于覆盖 Element Plus 的默认主题变量
* 需要在 element-plus.scss 中导入,而不是在 variables.scss 中
*/
@forward "element-plus/theme-chalk/src/common/var.scss" with (
$colors: (
"primary": (
// 默认主题色 - 修改此值时需同步修改 src/settings.ts 中的 themeColor
"base": #4080ff,
),
"success": (
"base": #23c343,
),
"warning": (
"base": #ff9a2e,
),
"danger": (
"base": #f76560,
),
"info": (
"base": #a9aeb8,
),
),
$bg-color: (
"page": #f5f8fd,
)
);

View File

@@ -1,3 +1,6 @@
// Element Plus 变量覆盖(必须在最前面)
@use "./element-plus-vars";
$border: 1px solid var(--el-border-color-light); $border: 1px solid var(--el-border-color-light);
/* el-dialog */ /* el-dialog */

View File

@@ -1,12 +1,12 @@
// 基础变量与主题色 // 1. 基础重置(补充 UnoCSS 预设未覆盖的全局样式)
@use "./variables";
// 基础重置与组件细化样式
@use "./reset"; @use "./reset";
@use "./element-plus";
// Vxe Table 主题覆写CSS 变量 + 自定义样式 // 2. 项目自定义主题变量CSS 变量 / SCSS 变量 / JS 导出
@use "./variables" as *;
// 3. UI 框架适配Element Plus & Vxe Table
@use "./element-plus";
@use "./vxe-table"; @use "./vxe-table";
// 业务通用样式 // 4. 业务通用样式
@use "./common"; @use "./common";

View File

@@ -1,3 +1,5 @@
// 全局基础重置:补充 UnoCSS 预设未覆盖的项目级样式
#app { #app {
width: 100%; width: 100%;
height: 100%; height: 100%;
@@ -36,11 +38,6 @@ body {
text-rendering: optimizelegibility; text-rendering: optimizelegibility;
} }
a {
color: inherit;
text-decoration: inherit;
}
img, img,
svg { svg {
display: inline-block; display: inline-block;

View File

@@ -0,0 +1,16 @@
/* stylelint-disable property-no-unknown */
// 通过 SCSS 变量导出给 JS/TS 使用的模块文件
// 注意:依赖 src/styles/variables.scss 中定义的 SCSS 变量
:export {
sidebar-width: $sidebar-width;
navbar-height: $navbar-height;
tags-view-height: $tags-view-height;
menu-background: $menu-background;
menu-text: $menu-text;
menu-active-text: $menu-active-text;
menu-hover: $menu-hover;
}
/* stylelint-enable property-no-unknown */

View File

@@ -1,29 +1,9 @@
@forward "element-plus/theme-chalk/src/common/var.scss" with ( /**
$colors: ( * 项目自定义主题变量CSS 变量 / SCSS 变量 / JS 导出)
"primary": ( * 与 Element Plus 主题变量覆盖element-plus-vars.scss职责分离
// 默认主题色 - 修改此值时需同步修改 src/settings.ts 中的 themeColor *
"base": #4080ff, * 注意:此文件以下划线开头,是 Sass partial不会被单独编译只能被其他文件导入
), */
"success": (
"base": #23c343,
),
"warning": (
"base": #ff9a2e,
),
"danger": (
"base": #f76560,
),
"info": (
"base": #a9aeb8,
),
),
$bg-color: (
"page": #f5f8fd,
)
);
/** 全局SCSS变量 */
:root { :root {
--menu-background: #fff; // 菜单背景色 --menu-background: #fff; // 菜单背景色
@@ -56,6 +36,11 @@ html.dark {
--sidebar-logo-background: rgb(0 0 0 / 20%); --sidebar-logo-background: rgb(0 0 0 / 20%);
--sidebar-logo-text-color: #fff; --sidebar-logo-text-color: #fff;
.el-table {
/* 自定义表格选中高亮时当前行的背景颜色(暗黑模式) */
--el-table-current-row-bg-color: var(--el-fill-color-light);
}
/** WangEditor Dark */ /** WangEditor Dark */
/* Textarea - css vars */ /* Textarea - css vars */
--w-e-textarea-bg-color: var(--el-bg-color); /* 深色背景 */ --w-e-textarea-bg-color: var(--el-bg-color); /* 深色背景 */
@@ -92,7 +77,7 @@ $sidebar-width-collapsed: 54px; // 侧边栏收缩宽度
$navbar-height: 50px; // 导航栏高度 $navbar-height: 50px; // 导航栏高度
$tags-view-height: 34px; // TagsView 高度 $tags-view-height: 34px; // TagsView 高度
/* 供 JS/TS 侧按需读取的变量导出(保持与原 module 一致) */ /* 供 JS/TS 侧按需读取的变量导出 */
/* stylelint-disable property-no-unknown */ /* stylelint-disable property-no-unknown */
:export { :export {
sidebar-width: $sidebar-width; sidebar-width: $sidebar-width;

View File

@@ -4,7 +4,6 @@ import { ApiCodeEnum } from "@/enums/api";
import { AuthStorage, redirectToLogin } from "@/utils/auth"; import { AuthStorage, redirectToLogin } from "@/utils/auth";
import { useTokenRefresh } from "@/composables/auth/useTokenRefresh"; import { useTokenRefresh } from "@/composables/auth/useTokenRefresh";
import { authConfig } from "@/settings"; import { authConfig } from "@/settings";
import { useTenantStoreHook } from "@/store/modules/tenant-store";
// 初始化token刷新组合式函数 // 初始化token刷新组合式函数
const { refreshTokenAndRetry } = useTokenRefresh(); const { refreshTokenAndRetry } = useTokenRefresh();
@@ -20,7 +19,7 @@ const httpRequest = axios.create({
}); });
/** /**
* 请求拦截器 - 添加 Authorization 头和租户ID * 请求拦截器 - 添加 Authorization 头
*/ */
httpRequest.interceptors.request.use( httpRequest.interceptors.request.use(
(config: InternalAxiosRequestConfig) => { (config: InternalAxiosRequestConfig) => {
@@ -33,19 +32,6 @@ httpRequest.interceptors.request.use(
delete config.headers.Authorization; delete config.headers.Authorization;
} }
// 添加租户ID到请求头如果存在
// 注意只有在登录成功后tenantStore 才会初始化,所以这里需要 try-catch
try {
const tenantStore = useTenantStoreHook();
const tenantId = tenantStore.currentTenantId;
if (tenantId) {
config.headers["tenant-id"] = String(tenantId);
}
} catch (error) {
// 如果租户 store 未初始化(如登录前),忽略错误
// 这是正常的,因为多租户功能是可选的,未启用时不会初始化 tenantStore
}
return config; return config;
}, },
(error) => { (error) => {

View File

@@ -97,16 +97,7 @@
> >
<div class="tenant-select-content"> <div class="tenant-select-content">
<p class="tenant-select-tip">检测到你的账号属于多个租户请选择登录租户</p> <p class="tenant-select-tip">检测到你的账号属于多个租户请选择登录租户</p>
<el-radio-group v-model="selectedTenantId" class="tenant-radio-group"> <TenantSwitcher @change="(id: number) => (selectedTenantId = id)" />
<el-radio
v-for="tenant in pendingTenants"
:key="tenant.id"
:label="tenant.id"
class="tenant-radio"
>
{{ tenant.name }}
</el-radio>
</el-radio-group>
</div> </div>
<template #footer> <template #footer>
<el-button @click="tenantDialogVisible = false">取消</el-button> <el-button @click="tenantDialogVisible = false">取消</el-button>
@@ -146,12 +137,15 @@ import AuthAPI from "@/api/auth";
import type { LoginRequest } from "@/types/api"; import type { LoginRequest } from "@/types/api";
import router from "@/router"; import router from "@/router";
import { useUserStore } from "@/store"; import { useUserStore } from "@/store";
import { useTenantStoreHook } from "@/store/modules/tenant-store";
import CommonWrapper from "@/components/CommonWrapper/index.vue"; import CommonWrapper from "@/components/CommonWrapper/index.vue";
import TenantSwitcher from "@/components/TenantSwitcher/index.vue";
import { AuthStorage } from "@/utils/auth"; import { AuthStorage } from "@/utils/auth";
import { ApiCodeEnum } from "@/enums"; import { ApiCodeEnum } from "@/enums";
const { t } = useI18n(); const { t } = useI18n();
const userStore = useUserStore(); const userStore = useUserStore();
const tenantStore = useTenantStoreHook();
const route = useRoute(); const route = useRoute();
onMounted(() => getCaptcha()); onMounted(() => getCaptcha());
@@ -219,9 +213,6 @@ function getCaptcha() {
.finally(() => (codeLoading.value = false)); .finally(() => (codeLoading.value = false));
} }
// 待选择的租户列表
const pendingTenants = ref<Array<{ id: number; name: string }>>([]);
/** /**
* 登录提交 * 登录提交
*/ */
@@ -243,7 +234,8 @@ async function handleLoginSubmit() {
// 检查是否是 choose_tenant 响应 // 检查是否是 choose_tenant 响应
if (error?.code === ApiCodeEnum.CHOOSE_TENANT && error?.data?.tenants) { if (error?.code === ApiCodeEnum.CHOOSE_TENANT && error?.data?.tenants) {
// 需要选择租户 // 需要选择租户
pendingTenants.value = error.data.tenants; tenantStore.setTenantList(error.data.tenants);
selectedTenantId.value = error.data.tenants[0]?.id || null;
tenantDialogVisible.value = true; tenantDialogVisible.value = true;
return; // 等待用户选择租户 return; // 等待用户选择租户
} }
@@ -304,12 +296,12 @@ function toOtherForm(type: "register" | "resetPwd") {
.auth-panel-form { .auth-panel-form {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 1rem; gap: 0.75rem;
} }
.auth-panel-form__title { .auth-panel-form__title {
margin: 0 0 0.75rem; margin: 0 0 0.5rem;
font-size: 1.25rem; font-size: 1.125rem;
font-weight: 600; font-weight: 600;
} }
@@ -317,7 +309,7 @@ function toOtherForm(type: "register" | "resetPwd") {
.divider-container { .divider-container {
display: flex; display: flex;
align-items: center; align-items: center;
margin: 24px 0; margin: 16px 0;
.divider-line { .divider-line {
flex: 1; flex: 1;
@@ -342,33 +334,5 @@ function toOtherForm(type: "register" | "resetPwd") {
font-size: 14px; font-size: 14px;
color: var(--el-text-color-regular); color: var(--el-text-color-regular);
} }
.tenant-radio-group {
display: flex;
flex-direction: column;
gap: 12px;
width: 100%;
.tenant-radio {
display: flex;
align-items: center;
padding: 12px 16px;
border: 1px solid var(--el-border-color);
border-radius: 8px;
transition: all 0.3s;
&:hover {
background-color: var(--el-color-primary-light-9);
border-color: var(--el-color-primary);
}
:deep(.el-radio__input.is-checked) {
+ .el-radio__label {
font-weight: 500;
color: var(--el-color-primary);
}
}
}
}
} }
</style> </style>

View File

@@ -123,9 +123,7 @@ onBeforeUnmount(() => {
height: 100%; height: 100%;
padding: clamp(1rem, 3vw, 2rem); padding: clamp(1rem, 3vw, 2rem);
overflow: hidden; overflow: hidden;
background: background-color: #f5f7ff;
radial-gradient(circle at 20% 20%, rgba(64, 128, 255, 0.18), transparent 55%),
radial-gradient(circle at 80% 80%, rgba(22, 93, 255, 0.16), transparent 50%);
&::before { &::before {
position: fixed; position: fixed;
@@ -201,13 +199,11 @@ onBeforeUnmount(() => {
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: center;
padding: clamp(1.5rem, 3vw, 3rem); padding: clamp(1.5rem, 3vw, 3rem);
color: rgba(20, 40, 80, 0.95); color: var(--el-text-color-primary);
text-shadow: 0 4px 16px rgba(15, 60, 110, 0.12);
animation: featureFade 0.8s ease-out; animation: featureFade 0.8s ease-out;
@media (prefers-color-scheme: dark) { @media (prefers-color-scheme: dark) {
color: rgba(236, 242, 255, 0.92); color: rgba(236, 242, 255, 0.92);
text-shadow: none;
} }
} }
@@ -272,7 +268,7 @@ onBeforeUnmount(() => {
margin-bottom: 1.5rem; margin-bottom: 1.5rem;
font-size: 1rem; font-size: 1rem;
line-height: 1.7; line-height: 1.7;
color: rgba(35, 40, 65, 0.85); color: var(--el-text-color-regular);
@media (prefers-color-scheme: dark) { @media (prefers-color-scheme: dark) {
color: rgba(220, 230, 255, 0.75); color: rgba(220, 230, 255, 0.75);
@@ -292,8 +288,8 @@ onBeforeUnmount(() => {
align-items: flex-start; align-items: flex-start;
padding: 0.75rem 1rem; padding: 0.75rem 1rem;
font-weight: 500; font-weight: 500;
color: rgba(32, 37, 60, 0.9); color: var(--el-text-color-primary);
background: rgba(255, 255, 255, 0.55); background: rgba(255, 255, 255, 0.9);
border: 1px solid rgba(64, 128, 255, 0.08); border: 1px solid rgba(64, 128, 255, 0.08);
border-radius: 12px; border-radius: 12px;
backdrop-filter: blur(6px); backdrop-filter: blur(6px);
@@ -321,11 +317,12 @@ onBeforeUnmount(() => {
.auth-panel { .auth-panel {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 1.5rem; gap: 1rem;
align-self: center;
justify-content: flex-start; justify-content: flex-start;
justify-self: end; justify-self: end;
width: min(520px, 100%); width: min(420px, 100%);
padding: clamp(2rem, 3vw, 2.75rem); padding: clamp(1.5rem, 3vw, 2rem);
margin-inline: auto; margin-inline: auto;
background: rgba(255, 255, 255, 0.95); background: rgba(255, 255, 255, 0.95);
border: 1px solid rgba(22, 93, 255, 0.1); border: 1px solid rgba(22, 93, 255, 0.1);
@@ -359,11 +356,11 @@ onBeforeUnmount(() => {
.auth-panel__brand { .auth-panel__brand {
display: flex; display: flex;
gap: 1rem; gap: 0.75rem;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
padding-bottom: 1.25rem; padding-bottom: 0.875rem;
margin-bottom: 1.5rem; margin-bottom: 1rem;
border-bottom: 1px solid rgba(22, 93, 255, 0.06); border-bottom: 1px solid rgba(22, 93, 255, 0.06);
@media (prefers-color-scheme: dark) { @media (prefers-color-scheme: dark) {
@@ -449,7 +446,7 @@ onBeforeUnmount(() => {
margin-inline: auto; margin-inline: auto;
:deep(.el-form-item) { :deep(.el-form-item) {
margin-bottom: 1.25rem; margin-bottom: 1rem;
} }
:deep(.el-input__wrapper) { :deep(.el-input__wrapper) {
@@ -472,8 +469,8 @@ onBeforeUnmount(() => {
} }
.auth-panel__footer { .auth-panel__footer {
padding-top: 1.25rem; padding-top: 0.875rem;
margin-top: 0.25rem; margin-top: 0.125rem;
font-size: 0.875rem; font-size: 0.875rem;
text-align: center; text-align: center;
border-top: 1px solid rgba(22, 93, 255, 0.06); border-top: 1px solid rgba(22, 93, 255, 0.06);