diff --git a/.env b/.env index 4c3b1eb..4c0e516 100644 --- a/.env +++ b/.env @@ -8,7 +8,7 @@ VITE_SUBTITLE = 快速开发web应用的模板工具 # 部署路径: 当为 ./ 时路由模式需为 hash VITE_BASE = / # 接口前缀:参见 axios 的 baseURL -VITE_API = / +VITE_API = https://appnify.app.juetan.cn/ # 首页路径 VITE_HOME_PATH = /home/home # 路由模式:web(路径) hash(锚点) diff --git a/src/api/instance/axios.d.ts b/src/api/instance/axios.d.ts index ced771e..de21309 100644 --- a/src/api/instance/axios.d.ts +++ b/src/api/instance/axios.d.ts @@ -13,6 +13,11 @@ declare module "axios" { * @private */ closeToast?: () => void; + /** + * 是否在请求成功后提示 + * @default false + */ + msg?: boolean | string; /** * 响应异常提示 */ diff --git a/src/api/interceptors/toast.ts b/src/api/interceptors/toast.ts index 0fb3b42..8db37b9 100644 --- a/src/api/interceptors/toast.ts +++ b/src/api/interceptors/toast.ts @@ -1,5 +1,6 @@ -import { IToastOptions, toast } from "@/components"; -import { AxiosInstance } from "axios"; +import { IToastOptions, toast } from '@/components'; +import { Message } from '@arco-design/web-vue'; +import { AxiosInstance } from 'axios'; /** * 提示拦截器 @@ -7,31 +8,35 @@ import { AxiosInstance } from "axios"; */ export function addToastInterceptor(axios: AxiosInstance) { axios.interceptors.request.use( - (config) => { + config => { if (config.toast) { let options: IToastOptions = {}; - if (typeof config.toast === "string") { + if (typeof config.toast === 'string') { options = { message: config.toast }; } - if (typeof config.toast === "object") { + if (typeof config.toast === 'object') { options = config.toast; } config.closeToast = toast(options); } return config; }, - (error) => { + error => { error.config.closeToast?.(); return Promise.reject(error); } ); axios.interceptors.response.use( - (response) => { - response.config.closeToast?.(); + response => { + const { closeToast, msg } = response.config; + closeToast?.(); + if (msg) { + Message.success(`提示: ${typeof msg === 'string' ? msg : response.data?.message}`); + } return response; }, - (error) => { + error => { error.config.closeToast?.(); return Promise.reject(error); } diff --git a/src/components/AnForm/components/Form.tsx b/src/components/AnForm/components/Form.tsx index 8e4c819..e4f34a1 100644 --- a/src/components/AnForm/components/Form.tsx +++ b/src/components/AnForm/components/Form.tsx @@ -1,18 +1,18 @@ -import { Form, FormInstance } from "@arco-design/web-vue"; -import { useVModel } from "@vueuse/core"; -import { PropType } from "vue"; -import { FormContextKey } from "../core/useFormContext"; -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 { Form, FormInstance } from '@arco-design/web-vue'; +import { useVModel } from '@vueuse/core'; +import { PropType } from 'vue'; +import { FormContextKey } from './useFormContext'; +import { useFormItems } from './useFormItems'; +import { useFormModel } from './useFormModel'; +import { useFormRef } from './useFormRef'; +import { useFormSubmit } from './useFormSubmit'; +import { AnFormItem, IAnFormItem } from './FormItem'; /** * 表单组件 */ export const AnForm = defineComponent({ - name: "AnForm", + name: 'AnForm', props: { /** * 表单数据 @@ -38,9 +38,7 @@ export const AnForm = defineComponent({ * 提交表单 * @example * ```ts - * (model) => { - * return api.user.addUser(model) - * } + * (model) => api.user.addUser(model) * ``` */ submit: { @@ -59,9 +57,9 @@ export const AnForm = defineComponent({ type: Object as IAnFormProps, }, }, - emits: ["update:model"], + emits: ['update:model'], setup(props, { slots, emit }) { - const model = useVModel(props, "model", emit); + const model = useVModel(props, 'model', emit); const items = computed(() => props.items); const formRefes = useFormRef(); const formModel = useFormModel(model, formRefes.clearValidate); @@ -75,9 +73,12 @@ export const AnForm = defineComponent({ render() { return (
- {this.items.map((item) => ( + {this.items.map(item => ( ))} + {this.submit && this.submitItem && ( + + )}
); }, @@ -85,11 +86,11 @@ export const AnForm = defineComponent({ export type AnFormInstance = InstanceType; -export type AnFormProps = AnFormInstance["$props"]; +export type AnFormProps = AnFormInstance['$props']; -export type IAnFormProps = PropType>; +export type IAnFormProps = PropType>; -export type IAnForm = Pick; +export type IAnForm = Pick; export type IAnFormSubmitFn = (model: Recordable, items: IAnFormItem[]) => any; diff --git a/src/components/AnForm/components/FormItem.tsx b/src/components/AnForm/components/FormItem.tsx index c3c8bee..4969efc 100644 --- a/src/components/AnForm/components/FormItem.tsx +++ b/src/components/AnForm/components/FormItem.tsx @@ -8,7 +8,7 @@ import { import { InjectionKey, PropType, provide } from 'vue'; import { SetterItem, SetterType, setterMap } from './FormSetter'; -export const FormItemContextKey = Symbol('FormItemContextKey') as InjectionKey; +export const FormItemContextKey = Symbol('FormItemContextKey') as InjectionKey; /** * 表单项 @@ -42,44 +42,43 @@ export const AnFormItem = defineComponent({ const rules = computed(() => props.item.rules?.filter(i => !i.disable?.(props))); const disabled = computed(() => Boolean(props.item.disable?.(props))); - const setterSlots = computed(() => { + const setterSlots = (() => { const slots = props.item.setterSlots; if (!slots) { return null; } const items: Recordable = {}; for (const [name, Slot] of Object.entries(slots)) { - items[name] = () => ; + items[name] = (p: Recordable) => ; } return items; - }); + })(); - const contentRender = () => { - const Slot = props.item.itemSlots?.default; - if (Slot) { - return ; - } + const itemSlots = (() => { const Setter = setterMap[props.item.setter as SetterType]?.setter as any; - if (!Setter) { + const slots = props.item.itemSlots; + if (!slots && !Setter) { return null; } - return ( + const SetterRender = () => ( - {setterSlots.value} + {setterSlots} ); - }; - - const makeSlot = (name: 'help' | 'extra' | 'label' | 'default') => { - return () => { - const Slot = props.item.itemSlots?.[name]; - return Slot ? () => : null; - }; - }; - - const help = computed(makeSlot('help')); - const extra = computed(makeSlot('extra')); - const label = computed(makeSlot('label')); + if (!slots) { + return { + default: SetterRender, + }; + } + const items: Recordable = {}; + for (const [name, Slot] of Object.entries(slots)) { + items[name] = (p: Recordable) => ; + } + if (Setter) { + items.default = SetterRender; + } + return items; + })(); provide(FormItemContextKey, props); @@ -95,12 +94,7 @@ export const AnFormItem = defineComponent({ disabled={disabled.value} field={props.item.field} > - {{ - default: contentRender, - help: help.value, - extra: extra.value, - label: label.value, - }} + {itemSlots} ); }; @@ -159,7 +153,7 @@ export type IAnFormItemBase = { /** * 标签 - * @example + * @example * ```ts * '昵称' * ``` @@ -174,7 +168,7 @@ export type IAnFormItemBase = { /** * 是否可见 - * @example + * @example * ```ts * (props) => Boolean(props.model.id) * ``` @@ -183,7 +177,7 @@ export type IAnFormItemBase = { /** * 是否禁用 - * @example + * @example * ```ts * (props) => Boolean(props.model.id) * ``` @@ -211,6 +205,12 @@ export type IAnFormItemBase = { * @see 1 */ itemSlots?: IAnFormItemSlots; + + /** + * 内置 + * @private + */ + $init?: () => void; }; export type IAnFormItem = IAnFormItemBase & SetterItem; diff --git a/src/components/AnForm/components/FormModal.tsx b/src/components/AnForm/components/FormModal.tsx index e49bf12..4bf95e8 100644 --- a/src/components/AnForm/components/FormModal.tsx +++ b/src/components/AnForm/components/FormModal.tsx @@ -1,8 +1,8 @@ import { useVisible } from "@/hooks/useVisible"; import { Button, ButtonInstance, Modal } from "@arco-design/web-vue"; import { PropType } from "vue"; -import { useModalSubmit } from "../core/useModalSubmit"; -import { useModalTrigger } from "../core/useModalTrigger"; +import { useModalSubmit } from "./useModalSubmit"; +import { useModalTrigger } from "./useModalTrigger"; import { AnForm, IAnFormProps, IAnFormSubmit } from "./Form"; import { IAnFormItem } from "./FormItem"; diff --git a/src/components/AnForm/components/FormSetter.tsx b/src/components/AnForm/components/FormSetter.tsx index c65883b..ea02aaf 100644 --- a/src/components/AnForm/components/FormSetter.tsx +++ b/src/components/AnForm/components/FormSetter.tsx @@ -13,10 +13,10 @@ export type SetterItemMap = { * 'input' * ``` */ - setter?: key; + setter: key; /** * 控件参数 - * @example + * @example * ```tsx * { type: "password" } * ``` @@ -26,20 +26,13 @@ export type SetterItemMap = { * 控件插槽 * @example * ```tsx - * { - * help: (props) => { - * return - * {props.item.label} - * - * } - * } + * label: (props) => {props.item.label} * ``` */ setterSlots?: SetterMap[key]['setterSlots']; }; }; -export type SetterItem = SetterItemMap[SetterType]; +export type SetterItem = SetterItemMap[SetterType] | { setter?: undefined; setterProps?: undefined; setterSlots?: undefined }; export { setterMap }; - diff --git a/src/components/AnForm/core/useFormContext.ts b/src/components/AnForm/components/useFormContext.tsx similarity index 100% rename from src/components/AnForm/core/useFormContext.ts rename to src/components/AnForm/components/useFormContext.tsx diff --git a/src/components/AnForm/core/useFormItems.ts b/src/components/AnForm/components/useFormItems.tsx similarity index 54% rename from src/components/AnForm/core/useFormItems.ts rename to src/components/AnForm/components/useFormItems.tsx index 7082bba..1ae4d1f 100644 --- a/src/components/AnForm/core/useFormItems.ts +++ b/src/components/AnForm/components/useFormItems.tsx @@ -1,9 +1,10 @@ -import { Ref } from "vue"; -import { IAnFormItem } from "../components/FormItem"; +import { Ref } from 'vue'; +import { IAnFormItem } from './FormItem'; +import { setterMap } from './FormSetter'; export function useFormItems(items: Ref, model: Ref) { const getItem = (field: string) => { - return items.value.find((i) => i.field === field); + return items.value.find(i => i.field === field); }; const getItemOptions = (field: string) => { @@ -15,18 +16,30 @@ export function useFormItems(items: Ref, model: Ref) const initItemOptions = (field: string) => { 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 = () => { 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 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(() => { diff --git a/src/components/AnForm/core/useFormModel.ts b/src/components/AnForm/components/useFormModel.tsx similarity index 97% rename from src/components/AnForm/core/useFormModel.ts rename to src/components/AnForm/components/useFormModel.tsx index f815bec..cd18784 100644 --- a/src/components/AnForm/core/useFormModel.ts +++ b/src/components/AnForm/components/useFormModel.tsx @@ -43,6 +43,9 @@ export function formatModel(model: Recordable) { const data: Recordable = {}; for (const [key, value] of Object.entries(model)) { + if (value === '') { + continue; + } if (/^\[.+\]$/.test(key)) { formatModelArray(key, value, data); continue; diff --git a/src/components/AnForm/core/useFormRef.ts b/src/components/AnForm/components/useFormRef.tsx similarity index 100% rename from src/components/AnForm/core/useFormRef.ts rename to src/components/AnForm/components/useFormRef.tsx diff --git a/src/components/AnForm/core/useFormSubmit.ts b/src/components/AnForm/components/useFormSubmit.tsx similarity index 64% rename from src/components/AnForm/core/useFormSubmit.ts rename to src/components/AnForm/components/useFormSubmit.tsx index c2226fc..d07b385 100644 --- a/src/components/AnForm/core/useFormSubmit.ts +++ b/src/components/AnForm/components/useFormSubmit.tsx @@ -1,8 +1,23 @@ -import { Message } from "@arco-design/web-vue"; -import { IAnForm } from "../components/Form"; +import { Message } from '@arco-design/web-vue'; +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) { const loading = ref(false); + const submitItem = ref(null); + + if (props.submit) { + submitItem.value = cloneDeep(SUBMIT_ITEM); + } /** * 设置loading @@ -19,7 +34,7 @@ export function useFormSubmit(props: IAnForm, validate: any, getModel: any) { if (await validate()) { return; } - const submit = typeof props.submit === "string" ? () => null : props.submit; + const submit = typeof props.submit === 'string' ? () => null : props.submit; try { loading.value = true; const data = getModel(); @@ -40,6 +55,7 @@ export function useFormSubmit(props: IAnForm, validate: any, getModel: any) { return { loading, + submitItem, setLoading, submitForm, cancelForm, diff --git a/src/components/AnForm/core/useModalSubmit.ts b/src/components/AnForm/components/useModalSubmit.tsx similarity index 100% rename from src/components/AnForm/core/useModalSubmit.ts rename to src/components/AnForm/components/useModalSubmit.tsx diff --git a/src/components/AnForm/core/useModalTrigger.tsx b/src/components/AnForm/components/useModalTrigger.tsx similarity index 100% rename from src/components/AnForm/core/useModalTrigger.tsx rename to src/components/AnForm/components/useModalTrigger.tsx diff --git a/src/components/AnForm/hooks/useForm.tsx b/src/components/AnForm/hooks/useForm.tsx index 9afeed6..658959e 100644 --- a/src/components/AnForm/hooks/useForm.tsx +++ b/src/components/AnForm/hooks/useForm.tsx @@ -1,16 +1,18 @@ -import { AnForm, IAnForm } from "../components/Form"; -import { FormItem, useItems } from "./useItems"; +import { AnForm, IAnForm } from '../components/Form'; +import { FormItem, useItems } from './useItems'; -export type FormUseOptions = Partial> & { +export type FormUseOptions = Partial> & { /** * 表单项 * @example * ```ts - * [{ - * field: 'name', - * label: '昵称', - * setter: 'input' - * }] + * [ + * { + * field: 'name', + * label: '昵称', + * setter: 'input' + * } + * ] * ``` */ items?: FormItem[]; @@ -21,33 +23,27 @@ export type FormUseOptions = Partial> & { */ export const useForm = (options: FormUseOptions) => { 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 formProps = ref(_props); - const instance = ref | null>(null); + const formRef = ref | null>(null); - const component = () => { - const onUpdateModel = (value: Recordable) => { - model.value = value; - }; - return ( - (instance.value = el)} - model={model.value} - onUpdate:model={onUpdateModel} - items={items.value} - submit={submit} - formProps={formProps.value} - > - ); - }; + const AnFormer = () => ( + (formRef.value = el)} + v-model:model={model.value} + items={items.value} + submit={submit} + formProps={formProps.value} + > + ); return { - component, - instance, + component: AnFormer, model, items, - formProps, submit, + formProps, + formRef, }; }; diff --git a/src/components/AnForm/hooks/useItems.ts b/src/components/AnForm/hooks/useItems.tsx similarity index 61% rename from src/components/AnForm/hooks/useItems.ts rename to src/components/AnForm/hooks/useItems.tsx index 4c0c9ba..d15739a 100644 --- a/src/components/AnForm/hooks/useItems.ts +++ b/src/components/AnForm/hooks/useItems.tsx @@ -10,7 +10,7 @@ export type FormItem = Omit & SetterItem & { /** * 默认值 - * @example + * @example * ```ts * '1' * ``` @@ -28,7 +28,7 @@ export type FormItem = Omit & /** * 校验规则 - * @example + * @example * ```ts * ['email'] * ``` @@ -38,37 +38,22 @@ export type FormItem = Omit & const ITEM: Partial = { setter: 'input', - itemProps: {}, }; -const SUBMIT_ITEM: FormItem = { - field: 'id', - setter: 'submit', - itemProps: { - hideLabel: true, - }, -}; - -export function useItems(list: FormItem[], model: Recordable, submit: boolean) { +export function useItems(list: FormItem[], model: Recordable) { const items = ref([]); - let hasSubmit = false; for (const item of list) { let target: any = defaultsDeep({}, ITEM); if (!item.setter || typeof item.setter === 'string') { - const defaults = setterMap[item.setter ?? 'input']; - if (defaults) { - defaultsDeep(target, defaults); + const setter = setterMap[item.setter ?? 'input']; + if (setter) { + defaultsDeep(target, { setterProps: setter.setterProps ?? {} }); } } - if (item.setter === 'submit') { - target = merge(target, SUBMIT_ITEM); - hasSubmit = true; - } - - target = merge(target, omit(item, ['required', 'rules'])); + target = merge(target, omit(item, ['required', 'rules', 'value'])); const rules = useRules(item); if (rules) { @@ -79,9 +64,5 @@ export function useItems(list: FormItem[], model: Recordable, submit: boolean) { items.value.push(target); } - if (submit && !hasSubmit) { - items.value.push(defaultsDeep({}, SUBMIT_ITEM, setterMap.submit)); - } - return items; } diff --git a/src/components/AnForm/hooks/useRules.ts b/src/components/AnForm/hooks/useRules.tsx similarity index 100% rename from src/components/AnForm/hooks/useRules.ts rename to src/components/AnForm/hooks/useRules.tsx diff --git a/src/components/AnForm/index.ts b/src/components/AnForm/index.ts index 92fcbbc..54a08c5 100644 --- a/src/components/AnForm/index.ts +++ b/src/components/AnForm/index.ts @@ -1,4 +1,5 @@ -export * from "./components/Form"; -export * from "./hooks/useForm"; -export * from "./core/useFormContext"; -export * from "./components/FormItem"; +export * from './components/Form'; +export * from './hooks/useForm'; +export * from './components/useFormContext'; +export * from './components/FormItem'; +export * from './components/useFormModel'; diff --git a/src/components/AnForm/setters/Custom.tsx b/src/components/AnForm/setters/Custom.tsx deleted file mode 100644 index 84d8bc7..0000000 --- a/src/components/AnForm/setters/Custom.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { defineSetter } from './util'; - -export default defineSetter<{ a: number }, '11'>({ - setter: () => '1', -}); diff --git a/src/components/AnForm/setters/Date.tsx b/src/components/AnForm/setters/Date.tsx index 4dacaac..419b42f 100644 --- a/src/components/AnForm/setters/Date.tsx +++ b/src/components/AnForm/setters/Date.tsx @@ -1,14 +1,23 @@ import { DatePicker, DatePickerInstance } from '@arco-design/web-vue'; import { defineSetter } from './util'; +import { PickerProps } from '@arco-design/web-vue/es/date-picker/interface'; -type DateProps = DatePickerInstance['$props']; +type DateProps = DatePickerInstance['$props'] & Partial; -type DateSlots = 'prefix' | 'suffixIcon' | 'iconNextDouble' | 'iconPrevDouble' | 'iconNext' | 'iconPrev' | 'cell' | 'extra'; +type DateSlots = + | 'prefix' + | 'suffixIcon' + | 'iconNextDouble' + | 'iconPrevDouble' + | 'iconNext' + | 'iconPrev' + | 'cell' + | 'extra'; export default defineSetter({ setter: DatePicker, setterProps: { placeholder: '请选择', allowClear: true, - } as any, + }, }); diff --git a/src/components/AnForm/setters/DateRange.tsx b/src/components/AnForm/setters/DateRange.tsx index 4b8a90f..f2d8963 100644 --- a/src/components/AnForm/setters/DateRange.tsx +++ b/src/components/AnForm/setters/DateRange.tsx @@ -3,7 +3,7 @@ import { defineSetter } from './util'; type RangeProps = RangePickerInstance['$props']; -type RangeSlots = "1"; +type RangeSlots = "none"; export default defineSetter({ setter: RangePicker, diff --git a/src/components/AnForm/setters/Input.tsx b/src/components/AnForm/setters/Input.tsx index 2a990eb..aa41360 100644 --- a/src/components/AnForm/setters/Input.tsx +++ b/src/components/AnForm/setters/Input.tsx @@ -3,7 +3,7 @@ import { defineSetter } from './util'; type InputProps = InputInstance['$props']; -type InputSlots = "2"; +type InputSlots = 'prepend' | 'append' | 'suffix' | 'prefix'; export default defineSetter({ setter: Input, diff --git a/src/components/AnForm/setters/Number.tsx b/src/components/AnForm/setters/Number.tsx index afd0f2e..4c5ecaf 100644 --- a/src/components/AnForm/setters/Number.tsx +++ b/src/components/AnForm/setters/Number.tsx @@ -3,7 +3,7 @@ import { defineSetter } from './util'; type NumberProps = InputInstance['$props'] | InputNumberInstance['$props']; -type NumberSlots = "3"; +type NumberSlots = 'minus' | 'plus' | 'append' | 'prepend' | 'suffix' | 'prefix'; export default defineSetter({ setter: InputNumber, diff --git a/src/components/AnForm/setters/Password.tsx b/src/components/AnForm/setters/Password.tsx index de57ccb..2de9634 100644 --- a/src/components/AnForm/setters/Password.tsx +++ b/src/components/AnForm/setters/Password.tsx @@ -3,7 +3,7 @@ import { defineSetter } from './util'; type PasswordProps = InputInstance['$props'] & InputPasswordInstance['$props']; -type PasswordSlots = "4"; +type PasswordSlots = 'none'; export default defineSetter({ setter: InputPassword, diff --git a/src/components/AnForm/setters/Search.tsx b/src/components/AnForm/setters/Search.tsx index 6da2933..f544771 100644 --- a/src/components/AnForm/setters/Search.tsx +++ b/src/components/AnForm/setters/Search.tsx @@ -3,7 +3,7 @@ import { defineSetter } from './util'; type SearchProps = InputInstance['$props'] & InputSearchInstance['$props']; -type SearchSlots = "5"; +type SearchSlots = "none"; export default defineSetter({ setter: InputSearch, diff --git a/src/components/AnForm/setters/Select.tsx b/src/components/AnForm/setters/Select.tsx index a5be68c..ab6aaa0 100644 --- a/src/components/AnForm/setters/Select.tsx +++ b/src/components/AnForm/setters/Select.tsx @@ -4,7 +4,17 @@ import { defineSetter } from './util'; type SelectProps = SelectInstance['$props']; -type SelectSlots = "6"; +type SelectSlots = + | 'trigger' + | 'prefix' + | 'searchIcon' + | 'loadingIcon' + | 'arrowIcon' + | 'footer' + | 'header' + | 'label' + | 'option' + | 'empty'; export default defineSetter({ setter: Select, diff --git a/src/components/AnForm/setters/Submit.tsx b/src/components/AnForm/setters/Submit.tsx index d51410a..46b625a 100644 --- a/src/components/AnForm/setters/Submit.tsx +++ b/src/components/AnForm/setters/Submit.tsx @@ -1,8 +1,8 @@ -import { Button } from "@arco-design/web-vue"; -import { FormContextKey } from "../core/useFormContext"; -import { defineSetter } from "./util"; +import { Button } from '@arco-design/web-vue'; +import { FormContextKey } from '../components/useFormContext'; +import { defineSetter } from './util'; -export default defineSetter<{ a1?: number }, "10">({ +export default defineSetter<{}, 'none'>({ setter() { const { loading, submitForm, resetModel } = inject(FormContextKey)!; return ( diff --git a/src/components/AnForm/setters/Textarea.tsx b/src/components/AnForm/setters/Textarea.tsx index 3533199..ee77484 100644 --- a/src/components/AnForm/setters/Textarea.tsx +++ b/src/components/AnForm/setters/Textarea.tsx @@ -3,7 +3,7 @@ import { defineSetter } from './util'; type TextareaProps = InputInstance['$props'] & TextareaInstance['$props']; -type TextareaSlots = "7"; +type TextareaSlots = "none"; export default defineSetter({ setter: Textarea, diff --git a/src/components/AnForm/setters/Time.tsx b/src/components/AnForm/setters/Time.tsx index b9a4813..948b99f 100644 --- a/src/components/AnForm/setters/Time.tsx +++ b/src/components/AnForm/setters/Time.tsx @@ -3,7 +3,7 @@ import { defineSetter } from './util'; type TimeProps = TimePickerInstance['$props']; -type TimeSlots = "8"; +type TimeSlots = 'prefix' | 'suffixIcon' | 'extra'; export default defineSetter({ setter: TimePicker, diff --git a/src/components/AnForm/setters/TreeSelect.tsx b/src/components/AnForm/setters/TreeSelect.tsx index dea71ec..3bba44b 100644 --- a/src/components/AnForm/setters/TreeSelect.tsx +++ b/src/components/AnForm/setters/TreeSelect.tsx @@ -4,7 +4,18 @@ import { defineSetter } from './util'; type TreeSelectProps = TreeSelectInstance['$props']; -type TreeSelectSlots = "9"; +type TreeSelectSlots = + | 'trigger' + | 'prefix' + | 'label' + | 'header' + | 'loader' + | 'empty' + | 'footer' + | 'treeSlotExtra' + | 'treeSlotTitle' + | 'treeSlotIcon' + | 'treeSlotSwitcherIcon'; export default defineSetter({ setter: TreeSelect, diff --git a/src/components/AnForm/setters/index.ts b/src/components/AnForm/setters/index.ts index e08c10d..dbb460f 100644 --- a/src/components/AnForm/setters/index.ts +++ b/src/components/AnForm/setters/index.ts @@ -1,5 +1,4 @@ import cascader from './Cascader'; -import custom from './Custom'; import date from './Date'; import dateRange from './DateRange'; import input from './Input'; @@ -24,6 +23,5 @@ export default { cascader, date, submit, - custom, dateRange, }; diff --git a/src/components/AnForm/setters/util.ts b/src/components/AnForm/setters/util.ts index abbbbda..c9ce455 100644 --- a/src/components/AnForm/setters/util.ts +++ b/src/components/AnForm/setters/util.ts @@ -31,7 +31,7 @@ export interface ItemSetter

{ /** * 初始化钩子 */ - onSetup?: (model: Recordable, item: IAnFormItemBase, items: IAnFormItemBase[]) => void; + onSetup?: (args: { model: Recordable; item: IAnFormItemBase; items: IAnFormItemBase[] }) => void; } export function defineSetter

(setter: ItemSetter) { diff --git a/src/components/AnForm/utils/initOptions.ts b/src/components/AnForm/utils/initOptions.ts index 39860c8..2d8c4aa 100644 --- a/src/components/AnForm/utils/initOptions.ts +++ b/src/components/AnForm/utils/initOptions.ts @@ -1,30 +1,28 @@ -export function initOptions({ item, model }: any, key = "options") { - if (Array.isArray(item.options)) { - item.nodeProps[key] = item.options; +import { IAnFormItemFnProps } from '../components/FormItem'; + +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; } - if (item.options && typeof item.options === "object") { - const { value, source } = item.options; - item._updateOptions = async () => {}; - return; - } - if (typeof item.options === "function") { - 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 (typeof item.options === 'function') { + setterProps[key] = reactive([]); + 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; } + const data = res?.data?.data; if (Array.isArray(data)) { - item.nodeProps[key].splice(0); - item.nodeProps[key].push(...data); + const maped = data.map((i: any) => ({ ...i, value: i.id, label: i.name })); + setterProps[key].splice(0); + setterProps[key].push(...maped); + return; } }; - item._updateOptions(); + item.$init(); } } diff --git a/src/components/AnTable/components/Table.tsx b/src/components/AnTable/components/Table.tsx index 1956ed3..c0eb635 100644 --- a/src/components/AnTable/components/Table.tsx +++ b/src/components/AnTable/components/Table.tsx @@ -1,59 +1,58 @@ -import { AnForm, IAnForm, IAnFormProps } from "@/components/AnForm"; -import { AnFormModal } from "@/components/AnForm/components/FormModal"; -import AniEmpty from "@/components/empty/AniEmpty.vue"; -import { FormModalProps } from "@/components/form"; +import { IAnForm } from '@/components/AnForm'; +import AniEmpty from '@/components/empty/AniEmpty.vue'; +import { FormModalProps } from '@/components/form'; import { TableColumnData as BaseColumn, TableData as BaseData, Table as BaseTable, + Button, PaginationProps, -} from "@arco-design/web-vue"; -import { isObject, merge } from "lodash-es"; -import { PropType, computed, defineComponent, ref } from "vue"; +} from '@arco-design/web-vue'; +import { isObject, merge } from 'lodash-es'; +import { PropType, defineComponent, ref } from 'vue'; -type DataFn = (search: Record, paging: { page: number; size: number }) => Promise; +type DataFn = (filter: { page: number; size: number; [key: string]: any }) => any | Promise; /** * 表格组件 - * @see src/components/table/table.tsx */ export const AnTable = defineComponent({ - name: "AnTable", + name: 'AnTable', props: { /** * 表格数据 - * @description 可以是数组或者函数 + * @description 数组或者函数 */ data: { type: [Array, Function] as PropType, }, /** - * 表格列设置 + * 表格列 */ columns: { type: Array as PropType, default: () => [], }, /** - * 分页参数配置 + * 分页配置 */ pagination: { - type: Object as PropType, + type: Object as PropType, }, /** - * 搜索表单配置 + * 搜索表单 */ search: { type: Object as PropType, }, /** - * 新建弹窗配置 + * 新建弹窗 */ create: { type: Object as PropType, }, /** - * 修改弹窗配置 + * 修改弹窗 */ modify: { type: Object as PropType, @@ -62,33 +61,25 @@ export const AnTable = defineComponent({ * 传递给 Table 组件的属性 */ tableProps: { - type: Object as PropType["$props"]>, + type: Object as PropType['$props']>, }, }, setup(props) { const loading = ref(false); const tableRef = ref>(); - const searchRef = ref(); - const createRef = ref(); - const modifyRef = ref(); const renderData = ref([]); - 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); + const reloadData = () => loadData(); const useTablePaging = () => { const getPaging = () => { - if (isObject(props.pagination)) { - return { - page: props.pagination.current, - size: props.pagination.pageSize, - }; - } - return {}; + return { + page: props.pagination?.current ?? 1, + size: props.pagination?.pageSize ?? 10, + }; }; const setPaging = (paging: PaginationProps) => { - if (isObject(props.pagination)) { + if (props.pagination) { merge(props.pagination, paging); } }; @@ -112,28 +103,10 @@ export const AnTable = defineComponent({ */ const loadData = async () => { const paging = getPaging(); - const model = searchRef.value?.getModel() ?? {}; - - // 本地加载 - 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") { + if (typeof props.data === 'function') { try { loading.value = true; - const resData = await props.data(model, paging); + const resData = await props.data({ ...paging }); const { data = [], total = 0 } = resData?.data || {}; renderData.value = data; setPaging({ total }); @@ -156,24 +129,27 @@ export const AnTable = defineComponent({ loadData(); }); - if (props.search) { - merge(props.search, { formProps: { layout: "inline" } }); - } + const onPageChange = (page: number) => { + setPaging({ current: page }); + loadData(); + }; + + const onPageSizeChange = (size: number) => { + setPaging({ current: 1, pageSize: size }); + loadData(); + }; const state = { loading, - inlined, tableRef, - searchRef, - createRef, - modifyRef, renderData, loadData, reloadData, - openModifyModal, + onPageChange, + onPageSizeChange, }; - provide("ref:table", { ...state, ...props }); + provide('ref:table', { ...state, ...props }); return state; }, @@ -181,53 +157,30 @@ export const AnTable = defineComponent({ (this.columns as any).instance = this; return (

- {!this.inlined && this.search && ( -
- +
+
TODO
+
+
+ + +
- )} - -
-
- {this.create && ( - - )} - {this.modify && ( - - )} - {this.$slots.action?.()} -
-
{this.inlined &&
}
this.loadData({ current })} + onPageChange={this.onPageChange} + onPageSizeChange={this.onPageSizeChange} > {{ empty: () => , @@ -242,9 +195,9 @@ export const AnTable = defineComponent({ /** * 表格组件实例 */ -export type TableInstance = InstanceType; +export type TableInstance = InstanceType; /** * 表格组件参数 */ -export type TableProps = TableInstance["$props"]; +export type TableProps = TableInstance['$props']; diff --git a/src/components/AnTable/hooks/useTable.ts b/src/components/AnTable/hooks/useTable.ts deleted file mode 100644 index 7071a43..0000000 --- a/src/components/AnTable/hooks/useTable.ts +++ /dev/null @@ -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' - } - ] -}) \ No newline at end of file diff --git a/src/components/AnTable/hooks/useTable.tsx b/src/components/AnTable/hooks/useTable.tsx new file mode 100644 index 0000000..031a895 --- /dev/null +++ b/src/components/AnTable/hooks/useTable.tsx @@ -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 { + /** + * 表格列 + */ + 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 | null>(null); + + const AnTabler = () => ( + (tableRef.value = el)} + columns={columns.value} + data={data.value} + pagination={pagination.value} + tableProps={tableProps.value} + > + ); + + return { + component: AnTabler, + columns, + tableRef, + }; +} diff --git a/src/components/AnTable/hooks/useTableColumn.ts b/src/components/AnTable/hooks/useTableColumn.ts deleted file mode 100644 index 848f50b..0000000 --- a/src/components/AnTable/hooks/useTableColumn.ts +++ /dev/null @@ -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([]); - - // 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() {} diff --git a/src/components/AnTable/hooks/useTableColumn.tsx b/src/components/AnTable/hooks/useTableColumn.tsx new file mode 100644 index 0000000..7283f40 --- /dev/null +++ b/src/components/AnTable/hooks/useTableColumn.tsx @@ -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([]); + + // 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 && } + item.onClick?.(props)}> + {item.text} + + + ); + }); + }; + return column; +} + +function useTableDropdownColumn() {} diff --git a/src/components/AnTable/hooks/useTableColumnFilter.tsx b/src/components/AnTable/hooks/useTableColumnFilter.tsx new file mode 100644 index 0000000..e69de29 diff --git a/src/components/AnTable/index.ts b/src/components/AnTable/index.ts new file mode 100644 index 0000000..d79f6a4 --- /dev/null +++ b/src/components/AnTable/index.ts @@ -0,0 +1 @@ +export * from './hooks/useTable'; diff --git a/src/pages/home/components/ColumnConfiger.vue b/src/pages/home/components/ColumnConfiger.vue new file mode 100644 index 0000000..f222a24 --- /dev/null +++ b/src/pages/home/components/ColumnConfiger.vue @@ -0,0 +1,119 @@ + + + + + diff --git a/src/pages/home/home.vue b/src/pages/home/home.vue index 8e724ed..cff71a8 100644 --- a/src/pages/home/home.vue +++ b/src/pages/home/home.vue @@ -1,14 +1,56 @@