wip: 临时提交

This commit is contained in:
Ray.Hao
2025-05-28 23:39:26 +08:00
parent b2333e8b70
commit 9fba279490
37 changed files with 536 additions and 3128 deletions

View File

@@ -1,5 +1,5 @@
import { getToken } from "@/utils/storage";
import { ResultCodeEnum } from "@/enums/ResultCodeEnum";
import { ApiCode } from "@/enums/api-code.enum";
// H5 使用 VITE_APP_BASE_API 作为代理路径,其他平台使用 VITE_APP_API_URL 作为请求路径
let baseApi = import.meta.env.VITE_APP_API_URL;
@@ -31,7 +31,7 @@ const FileAPI = {
success: (response) => {
const resData = JSON.parse(response.data) as ResponseData<FileInfo>;
// 业务状态码 00000 表示成功
if (resData.code === ResultCodeEnum.SUCCESS) {
if (resData.code === ApiCode.SUCCESS) {
resolve(resData.data);
} else {
// 其他业务处理失败

View File

@@ -1,22 +0,0 @@
import type { ConfigProviderThemeVars } from "wot-design-uni";
/* 默认的主题list */
export const colorColumns = [
{
value: "#0055FE",
label: "蓝色",
},
{
value: "#CD5C5C",
label: "红色",
},
{
value: "#228B22",
label: "绿色",
},
];
/* 默认的主题 */
export const initThemState = "light";
/* 默认的主题 */
export const initThemeVars: ConfigProviderThemeVars = {
colorTheme: colorColumns[0].value,
};

View File

@@ -1,74 +0,0 @@
import { colorColumns, initThemState, initThemeVars } from "./rootTheme";
/* 暗黑模式切换 */
const theme = ref<"light" | "dark">(initThemState);
/* 组件库的主题色 */
const themeVars = ref({ ...initThemeVars });
// 从本地存储恢复主题设置
function loadThemeFromStorage() {
try {
const savedTheme = uni.getStorageSync("app_theme_mode");
const savedThemeColor = uni.getStorageSync("app_theme_color");
if (savedTheme && (savedTheme === "light" || savedTheme === "dark")) {
theme.value = savedTheme;
}
if (savedThemeColor) {
themeVars.value.colorTheme = savedThemeColor;
}
} catch (error) {
console.error("加载主题设置失败:", error);
}
}
// 保存主题设置到本地存储
function saveThemeToStorage() {
try {
uni.setStorageSync("app_theme_mode", theme.value);
uni.setStorageSync("app_theme_color", themeVars.value.colorTheme);
} catch (error) {
console.error("保存主题设置失败:", error);
}
}
export function useTheme() {
/* 切换暗黑模式 */
function toggleTheme(mode?: "light" | "dark") {
theme.value = mode || (theme.value === "light" ? "dark" : "light");
setNavigationBarColor();
saveThemeToStorage();
}
/* 设置主题色 */
function setThemeColor(color: string) {
themeVars.value.colorTheme = color;
saveThemeToStorage();
}
/* 初始化theme */
function initTheme() {
loadThemeFromStorage();
setNavigationBarColor();
}
function setNavigationBarColor() {
uni.setNavigationBarColor({
frontColor: theme.value === "light" ? "#000000" : "#ffffff",
backgroundColor: theme.value === "light" ? "#ffffff" : "#000000",
animation: {
duration: 400,
timingFunc: "easeIn",
},
});
}
return {
theme,
themeVars,
initTheme,
colorColumns,
toggleTheme,
setThemeColor,
};
}

View File

@@ -1,5 +1,5 @@
import { Client, type IMessage, type StompSubscription } from "@stomp/stompjs";
import { Auth } from "@/utils/auth";
import { getAccessToken } from "@/utils/auth";
export interface UseStompOptions {
/** WebSocket 地址,不传时使用 VITE_APP_WS_ENDPOINT 环境变量 */
@@ -70,7 +70,7 @@ export function useStomp(options: UseStompOptions = {}) {
}
// 每次连接前重新获取最新令牌不依赖之前的token值
const currentToken = Auth.getAccessToken();
const currentToken = getAccessToken();
// 检查令牌是否为空,如果为空则不进行连接
if (!currentToken) {
@@ -123,7 +123,7 @@ export function useStomp(options: UseStompOptions = {}) {
};
// 设置 Web Socket 关闭监听器
client.value.onWebSocketClose = (event) => {
client.value.onWebSocketClose = (event: CloseEvent) => {
isConnected.value = false;
isConnecting = false;
console.log(`WebSocket已关闭: ${event?.code} ${event?.reason}`);
@@ -147,7 +147,7 @@ export function useStomp(options: UseStompOptions = {}) {
};
// 设置错误监听器
client.value.onStompError = (frame) => {
client.value.onStompError = (frame: any) => {
console.error("STOMP错误:", frame.headers, frame.body);
isConnecting = false;

View File

@@ -1,61 +0,0 @@
/*
* @Author: weisheng
* @Date: 2024-10-29 22:12:54
* @LastEditTime: 2025-01-13 16:45:28
* @LastEditors: 810505339
* @Description:
* @FilePath: \wot-demo\src\composables\useTabbar.ts
* 记得注释
*/
export interface TabbarItem {
name: string;
value: number | null;
active: boolean;
title: string;
icon: string;
}
const tabbarItems = ref<TabbarItem[]>([
{ name: "home", value: null, active: true, title: "home", icon: "home" },
{ name: "hi", value: null, active: false, title: "hi", icon: "app" },
{ name: "setting", value: null, active: false, title: "setting", icon: "setting" },
]);
export function useTabbar() {
const tabbarList = computed(() => tabbarItems.value);
const activeTabbar = computed(() => {
const item = tabbarItems.value.find((item) => item.active);
return item || tabbarItems.value[0];
});
const getTabbarItemValue = (name: string) => {
const item = tabbarItems.value.find((item) => item.name === name);
return item && item.value ? item.value : null;
};
const setTabbarItem = (name: string, value: number) => {
const tabbarItem = tabbarItems.value.find((item) => item.name === name);
if (tabbarItem) {
tabbarItem.value = value;
}
};
const setTabbarItemActive = (name: string) => {
tabbarItems.value.forEach((item) => {
if (item.name === name) {
item.active = true;
} else {
item.active = false;
}
});
};
return {
tabbarList,
activeTabbar,
getTabbarItemValue,
setTabbarItem,
setTabbarItemActive,
};
}

View File

@@ -0,0 +1,76 @@
import { ref, computed } from "vue";
import type { ConfigProviderThemeVars } from "wot-design-uni";
/* 默认的主题list */
export const colorColumns = [
{
value: "#0055FE",
label: "蓝色",
},
{
value: "#CD5C5C",
label: "红色",
},
{
value: "#228B22",
label: "绿色",
},
];
/* 默认的主题 */
export const initThemState = "light";
/* 默认的主题变量 - 只使用wot-design-uni支持的变量 */
export const initThemeVars: ConfigProviderThemeVars = {
colorTheme: colorColumns[0].value,
};
/* 暗黑模式主题变量 */
export const darkThemeVars: ConfigProviderThemeVars = {
colorTheme: colorColumns[0].value,
};
/* 主题状态 */
export const themeState = ref(initThemState);
/* 主题变量 */
export const themeVars = ref<ConfigProviderThemeVars>(initThemeVars);
/* 计算属性:根据主题状态返回对应的主题变量 */
export const computedThemeVars = computed(() => {
const baseVars = themeState.value === "dark" ? darkThemeVars : initThemeVars;
return {
...baseVars,
...themeVars.value,
};
});
/* 切换主题 */
export const toggleTheme = () => {
themeState.value = themeState.value === "light" ? "dark" : "light";
// 更新主题变量
const newBaseVars = themeState.value === "dark" ? darkThemeVars : initThemeVars;
themeVars.value = {
...newBaseVars,
colorTheme: themeVars.value.colorTheme, // 保持用户选择的主题色
};
};
/* 设置主题色 */
export const setThemeColor = (color: string) => {
themeVars.value = {
...themeVars.value,
colorTheme: color,
};
};
/* 导出主题相关的工具 */
export const useTheme = () => {
return {
themeState,
themeVars: computedThemeVars,
toggleTheme,
setThemeColor,
colorColumns,
};
};

View File

@@ -1,4 +1,6 @@
export const ROLE_ROOT = "ROOT";
/**
* 常量统一导出
*/
// 🔗 导出所有存储键常量
export * from "./storage-keys";
// 存储相关常量
export * from "./storage.constant";

View File

@@ -1,10 +0,0 @@
import type { App } from "vue";
import { hasPerm, hasRole } from "./permission";
// 全局注册 directive
export function setupDirective(app: App<Element>) {
// 使 v-hasPerm 在所有组件中都可用
app.directive("hasPerm", hasPerm);
app.directive("hasRole", hasRole);
}

View File

@@ -1,69 +0,0 @@
import type { Directive, DirectiveBinding } from "vue";
import { useUserStore } from "@/store";
/**
* 按钮权限
*/
export const hasPerm: Directive = {
mounted(el: HTMLElement, binding: DirectiveBinding) {
const requiredPerms = binding.value;
console.log("requiredPerms", requiredPerms);
if (!requiredPerms) {
return;
}
// 校验传入的权限值是否合法
if (!requiredPerms || (typeof requiredPerms !== "string" && !Array.isArray(requiredPerms))) {
throw new Error(
"需要提供权限标识例如v-has-perm=\"'sys:user:add'\" 或 v-has-perm=\"['sys:user:add', 'sys:user:edit']\""
);
}
const { roles = [], perms = [] } = useUserStore().userInfo || {};
// 超级管理员拥有所有权限
if (roles?.includes("ROOT")) {
return;
}
// 检查权限
const hasAuth = Array.isArray(requiredPerms)
? requiredPerms.some((perm) => perms.includes(perm))
: perms.includes(requiredPerms);
// 如果没有权限,移除该元素
if (!hasAuth && el.parentNode) {
el.parentNode.removeChild(el);
}
},
};
/**
* 角色权限指令
*/
export const hasRole: Directive = {
mounted(el: HTMLElement, binding: DirectiveBinding) {
const requiredRoles = binding.value;
// 校验传入的角色值是否合法
if (!requiredRoles || (typeof requiredRoles !== "string" && !Array.isArray(requiredRoles))) {
throw new Error(
"需要提供角色标识例如v-has-role=\"'ADMIN'\" 或 v-has-role=\"['ADMIN', 'TEST']\""
);
}
const { roles = [] } = useUserStore().userInfo || {};
// 检查是否有对应角色权限
const hasAuth = Array.isArray(requiredRoles)
? requiredRoles.some((role) => roles.includes(role))
: roles.includes(requiredRoles);
// 如果没有权限,移除元素
if (!hasAuth && el.parentNode) {
el.parentNode.removeChild(el);
}
},
};

View File

@@ -1,18 +0,0 @@
/**
* 响应码枚举
*/
export const enum ResultCodeEnum {
/**
* 成功
*/
SUCCESS = "00000",
/**
* 错误
*/
ERROR = "B0001",
/**
* 令牌无效或过期
*/
TOKEN_INVALID = "A0230",
}

View File

@@ -0,0 +1,49 @@
/**
* API响应码枚举
*/
export const enum ApiCode {
/**
* 成功
*/
SUCCESS = "00000",
/**
* 通用错误
*/
ERROR = "B0001",
/**
* 令牌无效或过期
*/
TOKEN_INVALID = "A0230",
/**
* 令牌已过期
*/
TOKEN_EXPIRED = "A0231",
/**
* 未授权访问
*/
UNAUTHORIZED = "A0232",
/**
* 禁止访问
*/
FORBIDDEN = "A0233",
/**
* 参数校验失败
*/
PARAM_INVALID = "A0400",
/**
* 资源不存在
*/
NOT_FOUND = "A0404",
/**
* 服务器内部错误
*/
INTERNAL_ERROR = "B0500",
}

View File

@@ -1,4 +1,32 @@
<script lang="ts" setup>
import type { ConfigProviderThemeVars } from "wot-design-uni";
const themeVars = reactive<ConfigProviderThemeVars>({
colorTheme: "#FF5454",
tabsNavLineBgColor: "red",
navbarColor: "#ffffff",
});
</script>
<script lang="ts">
export default {
options: {
addGlobalClass: true,
virtualHost: true,
styleIsolation: "shared",
},
};
</script>
<template>
<div>默认布局</div>
<slot></slot>
<wd-config-provider
:theme-vars="themeVars"
custom-style="background-color: #f5f5f5;min-height: 100vh"
>
<slot />
<wd-notify />
<wd-toast />
<wd-message-box />
<privacy-popup />
</wd-config-provider>
</template>

View File

@@ -8,26 +8,79 @@
* 记得注释
-->
<script lang="ts" setup>
import { useTabbar } from "@/composables/useTabbar";
import { useTheme } from "@/composables/theme/theme";
import { ref, computed, onMounted, nextTick } from "vue";
import { useTheme } from "@/composables/useTheme";
const router = useRouter();
// 定义 TabbarItem 接口
interface TabbarItem {
name: string;
value: number | null;
active: boolean;
title: string;
icon: string;
}
const route = useRoute();
// 根据 pages.json 配置的 tabbar 项目
const tabbarItems = ref<TabbarItem[]>([
{ name: "index", value: null, active: true, title: "首页", icon: "home" },
{ name: "mine", value: null, active: false, title: "我的", icon: "user" },
]);
const { themeVars, theme } = useTheme();
const { themeVars } = useTheme();
const { activeTabbar, getTabbarItemValue, setTabbarItemActive, tabbarList } = useTabbar();
// 计算属性
const tabbarList = computed(() => tabbarItems.value);
const activeTabbar = computed(() => {
const item = tabbarItems.value.find((item) => item.active);
return item || tabbarItems.value[0];
});
// 方法
const getTabbarItemValue = (name: string) => {
const item = tabbarItems.value.find((item) => item.name === name);
return item && item.value ? item.value : null;
};
const setTabbarItemActive = (name: string) => {
tabbarItems.value.forEach((item) => {
if (item.name === name) {
item.active = true;
} else {
item.active = false;
}
});
};
function handleTabbarChange({ value }: { value: string }) {
setTabbarItemActive(value);
router.pushTab({ name: value });
// 根据 tabbar 项目导航到对应页面
if (value === "index") {
uni.switchTab({
url: "/pages/index/index",
});
} else if (value === "mine") {
uni.switchTab({
url: "/pages/mine/index",
});
}
}
onMounted(() => {
nextTick(() => {
if (route.name && route.name !== activeTabbar.value.name) {
setTabbarItemActive(route.name);
// 获取当前页面路径
const pages = getCurrentPages();
if (pages.length > 0) {
const currentPage = pages[pages.length - 1];
const route = currentPage.route;
// 根据当前路由设置活跃的 tabbar
if (route?.includes("pages/index/index")) {
setTabbarItemActive("index");
} else if (route?.includes("pages/mine/index")) {
setTabbarItemActive("mine");
}
}
});
});
@@ -50,7 +103,7 @@ export default {
</script>
<template>
<wd-config-provider :theme-vars="themeVars" custom-style="min-height: 100vh" :theme="theme">
<wd-config-provider :theme-vars="themeVars" custom-style="min-height: 100vh" theme="dark">
<wd-navbar
:title="activeTabbar.title"
safe-area-inset-top

View File

@@ -1,6 +1,5 @@
import { createSSRApp } from "vue";
import App from "./App.vue";
import setupPlugins from "@/plugins";
import "uno.css";
import "@/styles/global.scss";
@@ -11,7 +10,6 @@ export function createApp() {
const app = createSSRApp(App);
setupStore(app);
app.use(setupPlugins);
return {
app,
};

View File

@@ -2,64 +2,97 @@
"pages": [
{
"path": "pages/index/index",
"type": "home"
"type": "home",
"style": {},
"layout": "tabbar"
},
{
"path": "pages/login/complete-profile",
"type": "page"
"type": "page",
"style": {}
},
{
"path": "pages/login/index",
"type": "page"
"type": "page",
"style": {}
},
{
"path": "pages/mine/index",
"type": "page"
"type": "page",
"style": {},
"layout": "tabbar"
},
{
"path": "pages/mine/about/index",
"type": "page"
"type": "page",
"style": {}
},
{
"path": "pages/mine/faq/index",
"type": "page"
"type": "page",
"style": {}
},
{
"path": "pages/mine/feedback/index",
"type": "page"
"type": "page",
"style": {}
},
{
"path": "pages/mine/profile/index",
"type": "page"
"type": "page",
"style": {}
},
{
"path": "pages/mine/settings/index",
"type": "page"
"type": "page",
"style": {}
},
{
"path": "pages/mine/settings/account/index",
"type": "page"
"type": "page",
"style": {}
},
{
"path": "pages/mine/settings/agreement/index",
"type": "page"
"type": "page",
"style": {}
},
{
"path": "pages/mine/settings/network/index",
"type": "page"
"type": "page",
"style": {}
},
{
"path": "pages/mine/settings/privacy/index",
"type": "page"
"type": "page",
"style": {}
},
{
"path": "pages/mine/settings/theme/index",
"type": "page"
"type": "page",
"style": {}
}
],
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "@uni-helper"
"navigationBarTitleText": "vue-uniapp-template"
},
"tabBar": {
"custom": true,
"height": "0px",
"color": "#00000000",
"selectedColor": "#00000000",
"backgroundColor": "#00000000",
"list": [
{
"text": "首页",
"pagePath": "pages/index/index"
},
{
"text": "我的",
"pagePath": "pages/mine/index"
}
]
},
"subPackages": []
}
}

View File

@@ -92,17 +92,25 @@
<script setup lang="ts">
import { dayjs } from "wot-design-uni";
import LogAPI, { VisitStatsVO } from "@/api/system/log";
// 定义访问统计数据类型
interface VisitStatsVO {
todayUvCount: number;
uvGrowthRate: number;
totalUvCount: number;
todayPvCount: number;
pvGrowthRate: number;
totalPvCount: number;
}
const current = ref<number>(0);
const visitStatsData = ref<VisitStatsVO>({
todayUvCount: 0,
uvGrowthRate: 0,
totalUvCount: 0,
todayPvCount: 0,
pvGrowthRate: 0,
totalPvCount: 0,
todayUvCount: 1234,
uvGrowthRate: 15.6,
totalUvCount: 45678,
todayPvCount: 5678,
pvGrowthRate: 23.4,
totalPvCount: 123456,
});
// 图表数据
@@ -163,6 +171,31 @@ const navList = reactive([
},
]);
// 生成静态的访问趋势数据
const generateStaticTrendData = (days: number) => {
const dates = [];
const ipList = [];
const pvList = [];
const today = new Date();
for (let i = days - 1; i >= 0; i--) {
const date = new Date(today);
date.setDate(today.getDate() - i);
dates.push(dayjs(date).format("MM-DD"));
// 生成模拟数据
ipList.push(Math.floor(Math.random() * 500) + 200);
pvList.push(Math.floor(Math.random() * 1000) + 500);
}
return {
dates,
ipList,
pvList,
};
};
function handleClick(e: any) {
console.log(e);
}
@@ -170,25 +203,27 @@ function onChange(e: any) {
console.log(e);
}
// 加载访问统计数据
// 加载访问统计数据(使用静态数据)
const loadVisitStatsData = async () => {
LogAPI.getVisitStats().then((data) => {
visitStatsData.value = data;
});
// 模拟异步加载
setTimeout(() => {
visitStatsData.value = {
todayUvCount: 1234,
uvGrowthRate: 15.6,
totalUvCount: 45678,
todayPvCount: 5678,
pvGrowthRate: 23.4,
totalPvCount: 123456,
};
}, 100);
};
// 加载访问趋势数据
// 加载访问趋势数据(使用静态数据)
const loadVisitTrendData = () => {
const endDate = new Date();
const startDate = new Date(endDate);
startDate.setDate(endDate.getDate() - recentDaysRange.value + 1);
// 模拟异步加载
setTimeout(() => {
const data = generateStaticTrendData(recentDaysRange.value);
const visitTrendQuery = {
startDate: dayjs(startDate).format("YYYY-MM-DD"),
endDate: dayjs(endDate).format("YYYY-MM-DD"),
};
LogAPI.getVisitTrend(visitTrendQuery).then((data) => {
const res = {
categories: data.dates,
series: [
@@ -203,7 +238,7 @@ const loadVisitTrendData = () => {
],
};
chartData.value = JSON.parse(JSON.stringify(res));
});
}, 100);
};
// 数据范围变化

View File

@@ -115,7 +115,7 @@
import { ref, reactive, computed } from "vue";
import { onLoad } from "@dcloudio/uni-app";
import { useToast } from "wot-design-uni";
import { useUserStore } from "@/store/modules/user";
import { useUserStore } from "@/store/modules/user.store";
import UserAPI, { type UserProfileForm } from "@/api/user";
import FileAPI, { type FileInfo } from "@/api/file";
import WechatProfile from "@/components/WechatProfile.vue";

View File

@@ -92,7 +92,7 @@
<script lang="ts" setup>
import { onLoad } from "@dcloudio/uni-app";
import { type LoginFormData } from "@/api/auth";
import { useUserStore } from "@/store/modules/user";
import { useUserStore } from "@/store/modules/user.store";
import { useToast } from "wot-design-uni";
import { ref } from "vue";

View File

@@ -153,8 +153,8 @@
<script lang="ts" setup>
import { useToast } from "wot-design-uni";
import { useUserStore } from "@/store/modules/user";
import { useThemeStore } from "@/store/modules/theme";
import { useUserStore } from "@/store/modules/user.store";
import { useThemeStore } from "@/store/modules/theme.store";
import { computed } from "vue";
const toast = useToast();

View File

@@ -35,7 +35,7 @@
</template>
<script lang="ts" setup>
import { useUserStore } from "@/store/modules/user";
import { useUserStore } from "@/store/modules/user.store";
import { checkLogin } from "@/utils/auth";
import { computed, ref } from "vue";
@@ -193,8 +193,6 @@ const handleLogout = () => {
// 检查登录状态
onLoad(() => {
if (!checkLogin()) return;
getCacheSize();
});
</script>

View File

@@ -114,12 +114,12 @@
<script lang="ts" setup>
import { onLoad, onShow } from "@dcloudio/uni-app";
import { useTheme } from "@/composables/theme/theme";
import { useThemeStore } from "@/store/modules/theme";
import { useTheme } from "@/composables/useTheme";
import { useThemeStore } from "@/store/modules/theme.store";
import { applyThemeOnPageShow } from "@/utils/theme";
import { ref, computed, watch, onMounted } from "vue";
const { toggleTheme, themeVars, theme, setThemeColor } = useTheme();
const { toggleTheme, themeVars, themeState, setThemeColor } = useTheme();
const themeStore = useThemeStore();
// 暗黑模式状态
@@ -151,7 +151,7 @@ const currentThemeColor = computed(() => {
});
// 监听暗黑模式变化
watch(theme, (newTheme) => {
watch(themeState, (newTheme) => {
isDark.value = newTheme === "dark";
});
@@ -240,8 +240,8 @@ const resetTheme = () => {
customColor.value = defaultColor;
// 重置为浅色模式
if (theme.value === "dark") {
toggleTheme("light");
if (themeState.value === "dark") {
toggleTheme();
}
uni.showToast({
@@ -261,7 +261,7 @@ onLoad(() => {
onMounted(() => {
// 初始化暗黑模式状态
isDark.value = theme.value === "dark";
isDark.value = themeState.value === "dark";
customColor.value = currentThemeColor.value;
});

View File

@@ -1,9 +0,0 @@
import type { App } from "vue";
import { setupDirective } from "@/directive";
export default {
install(app: App<Element>) {
// 自定义指令(directive)
setupDirective(app);
},
};

View File

@@ -8,6 +8,6 @@ export function setupStore(app: App<Element>) {
app.use(store);
}
export * from "./modules/user";
export * from "./modules/theme";
export * from "./modules/user.store";
export * from "./modules/theme.store";
export { store };

View File

@@ -1,7 +1,24 @@
import { useUserStore } from "@/store/modules/user";
import { useUserStore } from "@/store/modules/user.store";
import { Storage } from "./storage";
import { ACCESS_TOKEN_KEY, REFRESH_TOKEN_KEY } from "@/constants";
/**
* 认证工具函数
*
* 使用示例:
*
* 1. 检查登录状态并自动跳转:
* if (!checkLogin()) return; // 未登录会自动跳转到登录页
*
* 2. 静默检查登录状态:
* if (!isLoggedIn()) {
* // 处理未登录逻辑,不会自动跳转
* }
*
* 3. 强制要求登录:
* requireLogin(); // 清除无效状态并跳转到登录页
*/
/**
* 获取访问令牌
* @returns 返回访问令牌如果不存在则返回null
@@ -44,21 +61,82 @@ export function clearTokens(): void {
/**
* 检查用户登录状态,未登录则跳转到登录页面
* @param silent 是否静默检查,不跳转登录页面
* @returns 返回用户是否已登录
*/
export function checkLogin(): boolean {
export function checkLogin(silent: boolean = false): boolean {
const userStore = useUserStore();
const accessToken = getAccessToken();
if (!userStore.userInfo) {
const pages = getCurrentPages();
const currentPage = pages[pages.length - 1];
const currentPagePath = `/${currentPage.route}`;
// 检查 token 和用户信息是否都存在
const isLoggedIn = !!(accessToken && userStore.userInfo);
uni.navigateTo({
url: `/pages/login/index?redirect=${encodeURIComponent(currentPagePath)}`,
});
return false;
if (!isLoggedIn && !silent) {
try {
// 获取当前页面路径
let currentPagePath = "/pages/index/index"; // 默认路径
const pages = getCurrentPages();
if (pages && pages.length > 0) {
const currentPage = pages[pages.length - 1];
if (currentPage && currentPage.route) {
currentPagePath = `/${currentPage.route}`;
// 处理页面参数 - 使用类型断言
const pageOptions = (currentPage as any).options;
if (pageOptions && Object.keys(pageOptions).length > 0) {
const params = new URLSearchParams(pageOptions as Record<string, string>);
currentPagePath += `?${params.toString()}`;
}
}
}
// 跳转到登录页面
uni.navigateTo({
url: `/pages/login/index?redirect=${encodeURIComponent(currentPagePath)}`,
fail: (error) => {
console.error("跳转登录页面失败:", error);
// 如果 navigateTo 失败,尝试使用 reLaunch
uni.reLaunch({
url: "/pages/login/index",
});
},
});
} catch (error) {
console.error("检查登录状态时发生错误:", error);
// 发生错误时,尝试直接跳转到登录页
uni.reLaunch({
url: "/pages/login/index",
});
}
}
return true;
return isLoggedIn;
}
/**
* 检查用户是否已登录(静默检查,不跳转)
* @returns 返回用户是否已登录
*/
export function isLoggedIn(): boolean {
return checkLogin(true);
}
/**
* 强制用户登录,清除无效的登录状态
*/
export function requireLogin(): void {
const userStore = useUserStore();
const accessToken = getAccessToken();
if (!accessToken || !userStore.userInfo) {
// 清除可能存在的无效状态
clearTokens();
userStore.logout();
// 跳转到登录页
uni.reLaunch({
url: "/pages/login/index",
});
}
}

View File

@@ -1,5 +1,5 @@
import { getAccessToken, clearTokens } from "@/utils/auth";
import { ResultCodeEnum } from "@/enums/ResultCodeEnum";
import { ApiCode } from "@/enums/api-code.enum";
export default function request<T>(options: UniApp.RequestOptions): Promise<T> {
// H5 使用 VITE_APP_BASE_API 作为代理路径,其他平台使用 VITE_APP_API_URL 作为请求路径
@@ -21,11 +21,11 @@ export default function request<T>(options: UniApp.RequestOptions): Promise<T> {
const resData = response.data as ResponseData<T>;
// 业务状态码 00000 表示成功
if (resData.code === ResultCodeEnum.SUCCESS) {
if (resData.code === ApiCode.SUCCESS) {
resolve(resData.data);
}
// 令牌失效或过期处理
else if (resData.code === ResultCodeEnum.TOKEN_INVALID) {
else if (resData.code === ApiCode.TOKEN_INVALID) {
console.log("令牌失效或过期处理");
clearTokens();
// 跳转到登录页