// 功能插件
|
import log from '../../utils/log'
|
import Vue from 'vue'
|
const EVENTS = {
|
CLICK: 'on-node-click',
|
DBLCLICK: 'on-node-dblclick',
|
CONTEXTMENU: 'on-node-contextmenu',
|
MOUSEENTER: 'on-node-mouseenter',
|
MOUSELEAVE: 'on-node-mouseleave'
|
}
|
|
function createListener (handler, data) {
|
if (typeof handler === 'function') {
|
return function (e) {
|
if (e.target.className.indexOf('org-tree-node-btn') > -1) return
|
|
handler.apply(null, [e, data])
|
}
|
}
|
}
|
// 判断是否叶子节点
|
const isLeaf = (data, prop, lazy) => {
|
const children = data[prop]
|
const hasChild = Array.isArray(children) && children.length > 0
|
const isEmpty = lazy && Array.isArray(children) && children.length === 0
|
return (!hasChild && !lazy) || data.isLeaf || isEmpty
|
}
|
|
// 创建 node 节点
|
export const renderNode = (h, data, context, root) => {
|
const { props } = context;
|
const cls = ['tree-org-node']
|
const childNodes = []
|
const children = data[props.props.children]
|
const expandKey = props.props.expand
|
if(data[expandKey]===undefined && data.$$level < props.defaultExpandLevel) {
|
Vue.prototype.$set(data, expandKey, true)
|
}
|
const isExpand = data[expandKey]
|
// 如果是叶子节点则追加leaf事件
|
if (isLeaf(data, props.props.children, props.lazy)) {
|
cls.push('is-leaf')
|
} else if (props.collapsable && !isExpand) { // 追加是否展开class
|
cls.push('collapsed')
|
}
|
if(data.moving) {
|
cls.push('tree-org-node__moving')
|
}
|
// 渲染label块
|
childNodes.push(renderLabel(h, data, context, root))
|
|
if (!props.collapsable || isExpand) {
|
childNodes.push(renderChildren(h, children, context, data.$$level))
|
}
|
return h('div', {
|
'class': cls,
|
'key': data[props.props.id],
|
'directives' :[ {
|
name: 'show',
|
value: !data.hidden
|
}]
|
}, childNodes)
|
}
|
|
// 创建展开折叠按钮
|
export const renderBtn = (h, data, context) => {
|
const { props, listeners } = context;
|
const expandHandler = listeners['on-expand']
|
|
let cls = ['tree-org-node__expand']
|
|
if (data[props.props.expand]) {
|
cls.push('expanded')
|
}
|
const children = []
|
if (context.scopedSlots.expand) {
|
children.push(context.scopedSlots.expand({node: data}))
|
} else {
|
children.push(h('span', {'class': 'tree-org-node__expand-btn'}))
|
}
|
return h('span', {
|
'class': cls,
|
on: {
|
mousedown: (e) => { e.stopPropagation(); },
|
dblclick: (e) => { e.stopPropagation(); },
|
click: e => { e.stopPropagation(); expandHandler && expandHandler(e, data)}
|
}
|
}, children)
|
}
|
|
// 创建 label 节点
|
export const renderLabel = (h, data, context, root) => {
|
const { props, listeners } = context
|
const label = data[props.props.label]
|
const renderContent = props.renderContent
|
const { directives } = context.data;
|
|
const childNodes = []
|
if (context.scopedSlots.default) {
|
childNodes.push(context.scopedSlots.default({node: data}))
|
} else if (typeof renderContent === 'function') {
|
log.warning('scoped-slot header is easier to use. We recommend users to use scoped-slot header.');
|
let vnode = renderContent(h, data)
|
vnode && childNodes.push(vnode)
|
} else {
|
childNodes.push(h('div', {
|
class: 'tree-org-node__text'
|
}, label))
|
}
|
|
if (props.collapsable && !isLeaf(data, props.props.children, props.lazy)) {
|
childNodes.push(renderBtn(h, data, context))
|
}
|
|
const cls = ['tree-org-node__inner']
|
let { labelStyle, labelClassName, selectedClassName, selectedKey } = props
|
|
if (typeof labelClassName === 'function') {
|
labelClassName = labelClassName(data)
|
}
|
|
labelClassName && cls.push(labelClassName)
|
data.className && cls.push(data.className)
|
// add selected class and key from props
|
if (typeof selectedClassName === 'function') {
|
selectedClassName = selectedClassName(data)
|
}
|
|
selectedClassName && selectedKey && data[selectedKey] && cls.push(selectedClassName)
|
const nodeLabelClass = ['tree-org-node__content'];
|
if (root) {
|
nodeLabelClass.push(`is-root_${props.suffix}`)
|
}
|
if (!data[props.props.label]) {
|
nodeLabelClass.push('is-empty')
|
}
|
if (data.focused) {
|
nodeLabelClass.push('is-edit')
|
}
|
// directives
|
let cloneDirs
|
if(Array.isArray(directives)){
|
cloneDirs = directives.map(item=>{
|
const newValue = Object.assign({node: data}, item.value)
|
return Object.assign({...item}, {value: newValue})
|
})
|
}
|
// event handlers
|
const NODEEVENTS = {};
|
for (let EKEY in EVENTS){
|
if (Object.prototype.hasOwnProperty.call(EVENTS,EKEY)){
|
const EVENT = EVENTS[EKEY];
|
let handler = listeners[EVENT];
|
if(handler){
|
NODEEVENTS[EKEY.toLowerCase()] = createListener(handler, data);
|
}
|
}
|
}
|
// texterea event handles
|
const focusHandler = listeners['on-node-focus']
|
const blurHandler = listeners['on-node-blur']
|
return h('div', {
|
'class': nodeLabelClass,
|
}, [h('div', {
|
'class': cls,
|
'directives': root? [] : cloneDirs,
|
style: data['style'] ? data['style'] : labelStyle,
|
on: NODEEVENTS,
|
},childNodes), h('textarea', {
|
'class': "tree-org-node__textarea",
|
'directives' :[{
|
name: 'show',
|
value: data.focused
|
}, {
|
name: 'focus',
|
value: data.focused
|
}],
|
"domProps":{
|
placeholder: "请输入节点名称",
|
value: data[props.props.label],
|
},
|
on: {
|
focus: e => focusHandler && focusHandler(e, data),
|
input: e => { data[props.props.label] = e.target.value },
|
blur: e => { data.focused = false; blurHandler && blurHandler(e, data)},
|
click: e => e.stopPropagation()
|
}
|
})])
|
}
|
|
// 创建 node 子节点
|
export const renderChildren = (h, list, context, level) => {
|
if (Array.isArray(list) && list.length) {
|
const children = list.filter(item => !item.hidden).map(item => {
|
item.$$level = level + 1
|
return renderNode(h, item, context, false)
|
})
|
|
return h('div', {
|
'class': 'tree-org-node__children'
|
}, children)
|
}
|
return ''
|
}
|
|
export const render = (h, context) => {
|
const { props } = context
|
props.data.root = props.isClone ? false : true;
|
props.data.$$level = 0
|
return renderNode(h, props.data, context, true)
|
}
|
|
export default render
|