This commit is contained in:
李志强 2026-01-05 20:16:30 +08:00
parent 9e49288dbb
commit 1e64ef96f7
7 changed files with 264 additions and 80 deletions

View File

@ -137,9 +137,35 @@ class LoginController extends Base
AdminUser::where('uid', $aUser['uid'])->update( AdminUser::where('uid', $aUser['uid'])->update(
['login_count' => $aUser['login_count'] + 1, 'update_time' => time()] ['login_count' => $aUser['login_count'] + 1, 'update_time' => time()]
); );
// 生成token简单的base64编码包含用户ID和时间戳
$tokenData = [
'user_id' => $aUser['uid'],
'timestamp' => time(),
'random' => rand(100000, 999999)
];
$token = base64_encode(json_encode($tokenData));
// 记录登录成功日志 // 记录登录成功日志
$this->recordLoginLog($account, 1); $this->recordLoginLog($account, 1);
return json(['code' => 0, 'msg' => '登录成功', 'data' => []]);
return json([
'code' => 0,
'msg' => '登录成功',
'data' => [
'token' => $token,
'user_info' => [
'id' => $aUser['uid'],
'account' => $aUser['account'],
'name' => $aUser['name'],
'avatar' => $aUser['avatar'] ?? '/static/images/avatar.png',
'phone' => $aUser['phone'] ?? '',
'sex' => $aUser['sex'] ?? 1,
'qq' => $aUser['qq'] ?? '',
'wechat' => $aUser['wechat'] ?? '',
'create_time' => $aUser['create_time'] ?? 0
]
]
]);
} }
} }

View File

@ -0,0 +1,96 @@
<?php
/**
* 商业使用授权协议
*
* Copyright (c) 2026 [云泽网]. 保留所有权利.
*
* 本软件仅供评估使用。任何商业用途必须获得书面授权许可。
* 未经授权商业使用本软件属于侵权行为,将承担法律责任。
*
* 授权购买请联系: 357099073@qq.com
* 官方网站: https://www.yunzer.cn
*
* 评估用户须知:
* 1. 禁止移除版权声明
* 2. 禁止用于生产环境
* 3. 禁止转售或分发
*/
/**
* 后台管理系统-系统管理
*/
namespace app\admin\controller;
use think\facade\Db;
use app\admin\controller\BaseController;
use app\admin\model\System\SystemMenu;
class SystemController extends BaseController
{
// 获取菜单列表
public function getMenuList()
{
try {
// 获取所有菜单,按排序和层级关系组织
$menus = SystemMenu::where('status', 1)
->order('sort', 'asc')
->order('id', 'asc')
->select()
->toArray();
// 按父子关系组织菜单
$menuTree = $this->buildMenuTree($menus);
// 直接输出JSON响应
header('Content-type: application/json');
echo json_encode([
'code' => 0,
'msg' => '获取菜单列表成功',
'data' => $menuTree
]);
exit;
} catch (\Exception $e) {
header('Content-type: application/json');
echo json_encode([
'code' => 1,
'msg' => '获取菜单列表失败:' . $e->getMessage()
]);
exit;
}
}
// 递归构建菜单树
private function buildMenuTree($menus, $parentId = 0)
{
$tree = [];
foreach ($menus as $menu) {
if ($menu['pid'] == $parentId) {
$children = $this->buildMenuTree($menus, $menu['id']);
$menuItem = [
'id' => $menu['id'],
'pid' => $menu['pid'],
'name' => $menu['name'] ?? '',
'title' => $menu['title'] ?? '',
'icon' => $menu['icon'] ?? '',
'path' => $menu['path'] ?? '',
'component' => $menu['component'] ?? '',
'redirect' => $menu['redirect'] ?? '',
'sort' => $menu['sort'] ?? 0,
'status' => $menu['status'] ?? 1,
'type' => $menu['type'] ?? 0
];
if (!empty($children)) {
$menuItem['children'] = $children;
}
$tree[] = $menuItem;
}
}
return $tree;
}
}

View File

@ -0,0 +1,25 @@
<?php
/**
* 商业使用授权协议
*
* Copyright (c) 2026 [云泽网]. 保留所有权利.
*
* 本软件仅供评估使用。任何商业用途必须获得书面授权许可。
* 未经授权商业使用本软件属于侵权行为,将承担法律责任。
*
* 授权购买请联系: 357099073@qq.com
* 官方网站: https://www.yunzer.cn
*
* 评估用户须知:
* 1. 禁止移除版权声明
* 2. 禁止用于生产环境
* 3. 禁止转售或分发
*/
namespace app\admin\model\System;
use think\Model;
class SystemMenu extends Model
{
}

View File

@ -81,16 +81,31 @@ class UserController extends BaseController
// 记录登录日志 // 记录登录日志
Log::record('用户登录成功:' . $user->account, 'info'); Log::record('用户登录成功:' . $user->account, 'info');
// 生成token简单的base64编码包含用户ID和时间戳
$tokenData = [
'user_id' => $user->id,
'timestamp' => time(),
'random' => rand(100000, 999999)
];
$token = base64_encode(json_encode($tokenData));
// 返回用户信息给前端缓存 // 返回用户信息给前端缓存
return json([ return json([
'code' => 0, 'code' => 0,
'msg' => '登录成功', 'msg' => '登录成功',
'data' => [ 'data' => [
'uid' => $user->uid, 'token' => $token,
'user_name' => $user->name, 'user_info' => [
'user_account' => $user->account, 'id' => $user->id,
'user_avatar' => $user->avatar ?? '/static/images/avatar.png', 'account' => $user->account,
'login_time' => time() 'name' => $user->name,
'avatar' => $user->avatar ?? '/static/images/avatar.png',
'phone' => $user->phone ?? '',
'sex' => $user->sex ?? 1,
'qq' => $user->qq ?? '',
'wechat' => $user->wechat ?? '',
'create_time' => $user->create_time ?? 0
]
] ]
]); ]);
@ -183,6 +198,8 @@ class UserController extends BaseController
$cookies = [ $cookies = [
'user_id', 'user_id',
'user_account', 'user_account',
'user-store',
'appfastnav',
'user_name', 'user_name',
'user_avatar', 'user_avatar',
'expire_time', 'expire_time',

View File

@ -2,7 +2,7 @@
const ENV_CONFIG = { const ENV_CONFIG = {
// API配置 // API配置
API_BASE_URL: import.meta.env.VITE_APP_API_BASE_URL || (import.meta.env.DEV ? 'http://localhost:8000/api' : 'https://www.yunzer.cn/api'), API_BASE_URL: import.meta.env.VITE_APP_API_BASE_URL,
REQUEST_TIMEOUT: 10000, REQUEST_TIMEOUT: 10000,
// 应用配置 // 应用配置
@ -11,6 +11,8 @@ const ENV_CONFIG = {
// 本地存储key // 本地存储key
TOKEN_KEY: 'admin_token', TOKEN_KEY: 'admin_token',
USER_INFO_KEY: 'admin_user_info', USER_INFO_KEY: 'admin_user_info',
USER_STORE_KEY: 'user-store',
APPFASTNAV_KEY: 'appfastnav',
// 是否为开发环境 // 是否为开发环境
IS_DEV: import.meta.env.DEV, IS_DEV: import.meta.env.DEV,

View File

@ -1,36 +1,40 @@
import { defineStore } from 'pinia' import { defineStore } from "pinia";
import { login as loginApi, getUserInfo as getUserInfoApi, logout as logoutApi } from '@/api/user' import {
import ENV_CONFIG from '@/config/env' login as loginApi,
getUserInfo as getUserInfoApi,
logout as logoutApi,
} from "@/api/user";
import ENV_CONFIG from "@/config/env";
interface LoginForm { interface LoginForm {
account: string account: string;
password: string password: string;
} }
interface UserInfo { interface UserInfo {
id: number id: number;
account: string account: string;
name: string name: string;
avatar?: string avatar?: string;
phone?: string phone?: string;
sex?: number sex?: number;
qq?: string qq?: string;
wechat?: string wechat?: string;
create_time?: number create_time?: number;
} }
interface LoginResponse { interface LoginResponse {
token: string token: string;
user_info: UserInfo user_info: UserInfo;
} }
const useUserStore = defineStore('user', { const useUserStore = defineStore("user", {
state: () => { state: () => {
return { return {
token: '', token: "",
userInfo: null as UserInfo | null, userInfo: null as UserInfo | null,
isLogin: false isLogin: false,
} };
}, },
getters: { getters: {
@ -39,40 +43,45 @@ const useUserStore = defineStore('user', {
// 获取token // 获取token
getToken: (state) => state.token, getToken: (state) => state.token,
// 是否已登录 // 是否已登录
getIsLogin: (state) => state.isLogin getIsLogin: (state) => state.isLogin,
}, },
actions: { actions: {
// 用户登录 // 用户登录
async userLogin(loginForm: LoginForm) { async userLogin(loginForm: LoginForm) {
try { try {
const response = await loginApi(loginForm) as unknown as LoginResponse const response = (await loginApi(
loginForm
)) as unknown as LoginResponse;
// 后端返回格式为 { token: string, user_info: UserInfo } // 后端返回格式为 { token: string, user_info: UserInfo }
if (response && response.token && response.user_info) { if (response && response.token && response.user_info) {
const { token, user_info } = response const { token, user_info } = response;
// 保存登录状态 // 保存登录状态
this.token = token this.token = token;
this.userInfo = user_info this.userInfo = user_info;
this.isLogin = true this.isLogin = true;
// 保存到 localStorage // 保存到 localStorage
localStorage.setItem(ENV_CONFIG.TOKEN_KEY, token) localStorage.setItem(ENV_CONFIG.TOKEN_KEY, token);
localStorage.setItem(ENV_CONFIG.USER_INFO_KEY, JSON.stringify(user_info)) localStorage.setItem(
ENV_CONFIG.USER_INFO_KEY,
return user_info JSON.stringify(user_info)
);
return user_info;
} else { } else {
throw new Error('登录响应数据格式错误') throw new Error("登录响应数据格式错误");
} }
} catch (error: any) { } catch (error: any) {
// 处理不同类型的错误 // 处理不同类型的错误
if (error.response?.data?.msg) { if (error.response?.data?.msg) {
throw new Error(error.response.data.msg) throw new Error(error.response.data.msg);
} else if (error.message) { } else if (error.message) {
throw new Error(error.message) throw new Error(error.message);
} else { } else {
throw new Error('登录失败,请稍后重试') throw new Error("登录失败,请稍后重试");
} }
} }
}, },
@ -80,54 +89,54 @@ const useUserStore = defineStore('user', {
// 用户登出 // 用户登出
async userLogout() { async userLogout() {
try { try {
await logoutApi() await logoutApi();
} catch (error) { } catch (error) {
console.error('登出API调用失败:', error) console.error("登出API调用失败:", error);
} finally { } finally {
this.token = '' this.token = "";
this.userInfo = null this.userInfo = null;
this.isLogin = false this.isLogin = false;
// 清除 localStorage // 清除 localStorage
localStorage.removeItem(ENV_CONFIG.TOKEN_KEY) localStorage.removeItem(ENV_CONFIG.TOKEN_KEY);
localStorage.removeItem(ENV_CONFIG.USER_INFO_KEY) localStorage.removeItem(ENV_CONFIG.USER_INFO_KEY);
} }
}, },
// 初始化用户状态(从 localStorage 恢复) // 初始化用户状态(从 localStorage 恢复)
async initUserState() { async initUserState() {
const token = localStorage.getItem(ENV_CONFIG.TOKEN_KEY) const token = localStorage.getItem(ENV_CONFIG.TOKEN_KEY);
const userInfoStr = localStorage.getItem(ENV_CONFIG.USER_INFO_KEY) const userInfoStr = localStorage.getItem(ENV_CONFIG.USER_INFO_KEY);
if (token && userInfoStr) { if (token && userInfoStr) {
try { try {
// 验证token是否有效 // 验证token是否有效
const response = await getUserInfoApi() as unknown as UserInfo const response = (await getUserInfoApi()) as unknown as UserInfo;
if (response && response.id) { if (response && response.id) {
this.token = token this.token = token;
this.userInfo = response this.userInfo = response;
this.isLogin = true this.isLogin = true;
} else { } else {
// token无效清除本地存储但不调用logout API // token无效清除本地存储但不调用logout API
this.clearUserState() this.clearUserState();
} }
} catch (error: any) { } catch (error: any) {
console.error('获取用户信息失败:', error) console.error("获取用户信息失败:", error);
// 如果有本地token和用户信息先保持登录状态 // 如果有本地token和用户信息先保持登录状态
// 只有在明确知道token无效时才清除 // 只有在明确知道token无效时才清除
if (error.response?.status === 401) { if (error.response?.status === 401) {
this.clearUserState() this.clearUserState();
} else { } else {
// 网络错误或其他错误,使用本地存储的数据 // 网络错误或其他错误,使用本地存储的数据
try { try {
const userInfo = JSON.parse(userInfoStr) const userInfo = JSON.parse(userInfoStr);
this.token = token this.token = token;
this.userInfo = userInfo this.userInfo = userInfo;
this.isLogin = true this.isLogin = true;
} catch (parseError) { } catch (parseError) {
// 本地数据解析失败,清除状态 // 本地数据解析失败,清除状态
this.clearUserState() this.clearUserState();
} }
} }
} }
@ -136,21 +145,23 @@ const useUserStore = defineStore('user', {
// 清除用户状态不调用API // 清除用户状态不调用API
clearUserState() { clearUserState() {
this.token = '' this.token = "";
this.userInfo = null this.userInfo = null;
this.isLogin = false this.isLogin = false;
// 清除 localStorage // 清除 localStorage
localStorage.removeItem(ENV_CONFIG.TOKEN_KEY) localStorage.removeItem(ENV_CONFIG.TOKEN_KEY);
localStorage.removeItem(ENV_CONFIG.USER_INFO_KEY) localStorage.removeItem(ENV_CONFIG.USER_INFO_KEY);
} localStorage.removeItem(ENV_CONFIG.USER_STORE_KEY);
localStorage.removeItem(ENV_CONFIG.APPFASTNAV_KEY);
},
}, },
persist: { persist: {
key: 'user-store', key: "user-store",
storage: localStorage, storage: localStorage,
paths: ['token', 'userInfo', 'isLogin'] paths: ["token", "userInfo", "isLogin"],
} },
}) });
export default useUserStore export default useUserStore;

View File

@ -66,3 +66,10 @@ Route::group('api', function () {
// 发布文章接口 // 发布文章接口
Route::post('articles/publish', 'index/Articles/publishArticle'); Route::post('articles/publish', 'index/Articles/publishArticle');
})->middleware(\app\middleware\Cors::class); })->middleware(\app\middleware\Cors::class);
// ADMIN路由组
Route::group('admin', function () {
// 系统管理
Route::get('system/getMenuList', 'admin/System/getMenuList');
})->middleware(\app\middleware\Cors::class);