feat: 添加表格列设置组件

master
luoer 2023-11-16 17:44:58 +08:00
parent 17c695d065
commit 85b781d946
45 changed files with 658 additions and 421 deletions

2
.env
View File

@ -8,7 +8,7 @@ VITE_SUBTITLE = 快速开发web应用的模板工具
# 部署路径: 当为 ./ 时路由模式需为 hash # 部署路径: 当为 ./ 时路由模式需为 hash
VITE_BASE = / VITE_BASE = /
# 接口前缀:参见 axios 的 baseURL # 接口前缀:参见 axios 的 baseURL
VITE_API = / VITE_API = https://appnify.app.juetan.cn/
# 首页路径 # 首页路径
VITE_HOME_PATH = /home/home VITE_HOME_PATH = /home/home
# 路由模式web(路径) hash(锚点) # 路由模式web(路径) hash(锚点)

View File

@ -13,6 +13,11 @@ declare module "axios" {
* @private * @private
*/ */
closeToast?: () => void; closeToast?: () => void;
/**
*
* @default false
*/
msg?: boolean | string;
/** /**
* *
*/ */

View File

@ -1,5 +1,6 @@
import { IToastOptions, toast } from "@/components"; import { IToastOptions, toast } from '@/components';
import { AxiosInstance } from "axios"; import { Message } from '@arco-design/web-vue';
import { AxiosInstance } from 'axios';
/** /**
* *
@ -7,31 +8,35 @@ import { AxiosInstance } from "axios";
*/ */
export function addToastInterceptor(axios: AxiosInstance) { export function addToastInterceptor(axios: AxiosInstance) {
axios.interceptors.request.use( axios.interceptors.request.use(
(config) => { config => {
if (config.toast) { if (config.toast) {
let options: IToastOptions = {}; let options: IToastOptions = {};
if (typeof config.toast === "string") { if (typeof config.toast === 'string') {
options = { message: config.toast }; options = { message: config.toast };
} }
if (typeof config.toast === "object") { if (typeof config.toast === 'object') {
options = config.toast; options = config.toast;
} }
config.closeToast = toast(options); config.closeToast = toast(options);
} }
return config; return config;
}, },
(error) => { error => {
error.config.closeToast?.(); error.config.closeToast?.();
return Promise.reject(error); return Promise.reject(error);
} }
); );
axios.interceptors.response.use( axios.interceptors.response.use(
(response) => { response => {
response.config.closeToast?.(); const { closeToast, msg } = response.config;
closeToast?.();
if (msg) {
Message.success(`提示: ${typeof msg === 'string' ? msg : response.data?.message}`);
}
return response; return response;
}, },
(error) => { error => {
error.config.closeToast?.(); error.config.closeToast?.();
return Promise.reject(error); return Promise.reject(error);
} }

View File

@ -1,18 +1,18 @@
import { Form, FormInstance } from "@arco-design/web-vue"; import { Form, FormInstance } from '@arco-design/web-vue';
import { useVModel } from "@vueuse/core"; import { useVModel } from '@vueuse/core';
import { PropType } from "vue"; import { PropType } from 'vue';
import { FormContextKey } from "../core/useFormContext"; import { FormContextKey } from './useFormContext';
import { useFormItems } from "../core/useFormItems"; import { useFormItems } from './useFormItems';
import { useFormModel } from "../core/useFormModel"; import { useFormModel } from './useFormModel';
import { useFormRef } from "../core/useFormRef"; import { useFormRef } from './useFormRef';
import { useFormSubmit } from "../core/useFormSubmit"; import { useFormSubmit } from './useFormSubmit';
import { AnFormItem, IAnFormItem } from "./FormItem"; import { AnFormItem, IAnFormItem } from './FormItem';
/** /**
* *
*/ */
export const AnForm = defineComponent({ export const AnForm = defineComponent({
name: "AnForm", name: 'AnForm',
props: { props: {
/** /**
* *
@ -38,9 +38,7 @@ export const AnForm = defineComponent({
* *
* @example * @example
* ```ts * ```ts
* (model) => { * (model) => api.user.addUser(model)
* return api.user.addUser(model)
* }
* ``` * ```
*/ */
submit: { submit: {
@ -59,9 +57,9 @@ export const AnForm = defineComponent({
type: Object as IAnFormProps, type: Object as IAnFormProps,
}, },
}, },
emits: ["update:model"], emits: ['update:model'],
setup(props, { slots, emit }) { setup(props, { slots, emit }) {
const model = useVModel(props, "model", emit); const model = useVModel(props, 'model', emit);
const items = computed(() => props.items); const items = computed(() => props.items);
const formRefes = useFormRef(); const formRefes = useFormRef();
const formModel = useFormModel(model, formRefes.clearValidate); const formModel = useFormModel(model, formRefes.clearValidate);
@ -75,9 +73,12 @@ export const AnForm = defineComponent({
render() { render() {
return ( return (
<Form layout="vertical" {...this.$attrs} {...this.formProps} ref="formRef" model={this.model}> <Form layout="vertical" {...this.$attrs} {...this.formProps} ref="formRef" model={this.model}>
{this.items.map((item) => ( {this.items.map(item => (
<AnFormItem key={item.field} item={item} items={this.items} model={this.model}></AnFormItem> <AnFormItem key={item.field} item={item} items={this.items} model={this.model}></AnFormItem>
))} ))}
{this.submit && this.submitItem && (
<AnFormItem item={this.submitItem} items={this.items} model={this.model}></AnFormItem>
)}
</Form> </Form>
); );
}, },
@ -85,11 +86,11 @@ export const AnForm = defineComponent({
export type AnFormInstance = InstanceType<typeof AnForm>; export type AnFormInstance = InstanceType<typeof AnForm>;
export type AnFormProps = AnFormInstance["$props"]; export type AnFormProps = AnFormInstance['$props'];
export type IAnFormProps = PropType<Omit<FormInstance["$props"], "model">>; export type IAnFormProps = PropType<Omit<FormInstance['$props'], 'model'>>;
export type IAnForm = Pick<AnFormProps, "model" | "items" | "submit" | "formProps">; export type IAnForm = Pick<AnFormProps, 'model' | 'items' | 'submit' | 'formProps'>;
export type IAnFormSubmitFn = (model: Recordable, items: IAnFormItem[]) => any; export type IAnFormSubmitFn = (model: Recordable, items: IAnFormItem[]) => any;

View File

@ -8,7 +8,7 @@ import {
import { InjectionKey, PropType, provide } from 'vue'; import { InjectionKey, PropType, provide } from 'vue';
import { SetterItem, SetterType, setterMap } from './FormSetter'; import { SetterItem, SetterType, setterMap } from './FormSetter';
export const FormItemContextKey = Symbol('FormItemContextKey') as InjectionKey<any>; export const FormItemContextKey = Symbol('FormItemContextKey') as InjectionKey<IAnFormItemFnProps>;
/** /**
* *
@ -42,44 +42,43 @@ export const AnFormItem = defineComponent({
const rules = computed(() => props.item.rules?.filter(i => !i.disable?.(props))); const rules = computed(() => props.item.rules?.filter(i => !i.disable?.(props)));
const disabled = computed(() => Boolean(props.item.disable?.(props))); const disabled = computed(() => Boolean(props.item.disable?.(props)));
const setterSlots = computed(() => { const setterSlots = (() => {
const slots = props.item.setterSlots; const slots = props.item.setterSlots;
if (!slots) { if (!slots) {
return null; return null;
} }
const items: Recordable = {}; const items: Recordable = {};
for (const [name, Slot] of Object.entries(slots)) { for (const [name, Slot] of Object.entries(slots)) {
items[name] = () => <Slot {...props} />; items[name] = (p: Recordable) => <Slot {...p} {...props} />;
} }
return items; return items;
}); })();
const contentRender = () => { const itemSlots = (() => {
const Slot = props.item.itemSlots?.default;
if (Slot) {
return <Slot {...props} />;
}
const Setter = setterMap[props.item.setter as SetterType]?.setter as any; const Setter = setterMap[props.item.setter as SetterType]?.setter as any;
if (!Setter) { const slots = props.item.itemSlots;
if (!slots && !Setter) {
return null; return null;
} }
return ( const SetterRender = () => (
<Setter {...props.item.setterProps} v-model={props.model[props.item.field]}> <Setter {...props.item.setterProps} v-model={props.model[props.item.field]}>
{setterSlots.value} {setterSlots}
</Setter> </Setter>
); );
if (!slots) {
return {
default: SetterRender,
}; };
}
const makeSlot = (name: 'help' | 'extra' | 'label' | 'default') => { const items: Recordable = {};
return () => { for (const [name, Slot] of Object.entries(slots)) {
const Slot = props.item.itemSlots?.[name]; items[name] = (p: Recordable) => <Slot {...p} {...props}></Slot>;
return Slot ? () => <Slot {...props} /> : null; }
}; if (Setter) {
}; items.default = SetterRender;
}
const help = computed(makeSlot('help')); return items;
const extra = computed(makeSlot('extra')); })();
const label = computed(makeSlot('label'));
provide(FormItemContextKey, props); provide(FormItemContextKey, props);
@ -95,12 +94,7 @@ export const AnFormItem = defineComponent({
disabled={disabled.value} disabled={disabled.value}
field={props.item.field} field={props.item.field}
> >
{{ {itemSlots}
default: contentRender,
help: help.value,
extra: extra.value,
label: label.value,
}}
</BaseFormItem> </BaseFormItem>
); );
}; };
@ -211,6 +205,12 @@ export type IAnFormItemBase = {
* @see 1 * @see 1
*/ */
itemSlots?: IAnFormItemSlots; itemSlots?: IAnFormItemSlots;
/**
*
* @private
*/
$init?: () => void;
}; };
export type IAnFormItem = IAnFormItemBase & SetterItem; export type IAnFormItem = IAnFormItemBase & SetterItem;

View File

@ -1,8 +1,8 @@
import { useVisible } from "@/hooks/useVisible"; import { useVisible } from "@/hooks/useVisible";
import { Button, ButtonInstance, Modal } from "@arco-design/web-vue"; import { Button, ButtonInstance, Modal } from "@arco-design/web-vue";
import { PropType } from "vue"; import { PropType } from "vue";
import { useModalSubmit } from "../core/useModalSubmit"; import { useModalSubmit } from "./useModalSubmit";
import { useModalTrigger } from "../core/useModalTrigger"; import { useModalTrigger } from "./useModalTrigger";
import { AnForm, IAnFormProps, IAnFormSubmit } from "./Form"; import { AnForm, IAnFormProps, IAnFormSubmit } from "./Form";
import { IAnFormItem } from "./FormItem"; import { IAnFormItem } from "./FormItem";

View File

@ -13,7 +13,7 @@ export type SetterItemMap = {
* 'input' * 'input'
* ``` * ```
*/ */
setter?: key; setter: key;
/** /**
* *
* @example * @example
@ -26,20 +26,13 @@ export type SetterItemMap = {
* *
* @example * @example
* ```tsx * ```tsx
* { * label: (props) => <span>{props.item.label}</span>
* help: (props) => {
* return <span>
* {props.item.label}
* </span>
* }
* }
* ``` * ```
*/ */
setterSlots?: SetterMap[key]['setterSlots']; setterSlots?: SetterMap[key]['setterSlots'];
}; };
}; };
export type SetterItem = SetterItemMap[SetterType]; export type SetterItem = SetterItemMap[SetterType] | { setter?: undefined; setterProps?: undefined; setterSlots?: undefined };
export { setterMap }; export { setterMap };

View File

@ -1,9 +1,10 @@
import { Ref } from "vue"; import { Ref } from 'vue';
import { IAnFormItem } from "../components/FormItem"; import { IAnFormItem } from './FormItem';
import { setterMap } from './FormSetter';
export function useFormItems(items: Ref<IAnFormItem[]>, model: Ref<Recordable>) { export function useFormItems(items: Ref<IAnFormItem[]>, model: Ref<Recordable>) {
const getItem = (field: string) => { const getItem = (field: string) => {
return items.value.find((i) => i.field === field); return items.value.find(i => i.field === field);
}; };
const getItemOptions = (field: string) => { const getItemOptions = (field: string) => {
@ -15,18 +16,30 @@ export function useFormItems(items: Ref<IAnFormItem[]>, model: Ref<Recordable>)
const initItemOptions = (field: string) => { const initItemOptions = (field: string) => {
const item = getItem(field); const item = getItem(field);
item && item.init?.(); if (!item) {
return;
}
const setter = setterMap[item.setter!];
if (!setter) {
return;
}
setter.onSetup?.({ item, items: items.value, model: model.value });
}; };
const initItems = () => { const initItems = () => {
for (const item of items.value) { for (const item of items.value) {
item.init?.({ item, model: model.value }); const setter = setterMap[item?.setter!];
setter.onSetup?.({ item, items: items.value, model: model.value });
} }
}; };
const initItem = (field: string) => { const initItem = (field: string) => {
const item = getItem(field); const item = getItem(field);
item && item.init?.({ item, model: model.value }); if (!item) {
return;
}
const setter = setterMap[item?.setter!];
setter.onSetup?.({ item, items: items.value, model: model.value });
}; };
onMounted(() => { onMounted(() => {

View File

@ -43,6 +43,9 @@ export function formatModel(model: Recordable) {
const data: Recordable = {}; const data: Recordable = {};
for (const [key, value] of Object.entries(model)) { for (const [key, value] of Object.entries(model)) {
if (value === '') {
continue;
}
if (/^\[.+\]$/.test(key)) { if (/^\[.+\]$/.test(key)) {
formatModelArray(key, value, data); formatModelArray(key, value, data);
continue; continue;

View File

@ -1,8 +1,23 @@
import { Message } from "@arco-design/web-vue"; import { Message } from '@arco-design/web-vue';
import { IAnForm } from "../components/Form"; import { IAnForm } from './Form';
import { IAnFormItem } from './FormItem';
import { cloneDeep } from 'lodash-es';
const SUBMIT_ITEM = {
field: 'id',
setter: 'submit' as const,
itemProps: {
hideLabel: true,
},
};
export function useFormSubmit(props: IAnForm, validate: any, getModel: any) { export function useFormSubmit(props: IAnForm, validate: any, getModel: any) {
const loading = ref(false); const loading = ref(false);
const submitItem = ref<IAnFormItem | null>(null);
if (props.submit) {
submitItem.value = cloneDeep(SUBMIT_ITEM);
}
/** /**
* loading * loading
@ -19,7 +34,7 @@ export function useFormSubmit(props: IAnForm, validate: any, getModel: any) {
if (await validate()) { if (await validate()) {
return; return;
} }
const submit = typeof props.submit === "string" ? () => null : props.submit; const submit = typeof props.submit === 'string' ? () => null : props.submit;
try { try {
loading.value = true; loading.value = true;
const data = getModel(); const data = getModel();
@ -40,6 +55,7 @@ export function useFormSubmit(props: IAnForm, validate: any, getModel: any) {
return { return {
loading, loading,
submitItem,
setLoading, setLoading,
submitForm, submitForm,
cancelForm, cancelForm,

View File

@ -1,16 +1,18 @@
import { AnForm, IAnForm } from "../components/Form"; import { AnForm, IAnForm } from '../components/Form';
import { FormItem, useItems } from "./useItems"; import { FormItem, useItems } from './useItems';
export type FormUseOptions = Partial<Omit<IAnForm, "items">> & { export type FormUseOptions = Partial<Omit<IAnForm, 'items'>> & {
/** /**
* *
* @example * @example
* ```ts * ```ts
* [{ * [
* {
* field: 'name', * field: 'name',
* label: '昵称', * label: '昵称',
* setter: 'input' * setter: 'input'
* }] * }
* ]
* ``` * ```
*/ */
items?: FormItem[]; items?: FormItem[];
@ -21,33 +23,27 @@ export type FormUseOptions = Partial<Omit<IAnForm, "items">> & {
*/ */
export const useForm = (options: FormUseOptions) => { export const useForm = (options: FormUseOptions) => {
const { items: _items = [], model: _model = {}, submit, formProps: _props = {} } = options; const { items: _items = [], model: _model = {}, submit, formProps: _props = {} } = options;
const items = useItems(_items, _model, Boolean(options.submit)); const items = useItems(_items, _model);
const model = ref(_model); const model = ref(_model);
const formProps = ref(_props); const formProps = ref(_props);
const instance = ref<InstanceType<typeof AnForm> | null>(null); const formRef = ref<InstanceType<typeof AnForm> | null>(null);
const component = () => { const AnFormer = () => (
const onUpdateModel = (value: Recordable) => {
model.value = value;
};
return (
<AnForm <AnForm
ref={(el: any) => (instance.value = el)} ref={(el: any) => (formRef.value = el)}
model={model.value} v-model:model={model.value}
onUpdate:model={onUpdateModel}
items={items.value} items={items.value}
submit={submit} submit={submit}
formProps={formProps.value} formProps={formProps.value}
></AnForm> ></AnForm>
); );
};
return { return {
component, component: AnFormer,
instance,
model, model,
items, items,
formProps,
submit, submit,
formProps,
formRef,
}; };
}; };

View File

@ -38,37 +38,22 @@ export type FormItem = Omit<IAnFormItemBase, 'rules'> &
const ITEM: Partial<FormItem> = { const ITEM: Partial<FormItem> = {
setter: 'input', setter: 'input',
itemProps: {},
}; };
const SUBMIT_ITEM: FormItem = { export function useItems(list: FormItem[], model: Recordable) {
field: 'id',
setter: 'submit',
itemProps: {
hideLabel: true,
},
};
export function useItems(list: FormItem[], model: Recordable, submit: boolean) {
const items = ref<IAnFormItem[]>([]); const items = ref<IAnFormItem[]>([]);
let hasSubmit = false;
for (const item of list) { for (const item of list) {
let target: any = defaultsDeep({}, ITEM); let target: any = defaultsDeep({}, ITEM);
if (!item.setter || typeof item.setter === 'string') { if (!item.setter || typeof item.setter === 'string') {
const defaults = setterMap[item.setter ?? 'input']; const setter = setterMap[item.setter ?? 'input'];
if (defaults) { if (setter) {
defaultsDeep(target, defaults); defaultsDeep(target, { setterProps: setter.setterProps ?? {} });
} }
} }
if (item.setter === 'submit') { target = merge(target, omit(item, ['required', 'rules', 'value']));
target = merge(target, SUBMIT_ITEM);
hasSubmit = true;
}
target = merge(target, omit(item, ['required', 'rules']));
const rules = useRules(item); const rules = useRules(item);
if (rules) { if (rules) {
@ -79,9 +64,5 @@ export function useItems(list: FormItem[], model: Recordable, submit: boolean) {
items.value.push(target); items.value.push(target);
} }
if (submit && !hasSubmit) {
items.value.push(defaultsDeep({}, SUBMIT_ITEM, setterMap.submit));
}
return items; return items;
} }

View File

@ -1,4 +1,5 @@
export * from "./components/Form"; export * from './components/Form';
export * from "./hooks/useForm"; export * from './hooks/useForm';
export * from "./core/useFormContext"; export * from './components/useFormContext';
export * from "./components/FormItem"; export * from './components/FormItem';
export * from './components/useFormModel';

View File

@ -1,5 +0,0 @@
import { defineSetter } from './util';
export default defineSetter<{ a: number }, '11'>({
setter: () => '1',
});

View File

@ -1,14 +1,23 @@
import { DatePicker, DatePickerInstance } from '@arco-design/web-vue'; import { DatePicker, DatePickerInstance } from '@arco-design/web-vue';
import { defineSetter } from './util'; import { defineSetter } from './util';
import { PickerProps } from '@arco-design/web-vue/es/date-picker/interface';
type DateProps = DatePickerInstance['$props']; type DateProps = DatePickerInstance['$props'] & Partial<PickerProps>;
type DateSlots = 'prefix' | 'suffixIcon' | 'iconNextDouble' | 'iconPrevDouble' | 'iconNext' | 'iconPrev' | 'cell' | 'extra'; type DateSlots =
| 'prefix'
| 'suffixIcon'
| 'iconNextDouble'
| 'iconPrevDouble'
| 'iconNext'
| 'iconPrev'
| 'cell'
| 'extra';
export default defineSetter<DateProps, DateSlots>({ export default defineSetter<DateProps, DateSlots>({
setter: DatePicker, setter: DatePicker,
setterProps: { setterProps: {
placeholder: '请选择', placeholder: '请选择',
allowClear: true, allowClear: true,
} as any, },
}); });

View File

@ -3,7 +3,7 @@ import { defineSetter } from './util';
type RangeProps = RangePickerInstance['$props']; type RangeProps = RangePickerInstance['$props'];
type RangeSlots = "1"; type RangeSlots = "none";
export default defineSetter<RangeProps, RangeSlots>({ export default defineSetter<RangeProps, RangeSlots>({
setter: RangePicker, setter: RangePicker,

View File

@ -3,7 +3,7 @@ import { defineSetter } from './util';
type InputProps = InputInstance['$props']; type InputProps = InputInstance['$props'];
type InputSlots = "2"; type InputSlots = 'prepend' | 'append' | 'suffix' | 'prefix';
export default defineSetter<InputProps, InputSlots>({ export default defineSetter<InputProps, InputSlots>({
setter: Input, setter: Input,

View File

@ -3,7 +3,7 @@ import { defineSetter } from './util';
type NumberProps = InputInstance['$props'] | InputNumberInstance['$props']; type NumberProps = InputInstance['$props'] | InputNumberInstance['$props'];
type NumberSlots = "3"; type NumberSlots = 'minus' | 'plus' | 'append' | 'prepend' | 'suffix' | 'prefix';
export default defineSetter<NumberProps, NumberSlots>({ export default defineSetter<NumberProps, NumberSlots>({
setter: InputNumber, setter: InputNumber,

View File

@ -3,7 +3,7 @@ import { defineSetter } from './util';
type PasswordProps = InputInstance['$props'] & InputPasswordInstance['$props']; type PasswordProps = InputInstance['$props'] & InputPasswordInstance['$props'];
type PasswordSlots = "4"; type PasswordSlots = 'none';
export default defineSetter<PasswordProps, PasswordSlots>({ export default defineSetter<PasswordProps, PasswordSlots>({
setter: InputPassword, setter: InputPassword,

View File

@ -3,7 +3,7 @@ import { defineSetter } from './util';
type SearchProps = InputInstance['$props'] & InputSearchInstance['$props']; type SearchProps = InputInstance['$props'] & InputSearchInstance['$props'];
type SearchSlots = "5"; type SearchSlots = "none";
export default defineSetter<SearchProps, SearchSlots>({ export default defineSetter<SearchProps, SearchSlots>({
setter: InputSearch, setter: InputSearch,

View File

@ -4,7 +4,17 @@ import { defineSetter } from './util';
type SelectProps = SelectInstance['$props']; type SelectProps = SelectInstance['$props'];
type SelectSlots = "6"; type SelectSlots =
| 'trigger'
| 'prefix'
| 'searchIcon'
| 'loadingIcon'
| 'arrowIcon'
| 'footer'
| 'header'
| 'label'
| 'option'
| 'empty';
export default defineSetter<SelectProps, SelectSlots>({ export default defineSetter<SelectProps, SelectSlots>({
setter: Select, setter: Select,

View File

@ -1,8 +1,8 @@
import { Button } from "@arco-design/web-vue"; import { Button } from '@arco-design/web-vue';
import { FormContextKey } from "../core/useFormContext"; import { FormContextKey } from '../components/useFormContext';
import { defineSetter } from "./util"; import { defineSetter } from './util';
export default defineSetter<{ a1?: number }, "10">({ export default defineSetter<{}, 'none'>({
setter() { setter() {
const { loading, submitForm, resetModel } = inject(FormContextKey)!; const { loading, submitForm, resetModel } = inject(FormContextKey)!;
return ( return (

View File

@ -3,7 +3,7 @@ import { defineSetter } from './util';
type TextareaProps = InputInstance['$props'] & TextareaInstance['$props']; type TextareaProps = InputInstance['$props'] & TextareaInstance['$props'];
type TextareaSlots = "7"; type TextareaSlots = "none";
export default defineSetter<TextareaProps, TextareaSlots>({ export default defineSetter<TextareaProps, TextareaSlots>({
setter: Textarea, setter: Textarea,

View File

@ -3,7 +3,7 @@ import { defineSetter } from './util';
type TimeProps = TimePickerInstance['$props']; type TimeProps = TimePickerInstance['$props'];
type TimeSlots = "8"; type TimeSlots = 'prefix' | 'suffixIcon' | 'extra';
export default defineSetter<TimeProps, TimeSlots>({ export default defineSetter<TimeProps, TimeSlots>({
setter: TimePicker, setter: TimePicker,

View File

@ -4,7 +4,18 @@ import { defineSetter } from './util';
type TreeSelectProps = TreeSelectInstance['$props']; type TreeSelectProps = TreeSelectInstance['$props'];
type TreeSelectSlots = "9"; type TreeSelectSlots =
| 'trigger'
| 'prefix'
| 'label'
| 'header'
| 'loader'
| 'empty'
| 'footer'
| 'treeSlotExtra'
| 'treeSlotTitle'
| 'treeSlotIcon'
| 'treeSlotSwitcherIcon';
export default defineSetter<TreeSelectProps, TreeSelectSlots>({ export default defineSetter<TreeSelectProps, TreeSelectSlots>({
setter: TreeSelect, setter: TreeSelect,

View File

@ -1,5 +1,4 @@
import cascader from './Cascader'; import cascader from './Cascader';
import custom from './Custom';
import date from './Date'; import date from './Date';
import dateRange from './DateRange'; import dateRange from './DateRange';
import input from './Input'; import input from './Input';
@ -24,6 +23,5 @@ export default {
cascader, cascader,
date, date,
submit, submit,
custom,
dateRange, dateRange,
}; };

View File

@ -31,7 +31,7 @@ export interface ItemSetter<P extends object, S extends string> {
/** /**
* *
*/ */
onSetup?: (model: Recordable, item: IAnFormItemBase, items: IAnFormItemBase[]) => void; onSetup?: (args: { model: Recordable; item: IAnFormItemBase; items: IAnFormItemBase[] }) => void;
} }
export function defineSetter<P extends object, S extends string>(setter: ItemSetter<P, S>) { export function defineSetter<P extends object, S extends string>(setter: ItemSetter<P, S>) {

View File

@ -1,30 +1,28 @@
export function initOptions({ item, model }: any, key = "options") { import { IAnFormItemFnProps } from '../components/FormItem';
if (Array.isArray(item.options)) {
item.nodeProps[key] = item.options; export function initOptions({ item, model }: IAnFormItemFnProps, key: string = 'options') {
const setterProps: Recordable = item.setterProps!;
if (Array.isArray(item.options) && item.setterProps) {
setterProps[key] = item.options;
return; return;
} }
if (item.options && typeof item.options === "object") { if (typeof item.options === 'function') {
const { value, source } = item.options; setterProps[key] = reactive([]);
item._updateOptions = async () => {}; item.$init = async () => {
const res = await (item as any).options({ item, model });
if (Array.isArray(res)) {
setterProps[key].splice(0);
setterProps[key].push(...res);
return; return;
} }
if (typeof item.options === "function") { const data = res?.data?.data;
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)) { if (Array.isArray(data)) {
item.nodeProps[key].splice(0); const maped = data.map((i: any) => ({ ...i, value: i.id, label: i.name }));
item.nodeProps[key].push(...data); setterProps[key].splice(0);
setterProps[key].push(...maped);
return;
} }
}; };
item._updateOptions(); item.$init();
} }
} }

View File

@ -1,59 +1,58 @@
import { AnForm, IAnForm, IAnFormProps } from "@/components/AnForm"; import { IAnForm } from '@/components/AnForm';
import { AnFormModal } from "@/components/AnForm/components/FormModal"; import AniEmpty from '@/components/empty/AniEmpty.vue';
import AniEmpty from "@/components/empty/AniEmpty.vue"; import { FormModalProps } from '@/components/form';
import { FormModalProps } from "@/components/form";
import { import {
TableColumnData as BaseColumn, TableColumnData as BaseColumn,
TableData as BaseData, TableData as BaseData,
Table as BaseTable, Table as BaseTable,
Button,
PaginationProps, PaginationProps,
} from "@arco-design/web-vue"; } from '@arco-design/web-vue';
import { isObject, merge } from "lodash-es"; import { isObject, merge } from 'lodash-es';
import { PropType, computed, defineComponent, ref } from "vue"; import { PropType, defineComponent, ref } from 'vue';
type DataFn = (search: Record<string, any>, paging: { page: number; size: number }) => Promise<any>; type DataFn = (filter: { page: number; size: number; [key: string]: any }) => any | Promise<any>;
/** /**
* *
* @see src/components/table/table.tsx
*/ */
export const AnTable = defineComponent({ export const AnTable = defineComponent({
name: "AnTable", name: 'AnTable',
props: { props: {
/** /**
* *
* @description * @description
*/ */
data: { data: {
type: [Array, Function] as PropType<BaseData[] | DataFn>, type: [Array, Function] as PropType<BaseData[] | DataFn>,
}, },
/** /**
* *
*/ */
columns: { columns: {
type: Array as PropType<BaseColumn[]>, type: Array as PropType<BaseColumn[]>,
default: () => [], default: () => [],
}, },
/** /**
* *
*/ */
pagination: { pagination: {
type: Object as PropType<boolean | PaginationProps>, type: Object as PropType<PaginationProps & { hide?: boolean }>,
}, },
/** /**
* *
*/ */
search: { search: {
type: Object as PropType<IAnForm>, type: Object as PropType<IAnForm>,
}, },
/** /**
* *
*/ */
create: { create: {
type: Object as PropType<FormModalProps>, type: Object as PropType<FormModalProps>,
}, },
/** /**
* *
*/ */
modify: { modify: {
type: Object as PropType<FormModalProps>, type: Object as PropType<FormModalProps>,
@ -62,33 +61,25 @@ export const AnTable = defineComponent({
* Table * Table
*/ */
tableProps: { tableProps: {
type: Object as PropType<InstanceType<typeof BaseTable>["$props"]>, type: Object as PropType<InstanceType<typeof BaseTable>['$props']>,
}, },
}, },
setup(props) { setup(props) {
const loading = ref(false); const loading = ref(false);
const tableRef = ref<InstanceType<typeof BaseTable>>(); const tableRef = ref<InstanceType<typeof BaseTable>>();
const searchRef = ref<FormInstance>();
const createRef = ref<FormModalInstance>();
const modifyRef = ref<FormModalInstance>();
const renderData = ref<BaseData[]>([]); const renderData = ref<BaseData[]>([]);
const inlined = computed(() => (props.search?.items?.length ?? 0) <= config.searchInlineCount); const reloadData = () => loadData();
const reloadData = () => loadData({ current: 1, pageSize: 10 });
const openModifyModal = (data: any) => modifyRef.value?.open(data);
const useTablePaging = () => { const useTablePaging = () => {
const getPaging = () => { const getPaging = () => {
if (isObject(props.pagination)) {
return { return {
page: props.pagination.current, page: props.pagination?.current ?? 1,
size: props.pagination.pageSize, size: props.pagination?.pageSize ?? 10,
}; };
}
return {};
}; };
const setPaging = (paging: PaginationProps) => { const setPaging = (paging: PaginationProps) => {
if (isObject(props.pagination)) { if (props.pagination) {
merge(props.pagination, paging); merge(props.pagination, paging);
} }
}; };
@ -112,28 +103,10 @@ export const AnTable = defineComponent({
*/ */
const loadData = async () => { const loadData = async () => {
const paging = getPaging(); const paging = getPaging();
const model = searchRef.value?.getModel() ?? {}; if (typeof props.data === 'function') {
// 本地加载
if (Array.isArray(props.data)) {
const filters = Object.entries(model);
const data = props.data.filter((item) => {
return filters.every(([key, value]) => {
if (typeof value === "string") {
return item[key].includes(value);
}
return item[key] === value;
});
});
renderData.value = data;
setPaging({ total: renderData.value.length, current: 1 });
}
// 远程加载
if (typeof props.data === "function") {
try { try {
loading.value = true; loading.value = true;
const resData = await props.data(model, paging); const resData = await props.data({ ...paging });
const { data = [], total = 0 } = resData?.data || {}; const { data = [], total = 0 } = resData?.data || {};
renderData.value = data; renderData.value = data;
setPaging({ total }); setPaging({ total });
@ -156,24 +129,27 @@ export const AnTable = defineComponent({
loadData(); loadData();
}); });
if (props.search) { const onPageChange = (page: number) => {
merge(props.search, { formProps: { layout: "inline" } }); setPaging({ current: page });
} loadData();
};
const onPageSizeChange = (size: number) => {
setPaging({ current: 1, pageSize: size });
loadData();
};
const state = { const state = {
loading, loading,
inlined,
tableRef, tableRef,
searchRef,
createRef,
modifyRef,
renderData, renderData,
loadData, loadData,
reloadData, reloadData,
openModifyModal, onPageChange,
onPageSizeChange,
}; };
provide("ref:table", { ...state, ...props }); provide('ref:table', { ...state, ...props });
return state; return state;
}, },
@ -181,53 +157,30 @@ export const AnTable = defineComponent({
(this.columns as any).instance = this; (this.columns as any).instance = this;
return ( return (
<div class="table w-full"> <div class="table w-full">
{!this.inlined && this.search && ( <div class={`mb-3 flex toolbar justify-between`}>
<div class="border-b pb-0 border-slate-200 mb-3"> <div class={`flex-1 flex gap-2 `}>TODO</div>
<AnForm <div>
ref="searchRef" <div class="flex gap-1">
class="!grid grid-cols-4 gap-x-6" <Button disabled={this.loading} onClick={this.loadData}>
v-model:model={this.search.model} {{ icon: () => <span class="icon-park-outline-redo"></span> }}
items={this.search.items} </Button>
submit={this.search.submit} <Button>{{ icon: () => <span class="icon-park-outline-config"></span> }}</Button>
formProps={this.search.formProps}
></AnForm>
</div> </div>
)}
<div class={`mb-3 flex toolbar justify-between ${!this.inlined && "mt-2"}`}>
<div class={`${this.create || this.$slots.action ? null : "!hidden"} flex-1 flex gap-2 `}>
{this.create && (
<AnFormModal
ref="createRef"
{...this.create}
v-model:model={this.create.model}
onSubmited={this.reloadData}
></AnFormModal>
)}
{this.modify && (
<FormModal
{...(this.modify as any)}
ref="modifyRef"
onSubmited={this.reloadData}
trigger={false}
></FormModal>
)}
{this.$slots.action?.()}
</div> </div>
<div>{this.inlined && <Form ref="searchRef" {...this.search}></Form>}</div>
</div> </div>
<BaseTable <BaseTable
ref="tableRef"
row-key="id" row-key="id"
bordered={false} bordered={false}
{...this.$attrs} {...this.$attrs}
{...this.tableProps} {...this.tableProps}
ref="tableRef"
loading={this.loading} loading={this.loading}
pagination={this.pagination} pagination={this.pagination?.hide ? false : this.pagination}
data={this.renderData} data={this.renderData}
columns={this.columns} columns={this.columns}
onPageChange={(current: number) => this.loadData({ current })} onPageChange={this.onPageChange}
onPageSizeChange={this.onPageSizeChange}
> >
{{ {{
empty: () => <AniEmpty />, empty: () => <AniEmpty />,
@ -242,9 +195,9 @@ export const AnTable = defineComponent({
/** /**
* *
*/ */
export type TableInstance = InstanceType<typeof Table>; export type TableInstance = InstanceType<typeof AnTable>;
/** /**
* *
*/ */
export type TableProps = TableInstance["$props"]; export type TableProps = TableInstance['$props'];

View File

@ -1,44 +0,0 @@
import { FormModalUseOptions } from "../../AnForm/hooks/useFormModal";
import { ModifyForm } from "./useModiyForm";
import { SearchForm } from "./useSearchForm";
import { TableColumn } from "./useTableColumn";
export interface TableUseOptions {
/**
*
*/
columns?: TableColumn[];
/**
*
*/
search?: SearchForm;
/**
*
*/
create?: FormModalUseOptions;
/**
*
*/
modify?: ModifyForm;
/**
*
*/
detail?: any;
/**
*
*/
delete?: any;
}
export function useTable(options: TableUseOptions) {
return 0;
}
useTable({
columns: [
{
title: '测试',
type: 'index'
}
]
})

View File

@ -0,0 +1,56 @@
import { FormModalUseOptions } from '../../AnForm/hooks/useFormModal';
import { AnTable, TableProps } from '../components/Table';
import { ModifyForm } from './useModiyForm';
import { SearchForm } from './useSearchForm';
import { TableColumn, useTableColumns } from './useTableColumn';
export interface TableUseOptions extends Pick<TableProps, 'data' | 'tableProps' | 'pagination'> {
/**
*
*/
columns?: TableColumn[];
/**
*
*/
search?: SearchForm;
/**
*
*/
create?: FormModalUseOptions;
/**
*
*/
modify?: ModifyForm;
/**
*
*/
detail?: any;
/**
*
*/
delete?: any;
}
export function useTable(options: TableUseOptions) {
const { columns } = useTableColumns(options.columns ?? []);
const data = ref(options.data);
const pagination = ref({ hide: false, showTotal: true, showPageSize: true, ...(options.pagination ?? {}) });
const tableProps = ref(options.tableProps ?? {});
const tableRef = ref<InstanceType<typeof AnTable> | null>(null);
const AnTabler = () => (
<AnTable
ref={(el: any) => (tableRef.value = el)}
columns={columns.value}
data={data.value}
pagination={pagination.value}
tableProps={tableProps.value}
></AnTable>
);
return {
component: AnTabler,
columns,
tableRef,
};
}

View File

@ -1,80 +0,0 @@
import { TableColumnData } from "@arco-design/web-vue";
interface TableBaseColumn {
/**
*
*/
type?: undefined;
}
interface TableIndexColumn {
/**
*
*/
type: "index";
}
interface TableButtonColumn {
/**
*
*/
type: "button";
/**
*
*/
buttons: any[];
}
interface TableDropdownColumn {
/**
*
*/
type: "dropdown";
/**
*
*/
dropdowns: any[];
}
export type TableColumn = TableColumnData &
(TableIndexColumn | TableBaseColumn | TableButtonColumn | TableDropdownColumn);
export function useTableColumns(data: TableColumn[]) {
const columns = ref<any>([]);
// for (let column of data) {
// if (column.type === "index") {
// column = useTableIndexColumn(column);
// }
// if (column.type === "button") {
// column = useTableButtonColumn(column);
// }
// if (column.type === "dropdown") {
// column = useTableDropdownColumn(column);
// }
// columns.push({ ...config.columnBase, ...column });
// }
return {
columns,
};
}
useTableColumns([
{
type: "button",
buttons: [{}],
},
{
title: "11",
},
]);
function useTableIndexColumn() {}
function useTableButtonColumn() {}
function useTableDropdownColumn() {}

View File

@ -0,0 +1,143 @@
import { Divider, Link, TableColumnData } from '@arco-design/web-vue';
interface TableBaseColumn {
/**
*
*/
type?: undefined;
}
interface TableIndexColumn {
/**
*
*/
type: 'index';
}
interface TableColumnButton {
/**
*
* @example
* ```ts
* 'delete'
* ```
*/
type?: 'modify' | 'delete';
/**
*
* @example
* ```ts
* '修改'
* ```
*/
text?: string;
/**
*
* @see ALink
*/
buttonProps?: Recordable;
/**
*
* @example
* ```ts
* (props) => props.record.status === 1
* ```
*/
visible?: (args: Recordable) => boolean;
/**
*
* @example
* ```ts
* (props) => props.record.status === 1
* ```
*/
disable?: (args: Recordable) => boolean;
/**
*
* @example
* ```ts
* (props) => api.user.rmUser(props.record.id)
* ```
*/
onClick?: (props: any) => void;
}
interface TableButtonColumn {
/**
*
*/
type: 'button';
/**
*
*/
buttons: TableColumnButton[];
}
interface TableDropdownColumn {
/**
*
*/
type: 'dropdown';
/**
*
*/
dropdowns: any[];
}
export type TableColumn = TableColumnData &
(TableIndexColumn | TableBaseColumn | TableButtonColumn | TableDropdownColumn);
export function useTableColumns(data: TableColumn[]) {
const columns = ref<TableColumnData[]>([]);
// for (let column of data) {
// if (column.type === "index") {
// column = useTableIndexColumn(column);
// }
for (let column of data) {
if (column.type === 'button') {
column = useTableButtonColumn(column);
}
columns.value.push(column);
}
// if (column.type === "dropdown") {
// column = useTableDropdownColumn(column);
// }
// columns.push({ ...config.columnBase, ...column });
// }
return {
columns,
};
}
function useTableIndexColumn() {}
function useTableButtonColumn(column: TableButtonColumn & TableColumnData) {
const { type, buttons } = column;
const items: TableColumnButton[] = [];
for (const button of buttons) {
items.push(button);
}
column.render = props => {
return items.map((item, index) => {
if (item.visible && !item.visible(props)) {
return null;
}
return (
<>
{index !== 0 && <Divider direction="vertical" margin={4} />}
<Link {...item.buttonProps} disabled={item.disable?.(props)} onClick={() => item.onClick?.(props)}>
{item.text}
</Link>
</>
);
});
};
return column;
}
function useTableDropdownColumn() {}

View File

@ -0,0 +1 @@
export * from './hooks/useTable';

View File

@ -0,0 +1,119 @@
<template>
<a-popover position="br" trigger="click">
<a-button class="float-right">设置</a-button>
<template #content>
<div class="mb-1 leading-none border-b border-gray-100 pb-3">设置表格列</div>
<a-scrollbar outer-class="h-96 overflow-hidden" class="h-full overflow-auto">
<ul class="grid m-0 p-0 divide-y divide-gray-100 w-[700px] overflow-auto overscroll-contain">
<li
v-for="(item, index) in items"
:key="item.dataIndex"
class="group flex items-center justify-between py-2 pr-8 select-none"
>
<div class="flex gap-2">
<a-checkbox v-model="item.enable" :disabled="item.disable" size="large" @change="onItemChange">
{{ item.dataIndex }}
</a-checkbox>
<span class="hidden group-hover:inline-block ml-4">
<i v-show="!item.disable" class="icon-park-outline-drag cursor-move"></i>
</span>
</div>
<div class="flex gap-2 items-center">
<a-checkbox v-model="item.autoWidth" :disabled="item.disable">
<template #checkbox="{ checked }">
<a-tag :checked="checked" :checkable="!item.disable" color="blue">自适应</a-tag>
</template>
</a-checkbox>
<a-divider direction="vertical" :margin="8"></a-divider>
<a-input-number
size="small"
v-model="item.width"
:disabled="item.autoWidth || item.disable"
:min="60"
:step="10"
class="!w-20"
/>
<span class="text-gray-400">像素</span>
</div>
</li>
</ul>
</a-scrollbar>
<div class="mt-4 flex gap-2 items-center justify-between">
<div class="flex items-center">
<a-checkbox
:indeterminate="indeterminate"
v-model="checkAll"
@change="(v: any) => items.forEach(i => i.enable = v)"
>
全选
</a-checkbox>
<span class="text-xs text-gray-400 ml-1">
({{ items.filter(i => i.enable).length }}/{{ items.length }})
</span>
</div>
<div class="space-x-2">
<a-button>重置</a-button>
<a-button type="primary">确定</a-button>
</div>
</div>
</template>
</a-popover>
</template>
<script setup lang="ts">
import { computed, ref } from 'vue';
const items = ref(
Array(10)
.fill(0)
.map((v, i) => {
return {
dataIndex: `测试${i}`,
enable: true,
autoWidth: false,
width: 80,
disable: false,
};
})
);
items.value.unshift({
dataIndex: '顺序',
enable: true,
autoWidth: false,
width: 80,
disable: true,
});
items.value.push({
dataIndex: '操作',
enable: true,
autoWidth: false,
width: 80,
disable: true,
});
const checkAll = ref(false);
const indeterminate = computed(() => {
const check = checked.value.length;
const total = items.value.length;
return 0 < check && check < total;
});
const checked = computed(() => {
return items.value.filter(i => i.enable);
});
const onItemChange = () => {
if (checked.value.length === 0) {
checkAll.value = false;
return;
}
if (checked.value.length === items.value.length) {
checkAll.value = true;
}
};
</script>
<style scoped></style>

View File

@ -1,14 +1,56 @@
<template> <template>
<div class="m-4 bg-white p-4"> <div class="m-4 bg-white p-4">
<div class="border-2 border-green-500 px-2 w-40 text-3xl text-green-500 mb-4">AR K056</div> <div class="border-2 border-green-500 px-2 w-40 text-3xl text-green-500 mb-4">AR K056</div>
<column-configer></column-configer>
<user-table></user-table>
<div>{{ formatModel(emodel) }}</div> <div>{{ formatModel(emodel) }}</div>
<UpForm /> <UpForm />
</div> </div>
</template> </template>
<script setup lang="tsx"> <script setup lang="tsx">
import { api } from '@/api';
import { useForm } from '@/components/AnForm'; import { useForm } from '@/components/AnForm';
import { formatModel } from '@/components/AnForm/core/useFormModel'; import { formatModel } from '@/components/AnForm';
import { useTable } from '@/components/AnTable';
import ColumnConfiger from './components/ColumnConfiger.vue';
const { component: UserTable } = useTable({
data(search) {
return api.user.getUsers(search);
},
pagination: {
hide: false,
},
columns: [
{
dataIndex: 'id',
title: 'ID',
},
{
dataIndex: 'nickname',
title: '用户名称',
},
{
title: '操作',
type: 'button',
width: 140,
buttons: [
{
text: '修改',
},
{
text: '修改',
visible: () => false,
},
{
text: '修改',
disable: () => true,
},
],
},
],
});
const { component: UpForm, model: emodel } = useForm({ const { component: UpForm, model: emodel } = useForm({
formProps: { formProps: {
@ -19,16 +61,22 @@ const { component: UpForm, model: emodel } = useForm({
field: 'id', field: 'id',
label: '输入组件', label: '输入组件',
setter: 'input', setter: 'input',
setterSlots: {
prefix: () => <span>123</span>,
},
itemSlots: { itemSlots: {
help(props) { help: props => props.item.label,
return props.item.label; extra: () => 'extra',
}, },
}, },
{
field: 'todo',
label: '测试',
}, },
{ {
field: 'xsa', field: 'xsa',
label: '动态渲染', label: '动态渲染',
setter: 'cascader', setter: 'input',
visible: props => props.model.id, visible: props => props.model.id,
}, },
{ {
@ -42,7 +90,7 @@ const { component: UpForm, model: emodel } = useForm({
label: '校验规则', label: '校验规则',
setter: 'input', setter: 'input',
// required: true, // required: true,
// rules: ["email"], rules: ['email'],
}, },
{ {
field: 'sgss', field: 'sgss',
@ -115,3 +163,4 @@ const { component: UpForm, model: emodel } = useForm({
} }
} }
</route> </route>
@/components/AnForm/components/useFormModel

View File

@ -4,13 +4,14 @@ import { useProgressGard } from "../guards/progress";
import { useTitleGuard } from "../guards/title"; import { useTitleGuard } from "../guards/title";
import { baseRoutes } from "../routes/base"; import { baseRoutes } from "../routes/base";
import { historyMode } from "./util"; import { historyMode } from "./util";
import { routes } from "../routes/page";
/** /**
* *
*/ */
export const router = createRouter({ export const router = createRouter({
history: historyMode(), history: historyMode(),
routes: [...baseRoutes], routes: [...baseRoutes, ...routes],
}); });
/** /**

View File

@ -37,8 +37,8 @@ body {
overflow: hidden; overflow: hidden;
} }
.arco-modal-header { .arco-modal-header {
background: var(--color-fill-3); // background: var(--color-fill-3);
border-bottom: none; // border-bottom: none;
} }
.arco-modal-footer { .arco-modal-footer {
padding-top: 0; padding-top: 0;

View File

@ -30,6 +30,7 @@ declare module '@vue/runtime-core' {
AImagePreview: typeof import('@arco-design/web-vue')['ImagePreview'] AImagePreview: typeof import('@arco-design/web-vue')['ImagePreview']
AInput: typeof import('@arco-design/web-vue')['Input'] AInput: typeof import('@arco-design/web-vue')['Input']
AInputNumber: typeof import('@arco-design/web-vue')['InputNumber'] AInputNumber: typeof import('@arco-design/web-vue')['InputNumber']
AInputPassword: typeof import('@arco-design/web-vue')['InputPassword']
AInputSearch: typeof import('@arco-design/web-vue')['InputSearch'] AInputSearch: typeof import('@arco-design/web-vue')['InputSearch']
ALayout: typeof import('@arco-design/web-vue')['Layout'] ALayout: typeof import('@arco-design/web-vue')['Layout']
ALayoutContent: typeof import('@arco-design/web-vue')['LayoutContent'] ALayoutContent: typeof import('@arco-design/web-vue')['LayoutContent']
@ -50,8 +51,11 @@ declare module '@vue/runtime-core' {
ARadioGroup: typeof import('@arco-design/web-vue')['RadioGroup'] ARadioGroup: typeof import('@arco-design/web-vue')['RadioGroup']
AScrollbar: typeof import('@arco-design/web-vue')['Scrollbar'] AScrollbar: typeof import('@arco-design/web-vue')['Scrollbar']
ASelect: typeof import('@arco-design/web-vue')['Select'] ASelect: typeof import('@arco-design/web-vue')['Select']
ASpace: typeof import('@arco-design/web-vue')['Space']
ASpin: typeof import('@arco-design/web-vue')['Spin'] ASpin: typeof import('@arco-design/web-vue')['Spin']
ASwitch: typeof import('@arco-design/web-vue')['Switch'] ASwitch: typeof import('@arco-design/web-vue')['Switch']
ATable: typeof import('@arco-design/web-vue')['Table']
ATableColumn: typeof import('@arco-design/web-vue')['TableColumn']
ATabPane: typeof import('@arco-design/web-vue')['TabPane'] ATabPane: typeof import('@arco-design/web-vue')['TabPane']
ATabs: typeof import('@arco-design/web-vue')['Tabs'] ATabs: typeof import('@arco-design/web-vue')['Tabs']
ATag: typeof import('@arco-design/web-vue')['Tag'] ATag: typeof import('@arco-design/web-vue')['Tag']