From 83513d519d4b6b8fbfd48db266b9bd7b3a998d63 Mon Sep 17 00:00:00 2001 From: kailong321200875 <321200875@qq.com> Date: Tue, 23 May 2023 15:36:33 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20Radio=E6=94=B9=E9=80=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- src/components/Form/src/Form.vue | 106 ++++++---- src/components/Form/src/componentMap.ts | 6 +- .../Form/src/components/useRenderRadio.tsx | 6 +- .../Form/src/components/useRenderSelect.tsx | 10 +- src/components/Form/src/helper.ts | 6 +- src/types/components.d.ts | 66 ++++-- src/types/form.d.ts | 26 ++- src/utils/index.ts | 7 + src/views/Components/Form/DefaultForm.vue | 200 ++++++++++-------- 10 files changed, 274 insertions(+), 161 deletions(-) diff --git a/package.json b/package.json index 48add6bfc..007d6a15a 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ "dayjs": "^1.11.7", "echarts": "^5.4.2", "echarts-wordcloud": "^2.1.0", - "element-plus": "2.3.5", + "element-plus": "^2.3.5", "intro.js": "^7.0.1", "lodash-es": "^4.17.21", "mitt": "^3.0.0", diff --git a/src/components/Form/src/Form.vue b/src/components/Form/src/Form.vue index 6e3b3373e..04c0adf48 100644 --- a/src/components/Form/src/Form.vue +++ b/src/components/Form/src/Form.vue @@ -21,7 +21,16 @@ import { set } from 'lodash-es' import { FormProps } from './types' import { Icon } from '@/components/Icon' import { FormSchema, FormSetPropsType } from '@/types/form' -import { ComponentNameEnum, SelectComponentProps, SelectOption } from '@/types/components.d' +import { + ComponentNameEnum, + SelectComponentProps, + SelectOption, + RadioComponentProps +} from '@/types/components.d' + +const { renderSelectOptions } = useRenderSelect() +const { renderRadioOptions } = useRenderRadio() +const { renderCheckboxOptions } = useRenderCheckbox() const { getPrefixCls } = useDesign() @@ -181,7 +190,7 @@ export default defineComponent({ // 如果是select组件,并且没有自定义模板,自动渲染options if (item.component === ComponentNameEnum.SELECT) { slotsMap.default = !componentSlots.default - ? () => renderOptions(item) + ? () => renderSelectOptions(item) : () => { return componentSlots.default( unref((item?.componentProps as SelectComponentProps)?.options) @@ -231,24 +240,52 @@ export default defineComponent({ {{ default: () => { - const Com = componentMap[item.component as string] as ReturnType< - typeof defineComponent - > - - const { autoSetPlaceholder } = unref(getProps) - - return slots[item.field] ? ( - getSlot(slots, item.field, formModel.value) - ) : ( - - {{ ...slotsMap }} - - ) + + const { autoSetPlaceholder } = unref(getProps) + + // 需要特殊处理的组件 + const specialComponents = [ComponentNameEnum.RADIO] + + if (specialComponents.findIndex((v) => v === item.component) !== -1) { + switch (item.component) { + case ComponentNameEnum.RADIO: + const componentProps = item.componentProps as RadioComponentProps + const valueAlias = componentProps?.props?.value || 'value' + const labelAlias = componentProps?.props?.label || 'label' + const disabledAlias = componentProps?.props?.disabled || 'disabled' + + return componentProps?.options?.map((v) => { + return ( + + {v[labelAlias]} + + ) + }) + } + } + + return ( + + {{ ...slotsMap }} + + ) + } } }} @@ -256,23 +293,20 @@ export default defineComponent({ } // 渲染options - const renderOptions = (item: FormSchema) => { - switch (item.component) { - case ComponentNameEnum.SELECT: - const { renderSelectOptions } = useRenderSelect() - return renderSelectOptions(item) - case 'Radio': - case 'RadioButton': - const { renderRadioOptions } = useRenderRadio() - return renderRadioOptions(item) - case 'Checkbox': - case 'CheckboxButton': - const { renderCheckboxOptions } = useRenderCheckbox() - return renderCheckboxOptions(item) - default: - break - } - } + // const renderOptions = (item: FormSchema) => { + // switch (item.component) { + // case ComponentNameEnum.SELECT: + // return renderSelectOptions(item) + // case ComponentNameEnum.RADIO: + // case 'RadioButton': + // return renderRadioOptions(item) + // case 'Checkbox': + // case 'CheckboxButton': + // return renderCheckboxOptions(item) + // default: + // break + // } + // } // 过滤传入Form组件的属性 const getFormBindValue = () => { diff --git a/src/components/Form/src/componentMap.ts b/src/components/Form/src/componentMap.ts index 9fdb1db7a..f5d7f175b 100644 --- a/src/components/Form/src/componentMap.ts +++ b/src/components/Form/src/componentMap.ts @@ -16,14 +16,16 @@ import { ElTimeSelect, ElTransfer, ElAutocomplete, - ElDivider + ElDivider, + ElRadio } from 'element-plus' import { InputPassword } from '@/components/InputPassword' import { Editor } from '@/components/Editor' import { ComponentName } from '@/types/components' const componentMap: Recordable = { - Radio: ElRadioGroup, + Radio: ElRadio, + // RadioGroup: ElRadioGroup, Checkbox: ElCheckboxGroup, CheckboxButton: ElCheckboxGroup, Input: ElInput, diff --git a/src/components/Form/src/components/useRenderRadio.tsx b/src/components/Form/src/components/useRenderRadio.tsx index 45b65bfda..55fac6308 100644 --- a/src/components/Form/src/components/useRenderRadio.tsx +++ b/src/components/Form/src/components/useRenderRadio.tsx @@ -1,9 +1,13 @@ import { FormSchema } from '@/types/form' import { ElRadio, ElRadioButton } from 'element-plus' import { defineComponent } from 'vue' +import { ComponentNameEnum } from '@/types/components.d' export const useRenderRadio = () => { - const renderRadioOptions = (item: FormSchema) => { + const renderRadioOptions = ( + item: FormSchema, + type?: ComponentNameEnum.RADIO | ComponentNameEnum.RADIO_BUTTON = ComponentNameEnum.RADIO + ) => { // 如果有别名,就取别名 const labelAlias = item?.componentProps?.optionsAlias?.labelField const valueAlias = item?.componentProps?.optionsAlias?.valueField diff --git a/src/components/Form/src/components/useRenderSelect.tsx b/src/components/Form/src/components/useRenderSelect.tsx index 4f33f2ae7..84e327a03 100644 --- a/src/components/Form/src/components/useRenderSelect.tsx +++ b/src/components/Form/src/components/useRenderSelect.tsx @@ -8,8 +8,8 @@ export const useRenderSelect = () => { const componentsProps = item.componentProps as SelectComponentProps const optionGroupDefaultSlot = componentsProps.slots?.optionGroupDefault // 如果有别名,就取别名 - const labelAlias = componentsProps?.labelAlias - const keyAlias = componentsProps?.keyAlias + const labelAlias = componentsProps?.props?.label + const keyAlias = componentsProps?.props?.key return componentsProps?.options?.map((option) => { if (option?.options?.length) { return optionGroupDefaultSlot ? ( @@ -34,9 +34,9 @@ export const useRenderSelect = () => { const renderSelectOptionItem = (item: FormSchema, option: SelectOption) => { // 如果有别名,就取别名 const componentsProps = item.componentProps as SelectComponentProps - const labelAlias = componentsProps?.labelAlias - const valueAlias = componentsProps?.valueAlias - const keyAlias = componentsProps?.keyAlias + const labelAlias = componentsProps?.props?.label + const valueAlias = componentsProps?.props?.value + const keyAlias = componentsProps?.props?.key const optionDefaultSlot = componentsProps.slots?.optionDefault return ( diff --git a/src/components/Form/src/helper.ts b/src/components/Form/src/helper.ts index 7f9a6facc..85efd8e72 100644 --- a/src/components/Form/src/helper.ts +++ b/src/components/Form/src/helper.ts @@ -5,7 +5,7 @@ import { PlaceholderMoel } from './types' import { FormSchema } from '@/types/form.d' import { ColProps, ComponentNameEnum } from '@/types/components.d' import { isFunction } from '@/utils/is' -import { firstUpperCase } from '@/utils' +import { firstUpperCase, humpToDash } from '@/utils' const { t } = useI18n() @@ -123,11 +123,11 @@ export const setItemComponentSlots = (slotsProps: Recordable = {}): Recordable = for (const key in slotsProps) { if (slotsProps[key]) { if (isFunction(slotsProps[key])) { - slotObj[key] = (...args: any[]) => { + slotObj[humpToDash(key)] = (...args: any[]) => { return slotsProps[key]?.(...args) } } else { - slotObj[key] = () => { + slotObj[humpToDash(key)] = () => { return slotsProps[key] } } diff --git a/src/types/components.d.ts b/src/types/components.d.ts index 1c6774817..5471e40e6 100644 --- a/src/types/components.d.ts +++ b/src/types/components.d.ts @@ -1,4 +1,4 @@ -import { CSSProperties } from 'vue' +import { CSSProperties, VNodeProps, VNode } from 'vue' import { InputProps, AutocompleteProps, @@ -148,7 +148,7 @@ export interface InputNumberComponentProps { style?: CSSProperties } -interface SelectOption { +export interface SelectOption { label?: string disabled?: boolean value?: any @@ -208,19 +208,14 @@ export interface SelectComponentProps { | 'right-end' maxCollapseTags?: number /** - * label别名 + * 数据源的字段别名 */ - labelAlias?: string - - /** - * value别名 - */ - valueAlias?: string - - /** - * key别名 - */ - keyAlias?: string + props?: { + key?: string + value?: string + label?: string + children?: string + } on?: { change?: (value: string | number | boolean | Object) => void visibleChange?: (visible: boolean) => void @@ -390,14 +385,17 @@ export interface ColorPickerComponentProps { export interface TransferComponentProps { value?: any[] - data?: Array<{ key; label; disabled }> + data?: any[] filterable?: boolean filterPlaceholder?: string filterMethod?: (query: string, item: any) => boolean targetOrder?: string titles?: string[] buttonTexts?: string[] - renderContent?: (h: any, option: any) => JSX.Element + renderContent?: ( + h: (type: string, props: VNodeProps | null, children?: string) => VNode, + option: any + ) => JSX.Element format?: { noChecked?: string hasChecked?: string @@ -411,7 +409,11 @@ export interface TransferComponentProps { rightDefaultChecked?: any[] validateEvent?: boolean on?: { - change?: (value: any[]) => void + change?: ( + value: number | string, + direction: 'left' | 'right', + movedKeys: string[] | number[] + ) => void leftCheckChange?: (value: any[]) => void rightCheckChange?: (value: any[]) => void } @@ -423,6 +425,36 @@ export interface TransferComponentProps { style?: CSSProperties } +export interface RadioOption { + label?: string + value?: string | number | boolean + disabled?: boolean + [key: string]: any +} +export interface RadioComponentProps { + value?: string | number | boolean + label?: string | number | boolean + disabled?: boolean + border?: boolean + size?: ElementPlusSize + options?: RadioOption[] + /** + * 数据源的字段别名 + */ + props: { + label?: string + value?: string + disabled?: string + } + name?: string + on?: { + change?: (value: string | number | boolean) => void + } + slots?: { + default?: (...args: any[]) => JSX.Element | null + } +} + export interface ColProps { span?: number xs?: number diff --git a/src/types/form.d.ts b/src/types/form.d.ts index 2612acb3e..899a921bd 100644 --- a/src/types/form.d.ts +++ b/src/types/form.d.ts @@ -12,7 +12,8 @@ import { SwitchComponentProps, RateComponentProps, ColorPickerComponentProps, - TransferComponentProps + TransferComponentProps, + RadioComponentProps } from '@/types/components' import { FormValueType, FormValueType } from '@/types/form' import type { AxiosPromise } from 'axios' @@ -36,26 +37,28 @@ export type FormItemProps = { } // 定义联合类型和条件类型 -type ComponentPropsForComponent = T extends 'input' +type ComponentPropsForComponent = T extends 'Input' ? InputComponentProps - : T extends 'autocomplete' + : T extends 'Autocomplete' ? AutocompleteComponentProps - : T extends 'inputNumber' + : T extends 'InputNumber' ? InputNumberComponentProps - : T extends 'select' + : T extends 'Select' ? SelectComponentProps - : T extends 'selectV2' + : T extends 'SelectV2' ? SelectV2ComponentProps - : T extends 'cascader' + : T extends 'Cascader' ? CascaderComponentProps - : T extends 'switch' + : T extends 'Switch' ? SwitchComponentProps - : T extends 'rate' + : T extends 'Rate' ? RateComponentProps - : T extends 'colorPicker' + : T extends 'ColorPicker' ? ColorPickerComponentProps - : T extends 'transfer' + : T extends 'Transfer' ? TransferComponentProps + : T extends 'Radio' + ? RadioComponentProps : any export interface FormSchema { @@ -93,6 +96,7 @@ export interface FormSchema { | RateComponentProps | ColorPickerComponentProps | TransferComponentProps + | RadioComponentProps /** * formItem组件属性,具体可以查看element-plus文档 diff --git a/src/utils/index.ts b/src/utils/index.ts index aa00c95ca..77aca1242 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -36,6 +36,13 @@ export const underlineToHump = (str: string): string => { }) } +/** + * 驼峰转横杠 + */ +export const humpToDash = (str: string): string => { + return str.replace(/([A-Z])/g, '-$1').toLowerCase() +} + export const setCssVar = (prop: string, val: any, dom = document.documentElement) => { dom.style.setProperty(prop, val) } diff --git a/src/views/Components/Form/DefaultForm.vue b/src/views/Components/Form/DefaultForm.vue index 9abdabfec..7eb02cedf 100644 --- a/src/views/Components/Form/DefaultForm.vue +++ b/src/views/Components/Form/DefaultForm.vue @@ -8,7 +8,7 @@ import { useAppStore } from '@/store/modules/app' import { FormSchema } from '@/types/form' import { ComponentOptions, SelectOption, SelectComponentProps } from '@/types/components' import { useForm } from '@/hooks/web/useForm' -import { ElOption, ElOptionGroup } from 'element-plus' +import { ElOption, ElOptionGroup, ElButton } from 'element-plus' const appStore = useAppStore() @@ -840,92 +840,117 @@ const schema = reactive([ field: 'field34', label: t('formDemo.transfer'), component: 'Divider' + }, + { + field: 'field35', + label: t('formDemo.default'), + component: 'Transfer', + componentProps: { + props: { + key: 'value', + label: 'desc' + }, + data: generateData() + }, + value: [], + colProps: { + span: 24 + } + }, + { + field: 'field36', + label: t('formDemo.slot'), + component: 'Transfer', + componentProps: { + props: { + key: 'value', + label: 'desc' + }, + filterable: true, + leftDefaultChecked: [2, 3], + rightDefaultChecked: [1], + titles: ['Source', 'Target'], + buttonTexts: ['To Left', 'To Right'], + format: { + noChecked: '${total}', + hasChecked: '${checked}/${total}' + }, + data: generateData(), + slots: { + default: ({ option }) => { + return ( + + {option.value} - {option.desc} + + ) + }, + leftFooter: () => { + return ( + + Operation + + ) + }, + rightFooter: () => { + return ( + + Operation + + ) + } + } + }, + value: [1], + colProps: { + span: 24 + } + }, + { + field: 'field37', + label: `${t('formDemo.render')}`, + component: 'Transfer', + componentProps: { + props: { + key: 'value', + label: 'desc', + disabled: 'disabled' + }, + leftDefaultChecked: [2, 3], + rightDefaultChecked: [1], + data: generateData(), + renderContent: (h, option) => { + return h('span', null, `${option.value} - ${option.desc}`) + } + }, + value: [1], + colProps: { + span: 24 + } + }, + { + field: 'field38', + label: t('formDemo.radio'), + component: 'Divider' + }, + { + field: 'field39', + label: t('formDemo.default'), + component: 'Radio', + componentProps: { + options: [ + { + // disabled: true, + label: 'option-1', + value: '1' + }, + { + label: 'option-2', + value: '2' + } + ] + } } // { - // field: 'field35', - // label: t('formDemo.default'), - // component: 'Transfer', - // componentProps: { - // props: { - // key: 'value', - // label: 'desc', - // disabled: 'disabled' - // }, - // data: generateData() - // }, - // value: [], - // colProps: { - // span: 24 - // } - // }, - // { - // field: 'field36', - // label: t('formDemo.slot'), - // component: 'Transfer', - // componentProps: { - // props: { - // key: 'value', - // label: 'desc', - // disabled: 'disabled' - // }, - // leftDefaultChecked: [2, 3], - // rightDefaultChecked: [1], - // data: generateData(), - // slots: { - // default: true - // } - // }, - // value: [1], - // colProps: { - // span: 24 - // } - // }, - // { - // field: 'field37', - // label: `${t('formDemo.render')}`, - // component: 'Transfer', - // componentProps: { - // props: { - // key: 'value', - // label: 'desc', - // disabled: 'disabled' - // }, - // leftDefaultChecked: [2, 3], - // rightDefaultChecked: [1], - // data: generateData(), - // renderContent: (h: Fn, option: Recordable) => { - // return h('span', null, `${option.value} - ${option.desc}`) - // } - // }, - // value: [1], - // colProps: { - // span: 24 - // } - // }, - // { - // field: 'field38', - // label: t('formDemo.radio'), - // component: 'Divider' - // }, - // { - // field: 'field39', - // label: t('formDemo.default'), - // component: 'Radio', - // componentProps: { - // options: [ - // { - // disabled: true, - // label: 'option-1', - // value: '1' - // }, - // { - // label: 'option-2', - // value: '2' - // } - // ] - // } - // }, - // { // field: 'field40', // label: t('formDemo.button'), // component: 'RadioButton', @@ -1360,4 +1385,9 @@ const changeToggle = () => { transform: translateX(-50%); } } + +.transfer-footer { + margin-left: 15px; + padding: 6px 5px; +}