<template>
<div>
<a-modal
:width="800"
:title="title"
:visible="show"
:confirm-loading="confirmLoading"
:loading="loading"
@ok="handleOk"
@cancel="handleCancel"
>
<div class="transfer-box">
<a-transfer
:data-source="dataSourceTransfer"
:target-keys="filterTargetKeys"
:render="(item) => item.title"
:show-select-all="true"
@change="handleTransferChange"
:list-style="{
width: '300px',
height: '360px'
}"
show-search
:titles="['可选城市', '已选城市']"
@search="onSearch"
>
<template
slot="children"
slot-scope="{ props: { direction, selectedKeys }, on: { itemSelect, itemSelectAll } }"
>
<a-tree
v-if="direction === 'left'"
:key="searchValue"
blockNode
checkable
:defaultExpandAll="false"
:checkedKeys="[...selectedKeys, ...targetKeys]"
:treeData="cityOptions"
@expand="onExpand"
:expanded-keys.sync="expandedKeys"
:auto-expand-parent="autoExpandParent"
@check="
(_, props) => {
handleTreeChecked(_, props, [...selectedKeys, ...targetKeys], itemSelect, itemSelectAll)
}
"
@select="
(_, props) => {
handleTreeChecked(_, props, [...selectedKeys, ...targetKeys], itemSelect, itemSelectAll)
}
"
>
<template slot="title" slot-scope="{ title }">
<span v-if="title.indexOf(searchValue) > -1">
{{ title.substr(0, title.indexOf(searchValue)) }}
<span :id="title.indexOf(searchValue) > -1 ? 'target-elem' : ''" style="color: #f50">{{
searchValue
}}</span>
{{ title.substr(title.indexOf(searchValue) + searchValue.length) }}
</span>
<span v-else>{{ title }}</span>
</template>
</a-tree>
</template>
</a-transfer>
</div>
<template slot="footer">
<a-button key="cancel" class="normal-btn" @click="handleCancel">取消</a-button>
<a-button key="submit" class="common-btn" :loading="confirmLoading" @click="handleOk">提交</a-button>
</template>
</a-modal>
</div>
</template>
<script>
import { cloneDeep } from '@/views/financialBillSystem/billingManagement/utils/mapping'
function isChecked(selectedKeys, eventKey) {
return selectedKeys.indexOf(eventKey) !== -1
}
function handleTreeData(data, targetKeys = []) {
data.forEach((item) => {
item['disabled'] = targetKeys.includes(item.key)
if (item.children) {
handleTreeData(item.children, targetKeys)
}
})
return data
}
export default {
name: 'ChooseCity',
props: {
sureCityList: {
required: true,
type: Array
},
cityList: {
required: true,
type: Array
}
},
data() {
return {
title: '选择城市',
show: true,
loading: false,
confirmLoading: false,
targetKeys: [],
dataSource: [],
cityOptions: [],
arr: [],
expandedKeys: [],
searchValue: '',
autoExpandParent: true,
dataList: [],
parentKeys: [],
filterTargetKeys: [],
dataSourceTransfer: []
}
},
created() {
console.log('sureCityList', this.sureCityList, this.cityList)
this.$nextTick(() => {
this.initCityTreeData()
})
},
methods: {
scrollPosition() {
this.$nextTick(() => {
const elem = document.querySelector('#target-elem')
console.log('elem', elem)
if (!elem) return
const scrollableElem = document.querySelector('.ant-transfer-list-body-customize-wrapper ')
console.log(scrollableElem)
const scrollY = scrollableElem.scrollTop
const scrollHeight = scrollableElem.scrollHeight
const clientHeight = scrollableElem.clientHeight
const targetY = elem.offsetTop - scrollableElem.offsetTop
const scrollToY = Math.min(targetY, scrollHeight - clientHeight)
scrollableElem.scrollTo({
top: scrollToY,
behavior: 'smooth'
})
})
},
flatten(list = []) {
list.forEach((item) => {
this.dataSource.push(item)
this.flatten(item.children)
})
},
generateData(_level, _preKey, _tns) {
const preKey = _preKey || '0'
const tns = _tns || this.cityOptions
const children = []
for (let i = 0; i < x; i++) {
const key = `${preKey}-${i}`
tns.push({ title: key, key, scopedSlots: { title: 'title' } })
if (i < y) {
children.push(key)
}
}
if (_level < 0) {
return tns
}
const level = _level - 1
children.forEach((key, index) => {
tns[index].children = []
return this.generateData(level, key, tns[index].children)
})
},
generateList(data) {
for (let i = 0; i < data.length; i++) {
const node = data[i]
const key = node.key
const title = node.title
this.dataList.push({ key, title })
if (node.children) {
this.generateList(node.children)
}
}
},
getParentKey(key, tree) {
let parentKey
for (let i = 0; i < tree.length; i++) {
const node = tree[i]
if (node.children) {
if (node.children.some((item) => item.key === key)) {
parentKey = node.key
} else if (this.getParentKey(key, node.children)) {
parentKey = this.getParentKey(key, node.children)
}
}
}
return parentKey
},
onExpand(expandedKeys) {
console.log('expandedKeys', expandedKeys)
this.expandedKeys = expandedKeys
this.autoExpandParent = false
},
handleSearch(e) {
setTimeout(() => {
const value = e
const expandedKeys = this.dataList
.map((item) => {
if (item.title.indexOf(value) > -1) {
return this.getParentKey(item.key, this.cityOptions)
}
return null
})
.filter((item, i, self) => item && self.indexOf(item) === i)
console.log(expandedKeys)
Object.assign(this, {
expandedKeys: value ? expandedKeys : [],
searchValue: value
})
this.scrollPosition()
console.log(this.autoExpandParent)
}, 200)
},
onSearch(dir, val) {
console.log(dir, val)
if (dir === 'left') {
this.handleSearch(val)
}
},
checkDetailParentKey() {
let parKey = []
if (this.sureCityList && this.sureCityList.length > 0) {
this.filterTargetKeys = this.sureCityList.map((item) => {
return item.key
})
parKey = this.searchParents(this.dataSource, this.filterTargetKeys, false)
console.log('初始化', this.targetKeys, this.filterTargetKeys, parKey)
}
return parKey
},
getAllSelectParentKey(parentList) {
let getRepeatObj = this.getShowAcount(parentList)
console.log(getRepeatObj)
let newObj = {}
for (let k in getRepeatObj) {
this.cityList.forEach((item) => {
if (item.key == k) {
item.children.length > getRepeatObj[k] ? (newObj[k] = 0) : (newObj[k] = getRepeatObj[k])
}
})
}
console.log('找到父节点的子节全被选择的父节点', newObj)
let allSelectParKey = []
let noAllSelectparKey = []
for (let k in newObj) {
if (newObj[k] !== 0) {
allSelectParKey.push(k)
} else {
noAllSelectparKey.push(k)
}
}
return [allSelectParKey, noAllSelectparKey]
},
initCityTreeData() {
this.cityOptions = cloneDeep(this.cityList)
this.dataSourceTransfer = this.getAllChild(cloneDeep(this.cityList))
this.generateList(this.cityOptions)
this.flatten(this.cityList)
this.parentKeys = this.cityOptions.map((item) => {
return item.key
})
let parKey = this.checkDetailParentKey()
let parKeys = this.getAllSelectParentKey(parKey)[0]
this.targetKeys = [...this.filterTargetKeys, ...parKeys]
this.cityOptions = handleTreeData(this.cityOptions, this.targetKeys)
},
getShowAcount(names) {
var countedNames = names.reduce((obj, name) => {
if (name in obj) {
obj[name]++
} else {
obj[name] = 1
}
return obj
}, {})
return countedNames
},
filterTree(tree = [], targetKeys = [], validate = () => {}) {
if (!tree.length) {
return []
}
const result = []
for (let item of tree) {
if (item.children && item.children.length) {
let node = {
...item,
children: [],
disabled: targetKeys.includes(item.key)
}
for (let o of item.children) {
if (!validate.apply(null, [o, targetKeys])) continue
node.children.push({ ...o, disabled: targetKeys.includes(o.key) })
}
if (node.children.length) {
result.push(node)
}
}
}
return result
},
formatOptions(data, children) {
const arr = children || []
data.forEach((e) => {
const item = {
key: e.id,
title: e.name,
scopedSlots: { title: 'title' }
}
if (e.children && e.children.length) {
item.children = []
this.formatOptions(e.children, item.children)
}
arr.push(item)
})
return arr
},
onChange(targetKeys) {
},
handleTransferChange(targetKeys, direction, moveKeys) {
if (direction === 'left') {
if (this.isSelectAllCity(moveKeys, this.getAllChild(this.cityOptions))) {
this.targetKeys = []
this.filterTargetKeys = []
} else {
let parKey = this.searchParents(this.dataSource, moveKeys)
this.targetKeys = this.targetKeys.filter((item) => ![...moveKeys, ...parKey].includes(item))
this.filterTargetKeys = this.filterTargetKeys.filter((item) => !moveKeys.includes(item))
}
this.cityOptions = handleTreeData(this.cityOptions, this.targetKeys)
} else {
if (this.isSelectAllCity(targetKeys, this.getAllChild(this.cityOptions))) {
console.log('全选')
this.targetKeys = [...targetKeys, ...this.parentKeys]
this.filterTargetKeys = targetKeys
} else {
console.log('非全选')
let parKey = this.searchParents(this.dataSource, targetKeys)
this.targetKeys = [...targetKeys, ...parKey]
this.filterTargetKeys = targetKeys
}
console.log(' this.targetKeys', this.targetKeys, ',this.filterTargetKeys', this.filterTargetKeys)
this.cityOptions = handleTreeData(this.cityOptions, this.targetKeys)
}
},
handleTreeChecked(keys, e, checkedKeys, itemSelect, itemSelectAll) {
const {
eventKey,
checked,
dataRef: { children }
} = e.node
if (this.parentKeys && this.parentKeys.includes(eventKey)) {
let abledChildKeys = []
children.forEach((item) => {
if (!item.disabled) {
abledChildKeys.push(item.key)
}
})
let childKeys = abledChildKeys.length ? abledChildKeys : []
if (childKeys.length) itemSelectAll(childKeys, !checked)
} else {
itemSelect(eventKey, isChecked(keys, eventKey))
}
},
closeEditModal(nodeData) {
this.isShowEditModal = nodeData
},
handleBillNameEdit(type) {
console.log('编辑')
this.type = type
this.isShowEditModal = true
},
searchParents(list, moveKeys, flag = true) {
let arr = []
list.forEach((item) => {
if (moveKeys.includes(item.key)) {
if (item.key != '0') {
arr.push(item.parentId)
}
}
})
if (flag) {
arr = [...new Set(arr)]
}
return arr
},
handleCancel() {
this.$emit('closeCityModal', false)
},
handleOk() {
console.log(this.filterTargetKeys)
if (this.filterTargetKeys.length === 0) {
return this.$message.info('至少选择一个城市')
}
let sureCityList = this.dataList.filter((item) => {
return this.filterTargetKeys.includes(item.key)
})
let flag = this.isSelectAllCity(sureCityList, this.getAllChild(this.cityOptions))
this.$emit('handleChangeCity', sureCityList, this.dataSource, flag)
this.$emit('closeCityModal', false)
},
getAllChild(list) {
let arr = []
list.forEach((item) => {
if (item.children.length) {
item.children.forEach((child) => {
arr.push(child)
})
}
})
return arr
},
isSelectAllCity(selectCity, childAllCity) {
return selectCity.length === childAllCity.length
}
}
}
</script>
<style lang="less" scoped>
.transfer-box {
padding-bottom: 35px;
display: flex;
justify-content: center;
}
::v-deep .ant-transfer-list-body.ant-transfer-list-body-with-search {
overflow-y: hidden;
padding-top: 50px;
width: 100%;
}
::v-deep .ant-transfer-list-body-customize-wrapper {
overflow: auto;
height: 100%;
}
::v-deep .ant-transfer-list-content {
padding-bottom: 15px;
height: 100%;
}
::v-deep .ant-transfer-list-body.ant-transfer-list-body-with-search .ant-transfer-list-body-search-wrapper {
position: absolute;
z-index: 99;
width: 100%;
background-color: #fff;
}
</style>
<!-- 选择城市 -->
<ChooseCity
ref="chooseCity"
v-if="isShowCityModal"
:cityList="cityList"
@closeCityModal="closeCityModal"
@handleChangeCity="handleChangeCity"
:sureCityList="form.sureCityList"
/>