前端types修改
This commit is contained in:
71
schoolNewsWeb/src/apis/auth.ts
Normal file
71
schoolNewsWeb/src/apis/auth.ts
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
/**
|
||||||
|
* @description 认证相关API
|
||||||
|
* @author yslg
|
||||||
|
* @since 2025-10-06
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { api } from './index';
|
||||||
|
import type { LoginParam, LoginDomain } from '@/types';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 认证API服务
|
||||||
|
*/
|
||||||
|
export const authApi = {
|
||||||
|
/**
|
||||||
|
* 用户登录
|
||||||
|
* @param loginParam 登录参数
|
||||||
|
* @returns Promise<LoginDomain>
|
||||||
|
*/
|
||||||
|
async login(loginParam: LoginParam): Promise<LoginDomain> {
|
||||||
|
const response = await api.post<LoginDomain>('/auth/login', loginParam);
|
||||||
|
return response.data.data!;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户登出
|
||||||
|
* @returns Promise<string>
|
||||||
|
*/
|
||||||
|
async logout(): Promise<string> {
|
||||||
|
const response = await api.post<string>('/auth/logout');
|
||||||
|
return response.data.data!;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取验证码
|
||||||
|
* @returns Promise<{captchaId: string, captchaImage: string}>
|
||||||
|
*/
|
||||||
|
async getCaptcha(): Promise<{ captchaId: string; captchaImage: string }> {
|
||||||
|
const response = await api.get<{ captchaId: string; captchaImage: string }>('/auth/captcha');
|
||||||
|
return response.data.data!;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 刷新Token
|
||||||
|
* @returns Promise<string>
|
||||||
|
*/
|
||||||
|
async refreshToken(): Promise<string> {
|
||||||
|
const response = await api.post<string>('/auth/refresh-token');
|
||||||
|
return response.data.data!;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送手机验证码
|
||||||
|
* @param phone 手机号
|
||||||
|
* @returns Promise<boolean>
|
||||||
|
*/
|
||||||
|
async sendSmsCode(phone: string): Promise<boolean> {
|
||||||
|
const response = await api.post<boolean>('/auth/send-sms-code', { phone });
|
||||||
|
return response.data.data!;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送邮箱验证码
|
||||||
|
* @param email 邮箱
|
||||||
|
* @returns Promise<boolean>
|
||||||
|
*/
|
||||||
|
async sendEmailCode(email: string): Promise<boolean> {
|
||||||
|
const response = await api.post<boolean>('/auth/send-email-code', { email });
|
||||||
|
return response.data.data!;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
@@ -1,50 +1,249 @@
|
|||||||
import axios, { AxiosRequestConfig } from "axios";
|
import axios, { AxiosRequestConfig, AxiosResponse, AxiosError } from "axios";
|
||||||
import { ElLoading } from "element-plus";
|
import { ElLoading, ElMessage } from "element-plus";
|
||||||
|
import type { ResultDomain } from "@/types";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 扩展AxiosRequestConfig以支持自定义配置
|
||||||
|
*/
|
||||||
|
interface CustomAxiosRequestConfig extends AxiosRequestConfig {
|
||||||
|
/** 是否显示加载动画 */
|
||||||
|
showLoading?: boolean;
|
||||||
|
/** 是否显示错误提示 */
|
||||||
|
showError?: boolean;
|
||||||
|
/** 是否需要token */
|
||||||
|
requiresAuth?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Token管理
|
||||||
|
*/
|
||||||
|
export const TokenManager = {
|
||||||
|
/** 获取token */
|
||||||
|
getToken(): string | null {
|
||||||
|
return localStorage.getItem('token');
|
||||||
|
},
|
||||||
|
|
||||||
|
/** 设置token */
|
||||||
|
setToken(token: string): void {
|
||||||
|
localStorage.setItem('token', token);
|
||||||
|
},
|
||||||
|
|
||||||
|
/** 移除token */
|
||||||
|
removeToken(): void {
|
||||||
|
localStorage.removeItem('token');
|
||||||
|
},
|
||||||
|
|
||||||
|
/** 检查是否有token */
|
||||||
|
hasToken(): boolean {
|
||||||
|
return !!this.getToken();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建axios实例
|
||||||
|
*/
|
||||||
const request = axios.create({
|
const request = axios.create({
|
||||||
baseURL: "/api",
|
baseURL: import.meta.env.VITE_API_BASE_URL || "/api",
|
||||||
timeout: 30000,
|
timeout: 30000,
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json;charset=UTF-8',
|
'Content-Type': 'application/json;charset=UTF-8',
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
let loadingInstance: ReturnType<typeof ElLoading.service> | null = null;
|
let loadingInstance: ReturnType<typeof ElLoading.service> | null = null;
|
||||||
|
|
||||||
request.interceptors.request.use(config => {
|
/**
|
||||||
if (config.showLoading) {
|
* 请求拦截器
|
||||||
|
*/
|
||||||
|
request.interceptors.request.use(
|
||||||
|
(config: CustomAxiosRequestConfig) => {
|
||||||
|
// 显示加载动画
|
||||||
|
if (config.showLoading !== false) {
|
||||||
loadingInstance = ElLoading.service({
|
loadingInstance = ElLoading.service({
|
||||||
lock: true,
|
lock: true,
|
||||||
text: "加载中...",
|
text: "加载中...",
|
||||||
background: "rgba(0, 0, 0, 0.7)",
|
background: "rgba(0, 0, 0, 0.7)",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return config;
|
|
||||||
});
|
|
||||||
|
|
||||||
request.interceptors.response.use(response => {
|
// 添加token
|
||||||
|
const token = TokenManager.getToken();
|
||||||
|
if (token && config.headers) {
|
||||||
|
config.headers.Authorization = `Bearer ${token}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return config;
|
||||||
|
},
|
||||||
|
(error: AxiosError) => {
|
||||||
if (loadingInstance) {
|
if (loadingInstance) {
|
||||||
loadingInstance.close();
|
loadingInstance.close();
|
||||||
loadingInstance = null;
|
loadingInstance = null;
|
||||||
}
|
}
|
||||||
return response;
|
return Promise.reject(error);
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 响应拦截器
|
||||||
|
*/
|
||||||
|
request.interceptors.response.use(
|
||||||
|
(response: AxiosResponse<ResultDomain<any>>) => {
|
||||||
|
// 关闭加载动画
|
||||||
|
if (loadingInstance) {
|
||||||
|
loadingInstance.close();
|
||||||
|
loadingInstance = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = response.data;
|
||||||
|
|
||||||
|
// 检查是否为ResultDomain格式
|
||||||
|
if (result && typeof result === 'object' && 'code' in result) {
|
||||||
|
// 检查登录状态
|
||||||
|
if (result.login === false) {
|
||||||
|
ElMessage.error(result.message || '请先登录');
|
||||||
|
TokenManager.removeToken();
|
||||||
|
// 跳转到登录页
|
||||||
|
window.location.href = '/login';
|
||||||
|
return Promise.reject(new Error('未登录'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查权限
|
||||||
|
if (result.auth === false) {
|
||||||
|
ElMessage.error(result.message || '没有权限访问');
|
||||||
|
return Promise.reject(new Error('无权限'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查业务逻辑是否成功
|
||||||
|
if (!result.success) {
|
||||||
|
const config = response.config as CustomAxiosRequestConfig;
|
||||||
|
if (config.showError !== false) {
|
||||||
|
ElMessage.error(result.message || '操作失败');
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error(result.message || '操作失败'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回成功的数据
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 非ResultDomain格式,直接返回
|
||||||
|
return response;
|
||||||
|
},
|
||||||
|
(error: AxiosError<ResultDomain<any>>) => {
|
||||||
|
// 关闭加载动画
|
||||||
|
if (loadingInstance) {
|
||||||
|
loadingInstance.close();
|
||||||
|
loadingInstance = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const config = error.config as CustomAxiosRequestConfig;
|
||||||
|
|
||||||
|
// 处理HTTP错误
|
||||||
|
if (error.response) {
|
||||||
|
const { status, data } = error.response;
|
||||||
|
|
||||||
|
switch (status) {
|
||||||
|
case 401:
|
||||||
|
ElMessage.error('认证失败,请重新登录');
|
||||||
|
TokenManager.removeToken();
|
||||||
|
window.location.href = '/login';
|
||||||
|
break;
|
||||||
|
case 403:
|
||||||
|
ElMessage.error('没有权限访问该资源');
|
||||||
|
break;
|
||||||
|
case 404:
|
||||||
|
ElMessage.error('请求的资源不存在');
|
||||||
|
break;
|
||||||
|
case 500:
|
||||||
|
ElMessage.error(data?.message || '服务器内部错误');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (config?.showError !== false) {
|
||||||
|
ElMessage.error(data?.message || '请求失败');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (error.request) {
|
||||||
|
// 请求已发送但没有收到响应
|
||||||
|
ElMessage.error('网络错误,请检查网络连接');
|
||||||
|
} else {
|
||||||
|
// 其他错误
|
||||||
|
if (config?.showError !== false) {
|
||||||
|
ElMessage.error(error.message || '请求失败');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* API封装
|
||||||
|
*/
|
||||||
export const api = {
|
export const api = {
|
||||||
get: function(url: string, config = {} as AxiosRequestConfig) { return request.get(url, config) },
|
/**
|
||||||
post: function(url: string, data = {}, config = {} as AxiosRequestConfig) { return request.post(url, data, config) },
|
* GET请求
|
||||||
put: function(url: string, data = {}, config = {} as AxiosRequestConfig) { return request.put(url, data, config) },
|
*/
|
||||||
delete: function(url: string, config = {} as AxiosRequestConfig) { return request.delete(url, config) },
|
get<T = any>(url: string, config?: CustomAxiosRequestConfig): Promise<AxiosResponse<ResultDomain<T>>> {
|
||||||
upload: function(url: string, formData: FormData, config = {} as AxiosRequestConfig) {
|
return request.get<ResultDomain<T>>(url, config);
|
||||||
return request.post(url, formData, {
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* POST请求
|
||||||
|
*/
|
||||||
|
post<T = any>(url: string, data?: any, config?: CustomAxiosRequestConfig): Promise<AxiosResponse<ResultDomain<T>>> {
|
||||||
|
return request.post<ResultDomain<T>>(url, data, config);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PUT请求
|
||||||
|
*/
|
||||||
|
put<T = any>(url: string, data?: any, config?: CustomAxiosRequestConfig): Promise<AxiosResponse<ResultDomain<T>>> {
|
||||||
|
return request.put<ResultDomain<T>>(url, data, config);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DELETE请求
|
||||||
|
*/
|
||||||
|
delete<T = any>(url: string, config?: CustomAxiosRequestConfig): Promise<AxiosResponse<ResultDomain<T>>> {
|
||||||
|
return request.delete<ResultDomain<T>>(url, config);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PATCH请求
|
||||||
|
*/
|
||||||
|
patch<T = any>(url: string, data?: any, config?: CustomAxiosRequestConfig): Promise<AxiosResponse<ResultDomain<T>>> {
|
||||||
|
return request.patch<ResultDomain<T>>(url, data, config);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件上传
|
||||||
|
*/
|
||||||
|
upload<T = any>(url: string, formData: FormData, config?: CustomAxiosRequestConfig): Promise<AxiosResponse<ResultDomain<T>>> {
|
||||||
|
return request.post<ResultDomain<T>>(url, formData, {
|
||||||
...config,
|
...config,
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'multipart/form-data',
|
'Content-Type': 'multipart/form-data',
|
||||||
...(config.headers as Record<string, string>)
|
...(config?.headers as Record<string, string>)
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
},
|
||||||
}
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件下载
|
||||||
|
*/
|
||||||
|
download(url: string, filename?: string, config?: CustomAxiosRequestConfig): Promise<void> {
|
||||||
|
return request.get(url, {
|
||||||
|
...config,
|
||||||
|
responseType: 'blob'
|
||||||
|
}).then((response) => {
|
||||||
|
const blob = new Blob([response.data]);
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.href = window.URL.createObjectURL(blob);
|
||||||
|
link.download = filename || 'download';
|
||||||
|
link.click();
|
||||||
|
window.URL.revokeObjectURL(link.href);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export default request;
|
export default request;
|
||||||
92
schoolNewsWeb/src/apis/menu.ts
Normal file
92
schoolNewsWeb/src/apis/menu.ts
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
/**
|
||||||
|
* @description 菜单相关API
|
||||||
|
* @author yslg
|
||||||
|
* @since 2025-10-06
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { api } from './index';
|
||||||
|
import type { SysMenu, MenuTreeNode } from '@/types';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 菜单API服务
|
||||||
|
*/
|
||||||
|
export const menuApi = {
|
||||||
|
/**
|
||||||
|
* 获取当前用户的菜单树
|
||||||
|
* @returns Promise<MenuTreeNode[]>
|
||||||
|
*/
|
||||||
|
async getCurrentUserMenuTree(): Promise<MenuTreeNode[]> {
|
||||||
|
const response = await api.get<SysMenu>('/menu/user-tree');
|
||||||
|
return response.data.dataList! as MenuTreeNode[];
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有菜单列表
|
||||||
|
* @returns Promise<SysMenu[]>
|
||||||
|
*/
|
||||||
|
async getMenuList(): Promise<SysMenu[]> {
|
||||||
|
const response = await api.get<SysMenu>('/menu/list');
|
||||||
|
return response.data.dataList!;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取菜单树
|
||||||
|
* @returns Promise<MenuTreeNode[]>
|
||||||
|
*/
|
||||||
|
async getMenuTree(): Promise<MenuTreeNode[]> {
|
||||||
|
const response = await api.get<SysMenu>('/menu/tree');
|
||||||
|
return response.data.dataList! as MenuTreeNode[];
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据ID获取菜单
|
||||||
|
* @param menuId 菜单ID
|
||||||
|
* @returns Promise<SysMenu>
|
||||||
|
*/
|
||||||
|
async getMenuById(menuId: string): Promise<SysMenu> {
|
||||||
|
const response = await api.get<SysMenu>(`/menu/${menuId}`);
|
||||||
|
return response.data.data!;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建菜单
|
||||||
|
* @param menu 菜单信息
|
||||||
|
* @returns Promise<string> 返回菜单ID
|
||||||
|
*/
|
||||||
|
async createMenu(menu: SysMenu): Promise<string> {
|
||||||
|
const response = await api.post<string>('/menu', menu);
|
||||||
|
return response.data.data!;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新菜单
|
||||||
|
* @param menuId 菜单ID
|
||||||
|
* @param menu 菜单信息
|
||||||
|
* @returns Promise<boolean>
|
||||||
|
*/
|
||||||
|
async updateMenu(menuId: string, menu: SysMenu): Promise<boolean> {
|
||||||
|
const response = await api.put<boolean>(`/menu/${menuId}`, menu);
|
||||||
|
return response.data.data!;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除菜单
|
||||||
|
* @param menuId 菜单ID
|
||||||
|
* @returns Promise<boolean>
|
||||||
|
*/
|
||||||
|
async deleteMenu(menuId: string): Promise<boolean> {
|
||||||
|
const response = await api.delete<boolean>(`/menu/${menuId}`);
|
||||||
|
return response.data.data!;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取子菜单列表
|
||||||
|
* @param parentId 父菜单ID
|
||||||
|
* @returns Promise<SysMenu[]>
|
||||||
|
*/
|
||||||
|
async getChildMenus(parentId: string): Promise<SysMenu[]> {
|
||||||
|
const response = await api.get<SysMenu>(`/menu/children/${parentId}`);
|
||||||
|
return response.data.dataList!;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
153
schoolNewsWeb/src/apis/user.ts
Normal file
153
schoolNewsWeb/src/apis/user.ts
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
/**
|
||||||
|
* @description 用户相关API
|
||||||
|
* @author yslg
|
||||||
|
* @since 2025-10-06
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { api } from './index';
|
||||||
|
import type { SysUser, SysUserInfo, UserVO, PageDomain, PageParam } from '@/types';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户API服务
|
||||||
|
*/
|
||||||
|
export const userApi = {
|
||||||
|
/**
|
||||||
|
* 获取当前用户信息
|
||||||
|
* @returns Promise<UserVO>
|
||||||
|
*/
|
||||||
|
async getCurrentUser(): Promise<UserVO> {
|
||||||
|
const response = await api.get<UserVO>('/user/current');
|
||||||
|
return response.data.data!;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新当前用户信息
|
||||||
|
* @param user 用户信息
|
||||||
|
* @returns Promise<boolean>
|
||||||
|
*/
|
||||||
|
async updateCurrentUser(user: SysUser): Promise<boolean> {
|
||||||
|
const response = await api.put<boolean>('/user/current', user);
|
||||||
|
return response.data.data!;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新用户详细信息
|
||||||
|
* @param userInfo 用户详细信息
|
||||||
|
* @returns Promise<boolean>
|
||||||
|
*/
|
||||||
|
async updateUserInfo(userInfo: SysUserInfo): Promise<boolean> {
|
||||||
|
const response = await api.put<boolean>('/user/info', userInfo);
|
||||||
|
return response.data.data!;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改密码
|
||||||
|
* @param oldPassword 旧密码
|
||||||
|
* @param newPassword 新密码
|
||||||
|
* @returns Promise<boolean>
|
||||||
|
*/
|
||||||
|
async changePassword(oldPassword: string, newPassword: string): Promise<boolean> {
|
||||||
|
const response = await api.post<boolean>('/user/change-password', {
|
||||||
|
oldPassword,
|
||||||
|
newPassword
|
||||||
|
});
|
||||||
|
return response.data.data!;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户列表(分页)
|
||||||
|
* @param pageParam 分页参数
|
||||||
|
* @returns Promise<PageDomain<SysUser>>
|
||||||
|
*/
|
||||||
|
async getUserList(pageParam: PageParam): Promise<PageDomain<SysUser>> {
|
||||||
|
const response = await api.get<SysUser>('/user/list', {
|
||||||
|
params: pageParam
|
||||||
|
});
|
||||||
|
return response.data.pageDomain!;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据ID获取用户
|
||||||
|
* @param userId 用户ID
|
||||||
|
* @returns Promise<SysUser>
|
||||||
|
*/
|
||||||
|
async getUserById(userId: string): Promise<SysUser> {
|
||||||
|
const response = await api.get<SysUser>(`/user/${userId}`);
|
||||||
|
return response.data.data!;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建用户
|
||||||
|
* @param user 用户信息
|
||||||
|
* @returns Promise<string> 返回用户ID
|
||||||
|
*/
|
||||||
|
async createUser(user: SysUser): Promise<string> {
|
||||||
|
const response = await api.post<string>('/user', user);
|
||||||
|
return response.data.data!;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新用户
|
||||||
|
* @param userId 用户ID
|
||||||
|
* @param user 用户信息
|
||||||
|
* @returns Promise<boolean>
|
||||||
|
*/
|
||||||
|
async updateUser(userId: string, user: SysUser): Promise<boolean> {
|
||||||
|
const response = await api.put<boolean>(`/user/${userId}`, user);
|
||||||
|
return response.data.data!;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除用户
|
||||||
|
* @param userId 用户ID
|
||||||
|
* @returns Promise<boolean>
|
||||||
|
*/
|
||||||
|
async deleteUser(userId: string): Promise<boolean> {
|
||||||
|
const response = await api.delete<boolean>(`/user/${userId}`);
|
||||||
|
return response.data.data!;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量删除用户
|
||||||
|
* @param userIds 用户ID列表
|
||||||
|
* @returns Promise<boolean>
|
||||||
|
*/
|
||||||
|
async batchDeleteUsers(userIds: string[]): Promise<boolean> {
|
||||||
|
const response = await api.post<boolean>('/user/batch-delete', { userIds });
|
||||||
|
return response.data.data!;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 启用/禁用用户
|
||||||
|
* @param userId 用户ID
|
||||||
|
* @param status 状态 0-启用 1-禁用
|
||||||
|
* @returns Promise<boolean>
|
||||||
|
*/
|
||||||
|
async changeUserStatus(userId: string, status: number): Promise<boolean> {
|
||||||
|
const response = await api.patch<boolean>(`/user/${userId}/status`, { status });
|
||||||
|
return response.data.data!;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重置用户密码
|
||||||
|
* @param userId 用户ID
|
||||||
|
* @returns Promise<string> 返回新密码
|
||||||
|
*/
|
||||||
|
async resetPassword(userId: string): Promise<string> {
|
||||||
|
const response = await api.post<string>(`/user/${userId}/reset-password`);
|
||||||
|
return response.data.data!;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传用户头像
|
||||||
|
* @param file 文件
|
||||||
|
* @returns Promise<string> 返回头像URL
|
||||||
|
*/
|
||||||
|
async uploadAvatar(file: File): Promise<string> {
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('file', file);
|
||||||
|
const response = await api.upload<string>('/user/avatar', formData);
|
||||||
|
return response.data.data!;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
// 导出通用类型
|
|
||||||
export * from "./types/common";
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
// 通用响应类型
|
|
||||||
interface ApiResponse<T> {
|
|
||||||
code: number;
|
|
||||||
message: string;
|
|
||||||
success: boolean;
|
|
||||||
failure: boolean;
|
|
||||||
auth: boolean;
|
|
||||||
data?: T;
|
|
||||||
dataList?: T[];
|
|
||||||
}
|
|
||||||
|
|
||||||
// 扩展 axios 配置类型
|
|
||||||
declare module "axios" {
|
|
||||||
interface AxiosRequestConfig {
|
|
||||||
showLoading?: boolean;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export { ApiResponse };
|
|
||||||
86
schoolNewsWeb/src/types/auth/index.ts
Normal file
86
schoolNewsWeb/src/types/auth/index.ts
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
/**
|
||||||
|
* @description 认证相关类型定义
|
||||||
|
* @author yslg
|
||||||
|
* @since 2025-10-06
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { SysUser, SysUserInfo } from '../user';
|
||||||
|
import { DeptRoleVO } from '../dept';
|
||||||
|
import { SysPermission } from '../permission';
|
||||||
|
import { SysMenu } from '../menu';
|
||||||
|
import { LoginType } from '../enums';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录参数
|
||||||
|
*/
|
||||||
|
export interface LoginParam {
|
||||||
|
/** 登录类型 */
|
||||||
|
loginType?: LoginType;
|
||||||
|
/** 用户名 */
|
||||||
|
username?: string;
|
||||||
|
/** 邮箱 */
|
||||||
|
email?: string;
|
||||||
|
/** 手机号 */
|
||||||
|
phone?: string;
|
||||||
|
/** 微信ID */
|
||||||
|
wechatID?: string;
|
||||||
|
/** 密码 */
|
||||||
|
password?: string;
|
||||||
|
/** 验证码 */
|
||||||
|
captcha?: string;
|
||||||
|
/** 验证码ID */
|
||||||
|
captchaId?: string;
|
||||||
|
/** 记住我 */
|
||||||
|
rememberMe?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录域对象 - 包含登录后的所有用户信息
|
||||||
|
*/
|
||||||
|
export interface LoginDomain {
|
||||||
|
/** 用户基本信息 */
|
||||||
|
user?: SysUser;
|
||||||
|
/** 用户详细信息 */
|
||||||
|
userInfo?: SysUserInfo;
|
||||||
|
/** 用户角色列表 */
|
||||||
|
roles?: DeptRoleVO[];
|
||||||
|
/** 用户权限列表 */
|
||||||
|
permissions?: SysPermission[];
|
||||||
|
/** 用户菜单列表 */
|
||||||
|
menus?: SysMenu[];
|
||||||
|
/** JWT令牌 */
|
||||||
|
token?: string;
|
||||||
|
/** 令牌过期时间 */
|
||||||
|
tokenExpireTime?: string;
|
||||||
|
/** 登录时间 */
|
||||||
|
loginTime?: string;
|
||||||
|
/** 登录IP地址 */
|
||||||
|
ipAddress?: string;
|
||||||
|
/** 登录类型 */
|
||||||
|
loginType?: LoginType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录日志
|
||||||
|
*/
|
||||||
|
export interface SysLoginLog {
|
||||||
|
/** ID */
|
||||||
|
id?: string;
|
||||||
|
/** 用户ID */
|
||||||
|
userID?: string;
|
||||||
|
/** 用户名 */
|
||||||
|
username?: string;
|
||||||
|
/** 登录状态 0-失败 1-成功 */
|
||||||
|
status?: number;
|
||||||
|
/** 登录消息 */
|
||||||
|
message?: string;
|
||||||
|
/** 登录时间 */
|
||||||
|
loginTime?: string;
|
||||||
|
/** 登录IP */
|
||||||
|
ipAddress?: string;
|
||||||
|
/** 浏览器 */
|
||||||
|
browser?: string;
|
||||||
|
/** 操作系统 */
|
||||||
|
os?: string;
|
||||||
|
}
|
||||||
|
|
||||||
76
schoolNewsWeb/src/types/base/index.ts
Normal file
76
schoolNewsWeb/src/types/base/index.ts
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
/**
|
||||||
|
* @description 基础数据传输对象类型定义
|
||||||
|
* @author yslg
|
||||||
|
* @since 2025-10-06
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 基础DTO - 包含所有实体的公共字段
|
||||||
|
*/
|
||||||
|
export interface BaseDTO {
|
||||||
|
/** 主键ID */
|
||||||
|
id?: string;
|
||||||
|
/** 创建时间 */
|
||||||
|
createTime?: string;
|
||||||
|
/** 更新时间 */
|
||||||
|
updateTime?: string;
|
||||||
|
/** 删除时间 */
|
||||||
|
deleteTime?: string;
|
||||||
|
/** 是否删除 */
|
||||||
|
deleted?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页参数
|
||||||
|
*/
|
||||||
|
export interface PageParam {
|
||||||
|
/** 当前页码 */
|
||||||
|
page?: number;
|
||||||
|
/** 每页条数 */
|
||||||
|
size?: number;
|
||||||
|
/** 排序字段 */
|
||||||
|
orderBy?: string;
|
||||||
|
/** 排序方向 asc/desc */
|
||||||
|
orderDirection?: 'asc' | 'desc';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页结果
|
||||||
|
*/
|
||||||
|
export interface PageDomain<T> {
|
||||||
|
/** 当前页码 */
|
||||||
|
page: number;
|
||||||
|
/** 每页条数 */
|
||||||
|
size: number;
|
||||||
|
/** 总记录数 */
|
||||||
|
total: number;
|
||||||
|
/** 总页数 */
|
||||||
|
pages: number;
|
||||||
|
/** 数据列表 */
|
||||||
|
records: T[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 统一返回结果
|
||||||
|
*/
|
||||||
|
export interface ResultDomain<T> {
|
||||||
|
/** 状态码 */
|
||||||
|
code: number;
|
||||||
|
/** 返回消息 */
|
||||||
|
message: string;
|
||||||
|
/** 操作是否成功 */
|
||||||
|
success: boolean;
|
||||||
|
/** 是否登录 */
|
||||||
|
login: boolean;
|
||||||
|
/** 是否有权限 */
|
||||||
|
auth: boolean;
|
||||||
|
/** 返回数据 */
|
||||||
|
data?: T;
|
||||||
|
/** 返回数据列表 */
|
||||||
|
dataList?: T[];
|
||||||
|
/** 分页参数 */
|
||||||
|
pageParam?: PageParam;
|
||||||
|
/** 分页信息 */
|
||||||
|
pageDomain?: PageDomain<T>;
|
||||||
|
}
|
||||||
|
|
||||||
30
schoolNewsWeb/src/types/constants/index.ts
Normal file
30
schoolNewsWeb/src/types/constants/index.ts
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
/**
|
||||||
|
* @description 常量定义
|
||||||
|
* @author yslg
|
||||||
|
* @since 2025-10-06
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 存储键名常量
|
||||||
|
*/
|
||||||
|
export const StorageKeys = {
|
||||||
|
/** Token */
|
||||||
|
TOKEN: 'token',
|
||||||
|
/** 用户信息 */
|
||||||
|
USER_INFO: 'userInfo',
|
||||||
|
/** 登录信息 */
|
||||||
|
LOGIN_INFO: 'loginInfo'
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页默认值常量
|
||||||
|
*/
|
||||||
|
export const PageDefaults = {
|
||||||
|
/** 默认页码 */
|
||||||
|
PAGE: 1,
|
||||||
|
/** 默认每页条数 */
|
||||||
|
SIZE: 10,
|
||||||
|
/** 每页条数选项 */
|
||||||
|
SIZE_OPTIONS: [10, 20, 50, 100]
|
||||||
|
} as const;
|
||||||
|
|
||||||
47
schoolNewsWeb/src/types/dept/index.ts
Normal file
47
schoolNewsWeb/src/types/dept/index.ts
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
/**
|
||||||
|
* @description 部门相关类型定义
|
||||||
|
* @author yslg
|
||||||
|
* @since 2025-10-06
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { BaseDTO } from '../base';
|
||||||
|
import { SysRole } from '../role';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统部门
|
||||||
|
*/
|
||||||
|
export interface SysDept extends BaseDTO {
|
||||||
|
/** 部门ID */
|
||||||
|
deptID?: string;
|
||||||
|
/** 父部门ID */
|
||||||
|
parentID?: string;
|
||||||
|
/** 部门名称 */
|
||||||
|
name?: string;
|
||||||
|
/** 部门描述 */
|
||||||
|
description?: string;
|
||||||
|
/** 创建人 */
|
||||||
|
creator?: string;
|
||||||
|
/** 更新人 */
|
||||||
|
updater?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 部门角色VO
|
||||||
|
*/
|
||||||
|
export interface DeptRoleVO {
|
||||||
|
/** 部门信息 */
|
||||||
|
dept?: SysDept;
|
||||||
|
/** 角色信息 */
|
||||||
|
role?: SysRole;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 部门角色关联
|
||||||
|
*/
|
||||||
|
export interface SysDeptRole extends BaseDTO {
|
||||||
|
/** 部门ID */
|
||||||
|
deptID?: string;
|
||||||
|
/** 角色ID */
|
||||||
|
roleID?: string;
|
||||||
|
}
|
||||||
|
|
||||||
76
schoolNewsWeb/src/types/enums/index.ts
Normal file
76
schoolNewsWeb/src/types/enums/index.ts
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
/**
|
||||||
|
* @description 枚举类型统一导出
|
||||||
|
* @author yslg
|
||||||
|
* @since 2025-10-06
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP 状态码枚举
|
||||||
|
*/
|
||||||
|
export enum HttpStatus {
|
||||||
|
/** 成功 */
|
||||||
|
OK = 200,
|
||||||
|
/** 错误请求 */
|
||||||
|
BAD_REQUEST = 400,
|
||||||
|
/** 未授权 */
|
||||||
|
UNAUTHORIZED = 401,
|
||||||
|
/** 禁止访问 */
|
||||||
|
FORBIDDEN = 403,
|
||||||
|
/** 未找到 */
|
||||||
|
NOT_FOUND = 404,
|
||||||
|
/** 服务器错误 */
|
||||||
|
INTERNAL_SERVER_ERROR = 500
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户状态枚举
|
||||||
|
*/
|
||||||
|
export enum UserStatus {
|
||||||
|
/** 正常 */
|
||||||
|
NORMAL = 0,
|
||||||
|
/** 禁用 */
|
||||||
|
DISABLED = 1,
|
||||||
|
/** 锁定 */
|
||||||
|
LOCKED = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 性别枚举
|
||||||
|
*/
|
||||||
|
export enum Gender {
|
||||||
|
/** 未知 */
|
||||||
|
UNKNOWN = 0,
|
||||||
|
/** 男 */
|
||||||
|
MALE = 1,
|
||||||
|
/** 女 */
|
||||||
|
FEMALE = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 菜单类型枚举
|
||||||
|
*/
|
||||||
|
export enum MenuType {
|
||||||
|
/** 目录 */
|
||||||
|
DIRECTORY = 0,
|
||||||
|
/** 菜单 */
|
||||||
|
MENU = 1,
|
||||||
|
/** 按钮 */
|
||||||
|
BUTTON = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录类型枚举
|
||||||
|
*/
|
||||||
|
export enum LoginType {
|
||||||
|
/** 用户名登录 */
|
||||||
|
USERNAME = 'username',
|
||||||
|
/** 邮箱登录 */
|
||||||
|
EMAIL = 'email',
|
||||||
|
/** 手机号登录 */
|
||||||
|
PHONE = 'phone',
|
||||||
|
/** 微信登录 */
|
||||||
|
WECHAT = 'wechat',
|
||||||
|
/** 密码登录 */
|
||||||
|
PASSWORD = 'password'
|
||||||
|
}
|
||||||
|
|
||||||
33
schoolNewsWeb/src/types/index.ts
Normal file
33
schoolNewsWeb/src/types/index.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
/**
|
||||||
|
* @description 类型定义统一导出入口
|
||||||
|
* @author yslg
|
||||||
|
* @since 2025-10-06
|
||||||
|
*/
|
||||||
|
|
||||||
|
// 基础类型
|
||||||
|
export * from './base';
|
||||||
|
|
||||||
|
// 用户相关
|
||||||
|
export * from './user';
|
||||||
|
|
||||||
|
// 角色相关
|
||||||
|
export * from './role';
|
||||||
|
|
||||||
|
// 部门相关
|
||||||
|
export * from './dept';
|
||||||
|
|
||||||
|
// 菜单相关
|
||||||
|
export * from './menu';
|
||||||
|
|
||||||
|
// 权限相关
|
||||||
|
export * from './permission';
|
||||||
|
|
||||||
|
// 认证相关
|
||||||
|
export * from './auth';
|
||||||
|
|
||||||
|
// 枚举类型
|
||||||
|
export * from './enums';
|
||||||
|
|
||||||
|
// 常量
|
||||||
|
export * from './constants';
|
||||||
|
|
||||||
59
schoolNewsWeb/src/types/menu/index.ts
Normal file
59
schoolNewsWeb/src/types/menu/index.ts
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
/**
|
||||||
|
* @description 菜单相关类型定义
|
||||||
|
* @author yslg
|
||||||
|
* @since 2025-10-06
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { BaseDTO } from '../base';
|
||||||
|
import { MenuType } from '../enums';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统菜单
|
||||||
|
*/
|
||||||
|
export interface SysMenu extends BaseDTO {
|
||||||
|
/** 菜单ID */
|
||||||
|
menuID?: string;
|
||||||
|
/** 父菜单ID */
|
||||||
|
parentID?: string;
|
||||||
|
/** 菜单名称 */
|
||||||
|
name?: string;
|
||||||
|
/** 菜单描述 */
|
||||||
|
description?: string;
|
||||||
|
/** 菜单URL/路径 */
|
||||||
|
url?: string;
|
||||||
|
/** 菜单图标 */
|
||||||
|
icon?: string;
|
||||||
|
/** 菜单顺序 */
|
||||||
|
orderNum?: number;
|
||||||
|
/** 菜单类型 0-目录 1-菜单 2-按钮 */
|
||||||
|
type?: MenuType;
|
||||||
|
/** 创建人 */
|
||||||
|
creator?: string;
|
||||||
|
/** 更新人 */
|
||||||
|
updater?: string;
|
||||||
|
/** 子菜单列表(前端使用) */
|
||||||
|
children?: SysMenu[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 菜单权限关联
|
||||||
|
*/
|
||||||
|
export interface SysMenuPermission extends BaseDTO {
|
||||||
|
/** 菜单ID */
|
||||||
|
menuID?: string;
|
||||||
|
/** 权限ID */
|
||||||
|
permissionID?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 菜单树节点(用于树形展示)
|
||||||
|
*/
|
||||||
|
export interface MenuTreeNode extends SysMenu {
|
||||||
|
/** 子节点 */
|
||||||
|
children?: MenuTreeNode[];
|
||||||
|
/** 是否展开 */
|
||||||
|
expanded?: boolean;
|
||||||
|
/** 是否选中 */
|
||||||
|
checked?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
26
schoolNewsWeb/src/types/permission/index.ts
Normal file
26
schoolNewsWeb/src/types/permission/index.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
/**
|
||||||
|
* @description 权限相关类型定义
|
||||||
|
* @author yslg
|
||||||
|
* @since 2025-10-06
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { BaseDTO } from '../base';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统权限
|
||||||
|
*/
|
||||||
|
export interface SysPermission extends BaseDTO {
|
||||||
|
/** 权限ID */
|
||||||
|
permissionID?: string;
|
||||||
|
/** 权限名称 */
|
||||||
|
name?: string;
|
||||||
|
/** 权限描述 */
|
||||||
|
description?: string;
|
||||||
|
/** 权限编码 */
|
||||||
|
code?: string;
|
||||||
|
/** 创建人 */
|
||||||
|
creator?: string;
|
||||||
|
/** 更新人 */
|
||||||
|
updater?: string;
|
||||||
|
}
|
||||||
|
|
||||||
34
schoolNewsWeb/src/types/role/index.ts
Normal file
34
schoolNewsWeb/src/types/role/index.ts
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
/**
|
||||||
|
* @description 角色相关类型定义
|
||||||
|
* @author yslg
|
||||||
|
* @since 2025-10-06
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { BaseDTO } from '../base';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统角色
|
||||||
|
*/
|
||||||
|
export interface SysRole extends BaseDTO {
|
||||||
|
/** 角色ID */
|
||||||
|
roleID?: string;
|
||||||
|
/** 角色名称 */
|
||||||
|
name?: string;
|
||||||
|
/** 角色描述 */
|
||||||
|
description?: string;
|
||||||
|
/** 创建人 */
|
||||||
|
creator?: string;
|
||||||
|
/** 更新人 */
|
||||||
|
updater?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 角色权限关联
|
||||||
|
*/
|
||||||
|
export interface SysRolePermission extends BaseDTO {
|
||||||
|
/** 角色ID */
|
||||||
|
roleID?: string;
|
||||||
|
/** 权限ID */
|
||||||
|
permissionID?: string;
|
||||||
|
}
|
||||||
|
|
||||||
70
schoolNewsWeb/src/types/user/index.ts
Normal file
70
schoolNewsWeb/src/types/user/index.ts
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
/**
|
||||||
|
* @description 用户相关类型定义
|
||||||
|
* @author yslg
|
||||||
|
* @since 2025-10-06
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { BaseDTO } from '../base';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统用户
|
||||||
|
*/
|
||||||
|
export interface SysUser extends BaseDTO {
|
||||||
|
/** 用户名 */
|
||||||
|
username?: string;
|
||||||
|
/** 密码(不应在前端显示) */
|
||||||
|
password?: string;
|
||||||
|
/** 邮箱 */
|
||||||
|
email?: string;
|
||||||
|
/** 手机号 */
|
||||||
|
phone?: string;
|
||||||
|
/** 微信ID */
|
||||||
|
wechatID?: string;
|
||||||
|
/** 用户状态 0-正常 1-禁用 */
|
||||||
|
status?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户详细信息
|
||||||
|
*/
|
||||||
|
export interface SysUserInfo extends BaseDTO {
|
||||||
|
/** 用户ID */
|
||||||
|
userID?: string;
|
||||||
|
/** 真实姓名 */
|
||||||
|
realName?: string;
|
||||||
|
/** 昵称 */
|
||||||
|
nickname?: string;
|
||||||
|
/** 头像URL */
|
||||||
|
avatar?: string;
|
||||||
|
/** 性别 0-未知 1-男 2-女 */
|
||||||
|
gender?: number;
|
||||||
|
/** 出生日期 */
|
||||||
|
birthday?: string;
|
||||||
|
/** 个人简介 */
|
||||||
|
bio?: string;
|
||||||
|
/** 地址 */
|
||||||
|
address?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户VO - 用于前端展示
|
||||||
|
*/
|
||||||
|
export interface UserVO {
|
||||||
|
/** 用户基本信息 */
|
||||||
|
user?: SysUser;
|
||||||
|
/** 用户详细信息 */
|
||||||
|
userInfo?: SysUserInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户部门角色关联
|
||||||
|
*/
|
||||||
|
export interface SysUserDeptRole extends BaseDTO {
|
||||||
|
/** 用户ID */
|
||||||
|
userID?: string;
|
||||||
|
/** 部门ID */
|
||||||
|
deptID?: string;
|
||||||
|
/** 角色ID */
|
||||||
|
roleID?: string;
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user