feat: 临时提交
parent
71baafecc7
commit
51e287c747
|
|
@ -0,0 +1,79 @@
|
|||
import { Form as BaseForm, FormInstance as BaseFormInstance } from "@arco-design/web-vue";
|
||||
import { PropType } from "vue";
|
||||
import { FormContextKey } from "../core/interface";
|
||||
import { useFormItems } from "../core/useFormItems";
|
||||
import { useFormModel } from "../core/useFormModel";
|
||||
import { useFormRef } from "../core/useFormRef";
|
||||
import { useFormSubmit } from "../core/useFormSubmit";
|
||||
import { AnFormItem, IAnFormItem } from "./FormItem";
|
||||
import { SubmitFn } from "./types/Form";
|
||||
|
||||
/**
|
||||
* 表单组件
|
||||
*/
|
||||
export const AnForm = defineComponent({
|
||||
name: "AnForm",
|
||||
props: {
|
||||
/**
|
||||
* 表单数据
|
||||
*/
|
||||
model: {
|
||||
type: Object as PropType<Recordable>,
|
||||
required: true,
|
||||
},
|
||||
/**
|
||||
* 表单项
|
||||
*/
|
||||
items: {
|
||||
type: Array as PropType<IAnFormItem[]>,
|
||||
default: () => [],
|
||||
},
|
||||
/**
|
||||
* 提交表单
|
||||
*/
|
||||
submit: {
|
||||
type: Function as PropType<SubmitFn>,
|
||||
},
|
||||
/**
|
||||
* 传给Form组件的参数
|
||||
*/
|
||||
formProps: {
|
||||
type: Object as PropType<Omit<BaseFormInstance["$props"], "model">>,
|
||||
},
|
||||
},
|
||||
setup(props, { slots }) {
|
||||
const model = computed(() => props.model);
|
||||
const items = computed(() => props.items);
|
||||
const submit = computed(() => props.submit);
|
||||
const formRefes = useFormRef();
|
||||
const formModel = useFormModel(model);
|
||||
const formItems = useFormItems(items, model);
|
||||
const formSubmit = useFormSubmit({ items, model, validate: formRefes.validate, submit });
|
||||
|
||||
const context = {
|
||||
slots,
|
||||
...formModel,
|
||||
...formItems,
|
||||
...formRefes,
|
||||
...formSubmit,
|
||||
};
|
||||
|
||||
provide(FormContextKey, context);
|
||||
return context;
|
||||
},
|
||||
render() {
|
||||
return (
|
||||
<BaseForm layout="vertical" {...this.$attrs} {...this.formProps} ref="formRef" model={this.model}>
|
||||
{this.items.map((item) => (
|
||||
<AnFormItem item={item} items={this.items} model={this.model}></AnFormItem>
|
||||
))}
|
||||
</BaseForm>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
export type AnFormInstance = InstanceType<typeof AnForm>;
|
||||
|
||||
export type AnFormProps = AnFormInstance["$props"];
|
||||
|
||||
export type IAnForm = Pick<AnFormProps, "model" | "items" | "submit" | "formProps">;
|
||||
|
|
@ -0,0 +1,143 @@
|
|||
import { FormItem as BaseFormItem, FormItemInstance } from "@arco-design/web-vue";
|
||||
import { isFunction } from "lodash-es";
|
||||
import { PropType } from "vue";
|
||||
import { FieldObjectRule } from "../hooks/useRules";
|
||||
import { NodeType, NodeUnion, nodeMap } from "../nodes";
|
||||
import { strOrFnRender } from "../utils/strOrFnRender";
|
||||
|
||||
/**
|
||||
* 表单项
|
||||
*/
|
||||
export const AnFormItem = defineComponent({
|
||||
name: "AnFormItem",
|
||||
props: {
|
||||
/**
|
||||
* 表单项
|
||||
*/
|
||||
item: {
|
||||
type: Object as PropType<IAnFormItem>,
|
||||
required: true,
|
||||
},
|
||||
/**
|
||||
* 表单项数组
|
||||
*/
|
||||
items: {
|
||||
type: Array as PropType<IAnFormItem[]>,
|
||||
required: true,
|
||||
},
|
||||
/**
|
||||
* 表单数据
|
||||
*/
|
||||
model: {
|
||||
type: Object as PropType<Recordable>,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
/**
|
||||
* 校验规则
|
||||
*/
|
||||
const rules = computed(() => props.item.rules?.filter((i) => !i.disable?.(props)));
|
||||
|
||||
/**
|
||||
* 是否禁用
|
||||
*/
|
||||
const disabled = computed(() => Boolean(props.item.disable?.(props)));
|
||||
|
||||
/**
|
||||
* 渲染函数
|
||||
*/
|
||||
const render = () => {
|
||||
let render = (props.item as any).render;
|
||||
if (!render) {
|
||||
return null;
|
||||
}
|
||||
if (typeof render === "string") {
|
||||
render = nodeMap[render as NodeType]?.render;
|
||||
if (!render) {
|
||||
return null;
|
||||
}
|
||||
return <render {...props.item.nodeProps} v-model={props.model[props.item.field]} />;
|
||||
}
|
||||
if (isFunction(render)) {
|
||||
return <render {...props.item.nodeProps} items={props.items} model={props.model} item={props.item} />;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 标签渲染
|
||||
*/
|
||||
const label = strOrFnRender(props.item.label, props);
|
||||
|
||||
/**
|
||||
* 帮助信息渲染函数
|
||||
*/
|
||||
const help = strOrFnRender(props.item.help, props);
|
||||
|
||||
/**
|
||||
* 额外信息渲染函数
|
||||
*/
|
||||
const extra = strOrFnRender(props.item.extra, props);
|
||||
|
||||
return () => {
|
||||
if (props.item.visible && !props.item.visible(props)) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<BaseFormItem {...props.item.itemProps} rules={rules.value} disabled={disabled.value} field={props.item.field}>
|
||||
{{ default: render, label, help, extra }}
|
||||
</BaseFormItem>
|
||||
);
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
type FormItemFnArg<T = IAnFormItem> = {
|
||||
item: T;
|
||||
items: T[];
|
||||
model: Record<string, any>;
|
||||
};
|
||||
|
||||
type FormItemBase = {
|
||||
/**
|
||||
* 字段名,用于表单、校验和输入框绑定,支持特殊语法。
|
||||
*/
|
||||
field: string;
|
||||
|
||||
/**
|
||||
* 传递给`FormItem`组件的参数
|
||||
*/
|
||||
itemProps?: Partial<Omit<FormItemInstance["$props"], "field" | "label" | "required" | "rules" | "disabled">>;
|
||||
|
||||
/**
|
||||
* 校验规则数组
|
||||
*/
|
||||
rules?: FieldObjectRule<IAnFormItem>[];
|
||||
|
||||
/**
|
||||
* 是否可见
|
||||
*/
|
||||
visible?: (arg: FormItemFnArg) => boolean;
|
||||
|
||||
/**
|
||||
* 是否禁用
|
||||
*/
|
||||
disable?: (arg: FormItemFnArg) => boolean;
|
||||
|
||||
/**
|
||||
* 标签名
|
||||
*/
|
||||
label?: string | ((args: FormItemFnArg) => any);
|
||||
|
||||
/**
|
||||
* 帮助提示
|
||||
*/
|
||||
help?: string | ((args: FormItemFnArg) => any);
|
||||
|
||||
/**
|
||||
* 额外内容
|
||||
*/
|
||||
extra?: string | ((args: FormItemFnArg) => any);
|
||||
};
|
||||
|
||||
export type IAnFormItem = FormItemBase & NodeUnion;
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
import { nodeMap, NodeMap, NodeType } from "../nodes";
|
||||
|
||||
type NodeUnion = {
|
||||
[key in NodeType]: Partial<
|
||||
NodeMap[key] & {
|
||||
/**
|
||||
* 组件类型
|
||||
*/
|
||||
type: key;
|
||||
}
|
||||
>;
|
||||
}[NodeType];
|
||||
|
||||
export { nodeMap };
|
||||
export type { NodeMap, NodeType, NodeUnion };
|
||||
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
import { FieldRule, FormInstance, FormItemInstance, SelectOptionData } from "@arco-design/web-vue";
|
||||
import { InjectionKey, Ref } from "vue";
|
||||
import { NodeUnion } from "../../nodes";
|
||||
|
||||
/**
|
||||
* 函数参数
|
||||
*/
|
||||
export type FormItemFnArg<T = AppFormItem> = {
|
||||
item: T;
|
||||
items: T[];
|
||||
model: Recordable;
|
||||
};
|
||||
|
||||
/**
|
||||
* 表单项基础
|
||||
*/
|
||||
type BaseFormItem = {
|
||||
/**
|
||||
* 传递给`FormItem`组件的参数
|
||||
* @description 部分属性会不可用,如field、label、required、rules、disabled等
|
||||
*/
|
||||
itemProps: Omit<FormItemInstance["$props"], "field" | "label" | "required" | "rules" | "disabled">;
|
||||
|
||||
/**
|
||||
* 是否可见
|
||||
* @description 动态控制表单项是否可见
|
||||
*/
|
||||
visible?: (arg: FormItemFnArg) => boolean;
|
||||
|
||||
/**
|
||||
* 是否禁用
|
||||
* @description 动态控制表单项是否禁用
|
||||
*/
|
||||
disable?: (arg: FormItemFnArg) => boolean;
|
||||
|
||||
/**
|
||||
* 选项,数组或者函数
|
||||
* @description 用于下拉框、单选框、多选框等组件, 支持动态加载
|
||||
*/
|
||||
options?: SelectOptionData[] | ((arg: FormItemFnArg) => PromiseLike<Recordable[]>);
|
||||
};
|
||||
|
||||
/**
|
||||
* 表单项插槽
|
||||
*/
|
||||
type BaseFormItemSlots = {
|
||||
/**
|
||||
* 渲染函数
|
||||
* @description 用于自定义表单项内容
|
||||
*/
|
||||
render: (args: FormItemFnArg) => any;
|
||||
|
||||
/**
|
||||
* 标签名
|
||||
* @description 同FormItem组件的label属性
|
||||
*/
|
||||
label?: string | ((args: FormItemFnArg) => any);
|
||||
|
||||
/**
|
||||
* 帮助提示
|
||||
* @description 同FormItem组件的help插槽
|
||||
*/
|
||||
help?: string | ((args: FormItemFnArg) => any);
|
||||
|
||||
/**
|
||||
* 额外内容
|
||||
* @description 同FormItem组件的extra插槽
|
||||
*/
|
||||
extra?: string | ((args: FormItemFnArg) => any);
|
||||
};
|
||||
|
||||
/**
|
||||
* 表单项校验
|
||||
*/
|
||||
type BaseFormItemRules = {
|
||||
/**
|
||||
* 校验规则
|
||||
* @description 支持字符串(内置)、对象形式
|
||||
*/
|
||||
rules?: FieldRule<AppFormItem>[];
|
||||
};
|
||||
|
||||
/**
|
||||
* 表单项数据
|
||||
*/
|
||||
type BaseFormItemModel = {
|
||||
/**
|
||||
* 字段名,特殊语法在提交时会自动转换。
|
||||
* @example
|
||||
* ```typescript
|
||||
* '[v1,v2]' => { v1: 1, v2: 2 }
|
||||
* ```
|
||||
*/
|
||||
field: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* 表单项
|
||||
*/
|
||||
export type AppFormItem = BaseFormItem & BaseFormItemModel & BaseFormItemRules & BaseFormItemSlots & NodeUnion;
|
||||
|
||||
export type SubmitFn = (arg: { model: Recordable; items: AppFormItem[] }) => PromiseLike<void | { message?: string }>;
|
||||
|
||||
interface FormContext {
|
||||
loading: Ref<boolean>;
|
||||
formRef: Ref<FormInstance | null>;
|
||||
submitForm: () => PromiseLike<any>;
|
||||
}
|
||||
|
||||
export const FormKey = Symbol("AppnifyForm") as InjectionKey<FormContext>;
|
||||
|
|
@ -1,9 +1,14 @@
|
|||
import { InjectionKey } from "vue";
|
||||
import { FormRef } from "./useFormRef";
|
||||
import { FormSubmit } from "./useFormSubmit";
|
||||
import { FormItems } from "./useFormItems";
|
||||
import { FormModel } from "./useFormModel";
|
||||
import { FormRef } from "./useFormRef";
|
||||
import { FormSubmit } from "./useFormSubmit";
|
||||
|
||||
export type FormContextInterface = FormModel & FormItems & FormRef & FormSubmit;
|
||||
export type FormContextInterface = FormModel &
|
||||
FormItems &
|
||||
FormRef &
|
||||
FormSubmit & {
|
||||
slots: Recordable;
|
||||
};
|
||||
|
||||
export const FormContext = Symbol("FormKey") as InjectionKey<FormContextInterface>;
|
||||
export const FormContextKey = Symbol("FormKey") as InjectionKey<FormContextInterface>;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { Ref } from "vue";
|
||||
import { FormItem } from "../hooks/types/FormItem";
|
||||
import { IAnFormItem } from "../components/FormItem";
|
||||
|
||||
export function useFormItems(items: Ref<FormItem[]>, model: Ref<Recordable>) {
|
||||
export function useFormItems(items: Ref<IAnFormItem[]>, model: Ref<Recordable>) {
|
||||
const getItem = (field: string) => {
|
||||
return items.value.find((i) => i.field === field);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import { Ref } from "vue";
|
||||
import { FormItem } from "../hooks/types/FormItem";
|
||||
import { FormInstance, Message } from "@arco-design/web-vue";
|
||||
import { Ref } from "vue";
|
||||
import { IFormItem } from "../components/FormItem";
|
||||
|
||||
interface Options {
|
||||
items: Ref<FormItem[]>;
|
||||
items: Ref<IFormItem[]>;
|
||||
model: Ref<Recordable>;
|
||||
submit: Ref<Function | undefined>;
|
||||
validate: FormInstance["validate"];
|
||||
|
|
|
|||
|
|
@ -1,90 +1,80 @@
|
|||
import { FormItem as BaseFormItem, FormItemInstance, SelectOptionData } from "@arco-design/web-vue";
|
||||
import { NodeUnion, nodeMap } from "./form-node";
|
||||
import { FieldObjectRule, FieldRuleMap, Rule } from "./form-rules";
|
||||
import { PropType } from "vue";
|
||||
import { strOrFnRender } from "./util";
|
||||
import { FormItem as BaseFormItem, FieldRule, FormItemInstance, SelectOptionData } from "@arco-design/web-vue";
|
||||
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;
|
||||
|
||||
/**
|
||||
* 表单项
|
||||
*/
|
||||
export const FormItem = defineComponent({
|
||||
name: "AppnifyFormItem",
|
||||
props: {
|
||||
/**
|
||||
* 表单项
|
||||
*/
|
||||
item: {
|
||||
type: Object as PropType<IFormItem>,
|
||||
required: true,
|
||||
},
|
||||
/**
|
||||
* 表单项数组
|
||||
*/
|
||||
items: {
|
||||
type: Array as PropType<IFormItem[]>,
|
||||
required: true,
|
||||
},
|
||||
/**
|
||||
* 表单数据
|
||||
*/
|
||||
model: {
|
||||
type: Object as PropType<Recordable>,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
/**
|
||||
* 校验规则
|
||||
*/
|
||||
const rules = computed(() => props.item.rules?.filter((i) => !i.disable?.(props)));
|
||||
export const FormItem = (props: any, { emit }: any) => {
|
||||
const { item } = props;
|
||||
const args = {
|
||||
...props,
|
||||
field: item.field,
|
||||
};
|
||||
|
||||
/**
|
||||
* 是否禁用
|
||||
*/
|
||||
const disabled = computed(() => Boolean(props.item.disable?.(props)));
|
||||
|
||||
if (props.item.visible && !props.item.visible(props as any)) {
|
||||
return null;
|
||||
const rules = computed(() => {
|
||||
const result = [];
|
||||
if (item.required) {
|
||||
result.push(RuleMap.required);
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染函数
|
||||
*/
|
||||
const render = () => {
|
||||
const Item = props.item.component as any;
|
||||
if (props.item.type === "custom") {
|
||||
return <Item {...props.item.nodeProps} items={props.items} model={props.model} item={props.item} />;
|
||||
item.rules?.forEach((rule: any) => {
|
||||
if (typeof rule === "string") {
|
||||
result.push(RuleMap[rule as FieldStringRule]);
|
||||
return;
|
||||
}
|
||||
return <Item {...props.item.nodeProps} v-model={props.model[props.item.field]} />;
|
||||
};
|
||||
if (!rule.disable) {
|
||||
result.push(rule);
|
||||
return;
|
||||
}
|
||||
if (!rule.disable({ model: props.model, item, items: props.items })) {
|
||||
result.push(rule);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
});
|
||||
|
||||
/**
|
||||
* 标签渲染
|
||||
*/
|
||||
const label = strOrFnRender(props.item.label, props);
|
||||
const disabled = computed(() => {
|
||||
if (item.disable === undefined) {
|
||||
return false;
|
||||
}
|
||||
if (typeof item.disable === "function") {
|
||||
return item.disable(args);
|
||||
}
|
||||
return item.disable;
|
||||
});
|
||||
|
||||
/**
|
||||
* 帮助信息渲染函数
|
||||
*/
|
||||
const help = strOrFnRender(props.item.help, props);
|
||||
if (item.visible && !item.visible(args)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 额外信息渲染函数
|
||||
*/
|
||||
const extra = strOrFnRender(props.item.extra, props);
|
||||
|
||||
return () => (
|
||||
<BaseFormItem {...props.item.itemProps} rules={rules.value} disabled={disabled.value} field={props.item.field}>
|
||||
{{ default: render, label, help, extra }}
|
||||
</BaseFormItem>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
type FormItemFnArg<T = IFormItem> = {
|
||||
item: T;
|
||||
items: T[];
|
||||
model: Record<string, any>;
|
||||
return (
|
||||
<BaseFormItem {...item.itemProps} rules={rules.value} disabled={disabled.value} field={item.field}>
|
||||
{{
|
||||
default: () => {
|
||||
if (item.component) {
|
||||
return <item.component {...item.nodeProps} model={props.model} item={props.item} />;
|
||||
}
|
||||
const comp = nodeMap[item.type as NodeType]?.component;
|
||||
if (!comp) {
|
||||
return null;
|
||||
}
|
||||
if (item.type === "submit") {
|
||||
return <comp loading={props.loading} onSubmit={() => emit("submit")} onCancel={emit("cancel")} />;
|
||||
}
|
||||
return <comp v-model={props.model[item.field]} {...item.nodeProps} />;
|
||||
},
|
||||
label: item.label && (() => (typeof item.label === "string" ? item.label : item.label?.(args))),
|
||||
help: item.help && (() => (typeof item.help === "string" ? item.help : item.help?.(args))),
|
||||
extra: item.extra && (() => (typeof item.extra === "string" ? item.extra : item.extra?.(args))),
|
||||
}}
|
||||
</BaseFormItem>
|
||||
);
|
||||
};
|
||||
|
||||
type FormItemBase = {
|
||||
|
|
@ -115,7 +105,7 @@ type FormItemBase = {
|
|||
* 标签名
|
||||
* @description 同FormItem组件的label属性
|
||||
*/
|
||||
label?: string | ((args: FormItemFnArg) => any);
|
||||
label?: string | ((args: { item: IFormItem; model: Record<string, any> }) => any);
|
||||
|
||||
/**
|
||||
* 传递给`FormItem`组件的参数
|
||||
|
|
@ -146,45 +136,45 @@ type FormItemBase = {
|
|||
*```
|
||||
* @see https://arco.design/vue/component/form#FieldRule
|
||||
*/
|
||||
rules?: FieldObjectRule<IFormItem>[];
|
||||
rules?: FieldRuleType[];
|
||||
|
||||
/**
|
||||
* 是否可见
|
||||
* @description 动态控制表单项是否可见
|
||||
*/
|
||||
visible?: (arg: FormItemFnArg) => boolean;
|
||||
visible?: (arg: { item: IFormItem; model: Record<string, any> }) => boolean;
|
||||
|
||||
/**
|
||||
* 是否禁用
|
||||
* @description 动态控制表单项是否禁用
|
||||
*/
|
||||
disable?: (arg: FormItemFnArg) => boolean;
|
||||
disable?: (arg: { item: IFormItem; model: Record<string, any> }) => boolean;
|
||||
|
||||
/**
|
||||
* 选项,数组或者函数
|
||||
* @description 用于下拉框、单选框、多选框等组件, 支持动态加载
|
||||
*/
|
||||
options?: SelectOptionData[] | ((arg: FormItemFnArg) => Promise<any>);
|
||||
options?: SelectOptionData[] | ((arg: { item: IFormItem; model: Record<string, any> }) => Promise<any>);
|
||||
|
||||
/**
|
||||
* 表单项内容的渲染函数
|
||||
* @description 用于自定义表单项内容
|
||||
*/
|
||||
component?: (args: FormItemFnArg) => any;
|
||||
component?: (args: { item: IFormItem; model: Record<string, any>; field: string }) => any;
|
||||
|
||||
/**
|
||||
* 帮助提示
|
||||
* @description 同FormItem组件的help插槽
|
||||
* @see https://arco.design/vue/component/form#form-item%20Slots
|
||||
*/
|
||||
help?: string | ((args: FormItemFnArg) => any);
|
||||
help?: string | ((args: { item: IFormItem; model: Record<string, any> }) => any);
|
||||
|
||||
/**
|
||||
* 额外内容
|
||||
* @description 同FormItem组件的extra插槽
|
||||
* @see https://arco.design/vue/component/form#form-item%20Slots
|
||||
*/
|
||||
extra?: string | ((args: FormItemFnArg) => any);
|
||||
extra?: string | ((args: { item: IFormItem; model: Record<string, any> }) => any);
|
||||
};
|
||||
|
||||
export type IFormItem = FormItemBase & NodeUnion;
|
||||
|
|
@ -224,7 +224,7 @@ export type NodeUnion = {
|
|||
/**
|
||||
* 输入框类型,默认为`input`
|
||||
*/
|
||||
type?: key;
|
||||
type: key;
|
||||
/**
|
||||
* 传递给`type`属性对应组件的参数
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
import { FieldRule } from "@arco-design/web-vue";
|
||||
import { isString } from "lodash-es";
|
||||
|
||||
/**
|
||||
* 内置规则
|
||||
*/
|
||||
export const FieldRuleMap = defineRuleMap({
|
||||
const defineRuleMap = <T extends Record<string, FieldRule>>(ruleMap: T) => ruleMap;
|
||||
|
||||
export const RuleMap = defineRuleMap({
|
||||
required: {
|
||||
required: true,
|
||||
message: "该项不能为空",
|
||||
|
|
@ -46,47 +44,3 @@ export const FieldRuleMap = defineRuleMap({
|
|||
message: "至少包含大写字母、小写字母、数字和特殊字符",
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* 字符串形式(枚举)
|
||||
*/
|
||||
export type FieldStringRule = keyof typeof FieldRuleMap;
|
||||
|
||||
/**
|
||||
* 对象形式
|
||||
*/
|
||||
export type FieldObjectRule<T> = FieldRule & {
|
||||
disable?: (arg: { item: T; model: Record<string, any> }) => boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* 完整类型
|
||||
*/
|
||||
export type Rule<T> = FieldStringRule | FieldObjectRule<T>;
|
||||
|
||||
/**
|
||||
* 助手函数(获得TS提示)
|
||||
*/
|
||||
function defineRuleMap<T extends Record<string, FieldRule>>(ruleMap: T) {
|
||||
return ruleMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取表单规则
|
||||
* @param item 表单项
|
||||
* @returns
|
||||
*/
|
||||
export const useFieldRules = <T extends { required?: boolean; rules?: Rule<any>[] }>(item: T) => {
|
||||
const rules: FieldObjectRule<T>[] = [];
|
||||
if (item.required) {
|
||||
rules.push(FieldRuleMap.required);
|
||||
}
|
||||
for (const rule of item.rules ?? []) {
|
||||
if (isString(rule)) {
|
||||
rules.push(FieldRuleMap[rule]);
|
||||
} else {
|
||||
rules.push(rule);
|
||||
}
|
||||
}
|
||||
return rules;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import { Form as BaseForm, FormInstance as BaseFormInstance, Message } from "@arco-design/web-vue";
|
||||
import { assign, cloneDeep, defaultsDeep, merge } from "lodash-es";
|
||||
import { assign, cloneDeep, defaultsDeep } from "lodash-es";
|
||||
import { PropType } from "vue";
|
||||
import { FormItem, IFormItem } from "./form-item";
|
||||
import { nodeMap } from "./form-node";
|
||||
import { config } from "./form-config";
|
||||
import { FormItem, IFormItem } from "./form-item";
|
||||
import { NodeType, nodeMap } from "./form-node";
|
||||
|
||||
type SubmitFn = (arg: { model: Record<string, any>; items: IFormItem[] }) => Promise<any>;
|
||||
|
||||
|
|
@ -11,13 +11,13 @@ type SubmitFn = (arg: { model: Record<string, any>; items: IFormItem[] }) => Pro
|
|||
* 表单组件
|
||||
*/
|
||||
export const Form = defineComponent({
|
||||
name: "AppnifyForm",
|
||||
name: "Form",
|
||||
props: {
|
||||
/**
|
||||
* 表单数据
|
||||
*/
|
||||
model: {
|
||||
type: Object as PropType<Recordable>,
|
||||
type: Object as PropType<Record<any, any>>,
|
||||
default: () => reactive({}),
|
||||
},
|
||||
/**
|
||||
|
|
@ -45,11 +45,11 @@ export const Form = defineComponent({
|
|||
const formRef = ref<InstanceType<typeof BaseForm>>();
|
||||
const loading = ref(false);
|
||||
|
||||
for (const item of props.items) {
|
||||
const node = nodeMap[item.type] as any;
|
||||
props.items.forEach((item: any) => {
|
||||
const node = nodeMap[item.type as NodeType];
|
||||
defaultsDeep(item, { nodeProps: node?.nodeProps ?? {} });
|
||||
(node as any)?.init?.({ item, model: props.model });
|
||||
}
|
||||
});
|
||||
|
||||
const getItem = (field: string) => {
|
||||
return props.items.find((item) => item.field === field);
|
||||
|
|
@ -64,7 +64,7 @@ export const Form = defineComponent({
|
|||
};
|
||||
|
||||
const resetModel = () => {
|
||||
assign(props.model, merge({}, model));
|
||||
assign(props.model, model);
|
||||
};
|
||||
|
||||
const submitForm = async () => {
|
||||
|
|
@ -84,7 +84,7 @@ export const Form = defineComponent({
|
|||
}
|
||||
};
|
||||
|
||||
const injected = {
|
||||
return {
|
||||
formRef,
|
||||
loading,
|
||||
getItem,
|
||||
|
|
@ -93,10 +93,6 @@ export const Form = defineComponent({
|
|||
setModel,
|
||||
getModel,
|
||||
};
|
||||
|
||||
provide("form1", injected);
|
||||
|
||||
return injected;
|
||||
},
|
||||
render() {
|
||||
(this.items as any).instance = this;
|
||||
|
|
@ -108,9 +104,9 @@ export const Form = defineComponent({
|
|||
};
|
||||
|
||||
return (
|
||||
<BaseForm layout="vertical" {...this.$attrs} {...this.formProps} ref="formRef" model={this.model}>
|
||||
<BaseForm ref="formRef" layout="vertical" model={this.model} {...this.$attrs} {...this.formProps}>
|
||||
{this.items.map((item) => (
|
||||
<FormItem item={item} {...props}></FormItem>
|
||||
<FormItem loading={this.loading} onSubmit={this.submitForm} item={item} {...props}></FormItem>
|
||||
))}
|
||||
</BaseForm>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { FormItemInstance, SelectOptionData } from "@arco-design/web-vue";
|
||||
import { NodeType, NodeUnion } from "../../nodes";
|
||||
import { Rule } from "../useRules";
|
||||
import { NodeUnion } from "../../form-node";
|
||||
|
||||
/**
|
||||
* 函数参数
|
||||
|
|
@ -48,7 +48,7 @@ type BaseFormItemSlots = {
|
|||
* 渲染函数
|
||||
* @description 用于自定义表单项内容
|
||||
*/
|
||||
render?: (args: FormItemFnArg) => any;
|
||||
render?: NodeType | ((args: FormItemFnArg) => any);
|
||||
|
||||
/**
|
||||
* 标签名
|
||||
|
|
|
|||
|
|
@ -1,22 +1,19 @@
|
|||
import { useModel } from "./useModel";
|
||||
import { useItems } from "./useItems";
|
||||
import { UseOptions } from "./interface";
|
||||
import { UseForm } from "./types/Form";
|
||||
import { useItems } from "./useItems";
|
||||
|
||||
/**
|
||||
* 构建表单组件的参数
|
||||
*/
|
||||
export const useForm = <T extends UseForm>(options: T) => {
|
||||
const initModel = options.model ?? {};
|
||||
const { items, updateItemOptions } = useItems(options.items ?? [], initModel, Boolean(options.submit));
|
||||
const { model, resetModel, setModel, getModel } = useModel(initModel);
|
||||
export const useForm = (options: UseForm) => {
|
||||
const { model: _model = {}, items: _items = [], submit, formProps: _formProps } = options;
|
||||
const items = ref(useItems(_items, _model, Boolean(options.submit)))
|
||||
const model = ref(_model);
|
||||
const formProps = ref(_formProps);
|
||||
|
||||
return {
|
||||
model,
|
||||
items,
|
||||
resetModel,
|
||||
setModel,
|
||||
getModel,
|
||||
updateItemOptions,
|
||||
submit,
|
||||
formProps,
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,28 +1,28 @@
|
|||
import { merge } from "lodash-es";
|
||||
import { nodeMap } from "../nodes";
|
||||
import { FormItem } from "./types/FormItem";
|
||||
import { nodeMap } from "../form-node";
|
||||
import { useRules } from "./useRules";
|
||||
|
||||
const ITEM: Partial<FormItem> = {
|
||||
type: "input",
|
||||
render: "input",
|
||||
};
|
||||
|
||||
const SUBMIT_ITEM: FormItem = {
|
||||
field: "id",
|
||||
type: "submit",
|
||||
render: "submit",
|
||||
itemProps: {
|
||||
hideLabel: true,
|
||||
},
|
||||
};
|
||||
|
||||
export function useItems(list: FormItem[], model: Recordable, submit: boolean) {
|
||||
const items = ref<FormItem[]>([]);
|
||||
const items = [];
|
||||
let hasSubmit = false;
|
||||
|
||||
for (const item of list) {
|
||||
let target: Recordable = merge({}, nodeMap[item.type ?? "input"]);
|
||||
let target: Recordable = merge({}, nodeMap[typeof item.render === "string" ? item.render : "input"]);
|
||||
|
||||
if (item.type === "submit") {
|
||||
if (item.render === "submit") {
|
||||
target = merge(item, SUBMIT_ITEM);
|
||||
hasSubmit = true;
|
||||
}
|
||||
|
|
@ -31,22 +31,12 @@ export function useItems(list: FormItem[], model: Recordable, submit: boolean) {
|
|||
target.rules = useRules(item);
|
||||
|
||||
model[item.field] = model[item.field] ?? item.initial;
|
||||
items.value.push(target as any);
|
||||
items.push(target as any);
|
||||
}
|
||||
|
||||
if (submit && !hasSubmit) {
|
||||
items.value.push(merge({}, SUBMIT_ITEM));
|
||||
items.push(merge({}, SUBMIT_ITEM));
|
||||
}
|
||||
|
||||
const updateItemOptions = (field: string) => {
|
||||
const item = items.value.find((i) => i.field === field);
|
||||
if (item) {
|
||||
(item as any)._updateOptions?.();
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
items,
|
||||
updateItemOptions,
|
||||
};
|
||||
return items;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { cloneDeep } from "lodash-es";
|
||||
|
||||
function formatModel(model: Recordable) {
|
||||
export function formatModel(model: Recordable) {
|
||||
const data: Recordable = {};
|
||||
for (const [key, val] of Object.entries(model)) {
|
||||
// 数组类型
|
||||
|
|
|
|||
|
|
@ -83,7 +83,9 @@ export const useRules = <T extends { required?: boolean; rules?: Rule<any>[] }>(
|
|||
}
|
||||
for (const rule of item.rules ?? []) {
|
||||
if (isString(rule)) {
|
||||
rules.push(FieldRuleMap[rule]);
|
||||
if (FieldRuleMap[rule]) {
|
||||
rules.push(FieldRuleMap[rule]);
|
||||
}
|
||||
} else {
|
||||
rules.push(rule);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
import { Cascader, CascaderInstance } from "@arco-design/web-vue";
|
||||
import { initOptions } from "../form-config";
|
||||
|
||||
type Props = CascaderInstance["$props"];
|
||||
|
||||
export default {
|
||||
render: Cascader,
|
||||
init: initOptions,
|
||||
nodeProps: {
|
||||
placeholder: "请选择",
|
||||
allowClear: true,
|
||||
expandTrigger: "hover",
|
||||
} as Props,
|
||||
};
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
export default {
|
||||
render: () => {
|
||||
return "1";
|
||||
},
|
||||
nodeProps: {},
|
||||
};
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
import { DatePicker, DatePickerInstance } from "@arco-design/web-vue";
|
||||
|
||||
type Props = DatePickerInstance["$props"];
|
||||
|
||||
export default {
|
||||
render: DatePicker,
|
||||
nodeProps: {
|
||||
placeholder: "请输入",
|
||||
allowClear: true,
|
||||
} as Props,
|
||||
};
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
import { Input, InputInstance } from "@arco-design/web-vue";
|
||||
|
||||
type Props = InputInstance["$props"];
|
||||
|
||||
export default {
|
||||
render: Input,
|
||||
nodeProps: {
|
||||
placeholder: "请输入",
|
||||
allowClear: true,
|
||||
} as Props,
|
||||
};
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
import { InputInstance, InputNumber, InputNumberInstance } from "@arco-design/web-vue";
|
||||
|
||||
type Props = InputInstance["$props"] & InputNumberInstance["$props"];
|
||||
|
||||
export default {
|
||||
render: InputNumber,
|
||||
nodeProps: {
|
||||
placeholder: "请输入",
|
||||
defaultValue: 0,
|
||||
allowClear: true,
|
||||
} as Props,
|
||||
};
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
import { InputInstance, InputPassword, InputPasswordInstance } from "@arco-design/web-vue";
|
||||
|
||||
type Props = InputInstance["$props"] & InputPasswordInstance["$props"];
|
||||
|
||||
export default {
|
||||
render: InputPassword,
|
||||
nodeProps: {
|
||||
placeholder: "请输入",
|
||||
} as Props,
|
||||
};
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
import { InputInstance, InputSearch, InputSearchInstance } from "@arco-design/web-vue";
|
||||
|
||||
type Props = InputInstance["$props"] & InputSearchInstance["$props"];
|
||||
|
||||
export default {
|
||||
render: InputSearch,
|
||||
nodeProps: {
|
||||
placeholder: "请输入",
|
||||
allowClear: true,
|
||||
} as Props,
|
||||
};
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
import { Select, SelectInstance } from "@arco-design/web-vue";
|
||||
import { initOptions } from "../form-config";
|
||||
|
||||
type Props = SelectInstance["$props"];
|
||||
|
||||
export default {
|
||||
render: Select,
|
||||
init: initOptions,
|
||||
nodeProps: {
|
||||
placeholder: "请选择",
|
||||
allowClear: true,
|
||||
allowSearch: true,
|
||||
options: [],
|
||||
} as Props,
|
||||
};
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
import { Button } from "@arco-design/web-vue";
|
||||
|
||||
export default {
|
||||
render: (props: any, { emit }: any) => {
|
||||
return (
|
||||
<>
|
||||
<Button type="primary" loading={props.loading} onClick={() => emit("submit")} class="mr-3">
|
||||
立即提交
|
||||
</Button>
|
||||
{/* <Button loading={props.loading} onClick={() => emit("cancel")}>
|
||||
重置
|
||||
</Button> */}
|
||||
</>
|
||||
);
|
||||
},
|
||||
nodeProps: {},
|
||||
};
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
import { InputInstance, Textarea, TextareaInstance } from "@arco-design/web-vue";
|
||||
|
||||
type Props = InputInstance["$props"] & TextareaInstance["$props"];
|
||||
|
||||
export default {
|
||||
render: Textarea,
|
||||
nodeProps: {
|
||||
placeholder: "请输入",
|
||||
allowClear: true,
|
||||
} as Props,
|
||||
};
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
import { TimePicker, TimePickerInstance } from "@arco-design/web-vue";
|
||||
|
||||
type Props = TimePickerInstance["$props"];
|
||||
|
||||
export default {
|
||||
render: TimePicker,
|
||||
nodeProps: {
|
||||
allowClear: true,
|
||||
} as Props,
|
||||
};
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
import { TreeSelect, TreeSelectInstance } from "@arco-design/web-vue";
|
||||
import { initOptions } from "../form-config";
|
||||
|
||||
type Props = TreeSelectInstance["$props"];
|
||||
|
||||
export default {
|
||||
render: TreeSelect,
|
||||
init: (arg: any) => initOptions(arg, "data"),
|
||||
nodeProps: {
|
||||
placeholder: "请选择",
|
||||
allowClear: true,
|
||||
allowSearch: true,
|
||||
options: [],
|
||||
} as Props,
|
||||
};
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
import cascader from "./Cascader";
|
||||
import custom from "./Custom";
|
||||
import date from "./Date";
|
||||
import input from "./Input";
|
||||
import number from "./Number";
|
||||
import password from "./Password";
|
||||
import search from "./Search";
|
||||
import select from "./Select";
|
||||
import submit from "./Submit";
|
||||
import textarea from "./Textarea";
|
||||
import time from "./Time";
|
||||
import treeSelect from "./TreeSelect";
|
||||
|
||||
export const nodeMap = {
|
||||
input,
|
||||
number,
|
||||
search,
|
||||
textarea,
|
||||
select,
|
||||
treeSelect,
|
||||
time,
|
||||
password,
|
||||
cascader,
|
||||
date,
|
||||
submit,
|
||||
custom,
|
||||
};
|
||||
|
||||
export type NodeMap = typeof nodeMap;
|
||||
|
||||
export type NodeType = keyof NodeMap;
|
||||
|
||||
export type NodeUnion = {
|
||||
[key in NodeType]: Partial<
|
||||
Omit<NodeMap[key], "render"> & {
|
||||
/**
|
||||
* 组件类型
|
||||
*/
|
||||
render: key | ((...args: any[]) => any);
|
||||
}
|
||||
>;
|
||||
}[NodeType];
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
import { FormInstance } from "@arco-design/web-vue";
|
||||
import { merge } from "lodash-es";
|
||||
import { IFormItem } from "./form-item";
|
||||
import { useModel } from "./hooks/useModel";
|
||||
import { useItems } from "./hooks/useItems";
|
||||
|
||||
export type Options = {
|
||||
/**
|
||||
|
|
@ -27,16 +26,34 @@ export type Options = {
|
|||
* @see src/components/form/use-form.tsx
|
||||
*/
|
||||
export const useForm = (options: Options) => {
|
||||
const initModel = options.model ?? {};
|
||||
const { items, updateItemOptions } = useItems(options.items, initModel, Boolean(options.submit));
|
||||
const { model, resetModel, setModel, getModel } = useModel(initModel);
|
||||
const { model: _model = {} } = options;
|
||||
const model: Record<string, any> = { id: undefined, ..._model };
|
||||
const items: IFormItem[] = [];
|
||||
|
||||
return {
|
||||
model,
|
||||
items,
|
||||
resetModel,
|
||||
setModel,
|
||||
getModel,
|
||||
updateItemOptions,
|
||||
};
|
||||
for (const item of options.items) {
|
||||
if (!item.nodeProps) {
|
||||
item.nodeProps = {} as any;
|
||||
}
|
||||
model[item.field] = model[item.field] ?? item.initial;
|
||||
items.push(item);
|
||||
}
|
||||
|
||||
if (options.submit) {
|
||||
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;
|
||||
};
|
||||
|
|
@ -32,21 +32,3 @@ export function setModel(model: any, data: Record<string, any>) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 字符串或函数渲染
|
||||
* @param value 值
|
||||
* @param arg 参数
|
||||
* @returns
|
||||
*/
|
||||
export function strOrFnRender(value?: string | Function, arg?: any) {
|
||||
if (typeof value === "string") {
|
||||
return () => value;
|
||||
}
|
||||
if (typeof value === "function") {
|
||||
return () => value(arg);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export const falsy = () => false;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
export function strOrFnRender(fn: any, options: any) {
|
||||
if (typeof fn === "string") {
|
||||
return () => fn;
|
||||
}
|
||||
if (typeof fn === "function") {
|
||||
return fn(options);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
@ -1,16 +1,11 @@
|
|||
import {
|
||||
TableColumnData as BaseColumn,
|
||||
TableData as BaseData,
|
||||
Table as BaseTable,
|
||||
PaginationProps,
|
||||
} from "@arco-design/web-vue";
|
||||
import { cloneDeep, isBoolean, isObject, merge } from "lodash-es";
|
||||
import AniEmpty from "@/components/empty/AniEmpty.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 } from "vue";
|
||||
import AniEmpty from "../empty/AniEmpty.vue";
|
||||
import { Form, FormInstance, FormModal, FormModalInstance, FormModalProps, FormProps } from "../form";
|
||||
import { config } from "./table.config";
|
||||
|
||||
type DataFn = (search: Record<string, any>, paging: { page: number; size: number }) => PromiseLike<any>;
|
||||
type DataFn = (search: Record<string, any>, paging: { page: number; size: number }) => Promise<any>;
|
||||
|
||||
/**
|
||||
* 表格组件
|
||||
|
|
@ -37,7 +32,8 @@ export const Table = defineComponent({
|
|||
* 分页参数配置
|
||||
*/
|
||||
pagination: {
|
||||
type: [Boolean, Object] as PropType<boolean | PaginationProps>,
|
||||
type: Object as PropType<any>,
|
||||
default: () => reactive(config.pagination),
|
||||
},
|
||||
/**
|
||||
* 搜索表单配置
|
||||
|
|
@ -77,7 +73,6 @@ export const Table = defineComponent({
|
|||
const createRef = ref<FormModalInstance>();
|
||||
const modifyRef = ref<FormModalInstance>();
|
||||
const renderData = ref<BaseData[]>([]);
|
||||
const paging = ref<PaginationProps>(merge({}, isObject(props.pagination) ? props.pagination : config.pagination));
|
||||
const inlined = computed(() => (props.search?.items?.length ?? 0) <= config.searchInlineCount);
|
||||
const reloadData = () => loadData({ current: 1, pageSize: 10 });
|
||||
const openModifyModal = (data: any) => modifyRef.value?.open(data);
|
||||
|
|
@ -86,8 +81,9 @@ export const Table = defineComponent({
|
|||
* 加载数据
|
||||
* @param pagination 自定义分页
|
||||
*/
|
||||
const loadData = async (pagination: Partial<PaginationProps> = {}) => {
|
||||
const { current: page = 1, pageSize: size = 10 } = { ...paging.value, ...pagination };
|
||||
const loadData = async (pagination: Partial<any> = {}) => {
|
||||
const merged = { ...props.pagination, ...pagination };
|
||||
const paging = { page: merged.current, size: merged.pageSize };
|
||||
const model = searchRef.value?.getModel() ?? {};
|
||||
|
||||
// 本地加载
|
||||
|
|
@ -102,21 +98,21 @@ export const Table = defineComponent({
|
|||
});
|
||||
});
|
||||
renderData.value = data;
|
||||
paging.value.total = renderData.value.length;
|
||||
paging.value.current = 1;
|
||||
props.pagination.total = renderData.value.length;
|
||||
props.pagination.current = 1;
|
||||
}
|
||||
|
||||
// 远程加载
|
||||
if (typeof props.data === "function") {
|
||||
try {
|
||||
loading.value = true;
|
||||
const resData = await props.data(model, { page, size });
|
||||
const resData = await props.data(model, paging);
|
||||
const { data = [], total = 0 } = resData?.data || {};
|
||||
renderData.value = data;
|
||||
paging.value.total = total;
|
||||
paging.value.current = page;
|
||||
props.pagination.total = total;
|
||||
props.pagination.current = paging.page;
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
// todo
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
|
|
@ -126,8 +122,8 @@ export const Table = defineComponent({
|
|||
watchEffect(() => {
|
||||
if (Array.isArray(props.data)) {
|
||||
renderData.value = props.data;
|
||||
paging.value.total = props.data.length;
|
||||
paging.value.current = 1;
|
||||
props.pagination.total = props.data.length;
|
||||
props.pagination.current = 1;
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -147,7 +143,6 @@ export const Table = defineComponent({
|
|||
createRef,
|
||||
modifyRef,
|
||||
renderData,
|
||||
paging,
|
||||
loadData,
|
||||
reloadData,
|
||||
openModifyModal,
|
||||
|
|
@ -182,10 +177,7 @@ export const Table = defineComponent({
|
|||
)}
|
||||
{this.$slots.action?.()}
|
||||
</div>
|
||||
<div>
|
||||
{this.inlined && <Form ref="searchRef" {...this.search}></Form>}
|
||||
{this.$slots.tool?.(this.renderData)}
|
||||
</div>
|
||||
<div>{this.inlined && <Form ref="searchRef" {...this.search}></Form>}</div>
|
||||
</div>
|
||||
|
||||
<BaseTable
|
||||
|
|
@ -195,7 +187,7 @@ export const Table = defineComponent({
|
|||
{...this.$attrs}
|
||||
{...this.tableProps}
|
||||
loading={this.loading}
|
||||
pagination={this.pagination && this.paging}
|
||||
pagination={this.pagination}
|
||||
data={this.renderData}
|
||||
columns={this.columns}
|
||||
onPageChange={(current: number) => this.loadData({ current })}
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@
|
|||
</template>
|
||||
<router-view v-slot="{ Component }">
|
||||
<keep-alive :include="menuStore.cacheAppNames">
|
||||
<component :is="Component"></component>
|
||||
<component v-if="!appStore.pageLoding" :is="Component"></component>
|
||||
</keep-alive>
|
||||
</router-view>
|
||||
</a-spin>
|
||||
|
|
@ -73,11 +73,11 @@
|
|||
|
||||
<script lang="ts" setup>
|
||||
import { useAppStore } from "@/store";
|
||||
import { useMenuStore } from "@/store/menu";
|
||||
import { Message } from "@arco-design/web-vue";
|
||||
import { IconSync } from "@arco-design/web-vue/es/icon";
|
||||
import Menu from "./components/menu.vue";
|
||||
import userDropdown from "./components/userDropdown.vue";
|
||||
import { useMenuStore } from "@/store/menu";
|
||||
|
||||
defineOptions({ name: "LayoutPage" });
|
||||
|
||||
|
|
|
|||
|
|
@ -1,17 +1,34 @@
|
|||
<template>
|
||||
<div class="m-4">
|
||||
<a-card :bordered="false">
|
||||
<template #title>
|
||||
<div class="flex items-center">
|
||||
<i class="icon-park-outline-config mr-2"></i>
|
||||
系统参数
|
||||
</div>
|
||||
</template>
|
||||
</a-card>
|
||||
<div class="border-2 border-green-500 px-2 w-40 text-3xl text-green-500">AR K056</div>
|
||||
<AnForm :model="model" :items="items" :submit="submit" :form-props="formProps"></AnForm>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="tsx"></script>
|
||||
<script setup lang="tsx">
|
||||
import { AnForm } from "@/components/form/components/Form";
|
||||
import { useForm } from "@/components/form/hooks/useForm";
|
||||
|
||||
const { model, items, submit, formProps } = useForm({
|
||||
model: {},
|
||||
items: [
|
||||
{
|
||||
field: "id",
|
||||
render: "input",
|
||||
},
|
||||
{
|
||||
field: 'tod',
|
||||
render: 'select',
|
||||
options: [
|
||||
{
|
||||
label: '测试',
|
||||
value: 1
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@ declare module '@vue/runtime-core' {
|
|||
AImagePreview: typeof import('@arco-design/web-vue')['ImagePreview']
|
||||
AInput: typeof import('@arco-design/web-vue')['Input']
|
||||
AInputNumber: typeof import('@arco-design/web-vue')['InputNumber']
|
||||
AInputPassword: typeof import('@arco-design/web-vue')['InputPassword']
|
||||
AInputSearch: typeof import('@arco-design/web-vue')['InputSearch']
|
||||
ALayout: typeof import('@arco-design/web-vue')['Layout']
|
||||
ALayoutContent: typeof import('@arco-design/web-vue')['LayoutContent']
|
||||
|
|
@ -51,7 +50,6 @@ declare module '@vue/runtime-core' {
|
|||
ARadioGroup: typeof import('@arco-design/web-vue')['RadioGroup']
|
||||
AScrollbar: typeof import('@arco-design/web-vue')['Scrollbar']
|
||||
ASelect: typeof import('@arco-design/web-vue')['Select']
|
||||
ASpace: typeof import('@arco-design/web-vue')['Space']
|
||||
ASpin: typeof import('@arco-design/web-vue')['Spin']
|
||||
ASwitch: typeof import('@arco-design/web-vue')['Switch']
|
||||
ATabPane: typeof import('@arco-design/web-vue')['TabPane']
|
||||
|
|
@ -68,7 +66,6 @@ declare module '@vue/runtime-core' {
|
|||
DragResizer: typeof import('./../components/editor/components/DragResizer.vue')['default']
|
||||
Editor: typeof import('./../components/editor/components/Editor.vue')['default']
|
||||
EditorPreview: typeof import('./../components/editor/components/EditorPreview.vue')['default']
|
||||
Empty: typeof import('./../components/empty/index.vue')['default']
|
||||
ImagePicker: typeof import('./../components/editor/components/ImagePicker.vue')['default']
|
||||
InputColor: typeof import('./../components/editor/components/InputColor.vue')['default']
|
||||
InputImage: typeof import('./../components/editor/components/InputImage.vue')['default']
|
||||
|
|
|
|||
Loading…
Reference in New Issue