From c84da369cf824f3d3fa3e47ddbd883edcbe92554 Mon Sep 17 00:00:00 2001 From: luoer Date: Thu, 2 Nov 2023 17:37:40 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E5=88=86=E7=B1=BB=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env | 2 +- src/api/service/Api.ts | 623 ++++++++++++++++-- src/components/form/form-config.ts | 29 + src/components/form/form-node.tsx | 27 +- src/components/table/table.tsx | 42 +- src/components/table/use-table.tsx | 31 +- src/pages/post/media/components/group.vue | 112 ++-- src/pages/post/media/components/upload.vue | 15 +- src/pages/post/media/components/util.ts | 20 + src/pages/post/media/index.vue | 39 +- src/pages/system/dict/index.vue | 16 +- .../system/{login-log => logl}/index.vue | 0 .../system/{operation-log => logo}/index.vue | 0 src/pages/system/menu/index.vue | 34 +- 14 files changed, 804 insertions(+), 186 deletions(-) create mode 100644 src/pages/post/media/components/util.ts rename src/pages/system/{login-log => logl}/index.vue (100%) rename src/pages/system/{operation-log => logo}/index.vue (100%) diff --git a/.env b/.env index e08824b..8de803f 100644 --- a/.env +++ b/.env @@ -6,7 +6,7 @@ VITE_TITLE = 绝弹项目管理 # 网站副标题 VITE_SUBTITLE = 快速开发web应用的模板工具 # 接口前缀 说明:参见 axios 的 baseURL -VITE_API = / +VITE_API = http://127.0.0.1:3030/ # ===================================================================================== # 开发设置 diff --git a/src/api/service/Api.ts b/src/api/service/Api.ts index bcd3ec6..628a20d 100644 --- a/src/api/service/Api.ts +++ b/src/api/service/Api.ts @@ -152,7 +152,7 @@ export interface CreateRoleDto { * 角色标识 * @example "admin" */ - slug: string; + code: string; /** * 角色描述 * @example "一段很长的描述" @@ -175,7 +175,7 @@ export interface UpdateRoleDto { * 角色标识 * @example "admin" */ - slug?: string; + code?: string; /** * 角色描述 * @example "一段很长的描述" @@ -310,7 +310,7 @@ export interface File { mimetype: string; /** * 文件路径 - * @example "/upload/2021/10/01/xxx.jpg" + * @example "/upload/2021-10-01/xxx.jpg" */ path: string; /** @@ -323,6 +323,11 @@ export interface File { * @example ".jpg" */ extension: string; + /** + * 分类ID + * @example 0 + */ + categoryId: number; /** * 自增ID * @example 1 @@ -357,12 +362,110 @@ export interface UpdateFileDto { * 文件名 * @example "头像.jpg" */ - name: string; + name?: string; /** * 描述 * @example "一段很长的描述" */ description?: string; + /** + * 分类ID + * @example 1 + */ + categoryId?: number; +} + +export interface CreateFileCategoryDto { + /** + * 分类名称 + * @example "风景" + */ + name: string; + /** + * 分类编码 + * @example "view" + */ + code: string; + /** + * 分类描述 + * @example "这是一段很长的描述" + */ + description?: string; + /** + * 父级ID + * @example 0 + */ + parentId?: number; +} + +export interface FileCategory { + /** + * 分类名称 + * @example "风景" + */ + name: string; + /** + * 分类编码 + * @example "view" + */ + code: string; + /** + * 分类描述 + * @example "这是一段很长的描述" + */ + description?: string; + /** 父级ID */ + parentId?: number; + /** + * 自增ID + * @example 1 + */ + id: number; + /** + * 创建时间 + * @format date-time + * @example "2022-01-01 10:10:10" + */ + createdAt: string; + /** + * 创建人 + * @example "绝弹" + */ + createdBy: string; + /** + * 更新时间 + * @format date-time + * @example "2022-01-02 11:11:11" + */ + updatedAt: string; + /** + * 更新人 + * @example "绝弹" + */ + updatedBy: string; +} + +export interface UpdateFileCategoryDto { + /** + * 分类名称 + * @example "风景" + */ + name?: string; + /** + * 分类编码 + * @example "view" + */ + code?: string; + /** + * 分类描述 + * @example "这是一段很长的描述" + */ + description?: string; + /** + * 父级ID + * @example 0 + */ + parentId?: number; } export interface CreatePostDto { @@ -985,6 +1088,75 @@ export interface GetLoginLogsParams { createdFrom?: string; } +export interface GetFilesParams { + /** + * 文件名称 + * @example "风景" + */ + name?: string; + /** + * 分类ID + * @example 1 + */ + categoryId?: number; + /** + * 排序规则 + * @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; + /** + * 创建起始事件 + * @example "2020-02-02 02:02:02" + */ + createdFrom?: string; +} + +export interface GetFileCategorysParams { + /** + * 分类名称 + * @example "风景" + */ + name?: 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; + /** + * 创建起始事件 + * @example "2020-02-02 02:02:02" + */ + createdFrom?: string; +} + export interface GetPostsParams { /** * 排序规则 @@ -1685,10 +1857,84 @@ export namespace File { */ export namespace GetFiles { export type RequestParams = {}; - export type RequestQuery = {}; + export type RequestQuery = { + /** + * 文件名称 + * @example "风景" + */ + name?: string; + /** + * 分类ID + * @example 1 + */ + categoryId?: number; + /** + * 排序规则 + * @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; + /** + * 创建起始事件 + * @example "2020-02-02 02:02:02" + */ + createdFrom?: string; + }; export type RequestBody = never; export type RequestHeaders = {}; - export type ResponseBody = Response; + export type ResponseBody = { + /** + * 状态码 + * @format int32 + * @example 2000 + */ + code: number; + /** + * 提示信息 + * @example "请求成功" + */ + message: string; + data?: object; + }; + } + /** + * @description 批量删除文件 + * @tags file + * @name DelFiles + * @request DELETE:/api/v1/file + */ + export namespace DelFiles { + export type RequestParams = {}; + export type RequestQuery = {}; + export type RequestBody = string[]; + export type RequestHeaders = {}; + export type ResponseBody = { + /** + * 状态码 + * @format int32 + * @example 2000 + */ + code: number; + /** + * 提示信息 + * @example "请求成功" + */ + message: string; + data?: object; + }; } /** * @description 查询 @@ -1731,20 +1977,7 @@ export namespace File { export type RequestQuery = {}; export type RequestBody = UpdateFileDto; export type RequestHeaders = {}; - export type ResponseBody = { - /** - * 状态码 - * @format int32 - * @example 2000 - */ - code: number; - /** - * 提示信息 - * @example "请求成功" - */ - message: string; - data?: string; - }; + export type ResponseBody = Response; } /** * @description 删除 @@ -1791,6 +2024,149 @@ export namespace File { } } +export namespace FileCategory { + /** + * @description 新增文件分类 + * @tags fileCategory + * @name AddFileCategory + * @request POST:/api/v1/fileCategorys + */ + export namespace AddFileCategory { + export type RequestParams = {}; + export type RequestQuery = {}; + export type RequestBody = CreateFileCategoryDto; + export type RequestHeaders = {}; + export type ResponseBody = { + /** + * 状态码 + * @format int32 + * @example 2000 + */ + code: number; + /** + * 提示信息 + * @example "请求成功" + */ + message: string; + data?: number; + }; + } + /** + * @description 查询文件分类 + * @tags fileCategory + * @name GetFileCategorys + * @request GET:/api/v1/fileCategorys + */ + export namespace GetFileCategorys { + export type RequestParams = {}; + export type RequestQuery = { + /** + * 分类名称 + * @example "风景" + */ + name?: 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; + /** + * 创建起始事件 + * @example "2020-02-02 02:02:02" + */ + createdFrom?: string; + }; + export type RequestBody = never; + export type RequestHeaders = {}; + export type ResponseBody = { + /** + * 状态码 + * @format int32 + * @example 2000 + */ + code: number; + /** + * 提示信息 + * @example "请求成功" + */ + message: string; + data?: FileCategory[]; + }; + } + /** + * @description 获取文件分类 + * @tags fileCategory + * @name GetFileCategory + * @request GET:/api/v1/fileCategorys/{id} + */ + export namespace GetFileCategory { + export type RequestParams = { + id: number; + }; + export type RequestQuery = {}; + export type RequestBody = never; + export type RequestHeaders = {}; + export type ResponseBody = { + /** + * 状态码 + * @format int32 + * @example 2000 + */ + code: number; + /** + * 提示信息 + * @example "请求成功" + */ + message: string; + data?: FileCategory; + }; + } + /** + * @description 更新文件分类 + * @tags fileCategory + * @name SetFileCategory + * @request PATCH:/api/v1/fileCategorys/{id} + */ + export namespace SetFileCategory { + export type RequestParams = { + id: number; + }; + export type RequestQuery = {}; + export type RequestBody = UpdateFileCategoryDto; + export type RequestHeaders = {}; + export type ResponseBody = Response; + } + /** + * @description 删除文件分类 + * @tags fileCategory + * @name DelFileCategory + * @request DELETE:/api/v1/fileCategorys/{id} + */ + export namespace DelFileCategory { + export type RequestParams = { + id: number; + }; + export type RequestQuery = {}; + export type RequestBody = never; + export type RequestHeaders = {}; + export type ResponseBody = Response; + } +} + export namespace Post { /** * @description 创建文章 @@ -3183,10 +3559,61 @@ export class Api extends HttpClient { - return this.request({ + getFiles: (query: GetFilesParams, params: RequestParams = {}) => { + return this.request< + { + /** + * 状态码 + * @format int32 + * @example 2000 + */ + code: number; + /** + * 提示信息 + * @example "请求成功" + */ + message: string; + data?: object; + }, + any + >({ path: `/api/v1/file`, method: "GET", + query: query, + format: "json", + ...params, + }); + }, + + /** + * 批量删除文件 + * + * @tags file + * @name DelFiles + * @request DELETE:/api/v1/file + */ + delFiles: (data: string[], params: RequestParams = {}) => { + return this.request< + { + /** + * 状态码 + * @format int32 + * @example 2000 + */ + code: number; + /** + * 提示信息 + * @example "请求成功" + */ + message: string; + data?: object; + }, + any + >({ + path: `/api/v1/file`, + method: "DELETE", + body: data, + type: ContentType.Json, format: "json", ...params, }); @@ -3232,23 +3659,7 @@ export class Api extends HttpClient { - return this.request< - { - /** - * 状态码 - * @format int32 - * @example 2000 - */ - code: number; - /** - * 提示信息 - * @example "请求成功" - */ - message: string; - data?: string; - }, - any - >({ + return this.request({ path: `/api/v1/file/${id}`, method: "PATCH", body: data, @@ -3306,6 +3717,140 @@ export class Api extends HttpClient { + return this.request< + { + /** + * 状态码 + * @format int32 + * @example 2000 + */ + code: number; + /** + * 提示信息 + * @example "请求成功" + */ + message: string; + data?: number; + }, + any + >({ + path: `/api/v1/fileCategorys`, + method: "POST", + body: data, + type: ContentType.Json, + format: "json", + ...params, + }); + }, + + /** + * 查询文件分类 + * + * @tags fileCategory + * @name GetFileCategorys + * @request GET:/api/v1/fileCategorys + */ + getFileCategorys: (query: GetFileCategorysParams, params: RequestParams = {}) => { + return this.request< + { + /** + * 状态码 + * @format int32 + * @example 2000 + */ + code: number; + /** + * 提示信息 + * @example "请求成功" + */ + message: string; + data?: FileCategory[]; + }, + any + >({ + path: `/api/v1/fileCategorys`, + method: "GET", + query: query, + format: "json", + ...params, + }); + }, + + /** + * 获取文件分类 + * + * @tags fileCategory + * @name GetFileCategory + * @request GET:/api/v1/fileCategorys/{id} + */ + getFileCategory: (id: number, params: RequestParams = {}) => { + return this.request< + { + /** + * 状态码 + * @format int32 + * @example 2000 + */ + code: number; + /** + * 提示信息 + * @example "请求成功" + */ + message: string; + data?: FileCategory; + }, + any + >({ + path: `/api/v1/fileCategorys/${id}`, + method: "GET", + format: "json", + ...params, + }); + }, + + /** + * 更新文件分类 + * + * @tags fileCategory + * @name SetFileCategory + * @request PATCH:/api/v1/fileCategorys/{id} + */ + setFileCategory: (id: number, data: UpdateFileCategoryDto, params: RequestParams = {}) => { + return this.request({ + path: `/api/v1/fileCategorys/${id}`, + method: "PATCH", + body: data, + type: ContentType.Json, + format: "json", + ...params, + }); + }, + + /** + * 删除文件分类 + * + * @tags fileCategory + * @name DelFileCategory + * @request DELETE:/api/v1/fileCategorys/{id} + */ + delFileCategory: (id: number, params: RequestParams = {}) => { + return this.request({ + path: `/api/v1/fileCategorys/${id}`, + method: "DELETE", + format: "json", + ...params, + }); + }, + }; post = { /** * 创建文章 diff --git a/src/components/form/form-config.ts b/src/components/form/form-config.ts index 972ae2a..171011b 100644 --- a/src/components/form/form-config.ts +++ b/src/components/form/form-config.ts @@ -75,3 +75,32 @@ export const config = { return data; }, }; + +export function initOptions({ item, model }: any, key = "options") { + if (Array.isArray(item.options)) { + item.nodeProps[key] = item.options; + } + if (item.options && typeof item.options === "object") { + const { value, source } = item.options; + item._updateOptions = async () => {}; + } + if (typeof item.options === "function") { + const loadData = item.options; + item.nodeProps[key] = reactive([]); + item._updateOptions = async () => { + let data = await loadData({ item, model }); + if (Array.isArray(data?.data?.data)) { + data = data.data.data.map((i: any) => ({ + ...i, + label: i.name, + value: i.id, + })); + } + if (Array.isArray(data)) { + item.nodeProps[key].splice(0); + item.nodeProps[key].push(...data); + } + }; + item._updateOptions(); + } +} diff --git a/src/components/form/form-node.tsx b/src/components/form/form-node.tsx index 020d7d8..2b0eb5f 100644 --- a/src/components/form/form-node.tsx +++ b/src/components/form/form-node.tsx @@ -16,27 +16,7 @@ import { TimePicker, TreeSelect, } from "@arco-design/web-vue"; - -const initOptions = ({ item, model }: any, key = "options") => { - if (Array.isArray(item.options)) { - item.nodeProps[key] = item.options; - } - if (typeof item.options === "function") { - const loadData = item.options; - item.nodeProps[key] = reactive([]); - item._updateOptions = async () => { - let data = await loadData({ item, model }); - if (Array.isArray(data?.data?.data)) { - data = data.data.data.map((i: any) => ({ label: i.name, value: i.id })); - } - if (Array.isArray(data)) { - item.nodeProps[key].splice(0); - item.nodeProps[key].push(...data); - } - }; - item._updateOptions(); - } -}; +import { initOptions } from "./form-config"; /** * 表单项组件映射 @@ -114,7 +94,10 @@ export const nodeMap = { placeholder: "请选择", allowClear: true, allowSearch: true, - options: [{}], + options: [], + onChange(value) { + value; + }, } as InstanceType["$props"], init: (arg: any) => initOptions(arg, "data"), }, diff --git a/src/components/table/table.tsx b/src/components/table/table.tsx index d4690ad..8a60657 100644 --- a/src/components/table/table.tsx +++ b/src/components/table/table.tsx @@ -1,11 +1,6 @@ -import { - TableColumnData as BaseColumn, - TableData as BaseData, - Table as BaseTable, - Message, -} from "@arco-design/web-vue"; +import { TableColumnData as BaseColumn, TableData as BaseData, Table as BaseTable } from "@arco-design/web-vue"; import { merge } from "lodash-es"; -import { PropType, computed, defineComponent, reactive, ref, watch } from "vue"; +import { PropType, computed, defineComponent, reactive, ref } from "vue"; import { Form, FormInstance, FormModal, FormModalInstance, FormModalProps, FormProps } from "../form"; import { config } from "./table.config"; @@ -72,7 +67,7 @@ export const Table = defineComponent({ }, setup(props) { const loading = ref(false); - const tableRef = ref>() + const tableRef = ref>(); const searchRef = ref(); const createRef = ref(); const modifyRef = ref(); @@ -81,10 +76,16 @@ export const Table = defineComponent({ const reloadData = () => loadData({ current: 1, pageSize: 10 }); const openModifyModal = (data: any) => modifyRef.value?.open(data); + /** + * 加载数据 + * @param pagination 自定义分页 + */ const loadData = async (pagination: Partial = {}) => { const merged = { ...props.pagination, ...pagination }; const paging = { page: merged.current, size: merged.pageSize }; const model = searchRef.value?.getModel() ?? {}; + + // 本地加载 if (Array.isArray(props.data)) { const filters = Object.entries(model); const data = props.data.filter((item) => { @@ -99,6 +100,8 @@ export const Table = defineComponent({ props.pagination.total = renderData.value.length; props.pagination.current = 1; } + + // 远程加载 if (typeof props.data === "function") { try { loading.value = true; @@ -107,30 +110,19 @@ export const Table = defineComponent({ renderData.value = data; props.pagination.total = total; props.pagination.current = paging.page; - } catch (error) { - const message = config.getApiErrorMessage(error); - if (message) { - Message.error(`提示:${message}`); - } } finally { loading.value = false; } } }; - watch( - () => props.data, - (data) => { - if (Array.isArray(data)) { - renderData.value = data; - props.pagination.total = data.length; - props.pagination.current = 1; - } - }, - { - immediate: true, + watchEffect(() => { + if (Array.isArray(props.data)) { + renderData.value = props.data; + props.pagination.total = props.data.length; + props.pagination.current = 1; } - ); + }); onMounted(() => { loadData(); diff --git a/src/components/table/use-table.tsx b/src/components/table/use-table.tsx index ce727c7..6629970 100644 --- a/src/components/table/use-table.tsx +++ b/src/components/table/use-table.tsx @@ -248,12 +248,39 @@ export const useAniTable = (options: UseTableOptions): TableReturnType => { props, tableRef, refresh: () => tableRef.value?.reloadData(), + getTableInstance() { + return tableRef.value?.tableRef; + }, + getSearchInstance() { + return tableRef.value?.searchRef; + }, + getCreateInstance() { + return tableRef.value?.createRef; + }, + /** + * 获取创建表单组件实例 + */ + getCreateFormInstance() { + return this.getCreateInstance()?.formRef; + }, + /** + * 获取修改表单弹窗组件实例 + */ + getModifyInstance() { + return tableRef.value?.modifyRef; + }, + /** + * 获取修改表单组件实例 + */ + getModifyFormInstance() { + return this.getModifyInstance()?.formRef; + }, }; const aniTable = defineComponent({ name: "AniTableWrapper", - setup() { + setup(p, { slots }) { const onRef = (el: TableInstance) => (tableRef.value = el); - return () =>
; + return () => {slots}
; }, }); return [aniTable, context]; diff --git a/src/pages/post/media/components/group.vue b/src/pages/post/media/components/group.vue index 4c258b9..2aa1def 100644 --- a/src/pages/post/media/components/group.vue +++ b/src/pages/post/media/components/group.vue @@ -1,8 +1,8 @@