UNPKG

vxe-pc-ui

Version:
508 lines (464 loc) 16.9 kB
import { defineComponent, ref, h, PropType, reactive, provide, watch, nextTick, ComponentOptions, createCommentVNode } from 'vue' import { VxeUI, getConfig, getIcon, getI18n, renderer, useSize, createEvent, renderEmptyElement } from '../../ui' import { toCssUnit } from '../../ui/src/dom' import { FormDesignWidgetInfo, getWidgetConfig, getWidgetConfigCustomGroup, configToWidget } from './widget-info' import XEUtils from 'xe-utils' import VxeButtonComponent from '../../button/src/button' import LayoutWidgetComponent from './layout-widget' import LayoutPreviewComponent from './layout-preview' import LayoutSettingComponent from './layout-setting' import LayoutStyleComponent from './layout-style' import { getDefaultSettingFormData } from './default-setting-data' import type { VxeFormDesignDefines, VxeFormDesignPropTypes, VxeFormDesignEmits, FormDesignInternalData, FormDesignReactData, FormDesignPrivateRef, VxeFormDesignPrivateComputed, VxeFormDesignConstructor, VxeFormDesignPrivateMethods, FormDesignMethods, FormDesignPrivateMethods } from '../../../types' export default defineComponent({ name: 'VxeFormDesign', props: { size: { type: String as PropType<VxeFormDesignPropTypes.Size>, default: () => getConfig().formDesign.size || getConfig().size }, config: Object as PropType<VxeFormDesignPropTypes.Config>, height: { type: [String, Number] as PropType<VxeFormDesignPropTypes.Height>, default: () => getConfig().formDesign.height }, widgets: { type: Array as PropType<VxeFormDesignPropTypes.Widgets>, default: () => XEUtils.clone(getConfig().formDesign.widgets) || [] }, showHeader: { type: Boolean as PropType<VxeFormDesignPropTypes.ShowHeader>, default: () => getConfig().formDesign.showHeader }, showPc: { type: Boolean as PropType<VxeFormDesignPropTypes.ShowPc>, default: () => getConfig().formDesign.showPc }, showMobile: { type: Boolean as PropType<VxeFormDesignPropTypes.ShowMobile>, default: () => getConfig().formDesign.showMobile }, formRender: Object as PropType<VxeFormDesignPropTypes.FormRender> }, emits: [ 'click-widget', 'add-widget', 'copy-widget', 'remove-widget', 'drag-widget' ] as VxeFormDesignEmits, setup (props, context) { const { emit, slots } = context const xID = XEUtils.uniqueId() const refElem = ref<HTMLDivElement>() const refLayoutStyle = ref<any>() const { computeSize } = useSize(props) const reactData = reactive<FormDesignReactData>({ formData: {} as VxeFormDesignDefines.DefaultSettingFormDataObjVO, widgetConfigs: [], widgetObjList: [], dragWidget: null, sortWidget: null, activeWidget: null }) const internalData = reactive<FormDesignInternalData>({ }) const refMaps: FormDesignPrivateRef = { refElem } const computeMaps: VxeFormDesignPrivateComputed = { computeSize } const $xeFormDesign = { xID, props, context, reactData, internalData, getRefMaps: () => refMaps, getComputeMaps: () => computeMaps } as unknown as VxeFormDesignConstructor & VxeFormDesignPrivateMethods const createWidget = (name: string) => { return new FormDesignWidgetInfo($xeFormDesign, name, reactData.widgetObjList) as VxeFormDesignDefines.WidgetObjItem } const createEmptyWidget = () => { return new FormDesignWidgetInfo($xeFormDesign, '', reactData.widgetObjList) as VxeFormDesignDefines.WidgetObjItem } const loadConfig = (config: Partial<VxeFormDesignDefines.FormDesignConfig>) => { if (config) { const { formConfig, widgetData } = config if (formConfig) { loadFormConfig(formConfig) } if (widgetData) { loadWidgetData(widgetData) } } const { activeWidget, widgetObjList } = reactData if (activeWidget) { const rest = XEUtils.findTree(widgetObjList, item => item.id === activeWidget.id, { children: 'children' }) if (rest) { reactData.activeWidget = rest.item } else { reactData.activeWidget = widgetObjList[0] || null } } else { reactData.activeWidget = widgetObjList[0] || null } return nextTick() } const reloadConfig = (config: Partial<VxeFormDesignDefines.FormDesignConfig>) => { clearConfig() return loadConfig(config) } const getFormConfig = (): VxeFormDesignPropTypes.FormData => { return XEUtils.clone(reactData.formData, true) } const loadFormConfig = (data: VxeFormDesignPropTypes.FormData) => { reactData.formData = Object.assign({}, createSettingForm(), data) as VxeFormDesignDefines.DefaultSettingFormDataObjVO return nextTick() } const getWidgetById = (id: number | string | null | undefined) => { const { widgetObjList } = reactData if (id) { const widgetId = XEUtils.toNumber(id) const rest = XEUtils.findTree(widgetObjList, item => item && item.id === widgetId, { children: 'children' }) if (rest) { return rest.item } } return null } const getWidgetData = (): VxeFormDesignDefines.WidgetObjItem[] => { const objList = XEUtils.clone(reactData.widgetObjList, true) XEUtils.eachTree(objList, item => { item.model.value = null }, { children: 'children' }) return objList } const loadWidgetData = (widgetData: VxeFormDesignDefines.WidgetObjItem[]) => { reactData.widgetObjList = (widgetData || []).map(item => configToWidget(item)) return nextTick() } const openStyleSetting = () => { const $layoutStyle = refLayoutStyle.value if ($layoutStyle) { $layoutStyle.openStylePreview() } return nextTick() } const clearConfig = () => { reactData.widgetObjList = [] initSettingForm() return nextTick() } const formDesignMethods: FormDesignMethods = { dispatchEvent (type, params, evnt) { emit(type, createEvent(evnt, { $xeFormDesign }, params)) }, createWidget, createEmptyWidget, getConfig () { return { formConfig: getFormConfig(), widgetData: getWidgetData() } }, clearConfig, loadConfig, reloadConfig, getFormConfig, loadFormConfig, getWidgetById, getFormData () { const { widgetObjList } = reactData const formData: Record<string, any> = {} XEUtils.eachTree(widgetObjList, widget => { formData[widget.field] = null }, { children: 'children' }) return formData }, getWidgetData, loadWidgetData, refreshPreviewView () { const $layoutStyle = refLayoutStyle.value if ($layoutStyle) { $layoutStyle.updatePreviewView() } return nextTick() }, openStyleSetting } const updateWidgetConfigs = () => { const { widgets } = props const widgetConfs: VxeFormDesignDefines.WidgetConfigGroup[] = [] const baseWidgets: VxeFormDesignDefines.WidgetObjItem[] = [] const layoutWidgets: VxeFormDesignDefines.WidgetObjItem[] = [] const advancedWidgets: VxeFormDesignDefines.WidgetObjItem[] = [] const customGroups: VxeFormDesignDefines.WidgetConfigGroup[] = [] renderer.forEach((item, name) => { const { createFormDesignWidgetConfig } = item if (createFormDesignWidgetConfig) { const widthItem = createWidget(name) const widgetConf = getWidgetConfig(name) const widgetCustomGroup = getWidgetConfigCustomGroup(name, $xeFormDesign) // 如果自定义组 if (widgetCustomGroup) { const cusGroup = customGroups.find(item => item.title === widgetCustomGroup) if (cusGroup) { cusGroup.children.push(widthItem) } else { customGroups.push({ title: widgetCustomGroup, children: [widthItem] }) } } else { switch (widgetConf.group) { case 'layout': layoutWidgets.push(widthItem) break case 'advanced': advancedWidgets.push(widthItem) break default: // 已废弃 title if (!['title'].includes(widthItem.name)) { baseWidgets.push(widthItem) } break } } } }) if (baseWidgets.length) { widgetConfs.push({ group: 'base', children: baseWidgets }) } if (layoutWidgets.length) { widgetConfs.push({ group: 'layout', children: layoutWidgets }) } if (advancedWidgets.length) { widgetConfs.push({ group: 'advanced', children: advancedWidgets }) } if (customGroups.length) { widgetConfs.push(...customGroups) } if (widgets && widgets.length) { reactData.widgetConfigs = props.widgets.map(config => { return { title: config.customGroup, group: config.group, children: config.children ? config.children.map(name => { const widthItem = createWidget(name) return widthItem }) : [] } }) } else { reactData.widgetConfigs = widgetConfs } } const validWidgetUnique = (widgetName: string) => { const { widgetObjList } = reactData const widgetConf = getWidgetConfig(widgetName) if (widgetConf.unique) { const existWidgetList: VxeFormDesignDefines.WidgetObjItem[] = [] XEUtils.eachTree(widgetObjList, obj => { if (obj.name === widgetName) { existWidgetList.push(obj) } }, { children: 'children' }) const status = existWidgetList.length < 1 if (!status) { if (VxeUI.modal) { VxeUI.modal.message({ content: getI18n('vxe.formDesign.error.wdFormUni'), status: 'error', id: 'wdFormUni' }) } } return status } return true } const formDesignPrivateMethods: FormDesignPrivateMethods = { validWidgetUnique, handleClickWidget (evnt: KeyboardEvent, item: VxeFormDesignDefines.WidgetObjItem) { if (item && item.name) { evnt.stopPropagation() reactData.activeWidget = item formDesignMethods.dispatchEvent('click-widget', { widget: item }, evnt) } }, handleCopyWidget (evnt: KeyboardEvent, widget: VxeFormDesignDefines.WidgetObjItem) { const { widgetObjList } = reactData const rest = XEUtils.findTree(widgetObjList, obj => obj.id === widget.id, { children: 'children' }) if (rest) { evnt.stopPropagation() if (validWidgetUnique(widget.name)) { const { path } = rest const rootIndex = Number(path[0]) const newWidget = createWidget(widget.name) // 标题副本 if (newWidget.title) { newWidget.title = getI18n('vxe.formDesign.widget.copyTitle', [`${widget.title}`.replace(getI18n('vxe.formDesign.widget.copyTitle', ['']), '')]) } if (rootIndex >= widgetObjList.length - 1) { widgetObjList.push(newWidget) } else { widgetObjList.splice(rootIndex + 1, 0, newWidget) } reactData.activeWidget = newWidget reactData.widgetObjList = [...widgetObjList] formDesignMethods.dispatchEvent('copy-widget', { widget, newWidget }, evnt) } } }, handleRemoveWidget (evnt: KeyboardEvent, widget: VxeFormDesignDefines.WidgetObjItem) { const { widgetObjList } = reactData const rest = XEUtils.findTree(widgetObjList, obj => obj.id === widget.id, { children: 'children' }) if (rest) { const { index, parent, items } = rest evnt.stopPropagation() if (index >= items.length - 1) { reactData.activeWidget = items[index - 1] } else { reactData.activeWidget = items[index + 1] || null } // 如果是行控件,使用空的控件占位 if (parent && parent.name === 'row') { items[index] = createEmptyWidget() } else { items.splice(index, 1) } reactData.widgetObjList = [...widgetObjList] formDesignMethods.dispatchEvent('remove-widget', { widget }, evnt) } } } const createSettingForm = () => { const { formRender, showPc, showMobile } = props let conf: Record<string, any> = getDefaultSettingFormData({ pcVisible: showPc, mobileVisible: showMobile }) // 如果为自定义渲染 if (formRender) { const compConf = renderer.get(formRender.name) const createFormConfig = compConf ? compConf.createFormDesignSettingFormConfig : null conf = (createFormConfig ? createFormConfig({}) : {}) || {} } return conf as VxeFormDesignDefines.DefaultSettingFormDataObjVO } const initSettingForm = () => { reactData.formData = createSettingForm() } const openStylePreviewEvent = () => { openStyleSetting() } Object.assign($xeFormDesign, formDesignMethods, formDesignPrivateMethods) const renderLayoutHeader = () => { const extraSlot = slots.extra return h('div', { class: 'vxe-form-design--header-wrapper' }, [ h('div', { class: 'vxe-form-design--header-left' }), h('div', { class: 'vxe-form-design--header-middle' }), h('div', { class: 'vxe-form-design--header-right' }, [ extraSlot ? h('div', { class: 'vxe-form-design--header-extra' }, extraSlot({})) : renderEmptyElement($xeFormDesign), h('div', { class: 'vxe-form-design--header-setting' }, [ h(VxeButtonComponent, { mode: 'text', status: 'primary', icon: getIcon().FORM_DESIGN_STYLE_SETTING, content: getI18n('vxe.formDesign.styleSetting.btn'), onClick: openStylePreviewEvent }) ]) ]) ]) } const renderVN = () => { const { height, showHeader } = props const vSize = computeSize.value const headerSlot = slots.header const footerSlot = slots.footer return h('div', { ref: refElem, class: ['vxe-form-design', { [`size--${vSize}`]: vSize }], style: height ? { height: toCssUnit(height) } : null }, [ showHeader || headerSlot ? h('div', { class: 'vxe-form-design--header' }, headerSlot ? headerSlot({}) : renderLayoutHeader()) : createCommentVNode(), h('div', { class: 'vxe-form-design--body' }, [ h(LayoutWidgetComponent), h(LayoutPreviewComponent), h(LayoutSettingComponent), h(LayoutStyleComponent as ComponentOptions, { ref: refLayoutStyle }) ]), footerSlot ? h('div', { class: 'vxe-form-design--footer' }, footerSlot ? footerSlot({}) : []) : createCommentVNode() ]) } $xeFormDesign.renderVN = renderVN watch(() => props.widgets, () => { updateWidgetConfigs() }) watch(() => props.widgets, () => { updateWidgetConfigs() }) watch(() => props.config, (value) => { loadConfig(value || {}) }) initSettingForm() updateWidgetConfigs() if (props.config) { loadConfig(props.config) } provide('$xeFormDesign', $xeFormDesign) return $xeFormDesign }, render () { return this.renderVN() } })