UNPKG

magiccube-vue3

Version:

vue3-js版组件库

242 lines (236 loc) 8.46 kB
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 }