feat: 优化表单组件取值/获值功能

master
luoer 2023-08-08 11:32:30 +08:00
parent dc178f5b60
commit 8a2b29ef01
15 changed files with 180 additions and 89 deletions

2
.env
View File

@ -2,7 +2,7 @@
# 应用配置
# =====================================================================================
# 网站标题
VITE_TITLE = 绝弹中心
VITE_TITLE = 绝弹管理系统
# 网站副标题
VITE_SUBTITLE = 快速开发web应用的模板工具
# API接口前缀参见 axios 的 baseURL

View File

@ -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;
};

View File

@ -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[];
}

View File

@ -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;
},
};

View File

@ -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) {

View File

@ -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 () => {

View File

@ -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 = () => {

View File

@ -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;

View File

@ -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;

View File

@ -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">
上传者

View File

@ -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: {

View File

@ -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);
},

View File

@ -123,7 +123,7 @@ const table = useTable({
type: "select",
},
{
field: "startTime:endTime",
field: "[startTime,endTime]",
label: "日期范围",
type: "dateRange",
nodeProps: {},

View File

@ -1,7 +0,0 @@
import { Message } from "@arco-design/web-vue";
export const message = {
warning() {
Message.warning(``)
}
}

31
src/utils/modal.ts Normal file
View File

@ -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);
});
},
};