diff --git a/.env b/.env index 5556db0..ac23070 100644 --- a/.env +++ b/.env @@ -2,7 +2,7 @@ # 应用配置 # ===================================================================================== # 网站标题 -VITE_TITLE = 绝弹中心 +VITE_TITLE = 绝弹管理系统 # 网站副标题 VITE_SUBTITLE = 快速开发web应用的模板工具 # API接口前缀:参见 axios 的 baseURL diff --git a/scripts/openapi/index.ts b/scripts/openapi/index.ts index b43993b..299678b 100644 --- a/scripts/openapi/index.ts +++ b/scripts/openapi/index.ts @@ -55,7 +55,7 @@ const run = async () => { // ] // for (const file of files) { // createFile(file) - // } + // } pathParams, queryParams, bodyParams, headerParams, formDataParams, responses, method, url debugger return output; }; diff --git a/src/api/service/Api.ts b/src/api/service/Api.ts index e78b1c6..0001dbb 100644 --- a/src/api/service/Api.ts +++ b/src/api/service/Api.ts @@ -63,6 +63,7 @@ export interface User { * @example "example@mail.com" */ email: string; + /** 用户角色ID */ roleIds: number[]; } @@ -196,6 +197,7 @@ export interface LoginedUserVo { * @example "example@mail.com" */ email: string; + /** 用户角色ID */ roleIds: number[]; } diff --git a/src/components/form/form-config.ts b/src/components/form/form-config.ts index 153d7b4..972ae2a 100644 --- a/src/components/form/form-config.ts +++ b/src/components/form/form-config.ts @@ -1,5 +1,77 @@ export const config = { + /** + * 获取API错误信息 + */ getApiErrorMessage(error: any) { return error?.response?.data?.message || error?.message || "Error"; }, + /** + * 设置表单数据 + */ + setModel: function setModel(model: any, data: any) { + for (const key of Object.keys(model)) { + // 数组类型 + if (/^\[.+\]$/.test(key)) { + const subkeysStr = key.replaceAll(/\s/g, "").match(/^\[(.+)\]$/)?.[1]; + if (!subkeysStr) { + model[key] = data[key]; + continue; + } + const subkeys = subkeysStr.split(","); + const value = new Array(subkeys.length); + subkeys.forEach((subkey, index) => { + if (/.+:number$/.test(subkey)) { + subkey = subkey.replace(/:number$/, ""); + value[index] = Number(data[subkey]); + return; + } + if (/.+:boolean$/.test(subkey)) { + subkey = subkey.replace(/:boolean$/, ""); + value[index] = Boolean(data[subkey]); + return; + } + value[index] = data[subkey]; + }); + model[key] = value; + continue; + } + // 默认类型 + model[key] = data[key]; + } + return model; + }, + /** + * 获取表单数据 + */ + getModel: function getModel(model: Record) { + const data: any = {}; + for (const [key, val] of Object.entries(model)) { + // 数组类型 + if (/^\[.+\]$/.test(key)) { + const subkeysStr = key.replaceAll(/\s/g, "").match(/^\[(.+)\]$/)?.[1]; + if (!subkeysStr) { + data[key] = val; + continue; + } + const subkeys = subkeysStr.split(","); + subkeys.forEach((subkey, index) => { + if (/(.+)?:number$/.test(subkey)) { + subkey = subkey.replace(/:number$/, ""); + data[subkey] = val?.[index] && Number(val[index]); + return; + } + if (/(.+)?:boolean$/.test(subkey)) { + subkey = subkey.replace(/:boolean$/, ""); + data[subkey] = val?.[index] && Boolean(val[index]); + return; + } + data[subkey] = val?.[index]; + }); + continue; + } + // 默认类型 + data[key] = val; + } + return data; + }, }; diff --git a/src/components/form/form-item.tsx b/src/components/form/form-item.tsx index aa974f9..96000fd 100644 --- a/src/components/form/form-item.tsx +++ b/src/components/form/form-item.tsx @@ -3,11 +3,9 @@ import { NodeType, NodeUnion, nodeMap } from "./form-node"; import { RuleMap } from "./form-rules"; export type FieldStringRule = keyof typeof RuleMap; - export type FieldObjectRule = FieldRule & { disable?: (arg: { item: IFormItem; model: Record }) => boolean; }; - export type FieldRuleType = FieldStringRule | FieldObjectRule; /** @@ -56,7 +54,7 @@ export const FormItem = (props: any, { emit }: any) => { } return ( - + {{ default: () => { if (item.component) { diff --git a/src/components/form/form-modal.tsx b/src/components/form/form-modal.tsx index 2733f97..f594861 100644 --- a/src/components/form/form-modal.tsx +++ b/src/components/form/form-modal.tsx @@ -46,7 +46,7 @@ export const FormModal = defineComponent({ * 表单数据 */ model: { - type: Object as PropType>, + type: Object as PropType>, required: true, }, /** @@ -81,9 +81,7 @@ export const FormModal = defineComponent({ const open = async (data: Record = {}) => { visible.value = true; await nextTick(); - for (const key in data) { - props.model[key] = data[key]; - } + config.setModel(props.model, data); }; const onBeforeOk = async () => { diff --git a/src/components/form/form.tsx b/src/components/form/form.tsx index 5d3c877..b215a51 100644 --- a/src/components/form/form.tsx +++ b/src/components/form/form.tsx @@ -3,6 +3,7 @@ import { assign, cloneDeep, defaultsDeep } from "lodash-es"; import { PropType } from "vue"; import { FormItem, IFormItem } from "./form-item"; import { NodeType, nodeMap } from "./form-node"; +import { config } from "./form-config"; type SubmitFn = (arg: { model: Record; items: IFormItem[] }) => Promise; @@ -16,7 +17,7 @@ export const Form = defineComponent({ * 表单数据 */ model: { - type: Object as PropType>, + type: Object as PropType>, default: () => reactive({}), }, /** @@ -55,30 +56,11 @@ export const Form = defineComponent({ }; const getModel = () => { - const model: Record = {}; - for (const key of Object.keys(props.model)) { - if (/[^:]+:[^:]+/.test(key)) { - const keys = key.split(":"); - const vals = cloneDeep(props.model[key] || []); - for (const k of keys) { - model[k] = vals.shift(); - } - } else { - model[key] = cloneDeep(props.model[key]); - } - } - return model; + return config.getModel(props.model); }; - const setModel = (model: Record) => { - for (const key of Object.keys(props.model)) { - if (/[^:]+:[^:]+/.test(key)) { - const [key1, key2] = key.split(":"); - props.model[key] = [model[key1], model[key2]]; - } else { - props.model[key] = model[key]; - } - } + const setModel = (data: Record) => { + config.setModel(props.model, data); }; const resetModel = () => { diff --git a/src/components/form/use-form.tsx b/src/components/form/use-form.tsx index 498b86c..0fd1fa8 100644 --- a/src/components/form/use-form.tsx +++ b/src/components/form/use-form.tsx @@ -1,5 +1,6 @@ import { FormInstance } from "@arco-design/web-vue"; import { IFormItem } from "./form-item"; +import { merge } from "lodash-es"; export type Options = { /** @@ -28,34 +29,29 @@ export const useForm = (options: Options) => { const { model = { id: undefined } } = options; const items: IFormItem[] = []; - options.items.forEach((item) => { + for (const item of options.items) { if (!item.nodeProps) { item.nodeProps = {} as any; } - if (/(.+)\?(.+)/.test(item.field)) { - const [field, condition] = item.field.split("?"); - model[field] = item.initial ?? model[item.field]; - const params = new URLSearchParams(condition); - for (const [key, value] of params.entries()) { - model[key] = value; - } - } model[item.field] = model[item.field] ?? item.initial; - const _item = { ...item }; - items.push(_item); - }); + items.push(item); + } if (options.submit) { - const submit = items.find((item) => item.type === "submit"); - if (!submit) { - items.push({ - field: "id", - type: "submit", - itemProps: { - hideLabel: true, + const submit = items.find((item) => item.type === "submit") || {}; + items.push( + merge( + {}, + { + field: "id", + type: "submit", + itemProps: { + hideLabel: true, + }, }, - }); - } + submit + ) as any + ); } return reactive({ ...options, model, items }) as any; diff --git a/src/components/table/use-table.tsx b/src/components/table/use-table.tsx index ccab7f2..d44695a 100644 --- a/src/components/table/use-table.tsx +++ b/src/components/table/use-table.tsx @@ -1,10 +1,11 @@ -import { Link, Message, Modal, TableColumnData } from "@arco-design/web-vue"; -import { defaultsDeep, isArray, isFunction, merge } from "lodash-es"; +import { Link, Message, TableColumnData } from "@arco-design/web-vue"; +import { defaultsDeep, isArray, merge } from "lodash-es"; import { reactive } from "vue"; import { useFormModal } from "../form"; import { TableInstance } from "./table"; import { config } from "./table.config"; import { UseTableOptions } from "./use-interface"; +import { modal } from "@/utils/modal"; /** * 表格组件hook @@ -43,25 +44,21 @@ export const useTable = (optionsOrFn: UseTableOptions | (() => UseTableOptions)) column.buttons = column.buttons?.map((action) => { let onClick = action?.onClick; if (action.type === "delete") { - onClick = (data) => { - Modal.warning({ - ...config.columnButtonDelete, - onOk: async () => { - try { - const resData: any = await action?.onClick?.(data); - const message = resData?.data?.message; - if (message) { - Message.success(`提示:${message}`); - } - getTable()?.loadData(); - } catch (error: any) { - const message = error.response?.data?.message; - if (message) { - Message.warning(`提示:${message}`); - } - } - }, - }); + onClick = async (data) => { + await modal.delConfirm(); + try { + const resData: any = await action?.onClick?.(data); + const message = resData?.data?.message; + if (message) { + Message.success(`提示:${message}`); + } + getTable()?.loadData(); + } catch (error: any) { + const message = error.response?.data?.message; + if (message) { + Message.warning(`提示:${message}`); + } + } }; } return { ...config.columnButtonBase, ...action, onClick } as any; diff --git a/src/pages/demo/test.vue b/src/pages/demo/test.vue index f255602..1cd23e6 100644 --- a/src/pages/demo/test.vue +++ b/src/pages/demo/test.vue @@ -8,7 +8,7 @@ -
+
上传者 diff --git a/src/pages/post/index.vue b/src/pages/post/index.vue index 09a57c1..8031b27 100644 --- a/src/pages/post/index.vue +++ b/src/pages/post/index.vue @@ -35,19 +35,26 @@ const table = useTable({ { title: "操作", type: "button", - width: 70, + width: 136, buttons: [ { type: "modify", text: "修改", }, + { + type: "delete", + text: "删除", + onClick: ({ record }) => { + return api.post.delPost(record.id); + }, + }, ], }, ], search: { items: [ { - extend: "name", + extend: "title", required: false, }, ], @@ -85,7 +92,7 @@ const table = useTable({ }, ], submit: ({ model }) => { - return api.role.addRole(model); + return api.post.addPost(model); }, }, modify: { diff --git a/src/pages/role/index.vue b/src/pages/role/index.vue index 58c5187..c39f0ea 100644 --- a/src/pages/role/index.vue +++ b/src/pages/role/index.vue @@ -43,12 +43,25 @@ const table = useTable({ { title: "操作", type: "button", - width: 70, + width: 184, buttons: [ { type: "modify", text: "修改", }, + { + text: '分配权限', + onClick: ({ record }) => { + console.log(record); + }, + }, + { + text: "删除", + type: "delete", + onClick: ({ record }) => { + return api.role.delRole(record.id); + }, + } ], }, ], @@ -88,12 +101,14 @@ const table = useTable({ }, { field: "permissions", - label: "关联角色", + label: "关联权限", type: "select", options: () => api.permission.getPermissions(), + nodeProps: { + multiple: true, + }, }, ], - submit: ({ model }) => { return api.role.addRole(model); }, diff --git a/src/pages/user/index.vue b/src/pages/user/index.vue index 2eba217..e578f1f 100644 --- a/src/pages/user/index.vue +++ b/src/pages/user/index.vue @@ -123,7 +123,7 @@ const table = useTable({ type: "select", }, { - field: "startTime:endTime", + field: "[startTime,endTime]", label: "日期范围", type: "dateRange", nodeProps: {}, diff --git a/src/utils/message.ts b/src/utils/message.ts deleted file mode 100644 index 9508226..0000000 --- a/src/utils/message.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { Message } from "@arco-design/web-vue"; - -export const message = { - warning() { - Message.warning(``) - } -} \ No newline at end of file diff --git a/src/utils/modal.ts b/src/utils/modal.ts new file mode 100644 index 0000000..9c9713d --- /dev/null +++ b/src/utils/modal.ts @@ -0,0 +1,31 @@ +import { Modal, ModalConfig } from "@arco-design/web-vue"; +import { merge } from "lodash-es"; + +export const modal = { + delConfirm(config: Partial> = {}) { + return new Promise((resolve, reject) => { + const mergedConfig = merge( + { + title: "提示", + titleAlign: "start", + width: 432, + content: "确定删除该数据吗?注意:该操作不可恢复!", + maskClosable: false, + closable: false, + okText: "确定删除", + okButtonProps: { + status: "danger", + }, + onOk: () => { + resolve(); + }, + onCancel: () => { + reject(); + }, + }, + config + ); + Modal.open(mergedConfig); + }); + }, +};