feat: 优化表单组件取值/获值功能
parent
dc178f5b60
commit
8a2b29ef01
2
.env
2
.env
|
|
@ -2,7 +2,7 @@
|
|||
# 应用配置
|
||||
# =====================================================================================
|
||||
# 网站标题
|
||||
VITE_TITLE = 绝弹中心
|
||||
VITE_TITLE = 绝弹管理系统
|
||||
# 网站副标题
|
||||
VITE_SUBTITLE = 快速开发web应用的模板工具
|
||||
# API接口前缀:参见 axios 的 baseURL
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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[];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<string, any>) {
|
||||
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;
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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<string, any> }) => boolean;
|
||||
};
|
||||
|
||||
export type FieldRuleType = FieldStringRule | FieldObjectRule;
|
||||
|
||||
/**
|
||||
|
|
@ -56,7 +54,7 @@ export const FormItem = (props: any, { emit }: any) => {
|
|||
}
|
||||
|
||||
return (
|
||||
<BaseFormItem rules={rules.value} disabled={disabled.value} field={item.field} {...item.itemProps}>
|
||||
<BaseFormItem {...item.itemProps} rules={rules.value} disabled={disabled.value} field={item.field}>
|
||||
{{
|
||||
default: () => {
|
||||
if (item.component) {
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ export const FormModal = defineComponent({
|
|||
* 表单数据
|
||||
*/
|
||||
model: {
|
||||
type: Object as PropType<Record<string, any>>,
|
||||
type: Object as PropType<Record<any, any>>,
|
||||
required: true,
|
||||
},
|
||||
/**
|
||||
|
|
@ -81,9 +81,7 @@ export const FormModal = defineComponent({
|
|||
const open = async (data: Record<string, any> = {}) => {
|
||||
visible.value = true;
|
||||
await nextTick();
|
||||
for (const key in data) {
|
||||
props.model[key] = data[key];
|
||||
}
|
||||
config.setModel(props.model, data);
|
||||
};
|
||||
|
||||
const onBeforeOk = async () => {
|
||||
|
|
|
|||
|
|
@ -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<string, any>; items: IFormItem[] }) => Promise<any>;
|
||||
|
||||
|
|
@ -16,7 +17,7 @@ export const Form = defineComponent({
|
|||
* 表单数据
|
||||
*/
|
||||
model: {
|
||||
type: Object as PropType<Record<string, any>>,
|
||||
type: Object as PropType<Record<any, any>>,
|
||||
default: () => reactive({}),
|
||||
},
|
||||
/**
|
||||
|
|
@ -55,30 +56,11 @@ export const Form = defineComponent({
|
|||
};
|
||||
|
||||
const getModel = () => {
|
||||
const model: Record<string, any> = {};
|
||||
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<string, any>) => {
|
||||
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<string, any>) => {
|
||||
config.setModel(props.model, data);
|
||||
};
|
||||
|
||||
const resetModel = () => {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
<ACheckbox></ACheckbox>
|
||||
<AInput class="inline-block w-80" placeholder="输入名称关键字"></AInput>
|
||||
</div>
|
||||
<div class="flex items-center gap-4">
|
||||
<div class="flex items-center gap-4 text-gray-500">
|
||||
<ADropdown>
|
||||
<span class="cursor-pointer">
|
||||
上传者
|
||||
|
|
|
|||
|
|
@ -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: {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ const table = useTable({
|
|||
type: "select",
|
||||
},
|
||||
{
|
||||
field: "startTime:endTime",
|
||||
field: "[startTime,endTime]",
|
||||
label: "日期范围",
|
||||
type: "dateRange",
|
||||
nodeProps: {},
|
||||
|
|
|
|||
|
|
@ -1,7 +0,0 @@
|
|||
import { Message } from "@arco-design/web-vue";
|
||||
|
||||
export const message = {
|
||||
warning() {
|
||||
Message.warning(``)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
import { Modal, ModalConfig } from "@arco-design/web-vue";
|
||||
import { merge } from "lodash-es";
|
||||
|
||||
export const modal = {
|
||||
delConfirm(config: Partial<Omit<ModalConfig, "onOk" | "onCancel">> = {}) {
|
||||
return new Promise<void>((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);
|
||||
});
|
||||
},
|
||||
};
|
||||
Loading…
Reference in New Issue