更新
This commit is contained in:
parent
9e49288dbb
commit
1e64ef96f7
@ -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
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
96
app/admin/controller/SystemController.php
Normal file
96
app/admin/controller/SystemController.php
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
25
app/admin/model/System/SystemMenu.php
Normal file
25
app/admin/model/System/SystemMenu.php
Normal 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
|
||||||
|
{
|
||||||
|
}
|
||||||
@ -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',
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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);
|
||||||
Loading…
Reference in New Issue
Block a user