Merge branch 'develop' of https://gitee.com/youlaiorg/vue3-element-admin into develop
This commit is contained in:
@@ -8,10 +8,11 @@ export function setupStore(app: App<Element>) {
|
||||
app.use(store);
|
||||
}
|
||||
|
||||
export * from "./modules/app-store";
|
||||
export * from "./modules/permission-store";
|
||||
export * from "./modules/settings-store";
|
||||
export * from "./modules/tags-view-store";
|
||||
export * from "./modules/user-store";
|
||||
export * from "./modules/dict-store";
|
||||
export * from "./modules/app";
|
||||
export * from "./modules/permission";
|
||||
export * from "./modules/settings";
|
||||
export * from "./modules/tags-view";
|
||||
export * from "./modules/user";
|
||||
export * from "./modules/dict";
|
||||
export * from "./modules/tenant";
|
||||
export { store };
|
||||
|
||||
@@ -1,86 +1,54 @@
|
||||
import { defaultSettings } from "@/settings";
|
||||
|
||||
// 导入 Element Plus 中英文语言包
|
||||
import zhCn from "element-plus/es/locale/lang/zh-cn";
|
||||
import en from "element-plus/es/locale/lang/en";
|
||||
import { store } from "@/store";
|
||||
import { DeviceEnum } from "@/enums/settings/device-enum";
|
||||
import { SidebarStatus } from "@/enums/settings/layout-enum";
|
||||
import { DeviceEnum, SidebarStatus } from "@/enums";
|
||||
import { STORAGE_KEYS } from "@/constants";
|
||||
import { defaults } from "@/settings";
|
||||
|
||||
export const useAppStore = defineStore("app", () => {
|
||||
// 设备类型
|
||||
const device = useStorage(STORAGE_KEYS.DEVICE, DeviceEnum.DESKTOP);
|
||||
// 布局大小
|
||||
const size = useStorage(STORAGE_KEYS.SIZE, defaultSettings.size);
|
||||
// 语言
|
||||
const language = useStorage(STORAGE_KEYS.LANGUAGE, defaultSettings.language);
|
||||
// 侧边栏状态
|
||||
const size = useStorage(STORAGE_KEYS.SIZE, defaults.size);
|
||||
const language = useStorage(STORAGE_KEYS.LANGUAGE, defaults.language);
|
||||
const sidebarStatus = useStorage(STORAGE_KEYS.SIDEBAR_STATUS, SidebarStatus.CLOSED);
|
||||
const sidebar = reactive({
|
||||
opened: sidebarStatus.value === SidebarStatus.OPENED,
|
||||
withoutAnimation: false,
|
||||
});
|
||||
|
||||
// 顶部菜单激活路径
|
||||
const activeTopMenuPath = useStorage(STORAGE_KEYS.ACTIVE_TOP_MENU_PATH, "");
|
||||
|
||||
/**
|
||||
* 根据语言标识读取对应的语言包
|
||||
*/
|
||||
const locale = computed(() => {
|
||||
if (language?.value == "en") {
|
||||
return en;
|
||||
} else {
|
||||
return zhCn;
|
||||
}
|
||||
});
|
||||
const locale = computed(() => (language?.value === "en" ? en : zhCn));
|
||||
|
||||
// 切换侧边栏
|
||||
function toggleSidebar() {
|
||||
sidebar.opened = !sidebar.opened;
|
||||
sidebarStatus.value = sidebar.opened ? SidebarStatus.OPENED : SidebarStatus.CLOSED;
|
||||
}
|
||||
|
||||
// 关闭侧边栏
|
||||
function closeSideBar() {
|
||||
sidebar.opened = false;
|
||||
sidebarStatus.value = SidebarStatus.CLOSED;
|
||||
}
|
||||
|
||||
// 打开侧边栏
|
||||
function openSideBar() {
|
||||
sidebar.opened = true;
|
||||
sidebarStatus.value = SidebarStatus.OPENED;
|
||||
}
|
||||
|
||||
// 切换设备
|
||||
function toggleDevice(val: string) {
|
||||
device.value = val;
|
||||
}
|
||||
|
||||
/**
|
||||
* 改变布局大小
|
||||
*
|
||||
* @param val 布局大小 default | small | large
|
||||
*/
|
||||
function changeSize(val: string) {
|
||||
size.value = val;
|
||||
}
|
||||
/**
|
||||
* 切换语言
|
||||
*
|
||||
* @param val
|
||||
*/
|
||||
|
||||
function changeLanguage(val: string) {
|
||||
language.value = val;
|
||||
}
|
||||
/**
|
||||
* 混合模式顶部切换
|
||||
*/
|
||||
|
||||
function activeTopMenu(val: string) {
|
||||
activeTopMenuPath.value = val;
|
||||
}
|
||||
|
||||
return {
|
||||
device,
|
||||
sidebar,
|
||||
@@ -98,11 +66,6 @@ export const useAppStore = defineStore("app", () => {
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* 用于在组件外部(如在Pinia Store 中)使用 Pinia 提供的 store 实例。
|
||||
* 官方文档解释了如何在组件外部使用 Pinia Store:
|
||||
* https://pinia.vuejs.org/core-concepts/outside-component-usage.html#using-a-store-outside-of-a-component
|
||||
*/
|
||||
export function useAppStoreHook() {
|
||||
return useAppStore(store);
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import { store } from "@/store";
|
||||
import DictAPI, { type DictItemOption } from "@/api/system/dict-api";
|
||||
import DictAPI from "@/api/system/dict";
|
||||
import type { DictItemOption } from "@/types/api";
|
||||
import { STORAGE_KEYS } from "@/constants";
|
||||
|
||||
export const useDictStore = defineStore("dict", () => {
|
||||
@@ -3,7 +3,8 @@ import { constantRoutes } from "@/router";
|
||||
import { store } from "@/store";
|
||||
import router from "@/router";
|
||||
|
||||
import MenuAPI, { type RouteVO } from "@/api/system/menu-api";
|
||||
import MenuAPI from "@/api/system/menu";
|
||||
import { RouteVo } from "@/types";
|
||||
const modules = import.meta.glob("../../views/**/**.vue");
|
||||
const Layout = () => import("../../layouts/index.vue");
|
||||
|
||||
@@ -68,7 +69,7 @@ export const usePermissionStore = defineStore("permission", () => {
|
||||
* 转换后端路由数据为Vue Router配置
|
||||
* 处理组件路径映射和Layout层级嵌套
|
||||
*/
|
||||
const transformRoutes = (routes: RouteVO[], isTopLevel: boolean = true): RouteRecordRaw[] => {
|
||||
const transformRoutes = (routes: RouteVo[], isTopLevel: boolean = true): RouteRecordRaw[] => {
|
||||
return routes.map((route) => {
|
||||
const { component, children, ...args } = route;
|
||||
|
||||
@@ -1,208 +0,0 @@
|
||||
import { defaultSettings } from "@/settings";
|
||||
import { SidebarColor, ThemeMode } from "@/enums/settings/theme-enum";
|
||||
import type { LayoutMode } from "@/enums/settings/layout-enum";
|
||||
import { applyTheme, generateThemeColors, toggleDarkMode, toggleSidebarColor } from "@/utils/theme";
|
||||
import { STORAGE_KEYS } from "@/constants";
|
||||
|
||||
// 🎯 设置项类型定义
|
||||
interface SettingsState {
|
||||
// 界面显示设置
|
||||
settingsVisible: boolean;
|
||||
showTagsView: boolean;
|
||||
showAppLogo: boolean;
|
||||
showWatermark: boolean;
|
||||
enableAiAssistant: boolean;
|
||||
|
||||
// 布局设置
|
||||
layout: LayoutMode;
|
||||
sidebarColorScheme: string;
|
||||
|
||||
// 主题设置
|
||||
theme: ThemeMode;
|
||||
themeColor: string;
|
||||
}
|
||||
|
||||
// 🎯 可变更的设置项类型
|
||||
type MutableSetting = Exclude<keyof SettingsState, "settingsVisible">;
|
||||
type SettingValue<K extends MutableSetting> = SettingsState[K];
|
||||
|
||||
export const useSettingsStore = defineStore("setting", () => {
|
||||
// 设置面板可见性
|
||||
const settingsVisible = ref<boolean>(false);
|
||||
|
||||
// 是否显示标签页视图
|
||||
const showTagsView = useStorage<boolean>(
|
||||
STORAGE_KEYS.SHOW_TAGS_VIEW,
|
||||
defaultSettings.showTagsView
|
||||
);
|
||||
|
||||
// 是否显示应用Logo
|
||||
const showAppLogo = useStorage<boolean>(STORAGE_KEYS.SHOW_APP_LOGO, defaultSettings.showAppLogo);
|
||||
|
||||
// 是否显示水印
|
||||
const showWatermark = useStorage<boolean>(
|
||||
STORAGE_KEYS.SHOW_WATERMARK,
|
||||
defaultSettings.showWatermark
|
||||
);
|
||||
|
||||
// 是否启用 AI 助手
|
||||
const enableAiAssistant = useStorage<boolean>(
|
||||
STORAGE_KEYS.ENABLE_AI_ASSISTANT,
|
||||
defaultSettings.enableAiAssistant
|
||||
);
|
||||
|
||||
// 侧边栏配色方案
|
||||
const sidebarColorScheme = useStorage<string>(
|
||||
STORAGE_KEYS.SIDEBAR_COLOR_SCHEME,
|
||||
defaultSettings.sidebarColorScheme
|
||||
);
|
||||
|
||||
// 布局模式
|
||||
const layout = useStorage<LayoutMode>(STORAGE_KEYS.LAYOUT, defaultSettings.layout as LayoutMode);
|
||||
|
||||
// 主题颜色
|
||||
const themeColor = useStorage<string>(STORAGE_KEYS.THEME_COLOR, defaultSettings.themeColor);
|
||||
|
||||
// 主题模式(亮色/暗色)
|
||||
const theme = useStorage<ThemeMode>(STORAGE_KEYS.THEME, defaultSettings.theme);
|
||||
|
||||
// 系统主题媒体查询(用于 ThemeMode.AUTO)
|
||||
const prefersDarkMedia =
|
||||
typeof window !== "undefined" && typeof window.matchMedia === "function"
|
||||
? window.matchMedia("(prefers-color-scheme: dark)")
|
||||
: null;
|
||||
|
||||
function getSystemTheme(): ThemeMode {
|
||||
return prefersDarkMedia && prefersDarkMedia.matches ? ThemeMode.DARK : ThemeMode.LIGHT;
|
||||
}
|
||||
|
||||
function handleSystemThemeChange(e: MediaQueryListEvent | MediaQueryList) {
|
||||
// 仅在用户选择自动(跟随系统)时响应系统主题变化
|
||||
if (theme.value === ThemeMode.AUTO) {
|
||||
const effectiveTheme = (e as MediaQueryList).matches ? ThemeMode.DARK : ThemeMode.LIGHT;
|
||||
toggleDarkMode(effectiveTheme === ThemeMode.DARK);
|
||||
const colors = generateThemeColors(themeColor.value, effectiveTheme);
|
||||
applyTheme(colors);
|
||||
}
|
||||
}
|
||||
|
||||
if (prefersDarkMedia) {
|
||||
// 兼容旧浏览器和新浏览器的监听方式
|
||||
if (typeof prefersDarkMedia.addEventListener === "function") {
|
||||
prefersDarkMedia.addEventListener("change", handleSystemThemeChange);
|
||||
} else if (typeof (prefersDarkMedia as any).addListener === "function") {
|
||||
(prefersDarkMedia as any).addListener(handleSystemThemeChange);
|
||||
}
|
||||
}
|
||||
|
||||
// 设置项映射,用于统一管理
|
||||
const settingsMap = {
|
||||
showTagsView,
|
||||
showAppLogo,
|
||||
showWatermark,
|
||||
enableAiAssistant,
|
||||
sidebarColorScheme,
|
||||
layout,
|
||||
} as const;
|
||||
|
||||
// 监听主题变化,自动应用样式
|
||||
watch(
|
||||
[theme, themeColor],
|
||||
([newTheme, newThemeColor]: [ThemeMode, string]) => {
|
||||
// 计算实际生效的主题:若为 AUTO 则使用系统当前偏好
|
||||
const effectiveTheme: ThemeMode = newTheme === ThemeMode.AUTO ? getSystemTheme() : newTheme;
|
||||
|
||||
toggleDarkMode(effectiveTheme === ThemeMode.DARK);
|
||||
const colors = generateThemeColors(newThemeColor, effectiveTheme);
|
||||
applyTheme(colors);
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
// 监听侧边栏配色变化
|
||||
watch(
|
||||
[sidebarColorScheme],
|
||||
([newSidebarColorScheme]) => {
|
||||
toggleSidebarColor(newSidebarColorScheme === SidebarColor.CLASSIC_BLUE);
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
// 通用设置更新方法
|
||||
function updateSetting<K extends keyof typeof settingsMap>(key: K, value: SettingValue<K>): void {
|
||||
const setting = settingsMap[key];
|
||||
if (setting) {
|
||||
(setting as Ref<any>).value = value;
|
||||
}
|
||||
}
|
||||
|
||||
// 主题更新方法
|
||||
function updateTheme(newTheme: ThemeMode): void {
|
||||
theme.value = newTheme;
|
||||
}
|
||||
|
||||
function updateThemeColor(newColor: string): void {
|
||||
themeColor.value = newColor;
|
||||
}
|
||||
|
||||
function updateSidebarColorScheme(newScheme: string): void {
|
||||
sidebarColorScheme.value = newScheme;
|
||||
}
|
||||
|
||||
function updateLayout(newLayout: LayoutMode): void {
|
||||
layout.value = newLayout;
|
||||
}
|
||||
|
||||
// 设置面板控制
|
||||
function toggleSettingsPanel(): void {
|
||||
settingsVisible.value = !settingsVisible.value;
|
||||
}
|
||||
|
||||
function showSettingsPanel(): void {
|
||||
settingsVisible.value = true;
|
||||
}
|
||||
|
||||
function hideSettingsPanel(): void {
|
||||
settingsVisible.value = false;
|
||||
}
|
||||
|
||||
// 重置所有设置
|
||||
function resetSettings(): void {
|
||||
showTagsView.value = defaultSettings.showTagsView;
|
||||
showAppLogo.value = defaultSettings.showAppLogo;
|
||||
showWatermark.value = defaultSettings.showWatermark;
|
||||
enableAiAssistant.value = defaultSettings.enableAiAssistant;
|
||||
sidebarColorScheme.value = defaultSettings.sidebarColorScheme;
|
||||
layout.value = defaultSettings.layout as LayoutMode;
|
||||
themeColor.value = defaultSettings.themeColor;
|
||||
theme.value = defaultSettings.theme;
|
||||
}
|
||||
|
||||
return {
|
||||
// 状态
|
||||
settingsVisible,
|
||||
showTagsView,
|
||||
showAppLogo,
|
||||
showWatermark,
|
||||
enableAiAssistant,
|
||||
sidebarColorScheme,
|
||||
layout,
|
||||
themeColor,
|
||||
theme,
|
||||
|
||||
// 更新方法
|
||||
updateSetting,
|
||||
updateTheme,
|
||||
updateThemeColor,
|
||||
updateSidebarColorScheme,
|
||||
updateLayout,
|
||||
|
||||
// 面板控制
|
||||
toggleSettingsPanel,
|
||||
showSettingsPanel,
|
||||
hideSettingsPanel,
|
||||
|
||||
// 重置功能
|
||||
resetSettings,
|
||||
};
|
||||
});
|
||||
93
src/store/modules/settings.ts
Normal file
93
src/store/modules/settings.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
import { SidebarColor, ThemeMode } from "@/enums";
|
||||
import type { LayoutMode } from "@/enums";
|
||||
import { applyTheme, generateThemeColors, toggleDarkMode, toggleSidebarColor } from "@/utils/theme";
|
||||
import { STORAGE_KEYS } from "@/constants";
|
||||
import { appConfig, defaults } from "@/settings";
|
||||
|
||||
export const useSettingsStore = defineStore("setting", () => {
|
||||
// 界面显示
|
||||
const settingsVisible = ref(false);
|
||||
const showTagsView = useStorage(STORAGE_KEYS.SHOW_TAGS_VIEW, defaults.showTagsView);
|
||||
const showAppLogo = useStorage(STORAGE_KEYS.SHOW_APP_LOGO, defaults.showAppLogo);
|
||||
const showWatermark = useStorage(STORAGE_KEYS.SHOW_WATERMARK, defaults.showWatermark);
|
||||
|
||||
// 布局
|
||||
const layout = useStorage<LayoutMode>(STORAGE_KEYS.LAYOUT, defaults.layout as LayoutMode);
|
||||
const sidebarColorScheme = useStorage(
|
||||
STORAGE_KEYS.SIDEBAR_COLOR_SCHEME,
|
||||
defaults.sidebarColorScheme
|
||||
);
|
||||
|
||||
// 主题
|
||||
const theme = useStorage<ThemeMode>(STORAGE_KEYS.THEME, defaults.theme);
|
||||
const themeColor = useStorage(STORAGE_KEYS.THEME_COLOR, defaults.themeColor);
|
||||
|
||||
// 特殊模式
|
||||
const grayMode = useStorage(STORAGE_KEYS.GRAY_MODE, false);
|
||||
const colorWeak = useStorage(STORAGE_KEYS.COLOR_WEAK, false);
|
||||
|
||||
// AI 助手:系统级 && 用户级
|
||||
const userEnableAi = useStorage(STORAGE_KEYS.ENABLE_AI_ASSISTANT, false);
|
||||
const enableAiAssistant = computed(() => appConfig.aiEnabled && userEnableAi.value);
|
||||
|
||||
// 主题变化监听
|
||||
watch(
|
||||
[theme, themeColor],
|
||||
([t, c]: [ThemeMode, string]) => {
|
||||
toggleDarkMode(t === ThemeMode.DARK);
|
||||
applyTheme(generateThemeColors(c, t));
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
watch(sidebarColorScheme, (v) => toggleSidebarColor(v === SidebarColor.CLASSIC_BLUE), {
|
||||
immediate: true,
|
||||
});
|
||||
|
||||
// 灰色模式监听
|
||||
watch(
|
||||
grayMode,
|
||||
(v) => {
|
||||
document.documentElement.style.filter = v ? "grayscale(100%)" : "";
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
// 色弱模式监听
|
||||
watch(
|
||||
colorWeak,
|
||||
(v) => {
|
||||
document.documentElement.classList.toggle("color-weak", v);
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
function resetSettings() {
|
||||
showTagsView.value = defaults.showTagsView;
|
||||
showAppLogo.value = defaults.showAppLogo;
|
||||
showWatermark.value = defaults.showWatermark;
|
||||
userEnableAi.value = false;
|
||||
grayMode.value = false;
|
||||
colorWeak.value = false;
|
||||
sidebarColorScheme.value = defaults.sidebarColorScheme;
|
||||
layout.value = defaults.layout as LayoutMode;
|
||||
themeColor.value = defaults.themeColor;
|
||||
theme.value = defaults.theme;
|
||||
}
|
||||
|
||||
return {
|
||||
settingsVisible,
|
||||
showTagsView,
|
||||
showAppLogo,
|
||||
showWatermark,
|
||||
enableAiAssistant,
|
||||
userEnableAi,
|
||||
grayMode,
|
||||
colorWeak,
|
||||
sidebarColorScheme,
|
||||
layout,
|
||||
themeColor,
|
||||
theme,
|
||||
resetSettings,
|
||||
};
|
||||
});
|
||||
209
src/store/modules/tenant.ts
Normal file
209
src/store/modules/tenant.ts
Normal file
@@ -0,0 +1,209 @@
|
||||
import { store } from "@/store";
|
||||
import TenantAPI from "@/api/system/tenant";
|
||||
import type { TenantInfo } from "@/types/api";
|
||||
import { STORAGE_KEYS } from "@/constants";
|
||||
|
||||
/**
|
||||
* 租户 Store
|
||||
*/
|
||||
export const useTenantStore = defineStore("tenant", () => {
|
||||
// 当前租户ID
|
||||
const currentTenantId = ref<number | null>(null);
|
||||
// 当前租户信息
|
||||
const currentTenant = ref<TenantInfo | null>(null);
|
||||
// 用户可访问的租户列表
|
||||
const tenantList = ref<TenantInfo[]>([]);
|
||||
|
||||
/**
|
||||
* 恢复租户信息
|
||||
* 从 localStorage 恢复上次使用的租户
|
||||
*/
|
||||
function restoreTenant() {
|
||||
const savedTenantId = localStorage.getItem(STORAGE_KEYS.TENANT_ID);
|
||||
const savedTenantInfo = localStorage.getItem(STORAGE_KEYS.TENANT_INFO);
|
||||
|
||||
if (savedTenantId) {
|
||||
currentTenantId.value = Number(savedTenantId);
|
||||
}
|
||||
|
||||
if (savedTenantInfo) {
|
||||
try {
|
||||
currentTenant.value = JSON.parse(savedTenantInfo);
|
||||
} catch (e) {
|
||||
console.error("解析租户信息失败", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户租户列表
|
||||
*/
|
||||
function fetchTenantList() {
|
||||
return new Promise<TenantInfo[]>((resolve, reject) => {
|
||||
TenantAPI.getTenantList()
|
||||
.then((data) => {
|
||||
tenantList.value = data || [];
|
||||
resolve(data || []);
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载租户
|
||||
*
|
||||
* 执行流程:
|
||||
* 1. 获取用户可访问的租户列表
|
||||
* 2. 尝试获取后端当前租户
|
||||
* 3. 如果只有一个租户,自动选中
|
||||
* 4. 否则等待用户手动选择
|
||||
*
|
||||
* @remarks
|
||||
* 此方法由路由守卫调用,仅在启用多租户时执行
|
||||
*/
|
||||
async function loadTenant() {
|
||||
restoreTenant();
|
||||
|
||||
// 1. 获取租户列表
|
||||
await fetchTenantList();
|
||||
|
||||
// 2. 校验本地恢复的租户是否仍然可用(避免 tenantId 不在列表导致无默认选中)
|
||||
if (
|
||||
currentTenantId.value &&
|
||||
tenantList.value.length > 0 &&
|
||||
!tenantList.value.some((t) => t.id === currentTenantId.value)
|
||||
) {
|
||||
console.debug("[Tenant] 本地租户已不可用,清除并重新选择:", currentTenantId.value);
|
||||
currentTenantId.value = null;
|
||||
currentTenant.value = null;
|
||||
localStorage.removeItem(STORAGE_KEYS.TENANT_ID);
|
||||
localStorage.removeItem(STORAGE_KEYS.TENANT_INFO);
|
||||
}
|
||||
|
||||
// 3. 如果已有租户列表,则保证一定有一个默认租户被选中
|
||||
if (tenantList.value.length > 0) {
|
||||
// 3.1 优先后端当前租户
|
||||
if (!currentTenantId.value) {
|
||||
try {
|
||||
const currentTenantInfo = await TenantAPI.getCurrentTenant();
|
||||
if (currentTenantInfo) {
|
||||
setCurrentTenant(currentTenantInfo);
|
||||
return;
|
||||
}
|
||||
} catch (error) {
|
||||
console.debug("[Tenant] 获取当前租户失败,尝试本地/默认选择:", error);
|
||||
}
|
||||
}
|
||||
|
||||
// 3.2 本地已有 tenantId,但 currentTenant 为空时,从列表补全 tenantInfo(保持展示名称一致)
|
||||
if (currentTenantId.value && !currentTenant.value) {
|
||||
const matched = tenantList.value.find((t) => t.id === currentTenantId.value);
|
||||
if (matched) {
|
||||
setCurrentTenant(matched);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 3.3 兜底:默认选中第一个(即使有多个租户,也保证 TenantSwitcher 有默认选中)
|
||||
if (!currentTenantId.value) {
|
||||
setCurrentTenant(tenantList.value[0]);
|
||||
console.debug("[Tenant] 默认选中第一个租户:", tenantList.value[0].name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前租户
|
||||
*
|
||||
* @param tenant 租户信息
|
||||
*/
|
||||
function setCurrentTenant(tenant: TenantInfo) {
|
||||
currentTenantId.value = tenant.id;
|
||||
currentTenant.value = tenant;
|
||||
|
||||
// 保存到 localStorage
|
||||
localStorage.setItem(STORAGE_KEYS.TENANT_ID, String(tenant.id));
|
||||
localStorage.setItem(STORAGE_KEYS.TENANT_INFO, JSON.stringify(tenant));
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换租户
|
||||
*
|
||||
* @param tenantId 目标租户ID
|
||||
*/
|
||||
async function switchTenant(tenantId: number): Promise<void> {
|
||||
try {
|
||||
// 调用后端切换接口
|
||||
const tenantInfo = await TenantAPI.switchTenant(tenantId);
|
||||
|
||||
// 后端返回切换后的租户信息
|
||||
if (tenantInfo) {
|
||||
setCurrentTenant(tenantInfo);
|
||||
} else {
|
||||
// 如果后端未返回,从租户列表中找到对应的租户信息
|
||||
const tenant = tenantList.value.find((t) => t.id === tenantId);
|
||||
if (tenant) {
|
||||
setCurrentTenant(tenant);
|
||||
} else {
|
||||
// 如果列表中没有,重新获取租户信息
|
||||
try {
|
||||
const info = await TenantAPI.getCurrentTenant();
|
||||
if (info) {
|
||||
setCurrentTenant(info);
|
||||
} else {
|
||||
throw new Error("无法获取租户信息");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("获取租户信息失败:", error);
|
||||
throw new Error("切换租户后无法获取租户信息");
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("切换租户失败:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除租户信息
|
||||
*/
|
||||
function clearTenant() {
|
||||
currentTenantId.value = null;
|
||||
currentTenant.value = null;
|
||||
tenantList.value = [];
|
||||
localStorage.removeItem(STORAGE_KEYS.TENANT_ID);
|
||||
localStorage.removeItem(STORAGE_KEYS.TENANT_INFO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置租户列表
|
||||
*/
|
||||
function setTenantList(list: TenantInfo[]) {
|
||||
tenantList.value = list || [];
|
||||
}
|
||||
|
||||
// 恢复本地租户信息
|
||||
restoreTenant();
|
||||
|
||||
return {
|
||||
currentTenantId,
|
||||
currentTenant,
|
||||
tenantList,
|
||||
loadTenant,
|
||||
fetchTenantList,
|
||||
setTenantList,
|
||||
setCurrentTenant,
|
||||
switchTenant,
|
||||
clearTenant,
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* 在组件外部使用 TenantStore 的钩子函数
|
||||
*/
|
||||
export function useTenantStoreHook() {
|
||||
return useTenantStore(store);
|
||||
}
|
||||
@@ -1,13 +1,14 @@
|
||||
import { store } from "@/store";
|
||||
|
||||
import AuthAPI, { type LoginFormData } from "@/api/auth-api";
|
||||
import UserAPI, { type UserInfo } from "@/api/system/user-api";
|
||||
import AuthAPI from "@/api/auth";
|
||||
import UserAPI from "@/api/system/user";
|
||||
import type { LoginRequest, UserInfo } from "@/types/api";
|
||||
|
||||
import { AuthStorage } from "@/utils/auth";
|
||||
import { usePermissionStoreHook } from "@/store/modules/permission-store";
|
||||
import { useDictStoreHook } from "@/store/modules/dict-store";
|
||||
import { usePermissionStoreHook } from "@/store/modules/permission";
|
||||
import { useDictStoreHook } from "@/store/modules/dict";
|
||||
import { useTagsViewStore } from "@/store";
|
||||
import { cleanupWebSocket } from "@/plugins/websocket";
|
||||
import { cleanupWebSocket } from "@/composables";
|
||||
|
||||
export const useUserStore = defineStore("user", () => {
|
||||
// 用户信息
|
||||
@@ -18,16 +19,16 @@ export const useUserStore = defineStore("user", () => {
|
||||
/**
|
||||
* 登录
|
||||
*
|
||||
* @param {LoginFormData}
|
||||
* @param {LoginRequest}
|
||||
* @returns
|
||||
*/
|
||||
function login(LoginFormData: LoginFormData) {
|
||||
function login(loginRequest: LoginRequest) {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
AuthAPI.login(LoginFormData)
|
||||
AuthAPI.login(loginRequest)
|
||||
.then((data) => {
|
||||
const { accessToken, refreshToken } = data;
|
||||
// 保存记住我状态和token
|
||||
rememberMe.value = LoginFormData.rememberMe;
|
||||
rememberMe.value = loginRequest.rememberMe ?? false;
|
||||
AuthStorage.setTokens(accessToken, refreshToken, rememberMe.value);
|
||||
resolve();
|
||||
})
|
||||
Reference in New Issue
Block a user