mirror of
https://github.com/fastapiadmin/FastapiAdmin.git
synced 2026-10-13 12:49:30 +00:00
feat: 新增文件上传下载功能并优化用户认证流程
- 新增文件上传下载服务及API接口 - 重构用户认证模块,支持验证码登录 - 优化.env配置文件结构 - 更新前端API请求工具和类型定义 - 添加工作台菜单项 - 修复用户登出逻辑 - 统一API响应数据结构
This commit is contained in:
+87
-70
@@ -1,60 +1,58 @@
|
||||
import request, { publicRequest } from "@/utils/request";
|
||||
|
||||
const AUTH_BASE_URL = "/api/v1/auth";
|
||||
|
||||
export interface LoginData {
|
||||
username: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
export interface WxLoginData {
|
||||
code: string;
|
||||
encryptedData?: string;
|
||||
iv?: string;
|
||||
phoneCode?: string;
|
||||
}
|
||||
|
||||
export interface LoginResult {
|
||||
accessToken: string;
|
||||
refreshToken?: string;
|
||||
tokenType: string;
|
||||
expiresIn: number;
|
||||
isNewUser?: boolean;
|
||||
isProfileComplete?: boolean;
|
||||
}
|
||||
const AUTH_BASE_URL = "/api/v1/system/auth";
|
||||
|
||||
const AuthAPI = {
|
||||
/**
|
||||
* 账号密码登录
|
||||
* @param data 登录表单数据
|
||||
* 登录
|
||||
* @param body 登录表单数据
|
||||
* @returns 登录结果
|
||||
*/
|
||||
login(data: LoginData): Promise<LoginResult> {
|
||||
const formData = {
|
||||
username: data.username,
|
||||
password: data.password,
|
||||
};
|
||||
|
||||
return publicRequest<LoginResult>({
|
||||
login(body: LoginFormData): Promise<LoginResult> {
|
||||
return request<LoginResult>({
|
||||
url: `${AUTH_BASE_URL}/login`,
|
||||
method: "POST",
|
||||
data: formData,
|
||||
header: {
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
headers: {
|
||||
"Content-Type": "multipart/form-data",
|
||||
},
|
||||
data: body,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 微信小程序授权登录 (仅使用code获取OpenID)
|
||||
* @param code 微信登录凭证
|
||||
* @returns 登录结果
|
||||
* 刷新令牌
|
||||
* @param body 刷新令牌请求体
|
||||
* @returns 新的访问令牌
|
||||
*/
|
||||
loginByWxMiniAppCode(code: string): Promise<LoginResult> {
|
||||
return publicRequest<LoginResult>({
|
||||
url: `${AUTH_BASE_URL}/wx/miniapp/code-login`,
|
||||
refreshToken(body: RefreshToekenBody): Promise<any> {
|
||||
return request<LoginResult>({
|
||||
url: `${AUTH_BASE_URL}/token/refresh`,
|
||||
method: "POST",
|
||||
data: { code },
|
||||
data: body,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取验证码
|
||||
* @returns 验证码信息
|
||||
*/
|
||||
getCaptcha(): Promise<any> {
|
||||
return request<CaptchaInfo>({
|
||||
url: `${AUTH_BASE_URL}/captcha/get`,
|
||||
method: "GET",
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 登出
|
||||
* @param body 登出请求体
|
||||
* @returns 登出结果
|
||||
*/
|
||||
logout(body: LogoutBody): Promise<any> {
|
||||
return request<ApiResponse>({
|
||||
url: `${AUTH_BASE_URL}/logout`,
|
||||
method: "POST",
|
||||
data: body,
|
||||
});
|
||||
},
|
||||
|
||||
@@ -72,39 +70,58 @@ const AuthAPI = {
|
||||
},
|
||||
|
||||
/**
|
||||
* 检查会话有效性
|
||||
* @returns 会话是否有效
|
||||
* 微信小程序授权登录 (仅使用code获取OpenID)
|
||||
* @param code 微信登录凭证
|
||||
* @returns 登录结果
|
||||
*/
|
||||
checkSession(): Promise<{ valid: boolean }> {
|
||||
return request<{ valid: boolean }>({
|
||||
url: `${AUTH_BASE_URL}/check-session`,
|
||||
method: "GET",
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 登出
|
||||
* @returns 登出结果
|
||||
*/
|
||||
logout(): Promise<any> {
|
||||
return request<any>({
|
||||
url: `${AUTH_BASE_URL}/logout`,
|
||||
loginByWxMiniAppCode(code: string): Promise<LoginResult> {
|
||||
return publicRequest<LoginResult>({
|
||||
url: `${AUTH_BASE_URL}/wx/miniapp/code-login`,
|
||||
method: "POST",
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 刷新令牌
|
||||
* @param refreshToken 刷新令牌
|
||||
* @returns 新的访问令牌
|
||||
*/
|
||||
refreshToken(refreshToken: string): Promise<{ accessToken: string; expiresIn: number }> {
|
||||
return publicRequest<{ accessToken: string; expiresIn: number }>({
|
||||
url: `${AUTH_BASE_URL}/refresh-token`,
|
||||
method: "POST",
|
||||
data: { refreshToken },
|
||||
data: { code },
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export default AuthAPI;
|
||||
|
||||
export interface WxLoginData {
|
||||
code: string;
|
||||
encryptedData?: string;
|
||||
iv?: string;
|
||||
phoneCode?: string;
|
||||
}
|
||||
|
||||
/** 登录表单数据 */
|
||||
export interface LoginFormData {
|
||||
username: string;
|
||||
password: string;
|
||||
captcha_key: string;
|
||||
captcha: string;
|
||||
remember: boolean;
|
||||
}
|
||||
|
||||
// 刷新令牌
|
||||
export interface RefreshToekenBody {
|
||||
refresh_token: string;
|
||||
}
|
||||
|
||||
/** 登录响应 */
|
||||
export interface LoginResult {
|
||||
access_token: string;
|
||||
refresh_token: string;
|
||||
token_type: string;
|
||||
expires_in: number;
|
||||
}
|
||||
|
||||
/** 验证码信息 */
|
||||
export interface CaptchaInfo {
|
||||
enable: boolean;
|
||||
key: string;
|
||||
img_base: string;
|
||||
}
|
||||
|
||||
/** 退出登录操作 */
|
||||
export interface LogoutBody {
|
||||
token: string;
|
||||
}
|
||||
|
||||
+5
-15
@@ -1,8 +1,8 @@
|
||||
import { getToken } from "@/utils/storage";
|
||||
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;
|
||||
// H5 使用 VITE_APP_BASE_API 作为代理路径,其他平台使用 VITE_API_BASE_URL 作为请求路径
|
||||
let baseApi = import.meta.env.VITE_API_BASE_URL;
|
||||
// #ifdef H5
|
||||
baseApi = import.meta.env.VITE_APP_BASE_API;
|
||||
// #endif
|
||||
@@ -11,14 +11,14 @@ const FileAPI = {
|
||||
/**
|
||||
* 文件上传地址
|
||||
*/
|
||||
uploadUrl: baseApi + "/api/v1/files",
|
||||
uploadUrl: baseApi + "/api/v1/files/upload",
|
||||
|
||||
/**
|
||||
* 上传文件
|
||||
*
|
||||
* @param filePath
|
||||
*/
|
||||
upload(filePath: string): Promise<FileInfo> {
|
||||
upload(filePath: string): Promise<UploadFilePath> {
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.uploadFile({
|
||||
url: this.uploadUrl,
|
||||
@@ -29,7 +29,7 @@ const FileAPI = {
|
||||
},
|
||||
formData: {},
|
||||
success: (response) => {
|
||||
const resData = JSON.parse(response.data) as ResponseData<FileInfo>;
|
||||
const resData = JSON.parse(response.data) as ApiResponse<UploadFilePath>;
|
||||
// 业务状态码 00000 表示成功
|
||||
if (resData.code === ApiCode.SUCCESS) {
|
||||
resolve(resData.data);
|
||||
@@ -63,13 +63,3 @@ const FileAPI = {
|
||||
};
|
||||
|
||||
export default FileAPI;
|
||||
|
||||
/**
|
||||
* 文件API类型声明
|
||||
*/
|
||||
export interface FileInfo {
|
||||
/** 文件名 */
|
||||
name: string;
|
||||
/** 文件路径 */
|
||||
url: string;
|
||||
}
|
||||
|
||||
+250
-244
@@ -1,44 +1,103 @@
|
||||
import request from "@/utils/request";
|
||||
|
||||
const USER_BASE_URL = "/api/v1/users";
|
||||
const USER_BASE_URL = "/api/v1/system/user";
|
||||
|
||||
const UserAPI = {
|
||||
/**
|
||||
* 获取当前登录用户信息
|
||||
* 个人中心用户信息
|
||||
*
|
||||
* @returns 登录用户昵称、头像信息,包括角色和权限
|
||||
*/
|
||||
getUserInfo(): Promise<UserInfo> {
|
||||
getCurrentUserInfo(): Promise<UserInfo> {
|
||||
return request<UserInfo>({
|
||||
url: `${USER_BASE_URL}/me`,
|
||||
url: `${USER_BASE_URL}/current/info`,
|
||||
method: "GET",
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 当前用户头像上传
|
||||
*
|
||||
* @param body
|
||||
* @returns 上传后的文件路径
|
||||
*/
|
||||
uploadCurrentUserAvatar(body: any): Promise<UploadFilePath> {
|
||||
return request<UploadFilePath>({
|
||||
url: `${USER_BASE_URL}/current/avatar/upload`,
|
||||
method: "POST",
|
||||
data: body,
|
||||
headers: { "Content-Type": "multipart/form-data" },
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 修改个人中心用户信息
|
||||
*
|
||||
* @param body
|
||||
* @returns 修改后的用户信息
|
||||
*/
|
||||
updateCurrentUserInfo(body: UserProfileForm): Promise<UserInfo> {
|
||||
return request<UserInfo>({
|
||||
url: `${USER_BASE_URL}/current/info/update`,
|
||||
method: "PUT",
|
||||
data: body,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 修改个人中心用户密码
|
||||
*
|
||||
* @param body
|
||||
* @returns 修改后的用户信息
|
||||
*/
|
||||
changeCurrentUserPassword(body: PasswordChangeForm): Promise<ApiResponse> {
|
||||
return request<ApiResponse>({
|
||||
url: `${USER_BASE_URL}/current/password/change`,
|
||||
method: "PUT",
|
||||
data: body,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 注册用户
|
||||
*
|
||||
* @param body
|
||||
* @returns 忘记密码结果
|
||||
*/
|
||||
registerUser(body: RegisterForm): Promise<ApiResponse> {
|
||||
return request<ApiResponse>({
|
||||
url: `${USER_BASE_URL}/register`,
|
||||
method: "POST",
|
||||
data: body,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 忘记密码
|
||||
*
|
||||
* @param body
|
||||
* @returns 忘记密码结果
|
||||
*/
|
||||
forgetPassword(body: ForgetPasswordForm): Promise<ApiResponse> {
|
||||
return request<ApiResponse>({
|
||||
url: `${USER_BASE_URL}/forget/password`,
|
||||
method: "POST",
|
||||
data: body,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取用户分页列表
|
||||
*
|
||||
*POST
|
||||
* @param queryParams 查询参数
|
||||
*/
|
||||
getPage(queryParams: UserPageQuery) {
|
||||
return request<PageResult<UserPageVO[]>>({
|
||||
url: `${USER_BASE_URL}/page`,
|
||||
getUserPage(queryParams: UserPageQuery): Promise<PageResult<UserInfo[]>> {
|
||||
return request<PageResult<UserInfo[]>>({
|
||||
url: `${USER_BASE_URL}/list`,
|
||||
method: "GET",
|
||||
data: queryParams,
|
||||
});
|
||||
},
|
||||
/**
|
||||
* 添加用户
|
||||
*
|
||||
* @param data 用户表单数据
|
||||
*/
|
||||
add(data: UserForm) {
|
||||
return request({
|
||||
url: `${USER_BASE_URL}`,
|
||||
method: "POST",
|
||||
data: data,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取用户表单详情
|
||||
@@ -46,245 +105,216 @@ const UserAPI = {
|
||||
* @param userId 用户ID
|
||||
* @returns 用户表单详情
|
||||
*/
|
||||
getFormData(userId: number) {
|
||||
getUserDetail(userId: number): Promise<UserForm> {
|
||||
return request<UserForm>({
|
||||
url: `${USER_BASE_URL}/${userId}/form`,
|
||||
url: `${USER_BASE_URL}/detail/${userId}`,
|
||||
method: "GET",
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 添加用户
|
||||
*
|
||||
* @param body 用户表单数据
|
||||
*/
|
||||
addUser(body: UserForm): Promise<ApiResponse> {
|
||||
return request<ApiResponse>({
|
||||
url: `${USER_BASE_URL}/create`,
|
||||
method: "POST",
|
||||
data: body,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 修改用户
|
||||
*
|
||||
* @param id 用户ID
|
||||
* @param data 用户表单数据
|
||||
* @param body 用户表单数据
|
||||
*/
|
||||
update(id: number, data: UserForm) {
|
||||
updateUser(body: UserForm): Promise<ApiResponse> {
|
||||
return request({
|
||||
url: `${USER_BASE_URL}/${id}`,
|
||||
url: `${USER_BASE_URL}/update`,
|
||||
method: "PUT",
|
||||
data: data,
|
||||
});
|
||||
},
|
||||
|
||||
/** 获取个人中心用户信息 */
|
||||
getProfile() {
|
||||
return request<UserProfileVO>({
|
||||
url: `${USER_BASE_URL}/profile`,
|
||||
method: "GET",
|
||||
});
|
||||
},
|
||||
|
||||
/** 修改个人中心用户信息 */
|
||||
updateProfile(data: UserProfileForm) {
|
||||
return request({
|
||||
url: `${USER_BASE_URL}/profile`,
|
||||
method: "PUT",
|
||||
data: data,
|
||||
});
|
||||
},
|
||||
|
||||
/** 修改个人中心用户密码 */
|
||||
changePassword(data: PasswordChangeForm) {
|
||||
return request({
|
||||
url: `${USER_BASE_URL}/password`,
|
||||
method: "PUT",
|
||||
data: data,
|
||||
data: body,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 发送手机/邮箱验证码
|
||||
* 删除用户
|
||||
*
|
||||
* @param contact 联系方式 手机号/邮箱
|
||||
* @param contactType 联系方式类型 MOBILE:手机;EMAIL:邮箱
|
||||
* @param ids 用户ID数组
|
||||
*/
|
||||
sendVerificationCode(contact: string, contactType: string) {
|
||||
return request({
|
||||
url: `${USER_BASE_URL}/send-verification-code?contact=${contact}&contactType=${contactType}`,
|
||||
method: "POST",
|
||||
});
|
||||
},
|
||||
|
||||
/** 绑定个人中心用户手机 */
|
||||
bindMobile(data: MobileBindingForm) {
|
||||
return request({
|
||||
url: `${USER_BASE_URL}/mobile`,
|
||||
method: "PUT",
|
||||
data: data,
|
||||
});
|
||||
},
|
||||
|
||||
/** 绑定个人中心用户邮箱 */
|
||||
bindEmail(data: EmailBindingForm) {
|
||||
return request({
|
||||
url: `${USER_BASE_URL}/email`,
|
||||
method: "PUT",
|
||||
data: data,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 批量删除用户,多个以英文逗号(,)分割
|
||||
*
|
||||
* @param ids 用户ID字符串,多个以英文逗号(,)分割
|
||||
*/
|
||||
deleteByIds(ids: string) {
|
||||
return request({
|
||||
url: `${USER_BASE_URL}/${ids}`,
|
||||
deleteUser(ids: number[]): Promise<ApiResponse> {
|
||||
return request<ApiResponse>({
|
||||
url: `/system/user/delete`,
|
||||
method: "DELETE",
|
||||
});
|
||||
},
|
||||
|
||||
/** 获取微信手机号 */
|
||||
getPhoneNumber(data: WechatPhoneData): Promise<PhoneNumberResult> {
|
||||
return request<PhoneNumberResult>({
|
||||
url: `${USER_BASE_URL}/wechat-phone`,
|
||||
method: "POST",
|
||||
data: data,
|
||||
data: ids,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export default UserAPI;
|
||||
|
||||
/** 登录用户信息 */
|
||||
export interface UserInfo {
|
||||
/** 用户ID */
|
||||
userId?: number;
|
||||
|
||||
/** 用户名 */
|
||||
username?: string;
|
||||
|
||||
/** 昵称 */
|
||||
nickname?: string;
|
||||
|
||||
/** 头像URL */
|
||||
avatar?: string;
|
||||
|
||||
/** 角色 */
|
||||
roles?: string[];
|
||||
|
||||
/** 权限 */
|
||||
perms?: string[];
|
||||
/* 忘记密码表单 */
|
||||
export interface ForgetPasswordForm {
|
||||
username: string;
|
||||
new_password: string;
|
||||
confirmPassword: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户分页查询对象
|
||||
*/
|
||||
/* 注册表单 */
|
||||
export interface RegisterForm {
|
||||
username: string;
|
||||
password: string;
|
||||
confirmPassword: string;
|
||||
}
|
||||
|
||||
/* 分页查询表单 */
|
||||
export interface UserPageQuery extends PageQuery {
|
||||
/** 搜索关键字 */
|
||||
keywords?: string;
|
||||
|
||||
/** 用户状态 */
|
||||
status?: number;
|
||||
|
||||
/** 部门ID */
|
||||
deptId?: number;
|
||||
|
||||
/** 开始时间 */
|
||||
createTime?: [string, string] | string;
|
||||
|
||||
/** 排序字段 */
|
||||
field?: string;
|
||||
|
||||
/** 排序方式(asc:正序,desc:倒序) */
|
||||
direction?: string;
|
||||
}
|
||||
|
||||
/** 用户分页对象 */
|
||||
export interface UserPageVO {
|
||||
/** 用户头像URL */
|
||||
avatar?: string;
|
||||
/** 创建时间 */
|
||||
createTime?: string;
|
||||
/** 部门名称 */
|
||||
deptName?: string;
|
||||
/** 用户邮箱 */
|
||||
email?: string;
|
||||
/** 性别 */
|
||||
gender?: number;
|
||||
/** 用户ID */
|
||||
id: number;
|
||||
/** 手机号 */
|
||||
mobile?: string;
|
||||
/** 用户昵称 */
|
||||
nickname?: string;
|
||||
/** 角色名称,多个使用英文逗号(,)分割 */
|
||||
roleNames?: string;
|
||||
/** 用户状态(1:启用;0:禁用) */
|
||||
status?: number;
|
||||
/** 用户名 */
|
||||
username?: string;
|
||||
name?: string;
|
||||
status?: boolean;
|
||||
dept_id?: number;
|
||||
start_time?: string;
|
||||
end_time?: string;
|
||||
}
|
||||
|
||||
/** 个人中心用户信息 */
|
||||
export interface UserProfileVO {
|
||||
/** 用户ID */
|
||||
/* 搜索选择器数据类型 */
|
||||
export interface searchSelectDataType {
|
||||
name?: string;
|
||||
status?: string;
|
||||
}
|
||||
|
||||
/* 用户表单 */
|
||||
export interface UserForm {
|
||||
id?: number;
|
||||
|
||||
/** 用户名 */
|
||||
username?: string;
|
||||
|
||||
/** 昵称 */
|
||||
nickname?: string;
|
||||
|
||||
/** 头像URL */
|
||||
avatar?: string;
|
||||
|
||||
/** 性别 */
|
||||
name?: string;
|
||||
dept_id?: number;
|
||||
dept_name?: string;
|
||||
role_ids?: number[];
|
||||
roleNames?: string[];
|
||||
position_ids?: number[];
|
||||
positionNames?: string[];
|
||||
password?: string;
|
||||
gender?: number;
|
||||
|
||||
/** 手机号 */
|
||||
mobile?: string;
|
||||
|
||||
/** 邮箱 */
|
||||
email?: string;
|
||||
|
||||
/** 部门名称 */
|
||||
deptName?: string;
|
||||
|
||||
/** 角色名称,多个使用英文逗号(,)分割 */
|
||||
roleNames?: string;
|
||||
|
||||
/** 创建时间 */
|
||||
createTime?: string;
|
||||
mobile?: string;
|
||||
is_superuser?: boolean;
|
||||
status?: boolean;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
/** 个人中心用户信息表单 */
|
||||
/* 登录用户信息 */
|
||||
export interface UserInfo {
|
||||
index?: number;
|
||||
id?: number;
|
||||
username?: string;
|
||||
name?: string;
|
||||
avatar?: string;
|
||||
email?: string;
|
||||
mobile?: string;
|
||||
gender?: string;
|
||||
password?: string;
|
||||
menus?: MenuTable[];
|
||||
dept?: deptTreeType;
|
||||
dept_id?: deptTreeType["id"];
|
||||
dept_name?: deptTreeType["name"];
|
||||
roles?: roleSelectorType[];
|
||||
roleNames?: roleSelectorType["name"][];
|
||||
role_ids?: roleSelectorType["id"][];
|
||||
positions?: positionSelectorType[];
|
||||
positionNames?: positionSelectorType["name"][];
|
||||
position_ids?: positionSelectorType["id"][];
|
||||
is_superuser?: boolean;
|
||||
status?: boolean;
|
||||
description?: string;
|
||||
last_login?: string;
|
||||
created_at?: string;
|
||||
updated_at?: string;
|
||||
creator?: creatorType;
|
||||
}
|
||||
|
||||
/* 菜单表 */
|
||||
export interface MenuTable {
|
||||
index?: number;
|
||||
id?: number;
|
||||
name?: string;
|
||||
type?: number;
|
||||
icon?: string;
|
||||
order?: number;
|
||||
permission?: string;
|
||||
route_name?: string;
|
||||
route_path?: string;
|
||||
component_path?: string;
|
||||
redirect?: string;
|
||||
parent_id?: number;
|
||||
parent_name?: string;
|
||||
keep_alive?: boolean;
|
||||
hidden?: boolean;
|
||||
always_show?: boolean;
|
||||
title?: string;
|
||||
params?: { key: string; value: string }[];
|
||||
affix?: boolean;
|
||||
status?: boolean;
|
||||
description?: string;
|
||||
created_at?: string;
|
||||
updated_at?: string;
|
||||
children?: MenuTable[];
|
||||
}
|
||||
|
||||
/* 部门树 */
|
||||
export interface deptTreeType {
|
||||
id?: number;
|
||||
name?: string;
|
||||
parent_id?: number;
|
||||
children?: deptTreeType[];
|
||||
}
|
||||
|
||||
/* 角色选择器 */
|
||||
export interface roleSelectorType {
|
||||
id?: number;
|
||||
name?: string;
|
||||
status?: boolean;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
/* 职位选择器 */
|
||||
export interface positionSelectorType {
|
||||
id?: number;
|
||||
name?: string;
|
||||
status?: boolean;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
/* 个人中心用户信息表单 */
|
||||
export interface UserProfileForm {
|
||||
/** 用户ID */
|
||||
id?: number;
|
||||
|
||||
/** 用户名 */
|
||||
username?: string;
|
||||
|
||||
/** 昵称 */
|
||||
nickname?: string;
|
||||
|
||||
/** 头像URL */
|
||||
avatar?: string;
|
||||
|
||||
/** 性别 */
|
||||
gender?: number;
|
||||
|
||||
/** 手机号 */
|
||||
mobile?: string;
|
||||
|
||||
/** 邮箱 */
|
||||
email?: string;
|
||||
name: string;
|
||||
gender: number;
|
||||
mobile: string;
|
||||
email: string;
|
||||
username: string;
|
||||
dept_name: string;
|
||||
positions: positionSelectorType[];
|
||||
roles: roleSelectorType[];
|
||||
avatar: string;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
/** 修改密码表单 */
|
||||
/* 修改密码表单 */
|
||||
export interface PasswordChangeForm {
|
||||
/** 原密码 */
|
||||
oldPassword?: string;
|
||||
/** 新密码 */
|
||||
newPassword?: string;
|
||||
/** 确认新密码 */
|
||||
confirmPassword?: string;
|
||||
old_password: string;
|
||||
new_password: string;
|
||||
confirm_password: string;
|
||||
}
|
||||
|
||||
/** 修改手机表单 */
|
||||
/* 重置密码表单 */
|
||||
export interface ResetPasswordForm {
|
||||
id: number;
|
||||
password: string;
|
||||
}
|
||||
|
||||
/* 修改手机表单 */
|
||||
export interface MobileBindingForm {
|
||||
/** 手机号 */
|
||||
mobile?: string;
|
||||
@@ -292,7 +322,7 @@ export interface MobileBindingForm {
|
||||
code?: string;
|
||||
}
|
||||
|
||||
/** 修改邮箱表单 */
|
||||
/* 修改邮箱表单 */
|
||||
export interface EmailBindingForm {
|
||||
/** 邮箱 */
|
||||
email?: string;
|
||||
@@ -300,31 +330,7 @@ export interface EmailBindingForm {
|
||||
code?: string;
|
||||
}
|
||||
|
||||
/** 用户表单 */
|
||||
export interface UserForm {
|
||||
/** 用户头像 */
|
||||
avatar?: string;
|
||||
/** 部门ID */
|
||||
deptId?: number;
|
||||
/** 用户邮箱 */
|
||||
email?: string;
|
||||
/** 性别 */
|
||||
gender?: number;
|
||||
/** 用户ID */
|
||||
id?: number;
|
||||
/** 手机号 */
|
||||
mobile?: string;
|
||||
/** 昵称 */
|
||||
nickname?: string;
|
||||
/** 角色ID集合 */
|
||||
roleIds: number[];
|
||||
/** 用户状态(1:正常;0:禁用) */
|
||||
status?: number;
|
||||
/** 用户名 */
|
||||
username?: string;
|
||||
}
|
||||
|
||||
/** 微信手机号授权数据 */
|
||||
/* 微信手机号授权数据 */
|
||||
export interface WechatPhoneData {
|
||||
/** 微信授权码 */
|
||||
code: string;
|
||||
@@ -334,7 +340,7 @@ export interface WechatPhoneData {
|
||||
iv?: string;
|
||||
}
|
||||
|
||||
/** 手机号获取结果 */
|
||||
/* 手机号获取结果 */
|
||||
export interface PhoneNumberResult {
|
||||
/** 手机号 */
|
||||
phoneNumber: string;
|
||||
|
||||
@@ -8,6 +8,7 @@ export interface TabbarItem {
|
||||
|
||||
const tabbarItems = ref<TabbarItem[]>([
|
||||
{ name: "home", value: null, active: true, title: "首页", icon: "home" },
|
||||
{ name: "work", value: null, active: true, title: "工作台", icon: "work" },
|
||||
{ name: "mine", value: null, active: false, title: "我的", icon: "user" },
|
||||
]);
|
||||
|
||||
|
||||
@@ -5,45 +5,40 @@ export const enum ApiCode {
|
||||
/**
|
||||
* 成功
|
||||
*/
|
||||
SUCCESS = "00000",
|
||||
SUCCESS = 0,
|
||||
|
||||
/**
|
||||
* 通用错误
|
||||
*/
|
||||
ERROR = "B0001",
|
||||
ERROR = 1,
|
||||
|
||||
/**
|
||||
* 令牌无效或过期
|
||||
* 异常
|
||||
*/
|
||||
TOKEN_INVALID = "A0230",
|
||||
|
||||
/**
|
||||
* 令牌已过期
|
||||
*/
|
||||
TOKEN_EXPIRED = "A0231",
|
||||
EXCEPTION = -1,
|
||||
|
||||
/**
|
||||
* 未授权访问
|
||||
*/
|
||||
UNAUTHORIZED = "A0232",
|
||||
UNAUTHORIZED = 401,
|
||||
|
||||
/**
|
||||
* 禁止访问
|
||||
* 令牌已过期
|
||||
*/
|
||||
FORBIDDEN = "A0233",
|
||||
TOKEN_EXPIRED = 403,
|
||||
|
||||
/**
|
||||
* 参数校验失败
|
||||
*/
|
||||
PARAM_INVALID = "A0400",
|
||||
PARAM_INVALID = 400,
|
||||
|
||||
/**
|
||||
* 资源不存在
|
||||
*/
|
||||
NOT_FOUND = "A0404",
|
||||
NOT_FOUND = 404,
|
||||
|
||||
/**
|
||||
* 服务器内部错误
|
||||
*/
|
||||
INTERNAL_ERROR = "B0500",
|
||||
INTERNAL_ERROR = 500,
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<script lang="ts" setup>
|
||||
import { useTheme } from "@/composables/useTheme";
|
||||
|
||||
const { theme, themeVars } = useTheme();
|
||||
</script>
|
||||
|
||||
|
||||
@@ -50,6 +50,25 @@
|
||||
@click="showPassword = !showPassword"
|
||||
/>
|
||||
</view>
|
||||
<!-- 验证码输入框 -->
|
||||
<view class="form-item">
|
||||
<wd-icon
|
||||
name="lock-on"
|
||||
size="22"
|
||||
:color="isDarkMode ? '#7AC5FF' : '#333'"
|
||||
class="input-icon"
|
||||
/>
|
||||
<input
|
||||
v-model="loginFormData.captcha"
|
||||
class="form-input input-transparent"
|
||||
type="text"
|
||||
placeholder="请输入验证码"
|
||||
placeholder-class="input-placeholder"
|
||||
/>
|
||||
<view class="captcha-img">
|
||||
<image :src="captchaImg" class="captcha-img" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="divider"></view>
|
||||
|
||||
<!-- 登录按钮 -->
|
||||
@@ -122,12 +141,12 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onLoad } from "@dcloudio/uni-app";
|
||||
import { type LoginData } from "@/api/auth";
|
||||
import { useUserStore } from "@/store/modules/user.store";
|
||||
import { useToast } from "wot-design-uni";
|
||||
import { useWechat } from "@/composables/useWechat";
|
||||
import { useTheme } from "@/composables/useTheme";
|
||||
import { computed, onMounted } from "vue";
|
||||
import AuthAPI, { type LoginFormData } from "@/api/auth";
|
||||
|
||||
const loginFormRef = ref();
|
||||
const toast = useToast();
|
||||
@@ -138,13 +157,18 @@ const loginType = ref<"account" | "phone">("account");
|
||||
const { authState, getLoginCode, getPhoneNumber } = useWechat();
|
||||
const { theme } = useTheme();
|
||||
|
||||
const captchaImg = ref("");
|
||||
|
||||
// 是否暗黑模式
|
||||
const isDarkMode = computed(() => theme.value === "dark");
|
||||
|
||||
// 登录表单数据
|
||||
const loginFormData = ref<LoginData>({
|
||||
const loginFormData = ref<LoginFormData>({
|
||||
username: "admin",
|
||||
password: "123456",
|
||||
captcha_key: "",
|
||||
captcha: "",
|
||||
remember: true,
|
||||
});
|
||||
|
||||
// 获取重定向参数
|
||||
@@ -155,6 +179,17 @@ onLoad((options) => {
|
||||
}
|
||||
});
|
||||
|
||||
// 获取验证码
|
||||
const getLoginCaptcha = async () => {
|
||||
try {
|
||||
const result = await AuthAPI.getCaptcha();
|
||||
captchaImg.value = result.captcha_key;
|
||||
toast.success("验证码发送成功");
|
||||
} catch (error: any) {
|
||||
toast.error(error?.message || "验证码发送失败");
|
||||
}
|
||||
};
|
||||
|
||||
// 强制清除输入框背景色
|
||||
onMounted(() => {
|
||||
setTimeout(() => {
|
||||
@@ -164,6 +199,8 @@ onMounted(() => {
|
||||
input.style.boxShadow = "none";
|
||||
});
|
||||
}, 100);
|
||||
|
||||
getLoginCaptcha();
|
||||
});
|
||||
|
||||
// 账号密码登录处理
|
||||
@@ -179,6 +216,11 @@ const handleAccountLogin = () => {
|
||||
toast.error("请输入密码");
|
||||
return;
|
||||
}
|
||||
if (loginFormData.value.captcha_key && !loginFormData.value.captcha) {
|
||||
toast.error("请输入验证码");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
loading.value = true;
|
||||
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import { defineStore } from "pinia";
|
||||
import AuthAPI, { type LoginData, type WxLoginData } from "@/api/auth";
|
||||
import AuthAPI, {
|
||||
type LoginFormData,
|
||||
type WxLoginData,
|
||||
type LoginResult,
|
||||
type LogoutBody,
|
||||
} from "@/api/auth";
|
||||
import UserAPI, { type UserInfo } from "@/api/user";
|
||||
import { setAccessToken, clearTokens } from "@/utils/auth";
|
||||
import { getUserInfo, setUserInfo } from "@/utils/storage";
|
||||
@@ -10,11 +15,11 @@ export const useUserStore = defineStore("user", () => {
|
||||
const userInfo = ref<UserInfo | undefined>(getUserInfo());
|
||||
|
||||
// 账号密码登录
|
||||
const login = (data: LoginData) => {
|
||||
const login = (data: LoginFormData) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
AuthAPI.login(data)
|
||||
.then((data) => {
|
||||
setAccessToken(data.accessToken);
|
||||
.then((data: LoginResult) => {
|
||||
setAccessToken(data.access_token);
|
||||
resolve(data);
|
||||
})
|
||||
.catch((error) => {
|
||||
@@ -28,8 +33,8 @@ export const useUserStore = defineStore("user", () => {
|
||||
const loginWithWxCode = (code: string) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
AuthAPI.loginByWxMiniAppCode(code)
|
||||
.then((data) => {
|
||||
setAccessToken(data.accessToken);
|
||||
.then((data: LoginResult) => {
|
||||
setAccessToken(data.access_token);
|
||||
resolve(data);
|
||||
})
|
||||
.catch((error: any) => {
|
||||
@@ -43,8 +48,8 @@ export const useUserStore = defineStore("user", () => {
|
||||
const loginWithWxPhone = (data: WxLoginData): Promise<any> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
AuthAPI.loginByWxMiniAppPhone(data)
|
||||
.then((result: any) => {
|
||||
setAccessToken(result.accessToken);
|
||||
.then((result: LoginResult) => {
|
||||
setAccessToken(result.access_token);
|
||||
resolve(result);
|
||||
})
|
||||
.catch((error: any) => {
|
||||
@@ -54,23 +59,10 @@ export const useUserStore = defineStore("user", () => {
|
||||
});
|
||||
};
|
||||
|
||||
// 检查会话状态
|
||||
const checkSession = (): Promise<boolean> => {
|
||||
return new Promise((resolve) => {
|
||||
AuthAPI.checkSession()
|
||||
.then((result) => {
|
||||
resolve(result.valid);
|
||||
})
|
||||
.catch(() => {
|
||||
resolve(false);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// 获取用户信息
|
||||
const getInfo = () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
UserAPI.getUserInfo()
|
||||
UserAPI.getCurrentUserInfo()
|
||||
.then((data) => {
|
||||
setUserInfo(data);
|
||||
userInfo.value = data;
|
||||
@@ -84,9 +76,9 @@ export const useUserStore = defineStore("user", () => {
|
||||
};
|
||||
|
||||
// 登出
|
||||
const logout = async () => {
|
||||
const logout = async (data: LogoutBody) => {
|
||||
try {
|
||||
await AuthAPI.logout(); // 调用后台注销接口
|
||||
await AuthAPI.logout(data); // 调用后台注销接口
|
||||
} catch (error) {
|
||||
console.error("登出失败", error);
|
||||
} finally {
|
||||
@@ -105,7 +97,7 @@ export const useUserStore = defineStore("user", () => {
|
||||
const isUserInfoComplete = (): boolean => {
|
||||
if (!userInfo.value) return false;
|
||||
|
||||
return !!(userInfo.value.nickname && userInfo.value.avatar);
|
||||
return !!(userInfo.value.username && userInfo.value.avatar);
|
||||
};
|
||||
|
||||
return {
|
||||
@@ -115,7 +107,6 @@ export const useUserStore = defineStore("user", () => {
|
||||
loginWithWxPhone,
|
||||
logout,
|
||||
getInfo,
|
||||
checkSession,
|
||||
isUserInfoComplete,
|
||||
};
|
||||
});
|
||||
|
||||
Vendored
+9
-6
@@ -6,15 +6,18 @@ interface ImportMetaEnv {
|
||||
/**
|
||||
* 应用端口
|
||||
*/
|
||||
VITE_APP_PORT: number;
|
||||
readonly VITE_APP_PORT: number;
|
||||
/** 超时时间 */
|
||||
readonly VITE_TIMEOUT: number;
|
||||
/** API基础路径 */
|
||||
readonly VITE_APP_BASE_API: string;
|
||||
/** API服务器URL */
|
||||
readonly VITE_APP_API_URL: string;
|
||||
|
||||
/**
|
||||
* WebSocket 端点
|
||||
*/
|
||||
readonly VITE_API_BASE_URL: string;
|
||||
/** 环境 */
|
||||
readonly VITE_APP_ENV: string;
|
||||
/** 项目名称 */
|
||||
readonly VITE_APP_TITLE: string;
|
||||
/** WebSocket 端点 */
|
||||
readonly VITE_APP_WS_ENDPOINT?: string;
|
||||
}
|
||||
|
||||
|
||||
Vendored
+34
-9
@@ -1,10 +1,22 @@
|
||||
declare global {
|
||||
/**
|
||||
* 响应数据
|
||||
*/
|
||||
interface ApiResponse<T = any> {
|
||||
code: number;
|
||||
data: T;
|
||||
msg: string;
|
||||
status_code: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询参数
|
||||
*/
|
||||
interface PageQuery {
|
||||
pageNum: number;
|
||||
pageSize: number;
|
||||
/** 当前页码 */
|
||||
page_no: number;
|
||||
/** 每页条数 */
|
||||
page_size: number;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -12,13 +24,16 @@ declare global {
|
||||
*/
|
||||
interface PageResult<T> {
|
||||
/** 数据列表 */
|
||||
list: T;
|
||||
items: T;
|
||||
/** 总数 */
|
||||
total: number;
|
||||
page_no: number;
|
||||
page_size: number;
|
||||
has_next: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 组件数据源
|
||||
* 下拉选项数据类型
|
||||
*/
|
||||
interface OptionType {
|
||||
/** 值 */
|
||||
@@ -30,12 +45,22 @@ declare global {
|
||||
}
|
||||
|
||||
/**
|
||||
* 响应数据
|
||||
* 创建人
|
||||
*/
|
||||
interface ResponseData<T = any> {
|
||||
code: string;
|
||||
data: T;
|
||||
msg: string;
|
||||
interface creatorType {
|
||||
id?: number;
|
||||
name?: string;
|
||||
username?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文件返回
|
||||
*/
|
||||
interface UploadFilePath {
|
||||
file_path: string;
|
||||
file_name: string;
|
||||
origin_name: string;
|
||||
file_url: string;
|
||||
}
|
||||
}
|
||||
export {};
|
||||
|
||||
@@ -132,7 +132,7 @@ export function requireLogin(): void {
|
||||
if (!accessToken || !userStore.userInfo) {
|
||||
// 清除可能存在的无效状态
|
||||
clearTokens();
|
||||
userStore.logout();
|
||||
userStore.logout({ token: accessToken || "" });
|
||||
|
||||
// 跳转到登录页
|
||||
uni.reLaunch({
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* @returns
|
||||
*/
|
||||
const debounce = <T extends (...args: any[]) => any>(fn: T, delay: number) => {
|
||||
let timer: number | null = null;
|
||||
let timer: ReturnType<typeof setTimeout> | null = null;
|
||||
return function (this: any, ...args: Parameters<T>) {
|
||||
if (timer) clearTimeout(timer);
|
||||
timer = setTimeout(() => {
|
||||
|
||||
@@ -5,23 +5,25 @@ interface RequestOptions<T = any> {
|
||||
url: string;
|
||||
method: "GET" | "POST" | "PUT" | "DELETE";
|
||||
data?: T;
|
||||
header?: Record<string, string>;
|
||||
headers?: Record<string, string>;
|
||||
timeout?: number;
|
||||
responseType?: "text" | "arraybuffer";
|
||||
responseType?: "text" | "arraybuffer" | "blob" | "json";
|
||||
skipAuth?: boolean; // 标记是否跳过认证
|
||||
}
|
||||
|
||||
// 请求函数
|
||||
/**
|
||||
* 请求拦截器 - 添加 Authorization 头
|
||||
*/
|
||||
function request<T = any>(options: RequestOptions): Promise<T> {
|
||||
return new Promise<T>((resolve, reject) => {
|
||||
// 构建请求头
|
||||
const header = Object.assign({}, options.header || {});
|
||||
const header = Object.assign({}, options.headers || {});
|
||||
|
||||
// 检查是否需要添加认证令牌
|
||||
if (!options.skipAuth) {
|
||||
const token = getAccessToken();
|
||||
if (token) {
|
||||
header["Authorization"] = `Bearer ${token}`;
|
||||
const accessToken = getAccessToken();
|
||||
if (accessToken) {
|
||||
header["Authorization"] = `Bearer ${accessToken}`;
|
||||
} else {
|
||||
// 需要认证但没有令牌,跳转到登录页
|
||||
uni.navigateTo({
|
||||
@@ -35,7 +37,7 @@ function request<T = any>(options: RequestOptions): Promise<T> {
|
||||
let requestUrl = options.url;
|
||||
// #ifdef MP-WEIXIN
|
||||
// 微信小程序环境,使用完整URL
|
||||
requestUrl = `${import.meta.env.VITE_APP_API_URL}${options.url}`;
|
||||
requestUrl = `${import.meta.env.VITE_API_BASE_URL}${options.url}`;
|
||||
// #endif
|
||||
|
||||
// #ifndef MP-WEIXIN
|
||||
@@ -43,13 +45,18 @@ function request<T = any>(options: RequestOptions): Promise<T> {
|
||||
requestUrl = `${import.meta.env.VITE_APP_BASE_API}${options.url}`;
|
||||
// #endif
|
||||
|
||||
let timeout = Number(import.meta.env.VITE_TIMEOUT);
|
||||
if (options.timeout) {
|
||||
timeout = options.timeout;
|
||||
}
|
||||
|
||||
// 统一处理请求
|
||||
uni.request({
|
||||
url: requestUrl,
|
||||
method: options.method,
|
||||
data: options.data,
|
||||
header,
|
||||
timeout: options.timeout || 30000,
|
||||
header: header,
|
||||
timeout: timeout,
|
||||
responseType: options.responseType,
|
||||
success: (res: any) => {
|
||||
// 请求成功
|
||||
|
||||
Reference in New Issue
Block a user