Просмотр исходного кода

feat:管理后台路由权限校验使用更细粒度的校验,按用户角色进行校验;完善用户管理;对接服务端API参数使用具体的类型;

yangyi 2 дней назад
Родитель
Сommit
c818b4f36d
52 измененных файлов с 582 добавлено и 273 удалено
  1. 3 7
      src/api/attachment-controller.ts
  2. 29 4
      src/api/meta-controller.ts
  3. 0 9
      src/api/models/adminRechargeBody.ts
  4. 19 0
      src/api/models/adminRechargeDto.ts
  5. 0 9
      src/api/models/applyWithdrawBody.ts
  6. 0 9
      src/api/models/createAndPayBody.ts
  7. 5 2
      src/api/models/createPostVo.ts
  8. 15 0
      src/api/models/createTipOrderDto.ts
  9. 19 0
      src/api/models/createTipOrderVo.ts
  10. 26 18
      src/api/models/index.ts
  11. 25 0
      src/api/models/ossConfigDto.ts
  12. 31 0
      src/api/models/profileVo.ts
  13. 17 0
      src/api/models/realnameReviewDto.ts
  14. 27 0
      src/api/models/realnameSubmitDto.ts
  15. 7 1
      src/api/models/rechargeDto.ts
  16. 3 3
      src/api/models/responseCreatePostVo.ts
  17. 3 3
      src/api/models/responseCreateTipOrderVo.ts
  18. 3 3
      src/api/models/responseProfileVo.ts
  19. 2 2
      src/api/models/responseString.ts
  20. 20 0
      src/api/models/responseUnreadCountVo.ts
  21. 20 0
      src/api/models/responseUploadVo.ts
  22. 20 0
      src/api/models/responseWalletVo.ts
  23. 20 0
      src/api/models/responseWithdrawApplyVo.ts
  24. 0 9
      src/api/models/reviewBody.ts
  25. 0 9
      src/api/models/reviewWithdrawBody.ts
  26. 0 9
      src/api/models/submitBody.ts
  27. 15 0
      src/api/models/unreadCountVo.ts
  28. 0 9
      src/api/models/updateConfigBody.ts
  29. 0 9
      src/api/models/updateHitStatusBody.ts
  30. 18 0
      src/api/models/updateHitStatusDto.ts
  31. 0 9
      src/api/models/updateRoleBody.ts
  32. 18 0
      src/api/models/updateRoleDto.ts
  33. 0 9
      src/api/models/updateViewCountBody.ts
  34. 15 0
      src/api/models/updateViewCountDto.ts
  35. 5 2
      src/api/models/uploadVo.ts
  36. 8 0
      src/api/models/userDto.ts
  37. 2 0
      src/api/models/userVo.ts
  38. 5 2
      src/api/models/walletVo.ts
  39. 19 0
      src/api/models/withdrawApplyVo.ts
  40. 15 0
      src/api/models/withdrawDto.ts
  41. 17 0
      src/api/models/withdrawReviewDto.ts
  42. 5 5
      src/api/notification-controller.ts
  43. 16 16
      src/api/post-controller.ts
  44. 10 10
      src/api/realname-auth-controller.ts
  45. 0 41
      src/api/system-config-controller.ts
  46. 7 7
      src/api/tip-order-controller.ts
  47. 8 9
      src/api/user-controller.ts
  48. 27 28
      src/api/wallet-controller.ts
  49. 7 4
      src/layout/AdminLayout.vue
  50. 15 8
      src/router/index.ts
  51. 12 3
      src/view/SiteSettingsView.vue
  52. 24 5
      src/view/UserView.vue

+ 3 - 7
src/api/attachment-controller.ts

@@ -5,11 +5,7 @@
  * Serve应用接口文档
  * OpenAPI spec version: 0.0.1-SNAPSHOT
  */
-import type {
-  ResponseMapStringString,
-  UploadBody,
-  UploadParams,
-} from "./models";
+import type { ResponseUploadVo, UploadBody, UploadParams } from "./models";
 import { customAxiosInstance } from "../util/axios-instance";
 
 export const getAttachmentController = () => {
@@ -17,8 +13,8 @@ export const getAttachmentController = () => {
    * @summary 上传附件到OSS(需对接OSS配置)
    */
   const upload = (uploadBody: UploadBody, params?: UploadParams) => {
-    return customAxiosInstance<ResponseMapStringString>({
-      url: `/api/attachments/upload`,
+    return customAxiosInstance<ResponseUploadVo>({
+      url: `/attachments/upload`,
       method: "POST",
       headers: { "Content-Type": "application/json" },
       data: uploadBody,

+ 29 - 4
src/api/meta-controller.ts

@@ -5,29 +5,54 @@
  * Serve应用接口文档
  * OpenAPI spec version: 0.0.1-SNAPSHOT
  */
-import type { Response, WebsiteMetaDto } from "./models";
+import type {
+  OssConfigDto,
+  ResponseObject,
+  ResponseVoid,
+  WebsiteMetaDto,
+} from "./models";
 import { customAxiosInstance } from "../util/axios-instance";
 
 export const getMetaController = () => {
   const updateWebsiteMeta = (websiteMetaDto: WebsiteMetaDto) => {
-    return customAxiosInstance<Response>({
+    return customAxiosInstance<ResponseVoid>({
       url: `/meta/updateWebsiteMeta`,
       method: "POST",
       headers: { "Content-Type": "application/json" },
       data: websiteMetaDto,
     });
   };
+  const updateOssConfig = (ossConfigDto: OssConfigDto) => {
+    return customAxiosInstance<ResponseVoid>({
+      url: `/meta/updateOssConfig`,
+      method: "POST",
+      headers: { "Content-Type": "application/json" },
+      data: ossConfigDto,
+    });
+  };
   const getWebsiteMeta = () => {
-    return customAxiosInstance<Response>({
+    return customAxiosInstance<ResponseObject>({
       url: `/meta/getWebsiteMeta`,
       method: "GET",
     });
   };
-  return { updateWebsiteMeta, getWebsiteMeta };
+  const getOssConfig = () => {
+    return customAxiosInstance<ResponseObject>({
+      url: `/meta/getOssConfig`,
+      method: "GET",
+    });
+  };
+  return { updateWebsiteMeta, updateOssConfig, getWebsiteMeta, getOssConfig };
 };
 export type UpdateWebsiteMetaResult = NonNullable<
   Awaited<ReturnType<ReturnType<typeof getMetaController>["updateWebsiteMeta"]>>
 >;
+export type UpdateOssConfigResult = NonNullable<
+  Awaited<ReturnType<ReturnType<typeof getMetaController>["updateOssConfig"]>>
+>;
 export type GetWebsiteMetaResult = NonNullable<
   Awaited<ReturnType<ReturnType<typeof getMetaController>["getWebsiteMeta"]>>
 >;
+export type GetOssConfigResult = NonNullable<
+  Awaited<ReturnType<ReturnType<typeof getMetaController>["getOssConfig"]>>
+>;

+ 0 - 9
src/api/models/adminRechargeBody.ts

@@ -1,9 +0,0 @@
-/**
- * Generated by orval v7.0.1 🍺
- * Do not edit manually.
- * Serve API
- * Serve应用接口文档
- * OpenAPI spec version: 0.0.1-SNAPSHOT
- */
-
-export type AdminRechargeBody = { [key: string]: unknown };

+ 19 - 0
src/api/models/adminRechargeDto.ts

@@ -0,0 +1,19 @@
+/**
+ * Generated by orval v7.0.1 🍺
+ * Do not edit manually.
+ * Serve API
+ * Serve应用接口文档
+ * OpenAPI spec version: 0.0.1-SNAPSHOT
+ */
+
+/**
+ * 管理员代充值请求参数
+ */
+export interface AdminRechargeDto {
+  /** 充值金额 */
+  amount: number;
+  /** 备注 */
+  remark?: string;
+  /** 目标用户ID */
+  userId: number;
+}

+ 0 - 9
src/api/models/applyWithdrawBody.ts

@@ -1,9 +0,0 @@
-/**
- * Generated by orval v7.0.1 🍺
- * Do not edit manually.
- * Serve API
- * Serve应用接口文档
- * OpenAPI spec version: 0.0.1-SNAPSHOT
- */
-
-export type ApplyWithdrawBody = { [key: string]: number };

+ 0 - 9
src/api/models/createAndPayBody.ts

@@ -1,9 +0,0 @@
-/**
- * Generated by orval v7.0.1 🍺
- * Do not edit manually.
- * Serve API
- * Serve应用接口文档
- * OpenAPI spec version: 0.0.1-SNAPSHOT
- */
-
-export type CreateAndPayBody = { [key: string]: number };

+ 5 - 2
src/api/models/responseMapStringObjectData.ts → src/api/models/createPostVo.ts

@@ -7,6 +7,9 @@
  */
 
 /**
- * 响应的具体数据
+ * 创建帖子结果视图
  */
-export type ResponseMapStringObjectData = { [key: string]: unknown };
+export interface CreatePostVo {
+  /** 帖子ID */
+  id?: string;
+}

+ 15 - 0
src/api/models/createTipOrderDto.ts

@@ -0,0 +1,15 @@
+/**
+ * Generated by orval v7.0.1 🍺
+ * Do not edit manually.
+ * Serve API
+ * Serve应用接口文档
+ * OpenAPI spec version: 0.0.1-SNAPSHOT
+ */
+
+/**
+ * 打赏请求参数
+ */
+export interface CreateTipOrderDto {
+  /** 帖子ID */
+  postId: number;
+}

+ 19 - 0
src/api/models/createTipOrderVo.ts

@@ -0,0 +1,19 @@
+/**
+ * Generated by orval v7.0.1 🍺
+ * Do not edit manually.
+ * Serve API
+ * Serve应用接口文档
+ * OpenAPI spec version: 0.0.1-SNAPSHOT
+ */
+
+/**
+ * 打赏结果视图
+ */
+export interface CreateTipOrderVo {
+  /** 金额 */
+  amount?: number;
+  /** 订单ID */
+  orderId?: string;
+  /** 状态 */
+  status?: string;
+}

+ 26 - 18
src/api/models/index.ts

@@ -6,10 +6,11 @@
  * OpenAPI spec version: 0.0.1-SNAPSHOT
  */
 
-export * from "./adminRechargeBody";
-export * from "./applyWithdrawBody";
+export * from "./adminRechargeDto";
 export * from "./authTokenVo";
-export * from "./createAndPayBody";
+export * from "./createPostVo";
+export * from "./createTipOrderDto";
+export * from "./createTipOrderVo";
 export * from "./deleteByIdParams";
 export * from "./fieldError";
 export * from "./getTransactionsParams";
@@ -20,49 +21,56 @@ export * from "./listPreviousPostsParams";
 export * from "./loginDto";
 export * from "./notificationVo";
 export * from "./orderTipVo";
+export * from "./ossConfigDto";
 export * from "./pageVoListOrderTipVo";
 export * from "./pageVoListPostVo";
 export * from "./pageVoListUserVo";
 export * from "./postDto";
 export * from "./postVo";
+export * from "./profileVo";
 export * from "./queryByPageParams";
 export * from "./realnameAuthVo";
-export * from "./rechargeBody";
+export * from "./realnameReviewDto";
+export * from "./realnameSubmitDto";
+export * from "./rechargeDto";
 export * from "./registerDto";
-export * from "./response";
 export * from "./responseAuthTokenVo";
 export * from "./responseBoolean";
+export * from "./responseCreatePostVo";
+export * from "./responseCreateTipOrderVo";
 export * from "./responseListFieldError";
 export * from "./responseListNotificationVo";
 export * from "./responseListRealnameAuthVo";
 export * from "./responseListWalletTransactionVo";
-export * from "./responseMapStringLong";
-export * from "./responseMapStringLongData";
-export * from "./responseMapStringObject";
-export * from "./responseMapStringObjectData";
-export * from "./responseMapStringString";
-export * from "./responseMapStringStringData";
 export * from "./responseObject";
 export * from "./responsePageVoListOrderTipVo";
 export * from "./responsePageVoListPostVo";
 export * from "./responsePageVoListUserVo";
 export * from "./responsePostVo";
+export * from "./responseProfileVo";
 export * from "./responseRealnameAuthVo";
+export * from "./responseString";
+export * from "./responseUnreadCountVo";
+export * from "./responseUploadVo";
 export * from "./responseUserVo";
 export * from "./responseVoid";
-export * from "./reviewBody";
-export * from "./reviewWithdrawBody";
-export * from "./submitBody";
-export * from "./updateConfigBody";
-export * from "./updateHitStatusBody";
-export * from "./updateRoleBody";
+export * from "./responseWalletVo";
+export * from "./responseWithdrawApplyVo";
+export * from "./unreadCountVo";
+export * from "./updateHitStatusDto";
+export * from "./updateRoleDto";
 export * from "./updateUserAvatarDto";
 export * from "./updateUserPasswordDto";
 export * from "./updateUserStatusDto";
-export * from "./updateViewCountBody";
+export * from "./updateViewCountDto";
 export * from "./uploadBody";
 export * from "./uploadParams";
+export * from "./uploadVo";
 export * from "./userDto";
 export * from "./userVo";
 export * from "./walletTransactionVo";
+export * from "./walletVo";
 export * from "./websiteMetaDto";
+export * from "./withdrawApplyVo";
+export * from "./withdrawDto";
+export * from "./withdrawReviewDto";

+ 25 - 0
src/api/models/ossConfigDto.ts

@@ -0,0 +1,25 @@
+/**
+ * Generated by orval v7.0.1 🍺
+ * Do not edit manually.
+ * Serve API
+ * Serve应用接口文档
+ * OpenAPI spec version: 0.0.1-SNAPSHOT
+ */
+
+/**
+ * OSS配置DTO,用于更新OSS存储配置信息
+ */
+export interface OssConfigDto {
+  /** Access Key */
+  accessKey?: string;
+  /** Bucket */
+  bucket?: string;
+  /** Endpoint */
+  endpoint?: string;
+  /** 公开访问域名 */
+  publicDomain?: string;
+  /** Region */
+  region?: string;
+  /** Secret Key */
+  secretKey?: string;
+}

+ 31 - 0
src/api/models/profileVo.ts

@@ -0,0 +1,31 @@
+/**
+ * Generated by orval v7.0.1 🍺
+ * Do not edit manually.
+ * Serve API
+ * Serve应用接口文档
+ * OpenAPI spec version: 0.0.1-SNAPSHOT
+ */
+
+/**
+ * 用户个人信息视图
+ */
+export interface ProfileVo {
+  /** 账号 */
+  account?: string;
+  /** 头像URL */
+  avatar?: string;
+  /** 余额 */
+  balance?: number;
+  /** 用户ID */
+  id?: string;
+  /** 是否实名认证 */
+  isRealname?: boolean;
+  /** 会员等级 */
+  level?: string;
+  /** 手机号 */
+  phoneNumber?: string;
+  /** 角色 */
+  role?: string;
+  /** 用户名 */
+  username?: string;
+}

+ 17 - 0
src/api/models/realnameReviewDto.ts

@@ -0,0 +1,17 @@
+/**
+ * Generated by orval v7.0.1 🍺
+ * Do not edit manually.
+ * Serve API
+ * Serve应用接口文档
+ * OpenAPI spec version: 0.0.1-SNAPSHOT
+ */
+
+/**
+ * 实名认证审核请求参数
+ */
+export interface RealnameReviewDto {
+  /** 是否通过 */
+  approved: boolean;
+  /** 驳回原因 */
+  rejectReason?: string;
+}

+ 27 - 0
src/api/models/realnameSubmitDto.ts

@@ -0,0 +1,27 @@
+/**
+ * Generated by orval v7.0.1 🍺
+ * Do not edit manually.
+ * Serve API
+ * Serve应用接口文档
+ * OpenAPI spec version: 0.0.1-SNAPSHOT
+ */
+
+/**
+ * 实名认证提交请求参数
+ */
+export interface RealnameSubmitDto {
+  /**
+   * 身份证号
+   * @minLength 1
+   */
+  idCard: string;
+  /** 身份证反面照URL */
+  idCardBack?: string;
+  /** 身份证正面照URL */
+  idCardFront?: string;
+  /**
+   * 真实姓名
+   * @minLength 1
+   */
+  realName: string;
+}

+ 7 - 1
src/api/models/rechargeBody.ts → src/api/models/rechargeDto.ts

@@ -6,4 +6,10 @@
  * OpenAPI spec version: 0.0.1-SNAPSHOT
  */
 
-export type RechargeBody = { [key: string]: number };
+/**
+ * 充值请求参数
+ */
+export interface RechargeDto {
+  /** 充值金额 */
+  amount: number;
+}

+ 3 - 3
src/api/models/responseMapStringLong.ts → src/api/models/responseCreatePostVo.ts

@@ -5,16 +5,16 @@
  * Serve应用接口文档
  * OpenAPI spec version: 0.0.1-SNAPSHOT
  */
-import type { ResponseMapStringLongData } from "./responseMapStringLongData";
+import type { CreatePostVo } from "./createPostVo";
 
 /**
  * 后端统一的响应实体
  */
-export interface ResponseMapStringLong {
+export interface ResponseCreatePostVo {
   /** 状态码;200:成功 */
   code?: number;
   /** 响应的具体数据 */
-  data?: ResponseMapStringLongData;
+  data?: CreatePostVo;
   /** 响应附加信息 */
   message?: string;
 }

+ 3 - 3
src/api/models/responseMapStringString.ts → src/api/models/responseCreateTipOrderVo.ts

@@ -5,16 +5,16 @@
  * Serve应用接口文档
  * OpenAPI spec version: 0.0.1-SNAPSHOT
  */
-import type { ResponseMapStringStringData } from "./responseMapStringStringData";
+import type { CreateTipOrderVo } from "./createTipOrderVo";
 
 /**
  * 后端统一的响应实体
  */
-export interface ResponseMapStringString {
+export interface ResponseCreateTipOrderVo {
   /** 状态码;200:成功 */
   code?: number;
   /** 响应的具体数据 */
-  data?: ResponseMapStringStringData;
+  data?: CreateTipOrderVo;
   /** 响应附加信息 */
   message?: string;
 }

+ 3 - 3
src/api/models/responseMapStringObject.ts → src/api/models/responseProfileVo.ts

@@ -5,16 +5,16 @@
  * Serve应用接口文档
  * OpenAPI spec version: 0.0.1-SNAPSHOT
  */
-import type { ResponseMapStringObjectData } from "./responseMapStringObjectData";
+import type { ProfileVo } from "./profileVo";
 
 /**
  * 后端统一的响应实体
  */
-export interface ResponseMapStringObject {
+export interface ResponseProfileVo {
   /** 状态码;200:成功 */
   code?: number;
   /** 响应的具体数据 */
-  data?: ResponseMapStringObjectData;
+  data?: ProfileVo;
   /** 响应附加信息 */
   message?: string;
 }

+ 2 - 2
src/api/models/response.ts → src/api/models/responseString.ts

@@ -9,11 +9,11 @@
 /**
  * 后端统一的响应实体
  */
-export interface Response {
+export interface ResponseString {
   /** 状态码;200:成功 */
   code?: number;
   /** 响应的具体数据 */
-  data?: unknown;
+  data?: string;
   /** 响应附加信息 */
   message?: string;
 }

+ 20 - 0
src/api/models/responseUnreadCountVo.ts

@@ -0,0 +1,20 @@
+/**
+ * Generated by orval v7.0.1 🍺
+ * Do not edit manually.
+ * Serve API
+ * Serve应用接口文档
+ * OpenAPI spec version: 0.0.1-SNAPSHOT
+ */
+import type { UnreadCountVo } from "./unreadCountVo";
+
+/**
+ * 后端统一的响应实体
+ */
+export interface ResponseUnreadCountVo {
+  /** 状态码;200:成功 */
+  code?: number;
+  /** 响应的具体数据 */
+  data?: UnreadCountVo;
+  /** 响应附加信息 */
+  message?: string;
+}

+ 20 - 0
src/api/models/responseUploadVo.ts

@@ -0,0 +1,20 @@
+/**
+ * Generated by orval v7.0.1 🍺
+ * Do not edit manually.
+ * Serve API
+ * Serve应用接口文档
+ * OpenAPI spec version: 0.0.1-SNAPSHOT
+ */
+import type { UploadVo } from "./uploadVo";
+
+/**
+ * 后端统一的响应实体
+ */
+export interface ResponseUploadVo {
+  /** 状态码;200:成功 */
+  code?: number;
+  /** 响应的具体数据 */
+  data?: UploadVo;
+  /** 响应附加信息 */
+  message?: string;
+}

+ 20 - 0
src/api/models/responseWalletVo.ts

@@ -0,0 +1,20 @@
+/**
+ * Generated by orval v7.0.1 🍺
+ * Do not edit manually.
+ * Serve API
+ * Serve应用接口文档
+ * OpenAPI spec version: 0.0.1-SNAPSHOT
+ */
+import type { WalletVo } from "./walletVo";
+
+/**
+ * 后端统一的响应实体
+ */
+export interface ResponseWalletVo {
+  /** 状态码;200:成功 */
+  code?: number;
+  /** 响应的具体数据 */
+  data?: WalletVo;
+  /** 响应附加信息 */
+  message?: string;
+}

+ 20 - 0
src/api/models/responseWithdrawApplyVo.ts

@@ -0,0 +1,20 @@
+/**
+ * Generated by orval v7.0.1 🍺
+ * Do not edit manually.
+ * Serve API
+ * Serve应用接口文档
+ * OpenAPI spec version: 0.0.1-SNAPSHOT
+ */
+import type { WithdrawApplyVo } from "./withdrawApplyVo";
+
+/**
+ * 后端统一的响应实体
+ */
+export interface ResponseWithdrawApplyVo {
+  /** 状态码;200:成功 */
+  code?: number;
+  /** 响应的具体数据 */
+  data?: WithdrawApplyVo;
+  /** 响应附加信息 */
+  message?: string;
+}

+ 0 - 9
src/api/models/reviewBody.ts

@@ -1,9 +0,0 @@
-/**
- * Generated by orval v7.0.1 🍺
- * Do not edit manually.
- * Serve API
- * Serve应用接口文档
- * OpenAPI spec version: 0.0.1-SNAPSHOT
- */
-
-export type ReviewBody = { [key: string]: unknown };

+ 0 - 9
src/api/models/reviewWithdrawBody.ts

@@ -1,9 +0,0 @@
-/**
- * Generated by orval v7.0.1 🍺
- * Do not edit manually.
- * Serve API
- * Serve应用接口文档
- * OpenAPI spec version: 0.0.1-SNAPSHOT
- */
-
-export type ReviewWithdrawBody = { [key: string]: unknown };

+ 0 - 9
src/api/models/submitBody.ts

@@ -1,9 +0,0 @@
-/**
- * Generated by orval v7.0.1 🍺
- * Do not edit manually.
- * Serve API
- * Serve应用接口文档
- * OpenAPI spec version: 0.0.1-SNAPSHOT
- */
-
-export type SubmitBody = { [key: string]: string };

+ 15 - 0
src/api/models/unreadCountVo.ts

@@ -0,0 +1,15 @@
+/**
+ * Generated by orval v7.0.1 🍺
+ * Do not edit manually.
+ * Serve API
+ * Serve应用接口文档
+ * OpenAPI spec version: 0.0.1-SNAPSHOT
+ */
+
+/**
+ * 未读通知数量视图
+ */
+export interface UnreadCountVo {
+  /** 未读数量 */
+  count?: number;
+}

+ 0 - 9
src/api/models/updateConfigBody.ts

@@ -1,9 +0,0 @@
-/**
- * Generated by orval v7.0.1 🍺
- * Do not edit manually.
- * Serve API
- * Serve应用接口文档
- * OpenAPI spec version: 0.0.1-SNAPSHOT
- */
-
-export type UpdateConfigBody = { [key: string]: unknown };

+ 0 - 9
src/api/models/updateHitStatusBody.ts

@@ -1,9 +0,0 @@
-/**
- * Generated by orval v7.0.1 🍺
- * Do not edit manually.
- * Serve API
- * Serve应用接口文档
- * OpenAPI spec version: 0.0.1-SNAPSHOT
- */
-
-export type UpdateHitStatusBody = { [key: string]: string };

+ 18 - 0
src/api/models/updateHitStatusDto.ts

@@ -0,0 +1,18 @@
+/**
+ * Generated by orval v7.0.1 🍺
+ * Do not edit manually.
+ * Serve API
+ * Serve应用接口文档
+ * OpenAPI spec version: 0.0.1-SNAPSHOT
+ */
+
+/**
+ * 更新帖子命中状态请求参数
+ */
+export interface UpdateHitStatusDto {
+  /**
+   * 命中状态(pending/hit/miss)
+   * @minLength 1
+   */
+  hitStatus: string;
+}

+ 0 - 9
src/api/models/updateRoleBody.ts

@@ -1,9 +0,0 @@
-/**
- * Generated by orval v7.0.1 🍺
- * Do not edit manually.
- * Serve API
- * Serve应用接口文档
- * OpenAPI spec version: 0.0.1-SNAPSHOT
- */
-
-export type UpdateRoleBody = { [key: string]: string };

+ 18 - 0
src/api/models/updateRoleDto.ts

@@ -0,0 +1,18 @@
+/**
+ * Generated by orval v7.0.1 🍺
+ * Do not edit manually.
+ * Serve API
+ * Serve应用接口文档
+ * OpenAPI spec version: 0.0.1-SNAPSHOT
+ */
+
+/**
+ * 更新用户角色请求参数
+ */
+export interface UpdateRoleDto {
+  /**
+   * 角色(user/expert/admin)
+   * @minLength 1
+   */
+  role: string;
+}

+ 0 - 9
src/api/models/updateViewCountBody.ts

@@ -1,9 +0,0 @@
-/**
- * Generated by orval v7.0.1 🍺
- * Do not edit manually.
- * Serve API
- * Serve应用接口文档
- * OpenAPI spec version: 0.0.1-SNAPSHOT
- */
-
-export type UpdateViewCountBody = { [key: string]: number };

+ 15 - 0
src/api/models/updateViewCountDto.ts

@@ -0,0 +1,15 @@
+/**
+ * Generated by orval v7.0.1 🍺
+ * Do not edit manually.
+ * Serve API
+ * Serve应用接口文档
+ * OpenAPI spec version: 0.0.1-SNAPSHOT
+ */
+
+/**
+ * 修改帖子查看人数请求参数
+ */
+export interface UpdateViewCountDto {
+  /** 新的查看人数 */
+  viewCount: number;
+}

+ 5 - 2
src/api/models/responseMapStringStringData.ts → src/api/models/uploadVo.ts

@@ -7,6 +7,9 @@
  */
 
 /**
- * 响应的具体数据
+ * 附件上传结果视图
  */
-export type ResponseMapStringStringData = { [key: string]: string };
+export interface UploadVo {
+  /** 文件URL */
+  url?: string;
+}

+ 8 - 0
src/api/models/userDto.ts

@@ -16,6 +16,8 @@ export interface UserDto {
    * @maxLength 20
    */
   account: string;
+  /** 用户头像地址 */
+  avatar?: string;
   /** 用户ID */
   id?: string;
   /**
@@ -24,6 +26,12 @@ export interface UserDto {
    * @maxLength 20
    */
   password: string;
+  /**
+   * 用户手机号
+   * @minLength 11
+   * @maxLength 11
+   */
+  phoneNumber?: string;
   /**
    * 用户角色
    * @minLength 4

+ 2 - 0
src/api/models/userVo.ts

@@ -18,6 +18,8 @@ export interface UserVo {
   enable?: number;
   /** 用户ID */
   id?: string;
+  /** 用户手机号 */
+  phoneNumber?: string;
   /** 用户角色 */
   role?: string;
   /** 用户名称 */

+ 5 - 2
src/api/models/responseMapStringLongData.ts → src/api/models/walletVo.ts

@@ -7,6 +7,9 @@
  */
 
 /**
- * 响应的具体数据
+ * 钱包信息视图
  */
-export type ResponseMapStringLongData = { [key: string]: number };
+export interface WalletVo {
+  /** 余额 */
+  balance?: number;
+}

+ 19 - 0
src/api/models/withdrawApplyVo.ts

@@ -0,0 +1,19 @@
+/**
+ * Generated by orval v7.0.1 🍺
+ * Do not edit manually.
+ * Serve API
+ * Serve应用接口文档
+ * OpenAPI spec version: 0.0.1-SNAPSHOT
+ */
+
+/**
+ * 提现申请结果视图
+ */
+export interface WithdrawApplyVo {
+  /** 金额 */
+  amount?: number;
+  /** 状态 */
+  status?: string;
+  /** 交易ID */
+  transactionId?: string;
+}

+ 15 - 0
src/api/models/withdrawDto.ts

@@ -0,0 +1,15 @@
+/**
+ * Generated by orval v7.0.1 🍺
+ * Do not edit manually.
+ * Serve API
+ * Serve应用接口文档
+ * OpenAPI spec version: 0.0.1-SNAPSHOT
+ */
+
+/**
+ * 提现请求参数
+ */
+export interface WithdrawDto {
+  /** 提现金额 */
+  amount: number;
+}

+ 17 - 0
src/api/models/withdrawReviewDto.ts

@@ -0,0 +1,17 @@
+/**
+ * Generated by orval v7.0.1 🍺
+ * Do not edit manually.
+ * Serve API
+ * Serve应用接口文档
+ * OpenAPI spec version: 0.0.1-SNAPSHOT
+ */
+
+/**
+ * 提现审核请求参数
+ */
+export interface WithdrawReviewDto {
+  /** 是否通过 */
+  approved: boolean;
+  /** 驳回原因 */
+  rejectReason?: string;
+}

+ 5 - 5
src/api/notification-controller.ts

@@ -7,7 +7,7 @@
  */
 import type {
   ResponseListNotificationVo,
-  ResponseMapStringLong,
+  ResponseUnreadCountVo,
   ResponseVoid,
 } from "./models";
 import { customAxiosInstance } from "../util/axios-instance";
@@ -18,7 +18,7 @@ export const getNotificationController = () => {
    */
   const markRead = (id: number) => {
     return customAxiosInstance<ResponseVoid>({
-      url: `/api/notifications/${id}/read`,
+      url: `/notifications/${id}/read`,
       method: "PUT",
     });
   };
@@ -27,7 +27,7 @@ export const getNotificationController = () => {
    */
   const listNotifications = () => {
     return customAxiosInstance<ResponseListNotificationVo>({
-      url: `/api/notifications`,
+      url: `/notifications`,
       method: "GET",
     });
   };
@@ -35,8 +35,8 @@ export const getNotificationController = () => {
    * @summary 获取未读数
    */
   const getUnreadCount = () => {
-    return customAxiosInstance<ResponseMapStringLong>({
-      url: `/api/notifications/unread-count`,
+    return customAxiosInstance<ResponseUnreadCountVo>({
+      url: `/notifications/unread-count`,
       method: "GET",
     });
   };

+ 16 - 16
src/api/post-controller.ts

@@ -9,12 +9,12 @@ import type {
   ListPostsParams,
   ListPreviousPostsParams,
   PostDto,
-  ResponseMapStringObject,
+  ResponseCreatePostVo,
   ResponsePageVoListPostVo,
   ResponsePostVo,
   ResponseVoid,
-  UpdateHitStatusBody,
-  UpdateViewCountBody,
+  UpdateHitStatusDto,
+  UpdateViewCountDto,
 } from "./models";
 import { customAxiosInstance } from "../util/axios-instance";
 
@@ -24,7 +24,7 @@ export const getPostController = () => {
    */
   const getPostDetail = (id: number) => {
     return customAxiosInstance<ResponsePostVo>({
-      url: `/api/posts/${id}`,
+      url: `/posts/${id}`,
       method: "GET",
     });
   };
@@ -33,7 +33,7 @@ export const getPostController = () => {
    */
   const updatePost = (id: number, postDto: PostDto) => {
     return customAxiosInstance<ResponseVoid>({
-      url: `/api/posts/${id}`,
+      url: `/posts/${id}`,
       method: "PUT",
       headers: { "Content-Type": "application/json" },
       data: postDto,
@@ -44,7 +44,7 @@ export const getPostController = () => {
    */
   const deletePost = (id: number) => {
     return customAxiosInstance<ResponseVoid>({
-      url: `/api/posts/${id}`,
+      url: `/posts/${id}`,
       method: "DELETE",
     });
   };
@@ -53,13 +53,13 @@ export const getPostController = () => {
    */
   const updateViewCount = (
     id: number,
-    updateViewCountBody: UpdateViewCountBody,
+    updateViewCountDto: UpdateViewCountDto,
   ) => {
     return customAxiosInstance<ResponseVoid>({
-      url: `/api/posts/${id}/view-count`,
+      url: `/posts/${id}/view-count`,
       method: "PUT",
       headers: { "Content-Type": "application/json" },
-      data: updateViewCountBody,
+      data: updateViewCountDto,
     });
   };
   /**
@@ -67,13 +67,13 @@ export const getPostController = () => {
    */
   const updateHitStatus = (
     id: number,
-    updateHitStatusBody: UpdateHitStatusBody,
+    updateHitStatusDto: UpdateHitStatusDto,
   ) => {
     return customAxiosInstance<ResponseVoid>({
-      url: `/api/posts/${id}/hit-status`,
+      url: `/posts/${id}/hit-status`,
       method: "PUT",
       headers: { "Content-Type": "application/json" },
-      data: updateHitStatusBody,
+      data: updateHitStatusDto,
     });
   };
   /**
@@ -81,7 +81,7 @@ export const getPostController = () => {
    */
   const listPosts = (params?: ListPostsParams) => {
     return customAxiosInstance<ResponsePageVoListPostVo>({
-      url: `/api/posts`,
+      url: `/posts`,
       method: "GET",
       params,
     });
@@ -90,8 +90,8 @@ export const getPostController = () => {
    * @summary 创建帖子(仅专家/管理员)
    */
   const createPost = (postDto: PostDto) => {
-    return customAxiosInstance<ResponseMapStringObject>({
-      url: `/api/posts`,
+    return customAxiosInstance<ResponseCreatePostVo>({
+      url: `/posts`,
       method: "POST",
       headers: { "Content-Type": "application/json" },
       data: postDto,
@@ -105,7 +105,7 @@ export const getPostController = () => {
     params?: ListPreviousPostsParams,
   ) => {
     return customAxiosInstance<ResponsePageVoListPostVo>({
-      url: `/api/posts/expert/${expertId}/previous`,
+      url: `/posts/expert/${expertId}/previous`,
       method: "GET",
       params,
     });

+ 10 - 10
src/api/realname-auth-controller.ts

@@ -6,11 +6,11 @@
  * OpenAPI spec version: 0.0.1-SNAPSHOT
  */
 import type {
+  RealnameReviewDto,
+  RealnameSubmitDto,
   ResponseListRealnameAuthVo,
   ResponseRealnameAuthVo,
   ResponseVoid,
-  ReviewBody,
-  SubmitBody,
 } from "./models";
 import { customAxiosInstance } from "../util/axios-instance";
 
@@ -18,12 +18,12 @@ export const getRealnameAuthController = () => {
   /**
    * @summary 管理员审核实名认证
    */
-  const review = (id: number, reviewBody: ReviewBody) => {
+  const review = (id: number, realnameReviewDto: RealnameReviewDto) => {
     return customAxiosInstance<ResponseVoid>({
-      url: `/api/realname/${id}/review`,
+      url: `/realname/${id}/review`,
       method: "PUT",
       headers: { "Content-Type": "application/json" },
-      data: reviewBody,
+      data: realnameReviewDto,
     });
   };
   /**
@@ -31,19 +31,19 @@ export const getRealnameAuthController = () => {
    */
   const getMyAuth = () => {
     return customAxiosInstance<ResponseRealnameAuthVo>({
-      url: `/api/realname`,
+      url: `/realname`,
       method: "GET",
     });
   };
   /**
    * @summary 提交实名认证
    */
-  const submit = (submitBody: SubmitBody) => {
+  const submit = (realnameSubmitDto: RealnameSubmitDto) => {
     return customAxiosInstance<ResponseVoid>({
-      url: `/api/realname`,
+      url: `/realname`,
       method: "POST",
       headers: { "Content-Type": "application/json" },
-      data: submitBody,
+      data: realnameSubmitDto,
     });
   };
   /**
@@ -51,7 +51,7 @@ export const getRealnameAuthController = () => {
    */
   const listPending = () => {
     return customAxiosInstance<ResponseListRealnameAuthVo>({
-      url: `/api/realname/pending`,
+      url: `/realname/pending`,
       method: "GET",
     });
   };

+ 0 - 41
src/api/system-config-controller.ts

@@ -1,41 +0,0 @@
-/**
- * Generated by orval v7.0.1 🍺
- * Do not edit manually.
- * Serve API
- * Serve应用接口文档
- * OpenAPI spec version: 0.0.1-SNAPSHOT
- */
-import type { ResponseObject, ResponseVoid, UpdateConfigBody } from "./models";
-import { customAxiosInstance } from "../util/axios-instance";
-
-export const getSystemConfigController = () => {
-  /**
-   * @summary 获取配置
-   */
-  const getConfig = (key: string) => {
-    return customAxiosInstance<ResponseObject>({
-      url: `/api/config/${key}`,
-      method: "GET",
-    });
-  };
-  /**
-   * @summary 更新配置(管理员)
-   */
-  const updateConfig = (key: string, updateConfigBody: UpdateConfigBody) => {
-    return customAxiosInstance<ResponseVoid>({
-      url: `/api/config/${key}`,
-      method: "PUT",
-      headers: { "Content-Type": "application/json" },
-      data: updateConfigBody,
-    });
-  };
-  return { getConfig, updateConfig };
-};
-export type GetConfigResult = NonNullable<
-  Awaited<ReturnType<ReturnType<typeof getSystemConfigController>["getConfig"]>>
->;
-export type UpdateConfigResult = NonNullable<
-  Awaited<
-    ReturnType<ReturnType<typeof getSystemConfigController>["updateConfig"]>
-  >
->;

+ 7 - 7
src/api/tip-order-controller.ts

@@ -6,9 +6,9 @@
  * OpenAPI spec version: 0.0.1-SNAPSHOT
  */
 import type {
-  CreateAndPayBody,
+  CreateTipOrderDto,
   ListOrdersParams,
-  ResponseMapStringObject,
+  ResponseCreateTipOrderVo,
   ResponsePageVoListOrderTipVo,
 } from "./models";
 import { customAxiosInstance } from "../util/axios-instance";
@@ -17,12 +17,12 @@ export const getTipOrderController = () => {
   /**
    * @summary 打赏并即时扣款
    */
-  const createAndPay = (createAndPayBody: CreateAndPayBody) => {
-    return customAxiosInstance<ResponseMapStringObject>({
-      url: `/api/orders/tip`,
+  const createAndPay = (createTipOrderDto: CreateTipOrderDto) => {
+    return customAxiosInstance<ResponseCreateTipOrderVo>({
+      url: `/orders/tip`,
       method: "POST",
       headers: { "Content-Type": "application/json" },
-      data: createAndPayBody,
+      data: createTipOrderDto,
     });
   };
   /**
@@ -30,7 +30,7 @@ export const getTipOrderController = () => {
    */
   const listOrders = (params?: ListOrdersParams) => {
     return customAxiosInstance<ResponsePageVoListOrderTipVo>({
-      url: `/api/orders`,
+      url: `/orders`,
       method: "GET",
       params,
     });

+ 8 - 9
src/api/user-controller.ts

@@ -8,13 +8,12 @@
 import type {
   DeleteByIdParams,
   QueryByPageParams,
-  Response,
   ResponseBoolean,
-  ResponseMapStringObject,
   ResponsePageVoListUserVo,
+  ResponseProfileVo,
   ResponseUserVo,
   ResponseVoid,
-  UpdateRoleBody,
+  UpdateRoleDto,
   UpdateUserAvatarDto,
   UpdateUserPasswordDto,
   UpdateUserStatusDto,
@@ -53,16 +52,16 @@ export const getUserController = () => {
       params,
     });
   };
-  const updateRole = (id: number, updateRoleBody: UpdateRoleBody) => {
+  const updateRole = (id: number, updateRoleDto: UpdateRoleDto) => {
     return customAxiosInstance<ResponseVoid>({
       url: `/user/${id}/role`,
       method: "PUT",
       headers: { "Content-Type": "application/json" },
-      data: updateRoleBody,
+      data: updateRoleDto,
     });
   };
   const updateUserStatus = (updateUserStatusDto: UpdateUserStatusDto) => {
-    return customAxiosInstance<Response>({
+    return customAxiosInstance<ResponseBoolean>({
       url: `/user/updateStatus`,
       method: "PUT",
       headers: { "Content-Type": "application/json" },
@@ -70,7 +69,7 @@ export const getUserController = () => {
     });
   };
   const updatePassword = (updateUserPasswordDto: UpdateUserPasswordDto) => {
-    return customAxiosInstance<Response>({
+    return customAxiosInstance<ResponseBoolean>({
       url: `/user/updatePassword`,
       method: "PUT",
       headers: { "Content-Type": "application/json" },
@@ -78,7 +77,7 @@ export const getUserController = () => {
     });
   };
   const updateUserAvatar = (updateUserAvatarDto: UpdateUserAvatarDto) => {
-    return customAxiosInstance<Response>({
+    return customAxiosInstance<ResponseBoolean>({
       url: `/user/updateAvatar`,
       method: "PUT",
       headers: { "Content-Type": "application/json" },
@@ -92,7 +91,7 @@ export const getUserController = () => {
     });
   };
   const profile = () => {
-    return customAxiosInstance<ResponseMapStringObject>({
+    return customAxiosInstance<ResponseProfileVo>({
       url: `/user/profile`,
       method: "GET",
     });

+ 27 - 28
src/api/wallet-controller.ts

@@ -6,15 +6,17 @@
  * OpenAPI spec version: 0.0.1-SNAPSHOT
  */
 import type {
-  AdminRechargeBody,
-  ApplyWithdrawBody,
+  AdminRechargeDto,
   GetTransactionsParams,
   ListPendingWithdrawalsParams,
-  RechargeBody,
+  RechargeDto,
   ResponseListWalletTransactionVo,
-  ResponseMapStringObject,
+  ResponseString,
   ResponseVoid,
-  ReviewWithdrawBody,
+  ResponseWalletVo,
+  ResponseWithdrawApplyVo,
+  WithdrawDto,
+  WithdrawReviewDto,
 } from "./models";
 import { customAxiosInstance } from "../util/axios-instance";
 
@@ -22,48 +24,45 @@ export const getWalletController = () => {
   /**
    * @summary 审核提现(管理员)
    */
-  const reviewWithdraw = (
-    id: number,
-    reviewWithdrawBody: ReviewWithdrawBody,
-  ) => {
+  const reviewWithdraw = (id: number, withdrawReviewDto: WithdrawReviewDto) => {
     return customAxiosInstance<ResponseVoid>({
-      url: `/api/wallet/withdraw/${id}/review`,
+      url: `/wallet/withdraw/${id}/review`,
       method: "PUT",
       headers: { "Content-Type": "application/json" },
-      data: reviewWithdrawBody,
+      data: withdrawReviewDto,
     });
   };
   /**
    * @summary 提现申请
    */
-  const applyWithdraw = (applyWithdrawBody: ApplyWithdrawBody) => {
-    return customAxiosInstance<ResponseMapStringObject>({
-      url: `/api/wallet/withdraw`,
+  const applyWithdraw = (withdrawDto: WithdrawDto) => {
+    return customAxiosInstance<ResponseWithdrawApplyVo>({
+      url: `/wallet/withdraw`,
       method: "POST",
       headers: { "Content-Type": "application/json" },
-      data: applyWithdrawBody,
+      data: withdrawDto,
     });
   };
   /**
    * @summary 充值(预留接口,待对接支付平台)
    */
-  const recharge = (rechargeBody: RechargeBody) => {
-    return customAxiosInstance<ResponseMapStringObject>({
-      url: `/api/wallet/recharge`,
+  const recharge = (rechargeDto: RechargeDto) => {
+    return customAxiosInstance<ResponseString>({
+      url: `/wallet/recharge`,
       method: "POST",
       headers: { "Content-Type": "application/json" },
-      data: rechargeBody,
+      data: rechargeDto,
     });
   };
   /**
    * @summary 管理员代充值
    */
-  const adminRecharge = (adminRechargeBody: AdminRechargeBody) => {
-    return customAxiosInstance<ResponseMapStringObject>({
-      url: `/api/wallet/admin-recharge`,
+  const adminRecharge = (adminRechargeDto: AdminRechargeDto) => {
+    return customAxiosInstance<ResponseString>({
+      url: `/wallet/admin-recharge`,
       method: "POST",
       headers: { "Content-Type": "application/json" },
-      data: adminRechargeBody,
+      data: adminRechargeDto,
     });
   };
   /**
@@ -71,7 +70,7 @@ export const getWalletController = () => {
    */
   const listPendingWithdrawals = (params?: ListPendingWithdrawalsParams) => {
     return customAxiosInstance<ResponseListWalletTransactionVo>({
-      url: `/api/wallet/withdraw/pending`,
+      url: `/wallet/withdraw/pending`,
       method: "GET",
       params,
     });
@@ -80,8 +79,8 @@ export const getWalletController = () => {
    * @summary 资金明细
    */
   const getTransactions = (params?: GetTransactionsParams) => {
-    return customAxiosInstance<ResponseMapStringObject>({
-      url: `/api/wallet/transactions`,
+    return customAxiosInstance<ResponseListWalletTransactionVo>({
+      url: `/wallet/transactions`,
       method: "GET",
       params,
     });
@@ -90,8 +89,8 @@ export const getWalletController = () => {
    * @summary 查询余额
    */
   const getBalance = () => {
-    return customAxiosInstance<ResponseMapStringObject>({
-      url: `/api/wallet/balance`,
+    return customAxiosInstance<ResponseWalletVo>({
+      url: `/wallet/balance`,
       method: "GET",
     });
   };

+ 7 - 4
src/layout/AdminLayout.vue

@@ -1,7 +1,6 @@
 <script setup lang="ts">
 import {useLoginUserStore, useMetaStore} from "../store";
-import {computed, type ComputedRef, onMounted, ref, watch} from "vue";
-import type {RouteRecordRaw} from "vue-router";
+import {computed, onMounted, ref, watch} from "vue";
 import router, {adminRoutes} from "../router";
 
 const { websiteMeta,  fetchMeta } = useMetaStore()
@@ -14,8 +13,12 @@ const logoutHandler = () => {
   loginUserStore.logoutUser()
   router.push('/')
 }
-const menus:ComputedRef<RouteRecordRaw[] | undefined> = computed(()=>{
-  return adminRoutes[0].children;
+const menus = computed(() => {
+  const userRole = loginUserStore.loginUser.user?.role
+  return adminRoutes[0].children?.filter(child => {
+    const allowedRoles = child.meta?.roles as string[] | undefined
+    return !allowedRoles || allowedRoles.includes(userRole ?? '')
+  })
 })
 const isCollapse = ref(false)
 const mobileMenuVisible = ref(false)

+ 15 - 8
src/router/index.ts

@@ -96,34 +96,34 @@ export const adminRoutes: RouteRecordRaw[] = [
         path: '/admin/users',
         name: 'users',
         component: UserView,
-        meta: { title: '用户管理', icon: User, requiresAdmin: true }
+        meta: { title: '用户管理', icon: User, roles: ['admin'] }
       },
       {
         path: '/admin/posts',
         name: 'adminPosts',
         component: PostManageView,
-        meta: { title: '帖子管理', icon: Document, requiresAdmin: true }
+        meta: { title: '帖子管理', icon: Document, roles: ['admin', 'expert'] }
       },
       {
         path: '/admin/realname',
         name: 'adminRealname',
         component: RealnameReviewView,
-        meta: { title: '实名审核', icon: Edit, requiresAdmin: true }
+        meta: { title: '实名审核', icon: Edit, roles: ['admin'] }
       },
       {
         path: '/admin/withdraw',
         name: 'adminWithdraw',
         component: WithdrawReviewView,
-        meta: { title: '提现审核', icon: Edit, requiresAdmin: true }
+        meta: { title: '提现审核', icon: Edit, roles: ['admin'] }
       },
       {
         path: '/admin/settings',
         name: 'settings',
         component: SiteSettingsView,
-        meta: { title: '网站设置', icon: Setting, requiresAdmin: true }
+        meta: { title: '网站设置', icon: Setting, roles: ['admin'] }
       }
     ],
-    meta: { requiresAdmin: true }
+    meta: { roles: ['admin', 'expert'] }
   }
 ]
 
@@ -137,12 +137,19 @@ const router: Router = createRouter({
 })
 
 router.beforeEach((to, _from, next) => {
-  if (to.matched.some(record => record.meta.requiresAdmin)) {
+  const restrictedRecords = to.matched.filter(record => record.meta.roles)
+  if (restrictedRecords.length > 0) {
     const loginUserStore = useLoginUserStore()
     if (!loginUserStore.loginUser.isLogin) {
       ElMessage.warning('请先登录')
       next({ path: '/login', query: { redirect: to.fullPath } })
-    } else if (loginUserStore.loginUser.user?.role !== 'admin') {
+      return
+    }
+    const userRole = loginUserStore.loginUser.user?.role ?? ''
+    const hasAccess = restrictedRecords.every(record =>
+      (record.meta.roles as string[]).includes(userRole)
+    )
+    if (!hasAccess) {
       ElMessage.error('无权访问')
       next('/')
     } else {

+ 12 - 3
src/view/SiteSettingsView.vue

@@ -2,7 +2,8 @@
 import { onMounted, reactive, ref } from 'vue'
 import { useMetaStore } from '../store'
 import { getMetaController } from '../api/meta-controller'
-import { getSystemConfigController } from '../api/system-config-controller'
+import type { ResponseObject, ResponseVoid } from '../api/models'
+import { customAxiosInstance } from '../util/axios-instance'
 import { ElMessage, type FormInstance, type FormRules } from 'element-plus'
 
 const activeTab = ref('basic')
@@ -43,7 +44,10 @@ onMounted(async () => {
   form.announcement = websiteMeta.announcement
 
   try {
-    const res = await getSystemConfigController().getConfig('oss')
+    const res = await customAxiosInstance<ResponseObject>({
+      url: '/meta/getOssConfig',
+      method: 'GET',
+    })
     if (res.code === 200 && res.data) {
       const d = res.data as Record<string, unknown>
       ossForm.endpoint = String(d.endpoint ?? '')
@@ -85,7 +89,12 @@ const handleSubmit = async () => {
 async function handleOssSave() {
   ossSaving.value = true
   try {
-    await getSystemConfigController().updateConfig('oss', { value: ossForm })
+    await customAxiosInstance<ResponseVoid>({
+      url: '/meta/updateOssConfig',
+      method: 'POST',
+      headers: { 'Content-Type': 'application/json' },
+      data: ossForm,
+    })
     ElMessage.success('OSS 配置已保存')
   } catch {
     ElMessage.error('保存失败')

+ 24 - 5
src/view/UserView.vue

@@ -25,7 +25,9 @@ const userForm = reactive<Partial<UserDto>>({
   username: '',
   account: '',
   password: '',
-  role: ''
+  role: '',
+  phoneNumber: '',
+  avatar: ''
 })
 const formRules: FormRules = {
   username: [
@@ -157,7 +159,7 @@ const handleSizeChange = (size: number) => {
 // 打开新增
 const handleAdd = () => {
   dialogTitle.value = '新增用户'
-  Object.assign(userForm, { id: undefined, username: '', account: '', password: '', role: '' })
+  Object.assign(userForm, { id: undefined, username: '', account: '', password: '', role: '', phoneNumber: '', avatar: '' })
   dialogVisible.value = true
 }
 
@@ -173,7 +175,9 @@ const handleEdit = async (row: UserVo) => {
         username: res.data?.username,
         account: res.data?.account,
         password: '',
-        role: res.data?.role
+        role: res.data?.role,
+        phoneNumber: res.data?.phoneNumber ?? '',
+        avatar: res.data?.avatar ?? ''
       })
       dialogVisible.value = true
     }
@@ -197,7 +201,9 @@ const handleSave = async () => {
         const data: Partial<UserDto> = {
           id: userForm.id,
           ...baseData,
-          ...(userForm.password ? { password: userForm.password } : {})
+          ...(userForm.password ? { password: userForm.password } : {}),
+          phoneNumber: userForm.phoneNumber || undefined,
+          avatar: userForm.avatar || undefined
         }
         const res = await api.edit(data as UserDto)
         if (res?.code === 0 || res?.code === 200) {
@@ -214,7 +220,9 @@ const handleSave = async () => {
         }
         const data: UserDto = {
           ...baseData,
-          password: userForm.password!
+          password: userForm.password!,
+          ...(userForm.phoneNumber ? { phoneNumber: userForm.phoneNumber } : {}),
+          ...(userForm.avatar ? { avatar: userForm.avatar } : {})
         }
         const res = await api.add(data)
         if (res?.code === 0 || res?.code === 200) {
@@ -442,6 +450,17 @@ fetchData()
             <el-option v-for="item in roleOptions" :key="item.value" :label="item.label" :value="item.value" />
           </el-select>
         </el-form-item>
+        <el-form-item label="手机号" prop="phoneNumber">
+          <el-input v-model="userForm.phoneNumber" placeholder="请输入手机号" maxlength="11" />
+        </el-form-item>
+        <el-form-item label="头像" prop="avatar">
+          <el-input v-model="userForm.avatar" placeholder="请输入头像URL地址">
+            <template #append>
+              <el-avatar v-if="userForm.avatar" :src="userForm.avatar" :size="28" style="vertical-align: middle" />
+              <span v-else style="padding: 0 8px; color: #999">预览</span>
+            </template>
+          </el-input>
+        </el-form-item>
       </el-form>
       <template #footer>
         <el-button @click="dialogVisible = false">取消</el-button>