共计 9226 个字符,预计需要花费 24 分钟才能阅读完成。
<template>
<div class="cascader-wrapper">
<crec-popover
placement="bottomLeft"
trigger="click"
:popper-class="popOverClass"
v-model="showPopover"
>
<!-- <div slot="reference"> -->
<!-- multiple
mode="tags" -->
<crec-select
v-model="selectedLabels"
:default-value="defaultLabels"
:placeholder="placeholder"
:disabled="disabled"
:size="size"
:showArrow="true"
style="width: 100%;"
popper-class="hide-popper"
:open="false"
@focus="handleFocus"
@dropdown-visible-change="visibleChange"
></crec-select>
<!-- </div> -->
<template slot="content">
<div
class="cascader-menu-wrapper"
v-clickoutside="hidePopover"
>
<template v-if="options.length > 0">
<ul
class="crec-cascader-menu cascader-menu"
:style="{'width': panelWidth ==='auto'?'auto': panelWidth +'px'}"
v-for="(cas, index) in casTree"
:key="index"
>
<!-- -->
<!-- 'can-load-children': !item.isLeaf && !item[childrenKey] && allowLoadChildren && showLoadingIndicator, -->
<!-- 'loading-children': !item.isLeaf && item.loading && allowLoadChildren && showLoadingIndicator, -->
<!-- 'crec-cascader-menu-item-expand': item[childrenKey] && item[childrenKey].length > 0, -->
<li
:class="{'crec-cascader-menu-item': true,'crec-cascader-menu-item-expand': true,'has-checked-child': item.indeterminate || item.hasCheckedChild,'crec-cascader-menu-item-active': item.checked,}"@click="spreadNext(item[childrenKey], index, item)"v-for="(item, itemIdx) in cas":key="itemIdx":title="item[labelKey]"
>
<crec-checkbox
class="cascader-checkbox"
@click.native.stop
:disabled="item.disabled"
v-model="item.checked"
:indeterminate="item.indeterminate"
@change="({target}) => {checkedChange(item, target.checked) }"
></crec-checkbox>
<span>{{item[labelKey] }}</span>
<span
v-if="item[childrenKey] && item[childrenKey].length > 0"
class="crec-cascader-menu-item-expand-icon"
>
<crec-icon type="right"></crec-icon>
</span>
</li>
</ul>
</template>
<template v-else>
<ul class="crec-cascader-menu cascader-menu">
<li class="crec-cascader-menu-item dropdown__empty">
{{noDataText}}
</li>
</ul>
</template>
</div>
</template>
</crec-popover>
</div>
</template>
<script>
import Clickoutside from './clickoutside'
import {props, hasArrayChild, deepClone, getId, fireEvent, isPromise} from './utils'
export default {
name: 'TagSelect',
props,
watch: {
selectedLabels: {
deep: true,
handler () {console.log(this.selectedLabels)
if (this.selectedLabels === '') {this.selectedLabels = this.defaultLabels}
}
},
options: {
deep: true,
handler () {this.initOpts()
this.initDatas()}
},
defaultOptions: {
deep: true,
handler () {console.log(this.defaultOptions)
this.defaultLabels = this.defaultOptions.map(e => e.organId)[0]
}
},
value: {
deep: true,
handler () {console.log(this.selectedValues, this.value)
if (this.selectedValues !== this.value) {this.initOpts()
this.initDatas()}
}
},
disabled (disabled) {if (disabled) {this.hidePopover()
}
},
showPopover (flag) {if (flag) {console.log(this.value)
this.$emit('focus')
}
}
},
directives: {Clickoutside},
created () {this.classRef = `popper-class-${getId()}`
this.popOverClass = `cascader-popper ${this.classRef} ${this.popperClass}`
this.initOpts()
this.initDatas()},
mounted () {
// 设置弹出层宽度
this.elWidth = this.$el.offsetWidth
},
destroyed () {
this.clonedOpts = null
this.casTree = null
this.selectedItems = null
this.selectedLabels = null
this.selectedvalues = null
},
data () {
return {
elWidth: '',
popperWidth: '',
popOverClass: '',
classRef: '',
showPopover: false,
clonedOpts: [],
casTree: [],
selectedItems: {},
selectedLabels: '',
selectedValues: '',
loadChildrenPromise: null,
defaultLabels: ''
}
},
methods: {initOpts () {console.log(this.defaultOptions)
this.defaultLabels = this.defaultOptions.map(e => e.organId)[0]
console.log(this.defaultLabels)
this.clonedOpts = deepClone(this.options)
this.casTree = [this.clonedOpts]
console.log(this.casTree)
},
/**
* 初始化数据
* 空值初始化,两个绑定不统一的状况
*/
initDatas () {this.pickCheckedItem(this.clonedOpts)
},
/**
* 依据以后节点 checked
* 更改所有子孙节点 checked
* 依赖 this.selectChildren
*/
markChildrenChecked (node) {
const vm = this
function loop (children, status) {if (children) {
children.map(child => {if (!child.disabled) {
child.checked = status
if (child.checked) {child.indeterminate = false}
}
if (hasArrayChild(child, vm.childrenKey)) {loop(child[vm.childrenKey], status)
}
})
}
}
if (node && hasArrayChild(node, vm.childrenKey) && this.selectChildren) {loop(node[vm.childrenKey], node.checked)
}
},
/**
* 标记父节点 checked、indeterminate 状态
* 依赖 this.selectChildren
*/
markParentChecked (node) {
const vm = this
node.indeterminate = false
function loop (node) {
let checkCount = 0
if (hasArrayChild(node, vm.childrenKey)) {const childIndeterminate = node[vm.childrenKey].some(child => child.indeterminate)
node[vm.childrenKey].map(child => {if (child.checked) {checkCount++}
})
// 子节点全副被选中
if (checkCount === node[vm.childrenKey].length) {
node.checked = true
node.indeterminate = false
} else {
node.checked = false
if (checkCount > 0 || childIndeterminate) {node.indeterminate = true} else {node.indeterminate = false}
}
}
if (node.parent) {loop(node.parent)
}
}
if (node && node.parent && this.selectChildren) {loop(node.parent)
}
},
/**
* 标记是否有被选子项
* 依赖 this.selectChildren
*/
markParentHasCheckChild (node) {
const vm = this
node.hasCheckedChild = false
function loop (node) {
let checkCount = 0
if (hasArrayChild(node, vm.childrenKey)) {const childHasCheckedChild = node[vm.childrenKey].some(child => child.hasCheckedChild)
node[vm.childrenKey].map(child => {if (child.checked) {checkCount++}
})
// 子节点有被选中
node.hasCheckedChild = (checkCount > 0) || childHasCheckedChild
}
if (node.parent) {loop(node.parent)
}
}
if (node && node.parent && !this.selectChildren) {loop(node.parent)
}
},
// 展现标签所有层级
getLevel (node, key, leveled) {const levels = []
function loop (data) {levels.push(data[key])
if (data.parent) {loop(data.parent)
}
}
if (leveled) {loop(node)
return levels.reverse().join(this.separator)
} else {return node[key]
}
},
/**
* 解决已选中
* 从新遍历 tree,pick 除已选中我的项目
*/
pickCheckedItem (tree) {
const vm = this
/**
* 移除 parent 援用
*/
function removeParent (node) {const obj = {}
Object.keys(node).forEach(key => {if (key !== 'parent') {obj[key] = node[key]
}
})
if (hasArrayChild(obj, vm.childrenKey)) {obj[vm.childrenKey] = obj[vm.childrenKey].map(child => {return removeParent(child)
})
}
return obj
}
vm.selectedItems = {}
vm.selectedLabels = ''vm.selectedValues =''
function loop (data) {if (Array.isArray(data)) {
data.map(item => {if (item.checked) {const newItem = removeParent(item)
vm.selectedItems = (newItem)
vm.selectedLabels = vm.getLevel(item, vm.labelKey, vm.showAllLevels)
vm.selectedValues = vm.getLevel(item, vm.valueKey, vm.outputLevelValue)
}
if (hasArrayChild(item, vm.childrenKey)) {loop(item[vm.childrenKey])
}
})
}
}
loop(tree)
},
clearTag () {
const vm = this
function loop (nodeArr) {
nodeArr.forEach(node => {
node.checked = false
node.indeterminate = false
if (hasArrayChild(node, vm.childrenKey)) {loop(node[vm.childrenKey])
}
})
}
// 敞开全副状态
loop(this.clonedOpts)
this.selectedLabels = ''this.selectedValues =''
this.selectedItems = {}
this.$emit('clear')
this.syncData()},
// 菜单选中变动
checkedChange (item, checked) {
this.defaultLabels = ''
this.clearTag()
console.log(item, checked)
item.checked = checked
this.$emit('clickItem', item)
this.markChildrenChecked(item)
this.markParentChecked(item)
this.markParentHasCheckChild(item)
this.pickCheckedItem(this.clonedOpts)
this.refresPopover()
this.syncData()},
// 同步数据到下层
syncData () {console.log(this.selectedValues)
this.$emit('input', this.selectedValues)
this.$emit('change', this.selectedValues, this.selectedItems)
},
// 开展下一级
async spreadNext (children, index, item) {
const vm = this
if (
vm.allowLoadChildren &&
!children && !item[vm.childrenKey] &&
vm.loadChildrenMethod &&
vm.loadChildrenMethod.constructor === Function &&
!vm.loadChildrenPromise && // promise 不存在
!item.isLeaf
) {const isPromiseMethod = this.loadChildrenMethod(item)
if (isPromise(isPromiseMethod)) {
vm.loadChildrenPromise = isPromiseMethod
this.$set(item, 'loading', true)
const result = await vm.loadChildrenPromise.catch(e => {this.$set(item, 'loading', false)
})
this.$set(item, 'loading', false)
vm.loadChildrenPromise = null
if (result && result.constructor === Array) {this.$set(item, vm.childrenKey, result)
children = result
this.initDatas()} else {console.warn('The resolved value by loadChildrenMethod must be an Option Array !')
}
} else {console.warn('You must return a Promise instance in loadChildrenMethod !')
}
}
if (index || index === 0) {if (vm.casTree.indexOf(children) === -1) {if (children && children.length > 0) {vm.casTree.splice(index + 1, vm.casTree.length - 1, children)
} else {vm.casTree.splice(index + 1, vm.casTree.length - 1)
}
vm.$emit('spread', item)
}
}
},
visibleChange (visible) {if (visible) {this.showPopover = true}
},
handleFocus (evt) {if (this.disabled) return
this.$emit('focus', evt)
},
hidePopover (evt) {
this.showPopover = false
this.$emit('blur', evt)
},
refresPopover () {setTimeout(() => {fireEvent(window, 'resize')
}, 66)
}
}
}
</script>
<style lang="less">
.hide-popper {display: none;}
.cascader-popper {padding: 0px;}
.crec-popover-inner-content {padding: 0;}
.cascader-menu-wrapper {
white-space: nowrap;
.crec-cascader-menu {
padding: 8px 0;
height: 240px;
}
}
.cascader-menu-wrapper .cascader-checkbox {
margin-right: 10px;
font-weight: 500;
font-size: 14px;
cursor: pointer;
user-select: none;
}
.crec-cascader-menu-item.has-checked-child {background-color: #f5f7fa !important;}
.dropdown__empty {
height: 100%;
padding-top: 50%;
margin: 0;
text-align: center;
color: #999;
font-size: 14px;
}
.can-load-children {position: relative;}
.can-load-children::after {
content: '';
display: inline-block;
position: absolute;
width: 5px;
height: 5px;
background: #a5d279;
right: 20px;
top: 50%;
border-radius: 50%;
transform: translateY(-50%);
-webkit-transform: translateY(-50%);
}
.can-load-children.loading-children::after {
animation: loading 0.22s infinite alternate;
-moz-animation: loading 0.22s infinite alternate; /* Firefox */
-webkit-animation: loading 0.22s infinite alternate; /* Safari 和 Chrome */
-o-animation: loading 0.22s infinite alternate; /* Opera */
}
@keyframes loading {
from {background: #a5d279;}
to {background: #334d19;}
}
</style>
正文完