You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
263 lines
8.8 KiB
263 lines
8.8 KiB
<template>
|
|
<el-select
|
|
ref="select"
|
|
:value="value"
|
|
:disabled="readonly"
|
|
:size="size"
|
|
clearable
|
|
@clear="() => $emit('clear')"
|
|
@visible-change="visibleChange"
|
|
>
|
|
<template slot="empty">
|
|
<div class="rg-header">
|
|
<span>行政区划选择器</span>
|
|
<button class="rg-removeall-button" type="button" title="清除已选" @click="removeAll">
|
|
<i class="el-icon-delete"/>
|
|
</button>
|
|
<button class="rg-done-button" type="button" title="完成" @click="done">
|
|
<i class="el-icon-check"/>
|
|
</button>
|
|
</div>
|
|
<div class="rg-search">
|
|
<input
|
|
ref="searchInput"
|
|
v-model="searchText"
|
|
class="rg-input"
|
|
type="text"
|
|
placeholder="输入地区id或名称,回车搜索"
|
|
@keyup.enter="search"
|
|
>
|
|
</div>
|
|
<div class="rg-level-tabs">
|
|
<ul>
|
|
<li v-for="(tab,index) in selected" :key="tab.name" :class="{active:index+1 === currentLevel}">
|
|
<a href="javascript:" @click="()=>clickTab(index,tab)">{{tab.name}}</a>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
<div v-loading="loading" class="rg-results-container">
|
|
<ul class="rg-results">
|
|
<li
|
|
v-for="item in items"
|
|
:key="item.id"
|
|
:class="{'rg-item': true, 'active': isItemActive(item.id)}"
|
|
@click="()=>clickItem(item)"
|
|
>
|
|
{{limit ? item.name + `(${item.value})` : item.name}}
|
|
</li>
|
|
<li v-if="items.length === 0" class="rg-message-box">无匹配项目</li>
|
|
</ul>
|
|
</div>
|
|
</template>
|
|
</el-select>
|
|
</template>
|
|
|
|
<script>
|
|
import {createLimitTree, getNodeId} from "@/utils/tree"
|
|
import common from '../mixin'
|
|
|
|
const PROVINCE_CITIES = ['北京市', '天津市', '上海市', '重庆市']
|
|
const DEFAULT_TABS = [{name: '省/直辖市'}, {name: '市'}, {name: '区/县'}, {name: '乡/镇/街道'}]
|
|
const DEFAULT_MAX_LEVEL = 4
|
|
|
|
export default {
|
|
name: "RegionTabSelector",
|
|
|
|
mixins: [common],
|
|
|
|
props: {
|
|
value: [String, Array],
|
|
separation: {type: String, default: ','}
|
|
},
|
|
|
|
data() {
|
|
return {
|
|
loading: false,
|
|
searchText: '',
|
|
currentLevel: 1,
|
|
maxLevel: DEFAULT_MAX_LEVEL,
|
|
selected: [],
|
|
treeData: [],
|
|
items: []
|
|
}
|
|
},
|
|
|
|
computed: {
|
|
regionTree() {
|
|
return this.$store.state.dataCache.regionTree
|
|
},
|
|
},
|
|
|
|
watch: {
|
|
currentLevel(v) {
|
|
this.setItems(v)
|
|
}
|
|
},
|
|
|
|
methods: {
|
|
//select的下拉框展开收起
|
|
visibleChange(v) {
|
|
if (v) {
|
|
this.transformValueToSelected()
|
|
this.$nextTick(() => this.$refs.searchInput.focus())
|
|
}
|
|
else this.removeAll()
|
|
},
|
|
|
|
search() {
|
|
const parent = this.selected[this.currentLevel - 2] || {children: this.treeData}
|
|
|
|
if (!this.searchText) return this.items = parent.children
|
|
|
|
const predicate = item => item.id.startsWith(this.searchText) || item.name.startsWith(this.searchText)
|
|
|
|
this.items = parent.children.filter(predicate)
|
|
},
|
|
|
|
clickTab(index, tab) {
|
|
//点击当前激活的tab时不处理
|
|
if (index + 1 === this.currentLevel) return
|
|
|
|
this.currentLevel = index + 1
|
|
},
|
|
|
|
clickItem(item) {
|
|
this.setTabs(item, this.currentLevel)
|
|
|
|
this.nextTab()
|
|
},
|
|
|
|
setTabs(item, level) {
|
|
const prev = this.selected.slice(0, level - 1)
|
|
prev.push(item)
|
|
const next = JSON.parse(JSON.stringify(DEFAULT_TABS))
|
|
|
|
if (level === 1) {
|
|
//选择直辖市的时候,最大深度-1,移除'市'tab
|
|
if (PROVINCE_CITIES.includes(item.name)) {
|
|
next.splice(1, 1)
|
|
this.maxLevel = DEFAULT_MAX_LEVEL - 1
|
|
}
|
|
else this.maxLevel = DEFAULT_MAX_LEVEL
|
|
}
|
|
|
|
this.selected = prev.concat(next.slice(level, this.maxLevel))
|
|
},
|
|
|
|
nextTab() {
|
|
this.searchText = ''
|
|
//若选择的是最后一级节点,直接完成
|
|
if (this.currentLevel >= this.maxLevel) {
|
|
this.done()
|
|
}
|
|
else this.currentLevel++
|
|
},
|
|
|
|
//判断是否是已选项
|
|
isItemActive(id) {
|
|
const selected = this.selected[this.currentLevel - 1]
|
|
return selected && selected.id === id
|
|
},
|
|
|
|
//根据激活的tab改变显示的行政区划
|
|
setItems(level) {
|
|
//顶级节点返回完整的树
|
|
if (level === 1) this.items = this.treeData
|
|
|
|
//否则返回上一级节点的children
|
|
else {
|
|
const parent = this.selected[level - 2]
|
|
this.items = parent ? parent.children || [] : []
|
|
}
|
|
},
|
|
|
|
//将传入的value转换为已选项
|
|
transformValueToSelected() {
|
|
if (!this.value) return
|
|
|
|
const result = [],
|
|
loopArray = Array.isArray(this.value)
|
|
? this.value
|
|
: this.value.split(this.separation).filter(Boolean)
|
|
|
|
for (let i = 0; i < loopArray.length; i++) {
|
|
const str = loopArray[i],
|
|
parent = result[i - 1] || {children: this.treeData}
|
|
|
|
const node = parent.children.find(predicate(str))
|
|
if (!node) break
|
|
result.push(node)
|
|
}
|
|
|
|
result.forEach((item, index) => this.setTabs(item, index + 1))
|
|
},
|
|
|
|
removeAll() {
|
|
this.maxLevel = DEFAULT_MAX_LEVEL
|
|
this.selected = JSON.parse(JSON.stringify(DEFAULT_TABS))
|
|
this.currentLevel = 1
|
|
},
|
|
|
|
done() {
|
|
const result = this.selected.filter(item => item.id)
|
|
this.$emit('input', result.map(item => item.name).join(this.separation))
|
|
|
|
if (result.length === 0) {
|
|
this.$emit('select', {}, [])
|
|
return this.$refs.select.blur()
|
|
}
|
|
|
|
const node = result[result.length - 1]
|
|
const payload = [node]
|
|
|
|
if (this.getChildrenOnSelect) {
|
|
const ids = getNodeId(node.children)
|
|
ids.unshift(node.id)
|
|
payload.push(ids)
|
|
}
|
|
|
|
this.$emit('select', ...payload)
|
|
|
|
this.$refs.select.blur()
|
|
},
|
|
|
|
init() {
|
|
this.loading = true
|
|
const hasInit = this.regionTree.length > 0
|
|
const promise = () => hasInit ? Promise.resolve() : this.$store.dispatch('dataCache/initRegion')
|
|
return promise()
|
|
.then(() => this.limit ? this.limitApi() : Promise.resolve())
|
|
.then(data => {
|
|
this.treeData = data ? createLimitTree(this.regionTree, data) : this.regionTree
|
|
this.setItems(this.currentLevel)
|
|
})
|
|
.finally(() => this.loading = false)
|
|
}
|
|
},
|
|
|
|
created() {
|
|
this.selected = JSON.parse(JSON.stringify(DEFAULT_TABS))
|
|
},
|
|
|
|
mounted() {
|
|
this.init()
|
|
}
|
|
}
|
|
|
|
//根据node的类型生成查找断言,支持{id:'10',...}、{name:'北京市',...}、'10'、'北京市' 等四种类型
|
|
function predicate(node) {
|
|
if (typeof node === 'object') {
|
|
const key = node.hasOwnProperty('id') ? 'id' : 'name'
|
|
return item => item[key] === node[key]
|
|
}
|
|
else {
|
|
const firstCharCode = node.charCodeAt(0)
|
|
const key = 48 <= firstCharCode && firstCharCode <= 57 ? 'id' : 'name'
|
|
return item => item[key] === node
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss">
|
|
@import "./style.scss";
|
|
</style>
|
|
|