magiccube-vue3
Version:
vue3-js版组件库
242 lines (236 loc) • 8.46 kB
JavaScript
import '../../style/department-tree.less'
import { ref, computed, provide, inject, watch } from 'vue'
import * as utils from '../../utils/common'
/**
* 更新祖级的treeData,可以使用provide和inject
*/
const TreeRender = {
name: 'TreeRender',
props: {
data: Array,
textClickable: {
type: Boolean,
default: true
},
multi: {
type: Boolean,
default: false
},
checkIsRelatedToName: {
type: Boolean,
default: true
},
},
setup(props, { emit, slots }) {
/**
* +- 处理
*/
const forefatherRefresh = inject('forefatherRefresh', () => { })
const handleExtend = (item) => {
item.isExtend = !item.isExtend
forefatherRefresh()
}
const extendNode = (item) => (
<div class="mc-department-tree__panel--node--content--symbol"
onClick={() => handleExtend(item)}>
{item.isExtend ? '-' : '+'}
</div>
)
/**
* √ 处理
*/
const forefatherCheckboxClick = inject('forefatherCheckboxClick', () => { })
const handleCheck = (item) => {
forefatherCheckboxClick(item)
}
const checkboxNode = (item) => {
return (
<McCheckbox v-model={item.isChecked} onChange={() => handleCheck(item)}></McCheckbox>
)
}
/**
* text 处理
*/
const forefatherTextClick = inject('forefatherTextClick', () => { })
const handleTextClick = (item) => {
if (!props.textClickable) return false
forefatherTextClick(item)
}
/**
* 文字变蓝有3种情况:
* 单选时 点击则代表选中,则(multi=== false && isChecked=== true)
* 多选时 如果点击则代表选中,则(multi=== true && checkIsRelatedToName ===true && isChecked=== true)
* 多选时 如果点击不代表选中,则(multi=== true && checkIsRelatedToName ===false && isTextClicked=== true)
* TODO 目前还没有checkIsRelatedToName ===false 的情况,所以暂缓处理
*/
const textNode = (item) => (
<span class={{
'mc-department-tree__panel--node--content--text': true,
'mc-department-tree__panel--node--content--text-clickable': props.textClickable,
'mc-department-tree__panel--node--content--text-active': item.isChecked,
}} onClick={() => handleTextClick(item)}>
{item.name}
</span>
)
/**
* 递归 处理
*/
const newPanel = (item) => (
<TreeRender data={item.children}
multi={props.multi}
textClickable={props.textClickable}
checkIsRelatedToName={props.checkIsRelatedToName} />
)
/**
* liNode 处理
*/
const liNode = (item, idx) => (
<li key={idx} class="mc-department-tree__panel--node">
<div class={{
'mc-department-tree__panel--node--content': true,
'mc-department-tree__panel--node--placeholder': !item.children?.length
}}>
{item.children?.length ? extendNode(item) : ''}
{props.multi ? checkboxNode(item) : ''}
{textNode(item)}
</div>
{
item.children?.length && item.isExtend ? newPanel(item) : ''
}
</li>
)
return () => (
<ul class="mc-department-tree__panel">
{props.data.map(liNode)}
</ul>
)
}
}
const DepartmentTree = {
name: 'McDepartmentTree',
props: {
data: {
type: Array,
default: () => []
},
multi: {
type: Boolean,
default: false
},
/**
* 文本默认能点击,
* 在能够点击的情况下:
* 点击会抛出trigger事件;并代表选中,会修改model.value(相当于selected)的值
*/
textClickable: {
type: Boolean,
default: true
},
/**
* 在能点击的情况下,会触发textClick
* 在textClick中
* 如果是多选并且checkIsRelatedToName为true,点击name会改变check值(同步勾选checkbox)
*/
checkIsRelatedToName: {
type: Boolean,
default: true
},
/**
* 表示选中的条目的key值的集合
*/
modelValue: [Array, String],
},
emits: ['trigger', 'update:modelValue'],
setup(props, { emit, slots }) {
/**
* 初始treeData
*/
const treeData = ref([])
const model = computed({
get() {
return props.modelValue ? typeof props.modelValue === 'string' ? [props.modelValue] : props.modelValue : []
},
set(val) {
emit('update:modelValue', val)
}
})
const getFathersKey = (_k) => {
const ary = []
for (let i = 0, len = _k.length; i < len; i++) {
const _s = _k.substring(0, i + 1)
if (_s && _s !== _k && _s.lastIndexOf('_') === _s.length - 1) {
ary.push(_s)
}
}
return ary
}
const modelFathers = computed(() => {
const res = []
model.value.length && model.value.map(item => {
getFathersKey(item).map(k => {
if (!res.includes(k)) res.push(k)
})
})
return res
})
const initTreeData = (data, selected = [], extendItems = []) => {
return data && data.length ? data.map(item => ({
...item,
isChecked: selected.length ? selected.includes(item.key) : false,
children: initTreeData(item.children, selected, extendItems),
isExtend: item.isExtend || extendItems.includes(item.key)
})) : []
}
treeData.value = initTreeData(props.data, model.value, modelFathers.value)
watch(() => props.modelValue, (n, o) => {
treeData.value = initTreeData(treeData.value, model.value, modelFathers.value)
})
/**
* 点击+-时,item的isExtend改变,更新祖级的treeData.value
*/
const refresh = () => {
treeData.value = utils.deepCopy(treeData.value)
}
provide('forefatherRefresh', refresh)
/**
* 点击checkbox后,item的isChecked改变,需要修改model.value的值(随后会根据watch改变treeData)
*/
const checkboxClick = (item) => {
const _k = item.key
const ary = [...model.value]
ary.includes(_k) ? ary.splice(ary.indexOf(_k), 1) : ary.push(_k)
model.value = [...ary]
}
provide('forefatherCheckboxClick', checkboxClick)
/**
* 点击文本后,会抛出点击事件;
* 单选时会自动更新model.value;多选时如果checkIsRelatedToName为true,则更新model.value;
* model.value更新后会通过watch更新祖级的treeData.value
*/
const textClick = (item) => {
if (props.multi) {
if (props.checkIsRelatedToName) {
// item.isChecked = !item.isChecked
checkboxClick(item)
}
} else {
model.value = [item.key]
}
emit('trigger', item)
}
provide('forefatherTextClick', textClick)
return () => (
<div class="mc-department-tree">
<TreeRender data={treeData.value}
multi={props.multi}
textClickable={props.textClickable}
checkIsRelatedToName={props.checkIsRelatedToName} />
</div>
)
}
}
DepartmentTree.install = Vue => {
Vue.component(DepartmentTree.name, DepartmentTree)
}
const McDepartmentTree = DepartmentTree
export { McDepartmentTree, McDepartmentTree as default }