From 9ce121af3f4f7c8bc1a869638dda1413ca2c7702 Mon Sep 17 00:00:00 2001 From: luoer Date: Tue, 26 Sep 2023 17:07:31 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=88=86=E7=B1=BB=E9=A1=B5=E9=9D=A2?= =?UTF-8?q?=E4=BB=A5=E6=A0=91=E7=BA=A7=E7=BB=93=E6=9E=84=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env | 2 +- src/api/instance/api.ts | 1 + src/api/service/Api.ts | 407 ++++++++++++++++++++++++++---- src/pages/post/category/index.vue | 123 ++++++--- src/pages/post/media/index.vue | 84 +++--- src/utils/listToTree.ts | 11 + 6 files changed, 503 insertions(+), 125 deletions(-) create mode 100644 src/utils/listToTree.ts diff --git a/.env b/.env index 60dfc9a..f84a78f 100644 --- a/.env +++ b/.env @@ -6,7 +6,7 @@ VITE_TITLE = 绝弹管理后台 # 网站副标题 VITE_SUBTITLE = 快速开发web应用的模板工具 # 接口前缀 说明:参见 axios 的 baseURL -VITE_API = http://demo.dev.juetan.cn/ +VITE_API = http://127.0.0.1:3030/ # ===================================================================================== # 开发设置 diff --git a/src/api/instance/api.ts b/src/api/instance/api.ts index 336ce40..8913b5a 100644 --- a/src/api/instance/api.ts +++ b/src/api/instance/api.ts @@ -69,6 +69,7 @@ api.instance.interceptors.response.use( if (error.response) { const code = error.response.data?.code; if (code === 4050 || code === 4051) { + Message.warning('提示:登陆过期,请重新登陆!') userStore.clearUser(); api.tokenExpiredHandler?.(); } diff --git a/src/api/service/Api.ts b/src/api/service/Api.ts index 9b21526..6ddb7d7 100644 --- a/src/api/service/Api.ts +++ b/src/api/service/Api.ts @@ -169,6 +169,11 @@ export interface LoginLog { * @example "1" */ description: string; + /** + * 操作状态 + * @example true + */ + status: boolean; /** * 登陆IP * @example "127.0.0.1" @@ -199,39 +204,6 @@ export interface UpdateLogDto { demo?: string; } -export interface Role { - /** - * 角色名称 - * @example "管理员" - */ - name: string; - /** - * 角色标识 - * @example "admin" - */ - slug: string; - /** - * 角色描述 - * @example "拥有所有权限" - */ - description: string; - /** - * 角色权限 - * @example [1,2,3] - */ - permissions: Permission[]; - /** - * 角色权限ID - * @example [1,2,3] - */ - permissionIds: number[]; - /** - * 角色用户 - * @example [1,2,3] - */ - user: User; -} - export interface Permission { /** * 权限名称 @@ -243,16 +215,16 @@ export interface Permission { * @example "post:list" */ slug: string; + /** + * 权限类型 + * @example "menu" + */ + type: "menu" | "api"; /** * 权限描述 * @example "文章列表" */ description: string; - /** - * 权限角色 - * @example "文章列表" - */ - roles: Role[]; } export interface CreateRoleDto { @@ -379,6 +351,120 @@ export interface UpdatePostDto { content?: string; } +export interface CreateCategoryDto { + /** + * 分类名称 + * @example "待分类" + */ + title: string; + /** + * 分类别名 + * @example "default" + */ + slug: string; + /** + * 分类描述 + * @example "默认分类" + */ + description?: string; + /** + * 分类图标 + * @example "default" + */ + icon?: string; + /** + * 分类排序 + * @example 0 + */ + sort?: number; + /** + * 分类类型 + * @example "category" + */ + type: object; + /** + * 父级分类ID + * @example 0 + */ + parentId?: number; +} + +export interface Category { + /** + * 分类名称 + * @example "待分类" + */ + title: string; + /** + * 分类别名 + * @example "default" + */ + slug: string; + /** + * 分类描述 + * @example "默认分类" + */ + description?: string; + /** + * 分类图标 + * @example "default" + */ + icon?: string; + /** + * 分类排序 + * @example 0 + */ + sort?: number; + /** + * 分类类型 + * @example "category" + */ + type?: object; + /** + * 父级分类ID + * @example 0 + */ + parentId?: number; +} + +export interface UpdateCategoryDto { + /** + * 分类名称 + * @example "待分类" + */ + title?: string; + /** + * 分类别名 + * @example "default" + */ + slug?: string; + /** + * 分类描述 + * @example "默认分类" + */ + description?: string; + /** + * 分类图标 + * @example "default" + */ + icon?: string; + /** + * 分类排序 + * @example 0 + */ + sort?: number; + /** + * 分类类型 + * @example "category" + */ + type?: object; + /** + * 父级分类ID + * @example 0 + */ + parentId?: number; +} + export interface Response { /** * 状态码 @@ -496,6 +582,33 @@ export interface GetPostsParams { size?: number; } +export interface GetCategorysParams { + /** + * 字段描述(Swagger用途) + * @example "示例值" + */ + demo?: string; + /** + * 排序规则 + * @default "id:desc" + * @pattern /^(\w+:\w+,)*\w+:\w+$/ + * @example "id:desc" + */ + sort?: string; + /** + * 页码 + * @min 1 + * @example 1 + */ + page?: number; + /** + * 每页条数 + * @min 0 + * @example 10 + */ + size?: number; +} + export namespace User { /** * @description 新增用户 @@ -676,7 +789,7 @@ export namespace Log { }; } /** - * No description + * @description 分页查询登陆日志 * @tags log * @name GetLoginLogs * @request GET:/api/v1/logs/login @@ -893,10 +1006,10 @@ export namespace Permission { /** * @description 更新权限 * @tags permission - * @name UpdatePermission + * @name SetPermission * @request PATCH:/api/v1/permissions/{id} */ - export namespace UpdatePermission { + export namespace SetPermission { export type RequestParams = { id: string; }; @@ -1115,6 +1228,111 @@ export namespace Post { } } +export namespace Category { + /** + * @description 新增分类 + * @tags category + * @name AddCategory + * @request POST:/api/v1/categories + */ + export namespace AddCategory { + export type RequestParams = {}; + export type RequestQuery = {}; + export type RequestBody = CreateCategoryDto; + export type RequestHeaders = {}; + export type ResponseBody = Response & { + data: number; + }; + } + /** + * @description 根据分页/过滤参数查询分类 + * @tags category + * @name GetCategorys + * @request GET:/api/v1/categories + */ + export namespace GetCategorys { + export type RequestParams = {}; + export type RequestQuery = { + /** + * 字段描述(Swagger用途) + * @example "示例值" + */ + demo?: string; + /** + * 排序规则 + * @default "id:desc" + * @pattern /^(\w+:\w+,)*\w+:\w+$/ + * @example "id:desc" + */ + sort?: string; + /** + * 页码 + * @min 1 + * @example 1 + */ + page?: number; + /** + * 每页条数 + * @min 0 + * @example 10 + */ + size?: number; + }; + export type RequestBody = never; + export type RequestHeaders = {}; + export type ResponseBody = Response & { + data: Category[]; + }; + } + /** + * @description 根据ID查询分类 + * @tags category + * @name GetCategory + * @request GET:/api/v1/categories/{id} + */ + export namespace GetCategory { + export type RequestParams = { + id: number; + }; + export type RequestQuery = {}; + export type RequestBody = never; + export type RequestHeaders = {}; + export type ResponseBody = Response & { + data: Category; + }; + } + /** + * @description 根据ID更新分类 + * @tags category + * @name UpdateCategory + * @request PATCH:/api/v1/categories/{id} + */ + export namespace UpdateCategory { + export type RequestParams = { + id: number; + }; + export type RequestQuery = {}; + export type RequestBody = UpdateCategoryDto; + export type RequestHeaders = {}; + export type ResponseBody = Response; + } + /** + * @description 根据ID删除分类 + * @tags category + * @name DelCategory + * @request DELETE:/api/v1/categories/{id} + */ + export namespace DelCategory { + export type RequestParams = { + id: number; + }; + export type RequestQuery = {}; + export type RequestBody = never; + export type RequestHeaders = {}; + export type ResponseBody = Response; + } +} + import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, HeadersDefaults, ResponseType } from "axios"; export type QueryParamsType = Record; @@ -1426,7 +1644,7 @@ export class Api extends HttpClient extends HttpClient { + setPermission: (id: string, data: UpdatePermissionDto, params: RequestParams = {}) => { return this.request({ path: `/api/v1/permissions/${id}`, method: "PATCH", @@ -1912,4 +2130,105 @@ export class Api extends HttpClient { + return this.request< + Response & { + data: number; + }, + any + >({ + path: `/api/v1/categories`, + method: "POST", + body: data, + type: ContentType.Json, + format: "json", + ...params, + }); + }, + + /** + * 根据分页/过滤参数查询分类 + * + * @tags category + * @name GetCategorys + * @request GET:/api/v1/categories + */ + getCategorys: (query: GetCategorysParams, params: RequestParams = {}) => { + return this.request< + Response & { + data: Category[]; + }, + any + >({ + path: `/api/v1/categories`, + method: "GET", + query: query, + format: "json", + ...params, + }); + }, + + /** + * 根据ID查询分类 + * + * @tags category + * @name GetCategory + * @request GET:/api/v1/categories/{id} + */ + getCategory: (id: number, params: RequestParams = {}) => { + return this.request< + Response & { + data: Category; + }, + any + >({ + path: `/api/v1/categories/${id}`, + method: "GET", + format: "json", + ...params, + }); + }, + + /** + * 根据ID更新分类 + * + * @tags category + * @name UpdateCategory + * @request PATCH:/api/v1/categories/{id} + */ + updateCategory: (id: number, data: UpdateCategoryDto, params: RequestParams = {}) => { + return this.request({ + path: `/api/v1/categories/${id}`, + method: "PATCH", + body: data, + type: ContentType.Json, + format: "json", + ...params, + }); + }, + + /** + * 根据ID删除分类 + * + * @tags category + * @name DelCategory + * @request DELETE:/api/v1/categories/{id} + */ + delCategory: (id: number, params: RequestParams = {}) => { + return this.request({ + path: `/api/v1/categories/${id}`, + method: "DELETE", + format: "json", + ...params, + }); + }, + }; } diff --git a/src/pages/post/category/index.vue b/src/pages/post/category/index.vue index 6637049..b7db5cb 100644 --- a/src/pages/post/category/index.vue +++ b/src/pages/post/category/index.vue @@ -1,6 +1,8 @@ @@ -8,53 +10,52 @@ import { api } from "@/api"; import { Table, useTable } from "@/components"; import { dayjs } from "@/libs/dayjs"; -import { Tag } from "@arco-design/web-vue"; +import { listToTree } from "@/utils/listToTree"; const table = useTable({ data: async (model, paging) => { - return api.log.getLoginLogs({ ...model, ...paging }); + const res = await api.category.getCategorys({ ...model, ...paging }); + const data = listToTree(res.data.data); + return { data: { data, total: (res.data as any).total } }; }, columns: [ { - title: "登陆账号", - dataIndex: "nickname", - width: 140, + title: "名称", + dataIndex: "title", + width: 240, }, { - title: "操作描述", + title: "描述", dataIndex: "description", - render: ({ record: { status, description } }) => { - return ( - - - { status === null || status ? "成功" : "失败" } - - {description} - - ); - }, }, { - title: "登陆地址", - dataIndex: "ip", + title: "别名", + dataIndex: "slug", width: 200, - render: ({ record }) => `${record.addr || "未知"}(${record.ip})`, }, { - title: "操作系统", - dataIndex: "os", - width: 160, - }, - { - title: "浏览器", - dataIndex: "browser", - width: 160, - }, - { - title: "登陆时间", + title: "创建时间", dataIndex: "createdAt", + width: 200, + render: ({ record }) => dayjs(record.createdAt).format(), + }, + { + type: "button", + title: "操作", width: 120, - render: ({ record }) => dayjs(record.createdAt).fromNow(), + buttons: [ + { + type: 'modify', + text: '修改' + }, + { + type: "delete", + text: "删除", + onClick({ record }) { + return api.category.delCategory(record.id); + }, + }, + ], }, ], search: { @@ -65,14 +66,68 @@ const table = useTable({ type: "input", required: false, nodeProps: { - placeholder: '请输入登陆账号', + placeholder: "名称关键字", }, itemProps: { hideLabel: true, - } + }, }, ], }, + create: { + title: "添加分类", + modalProps: { + width: 580, + }, + items: [ + { + field: "parentId", + label: "父级分类", + type: "select", + options: async () => { + const res = await api.category.getCategorys({ size: 0 }); + return res.data.data.map(({ id, title }: any) => ({ value: id, label: title })); + }, + }, + { + field: "title", + label: "分类名称", + type: "input", + required: true, + nodeProps: { + placeholder: "请输入分类名称", + }, + }, + { + field: "slug", + label: "分类别名", + type: "input", + required: true, + nodeProps: { + placeholder: "请输入分类别名", + }, + }, + { + field: "description", + label: "描述", + type: "textarea", + required: false, + nodeProps: { + placeholder: "请输入描述", + }, + }, + ], + submit: async ({ model }) => { + return api.category.addCategory(model); + }, + }, + modify: { + extend: true, + title: "修改分类", + submit: async ({ model }) => { + return api.category.updateCategory(model.id, model); + }, + }, }); diff --git a/src/pages/post/media/index.vue b/src/pages/post/media/index.vue index a7fea23..7072512 100644 --- a/src/pages/post/media/index.vue +++ b/src/pages/post/media/index.vue @@ -1,6 +1,15 @@ @@ -8,71 +17,54 @@ import { api } from "@/api"; import { Table, useTable } from "@/components"; import { dayjs } from "@/libs/dayjs"; -import { Tag } from "@arco-design/web-vue"; const table = useTable({ data: async (model, paging) => { - return api.log.getLoginLogs({ ...model, ...paging }); + return api.upload.getUploads(); }, columns: [ { - title: "登陆账号", - dataIndex: "nickname", - width: 140, + title: "文件名称", + dataIndex: "name", + width: 260, }, { title: "操作描述", dataIndex: "description", - render: ({ record: { status, description } }) => { - return ( - - - { status === null || status ? "成功" : "失败" } - - {description} - - ); - }, - }, - { - title: "登陆地址", - dataIndex: "ip", - width: 200, - render: ({ record }) => `${record.addr || "未知"}(${record.ip})`, - }, - { - title: "操作系统", - dataIndex: "os", - width: 160, - }, - { - title: "浏览器", - dataIndex: "browser", - width: 160, }, { title: "登陆时间", dataIndex: "createdAt", + width: 200, + render: ({ record }) => dayjs(record.createdAt).format(), + }, + { + type: "button", + title: "操作", width: 120, - render: ({ record }) => dayjs(record.createdAt).fromNow(), + buttons: [ + { + type: "modify", + }, + { + type: "delete", + text: "删除", + onClick({ record }) { + return api.upload.delFile(record.id); + }, + }, + ], }, ], search: { items: [ { - field: "nickname", - label: "登陆账号", - type: "input", - required: false, - nodeProps: { - placeholder: '请输入登陆账号', - }, - itemProps: { - hideLabel: true, - } - }, - ], - }, + field: 'name', + label: '文件名称', + type: 'input', + } + ] + } }); diff --git a/src/utils/listToTree.ts b/src/utils/listToTree.ts new file mode 100644 index 0000000..447da65 --- /dev/null +++ b/src/utils/listToTree.ts @@ -0,0 +1,11 @@ +export const listToTree = (list: any[], id = "id", pid = "parentId", cid = "children") => { + const map = list.reduce((res, v) => ((res[v[id]] = v), res), {}); + return list.filter((item) => { + const parent = map[item[pid]]; + if (parent) { + !parent[cid] && (parent[cid] = []); + parent[cid].push(item); + } + return !item[pid]; + }); +};