ActionRender.vue
复制代码
<script>
import { merge, pick, omit } from 'lodash'
import mergeProps from '@vue/babel-helper-vue-jsx-merge-props'
const props = {
inLine: { type: 'link', size: 'small' },
outLine: { type: 'primary', size: 'default' },
}
// 在vnode生成后的VnodeData中使用mergeProps 将事件和属性合并进去
function mergeVNodeProps(vnode, ...args) {
if (vnode.componentOptions) {
const { propsData, listeners, Ctor } = vnode.componentOptions
// 为了便于合并, 我们只取on和attrs两个参数,分别合并事件和属性
const { on, attrs } = mergeProps([
{ on: listeners, attrs: propsData },
...args,
])
// 获取组件中定义的props
const keys = Object.keys(Ctor.options.props || {})
// 分离出组件需要的propsData和attrs
const [$props, $attrs] = [pick(attrs, keys), omit(attrs, keys)]
// 这里偷懒使用lodash.merge函数进行递归合并
merge(vnode, {
data: { attrs: $attrs },
componentOptions: {
listeners: on,
propsData: $props,
},
})
} else {
// 非组件VNode,直接合并data。但需要注意事件使用的是on而非nativeOn
// 属性根据情况可以使用attrs或domProps,这二者的区别见后文
vnode.data = mergeProps([vnode.data, ...args])
}
return vnode
}
function setNewPropos(vNodes, lay, isInMore) {
return vNodes.map((vNode) => {
if (vNode?.componentOptions?.tag === 'a-popconfirm') {
const canMap =
Array.isArray(vNode?.componentOptions?.children) &&
vNode?.componentOptions?.children.length
if (canMap) {
const newChildren = vNode.componentOptions.children.map(
(vNodeChild) => {
const childNewItem = mergeVNodeProps(
vNodeChild,
{ on: { click: (e) => e.stopPropagation() } },
{ attrs: { ...props[`${lay}`] } }
)
return childNewItem
}
)
vNode.componentOptions.children = newChildren
}
const item = isInMore
? mergeVNodeProps(vNode, {
attrs: {
getPopupContainer: (node) => node.parentNode,
placement: 'topRight',
overlayStyle: { width: '155px' },
},
})
: vNode
return item
} else if (vNode?.componentOptions?.tag === 'a-button') {
const item = mergeVNodeProps(
vNode,
{ on: { click: (e) => e.stopPropagation() } },
{ attrs: { ...props[`${lay}`] } }
)
return item
} else {
return vNode
}
})
}
export function isEmptyElement(c) {
const isHidden = c.data?.directives?.filter(
(item) => ['has', 'show'].includes(item.name) && !item.value
)?.length
return !c.tag || isHidden
}
function filterEmpty(children = []) {
return children.filter((c) => !isEmptyElement(c))
}
export default {
name: 'EhActionRender',
props: {
btnNumber: {
type: [Number, String],
default: 2,
},
/**
* small | middle | large | number
*/
gap: {
type: [Number, String],
default: 'small',
},
text: {
type: String,
default: '更多',
},
/**
* 放置的位置 inRow 行内操作列,
*/
inRow: {
type: Boolean,
default: true,
},
},
data() {
return {
visible: false,
}
},
render() {
const { gap, inRow, text, btnNumber } = this
const ifRenderList = filterEmpty(this.$slots?.default)
const groupList = () => {
const vNodes =
ifRenderList.length >= btnNumber
? ifRenderList.slice(0, btnNumber)
: ifRenderList
return setNewPropos(vNodes, inRow ? 'inLine' : 'outLine')
}
const moreList = () => {
const vNodes =
ifRenderList.length >= btnNumber ? ifRenderList.slice(btnNumber) : []
return setNewPropos(vNodes, 'inLine', true)
}
const showMore = moreList().length > 0
const showGroup = groupList().length > 0
const groupVNodes = () => groupList()?.map((v) => v)
const moreVNodes = () =>
moreList()?.map((v, i) => {
return <a-menu-item key={i}>{v}</a-menu-item>
})
return (
<a-space size={inRow ? 0 : gap}>
{showGroup && groupVNodes()}
{showMore && (
<a-dropdown>
<a-button
type={inRow ? 'link' : 'default'}
size={inRow ? 'small' : 'default'}
>
{text}
<a-icon type="down" style={{ marginLeft: 0 }} />
</a-button>
<a-menu slot="overlay">{moreVNodes()}</a-menu>
</a-dropdown>
)}
</a-space>
)
},
}
</script>