vxe-pc-ui
Version:
A vue based PC component library
594 lines (564 loc) • 21.1 kB
text/typescript
import { ref, PropType, defineComponent, inject, h, createCommentVNode, TransitionGroup, computed, nextTick } from 'vue'
import { VxeUI, renderer, getIcon, getI18n } from '@vxe-ui/core'
import XEUtils from 'xe-utils'
import { hasFormDesignLayoutType } from '../src/util'
import { WidgetSubtableFormObjVO } from './subtable-data'
import { getSlotVNs } from '../../ui/src/vn'
import { useWidgetName } from '../../form-design/src/use'
import VxeFormItemComponent from '../../form/src/form-item'
import VxeButtonComponent from '../../button/src/button'
import VxeCheckboxComponent from '../../checkbox/src/checkbox'
import type { VxeGlobalRendererHandles, VxeFormDesignConstructor, VxeFormDesignDefines, VxeFormDesignPrivateMethods, VxeFormViewConstructor, VxeFormViewPrivateMethods } from '../../../types'
import type { VxeGridComponent, VxeGridPropTypes, VxeGridProps, VxeGridInstance } from '../../../types/components/grid'
import type { VxeTableDefines } from '../../../types/components/table'
import type { VxeColumnPropTypes } from '../../../types/components/column'
const ViewSubItemComponent = defineComponent({
props: {
parentWidget: {
type: Object as PropType<VxeFormDesignDefines.WidgetObjItem<WidgetSubtableFormObjVO>>,
default: () => ({})
},
widget: {
type: Object as PropType<VxeFormDesignDefines.WidgetObjItem<WidgetSubtableFormObjVO>>,
default: () => ({})
},
childIndex: {
type: Number,
default: 0
}
},
emits: [],
setup (props) {
const $xeFormDesign = inject<(VxeFormDesignConstructor & VxeFormDesignPrivateMethods) | null>('$xeFormDesign', null)
const $xeFormView = inject<(VxeFormViewConstructor & VxeFormViewPrivateMethods) | null>('$xeFormView', null)
if (!$xeFormDesign) {
return () => []
}
const { reactData: formDesignReactData } = $xeFormDesign
const sortDragstartSubItemEvent = (evnt: DragEvent) => {
evnt.stopPropagation()
const divEl = evnt.currentTarget as HTMLDivElement
const dataTransfer = evnt.dataTransfer
const widgetId = divEl.getAttribute('data-widget-id') || ''
const dragWidget = $xeFormDesign.getWidgetById(widgetId)
if (dataTransfer) {
dataTransfer.setData('text/plain', widgetId)
}
formDesignReactData.sortWidget = dragWidget
formDesignReactData.dragWidget = null
}
const sortDragenterSubItemEvent = (evnt: DragEvent) => {
const { parentWidget, childIndex } = props
const { widgetObjList, sortWidget } = formDesignReactData
const targetWidget = parentWidget.children[childIndex]
const formDesignInternalData = $xeFormDesign.internalData
const { lastDragTime } = formDesignInternalData
evnt.stopPropagation()
if (lastDragTime && lastDragTime > Date.now() - 300) {
evnt.preventDefault()
return
}
if (sortWidget && targetWidget && parentWidget) {
if (sortWidget.id === parentWidget.id) {
return
}
if (sortWidget.id === targetWidget.id) {
return
}
if (hasFormDesignLayoutType(sortWidget)) {
return
}
if (targetWidget && !hasFormDesignLayoutType(targetWidget)) {
const currRest = XEUtils.findTree(widgetObjList, item => item.id === sortWidget.id, { children: 'children' })
if (currRest) {
const { item, index, items, parent } = currRest
// 如果是 subtable 内移动
if (parent && parent.id === parentWidget.id) {
parentWidget.children[childIndex] = item
parentWidget.children[index] = targetWidget
} else {
parentWidget.children.splice(childIndex, 0, item)
items.splice(index, 1)
}
formDesignInternalData.lastDragTime = Date.now()
$xeFormDesign.dispatchEvent('drag-widget', { widget: item }, evnt)
}
}
}
}
return () => {
const { widget } = props
const { dragWidget, activeWidget, sortWidget } = formDesignReactData
const name = widget ? widget.name : ''
const compConf = renderer.get(name) || {}
const renderSubtableView = compConf.renderFormDesignWidgetSubtableEditView || compConf.renderFormDesignWidgetSubtableCellView || compConf.renderFormDesignWidgetSubtableDefaultView
const renderWidgetDesignView = compConf.renderFormDesignWidgetEdit || compConf.renderFormDesignWidgetView
const renderOpts: VxeGlobalRendererHandles.RenderFormDesignWidgetViewOptions = widget || { name }
const isEditMode = !!$xeFormDesign
const defParams: VxeGlobalRendererHandles.RenderFormDesignWidgetViewParams = { widget, readonly: false, disabled: false, isEditMode, isViewMode: !isEditMode, $formDesign: $xeFormDesign, $formView: $xeFormView }
const isActive = activeWidget && widget && activeWidget.id === widget.id
const subOpts = { name }
const subParams = {
$table: null,
$grid: null,
seq: '',
column: {
field: widget.field,
title: widget.title
} as VxeTableDefines.ColumnInfo,
columnIndex: 0,
$columnIndex: 0,
_columnIndex: 0,
rowid: '',
row: {},
rowIndex: 0,
$rowIndex: 0,
_rowIndex: 0,
isEdit: false,
isHidden: false,
fixed: null,
type: '',
level: 1,
visibleData: [],
items: [],
data: [],
widget: widget
}
return h('div', {
class: ['vxe-form-design--widget-subtable-view-item', {
'is--active': isActive,
'is--sort': sortWidget && widget && sortWidget.id === widget.id,
'is--drag': dragWidget && widget && dragWidget.id === widget.id
}],
draggable: true,
'data-widget-id': widget.id,
onDragstart: sortDragstartSubItemEvent,
onDragenter: sortDragenterSubItemEvent,
onClick (evnt: KeyboardEvent) {
if (widget) {
$xeFormDesign.handleClickWidget(evnt, widget)
}
}
}, [
h('div', {
class: 'vxe-form-design--widget-subtable-view-item-wrapper'
}, [
h('div', {
class: 'vxe-form-design--widget-subtable-view-item-box vxe-form--item-row'
}, renderSubtableView
? h(VxeFormItemComponent, {
class: ['vxe-form-design--widget-render-form-item'],
title: widget.title,
field: widget.field,
itemRender: {}
}, {
default () {
return getSlotVNs(renderSubtableView(subOpts, subParams))
}
})
: (renderWidgetDesignView
? getSlotVNs(renderWidgetDesignView(renderOpts, defParams))
: [])),
isActive
? h('div', {
class: 'vxe-form-design--preview-item-operate'
}, [
h(VxeButtonComponent, {
icon: getIcon().FORM_DESIGN_WIDGET_COPY,
status: 'primary',
size: 'mini',
circle: true,
onClick (params) {
$xeFormDesign.handleCopyWidget(params.$event, widget)
}
}),
h(VxeButtonComponent, {
icon: getIcon().FORM_DESIGN_WIDGET_DELETE,
status: 'danger',
size: 'mini',
circle: true,
onClick (params) {
$xeFormDesign.handleRemoveWidget(params.$event, widget)
}
})
])
: createCommentVNode()
])
])
}
}
})
export const WidgetSubtableEditComponent = defineComponent({
props: {
renderOpts: {
type: Object as PropType<VxeGlobalRendererHandles.RenderFormDesignWidgetEditOptions>,
default: () => ({})
},
renderParams: {
type: Object as PropType<VxeGlobalRendererHandles.RenderFormDesignWidgetEditParams<WidgetSubtableFormObjVO>>,
default: () => ({})
}
},
emits: [],
setup (props) {
const $xeFormDesign = inject<(VxeFormDesignConstructor & VxeFormDesignPrivateMethods) | null>('$xeFormDesign', null)
if (!$xeFormDesign) {
return () => []
}
const { reactData: formDesignReactData } = $xeFormDesign
const handleDragoverWrapperEvent = (evnt: DragEvent) => {
const { sortWidget, widgetObjList } = formDesignReactData
const { renderParams } = props
const { widget } = renderParams
evnt.stopPropagation()
if (!sortWidget || !widget || widget.id === sortWidget.id) {
return
}
if (hasFormDesignLayoutType(sortWidget)) {
if (VxeUI.modal) {
VxeUI.modal.message({
content: getI18n('vxe.formDesign.widgetProp.subtableProp.errSubDrag'),
status: 'error',
id: 'errSubDrag'
})
}
return
}
// 如果控件不在当前子表中,则拖进去
if (widget.name && !widget.children.some(item => item.id === sortWidget.id)) {
const rest = XEUtils.findTree(widgetObjList, item => item.id === sortWidget.id, { children: 'children' })
if (rest) {
const { item, index, items } = rest
formDesignReactData.sortWidget = null
formDesignReactData.activeWidget = item
widget.children.push(item)
items.splice(index, 1)
formDesignReactData.sortWidget = item
}
}
}
const handleDragenterWrapperEvent = (evnt: DragEvent) => {
evnt.stopPropagation()
}
return () => {
const { renderParams } = props
const { widget } = renderParams
const { title, children, options } = widget
const { showCheckbox } = options
return h(VxeFormItemComponent, {
title,
className: 'vxe-form-design--widget-subtable-form-item'
}, {
default () {
return h('div', {
class: 'vxe-form-design--widget-subtable-view',
onDragenter: handleDragenterWrapperEvent,
onDragover: handleDragoverWrapperEvent
}, [
h('div', {
class: 'vxe-form-design--widget-subtable-view-left'
}, [
showCheckbox
? h('div', {
class: 'vxe-form-design--widget-subtable-col'
}, [
h('div', {
class: 'vxe-form-design--widget-subtable-head'
}, [
h(VxeCheckboxComponent)
]),
h('div', {
class: 'vxe-form-design--widget-subtable-body'
}, [
h(VxeCheckboxComponent)
])
])
: createCommentVNode(),
h('div', {
class: 'vxe-form-design--widget-subtable-col'
}, [
h('div', {
class: 'vxe-form-design--widget-subtable-head'
}, getI18n('vxe.formDesign.widgetProp.subtableProp.seqTitle')),
h('div', {
class: 'vxe-form-design--widget-subtable-body'
}, '1')
])
]),
h('div', {
class: 'vxe-form-design--widget-subtable-view-right'
}, [
h('div', {
class: 'vxe-form-design--widget-subtable-view-wrapper'
}, [
h(TransitionGroup, {
class: 'vxe-form-design--widget-subtable-view-list',
tag: 'div',
name: 'vxe-form-design--widget-subtable-view-list'
}, {
default: () => {
return children
? children.map((childWidget, childIndex) => {
return h(ViewSubItemComponent, {
key: childWidget.id,
parentWidget: widget,
widget: childWidget,
childIndex
})
})
: []
}
}),
h('div', {
key: 'empty',
class: 'vxe-form-design--widget-subtable-view-empty'
}, getI18n('vxe.formDesign.widgetProp.subtableProp.colPlace'))
])
])
])
}
})
}
}
})
export const WidgetSubtableViewComponent = defineComponent({
props: {
renderOpts: {
type: Object as PropType<VxeGlobalRendererHandles.RenderFormDesignWidgetViewOptions>,
default: () => ({})
},
renderParams: {
type: Object as PropType<VxeGlobalRendererHandles.RenderFormDesignWidgetViewParams<WidgetSubtableFormObjVO>>,
default: () => ({})
}
},
emits: [],
setup (props) {
const VxeTableGridComponent = VxeUI.getComponent<VxeGridComponent>('VxeGrid')
const $xeFormView = inject<(VxeFormViewConstructor & VxeFormViewPrivateMethods) | null>('$xeFormView', null)
const { computeKebabCaseName } = useWidgetName(props)
const refGrid = ref<VxeGridInstance>()
const defaultDataList = ref([
{}
])
const computeFormReadonly = computed(() => {
if ($xeFormView) {
return $xeFormView.props.readonly
}
return false
})
const computeSubtableColumns = computed(() => {
const { renderParams } = props
const { widget } = renderParams
const { children, options } = widget
const formReadonly = computeFormReadonly.value
const columns: VxeGridPropTypes.Columns = []
if (options.showCheckbox) {
columns.push({
type: 'checkbox',
width: 60,
fixed: 'left'
})
}
columns.push({
type: 'seq',
width: 60,
fixed: 'left'
})
if (children) {
children.forEach(childWidget => {
const { name } = childWidget
const compConf = renderer.get(name) || {}
const parseSubtableColumn = compConf.parseFormDesignWidgetSubtableColumn
let colConf: VxeGridPropTypes.Column = {
field: childWidget.field,
title: childWidget.title
}
if (parseSubtableColumn) {
colConf = Object.assign(colConf, parseSubtableColumn({
$formView: $xeFormView,
name: childWidget.name,
widget: childWidget,
readonly: !!formReadonly
}))
} else {
if (formReadonly) {
colConf.cellRender = {
name: childWidget.name,
props: childWidget.options
}
} else {
colConf.editRender = {
name: childWidget.name,
props: childWidget.options
}
}
}
const renderSubtableDefaultView = compConf.renderFormDesignWidgetSubtableDefaultView
const renderSubtableCellView = compConf.renderFormDesignWidgetSubtableCellView || renderSubtableDefaultView
const renderSubtableEditView = compConf.renderFormDesignWidgetSubtableEditView
const colSlots: VxeColumnPropTypes.Slots = {}
if (renderSubtableDefaultView || renderSubtableCellView) {
colSlots.default = (slotParams) => {
const { isEdit, column } = slotParams
const { editRender, cellRender } = column
const params = Object.assign({ widget: childWidget }, slotParams)
if (isEdit && editRender) {
if (renderSubtableCellView) {
return getSlotVNs(renderSubtableCellView(editRender, params))
}
}
if (renderSubtableDefaultView) {
return getSlotVNs(renderSubtableDefaultView(cellRender || {}, params))
}
return []
}
}
if (renderSubtableEditView) {
colSlots.edit = (slotParams) => {
const { column } = slotParams
const { editRender } = column
const params = Object.assign({ widget: childWidget }, slotParams)
return getSlotVNs(renderSubtableEditView(editRender, params))
}
}
colConf.slots = colSlots
columns.push(colConf)
})
}
if (!formReadonly) {
columns.push({
field: 'action',
title: '操作',
fixed: 'right',
width: 80,
slots: {
default ({ row }) {
return h(VxeButtonComponent, {
mode: 'text',
icon: 'vxe-icon-delete',
status: 'error',
onClick () {
removeSubRow(row)
}
})
}
}
})
}
return columns
})
const computeGridOptions = computed(() => {
const { renderParams } = props
const { widget, isEditMode } = renderParams
const subtableColumns = computeSubtableColumns.value
const formReadonly = computeFormReadonly.value
const gridConf: VxeGridProps & Required<Pick<VxeGridProps, 'toolbarConfig'>> = {
border: true,
showOverflow: true,
height: 300,
columnConfig: {
resizable: true,
minWidth: 140
},
rowConfig: {
keyField: '_id'
},
data: isEditMode ? defaultDataList.value : $xeFormView ? $xeFormView.getItemValue(widget) : null,
columns: subtableColumns,
toolbarConfig: {
zoom: true,
custom: false,
slots: {
buttons: 'toolbarButtons'
}
}
}
if (!formReadonly) {
gridConf.keepSource = true
gridConf.editConfig = {
mode: 'row',
trigger: 'click',
showStatus: true
}
}
return gridConf
})
const getSubRecord = () => {
const { renderParams } = props
const { widget } = renderParams
const record: Record<string, any> = {
_id: Date.now()
}
XEUtils.each(widget.children, childWidget => {
record[childWidget.field] = null
})
return record
}
const addSubRowEvent = () => {
const { renderParams } = props
const { widget } = renderParams
if ($xeFormView) {
let list: any[] = $xeFormView.getItemValue(widget)
if (!XEUtils.isArray(list)) {
list = []
}
const newRow = getSubRecord()
list.unshift(newRow)
$xeFormView.setItemValue(widget, list.slice(0)).then(() => {
return nextTick().then(() => {
const $grid = refGrid.value
if ($grid) {
$grid.setEditRow(newRow)
}
})
})
}
}
const removeSubRow = (row: any) => {
const { renderParams } = props
const { widget } = renderParams
if ($xeFormView) {
const list: any[] = $xeFormView.getItemValue(widget)
if (list) {
$xeFormView.setItemValue(widget, list.filter(item => item._id !== row._id))
}
}
}
return () => {
const { renderParams } = props
const { widget } = renderParams
const kebabCaseName = computeKebabCaseName.value
const gridOptions = computeGridOptions.value
const formReadonly = computeFormReadonly.value
return h(VxeFormItemComponent, {
class: ['vxe-form-design--widget-render-form-item', `widget-${kebabCaseName}`],
title: widget.title,
field: widget.field,
span: 24
}, {
default () {
return VxeTableGridComponent
? h(VxeTableGridComponent, {
...gridOptions,
ref: refGrid
}, {
toolbarButtons () {
return formReadonly
? []
: [
h(VxeButtonComponent, {
content: '新增',
icon: 'vxe-icon-add',
status: 'primary',
onClick: addSubRowEvent
})
]
}
})
: createCommentVNode()
}
})
}
}
})