Files
dvcp_v2_webapp/ui/packages/common/AiAreaGet.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>