| | |
| | | defineComponent, |
| | | PropType, |
| | | ref, |
| | | Ref, |
| | | onMounted, |
| | | SetupContext, |
| | | computed, |
| | | unref, |
| | | markRaw, |
| | | DefineComponent, |
| | | Component, |
| | | watch, |
| | | Fragment, |
| | | useSlots, |
| | | } from 'vue' |
| | | import styles from './DyForm.module.scss' |
| | | import ElInput from 'element-plus/es/components/input/index' |
| | | import DyInput from '../Input/Input' |
| | | import Option from '@/components/Select/Option' |
| | | import Select from '@/components/Select/Select' |
| | | import SelectInput from '@/components/SelectInput/SelectInput' |
| | | import SearchSelect from '@/components/SearchSelect/SearchSelect' |
| | | import type { FormInstance } from 'element-plus' |
| | | import Icon from '../Icon/Icon' |
| | | import { Warning } from '@element-plus/icons-vue' |
| | | import RelationFlowDialog from '@/components/RelationFlowDialog/RelationFlowDialog' |
| | | import { |
| | | FormPropsType, |
| | | FormItemPropType, |
| | |
| | | import TextareaFlow from '../Flow/Flow' |
| | | import get from 'lodash/get' |
| | | import set from 'lodash/set' |
| | | import { has } from 'lodash' |
| | | import Tab from '@/components/Tab/Tab' |
| | | import TabPane from '@/components/Tab/TabPane' |
| | | import ElInputNumber from 'element-plus/es/components/input-number/index' |
| | | import { getCurrentLang, Language } from '@/libs/Language/Language' |
| | | |
| | | const formItemElementMap = markRaw<Record<string, any>>({ |
| | | input: ElInput, |
| | | const formItemElementMap: Record<string, any> = markRaw({ |
| | | input: DyInput, |
| | | inputNumber: ElInputNumber, |
| | | select: Select, |
| | | selectInput: SelectInput, |
| | | flow: RelationFlowDialog, |
| | | variable: Variable, |
| | | textareaFlow: TextareaFlow, |
| | | filterSelect: SearchSelect, |
| | | switch: (props: PropType<any>, { attrs }: SetupContext) => { |
| | | return <el-switch {...attrs} /> |
| | | }, |
| | | dateTime: (props: PropType<any>, { attrs }: SetupContext) => { |
| | | return <el-date-picker |
| | | type="datetime" |
| | | format="YYYY-MM-DD HH:mm:ss" |
| | | {...attrs} |
| | | ></el-date-picker> |
| | | }, |
| | | date: (props: PropType<any>, { attrs }: SetupContext) => { |
| | | return <el-date-picker |
| | | type="date" |
| | | format="YYYY-MM-DD" |
| | | {...attrs} |
| | | ></el-date-picker> |
| | | }, |
| | | }) |
| | | |
| | | const Type: Record<string, any> = { |
| | | select: 'select', |
| | | } |
| | | export default defineComponent<FormPropsType>({ |
| | | export default defineComponent({ |
| | | //@ts-ignore |
| | | name: '动态表单', |
| | | props: { |
| | |
| | | default: () => ({}), |
| | | }, |
| | | formItemProps: { |
| | | type: Array, |
| | | type: Array as PropType<FormItemPropType[]>, |
| | | default: () => [], |
| | | }, |
| | | inLine: { |
| | | type: Boolean, |
| | | default: false, |
| | | }, |
| | | customWidgetMap: { |
| | | type: Object, |
| | | default: () => ({}), |
| | | }, |
| | | isCategory: { |
| | | type: Boolean, |
| | | default: false, |
| | | }, |
| | | LanguageScopeKey: { |
| | | type: String, |
| | | default: '', |
| | | }, |
| | | }, |
| | | setup(props: PropsType, { attrs, emit, expose }: SetupContext) { |
| | | const formRef = ref<FormInstance>() |
| | | const active = ref('') |
| | | const isZh = ref(true) |
| | | const slots = useSlots() |
| | | const form: any = computed({ |
| | | get() { |
| | | return props.formData |
| | |
| | | emit('update:formData', v) |
| | | }, |
| | | }) |
| | | const formPropsRef = ref<any>({}) |
| | | |
| | | const currentWidgetModel = computed(() => { |
| | | return (path: string) => { |
| | | if (path.includes('.')) { |
| | | const args = path.split('.') |
| | | return get(form.value, args) |
| | | } |
| | | return get(form.value, path) |
| | | } |
| | | }) |
| | |
| | | }) |
| | | }) |
| | | } |
| | | /** |
| | | * 获取refs |
| | | * @returns |
| | | */ |
| | | const getRefByKey = (key: string) => { |
| | | if (key) return formPropsRef.value[key] |
| | | return formPropsRef.value |
| | | } |
| | | |
| | | const resetForm = () => { |
| | | if (!formRef.value) return false |
| | |
| | | } |
| | | |
| | | const formItemProps = computed(() => { |
| | | if (props.isCategory) { |
| | | const tabMap: Record<string, FormItemPropType> = {} |
| | | const tabs: FormItemPropType[] = [] |
| | | if (Array.isArray(props.formItemProps)) { |
| | | props.formItemProps.forEach((item: any) => { |
| | | tabMap[item.category] = tabMap[item.category] || [] |
| | | tabMap[item.category].push(item) |
| | | }) |
| | | Object.keys(tabMap).forEach((key: string) => { |
| | | tabs.push({ |
| | | name: key, |
| | | content: tabMap[key], |
| | | }) |
| | | }) |
| | | } |
| | | return tabs |
| | | } |
| | | |
| | | return props.formItemProps || [] |
| | | }) |
| | | |
| | | expose({ validate, resetForm }) |
| | | |
| | | const FormRender: any = ($props: any) => { |
| | | const item: FormItemPropType = $props.item |
| | |
| | | if (item.el && Type[item.el as string]) { |
| | | return options.map((el: OptionItemType) => ( |
| | | <Option |
| | | label={el.label || el.description || el.name} |
| | | value={el.value} |
| | | ></Option> |
| | | label={el.label || el.description || el.name} |
| | | class={styles.optionLabel} |
| | | key={el.value} |
| | | > |
| | | {el.label || el.description || el.name} |
| | | {el.tip ? ( |
| | | <el-tooltip |
| | | class="box-item" |
| | | effect="dark" |
| | | content={el.tip} |
| | | placement="top" |
| | | key={el.value} |
| | | persistent={false} |
| | | > |
| | | <el-icon> |
| | | <Warning /> |
| | | </el-icon> |
| | | </el-tooltip> |
| | | ) : null} |
| | | </Option> |
| | | )) |
| | | } |
| | | return null |
| | | } |
| | | |
| | | const onUpdateModelValue = (v: string | number, prop: string) => { |
| | | set(form.value, prop, v) |
| | | const onUpdateModelValue = (v: string | number, path: string) => { |
| | | if (path.includes('.')) { |
| | | const args = path.split('.') |
| | | return set(form.value, args, v) |
| | | } |
| | | set(form.value, path, v) |
| | | } |
| | | |
| | | const initFormData = () => { |
| | | formItemProps.value.forEach((item: FormItemPropType) => { |
| | | form.value[item.prop] = form.value[item.prop] || item.defaultValue |
| | | }) |
| | | active.value = formItemProps.value[0].name |
| | | } |
| | | |
| | | const checkZh = (lang: string) => { |
| | | const languageStr = lang.toLowerCase() |
| | | if (languageStr.includes('zh')) return true |
| | | if (languageStr.includes('original')) return true |
| | | return false |
| | | } |
| | | |
| | | const onLanguageChange = () => { |
| | | Language.useChange((language: any) => { |
| | | isZh.value = checkZh(language.lang) |
| | | }) |
| | | } |
| | | |
| | | onMounted(() => { |
| | | isZh.value = checkZh(getCurrentLang()) |
| | | onLanguageChange() |
| | | }) |
| | | |
| | | const RenderItemProps = ($props: any) => { |
| | | const WidgetMap = { |
| | | ...formItemElementMap, |
| | | ...props.customWidgetMap, |
| | | } |
| | | const formItemProps = $props.formItemProps |
| | | |
| | | return ( |
| | | <Fragment> |
| | | {formItemProps.map((item: FormItemPropType, index: number) => { |
| | | if (item.isTitle) { |
| | | if (typeof item.title === 'string') { |
| | | return <Title style="margin-bottom: 10px">{item.title}</Title> |
| | | } |
| | | return item.title |
| | | } |
| | | |
| | | const itemProps: FormItemPropType = {} |
| | | Object.entries(item).forEach(([key, value]) => { |
| | | itemProps[key] = unref(value) |
| | | }) |
| | | const el = |
| | | typeof itemProps.el === 'string' |
| | | ? WidgetMap[itemProps.el] |
| | | : itemProps.el || null |
| | | const hasSlot = itemProps.slot |
| | | const Component = hasSlot ? slots[itemProps.el] : el |
| | | |
| | | const isHide = has(item.isHide, 'value') |
| | | ? item.isHide?.value |
| | | : item.isHide |
| | | return Component && !isHide ? ( |
| | | <el-form-item |
| | | label={itemProps.label} |
| | | prop={itemProps.prop} |
| | | rules={itemProps.rules} |
| | | key={itemProps.prop + index} |
| | | class={styles.itemDistance} |
| | | labelPosition={itemProps.labelPosition} |
| | | labelWidth={isZh.value ? props.labelWidth : 'auto'} |
| | | vSlots={ |
| | | itemProps.icon |
| | | ? { |
| | | label: () => ( |
| | | <label |
| | | key={itemProps.prop} |
| | | class={styles.formitemPropsLabel} |
| | | > |
| | | {itemProps.label} |
| | | {itemProps.icon ? ( |
| | | <el-tooltip |
| | | class="box-item" |
| | | effect="dark" |
| | | content={itemProps.tip} |
| | | placement="top" |
| | | raw-content={itemProps.rawContent} |
| | | persistent={false} |
| | | > |
| | | <Icon |
| | | style="margin-left: 5px" |
| | | icon={itemProps.icon} |
| | | /> |
| | | </el-tooltip> |
| | | ) : null} |
| | | </label> |
| | | ), |
| | | } |
| | | : null |
| | | } |
| | | > |
| | | <Component |
| | | style={{ |
| | | width: itemProps.width, |
| | | height: itemProps.height, |
| | | }} |
| | | {...itemProps} |
| | | placeholder={itemProps.placeholder} |
| | | ref={(ref) => (formPropsRef.value[itemProps.prop] = ref)} |
| | | modelValue={currentWidgetModel.value(itemProps.prop)} |
| | | onUpdate:modelValue={(val: string | number) => |
| | | onUpdateModelValue(val, itemProps.prop) |
| | | } |
| | | > |
| | | <FormRender item={itemProps} /> |
| | | </Component> |
| | | </el-form-item> |
| | | ) : null |
| | | })} |
| | | </Fragment> |
| | | ) |
| | | } |
| | | |
| | | expose({ validate, resetForm, initFormData, getRefByKey }) |
| | | |
| | | return () => { |
| | | return ( |
| | |
| | | <el-form |
| | | labelPosition={props.labelPosition} |
| | | labelWidth={props.labelWidth} |
| | | // labelWidth={isZh.value ? props.labelWidth : 'auto'} |
| | | model={form.value} |
| | | ref={formRef} |
| | | inline={props.inLine} |
| | | > |
| | | {formItemProps.value.map( |
| | | (item: FormItemPropType, index: number) => { |
| | | if (item.isTitle) { |
| | | if (typeof item.title === 'string') { |
| | | return ( |
| | | <Title style="margin-bottom: 10px">{item.title}</Title> |
| | | ) |
| | | } |
| | | return item.title |
| | | } |
| | | |
| | | const itemProps: FormItemPropType = {} |
| | | Object.entries(item).forEach(([key, value]) => { |
| | | itemProps[key] = unref(value) |
| | | }) |
| | | |
| | | const el = |
| | | typeof itemProps.el === 'string' |
| | | ? formItemElementMap[itemProps.el] |
| | | : itemProps.el || null |
| | | const Component = el |
| | | return Component && !item.isHide ? ( |
| | | <el-form-item |
| | | label={itemProps.label} |
| | | prop={itemProps.prop} |
| | | rules={itemProps.rules} |
| | | key={itemProps.prop} |
| | | vSlots={ |
| | | itemProps.labelIcon |
| | | ? { |
| | | label: () => ( |
| | | <label class={styles.formitemPropsLabel}> |
| | | {itemProps.label} |
| | | <Icon icon={itemProps.labelIcon} /> |
| | | </label> |
| | | ), |
| | | } |
| | | : null |
| | | } |
| | | > |
| | | <Component |
| | | style={{ |
| | | width: itemProps.width, |
| | | height: itemProps.height, |
| | | }} |
| | | {...itemProps} |
| | | // v-model={form.value[itemProps.prop as keyof any]} |
| | | modelValue={currentWidgetModel.value(itemProps.prop)} |
| | | onUpdate:modelValue={(val: string | number) => |
| | | onUpdateModelValue(val, itemProps.prop) |
| | | } |
| | | > |
| | | <FormRender item={itemProps} /> |
| | | </Component> |
| | | </el-form-item> |
| | | ) : null |
| | | } |
| | | {props.isCategory ? ( |
| | | <Tab |
| | | active={active.value} |
| | | class={styles.tabContent} |
| | | size="small" |
| | | type="params" |
| | | > |
| | | {formItemProps.value.map((item: any) => { |
| | | return ( |
| | | <TabPane key={item.name} label={item.name} name={item.name}> |
| | | <RenderItemProps formItemProps={item.content} /> |
| | | </TabPane> |
| | | ) |
| | | })} |
| | | </Tab> |
| | | ) : ( |
| | | <RenderItemProps formItemProps={formItemProps.value} /> |
| | | )} |
| | | </el-form> |
| | | </div> |