296 lines
8.2 KiB
Vue
296 lines
8.2 KiB
Vue
<template>
|
|
<section class="AiAreaGet">
|
|
<el-cascader v-if="refresh" ref="areaCascader" :value="value" size="small" :props="props" :show-all-levels="showAll"
|
|
:options="options" @visible-change="editing=true" clearable
|
|
filterable :before-filter="handleFindArea" @change="handleAfterFilter"
|
|
v-bind="$attrs" v-on="$listeners" popper-class="popperSelectors"/>
|
|
</section>
|
|
</template>
|
|
|
|
<script>
|
|
/**
|
|
* 智能地区选择器
|
|
* @displayName AiAreaGet
|
|
*/
|
|
export default {
|
|
name: "AiAreaGet",
|
|
inject: {
|
|
elFormItem: {default: ""},
|
|
},
|
|
model: {
|
|
prop: 'value',
|
|
event: 'change',
|
|
},
|
|
props: {
|
|
/**
|
|
* 接口方法类:必填
|
|
*/
|
|
instance: {default: () => null},
|
|
/**
|
|
* 绑定地区编码
|
|
* @model
|
|
*/
|
|
value: {default: ""},
|
|
/**
|
|
* 是否多选
|
|
*/
|
|
multiple: Boolean,
|
|
/**
|
|
* 获取地区信息接口地址,默认为:/admin/area/queryAreaByParentId
|
|
*/
|
|
action: {default: "/admin/area/queryAreaByParentId"},
|
|
/**
|
|
* 限制获取地区编码的范围,1:省 2:地级市 3:县/区 4:镇/街道 5:村/社区
|
|
* @values 1,2,3,4,5
|
|
*/
|
|
valueLevel: {default: 5},
|
|
/**
|
|
* 指定根级地区范围
|
|
*/
|
|
root: {default: ""},
|
|
/**
|
|
* 获取地区名称,支持.sync 双向获取绑定
|
|
*/
|
|
name: {default: ""},
|
|
/**
|
|
* 显示完整地区名称
|
|
*/
|
|
showAll: Boolean
|
|
},
|
|
data() {
|
|
return {
|
|
rules: [10, 8, 6, 3, 0],
|
|
cacheOptions: [],
|
|
editing: false,
|
|
filterData: [],
|
|
caches: [],
|
|
roots: [],
|
|
refresh: true,
|
|
rootLoad: ""
|
|
}
|
|
},
|
|
watch: {
|
|
value(v) {
|
|
!this.editing && !this.caches.includes(this.value) && this.getCacheOptions()
|
|
this.dispatch('ElFormItem', 'el.form.change', [v]);
|
|
setTimeout(() => this.$emit("update:name", this.$refs.areaCascader?.inputValue))
|
|
},
|
|
root() {
|
|
if (this.value) {
|
|
this.getCacheOptions()
|
|
} else {
|
|
setTimeout(() => {
|
|
this.refresh = false
|
|
this.$nextTick(() => this.refresh = true)
|
|
}, 200)
|
|
}
|
|
},
|
|
options: {
|
|
handler() {
|
|
this.$nextTick(() => this.$forceUpdate())
|
|
}, deep: true, immediate: true
|
|
}
|
|
},
|
|
computed: {
|
|
fullArea() {
|
|
const length = 12,
|
|
getFull = v => this.rules.map(e => {
|
|
let reg = new RegExp(`(\\d{${length-e}})\\d{${e}}`, 'g')
|
|
return v?.replace(reg, '$1' + Array(e).fill(0).join(''))
|
|
}).filter((e, i) => i <= this.getLevel(v))
|
|
return this.multiple ? [this.value].flat()?.map(e => getFull(e)) : getFull(this.value)
|
|
},
|
|
props() {
|
|
return {
|
|
label: 'name',
|
|
value: 'id',
|
|
lazy: true,
|
|
multiple: this.multiple,
|
|
checkStrictly: true,
|
|
emitPath: false,
|
|
lazyLoad: (node, resolve) => {
|
|
if (!(this.caches.includes(node.value) && this.fullArea.includes(node.value)) || node.loading) {
|
|
if (node?.level == 0) {
|
|
this.getRoots(resolve, "lazyLoad")
|
|
} else if (node?.level > 0 && node.children?.length == 0) {
|
|
let {id, leaf} = node.data
|
|
leaf ? resolve([]) : this.getAreasByParent(id, resolve)
|
|
} else resolve([])
|
|
} else resolve([])
|
|
}
|
|
}
|
|
},
|
|
options() {
|
|
return [...this.cacheOptions, ...this.filterData]
|
|
},
|
|
filtering() {
|
|
let v = this.$refs?.areaCascader?.filtering
|
|
if (!v) this.filterData = []
|
|
return v
|
|
}
|
|
},
|
|
methods: {
|
|
getLevel(code) {
|
|
let lv = -1
|
|
this.rules.some((e, index) => {
|
|
let reg = new RegExp(`0{${e}}$`, "g")
|
|
if (reg.test(code)) {
|
|
lv = index
|
|
return true
|
|
}
|
|
})
|
|
return lv
|
|
},
|
|
getRoots(resolve, from) {
|
|
let url = '/admin/area/queryProvinceList'
|
|
if (this.root) {
|
|
url = "/admin/area/queryAreaByAreaid"
|
|
if (this.rootLoad == this.root) {
|
|
let waitRoots = (count = 0) => setTimeout(() => {
|
|
if (this.roots.length > 0 || count == 5) {
|
|
resolve(this.roots)
|
|
} else waitRoots(++count)
|
|
}, 500)
|
|
return from == "lazyLoad" ? '' : waitRoots()
|
|
}
|
|
}
|
|
this.rootLoad = JSON.parse(JSON.stringify(this.root))
|
|
if (this.roots.some(e => e.id == this.root)) {
|
|
resolve(this.roots)
|
|
} else this.instance.post(url, null, {
|
|
params: {id: this.root, from}, withoutToken: true
|
|
}).then(res => {
|
|
if (res?.data) {
|
|
this.roots = [res.data].flat().map(e => ({...e, leaf: e.type == this.valueLevel}))
|
|
resolve(this.roots)
|
|
}
|
|
})
|
|
},
|
|
getAreasByParent(id, resolve) {
|
|
id && this.instance.post(this.action, null, {
|
|
params: {id}, withoutToken: true,
|
|
}).then(res => {
|
|
if (res?.data) {
|
|
resolve(res.data.map(e => ({...e, leaf: e.type == this.valueLevel})))
|
|
}
|
|
})
|
|
},
|
|
async getCacheOptions() {
|
|
let finished = 0
|
|
const hasChild = ids => ids?.some(e => this.fullArea?.flat()?.includes(e)),
|
|
appendChildren = (area, resolve) => {
|
|
let values = [this.value].flat()
|
|
if (values.includes(area.id)) {
|
|
finished++
|
|
if (finished == values.length) {
|
|
this.$emit("update:name", area.name)
|
|
resolve()
|
|
}
|
|
} else this.getAreasByParent(area.id, data => {
|
|
this.$set(area, "children", data)
|
|
data.map(d => {
|
|
this.caches.push(d.id)
|
|
hasChild([d.id]) && appendChildren(d, resolve)
|
|
})
|
|
})
|
|
}
|
|
if (!!this.value?.toString()) {
|
|
this.cacheOptions = []
|
|
this.caches = []
|
|
await this.getRoots(data => {
|
|
this.caches = data?.map(e => e.id) || []
|
|
new Promise(resolve => {
|
|
if (hasChild(data.map(e => e.id))) {
|
|
data.map(e => hasChild([e.id]) && appendChildren(e, resolve))
|
|
} else resolve()
|
|
}).then(() => {
|
|
this.cacheOptions = data
|
|
})
|
|
}, "initWithValue")
|
|
} else if (!!this.root) {
|
|
this.caches = []
|
|
await this.getRoots(data => {
|
|
this.caches = data?.map(e => e.id) || []
|
|
new Promise(resolve => {
|
|
if (hasChild(data.map(e => e.id))) {
|
|
data.map(e => hasChild([e.id]) && appendChildren(e, resolve))
|
|
} else resolve()
|
|
}).then(() => {
|
|
this.cacheOptions = data
|
|
})
|
|
}, "initWithRoot")
|
|
}
|
|
},
|
|
/**
|
|
* 表单验证
|
|
* @param componentName
|
|
* @param eventName
|
|
* @param params
|
|
*/
|
|
dispatch(componentName, eventName, params) {
|
|
let parent = this.$parent || this.$root;
|
|
let name = parent.$options.componentName;
|
|
|
|
while (parent && (!name || name !== componentName)) {
|
|
parent = parent.$parent;
|
|
|
|
if (parent) {
|
|
name = parent.$options.componentName;
|
|
}
|
|
}
|
|
if (parent) {
|
|
parent.$emit.apply(parent, [eventName].concat(params));
|
|
}
|
|
},
|
|
handleFindArea(areaName) {
|
|
return new Promise(resolve => {
|
|
this.instance.post("/admin/area/queryAreaByAreaName", null, {
|
|
params: {areaName}
|
|
}).then(res => {
|
|
if (res?.data) {
|
|
let range = new RegExp(`^${this.root.replace(/0+$/g, '')||'\d'}`)
|
|
this.filterData = res.data.filter(e => !this.caches.includes(e.id) && range.test(e.id)).map(e => ({
|
|
...e,
|
|
leaf: e.type == this.valueLevel
|
|
}))
|
|
resolve()
|
|
}
|
|
})
|
|
})
|
|
},
|
|
handleAfterFilter(v) {
|
|
this.$emit('select', this.$refs.areaCascader?.getCheckedNodes(true))
|
|
this.$refs.areaCascader?.toggleDropDownVisible(false)
|
|
if (!this.multiple) {
|
|
if (this.filterData?.length > 0) {
|
|
this.filterData = []
|
|
}
|
|
this.editing = this.caches.includes(v);
|
|
}
|
|
}
|
|
},
|
|
created() {
|
|
setTimeout(() => {
|
|
this.cacheOptions.length == 0 && this.getCacheOptions()
|
|
})
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.AiAreaGet {
|
|
width: 100%;
|
|
|
|
.el-cascader {
|
|
width: 100%;
|
|
}
|
|
}
|
|
</style>
|
|
<style lang="scss">
|
|
.popperSelectors {
|
|
.el-cascader-menu__wrap {
|
|
height: 300px;
|
|
}
|
|
}
|
|
</style>
|